package com.nvarghese.beowulf.common.cobra.html.domimpl; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import org.mozilla.javascript.Context; import org.mozilla.javascript.EcmaError; import org.mozilla.javascript.Function; import org.mozilla.javascript.Scriptable; import org.w3c.dom.Document; import com.nvarghese.beowulf.common.cobra.html.UserAgentContext; import com.nvarghese.beowulf.common.cobra.html.js.Executor; import com.nvarghese.beowulf.common.cobra.js.JavaScript; /** * Implements common functionality of most elements. */ public class HTMLAbstractUIElement extends HTMLElementImpl { private Function onfocus, onblur, onclick, ondblclick, onmousedown, onmouseup, onmouseover, onmousemove, onmouseout, onkeypress, onkeydown, onkeyup, oncontextmenu; public HTMLAbstractUIElement(String name) { super(name); } public Function getOnblur() { return this.getEventFunction(onblur, "onblur"); } public void setOnblur(Function onblur) { this.onblur = onblur; } public Function getOnclick() { return this.getEventFunction(onclick, "onclick"); } public void setOnclick(Function onclick) { this.onclick = onclick; } public Function getOndblclick() { return this.getEventFunction(ondblclick, "ondblclick"); } public void setOndblclick(Function ondblclick) { this.ondblclick = ondblclick; } public Function getOnfocus() { return this.getEventFunction(onfocus, "onfocus"); } public void setOnfocus(Function onfocus) { this.onfocus = onfocus; } public Function getOnkeydown() { return this.getEventFunction(onkeydown, "onkeydown"); } public void setOnkeydown(Function onkeydown) { this.onkeydown = onkeydown; } public Function getOnkeypress() { return this.getEventFunction(onkeypress, "onkeypress"); } public void setOnkeypress(Function onkeypress) { this.onkeypress = onkeypress; } public Function getOnkeyup() { return this.getEventFunction(onkeyup, "onkeyup"); } public void setOnkeyup(Function onkeyup) { this.onkeyup = onkeyup; } public Function getOnmousedown() { return this.getEventFunction(onmousedown, "onmousedown"); } public void setOnmousedown(Function onmousedown) { this.onmousedown = onmousedown; } public Function getOnmousemove() { return this.getEventFunction(onmousemove, "onmousemove"); } public void setOnmousemove(Function onmousemove) { this.onmousemove = onmousemove; } public Function getOnmouseout() { return this.getEventFunction(onmouseout, "onmouseout"); } public void setOnmouseout(Function onmouseout) { this.onmouseout = onmouseout; } public Function getOnmouseover() { return this.getEventFunction(onmouseover, "onmouseover"); } public void setOnmouseover(Function onmouseover) { this.onmouseover = onmouseover; } public Function getOnmouseup() { return this.getEventFunction(onmouseup, "onmouseup"); } public void setOnmouseup(Function onmouseup) { this.onmouseup = onmouseup; } public Function getOncontextmenu() { return this.getEventFunction(oncontextmenu, "oncontextmenu"); } public void setOncontextmenu(Function oncontextmenu) { this.oncontextmenu = oncontextmenu; } public void focus() { UINode node = this.getUINode(); if (node != null) { node.focus(); } } public void blur() { UINode node = this.getUINode(); if (node != null) { node.blur(); } } private Map functionByAttribute = null; public Function getEventFunction(Function varValue, String attributeName) { if (varValue != null) { return varValue; } String normalAttributeName = this.normalizeAttributeName(attributeName); synchronized (this) { Map fba = this.functionByAttribute; Function f = fba == null ? null : (Function) fba.get(normalAttributeName); if (f != null) { return f; } UserAgentContext uac = this.getUserAgentContext(); if (uac == null) { throw new IllegalStateException("No user agent context."); } if (uac.isScriptingEnabled()) { String attributeValue = this.getAttribute(attributeName); if (attributeValue == null || attributeValue.length() == 0) { f = null; } else { String functionCode = "function " + normalAttributeName + "_" + System.identityHashCode(this) + "() { " + attributeValue + " }"; Document doc = this.document; if (doc == null) { throw new IllegalStateException("Element does not belong to a document."); } Context ctx = Executor.createContext(this.getDocumentURL(), uac); try { Scriptable scope = (Scriptable) doc.getUserData(Executor.SCOPE_KEY); if (scope == null) { throw new IllegalStateException("Scriptable (scope) instance was expected to be keyed as UserData to document using " + Executor.SCOPE_KEY); } Scriptable thisScope = (Scriptable) JavaScript.getInstance().getJavascriptObject(this, scope); try { // TODO: Get right line number for script. //TODO: // Optimize this in case it's called multiple times? // Is that done? f = ctx.compileFunction(thisScope, functionCode, this.getTagName() + "[" + this.getId() + "]." + attributeName, 1, null); } catch (EcmaError ecmaError) { logger.log(Level.WARNING, "Javascript error at " + ecmaError.getSourceName() + ":" + ecmaError.getLineNumber() + ": " + ecmaError.getMessage(), ecmaError); f = null; } catch (Throwable err) { logger.log(Level.WARNING, "Unable to evaluate Javascript code", err); f = null; } } finally { Context.exit(); } } if (fba == null) { fba = new HashMap(1); this.functionByAttribute = fba; } fba.put(normalAttributeName, f); } return f; } } protected void assignAttributeField(String normalName, String value) { super.assignAttributeField(normalName, value); if (normalName.startsWith("on")) { synchronized (this) { Map fba = this.functionByAttribute; if (fba != null) { fba.remove(normalName); } } } } }