| /* |
| * 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.dom.client; |
| |
| /** |
| * Mozilla implementation of StandardBrowser. |
| */ |
| class DOMImplMozilla extends DOMImplStandard { |
| |
| private static int cachedGeckoVersion = -2; |
| |
| private static int getGeckoVersion() { |
| if (cachedGeckoVersion == -2) { |
| cachedGeckoVersion = getNativeGeckoVersion(); |
| } |
| return cachedGeckoVersion; |
| } |
| |
| private static native int getNativeGeckoVersion() /*-{ |
| var result = /rv:([0-9]+)\.([0-9]+)(\.([0-9]+))?.*?/.exec(navigator.userAgent.toLowerCase()); |
| if (result && result.length >= 3) { |
| var version = (parseInt(result[1]) * 1000000) + (parseInt(result[2]) * 1000) + |
| parseInt(result.length >= 5 && !isNaN(result[4]) ? result[4] : 0); |
| return version; |
| } |
| return -1; // not gecko |
| }-*/; |
| |
| /** |
| * Return true if using Gecko 1.9 (Firefox 3) or later. |
| * |
| * @return true if using Gecko 1.9 (Firefox 3) or later |
| */ |
| private static boolean isGecko19() { |
| int geckoVersion = getGeckoVersion(); |
| return (geckoVersion != -1) && (geckoVersion >= 1009000); |
| } |
| |
| /** |
| * Return true if using Gecko 1.9.0 (Firefox 3) or earlier. |
| * |
| * @return true if using Gecko 1.9.0 (Firefox 3) or earlier |
| */ |
| private static boolean isGecko190OrBefore() { |
| int geckoVersion = getGeckoVersion(); |
| return (geckoVersion != -1) && (geckoVersion <= 1009000); |
| } |
| |
| /** |
| * Return true if using Gecko 1.9.1 (Firefox 3.5) or earlier. |
| * |
| * @return true if using Gecko 1.9.1 (Firefox 3.5) or earlier |
| */ |
| private static boolean isGecko191OrBefore() { |
| int geckoVersion = getGeckoVersion(); |
| return (geckoVersion != -1) && (geckoVersion <= 1009001); |
| } |
| |
| /** |
| * Return true if using Gecko 1.9.2 (Firefox 3.6) or earlier. |
| * |
| * @return true if using Gecko 1.9.2 (Firefox 3.6) or earlier |
| */ |
| private static boolean isGecko192OrBefore() { |
| int geckoVersion = getGeckoVersion(); |
| return (geckoVersion != -1) && (geckoVersion <= 1009002); |
| } |
| |
| /** |
| * Return true if using Gecko 2.0.0 (Firefox 4.0) or earlier. |
| * |
| * @return true if using Gecko 2.0.0 (Firefox 4.0) or earlier |
| */ |
| private static boolean isGecko2OrBefore() { |
| int geckoVersion = getGeckoVersion(); |
| return (geckoVersion != -1) && (geckoVersion < 2000000); |
| } |
| |
| @Override |
| public native void buttonClick(ButtonElement button) /*-{ |
| var doc = button.ownerDocument; |
| if (doc != null) { |
| var evt = doc.createEvent('MouseEvents'); |
| evt.initMouseEvent('click', true, true, null, 0, 0, |
| 0, 0, 0, false, false, false, false, 0, null); |
| button.dispatchEvent(evt); |
| } |
| }-*/; |
| |
| @Override |
| public NativeEvent createKeyCodeEvent(Document doc, String type, |
| boolean ctrlKey, boolean altKey, boolean shiftKey, boolean metaKey, |
| int keyCode) { |
| return createKeyEventImpl(doc, type, true, true, ctrlKey, altKey, shiftKey, |
| metaKey, keyCode, 0); |
| } |
| |
| @Override |
| @Deprecated |
| public NativeEvent createKeyEvent(Document doc, String type, |
| boolean canBubble, boolean cancelable, boolean ctrlKey, boolean altKey, |
| boolean shiftKey, boolean metaKey, int keyCode, int charCode) { |
| return createKeyEventImpl(doc, type, canBubble, cancelable, ctrlKey, |
| altKey, shiftKey, metaKey, keyCode, charCode); |
| } |
| |
| @Override |
| public NativeEvent createKeyPressEvent(Document doc, boolean ctrlKey, |
| boolean altKey, boolean shiftKey, boolean metaKey, int charCode) { |
| return createKeyEventImpl(doc, "keypress", true, true, ctrlKey, altKey, |
| shiftKey, metaKey, 0, charCode); |
| } |
| |
| @Override |
| public native int eventGetMouseWheelVelocityY(NativeEvent evt) /*-{ |
| return evt.detail || 0; |
| }-*/; |
| |
| @Override |
| public native EventTarget eventGetRelatedTarget(NativeEvent evt) /*-{ |
| // Hack around Mozilla bug 497780 (relatedTarget sometimes returns XUL |
| // elements). Trying to access relatedTarget.nodeName will throw an |
| // exception if it's a XUL element. |
| var relatedTarget = evt.relatedTarget; |
| if (!relatedTarget) { |
| return null; |
| } |
| try { |
| var nodeName = relatedTarget.nodeName; |
| return relatedTarget; |
| } catch (e) { |
| return null; |
| } |
| }-*/; |
| |
| @Override |
| public int getAbsoluteLeft(Element elem) { |
| return getAbsoluteLeftImpl(elem.getOwnerDocument().getViewportElement(), |
| elem); |
| } |
| |
| @Override |
| public int getAbsoluteTop(Element elem) { |
| return getAbsoluteTopImpl(elem.getOwnerDocument().getViewportElement(), |
| elem); |
| } |
| |
| @Override |
| public native int getBodyOffsetLeft(Document doc) /*-{ |
| var style = $wnd.getComputedStyle(doc.documentElement, null); |
| if (style == null) { |
| // Works around https://bugzilla.mozilla.org/show_bug.cgi?id=548397 |
| return 0; |
| } |
| return parseInt(style.marginLeft, 10) + parseInt(style.borderLeftWidth, 10); |
| }-*/; |
| |
| @Override |
| public native int getBodyOffsetTop(Document doc) /*-{ |
| var style = $wnd.getComputedStyle(doc.documentElement, null); |
| if (style == null) { |
| // Works around https://bugzilla.mozilla.org/show_bug.cgi?id=548397 |
| return 0; |
| } |
| return parseInt(style.marginTop, 10) + parseInt(style.borderTopWidth, 10); |
| }-*/; |
| |
| @Override |
| public native int getNodeType(Node node) /*-{ |
| try { |
| return node.nodeType; |
| } catch (e) { |
| // Give up on 'Permission denied to get property HTMLDivElement.nodeType' |
| // '0' is not a valid node type, which is appropriate in this case, since |
| // the node in question is completely inaccessible. |
| // |
| // See https://bugzilla.mozilla.org/show_bug.cgi?id=208427 |
| // and http://code.google.com/p/google-web-toolkit/issues/detail?id=1909 |
| return 0; |
| } |
| }-*/; |
| |
| @Override |
| public int getScrollLeft(Element elem) { |
| if (!isGecko19() && isRTL(elem)) { |
| return super.getScrollLeft(elem) |
| - (elem.getScrollWidth() - elem.getClientWidth()); |
| } |
| return super.getScrollLeft(elem); |
| } |
| |
| @Override |
| public native boolean isOrHasChild(Node parent, Node child) /*-{ |
| // For more information about compareDocumentPosition, see: |
| // http://www.quirksmode.org/blog/archives/2006/01/contains_for_mo.html |
| return (parent === child) || !!(parent.compareDocumentPosition(child) & 16); |
| }-*/; |
| |
| @Override |
| public void setScrollLeft(Element elem, int left) { |
| if (!isGecko19() && isRTL(elem)) { |
| left += elem.getScrollWidth() - elem.getClientWidth(); |
| } |
| super.setScrollLeft(elem, left); |
| } |
| |
| @Override |
| public native String toString(Element elem) /*-{ |
| // Basic idea is to use the innerHTML property by copying the node into a |
| // div and getting the innerHTML |
| var doc = elem.ownerDocument; |
| var temp = elem.cloneNode(true); |
| var tempDiv = doc.createElement("DIV"); |
| tempDiv.appendChild(temp); |
| outer = tempDiv.innerHTML; |
| temp.innerHTML = ""; |
| return outer; |
| }-*/; |
| |
| private native NativeEvent createKeyEventImpl(Document doc, String type, |
| boolean canBubble, boolean cancelable, boolean ctrlKey, boolean altKey, |
| boolean shiftKey, boolean metaKey, int keyCode, int charCode) /*-{ |
| var evt = doc.createEvent('KeyboardEvent'); |
| if (evt.initKeyEvent) { |
| // Gecko |
| evt.initKeyEvent(type, canBubble, cancelable, null, ctrlKey, altKey, |
| shiftKey, metaKey, keyCode, charCode); |
| } else { |
| // This happens to be IE11+ as of today |
| if ($wnd.console) { |
| $wnd.console.error("Synthetic keyboard events are not supported in this browser"); |
| } |
| } |
| return evt; |
| }-*/; |
| |
| private native int getAbsoluteLeftImpl(Element viewport, Element elem) /*-{ |
| // Firefox 3 is actively throwing errors when getBoxObjectFor() is called, |
| // so we use getBoundingClientRect() whenever possible (but it's not |
| // supported on older versions). If changing this code, make sure to check |
| // the museum entry for issue 1932. |
| // (x) | 0 is used to coerce the value to an integer |
| if (Element.prototype.getBoundingClientRect) { |
| return (elem.getBoundingClientRect().left + viewport.scrollLeft) | 0; |
| } else { |
| // We cannot use DOMImpl here because offsetLeft/Top return erroneous |
| // values when overflow is not visible. We have to difference screenX |
| // here due to a change in getBoxObjectFor which causes inconsistencies |
| // on whether the calculations are inside or outside of the element's |
| // border. |
| // If the element is in a scrollable div, getBoxObjectFor(elem) can return |
| // a value that varies by 1 pixel. |
| var doc = elem.ownerDocument; |
| return doc.getBoxObjectFor(elem).screenX - |
| doc.getBoxObjectFor(doc.documentElement).screenX; |
| } |
| }-*/; |
| |
| private native int getAbsoluteTopImpl(Element viewport, Element elem) /*-{ |
| // Firefox 3 is actively throwing errors when getBoxObjectFor() is called, |
| // so we use getBoundingClientRect() whenever possible (but it's not |
| // supported on older versions). If changing this code, make sure to check |
| // the museum entry for issue 1932. |
| // (x) | 0 is used to coerce the value to an integer |
| if (Element.prototype.getBoundingClientRect) { |
| return (elem.getBoundingClientRect().top + viewport.scrollTop) | 0; |
| } else { |
| // We cannot use DOMImpl here because offsetLeft/Top return erroneous |
| // values when overflow is not visible. We have to difference screenX |
| // here due to a change in getBoxObjectFor which causes inconsistencies |
| // on whether the calculations are inside or outside of the element's |
| // border. |
| var doc = elem.ownerDocument; |
| return doc.getBoxObjectFor(elem).screenY - |
| doc.getBoxObjectFor(doc.documentElement).screenY; |
| } |
| }-*/; |
| |
| private native boolean isRTL(Element elem) /*-{ |
| var style = elem.ownerDocument.defaultView.getComputedStyle(elem, null); |
| return style.direction == 'rtl'; |
| }-*/; |
| } |
| |