[HtmlUnit] SVN: [14642] trunk/htmlunit/src

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

[HtmlUnit] SVN: [14642] trunk/htmlunit/src

HtmlUnit - Dev mailing list
Revision: 14642
          http://sourceforge.net/p/htmlunit/code/14642
Author:   rbri
Date:     2017-07-03 17:37:27 +0000 (Mon, 03 Jul 2017)
Log Message:
-----------
take care of label elements during event bubbling; they have to trigger a click event for the associated element

Modified Paths:
--------------
    trunk/htmlunit/src/changes/changes.xml
    trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomElement.java
    trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/event/Event.java
    trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/event/Event2.java
    trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/event/EventTarget.java
    trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/event/MouseEvent.java
    trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/event/MouseEvent2.java
    trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/html/HtmlLabelTest.java

Modified: trunk/htmlunit/src/changes/changes.xml
===================================================================
--- trunk/htmlunit/src/changes/changes.xml 2017-07-03 17:33:52 UTC (rev 14641)
+++ trunk/htmlunit/src/changes/changes.xml 2017-07-03 17:37:27 UTC (rev 14642)
@@ -9,6 +9,9 @@
     <body>
         <release version="2.28" date="???" description="Bugfixes">
             <action type="fix" dev="rbri">
+                Take care of label elements during event bubbling; they have to trigger a click event for the associated element.
+            </action>
+            <action type="fix" dev="rbri">
                 JavaScript: fix window.getComputedStyle() pseudo handling if pseudo param starts with double colon.
             </action>
             <action type="update" dev="rbri">

Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomElement.java
===================================================================
--- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomElement.java 2017-07-03 17:33:52 UTC (rev 14641)
+++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/html/DomElement.java 2017-07-03 17:37:27 UTC (rev 14642)
@@ -892,15 +892,40 @@
      * @return the page contained in the current window as returned by {@link WebClient#getCurrentWindow()}
      * @exception IOException if an IO error occurs
      */
-    @SuppressWarnings("unchecked")
-    protected <P extends Page> P click(final boolean shiftKey, final boolean ctrlKey, final boolean altKey,
+    public <P extends Page> P click(final boolean shiftKey, final boolean ctrlKey, final boolean altKey,
             final boolean triggerMouseEvents) throws IOException {
+        return click(shiftKey, ctrlKey, altKey, triggerMouseEvents, false, false);
+    }
 
+    /**
+     * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br>
+     *
+     * Simulates clicking on this element, returning the page in the window that has the focus
+     * after the element has been clicked. Note that the returned page may or may not be the same
+     * as the original page, depending on the type of element being clicked, the presence of JavaScript
+     * action listeners, etc.
+     *
+     * @param shiftKey {@code true} if SHIFT is pressed during the click
+     * @param ctrlKey {@code true} if CTRL is pressed during the click
+     * @param altKey {@code true} if ALT is pressed during the click
+     * @param triggerMouseEvents if true trigger the mouse events also
+     * @param ignoreVisibility whether to ignore visibility or not
+     * @param disableProcessLabelAfterBubbling ignore label processing
+     * @param <P> the page type
+     * @return the page contained in the current window as returned by {@link WebClient#getCurrentWindow()}
+     * @exception IOException if an IO error occurs
+     */
+    @SuppressWarnings("unchecked")
+    public <P extends Page> P click(final boolean shiftKey, final boolean ctrlKey, final boolean altKey,
+            final boolean triggerMouseEvents, final boolean ignoreVisibility,
+            final boolean disableProcessLabelAfterBubbling) throws IOException {
+
         // make enclosing window the current one
         final SgmlPage page = getPage();
         page.getWebClient().setCurrentWindow(page.getEnclosingWindow());
 
-        if (!isDisplayed() || !(page instanceof HtmlPage)
+        if ((!ignoreVisibility && !isDisplayed())
+                || !(page instanceof HtmlPage)
                 || this instanceof DisabledElement && ((DisabledElement) this).isDisabled()) {
             return (P) page;
         }
@@ -927,7 +952,7 @@
             }
 
             if (getPage().getWebClient().getJavaScriptEngine() instanceof NashornJavaScriptEngine) {
-                final Event2 event;
+                final MouseEvent2 event;
                 if (getPage().getWebClient().getBrowserVersion().hasFeature(EVENT_ONCLICK_USES_POINTEREVENT)) {
                     event = new PointerEvent2(getEventTargetElement(), MouseEvent.TYPE_CLICK, shiftKey,
                             ctrlKey, altKey, MouseEvent.BUTTON_LEFT);
@@ -936,10 +961,14 @@
                     event = new MouseEvent2(getEventTargetElement(), MouseEvent.TYPE_CLICK, shiftKey,
                             ctrlKey, altKey, MouseEvent.BUTTON_LEFT);
                 }
+
+                if (disableProcessLabelAfterBubbling) {
+                    event.disableProcessLabelAfterBubbling();
+                }
                 return (P) click(event, false);
             }
 
-            final Event event;
+            final MouseEvent event;
             if (getPage().getWebClient().getBrowserVersion().hasFeature(EVENT_ONCLICK_USES_POINTEREVENT)) {
                 event = new PointerEvent(getEventTargetElement(), MouseEvent.TYPE_CLICK, shiftKey,
                         ctrlKey, altKey, MouseEvent.BUTTON_LEFT);
@@ -948,7 +977,11 @@
                 event = new MouseEvent(getEventTargetElement(), MouseEvent.TYPE_CLICK, shiftKey,
                         ctrlKey, altKey, MouseEvent.BUTTON_LEFT);
             }
-            return (P) click(event, false);
+
+            if (disableProcessLabelAfterBubbling) {
+                event.disableProcessLabelAfterBubbling();
+            }
+            return (P) click(event, ignoreVisibility);
         }
     }
 

Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/event/Event.java
===================================================================
--- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/event/Event.java 2017-07-03 17:33:52 UTC (rev 14641)
+++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/event/Event.java 2017-07-03 17:37:27 UTC (rev 14642)
@@ -666,4 +666,15 @@
         builder.append(");");
         return builder.toString();
     }
+
+    /**
+     * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br>
+     *
+     * If we click on a label, we have to simulate a click on the element referenced by the 'for' attribute also.
+     * To support this for special events we have this method here.
+     * @return false in this default impl
+     */
+    public boolean processLabelAfterBubbling() {
+        return false;
+    }
 }

Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/event/Event2.java
===================================================================
--- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/event/Event2.java 2017-07-03 17:33:52 UTC (rev 14641)
+++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/event/Event2.java 2017-07-03 17:37:27 UTC (rev 14642)
@@ -1097,4 +1097,15 @@
             return META_MASK;
         }
     }
+
+    /**
+     * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br>
+     *
+     * If we click on a label, we have to simulate a click on the element referenced by the 'for' attribute also.
+     * To support this for special events we have this method here.
+     * @return false in this default impl
+     */
+    public boolean processLabelAfterBubbling() {
+        return false;
+    }
 }

Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/event/EventTarget.java
===================================================================
--- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/event/EventTarget.java 2017-07-03 17:33:52 UTC (rev 14641)
+++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/event/EventTarget.java 2017-07-03 17:37:27 UTC (rev 14642)
@@ -32,6 +32,8 @@
 import com.gargoylesoftware.htmlunit.html.DomDocumentFragment;
 import com.gargoylesoftware.htmlunit.html.DomElement;
 import com.gargoylesoftware.htmlunit.html.DomNode;
+import com.gargoylesoftware.htmlunit.html.HtmlElement;
+import com.gargoylesoftware.htmlunit.html.HtmlLabel;
 import com.gargoylesoftware.htmlunit.javascript.SimpleScriptable;
 import com.gargoylesoftware.htmlunit.javascript.configuration.JsxClass;
 import com.gargoylesoftware.htmlunit.javascript.configuration.JsxConstructor;
@@ -38,6 +40,7 @@
 import com.gargoylesoftware.htmlunit.javascript.configuration.JsxFunction;
 import com.gargoylesoftware.htmlunit.javascript.host.Window;
 import com.gargoylesoftware.htmlunit.javascript.host.html.HTMLElement;
+import com.gargoylesoftware.htmlunit.javascript.host.html.HTMLLabelElement;
 
 import net.sourceforge.htmlunit.corejs.javascript.Context;
 import net.sourceforge.htmlunit.corejs.javascript.Function;
@@ -179,7 +182,13 @@
             // bubbling phase
             event.setEventPhase(Event.AT_TARGET);
             eventTarget = this;
+            HtmlLabel label = null;
+            final boolean processLabelAfterBubbling = event.processLabelAfterBubbling();
+
             while (eventTarget != null) {
+                if (label == null && processLabelAfterBubbling && eventTarget instanceof HTMLLabelElement) {
+                    label = (HtmlLabel) eventTarget.getDomNodeOrNull();
+                }
                 final EventTarget jsNode = eventTarget;
                 final EventListenersContainer elc = jsNode.eventListenersContainer_;
                 if (elc != null && !(jsNode instanceof Window) && (isAttached || !(jsNode instanceof HTMLElement))) {
@@ -197,6 +206,19 @@
                 event.setEventPhase(Event.BUBBLING_PHASE);
             }
 
+            if (label != null) {
+                System.out.println("call " + label);
+                final HtmlElement element = label.getReferencedElement();
+                if (element != null) {
+                    try {
+                        element.click(event.isShiftKey(), event.isCtrlKey(), event.isAltKey(), false, true, true);
+                    }
+                    catch (final IOException e) {
+                        // ignore for now
+                    }
+                }
+            }
+
             if (isAttached || windowEventIfDetached) {
                 final ScriptResult r = windowsListeners.executeBubblingListeners(event, args, propHandlerArgs);
                 result = ScriptResult.combine(r, result, ie);

Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/event/MouseEvent.java
===================================================================
--- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/event/MouseEvent.java 2017-07-03 17:33:52 UTC (rev 14641)
+++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/event/MouseEvent.java 2017-07-03 17:37:27 UTC (rev 14642)
@@ -39,6 +39,7 @@
  * @author Marc Guillemot
  * @author Ahmed Ashour
  * @author Frank Danek
+ * @author Ronald Brill
  */
 @JsxClass
 public class MouseEvent extends UIEvent {
@@ -107,6 +108,9 @@
     /** The button code according to W3C (0: left button, 1: middle button, 2: right button). */
     private int button_;
 
+    /** Switch to disable label handling if we already processing the event triggered from label processing */
+    private boolean processLabelAfterBubbling_ = true;
+
     /**
      * Used to build the prototype.
      */
@@ -376,4 +380,19 @@
     public boolean isShiftKey() {
         return super.isShiftKey();
     }
+
+    /**
+     * {@inheritDoc} Overridden take care of click events.
+     */
+    @Override
+    public boolean processLabelAfterBubbling() {
+        return MouseEvent.TYPE_CLICK  == getType() && processLabelAfterBubbling_;
+    }
+
+    /**
+     * Disable the lable processing if we are already processing one.
+     */
+    public void disableProcessLabelAfterBubbling() {
+        processLabelAfterBubbling_ = false;
+    }
 }

Modified: trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/event/MouseEvent2.java
===================================================================
--- trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/event/MouseEvent2.java 2017-07-03 17:33:52 UTC (rev 14641)
+++ trunk/htmlunit/src/main/java/com/gargoylesoftware/htmlunit/javascript/host/event/MouseEvent2.java 2017-07-03 17:37:27 UTC (rev 14642)
@@ -85,6 +85,9 @@
     /** The button code according to W3C (0: left button, 1: middle button, 2: right button). */
     private int button_;
 
+    /** Switch to disable label handling if we already processing the event triggered from label processing */
+    private boolean processLabelAfterBubbling_ = true;
+
     /**
      * Used to build the prototype.
      */
@@ -222,4 +225,19 @@
             super("MouseEvent");
         }
     }
+
+    /**
+     * {@inheritDoc} Overridden take care of click events.
+     */
+    @Override
+    public boolean processLabelAfterBubbling() {
+        return MouseEvent.TYPE_CLICK  == getType() && processLabelAfterBubbling_;
+    }
+
+    /**
+     * Disable the lable processing if we are already processing one.
+     */
+    public void disableProcessLabelAfterBubbling() {
+        processLabelAfterBubbling_ = false;
+    }
 }

Modified: trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/html/HtmlLabelTest.java
===================================================================
--- trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/html/HtmlLabelTest.java 2017-07-03 17:33:52 UTC (rev 14641)
+++ trunk/htmlunit/src/test/java/com/gargoylesoftware/htmlunit/html/HtmlLabelTest.java 2017-07-03 17:37:27 UTC (rev 14642)
@@ -72,4 +72,99 @@
         driver.findElement(By.id("label1")).click();
         verifyAlerts(driver, getExpectedAlerts());
     }
+
+    /**
+     * @throws Exception if an error occurs
+     */
+    @Test
+    @Alerts({"click span1", "click radio1Label", "click listItem1", "click list",
+                "click radio1", "click radio1Label",
+                "click listItem1", "click list"})
+    public void triggerRadioComplexCase() throws Exception {
+        final String html = "<html>\n"
+            + "<body>\n"
+            + "  <ul onclick='alert(\"click list\")'>\n"
+            + "  <li onclick='alert(\"click listItem1\")'>\n"
+            + "    <label id='radio1Label' for='radio1' onclick='alert(\"click radio1Label\")'>\n"
+            + "      <span>\n"
+            + "        <input id='radio1' name='radios' value='1' type='radio'"
+                            + "onclick='alert(\"click radio1\");'>\n"
+            + "        <span id='radio1Span' onclick='alert(\"click span1\")'>Radio 1</span>\n"
+            + "      </span>\n"
+            + "    </label>\n"
+            + "  </li>\n"
+            + "</ul>\n"
+            + "<button id='check' onclick='alert(document.getElementById(\"radio1\").checked)'>Check</button>\n"
+            + "</body></html>";
+
+        final WebDriver driver = loadPage2(html);
+        driver.findElement(By.id("radio1Span")).click();
+        verifyAlerts(driver, getExpectedAlerts());
+
+        driver.findElement(By.id("check")).click();
+        verifyAlerts(driver, "true");
+    }
+
+    /**
+     * @throws Exception if an error occurs
+     */
+    @Test
+    @Alerts({"click span1", "click radio1Label", "click listItem1", "click list",
+                "click radio1", "click radio1Label",
+                "click listItem1", "click list"})
+    public void triggerRadioComplexCaseHidden() throws Exception {
+        final String html = "<html>\n"
+            + "<body>\n"
+            + "  <ul onclick='alert(\"click list\")'>\n"
+            + "  <li onclick='alert(\"click listItem1\")'>\n"
+            + "    <label id='radio1Label' for='radio1' onclick='alert(\"click radio1Label\")'>\n"
+            + "      <span>\n"
+            + "        <input id='radio1' name='radios' value='1' type='radio' style='display: none;'"
+                            + "onclick='alert(\"click radio1\");'>\n"
+            + "        <span id='radio1Span' onclick='alert(\"click span1\")'>Radio 1</span>\n"
+            + "      </span>\n"
+            + "    </label>\n"
+            + "  </li>\n"
+            + "</ul>\n"
+            + "<button id='check' onclick='alert(document.getElementById(\"radio1\").checked)'>Check</button>\n"
+            + "</body></html>";
+
+        final WebDriver driver = loadPage2(html);
+        driver.findElement(By.id("radio1Span")).click();
+        verifyAlerts(2000000, driver, getExpectedAlerts());
+
+        driver.findElement(By.id("check")).click();
+        verifyAlerts(driver, "true");
+    }
+
+    /**
+     * @throws Exception if an error occurs
+     */
+    @Test
+    @Alerts({"click span1", "click radio1Label", "click listItem1", "click list",
+                "click radio1Label", "click listItem1", "click list"})
+    public void triggerRadioComplexCaseDisabled() throws Exception {
+        final String html = "<html>\n"
+            + "<body>\n"
+            + "  <ul onclick='alert(\"click list\")'>\n"
+            + "  <li onclick='alert(\"click listItem1\")'>\n"
+            + "    <label id='radio1Label' for='radio1' onclick='alert(\"click radio1Label\")'>\n"
+            + "      <span>\n"
+            + "        <input id='radio1' name='radios' value='1' type='radio' disabled"
+                            + "onclick='alert(\"click radio1\");'>\n"
+            + "        <span id='radio1Span' onclick='alert(\"click span1\")'>Radio 1</span>\n"
+            + "      </span>\n"
+            + "    </label>\n"
+            + "  </li>\n"
+            + "</ul>\n"
+            + "<button id='check' onclick='alert(document.getElementById(\"radio1\").checked)'>Check</button>\n"
+            + "</body></html>";
+
+        final WebDriver driver = loadPage2(html);
+        driver.findElement(By.id("radio1Span")).click();
+        verifyAlerts(driver, getExpectedAlerts());
+
+        driver.findElement(By.id("check")).click();
+        verifyAlerts(driver, "true");
+    }
 }


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
HtmlUnit-develop mailing list
[hidden email]
https://lists.sourceforge.net/lists/listinfo/htmlunit-develop