/* * 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: lobochief@users.sourceforge.net */ /* * Created on Nov 12, 2005 */ package com.nvarghese.beowulf.common.cobra.html.js; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.util.HashMap; import java.util.Map; import java.util.WeakHashMap; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.Timer; import org.mozilla.javascript.Context; import org.mozilla.javascript.EcmaError; import org.mozilla.javascript.Function; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; import org.w3c.dom.Document; import org.w3c.dom.html2.HTMLCollection; import org.w3c.dom.views.AbstractView; import org.w3c.dom.views.DocumentView; import com.nvarghese.beowulf.common.cobra.html.HtmlRendererContext; import com.nvarghese.beowulf.common.cobra.html.UserAgentContext; import com.nvarghese.beowulf.common.cobra.html.domimpl.HTMLDocumentImpl; import com.nvarghese.beowulf.common.cobra.html.domimpl.HTMLIFrameElementImpl; import com.nvarghese.beowulf.common.cobra.html.domimpl.HTMLImageElementImpl; import com.nvarghese.beowulf.common.cobra.html.domimpl.HTMLOptionElementImpl; import com.nvarghese.beowulf.common.cobra.html.domimpl.HTMLScriptElementImpl; import com.nvarghese.beowulf.common.cobra.html.domimpl.HTMLSelectElementImpl; import com.nvarghese.beowulf.common.cobra.js.AbstractScriptableDelegate; import com.nvarghese.beowulf.common.cobra.js.JavaClassWrapper; import com.nvarghese.beowulf.common.cobra.js.JavaClassWrapperFactory; import com.nvarghese.beowulf.common.cobra.js.JavaInstantiator; import com.nvarghese.beowulf.common.cobra.js.JavaObjectWrapper; import com.nvarghese.beowulf.common.cobra.js.JavaScript; import com.nvarghese.beowulf.common.cobra.util.ID; import com.nvarghese.beowulf.common.http.renderer.CobraUserAgent; public class Window extends AbstractScriptableDelegate implements AbstractView { /** * */ private static final long serialVersionUID = 448621862027374546L; private static final Logger logger = Logger.getLogger(Window.class.getName()); private static final Map CONTEXT_WINDOWS = new WeakHashMap(); // private static final JavaClassWrapper IMAGE_WRAPPER = // JavaClassWrapperFactory.getInstance().getClassWrapper(Image.class); private static final JavaClassWrapper XMLHTTPREQUEST_WRAPPER = JavaClassWrapperFactory.getInstance().getClassWrapper(XMLHttpRequest.class); private static int timerIdCounter = 0; private final transient HtmlRendererContext rcontext; private final UserAgentContext uaContext; private Navigator navigator; private Screen screen; private Location location; private Map taskMap; private volatile HTMLDocumentImpl document; static org.apache.log4j.Logger logger4J = org.apache.log4j.Logger.getLogger(Window.class); public Window(HtmlRendererContext rcontext, UserAgentContext uaContext) { // TODO: Probably need to create a new Window instance // for every document. Sharing of Window state between // different documents is not correct. this.rcontext = rcontext; this.uaContext = uaContext; } private static int generateTimerID() { synchronized (logger) { return timerIdCounter++; } } public HtmlRendererContext getHtmlRendererContext() { return this.rcontext; } public UserAgentContext getUserAgentContext() { return this.uaContext; } private void clearState() { Scriptable s = this.getWindowScope(); if (s != null) { Object[] ids = s.getIds(); for (int i = 0; i < ids.length; i++) { Object id = ids[i]; if (id instanceof String) { s.delete((String) id); } else if (id instanceof Integer) { s.delete(((Integer) id).intValue()); } } } } public void setDocument(HTMLDocumentImpl document) { Document prevDocument = this.document; if (prevDocument != document) { // Should clearing of the state be done // when window "unloads"? if (prevDocument != null) { // Only clearing when the previous document was not null // because state might have been set on the window before // the very first document is added. this.clearState(); } this.initWindowScope(document); this.forgetAllTasks(); Function onunload = this.onunload; if (onunload != null) { HTMLDocumentImpl oldDoc = (HTMLDocumentImpl) this.document; Executor.executeFunction(this.getWindowScope(), onunload, oldDoc.getDocumentURL(), this.uaContext); this.onunload = null; } this.document = document; } } public DocumentView getDocument() { return this.document; } public Document getDocumentNode() { return this.document; } private void putAndStartTask(Integer timeoutID, Timer timer, Object retained) { TaskWrapper oldTaskWrapper = null; synchronized (this) { Map taskMap = this.taskMap; if (taskMap == null) { taskMap = new HashMap(4); this.taskMap = taskMap; } else { oldTaskWrapper = (TaskWrapper) taskMap.get(timeoutID); } taskMap.put(timeoutID, new TaskWrapper(timer, retained)); } // Do this outside synchronized block, just in case. if (oldTaskWrapper != null) { oldTaskWrapper.timer.stop(); } timer.start(); } private void forgetTask(Integer timeoutID, boolean cancel) { TaskWrapper oldTimer = null; synchronized (this) { Map taskMap = this.taskMap; if (taskMap != null) { oldTimer = (TaskWrapper) taskMap.remove(timeoutID); } } if (oldTimer != null && cancel) { oldTimer.timer.stop(); } } private void forgetAllTasks() { TaskWrapper[] oldTaskWrappers = null; synchronized (this) { Map taskMap = this.taskMap; if (taskMap != null) { oldTaskWrappers = (TaskWrapper[]) taskMap.values().toArray(new TaskWrapper[0]); this.taskMap = null; } } if (oldTaskWrappers != null) { for (int i = 0; i < oldTaskWrappers.length; i++) { TaskWrapper taskWrapper = oldTaskWrappers[i]; taskWrapper.timer.stop(); } } } // private Timer getTask(Long timeoutID) { // synchronized(this) { // Map taskMap = this.taskMap; // if(taskMap != null) { // return (Timer) taskMap.get(timeoutID); // } // } // return null; // } /** * Delay is set to zero to prevent problems with scanner (patched) * * @param aFunction * Javascript function to invoke on each loop. * @param aTimeInMs * Time in millisecund between each loop. * @return Return the timer ID to use as reference * @see <a * href="http://developer.mozilla.org/en/docs/DOM:window.setInterval">Window.setInterval * interface definition</a> * @todo Make proper and refactore with * {@link Window#setTimeout(Function, double)}. */ public int setInterval(final Function aFunction, double aTimeInMs) { /* * aTimeInMs = 0; //patched if (aTimeInMs > Integer.MAX_VALUE || * aTimeInMs < 0) { throw new IllegalArgumentException("Timeout value " * + aTimeInMs + " is not supported."); } final int timeID = * generateTimerID(); final Integer timeIDInt = new Integer(timeID); * ActionListener task = new FunctionTimerTask(this, timeIDInt, * aFunction, false); int t = (int) aTimeInMs; if(t < 1) { t = 1; } * Timer timer = new Timer(t, task); timer.setRepeats(true); // The only * difference with setTimeout this.putAndStartTask(timeIDInt, timer, * aFunction); return timeID; */ return setTimeout(aFunction, aTimeInMs); } /** * Delay is set to zero to prevent problems with scanner (patched) * * @param aExpression * Javascript expression to invoke on each loop. * @param aTimeInMs * Time in millisecund between each loop. * @return Return the timer ID to use as reference * @see <a * href="http://developer.mozilla.org/en/docs/DOM:window.setInterval">Window.setInterval * interface definition</a> * @todo Make proper and refactore with * {@link Window#setTimeout(String, double)}. */ public int setInterval(final String aExpression, double aTimeInMs) { /* * aTimeInMs = 0; //patched if (aTimeInMs > Integer.MAX_VALUE || * aTimeInMs < 0) { throw new IllegalArgumentException("Timeout value " * + aTimeInMs + " is not supported."); } final int timeID = * generateTimerID(); final Integer timeIDInt = new Integer(timeID); * ActionListener task = new ExpressionTimerTask(this, timeIDInt, * aExpression, false); int t = (int) aTimeInMs; if(t < 1) { t = 1; } * Timer timer = new Timer(t, task); timer.setRepeats(true); // The only * difference with setTimeout this.putAndStartTask(timeIDInt, timer, * null); return timeID; */ return setTimeout(aExpression, aTimeInMs); } /** * @param aTimerID * Timer ID to stop. * @see <a * href="http://developer.mozilla.org/en/docs/DOM:window.clearInterval">Window.clearInterval * interface Definition</a> */ public void clearInterval(int aTimerID) { Integer key = new Integer(aTimerID); this.forgetTask(key, true); } /** * Disabled to prevent problems in scanner */ public void alert(String message) { /* * if(this.rcontext != null) { this.rcontext.alert(message); } */ logger.info("My Window alert" + message); } /** * Disabled to prevent problems in scanner */ public void back() { /* * HtmlRendererContext rcontext = this.rcontext; if(rcontext != null) { * rcontext.back(); } */ } public void blur() { HtmlRendererContext rcontext = this.rcontext; if (rcontext != null) { rcontext.blur(); } } public void clearTimeout(int timeoutID) { Integer key = new Integer(timeoutID); this.forgetTask(key, true); } public void close() { HtmlRendererContext rcontext = this.rcontext; if (rcontext != null) { rcontext.close(); } } /** * Disabled to prevent problems in scanner * * @param message * @return Always returns true */ public boolean confirm(String message) { /* * HtmlRendererContext rcontext = this.rcontext; if(rcontext != null) { * return rcontext.confirm(message); } else { return false; } */ return true; } public Object eval(String javascript) { HTMLDocumentImpl document = (HTMLDocumentImpl) this.document; if (document == null) { throw new IllegalStateException("Cannot evaluate if document is not set."); } Context ctx = Executor.createContext(document.getDocumentURL(), this.uaContext); try { Scriptable scope = this.getWindowScope(); if (scope == null) { throw new IllegalStateException("Scriptable (scope) instance was expected to be keyed as UserData to document using " + Executor.SCOPE_KEY); } String scriptURI = "window.eval"; if (logger.isLoggable(Level.INFO)) { logger.info("eval(): javascript follows...\r\n" + javascript); } return ctx.evaluateString(scope, javascript, scriptURI, 1, null); } finally { Context.exit(); } } public void focus() { HtmlRendererContext rcontext = this.rcontext; if (rcontext != null) { rcontext.focus(); } } private void initWindowScope(final Document doc) { // Special Javascript class: XMLHttpRequest final Scriptable ws = this.getWindowScope(); JavaInstantiator xi = new JavaInstantiator() { public Object newInstance() { Document d = doc; if (d == null) { throw new IllegalStateException("Cannot perform operation when document is unset."); } HTMLDocumentImpl hd; try { hd = (HTMLDocumentImpl) d; } catch (ClassCastException err) { throw new IllegalStateException("Cannot perform operation with documents of type " + d.getClass().getName() + "."); } return new XMLHttpRequest(uaContext, hd.getDocumentURL(), ws); } }; Function xmlHttpRequestC = JavaObjectWrapper.getConstructor("XMLHttpRequest", XMLHTTPREQUEST_WRAPPER, ws, xi); ScriptableObject.defineProperty(ws, "XMLHttpRequest", xmlHttpRequestC, ScriptableObject.READONLY); // HTML element classes this.defineElementClass(ws, doc, "Image", "img", HTMLImageElementImpl.class); this.defineElementClass(ws, doc, "Script", "script", HTMLScriptElementImpl.class); this.defineElementClass(ws, doc, "IFrame", "iframe", HTMLIFrameElementImpl.class); this.defineElementClass(ws, doc, "Option", "option", HTMLOptionElementImpl.class); this.defineElementClass(ws, doc, "Select", "select", HTMLSelectElementImpl.class); } private ScriptableObject windowScope; public Scriptable getWindowScope() { synchronized (this) { ScriptableObject windowScope = this.windowScope; if (windowScope != null) { return windowScope; } // Context.enter() OK in this particular case. Context ctx = Context.enter(); try { // Window scope needs to be top-most scope. windowScope = (ScriptableObject) JavaScript.getInstance().getJavascriptObject(this, null); ctx.initStandardObjects(windowScope); this.windowScope = windowScope; return windowScope; } finally { Context.exit(); } } } private final void defineElementClass(Scriptable scope, final Document document, final String jsClassName, final String elementName, Class javaClass) { JavaInstantiator ji = new JavaInstantiator() { public Object newInstance() { Document d = document; if (d == null) { throw new IllegalStateException("Document not set in current context."); } return d.createElement(elementName); } }; JavaClassWrapper classWrapper = JavaClassWrapperFactory.getInstance().getClassWrapper(javaClass); Function constructorFunction = JavaObjectWrapper.getConstructor(jsClassName, classWrapper, scope, ji); ScriptableObject.defineProperty(scope, jsClassName, constructorFunction, ScriptableObject.READONLY); } public static Window getWindow(HtmlRendererContext rcontext) { if (rcontext == null) { return null; } synchronized (CONTEXT_WINDOWS) { Reference wref = (Reference) CONTEXT_WINDOWS.get(rcontext); if (wref != null) { Window window = (Window) wref.get(); if (window != null) { return window; } } Window window = new Window(rcontext, rcontext.getUserAgentContext()); CONTEXT_WINDOWS.put(rcontext, new WeakReference(window)); return window; } } /** * For now, disabled to prevent problems with scanner. May be reenabled if * possible. * * @param relativeUrl * @param windowName * @param windowFeatures * @param replace * @return Always returns null */ public Window open(String relativeUrl, String windowName, String windowFeatures, boolean replace) { // return null; /* * HtmlRendererContext rcontext = this.rcontext; CobraUserAgent * uaContext = (CobraUserAgent) this.uaContext; if(rcontext != null) { * URI uri = null; Object document = this.document; if * (URIStringUtils.isUsableUri(relativeUrl) || false) { try { * if(document instanceof HTMLDocumentImpl) { uri = * UriFactory.makeAbsoluteUri(relativeUrl, ((HTMLDocumentImpl) * document).getBaseURI()); } } catch (URISyntaxException e) { // Don't * really care } * * if(uri!=null) { * * try { HttpGetTransaction request = new * HttpGetTransaction(uaContext.getScan(), uri, uaContext.getRefId(), * TransactionSource.SPIDER); * uaContext.getScan().getRequesterQueue().addSpiderRequest(request, * false, "Javascript: Window.open "); } catch (Exception e) { // TODO: * handle exception } * * } } } */ /* * HtmlRendererContext rcontext = this.rcontext; if (rcontext != null) { * java.net.URL url; Object document = this.document; if (document * instanceof HTMLDocumentImpl) { url = ((HTMLDocumentImpl) * document).getFullURL(relativeUrl); } else { try { url = new * java.net.URL(relativeUrl); } catch (java.net.MalformedURLException * mfu) { throw new IllegalArgumentException("Malformed URI: " + * relativeUrl); } } // SimpleHtmlRendererContext newContext = // * (SimpleHtmlRendererContext) rcontext.open(url, windowName, // * windowFeatures, replace); // return getWindow(newContext); * * return null; } else { return null; } */ return null; } public Window open(String url) { return this.open(url, "window:" + String.valueOf(ID.generateLong())); } /** * For now, disabled to prevent problems with scanner. May be reenabled if * possible. * * @param url * @param windowName * @return Always returns null */ public Window open(String url, String windowName) { return this.open(url, windowName, "", false); // return null; } /** * For now, disabled to prevent problems with scanner. May be reenabled if * possible. * * @param url * @param windowName * @param windowFeatures * @return Always returns null */ public Window open(String url, String windowName, String windowFeatures) { return this.open(url, windowName, windowFeatures, false); // return null; } /** * Disabled to prevent problems with the scanner. * * @param message * @return Always returns an empty string */ public String prompt(String message) { return ""; // return this.prompt(message, ""); } /** * Disabled to prevent problems with the scanner. * * @param message * @param inputDefault * @return Always returns the default */ public String prompt(String message, int inputDefault) { // return this.prompt(message, String.valueOf(inputDefault)); return String.valueOf(inputDefault); } /** * Disabled to prevent problems with the scanner. * * @param message * @param inputDefault * @returnAlways returns the default */ public String prompt(String message, String inputDefault) { return inputDefault; /* * HtmlRendererContext rcontext = this.rcontext; if(rcontext != null) { * return rcontext.prompt(message, inputDefault); } else { return null; * } */ } public void scrollTo(int x, int y) { HtmlRendererContext rcontext = this.rcontext; if (rcontext != null) { rcontext.scroll(x, y); } } public void scrollBy(int x, int y) { HtmlRendererContext rcontext = this.rcontext; if (rcontext != null) { rcontext.scrollBy(x, y); } } public void resizeTo(int width, int height) { HtmlRendererContext rcontext = this.rcontext; if (rcontext != null) { rcontext.resizeTo(width, height); } } public void resizeBy(int byWidth, int byHeight) { HtmlRendererContext rcontext = this.rcontext; if (rcontext != null) { rcontext.resizeBy(byWidth, byHeight); } } /* * Delay is always set to zero to prevent problems with the scanner * * @param expr * * @param millis */ public int setTimeout(final String expr, double millis) { CobraUserAgent cobraAgent = (CobraUserAgent) this.getUserAgentContext(); if (!cobraAgent.isRenderingMode()) { return 0; } millis = 0; // patched here if (millis > Integer.MAX_VALUE || millis < 0) { throw new IllegalArgumentException("Timeout value " + millis + " is not supported."); } final int timeID = generateTimerID(); final Integer timeIDInt = new Integer(timeID); // ActionListener task = new ExpressionTimerTask(this, timeIDInt, expr, // true); /* * Parser fails to recognize a function object if it doesn't use (). For * eg if showLink is a function which is defined in SCRIPT tags, then * * settimeout(showLink(),3000) parses correctly to have showLink as a * function object. But settimeout(showLink,3000) parses to have * showLink as a string object and execution comes here with the whole * function code as function showLink() {...} */ if (expr.trim().toLowerCase().startsWith("function")) { Function jFn = null; String sourceName = expr.substring("function".length() + 1, expr.indexOf("(")).trim(); HTMLDocumentImpl doc = document; Context ctx = Executor.createContext(doc.getDocumentURL(), uaContext); try { Scriptable thisScope = (Scriptable) JavaScript.getInstance().getJavascriptObject(getWindowScope(), null); try { // TODO: Get right line number for script. //TODO: Optimize // this in case it's called multiple times? Is that done? jFn = ctx.compileFunction(thisScope, expr.trim(), sourceName, 1, null); } catch (EcmaError ecmaError) { logger4J.error( "Javascript error at " + ecmaError.getSourceName() + ":" + ecmaError.getLineNumber() + ": " + ecmaError.getMessage(), ecmaError); } catch (Throwable err) { logger4J.error("Unable to evaluate Javascript code: " + err.getMessage(), err); } } finally { Context.exit(); } if (jFn != null) { Executor.executeFunction(getWindowScope(), jFn, doc.getDocumentURL(), uaContext); } // return setTimeout(jFn,millis); } ActionListener task = new ActionListener() { public void actionPerformed(ActionEvent e) { // This executes in the GUI thread and that's good. Window.this.forgetTask(timeIDInt, false); Window.this.eval(expr); } }; int t = (int) millis; if (t < 1) { t = 1; } Timer timer = new Timer(t, task); timer.setRepeats(false); this.putAndStartTask(timeIDInt, timer, null); return timeID; } /* * Delay is always set to zero to prevent problems with the scanner * * @param function * * @param millis */ public int setTimeout(final Function function, double millis) { CobraUserAgent cobraAgent = (CobraUserAgent) this.getUserAgentContext(); if (!cobraAgent.isRenderingMode()) { return 0; } millis = 0; // patched here if (millis > Integer.MAX_VALUE || millis < 0) { throw new IllegalArgumentException("Timeout value " + millis + " is not supported."); } final int timeID = generateTimerID(); final Integer timeIDInt = new Integer(timeID); // ActionListener task = new FunctionTimerTask(this, timeIDInt, // function, true); ActionListener task = new ActionListener() { public void actionPerformed(ActionEvent e) { // This executes in the GUI thread and that's good. Window.this.forgetTask(timeIDInt, false); HTMLDocumentImpl doc = document; if (doc == null) { throw new IllegalStateException("Cannot perform operation when document is unset."); } Executor.executeFunction(getWindowScope(), function, doc.getDocumentURL(), uaContext); } }; int t = (int) millis; if (t < 1) { t = 1; } Timer timer = new Timer(t, task); timer.setRepeats(false); this.putAndStartTask(timeIDInt, timer, function); return timeID; } public boolean isClosed() { HtmlRendererContext rcontext = this.rcontext; if (rcontext != null) { return rcontext.isClosed(); } else { return false; } } public String getDefaultStatus() { HtmlRendererContext rcontext = this.rcontext; if (rcontext != null) { return rcontext.getDefaultStatus(); } else { return null; } } public HTMLCollection getFrames() { return this.document.getFrames(); } private int length; private boolean lengthSet = false; /** * Gets the number of frames. */ public int getLength() { if (this.lengthSet) { return this.length; } else { HTMLCollection frames = this.getFrames(); return frames == null ? 0 : frames.getLength(); } } public void setLength(int length) { this.lengthSet = true; this.length = length; } public String getName() { HtmlRendererContext rcontext = this.rcontext; if (rcontext != null) { return rcontext.getName(); } else { return null; } } public Window getParent() { HtmlRendererContext rcontext = this.rcontext; if (rcontext != null) { return Window.getWindow(rcontext.getParent()); } else { return null; } } public Window getOpener() { HtmlRendererContext rcontext = this.rcontext; if (rcontext != null) { return Window.getWindow(rcontext.getOpener()); } else { return null; } } public void setOpener(Window opener) { HtmlRendererContext rcontext = this.rcontext; if (rcontext != null) { if (opener == null) { rcontext.setOpener(null); } else { rcontext.setOpener(opener.rcontext); } } } public Window getSelf() { return this; } public String getStatus() { HtmlRendererContext rcontext = this.rcontext; if (rcontext != null) { return rcontext.getStatus(); } else { return null; } } public void setStatus(String message) { HtmlRendererContext rcontext = this.rcontext; if (rcontext != null) { rcontext.setStatus(message); } } public Window getTop() { HtmlRendererContext rcontext = this.rcontext; if (rcontext != null) { return Window.getWindow(rcontext.getTop()); } else { return null; } } public Window getWindow() { return this; } public Navigator getNavigator() { synchronized (this) { Navigator nav = this.navigator; if (nav == null) { nav = new Navigator(this.uaContext); this.navigator = nav; } return nav; } } public Screen getScreen() { synchronized (this) { Screen nav = this.screen; if (nav == null) { nav = new Screen(); this.screen = nav; } return nav; } } public Location getLocation() { synchronized (this) { Location location = this.location; if (location == null) { location = new Location(this); this.location = location; } return location; } } /** * * * @param location */ public void setLocation(String location) { this.getLocation().setHref(location); // logger.info("setLocation: Disabled method; location="+ location); } private History history; public History getHistory() { synchronized (this) { History history = this.history; if (history == null) { history = new History(this); this.history = history; } return history; } } /* * public CSS2Properties getComputedStyle(HTMLElement element, String * pseudoElement) { if(element instanceof HTMLElementImpl) { return * ((HTMLElementImpl) element).getComputedStyle(pseudoElement); } else { * throw new * java.lang.IllegalArgumentException("Element implementation unknown: " + * element); } } */ public Function getOnload() { return this.document.getOnloadHandler(); } public void setOnload(Function onload) { // Note that body.onload overrides // window.onload. // Document doc = this.document; this.document.setOnloadHandler(onload); } private Function onunload; public Function getOnunload() { return onunload; } public void setOnunload(Function onunload) { this.onunload = onunload; } public org.w3c.dom.Node namedItem(String name) { // Bug 1928758: Element IDs are named objects in context. HTMLDocumentImpl doc = this.document; if (doc == null) { return null; } org.w3c.dom.Node node = doc.getElementById(name); if (node != null) { return node; } return null; } public void forceGC() { // System.gc(); } private static abstract class WeakWindowTask implements ActionListener { private final WeakReference windowRef; public WeakWindowTask(Window window) { this.windowRef = new WeakReference(window); } protected Window getWindow() { WeakReference ref = this.windowRef; return ref == null ? null : (Window) ref.get(); } } private static class FunctionTimerTask extends WeakWindowTask { // Implemented as a static WeakWindowTask to allow the Window // to get garbage collected, especially in infinite loop // scenarios. private final Integer timeIDInt; private final WeakReference functionRef; private final boolean removeTask; public FunctionTimerTask(Window window, Integer timeIDInt, Function function, boolean removeTask) { super(window); this.timeIDInt = timeIDInt; this.functionRef = new WeakReference(function); this.removeTask = removeTask; } public void actionPerformed(ActionEvent e) { // This executes in the GUI thread and that's good. try { Window window = this.getWindow(); if (window == null) { if (logger.isLoggable(Level.INFO)) { logger.info("actionPerformed(): Window is no longer available."); } return; } if (this.removeTask) { window.forgetTask(this.timeIDInt, false); } HTMLDocumentImpl doc = (HTMLDocumentImpl) window.getDocument(); if (doc == null) { throw new IllegalStateException("Cannot perform operation when document is unset."); } Function function = (Function) this.functionRef.get(); if (function == null) { throw new IllegalStateException("Cannot perform operation. Function is no longer available."); } Executor.executeFunction(window.getWindowScope(), function, doc.getDocumentURL(), window.getUserAgentContext()); } catch (Throwable err) { logger.log(Level.WARNING, "actionPerformed()", err); } } } private static class ExpressionTimerTask extends WeakWindowTask { // Implemented as a static WeakWindowTask to allow the Window // to get garbage collected, especially in infinite loop // scenarios. private final Integer timeIDInt; private final String expression; private final boolean removeTask; public ExpressionTimerTask(Window window, Integer timeIDInt, String expression, boolean removeTask) { super(window); this.timeIDInt = timeIDInt; this.expression = expression; this.removeTask = removeTask; } public void actionPerformed(ActionEvent e) { // This executes in the GUI thread and that's good. try { Window window = this.getWindow(); if (window == null) { if (logger.isLoggable(Level.INFO)) { logger.info("actionPerformed(): Window is no longer available."); } return; } if (this.removeTask) { window.forgetTask(this.timeIDInt, false); } HTMLDocumentImpl doc = (HTMLDocumentImpl) window.getDocument(); if (doc == null) { throw new IllegalStateException("Cannot perform operation when document is unset."); } window.eval(this.expression); } catch (Throwable err) { logger.log(Level.WARNING, "actionPerformed()", err); } } } private static class TaskWrapper { public final Timer timer; private final Object retained; public TaskWrapper(Timer timer, Object retained) { super(); this.timer = timer; this.retained = retained; } } /** * Used for testing for XSS using a function name that won't be blocked by * stupid filters * * @param token */ public void testXSS(String token) { document.setXssToken(token); } @Deprecated public void scroll(int x, int y) { HtmlRendererContext rcontext = this.rcontext; if (rcontext != null) { rcontext.scroll(x, y); } } }