Index: user/src/com/google/gwt/event/dom/client/DomEvent.java
===================================================================
--- user/src/com/google/gwt/event/dom/client/DomEvent.java	(revision 4247)
+++ user/src/com/google/gwt/event/dom/client/DomEvent.java	(working copy)
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.event.dom.client;
 
+import com.google.gwt.event.logical.shared.HasNativeEvent;
 import com.google.gwt.event.shared.EventHandler;
 import com.google.gwt.event.shared.GwtEvent;
 import com.google.gwt.event.shared.HandlerManager;
@@ -29,7 +30,8 @@
  * @param <H> handler type
  * 
  */
-public abstract class DomEvent<H extends EventHandler> extends GwtEvent<H> {
+public abstract class DomEvent<H extends EventHandler> extends GwtEvent<H>
+    implements HasNativeEvent {
   /**
    * Type class used by dom event subclasses. Type is specialized for dom in
    * order to carry information about the native event.
@@ -61,10 +63,10 @@
      * 
      * 
      * @param eventToSink the integer value used by sink events to set up event
-     * handling for this dom type
+     *          handling for this dom type
      * @param eventName the raw native event name
      * @param flyweight the instance that will be used as a flyweight to wrap a
-     * native event
+     *          native event
      */
     protected Type(int eventToSink, String eventName, DomEvent<H> flyweight) {
       this.flyweight = flyweight;
@@ -100,7 +102,7 @@
   }
 
   private static PrivateMap<Type<?>> registered;
- 
+
   /**
    * Fires the given native event on the specified handlers.
    * 
@@ -122,7 +124,7 @@
       }
     }
   }
- 
+
   // This method can go away once we have eager clinits.
   static void init() {
     registered = new PrivateMap<Type<?>>();
@@ -130,11 +132,6 @@
 
   private Event nativeEvent;
 
-  /**
-   * Gets the underlying native event for this {@link DomEvent}.
-   * 
-   * @return gets the native event
-   */
   public final Event getNativeEvent() {
     assertLive();
     return nativeEvent;
Index: user/src/com/google/gwt/event/logical/shared/HasNativeEvent.java
===================================================================
--- user/src/com/google/gwt/event/logical/shared/HasNativeEvent.java	(revision 0)
+++ user/src/com/google/gwt/event/logical/shared/HasNativeEvent.java	(revision 0)
@@ -0,0 +1,32 @@
+/*
+ * 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.event.logical.shared;
+
+import com.google.gwt.user.client.Event;
+
+/**
+ * An object that implements this interface has a native event associated with
+ * it.
+ */
+public interface HasNativeEvent {
+  /**
+   * Gets the underlying native event.
+   * 
+   * @return the native event
+   */
+  Event getNativeEvent();
+}
Index: user/src/com/google/gwt/user/client/DOM.java
===================================================================
--- user/src/com/google/gwt/user/client/DOM.java	(revision 4247)
+++ user/src/com/google/gwt/user/client/DOM.java	(working copy)
@@ -39,6 +39,7 @@
   private static Element sCaptureElem;
 
   // <BrowserEventPreview>
+  @SuppressWarnings("deprecation")
   private static ArrayList<EventPreview> sEventPreviewStack;
 
   /**
@@ -49,7 +50,10 @@
    * handlers only receive explicitly sunk events.
    * 
    * @param preview the event preview to be added to the stack.
+   * @deprecated replaced by
+   *             {@link Event#addNativePreviewHandler(Event.NativePreviewHandler)}
    */
+  @Deprecated
   public static void addEventPreview(EventPreview preview) {
     impl.maybeInitializeEventSystem();
 
@@ -376,8 +380,7 @@
   }
 
   /**
-   * Generates a unique DOM id. The id is of the form "gwt-id-<unique
-   * integer>".
+   * Generates a unique DOM id. The id is of the form "gwt-id-<unique integer>".
    * 
    * @return a unique DOM id
    */
@@ -934,8 +937,8 @@
    *          <code>&lt;option&gt;</code>; cannot be <code>null</code>
    * @param index the index at which to insert the child
    */
-  public static void insertListItem(Element selectElem, String item, String value,
-      int index) {
+  public static void insertListItem(Element selectElem, String item,
+      String value, int index) {
     SelectElement select = selectElem.<SelectElement> cast();
     OptionElement option = Document.get().createOptionElement();
     option.setText(item);
@@ -999,7 +1002,11 @@
    * capture events, though any preview underneath it will begin to do so.
    * 
    * @param preview the event preview to be removed from the stack
+   * @deprecated use {@link com.google.gwt.event.shared.HandlerRegistration}
+   *             returned from
+   *             {@link Event#addNativePreviewHandler(Event.NativePreviewHandler)}
    */
+  @Deprecated
   public static void removeEventPreview(EventPreview preview) {
     // Remove the event preview from the stack. If it was on top,
     // any preview underneath it will automatically begin to
@@ -1265,9 +1272,12 @@
    * @param evt a handle to the event being previewed
    * @return <code>false</code> to cancel the event
    */
+  @SuppressWarnings("deprecation")
   static boolean previewEvent(Event evt) {
+    // Fire a NativePreviewEvent to NativePreviewHandlers
+    boolean ret = Event.fireNativePreviewEvent(evt);
+
     // If event previews are present, redirect events to the topmost of them.
-    boolean ret = true;
     if (sEventPreviewStack != null && sEventPreviewStack.size() > 0) {
       EventPreview preview = sEventPreviewStack.get(sEventPreviewStack.size() - 1);
       if (!(ret = preview.onEventPreview(evt))) {
Index: user/src/com/google/gwt/user/client/Event.java
===================================================================
--- user/src/com/google/gwt/user/client/Event.java	(revision 4247)
+++ user/src/com/google/gwt/user/client/Event.java	(working copy)
@@ -17,6 +17,11 @@
 
 import com.google.gwt.core.client.JavaScriptObject;
 import com.google.gwt.dom.client.Element;
+import com.google.gwt.event.logical.shared.HasNativeEvent;
+import com.google.gwt.event.shared.EventHandler;
+import com.google.gwt.event.shared.GwtEvent;
+import com.google.gwt.event.shared.HandlerManager;
+import com.google.gwt.event.shared.HandlerRegistration;
 
 /**
  * <p>
@@ -42,8 +47,94 @@
  * </p>
  */
 public class Event extends JavaScriptObject {
+  /**
+   * Fired when any native event occurs.
+   */
+  public static class NativePreviewEvent extends GwtEvent<NativePreviewHandler>
+      implements HasNativeEvent {
 
+    /**
+     * Handler type.
+     */
+    private static Type<NativePreviewHandler> TYPE;
+
+    /**
+     * Gets the type associated with this event.
+     * 
+     * @return returns the handler type
+     */
+    static Type<NativePreviewHandler> getType() {
+      if (TYPE == null) {
+        TYPE = new Type<NativePreviewHandler>();
+      }
+      return TYPE;
+    }
+
+    /**
+     * A boolean indicating that the event should be canceled.
+     */
+    private boolean canceled;
+
+    /**
+     * The event being previewed.
+     */
+    private Event nativeEvent;
+
+    /**
+     * Creates a new {@link NativePreviewEvent}.
+     * 
+     * @param nativeEvent the native {@link Event} being previewed.
+     */
+    private NativePreviewEvent(Event nativeEvent) {
+      this.nativeEvent = nativeEvent;
+    }
+
+    /**
+     * Cancel the preview event.
+     * 
+     * Classes overriding this method should still call super.cancel().
+     */
+    public void cancel() {
+      canceled = true;
+    }
+
+    public Event getNativeEvent() {
+      return nativeEvent;
+    }
+
+    /**
+     * Has the event already been canceled?
+     * 
+     * @return true if the event has been canceled
+     */
+    public boolean isCanceled() {
+      return canceled;
+    }
+
+    @Override
+    protected void dispatch(NativePreviewHandler handler) {
+      handler.onPreviewNativeEvent(this);
+    }
+
+    @Override
+    protected Type<NativePreviewHandler> getAssociatedType() {
+      return TYPE;
+    }
+  }
+
   /**
+   * Handler interface for {@link NativePreviewEvent} events.
+   */
+  public static interface NativePreviewHandler extends EventHandler {
+    /**
+     * Called when {@link NativePreviewEvent} is fired.
+     * 
+     * @param event the {@link NativePreviewEvent} that was fired
+     */
+    void onPreviewNativeEvent(NativePreviewEvent event);
+  }
+
+  /**
    * The left mouse button (used in {@link DOM#eventGetButton(Event)}).
    */
   public static final int BUTTON_LEFT = 1;
@@ -183,6 +274,8 @@
   @Deprecated
   public static final int UNDEFINED = 0;
 
+  private static HandlerManager handlerManager;
+
   /**
    * Adds an event preview to the preview stack. As long as this preview remains
    * on the top of the stack, it will receive all events before they are fired
@@ -191,12 +284,32 @@
    * handlers only receive explicitly sunk events.
    * 
    * @param preview the event preview to be added to the stack.
+   * @deprecated replaced by
+   *             {@link #addNativePreviewHandler(NativePreviewHandler)}
    */
+  @Deprecated
   public static void addEventPreview(EventPreview preview) {
     DOM.addEventPreview(preview);
   }
 
   /**
+   * Adds a {@link NativePreviewHandler} that will receive all events before
+   * they are fired to their handlers. Note that the event preview will receive
+   * <u>all</u> native events, including those received due to bubbling, whereas
+   * normal event handlers only receive explicitly sunk events.
+   * 
+   * @param handler the {@link NativePreviewHandler}
+   * @return {@link HandlerRegistration} used to remove this handler
+   */
+  public static HandlerRegistration addNativePreviewHandler(
+      NativePreviewHandler handler) {
+    if (handlerManager == null) {
+      handlerManager = new HandlerManager(null);
+    }
+    return handlerManager.addHandler(NativePreviewEvent.getType(), handler);
+  }
+
+  /**
    * Gets the current event that is being fired. The current event is only
    * available within the lifetime of the onBrowserEvent function. Once the
    * onBrowserEvent method returns, the current event is reset to null.
@@ -234,7 +347,10 @@
    * capture events, though any preview underneath it will begin to do so.
    * 
    * @param preview the event preview to be removed from the stack
+   * @deprecated use {@link HandlerRegistration} returned from
+   *             {@link Event#addNativePreviewHandler(NativePreviewHandler)}
    */
+  @Deprecated
   public static void removeEventPreview(EventPreview preview) {
     DOM.removeEventPreview(preview);
   }
@@ -263,6 +379,21 @@
   }
 
   /**
+   * Fire a {@link NativePreviewEvent} for the native event.
+   * 
+   * @param nativeEvent the native event
+   * @return true to fire the event normally, false to cancel the event
+   */
+  static boolean fireNativePreviewEvent(Event nativeEvent) {
+    if (handlerManager != null) {
+      NativePreviewEvent event = new NativePreviewEvent(nativeEvent);
+      handlerManager.fireEvent(event);
+      return !event.isCanceled();
+    }
+    return true;
+  }
+
+  /**
    * Not directly instantiable. Subclasses should also define a protected no-arg
    * constructor to prevent client code from directly instantiating the class.
    */
Index: user/src/com/google/gwt/user/client/EventPreview.java
===================================================================
--- user/src/com/google/gwt/user/client/EventPreview.java	(revision 4247)
+++ user/src/com/google/gwt/user/client/EventPreview.java	(working copy)
@@ -19,7 +19,10 @@
  * A listener interface for previewing browser events.
  * 
  * @see com.google.gwt.user.client.DOM#addEventPreview(EventPreview)
+ * @deprecated replaced by
+ *             {@link com.google.gwt.user.client.Event.NativePreviewHandler}
  */
+@Deprecated
 public interface EventPreview {
 
   /**
@@ -29,6 +32,9 @@
    * @param event the browser event
    * @return <code>false</code> to cancel the event
    * @see DOM#addEventPreview(EventPreview)
+   * @deprecated replaced by
+   *             {@link com.google.gwt.user.client.Event.NativePreviewHandler#onPreviewNativeEvent(com.google.gwt.user.client.Event.NativePreviewEvent)}
    */
+  @Deprecated
   boolean onEventPreview(Event event);
 }
Index: user/src/com/google/gwt/user/client/ui/PopupPanel.java
===================================================================
--- user/src/com/google/gwt/user/client/ui/PopupPanel.java	(revision 4247)
+++ user/src/com/google/gwt/user/client/ui/PopupPanel.java	(working copy)
@@ -18,6 +18,7 @@
 import com.google.gwt.animation.client.Animation;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.dom.client.Document;
+import com.google.gwt.dom.client.Element;
 import com.google.gwt.event.logical.shared.CloseEvent;
 import com.google.gwt.event.logical.shared.CloseHandler;
 import com.google.gwt.event.logical.shared.HasCloseHandlers;
@@ -26,10 +27,11 @@
 import com.google.gwt.user.client.Command;
 import com.google.gwt.user.client.DOM;
 import com.google.gwt.user.client.DeferredCommand;
-import com.google.gwt.user.client.Element;
 import com.google.gwt.user.client.Event;
 import com.google.gwt.user.client.EventPreview;
 import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.Event.NativePreviewEvent;
+import com.google.gwt.user.client.Event.NativePreviewHandler;
 import com.google.gwt.user.client.ui.impl.PopupImpl;
 
 /**
@@ -60,6 +62,7 @@
  * <li>.gwt-PopupPanel .popupContent { the wrapper around the content }</li>
  * </ul>
  */
+@SuppressWarnings("deprecation")
 public class PopupPanel extends SimplePanel implements SourcesPopupEvents,
     EventPreview, HasAnimation, HasCloseHandlers<PopupPanel> {
 
@@ -264,6 +267,8 @@
    */
   private AnimationType animType = AnimationType.CENTER;
 
+  private HandlerRegistration nativePreviewHandlerRegistration = null;
+
   private boolean autoHide, modal, showing;
 
   // Used to track requested size across changing child widgets
@@ -454,72 +459,12 @@
     return modal;
   }
 
+  /**
+   * @deprecated use {@link Event#addNativePreviewHandler(NativePreviewHandler)}
+   *             instead
+   */
   public boolean onEventPreview(Event event) {
-    Element target = DOM.eventGetTarget(event);
-
-    boolean eventTargetsPopup = (target != null)
-        && DOM.isOrHasChild(getElement(), target);
-
-    int type = DOM.eventGetType(event);
-    switch (type) {
-      case Event.ONKEYDOWN: {
-        boolean allow = onKeyDownPreview((char) DOM.eventGetKeyCode(event),
-            KeyboardListenerCollection.getKeyboardModifiers(event));
-        return allow && (eventTargetsPopup || !modal);
-      }
-      case Event.ONKEYUP: {
-        boolean allow = onKeyUpPreview((char) DOM.eventGetKeyCode(event),
-            KeyboardListenerCollection.getKeyboardModifiers(event));
-        return allow && (eventTargetsPopup || !modal);
-      }
-      case Event.ONKEYPRESS: {
-        boolean allow = onKeyPressPreview((char) DOM.eventGetKeyCode(event),
-            KeyboardListenerCollection.getKeyboardModifiers(event));
-        return allow && (eventTargetsPopup || !modal);
-      }
-
-      case Event.ONMOUSEDOWN:
-        // Don't eat events if event capture is enabled, as this can interfere
-        // with dialog dragging, for example.
-        if (DOM.getCaptureElement() != null) {
-          return true;
-        }
-
-        if (!eventTargetsPopup && autoHide) {
-          hide(true);
-          return true;
-        }
-        break;
-      case Event.ONMOUSEUP:
-      case Event.ONMOUSEMOVE:
-      case Event.ONCLICK:
-      case Event.ONDBLCLICK: {
-        // Don't eat events if event capture is enabled, as this can interfere
-        // with dialog dragging, for example.
-        if (DOM.getCaptureElement() != null) {
-          return true;
-        }
-
-        // If it's an outside click and auto-hide is enabled:
-        // hide the popup and _don't_ eat the event. ONMOUSEDOWN is used to
-        // prevent problems with showing a popup in response to a mousedown.
-        if (!eventTargetsPopup && autoHide && (type == Event.ONMOUSEDOWN)) {
-          hide(true);
-          return true;
-        }
-
-        break;
-      }
-
-      case Event.ONFOCUS: {
-        if (modal && !eventTargetsPopup && (target != null)) {
-          blur(target);
-          return false;
-        }
-      }
-    }
-
-    return !modal || eventTargetsPopup;
+    return true;
   }
 
   /**
@@ -635,8 +580,8 @@
     // called before show() is called (so a popup can be positioned without it
     // 'jumping' on the screen).
     Element elem = getElement();
-    DOM.setStyleAttribute(elem, "left", left + "px");
-    DOM.setStyleAttribute(elem, "top", top + "px");
+    elem.getStyle().setPropertyPx("left", left);
+    elem.getStyle().setPropertyPx("top", top);
   }
 
   /**
@@ -660,9 +605,9 @@
   public void setTitle(String title) {
     Element containerElement = getContainerElement();
     if (title == null || title.length() == 0) {
-      DOM.removeElementAttribute(containerElement, "title");
+      containerElement.removeAttribute("title");
     } else {
-      DOM.setElementAttribute(containerElement, "title", title);
+      containerElement.setAttribute("title", title);
     }
   }
 
@@ -724,12 +669,107 @@
       return;
     }
     showing = true;
-    DOM.addEventPreview(this);
+    nativePreviewHandlerRegistration = Event.addNativePreviewHandler(new NativePreviewHandler() {
+      public void onPreviewNativeEvent(NativePreviewEvent event) {
+        // If a previous handler already canceled the event, ignore it
+        if (event.isCanceled()) {
+          return;
+        }
+
+        // Cancel the event based on the deprecated onEventPreview
+        Event nativeEvent = event.getNativeEvent();
+        if (!onEventPreview(nativeEvent)) {
+          event.cancel();
+          return;
+        }
+
+        // Check if the event targets the popup
+        Element target = nativeEvent.getTarget();
+        boolean eventTargetsPopup = (target != null)
+            && getElement().isOrHasChild(target);
+
+        // Switch on the event type
+        int type = nativeEvent.getTypeInt();
+        switch (type) {
+          case Event.ONKEYDOWN: {
+            boolean allow = onKeyDownPreview((char) nativeEvent.getKeyCode(),
+                KeyboardListenerCollection.getKeyboardModifiers(nativeEvent));
+            if (!allow || (!eventTargetsPopup && modal)) {
+              event.cancel();
+              return;
+            }
+          }
+          case Event.ONKEYUP: {
+            boolean allow = onKeyUpPreview((char) nativeEvent.getKeyCode(),
+                KeyboardListenerCollection.getKeyboardModifiers(nativeEvent));
+            if (!allow || (!eventTargetsPopup && modal)) {
+              event.cancel();
+              return;
+            }
+          }
+          case Event.ONKEYPRESS: {
+            boolean allow = onKeyPressPreview((char) nativeEvent.getKeyCode(),
+                KeyboardListenerCollection.getKeyboardModifiers(nativeEvent));
+            if (!allow || (!eventTargetsPopup && modal)) {
+              event.cancel();
+              return;
+            }
+          }
+
+          case Event.ONMOUSEDOWN:
+            // Don't eat events if event capture is enabled, as this can
+            // interfere
+            // with dialog dragging, for example.
+            if (DOM.getCaptureElement() != null) {
+              return;
+            }
+
+            if (!eventTargetsPopup && autoHide) {
+              hide(true);
+              return;
+            }
+            break;
+          case Event.ONMOUSEUP:
+          case Event.ONMOUSEMOVE:
+          case Event.ONCLICK:
+          case Event.ONDBLCLICK: {
+            // Don't eat events if event capture is enabled, as this can
+            // interfere
+            // with dialog dragging, for example.
+            if (DOM.getCaptureElement() != null) {
+              return;
+            }
+
+            // If it's an outside click and auto-hide is enabled:
+            // hide the popup and _don't_ eat the event. ONMOUSEDOWN is used to
+            // prevent problems with showing a popup in response to a mousedown.
+            if (!eventTargetsPopup && autoHide && (type == Event.ONMOUSEDOWN)) {
+              hide(true);
+              return;
+            }
+
+            break;
+          }
+
+          case Event.ONFOCUS: {
+            if (modal && !eventTargetsPopup && (target != null)) {
+              blur(target);
+              event.cancel();
+              return;
+            }
+          }
+        }
+
+        if (modal && !eventTargetsPopup) {
+          event.cancel();
+        }
+      }
+    });
     resizeAnimation.setState(true);
   }
 
   @Override
-  protected Element getContainerElement() {
+  protected com.google.gwt.user.client.Element getContainerElement() {
     return impl.getContainerElement(DOM.getFirstChild(super.getContainerElement()));
   }
 
@@ -740,7 +780,10 @@
    */
   @Override
   protected void onDetach() {
-    DOM.removeEventPreview(this);
+    if (nativePreviewHandlerRegistration != null) {
+      nativePreviewHandlerRegistration.removeHandler();
+      nativePreviewHandlerRegistration = null;
+    }
     super.onDetach();
   }
 
