/*
* Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javafx.scene.web;
import com.sun.webkit.BackForwardList;
import com.sun.webkit.WebPage;
import com.sun.webkit.event.WCChangeEvent;
import com.sun.webkit.event.WCChangeListener;
import java.net.URL;
import java.util.Date;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ReadOnlyIntegerProperty;
import javafx.beans.property.ReadOnlyIntegerWrapper;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
The WebHistory
class represents a session history associated with a WebEngine
instance. A single instance of WebHistory
for a particular web engine can be obtained through the WebEngine.getHistory()
method. The history is basically a list of entries. Each entry represents a visited page and it provides access to relevant page info, such as URL, title, and the date the page was last visited. Entries in the list are arranged in the order in which the corresponding pages were visited from oldest to newest. The list can be obtained by using the getEntries()
method. The history and the corresponding list of entries change as WebEngine
navigates across the web. The list may expand or shrink depending on browser actions. These changes can be listened to by the ObservableList
API that the list exposes. The index of the history entry associated with the currently visited page is represented by the currentIndexProperty
. The current index can be used to navigate to any entry in the history by using the go(int)
method. The maxSizeProperty()
sets the maximum history size, which is the size of the history list. Since: JavaFX 2.2
/**
* The {@code WebHistory} class represents a session history associated with
* a {@link WebEngine} instance.
*
* A single instance of {@code WebHistory} for a particular web engine can be
* obtained through the {@link WebEngine#getHistory()} method.
*
* The history is basically a list of entries. Each entry represents a visited page
* and it provides access to relevant page info, such as URL, title, and the date
* the page was last visited. Entries in the list are arranged in the order
* in which the corresponding pages were visited from oldest to newest. The list can
* be obtained by using the {@link #getEntries()} method.
*
* The history and the corresponding list of entries change as {@code WebEngine} navigates
* across the web. The list may expand or shrink depending on browser actions. These
* changes can be listened to by the {@link javafx.collections.ObservableList}
* API that the list exposes.
*
* The index of the history entry associated with the currently visited page
* is represented by the {@link #currentIndexProperty}. The current index can be
* used to navigate to any entry in the history by using the {@link #go(int)} method.
*
* The {@link #maxSizeProperty()} sets the maximum history size, which is the size of the
* history list.
*
* @since JavaFX 2.2
*/
public final class WebHistory {
The Entry
class represents a single entry in the session history. An entry instance is associated with the visited page. Since: JavaFX 2.2
/**
* The {@code Entry} class represents a single entry in the session history.
* An entry instance is associated with the visited page.
*
* @since JavaFX 2.2
*/
public final class Entry {
private final URL url;
private final ReadOnlyObjectWrapper<String> title = new ReadOnlyObjectWrapper(this, "title");
private final ReadOnlyObjectWrapper<Date> lastVisitedDate = new ReadOnlyObjectWrapper(this, "lastVisitedDate");
private final BackForwardList.Entry peer;
private Entry(final BackForwardList.Entry entry) {
this.url = entry.getURL();
this.title.set(entry.getTitle());
this.lastVisitedDate.set(entry.getLastVisitedDate());
this.peer = entry;
entry.addChangeListener(e -> {
String _title = entry.getTitle();
// null title is acceptable
if (_title == null || !_title.equals(getTitle())) {
title.set(_title);
}
Date _date = entry.getLastVisitedDate();
// null date is not acceptable
if (_date != null && !_date.equals(getLastVisitedDate())) {
lastVisitedDate.set(_date);
}
});
}
Returns the URL of the page.
Returns: the url of the page
/**
* Returns the URL of the page.
*
* @return the url of the page
*/
public String getUrl() {
assert url != null;
return url.toString();
}
Defines the title of the page.
Returns: the title property
/**
* Defines the title of the page.
* @return the title property
*/
public ReadOnlyObjectProperty<String> titleProperty() {
return title.getReadOnlyProperty();
}
public String getTitle() {
return title.get();
}
Defines the Date
the page was last visited. Returns: the lastVisitedDate property
/**
* Defines the {@link java.util.Date} the page was last visited.
* @return the lastVisitedDate property
*/
public ReadOnlyObjectProperty<Date> lastVisitedDateProperty() {
return lastVisitedDate.getReadOnlyProperty();
}
public Date getLastVisitedDate() {
return lastVisitedDate.get();
}
boolean isPeer(BackForwardList.Entry entry) {
return peer == entry;
}
@Override
public String toString() {
return "[url: " + getUrl()
+ ", title: " + getTitle()
+ ", date: " + getLastVisitedDate()
+ "]";
}
}
private final BackForwardList bfl; // backend history impl
private final ObservableList<Entry> list;
private final ObservableList<Entry> ulist; // unmodifiable wrapper
WebHistory(WebPage page) {
this.list = FXCollections.<Entry>observableArrayList();
this.ulist = FXCollections.unmodifiableObservableList(list);
this.bfl = page.createBackForwardList();
setMaxSize(getMaxSize()); // init default
this.bfl.addChangeListener(e -> {
// 1. Size has increased
// - one new entry is appended.
// - currentIndex is set to the new entry.
if (bfl.size() > list.size()) {
assert (bfl.size() == list.size() + 1);
list.add(new Entry(bfl.getCurrentEntry()));
WebHistory.this.setCurrentIndex(list.size() - 1);
return;
}
// 2. Size hasn't changed
if (bfl.size() == list.size()) {
if (list.size() == 0) {
return; // no changes
}
assert (list.size() > 0);
BackForwardList.Entry last = bfl.get(list.size() - 1);
BackForwardList.Entry first = bfl.get(0);
// - currentIndex may change
if (list.get(list.size() - 1).isPeer(last)) {
WebHistory.this.setCurrentIndex(bfl.getCurrentIndex());
return;
// - first entry is removed.
// - one new entry is appended.
// - currentIndex is set to the new entry.
} else if (!list.get(0).isPeer(first)) {
list.remove(0);
list.add(new Entry(last));
WebHistory.this.setCurrentIndex(bfl.getCurrentIndex());
return;
}
}
// 3. Size has decreased or hasn't changed (due to maxSize or navigation)
// - one or more entries are popped.
// - one new entry may be appended.
// - currentIndex may be set to the new entry.
assert (bfl.size() <= list.size());
list.remove(bfl.size(), list.size()); // no-op if equals
int lastIndex = list.size() - 1;
if (lastIndex >= 0 && !list.get(lastIndex).isPeer(bfl.get(lastIndex))) {
list.remove(lastIndex);
list.add(new Entry(bfl.get(lastIndex)));
}
WebHistory.this.setCurrentIndex(bfl.getCurrentIndex());
});
}
private final ReadOnlyIntegerWrapper currentIndex =
new ReadOnlyIntegerWrapper(this, "currentIndex");
Defines the index of the current Entry
in the history. The current entry is the entry associated with the currently loaded page. The index belongs to the range of {@code(index >= 0 && index < getEntries().size())}. Returns: the currentIndex property
/**
* Defines the index of the current {@code Entry} in the history.
* The current entry is the entry associated with the currently loaded page.
* The index belongs to the range of {@code(index >= 0 && index < getEntries().size())}.
*
* @return the currentIndex property
*/
public ReadOnlyIntegerProperty currentIndexProperty() {
return currentIndex.getReadOnlyProperty();
}
public int getCurrentIndex() {
return currentIndexProperty().get();
}
private void setCurrentIndex(int value) {
currentIndex.set(value);
}
private IntegerProperty maxSize;
Defines the maximum size of the history list.
If the list reaches its maximum and a new entry is added,
the first entry is removed from the history.
The value specified for this property can not be negative, otherwise IllegalArgumentException
is thrown.
@defaultValue 100 Returns: the maxSize property
/**
* Defines the maximum size of the history list.
* If the list reaches its maximum and a new entry is added,
* the first entry is removed from the history.
* <p>
* The value specified for this property can not be negative, otherwise
* {@code IllegalArgumentException} is thrown.
*
* @defaultValue 100
* @return the maxSize property
*/
public IntegerProperty maxSizeProperty() {
if (maxSize == null) {
maxSize = new SimpleIntegerProperty(this, "maxSize", 100) {
@Override
public void set(int value) {
if (value < 0) {
throw new IllegalArgumentException("value cannot be negative.");
}
super.set(value);
}
};
}
return maxSize;
}
public void setMaxSize(int value) {
maxSizeProperty().set(value);
bfl.setMaximumSize(value);
}
public int getMaxSize() {
return maxSizeProperty().get();
}
Returns an unmodifiable observable list of all entries in the history.
Returns: list of all history entries
/**
* Returns an unmodifiable observable list of all entries in the history.
*
* @return list of all history entries
*/
public ObservableList<Entry> getEntries() {
return ulist;
}
Navigates the web engine to the URL defined by the Entry
object within the specified position relative to the current entry. A negative offset
value specifies the position preceding to the current entry, and a positive offset
value specifies the position following the current entry. For example, -1 points to the previous entry, and 1 points to the next entry, corresponding to pressing a web browser's 'back' and 'forward' buttons, respectively. The zero offset
value is silently ignored (no-op). The effective entry position should belong to the rage of [0..size-1]. Otherwise, IndexOutOfBoundsException
is thrown. Params: - offset – a negative value specifies a position preceding the
current entry, a positive value specifies a position following
the current entry, zero value causes no effect
Throws: - IndexOutOfBoundsException – if the effective entry position is out
of range
/**
* Navigates the web engine to the URL defined by the {@code Entry} object
* within the specified position relative to the current entry. A negative
* {@code offset} value specifies the position preceding to the current entry,
* and a positive {@code offset} value specifies the position following the
* current entry. For example, -1 points to the previous entry, and 1 points
* to the next entry, corresponding to pressing a web browser's 'back'
* and 'forward' buttons, respectively.
*
* The zero {@code offset} value is silently ignored (no-op).
*
* The effective entry position should belong to the rage of [0..size-1].
* Otherwise, {@code IndexOutOfBoundsException} is thrown.
*
* @param offset a negative value specifies a position preceding the
* current entry, a positive value specifies a position following
* the current entry, zero value causes no effect
* @throws IndexOutOfBoundsException if the effective entry position is out
* of range
*/
public void go(int offset) throws IndexOutOfBoundsException {
if (offset == 0)
return;
int index = getCurrentIndex() + offset;
if (index < 0 || index >= list.size()) {
throw new IndexOutOfBoundsException("the effective index " + index
+ " is out of the range [0.."
+ (list.size() - 1) + "]");
}
bfl.setCurrentIndex(index);
}
}