/* 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 */ package org.lobobrowser.html.renderer; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.lobobrowser.html.FormInput; import org.lobobrowser.html.HtmlAttributeProperties; import org.lobobrowser.html.HtmlProperties; import org.lobobrowser.html.HtmlRendererContext; import org.lobobrowser.html.dombl.ModelNode; import org.lobobrowser.html.domimpl.HTMLAbstractUIElement; import org.lobobrowser.html.domimpl.HTMLAnchorElementImpl; import org.lobobrowser.html.domimpl.HTMLButtonElementImpl; import org.lobobrowser.html.domimpl.HTMLDocumentImpl; import org.lobobrowser.html.domimpl.HTMLInputElementImpl; import org.lobobrowser.html.domimpl.HTMLSelectElementImpl; import org.lobobrowser.html.js.Executor; import org.lobobrowser.html.jsimpl.KeyboardEventImpl; import org.lobobrowser.html.jsimpl.MouseEventImpl; import org.mozilla.javascript.Function; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * The Class HtmlController. */ public class HtmlController { /** The Constant logger. */ private static final Logger logger = LogManager.getLogger(HtmlController.class.getName()); /** The Constant instance. */ private static final HtmlController instance = new HtmlController(); /** * Gets the Constant instance. * * @return the Constant instance */ public static HtmlController getInstance() { return instance; } /** * On enter pressed. * * @param node * the node * @param event * the event * @return True to propagate further and false if the event was consumed. */ public boolean onEnterPressed(ModelNode node, InputEvent event) { if (node instanceof HTMLInputElementImpl) { HTMLInputElementImpl hie = (HTMLInputElementImpl) node; if (hie.isSubmittableWithEnterKey()) { hie.submitForm(null); return false; } } // No propagation return false; } /** * On mouse click. * * @param node * the node * @param event * the event * @param x * the x * @param y * the y * @return True to propagate further and false if the event was consumed. */ public boolean onMouseClick(ModelNode node, MouseEvent event, RBlockViewport bodyLayout, int x, int y) { if (logger.isEnabled(Level.INFO)) { logger.info("onMouseClick(): node=" + node + ",class=" + node.getClass().getName()); } if (node instanceof HTMLDocumentImpl) { HTMLDocumentImpl uiDoc = (HTMLDocumentImpl) node; Function f = uiDoc.getOnclick(); if (f != null && bodyLayout == null) { if (!Executor.executeFunction(uiDoc, f, null)) { return false; } } else if (f == null && bodyLayout != null) { if (!bodyLayout.onMouseClick(event, x - bodyLayout.x, y - bodyLayout.y)) { return false; } } else if (f != null && bodyLayout != null) { boolean result = Executor.executeFunction(uiDoc, f, null); result = bodyLayout.onMouseClick(event, x - bodyLayout.x, y - bodyLayout.y); if (!result) { return result; } } } else if (node instanceof HTMLAnchorElementImpl) { ((HTMLAnchorElementImpl) node).navigate(); return false; } else if (node instanceof HTMLButtonElementImpl) { HTMLButtonElementImpl button = (HTMLButtonElementImpl) node; String rawType = button.getAttribute(HtmlAttributeProperties.TYPE); String type; if (rawType == null) { type = "submit"; } else { type = rawType.trim().toLowerCase(); } if ("submit".equals(type)) { FormInput[] formInputs; String name = button.getName(); if (name == null) { formInputs = null; } else { formInputs = new FormInput[] { new FormInput(name, button.getValue()) }; } button.submitForm(formInputs); } else if ("reset".equals(type)) { button.resetForm(); } else { // NOP for "button"! } return false; } else if (node instanceof HTMLAbstractUIElement) { HTMLAbstractUIElement uiElement = (HTMLAbstractUIElement) node; Function f = uiElement.getOnclick(); if (f != null) { MouseEventImpl jsEvent = new MouseEventImpl("click", uiElement, event, x, y); if (!Executor.executeFunction(uiElement, f, jsEvent)) { return false; } } HtmlRendererContext rcontext = uiElement.getHtmlRendererContext(); if (rcontext != null) { if (!rcontext.onMouseClick(uiElement, event)) { return false; } } } ModelNode parent = node.getParentModelNode(); if (parent == null) { return true; } return this.onMouseClick(parent, event, null, x, y); } /** * On context menu. * * @param node * the node * @param event * the event * @param x * the x * @param y * the y * @return true, if successful */ public boolean onContextMenu(ModelNode node, MouseEvent event, int x, int y) { if (logger.isEnabled(Level.INFO)) { logger.info("onContextMenu(): node=" + node + ",class=" + node.getClass().getName()); } if (node instanceof HTMLAbstractUIElement) { HTMLAbstractUIElement uiElement = (HTMLAbstractUIElement) node; Function f = uiElement.getOncontextmenu(); if (f != null) { MouseEventImpl jsEvent = new MouseEventImpl("contextmenu", uiElement, event, x, y); if (!Executor.executeFunction(uiElement, f, jsEvent)) { return false; } } HtmlRendererContext rcontext = uiElement.getHtmlRendererContext(); if (rcontext != null) { // Needs to be done after Javascript, so the script // is able to prevent it. if (!rcontext.onContextMenu(uiElement, event)) { return false; } } } ModelNode parent = node.getParentModelNode(); if (parent == null) { return true; } return this.onContextMenu(parent, event, x, y); } /** * On mouse over. * * @param node * the node * @param event * the event * @param x * the x * @param y * the y * @param limit * the limit */ public void onMouseOver(ModelNode node, MouseEvent event, int x, int y, ModelNode limit) { while (node != null) { if (node == limit) { break; } if (node instanceof HTMLDocumentImpl) { HTMLDocumentImpl uiDoc = (HTMLDocumentImpl) node; Function f = uiDoc.getOnmouseover(); Executor.executeFunction(uiDoc, f, null); } else if (node instanceof HTMLAbstractUIElement) { HTMLAbstractUIElement uiElement = (HTMLAbstractUIElement) node; uiElement.setMouseOver(true); Function f = uiElement.getOnmouseover(); if (f != null) { MouseEventImpl jsEvent = new MouseEventImpl("mouseover", uiElement, event, x, y); Executor.executeFunction(uiElement, f, jsEvent); } HtmlRendererContext rcontext = uiElement.getHtmlRendererContext(); if (rcontext != null) { rcontext.onMouseOver(uiElement, event); } } node = node.getParentModelNode(); } } /** * On mouse out. * * @param node * the node * @param event * the event * @param x * the x * @param y * the y * @param limit * the limit */ public void onMouseOut(ModelNode node, MouseEvent event, int x, int y, ModelNode limit) { while (node != null) { if (node == limit) { break; } if (node instanceof HTMLAbstractUIElement) { HTMLAbstractUIElement uiElement = (HTMLAbstractUIElement) node; uiElement.setMouseOver(false); Function f = uiElement.getOnmouseout(); if (f != null) { MouseEventImpl jsEvent = new MouseEventImpl("mouseout", uiElement, event, x, y); Executor.executeFunction(uiElement, f, jsEvent); } HtmlRendererContext rcontext = uiElement.getHtmlRendererContext(); if (rcontext != null) { rcontext.onMouseOut(uiElement, event); } } node = node.getParentModelNode(); } } /** * On double click. * * @param node * the node * @param event * the event * @param x * the x * @param y * the y * @return True to propagate further, false if consumed. */ public boolean onDoubleClick(ModelNode node, MouseEvent event, RBlockViewport bodyLayout, int x, int y) { if (logger.isEnabled(Level.INFO)) { logger.info("onDoubleClick(): node=" + node + ",class=" + node.getClass().getName()); } if (node instanceof HTMLDocumentImpl) { HTMLDocumentImpl uiDoc = (HTMLDocumentImpl) node; Function f = uiDoc.getOndblclick(); if (f != null && bodyLayout == null) { if (!Executor.executeFunction(uiDoc, f, null)) { return false; } } else if (f == null && bodyLayout != null) { if (!bodyLayout.onDoubleClick(event, x - bodyLayout.x, y - bodyLayout.y)) { return false; } } else if (f != null && bodyLayout != null) { boolean result = Executor.executeFunction(uiDoc, f, null); result = bodyLayout.onDoubleClick(event, x - bodyLayout.x, y - bodyLayout.y); if (!result) { return result; } } } else if (node instanceof HTMLAbstractUIElement) { HTMLAbstractUIElement uiElement = (HTMLAbstractUIElement) node; Function f = uiElement.getOndblclick(); if (f != null) { MouseEventImpl jsEvent = new MouseEventImpl("dblclick", uiElement, event, x, y); if (!Executor.executeFunction(uiElement, f, jsEvent)) { return false; } } HtmlRendererContext rcontext = uiElement.getHtmlRendererContext(); if (rcontext != null) { if (!rcontext.onDoubleClick(uiElement, event)) { return false; } } } ModelNode parent = node.getParentModelNode(); if (parent == null) { return true; } return this.onDoubleClick(parent, event, null, x, y); } /** * On mouse disarmed. * * @param node * the node * @param event * the event * @return True to propagate further, false if consumed. */ public boolean onMouseDisarmed(ModelNode node, MouseEvent event) { if (node instanceof HTMLAnchorElementImpl) { ((HTMLAnchorElementImpl) node).getCurrentStyle().setOverlayColor(null); return false; } ModelNode parent = node.getParentModelNode(); if (parent == null) { return true; } return this.onMouseDisarmed(parent, event); } /** * On mouse down. * * @param node * the node * @param event * the event * @param x * the x * @param y * the y * @return True to propagate further, false if consumed. */ public boolean onMouseDown(ModelNode node, MouseEvent event, RBlock block, int x, int y) { boolean pass = true; if (node instanceof HTMLDocumentImpl) { if (block != null) { RBlockViewport bodyLayout = block.bodyLayout; int newX = 0; int newY = 0; HTMLDocumentImpl uiDoc = (HTMLDocumentImpl) node; Function f = uiDoc.getOnmousedown(); if (f != null && bodyLayout == null) { block.setArmedRenderable(null); if (!Executor.executeFunction(uiDoc, f, null)) { return false; } } else if (f == null && bodyLayout != null) { newX = x - bodyLayout.x; newY = y - bodyLayout.y; if (bodyLayout.contains(newX, newY)) { block.setArmedRenderable(bodyLayout); if (!bodyLayout.onMousePressed(event, newX, newY)) { return false; } } else { block.setArmedRenderable(null); } } else if (f != null && bodyLayout != null) { boolean result = Executor.executeFunction(uiDoc, f, null); newX = x - bodyLayout.x; newY = y - bodyLayout.y; if (bodyLayout.contains(newX, newY)) { block.setArmedRenderable(bodyLayout); result = bodyLayout.onMousePressed(event, newX, newY); } else { block.setArmedRenderable(null); } if (!result) { return result; } } } } else if (node instanceof HTMLAbstractUIElement) { HTMLAbstractUIElement uiElement = (HTMLAbstractUIElement) node; Function f = uiElement.getOnmousedown(); if (f != null) { MouseEventImpl jsEvent = new MouseEventImpl("mousedown", uiElement, event, x, y); pass = Executor.executeFunction(uiElement, f, jsEvent); } } if (node instanceof HTMLAnchorElementImpl) { ((HTMLAnchorElementImpl) node).getCurrentStyle().setOverlayColor("#9090FF80"); return false; } if (!pass) { return false; } ModelNode parent = node.getParentModelNode(); if (parent == null) { return true; } return this.onMouseDown(parent, event, null, x, y); } /** * On mouse up. * * @param node * the node * @param event * the event * @param x * the x * @param y * the y * @return True to propagate further, false if consumed. */ public boolean onMouseUp(ModelNode node, MouseEvent event, RBlock block, int x, int y) { boolean pass = true; if (node instanceof HTMLDocumentImpl) { if (block != null) { RBlockViewport bodyLayout = block.bodyLayout; int newX = 0; int newY = 0; HTMLDocumentImpl uiDoc = (HTMLDocumentImpl) node; Function f = uiDoc.getOnmouseup(); if (f != null && bodyLayout == null) { block.setArmedRenderable(null); if (!Executor.executeFunction(uiDoc, f, null)) { return false; } } else if (f == null && bodyLayout != null) { newX = x - bodyLayout.x; newY = y - bodyLayout.y; if (bodyLayout.contains(newX, newY)) { block.setArmedRenderable(bodyLayout); if (!bodyLayout.onMouseReleased(event, newX, newY)) { return false; } } else { block.setArmedRenderable(null); } } else if (f != null && bodyLayout != null) { boolean result = Executor.executeFunction(uiDoc, f, null); newX = x - bodyLayout.x; newY = y - bodyLayout.y; if (bodyLayout.contains(newX, newY)) { block.setArmedRenderable(bodyLayout); result = bodyLayout.onMouseReleased(event, newX, newY); } else { block.setArmedRenderable(null); } if (!result) { return result; } } } } else if (node instanceof HTMLAbstractUIElement) { HTMLAbstractUIElement uiElement = (HTMLAbstractUIElement) node; Function f = uiElement.getOnmouseup(); if (f != null) { MouseEventImpl jsEvent = new MouseEventImpl("mouseup", uiElement, event, x, y); pass = Executor.executeFunction(uiElement, f, jsEvent); } } if (node instanceof HTMLAnchorElementImpl) { ((HTMLAnchorElementImpl) node).getCurrentStyle().setOverlayColor(null); return false; } if (!pass) { return false; } ModelNode parent = node.getParentModelNode(); if (parent == null) { return true; } return this.onMouseUp(parent, event, null, x, y); } /** * On pressed. * * @param node * The node generating the event. * @param event * the event * @param x * For images only, x coordinate of mouse click. * @param y * For images only, y coordinate of mouse click. * @return True to propagate further, false if consumed. */ public boolean onPressed(ModelNode node, InputEvent event, int x, int y) { if (node instanceof HTMLAbstractUIElement) { HTMLAbstractUIElement uiElement = (HTMLAbstractUIElement) node; Function f = uiElement.getOnclick(); if (f != null) { MouseEventImpl jsEvent = new MouseEventImpl("click", uiElement, event, x, y); if (!Executor.executeFunction(uiElement, f, jsEvent)) { return false; } } } if (node instanceof HTMLInputElementImpl) { HTMLInputElementImpl hie = (HTMLInputElementImpl) node; HTMLDocumentImpl doc = (HTMLDocumentImpl) hie.getOwnerDocument(); NodeList list = doc.getElementsByTagName(HtmlProperties.INPUT); if (hie.isSubmitInput()) { FormInput[] formInputs = new FormInput[list.getLength()]; for (int i = 0; i < list.getLength(); i++) { Node n = (Node) list.item(i); if (n instanceof HTMLInputElementImpl) { HTMLInputElementImpl input = (HTMLInputElementImpl) n; String name = input.getName(); if (name == null) { formInputs[i] = new FormInput("", ""); } else { String value = ""; if (input != null) { value = input.getValue(); } formInputs[i] = new FormInput(name, value); } } } hie.submitForm(formInputs); } else if (hie.isImageInput()) { FormInput[] formInputs = new FormInput[list.getLength() + 2]; String name = hie.getName(); String prefix = name == null ? "" : name + "."; int count = 0; for (int i = 0; i < list.getLength(); i++) { Node n = (Node) list.item(i); if (n instanceof HTMLInputElementImpl) { HTMLInputElementImpl input = (HTMLInputElementImpl) n; String inputName = input.getName(); if (inputName == null) { formInputs[i] = new FormInput("", ""); } else { String value = ""; if (input != null) { value = input.getValue(); } formInputs[i] = new FormInput(inputName, value); } } count = i; } formInputs[count + 1] = new FormInput(prefix + "x", String.valueOf(x)); formInputs[count + 2] = new FormInput(prefix + "y", String.valueOf(y)); hie.submitForm(formInputs); } else if (hie.isResetInput()) { hie.resetForm(); } } return false; } /** * On change. * * @param node * the node * @return true, if successful */ public boolean onChange(ModelNode node) { if (node instanceof HTMLSelectElementImpl) { HTMLSelectElementImpl uiElement = (HTMLSelectElementImpl) node; Function f = uiElement.getOnchange(); if (f != null) { MouseEventImpl jsEvent = new MouseEventImpl("change", uiElement); if (!Executor.executeFunction(uiElement, f, jsEvent)) { return false; } } } // No propagate return false; } /** * On key down. * * @param node * the node * @param event * the event * @return true, if successful */ public boolean onKeyDown(ModelNode node, KeyEvent event) { if (node instanceof HTMLDocumentImpl) { HTMLDocumentImpl uiDoc = (HTMLDocumentImpl) node; Function f = uiDoc.getOnkeydown(); if (!Executor.executeFunction(uiDoc, f, null)) { return false; } } else if (node instanceof HTMLInputElementImpl) { HTMLInputElementImpl uiElement = (HTMLInputElementImpl) node; Function f = uiElement.getOnkeydown(); if (f != null) { KeyboardEventImpl jsEvent = new KeyboardEventImpl("keydown", uiElement, event); if (!Executor.executeFunction(uiElement, f, jsEvent)) { return false; } } } // No propagate return false; } /** * On key press. * * @param node * the node * @param event * the event * @return true, if successful */ public boolean onKeyPress(ModelNode node, KeyEvent event) { if (node instanceof HTMLDocumentImpl) { HTMLDocumentImpl uiDoc = (HTMLDocumentImpl) node; Function f = uiDoc.getOnkeypress(); if (!Executor.executeFunction(uiDoc, f, null)) { return false; } } else if (node instanceof HTMLInputElementImpl) { HTMLInputElementImpl uiElement = (HTMLInputElementImpl) node; Function f = uiElement.getOnkeypress(); if (f != null) { KeyboardEventImpl jsEvent = new KeyboardEventImpl("keypress", uiElement, event); if (!Executor.executeFunction(uiElement, f, jsEvent)) { return false; } } } return false; } /** * On key up. * * @param node * the node * @param event * the event * @return true, if successful */ public boolean onKeyUp(ModelNode node, KeyEvent event) { if (node instanceof HTMLDocumentImpl) { HTMLDocumentImpl uiDoc = (HTMLDocumentImpl) node; Function f = uiDoc.getOnkeyup(); if (!Executor.executeFunction(uiDoc, f, null)) { return false; } } else if (node instanceof HTMLInputElementImpl) { HTMLInputElementImpl uiElement = (HTMLInputElementImpl) node; Function f = uiElement.getOnkeyup(); if (f != null) { KeyboardEventImpl jsEvent = new KeyboardEventImpl("keyup", uiElement, event); if (!Executor.executeFunction(uiElement, f, jsEvent)) { return false; } } } // No propagate return false; } }