Adds IE8 user-agent and fixes outstanding bugs in IE8-super-standards mode.
Review: http://gwt-code-reviews.appspot.com/29803


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@5292 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/dom/DOM.gwt.xml b/user/src/com/google/gwt/dom/DOM.gwt.xml
index e6c8d89..a0cd339 100644
--- a/user/src/com/google/gwt/dom/DOM.gwt.xml
+++ b/user/src/com/google/gwt/dom/DOM.gwt.xml
@@ -30,6 +30,11 @@
     <when-property-is name="user.agent" value="safari"/>
   </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"/>
+  </replace-with>
+
   <replace-with class="com.google.gwt.dom.client.DOMImplIE6">
     <when-type-is class="com.google.gwt.dom.client.DOMImpl"/>
     <when-property-is name="user.agent" value="ie6"/>
@@ -47,6 +52,9 @@
 
   <replace-with class="com.google.gwt.dom.client.StyleInjector.StyleInjectorImplIE">
     <when-type-is class="com.google.gwt.dom.client.StyleInjector.StyleInjectorImpl"/>
-    <when-property-is name="user.agent" value="ie6"/>
+    <any>
+      <when-property-is name="user.agent" value="ie6"/>
+      <when-property-is name="user.agent" value="ie8"/>
+    </any>
   </replace-with>
 </module>
diff --git a/user/src/com/google/gwt/dom/client/DOMImplIE6.java b/user/src/com/google/gwt/dom/client/DOMImplIE6.java
index 59c6af0..d348747 100644
--- a/user/src/com/google/gwt/dom/client/DOMImplIE6.java
+++ b/user/src/com/google/gwt/dom/client/DOMImplIE6.java
@@ -19,180 +19,22 @@
  * Internet Explorer 6 implementation of
  * {@link com.google.gwt.user.client.impl.DOMImpl}.
  */
-class DOMImplIE6 extends DOMImpl {
-
-  /**
-   * This field *must* be filled in from JSNI code before dispatching an event
-   * on IE. It should be set to the 'this' context of the handler that receives
-   * the event, then restored to its initial value when the dispatcher is done.
-   * See {@link com.google.gwt.user.client.impl.DOMImplIE6#initEventSystem()}
-   * for an example of how this should be done.
-   */
-  private static EventTarget currentEventTarget;
-
-  @Override
-  public native NativeEvent createHtmlEvent(Document doc, String type, boolean canBubble,
-      boolean cancelable) /*-{
-    // NOTE: IE doesn't support changing bubbling and canceling behavior (this
-    // is documented publicly in Document.createHtmlEvent()).
-    var evt = doc.createEventObject();
-    evt.type = type;
-    return evt;
-  }-*/;
-
-  @Override
-  public native InputElement createInputRadioElement(Document doc, String name) /*-{
-    return doc.createElement("<INPUT type='RADIO' name='" + name + "'>");
-  }-*/;
-
-  @Override
-  public native NativeEvent createKeyEvent(Document doc, String type, boolean canBubble,
-      boolean cancelable, boolean ctrlKey, boolean altKey, boolean shiftKey,
-      boolean metaKey, int keyCode, int charCode) /*-{
-    // NOTE: IE doesn't support changing bubbling and canceling behavior (this
-    // is documented publicly in Document.createKeyEvent()).
-    var evt = doc.createEventObject();
-    evt.type = type;
-    evt.ctrlKey = ctrlKey;
-    evt.altKey = altKey;
-    evt.shiftKey = shiftKey;
-    evt.metaKey = metaKey;
-    evt.keyCode = keyCode;
-    evt.charCode = charCode;
-
-    return evt;
-  }-*/;
-
-  @Override
-  public native NativeEvent createMouseEvent(Document doc, String type, boolean canBubble,
-      boolean cancelable, int detail, int screenX, int screenY, int clientX,
-      int clientY, boolean ctrlKey, boolean altKey, boolean shiftKey,
-      boolean metaKey, int button, Element relatedTarget) /*-{
-    // NOTE: IE doesn't support changing bubbling and canceling behavior (this
-    // is documented publicly in Document.createMouseEvent()).
-    var evt = doc.createEventObject();
-    evt.type = type;
-    evt.detail = detail;
-    evt.screenX = screenX;
-    evt.screenY = screenY;
-    evt.clientX = clientX;
-    evt.clientY = clientY;
-    evt.ctrlKey = ctrlKey;
-    evt.altKey = altKey;
-    evt.shiftKey = shiftKey;
-    evt.metaKey = metaKey;
-    evt.button = button;
-
-    // It would make sense to set evt.[fromElement | toElement] here, because
-    // that's what IE uses. However, setting these properties has no effect for
-    // some reason. So instead we set releatedTarget, and explicitly check for
-    // its existence in eventGetFromElement() and eventGetToElement().
-    evt.relatedTarget = relatedTarget;
-
-    return evt;
-  }-*/;
-
-  /**
-   * Supports creating a select control with the multiple attribute to work
-   * around a bug in IE6 where changing the multiple attribute in a setAttribute
-   * call can cause subsequent setSelected calls to misbehave. Although this bug
-   * is fixed in IE7, this DOMImpl specialization is used for both IE6 and IE7,
-   * but it should be harmless.
-   */
-  @Override
-  public native SelectElement createSelectElement(Document doc, boolean multiple) /*-{
-    var html = multiple ? "<SELECT MULTIPLE>" : "<SELECT>"; 
-    return doc.createElement(html);
-  }-*/;
-
-  public native void dispatchEvent(Element target, NativeEvent evt) /*-{
-    target.fireEvent("on" + evt.type, evt);
-  }-*/;
-
-  @Override
-  public EventTarget eventGetCurrentTarget(NativeEvent event) {
-    return currentEventTarget;
-  }
-
-  @Override
-  public native int eventGetMouseWheelVelocityY(NativeEvent evt) /*-{
-    return Math.round(-evt.wheelDelta / 40) || 0;
-  }-*/;
-
-  @Override
-  public native EventTarget eventGetRelatedTarget(NativeEvent evt) /*-{
-    // Prefer 'relatedTarget' if it's set (see createMouseEvent(), which
-    // explicitly sets relatedTarget when synthesizing mouse events).
-    return evt.relatedTarget ||
-           (evt.type == "mouseout" ? evt.toElement:evt.fromElement);
-  }-*/;
-
-  @Override
-  public native EventTarget eventGetTarget(NativeEvent evt) /*-{
-    return evt.srcElement;
-  }-*/;
-
-  @Override
-  public native void eventPreventDefault(NativeEvent evt) /*-{
-    evt.returnValue = false;
-  }-*/;
-
-  @Override
-  public native void eventStopPropagation(NativeEvent evt) /*-{
-    evt.cancelBubble = true;
-  }-*/;
-
-  @Override
-  public native String eventToString(NativeEvent evt) /*-{
-    if (evt.toString) return evt.toString();
-      return "[event" + evt.type + "]";
-  }-*/;
+class DOMImplIE6 extends DOMImplTrident {
 
   @Override
   public int getAbsoluteLeft(Element elem) {
     Document doc = elem.getOwnerDocument();
-    return (int) Math.floor(getBoundingClientRectLeft(elem)
+    return (int) Math.floor(super.getAbsoluteLeft(elem)
         / getZoomMultiple(doc) + doc.getScrollLeft());
   }
 
   @Override
   public int getAbsoluteTop(Element elem) {
     Document doc = elem.getOwnerDocument();
-    return (int) Math.floor(getBoundingClientRectTop(elem)
+    return (int) Math.floor(super.getAbsoluteTop(elem)
         / getZoomMultiple(doc) + doc.getScrollTop());
   }
 
-  /**
-   * IE returns a numeric type for some attributes that are really properties,
-   * such as offsetWidth.  We need to coerce these to strings to prevent a
-   * runtime JS exception.
-   */
-  @Override
-  public native String getAttribute(Element elem, String name) /*-{
-    var attr = elem.getAttribute(name);
-    return attr == null? '' : attr + '';
-  }-*/;
-
-  @Override
-  public int getBodyOffsetLeft(Document doc) {
-    return getClientLeft(doc.getViewportElement());
-  }
-
-  @Override
-  public int getBodyOffsetTop(Document doc) {
-    return getClientTop(doc.getViewportElement());
-  }
-
-  @Override
-  public native String getInnerText(Element elem) /*-{
-    return elem.innerText;
-  }-*/;
-
-  @Override
-  public native Element getParentElement(Element elem) /*-{
-    return elem.parentElement;
-  }-*/;
-
   @Override
   public int getScrollLeft(Element elem) {
     if (isRTL(elem)) {
@@ -223,29 +65,6 @@
   }
 
   @Override
-  public native boolean isOrHasChild(Element parent, Element child) /*-{
-    // An extra equality check is required due to the fact that
-    // elem.contains(elem) is false if elem is not attached to the DOM.
-    return (parent === child) || parent.contains(child);
-  }-*/;
-
-  @Override
-  public native void selectAdd(SelectElement select, OptionElement option,
-      OptionElement before) /*-{
-    // IE only accepts indices for the second argument.
-    if (before) {
-      select.add(option, before.index);
-    } else {
-      select.add(option);
-    }
-  }-*/;
-
-  @Override
-  public native void setInnerText(Element elem, String text) /*-{
-    elem.innerText = text || '';
-  }-*/;
-
-  @Override
   public void setScrollLeft(Element elem, int left) {
     if (isRTL(elem)) {
       left += elem.getScrollWidth() - elem.getClientWidth();
@@ -253,40 +72,6 @@
     super.setScrollLeft(elem, left);
   }
 
-  private native int getBoundingClientRectLeft(Element elem) /*-{
-    // getBoundingClientRect() throws a JS exception if the elem is not attached
-    // to the document, so we wrap it in a try/catch block
-    try {
-      return elem.getBoundingClientRect().left;
-    } catch (e) {
-      return 0;
-    }
-  }-*/;
-
-  private native int getBoundingClientRectTop(Element elem) /*-{
-    // getBoundingClientRect() throws a JS exception if the elem is not attached
-    // to the document, so we wrap it in a try/catch block
-    try {
-      return elem.getBoundingClientRect().top;
-    } catch (e) {
-      return 0;
-    }
-  }-*/;
-
-  /**
-   * clientLeft is non-standard and not implemented on all browsers.
-   */
-  private native int getClientLeft(Element elem) /*-{
-    return elem.clientLeft;
-  }-*/;
-
-  /**
-   * clientTop is non-standard and not implemented on all browsers.
-   */
-  private native int getClientTop(Element elem) /*-{
-    return elem.clientTop;
-  }-*/;
-
   /**
    * Get the zoom multiple based on the current IE zoom level. A multiple of 2.0
    * means that the user has zoomed in to 200%.
@@ -302,8 +87,4 @@
         doc.getBody().getOffsetWidth();
     }
   }
-
-  private native boolean isRTL(Element elem) /*-{
-    return elem.currentStyle.direction == 'rtl';
-  }-*/;
 }
diff --git a/user/src/com/google/gwt/dom/client/DOMImplIE8.java b/user/src/com/google/gwt/dom/client/DOMImplIE8.java
new file mode 100644
index 0000000..e3b3826
--- /dev/null
+++ b/user/src/com/google/gwt/dom/client/DOMImplIE8.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2009 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;
+
+class DOMImplIE8 extends DOMImplTrident {
+}
diff --git a/user/src/com/google/gwt/dom/client/DOMImplTrident.java b/user/src/com/google/gwt/dom/client/DOMImplTrident.java
new file mode 100644
index 0000000..e51398b
--- /dev/null
+++ b/user/src/com/google/gwt/dom/client/DOMImplTrident.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2009 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;
+
+abstract class DOMImplTrident extends DOMImpl {
+
+  /**
+   * This field *must* be filled in from JSNI code before dispatching an event
+   * on IE. It should be set to the 'this' context of the handler that receives
+   * the event, then restored to its initial value when the dispatcher is done.
+   * See {@link com.google.gwt.user.client.impl.DOMImplTrident#initEventSystem()}
+   * for an example of how this should be done.
+   */
+  private static EventTarget currentEventTarget;
+
+  @Override
+  public native NativeEvent createHtmlEvent(Document doc, String type, boolean canBubble,
+      boolean cancelable) /*-{
+    // NOTE: IE doesn't support changing bubbling and canceling behavior (this
+    // is documented publicly in Document.createHtmlEvent()).
+    var evt = doc.createEventObject();
+    evt.type = type;
+    return evt;
+  }-*/;
+
+  @Override
+  public native InputElement createInputRadioElement(Document doc, String name) /*-{
+    return doc.createElement("<INPUT type='RADIO' name='" + name + "'>");
+  }-*/;
+
+  @Override
+  public native NativeEvent createKeyEvent(Document doc, String type, boolean canBubble,
+      boolean cancelable, boolean ctrlKey, boolean altKey, boolean shiftKey,
+      boolean metaKey, int keyCode, int charCode) /*-{
+    // NOTE: IE doesn't support changing bubbling and canceling behavior (this
+    // is documented publicly in Document.createKeyEvent()).
+    var evt = doc.createEventObject();
+    evt.type = type;
+    evt.ctrlKey = ctrlKey;
+    evt.altKey = altKey;
+    evt.shiftKey = shiftKey;
+    evt.metaKey = metaKey;
+    evt.keyCode = keyCode;
+    evt.charCode = charCode;
+
+    return evt;
+  }-*/;
+
+  @Override
+  public native NativeEvent createMouseEvent(Document doc, String type, boolean canBubble,
+      boolean cancelable, int detail, int screenX, int screenY, int clientX,
+      int clientY, boolean ctrlKey, boolean altKey, boolean shiftKey,
+      boolean metaKey, int button, Element relatedTarget) /*-{
+    // NOTE: IE doesn't support changing bubbling and canceling behavior (this
+    // is documented publicly in Document.createMouseEvent()).
+    var evt = doc.createEventObject();
+    evt.type = type;
+    evt.detail = detail;
+    evt.screenX = screenX;
+    evt.screenY = screenY;
+    evt.clientX = clientX;
+    evt.clientY = clientY;
+    evt.ctrlKey = ctrlKey;
+    evt.altKey = altKey;
+    evt.shiftKey = shiftKey;
+    evt.metaKey = metaKey;
+    evt.button = button;
+
+    // It would make sense to set evt.[fromElement | toElement] here, because
+    // that's what IE uses. However, setting these properties has no effect for
+    // some reason. So instead we set releatedTarget, and explicitly check for
+    // its existence in eventGetFromElement() and eventGetToElement().
+    evt.relatedTarget = relatedTarget;
+
+    return evt;
+  }-*/;
+
+  /**
+   * Supports creating a select control with the multiple attribute to work
+   * around a bug in IE6 where changing the multiple attribute in a setAttribute
+   * call can cause subsequent setSelected calls to misbehave. Although this bug
+   * is fixed in IE7, this DOMImpl specialization is used for both IE6 and IE7,
+   * but it should be harmless.
+   */
+  @Override
+  public native SelectElement createSelectElement(Document doc, boolean multiple) /*-{
+    var html = multiple ? "<SELECT MULTIPLE>" : "<SELECT>"; 
+    return doc.createElement(html);
+  }-*/;
+
+  public native void dispatchEvent(Element target, NativeEvent evt) /*-{
+    target.fireEvent("on" + evt.type, evt);
+  }-*/;
+
+  @Override
+  public EventTarget eventGetCurrentTarget(NativeEvent event) {
+    return currentEventTarget;
+  }
+
+  @Override
+  public native int eventGetMouseWheelVelocityY(NativeEvent evt) /*-{
+    return Math.round(-evt.wheelDelta / 40) || 0;
+  }-*/;
+
+  @Override
+  public native EventTarget eventGetRelatedTarget(NativeEvent evt) /*-{
+    // Prefer 'relatedTarget' if it's set (see createMouseEvent(), which
+    // explicitly sets relatedTarget when synthesizing mouse events).
+    return evt.relatedTarget ||
+           (evt.type == "mouseout" ? evt.toElement:evt.fromElement);
+  }-*/;
+
+  @Override
+  public native EventTarget eventGetTarget(NativeEvent evt) /*-{
+    return evt.srcElement;
+  }-*/;
+
+  @Override
+  public native void eventPreventDefault(NativeEvent evt) /*-{
+    evt.returnValue = false;
+  }-*/;
+
+  @Override
+  public native void eventStopPropagation(NativeEvent evt) /*-{
+    evt.cancelBubble = true;
+  }-*/;
+
+  @Override
+  public native String eventToString(NativeEvent evt) /*-{
+    if (evt.toString) return evt.toString();
+    return "[event" + evt.type + "]";
+  }-*/;
+
+  @Override
+  public int getAbsoluteLeft(Element elem) {
+    return getBoundingClientRectLeft(elem);
+  }
+
+  @Override
+  public int getAbsoluteTop(Element elem) {
+    return getBoundingClientRectTop(elem);
+  }
+
+  /**
+   * IE returns a numeric type for some attributes that are really properties,
+   * such as offsetWidth.  We need to coerce these to strings to prevent a
+   * runtime JS exception.
+   */
+  @Override
+  public native String getAttribute(Element elem, String name) /*-{
+    var attr = elem.getAttribute(name);
+    return attr == null ? '' : attr + '';
+  }-*/;
+
+  @Override
+  public int getBodyOffsetLeft(Document doc) {
+    return getClientLeft(doc.getViewportElement());
+  }
+
+  @Override
+  public int getBodyOffsetTop(Document doc) {
+    return getClientTop(doc.getViewportElement());
+  }
+
+  @Override
+  public native String getInnerText(Element elem) /*-{
+    return elem.innerText;
+  }-*/;
+
+  @Override
+  public native Element getParentElement(Element elem) /*-{
+    return elem.parentElement;
+  }-*/;
+
+  @Override
+  public int getScrollLeft(Element elem) {
+    if (isRTL(elem)) {
+      // IE8 returns increasingly *positive* values as you scroll left in RTL.
+      return -super.getScrollLeft(elem);
+    }
+    return super.getScrollLeft(elem);
+  }
+
+  @Override
+  public native boolean isOrHasChild(Element parent, Element child) /*-{
+    // An extra equality check is required due to the fact that
+    // elem.contains(elem) is false if elem is not attached to the DOM.
+    return (parent === child) || parent.contains(child);
+  }-*/;
+
+  @Override
+  public native void selectAdd(SelectElement select, OptionElement option,
+      OptionElement before) /*-{
+    // IE only accepts indices for the second argument.
+    if (before) {
+      select.add(option, before.index);
+    } else {
+      select.add(option);
+    }
+  }-*/;
+
+  @Override
+  public native void setInnerText(Element elem, String text) /*-{
+    elem.innerText = text || '';
+  }-*/;
+
+  @Override
+  public void setScrollLeft(Element elem, int left) {
+    if (isRTL(elem)) {
+      // IE8 returns increasingly *positive* values as you scroll left in RTL.
+      left = -left;
+    }
+    super.setScrollLeft(elem, left);
+  }
+
+  private native int getBoundingClientRectLeft(Element elem) /*-{
+    // getBoundingClientRect() throws a JS exception if the elem is not attached
+    // to the document, so we wrap it in a try/catch block
+    try {
+      return elem.getBoundingClientRect().left;
+    } catch (e) {
+      return 0;
+    }
+  }-*/;
+
+  private native int getBoundingClientRectTop(Element elem) /*-{
+    // getBoundingClientRect() throws a JS exception if the elem is not attached
+    // to the document, so we wrap it in a try/catch block
+    try {
+      return elem.getBoundingClientRect().top;
+    } catch (e) {
+      return 0;
+    }
+  }-*/;
+
+  /**
+   * clientLeft is non-standard and not implemented on all browsers.
+   */
+  private native int getClientLeft(Element elem) /*-{
+    return elem.clientLeft;
+  }-*/;
+
+  /**
+   * clientTop is non-standard and not implemented on all browsers.
+   */
+  private native int getClientTop(Element elem) /*-{
+    return elem.clientTop;
+  }-*/;
+
+  protected native boolean isRTL(Element elem) /*-{
+    return elem.currentStyle.direction == 'rtl';
+  }-*/;
+}
diff --git a/user/src/com/google/gwt/dom/client/Element.java b/user/src/com/google/gwt/dom/client/Element.java
index 4745c28..ab15aaa 100644
--- a/user/src/com/google/gwt/dom/client/Element.java
+++ b/user/src/com/google/gwt/dom/client/Element.java
@@ -516,4 +516,15 @@
      // on some browsers.
      this.title = title || '';
    }-*/;
+
+  /**
+   * Determines whether an element has an attribute with a given name.
+   * 
+   * @param name the name of the attribute
+   * @return whether this element has the specified attribute
+   */
+  public final boolean hasAttribute(String name) {
+    String value = this.getAttribute(name);
+    return value != null && value.length() > 0;
+  }
 }
diff --git a/user/src/com/google/gwt/user/ClippedImage.gwt.xml b/user/src/com/google/gwt/user/ClippedImage.gwt.xml
index d333d77..5a58532 100644
--- a/user/src/com/google/gwt/user/ClippedImage.gwt.xml
+++ b/user/src/com/google/gwt/user/ClippedImage.gwt.xml
@@ -27,7 +27,7 @@
 	<inherits name="com.google.gwt.core.Core" />
 	<inherits name="com.google.gwt.user.UserAgent" />
 
-	<!-- IE needs a different implementation -->
+	<!-- IE6 needs a different implementation. IE8 does not. -->
 	<replace-with
 		class="com.google.gwt.user.client.ui.impl.ClippedImageImplIE6">
 		<when-type-is
diff --git a/user/src/com/google/gwt/user/DOM.gwt.xml b/user/src/com/google/gwt/user/DOM.gwt.xml
index 936c6e3..110d6e0 100644
--- a/user/src/com/google/gwt/user/DOM.gwt.xml
+++ b/user/src/com/google/gwt/user/DOM.gwt.xml
@@ -31,6 +31,11 @@
 		<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.DOMImplIE6">
 		<when-type-is class="com.google.gwt.user.client.impl.DOMImpl"/>
 		<when-property-is name="user.agent" value="ie6"/>
diff --git a/user/src/com/google/gwt/user/Focus.gwt.xml b/user/src/com/google/gwt/user/Focus.gwt.xml
index 9c591a8..f3f83f2 100644
--- a/user/src/com/google/gwt/user/Focus.gwt.xml
+++ b/user/src/com/google/gwt/user/Focus.gwt.xml
@@ -17,29 +17,29 @@
 <!-- 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"/>
 
-	<!-- old Mozilla needs a different implementation -->
-	<replace-with class="com.google.gwt.user.client.ui.impl.FocusImplOld">
-		<when-type-is class="com.google.gwt.user.client.ui.impl.FocusImpl"/>
-		<any>
-		    <when-property-is name="user.agent" value="gecko"/>
- 	    </any>
-	</replace-with>
+  <!-- old Mozilla needs a different implementation -->
+  <replace-with class="com.google.gwt.user.client.ui.impl.FocusImplOld">
+    <when-type-is class="com.google.gwt.user.client.ui.impl.FocusImpl"/>
+    <any>
+      <when-property-is name="user.agent" value="gecko"/>
+    </any>
+  </replace-with>
 
-	<!--  Safari needs a different hidden input -->	
-	<replace-with class="com.google.gwt.user.client.ui.impl.FocusImplSafari">
-		<when-type-is class="com.google.gwt.user.client.ui.impl.FocusImpl"/>
-		<when-property-is name="user.agent" value="safari"/>
-	</replace-with>
+  <!--  Safari needs a different hidden input -->
+  <replace-with class="com.google.gwt.user.client.ui.impl.FocusImplSafari">
+    <when-type-is class="com.google.gwt.user.client.ui.impl.FocusImpl"/>
+    <when-property-is name="user.agent" value="safari"/>
+  </replace-with>
 
-	<!-- IE's implementation traps exceptions on invalid setFocus() -->
-	<replace-with class="com.google.gwt.user.client.ui.impl.FocusImplIE6">
-		<when-type-is class="com.google.gwt.user.client.ui.impl.FocusImpl"/>
-		<any>
-		    <when-property-is name="user.agent" value="ie6"/>
- 	    </any>
-	</replace-with>
-
+  <!-- IE's implementation traps exceptions on invalid setFocus() -->
+  <replace-with class="com.google.gwt.user.client.ui.impl.FocusImplIE6">
+    <when-type-is class="com.google.gwt.user.client.ui.impl.FocusImpl"/>
+    <any>
+      <when-property-is name="user.agent" value="ie6"/>
+      <when-property-is name="user.agent" value="ie8"/>
+    </any>
+  </replace-with>
 </module>
diff --git a/user/src/com/google/gwt/user/Form.gwt.xml b/user/src/com/google/gwt/user/Form.gwt.xml
index 0db301e..c02bfb8 100644
--- a/user/src/com/google/gwt/user/Form.gwt.xml
+++ b/user/src/com/google/gwt/user/Form.gwt.xml
@@ -17,16 +17,19 @@
 <!-- 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"/>
 
-	<!-- Fall through to this rule for everything but IE -->
-	<replace-with class="com.google.gwt.user.client.ui.impl.FormPanelImpl">
-		<when-type-is class="com.google.gwt.user.client.ui.impl.FormPanelImpl"/>
-	</replace-with>
+  <!-- Fall through to this rule for everything but IE -->
+  <replace-with class="com.google.gwt.user.client.ui.impl.FormPanelImpl">
+    <when-type-is class="com.google.gwt.user.client.ui.impl.FormPanelImpl"/>
+  </replace-with>
 
-	<replace-with class="com.google.gwt.user.client.ui.impl.FormPanelImplIE6">
-		<when-type-is class="com.google.gwt.user.client.ui.impl.FormPanelImpl"/>
-		<when-property-is name="user.agent" value="ie6"/>
-	</replace-with>
+  <replace-with class="com.google.gwt.user.client.ui.impl.FormPanelImplIE6">
+    <when-type-is class="com.google.gwt.user.client.ui.impl.FormPanelImpl"/>
+    <any>
+      <when-property-is name="user.agent" value="ie6"/>
+      <when-property-is name="user.agent" value="ie8"/>
+    </any>
+  </replace-with>
 </module>
diff --git a/user/src/com/google/gwt/user/History.gwt.xml b/user/src/com/google/gwt/user/History.gwt.xml
index a6bfaec..bad06a1 100644
--- a/user/src/com/google/gwt/user/History.gwt.xml
+++ b/user/src/com/google/gwt/user/History.gwt.xml
@@ -20,11 +20,6 @@
 	<inherits name="com.google.gwt.core.Core"/>
 	<inherits name="com.google.gwt.user.UserAgent"/>
 
-	<!-- Fall through to this rule for all other browsers. -->
-	<replace-with class="com.google.gwt.user.client.impl.HistoryImplStandard">
-		<when-type-is class="com.google.gwt.user.client.impl.HistoryImpl"/>
-	</replace-with>
-
   <!-- Mozilla has a slightly different history implementation than the     -->
   <!-- standard case.                                                       -->
   <replace-with class="com.google.gwt.user.client.impl.HistoryImplMozilla">
@@ -35,15 +30,16 @@
 		</any>
   </replace-with>
 
-  <!-- IE has a completely different history implementation. -->
-	<replace-with class="com.google.gwt.user.client.impl.HistoryImplIE6">
-		<when-type-is class="com.google.gwt.user.client.impl.HistoryImpl"/>
-		<when-property-is name="user.agent" value="ie6"/>
-	</replace-with>
-
-	<!-- Safari has yet another history implementation. -->
+	<!-- Safari has yet another slightly different implementation. -->
 	<replace-with class="com.google.gwt.user.client.impl.HistoryImplSafari">
 		<when-type-is class="com.google.gwt.user.client.impl.HistoryImpl"/>
 		<when-property-is name="user.agent" value="safari"/>
 	</replace-with>
+
+  <!-- IE6 has a completely different history implementation. IE8 used the -->
+  <!-- standard implementation. -->
+	<replace-with class="com.google.gwt.user.client.impl.HistoryImplIE6">
+		<when-type-is class="com.google.gwt.user.client.impl.HistoryImpl"/>
+		<when-property-is name="user.agent" value="ie6"/>
+	</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 e667c13..787fbc7 100644
--- a/user/src/com/google/gwt/user/Hyperlink.gwt.xml
+++ b/user/src/com/google/gwt/user/Hyperlink.gwt.xml
@@ -14,6 +14,9 @@
 
   <replace-with class="com.google.gwt.user.client.ui.impl.HyperlinkImplIE">
     <when-type-is class="com.google.gwt.user.client.ui.impl.HyperlinkImpl"/>
-    <when-property-is name="user.agent" value="ie6"/>
+    <any>
+	    <when-property-is name="user.agent" value="ie6"/>
+	    <when-property-is name="user.agent" value="ie8"/>
+    </any>
   </replace-with>
 </module>
diff --git a/user/src/com/google/gwt/user/Popup.gwt.xml b/user/src/com/google/gwt/user/Popup.gwt.xml
index b5d06bf..ee886c8 100644
--- a/user/src/com/google/gwt/user/Popup.gwt.xml
+++ b/user/src/com/google/gwt/user/Popup.gwt.xml
@@ -20,11 +20,6 @@
   <inherits name="com.google.gwt.core.Core"/>
   <inherits name="com.google.gwt.user.UserAgent"/>
 
-  <!-- Fall through to this rule is the browser isn't IE or Mozilla -->
-  <replace-with class="com.google.gwt.user.client.ui.impl.PopupImpl">
-    <when-type-is class="com.google.gwt.user.client.ui.impl.PopupImpl"/>
-  </replace-with>
-
   <!-- Mozilla needs a different implementation due to issue #410 -->
   <replace-with class="com.google.gwt.user.client.ui.impl.PopupImplMozilla">
     <when-type-is class="com.google.gwt.user.client.ui.impl.PopupImpl"/>
@@ -33,8 +28,9 @@
       <when-property-is name="user.agent" value="gecko1_8"/>
     </any>
   </replace-with>
-  
-  <!-- IE has a completely different popup implementation -->
+
+  <!-- IE6 has a completely different popup implementation. It is no longer -->
+  <!-- necessary on IE8. -->
   <replace-with class="com.google.gwt.user.client.ui.impl.PopupImplIE6">
     <when-type-is class="com.google.gwt.user.client.ui.impl.PopupImpl"/>
     <when-property-is name="user.agent" value="ie6"/>
diff --git a/user/src/com/google/gwt/user/RichText.gwt.xml b/user/src/com/google/gwt/user/RichText.gwt.xml
index ff2fb56..76dba69 100644
--- a/user/src/com/google/gwt/user/RichText.gwt.xml
+++ b/user/src/com/google/gwt/user/RichText.gwt.xml
@@ -15,45 +15,48 @@
 <!-- Deferred binding rules for browser selection.                          -->
 <!--                                                                        -->
 <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" />
 
-	<!-- IE-specific implementation -->
-	<replace-with
-		class="com.google.gwt.user.client.ui.impl.RichTextAreaImplIE6">
-		<when-type-is
-			class="com.google.gwt.user.client.ui.impl.RichTextAreaImpl" />
-		<when-property-is name="user.agent" value="ie6" />
-	</replace-with>
+  <!-- IE-specific implementation -->
+  <replace-with
+    class="com.google.gwt.user.client.ui.impl.RichTextAreaImplIE6">
+    <when-type-is
+      class="com.google.gwt.user.client.ui.impl.RichTextAreaImpl" />
+    <any>
+      <when-property-is name="user.agent" value="ie6" />
+      <when-property-is name="user.agent" value="ie8" />
+    </any>
+  </replace-with>
 
-	<!-- Mozilla-specific implementation -->
-	<replace-with
-		class="com.google.gwt.user.client.ui.impl.RichTextAreaImplMozilla">
-		<when-type-is
-			class="com.google.gwt.user.client.ui.impl.RichTextAreaImpl" />
-		<any>
-			<when-property-is name="user.agent" value="gecko1_8" />
-			<when-property-is name="user.agent" value="gecko" />
-		</any>
-	</replace-with>
+  <!-- Mozilla-specific implementation -->
+  <replace-with
+    class="com.google.gwt.user.client.ui.impl.RichTextAreaImplMozilla">
+    <when-type-is
+      class="com.google.gwt.user.client.ui.impl.RichTextAreaImpl" />
+    <any>
+      <when-property-is name="user.agent" value="gecko1_8" />
+      <when-property-is name="user.agent" value="gecko" />
+    </any>
+  </replace-with>
 
-	<!-- Safari-specific implementation -->
-	<replace-with
-		class="com.google.gwt.user.client.ui.impl.RichTextAreaImplSafari">
-		<when-type-is
-			class="com.google.gwt.user.client.ui.impl.RichTextAreaImpl" />
-		<any>
-			<when-property-is name="user.agent" value="safari" />
-		</any>
-	</replace-with>
+  <!-- Safari-specific implementation -->
+  <replace-with
+    class="com.google.gwt.user.client.ui.impl.RichTextAreaImplSafari">
+    <when-type-is
+      class="com.google.gwt.user.client.ui.impl.RichTextAreaImpl" />
+    <any>
+      <when-property-is name="user.agent" value="safari" />
+    </any>
+  </replace-with>
 
-	<!-- Opera-specific implementation -->
-	<replace-with
-		class="com.google.gwt.user.client.ui.impl.RichTextAreaImplOpera">
-		<when-type-is
-			class="com.google.gwt.user.client.ui.impl.RichTextAreaImpl" />
-		<any>
-			<when-property-is name="user.agent" value="opera" />
-		</any>
-	</replace-with>
+  <!-- Opera-specific implementation -->
+  <replace-with
+    class="com.google.gwt.user.client.ui.impl.RichTextAreaImplOpera">
+    <when-type-is
+      class="com.google.gwt.user.client.ui.impl.RichTextAreaImpl" />
+    <any>
+      <when-property-is name="user.agent" value="opera" />
+    </any>
+  </replace-with>
 </module>
diff --git a/user/src/com/google/gwt/user/SplitPanel.gwt.xml b/user/src/com/google/gwt/user/SplitPanel.gwt.xml
index a0bb8db..9e09bcc 100644
--- a/user/src/com/google/gwt/user/SplitPanel.gwt.xml
+++ b/user/src/com/google/gwt/user/SplitPanel.gwt.xml
@@ -22,7 +22,10 @@
 
   <replace-with class="com.google.gwt.user.client.ui.HorizontalSplitPanel.ImplIE6">
     <when-type-is class="com.google.gwt.user.client.ui.HorizontalSplitPanel.Impl"/>
-    <when-property-is name="user.agent" value="ie6"/>
+    <any>
+	    <when-property-is name="user.agent" value="ie6"/>
+	    <when-property-is name="user.agent" value="ie8"/>
+	  </any>
   </replace-with>
 
   <replace-with class="com.google.gwt.user.client.ui.HorizontalSplitPanel.ImplSafari">
@@ -36,7 +39,9 @@
 
   <replace-with class="com.google.gwt.user.client.ui.VerticalSplitPanel.ImplIE6">
     <when-type-is class="com.google.gwt.user.client.ui.VerticalSplitPanel.Impl"/>
-    <when-property-is name="user.agent" value="ie6"/>
+    <any>
+	    <when-property-is name="user.agent" value="ie6"/>
+	    <when-property-is name="user.agent" value="ie8"/>
+    </any>
   </replace-with>
-
 </module>
diff --git a/user/src/com/google/gwt/user/TextBox.gwt.xml b/user/src/com/google/gwt/user/TextBox.gwt.xml
index 1b27b62..fc99c19 100644
--- a/user/src/com/google/gwt/user/TextBox.gwt.xml
+++ b/user/src/com/google/gwt/user/TextBox.gwt.xml
@@ -17,17 +17,20 @@
 <!-- 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"/>
 
-	<!-- Fall through to this rule is the browser isn't IE -->
-	<replace-with class="com.google.gwt.user.client.ui.impl.TextBoxImpl">
-		<when-type-is class="com.google.gwt.user.client.ui.impl.TextBoxImpl"/>
-	</replace-with>
+  <!-- Fall through to this rule is the browser isn't IE -->
+  <replace-with class="com.google.gwt.user.client.ui.impl.TextBoxImpl">
+    <when-type-is class="com.google.gwt.user.client.ui.impl.TextBoxImpl"/>
+  </replace-with>
 
-	<!-- IE has a completely different TextBox implementation -->
-	<replace-with class="com.google.gwt.user.client.ui.impl.TextBoxImplIE6">
-		<when-type-is class="com.google.gwt.user.client.ui.impl.TextBoxImpl"/>
-		<when-property-is name="user.agent" value="ie6"/>
-	</replace-with>
+  <!-- IE has a completely different TextBox implementation -->
+  <replace-with class="com.google.gwt.user.client.ui.impl.TextBoxImplIE6">
+    <when-type-is class="com.google.gwt.user.client.ui.impl.TextBoxImpl"/>
+    <any>
+      <when-property-is name="user.agent" value="ie6"/>
+      <when-property-is name="user.agent" value="ie8"/>
+    </any>
+  </replace-with>
 </module>
diff --git a/user/src/com/google/gwt/user/Tree.gwt.xml b/user/src/com/google/gwt/user/Tree.gwt.xml
index 626f52e..94b14e5 100644
--- a/user/src/com/google/gwt/user/Tree.gwt.xml
+++ b/user/src/com/google/gwt/user/Tree.gwt.xml
@@ -20,6 +20,9 @@
   <!-- IE-specific implementation -->
   <replace-with class="com.google.gwt.user.client.ui.TreeItem.TreeItemImplIE6">
     <when-type-is class="com.google.gwt.user.client.ui.TreeItem.TreeItemImpl"/>
-    <when-property-is name="user.agent" value="ie6"/>
+    <any>
+	    <when-property-is name="user.agent" value="ie6"/>
+	    <when-property-is name="user.agent" value="ie8"/>
+	  </any>
   </replace-with>
 </module>
diff --git a/user/src/com/google/gwt/user/UserAgent.gwt.xml b/user/src/com/google/gwt/user/UserAgent.gwt.xml
index d073cd7..af299b3 100644
--- a/user/src/com/google/gwt/user/UserAgent.gwt.xml
+++ b/user/src/com/google/gwt/user/UserAgent.gwt.xml
@@ -19,12 +19,12 @@
 <module>
 
   <!-- Browser-sensitive code should use the 'user.agent' property -->
-  <define-property name="user.agent" values="ie6,gecko,gecko1_8,safari,opera"/>
+  <define-property name="user.agent" values="ie6,ie8,gecko,gecko1_8,safari,opera"/>
 
   <property-provider name="user.agent"><![CDATA[
       var ua = navigator.userAgent.toLowerCase();
       var makeVersion = function(result) {
-          return (parseInt(result[1]) * 1000) + parseInt(result[2]);
+        return (parseInt(result[1]) * 1000) + parseInt(result[2]);
       };
 
       if (ua.indexOf("opera") != -1) {
@@ -32,10 +32,15 @@
       } else if (ua.indexOf("webkit") != -1) {
         return "safari";
       } else if (ua.indexOf("msie") != -1) {
-        var result = /msie ([0-9]+)\.([0-9]+)/.exec(ua);
-        if (result && result.length == 3) {
-          if (makeVersion(result) >= 6000) {
-            return "ie6";
+        if (document.documentMode >= 8) {
+          return "ie8";
+        } else {
+          var result = /msie ([0-9]+)\.([0-9]+)/.exec(ua);
+          if (result && result.length == 3) {
+            var v = makeVersion(result);
+            if (v >= 6000) {
+              return "ie6";
+            }
           }
         }
       } else if (ua.indexOf("gecko") != -1) {
diff --git a/user/src/com/google/gwt/user/Window.gwt.xml b/user/src/com/google/gwt/user/Window.gwt.xml
index 9511634..8ae46ce 100644
--- a/user/src/com/google/gwt/user/Window.gwt.xml
+++ b/user/src/com/google/gwt/user/Window.gwt.xml
@@ -22,6 +22,9 @@
 
    <replace-with class="com.google.gwt.user.client.impl.WindowImplIE">
       <when-type-is class="com.google.gwt.user.client.impl.WindowImpl"/>
-      <when-property-is name="user.agent" value="ie6"/>
+      <any>
+	      <when-property-is name="user.agent" value="ie6"/>
+	      <when-property-is name="user.agent" value="ie8"/>
+	    </any>
    </replace-with>
 </module>
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImplIE6.java b/user/src/com/google/gwt/user/client/impl/DOMImplIE6.java
index 3ce49bd..d71460d 100644
--- a/user/src/com/google/gwt/user/client/impl/DOMImplIE6.java
+++ b/user/src/com/google/gwt/user/client/impl/DOMImplIE6.java
@@ -15,202 +15,9 @@
  */
 package com.google.gwt.user.client.impl;
 
-import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.user.client.Element;
-import com.google.gwt.user.client.Event;
-
 /**
  * Internet Explorer 6 implementation of
  * {@link com.google.gwt.user.client.impl.DOMImpl}.
  */
-class DOMImplIE6 extends DOMImpl {
-
-  @SuppressWarnings("unused")
-  private static JavaScriptObject dispatchEvent;
-
-  @SuppressWarnings("unused")
-  private static JavaScriptObject dispatchDblClickEvent;
-
-  @Override
-  public native Element eventGetFromElement(Event evt) /*-{
-    // Prefer 'relatedTarget' if it's set (see createMouseEvent(), which
-    // explicitly sets relatedTarget when synthesizing mouse events).
-    return evt.relatedTarget || evt.fromElement;
-  }-*/;
-
-  @Override
-  public native Element eventGetToElement(Event evt) /*-{
-    // Prefer 'relatedTarget' if it's set (see createMouseEvent(), which
-    // explicitly sets relatedTarget when synthesizing mouse events).
-    return evt.relatedTarget || evt.toElement;
-  }-*/;
-
-  @Override
-  public native Element getChild(Element elem, int index) /*-{
-    return elem.children[index];
-  }-*/;
-
-  @Override
-  public native int getChildCount(Element elem) /*-{
-    return elem.children.length;
-  }-*/;
-
-  @Override
-  public native int getChildIndex(Element parent, Element child) /*-{
-    var count = parent.children.length;
-    for (var i = 0; i < count; ++i) {
-      if (child === parent.children[i]) {
-        return i;
-      }
-    }
-    return -1;
-  }-*/;
-
-  @Override
-  public native void initEventSystem() /*-{
-    @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent = function() {
-      // IE doesn't define event.currentTarget, so we squirrel it away here. It
-      // also seems that IE won't allow you to add expandos to the event object,
-      // so we have to store it in a global. This is ok because only one event
-      // can actually be dispatched at a time.
-      var oldEventTarget = @com.google.gwt.dom.client.DOMImplIE6::currentEventTarget;
-      @com.google.gwt.dom.client.DOMImplIE6::currentEventTarget = this;
-
-      if ($wnd.event.returnValue == null) {
-        $wnd.event.returnValue = true;
-        if (!@com.google.gwt.user.client.DOM::previewEvent(Lcom/google/gwt/user/client/Event;)($wnd.event)) {
-          @com.google.gwt.dom.client.DOMImplIE6::currentEventTarget = oldEventTarget;
-          return;
-        }
-      }
-
-      var listener, curElem = this;
-      while (curElem && !(listener = curElem.__listener)) {
-        curElem = curElem.parentElement;
-      }
-
-      if (listener) {
-        if (@com.google.gwt.user.client.impl.DOMImpl::isMyListener(Ljava/lang/Object;)(listener)) {
-          @com.google.gwt.user.client.DOM::dispatchEvent(Lcom/google/gwt/user/client/Event;Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/EventListener;)($wnd.event, curElem, listener);
-        }
-      }
-
-      @com.google.gwt.dom.client.DOMImplIE6::currentEventTarget = oldEventTarget;
-    };
-
-    @com.google.gwt.user.client.impl.DOMImplIE6::dispatchDblClickEvent = function() {
-      var newEvent = $doc.createEventObject();
-      // Synthesize a click event if one hasn't already been synthesized.
-      if ($wnd.event.returnValue == null) {
-        $wnd.event.srcElement.fireEvent('onclick', newEvent);
-      }
-      if (this.__eventBits & 2) {
-        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent.call(this);
-      } else if ($wnd.event.returnValue == null) {
-        // Ensure that we preview the event even if we aren't handling it.
-        $wnd.event.returnValue = true;
-        @com.google.gwt.user.client.DOM::previewEvent(Lcom/google/gwt/user/client/Event;)($wnd.event);
-      }
-    };
-
-    // We need to create these delegate functions to fix up the 'this' context.
-    // Normally, 'this' is the firing element, but this is only true for
-    // 'onclick = ...' event handlers, not for handlers setup via attachEvent().
-    var bodyDispatcher = function() { @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent.call($doc.body); };
-    var bodyDblClickDispatcher = function() { @com.google.gwt.user.client.impl.DOMImplIE6::dispatchDblClickEvent.call($doc.body); };
-
-    $doc.body.attachEvent('onclick', bodyDispatcher);
-    $doc.body.attachEvent('onmousedown', bodyDispatcher);
-    $doc.body.attachEvent('onmouseup', bodyDispatcher);
-    $doc.body.attachEvent('onmousemove', bodyDispatcher);
-    $doc.body.attachEvent('onmousewheel', bodyDispatcher);
-    $doc.body.attachEvent('onkeydown', bodyDispatcher);
-    $doc.body.attachEvent('onkeypress', bodyDispatcher);
-    $doc.body.attachEvent('onkeyup', bodyDispatcher);
-    $doc.body.attachEvent('onfocus', bodyDispatcher);
-    $doc.body.attachEvent('onblur', bodyDispatcher);
-    $doc.body.attachEvent('ondblclick', bodyDblClickDispatcher);
-    $doc.body.attachEvent('oncontextmenu', bodyDispatcher);
-  }-*/;
-
-  @Override
-  public native void insertChild(Element parent, Element child, int index) /*-{
-    if (index >= parent.children.length)
-      parent.appendChild(child);
-    else
-      parent.insertBefore(child, parent.children[index]);
-  }-*/;
-
-  @Override
-  public void releaseCapture(Element elem) {
-    maybeInitializeEventSystem();
-    releaseCaptureImpl(elem);
-  }
-
-  @Override
-  public void setCapture(Element elem) {
-    maybeInitializeEventSystem();
-    setCaptureImpl(elem);
-  }
-
-  @Override
-  public void sinkEvents(Element elem, int bits) {
-    maybeInitializeEventSystem();
-    sinkEventsImpl(elem, bits);
-  }
-
-  private native void releaseCaptureImpl(Element elem) /*-{
-    elem.releaseCapture();
-  }-*/;
-
-  private native void setCaptureImpl(Element elem) /*-{
-    elem.setCapture();
-  }-*/;
-
-  private native void sinkEventsImpl(Element elem, int bits) /*-{
-    var chMask = (elem.__eventBits || 0) ^ bits;
-    elem.__eventBits = bits;
-    if (!chMask) return;
-
-    if (chMask & 0x00001) elem.onclick       = (bits & 0x00001) ?
-        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
-    // Add a ondblclick handler if onclick is desired to ensure that 
-    // a user's double click will result in two onclick events.
-    if (chMask & (0x00003)) elem.ondblclick  = (bits & (0x00003)) ?
-        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchDblClickEvent : null;
-    if (chMask & 0x00004) elem.onmousedown   = (bits & 0x00004) ?
-        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
-    if (chMask & 0x00008) elem.onmouseup     = (bits & 0x00008) ?
-        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
-    if (chMask & 0x00010) elem.onmouseover   = (bits & 0x00010) ?
-        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
-    if (chMask & 0x00020) elem.onmouseout    = (bits & 0x00020) ?
-        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
-    if (chMask & 0x00040) elem.onmousemove   = (bits & 0x00040) ?
-        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
-    if (chMask & 0x00080) elem.onkeydown     = (bits & 0x00080) ?
-        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
-    if (chMask & 0x00100) elem.onkeypress    = (bits & 0x00100) ?
-        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
-    if (chMask & 0x00200) elem.onkeyup       = (bits & 0x00200) ?
-        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
-    if (chMask & 0x00400) elem.onchange      = (bits & 0x00400) ?
-        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
-    if (chMask & 0x00800) elem.onfocus       = (bits & 0x00800) ?
-        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
-    if (chMask & 0x01000) elem.onblur        = (bits & 0x01000) ?
-        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
-    if (chMask & 0x02000) elem.onlosecapture = (bits & 0x02000) ?
-        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
-    if (chMask & 0x04000) elem.onscroll      = (bits & 0x04000) ?
-        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
-    if (chMask & 0x08000) elem.onload        = (bits & 0x08000) ?
-        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
-    if (chMask & 0x10000) elem.onerror       = (bits & 0x10000) ?
-        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
-    if (chMask & 0x20000) elem.onmousewheel  = (bits & 0x20000) ? 
-        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
-    if (chMask & 0x40000) elem.oncontextmenu = (bits & 0x40000) ? 
-        @com.google.gwt.user.client.impl.DOMImplIE6::dispatchEvent : null;
-  }-*/;
+class DOMImplIE6 extends DOMImplTrident {
 }
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImplIE8.java b/user/src/com/google/gwt/user/client/impl/DOMImplIE8.java
new file mode 100644
index 0000000..e8ea7b8
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/impl/DOMImplIE8.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2009 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;
+
+public class DOMImplIE8 extends DOMImplTrident {
+}
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImplTrident.java b/user/src/com/google/gwt/user/client/impl/DOMImplTrident.java
new file mode 100644
index 0000000..4923ff5
--- /dev/null
+++ b/user/src/com/google/gwt/user/client/impl/DOMImplTrident.java
@@ -0,0 +1,212 @@
+/*
+ * Copyright 2009 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.JavaScriptObject;
+import com.google.gwt.user.client.Element;
+import com.google.gwt.user.client.Event;
+
+public abstract class DOMImplTrident extends DOMImpl {
+
+  @SuppressWarnings("unused")
+  private static JavaScriptObject dispatchEvent;
+
+  @SuppressWarnings("unused")
+  private static JavaScriptObject dispatchDblClickEvent;
+
+  @Override
+  public native Element eventGetFromElement(Event evt) /*-{
+    // Prefer 'relatedTarget' if it's set (see createMouseEvent(), which
+    // explicitly sets relatedTarget when synthesizing mouse events).
+    return evt.relatedTarget || evt.fromElement;
+  }-*/;
+
+  @Override
+  public native Element eventGetToElement(Event evt) /*-{
+    // Prefer 'relatedTarget' if it's set (see createMouseEvent(), which
+    // explicitly sets relatedTarget when synthesizing mouse events).
+    return evt.relatedTarget || evt.toElement;
+  }-*/;
+
+  @Override
+  public native Element getChild(Element elem, int index) /*-{
+    return elem.children[index];
+  }-*/;
+
+  @Override
+  public native int getChildCount(Element elem) /*-{
+    return elem.children.length;
+  }-*/;
+
+  @Override
+  public native int getChildIndex(Element parent, Element child) /*-{
+    var count = parent.children.length;
+    for (var i = 0; i < count; ++i) {
+      if (child === parent.children[i]) {
+        return i;
+      }
+    }
+    return -1;
+  }-*/;
+
+  @Override
+  public native void initEventSystem() /*-{
+    @com.google.gwt.user.client.impl.DOMImplTrident::dispatchEvent = function() {
+      // IE doesn't define event.currentTarget, so we squirrel it away here. It
+      // also seems that IE won't allow you to add expandos to the event object,
+      // so we have to store it in a global. This is ok because only one event
+      // can actually be dispatched at a time.
+      var oldEventTarget = @com.google.gwt.dom.client.DOMImplTrident::currentEventTarget;
+      @com.google.gwt.dom.client.DOMImplTrident::currentEventTarget = this;
+
+      if ($wnd.event.returnValue == null) {
+        $wnd.event.returnValue = true;
+        if (!@com.google.gwt.user.client.DOM::previewEvent(Lcom/google/gwt/user/client/Event;)($wnd.event)) {
+          @com.google.gwt.dom.client.DOMImplTrident::currentEventTarget = oldEventTarget;
+          return;
+        }
+      }
+
+      var listener, curElem = this;
+      while (curElem && !(listener = curElem.__listener)) {
+        curElem = curElem.parentElement;
+      }
+
+      if (listener) {
+        if (@com.google.gwt.user.client.impl.DOMImpl::isMyListener(Ljava/lang/Object;)(listener)) {
+          @com.google.gwt.user.client.DOM::dispatchEvent(Lcom/google/gwt/user/client/Event;Lcom/google/gwt/user/client/Element;Lcom/google/gwt/user/client/EventListener;)($wnd.event, curElem, listener);
+        }
+      }
+
+      @com.google.gwt.dom.client.DOMImplTrident::currentEventTarget = oldEventTarget;
+    };
+
+    @com.google.gwt.user.client.impl.DOMImplTrident::dispatchDblClickEvent = function() {
+      var newEvent = $doc.createEventObject();
+      // Synthesize a click event if one hasn't already been synthesized.
+      if ($wnd.event.returnValue == null) {
+        $wnd.event.srcElement.fireEvent('onclick', newEvent);
+      }
+      if (this.__eventBits & 2) {
+        @com.google.gwt.user.client.impl.DOMImplTrident::dispatchEvent.call(this);
+      } else if ($wnd.event.returnValue == null) {
+        // Ensure that we preview the event even if we aren't handling it.
+        $wnd.event.returnValue = true;
+        @com.google.gwt.user.client.DOM::previewEvent(Lcom/google/gwt/user/client/Event;)($wnd.event);
+      }
+    };
+
+    // We need to create these delegate functions to fix up the 'this' context.
+    // Normally, 'this' is the firing element, but this is only true for
+    // 'onclick = ...' event handlers, not for handlers setup via attachEvent().
+    var bodyDispatcher = function() { @com.google.gwt.user.client.impl.DOMImplTrident::dispatchEvent.call($doc.body); };
+    var bodyDblClickDispatcher = function() { @com.google.gwt.user.client.impl.DOMImplTrident::dispatchDblClickEvent.call($doc.body); };
+
+    $doc.body.attachEvent('onclick', bodyDispatcher);
+    $doc.body.attachEvent('onmousedown', bodyDispatcher);
+    $doc.body.attachEvent('onmouseup', bodyDispatcher);
+    $doc.body.attachEvent('onmousemove', bodyDispatcher);
+    $doc.body.attachEvent('onmousewheel', bodyDispatcher);
+    $doc.body.attachEvent('onkeydown', bodyDispatcher);
+    $doc.body.attachEvent('onkeypress', bodyDispatcher);
+    $doc.body.attachEvent('onkeyup', bodyDispatcher);
+    $doc.body.attachEvent('onfocus', bodyDispatcher);
+    $doc.body.attachEvent('onblur', bodyDispatcher);
+    $doc.body.attachEvent('ondblclick', bodyDblClickDispatcher);
+    $doc.body.attachEvent('oncontextmenu', bodyDispatcher);
+  }-*/;
+
+  @Override
+  public native void insertChild(Element parent, Element child, int index) /*-{
+    if (index >= parent.children.length)
+      parent.appendChild(child);
+    else
+      parent.insertBefore(child, parent.children[index]);
+  }-*/;
+
+  @Override
+  public void releaseCapture(Element elem) {
+    maybeInitializeEventSystem();
+    releaseCaptureImpl(elem);
+  }
+
+  @Override
+  public void setCapture(Element elem) {
+    maybeInitializeEventSystem();
+    setCaptureImpl(elem);
+  }
+
+  @Override
+  public void sinkEvents(Element elem, int bits) {
+    maybeInitializeEventSystem();
+    sinkEventsImpl(elem, bits);
+  }
+
+  private native void releaseCaptureImpl(Element elem) /*-{
+    elem.releaseCapture();
+  }-*/;
+
+  private native void setCaptureImpl(Element elem) /*-{
+    elem.setCapture();
+  }-*/;
+
+  private native void sinkEventsImpl(Element elem, int bits) /*-{
+    var chMask = (elem.__eventBits || 0) ^ bits;
+    elem.__eventBits = bits;
+    if (!chMask) return;
+
+    if (chMask & 0x00001) elem.onclick       = (bits & 0x00001) ?
+        @com.google.gwt.user.client.impl.DOMImplTrident::dispatchEvent : null;
+    // Add a ondblclick handler if onclick is desired to ensure that 
+    // a user's double click will result in two onclick events.
+    if (chMask & (0x00003)) elem.ondblclick  = (bits & (0x00003)) ?
+        @com.google.gwt.user.client.impl.DOMImplTrident::dispatchDblClickEvent : null;
+    if (chMask & 0x00004) elem.onmousedown   = (bits & 0x00004) ?
+        @com.google.gwt.user.client.impl.DOMImplTrident::dispatchEvent : null;
+    if (chMask & 0x00008) elem.onmouseup     = (bits & 0x00008) ?
+        @com.google.gwt.user.client.impl.DOMImplTrident::dispatchEvent : null;
+    if (chMask & 0x00010) elem.onmouseover   = (bits & 0x00010) ?
+        @com.google.gwt.user.client.impl.DOMImplTrident::dispatchEvent : null;
+    if (chMask & 0x00020) elem.onmouseout    = (bits & 0x00020) ?
+        @com.google.gwt.user.client.impl.DOMImplTrident::dispatchEvent : null;
+    if (chMask & 0x00040) elem.onmousemove   = (bits & 0x00040) ?
+        @com.google.gwt.user.client.impl.DOMImplTrident::dispatchEvent : null;
+    if (chMask & 0x00080) elem.onkeydown     = (bits & 0x00080) ?
+        @com.google.gwt.user.client.impl.DOMImplTrident::dispatchEvent : null;
+    if (chMask & 0x00100) elem.onkeypress    = (bits & 0x00100) ?
+        @com.google.gwt.user.client.impl.DOMImplTrident::dispatchEvent : null;
+    if (chMask & 0x00200) elem.onkeyup       = (bits & 0x00200) ?
+        @com.google.gwt.user.client.impl.DOMImplTrident::dispatchEvent : null;
+    if (chMask & 0x00400) elem.onchange      = (bits & 0x00400) ?
+        @com.google.gwt.user.client.impl.DOMImplTrident::dispatchEvent : null;
+    if (chMask & 0x00800) elem.onfocus       = (bits & 0x00800) ?
+        @com.google.gwt.user.client.impl.DOMImplTrident::dispatchEvent : null;
+    if (chMask & 0x01000) elem.onblur        = (bits & 0x01000) ?
+        @com.google.gwt.user.client.impl.DOMImplTrident::dispatchEvent : null;
+    if (chMask & 0x02000) elem.onlosecapture = (bits & 0x02000) ?
+        @com.google.gwt.user.client.impl.DOMImplTrident::dispatchEvent : null;
+    if (chMask & 0x04000) elem.onscroll      = (bits & 0x04000) ?
+        @com.google.gwt.user.client.impl.DOMImplTrident::dispatchEvent : null;
+    if (chMask & 0x08000) elem.onload        = (bits & 0x08000) ?
+        @com.google.gwt.user.client.impl.DOMImplTrident::dispatchEvent : null;
+    if (chMask & 0x10000) elem.onerror       = (bits & 0x10000) ?
+        @com.google.gwt.user.client.impl.DOMImplTrident::dispatchEvent : null;
+    if (chMask & 0x20000) elem.onmousewheel  = (bits & 0x20000) ? 
+        @com.google.gwt.user.client.impl.DOMImplTrident::dispatchEvent : null;
+    if (chMask & 0x40000) elem.oncontextmenu = (bits & 0x40000) ? 
+        @com.google.gwt.user.client.impl.DOMImplTrident::dispatchEvent : null;
+  }-*/;
+}
diff --git a/user/src/com/google/gwt/user/client/impl/HistoryImpl.java b/user/src/com/google/gwt/user/client/impl/HistoryImpl.java
index b4a05d6..57c9f59 100644
--- a/user/src/com/google/gwt/user/client/impl/HistoryImpl.java
+++ b/user/src/com/google/gwt/user/client/impl/HistoryImpl.java
@@ -28,10 +28,14 @@
 /**
  * Native implementation associated with
  * {@link com.google.gwt.user.client.History}.
- * 
  * User classes should not use this class directly.
+ * 
+ * <p>
+ * This base version uses the HTML5 standard window.onhashchange event to
+ * determine when the URL hash identifier changes.
+ * </p>
  */
-public abstract class HistoryImpl implements HasValueChangeHandlers<String>,
+public class HistoryImpl implements HasValueChangeHandlers<String>,
     HasHandlers {
 
   public static native String getToken() /*-{
@@ -70,7 +74,29 @@
     return handlers;
   }
 
-  public abstract boolean init();
+  public native boolean init() /*-{
+    var token = '';
+
+    // Get the initial token from the url's hash component.
+    var hash = $wnd.location.hash;
+    if (hash.length > 0) {
+      token = this.@com.google.gwt.user.client.impl.HistoryImpl::decodeFragment(Ljava/lang/String;)(hash.substring(1));
+    }
+
+    @com.google.gwt.user.client.impl.HistoryImpl::setToken(Ljava/lang/String;)(token);
+
+    var historyImpl = this;
+    $wnd.onhashchange = function() {
+      var token = '', hash = $wnd.location.hash;
+      if (hash.length > 0) {
+        token = historyImpl.@com.google.gwt.user.client.impl.HistoryImpl::decodeFragment(Ljava/lang/String;)(hash.substring(1));
+      }
+
+      historyImpl.@com.google.gwt.user.client.impl.HistoryImpl::newItemOnEvent(Ljava/lang/String;)(token);
+    };
+
+    return true;
+  }-*/;
 
   public final void newItem(String historyToken, boolean issueEvent) {
     historyToken = (historyToken == null) ? "" : historyToken;
@@ -102,9 +128,17 @@
     return encodeURI(fragment).replace("#", "%23");
   }-*/;
 
-  protected abstract void nativeUpdate(String historyToken);
+  /**
+   * The standard updateHash implementation assigns to location.hash() with an
+   * encoded history token.
+   */
+  protected native void nativeUpdate(String historyToken) /*-{
+    $wnd.location.hash = this.@com.google.gwt.user.client.impl.HistoryImpl::encodeFragment(Ljava/lang/String;)(historyToken);
+  }-*/;
 
-  protected abstract void nativeUpdateOnEvent(String historyToken);
+  protected void nativeUpdateOnEvent(String historyToken) {
+    // Do nothing, the hash is already updated.
+  }
 
   private void fireHistoryChanged(String newToken) {
     UncaughtExceptionHandler handler = GWT.getUncaughtExceptionHandler();
diff --git a/user/src/com/google/gwt/user/client/impl/HistoryImplFrame.java b/user/src/com/google/gwt/user/client/impl/HistoryImplFrame.java
deleted file mode 100644
index 956a978..0000000
--- a/user/src/com/google/gwt/user/client/impl/HistoryImplFrame.java
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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.user.client.Element;
-
-/**
- * An IFRAME implementation of
- * {@link com.google.gwt.user.client.impl.HistoryImpl}.
- */
-abstract class HistoryImplFrame extends HistoryImpl {
-
-  private static native Element findHistoryFrame() /*-{
-    return $doc.getElementById('__gwt_historyFrame');
-  }-*/;
-
-  private static native Element getTokenElement(Element historyFrame) /*-{
-    // Initialize the history iframe.  If '__gwt_historyToken' already exists, then
-    // we're probably backing into the app, so _don't_ set the iframe's location.
-    if (historyFrame.contentWindow) {
-      var doc = historyFrame.contentWindow.document;
-      return doc.getElementById('__gwt_historyToken');
-    }
-  }-*/;
-
-  protected Element historyFrame;
-
-  @Override
-  public boolean init() {
-    historyFrame = findHistoryFrame();
-    if (historyFrame == null) {
-      return false;
-    }
-
-    initHistoryToken();
-
-    // Initialize the history iframe. If a token element already exists, then
-    // we're probably backing into the app, so _don't_ create a new item.
-    Element tokenElement = getTokenElement(historyFrame);
-    if (tokenElement != null) {
-      setToken(getTokenElementContent(tokenElement));
-    } else {
-      navigateFrame(getToken());
-    }
-
-    injectGlobalHandler();
-    return true;
-  }
-
-  protected abstract String getTokenElementContent(Element tokenElement);
-
-  protected abstract void initHistoryToken();
-
-  protected abstract void injectGlobalHandler();
-
-  @Override
-  protected final void nativeUpdate(String historyToken) {
-    /*
-     * Must update the location hash since it isn't already correct.
-     */
-    updateHash(historyToken);
-    navigateFrame(historyToken);
-  }
-
-  @Override
-  protected final void nativeUpdateOnEvent(String historyToken) {
-    updateHash(historyToken);
-  }
-
-  protected abstract void navigateFrame(String historyToken);
-
-  protected abstract void updateHash(String historyToken);
-
-}
diff --git a/user/src/com/google/gwt/user/client/impl/HistoryImplIE6.java b/user/src/com/google/gwt/user/client/impl/HistoryImplIE6.java
index f2e3606..7651454 100644
--- a/user/src/com/google/gwt/user/client/impl/HistoryImplIE6.java
+++ b/user/src/com/google/gwt/user/client/impl/HistoryImplIE6.java
@@ -19,9 +19,10 @@
 import com.google.gwt.user.client.Element;
 
 /**
- * Internet Explorer 6 implementation HistoryImplFrame.
+ * History implementation for IE6 and IE7, which do not support the onhashchange
+ * event, and for which {@link HistoryImplTimer} will not work either.
  */
-class HistoryImplIE6 extends HistoryImplFrame {
+class HistoryImplIE6 extends HistoryImpl {
 
   /**
    * Sanitizes an untrusted string to be used in an HTML context. NOTE: This
@@ -37,6 +38,10 @@
     return DOM.getInnerHTML(div);
   }
 
+  private static native Element findHistoryFrame() /*-{
+    return $doc.getElementById('__gwt_historyFrame');
+  }-*/;
+
   /**
    * For IE6, reading from $wnd.location.hash drops part of the fragment if the
    * fragment contains a '?'. To avoid this bug, we use location.href instead.
@@ -48,22 +53,59 @@
     return (hashLoc > 0) ? href.substring(hashLoc) : "";
   }-*/;
 
+  private static native Element getTokenElement(Element historyFrame) /*-{
+    // Initialize the history iframe.  If '__gwt_historyToken' already exists, then
+    // we're probably backing into the app, so _don't_ set the iframe's location.
+    if (historyFrame.contentWindow) {
+      var doc = historyFrame.contentWindow.document;
+      return doc.getElementById('__gwt_historyToken');
+    }
+  }-*/;
+
+  protected Element historyFrame;
+
   @Override
   public boolean init() {
-    if (!super.init()) {
+    historyFrame = findHistoryFrame();
+    if (historyFrame == null) {
       return false;
     }
+
+    initHistoryToken();
+
+    // Initialize the history iframe. If a token element already exists, then
+    // we're probably backing into the app, so _don't_ create a new item.
+    Element tokenElement = getTokenElement(historyFrame);
+    if (tokenElement != null) {
+      setToken(getTokenElementContent(tokenElement));
+    } else {
+      navigateFrame(getToken());
+    }
+
+    injectGlobalHandler();
     initUrlCheckTimer();
     return true;
   }
 
   @Override
-  protected native String getTokenElementContent(Element tokenElement) /*-{
+  protected final void nativeUpdate(String historyToken) {
+    /*
+     * Must update the location hash since it isn't already correct.
+     */
+    updateHash(historyToken);
+    navigateFrame(historyToken);
+  }
+
+  @Override
+  protected final void nativeUpdateOnEvent(String historyToken) {
+    updateHash(historyToken);
+  }
+
+  private native String getTokenElementContent(Element tokenElement) /*-{
     return tokenElement.innerText;
   }-*/;
 
-  @Override
-  protected native void initHistoryToken() /*-{
+  private native void initHistoryToken() /*-{
     // Assume an empty token.
     var token = '';
     // Get the initial token from the url's hash component.
@@ -79,29 +121,6 @@
     @com.google.gwt.user.client.impl.HistoryImpl::setToken(Ljava/lang/String;)(token);
   }-*/;
 
-  @Override
-  protected native void injectGlobalHandler() /*-{
-    var historyImplRef = this;
-
-    $wnd.__gwt_onHistoryLoad = function(token) {
-      historyImplRef.@com.google.gwt.user.client.impl.HistoryImpl::newItemOnEvent(Ljava/lang/String;)(token);
-    };
-  }-*/;
-
-  @Override
-  protected native void navigateFrame(String token) /*-{
-    var escaped = @com.google.gwt.user.client.impl.HistoryImplIE6::escapeHtml(Ljava/lang/String;)(token);
-    var doc = this.@com.google.gwt.user.client.impl.HistoryImplFrame::historyFrame.contentWindow.document;
-    doc.open();
-    doc.write('<html><body onload="if(parent.__gwt_onHistoryLoad)parent.__gwt_onHistoryLoad(__gwt_historyToken.innerText)"><div id="__gwt_historyToken">' + escaped + '</div></body></html>');
-    doc.close();
-  }-*/;
-
-  @Override
-  protected native void updateHash(String token) /*-{
-    $wnd.location.hash = this.@com.google.gwt.user.client.impl.HistoryImpl::encodeFragment(Ljava/lang/String;)(token);
-  }-*/;
-
   private native void initUrlCheckTimer() /*-{
     // This is the URL check timer.  It detects when an unexpected change
     // occurs in the document's URL (e.g. when the user enters one manually
@@ -122,7 +141,7 @@
           // if someone entered or linked to a bad url.
           $wnd.location.reload();
         }
-
+  
         var historyToken = @com.google.gwt.user.client.impl.HistoryImpl::getToken()();
         if (historyToken && (token != historyToken)) {
           $wnd.location.reload();
@@ -131,4 +150,24 @@
     };
     urlChecker();
   }-*/;
+
+  private native void injectGlobalHandler() /*-{
+    var historyImplRef = this;
+
+    $wnd.__gwt_onHistoryLoad = function(token) {
+      historyImplRef.@com.google.gwt.user.client.impl.HistoryImpl::newItemOnEvent(Ljava/lang/String;)(token);
+    };
+  }-*/;
+
+  private native void navigateFrame(String token) /*-{
+    var escaped = @com.google.gwt.user.client.impl.HistoryImplIE6::escapeHtml(Ljava/lang/String;)(token);
+    var doc = this.@com.google.gwt.user.client.impl.HistoryImplIE6::historyFrame.contentWindow.document;
+    doc.open();
+    doc.write('<html><body onload="if(parent.__gwt_onHistoryLoad)parent.__gwt_onHistoryLoad(__gwt_historyToken.innerText)"><div id="__gwt_historyToken">' + escaped + '</div></body></html>');
+    doc.close();
+  }-*/;
+
+  private native void updateHash(String token) /*-{
+    $wnd.location.hash = this.@com.google.gwt.user.client.impl.HistoryImpl::encodeFragment(Ljava/lang/String;)(token);
+  }-*/;
 }
diff --git a/user/src/com/google/gwt/user/client/impl/HistoryImplMozilla.java b/user/src/com/google/gwt/user/client/impl/HistoryImplMozilla.java
index b0b7b3b..6d0da57 100644
--- a/user/src/com/google/gwt/user/client/impl/HistoryImplMozilla.java
+++ b/user/src/com/google/gwt/user/client/impl/HistoryImplMozilla.java
@@ -18,8 +18,8 @@
 /**
  * History implementation for Mozilla-based browsers.
  */
-class HistoryImplMozilla extends HistoryImplStandard {
-  
+class HistoryImplMozilla extends HistoryImplTimer {
+
   @Override
   protected String decodeFragment(String encodedFragment) {
     // Mozilla browsers pre-decode the result of location.hash, so there's no
diff --git a/user/src/com/google/gwt/user/client/impl/HistoryImplSafari.java b/user/src/com/google/gwt/user/client/impl/HistoryImplSafari.java
index 6986312..d3ffaa0 100644
--- a/user/src/com/google/gwt/user/client/impl/HistoryImplSafari.java
+++ b/user/src/com/google/gwt/user/client/impl/HistoryImplSafari.java
@@ -17,69 +17,15 @@
 
 /**
  * Safari implementation of
- * {@link com.google.gwt.user.client.impl.HistoryImplStandard}.
+ * {@link com.google.gwt.user.client.impl.HistoryImplTimer}.
  * 
  * This implementation works on both Safari 2 and 3, by detecting the version
  * and reverting to a stub implementation for Safari 2.
  */
-class HistoryImplSafari extends HistoryImplStandard {
-
-  static boolean isOldSafari = detectOldSafari();
-
-  static native boolean detectOldSafari() /*-{
-    var exp = / AppleWebKit\/([\d]+)/;
-    var result = exp.exec(navigator.userAgent);
-    if (result) {
-      // The standard history implementation works fine on WebKit >= 522
-      // (Safari 3 beta).
-      if (parseInt(result[1]) >= 522) {
-        return false;
-      }
-    }
-
-    // The standard history implementation works just fine on the iPhone, which
-    // unfortunately reports itself as WebKit/420+.
-    if (navigator.userAgent.indexOf('iPhone') != -1) {
-      return false;
-    }
-
-    return true;
-  }-*/;
+class HistoryImplSafari extends HistoryImplTimer {
 
   @Override
-  public boolean init() {
-    if (isOldSafari) {
-      initImpl();
-      return true;
-    } else {
-      return super.init();
-    }
-  }
-
-  @Override
-  protected void nativeUpdate(String historyToken) {
-    if (isOldSafari) {
-      oldNativeUpdate(historyToken);
-    } else {
-      newNativeUpdate(historyToken);
-    }
-  }
-  // HasXHandler(Selection.Handler
-
-  private native void initImpl() /*-{
-    var token = '';
-
-    // Get the initial token from the url's hash component.
-    var hash = $wnd.location.hash;
-    if (hash.length > 0) {
-      token = this.@com.google.gwt.user.client.impl.HistoryImpl::decodeFragment(Ljava/lang/String;)(hash.substring(1));
-    }
-
-    @com.google.gwt.user.client.impl.HistoryImpl::setToken(Ljava/lang/String;)(token);
-    this.@com.google.gwt.user.client.impl.HistoryImpl::fireHistoryChangedImpl(Ljava/lang/String;)($wnd.__gwt_historyToken);
-  }-*/;
-
-  private native void newNativeUpdate(String historyToken) /*-{
+  protected native void nativeUpdate(String historyToken) /*-{
     // Safari gets into a weird state (issue 2905) when setting the hash
     // component of the url to an empty string, but works fine as long as you
     // at least add a '#' to the end of the url. So we get around this by
@@ -87,19 +33,4 @@
     $wnd.location = $wnd.location.href.split('#')[0] + '#' +
       this.@com.google.gwt.user.client.impl.HistoryImpl::encodeFragment(Ljava/lang/String;)(historyToken);
   }-*/;
-
-  private native void oldNativeUpdate(String historyToken) /*-{
-    // Use a bizarre meta refresh trick to update the url's hash, without
-    // creating a history entry.
-    var meta = $doc.createElement('meta');
-    meta.setAttribute('http-equiv','refresh');
-
-    var newUrl = $wnd.location.href.split('#')[0] + '#' + this.@com.google.gwt.user.client.impl.HistoryImpl::encodeFragment(Ljava/lang/String;)(historyToken);
-    meta.setAttribute('content','0.01;url=' + newUrl);
-
-    $doc.body.appendChild(meta);
-    window.setTimeout(function() {
-      $doc.body.removeChild(meta);
-    }, 1);
-  }-*/;
 }
diff --git a/user/src/com/google/gwt/user/client/impl/HistoryImplStandard.java b/user/src/com/google/gwt/user/client/impl/HistoryImplTimer.java
similarity index 74%
rename from user/src/com/google/gwt/user/client/impl/HistoryImplStandard.java
rename to user/src/com/google/gwt/user/client/impl/HistoryImplTimer.java
index 0fb2b83..825fecd 100644
--- a/user/src/com/google/gwt/user/client/impl/HistoryImplStandard.java
+++ b/user/src/com/google/gwt/user/client/impl/HistoryImplTimer.java
@@ -16,9 +16,10 @@
 package com.google.gwt.user.client.impl;
 
 /**
- * Standard history implementation, currently used only on Opera browsers.
+ * Base class for history implementations that use a timer rather than the
+ * onhashchange event.
  */
-class HistoryImplStandard extends HistoryImpl {
+class HistoryImplTimer extends HistoryImpl {
 
   @Override
   public native boolean init() /*-{
@@ -49,17 +50,4 @@
     $wnd.__checkHistory();
     return true;
   }-*/;
-
-  /**
-   * The standard updateHash implementation assigns to location.hash() with an
-   * encoded history token.
-   */
-  protected native void nativeUpdate(String historyToken) /*-{
-    $wnd.location.hash = this.@com.google.gwt.user.client.impl.HistoryImpl::encodeFragment(Ljava/lang/String;)(historyToken);
-  }-*/;
-
-  @Override
-  protected void nativeUpdateOnEvent(String historyToken) {
-    // Do nothing, the hash is already updated.
-  }
 }
diff --git a/user/src/com/google/gwt/xml/XML.gwt.xml b/user/src/com/google/gwt/xml/XML.gwt.xml
index b9bf1a1..035a447 100644
--- a/user/src/com/google/gwt/xml/XML.gwt.xml
+++ b/user/src/com/google/gwt/xml/XML.gwt.xml
@@ -15,32 +15,34 @@
 <!-- XML parsing support.                                                   -->
 <!--                                                                        -->
 <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"/>
 
-	<!-- Fall through to this rule for all other browsers -->
-	<replace-with class="com.google.gwt.xml.client.impl.XMLParserImplStandard">
-		<when-type-is class="com.google.gwt.xml.client.impl.XMLParserImpl"/>
-	</replace-with>
+  <!-- Fall through to this rule for all other browsers -->
+  <replace-with class="com.google.gwt.xml.client.impl.XMLParserImplStandard">
+    <when-type-is class="com.google.gwt.xml.client.impl.XMLParserImpl"/>
+  </replace-with>
 
-	<replace-with class="com.google.gwt.xml.client.impl.XMLParserImplMozillaOld">
-		<when-type-is class="com.google.gwt.xml.client.impl.XMLParserImpl"/>
-		<when-property-is name="user.agent" value="gecko"/>
-	</replace-with>
+  <replace-with class="com.google.gwt.xml.client.impl.XMLParserImplMozillaOld">
+    <when-type-is class="com.google.gwt.xml.client.impl.XMLParserImpl"/>
+    <when-property-is name="user.agent" value="gecko"/>
+  </replace-with>
 
-	<replace-with class="com.google.gwt.xml.client.impl.XMLParserImplOpera">
-		<when-type-is class="com.google.gwt.xml.client.impl.XMLParserImpl"/>
-		<when-property-is name="user.agent" value="opera"/>
-	</replace-with>
+  <replace-with class="com.google.gwt.xml.client.impl.XMLParserImplOpera">
+    <when-type-is class="com.google.gwt.xml.client.impl.XMLParserImpl"/>
+    <when-property-is name="user.agent" value="opera"/>
+  </replace-with>
 
-	<replace-with class="com.google.gwt.xml.client.impl.XMLParserImplIE6">
-		<when-type-is class="com.google.gwt.xml.client.impl.XMLParserImpl"/>
-		<when-property-is name="user.agent" value="ie6"/>
-	</replace-with>
+  <replace-with class="com.google.gwt.xml.client.impl.XMLParserImplIE6">
+    <when-type-is class="com.google.gwt.xml.client.impl.XMLParserImpl"/>
+    <any>
+      <when-property-is name="user.agent" value="ie6"/>
+      <when-property-is name="user.agent" value="ie8"/>
+    </any>
+  </replace-with>
 
-	<replace-with class="com.google.gwt.xml.client.impl.XMLParserImplSafari">
-		<when-type-is class="com.google.gwt.xml.client.impl.XMLParserImpl"/>
-		<when-property-is name="user.agent" value="safari"/>
-	</replace-with>
-	
+  <replace-with class="com.google.gwt.xml.client.impl.XMLParserImplSafari">
+    <when-type-is class="com.google.gwt.xml.client.impl.XMLParserImpl"/>
+    <when-property-is name="user.agent" value="safari"/>
+  </replace-with>
 </module>
diff --git a/user/super/com/google/gwt/emul/EmulationWithUserAgent.gwt.xml b/user/super/com/google/gwt/emul/EmulationWithUserAgent.gwt.xml
index 4add1cf..2080888 100644
--- a/user/super/com/google/gwt/emul/EmulationWithUserAgent.gwt.xml
+++ b/user/super/com/google/gwt/emul/EmulationWithUserAgent.gwt.xml
@@ -26,6 +26,7 @@
     <when-type-is class="com.google.gwt.core.client.impl.StringBufferImpl"/>
     <any>
       <when-property-is name="user.agent" value="ie6"/>
+      <when-property-is name="user.agent" value="ie8"/>
     </any>
   </replace-with>