IE9 support (BETA) - 3/3.

Known Issues:
http://code.google.com/p/google-web-toolkit/issues/detail?id=6136
http://code.google.com/p/google-web-toolkit/issues/detail?id=6135
http://code.google.com/p/google-web-toolkit/issues/detail?id=6134


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9843 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java b/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java
index 8320610..5c382bb 100644
--- a/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java
+++ b/dev/core/src/com/google/gwt/dev/shell/StandardRebindOracle.java
@@ -161,10 +161,9 @@
       // and we may have a partial match based on fall back values
       assert minCostRuleSoFar != null;
       if (minCostRuleSoFar.getFallbackEvaluationCost() < Integer.MAX_VALUE) {
-        // todo - enable 
-        // logger.log(TreeLogger.WARN, "Could not find an exact match rule. Using 'closest' rule " + 
-        //    minCostRuleSoFar + " based on fall back values. You may need to implement a specific " +
-        //    "binding in case the fall back behavior does not replace the missing binding");
+        logger.log(TreeLogger.INFO, "Could not find an exact match rule. Using 'closest' rule " +
+          minCostRuleSoFar + " based on fall back values. You may need to implement a specific " +
+          "binding in case the fall back behavior does not replace the missing binding");
         if (!usedRules.contains(minCostRuleSoFar)) {
           usedRules.add(minCostRuleSoFar);
           logger.log(TreeLogger.DEBUG, "No exact match was found, using closest match rule " + minCostRuleSoFar);
diff --git a/user/src/com/google/gwt/dom/DOM.gwt.xml b/user/src/com/google/gwt/dom/DOM.gwt.xml
index 8187682..4fe314e 100644
--- a/user/src/com/google/gwt/dom/DOM.gwt.xml
+++ b/user/src/com/google/gwt/dom/DOM.gwt.xml
@@ -25,11 +25,16 @@
     <when-property-is name="user.agent" value="opera"/>
   </replace-with>
 
-  <replace-with class="com.google.gwt.dom.client.DOMImplSafari">
+  <replace-with class="com.google.gwt.dom.client.DOMImplWebkit">
     <when-type-is class="com.google.gwt.dom.client.DOMImpl"/>
     <when-property-is name="user.agent" value="safari"/>
   </replace-with>
 
+  <replace-with class="com.google.gwt.dom.client.DOMImplIE9">
+    <when-type-is class="com.google.gwt.dom.client.DOMImpl"/>
+    <when-property-is name="user.agent" value="ie9"/>
+  </replace-with>
+
   <replace-with class="com.google.gwt.dom.client.DOMImplIE8">
     <when-type-is class="com.google.gwt.dom.client.DOMImpl"/>
     <when-property-is name="user.agent" value="ie8"/>
@@ -50,6 +55,7 @@
     <any>
       <when-property-is name="user.agent" value="ie6"/>
       <when-property-is name="user.agent" value="ie8"/>
+      <when-property-is name="user.agent" value="ie9"/>
     </any>
   </replace-with>
 </module>
diff --git a/user/src/com/google/gwt/dom/client/DOMImplIE9.java b/user/src/com/google/gwt/dom/client/DOMImplIE9.java
new file mode 100644
index 0000000..9b3a13d
--- /dev/null
+++ b/user/src/com/google/gwt/dom/client/DOMImplIE9.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2011 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;
+
+/**
+ * IE9 based implementation of {@link com.google.gwt.user.client.impl.DOMImplStandardBase}.
+ */
+class DOMImplIE9 extends DOMImplStandardBase {
+}
+
diff --git a/user/src/com/google/gwt/dom/client/DOMImplSafari.java b/user/src/com/google/gwt/dom/client/DOMImplSafari.java
index 71b325e..697e3f0 100644
--- a/user/src/com/google/gwt/dom/client/DOMImplSafari.java
+++ b/user/src/com/google/gwt/dom/client/DOMImplSafari.java
@@ -15,307 +15,11 @@
  */
 package com.google.gwt.dom.client;
 
-import com.google.gwt.core.client.JavaScriptObject;
-
 /**
  * Safari implementation of {@link com.google.gwt.user.client.impl.DOMImpl}.
+ * @deprecated  As of release 2.3, replaced by {@link com.google.gwt.dom.client.DOMImplWebkit}
  */
-class DOMImplSafari extends DOMImplStandard {
-
-  /**
-   * Return true if using Webkit 525.x (Safari 3) or earlier.
-   * 
-   * @return true if using Webkit 525.x (Safari 3) or earlier.
-   */
-  @SuppressWarnings("unused")
-  private static native boolean isWebkit525OrBefore() /*-{
-    var result = /safari\/([\d.]+)/.exec(navigator.userAgent.toLowerCase());
-    if (result) {
-      var version = (parseFloat(result[1]));
-      if (version < 526) {
-        return true;
-      }
-    }
-    return false;
-  }-*/;
-
-  private static class ClientRect extends JavaScriptObject {
-    
-    @SuppressWarnings("unused")
-    protected ClientRect() {
-    }
-
-    public final native int getLeft() /*-{
-      return this.left;
-    }-*/;
-
-    public final native int getTop() /*-{
-      return this.top;
-    }-*/;
-  }
-
-  private static native int getAbsoluteLeftUsingOffsets(Element elem) /*-{
-    // Unattached elements and elements (or their ancestors) with style
-    // 'display: none' have no offsetLeft.
-    if (elem.offsetLeft == null) {
-      return 0;
-    }
-
-    var left = 0;
-    var doc = elem.ownerDocument;
-    var curr = elem.parentNode;
-    if (curr) {
-      // This intentionally excludes body which has a null offsetParent.
-      while (curr.offsetParent) {
-        left -= curr.scrollLeft;
-
-        // In RTL mode, offsetLeft is relative to the left edge of the
-        // scrollable area when scrolled all the way to the right, so we need
-        // to add back that difference.
-        if (doc.defaultView.getComputedStyle(curr, '').getPropertyValue('direction') == 'rtl') {
-          left += (curr.scrollWidth - curr.clientWidth);
-        }
-
-        curr = curr.parentNode;
-      }
-    }
-
-    while (elem) {
-      left += elem.offsetLeft;
-
-      if (doc.defaultView.getComputedStyle(elem, '')['position'] == 'fixed') {
-        left += doc.body.scrollLeft;
-        return left;
-      }
-
-      // Safari 3 does not include borders with offsetLeft, so we need to add
-      // the borders of the parent manually.
-      var parent = elem.offsetParent;
-      if (parent && $wnd.devicePixelRatio) {
-        left += parseInt(doc.defaultView.getComputedStyle(parent, '').getPropertyValue('border-left-width'));
-      }
-
-      // Safari bug: a top-level absolutely positioned element includes the
-      // body's offset position already.
-      if (parent && (parent.tagName == 'BODY') &&
-          (elem.style.position == 'absolute')) {
-        break;
-      }
-
-      elem = parent;
-    }
-    return left;
-  }-*/;
-
-  private static native int getAbsoluteTopUsingOffsets(Element elem) /*-{
-    // Unattached elements and elements (or their ancestors) with style
-    // 'display: none' have no offsetTop.
-    if (elem.offsetTop == null) {
-      return 0;
-    }
-
-    var top = 0;
-    var doc = elem.ownerDocument;
-    var curr = elem.parentNode;
-    if (curr) {
-      // This intentionally excludes body which has a null offsetParent.
-      while (curr.offsetParent) {
-        top -= curr.scrollTop;
-        curr = curr.parentNode;
-      }
-    }
-
-    while (elem) {
-      top += elem.offsetTop;
-
-      if (doc.defaultView.getComputedStyle(elem, '')['position'] == 'fixed') {
-        top += doc.body.scrollTop;
-        return top;
-      }
-
-      // Safari 3 does not include borders with offsetTop, so we need to add the
-      // borders of the parent manually.
-      var parent = elem.offsetParent;
-      if (parent && $wnd.devicePixelRatio) {
-        top += parseInt(doc.defaultView.getComputedStyle(parent, '').getPropertyValue('border-top-width'));
-      }
-
-      // Safari bug: a top-level absolutely positioned element includes the
-      // body's offset position already.
-      if (parent && (parent.tagName == 'BODY') &&
-          (elem.style.position == 'absolute')) {
-        break;
-      }
-
-      elem = parent;
-    }
-    return top;
-  }-*/;
-
-  private static native ClientRect getBoundingClientRect(Element element) /*-{
-    return element.getBoundingClientRect && element.getBoundingClientRect();
-  }-*/;
-
-  /**
-   * The type property on a button element is read-only in safari, so we need to
-   * set it using setAttribute.
-   */
-  @Override
-  public native ButtonElement createButtonElement(Document doc, String type) /*-{
-    var e = doc.createElement("BUTTON");
-    e.setAttribute('type', type);
-    return e;
-  }-*/;
-
-  @Override
-  public native NativeEvent createKeyCodeEvent(Document doc, String type,
-      boolean ctrlKey, boolean altKey, boolean shiftKey, boolean metaKey,
-      int keyCode) /*-{
-    var evt = this.@com.google.gwt.dom.client.DOMImplSafari::createKeyEvent(Lcom/google/gwt/dom/client/Document;Ljava/lang/String;ZZZZZZ)(doc, type, true, true, ctrlKey, altKey, shiftKey, metaKey)
-    evt.keyCode = keyCode;
-    return evt;
-  }-*/;
-
-  @Override
-  @Deprecated
-  public native NativeEvent createKeyEvent(Document doc, String type,
-      boolean canBubble, boolean cancelable, boolean ctrlKey, boolean altKey,
-      boolean shiftKey, boolean metaKey, int keyCode, int charCode) /*-{
-    var evt = this.@com.google.gwt.dom.client.DOMImplSafari::createKeyEvent(Lcom/google/gwt/dom/client/Document;Ljava/lang/String;ZZZZZZ)(doc, type, canBubble, cancelable, ctrlKey, altKey, shiftKey, metaKey)
-    evt.keyCode = keyCode;
-    evt.charCode = charCode;
-    return evt;
-  }-*/;
-
-  @Override
-  public native NativeEvent createKeyPressEvent(Document doc,
-      boolean ctrlKey, boolean altKey, boolean shiftKey, boolean metaKey,
-      int charCode) /*-{
-    var evt = this.@com.google.gwt.dom.client.DOMImplSafari::createKeyEvent(Lcom/google/gwt/dom/client/Document;Ljava/lang/String;ZZZZZZ)(doc, 'keypress', true, true, ctrlKey, altKey, shiftKey, metaKey)
-    evt.charCode = charCode;
-    return evt;
-  }-*/;
-
-  /**
-   * Safari 2 does not support {@link ScriptElement#setText(String)}.
-   */
-  @Override
-  public ScriptElement createScriptElement(Document doc, String source) {
-    ScriptElement elem = (ScriptElement) createElement(doc, "script");
-    elem.setInnerText(source);
-    return elem;
-  }
-
-  @Override
-  public native EventTarget eventGetCurrentTarget(NativeEvent event) /*-{
-    return event.currentTarget || $wnd;
-  }-*/;
-
-  @Override
-  public native int eventGetMouseWheelVelocityY(NativeEvent evt) /*-{
-    return Math.round(-evt.wheelDelta / 40) || 0;
-  }-*/;
-
-  @Override
-  public int getAbsoluteLeft(Element elem) {
-    ClientRect rect = getBoundingClientRect(elem);
-    return rect != null ? rect.getLeft()
-        + elem.getOwnerDocument().getBody().getScrollLeft()
-        : getAbsoluteLeftUsingOffsets(elem);
-  }
-
-  @Override
-  public int getAbsoluteTop(Element elem) {
-    ClientRect rect = getBoundingClientRect(elem);
-    return rect != null ? rect.getTop()
-        + elem.getOwnerDocument().getBody().getScrollTop()
-        : getAbsoluteTopUsingOffsets(elem);
-  }
-
-  @Override
-  public int getScrollLeft(Document doc) {
-    // Safari always applies document scrolling to the body element, even in
-    // strict mode.
-    return doc.getBody().getScrollLeft();
-  }
-
-  @Override
-  public int getScrollLeft(Element elem) {
-    if (isRTL(elem)) {
-      return super.getScrollLeft(elem)
-          - (elem.getScrollWidth() - elem.getClientWidth());
-    }
-    return super.getScrollLeft(elem);
-  }
-
-  @Override
-  public int getScrollTop(Document doc) {
-    // Safari always applies document scrolling to the body element, even in
-    // strict mode.
-    return doc.getBody().getScrollTop();
-  }
-
-  @Override
-  public native int getTabIndex(Element elem) /*-{ 
-    // tabIndex is undefined for divs and other non-focusable elements prior to
-    // Safari 4.
-    return typeof elem.tabIndex != 'undefined' ? elem.tabIndex : -1;
-  }-*/;
-
-  @Override
-  public native boolean isOrHasChild(Node parent, Node child) /*-{
-    while (child) {
-      if (parent == child) {
-        return true;
-      }
-      child = child.parentNode;
-      if (child && (child.nodeType != 1)) {
-        child = null;
-      }
-    }
-    return false;
-  }-*/;
-
-  @Override
-  public void setScrollLeft(Document doc, int left) {
-    // Safari always applies document scrolling to the body element, even in
-    // strict mode.
-    doc.getBody().setScrollLeft(left);
-  }
-
-  @Override
-  public void setScrollLeft(Element elem, int left) {
-    if (isRTL(elem)) {
-      left += elem.getScrollWidth() - elem.getClientWidth();
-    }
-    super.setScrollLeft(elem, left);
-  }
-
-  @Override
-  public void setScrollTop(Document doc, int top) {
-    // Safari always applies document scrolling to the body element, even in
-    // strict mode.
-    doc.getBody().setScrollTop(top);
-  }
-
-  @SuppressWarnings("unused")
-  private native NativeEvent createKeyEvent(Document doc, String type,
-      boolean canBubble, boolean cancelable, boolean ctrlKey, boolean altKey,
-      boolean shiftKey, boolean metaKey) /*-{
-    // WebKit's KeyboardEvent cannot set or even initialize charCode, keyCode, etc.
-    // And UIEvent's charCode and keyCode are read-only.
-    // So we "fake" an event using a raw Event and expandos
-    var evt = doc.createEvent('Event');
-    evt.initEvent(type, canBubble, cancelable);
-    evt.ctrlKey = ctrlKey;
-    evt.altKey = altKey;
-    evt.shiftKey = shiftKey;
-    evt.metaKey = metaKey;
-    return evt;
-  }-*/;
-
-  private native boolean isRTL(Element elem) /*-{
-    return elem.ownerDocument.defaultView.getComputedStyle(elem, '').direction == 'rtl';
-  }-*/;
+@Deprecated
+class DOMImplSafari extends DOMImplWebkit {
 }
 
diff --git a/user/src/com/google/gwt/dom/client/DOMImplStandardBase.java b/user/src/com/google/gwt/dom/client/DOMImplStandardBase.java
new file mode 100644
index 0000000..a2deb0d
--- /dev/null
+++ b/user/src/com/google/gwt/dom/client/DOMImplStandardBase.java
@@ -0,0 +1,304 @@
+/*
+ * Copyright 2011 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;
+
+import com.google.gwt.core.client.JavaScriptObject;
+
+/**
+ * StandardBase implementation of {@link com.google.gwt.user.client.impl.DOMImpl}.
+ */
+class DOMImplStandardBase extends DOMImplStandard {
+
+  private static class ClientRect extends JavaScriptObject {
+    
+    @SuppressWarnings("unused")
+    protected ClientRect() {
+    }
+
+    public final native int getLeft() /*-{
+      return this.left;
+    }-*/;
+
+    public final native int getTop() /*-{
+      return this.top;
+    }-*/;
+  }
+
+  private static native int getAbsoluteLeftUsingOffsets(Element elem) /*-{
+    // Unattached elements and elements (or their ancestors) with style
+    // 'display: none' have no offsetLeft.
+    if (elem.offsetLeft == null) {
+      return 0;
+    }
+
+    var left = 0;
+    var doc = elem.ownerDocument;
+    var curr = elem.parentNode;
+    if (curr) {
+      // This intentionally excludes body which has a null offsetParent.
+      while (curr.offsetParent) {
+        left -= curr.scrollLeft;
+
+        // In RTL mode, offsetLeft is relative to the left edge of the
+        // scrollable area when scrolled all the way to the right, so we need
+        // to add back that difference.
+        if (doc.defaultView.getComputedStyle(curr, '').getPropertyValue('direction') == 'rtl') {
+          left += (curr.scrollWidth - curr.clientWidth);
+        }
+
+        curr = curr.parentNode;
+      }
+    }
+
+    while (elem) {
+      left += elem.offsetLeft;
+
+      if (doc.defaultView.getComputedStyle(elem, '')['position'] == 'fixed') {
+        left += doc.body.scrollLeft;
+        return left;
+      }
+
+      // Safari 3 does not include borders with offsetLeft, so we need to add
+      // the borders of the parent manually.
+      var parent = elem.offsetParent;
+      if (parent && $wnd.devicePixelRatio) {
+        left += parseInt(doc.defaultView.getComputedStyle(parent, '').getPropertyValue('border-left-width'));
+      }
+
+      // Safari bug: a top-level absolutely positioned element includes the
+      // body's offset position already.
+      if (parent && (parent.tagName == 'BODY') &&
+          (elem.style.position == 'absolute')) {
+        break;
+      }
+
+      elem = parent;
+    }
+    return left;
+  }-*/;
+
+  private static native int getAbsoluteTopUsingOffsets(Element elem) /*-{
+    // Unattached elements and elements (or their ancestors) with style
+    // 'display: none' have no offsetTop.
+    if (elem.offsetTop == null) {
+      return 0;
+    }
+
+    var top = 0;
+    var doc = elem.ownerDocument;
+    var curr = elem.parentNode;
+    if (curr) {
+      // This intentionally excludes body which has a null offsetParent.
+      while (curr.offsetParent) {
+        top -= curr.scrollTop;
+        curr = curr.parentNode;
+      }
+    }
+
+    while (elem) {
+      top += elem.offsetTop;
+
+      if (doc.defaultView.getComputedStyle(elem, '')['position'] == 'fixed') {
+        top += doc.body.scrollTop;
+        return top;
+      }
+
+      // Safari 3 does not include borders with offsetTop, so we need to add the
+      // borders of the parent manually.
+      var parent = elem.offsetParent;
+      if (parent && $wnd.devicePixelRatio) {
+        top += parseInt(doc.defaultView.getComputedStyle(parent, '').getPropertyValue('border-top-width'));
+      }
+
+      // Safari bug: a top-level absolutely positioned element includes the
+      // body's offset position already.
+      if (parent && (parent.tagName == 'BODY') &&
+          (elem.style.position == 'absolute')) {
+        break;
+      }
+
+      elem = parent;
+    }
+    return top;
+  }-*/;
+
+  private static native ClientRect getBoundingClientRect(Element element) /*-{
+    return element.getBoundingClientRect && element.getBoundingClientRect();
+  }-*/;
+
+  /**
+   * The type property on a button element is read-only in safari, so we need to
+   * set it using setAttribute.
+   */
+  @Override
+  public native ButtonElement createButtonElement(Document doc, String type) /*-{
+    var e = doc.createElement("BUTTON");
+    e.setAttribute('type', type);
+    return e;
+  }-*/;
+
+  @Override
+  public native NativeEvent createKeyCodeEvent(Document doc, String type,
+      boolean ctrlKey, boolean altKey, boolean shiftKey, boolean metaKey,
+      int keyCode) /*-{
+    var evt = this.@com.google.gwt.dom.client.DOMImplStandardBase::createKeyEvent(Lcom/google/gwt/dom/client/Document;Ljava/lang/String;ZZZZZZ)(doc, type, true, true, ctrlKey, altKey, shiftKey, metaKey)
+    evt.keyCode = keyCode;
+    return evt;
+  }-*/;
+
+  @Override
+  @Deprecated
+  public native NativeEvent createKeyEvent(Document doc, String type,
+      boolean canBubble, boolean cancelable, boolean ctrlKey, boolean altKey,
+      boolean shiftKey, boolean metaKey, int keyCode, int charCode) /*-{
+    var evt = this.@com.google.gwt.dom.client.DOMImplStandardBase::createKeyEvent(Lcom/google/gwt/dom/client/Document;Ljava/lang/String;ZZZZZZ)(doc, type, canBubble, cancelable, ctrlKey, altKey, shiftKey, metaKey)
+    evt.keyCode = keyCode;
+    evt.charCode = charCode;
+    return evt;
+  }-*/;
+
+  @Override
+  public native NativeEvent createKeyPressEvent(Document doc,
+      boolean ctrlKey, boolean altKey, boolean shiftKey, boolean metaKey,
+      int charCode) /*-{
+    var evt = this.@com.google.gwt.dom.client.DOMImplStandardBase::createKeyEvent(Lcom/google/gwt/dom/client/Document;Ljava/lang/String;ZZZZZZ)(doc, 'keypress', true, true, ctrlKey, altKey, shiftKey, metaKey)
+    evt.charCode = charCode;
+    return evt;
+  }-*/;
+
+  /**
+   * Safari 2 does not support {@link ScriptElement#setText(String)}.
+   */
+  @Override
+  public ScriptElement createScriptElement(Document doc, String source) {
+    ScriptElement elem = (ScriptElement) createElement(doc, "script");
+    elem.setInnerText(source);
+    return elem;
+  }
+
+  @Override
+  public native EventTarget eventGetCurrentTarget(NativeEvent event) /*-{
+    return event.currentTarget || $wnd;
+  }-*/;
+
+  @Override
+  public native int eventGetMouseWheelVelocityY(NativeEvent evt) /*-{
+    return Math.round(-evt.wheelDelta / 40) || 0;
+  }-*/;
+
+  @Override
+  public int getAbsoluteLeft(Element elem) {
+    ClientRect rect = getBoundingClientRect(elem);
+    return rect != null ? rect.getLeft()
+        + elem.getOwnerDocument().getBody().getScrollLeft()
+        : getAbsoluteLeftUsingOffsets(elem);
+  }
+
+  @Override
+  public int getAbsoluteTop(Element elem) {
+    ClientRect rect = getBoundingClientRect(elem);
+    return rect != null ? rect.getTop()
+        + elem.getOwnerDocument().getBody().getScrollTop()
+        : getAbsoluteTopUsingOffsets(elem);
+  }
+
+  @Override
+  public int getScrollLeft(Document doc) {
+    // Safari always applies document scrolling to the body element, even in
+    // strict mode.
+    return doc.getBody().getScrollLeft();
+  }
+
+  @Override
+  public int getScrollLeft(Element elem) {
+    if (isRTL(elem)) {
+      return super.getScrollLeft(elem)
+          - (elem.getScrollWidth() - elem.getClientWidth());
+    }
+    return super.getScrollLeft(elem);
+  }
+
+  @Override
+  public int getScrollTop(Document doc) {
+    // Safari always applies document scrolling to the body element, even in
+    // strict mode.
+    return doc.getBody().getScrollTop();
+  }
+
+  @Override
+  public native int getTabIndex(Element elem) /*-{ 
+    // tabIndex is undefined for divs and other non-focusable elements prior to
+    // Safari 4.
+    return typeof elem.tabIndex != 'undefined' ? elem.tabIndex : -1;
+  }-*/;
+
+  @Override
+  public native boolean isOrHasChild(Node parent, Node child) /*-{
+    while (child) {
+      if (parent == child) {
+        return true;
+      }
+      child = child.parentNode;
+      if (child && (child.nodeType != 1)) {
+        child = null;
+      }
+    }
+    return false;
+  }-*/;
+
+  @Override
+  public void setScrollLeft(Document doc, int left) {
+    // Safari always applies document scrolling to the body element, even in
+    // strict mode.
+    doc.getBody().setScrollLeft(left);
+  }
+
+  @Override
+  public void setScrollLeft(Element elem, int left) {
+    if (isRTL(elem)) {
+      left += elem.getScrollWidth() - elem.getClientWidth();
+    }
+    super.setScrollLeft(elem, left);
+  }
+
+  @Override
+  public void setScrollTop(Document doc, int top) {
+    // Safari always applies document scrolling to the body element, even in
+    // strict mode.
+    doc.getBody().setScrollTop(top);
+  }
+
+  @SuppressWarnings("unused")
+  private native NativeEvent createKeyEvent(Document doc, String type,
+      boolean canBubble, boolean cancelable, boolean ctrlKey, boolean altKey,
+      boolean shiftKey, boolean metaKey) /*-{
+    // WebKit's KeyboardEvent cannot set or even initialize charCode, keyCode, etc.
+    // And UIEvent's charCode and keyCode are read-only.
+    // So we "fake" an event using a raw Event and expandos
+    var evt = doc.createEvent('Event');
+    evt.initEvent(type, canBubble, cancelable);
+    evt.ctrlKey = ctrlKey;
+    evt.altKey = altKey;
+    evt.shiftKey = shiftKey;
+    evt.metaKey = metaKey;
+    return evt;
+  }-*/;
+
+  private native boolean isRTL(Element elem) /*-{
+    return elem.ownerDocument.defaultView.getComputedStyle(elem, '').direction == 'rtl';
+  }-*/;
+}
+
diff --git a/user/src/com/google/gwt/dom/client/DOMImplWebkit.java b/user/src/com/google/gwt/dom/client/DOMImplWebkit.java
new file mode 100644
index 0000000..d292af9
--- /dev/null
+++ b/user/src/com/google/gwt/dom/client/DOMImplWebkit.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2011 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;
+
+/**
+ * WebKit based implementation of {@link com.google.gwt.user.client.impl.DOMImplStandardBase}.
+ */
+class DOMImplWebkit extends DOMImplStandardBase {
+
+  /**
+   * Return true if using Webkit 525.x (Safari 3) or earlier.
+   * 
+   * @return true if using Webkit 525.x (Safari 3) or earlier.
+   */
+  @SuppressWarnings("unused")
+  private static native boolean isWebkit525OrBefore() /*-{
+    var result = /safari\/([\d.]+)/.exec(navigator.userAgent.toLowerCase());
+    if (result) {
+      var version = (parseFloat(result[1]));
+      if (version < 526) {
+        return true;
+      }
+    }
+    return false;
+  }-*/;
+}
+
diff --git a/user/src/com/google/gwt/layout/Layout.gwt.xml b/user/src/com/google/gwt/layout/Layout.gwt.xml
index 17788a8..65797b5 100644
--- a/user/src/com/google/gwt/layout/Layout.gwt.xml
+++ b/user/src/com/google/gwt/layout/Layout.gwt.xml
@@ -20,7 +20,10 @@
 
   <replace-with class="com.google.gwt.layout.client.LayoutImplIE8">
     <when-type-is class="com.google.gwt.layout.client.LayoutImpl"/>
-    <when-property-is name="user.agent" value="ie8"/>
+    <any>
+      <when-property-is name="user.agent" value="ie8"/>
+      <when-property-is name="user.agent" value="ie9"/>
+    </any>
   </replace-with>
 
   <replace-with class="com.google.gwt.layout.client.LayoutImplIE6">
diff --git a/user/src/com/google/gwt/resources/Resources.gwt.xml b/user/src/com/google/gwt/resources/Resources.gwt.xml
index a78e59a..f46be7b 100644
--- a/user/src/com/google/gwt/resources/Resources.gwt.xml
+++ b/user/src/com/google/gwt/resources/Resources.gwt.xml
@@ -54,6 +54,7 @@
         <when-property-is name="user.agent" value="opera" />
         <when-property-is name="user.agent" value="gecko1_8" />
         <when-property-is name="user.agent" value="ie8" />
+        <when-property-is name="user.agent" value="ie9" />
       </any>
     </all>
   </generate-with>
diff --git a/user/src/com/google/gwt/storage/Storage.gwt.xml b/user/src/com/google/gwt/storage/Storage.gwt.xml
index 47ee937..8c3396f 100644
--- a/user/src/com/google/gwt/storage/Storage.gwt.xml
+++ b/user/src/com/google/gwt/storage/Storage.gwt.xml
@@ -41,7 +41,10 @@
 
   <replace-with class="com.google.gwt.storage.client.StorageImplNonNativeEvents">
     <when-type-is class="com.google.gwt.storage.client.StorageImpl" />
-    <when-property-is name="user.agent" value="safari" />
+    <any>
+      <when-property-is name="user.agent" value="safari" />
+      <when-property-is name="user.agent" value="ie9" />
+    </any>
   </replace-with>
 
   <replace-with class="com.google.gwt.storage.client.StorageImplIE8">
diff --git a/user/src/com/google/gwt/user/DOM.gwt.xml b/user/src/com/google/gwt/user/DOM.gwt.xml
index 9d3bef5..30c0508 100644
--- a/user/src/com/google/gwt/user/DOM.gwt.xml
+++ b/user/src/com/google/gwt/user/DOM.gwt.xml
@@ -17,32 +17,37 @@
 <!-- This module is typically inherited via com.google.gwt.user.User        -->
 <!--                                                                        -->
 <module>
-	<inherits name="com.google.gwt.core.Core"/>
-  <inherits name="com.google.gwt.dom.DOM"/>
-	<inherits name="com.google.gwt.user.UserAgent"/>
+  <inherits name="com.google.gwt.core.Core"/>
+    <inherits name="com.google.gwt.dom.DOM"/>
+  <inherits name="com.google.gwt.user.UserAgent"/>
 
-	<replace-with class="com.google.gwt.user.client.impl.DOMImplOpera">
-		<when-type-is class="com.google.gwt.user.client.impl.DOMImpl"/>
-		<when-property-is name="user.agent" value="opera"/>
-	</replace-with>
+  <replace-with class="com.google.gwt.user.client.impl.DOMImplOpera">
+    <when-type-is class="com.google.gwt.user.client.impl.DOMImpl"/>
+    <when-property-is name="user.agent" value="opera"/>
+  </replace-with>
 
-	<replace-with class="com.google.gwt.user.client.impl.DOMImplSafari">
-		<when-type-is class="com.google.gwt.user.client.impl.DOMImpl"/>
-		<when-property-is name="user.agent" value="safari"/>
-	</replace-with>
+  <replace-with class="com.google.gwt.user.client.impl.DOMImplWebkit">
+    <when-type-is class="com.google.gwt.user.client.impl.DOMImpl"/>
+    <when-property-is name="user.agent" value="safari"/>
+  </replace-with>
 
-	<replace-with class="com.google.gwt.user.client.impl.DOMImplIE8">
-		<when-type-is class="com.google.gwt.user.client.impl.DOMImpl"/>
-		<when-property-is name="user.agent" value="ie8"/>
-	</replace-with>
+    <replace-with class="com.google.gwt.user.client.impl.DOMImplIE9">
+        <when-type-is class="com.google.gwt.user.client.impl.DOMImpl"/>
+        <when-property-is name="user.agent" value="ie9"/>
+    </replace-with>
 
-	<replace-with class="com.google.gwt.user.client.impl.DOMImplIE6">
-		<when-type-is class="com.google.gwt.user.client.impl.DOMImpl"/>
-		<when-property-is name="user.agent" value="ie6"/>
-	</replace-with>
+  <replace-with class="com.google.gwt.user.client.impl.DOMImplIE8">
+    <when-type-is class="com.google.gwt.user.client.impl.DOMImpl"/>
+    <when-property-is name="user.agent" value="ie8"/>
+  </replace-with>
 
-	<replace-with class="com.google.gwt.user.client.impl.DOMImplMozilla">
-		<when-type-is class="com.google.gwt.user.client.impl.DOMImpl"/>
-		<when-property-is name="user.agent" value="gecko1_8"/>
-	</replace-with>
+  <replace-with class="com.google.gwt.user.client.impl.DOMImplIE6">
+    <when-type-is class="com.google.gwt.user.client.impl.DOMImpl"/>
+    <when-property-is name="user.agent" value="ie6"/>
+  </replace-with>
+
+  <replace-with class="com.google.gwt.user.client.impl.DOMImplMozilla">
+    <when-type-is class="com.google.gwt.user.client.impl.DOMImpl"/>
+    <when-property-is name="user.agent" value="gecko1_8"/>
+  </replace-with>
 </module>
diff --git a/user/src/com/google/gwt/user/Focus.gwt.xml b/user/src/com/google/gwt/user/Focus.gwt.xml
index d863ea3..1c1f5f9 100644
--- a/user/src/com/google/gwt/user/Focus.gwt.xml
+++ b/user/src/com/google/gwt/user/Focus.gwt.xml
@@ -39,6 +39,7 @@
     <any>
       <when-property-is name="user.agent" value="ie6"/>
       <when-property-is name="user.agent" value="ie8"/>
+      <when-property-is name="user.agent" value="ie9"/>
     </any>
   </replace-with>
 </module>
diff --git a/user/src/com/google/gwt/user/Hyperlink.gwt.xml b/user/src/com/google/gwt/user/Hyperlink.gwt.xml
index 787fbc7..fa24eda 100644
--- a/user/src/com/google/gwt/user/Hyperlink.gwt.xml
+++ b/user/src/com/google/gwt/user/Hyperlink.gwt.xml
@@ -15,8 +15,9 @@
   <replace-with class="com.google.gwt.user.client.ui.impl.HyperlinkImplIE">
     <when-type-is class="com.google.gwt.user.client.ui.impl.HyperlinkImpl"/>
     <any>
-	    <when-property-is name="user.agent" value="ie6"/>
-	    <when-property-is name="user.agent" value="ie8"/>
+      <when-property-is name="user.agent" value="ie6"/>
+      <when-property-is name="user.agent" value="ie8"/>
+      <when-property-is name="user.agent" value="ie9" />
     </any>
   </replace-with>
 </module>
diff --git a/user/src/com/google/gwt/user/RichText.gwt.xml b/user/src/com/google/gwt/user/RichText.gwt.xml
index 22694d1..be78d4e 100644
--- a/user/src/com/google/gwt/user/RichText.gwt.xml
+++ b/user/src/com/google/gwt/user/RichText.gwt.xml
@@ -26,6 +26,7 @@
     <any>
       <when-property-is name="user.agent" value="ie6" />
       <when-property-is name="user.agent" value="ie8" />
+      <when-property-is name="user.agent" value="ie9" />
     </any>
   </replace-with>
 
diff --git a/user/src/com/google/gwt/user/Tree.gwt.xml b/user/src/com/google/gwt/user/Tree.gwt.xml
index 94b14e5..3876ba6 100644
--- a/user/src/com/google/gwt/user/Tree.gwt.xml
+++ b/user/src/com/google/gwt/user/Tree.gwt.xml
@@ -21,8 +21,9 @@
   <replace-with class="com.google.gwt.user.client.ui.TreeItem.TreeItemImplIE6">
     <when-type-is class="com.google.gwt.user.client.ui.TreeItem.TreeItemImpl"/>
     <any>
-	    <when-property-is name="user.agent" value="ie6"/>
-	    <when-property-is name="user.agent" value="ie8"/>
-	  </any>
+      <when-property-is name="user.agent" value="ie6"/>
+      <when-property-is name="user.agent" value="ie8"/>
+      <when-property-is name="user.agent" value="ie9"/>
+    </any>
   </replace-with>
 </module>
diff --git a/user/src/com/google/gwt/user/Window.gwt.xml b/user/src/com/google/gwt/user/Window.gwt.xml
index 8ae46ce..1351b1b 100644
--- a/user/src/com/google/gwt/user/Window.gwt.xml
+++ b/user/src/com/google/gwt/user/Window.gwt.xml
@@ -17,14 +17,15 @@
 <!-- This module is typically inherited via com.google.gwt.user.User        -->
 <!--                                                                        -->
 <module>
-	<inherits name="com.google.gwt.core.Core"/>
-	<inherits name="com.google.gwt.user.UserAgent"/>
+  <inherits name="com.google.gwt.core.Core"/>
+  <inherits name="com.google.gwt.user.UserAgent"/>
 
    <replace-with class="com.google.gwt.user.client.impl.WindowImplIE">
       <when-type-is class="com.google.gwt.user.client.impl.WindowImpl"/>
       <any>
-	      <when-property-is name="user.agent" value="ie6"/>
-	      <when-property-is name="user.agent" value="ie8"/>
-	    </any>
+        <when-property-is name="user.agent" value="ie6"/>
+        <when-property-is name="user.agent" value="ie8"/>
+        <when-property-is name="user.agent" value="ie9"/>
+      </any>
    </replace-with>
 </module>
diff --git a/user/src/com/google/gwt/user/cellview/CellView.gwt.xml b/user/src/com/google/gwt/user/cellview/CellView.gwt.xml
index 00dacb0..ec6bcf2 100644
--- a/user/src/com/google/gwt/user/cellview/CellView.gwt.xml
+++ b/user/src/com/google/gwt/user/cellview/CellView.gwt.xml
@@ -35,6 +35,7 @@
     <any>
       <when-property-is name="user.agent" value="ie6"/>
       <when-property-is name="user.agent" value="ie8"/>
+      <when-property-is name="user.agent" value="ie9"/>
     </any>
   </replace-with>
 
@@ -52,6 +53,7 @@
     <any>
       <when-property-is name="user.agent" value="ie6"/>
       <when-property-is name="user.agent" value="ie8"/>
+      <when-property-is name="user.agent" value="ie9"/>
     </any>
   </replace-with>
 </module>
diff --git a/user/src/com/google/gwt/user/cellview/client/CellBasedWidgetImplTrident.java b/user/src/com/google/gwt/user/cellview/client/CellBasedWidgetImplTrident.java
index eea60dd..6f2fc44 100644
--- a/user/src/com/google/gwt/user/cellview/client/CellBasedWidgetImplTrident.java
+++ b/user/src/com/google/gwt/user/cellview/client/CellBasedWidgetImplTrident.java
@@ -354,7 +354,8 @@
    * @return the tab index, or -1 if not specified
    */
   private native int getTabIndexIfSpecified(Element elem) /*-{
-    return elem.getAttributeNode('tabIndex').specified ? elem.tabIndex : -1;
+    var attrNode = elem.getAttributeNode('tabIndex');
+    return (attrNode != null && attrNode.specified) ? elem.tabIndex : -1;
   }-*/;
 
   /**
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImplIE9.java b/user/src/com/google/gwt/user/client/impl/DOMImplIE9.java
new file mode 100644
index 0000000..a69fe1e
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/impl/DOMImplIE9.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2011 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;
+
+/**
+ * IE9 implementation of {@link com.google.gwt.user.client.impl.DOMImplStandardBase}.
+ */
+class DOMImplIE9 extends DOMImplStandardBase {
+}
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImplSafari.java b/user/src/com/google/gwt/user/client/impl/DOMImplSafari.java
index 16ab0ed..380ee0e 100644
--- a/user/src/com/google/gwt/user/client/impl/DOMImplSafari.java
+++ b/user/src/com/google/gwt/user/client/impl/DOMImplSafari.java
@@ -17,6 +17,8 @@
 
 /**
  * Safari implementation of {@link com.google.gwt.user.client.impl.DOMImpl}.
+ * @deprecated  As of release 2.3, replaced by {@link com.google.gwt.user.client.impl.DOMImplWebkit}
  */
-class DOMImplSafari extends DOMImplStandard {
+@Deprecated
+class DOMImplSafari extends DOMImplWebkit {
 }
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImplStandardBase.java b/user/src/com/google/gwt/user/client/impl/DOMImplStandardBase.java
new file mode 100644
index 0000000..3cfb006
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/impl/DOMImplStandardBase.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2011 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;
+
+/**
+ * Safari implementation of {@link com.google.gwt.user.client.impl.DOMImpl}.
+ */
+class DOMImplStandardBase extends DOMImplStandard {
+}
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImplWebkit.java b/user/src/com/google/gwt/user/client/impl/DOMImplWebkit.java
new file mode 100644
index 0000000..af79d27
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/impl/DOMImplWebkit.java
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2011 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;
+
+/**
+ * Webkit implementation of {@link com.google.gwt.user.client.impl.DOMImplStandardBase}.
+ */
+class DOMImplWebkit extends DOMImplStandardBase {
+}
diff --git a/user/src/com/google/gwt/xml/XML.gwt.xml b/user/src/com/google/gwt/xml/XML.gwt.xml
index be76d45..4eaf0d1 100644
--- a/user/src/com/google/gwt/xml/XML.gwt.xml
+++ b/user/src/com/google/gwt/xml/XML.gwt.xml
@@ -33,6 +33,7 @@
     <any>
       <when-property-is name="user.agent" value="ie6"/>
       <when-property-is name="user.agent" value="ie8"/>
+      <when-property-is name="user.agent" value="ie9"/>
     </any>
   </replace-with>
 
diff --git a/user/super/com/google/gwt/emul/EmulationWithUserAgent.gwt.xml b/user/super/com/google/gwt/emul/EmulationWithUserAgent.gwt.xml
index 2080888..2971b65 100644
--- a/user/super/com/google/gwt/emul/EmulationWithUserAgent.gwt.xml
+++ b/user/super/com/google/gwt/emul/EmulationWithUserAgent.gwt.xml
@@ -27,6 +27,7 @@
     <any>
       <when-property-is name="user.agent" value="ie6"/>
       <when-property-is name="user.agent" value="ie8"/>
+      <when-property-is name="user.agent" value="ie9"/>
     </any>
   </replace-with>
 
diff --git a/user/test/com/google/gwt/canvas/client/CanvasTest.java b/user/test/com/google/gwt/canvas/client/CanvasTest.java
index d1b308a..8b3958a 100644
--- a/user/test/com/google/gwt/canvas/client/CanvasTest.java
+++ b/user/test/com/google/gwt/canvas/client/CanvasTest.java
@@ -35,7 +35,7 @@
   protected Canvas canvas2;
 
   native boolean isWebkit525OrBefore() /*-{
-    return @com.google.gwt.dom.client.DOMImplSafari::isWebkit525OrBefore()();
+    return @com.google.gwt.dom.client.DOMImplWebkit::isWebkit525OrBefore()();
   }-*/;
 
   @Override
diff --git a/user/test/com/google/gwt/canvas/dom/client/Context2dTest.java b/user/test/com/google/gwt/canvas/dom/client/Context2dTest.java
index 259cf9a..49a6aa0 100644
--- a/user/test/com/google/gwt/canvas/dom/client/Context2dTest.java
+++ b/user/test/com/google/gwt/canvas/dom/client/Context2dTest.java
@@ -44,7 +44,7 @@
   }-*/;
 
   native boolean isWebkit525OrBefore() /*-{
-    return @com.google.gwt.dom.client.DOMImplSafari::isWebkit525OrBefore()();
+    return @com.google.gwt.dom.client.DOMImplWebkit::isWebkit525OrBefore()();
   }-*/;
 
   @Override
diff --git a/user/test/com/google/gwt/storage/client/StorageTest.java b/user/test/com/google/gwt/storage/client/StorageTest.java
index e98454a..08b8c85 100644
--- a/user/test/com/google/gwt/storage/client/StorageTest.java
+++ b/user/test/com/google/gwt/storage/client/StorageTest.java
@@ -43,7 +43,7 @@
   }-*/;
 
   private native boolean isSafari3OrBefore() /*-{
-    return @com.google.gwt.dom.client.DOMImplSafari::isWebkit525OrBefore()();
+    return @com.google.gwt.dom.client.DOMImplWebkit::isWebkit525OrBefore()();
   }-*/;
 
   @Override