Bug 447639. Update <video> and <audio> elements for spec changes, and add tests. r+sr=roc
authorChris Double <chris.double@double.co.nz>
Tue, 29 Jul 2008 21:55:27 -0700
changeset 16290 20427bfb85bc38f7f0aec97dfab6485a7f3ab14e
parent 16289 3f3ff0208421c029011ddc796136ca88208cb0da
child 16291 a50ca180ed3e4ec916fa16b72b991ae7305dc790
push idunknown
push userunknown
push dateunknown
bugs447639
milestone1.9.1a2pre
Bug 447639. Update <video> and <audio> elements for spec changes, and add tests. r+sr=roc
content/base/src/nsGkAtomList.h
content/html/content/public/nsHTMLMediaElement.h
content/html/content/src/nsHTMLMediaElement.cpp
content/html/content/src/nsHTMLSourceElement.cpp
content/media/video/Makefile.in
content/media/video/public/nsVideoDecoder.h
content/media/video/src/nsVideoDecoder.cpp
content/media/video/test/Makefile.in
content/media/video/test/test_autoplay.html
content/media/video/test/test_constants.html
content/media/video/test/test_controls.html
content/media/video/test/test_currentTime.html
content/media/video/test/test_defaultPlaybackRate.html
content/media/video/test/test_networkState.html
content/media/video/test/test_paused.html
content/media/video/test/test_playbackRate.html
content/media/video/test/test_readyState.html
content/media/video/test/test_start.html
dom/public/idl/html/Makefile.in
dom/public/idl/html/nsIDOMHTMLMediaElement.idl
dom/public/idl/html/nsIDOMHTMLSourceElement.idl
layout/style/forms.css
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -239,16 +239,19 @@ GK_ATOM(controls, "controls")
 #endif
 GK_ATOM(coords, "coords")
 GK_ATOM(copy, "copy")
 GK_ATOM(copyOf, "copy-of")
 GK_ATOM(count, "count")
 GK_ATOM(crop, "crop")
 GK_ATOM(curpos, "curpos")
 GK_ATOM(current, "current")
+#ifdef MOZ_MEDIA
+GK_ATOM(currentloop, "currentloop")
+#endif
 GK_ATOM(cycler, "cycler")
 GK_ATOM(data, "data")
 GK_ATOM(dataType, "data-type")
 GK_ATOM(datasources, "datasources")
 GK_ATOM(datetime, "datetime")
 GK_ATOM(dblclick, "dblclick")
 GK_ATOM(dd, "dd")
 GK_ATOM(debug, "debug")
@@ -483,16 +486,20 @@ GK_ATOM(listener, "listener")
 GK_ATOM(listhead, "listhead")
 GK_ATOM(listheader, "listheader")
 GK_ATOM(listing, "listing")
 GK_ATOM(listitem, "listitem")
 GK_ATOM(listrows, "listrows")
 GK_ATOM(load, "load")
 GK_ATOM(localName, "local-name")
 GK_ATOM(longdesc, "longdesc")
+#ifdef MOZ_MEDIA
+GK_ATOM(loopend, "loopend")
+GK_ATOM(loopstart, "loopstart")
+#endif
 GK_ATOM(lowerFirst, "lower-first")
 GK_ATOM(lowest, "lowest")
 GK_ATOM(lowsrc, "lowsrc")
 GK_ATOM(ltr, "ltr")
 GK_ATOM(map, "map")
 GK_ATOM(manifest, "manifest")
 GK_ATOM(marginheight, "marginheight")
 GK_ATOM(marginwidth, "marginwidth")
@@ -680,19 +687,23 @@ GK_ATOM(parameter, "parameter")
 GK_ATOM(parent, "parent")
 GK_ATOM(parsetype, "parsetype")
 GK_ATOM(patternSeparator, "pattern-separator")
 GK_ATOM(perMille, "per-mille")
 GK_ATOM(percent, "percent")
 GK_ATOM(persist, "persist")
 GK_ATOM(phase, "phase")
 GK_ATOM(ping, "ping")
+#ifdef MOZ_MEDIA
+GK_ATOM(pixelratio, "pixelratio")
+#endif
 GK_ATOM(plaintext, "plaintext")
 #ifdef MOZ_MEDIA
 GK_ATOM(playbackrate, "playbackrate")
+GK_ATOM(playcount, "playcount")
 #endif
 GK_ATOM(pointSize, "point-size")
 GK_ATOM(poly, "poly")
 GK_ATOM(polygon, "polygon")
 GK_ATOM(popup, "popup")
 GK_ATOM(popupList, "Popup-list")
 GK_ATOM(popupalign, "popupalign")
 GK_ATOM(popupanchor, "popupanchor")
--- a/content/html/content/public/nsHTMLMediaElement.h
+++ b/content/html/content/public/nsHTMLMediaElement.h
@@ -94,34 +94,39 @@ public:
   // Called by the video decoder object, on the main thread,
   // when the resource has a network error during loading.
   void NetworkError();
 
   // Called by the video decoder object, on the main thread,
   // when the video playback has ended.
   void PlaybackCompleted();
 
+  // Called by the decoder object, on the main thread, when
+  // approximately enough of the resource has been loaded to play
+  // through without pausing for buffering.
+  void CanPlayThrough();
+
   // Draw the latest video data. See nsVideoDecoder for 
   // details.
   void Paint(gfxContext* aContext, const gfxRect& aRect);
 
   // Dispatch events
   nsresult DispatchSimpleEvent(const nsAString& aName);
   nsresult DispatchProgressEvent(const nsAString& aName);
   nsresult DispatchAsyncSimpleEvent(const nsAString& aName);
   nsresult DispatchAsyncProgressEvent(const nsAString& aName);
 
+  // Use this method to change the mReadyState member, so required
+  // events can be fired.
+  void ChangeReadyState(nsMediaReadyState aState);
+
 protected:
   nsresult PickMediaElement(nsAString& aChosenMediaResource);
   virtual nsresult InitializeDecoder(nsAString& aChosenMediaResource);
 
-  // Use this method to change the mReadyState member, so required
-  // events can be fired.
-  void ChangeReadyState(nsMediaReadyState aState);
-
   nsRefPtr<nsVideoDecoder> mDecoder;
 
   // Error attribute
   nsCOMPtr<nsIDOMHTMLMediaError> mError;
 
   // Media loading flags. See: 
   //   http://www.whatwg.org/specs/web-apps/current-work/#video)
   nsMediaNetworkState mNetworkState;
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -86,27 +86,41 @@ public:
       mElement->DispatchProgressEvent(mName) :
       mElement->DispatchSimpleEvent(mName);
   }
 };
 
 // nsIDOMHTMLMediaElement
 NS_IMPL_URI_ATTR(nsHTMLMediaElement, Src, src)
 NS_IMPL_BOOL_ATTR(nsHTMLMediaElement, Controls, controls)
+NS_IMPL_BOOL_ATTR(nsHTMLMediaElement, Autoplay, autoplay)
 NS_IMPL_FLOAT_ATTR_DEFAULT_VALUE(nsHTMLMediaElement, PlaybackRate, playbackrate, 1.0)
 NS_IMPL_FLOAT_ATTR_DEFAULT_VALUE(nsHTMLMediaElement, DefaultPlaybackRate, defaultplaybackrate, 1.0)
+NS_IMPL_FLOAT_ATTR(nsHTMLMediaElement, Start, start)
+NS_IMPL_FLOAT_ATTR(nsHTMLMediaElement, End, end)
+NS_IMPL_FLOAT_ATTR(nsHTMLMediaElement, LoopStart, loopstart)
+NS_IMPL_FLOAT_ATTR(nsHTMLMediaElement, LoopEnd, loopend)
 
 /* readonly attribute nsIDOMHTMLMediaError error; */
 NS_IMETHODIMP nsHTMLMediaElement::GetError(nsIDOMHTMLMediaError * *aError)
 {
   NS_IF_ADDREF(*aError = mError);
 
   return NS_OK;
 }
 
+/* readonly attribute boolean ended; */
+NS_IMETHODIMP nsHTMLMediaElement::GetEnded(PRBool *aEnded)
+{
+  *aEnded = mEnded;
+
+  return NS_OK;
+}
+
+
 /* readonly attribute DOMString currentSrc; */
 NS_IMETHODIMP nsHTMLMediaElement::GetCurrentSrc(nsAString & aCurrentSrc)
 {
   nsCAutoString src;
   
   if (mDecoder) {
     nsCOMPtr<nsIURI> uri;
     mDecoder->GetCurrentURI(getter_AddRefs(uri));
@@ -129,22 +143,40 @@ NS_IMETHODIMP nsHTMLMediaElement::GetNet
 }
 
 /* readonly attribute float bufferingRate; */
 NS_IMETHODIMP nsHTMLMediaElement::GetBufferingRate(float *aBufferingRate)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
+/* readonly attribute boolean bufferingThrottled; */
+NS_IMETHODIMP nsHTMLMediaElement::GetBufferingThrottled(PRBool *aBufferingRate)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
 /* readonly attribute nsIDOMTimeRanges buffered; */
 NS_IMETHODIMP nsHTMLMediaElement::GetBuffered(nsIDOMHTMLTimeRanges * *aBuffered)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
+/* readonly attribute nsIDOMByteRanges bufferedBytes; */
+NS_IMETHODIMP nsHTMLMediaElement::GetBufferedBytes(nsIDOMHTMLByteRanges * *aBufferedBytes)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+/* readonly attribute unsigned long totalBytes; */
+NS_IMETHODIMP nsHTMLMediaElement::GetTotalBytes(PRUint32 *aTotalBytes)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
 /* void load (); */
 NS_IMETHODIMP nsHTMLMediaElement::Load()
 {
   if (mBegun) {
     mBegun = PR_FALSE;
     
     mError = new (std::nothrow) nsHTMLMediaError(nsHTMLMediaError::MEDIA_ERR_ABORTED);
     DispatchProgressEvent(NS_LITERAL_STRING("abort"));
@@ -237,42 +269,16 @@ NS_IMETHODIMP nsHTMLMediaElement::GetPla
 }
 
 /* readonly attribute nsIDOMHTMLTimeRanges seekable; */
 NS_IMETHODIMP nsHTMLMediaElement::GetSeekable(nsIDOMHTMLTimeRanges * *aSeekable)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-/* readonly attribute boolean ended; */
-NS_IMETHODIMP nsHTMLMediaElement::GetEnded(PRBool *aEnded)
-{
-  *aEnded = mEnded;
-
-  return NS_OK;
-}
-
-/* attribute boolean autoplay; */
-NS_IMETHODIMP nsHTMLMediaElement::GetAutoplay(PRBool *aAutoplay)
-{
-  if (HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay)) 
-    *aAutoplay = PR_TRUE;
-  else
-    *aAutoplay = PR_FALSE;
-  
-  return NS_OK;
-}
-NS_IMETHODIMP nsHTMLMediaElement::SetAutoplay(PRBool aAutoplay)
-{
-  return SetAttr(kNameSpaceID_None, 
-                 nsGkAtoms::autoplay,
-                 NS_LITERAL_STRING("true"),
-                 PR_TRUE);
-}
-
 /* void pause (); */
 NS_IMETHODIMP nsHTMLMediaElement::Pause()
 {
   if (!mDecoder) 
     return NS_OK;
 
   nsresult rv;
 
@@ -289,84 +295,46 @@ NS_IMETHODIMP nsHTMLMediaElement::Pause(
   if (!oldPaused) {
     DispatchAsyncSimpleEvent(NS_LITERAL_STRING("timeupdate"));
     DispatchAsyncSimpleEvent(NS_LITERAL_STRING("pause"));
   }
 
   return NS_OK;
 }
 
-/* attribute float start; */
-NS_IMETHODIMP nsHTMLMediaElement::GetStart(float *aStart)
-{
-    return NS_ERROR_NOT_IMPLEMENTED;
-}
-NS_IMETHODIMP nsHTMLMediaElement::SetStart(float aStart)
+/* attribute unsigned long playCount; */
+NS_IMETHODIMP nsHTMLMediaElement::GetPlayCount(PRUint32 *aPlayCount)
 {
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-/* attribute float end; */
-NS_IMETHODIMP nsHTMLMediaElement::GetEnd(float *aEnd)
-{
-    return NS_ERROR_NOT_IMPLEMENTED;
-}
-NS_IMETHODIMP nsHTMLMediaElement::SetEnd(float aEnd)
-{
-    return NS_ERROR_NOT_IMPLEMENTED;
+  return GetIntAttr(nsGkAtoms::playcount, 1, reinterpret_cast<PRInt32*>(aPlayCount));
 }
 
-/* attribute float loopStart; */
-NS_IMETHODIMP nsHTMLMediaElement::GetLoopStart(float *aLoopStart)
-{
-    return NS_ERROR_NOT_IMPLEMENTED;
-}
-NS_IMETHODIMP nsHTMLMediaElement::SetLoopStart(float aLoopStart)
-{
-    return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-/* attribute float loopEnd; */
-NS_IMETHODIMP nsHTMLMediaElement::GetLoopEnd(float *aLoopEnd)
+NS_IMETHODIMP nsHTMLMediaElement::SetPlayCount(PRUint32 aPlayCount)
 {
-    return NS_ERROR_NOT_IMPLEMENTED;
-}
-NS_IMETHODIMP nsHTMLMediaElement::SetLoopEnd(float aLoopEnd)
-{
-    return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-/* attribute unsigned long loopCount; */
-NS_IMETHODIMP nsHTMLMediaElement::GetLoopCount(PRUint32 *aLoopCount)
-{
-    return NS_ERROR_NOT_IMPLEMENTED;
-}
-NS_IMETHODIMP nsHTMLMediaElement::SetLoopCount(PRUint32 aLoopCount)
-{
-    return NS_ERROR_NOT_IMPLEMENTED;
+  return SetIntAttr(nsGkAtoms::playcount, static_cast<PRInt32>(aPlayCount));
 }
 
 /* attribute unsigned long currentLoop; */
 NS_IMETHODIMP nsHTMLMediaElement::GetCurrentLoop(PRUint32 *aCurrentLoop)
 {
-    return NS_ERROR_NOT_IMPLEMENTED;
+  return GetIntAttr(nsGkAtoms::currentloop, 0, reinterpret_cast<PRInt32*>(aCurrentLoop));
 }
+
 NS_IMETHODIMP nsHTMLMediaElement::SetCurrentLoop(PRUint32 aCurrentLoop)
 {
+  return SetIntAttr(nsGkAtoms::currentloop, static_cast<PRInt32>(aCurrentLoop));
+}
+
+/* void addCueRange (in DOMString className, in float start, in float end, in boolean pauseOnExit, in nsIDOMHTMLVoidCallback enterCallback, in nsIDOMHTMLVoidCallback exitCallback); */
+NS_IMETHODIMP nsHTMLMediaElement::AddCueRange(const nsAString & className, float start, float end, PRBool pauseOnExit, nsIDOMHTMLVoidCallback *enterCallback, nsIDOMHTMLVoidCallback *exitCallback)
+{
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
-/* void addCuePoint (in float time, in nsIDOMHTMLVoidCallback callback, in boolean pause); */
-NS_IMETHODIMP nsHTMLMediaElement::AddCuePoint(float time, nsIDOMHTMLVoidCallback *callback, PRBool pause)
-{
-    return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-/* void removeCuePoint (in float time, in nsIDOMHTMLVoidCallback callback); */
-NS_IMETHODIMP nsHTMLMediaElement::RemoveCuePoint(float time, nsIDOMHTMLVoidCallback *callback)
+/* void removeCueRanges (in DOMString className); */
+NS_IMETHODIMP nsHTMLMediaElement::RemoveCueRanges(const nsAString & className)
 {
     return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /* attribute float volume; */
 NS_IMETHODIMP nsHTMLMediaElement::GetVolume(float *aVolume)
 {
   if (mMuted)
@@ -477,17 +445,22 @@ nsHTMLMediaElement::ParseAttribute(PRInt
                                    nsAttrValue& aResult)
 {
   if (aNamespaceID == kNameSpaceID_None) {
     if (aAttribute == nsGkAtoms::src) {
       static const char* kWhitespace = " \n\r\t\b";
       aResult.SetTo(nsContentUtils::TrimCharsInSet(kWhitespace, aValue));
       return PR_TRUE;
     }
-    else if(aAttribute == nsGkAtoms::playbackrate || aAttribute == nsGkAtoms::defaultplaybackrate) {
+    else if(aAttribute == nsGkAtoms::playbackrate
+            || aAttribute == nsGkAtoms::defaultplaybackrate
+            || aAttribute == nsGkAtoms::loopstart
+            || aAttribute == nsGkAtoms::loopend
+            || aAttribute == nsGkAtoms::start
+            || aAttribute == nsGkAtoms::end) {
       return aResult.ParseFloatValue(aValue);
     }
     else if (ParseImageAttribute(aAttribute, aValue, aResult)) {
       return PR_TRUE;
     }
   }
 
   return nsGenericHTMLElement::ParseAttribute(aNamespaceID, aAttribute, aValue,
@@ -577,17 +550,17 @@ nsresult nsHTMLMediaElement::PickMediaEl
     }    
   }        
 
   return NS_ERROR_DOM_INVALID_STATE_ERR;
 }
 
 nsresult nsHTMLMediaElement::InitializeDecoder(nsAString& aChosenMediaResource)
 {
-  nsCOMPtr<nsIDocument> doc = GetCurrentDoc();
+  nsCOMPtr<nsIDocument> doc = GetOwnerDoc();
   if (!doc) {
     return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
 
   nsresult rv;
   nsCOMPtr<nsIURI> uri;
   nsCOMPtr<nsIURI> baseURL = GetBaseURI();
   const nsAFlatCString &charset = doc->GetDocumentCharacterSet();
@@ -607,16 +580,20 @@ nsresult nsHTMLMediaElement::InitializeD
   return rv;
 }
 
 void nsHTMLMediaElement::MetadataLoaded()
 {
   mNetworkState = nsIDOMHTMLMediaElement::LOADED_METADATA;
   DispatchAsyncSimpleEvent(NS_LITERAL_STRING("durationchange"));
   DispatchAsyncSimpleEvent(NS_LITERAL_STRING("loadedmetadata"));
+  float start = 0.0;
+  nsresult rv = GetStart(&start);
+  if (NS_SUCCEEDED(rv) && start > 0.0 && mDecoder)
+    mDecoder->Seek(start);
 }
 
 void nsHTMLMediaElement::FirstFrameLoaded()
 {
   mNetworkState = nsIDOMHTMLMediaElement::LOADED_FIRST_FRAME;
   ChangeReadyState(nsIDOMHTMLMediaElement::CAN_SHOW_CURRENT_FRAME);
   mLoadedFirstFrame = PR_TRUE;
   DispatchAsyncSimpleEvent(NS_LITERAL_STRING("loadedfirstframe"));
@@ -646,16 +623,21 @@ void nsHTMLMediaElement::PlaybackComplet
 {
   mBegun = PR_FALSE;
   mEnded = PR_TRUE;
   Pause();
   SetCurrentTime(0);
   DispatchSimpleEvent(NS_LITERAL_STRING("ended"));
 }
 
+void nsHTMLMediaElement::CanPlayThrough()
+{
+  ChangeReadyState(nsIDOMHTMLMediaElement::CAN_PLAY_THROUGH);
+}
+
 void nsHTMLMediaElement::ChangeReadyState(nsMediaReadyState aState)
 {
   mReadyState = aState;
   if (mNetworkState != nsIDOMHTMLMediaElement::EMPTY) {
     switch(mReadyState) {
     case nsIDOMHTMLMediaElement::DATA_UNAVAILABLE:
       DispatchAsyncSimpleEvent(NS_LITERAL_STRING("dataunavailable"));
       LOG(PR_LOG_DEBUG, ("Ready state changed to DATA_UNAVAILABLE"));
--- a/content/html/content/src/nsHTMLSourceElement.cpp
+++ b/content/html/content/src/nsHTMLSourceElement.cpp
@@ -98,16 +98,17 @@ NS_HTML_CONTENT_INTERFACE_TABLE_TAIL_CLA
 
 
 NS_IMPL_ELEMENT_CLONE(nsHTMLSourceElement)
 
 
 NS_IMPL_URI_ATTR(nsHTMLSourceElement, Src, src)
 NS_IMPL_STRING_ATTR(nsHTMLSourceElement, Type, type)
 NS_IMPL_STRING_ATTR(nsHTMLSourceElement, Media, media)
+NS_IMPL_FLOAT_ATTR(nsHTMLSourceElement, PixelRatio, pixelratio)
 
 
 PRBool
 nsHTMLSourceElement::ParseAttribute(PRInt32 aNamespaceID,
                                 nsIAtom* aAttribute,
                                 const nsAString& aValue,
                                 nsAttrValue& aResult)
 {
--- a/content/media/video/Makefile.in
+++ b/content/media/video/Makefile.in
@@ -41,10 +41,14 @@ VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 DIRS		= \
 		public \
 		src \
 		$(NULL)
 
+ifdef MOZ_MOCHITEST
+DIRS            += test
+endif
+
 include $(topsrcdir)/config/rules.mk
 
--- a/content/media/video/public/nsVideoDecoder.h
+++ b/content/media/video/public/nsVideoDecoder.h
@@ -151,16 +151,19 @@ class nsVideoDecoder : public nsIObserve
 protected:
   // Cleanup internal data structures
   virtual void Shutdown();
 
   // Start invalidating the video frame at the interval required
   // by the specificed framerate (in frames per second).
   nsresult StartInvalidating(double aFramerate);
 
+  // Stop invalidating the video frame
+  void StopInvalidating();
+
   // Start timer to update download progress information.
   nsresult StartProgress();
 
   // Stop progress information timer.
   nsresult StopProgress();
 
   // Set the RGB width, height and framerate. The passed RGB buffer is
   // copied to the mRGB buffer. This also allocates the mRGB buffer if
--- a/content/media/video/src/nsVideoDecoder.cpp
+++ b/content/media/video/src/nsVideoDecoder.cpp
@@ -107,16 +107,24 @@ nsresult nsVideoDecoder::StartInvalidati
     rv = mInvalidateTimer->InitWithFuncCallback(InvalidateCallback, 
                                                 this, 
                                                 static_cast<PRInt32>(1000.0/aFramerate), 
                                                 nsITimer::TYPE_REPEATING_PRECISE);
   }
   return rv;
 }
 
+void nsVideoDecoder::StopInvalidating()
+{
+  if (mInvalidateTimer) {
+    mInvalidateTimer->Cancel();
+    mInvalidateTimer = nsnull;
+  }
+}
+
 void nsVideoDecoder::Invalidate()
 {
   if (!mElement)
     return;
 
   nsIFrame* frame = mElement->GetPrimaryFrame();
   if (!frame)
     return;
new file mode 100644
--- /dev/null
+++ b/content/media/video/test/Makefile.in
@@ -0,0 +1,59 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (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.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is Mozilla code.
+#
+# The Initial Developer of the Original Code is the Mozilla Corporation.
+# Portions created by the Initial Developer are Copyright (C) 2007
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#  Chris Double <chris.double@double.co.nz>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+DEPTH		= ../../../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+relativesrcdir  = content/media/video/test
+
+include $(DEPTH)/config/autoconf.mk
+include $(topsrcdir)/config/rules.mk
+
+_TEST_FILES = 	test_autoplay.html \
+                test_constants.html \
+                test_controls.html \
+                test_currentTime.html \
+                test_defaultPlaybackRate.html \
+                test_networkState.html \
+                test_paused.html \
+                test_playbackRate.html \
+                test_readyState.html \
+		test_start.html \
+		$(NULL)
+
+libs:: $(_TEST_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/content/media/video/test/test_autoplay.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Media test: autoplay attribute get</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<video id='v1'></video><audio id='a1'></audio>
+<video id='v2' autoplay></video><audio id='a2' autoplay></audio>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+var v1 = document.getElementById('v1');
+var a1 = document.getElementById('a1');
+var v2 = document.getElementById('v2');
+var a2 = document.getElementById('a2');
+ok(!v1.autoplay, "v1.autoplay should be false by default");
+ok(!a1.autoplay, "v1.autoplay should be false by default");
+ok(v2.autoplay, "v2.autoplay should be true");
+ok(a2.autoplay, "v2.autoplay should be true");
+
+v1.autoplay = true;
+v1.autoplay = true;
+ok(v1.autoplay, "video.autoplay not true");
+ok(v1.autoplay, "audio.autoplay not true");
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/media/video/test/test_constants.html
@@ -0,0 +1,137 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+  Adapted from:
+  http://simon.html5.org/test/html/dom/interfaces/HTMLElement/HTMLMediaElement/const-unsigned-short/001.htm
+-->
+<head>
+  <title>Media test: constants</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<video><source></video><audio></audio>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+is(HTMLElement.EMPTY, undefined);
+is(HTMLElement.LOADING, undefined);
+is(HTMLElement.LOADED_METADATA, undefined);
+is(HTMLElement.LOADED_FIRST_FRAME, undefined);
+is(HTMLElement.LOADED, undefined);
+is(HTMLElement.DATA_UNAVAILABLE, undefined);
+is(HTMLElement.CAN_SHOW_CURRENT_FRAME, undefined);
+is(HTMLElement.CAN_PLAY, undefined);
+is(HTMLElement.CAN_PLAY_THROUGH, undefined);
+is(HTMLMediaElement.EMPTY, 0);
+is(HTMLMediaElement.LOADING, 1);
+is(HTMLMediaElement.LOADED_METADATA, 2);
+is(HTMLMediaElement.LOADED_FIRST_FRAME, 3);
+is(HTMLMediaElement.LOADED, 4);
+is(HTMLMediaElement.DATA_UNAVAILABLE, 0);
+is(HTMLMediaElement.CAN_SHOW_CURRENT_FRAME, 1);
+is(HTMLMediaElement.CAN_PLAY, 2);
+is(HTMLMediaElement.CAN_PLAY_THROUGH, 3);
+is(HTMLVideoElement.EMPTY, undefined);
+is(HTMLVideoElement.LOADING, undefined);
+is(HTMLVideoElement.LOADED_METADATA, undefined);
+is(HTMLVideoElement.LOADED_FIRST_FRAME, undefined);
+is(HTMLVideoElement.LOADED, undefined);
+is(HTMLVideoElement.DATA_UNAVAILABLE, undefined);
+is(HTMLVideoElement.CAN_SHOW_CURRENT_FRAME, undefined);
+is(HTMLVideoElement.CAN_PLAY, undefined);
+is(HTMLVideoElement.CAN_PLAY_THROUGH, undefined);
+is(HTMLAudioElement.EMPTY, undefined);
+is(HTMLAudioElement.LOADING, undefined);
+is(HTMLAudioElement.LOADED_METADATA, undefined);
+is(HTMLAudioElement.LOADED_FIRST_FRAME, undefined);
+is(HTMLAudioElement.LOADED, undefined);
+is(HTMLAudioElement.DATA_UNAVAILABLE, undefined);
+is(HTMLAudioElement.CAN_SHOW_CURRENT_FRAME, undefined);
+is(HTMLAudioElement.CAN_PLAY, undefined);
+is(HTMLAudioElement.CAN_PLAY_THROUGH, undefined);
+is(HTMLSourceElement.EMPTY, undefined);
+is(HTMLSourceElement.LOADING, undefined);
+is(HTMLSourceElement.LOADED_METADATA, undefined);
+is(HTMLSourceElement.LOADED_FIRST_FRAME, undefined);
+is(HTMLSourceElement.LOADED, undefined);
+is(HTMLSourceElement.DATA_UNAVAILABLE, undefined);
+is(HTMLSourceElement.CAN_SHOW_CURRENT_FRAME, undefined);
+is(HTMLSourceElement.CAN_PLAY, undefined);
+is(HTMLSourceElement.CAN_PLAY_THROUGH, undefined);
+is(document.body.EMPTY, undefined);
+is(document.body.LOADING, undefined);
+is(document.body.LOADED_METADATA, undefined);
+is(document.body.LOADED_FIRST_FRAME, undefined);
+is(document.body.LOADED, undefined);             
+is(document.body.DATA_UNAVAILABLE, undefined);
+is(document.body.CAN_SHOW_CURRENT_FRAME, undefined);
+is(document.body.CAN_PLAY, undefined);
+is(document.body.CAN_PLAY_THROUGH, undefined);
+is(document.getElementsByTagName("video")[0].EMPTY, 0);
+is(document.getElementsByTagName("video")[0].LOADING, 1);
+is(document.getElementsByTagName("video")[0].LOADED_METADATA, 2);
+is(document.getElementsByTagName("video")[0].LOADED_FIRST_FRAME, 3);
+is(document.getElementsByTagName("video")[0].LOADED, 4);
+is(document.getElementsByTagName("video")[0].DATA_UNAVAILABLE, 0);
+is(document.getElementsByTagName("video")[0].CAN_SHOW_CURRENT_FRAME, 1);
+is(document.getElementsByTagName("video")[0].CAN_PLAY, 2);
+is(document.getElementsByTagName("video")[0].CAN_PLAY_THROUGH, 3);
+is(document.getElementsByTagName("audio")[0].EMPTY, 0);
+is(document.getElementsByTagName("audio")[0].LOADING, 1);
+is(document.getElementsByTagName("audio")[0].LOADED_METADATA, 2);
+is(document.getElementsByTagName("audio")[0].LOADED_FIRST_FRAME, 3);
+is(document.getElementsByTagName("audio")[0].LOADED, 4);
+is(document.getElementsByTagName("audio")[0].DATA_UNAVAILABLE, 0);
+is(document.getElementsByTagName("audio")[0].CAN_SHOW_CURRENT_FRAME, 1);
+is(document.getElementsByTagName("audio")[0].CAN_PLAY, 2);
+is(document.getElementsByTagName("audio")[0].CAN_PLAY_THROUGH, 3);
+is(document.getElementsByTagName("source")[0].EMPTY, undefined);
+is(document.getElementsByTagName("source")[0].LOADING, undefined);
+is(document.getElementsByTagName("source")[0].LOADED_METADATA, undefined);
+is(document.getElementsByTagName("source")[0].LOADED_FIRST_FRAME, undefined);
+is(document.getElementsByTagName("source")[0].LOADED, undefined);
+is(document.getElementsByTagName("source")[0].DATA_UNAVAILABLE, undefined);
+is(document.getElementsByTagName("source")[0].CAN_SHOW_CURRENT_FRAME, undefined);
+is(document.getElementsByTagName("source")[0].CAN_PLAY, undefined);
+is(document.getElementsByTagName("source")[0].CAN_PLAY_THROUGH, undefined);
+is(HTMLElement.prototype.EMPTY, undefined);
+is(HTMLElement.prototype.LOADING, undefined);
+is(HTMLElement.prototype.LOADED_METADATA, undefined);
+is(HTMLElement.prototype.LOADED_FIRST_FRAME, undefined);
+is(HTMLElement.prototype.LOADED, undefined);
+is(HTMLElement.prototype.DATA_UNAVAILABLE, undefined);
+is(HTMLElement.prototype.CAN_SHOW_CURRENT_FRAME, undefined);
+is(HTMLElement.prototype.CAN_PLAY, undefined);
+is(HTMLElement.prototype.CAN_PLAY_THROUGH, undefined);
+is(HTMLVideoElement.prototype.EMPTY, 0);
+is(HTMLVideoElement.prototype.LOADING, 1);
+is(HTMLVideoElement.prototype.LOADED_METADATA, 2);
+is(HTMLVideoElement.prototype.LOADED_FIRST_FRAME, 3);
+is(HTMLVideoElement.prototype.LOADED, 4);
+is(HTMLVideoElement.prototype.DATA_UNAVAILABLE, 0);
+is(HTMLVideoElement.prototype.CAN_SHOW_CURRENT_FRAME, 1);
+is(HTMLVideoElement.prototype.CAN_PLAY, 2);
+is(HTMLVideoElement.prototype.CAN_PLAY_THROUGH, 3);
+is(HTMLAudioElement.prototype.EMPTY, 0);
+is(HTMLAudioElement.prototype.LOADING, 1);
+is(HTMLAudioElement.prototype.LOADED_METADATA, 2);
+is(HTMLAudioElement.prototype.LOADED_FIRST_FRAME, 3);
+is(HTMLAudioElement.prototype.LOADED, 4);
+is(HTMLAudioElement.prototype.DATA_UNAVAILABLE, 0);
+is(HTMLAudioElement.prototype.CAN_SHOW_CURRENT_FRAME, 1);
+is(HTMLAudioElement.prototype.CAN_PLAY, 2);
+is(HTMLAudioElement.prototype.CAN_PLAY_THROUGH, 3);
+is(HTMLSourceElement.prototype.EMPTY, undefined);
+is(HTMLSourceElement.prototype.LOADING, undefined);
+is(HTMLSourceElement.prototype.LOADED_METADATA, undefined);
+is(HTMLSourceElement.prototype.LOADED_FIRST_FRAME, undefined);
+is(HTMLSourceElement.prototype.LOADED, undefined);
+is(HTMLSourceElement.prototype.DATA_UNAVAILABLE, undefined);
+is(HTMLSourceElement.prototype.CAN_SHOW_CURRENT_FRAME, undefined);
+is(HTMLSourceElement.prototype.CAN_PLAY, undefined);
+is(HTMLSourceElement.prototype.CAN_PLAY_THROUGH, undefined);
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/media/video/test/test_controls.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Media test: controls</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<video id='v1'></video><audio id='a1'></audio>
+<video id='v2' controls></video><audio id='a2' controls></audio>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+var v1 = document.getElementById('v1');
+var a1 = document.getElementById('a1');
+var v2 = document.getElementById('v2');
+var a2 = document.getElementById('a2');
+ok(!v1.controls, "v1.controls should be false by default");
+ok(!a1.controls, "v1.controls should be false by default");
+ok(v2.controls, "v2.controls should be true");
+ok(a2.controls, "v2.controls should be true");
+v2.controls=false;
+a2.controls=false;
+ok(!v2.controls, "v2.controls should be false");
+ok(!a2.controls, "a2.controls should be false");
+v2.controls=true;
+a2.controls=true;
+ok(v2.controls, "v2.controls should be true");
+ok(a2.controls, "a2.controls should be true");
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/media/video/test/test_currentTime.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Media test: currentTime</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<video id='v1'></video><audio id='a1'></audio>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+var v1 = document.getElementById('v1');
+var a1 = document.getElementById('a1');
+is(v1.currentTime, 0.0);
+is(a1.currentTime, 0.0);
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/media/video/test/test_defaultPlaybackRate.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Media test: defaultPlaybackRate</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<video id='v1'></video><audio id='a1'></audio>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+var v1 = document.getElementById('v1');
+var a1 = document.getElementById('a1');
+var passed = true;
+
+is(v1.defaultPlaybackRate, 1);
+is(a1.defaultPlaybackRate, 1);
+
+v1.defaultPlaybackRate = 2.5;
+a1.defaultPlaybackRate = 2.5;
+is(v1.defaultPlaybackRate, 2.5);
+is(a1.defaultPlaybackRate, 2.5);
+
+try {
+  v1.defaultPlaybackRate = 0
+  a1.defaultPlaybackRate = 0
+  passed = false;
+} catch(e) { }
+todo(passed, "Should not be able to set defaultPlaybackRate to 0");
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/media/video/test/test_networkState.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Media test: networkState</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<video id='v1'></video><audio id='a1'></audio>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+var v1 = document.getElementById('v1');
+var a1 = document.getElementById('a1');
+var passed = true;
+
+is(v1.networkState, 0);
+is(a1.networkState, 0);
+
+try {
+  v1.networkState = 0;
+  a1.networkState = 0;
+  passed = false;
+} catch(e) { }
+ok(passed, "Should not be able to set networkState (readonly attribute)");
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/media/video/test/test_paused.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Media test: paused</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<video id='v1'></video><audio id='a1'></audio>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+var v1 = document.getElementById('v1');
+var a1 = document.getElementById('a1');
+ok(v1.paused, "v1.paused must initially be true");
+ok(a1.paused, "a1.paused must initially be true");
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/media/video/test/test_playbackRate.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Media test: playbackRate</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<video id='v1'></video><audio id='a1'></audio>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+var v1 = document.getElementById('v1');
+var a1 = document.getElementById('a1');
+var passed = true;
+
+is(v1.playbackRate, 1);
+is(a1.playbackRate, 1);
+
+v1.playbackRate = 2.5;
+a1.playbackRate = 2.5;
+is(v1.playbackRate, 2.5);
+is(a1.playbackRate, 2.5);
+
+try {
+  v1.playbackRate = 0
+  a1.playbackRate = 0
+  passed = false;
+} catch(e) { }
+todo(passed, "Should not be able to set playbackRate to 0");
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/media/video/test/test_readyState.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Media test: readyState</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<video id='v1'></video><audio id='a1'></audio>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+var v1 = document.getElementById('v1');
+var a1 = document.getElementById('a1');
+var passed = true;
+
+is(v1.readyState, 0);
+is(a1.readyState, 0);
+
+try {
+  v1.readyState = 0;
+  a1.readyState = 0;
+  passed = false;
+} catch(e) { }
+ok(passed, "Should not be able to set readyState (readonly attribute)");
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/content/media/video/test/test_start.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Media test: start</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<video id='v1'></video><audio id='a1'></audio>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+var v1 = document.getElementById('v1');
+var a1 = document.getElementById('a1');
+
+is(v1.start, 0);
+is(a1.start, 0);
+
+v1.start = 2.5;
+a1.start = 2.5;
+is(v1.start, 2.5);
+is(a1.start, 2.5);
+</script>
+</pre>
+</body>
+</html>
--- a/dom/public/idl/html/Makefile.in
+++ b/dom/public/idl/html/Makefile.in
@@ -105,16 +105,17 @@ SDK_XPIDLSRCS =					\
 	nsIDOMHTMLTitleElement.idl		\
 	nsIDOMHTMLUListElement.idl		\
 	$(NULL)
 
 ifdef MOZ_MEDIA
 SDK_XPIDLSRCS +=				\
 	nsIDOMHTMLMediaError.idl		\
 	nsIDOMHTMLTimeRanges.idl		\
+	nsIDOMHTMLByteRanges.idl		\
 	nsIDOMHTMLMediaElement.idl		\
 	nsIDOMHTMLSourceElement.idl		\
 	nsIDOMHTMLVideoElement.idl		\
 	nsIDOMHTMLAudioElement.idl		\
 	nsIDOMHTMLVoidCallback.idl		\
 	$(NULL)
 endif
 
--- a/dom/public/idl/html/nsIDOMHTMLMediaElement.idl
+++ b/dom/public/idl/html/nsIDOMHTMLMediaElement.idl
@@ -34,16 +34,17 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsIDOMHTMLElement.idl"
 #include "nsIDOMHTMLMediaError.idl"
 #include "nsIDOMHTMLTimeRanges.idl"
+#include "nsIDOMHTMLByteRanges.idl"
 #include "nsIDOMHTMLVoidCallback.idl"
 
 /**
  * The nsIDOMHTMLMediaElement interface is an interface to be implemented by the HTML
  * <audio> and <video> elements.
  *
  * For more information on this interface, please see
  * http://www.whatwg.org/specs/web-apps/current-work/#htmlmediaelement
@@ -62,17 +63,20 @@ interface nsIDOMHTMLMediaElement : nsIDO
   readonly attribute DOMString currentSrc;
   const unsigned short EMPTY = 0;
   const unsigned short LOADING = 1;
   const unsigned short LOADED_METADATA = 2;
   const unsigned short LOADED_FIRST_FRAME = 3;
   const unsigned short LOADED = 4;
   readonly attribute unsigned short networkState;
   readonly attribute float bufferingRate;
+  readonly attribute boolean bufferingThrottled;
   readonly attribute nsIDOMHTMLTimeRanges buffered;
+  readonly attribute nsIDOMHTMLByteRanges bufferedBytes;
+  readonly attribute unsigned long totalBytes;
   void load();
 
   // ready state
   const unsigned short DATA_UNAVAILABLE = 0;
   const unsigned short CAN_SHOW_CURRENT_FRAME = 1;
   const unsigned short CAN_PLAY = 2;
   const unsigned short CAN_PLAY_THROUGH = 3;
   readonly attribute unsigned short readyState;
@@ -91,21 +95,21 @@ interface nsIDOMHTMLMediaElement : nsIDO
   void play();
   void pause();
 
   // looping
            attribute float start;
            attribute float end;
            attribute float loopStart;
            attribute float loopEnd;
-           attribute unsigned long loopCount;
+           attribute unsigned long playCount;
            attribute unsigned long currentLoop;
 
   // cue points
-  void addCuePoint(in float time, in nsIDOMHTMLVoidCallback callback, in boolean pause);
-  void removeCuePoint(in float time, in nsIDOMHTMLVoidCallback callback);
+  void addCueRange(in DOMString className, in float start, in float end, in boolean pauseOnExit, in nsIDOMHTMLVoidCallback enterCallback, in nsIDOMHTMLVoidCallback exitCallback);
+  void removeCueRanges(in DOMString className);
+
 
   // controls
            attribute boolean controls;
            attribute float volume;
            attribute boolean muted;
 };
-
--- a/dom/public/idl/html/nsIDOMHTMLSourceElement.idl
+++ b/dom/public/idl/html/nsIDOMHTMLSourceElement.idl
@@ -46,12 +46,13 @@
  * http://www.whatwg.org/specs/web-apps/current-work/#source
  *
  * @status UNDER_DEVELOPMENT
  */
 
 [scriptable, uuid(BF80FED9-BDBA-4C5A-AD23-203FAA4C4892)]
 interface nsIDOMHTMLSourceElement : nsIDOMHTMLElement
 {
-           attribute DOMString           src;
-           attribute DOMString           type;
-           attribute DOMString           media;
+           attribute DOMString src;
+           attribute DOMString type;
+           attribute DOMString media;
+           attribute float     pixelRatio;
 };
--- a/layout/style/forms.css
+++ b/layout/style/forms.css
@@ -590,20 +590,16 @@ input[type="file"] > input[type="text"] 
 @media print {
   input, textarea, select, button {
     -moz-user-input: none !important;
   }
 
   input[type="file"] { height: 2em; }
 }
 
-video {
-  background-color: black;
-}
-
 video > xul|videocontrols {
   display: none;
 }
 video[controls] > xul|videocontrols {
   display: -moz-box;
   -moz-box-orient: vertical;
   -moz-binding: url("chrome://global/content/bindings/videocontrols.xml#videoControls");
 }