/*
* GNU LESSER GENERAL PUBLIC LICENSE Copyright (C) 2006 The Lobo Project
*
* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Contact info: xamjadmin@users.sourceforge.net
*/
/*
* Created on Oct 22, 2005
*/
package org.cobra_grendel.html.test;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashSet;
import java.util.Set;
import org.cobra_grendel.html.BrowserFrame;
import org.cobra_grendel.html.FormInput;
import org.cobra_grendel.html.HtmlObject;
import org.cobra_grendel.html.HtmlParserContext;
import org.cobra_grendel.html.HtmlRendererContext;
import org.cobra_grendel.html.UserAgentContext;
import org.cobra_grendel.html.domimpl.FrameNode;
import org.cobra_grendel.html.domimpl.HTMLDocumentImpl;
import org.cobra_grendel.html.parser.DocumentBuilderImpl;
import org.cobra_grendel.html.parser.InputSourceImpl;
import org.cobra_grendel.util.io.RecordedInputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.html2.HTMLCollection;
import org.w3c.dom.html2.HTMLElement;
import org.w3c.dom.html2.HTMLLinkElement;
/**
* The <code>SimpleHtmlRendererContext</code> class implements the {@link org.cobra_grendel.html.HtmlRendererContext} interface. Note that this class provides simple implementations of most methods,
* which should be overridden to provide real-world functionality.
*/
public class SimpleHtmlRendererContext implements HtmlRendererContext
{
private static final Logger LOGGER = LoggerFactory.getLogger(SimpleHtmlRendererContext.class);
private static final Set mediaNames = new HashSet();
private UserAgentContext bcontext = null;
private volatile HtmlRendererContext opener;
private final HtmlRendererContext parentRcontext;
private volatile String sourceCode;
static
{
// Media names claimed by this context.
Set mn = mediaNames;
mn.add("screen");
mn.add("tv");
mn.add("tty");
mn.add("all");
}
/**
* Constructs a SimpleHtmlRendererContext.
*
* @param contextComponent
* The component that will render HTML.
*/
public SimpleHtmlRendererContext()
{
super();
parentRcontext = null;
}
/**
* Constructs a simple <code>HtmlRendererContext</code> without a parent. This constructor should not be used to create the context of a frame with a parent.
*
* @param contextComponent
* @param pcontext
* @deprecated HtmlParserContext is no longer used in this class.
*/
@Deprecated
public SimpleHtmlRendererContext(final HtmlParserContext pcontext)
{
this(pcontext, null);
}
/**
* Constructs a SimpleHtmlRendererContext.
*
* @param contextComponent
* The component that will render HTML.
* @param pcontext
* A parser context.
* @param parentRcontext
* The parent's renderer context. This is <code>null</code> for the root renderer context. Normally ony frame renderer contexts would have parents.
* @deprecated HtmlParserContext is no longer used in this class.
*/
@Deprecated
public SimpleHtmlRendererContext(final HtmlParserContext pcontext, final HtmlRendererContext parentRcontext)
{
super();
this.parentRcontext = parentRcontext;
}
/**
* Constructs a SimpleHtmlRendererContext.
*
* @param contextComponent
* The component that will render HTML.
* @param parentRcontext
* The parent's renderer context. This is <code>null</code> for the root renderer context. Normally ony frame renderer contexts would have parents.
*/
public SimpleHtmlRendererContext(final HtmlRendererContext parentRcontext)
{
super();
this.parentRcontext = parentRcontext;
}
@Override
public void alert(final String message)
{
}
@Override
public void back()
{
}
@Override
public void blur()
{
this.warn("back(): Not overridden");
}
@Override
public void close()
{
this.warn("close(): Not overridden");
}
// Methods useful to Window below:
@Override
public boolean confirm(final String message)
{
return true;
}
/*
* (non-Javadoc)
*
* @see org.xamjwg.html.HtmlContext#createBrowserFrame()
*/
@Override
public BrowserFrame createBrowserFrame()
{
return null;
}
/**
* This method is called by the local navigate() implementation and creates a {@link SimpleHtmlParserContext}. Override this method if you need to use the local navigate() implementation with an
* overridden parser context.
*/
protected HtmlParserContext createParserContext(final java.net.URL url)
{
return new SimpleHtmlParserContext();
}
public void error(final String message)
{
LOGGER.error(message);
}
public void error(final String message, final Throwable throwable)
{
LOGGER.error(message, throwable);
}
@Override
public void focus()
{
this.warn("focus(): Not overridden");
}
@Override
public String getDefaultStatus()
{
this.warn("getDefaultStatus(): Not overridden");
return "";
}
/**
* This needs to be fixed for the scanner
*/
@Override
public HTMLCollection getFrames()
{
/*
* Object rootNode = this.htmlPanel.getRootNode();
*
* if(rootNode instanceof HTMLDocumentImpl) { return ((HTMLDocumentImpl) rootNode).getFrames(); } else { return null; }
*/
return null;
}
@Override
public HtmlObject getHtmlObject(final HTMLElement element)
{
HtmlObject result;
if ("OBJECT".equalsIgnoreCase(element.getTagName()))
{
result = null;
}
else
{
result = new SimpleHtmlObject(element);
}
this.warn("getHtmlObject(): Not overridden; returning " + result + " for " + element + ".");
return result;
}
public int getLength()
{
this.warn("getLength(): Not overridden");
return 0;
}
@Override
public String getName()
{
this.warn("getName(): Not overridden");
return "";
}
@Override
public HtmlRendererContext getOpener()
{
return opener;
}
@Override
public HtmlRendererContext getParent()
{
return parentRcontext;
}
public String getSourceCode()
{
return sourceCode;
}
@Override
public String getStatus()
{
this.warn("getStatus(): Not overridden");
return "";
}
@Override
public HtmlRendererContext getTop()
{
HtmlRendererContext ancestor = parentRcontext;
if (ancestor == null)
{
return this;
}
return ancestor.getTop();
}
@Override
public UserAgentContext getUserAgentContext()
{
this.warn("getUserAgentContext(): Not overridden; returning simple one.");
synchronized (this)
{
if (bcontext == null)
{
bcontext = new SimpleUserAgentContext();
}
return bcontext;
}
}
@Override
public boolean isClosed()
{
this.warn("isClosed(): Not overridden");
return false;
}
@Override
public boolean isMedia(final String mediaName)
{
return mediaNames.contains(mediaName.toLowerCase());
}
/**
* Should be overridden to return true if the link has been visited.
*/
@Override
public boolean isVisitedLink(final HTMLLinkElement link)
{
// TODO
return false;
}
/**
* Implements simple navigation with incremental rendering, and target processing, including frame lookup. Should be overridden to allow for more robust browser navigation.
* <p>
* <b>Notes:</b>
* <ul>
* <li>Encoding ISO-8859-1 assumed always.
* <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.
* </ul>
*/
@Override
public void navigate(final URL href, String target, final int referingTransactionId)
{
// This method implements simple incremental rendering.
if (target != null)
{
HtmlRendererContext topCtx = getTop();
HTMLCollection frames = topCtx.getFrames();
if (frames != null)
{
org.w3c.dom.Node frame = frames.namedItem(target);
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(href);
return;
}
}
}
target = target.trim().toLowerCase();
if ("_top".equals(target))
{
getTop().navigate(href, null, referingTransactionId);
return;
}
else if ("_parent".equals(target))
{
HtmlRendererContext parent = getParent();
if (parent != null)
{
parent.navigate(href, null, referingTransactionId);
return;
}
}
else if ("_blank".equals(target))
{
this.open(href.toExternalForm(), "cobra.blank", "", false);
return;
}
else
{
// fall through
}
}
URL urlForLoading;
if (href.getProtocol().equals("file"))
{
// Remove query so it works.
try
{
urlForLoading = new URL(href.getProtocol(), href.getHost(), href.getPort(), href.getPath());
}
catch (java.net.MalformedURLException throwable)
{
this.warn("malformed", throwable);
urlForLoading = href;
}
}
else
{
urlForLoading = href;
}
final URL finalURLForLoading = urlForLoading;
// Make request asynchronously.
new Thread()
{
@Override
public void run()
{
try
{
URL uri = href;
LOGGER.info("process(): Loading URI=[" + uri + "].");
long time0 = System.currentTimeMillis();
// Using potentially different URL for loading.
URLConnection connection = finalURLForLoading.openConnection();
connection.setRequestProperty("User-Agent", getUserAgentContext().getUserAgent());
connection.setRequestProperty("Cookie", "");
if (connection instanceof HttpURLConnection)
{
HttpURLConnection hc = (HttpURLConnection) connection;
hc.setInstanceFollowRedirects(true);
int responseCode = hc.getResponseCode();
LOGGER.info("process(): HTTP response code: " + responseCode);
}
InputStream in = connection.getInputStream();
try
{
sourceCode = null;
long time1 = System.currentTimeMillis();
RecordedInputStream rin = new RecordedInputStream(in);
InputStream bin = new BufferedInputStream(rin, 8192);
HtmlParserContext pcontext = createParserContext(uri);
DocumentBuilderImpl builder = new DocumentBuilderImpl(pcontext, SimpleHtmlRendererContext.this);
String actualURI = uri.toExternalForm();
// Only create document, don't parse.
HTMLDocumentImpl document = (HTMLDocumentImpl) builder.createDocument(new InputSourceImpl(bin, actualURI, "ISO-8859-1"), referingTransactionId);
// Set document in HtmlPanel. Safe to call outside GUI
// thread.
// SimpleHtmlRendererContext.this.htmlPanel.setDocument(document,
// SimpleHtmlRendererContext.this);
// Now start loading.
document.load();
long time2 = System.currentTimeMillis();
LOGGER.info("Parsed URI=[" + uri + "]: Parse elapsed: " + (time2 - time1) + " ms. Connection elapsed: " + (time1 - time0) + " ms.");
sourceCode = rin.getString("ISO-8859-1");
}
finally
{
in.close();
}
}
catch (Exception err)
{
SimpleHtmlRendererContext.this.error("navigate(): Error loading or parsing request.", err);
}
}
}.start();
}
@Override
public HtmlRendererContext open(final java.net.URL url, final String windowName, final String windowFeatures, final boolean replace)
{
return null;
}
@Override
public HtmlRendererContext open(final String url, final String windowName, final String windowFeatures, final boolean replace)
{
return null;
}
@Override
public String prompt(final String message, final String inputDefault)
{
return "";
}
/**
* Implements reload as navigation to current URL. Override to implement a more robust reloading mechanism.
*/
@Override
public void reload()
{
}
@Override
public void scroll(final int x, final int y)
{
this.warn("scroll(): Not overridden");
}
@Override
public void setDefaultStatus(final String message)
{
this.warn("setDefaultStatus(): Not overridden.");
}
@Override
public void setOpener(final HtmlRendererContext opener)
{
this.opener = opener;
}
@Override
public void setStatus(final String message)
{
this.warn("setStatus(): Not overridden");
}
/*
* (non-Javadoc)
*
* @see org.xamjwg.html.HtmlContext#submitForm(java.lang.String, java.lang.String, java.lang.String, java.lang.String, org.xamjwg.html.FormInput[])
*/
@Override
public void submitForm(final String method, final java.net.URL action, final String target, final String enctype, final FormInput[] formInputs)
{
StringBuffer sb = new StringBuffer();
String lineBreak = System.getProperty("line.separator");
if (formInputs != null)
{
for (FormInput formInput : formInputs)
{
sb.append("INPUT: " + formInput.toString());
sb.append(lineBreak);
}
}
this.warn("submitForm(): Not overridden; method=" + method + "; action=" + action + "; target=" + target + "; enctype=" + enctype + lineBreak + sb);
}
public void warn(final String message)
{
LOGGER.warn(message);
}
public void warn(final String message, final Throwable throwable)
{
LOGGER.warn(message, throwable);
}
}