blob: f3c36b3a8e6db6bfcec4a33202fc5df6a45aa3c6 [file] [log] [blame]
/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.gwt.user.client.impl;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.impl.Disposable;
import com.google.gwt.core.client.impl.Impl;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.EventListener;
/**
* Native implementation associated with {@link com.google.gwt.user.client.DOM}.
*/
public abstract class DOMImpl {
protected static boolean eventSystemIsInitialized;
/**
* Registers a raw DOM event listener to be cleaned up when the module is unloaded.
*/
public static native void addDisposableEvent(com.google.gwt.dom.client.Element elem, String event,
JavaScriptObject handler, boolean capture) /*-{
elem.__gwt_disposeEvent = elem.__gwt_disposeEvent || [];
elem.__gwt_disposeEvent.push({event: event, handler: handler, capture: capture});
}-*/;
/**
* Scan all DOM elements looking for event listeners from our module and remove event listeners from them.
* @param dom
*/
public static void cleanupDOM(DOMImpl dom) {
NodeList<com.google.gwt.dom.client.Element> allElements = Document.get().getElementsByTagName("*");
for (int i = 0; i < allElements.getLength(); i++) {
com.google.gwt.dom.client.Element elem = allElements.getItem(i);
Element userElem = (Element) elem;
EventListener listener = getEventListener(userElem);
if (GWT.isScript() && listener != null) {
dom.sinkEvents(userElem, 0);
setEventListener(userElem, null);
}
// cleans up DOM-style addEventListener registered handlers
maybeRemoveDisposableEvent(elem);
}
}
public static native EventListener getEventListener(Element elem) /*-{
// Return elem.__listener if and only if it was assigned from our module
var maybeListener = elem.__listener;
return @com.google.gwt.user.client.impl.DOMImpl::isMyListener(*)(maybeListener) ? maybeListener : null;
}-*/;
public static native void setEventListener(Element elem, EventListener listener) /*-{
elem.__listener = listener;
}-*/;
/**
* Returns <code>true</code>if the object is an instance of EventListener and
* the object belongs to this module.
* <p>
* Note that this method should only be called from JSNI, otherwise it can be inlined and compiler
* can remove instanceOf checks. E.g.
* <pre>
* EventListener listener = getEventListenerFromSomeJsniCode();
* if (isMyListener(listener)) {
* // This block will always be executed because the compiler proves that the instance of checks
* // are not required after inlining isMyListener.
* }
* </pre>
*/
private static boolean isMyListener(Object object) {
/*
* The first test ensures the Object belongs to this module in Production
* Mode by ensuring this is not a JavaScriptObject. In Production Mode,
* foreign Java objects appear to be JavaScriptObject. See
* Cast.isJavaScriptObject().
*
* The second test then checks the exact type.
*
* TODO: make the generated code smaller!
*/
return !(object instanceof JavaScriptObject)
&& (object instanceof com.google.gwt.user.client.EventListener);
}
private static native void maybeRemoveDisposableEvent(com.google.gwt.dom.client.Element elem) /*-{
var diEvents = elem.__gwt_disposeEvent;
if (diEvents) {
for (var i = 0, l = diEvents.length; i < l; i++) {
var diEvent = diEvents[i];
elem.removeEventListener(diEvent.event, diEvent.handler, diEvent.capture);
elem.__gwt_disposeEvent = null;
}
}
}-*/;
public native void eventCancelBubble(Event evt, boolean cancel) /*-{
evt.cancelBubble = cancel;
}-*/;
public abstract Element eventGetFromElement(Event evt);
public native boolean eventGetRepeat(Event evt) /*-{
return !!evt.repeat;
}-*/;
public abstract Element eventGetToElement(Event evt);
public final int eventGetTypeInt(Event evt) {
return eventGetTypeInt(evt.getType());
}
public native int eventGetTypeInt(String eventType) /*-{
switch (eventType) {
case "blur": return 0x01000;
case "change": return 0x00400;
case "click": return 0x00001;
case "dblclick": return 0x00002;
case "focus": return 0x00800;
case "keydown": return 0x00080;
case "keypress": return 0x00100;
case "keyup": return 0x00200;
case "load": return 0x08000;
case "losecapture": return 0x02000;
case "mousedown": return 0x00004;
case "mousemove": return 0x00040;
case "mouseout": return 0x00020;
case "mouseover": return 0x00010;
case "mouseup": return 0x00008;
case "scroll": return 0x04000;
case "error": return 0x10000;
case "mousewheel": return 0x20000;
case "DOMMouseScroll": return 0x20000;
case "contextmenu": return 0x40000;
case "paste": return 0x80000;
case "touchstart": return 0x100000;
case "touchmove": return 0x200000;
case "touchend": return 0x400000;
case "touchcancel": return 0x800000;
case "gesturestart": return 0x1000000;
case "gesturechange": return 0x2000000;
case "gestureend": return 0x4000000;
default: return -1;
}
}-*/;
public native void eventSetKeyCode(Event evt, char key) /*-{
evt.keyCode = key;
}-*/;
public abstract Element getChild(Element elem, int index);
public abstract int getChildCount(Element elem);
public abstract int getChildIndex(Element parent, Element child);
public native int getEventsSunk(Element elem) /*-{
return elem.__eventBits || 0;
}-*/;
public abstract void insertChild(Element parent, Element child, int index);
public void maybeInitializeEventSystem() {
if (!eventSystemIsInitialized) {
initEventSystem();
Impl.scheduleDispose(new Disposable() {
@Override
public void dispose() {
disposeEventSystem();
cleanupDOM(DOMImpl.this);
}
});
eventSystemIsInitialized = true;
}
}
public abstract void releaseCapture(Element elem);
public abstract void setCapture(Element elem);
public abstract void sinkBitlessEvent(Element elem, String eventTypeName);
public abstract void sinkEvents(Element elem, int eventBits);
protected abstract void disposeEventSystem();
/**
* Initializes the event dispatch system.
*/
protected abstract void initEventSystem();
}