Cherry picking r9652 into releases/2.2 git-svn-id: https://google-web-toolkit.googlecode.com/svn/releases/2.2@9654 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/dom/client/AudioElement.java b/user/src/com/google/gwt/dom/client/AudioElement.java new file mode 100644 index 0000000..2cf92e1 --- /dev/null +++ b/user/src/com/google/gwt/dom/client/AudioElement.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.dom.client; + +/** + * Audio element. + * + * <p> + * <span style="color:red">Experimental API: This API is still under development + * and is subject to change. + * </span> + * </p> + * + * @see <a href="http://www.w3.org/TR/html5/video.html#audio">W3C HTML 5 Specification</a> + */ +@TagName(AudioElement.TAG) +public class AudioElement extends MediaElement { + + /** + * The tag for this element. + */ + public static final String TAG = "audio"; + + protected AudioElement() { + } +}
diff --git a/user/src/com/google/gwt/dom/client/DOMImplMozilla.java b/user/src/com/google/gwt/dom/client/DOMImplMozilla.java index 900fcf9..d892460 100644 --- a/user/src/com/google/gwt/dom/client/DOMImplMozilla.java +++ b/user/src/com/google/gwt/dom/client/DOMImplMozilla.java
@@ -19,25 +19,50 @@ * Mozilla implementation of StandardBrowser. */ class DOMImplMozilla extends DOMImplStandard { - - /** - * Return true if using Gecko 1.9.0 (Firefox 3) or earlier. - * - * @return true if using Gecko 1.9.0 (Firefox 3) or earlier. - */ - @SuppressWarnings("unused") - private static native boolean isGecko190OrBefore() /*-{ + + private static native int getGeckoVersion() /*-{ var result = /rv:([0-9]+)\.([0-9]+)\.([0-9]+)?/.exec(navigator.userAgent.toLowerCase()); if (result && result.length >= 3) { var version = (parseInt(result[1]) * 1000000) + (parseInt(result[2]) * 1000) + parseInt(result.length == 4 ? result[3] : 0); - if (version <= 1009000) { - return true; - } + return version; } - return false; + return -1; // not gecko }-*/; + /** + * Return true if using Gecko 1.9.0 (Firefox 3) or earlier. + * + * @return true if using Gecko 1.9.0 (Firefox 3) or earlier + */ + @SuppressWarnings("unused") + private static boolean isGecko190OrBefore() { + int geckoVersion = getGeckoVersion(); + return (geckoVersion != -1) && (geckoVersion <= 1009000); + } + + /** + * Return true if using Gecko 1.9.1 (Firefox 3.5) or earlier. + * + * @return true if using Gecko 1.9.1 (Firefox 3.5) or earlier + */ + @SuppressWarnings("unused") + private static boolean isGecko191OrBefore() { + int geckoVersion = getGeckoVersion(); + return (geckoVersion != -1) && (geckoVersion <= 1009001); + } + + /** + * Return true if using Gecko 2.0.0 (Firefox 4.0) or earlier. + * + * @return true if using Gecko 2.0.0 (Firefox 4.0) or earlier + */ + @SuppressWarnings("unused") + private static boolean isGecko2OrBefore() { + int geckoVersion = getGeckoVersion(); + return (geckoVersion != -1) && (geckoVersion < 2000000); + } + @Override public native void buttonClick(ButtonElement button) /*-{ var doc = button.ownerDocument;
diff --git a/user/src/com/google/gwt/dom/client/Document.java b/user/src/com/google/gwt/dom/client/Document.java index 256f88a..8981878 100644 --- a/user/src/com/google/gwt/dom/client/Document.java +++ b/user/src/com/google/gwt/dom/client/Document.java
@@ -54,6 +54,15 @@ } /** + * Creates an <audio> element. + * + * @return the newly created element + */ + public final AudioElement createAudioElement() { + return (AudioElement) DOMImpl.impl.createElement(this, AudioElement.TAG); + } + + /** * Creates a <base> element. * * @return the newly created element @@ -1190,6 +1199,15 @@ }-*/; /** + * Creates a <video> element. + * + * @return the newly created element + */ + public final VideoElement createVideoElement() { + return (VideoElement) DOMImpl.impl.createElement(this, VideoElement.TAG); + } + + /** * Enables or disables scrolling of the document. * * @param enable whether scrolling should be enabled or disabled
diff --git a/user/src/com/google/gwt/dom/client/MediaElement.java b/user/src/com/google/gwt/dom/client/MediaElement.java new file mode 100644 index 0000000..16e24c3 --- /dev/null +++ b/user/src/com/google/gwt/dom/client/MediaElement.java
@@ -0,0 +1,551 @@ +/* + * Copyright 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.dom.client; + +import com.google.gwt.media.dom.client.MediaError; +import com.google.gwt.media.dom.client.TimeRanges; + +/** + * Common superclass for Audio and Video elements. + * + * See {@link <a href="http://www.w3.org/TR/html5/video.html">W3C HTML5 Video and Audio</a>} + */ +public class MediaElement extends Element { + + /** + * Constant returned from {@link #canPlayType(String)}. + */ + public static final String CAN_PLAY_PROBABLY = "probably"; + + /** + * Constant returned from {@link #canPlayType(String)}. + */ + public static final String CAN_PLAY_MAYBE = "maybe"; + + /** + * Constant returned from {@link #canPlayType(String)}. + */ + public static final String CANNOT_PLAY = ""; + + /** + * Constant returned from {@link #getReadyState()}. + */ + public static final int HAVE_NOTHING = 0; + + /** + * Constant returned from {@link #getReadyState()}. + */ + public static final int HAVE_METADATA = 1; + + /** + * Constant returned from {@link #getReadyState()}. + */ + public static final int HAVE_CURRENT_DATA = 2; + + /** + * Constant returned from {@link #getReadyState()}. + */ + public static final int HAVE_FUTURE_DATA = 3; + + /** + * Constant returned from {@link #getReadyState()}. + */ + public static final int HAVE_ENOUGH_DATA = 4; + + /** + * Constant returned from {@link #getNetworkState}. + */ + public static final int NETWORK_EMPTY = 0; + + /** + * Constant returned from {@link #getNetworkState}. + */ + public static final int NETWORK_IDLE = 1; + + /** + * Constant returned from {@link #getNetworkState}. + */ + public static final int NETWORK_LOADING = 2; + + /** + * Constant returned from {@link #getNetworkState}. + */ + public static final int NETWORK_NO_SOURCE = 3; + + /** + * Constant used by {@link #getPreload()} and {@link #setPreload(String)}. + */ + public static final String PRELOAD_AUTO = "auto"; + + /** + * Constant used by {@link #getPreload()} and {@link #setPreload(String)}. + */ + public static final String PRELOAD_METADATA = "metadata"; + + /** + * Constant used by {@link #getPreload()} and {@link #setPreload(String)}. + */ + public static final String PRELOAD_NONE = "none"; + + protected MediaElement() { + } + + /** + * Returns {@code true} if the native player is capable of playing content of + * the given MIME type. + * + * @param type a String representing a MIME type + * @return one of {@link #CAN_PLAY_PROBABLY}, {@link #CAN_PLAY_MAYBE}, or + * {@link #CANNOT_PLAY} + */ + public final native String canPlayType(String type) /*-{ + return this.canPlayType(type); + }-*/; + + /** + * Returns a {@link TimeRanges} object indicating which portions of the + * source have been buffered locally. + * + * @return a {@link TimeRanges} instance, or {@code null}. + */ + public final native TimeRanges getBuffered() /*-{ + return this.buffered; + }-*/; + + /** + * Returns the URL of the current media source, or the empty String + * if no source is set. + * + * @return a String URL + */ + public final native String getCurrentSrc() /*-{ + return this.currentSrc; + }-*/; + + /** + * Returns the current time within the source media stream. + * + * @return the time, in seconds, as a double + * + * @see #setCurrentTime(double) + */ + public final native double getCurrentTime() /*-{ + return this.currentTime; + }-*/; + + /** + * Returns the default playback rate, where 1.0 corresponds to normal + * playback. If no rate has been set, 1.0 is returned. + * + * @return the current default playback rate, or 1.0 if it has not been set + * + * @see #setDefaultPlaybackRate(double) + */ + public final double getDefaultPlaybackRate() { + return getDoubleAttr("defaultPlaybackRate", 1.0); + } + + /** + * Returns the duration of the source media stream, in seconds. If the + * duration is unknown, {@link Double#NaN} is returned. For unbounded media + * streams, {@link Double#POSITIVE_INFINITY} is returned. + * + * @return a positive duration in seconds, NaN, or Infinity + */ + public final native double getDuration() /*-{ + return this.duration; + }-*/; + + /** + * Returns the type of error that has occurred while attempting to load + * and play the media. If no error has occurred, {@code null} is returned. + * + * @return a {@link MediaError} instance, or {@code null} + */ + public final native MediaError getError() /*-{ + return this.error || null; + }-*/; + + /** + * Returns the time to which the media stream was seeked at the time it was + * loaded, in seconds, or 0.0 if the position is unknown. + * + * @return the initial time, or 0.0 if unknown + */ + public final double getInitialTime() { + return getDoubleAttr("initialTime", 0.0); + } + + /** + * Returns the network state, one of {@link #NETWORK_EMPTY}, + * {@link #NETWORK_IDLE}, {@link #NETWORK_LOADING}, or + * {@link #NETWORK_NO_SOURCE}. + * + * @return an integer constant indicating the network state + * + * @see #NETWORK_EMPTY + * @see #NETWORK_IDLE + * @see #NETWORK_LOADING + * @see #NETWORK_NO_SOURCE + */ + public final native int getNetworkState() /*-{ + return this.networkState; + }-*/; + + /** + * Returns the playback rate, where 1.0 corresponds to normal + * playback. If the rate has not been set, 1.0 is returned. + * + * @return the playback rate, if known, otherwise 1.0 + * + * @see #setPlaybackRate(double) + */ + public final native double getPlaybackRate() /*-{ + var rate = this.playbackRate; + if (rate != null && typeof(rate) == 'number') { + return rate; + } + return 1.0; + }-*/; + + /** + * Returns a {@link TimeRanges} object indicating which portions of the + * source have been played. + * + * @return a {@link TimeRanges} instance, or {@code null}. + */ + public final native TimeRanges getPlayed() /*-{ + return this.played; + }-*/; + + /** + * Returns the preload setting, one of {@link #PRELOAD_AUTO}, + * {@link #PRELOAD_METADATA}, or {@link #PRELOAD_NONE}. + * + * @return the preload setting + * + * @see #setPreload(String) + * @see #PRELOAD_AUTO + * @see #PRELOAD_METADATA + * @see #PRELOAD_NONE + */ + public final native String getPreload() /*-{ + return this.preload; + }-*/; + + /** + * Returns the current state of the media with respect to rendering the + * current playback position, as one of the constants + * {@link #HAVE_CURRENT_DATA}, {@link #HAVE_ENOUGH_DATA}, + * {@link #HAVE_FUTURE_DATA}, {@link #HAVE_METADATA}, or {@link #HAVE_NOTHING} + * . + * + * @return an integer constant indicating the ready state + * + * @see #HAVE_CURRENT_DATA + * @see #HAVE_ENOUGH_DATA + * @see #HAVE_FUTURE_DATA + * @see #HAVE_METADATA + * @see #HAVE_NOTHING + */ + public final native int getReadyState() /*-{ + return this.readyState; + }-*/; + + /** + * Returns a {@link TimeRanges} object indicating which portions of the + * source are seekable. + * + * @return a {@link TimeRanges} instance, or {@code null}. + */ + public final native TimeRanges getSeekable() /*-{ + return this.seekable; + }-*/; + + /** + * Returns the source URL for the media, or {@code null} if none is set. + * + * @return a String URL or {@code null} + * + * @see #setSrc(String) + */ + public final native String getSrc() /*-{ + return this.getAttribute('src'); + }-*/; + + /** + * Returns the time corresponding to the zero time in the media timeline, + * measured in seconds since midnight, January 1 1970 UTC, or + * {@link Double#NaN} if none is specified. + * + * @return the start time + */ + public final double getStartOffsetTime() { + return getDoubleAttr("startOffsetTime", Double.NaN); + } + + /** + * Returns the current audio volume setting for the media, as a number + * between 0.0 and 1.0. + * + * @return a number between 0.0 (silent) and 1.0 (loudest) + * + * @see #setVolume(double) + */ + public final native double getVolume() /*-{ + return this.volume; + }-*/; + + /** + * Returns {@code true} if the media player should display interactive + * controls (for example, to control play/pause, seek position, and volume), + * {@code false} otherwise. + * + * @return whether controls should be displayed + * + * @see #setControls(boolean) + */ + public final native boolean hasControls() /*-{ + return this.hasAttribute('controls'); + }-*/; + + /** + * Returns {@code true} if playback has reached the end of the media, {@code + * false} otherwise. + * + * @return whether playback has ended + */ + public final native boolean hasEnded() /*-{ + return this.ended; + }-*/; + + /** + * Returns {@code true} if autoplay is enabled, {@code false} otherwise. When + * autoplay is enabled, the user agent will begin playback automatically as + * soon as it can do so without stopping. + * + * @return the autoplay setting + * + * @see #setAutoplay(boolean) + */ + public final native boolean isAutoplay() /*-{ + return this.hasAttribute('autoplay'); + }-*/; + + /** + * Returns {@code true} if the user agent is to seek back to the start of the + * media once playing has ended, {@code false} otherwise. + * + * @return the loop setting + * + * @see #setLoop(boolean) + */ + public final native boolean isLoop() /*-{ + return this.hasAttribute('loop'); + }-*/; + + /** + * Returns {@code true} if the volume is to be muted (overriding the normal + * volume setting), {@code false} otherwise. + * + * @return the muting setting + * + * @see #setMuted(boolean) + * @see #getVolume() + * @see #setVolume(double) + */ + public final native boolean isMuted() /*-{ + return !!this.muted; + }-*/; + + /** + * Returns {@code true} if playback is paused, {@code false} otherwise. + * + * @return the paused setting + * + * @see #pause() + * @see #play() + */ + public final native boolean isPaused() /*-{ + return !!this.paused; + }-*/; + + /** + * Returns {@code true} if the playback position is in the process of changing + * discontinuously, e.g., by use of the interactive controls, {@code false} + * otherwise. + * + * @return the seeking status + * + * @see #setControls(boolean) + * @see #hasControls() + */ + public final native boolean isSeeking() /*-{ + return !!this.seeking; + }-*/; + + /** + * Causes the resource to be loaded. + */ + public final native void load() /*-{ + this.load(); + }-*/; + + /** + * Causes playback of the resource to be paused. + */ + public final native void pause() /*-{ + this.pause(); + }-*/; + + /** + * Causes playback of the resource to be started or resumed. + */ + public final native void play() /*-{ + this.play(); + }-*/; + + /** + * Enables or disables autoplay of the resource. + * + * @param autoplay if {@code true}, enable autoplay + * + * @see #isAutoplay() + */ + public final void setAutoplay(boolean autoplay) { + setBooleanAttr("autoplay", autoplay); + } + + /** + * Enables or disables interactive controls. + * + * @param controls if {@code true}, enable controls + * + * @see #hasControls() + */ + public final void setControls(boolean controls) { + setBooleanAttr("controls", controls); + } + + /** + * Sets the current playback time within the media stream, in seconds. + * + * @param time a number within the ranges given by {@link #getSeekable()} + * + * @see #getCurrentTime() + */ + public final native void setCurrentTime(double time) /*-{ + this.currentTime = time; + }-*/; + + /** + * Sets the default playback rate. + * + * @param rate a double value + * + * @see #getDefaultPlaybackRate() + */ + public final native void setDefaultPlaybackRate(double rate) /*-{ + this.defaultPlaybackRate = rate; + }-*/; + + /** + * Enables or disables looping. + * + * @param loop if {@code true}, enable looping + * + * @see #isLoop() + */ + public final void setLoop(boolean loop) { + setBooleanAttr("loop", loop); + } + + /** + * Enables or disables muting. + * + * @param muted if {@code true}, enable muting + * + * @see #isMuted() + */ + public final native void setMuted(boolean muted) /*-{ + this.muted = muted; + }-*/; + + /** + * Sets the playback rate. + * + * @param rate a double value + * + * @see #getPlaybackRate() + */ + public final native void setPlaybackRate(double rate) /*-{ + this.playbackRate = rate; + }-*/; + + /** + * Changes the preload setting to one of {@link #PRELOAD_AUTO}, + * {@link #PRELOAD_METADATA}, or {@link #PRELOAD_NONE}. + * + * @param preload a String constants + * + * @see #getPreload() + * @see #setPreload(String) + * @see #PRELOAD_AUTO + * @see #PRELOAD_METADATA + * @see #PRELOAD_NONE + */ + public final native void setPreload(String preload) /*-{ + this.preload = preload; + }-*/; + + /** + * Sets the source URL for the media. + * + * @param url a String URL + * + * @see #getSrc() + */ + public final native void setSrc(String url) /*-{ + this.src = url; + }-*/; + + /** + * Sets the playback volume. + * + * @param volume a value between 0.0 (silent) and 1.0 (loudest) + * + * @see #getVolume() + */ + public final native void setVolume(double volume) /*-{ + this.volume = volume + }-*/; + + private native double getDoubleAttr(String name, double def) /*-{ + var value = this.getAttribute(name); + if (value == null || typeof(value) == 'undefined') { + return def; + } + return value; + }-*/; + + private void setBooleanAttr(String name, boolean value) { + if (value) { + setAttribute(name, ""); + } else { + removeAttribute(name); + } + } +}
diff --git a/user/src/com/google/gwt/dom/client/VideoElement.java b/user/src/com/google/gwt/dom/client/VideoElement.java new file mode 100644 index 0000000..3fecf60 --- /dev/null +++ b/user/src/com/google/gwt/dom/client/VideoElement.java
@@ -0,0 +1,120 @@ +/* + * Copyright 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.google.gwt.dom.client; + +/** + * Video element. + * + * <p> + * <span style="color:red">Experimental API: This API is still under development + * and is subject to change. + * </span> + * </p> + * + * @see <a href="http://www.w3.org/TR/html5/video.html#video">W3C HTML 5 Specification</a> + */ +@TagName(VideoElement.TAG) +public class VideoElement extends MediaElement { + + /** + * The tag for this element. + */ + public static final String TAG = "video"; + + protected VideoElement() { + } + + /** + * Gets the height of the element. + * + * @return the height, in pixels + * @see #setHeight(int) + */ + public final native int getHeight() /*-{ + return this.height; + }-*/; + + /** + * Returns a poster URL. + * + * @return a URL containing a poster image + * + * @see #setPoster(String) + */ + public final native String getPoster() /*-{ + return this.poster; + }-*/; + + /** + * Gets the intrinsic height of video within the element. + * + * @return the height, in pixels + * @see #setHeight(int) + */ + public final native int getVideoHeight() /*-{ + return this.videoHeight; + }-*/; + + /** + * Gets the instrinsic width of the video within the element. + * + * @return the width, in pixels + * @see #setWidth(int) + */ + public final native int getVideoWidth() /*-{ + return this.videoWidth; + }-*/; + + /** + * Gets the width of the element. + * + * @return the width, in pixels + * @see #setWidth(int) + */ + public final native int getWidth() /*-{ + return this.width; + }-*/; + + /** + * Sets the height of the element. + * + * @param height the height, in pixels + * @see #getHeight() + */ + public final native void setHeight(int height) /*-{ + this.height = height; + }-*/; + + /** + * Sets the poster URL. + * + * @param url the poster image URL + * @see #getPoster + */ + public final native void setPoster(String url) /*-{ + this.poster = url; + }-*/; + + /** + * Sets the width of the element. + * + * @param width the width, in pixels + * @see #getWidth() + */ + public final native void setWidth(int width) /*-{ + this.width = width; + }-*/; +}
diff --git a/user/src/com/google/gwt/media/Media.gwt.xml b/user/src/com/google/gwt/media/Media.gwt.xml new file mode 100644 index 0000000..4588b70 --- /dev/null +++ b/user/src/com/google/gwt/media/Media.gwt.xml
@@ -0,0 +1,20 @@ +<!-- + 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. +--> +<module> + <inherits name="com.google.gwt.user.User"/> + <inherits name="com.google.gwt.media.dom.DOM"/> + <source path="client"/> +</module>
diff --git a/user/src/com/google/gwt/media/client/Audio.java b/user/src/com/google/gwt/media/client/Audio.java new file mode 100644 index 0000000..210c90a --- /dev/null +++ b/user/src/com/google/gwt/media/client/Audio.java
@@ -0,0 +1,166 @@ +/* + * 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.media.client; + +import com.google.gwt.core.client.GWT; +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.user.client.ui.FocusWidget; + +/** + * <p> + * A widget representing an <audio> element. + * + * <p> + * <span style="color:red">Experimental API: This API is still under development + * and is subject to change. + * </span> + * </p> + * + * This widget may not be supported on all browsers. + */ +@PartialSupport +public class Audio extends FocusWidget { + private static AudioElementSupportDetector detector; + + /** + * Return a new {@link Audio} if supported, and null otherwise. + * + * @return a new {@link Audio} if supported, and null otherwise + */ + public static Audio createIfSupported() { + if (detector == null) { + detector = GWT.create(AudioElementSupportDetector.class); + } + if (!detector.isSupportedCompileTime()) { + return null; + } + AudioElement element = Document.get().createAudioElement(); + if (!detector.isSupportedRunTime(element)) { + return null; + } + return new Audio(element); + } + + /** + * Runtime check for whether the audio element is supported in this browser. + * + * @return whether the audio element is supported + */ + public static boolean isSupported() { + if (detector == null) { + detector = GWT.create(AudioElementSupportDetector.class); + } + if (!detector.isSupportedCompileTime()) { + return false; + } + AudioElement element = Document.get().createAudioElement(); + if (!detector.isSupportedRunTime(element)) { + return false; + } + return true; + } + + /** + * Protected constructor. Use {@link #createIfSupported()} to create an Audio. + */ + private Audio(AudioElement element) { + setElement(element); + } + + /** + * Creates an Audio widget with a given source URL. + * + * @param src a String URL + */ + public Audio(String src) { + setElement(Document.get().createAudioElement()); + getAudioElement().setSrc(src); + } + + /** + * Returns the attached AudioElement. + * + * @return the AudioElement + */ + public AudioElement getAudioElement() { + return this.getElement().cast(); + } + + /** + * Detector for browser support of {@link AudioElement}. + */ + private static class AudioElementSupportDetector { + /** + * Using a run-time check, return true if the {@link AudioElement} is + * supported. + * + * @return true if supported, false otherwise. + */ + static native boolean isSupportedRunTime(AudioElement element) /*-{ + return !!element.getContext; + }-*/; + + /** + * Using a compile-time check, return true if {@link AudioElement} might + * be supported. + * + * @return true if might be supported, false otherwise. + */ + boolean isSupportedCompileTime() { + // will be true in AudioElementSupportDetectedMaybe + // will be false in AudioElementSupportDetectedNo + return false; + } + } + + /** + * Detector for permutations that might support {@link AudioElement}. + */ + @SuppressWarnings("unused") + private static class AudioElementSupportDetectedMaybe + extends AudioElementSupportDetector { + /** + * Using a compile-time check, return true if {@link AudioElement} might be + * supported. + * + * @return true if might be supported, false otherwise. + */ + @Override + boolean isSupportedCompileTime() { + return true; + } + } + + /** + * Detector for permutations that do not support {@link AudioElement}. + */ + @SuppressWarnings("unused") + private static class AudioElementSupportDetectedNo + extends AudioElementSupportDetector { + /** + * Using a compile-time check, return true if {@link AudioElement} might be + * supported. + * + * @return true if might be supported, false otherwise. + */ + @Override + boolean isSupportedCompileTime() { + return false; + } + } +}
diff --git a/user/src/com/google/gwt/media/client/Video.java b/user/src/com/google/gwt/media/client/Video.java new file mode 100644 index 0000000..bd1eac7 --- /dev/null +++ b/user/src/com/google/gwt/media/client/Video.java
@@ -0,0 +1,171 @@ +/* + * 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.media.client; + +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.user.client.ui.FocusWidget; + +/** + * <p> + * A widget representing a <video> element. + * + * <p> + * <span style="color:red">Experimental API: This API is still under development + * and is subject to change. + * </span> + * </p> + * + * This widget may not be supported on all browsers. + */ +public class Video extends FocusWidget { + private static VideoElementSupportDetector detector; + + /** + * Return a new {@link Video} if supported, and null otherwise. + * + * @return a new {@link Video} if supported, and null otherwise + */ + public static Video createIfSupported() { + if (detector == null) { + detector = GWT.create(VideoElementSupportDetector.class); + } + if (!detector.isSupportedCompileTime()) { + return null; + } + VideoElement element = Document.get().createVideoElement(); + if (!detector.isSupportedRunTime(element)) { + return null; + } + return new Video(element); + } + + /** + * Runtime check for whether the video element is supported in this browser. + * + * @return whether the video element is supported + */ + public static boolean isSupported() { + if (detector == null) { + detector = GWT.create(VideoElementSupportDetector.class); + } + if (!detector.isSupportedCompileTime()) { + return false; + } + VideoElement element = Document.get().createVideoElement(); + if (!detector.isSupportedRunTime(element)) { + return false; + } + return true; + } + + /** + * Protected constructor. Use {@link #createIfSupported()} to create a Video. + */ + private Video(VideoElement element) { + setElement(element); + } + + /** + * Creates a Video widget. + */ + public Video() { + setElement(Document.get().createVideoElement()); + } + + /** + * Creates a Video widget with a given source URL. + * + * @param src a String URL + */ + public Video(String src) { + setElement(Document.get().createVideoElement()); + getVideoElement().setSrc(src); + } + + /** + * Returns the attached VideoElement. + * + * @return the VideoElement + */ + public VideoElement getVideoElement() { + return this.getElement().cast(); + } + + /** + * Detector for browser support of {@link VideoElement}. + */ + private static class VideoElementSupportDetector { + /** + * Using a run-time check, return true if the {@link VideoElement} is + * supported. + * + * @return true if supported, false otherwise. + */ + static native boolean isSupportedRunTime(VideoElement element) /*-{ + return !!element.getContext; + }-*/; + + /** + * Using a compile-time check, return true if {@link VideoElement} might + * be supported. + * + * @return true if might be supported, false otherwise. + */ + boolean isSupportedCompileTime() { + // will be true in VideoElementSupportDetectedMaybe + // will be false in VideoElementSupportDetectedNo + return false; + } + } + + /** + * Detector for permutations that might support {@link VideoElement}. + */ + @SuppressWarnings("unused") + private static class VideoElementSupportDetectedMaybe + extends VideoElementSupportDetector { + /** + * Using a compile-time check, return true if {@link VideoElement} might be + * supported. + * + * @return true if might be supported, false otherwise. + */ + @Override + boolean isSupportedCompileTime() { + return true; + } + } + + /** + * Detector for permutations that do not support {@link VideoElement}. + */ + @SuppressWarnings("unused") + private static class VideoElementSupportDetectedNo + extends VideoElementSupportDetector { + /** + * Using a compile-time check, return true if {@link VideoElement} might be + * supported. + * + * @return true if might be supported, false otherwise. + */ + @Override + boolean isSupportedCompileTime() { + return false; + } + } +}
diff --git a/user/src/com/google/gwt/media/client/package-info.java b/user/src/com/google/gwt/media/client/package-info.java new file mode 100644 index 0000000..ae09cd7 --- /dev/null +++ b/user/src/com/google/gwt/media/client/package-info.java
@@ -0,0 +1,22 @@ +/* + * Copyright 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +/** + * <p> + * Widgets for HTML Audio and Video support. + */ +@com.google.gwt.util.PreventSpuriousRebuilds +package com.google.gwt.media.client;
diff --git a/user/src/com/google/gwt/media/dom/DOM.gwt.xml b/user/src/com/google/gwt/media/dom/DOM.gwt.xml new file mode 100644 index 0000000..961e68d --- /dev/null +++ b/user/src/com/google/gwt/media/dom/DOM.gwt.xml
@@ -0,0 +1,18 @@ +<!-- + 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. +--> +<module> + <source path="client"/> +</module>
diff --git a/user/src/com/google/gwt/media/dom/client/MediaError.java b/user/src/com/google/gwt/media/dom/client/MediaError.java new file mode 100644 index 0000000..1d0aea6 --- /dev/null +++ b/user/src/com/google/gwt/media/dom/client/MediaError.java
@@ -0,0 +1,72 @@ +/* + * 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.media.dom.client; + +import com.google.gwt.core.client.JavaScriptObject; + +/** + * <p> + * A {@link JavaScriptObject} indicating the type of error encountered by a + * {@link com.google.gwt.dom.client.MediaElement MediaElement}. + * + * <p> + * <span style="color:red">Experimental API: This API is still under development + * and is subject to change. + * </span> + * </p> + * + * @see com.google.gwt.dom.client.MediaElement#getError() + */ +public final class MediaError extends JavaScriptObject { + + /** + * A constant returned by {@link #getCode} indicating that playback + * was aborted at the user's request. + */ + public static final int MEDIA_ERR_ABORTED = 1; + + /** + * A constant returned by {@link #getCode} indicating that playback + * was aborted due to a network error. + */ + public static final int MEDIA_ERR_NETWORK = 2; + + /** + * A constant returned by {@link #getCode} indicating that playback + * was aborted due to an error in decoding. + */ + public static final int MEDIA_ERR_DECODE = 3; + + /** + * A constant returned by {@link #getCode} indicating that the format + * of the source stream was unsuitable for playback. + */ + public static final int MEDIA_ERR_SRC_NOT_SUPPORTED = 4; + + protected MediaError() { + } + + /** + * Returns an error code indicating the reason for the error. + * + * @return one of {@link MediaError#MEDIA_ERR_ABORTED}, + * {@link MediaError#MEDIA_ERR_NETWORK}, {@link MediaError#MEDIA_ERR_DECODE}, + * or {@link MediaError#MEDIA_ERR_SRC_NOT_SUPPORTED} + */ + public native int getCode() /*-{ + return this.code; + }-*/; +}
diff --git a/user/src/com/google/gwt/media/dom/client/TimeRanges.java b/user/src/com/google/gwt/media/dom/client/TimeRanges.java new file mode 100644 index 0000000..a11bf89 --- /dev/null +++ b/user/src/com/google/gwt/media/dom/client/TimeRanges.java
@@ -0,0 +1,74 @@ +/* + * 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.media.dom.client; + +import com.google.gwt.core.client.JavaScriptObject; + +/** + * <p> + * A {@link JavaScriptObject} representing a time range returned from a + * {@link com.google.gwt.dom.client.MediaElement MediaElement}. + * + * <p> + * <span style="color:red">Experimental API: This API is still under development + * and is subject to change. + * </span> + * </p> + * + * @see com.google.gwt.dom.client.MediaElement#getBuffered() + * @see com.google.gwt.dom.client.MediaElement#getPlayed() + * @see com.google.gwt.dom.client.MediaElement#getSeekable() + */ +public final class TimeRanges extends JavaScriptObject { + + protected TimeRanges() { + } + + /** + * Returns the end time of the range indexed by {@code index}. + * + * @param index the range index, between 0 (inclusive) and {@link #length()} + * (exclusive) + * @return a double indicating the end time in seconds + * + * @see #start(int) + */ + public native double end(int index) /*-{ + return this.end(index); + }-*/; + + /** + * Returns the number of distinct ranges contained in this object. + * + * @return an integer number of ranges + */ + public native int length() /*-{ + return this.length; + }-*/; + + /** + * Returns the start time of the range indexed by {@code index}. + * + * @param index the range index, between 0 (inclusive) and {@link #length()} + * (exclusive) + * @return a double indicating the start time in seconds + * + * @see #end(int) + */ + public native double start(int index) /*-{ + return this.start(index); + }-*/; +}
diff --git a/user/src/com/google/gwt/media/dom/client/package-info.java b/user/src/com/google/gwt/media/dom/client/package-info.java new file mode 100644 index 0000000..88e740d --- /dev/null +++ b/user/src/com/google/gwt/media/dom/client/package-info.java
@@ -0,0 +1,22 @@ +/* + * Copyright 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +/** + * <p> + * DOM classes for HTML Audio and Video support. + */ +@com.google.gwt.util.PreventSpuriousRebuilds +package com.google.gwt.media.dom.client;
diff --git a/user/src/com/google/gwt/user/User.gwt.xml b/user/src/com/google/gwt/user/User.gwt.xml index 905c37e..f6277dc 100644 --- a/user/src/com/google/gwt/user/User.gwt.xml +++ b/user/src/com/google/gwt/user/User.gwt.xml
@@ -26,6 +26,7 @@ <inherits name="com.google.gwt.editor.Editor" /> <inherits name="com.google.gwt.resources.Resources"/> <inherits name="com.google.gwt.layout.Layout"/> + <inherits name="com.google.gwt.media.Media"/> <inherits name="com.google.gwt.uibinder.UiBinder"/> <inherits name="com.google.gwt.user.AsyncProxy"/> <inherits name="com.google.gwt.user.RemoteService"/>
diff --git a/user/test/com/google/gwt/media/MediaSuite.java b/user/test/com/google/gwt/media/MediaSuite.java new file mode 100644 index 0000000..3174327 --- /dev/null +++ b/user/test/com/google/gwt/media/MediaSuite.java
@@ -0,0 +1,41 @@ +/* + * 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.media; + +import com.google.gwt.junit.tools.GWTTestSuite; + +import junit.framework.Test; + +/** + * Tests of Media. + */ +public class MediaSuite { + public static Test suite() { + GWTTestSuite suite = new GWTTestSuite("Test suite for Media GWTTestCases"); + + /* + * Tests disabled temporarily + * suite.addTestSuite(AudioTest.class); + * suite.addTestSuite(VideoTest.class); + */ + + return suite; + } + + private MediaSuite() { + } +}
diff --git a/user/test/com/google/gwt/media/MediaTest.gwt.xml b/user/test/com/google/gwt/media/MediaTest.gwt.xml new file mode 100644 index 0000000..11f6d65 --- /dev/null +++ b/user/test/com/google/gwt/media/MediaTest.gwt.xml
@@ -0,0 +1,26 @@ +<!-- --> +<!-- 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 --> +<!-- 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. License for the specific language governing permissions and --> +<!-- limitations under the License. --> + +<module> + <!-- Inherit the JUnit support --> + <inherits name='com.google.gwt.junit.JUnit'/> + + <!-- Make media files available on the server by abusing the 'script' tag --> + <source path="client"/> + <script src="jabberwocky.ogg"></script> + <script src="jabberwocky.mp3"></script> + <script src="testogg.ogv"></script> + <script src="testh264.mp4"></script> + <script src="poster.jpg"></script> +</module>
diff --git a/user/test/com/google/gwt/media/client/AudioTest.java b/user/test/com/google/gwt/media/client/AudioTest.java new file mode 100644 index 0000000..e491cd4 --- /dev/null +++ b/user/test/com/google/gwt/media/client/AudioTest.java
@@ -0,0 +1,119 @@ +/* + * 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.media.client; + +import com.google.gwt.dom.client.AudioElement; +import com.google.gwt.dom.client.MediaElement; +import com.google.gwt.junit.DoNotRunWith; +import com.google.gwt.junit.Platform; +import com.google.gwt.user.client.ui.RootPanel; + +/** + * Tests {@link AudioElement}. + * + * Because HtmlUnit does not support HTML5, you will need to run these tests + * manually in order to have them run. To do that, go to "run configurations" or + * "debug configurations", select the test you would like to run, and put this + * line in the VM args under the arguments tab: -Dgwt.args="-runStyle Manual:1" + */ +@DoNotRunWith(Platform.HtmlUnitUnknown) +public class AudioTest extends MediaTest { + Audio audio; + + final static String audioUrlMp3 = "jabberwocky.mp3"; + final static String audioFormatMp3 = "audio/mpeg"; + final static String audioUrlOgg = "jabberwocky.ogg"; + final static String audioFormatOgg = "audio/ogg"; + + @Override + public MediaElement getElement() { + if (audio == null) { + return null; + } + return audio.getAudioElement(); + } + + @Override + public String getElementState() { + StringBuilder sb = new StringBuilder(); + AudioElement e = audio.getAudioElement(); + sb.append("AudioElement["); + sb.append("currentSrc="); + sb.append(e.getCurrentSrc()); + sb.append(",currentTime="); + sb.append(e.getCurrentTime()); + sb.append(",defaultPlaybackRate="); + sb.append(e.getDefaultPlaybackRate()); + sb.append(",duration="); + sb.append(e.getDuration()); + sb.append(",initialTime="); + sb.append(e.getInitialTime()); + sb.append(",networkState="); + sb.append(e.getNetworkState()); + sb.append(",playbackRate="); + sb.append(e.getPlaybackRate()); + sb.append(",preload="); + sb.append(e.getPreload()); + sb.append(",readyState="); + sb.append(e.getReadyState()); + sb.append(",src="); + sb.append(e.getSrc()); + sb.append(",startOffsetTime="); + sb.append(e.getStartOffsetTime()); + sb.append(",seekable="); + sb.append(e.getSeekable()); + sb.append(",volume="); + sb.append(e.getVolume()); + sb.append("]"); + return sb.toString(); + } + + @Override + public String getModuleName() { + return "com.google.gwt.media.MediaTest"; + } + + @Override + protected void gwtSetUp() throws Exception { + audio = Audio.createIfSupported(); + + if (audio == null) { + return; // don't continue if not supported + } + + AudioElement element = audio.getAudioElement(); + String canPlayMp3 = element.canPlayType(audioFormatMp3); + String canPlayOgg = element.canPlayType(audioFormatOgg); + if (!canPlayMp3.equalsIgnoreCase(MediaElement.CANNOT_PLAY)) { + element.setSrc(audioUrlMp3); + } else if (!canPlayOgg.equalsIgnoreCase(MediaElement.CANNOT_PLAY)) { + element.setSrc(audioUrlOgg); + } else { + throw new Exception("Could not find suitable audio format"); + } + RootPanel.get().add(audio); + } + + @Override + protected void gwtTearDown() throws Exception { + if (audio == null) { + return; // don't continue if not supported + } + + audio.getAudioElement().pause(); + RootPanel.get().remove(audio); + } +}
diff --git a/user/test/com/google/gwt/media/client/MediaTest.java b/user/test/com/google/gwt/media/client/MediaTest.java new file mode 100644 index 0000000..9eda220 --- /dev/null +++ b/user/test/com/google/gwt/media/client/MediaTest.java
@@ -0,0 +1,253 @@ +/* + * 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.media.client; + +import com.google.gwt.dom.client.MediaElement; +import com.google.gwt.junit.DoNotRunWith; +import com.google.gwt.junit.Platform; +import com.google.gwt.junit.client.GWTTestCase; +import com.google.gwt.user.client.Timer; + +import junit.framework.Assert; + +/** + * Base test for {@link MediaElement}. + * + * Do not call this class directly. To use, extend this class and override the + * getElement and isSupported methods. + * + * Because HtmlUnit does not support HTML5, you will need to run these tests + * manually in order to have them run. To do that, go to "run configurations" or + * "debug configurations", select the test you would like to run, and put this + * line in the VM args under the arguments tab: -Dgwt.args="-runStyle Manual:1" + */ +@DoNotRunWith(Platform.HtmlUnitUnknown) +public abstract class MediaTest extends GWTTestCase { + + native boolean isOldFirefox() /*-{ + return @com.google.gwt.dom.client.DOMImplMozilla::isGecko191OrBefore()(); + }-*/; + + native boolean isFirefox40OrEarlier() /*-{ + return @com.google.gwt.dom.client.DOMImplMozilla::isGecko2OrBefore()(); + }-*/; + + public void disabled_testCurrentTime() { + MediaElement element = getElement(); + if (element == null) { + return; // don't continue if not supported + } + + Assert.assertTrue("currentTime must be positive.", + element.getCurrentTime() >= 0.0); + + double seekTime = 2.0; // seconds + element.setCurrentTime(seekTime); + Assert.assertEquals("currentTime must be able to be set.", seekTime, + element.getCurrentTime()); + } + + /** + * Return the MediaElement associated with the test. + * + * @return the MediaElement associated with the test. + */ + public MediaElement getElement() { + return null; + } + + public abstract String getElementState(); + + @Override + public String getModuleName() { + return "com.google.gwt.media.MediaTest"; + } + + public void testAutoPlay() { + MediaElement element = getElement(); + if (element == null) { + return; // don't continue if not supported + } + + element.setAutoplay(false); + assertFalse("Autoplay should be off.", element.isAutoplay()); + element.setAutoplay(true); + assertTrue("Autoplay should be on.", element.isAutoplay()); + } + + public void testControls() { + MediaElement element = getElement(); + if (element == null) { + return; // don't continue if not supported + } + + element.setControls(false); + assertFalse("Controls should be off.", element.hasControls()); + element.setControls(true); + assertTrue("Controls should be on.", element.hasControls()); + } + + public void testCurrentSrc() { + MediaElement element = getElement(); + if (element == null) { + return; // don't continue if not supported + } + + element.load(); + Assert.assertNotNull("currentSrc should be set in these tests.", + element.getCurrentSrc()); + } + + public void testLoop() { + MediaElement element = getElement(); + if (element == null) { + return; // don't continue if not supported + } + + element.setLoop(false); + assertFalse("Loop should be off.", element.isLoop()); + element.setLoop(true); + assertTrue("Loop should be on.", element.isLoop()); + } + + public void testMuted() { + MediaElement element = getElement(); + if (element == null) { + return; // don't continue if not supported + } + + element.setMuted(true); + assertTrue("Muted should be true.", element.isMuted()); + element.setMuted(false); + assertFalse("Muted should be false.", element.isMuted()); + } + + public void testNetworkState() { + MediaElement element = getElement(); + if (element == null) { + return; // don't continue if not supported + } + int state = element.getNetworkState(); + assertTrue("Illegal network state", state == MediaElement.NETWORK_EMPTY + || state == MediaElement.NETWORK_IDLE + || state == MediaElement.NETWORK_LOADING + || state == MediaElement.NETWORK_NO_SOURCE); + } + + public void testPlay() { + MediaElement element = getElement(); + if (element == null) { + return; // don't continue if not supported + } + + int waitMillis = 10000; + delayTestFinish(3 * waitMillis); + + element.setPlaybackRate(1.0); + element.play(); + + // wait a little, then make sure it played + new Timer() { + @Override + public void run() { + finishTest(); + } + }.schedule(waitMillis); + } + + public void testPlaybackRate() { + final MediaElement element = getElement(); + if (element == null) { + return; // don't continue if not supported + } + + int waitMillis = 5000; + delayTestFinish(3 * waitMillis); + + assertEquals("Default playback rate should be 1.0", 1.0, + element.getDefaultPlaybackRate()); + + element.play(); + + // wait a little, then make sure it played + new Timer() { + @Override + public void run() { + // set rate to 2.0 + double rate = 2.0; + element.setPlaybackRate(rate); + assertEquals("Should be able to change playback rate", rate, + element.getPlaybackRate()); + + // return to 1.0 + rate = 1.0; + element.setPlaybackRate(rate); + assertEquals("Should be able to change playback rate", rate, + element.getPlaybackRate()); + + finishTest(); + } + }.schedule(waitMillis); + } + + public void disabled_testPreload() { + MediaElement element = getElement(); + if (element == null) { + return; // don't continue if not supported + } + if (isFirefox40OrEarlier()) { + return; // don't continue on older versions of Firefox. + } + + String state = element.getPreload(); + assertNotNull(state); + assertTrue("Illegal preload state", state.equals(MediaElement.PRELOAD_AUTO) + || state.equals(MediaElement.PRELOAD_METADATA) + || state.equals(MediaElement.PRELOAD_NONE)); + + element.setPreload(MediaElement.PRELOAD_METADATA); + assertEquals("Preload should be able to be set.", + MediaElement.PRELOAD_METADATA, element.getPreload()); + } + + public void testReadyState() { + MediaElement element = getElement(); + if (element == null) { + return; // don't continue if not supported + } + + int state = element.getReadyState(); + assertTrue("Illegal ready state", state == MediaElement.HAVE_CURRENT_DATA + || state == MediaElement.HAVE_ENOUGH_DATA + || state == MediaElement.HAVE_FUTURE_DATA + || state == MediaElement.HAVE_METADATA + || state == MediaElement.HAVE_NOTHING); + } + + public void testVolume() { + MediaElement element = getElement(); + if (element == null) { + return; // don't continue if not supported + } + + element.setVolume(0.5); + assertEquals("Volume should be at one-half loudness.", 0.5, + element.getVolume()); + element.setVolume(0.75); + assertEquals("Volume should be at three-quarters loudness.", 0.75, + element.getVolume()); + } +}
diff --git a/user/test/com/google/gwt/media/client/VideoTest.java b/user/test/com/google/gwt/media/client/VideoTest.java new file mode 100644 index 0000000..069ef0f --- /dev/null +++ b/user/test/com/google/gwt/media/client/VideoTest.java
@@ -0,0 +1,184 @@ +/* + * 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.media.client; + +import com.google.gwt.dom.client.MediaElement; +import com.google.gwt.dom.client.VideoElement; +import com.google.gwt.junit.DoNotRunWith; +import com.google.gwt.junit.Platform; +import com.google.gwt.user.client.Timer; +import com.google.gwt.user.client.ui.RootPanel; + +/** + * Tests {@link VideoElement}. + * + * Because HtmlUnit does not support HTML5, you will need to run these tests + * manually in order to have them run. To do that, go to "run configurations" or + * "debug configurations", select the test you would like to run, and put this + * line in the VM args under the arguments tab: -Dgwt.args="-runStyle Manual:1" + */ +@DoNotRunWith(Platform.HtmlUnitUnknown) +public class VideoTest extends MediaTest { + Video video; + + final static String posterUrl = "poster.jpg"; + final static String videoUrlH264 = "testh264.mp4"; + final static String videoFormatH264 = "video/mp4; codecs=\"avc1.42E01E, mp4a.40.2\""; + final static String videoUrlOgg = "testogg.ogv"; + final static String videoFormatOgg = "video/ogg; codecs=\"theora, vorbis\""; + + final static int videoWidth = 480; + final static int videoHeight = 270; + + @Override + public MediaElement getElement() { + if (video == null) { + return null; + } + return video.getVideoElement(); + } + + @Override + public String getElementState() { + StringBuilder sb = new StringBuilder(); + VideoElement e = video.getVideoElement(); + + sb.append("AudioElement["); + sb.append("currentSrc="); + sb.append(e.getCurrentSrc()); + sb.append(",currentTime="); + sb.append(e.getCurrentTime()); + sb.append(",defaultPlaybackRate="); + sb.append(e.getDefaultPlaybackRate()); + sb.append(",duration="); + sb.append(e.getDuration()); + sb.append(",height="); + sb.append(e.getHeight()); + sb.append(",initialTime="); + sb.append(e.getInitialTime()); + sb.append(",networkState="); + sb.append(e.getNetworkState()); + sb.append(",playbackRate="); + sb.append(e.getPlaybackRate()); + sb.append(",poster="); + sb.append(e.getPoster()); + sb.append(",preload="); + sb.append(e.getPreload()); + sb.append(",readyState="); + sb.append(e.getReadyState()); + sb.append(",src="); + sb.append(e.getSrc()); + sb.append(",startOffsetTime="); + sb.append(e.getStartOffsetTime()); + sb.append(",seekable="); + sb.append(e.getSeekable()); + sb.append(",videoHeight="); + sb.append(e.getVideoHeight()); + sb.append(",videoWidth="); + sb.append(e.getVideoWidth()); + sb.append(",volume="); + sb.append(e.getVolume()); + sb.append(",width="); + sb.append(e.getWidth()); + sb.append("]"); + return sb.toString(); + } + + @Override + public String getModuleName() { + return "com.google.gwt.media.MediaTest"; + } + + public void testPoster() { + if (video == null) { + return; // don't continue if not supported + } + + VideoElement element = video.getVideoElement(); + element.setPoster(posterUrl); + String poster = element.getPoster(); + assertEquals(posterUrl, poster.substring(poster.lastIndexOf('/') + 1)); + } + + public void testSize() { + if (video == null) { + return; // don't continue if not supported + } + + int width = 100; + int height = 200; + VideoElement element = video.getVideoElement(); + element.setWidth(width); + element.setHeight(height); + assertEquals(width, element.getWidth()); + assertEquals(height, element.getHeight()); + } + + public void testVideoSize() { + if (video == null) { + return; // don't continue if not supported + } + + int waitMillis = 5000; + delayTestFinish(3 * waitMillis); + + final VideoElement element = video.getVideoElement(); + element.play(); + + // wait a little, then make sure it played + new Timer() { + @Override + public void run() { + assertEquals("Element = " + getElementState() + ", expected width " + + videoWidth, videoWidth, element.getVideoWidth()); + assertEquals("Element = " + getElementState() + ", expected height " + + videoHeight, videoHeight, element.getVideoHeight()); + finishTest(); + } + }.schedule(waitMillis); + } + + @Override + protected void gwtSetUp() throws Exception { + video = Video.createIfSupported(); + + if (video == null) { + return; // don't continue if not supported + } + + VideoElement element = video.getVideoElement(); + if (!element.canPlayType(videoFormatH264).equalsIgnoreCase( + MediaElement.CANNOT_PLAY)) { + element.setSrc(videoUrlH264); + } else if (!element.canPlayType(videoFormatOgg).equalsIgnoreCase( + MediaElement.CANNOT_PLAY)) { + element.setSrc(videoUrlOgg); + } else { + throw new Exception("Could not find suitable video format"); + } + RootPanel.get().add(video); + } + + @Override + protected void gwtTearDown() throws Exception { + if (video == null) { + return; // don't continue if not supported + } + + video.getVideoElement().pause(); + RootPanel.get().remove(video); + } +}
diff --git a/user/test/com/google/gwt/media/public/jabberwocky.mp3 b/user/test/com/google/gwt/media/public/jabberwocky.mp3 new file mode 100644 index 0000000..ead8687 --- /dev/null +++ b/user/test/com/google/gwt/media/public/jabberwocky.mp3 Binary files differ
diff --git a/user/test/com/google/gwt/media/public/jabberwocky.ogg b/user/test/com/google/gwt/media/public/jabberwocky.ogg new file mode 100644 index 0000000..d9a6b0f --- /dev/null +++ b/user/test/com/google/gwt/media/public/jabberwocky.ogg Binary files differ
diff --git a/user/test/com/google/gwt/media/public/poster.jpg b/user/test/com/google/gwt/media/public/poster.jpg new file mode 100644 index 0000000..980ed41 --- /dev/null +++ b/user/test/com/google/gwt/media/public/poster.jpg Binary files differ
diff --git a/user/test/com/google/gwt/media/public/testh264.mp4 b/user/test/com/google/gwt/media/public/testh264.mp4 new file mode 100644 index 0000000..df28ab1 --- /dev/null +++ b/user/test/com/google/gwt/media/public/testh264.mp4 Binary files differ
diff --git a/user/test/com/google/gwt/media/public/testogg.ogv b/user/test/com/google/gwt/media/public/testogg.ogv new file mode 100644 index 0000000..f428074 --- /dev/null +++ b/user/test/com/google/gwt/media/public/testogg.ogv Binary files differ