001    /*
002     * Copyright 2006 Google Inc.
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005     * use this file except in compliance with the License. You may obtain a copy of
006     * the License at
007     * 
008     * http://www.apache.org/licenses/LICENSE-2.0
009     * 
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013     * License for the specific language governing permissions and limitations under
014     * the License.
015     */
016    package com.google.gwt.user.client;
017    
018    import com.google.gwt.core.client.GWT;
019    import com.google.gwt.core.client.GWT.UncaughtExceptionHandler;
020    import com.google.gwt.user.client.impl.DOMImpl;
021    
022    import java.util.Vector;
023    
024    /**
025     * This class provides a set of static methods that allow you to manipulate the
026     * browser's Document Object Model (DOM). It contains methods for manipulating
027     * both {@link com.google.gwt.user.client.Element elements} and
028     * {@link com.google.gwt.user.client.Event events}.
029     */
030    public class DOM {
031    
032      private static DOMImpl impl;
033      private static Element sCaptureElem;
034    
035      private static Vector sEventPreviewStack = new Vector(); // <BrowserEventPreview>
036    
037      static {
038        impl = (DOMImpl) GWT.create(DOMImpl.class);
039        impl.init();
040      }
041    
042      /**
043       * Adds an event preview to the preview stack. As long as this preview remains
044       * on the top of the stack, it will receive all events before they are fired
045       * to their listeners. Note that the event preview will receive <u>all </u>
046       * events, including those received due to bubbling, whereas normal event
047       * handlers only receive explicitly sunk events.
048       * 
049       * @param preview the event preview to be added to the stack.
050       */
051      public static void addEventPreview(EventPreview preview) {
052        // Add the event preview to the stack. It will automatically
053        // begin receiving events.
054        sEventPreviewStack.add(preview);
055      }
056    
057      /**
058       * Appends one element to another's list of children.
059       * 
060       * @param parent the parent element
061       * @param child its new child
062       */
063      public static void appendChild(Element parent, Element child) {
064        impl.appendChild(parent, child);
065      }
066    
067      /**
068       * Compares two elements for equality (note that reference equality is not
069       * sufficient to determine equality among elements on most browsers).
070       * 
071       * @param elem1 the first element to be compared
072       * @param elem2 the second element to be compared
073       * @return <code>true</code> if they are in fact the same element
074       */
075      public static boolean compare(Element elem1, Element elem2) {
076        return impl.compare(elem1, elem2);
077      }
078    
079      /**
080       * Creates an HTML A element.
081       * 
082       * @return the newly-created element
083       */
084      public static Element createAnchor() {
085        return impl.createElement("A");
086      }
087    
088      /**
089       * Creates an HTML BUTTON element.
090       * 
091       * @return the newly-created element
092       */
093      public static Element createButton() {
094        return impl.createElement("button");
095      }
096    
097      /**
098       * Creates an HTML COL element.
099       * 
100       * @return the newly-created element
101       */
102      public static Element createCol() {
103        return impl.createElement("col");
104      }
105    
106      /**
107       * Creates an HTML DIV element.
108       * 
109       * @return the newly-created element
110       */
111      public static Element createDiv() {
112        return impl.createElement("div");
113      }
114    
115      /**
116       * Creates an HTML element.
117       * 
118       * @param tagName the HTML tag of the element to be created
119       * @return the newly-created element
120       */
121      public static Element createElement(String tagName) {
122        return impl.createElement(tagName);
123      }
124    
125      /**
126       * Creates an HTML FIELDSET element.
127       * 
128       * @return the newly-created element
129       */
130      public static Element createFieldSet() {
131        return impl.createElement("fieldset");
132      }
133    
134      /**
135       * Creates an HTML IFRAME element.
136       * 
137       * @return the newly-created element
138       */
139      public static Element createIFrame() {
140        return impl.createElement("iframe");
141      }
142    
143      /**
144       * Creates an HTML IMG element.
145       * 
146       * @return the newly-created element
147       */
148      public static Element createImg() {
149        return impl.createElement("img");
150      }
151    
152      /**
153       * Creates an HTML INPUT type='CHECK' element.
154       * 
155       * @return the newly-created element
156       */
157      public static Element createInputCheck() {
158        return impl.createInputElement("checkbox");
159      }
160    
161      /**
162       * Creates an HTML INPUT type='PASSWORD' element.
163       * 
164       * @return the newly-created element
165       */
166      public static Element createInputPassword() {
167        return impl.createInputElement("password");
168      }
169    
170      /**
171       * Creates an HTML INPUT type='RADIO' element.
172       * 
173       * @param group the name of the group with which this radio button will be
174       *          associated
175       * @return the newly-created element
176       */
177      public static Element createInputRadio(String group) {
178        return impl.createInputRadioElement(group);
179      }
180    
181      /**
182       * Creates an HTML INPUT type='TEXT' element.
183       * 
184       * @return the newly-created element
185       */
186      public static Element createInputText() {
187        return impl.createInputElement("text");
188      }
189    
190      /**
191       * Creates an HTML LABEL element.
192       * 
193       * @return the newly-created element
194       */
195      public static Element createLabel() {
196        return impl.createElement("label");
197      }
198    
199      /**
200       * Creates an HTML LEGEND element.
201       * 
202       * @return the newly-created element
203       */
204      public static Element createLegend() {
205        return impl.createElement("legend");
206      }
207    
208      /**
209       * Creates an HTML OPTIONS element.
210       * 
211       * @return the newly-created element
212       */
213      public static Element createOptions() {
214        return impl.createElement("options");
215      }
216    
217      /**
218       * Creates an HTML SELECT element.
219       * 
220       * @return the newly-created element
221       */
222      public static Element createSelect() {
223        return impl.createElement("select");
224      }
225    
226      /**
227       * Creates an HTML SPAN element.
228       * 
229       * @return the newly-created element
230       */
231      public static Element createSpan() {
232        return impl.createElement("span");
233      }
234    
235      /**
236       * Creates an HTML TABLE element.
237       * 
238       * @return the newly-created element
239       */
240      public static Element createTable() {
241        return impl.createElement("table");
242      }
243    
244      /**
245       * Creates an HTML BODY element.
246       * 
247       * @return the newly-created element
248       */
249      public static Element createTBody() {
250        return impl.createElement("tbody");
251      }
252    
253      /**
254       * Creates an HTML TD element.
255       * 
256       * @return the newly-created element
257       */
258      public static Element createTD() {
259        return impl.createElement("td");
260      }
261    
262      /**
263       * Creates an HTML TEXTAREA element.
264       * 
265       * @return the newly-created element
266       */
267      public static Element createTextArea() {
268        return impl.createElement("textarea");
269      }
270    
271      /**
272       * Creates an HTML THEAD element.
273       * 
274       * @return the newly-created element
275       */
276      public static Element createTH() {
277        return impl.createElement("th");
278      }
279    
280      /**
281       * Creates an HTML TR element.
282       * 
283       * @return the newly-created element
284       */
285      public static Element createTR() {
286        return impl.createElement("tr");
287      }
288    
289      /**
290       * Cancels bubbling for the given event. This will stop the event from being
291       * propagated to parent elements.
292       * 
293       * @param evt the event on which to cancel bubbling
294       * @param cancel <code>true</code> to cancel bubbling
295       */
296      public static void eventCancelBubble(Event evt, boolean cancel) {
297        impl.eventCancelBubble(evt, cancel);
298      }
299    
300      /**
301       * Gets whether the ALT key was depressed when the given event occurred.
302       * 
303       * @param evt the event to be tested
304       * @return <code>true</code> if ALT was depressed when the event occurred
305       */
306      public static boolean eventGetAltKey(Event evt) {
307        return impl.eventGetAltKey(evt);
308      }
309    
310      /**
311       * Gets the mouse buttons that were depressed when the given event occurred.
312       * 
313       * @param evt the event to be tested
314       * @return a bit-field, defined by {@link Event#BUTTON_LEFT},
315       *         {@link Event#BUTTON_MIDDLE}, and {@link Event#BUTTON_RIGHT}
316       */
317      public static int eventGetButton(Event evt) {
318        return impl.eventGetButton(evt);
319      }
320    
321      /**
322       * Gets the mouse x-position within the browser window's client area.
323       * 
324       * @param evt the event to be tested
325       * @return the mouse x-position
326       */
327      public static int eventGetClientX(Event evt) {
328        return impl.eventGetClientX(evt);
329      }
330    
331      /**
332       * Gets the mouse y-position within the browser window's client area.
333       * 
334       * @param evt the event to be tested
335       * @return the mouse y-position
336       */
337      public static int eventGetClientY(Event evt) {
338        return impl.eventGetClientY(evt);
339      }
340    
341      /**
342       * Gets whether the CTRL key was depressed when the given event occurred.
343       * 
344       * @param evt the event to be tested
345       * @return <code>true</code> if CTRL was depressed when the event occurred
346       */
347      public static boolean eventGetCtrlKey(Event evt) {
348        return impl.eventGetCtrlKey(evt);
349      }
350    
351      /**
352       * Gets the element from which the mouse pointer was moved (only valid for
353       * {@link Event#ONMOUSEOVER}).
354       * 
355       * @param evt the event to be tested
356       * @return the element from which the mouse pointer was moved
357       */
358      public static Element eventGetFromElement(Event evt) {
359        return impl.eventGetFromElement(evt);
360      }
361    
362      /**
363       * Gets the key code associated with this event.
364       * 
365       * <p>
366       * For {@link Event#ONKEYPRESS}, this method returns the Unicode value of the
367       * character generated. For {@link Event#ONKEYDOWN} and {@link Event#ONKEYUP},
368       * it returns the code associated with the physical key.
369       * </p>
370       * 
371       * @param evt the event to be tested
372       * @return the Unicode character or key code.
373       * @see com.google.gwt.user.client.ui.KeyboardListener
374       */
375      public static char eventGetKeyCode(Event evt) {
376        return impl.eventGetKeyCode(evt);
377      }
378    
379      /**
380       * Gets the key-repeat state of this event.
381       * 
382       * @param evt the event to be tested
383       * @return <code>true</code> if this key event was an auto-repeat
384       */
385      public static boolean eventGetRepeat(Event evt) {
386        return impl.eventGetRepeat(evt);
387      }
388    
389      /**
390       * Gets the mouse x-position on the user's display.
391       * 
392       * @param evt the event to be tested
393       * @return the mouse x-position
394       */
395      public static int eventGetScreenX(Event evt) {
396        return impl.eventGetScreenX(evt);
397      }
398    
399      /**
400       * Gets the mouse y-position on the user's display.
401       * 
402       * @param evt the event to be tested
403       * @return the mouse y-position
404       */
405      public static int eventGetScreenY(Event evt) {
406        return impl.eventGetScreenY(evt);
407      }
408    
409      /**
410       * Gets whether the shift key was depressed when the given event occurred.
411       * 
412       * @param evt the event to be tested
413       * @return <code>true</code> if shift was depressed when the event occurred
414       */
415      public static boolean eventGetShiftKey(Event evt) {
416        return impl.eventGetShiftKey(evt);
417      }
418    
419      /**
420       * Returns the element that was the actual target of the given event.
421       * 
422       * @param evt the event to be tested
423       * @return the target element
424       */
425      public static Element eventGetTarget(Event evt) {
426        return impl.eventGetTarget(evt);
427      }
428    
429      /**
430       * Gets the element to which the mouse pointer was moved (only valid for
431       * {@link Event#ONMOUSEOUT}).
432       * 
433       * @param evt the event to be tested
434       * @return the element to which the mouse pointer was moved
435       */
436      public static Element eventGetToElement(Event evt) {
437        return impl.eventGetToElement(evt);
438      }
439    
440      /**
441       * Gets the enumerated type of this event (as defined in {@link Event}).
442       * 
443       * @param evt the event to be tested
444       * @return the event's enumerated type
445       */
446      public static int eventGetType(Event evt) {
447        return impl.eventGetTypeInt(evt);
448      }
449    
450      /**
451       * Gets the type of the given event as a string.
452       * 
453       * @param evt the event to be tested
454       * @return the event's type name
455       */
456      public static String eventGetTypeString(Event evt) {
457        return impl.eventGetType(evt);
458      }
459    
460      /**
461       * Prevents the browser from taking its default action for the given event.
462       * 
463       * @param evt the event whose default action is to be prevented
464       */
465      public static void eventPreventDefault(Event evt) {
466        impl.eventPreventDefault(evt);
467      }
468    
469      /**
470       * Sets the key code associated with the given keyboard event.
471       * 
472       * @param evt the event whose key code is to be set
473       * @param key the new key code
474       */
475      public static void eventSetKeyCode(Event evt, char key) {
476        impl.eventSetKeyCode(evt, key);
477      }
478    
479      /**
480       * Returns a stringized version of the event. This string is for debugging
481       * purposes and will NOT be consistent on different browsers.
482       * 
483       * @param evt the event to stringize
484       * @return a string form of the event
485       */
486      public static String eventToString(Event evt) {
487        return impl.eventToString(evt);
488      }
489    
490      /**
491       * Gets an element's absolute left coordinate in the document's coordinate
492       * system.
493       * 
494       * @param elem the element to be measured
495       * @return the element's absolute left coordinate
496       */
497      public static int getAbsoluteLeft(Element elem) {
498        return impl.getAbsoluteLeft(elem);
499      }
500    
501      /**
502       * Gets an element's absolute top coordinate in the document's coordinate
503       * system.
504       * 
505       * @param elem the element to be measured
506       * @return the element's absolute top coordinate
507       */
508      public static int getAbsoluteTop(Element elem) {
509        return impl.getAbsoluteTop(elem);
510      }
511    
512      /**
513       * Gets any named attribute from an element, as a string.
514       * 
515       * @param elem the element whose attribute is to be retrieved
516       * @param attr the name of the attribute
517       * @return the attribute's value
518       */
519      public static String getAttribute(Element elem, String attr) {
520        return impl.getAttribute(elem, attr);
521      }
522    
523      /**
524       * Gets the element that currently has mouse capture.
525       * 
526       * @return a handle to the capture element, or <code>null</code> if none
527       *         exists
528       */
529      public static Element getCaptureElement() {
530        return sCaptureElem;
531      }
532    
533      /**
534       * Gets an element's n-th child element
535       * 
536       * @param parent the element whose child is to be retrieved
537       * @param index the index of the child element
538       * @return the n-th child element
539       */
540      public static Element getChild(Element parent, int index) {
541        return impl.getChild(parent, index);
542      }
543    
544      /**
545       * Gets the number of child elements present in a given parent element.
546       * 
547       * @param parent the element whose children are to be counted
548       * @return the number of children
549       */
550      public static int getChildCount(Element parent) {
551        return impl.getChildCount(parent);
552      }
553    
554      /**
555       * Gets the index of a given child element within its parent.
556       * 
557       * @param parent the parent element
558       * @param child the child element
559       * @return the child's index within its parent, or <code>-1</code> if it is
560       *         not a child of the given parent
561       */
562      public static int getChildIndex(Element parent, Element child) {
563        return impl.getChildIndex(parent, child);
564      }
565    
566      /**
567       * Gets the element associated with the given unique id within the entire
568       * document.
569       * 
570       * @param id the id whose associated element is to be retrieved
571       * @return the associated element, or <code>null</code> if none is found
572       */
573      public static Element getElementById(String id) {
574        return impl.getElementById(id);
575      }
576    
577      /**
578       * Gets the current set of events sunk by a given element.
579       * 
580       * @param elem the element whose events are to be retrieved
581       * @return a bitfield describing the events sunk on this element (its possible
582       *         values are described in {@link Event})
583       */
584      public static int getEventsSunk(Element elem) {
585        return impl.getEventsSunk(elem);
586      }
587    
588      /**
589       * Gets the first child element of the given element.
590       * 
591       * @param elem the element whose child is to be retrieved
592       * @return the child element
593       */
594      public static Element getFirstChild(Element elem) {
595        return impl.getFirstChild(elem);
596      }
597    
598      /**
599       * Gets an HTML representation of an element's children.
600       * 
601       * @param elem the element whose HTML is to be retrieved
602       * @return the HTML representation of the element's children
603       */
604      public static String getInnerHTML(Element elem) {
605        return impl.getInnerHTML(elem);
606      }
607    
608      /**
609       * Gets the text contained within an element. If the element has child
610       * elements, only the text between them will be retrieved.
611       * 
612       * @param elem the element whose inner text is to be retrieved
613       * @return the text inside this element
614       */
615      public static String getInnerText(Element elem) {
616        return impl.getInnerText(elem);
617      }
618    
619      /**
620       * Gets an integer attribute on a given element.
621       * 
622       * @param elem the element whose attribute is to be retrieved
623       * @param attr the name of the attribute to be retrieved
624       * @return the attribute's value as an integer
625       */
626      public static int getIntAttribute(Element elem, String attr) {
627        return impl.getIntAttribute(elem, attr);
628      }
629    
630      /**
631       * Gets an integer attribute on a given element's style.
632       * 
633       * @param elem the element whose style attribute is to be retrieved
634       * @param attr the name of the attribute to be retrieved
635       * @return the style attribute's value as an integer
636       */
637      public static int getIntStyleAttribute(Element elem, String attr) {
638        return impl.getIntStyleAttribute(elem, attr);
639      }
640    
641      /**
642       * Gets an element's next sibling element.
643       * 
644       * @param elem the element whose sibling is to be retrieved
645       * @return the sibling element
646       */
647      public static Element getNextSibling(Element elem) {
648        return impl.getNextSibling(elem);
649      }
650    
651      /**
652       * Gets an element's parent element.
653       * 
654       * @param elem the element whose parent is to be retrieved
655       * @return the parent element
656       */
657      public static Element getParent(Element elem) {
658        return impl.getParent(elem);
659      }
660    
661      /**
662       * Gets an attribute of the given element's style.
663       * 
664       * @param elem the element whose style attribute is to be retrieved
665       * @param attr the name of the style attribute to be retrieved
666       * @return the style attribute's value
667       */
668      public static String getStyleAttribute(Element elem, String attr) {
669        return impl.getStyleAttribute(elem, attr);
670      }
671    
672      /**
673       * Inserts an element as a child of the given parent element.
674       * 
675       * @param parent the parent element
676       * @param child the child element
677       * @param index the index before which the child will be inserted (any value
678       *          greater than the number of existing children will cause the child
679       *          to be appended)
680       */
681      public static void insertChild(Element parent, Element child, int index) {
682        impl.insertChild(parent, child, index);
683      }
684    
685      /**
686       * Determine whether one element is equal to, or the child of, another.
687       * 
688       * @param parent the potential parent element
689       * @param child the potential child element
690       * @return <code>true</code> if the relationship holds
691       */
692      public static boolean isOrHasChild(Element parent, Element child) {
693        return impl.isOrHasChild(parent, child);
694      }
695    
696      /**
697       * Releases mouse capture on the given element. Calling this method has no
698       * effect if the element does not currently have mouse capture.
699       * 
700       * @param elem the element to release capture
701       * @see #setCapture(Element)
702       */
703      public static void releaseCapture(Element elem) {
704        if ((sCaptureElem != null) && compare(elem, sCaptureElem))
705          sCaptureElem = null;
706        impl.releaseCapture(elem);
707      }
708    
709      /**
710       * Removes a child element from the given parent element.
711       * 
712       * @param parent the parent element
713       * @param child the child element to be removed
714       */
715      public static void removeChild(Element parent, Element child) {
716        impl.removeChild(parent, child);
717      }
718    
719      /**
720       * Removes an element from the preview stack. This element will no longer
721       * capture events, though any preview underneath it will begin to do so.
722       * 
723       * @param preview the event preview to be removed from the stack
724       */
725      public static void removeEventPreview(EventPreview preview) {
726        // Remove the event preview from the stack. If it was on top,
727        // any preview underneath it will automatically begin to
728        // receive events.
729        sEventPreviewStack.remove(preview);
730      }
731    
732      /**
733       * Sets an attribute on the given element.
734       * 
735       * @param elem the element whose attribute is to be set
736       * @param attr the name of the attribute to be set
737       * @param value the new attribute value
738       */
739      public static void setAttribute(Element elem, String attr, String value) {
740        impl.setAttribute(elem, attr, value);
741      }
742    
743      /**
744       * Sets mouse-capture on the given element. This element will directly receive
745       * all mouse events until {@link #releaseCapture(Element)} is called on it.
746       * 
747       * @param elem the element on which to set mouse capture
748       */
749      public static void setCapture(Element elem) {
750        sCaptureElem = elem;
751        impl.setCapture(elem);
752      }
753    
754      /**
755       * Sets the {@link EventListener} to receive events for the given element.
756       * Only one such listener may exist for a single element.
757       * 
758       * @param elem the element whose listener is to be set
759       * @param listener the listener to receive {@link Event events}
760       */
761      public static void setEventListener(Element elem, EventListener listener) {
762        impl.setEventListener(elem, listener);
763      }
764    
765      /**
766       * Sets the HTML contained within an element.
767       * 
768       * @param elem the element whose inner HTML is to be set
769       * @param html the new html
770       */
771      public static void setInnerHTML(Element elem, String html) {
772        impl.setInnerHTML(elem, html);
773      }
774    
775      /**
776       * Sets the text contained within an element. If the element already has
777       * children, they will be destroyed.
778       * 
779       * @param elem the element whose inner text is to be set
780       * @param text the new text
781       */
782      public static void setInnerText(Element elem, String text) {
783        impl.setInnerText(elem, text);
784      }
785    
786      /**
787       * Sets an integer attribute on the given element.
788       * 
789       * @param elem the element whose attribute is to be set
790       * @param attr the name of the attribute to be set
791       * @param value the attribute's new integer value
792       */
793      public static void setIntAttribute(Element elem, String attr, int value) {
794        setAttribute(elem, attr, Integer.toString(value));
795      }
796    
797      /**
798       * Sets an integer attribute on the given element's style.
799       * 
800       * @param elem the element whose style attribute is to be set
801       * @param attr the name of the style attribute to be set
802       * @param value the style attribute's new integer value
803       */
804      public static void setIntStyleAttribute(Element elem, String attr, int value) {
805        setStyleAttribute(elem, attr, Integer.toString(value));
806      }
807    
808      /**
809       * Sets an attribute on the given element's style.
810       * 
811       * @param elem the element whose style attribute is to be set
812       * @param attr the name of the style attribute to be set
813       * @param value the style attribute's new value
814       */
815      public static void setStyleAttribute(Element elem, String attr, String value) {
816        impl.setStyleAttribute(elem, attr, value);
817      }
818    
819      /**
820       * Sets the current set of events sunk by a given element. These events will
821       * be fired to the nearest {@link EventListener} specified on any of the
822       * element's parents.
823       * 
824       * @param elem the element whose events are to be retrieved
825       * @param eventBits a bitfield describing the events sunk on this element (its
826       *          possible values are described in {@link Event})
827       */
828      public static void sinkEvents(Element elem, int eventBits) {
829        impl.sinkEvents(elem, eventBits);
830      }
831    
832      /**
833       * Returns a stringized version of the element. This string is for debugging
834       * purposes and will NOT be consistent on different browsers.
835       * 
836       * @param elem the element to stringize
837       * @return a string form of the element
838       */
839      public static String toString(Element elem) {
840        return impl.toString(elem);
841      }
842    
843      /**
844       * This method is called directly by native code when any event is fired.
845       * 
846       * @param hEvent the handle to the event being fired.
847       * @param hElem the handle to the element that received the event.
848       * @param listener the listener associated with the element that received the
849       *          event.
850       */
851      static void dispatchEvent(Event evt, Element elem, EventListener listener) {
852        UncaughtExceptionHandler handler = GWT.getUncaughtExceptionHandler();
853        if (handler != null)
854          dispatchEventAndCatch(evt, elem, listener, handler);
855        else
856          dispatchEventImpl(evt, elem, listener);
857      }
858    
859      /**
860       * This method is called directly by native code when event preview is being
861       * used.
862       * 
863       * @param event a handle to the event being previewed
864       * @return <code>false</code> to cancel the event
865       */
866      static boolean previewEvent(Event evt) {
867        // If event previews are present, redirect events to the topmost of them.
868        boolean ret = true;
869        if (sEventPreviewStack.size() > 0) {
870          EventPreview preview = (EventPreview) sEventPreviewStack
871            .get(sEventPreviewStack.size() - 1);
872          if (!(ret = preview.onEventPreview(evt))) {
873            // If the preview cancels the event, stop it from bubbling and
874            // performing its default action.
875            eventCancelBubble(evt, true);
876            eventPreventDefault(evt);
877          }
878        }
879    
880        return ret;
881      }
882    
883      private static void dispatchEventAndCatch(Event evt, Element elem,
884          EventListener listener, UncaughtExceptionHandler handler) {
885        try {
886          dispatchEventImpl(evt, elem, listener);
887        } catch (Throwable e) {
888          handler.onUncaughtException(e);
889        }
890      }
891    
892      private static void dispatchEventImpl(Event evt, Element elem,
893          EventListener listener) {
894        // If this element has capture...
895        if (elem == sCaptureElem) {
896          // ... and it's losing capture, clear sCaptureElem.
897          if (eventGetType(evt) == Event.ONLOSECAPTURE)
898            sCaptureElem = null;
899        }
900    
901        // Pass the event to the listener.
902        listener.onBrowserEvent(evt);
903      }
904    }