/* GNU GENERAL LICENSE Copyright (C) 2006 The Lobo Project. Copyright (C) 2014 - 2017 Lobo Evolution This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either verion 3 of the License, or (at your option) any later version. This program 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 License for more details. You should have received a copy of the GNU General Public along with this program. If not, see <http://www.gnu.org/licenses/>. Contact info: lobochief@users.sourceforge.net; ivan.difrancesco@yahoo.it */ /* * Created on Oct 22, 2005 */ package org.lobobrowser.html.test; import java.awt.Component; import java.awt.Cursor; import java.awt.Window; import java.awt.event.MouseEvent; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.Proxy; import java.net.URL; import java.net.URLConnection; import java.net.URLEncoder; import java.util.Optional; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import javax.swing.JOptionPane; import org.lobobrowser.html.BrowserFrame; import org.lobobrowser.html.FormInput; import org.lobobrowser.html.HtmlObject; import org.lobobrowser.html.HtmlRendererContext; import org.lobobrowser.html.dombl.FrameNode; import org.lobobrowser.html.domimpl.HTMLDocumentImpl; import org.lobobrowser.html.gui.HtmlPanel; import org.lobobrowser.html.parser.DocumentBuilderImpl; import org.lobobrowser.html.parser.InputSourceImpl; import org.lobobrowser.http.UserAgentContext; import org.lobobrowser.util.SSLCertificate; import org.lobobrowser.util.Urls; import org.lobobrowser.util.io.BufferExceededException; import org.lobobrowser.util.io.RecordedInputStream; import org.lobobrowser.w3c.html.HTMLAnchorElement; import org.lobobrowser.w3c.html.HTMLCollection; import org.lobobrowser.w3c.html.HTMLElement; import org.w3c.dom.Node; /** * The <code>SimpleHtmlRendererContext</code> class implements the * {@link org.lobobrowser.html.HtmlRendererContext} interface. Note that this * class provides rudimentary implementations of most callback methods. * Overridding some of the methods in this class will usually be necessary in a * professional application. * <p> * A simple way to load a URL into the {@link HtmlPanel} of the renderer context * is to invoke {@link #navigate(String)}. */ public class SimpleHtmlRendererContext implements HtmlRendererContext { /** The Constant logger. */ private static final Logger logger = LogManager.getLogger(SimpleHtmlRendererContext.class); /** The html panel. */ private final HtmlPanel htmlPanel; /** The parent rcontext. */ private final HtmlRendererContext parentRcontext; /** * Constructs a SimpleHtmlRendererContext. * * @param contextComponent * The component that will render HTML. * @param ucontext * the ucontext * @see SimpleUserAgentContext */ public SimpleHtmlRendererContext(HtmlPanel contextComponent, UserAgentContext ucontext) { super(); this.htmlPanel = contextComponent; this.parentRcontext = null; this.bcontext = ucontext; } /** * Constructs a SimpleHtmlRendererContext that is a child of another * <code>{@link HtmlRendererContext}</code>. * * @param contextComponent * The component that will render HTML. * @param parentRcontext * The parent's renderer context. */ public SimpleHtmlRendererContext(HtmlPanel contextComponent, HtmlRendererContext parentRcontext) { super(); this.htmlPanel = contextComponent; this.parentRcontext = parentRcontext; this.bcontext = parentRcontext == null ? null : parentRcontext.getUserAgentContext(); } /** * Gets the html panel. * * @return the html panel */ public HtmlPanel getHtmlPanel() { return this.htmlPanel; } /** The source code. */ private volatile String sourceCode; /** * Gets the source code. * * @return the source code */ public String getSourceCode() { return this.sourceCode; } /** * Gets a collection of current document frames, by querying the document * currently held by the local {@link org.lobobrowser.html.gui.HtmlPanel} * instance. * * @return the frames */ @Override public HTMLCollection getFrames() { Object rootNode = this.htmlPanel.getRootNode(); if (rootNode instanceof HTMLDocumentImpl) { return ((HTMLDocumentImpl) rootNode).getFrames(); } else { return null; } } /** * Implements reload as navigation to current URL. Override to implement a * more robust reloading mechanism. */ @Override public void reload() { HTMLDocumentImpl document = (HTMLDocumentImpl) this.htmlPanel.getRootNode(); if (document != null) { try { URL url = new URL(document.getDocumentURI()); this.navigate(url, null); } catch (MalformedURLException throwable) { this.warn("reload(): Malformed URL", throwable); } } } /** * Implements the link click handler by invoking * {@link #navigate(URL, String)}. * * @param linkNode * the link node * @param url * the url * @param target * the target */ @Override public void linkClicked(HTMLElement linkNode, URL url, String target) { this.navigate(url, target); } /** * Gets the proxy. * * @return the proxy */ protected Proxy getProxy() { Object ucontext = this.getUserAgentContext(); if (ucontext instanceof SimpleUserAgentContext) { return ((SimpleUserAgentContext) ucontext).getProxy(); } return Proxy.NO_PROXY; } /** * Implements simple navigation with incremental rendering by invoking * {@link #submitForm(String, URL, String, String, FormInput[])} with a * <code>GET</code> request method. * * @param href * the href * @param target * the target */ @Override public void navigate(final URL href, String target) { this.submitForm("GET", href, target, null, null); } /** * Convenience method provided to allow loading a document into the * renderer. * * @param fullURL * The absolute URL of the document. * @throws MalformedURLException * the malformed url exception * @throws UnsupportedEncodingException * @see #navigate(URL, String) */ public void navigate(String fullURL) throws MalformedURLException, UnsupportedEncodingException { URL href = Urls.createURL(null, fullURL); this.navigate(href, "_this"); } /** * Implements simple navigation and form submission with incremental * rendering and target processing, including frame lookup. Should be * overridden to allow for more robust browser navigation and form * submission. * <p> * <b>Notes:</b> * <ul> * <li>Document encoding is defined by * {@link #getDocumentCharset(URLConnection)}. * <li>Caching is not implemented. * <li>Cookies are not implemented. * <li>Incremental rendering is not optimized for ignorable document change * notifications. * <li>Other HTTP features are not implemented. * <li>The only form encoding type supported is * <code>application/x-www-form-urlencoded</code>. * <li>Navigation is normally asynchronous. See * {@link #isNavigationAsynchronous()}. * </ul> * * @param method * the method * @param action * the action * @param target * the target * @param enctype * the enctype * @param formInputs * the form inputs * @see #navigate(URL, String) */ @Override public void submitForm(final String method, final URL action, final String target, final String enctype, final FormInput[] formInputs) { // This method implements simple incremental rendering. if (target != null) { HtmlRendererContext topCtx = this.getTop(); HTMLCollection frames = topCtx.getFrames(); if (frames != null) { Node frame = frames.namedItem(target); if (logger.isInfoEnabled()) { logger.info("submitForm(): Frame matching target=" + target + " is " + frame); } if (frame instanceof FrameNode) { BrowserFrame bframe = ((FrameNode) frame).getBrowserFrame(); if (bframe == null) { throw new IllegalStateException("Frame node without a BrowserFrame instance: " + frame); } if (bframe.getHtmlRendererContext() != this) { bframe.loadURL(action); return; } } } String actualTarget = target.trim().toLowerCase(); if ("_top".equals(actualTarget)) { this.getTop().navigate(action, null); return; } else if ("_parent".equals(actualTarget)) { HtmlRendererContext parent = this.getParent(); if (parent != null) { parent.navigate(action, null); return; } } else if ("_blank".equals(actualTarget)) { this.open(action, "cobra.blank", "", false); return; } else if ("_this".equals(actualTarget)) { // fall through } else { logger.warn("submitForm(): Link target unrecognized: " + actualTarget); } } // Make request asynchronously. if (this.isNavigationAsynchronous()) { new Thread() { @Override public void run() { try { SimpleHtmlRendererContext.this.submitFormSync(method, action, target, enctype, formInputs); } catch (Exception err) { SimpleHtmlRendererContext.this.error("navigate(): Error loading or parsing request.", err); } } }.start(); } else { try { SimpleHtmlRendererContext.this.submitFormSync(method, action, target, enctype, formInputs); } catch (Exception err) { SimpleHtmlRendererContext.this.error("navigate(): Error loading or parsing request.", err); } } } /** * Checks if is navigation asynchronous. * * @return true, if is navigation asynchronous */ protected boolean isNavigationAsynchronous() { return true; } /** * The connection currently opened by openSync() if any. */ protected URLConnection currentConnection; /** * Submits a form and/or navigates by making a <i>synchronous</i> request. * This method is invoked by * {@link #submitForm(String, URL, String, String, FormInput[])}. * * @param method * The request method. * @param action * The action URL. * @param target * The target identifier. * @param enctype * The encoding type. * @param formInputs * The form inputs. * @throws IOException * Signals that an I/O exception has occurred. * @throws SAXException * the SAX exception * @see #submitForm(String, URL, String, String, FormInput[]) */ protected void submitFormSync(final String method, final URL action, final String target, String enctype, final FormInput[] formInputs) throws IOException, org.xml.sax.SAXException { final String actualMethod = method.toUpperCase(); URL resolvedURL; if ("GET".equals(actualMethod) && (formInputs != null)) { boolean firstParam = true; // TODO: What about the userInfo part of the URL? URL noRefAction = new URL(action.getProtocol(), action.getHost(), action.getPort(), action.getFile()); StringBuffer newUrlBuffer = new StringBuffer(noRefAction.toExternalForm()); if (action.getQuery() == null) { newUrlBuffer.append("?"); } else { newUrlBuffer.append("&"); } for (int i = 0; i < formInputs.length; i++) { FormInput parameter = formInputs[i]; String name = parameter.getName(); String encName = URLEncoder.encode(name, "UTF-8"); if (parameter.isText()) { if (firstParam) { firstParam = false; } else { newUrlBuffer.append("&"); } String valueStr = parameter.getTextValue(); String encValue = URLEncoder.encode(valueStr, "UTF-8"); newUrlBuffer.append(encName); newUrlBuffer.append("="); newUrlBuffer.append(encValue); } else { logger.warn("postData(): Ignoring non-textual parameter " + name + " for GET."); } } resolvedURL = new URL(newUrlBuffer.toString()); } else { resolvedURL = action; } URL urlForLoading; if (resolvedURL.getProtocol().equalsIgnoreCase("file")) { // Remove query so it works. try { String ref = action.getRef(); String refText = (ref == null) || (ref.length() == 0) ? "" : "#" + ref; urlForLoading = new URL(resolvedURL.getProtocol(), action.getHost(), action.getPort(), action.getPath() + refText); } catch (MalformedURLException throwable) { this.warn("malformed", throwable); urlForLoading = action; } } else { urlForLoading = resolvedURL; } if (logger.isInfoEnabled()) { logger.info("process(): Loading URI=[" + urlForLoading + "]."); } long time0 = System.currentTimeMillis(); // Using potentially different URL for loading. Proxy proxy = SimpleHtmlRendererContext.this.getProxy(); boolean isPost = "POST".equals(actualMethod); SSLCertificate.setCertificate(); URLConnection connection = (proxy == null) || (proxy == Proxy.NO_PROXY) ? urlForLoading.openConnection() : urlForLoading.openConnection(proxy); this.currentConnection = connection; try { connection.setRequestProperty("User-Agent", getUserAgentContext().getUserAgent()); connection.setRequestProperty("Cookie", ""); if (connection instanceof HttpURLConnection) { HttpURLConnection hc = (HttpURLConnection) connection; hc.setRequestMethod(actualMethod); hc.setInstanceFollowRedirects(false); } if (isPost) { connection.setDoOutput(true); ByteArrayOutputStream bufOut = new ByteArrayOutputStream(); boolean firstParam = true; if (formInputs != null) { for (int i = 0; i < formInputs.length; i++) { FormInput parameter = formInputs[i]; String name = parameter.getName(); String encName = URLEncoder.encode(name, "UTF-8"); if (parameter.isText()) { if (firstParam) { firstParam = false; } else { bufOut.write((byte) '&'); } String valueStr = parameter.getTextValue(); String encValue = URLEncoder.encode(valueStr, "UTF-8"); bufOut.write(encName.getBytes("UTF-8")); bufOut.write((byte) '='); bufOut.write(encValue.getBytes("UTF-8")); } else { logger.warn("postData(): Ignoring non-textual parameter " + name + " for POST."); } } } // Do not add a line break to post content. Some servers // can be picky about that (namely, java.net). byte[] postContent = bufOut.toByteArray(); if (connection instanceof HttpURLConnection) { ((HttpURLConnection) connection).setFixedLengthStreamingMode(postContent.length); } connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); // connection.setRequestProperty("Content-Length", // String.valueOf(postContent.length)); OutputStream postOut = connection.getOutputStream(); postOut.write(postContent); postOut.flush(); } if (connection instanceof HttpURLConnection) { HttpURLConnection hc = (HttpURLConnection) connection; int responseCode = hc.getResponseCode(); if (logger.isInfoEnabled()) { logger.info("process(): HTTP response code: " + responseCode); } if ((responseCode == HttpURLConnection.HTTP_MOVED_PERM) || (responseCode == HttpURLConnection.HTTP_MOVED_TEMP) || (responseCode == HttpURLConnection.HTTP_SEE_OTHER)) { String location = hc.getHeaderField("Location"); if (location == null) { logger.warn("No Location header in redirect from " + action + "."); } else { URL href; href = Urls.createURL(action, location); SimpleHtmlRendererContext.this.navigate(href, target); } return; } } InputStream in = connection.getInputStream(); try { SimpleHtmlRendererContext.this.sourceCode = null; long time1 = System.currentTimeMillis(); RecordedInputStream rin = new RecordedInputStream(in, 1000000); InputStream bin = new BufferedInputStream(rin, 8192); String actualURI = urlForLoading.toExternalForm(); // Only create document, don't parse. HTMLDocumentImpl document = this .createDocument(new InputSourceImpl(bin, actualURI, getDocumentCharset(connection))); // Set document in HtmlPanel. Safe to call outside GUI thread. HtmlPanel panel = htmlPanel; panel.setDocument(document, SimpleHtmlRendererContext.this); // Now start loading. document.load(); long time2 = System.currentTimeMillis(); if (logger.isInfoEnabled()) { logger.info("Parsed URI=[" + urlForLoading + "]: Parse elapsed: " + (time2 - time1) + " ms. Connection elapsed: " + (time1 - time0) + " ms."); } String ref = urlForLoading.getRef(); if ((ref != null) && (ref.length() != 0)) { panel.scrollToElement(ref); } try { SimpleHtmlRendererContext.this.sourceCode = rin.getString("UTF-8"); } catch (BufferExceededException bee) { SimpleHtmlRendererContext.this.sourceCode = "[TOO BIG]"; } } finally { in.close(); } } finally { this.currentConnection = null; } } /** * Creates a blank document instance. This method is invoked whenever * navigation or form submission occur. It is provided so it can be * overridden to create specialized document implmentations. * * @param inputSource * The document input source. * @return the HTML document impl * @throws IOException * Signals that an I/O exception has occurred. * @throws SAXException * the SAX exception */ protected HTMLDocumentImpl createDocument(org.xml.sax.InputSource inputSource) throws IOException, org.xml.sax.SAXException { DocumentBuilderImpl builder = new DocumentBuilderImpl(this.getUserAgentContext(), SimpleHtmlRendererContext.this); return (HTMLDocumentImpl) builder.createDocument(inputSource); } /** * This method is invoked by * {@link #submitForm(String, URL, String, String, FormInput[])} to * determine the charset of a document. The charset is determined by looking * at the <code>Content-Type</code> header. * * @param connection * A URL connection. * @return the document charset */ protected String getDocumentCharset(URLConnection connection) { String encoding = Urls.getCharset(connection); return encoding == null ? "UTF-8" : encoding; } // Methods useful to Window below: /** * Opens a simple message dialog. * * @param message * the message */ @Override public void alert(String message) { JOptionPane.showMessageDialog(this.htmlPanel, message); } /** * It should give up focus on the current browser window. This * implementation does nothing and should be overridden. */ @Override public void blur() { this.warn("back(): Not overridden"); } /** * It should close the current browser window. This implementation does * nothing and should be overridden. */ @Override public void close() { this.warn("close(): Not overridden"); } /** * Opens a simple confirmation window. * * @param message * the message * @return true, if successful */ @Override public boolean confirm(String message) { int retValue = JOptionPane.showConfirmDialog(htmlPanel, message, "Confirm", JOptionPane.YES_NO_OPTION); return retValue == JOptionPane.YES_OPTION; } /** * It should request focus for the current browser window. This * implementation does nothing and should be overridden. */ @Override public void focus() { this.warn("focus(): Not overridden"); } /** * It should open a new browser window. This implementation does nothing and * should be overridden. * * @param url * The requested URL. * @param windowName * A window identifier. * @param windowFeatures * Window features specified in a format equivalent to that of * window.open() in Javascript. * @param replace * Whether an existing window with the same name should be * replaced. * @return the html renderer context */ @Override public HtmlRendererContext open(URL url, String windowName, String windowFeatures, boolean replace) { this.warn("open(): Not overridden"); return null; } /** * Shows a simple prompt dialog. * * @param message * the message * @param inputDefault * the input default * @return the string */ @Override public String prompt(String message, String inputDefault) { return JOptionPane.showInputDialog(htmlPanel, message); } /** * Changes the origin of the HTML block's scrollable area according to the * position given. * <p> * This method may be called outside of the GUI thread. The operation is * scheduled immediately in that thread as needed. * * @param x * The new x coordinate for the origin. * @param y * The new y coordinate for the origin. */ @Override public void scroll(int x, int y) { this.htmlPanel.scroll(x, y); } /* * (non-Javadoc) * * @see org.lobobrowser.html.HtmlRendererContext#scrollBy(int, int) */ @Override public void scrollBy(int x, int y) { this.htmlPanel.scrollBy(x, y); } /** * Should return true if and only if the current browser window is closed. * This implementation returns false and should be overridden. * * @return true, if is closed */ @Override public boolean isClosed() { this.warn("isClosed(): Not overridden"); return false; } /** * Should return true if and only if the current browser window is closed. * This implementation returns false and should be overridden. * * @return the default status */ @Override public String getDefaultStatus() { this.warn("getDefaultStatus(): Not overridden"); return ""; } /** * It should return the name of the browser window, if this renderer context * is for the top frame in the window. This implementation returns a blank * string, so it should be overridden. * * @return the name */ @Override public String getName() { this.warn("getName(): Not overridden"); return ""; } /* * (non-Javadoc) * * @see org.lobobrowser.html.HtmlRendererContext#getParent() */ @Override public HtmlRendererContext getParent() { return this.parentRcontext; } /** The opener. */ private volatile HtmlRendererContext opener; /* * (non-Javadoc) * * @see org.lobobrowser.html.HtmlRendererContext#getOpener() */ @Override public HtmlRendererContext getOpener() { return this.opener; } /* * (non-Javadoc) * * @see * org.lobobrowser.html.HtmlRendererContext#setOpener(org.lobobrowser.html. * HtmlRendererContext) */ @Override public void setOpener(HtmlRendererContext opener) { this.opener = opener; } /* * (non-Javadoc) * * @see org.lobobrowser.html.HtmlRendererContext#getStatus() */ @Override public String getStatus() { this.warn("getStatus(): Not overridden"); return ""; } /* * (non-Javadoc) * * @see org.lobobrowser.html.HtmlRendererContext#setStatus(java.lang.String) */ @Override public void setStatus(String message) { this.warn("setStatus(): Not overridden"); } /* * (non-Javadoc) * * @see org.lobobrowser.html.HtmlRendererContext#getTop() */ @Override public HtmlRendererContext getTop() { HtmlRendererContext ancestor = this.parentRcontext; if (ancestor == null) { return this; } return ancestor.getTop(); } /* * (non-Javadoc) * * @see org.lobobrowser.html.HtmlRendererContext#createBrowserFrame() */ @Override public BrowserFrame createBrowserFrame() { return new SimpleBrowserFrame(this); } /** * Warn. * * @param message * the message * @param throwable * the throwable */ public void warn(String message, Throwable throwable) { if (logger.isWarnEnabled()) { logger.error(message, throwable); } } /** * Error. * * @param message * the message * @param throwable * the throwable */ public void error(String message, Throwable throwable) { if (logger.isErrorEnabled()) { logger.log(Level.ERROR, message, throwable); } } /** * Warn. * * @param message * the message */ public void warn(String message) { if (logger.isWarnEnabled()) { logger.error(message); } } /** * Error. * * @param message * the message */ public void error(String message) { if (logger.isErrorEnabled()) { logger.log(Level.ERROR, message); } } /** * Returns <code>null</code>. This method should be overridden to provide * OBJECT, EMBED or APPLET functionality. * * @param element * the element * @return the html object */ @Override public HtmlObject getHtmlObject(HTMLElement element) { return null; } /* * (non-Javadoc) * * @see org.lobobrowser.html.HtmlRendererContext#setDefaultStatus(java.lang. * String) */ @Override public void setDefaultStatus(String message) { this.warn("setDefaultStatus(): Not overridden."); } /** The bcontext. */ private UserAgentContext bcontext = null; /** * If a {@link org.lobobrowser.http.UserAgentContext} instance was provided * in the constructor, then that instance is returned. Otherwise, an * instance of {@link SimpleUserAgentContext} is created and returned. * <p> * The context returned by this method is used by local request facilities * and other parts of the renderer. * * @return the user agent context */ @Override public UserAgentContext getUserAgentContext() { synchronized (this) { if (this.bcontext == null) { this.warn( "getUserAgentContext(): UserAgentContext not provided in constructor. Creating a simple one."); this.bcontext = new SimpleUserAgentContext(); } return this.bcontext; } } /** * Should be overridden to return true if the link has been visited. * * @param link * the link * @return true, if is visited link */ @Override public boolean isVisitedLink(HTMLAnchorElement link) { return false; } /** * This method must be overridden to implement a context menu. * * @param element * the element * @param event * the event * @return true, if successful */ @Override public boolean onContextMenu(HTMLElement element, MouseEvent event) { return true; } /** * This method can be overridden to receive notifications when the mouse * leaves an element. * * @param element * the element * @param event * the event */ @Override public void onMouseOut(HTMLElement element, MouseEvent event) { } /** * This method can be overridden to receive notifications when the mouse * first enters an element. * * @param element * the element * @param event * the event */ @Override public void onMouseOver(HTMLElement element, MouseEvent event) { } /* * (non-Javadoc) * * @see org.lobobrowser.html.HtmlRendererContext#isImageLoadingEnabled() */ @Override public boolean isImageLoadingEnabled() { return true; } /* * (non-Javadoc) * * @see * org.lobobrowser.html.HtmlRendererContext#onDoubleClick(org.lobobrowser. * html .w3c.HTMLElement, java.awt.event.MouseEvent) */ @Override public boolean onDoubleClick(HTMLElement element, MouseEvent event) { return true; } /* * (non-Javadoc) * * @see * org.lobobrowser.html.HtmlRendererContext#onMouseClick(org.lobobrowser. * html .w3c.HTMLElement, java.awt.event.MouseEvent) */ @Override public boolean onMouseClick(HTMLElement element, MouseEvent event) { return true; } /** * Gets the window. * * @param c * the c * @return the window */ private static Window getWindow(Component c) { Component current = c; while ((current != null) && !(current instanceof Window)) { current = current.getParent(); } return (Window) current; } /* * (non-Javadoc) * * @see org.lobobrowser.html.HtmlRendererContext#resizeBy(int, int) */ @Override public void resizeBy(int byWidth, int byHeight) { Window window = getWindow(this.htmlPanel); if (window != null) { window.setSize(window.getWidth() + byWidth, window.getHeight() + byHeight); } } /* * (non-Javadoc) * * @see org.lobobrowser.html.HtmlRendererContext#resizeTo(int, int) */ @Override public void resizeTo(int width, int height) { Window window = getWindow(this.htmlPanel); if (window != null) { window.setSize(width, height); } } /** * It should navigate back one page. This implementation does nothing and * should be overridden. */ @Override public void back() { if (logger.isWarnEnabled()) { logger.error("back() does nothing, unless overridden."); } } /* * (non-Javadoc) * * @see org.lobobrowser.html.HtmlRendererContext#forward() */ @Override public void forward() { if (logger.isWarnEnabled()) { logger.error("forward() does nothing, unless overridden."); } } /* * (non-Javadoc) * * @see org.lobobrowser.html.HtmlRendererContext#getCurrentURL() */ @Override public String getCurrentURL() { Object node = this.htmlPanel.getRootNode(); if (node instanceof HTMLDocumentImpl) { HTMLDocumentImpl doc = (HTMLDocumentImpl) node; return doc.getDocumentURI(); } return null; } /* * (non-Javadoc) * * @see org.lobobrowser.html.HtmlRendererContext#getHistoryLength() */ @Override public int getHistoryLength() { return 0; } /* * (non-Javadoc) * * @see org.lobobrowser.html.HtmlRendererContext#getNextURL() */ @Override public String getNextURL() { return null; } /* * (non-Javadoc) * * @see org.lobobrowser.html.HtmlRendererContext#getPreviousURL() */ @Override public String getPreviousURL() { return null; } /* * (non-Javadoc) * * @see * org.lobobrowser.html.HtmlRendererContext#goToHistoryURL(java.lang.String) */ @Override public void goToHistoryURL(String url) { if (logger.isWarnEnabled()) { logger.error("goToHistoryURL() does nothing, unless overridden."); } } /* * (non-Javadoc) * * @see org.lobobrowser.html.HtmlRendererContext#moveInHistory(int) */ @Override public void moveInHistory(int offset) { if (logger.isWarnEnabled()) { logger.error("moveInHistory() does nothing, unless overridden."); } } @Override public void setCursor(final Optional<Cursor> cursorOpt) { Cursor cursor = cursorOpt.orElse(Cursor.getDefaultCursor()); htmlPanel.setCursor(cursor); } }