Provide a hack to work around focus bugs in some browsers

WebKit based browsers (Safari, Chrome) can't correctly handle a table
that sinks click events when the table is wrapped inside a FocusPanel.
The event queue gets confused and events are delivered to the table
at the wrong coordinate, making it potentially impossible for the user
to click on the row they intended to.

Signed-off-by: Shawn O. Pearce <sop@google.com>
diff --git a/src/main/java/com/google/gwtexpui/user/User.gwt.xml b/src/main/java/com/google/gwtexpui/user/User.gwt.xml
index ffc2a59..8736beb 100644
--- a/src/main/java/com/google/gwtexpui/user/User.gwt.xml
+++ b/src/main/java/com/google/gwtexpui/user/User.gwt.xml
@@ -24,4 +24,11 @@
       <when-property-is name="user.agent" value="gecko1_8"/>
     </any>
   </replace-with>
+
+  <replace-with class="com.google.gwtexpui.user.client.FocusPanelFactorySafari">
+    <when-type-is class="com.google.gwtexpui.user.client.FocusPanelFactory" />
+    <any>
+      <when-property-is name="user.agent" value="safari"/>
+    </any>
+  </replace-with>
 </module>
diff --git a/src/main/java/com/google/gwtexpui/user/client/FocusPanelFactory.java b/src/main/java/com/google/gwtexpui/user/client/FocusPanelFactory.java
new file mode 100644
index 0000000..90f1412
--- /dev/null
+++ b/src/main/java/com/google/gwtexpui/user/client/FocusPanelFactory.java
@@ -0,0 +1,24 @@
+// 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.gwtexpui.user.client;
+
+import com.google.gwt.user.client.ui.FocusPanel;
+import com.google.gwt.user.client.ui.Widget;
+
+class FocusPanelFactory {
+  FocusPanel wrapFocusPanel(final Widget child) {
+    return new FocusPanel(child);
+  }
+}
diff --git a/src/main/java/com/google/gwtexpui/user/client/FocusPanelFactorySafari.java b/src/main/java/com/google/gwtexpui/user/client/FocusPanelFactorySafari.java
new file mode 100644
index 0000000..b9cb180
--- /dev/null
+++ b/src/main/java/com/google/gwtexpui/user/client/FocusPanelFactorySafari.java
@@ -0,0 +1,30 @@
+// 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.gwtexpui.user.client;
+
+import com.google.gwt.user.client.DOM;
+import com.google.gwt.user.client.Event;
+import com.google.gwt.user.client.ui.FocusPanel;
+import com.google.gwt.user.client.ui.Widget;
+
+class FocusPanelFactorySafari extends FocusPanelFactory {
+  @Override
+  FocusPanel wrapFocusPanel(final Widget child) {
+    if ((DOM.getEventsSunk(child.getElement()) & (Event.ONDBLCLICK | Event.ONCLICK)) != 0) {
+      return null;
+    }
+    return new FocusPanel(child);
+  }
+}
diff --git a/src/main/java/com/google/gwtexpui/user/client/UserAgent.java b/src/main/java/com/google/gwtexpui/user/client/UserAgent.java
index 3687864..ca7d913 100644
--- a/src/main/java/com/google/gwtexpui/user/client/UserAgent.java
+++ b/src/main/java/com/google/gwtexpui/user/client/UserAgent.java
@@ -16,6 +16,8 @@
 
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.FocusPanel;
+import com.google.gwt.user.client.ui.Widget;
 
 /**
  * User agent feature tests we don't create permutations for.
@@ -76,6 +78,21 @@
   private static native void bustOutOfIFrame(String newloc)
   /*-{ top.location.href = newloc }-*/;
 
+  private static final FocusPanelFactory focusFactory =
+      GWT.create(FocusPanelFactory.class);
+
+  /**
+   * @return a FocusPanel containing the supplied <code>child</code>;
+   *         <code>null</code> if the browser can't support it. Some browsers
+   *         (e.g. WebKit based ones) can't correctly wrap a table that sinks
+   *         click events; the events are delivered incorrectly and the table
+   *         doesn't get the click where the user actually clicked (it arrives
+   *         on another row).
+   */
+  public static FocusPanel wrapFocusPanel(final Widget child) {
+    return focusFactory.wrapFocusPanel(child);
+  }
+
   private UserAgent() {
   }
 }