View Javadoc
1   /*
2    * Copyright (c) 2002-2017 Gargoyle Software Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software
10   * distributed under the License is distributed on an "AS IS" BASIS,
11   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12   * See the License for the specific language governing permissions and
13   * limitations under the License.
14   */
15  package com.gargoylesoftware.htmlunit.javascript.host.event;
16  
17  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.EVENT_FOCUS_FOCUS_IN_BLUR_OUT;
18  import static com.gargoylesoftware.htmlunit.BrowserVersionFeatures.EVENT_ONLOAD_CANCELABLE_FALSE;
19  import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.CHROME;
20  import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.EDGE;
21  import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.FF;
22  import static com.gargoylesoftware.htmlunit.javascript.configuration.SupportedBrowser.IE;
23  
24  import java.lang.reflect.Method;
25  import java.util.LinkedList;
26  
27  import com.gargoylesoftware.htmlunit.ScriptResult;
28  import com.gargoylesoftware.htmlunit.html.DomNode;
29  import com.gargoylesoftware.htmlunit.javascript.SimpleScriptable;
30  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxClass;
31  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxConstant;
32  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxConstructor;
33  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxFunction;
34  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxGetter;
35  import com.gargoylesoftware.htmlunit.javascript.configuration.JsxSetter;
36  
37  import net.sourceforge.htmlunit.corejs.javascript.Context;
38  import net.sourceforge.htmlunit.corejs.javascript.Scriptable;
39  import net.sourceforge.htmlunit.corejs.javascript.ScriptableObject;
40  import net.sourceforge.htmlunit.corejs.javascript.Undefined;
41  
42  /**
43   * JavaScript object representing an event that is passed into event handlers when they are
44   * invoked. For general information on which properties and functions should be supported,
45   * see <a href="https://developer.mozilla.org/en-US/docs/DOM/event">the mozilla docs</a>,
46   * <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-Event">the W3C DOM
47   * Level 2 Event Documentation</a> or <a href="http://msdn2.microsoft.com/en-us/library/aa703876.aspx">IE's
48   * IHTMLEventObj interface</a>.
49   *
50   * @author <a href="mailto:chriseldredge@comcast.net">Chris Eldredge</a>
51   * @author Mike Bowler
52   * @author Chris Erskine
53   * @author Marc Guillemot
54   * @author Daniel Gredler
55   * @author Brad Murray
56   * @author Ahmed Ashour
57   * @author Rob Di Marco
58   * @author Ronald Brill
59   * @author Frank Danek
60   */
61  @JsxClass
62  public class Event extends SimpleScriptable {
63  
64      /**
65       * Key to place the event's target in the Context's scope during event processing
66       * to compute node coordinates compatible with those of the event.
67       */
68      protected static final String KEY_CURRENT_EVENT = "Event#current";
69  
70      /** The submit event type, triggered by {@code onsubmit} event handlers. */
71      public static final String TYPE_SUBMIT = "submit";
72  
73      /** The change event type, triggered by {@code onchange} event handlers. */
74      public static final String TYPE_CHANGE = "change";
75  
76      /** The load event type, triggered by {@code onload} event handlers. */
77      public static final String TYPE_LOAD = "load";
78  
79      /** The unload event type, triggered by {@code onunload} event handlers. */
80      public static final String TYPE_UNLOAD = "unload";
81  
82      /** The popstate event type, triggered by {@code onpopstate} event handlers. */
83      public static final String TYPE_POPSTATE = "popstate";
84  
85      /** The focus event type, triggered by {@code onfocus} event handlers. */
86      public static final String TYPE_FOCUS = "focus";
87  
88      /** The focus in event type, triggered by {@code onfocusin} event handlers. */
89      public static final String TYPE_FOCUS_IN = "focusin";
90  
91      /** The focus out event type, triggered by {@code onfocusout} event handlers. */
92      public static final String TYPE_FOCUS_OUT = "focusout";
93  
94      /** The blur event type, triggered by {@code onblur} event handlers. */
95      public static final String TYPE_BLUR = "blur";
96  
97      /** The key down event type, triggered by {@code onkeydown} event handlers. */
98      public static final String TYPE_KEY_DOWN = "keydown";
99  
100     /** The key down event type, triggered by {@code onkeypress} event handlers. */
101     public static final String TYPE_KEY_PRESS = "keypress";
102 
103     /** The input event type, triggered by {@code oninput} event handlers. */
104     public static final String TYPE_INPUT = "input";
105 
106     /** The key down event type, triggered by {@code onkeyup} event handlers. */
107     public static final String TYPE_KEY_UP = "keyup";
108 
109     /** The submit event type, triggered by {@code onreset} event handlers. */
110     public static final String TYPE_RESET = "reset";
111 
112     /** The beforeunload event type, triggered by {@code onbeforeunload} event handlers. */
113     public static final String TYPE_BEFORE_UNLOAD = "beforeunload";
114 
115     /** Triggered after the DOM has loaded but before images etc. */
116     public static final String TYPE_DOM_DOCUMENT_LOADED = "DOMContentLoaded";
117 
118     /** The event type triggered by {@code onpropertychange} event handlers. */
119     public static final String TYPE_PROPERTY_CHANGE = "propertychange";
120 
121     /** The event type triggered by {@code onhashchange} event handlers. */
122     public static final String TYPE_HASH_CHANGE = "hashchange";
123 
124     /** The event type triggered by {@code onreadystatechange} event handlers. */
125     public static final String TYPE_READY_STATE_CHANGE = "readystatechange";
126 
127     /** The event type triggered by {@code onerror} event handlers. */
128     public static final String TYPE_ERROR = "error";
129 
130     /** The message event type, triggered by postMessage. */
131     public static final String TYPE_MESSAGE = "message";
132 
133     /** The close event type, triggered by {@code onclose} event handlers. */
134     public static final String TYPE_CLOSE = "close";
135 
136     /** The open event type, triggered by {@code onopen} event handlers. */
137     public static final String TYPE_OPEN = "open";
138 
139     /** No event phase. */
140     @JsxConstant({CHROME, FF})
141     public static final short NONE = 0;
142 
143     /** The first event phase: the capturing phase. */
144     @JsxConstant
145     public static final short CAPTURING_PHASE = 1;
146 
147     /** The second event phase: at the event target. */
148     @JsxConstant
149     public static final short AT_TARGET = 2;
150 
151     /** The third (and final) event phase: the bubbling phase. */
152     @JsxConstant
153     public static final short BUBBLING_PHASE = 3;
154 
155     /** Constant. */
156     @JsxConstant(FF)
157     public static final int ALT_MASK = 0x1;
158 
159     /** Constant. */
160     @JsxConstant(FF)
161     public static final int CONTROL_MASK = 0x2;
162 
163     /** Constant. */
164     @JsxConstant(FF)
165     public static final int SHIFT_MASK = 0x4;
166 
167     /** Constant. */
168     @JsxConstant(FF)
169     public static final int META_MASK = 0x8;
170 
171     private Object srcElement_;        // IE-only writable equivalent of target.
172     private EventTarget target_;       // W3C standard read-only equivalent of srcElement.
173     private Scriptable currentTarget_; // Changes during event capturing and bubbling.
174     private String type_ = "";         // The event type.
175     private int keyCode_;              // Key code for a keypress
176     private boolean shiftKey_;         // Exposed here in IE, only in mouse events in FF.
177     private boolean ctrlKey_;          // Exposed here in IE, only in mouse events in FF.
178     private boolean altKey_;           // Exposed here in IE, only in mouse events in FF.
179     private String propertyName_;
180     private boolean stopPropagation_;
181     private boolean stopImmediatePropagation_;
182     private Object returnValue_;
183     private boolean preventDefault_;
184 
185     /**
186      * The current event phase. This is a W3C standard attribute. One of {@link #NONE},
187      * {@link #CAPTURING_PHASE}, {@link #AT_TARGET} or {@link #BUBBLING_PHASE}.
188      */
189     private short eventPhase_;
190 
191     /**
192      * Whether or not the event bubbles. The value of this attribute depends on the event type. To
193      * determine if a certain event type bubbles, see http://www.w3.org/TR/DOM-Level-2-Events/events.html
194      * Most event types do bubble, so this is true by default; event types which do not bubble should
195      * overwrite this value in their constructors.
196      */
197     private boolean bubbles_ = true;
198 
199     /**
200      * Whether or not the event can be canceled. The value of this attribute depends on the event type. To
201      * determine if a certain event type can be canceled, see http://www.w3.org/TR/DOM-Level-2-Events/events.html
202      * The more common event types are cancelable, so this is true by default; event types which cannot be
203      * canceled should overwrite this value in their constructors.
204      */
205     private boolean cancelable_ = true;
206 
207     /**
208      * The time at which the event was created.
209      */
210     private final long timeStamp_ = System.currentTimeMillis();
211 
212     /**
213      * Creates a new event instance.
214      * @param domNode the DOM node that triggered the event
215      * @param type the event type
216      */
217     public Event(final DomNode domNode, final String type) {
218         this((EventTarget) domNode.getScriptableObject(), type);
219         setDomNode(domNode, false);
220     }
221 
222     /**
223      * Creates a new event instance.
224      * @param target the target
225      * @param type the event type
226      */
227     public Event(final EventTarget target, final String type) {
228         srcElement_ = target;
229         target_ = target;
230         currentTarget_ = target;
231         type_ = type;
232         setParentScope(target);
233         setPrototype(getPrototype(getClass()));
234 
235         if (TYPE_CHANGE.equals(type)) {
236             cancelable_ = false;
237         }
238         else if (TYPE_LOAD.equals(type)) {
239             bubbles_ = false;
240             if (getBrowserVersion().hasFeature(EVENT_ONLOAD_CANCELABLE_FALSE)) {
241                 cancelable_ = false;
242             }
243         }
244     }
245 
246     /**
247      * Creates a new Event with {@link #TYPE_PROPERTY_CHANGE} type.
248      * @param domNode the DOM node that triggered the event
249      * @param propertyName the property name that was changed
250      * @return the new Event object
251      */
252     public static Event createPropertyChangeEvent(final DomNode domNode, final String propertyName) {
253         final Event event = new Event(domNode, TYPE_PROPERTY_CHANGE);
254         event.propertyName_ = propertyName;
255         return event;
256     }
257 
258     /**
259      * Used to build the prototype.
260      */
261     public Event() {
262     }
263 
264     /**
265      * Called whenever an event is created using <code>Document.createEvent(..)</code>.
266      * This method is called after the parent scope was set so you are able to access the browser version.
267      */
268     public void eventCreated() {
269         setBubbles(false);
270         setCancelable(false);
271     }
272 
273     /**
274      * JavaScript constructor.
275      *
276      * @param type the event type
277      * @param details the event details (optional)
278      */
279     @JsxConstructor({CHROME, FF, EDGE})
280     public void jsConstructor(final String type, final ScriptableObject details) {
281         boolean bubbles = false;
282         boolean cancelable = false;
283 
284         if (details != null && !Undefined.instance.equals(details)) {
285             final Boolean detailBubbles = (Boolean) details.get("bubbles");
286             if (detailBubbles != null) {
287                 bubbles = detailBubbles.booleanValue();
288             }
289 
290             final Boolean detailCancelable = (Boolean) details.get("cancelable");
291             if (detailCancelable != null) {
292                 cancelable = detailCancelable.booleanValue();
293             }
294         }
295         initEvent(type, bubbles, cancelable);
296     }
297 
298     /**
299      * Called when the event starts being fired.
300      */
301     @SuppressWarnings("unchecked")
302     public void startFire() {
303         final Context context = Context.getCurrentContext();
304         LinkedList<Event> events = (LinkedList<Event>) context.getThreadLocal(KEY_CURRENT_EVENT);
305         if (events == null) {
306             events = new LinkedList<>();
307             context.putThreadLocal(KEY_CURRENT_EVENT, events);
308         }
309         events.add(this);
310     }
311 
312     /**
313      * Called when the event being fired ends.
314      */
315     @SuppressWarnings("unchecked")
316     public void endFire() {
317         ((LinkedList<Event>) Context.getCurrentContext().getThreadLocal(KEY_CURRENT_EVENT)).removeLast();
318     }
319 
320     /**
321      * Returns the object that fired the event.
322      * @return the object that fired the event
323      */
324     @JsxGetter({IE, CHROME, EDGE})
325     public Object getSrcElement() {
326         return srcElement_;
327     }
328 
329     /**
330      * Sets the object that fired the event.
331      * @param srcElement the object that fired the event
332      */
333     @JsxSetter(IE)
334     public void setSrcElement(final Object srcElement) {
335         srcElement_ = srcElement;
336     }
337 
338     /**
339      * Returns the event target to which the event was originally dispatched.
340      * @return the event target to which the event was originally dispatched
341      */
342     @JsxGetter
343     public Object getTarget() {
344         return target_;
345     }
346 
347     /**
348      * Sets the event target.
349      * @param target the event target
350      */
351     public void setTarget(final EventTarget target) {
352         target_ = target;
353     }
354 
355     /**
356      * Returns the event target whose event listeners are currently being processed. This
357      * is useful during event capturing and event bubbling.
358      * @return the current event target
359      */
360     @JsxGetter
361     public Scriptable getCurrentTarget() {
362         return currentTarget_;
363     }
364 
365     /**
366      * Sets the current target.
367      * @param target the new value
368      */
369     public void setCurrentTarget(final Scriptable target) {
370         currentTarget_ = target;
371     }
372 
373     /**
374      * Returns the event type.
375      * @return the event type
376      */
377     @JsxGetter
378     public String getType() {
379         return type_;
380     }
381 
382     /**
383      * Sets the event type.
384      * @param type the event type
385      */
386     @JsxSetter
387     public void setType(final String type) {
388         type_ = type;
389     }
390 
391     /**
392      * Sets the event type.
393      * @param eventType the event type
394      */
395     public void setEventType(final String eventType) {
396         type_ = eventType;
397     }
398 
399     /**
400      * Returns the time at which this event was created.
401      * @return the time at which this event was created
402      */
403     @JsxGetter
404     public long getTimeStamp() {
405         return timeStamp_;
406     }
407 
408     /**
409      * Sets the key code.
410      * @param keyCode the virtual key code value of the key which was depressed, otherwise zero
411      */
412     protected void setKeyCode(final int keyCode) {
413         keyCode_ = keyCode;
414     }
415 
416     /**
417      * Returns the key code associated with the event.
418      * @return the key code associated with the event
419      */
420     public int getKeyCode() {
421         return keyCode_;
422     }
423 
424     /**
425      * Returns whether {@code SHIFT} has been pressed during this event or not.
426      * @return whether {@code SHIFT} has been pressed during this event or not
427      */
428     public boolean isShiftKey() {
429         return shiftKey_;
430     }
431 
432     /**
433      * Sets whether {@code SHIFT} key is pressed on not.
434      * @param shiftKey whether {@code SHIFT} has been pressed during this event or not
435      */
436     protected void setShiftKey(final boolean shiftKey) {
437         shiftKey_ = shiftKey;
438     }
439 
440     /**
441      * Returns whether {@code CTRL} has been pressed during this event or not.
442      * @return whether {@code CTRL} has been pressed during this event or not
443      */
444     public boolean isCtrlKey() {
445         return ctrlKey_;
446     }
447 
448     /**
449      * Sets whether {@code CTRL} key is pressed on not.
450      * @param ctrlKey whether {@code CTRL} has been pressed during this event or not
451      */
452     protected void setCtrlKey(final boolean ctrlKey) {
453         ctrlKey_ = ctrlKey;
454     }
455 
456     /**
457      * Returns whether {@code ALT} has been pressed during this event or not.
458      * @return whether {@code ALT} has been pressed during this event or not
459      */
460     public boolean isAltKey() {
461         return altKey_;
462     }
463 
464     /**
465      * Sets whether {@code ALT} key is pressed on not.
466      * @param altKey whether {@code ALT} has been pressed during this event or not
467      */
468     protected void setAltKey(final boolean altKey) {
469         altKey_ = altKey;
470     }
471 
472     /**
473      * Returns the current event phase for the event.
474      * @return the current event phase for the event
475      */
476     @JsxGetter
477     public int getEventPhase() {
478         return eventPhase_;
479     }
480 
481     /**
482      * Sets the current event phase. Must be one of {@link #CAPTURING_PHASE}, {@link #AT_TARGET} or
483      * {@link #BUBBLING_PHASE}.
484      *
485      * @param phase the phase the event is in
486      */
487     public void setEventPhase(final short phase) {
488         if (phase != CAPTURING_PHASE && phase != AT_TARGET && phase != BUBBLING_PHASE) {
489             throw new IllegalArgumentException("Illegal phase specified: " + phase);
490         }
491         eventPhase_ = phase;
492     }
493 
494     /**
495      * @return whether or not this event bubbles
496      */
497     @JsxGetter
498     public boolean isBubbles() {
499         return bubbles_;
500     }
501 
502     /**
503      * @param bubbles the bubbles to set
504      */
505     protected void setBubbles(final boolean bubbles) {
506         bubbles_ = bubbles;
507     }
508 
509     /**
510      * @return whether or not this event can be canceled
511      */
512     @JsxGetter
513     public boolean isCancelable() {
514         return cancelable_;
515     }
516 
517     /**
518      * @param cancelable the cancelable to set
519      */
520     protected void setCancelable(final boolean cancelable) {
521         cancelable_ = cancelable;
522     }
523 
524     /**
525      * Returns {@code true} if both <tt>cancelable</tt> is {@code true} and <tt>preventDefault()</tt> has been
526      * called for this event. Otherwise this attribute must return {@code false}.
527      * @return {@code true} if this event has been cancelled or not
528      */
529     @JsxGetter({FF, IE, EDGE})
530     public boolean isDefaultPrevented() {
531         return cancelable_ && preventDefault_;
532     }
533 
534     /**
535      * @return indicates if event propagation is stopped
536      */
537     @JsxGetter(IE)
538     public boolean isCancelBubble() {
539         return stopPropagation_;
540     }
541 
542     /**
543      * @param newValue indicates if event propagation is stopped
544      */
545     @JsxSetter(IE)
546     public void setCancelBubble(final boolean newValue) {
547         stopPropagation_ = newValue;
548     }
549 
550     /**
551      * Stops the event from propagating.
552      */
553     @JsxFunction
554     public void stopPropagation() {
555         stopPropagation_ = true;
556     }
557 
558     /**
559      * Indicates if event propagation is stopped.
560      * @return the status
561      */
562     public boolean isPropagationStopped() {
563         return stopPropagation_;
564     }
565 
566     /**
567      * Prevents other listeners of the same event from being called.
568      */
569     @JsxFunction
570     public void stopImmediatePropagation() {
571         stopImmediatePropagation_ = true;
572         stopPropagation();
573     }
574 
575     /**
576      * Indicates if event immediate propagation is stopped.
577      * @return the status
578      */
579     public boolean isImmediatePropagationStopped() {
580         return stopImmediatePropagation_;
581     }
582 
583     /**
584      * Returns the return value associated with the event.
585      * @return the return value associated with the event
586      */
587     public Object getReturnValue() {
588         return returnValue_;
589     }
590 
591     /**
592      * Sets the return value associated with the event.
593      * @param returnValue the return value associated with the event
594      */
595     public void setReturnValue(final Object returnValue) {
596         returnValue_ = returnValue;
597     }
598 
599     /**
600      * Returns the property name associated with the event.
601      * @return the property name associated with the event
602      */
603     public String getPropertyName() {
604         return propertyName_;
605     }
606 
607     /**
608      * Initializes this event.
609      * @param type the event type
610      * @param bubbles whether or not the event should bubble
611      * @param cancelable whether or not the event the event should be cancelable
612      */
613     @JsxFunction
614     public void initEvent(final String type, final boolean bubbles, final boolean cancelable) {
615         type_ = type;
616         bubbles_ = bubbles;
617         cancelable_ = cancelable;
618         if (TYPE_BEFORE_UNLOAD.equals(type) && getBrowserVersion().hasFeature(EVENT_FOCUS_FOCUS_IN_BLUR_OUT)) {
619             try {
620                 final Class<?> klass = getClass();
621                 final Method readMethod = klass.getMethod("getReturnValue");
622                 final Method writeMethod = klass.getMethod("setReturnValue", Object.class);
623                 defineProperty("returnValue", null, readMethod, writeMethod, ScriptableObject.EMPTY);
624                 if ("Event".equals(klass.getSimpleName())) {
625                     setReturnValue(Boolean.TRUE);
626                 }
627             }
628             catch (final Exception e) {
629                 throw Context.throwAsScriptRuntimeEx(e);
630             }
631         }
632     }
633 
634     /**
635      * If, during any stage of event flow, this method is called the event is canceled.
636      * Any default action associated with the event will not occur.
637      * Calling this method for a non-cancelable event has no effect.
638      */
639     @JsxFunction
640     public void preventDefault() {
641         preventDefault_ = true;
642     }
643 
644     /**
645      * Returns {@code true} if this event has been aborted via <tt>preventDefault()</tt> in
646      * standards-compliant browsers, or via the event's <tt>returnValue</tt> property in IE, or
647      * by the event handler returning {@code false}.
648      *
649      * @param result the event handler result (if {@code false}, the event is considered aborted)
650      * @return {@code true} if this event has been aborted
651      */
652     public boolean isAborted(final ScriptResult result) {
653         return ScriptResult.isFalse(result) || preventDefault_;
654     }
655 
656     /**
657      * {@inheritDoc}
658      */
659     @Override
660     public String toString() {
661         final StringBuilder builder = new StringBuilder("Event ");
662         builder.append(getType());
663         builder.append(" (");
664         builder.append("Current Target: ");
665         builder.append(currentTarget_);
666         builder.append(");");
667         return builder.toString();
668     }
669 }