blob: c58259728a315f5ca63307c6f256856cd493be7b [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.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';
}-*/;
}