Start at adding media events for audio/video elements. Note, the underlying implementation for HTML5 media
event handling is expected to change in the near future.

Review at http://gwt-code-reviews.appspot.com/1385804

Review by: pdr@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9887 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/dev/core/src/com/google/gwt/dev/js/ast/JsRootScope.java b/dev/core/src/com/google/gwt/dev/js/ast/JsRootScope.java
index 6f1dfc1..2589bfd 100644
--- a/dev/core/src/com/google/gwt/dev/js/ast/JsRootScope.java
+++ b/dev/core/src/com/google/gwt/dev/js/ast/JsRootScope.java
@@ -237,6 +237,11 @@
       "ongesturechange",
       "ongestureend",
 
+      // Media events
+      "oncanplaythrough",
+      "onended",
+      "onprogress",
+      
       // extra window methods
       "uneval",
 
diff --git a/user/src/com/google/gwt/event/dom/client/CanPlayThroughEvent.java b/user/src/com/google/gwt/event/dom/client/CanPlayThroughEvent.java
new file mode 100644
index 0000000..3a65e67
--- /dev/null
+++ b/user/src/com/google/gwt/event/dom/client/CanPlayThroughEvent.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.event.dom.client;
+
+/**
+ * Represents a native media can play through event.
+ * 
+ * <p>
+ * <span style="color:red">Experimental API: This API is still under development
+ * and is subject to change.
+ * </span>
+ * </p>
+ */
+public class CanPlayThroughEvent extends DomEvent<CanPlayThroughHandler> {
+
+  /**
+   * Event type for media can play through events. Represents the meta-data
+   * associated with this event.
+   */
+  private static final Type<CanPlayThroughHandler> TYPE = new Type<
+      CanPlayThroughHandler>("canplaythrough", new CanPlayThroughEvent());
+
+  /**
+   * Gets the event type associated with media can play through events.
+   *
+   * @return the handler type
+   */
+  public static Type<CanPlayThroughHandler> getType() {
+    return TYPE;
+  }
+
+  /**
+   * Protected constructor, use {@link
+   * DomEvent#fireNativeEvent(com.google.gwt.dom.client.NativeEvent,
+   * com.google.gwt.event.shared.HasHandlers)} to fire media can play through
+   * events.
+   */
+  protected CanPlayThroughEvent() {
+  }
+
+  @Override
+  public final Type<CanPlayThroughHandler> getAssociatedType() {
+    return TYPE;
+  }
+
+
+  @Override
+  protected void dispatch(CanPlayThroughHandler handler) {
+    handler.onCanPlayThrough(this);
+  }
+}
\ No newline at end of file
diff --git a/user/src/com/google/gwt/event/dom/client/CanPlayThroughHandler.java b/user/src/com/google/gwt/event/dom/client/CanPlayThroughHandler.java
new file mode 100644
index 0000000..9d89477
--- /dev/null
+++ b/user/src/com/google/gwt/event/dom/client/CanPlayThroughHandler.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.event.dom.client;
+
+import com.google.gwt.event.shared.EventHandler;
+
+/**
+ * Handler interface for {@link CanPlayThroughEvent} events.
+ * 
+ * <p>
+ * <span style="color:red">Experimental API: This API is still under development
+ * and is subject to change.
+ * </span>
+ * </p>
+ */
+public interface CanPlayThroughHandler extends EventHandler {
+
+  /**
+   * Called when CanPlayThroughEvent is fired.
+   *
+   * @param event the {@link CanPlayThroughEvent} that was fired
+   */
+  void onCanPlayThrough(CanPlayThroughEvent event);
+}
\ No newline at end of file
diff --git a/user/src/com/google/gwt/event/dom/client/EndedEvent.java b/user/src/com/google/gwt/event/dom/client/EndedEvent.java
new file mode 100644
index 0000000..45c5824
--- /dev/null
+++ b/user/src/com/google/gwt/event/dom/client/EndedEvent.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.event.dom.client;
+
+/**
+ * Represents a native media ended event.
+ * 
+ * <p>
+ * <span style="color:red">Experimental API: This API is still under development
+ * and is subject to change.
+ * </span>
+ * </p>
+ */
+public class EndedEvent extends DomEvent<EndedHandler> {
+
+  /**
+   * Event type for media ended events. Represents the meta-data associated with
+   * this event.
+   */
+  private static final Type<EndedHandler> TYPE = new Type<
+      EndedHandler>("ended", new EndedEvent());
+
+  /**
+   * Gets the event type associated with media ended events.
+   *
+   * @return the handler type
+   */
+  public static Type<EndedHandler> getType() {
+    return TYPE;
+  }
+
+  /**
+   * Protected constructor, use {@link
+   * DomEvent#fireNativeEvent(com.google.gwt.dom.client.NativeEvent,
+   * com.google.gwt.event.shared.HasHandlers)} to fire media ended events.
+   */
+  protected EndedEvent() {
+  }
+
+  @Override
+  public final Type<EndedHandler> getAssociatedType() {
+    return TYPE;
+  }
+
+
+  @Override
+  protected void dispatch(EndedHandler handler) {
+    handler.onEnded(this);
+  }
+}
\ No newline at end of file
diff --git a/user/src/com/google/gwt/event/dom/client/EndedHandler.java b/user/src/com/google/gwt/event/dom/client/EndedHandler.java
new file mode 100644
index 0000000..775a662
--- /dev/null
+++ b/user/src/com/google/gwt/event/dom/client/EndedHandler.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.event.dom.client;
+
+import com.google.gwt.event.shared.EventHandler;
+
+/**
+ * Handler interface for {@link EndedEvent} events.
+ * 
+ * <p>
+ * <span style="color:red">Experimental API: This API is still under development
+ * and is subject to change.
+ * </span>
+ * </p>
+ */
+public interface EndedHandler extends EventHandler {
+
+  /**
+   * Called when EndedEvent is fired.
+   *
+   * @param event the {@link EndedEvent} that was fired
+   */
+  void onEnded(EndedEvent event);
+}
\ No newline at end of file
diff --git a/user/src/com/google/gwt/event/dom/client/HasAllMediaHandlers.java b/user/src/com/google/gwt/event/dom/client/HasAllMediaHandlers.java
new file mode 100644
index 0000000..58422f5
--- /dev/null
+++ b/user/src/com/google/gwt/event/dom/client/HasAllMediaHandlers.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.gwt.event.dom.client;
+
+/**
+ * This is a convenience interface that includes all media handlers defined by
+ * the core GWT system.
+ * 
+ * <p>
+ * <span style="color:red">Experimental API: This API is still under development
+ * and is subject to change.
+ * </span>
+ * </p>
+ */
+public interface HasAllMediaHandlers extends HasEndedHandlers,
+    HasProgressHandlers, HasCanPlayThroughHandlers {
+}
\ No newline at end of file
diff --git a/user/src/com/google/gwt/event/dom/client/HasCanPlayThroughHandlers.java b/user/src/com/google/gwt/event/dom/client/HasCanPlayThroughHandlers.java
new file mode 100644
index 0000000..c85e88f
--- /dev/null
+++ b/user/src/com/google/gwt/event/dom/client/HasCanPlayThroughHandlers.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.event.dom.client;
+
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.event.shared.HasHandlers;
+
+/**
+ * A widget that implements this interface provides registration for
+ * {@link CanPlayThroughHandler} instances.
+ * 
+ * <p>
+ * <span style="color:red">Experimental API: This API is still under development
+ * and is subject to change.
+ * </span>
+ * </p>
+ */
+public interface HasCanPlayThroughHandlers extends HasHandlers {
+  /**
+   * Adds a {@link CanPlayThroughEvent} handler.
+   *
+   * @param handler the media CanPlayThrough handler
+   * @return {@link HandlerRegistration} used to remove this handler
+   */
+  HandlerRegistration addCanPlayThroughHandler(CanPlayThroughHandler handler);
+}
diff --git a/user/src/com/google/gwt/event/dom/client/HasEndedHandlers.java b/user/src/com/google/gwt/event/dom/client/HasEndedHandlers.java
new file mode 100644
index 0000000..6e1f2de
--- /dev/null
+++ b/user/src/com/google/gwt/event/dom/client/HasEndedHandlers.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.event.dom.client;
+
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.event.shared.HasHandlers;
+
+/**
+ * A widget that implements this interface provides registration for
+ * {@link EndedHandler} instances.
+ * 
+ * <p>
+ * <span style="color:red">Experimental API: This API is still under development
+ * and is subject to change.
+ * </span>
+ * </p>
+ */
+public interface HasEndedHandlers extends HasHandlers {
+  /**
+   * Adds a {@link EndedEvent} handler.
+   *
+   * @param handler the media ended handler
+   * @return {@link HandlerRegistration} used to remove this handler
+   */
+  HandlerRegistration addEndedHandler(EndedHandler handler);
+}
diff --git a/user/src/com/google/gwt/event/dom/client/HasProgressHandlers.java b/user/src/com/google/gwt/event/dom/client/HasProgressHandlers.java
new file mode 100644
index 0000000..526bf3a
--- /dev/null
+++ b/user/src/com/google/gwt/event/dom/client/HasProgressHandlers.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.event.dom.client;
+
+import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.event.shared.HasHandlers;
+
+/**
+ * A widget that implements this interface provides registration for
+ * {@link ProgressHandler} instances.
+ * 
+ * <p>
+ * <span style="color:red">Experimental API: This API is still under development
+ * and is subject to change.
+ * </span>
+ * </p>
+ */
+public interface HasProgressHandlers extends HasHandlers {
+  /**
+   * Adds a {@link ProgressEvent} handler.
+   *
+   * @param handler the media progress handler
+   * @return {@link HandlerRegistration} used to remove this handler
+   */
+  HandlerRegistration addProgressHandler(ProgressHandler handler);
+}
diff --git a/user/src/com/google/gwt/event/dom/client/ProgressEvent.java b/user/src/com/google/gwt/event/dom/client/ProgressEvent.java
new file mode 100644
index 0000000..4fa6a5e
--- /dev/null
+++ b/user/src/com/google/gwt/event/dom/client/ProgressEvent.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.event.dom.client;
+
+/**
+ * Represents a native media progress event.
+ * 
+ * <p>
+ * <span style="color:red">Experimental API: This API is still under development
+ * and is subject to change.
+ * </span>
+ * </p>
+ */
+public class ProgressEvent extends DomEvent<ProgressHandler> {
+
+  /**
+   * Event type for media progress events. Represents the meta-data associated
+   * with this event.
+   */
+  private static final Type<ProgressHandler> TYPE = new Type<
+  ProgressHandler>("progress", new ProgressEvent());
+
+  /**
+   * Gets the event type associated with media progress events.
+   *
+   * @return the handler type
+   */
+  public static Type<ProgressHandler> getType() {
+    return TYPE;
+  }
+
+  /**
+   * Protected constructor, use {@link
+   * DomEvent#fireNativeEvent(com.google.gwt.dom.client.NativeEvent,
+   * com.google.gwt.event.shared.HasHandlers)} to fire media progress events.
+   */
+  protected ProgressEvent() {
+  }
+
+  @Override
+  public final Type<ProgressHandler> getAssociatedType() {
+    return TYPE;
+  }
+
+
+  @Override
+  protected void dispatch(ProgressHandler handler) {
+    handler.onProgress(this);
+  }
+}
\ No newline at end of file
diff --git a/user/src/com/google/gwt/event/dom/client/ProgressHandler.java b/user/src/com/google/gwt/event/dom/client/ProgressHandler.java
new file mode 100644
index 0000000..f4aaad3
--- /dev/null
+++ b/user/src/com/google/gwt/event/dom/client/ProgressHandler.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.event.dom.client;
+
+import com.google.gwt.event.shared.EventHandler;
+
+/**
+ * Handler interface for {@link ProgressEvent} events.
+ * 
+ * <p>
+ * <span style="color:red">Experimental API: This API is still under development
+ * and is subject to change.
+ * </span>
+ * </p>
+ */
+public interface ProgressHandler extends EventHandler {
+
+  /**
+   * Called when ProgressEvent is fired.
+   *
+   * @param event the {@link ProgressEvent} that was fired
+   */
+  void onProgress(ProgressEvent event);
+}
\ No newline at end of file
diff --git a/user/src/com/google/gwt/media/client/Audio.java b/user/src/com/google/gwt/media/client/Audio.java
index 987dbb2..4152298 100644
--- a/user/src/com/google/gwt/media/client/Audio.java
+++ b/user/src/com/google/gwt/media/client/Audio.java
@@ -19,6 +19,14 @@
 import com.google.gwt.dom.client.AudioElement;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.PartialSupport;
+import com.google.gwt.event.dom.client.CanPlayThroughEvent;
+import com.google.gwt.event.dom.client.CanPlayThroughHandler;
+import com.google.gwt.event.dom.client.EndedEvent;
+import com.google.gwt.event.dom.client.EndedHandler;
+import com.google.gwt.event.dom.client.HasAllMediaHandlers;
+import com.google.gwt.event.dom.client.ProgressEvent;
+import com.google.gwt.event.dom.client.ProgressHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
 import com.google.gwt.user.client.ui.FocusWidget;
 
 /**
@@ -34,7 +42,7 @@
  * This widget may not be supported on all browsers.
  */
 @PartialSupport
-public class Audio extends FocusWidget {
+public class Audio extends FocusWidget implements HasAllMediaHandlers {
   private static AudioElementSupportDetector detector;
 
   /**
@@ -83,6 +91,18 @@
     setElement(element);
   }
 
+  public HandlerRegistration addCanPlayThroughHandler(CanPlayThroughHandler handler) {
+    return addDomHandler(handler, CanPlayThroughEvent.getType());
+  }
+
+  public HandlerRegistration addEndedHandler(EndedHandler handler) {
+    return addDomHandler(handler, EndedEvent.getType());
+  }
+
+  public HandlerRegistration addProgressHandler(ProgressHandler handler) {
+    return addDomHandler(handler, ProgressEvent.getType());
+  }
+
   /**
    * Returns the attached AudioElement.
    * 
diff --git a/user/src/com/google/gwt/media/client/Video.java b/user/src/com/google/gwt/media/client/Video.java
index 92ed14e..74eb17d 100644
--- a/user/src/com/google/gwt/media/client/Video.java
+++ b/user/src/com/google/gwt/media/client/Video.java
@@ -18,6 +18,14 @@
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.dom.client.Document;
 import com.google.gwt.dom.client.VideoElement;
+import com.google.gwt.event.dom.client.CanPlayThroughEvent;
+import com.google.gwt.event.dom.client.CanPlayThroughHandler;
+import com.google.gwt.event.dom.client.EndedEvent;
+import com.google.gwt.event.dom.client.EndedHandler;
+import com.google.gwt.event.dom.client.HasAllMediaHandlers;
+import com.google.gwt.event.dom.client.ProgressEvent;
+import com.google.gwt.event.dom.client.ProgressHandler;
+import com.google.gwt.event.shared.HandlerRegistration;
 import com.google.gwt.user.client.ui.FocusWidget;
 
 /**
@@ -32,7 +40,7 @@
  * 
  * This widget may not be supported on all browsers.
  */
-public class Video extends FocusWidget {
+public class Video extends FocusWidget implements HasAllMediaHandlers {
   private static VideoElementSupportDetector detector;
 
   /**
@@ -90,6 +98,18 @@
     getVideoElement().setSrc(src);
   }
 
+  public HandlerRegistration addCanPlayThroughHandler(CanPlayThroughHandler handler) {
+    return addDomHandler(handler, CanPlayThroughEvent.getType());
+  }
+
+  public HandlerRegistration addEndedHandler(EndedHandler handler) {
+    return addDomHandler(handler, EndedEvent.getType());
+  }
+
+  public HandlerRegistration addProgressHandler(ProgressHandler handler) {
+    return addDomHandler(handler, ProgressEvent.getType());
+  }
+
   /**
    * Returns the attached VideoElement.
    * 
diff --git a/user/src/com/google/gwt/user/client/DOM.java b/user/src/com/google/gwt/user/client/DOM.java
index 32329cd..180a60b 100644
--- a/user/src/com/google/gwt/user/client/DOM.java
+++ b/user/src/com/google/gwt/user/client/DOM.java
@@ -1211,6 +1211,17 @@
   }
 
   /**
+   * Sinks a named event. Events will be fired to the nearest
+   * {@link EventListener} specified on any of the element's parents.
+   * 
+   * @param elem the element whose events are to be retrieved
+   * @param eventTypeName name of the event to sink on this element
+   */
+  public static void sinkBitlessEvent(Element elem, String eventTypeName) {
+    impl.sinkBitlessEvent(elem, eventTypeName);
+  }
+  
+  /**
    * Sets the current set of events sunk by a given element. These events will
    * be fired to the nearest {@link EventListener} specified on any of the
    * element's parents.
diff --git a/user/src/com/google/gwt/user/client/Event.java b/user/src/com/google/gwt/user/client/Event.java
index f1e646e..317ff7e 100644
--- a/user/src/com/google/gwt/user/client/Event.java
+++ b/user/src/com/google/gwt/user/client/Event.java
@@ -399,6 +399,50 @@
   public static final int GESTUREEVENTS = ONGESTURESTART | ONGESTURECHANGE | ONGESTUREEND;
 
   /**
+   * Fired when media is fully buffered and can be played through to the end.
+   * 
+   * <p>
+   * <span style="color:red">Experimental API: This API is still under development
+   * and is subject to change.
+   * </span>
+   * </p>
+   */
+  public static final int ONCANPLAYTHROUGH = 0x20000000;
+
+  /**
+   * Fired when media stops playing.
+   * 
+   * <p>
+   * <span style="color:red">Experimental API: This API is still under development
+   * and is subject to change.
+   * </span>
+   * </p>
+   */
+  public static final int ONENDED = 0x8000000;
+
+  /**
+   * Fired when progress is made downloading media.
+   * 
+   * <p>
+   * <span style="color:red">Experimental API: This API is still under development
+   * and is subject to change.
+   * </span>
+   * </p>
+   */
+  public static final int ONPROGRESS = 0x10000000;
+
+  /**
+   * A bit-mask covering all media events.
+   * 
+   * <p>
+   * <span style="color:red">Experimental API: This API is still under development
+   * and is subject to change.
+   * </span>
+   * </p>
+   */
+  public static final int MEDIAEVENTS = ONCANPLAYTHROUGH | ONENDED | ONPROGRESS;
+
+  /**
    * Value returned by accessors when the actual integer value is undefined. In
    * Development Mode, most accessors assert that the requested attribute is
    * reliable across all supported browsers.
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImpl.java b/user/src/com/google/gwt/user/client/impl/DOMImpl.java
index 79c8b15..9493730 100644
--- a/user/src/com/google/gwt/user/client/impl/DOMImpl.java
+++ b/user/src/com/google/gwt/user/client/impl/DOMImpl.java
@@ -92,6 +92,9 @@
     case "gesturestart": return 0x1000000;
     case "gesturechange": return 0x2000000;
     case "gestureend": return 0x4000000;
+    case "ended": return 0x8000000;
+    case "progress": return 0x10000000;
+    case "canplaythrough": return 0x20000000;
     default: return -1;
     }
   }-*/; 
@@ -131,6 +134,8 @@
     elem.__listener = listener;
   }-*/;
 
+  public abstract void sinkBitlessEvent(Element elem, String eventTypeName);
+
   public abstract void sinkEvents(Element elem, int eventBits);
 
   /**
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImplStandard.java b/user/src/com/google/gwt/user/client/impl/DOMImplStandard.java
index dd70a3a..f9c44ec 100644
--- a/user/src/com/google/gwt/user/client/impl/DOMImplStandard.java
+++ b/user/src/com/google/gwt/user/client/impl/DOMImplStandard.java
@@ -133,6 +133,12 @@
   }
 
   @Override
+  public void sinkBitlessEvent(Element elem, String eventTypeName) {
+    maybeInitializeEventSystem();
+    sinkBitlessEventImpl(elem, eventTypeName);
+  }
+  
+  @Override
   public void sinkEvents(Element elem, int bits) {
     maybeInitializeEventSystem();
     sinkEventsImpl(elem, bits);
@@ -205,6 +211,17 @@
     $wnd.addEventListener('gestureend', @com.google.gwt.user.client.impl.DOMImplStandard::dispatchCapturedMouseEvent, true);
   }-*/;
 
+  protected native void sinkBitlessEventImpl(Element elem, String eventTypeName) /*-{
+    if (eventTypeName == "dragenter")
+      elem.ondragenter = @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent;
+    if (eventTypeName == "dragexit")
+      elem.ondragexit  = @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent;
+    if (eventTypeName == "dragover")
+      elem.ondragover  = @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent;
+    if (eventTypeName == "drop")
+      elem.ondrop      = @com.google.gwt.user.client.impl.DOMImplStandard::dispatchEvent;
+  }-*/;
+  
   protected native void sinkEventsImpl(Element elem, int bits) /*-{
     var chMask = (elem.__eventBits || 0) ^ bits;
     elem.__eventBits = bits;
diff --git a/user/src/com/google/gwt/user/client/impl/DOMImplTrident.java b/user/src/com/google/gwt/user/client/impl/DOMImplTrident.java
index 474cd7d..94d196d 100644
--- a/user/src/com/google/gwt/user/client/impl/DOMImplTrident.java
+++ b/user/src/com/google/gwt/user/client/impl/DOMImplTrident.java
@@ -219,6 +219,11 @@
   }
 
   @Override
+  public void sinkBitlessEvent(Element elem, String eventTypeName) {
+    // not supported
+  }
+
+  @Override
   public void sinkEvents(Element elem, int bits) {
     maybeInitializeEventSystem();
     sinkEventsImpl(elem, bits);
diff --git a/user/src/com/google/gwt/user/client/ui/UIObject.java b/user/src/com/google/gwt/user/client/ui/UIObject.java
index d891873..e8abab3 100644
--- a/user/src/com/google/gwt/user/client/ui/UIObject.java
+++ b/user/src/com/google/gwt/user/client/ui/UIObject.java
@@ -753,6 +753,18 @@
   }
 
   /**
+   * Sinks a named event. Note that only {@link Widget widgets} may actually
+   * receive events, but can receive events from all objects contained within
+   * them.
+   * 
+   * @param eventTypeName name of the event to sink on this element
+   * @see com.google.gwt.user.client.Event
+   */
+  public void sinkBitlessEvent(String eventTypeName) {
+    DOM.sinkBitlessEvent(getElement(), eventTypeName);
+  }
+  
+  /**
    * Adds a set of events to be sunk by this object. Note that only
    * {@link Widget widgets} may actually receive events, but can receive events
    * from all objects contained within them.
diff --git a/user/src/com/google/gwt/user/client/ui/Widget.java b/user/src/com/google/gwt/user/client/ui/Widget.java
index 18082ab..7bc1ba3 100644
--- a/user/src/com/google/gwt/user/client/ui/Widget.java
+++ b/user/src/com/google/gwt/user/client/ui/Widget.java
@@ -64,6 +64,28 @@
   }
 
   /**
+   * For <a href=
+   * "http://code.google.com/p/google-web-toolkit/wiki/UnderstandingMemoryLeaks"
+   * >browsers which do not leak</a>, adds a native event handler to the widget.
+   * Note that, unlike the
+   * {@link #addDomHandler(EventHandler, com.google.gwt.event.dom.client.DomEvent.Type)}
+   * implementation, there is no need to attach the widget to the DOM in order
+   * to cause the event handlers to be attached.
+   *
+   * @param <H> the type of handler to add
+   * @param type the event key
+   * @param handler the handler
+   * @return {@link HandlerRegistration} used to remove the handler
+   */
+  public final <H extends EventHandler> HandlerRegistration addBitlessDomHandler(
+      final H handler, DomEvent.Type<H> type) {
+    assert handler != null : "handler must not be null";
+    assert type != null : "type must not be null";
+    sinkBitlessEvent(type.getName());
+    return ensureHandlers().addHandler(type, handler);
+  }
+
+  /**
    * Adds a native event handler to the widget and sinks the corresponding
    * native event. If you do not want to sink the native event, use the generic
    * addHandler method instead.
diff --git a/user/test/com/google/gwt/media/MediaSuite.java b/user/test/com/google/gwt/media/MediaSuite.java
index 388c736..1ecc7e4 100644
--- a/user/test/com/google/gwt/media/MediaSuite.java
+++ b/user/test/com/google/gwt/media/MediaSuite.java
@@ -17,6 +17,7 @@
 package com.google.gwt.media;
 
 import com.google.gwt.junit.tools.GWTTestSuite;
+import com.google.gwt.user.client.MediaEventsSinkTest;
 
 import junit.framework.Test;
 
@@ -27,6 +28,8 @@
   public static Test suite() {
     GWTTestSuite suite = new GWTTestSuite("Test suite for Media GWTTestCases");
     
+    suite.addTestSuite(MediaEventsSinkTest.class);
+
     /* 
      * Tests disabled temporarily
      * suite.addTestSuite(AudioTest.class);
diff --git a/user/test/com/google/gwt/user/client/MediaEventsSinkTest.java b/user/test/com/google/gwt/user/client/MediaEventsSinkTest.java
new file mode 100644
index 0000000..b457d91
--- /dev/null
+++ b/user/test/com/google/gwt/user/client/MediaEventsSinkTest.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2011 Google Inc.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.google.gwt.user.client;
+
+import com.google.gwt.event.dom.client.CanPlayThroughEvent;
+import com.google.gwt.event.dom.client.CanPlayThroughHandler;
+import com.google.gwt.event.dom.client.EndedEvent;
+import com.google.gwt.event.dom.client.EndedHandler;
+import com.google.gwt.event.dom.client.HasAllMediaHandlers;
+import com.google.gwt.event.dom.client.ProgressEvent;
+import com.google.gwt.event.dom.client.ProgressHandler;
+import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.media.client.Audio;
+import com.google.gwt.media.client.Video;
+import com.google.gwt.user.client.ui.RootPanel;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * Test Case for sinking of media events.
+ */
+public class MediaEventsSinkTest extends GWTTestCase {
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.user.User";
+  }
+
+  public void testAudioMediaEventsSinkByAddingHandler() {
+    if (!Audio.isSupported()) {
+      return;
+    }
+    verifyProgressEventSinkOnAddHandler(Audio.createIfSupported());
+    verifyEndedEventSinkOnAddHandler(Audio.createIfSupported());
+    verifyCanPlayThroughEventSinkOnAddHandler(Audio.createIfSupported());
+  }
+
+  public void testVideoMediaEventsSinkByAddingHandler() {
+    if (!Audio.isSupported()) {
+      return;
+    }
+    verifyProgressEventSinkOnAddHandler(Video.createIfSupported());
+    verifyEndedEventSinkOnAddHandler(Video.createIfSupported());
+    verifyCanPlayThroughEventSinkOnAddHandler(Video.createIfSupported());
+  }
+
+  public void testMediaEventBitFieldsNotTriviallyZero() {
+    assertNotSame(0, Event.ONCANPLAYTHROUGH);
+    assertNotSame(0, Event.ONPROGRESS);
+    assertNotSame(0, Event.ONENDED);
+  }
+
+  @Override
+  protected void gwtTearDown() throws Exception {
+    // clean up after ourselves
+    RootPanel.get().clear();
+    super.gwtTearDown();
+  }
+
+  private <W extends Widget & HasAllMediaHandlers> void assertNotSunkAfterAttach(
+      W w, String eventName, boolean isSunk) {
+    assertFalse("Event should not be sunk on " + w.getClass().getName()
+        + " until a " + eventName + " handler has been added", isSunk);
+  }
+
+  private <W extends Widget & HasAllMediaHandlers> void assertSunkAfterAddHandler(
+      W w, String eventName, boolean isSunk) {
+    assertTrue("Event should have been sunk on " + w.getClass().getName()
+        + " once the widget has been attached and a " + eventName
+        + " handler has been added", isSunk);
+  }
+
+  private boolean isCanPlayThroughEventSunk(Element e) {
+    return (DOM.getEventsSunk(e) & Event.ONCANPLAYTHROUGH) != 0;
+  }
+
+  private boolean isEndedEventSunk(Element e) {
+    return (DOM.getEventsSunk(e) & Event.ONENDED) != 0;
+  }
+
+  private boolean isProgressEventSunk(Element e) {
+    return (DOM.getEventsSunk(e) & Event.ONPROGRESS) != 0;
+  }
+
+  private <W extends Widget & HasAllMediaHandlers> void verifyCanPlayThroughEventSinkOnAddHandler(
+      W w) {
+    verifyCanPlayThroughEventSinkOnAddHandler(w, w.getElement());
+  }
+
+  private <W extends Widget & HasAllMediaHandlers> void verifyCanPlayThroughEventSinkOnAddHandler(
+      W w, Element e) {
+    RootPanel.get().add(w);
+
+    assertNotSunkAfterAttach(w, CanPlayThroughEvent.getType().getName(),
+        isCanPlayThroughEventSunk(e));
+
+    w.addCanPlayThroughHandler(new CanPlayThroughHandler() {
+      public void onCanPlayThrough(CanPlayThroughEvent event) {
+      }
+    });
+
+    assertSunkAfterAddHandler(w, CanPlayThroughEvent.getType().getName(),
+        isCanPlayThroughEventSunk(e));
+  }
+
+  private <W extends Widget & HasAllMediaHandlers> void verifyEndedEventSinkOnAddHandler(
+      W w) {
+    verifyEndedEventSinkOnAddHandler(w, w.getElement());
+  }
+
+  private <W extends Widget & HasAllMediaHandlers> void verifyEndedEventSinkOnAddHandler(
+      W w, Element e) {
+    RootPanel.get().add(w);
+
+    assertNotSunkAfterAttach(w, EndedEvent.getType().getName(),
+        isEndedEventSunk(e));
+
+    w.addEndedHandler(new EndedHandler() {
+      public void onEnded(EndedEvent event) {
+      }
+    });
+
+    assertSunkAfterAddHandler(w, EndedEvent.getType().getName(),
+        isEndedEventSunk(e));
+  }
+
+  private <W extends Widget & HasAllMediaHandlers> void verifyProgressEventSinkOnAddHandler(
+      W w) {
+    verifyProgressEventSinkOnAddHandler(w, w.getElement());
+  }
+
+  private <W extends Widget & HasAllMediaHandlers> void verifyProgressEventSinkOnAddHandler(
+      W w, Element e) {
+    RootPanel.get().add(w);
+
+    assertNotSunkAfterAttach(w, ProgressEvent.getType().getName(),
+        isProgressEventSunk(e));
+
+    w.addProgressHandler(new ProgressHandler() {
+      public void onProgress(ProgressEvent event) {
+      }
+    });
+
+    assertSunkAfterAddHandler(w, ProgressEvent.getType().getName(),
+        isProgressEventSunk(e));
+  }
+}
\ No newline at end of file