Bug 449157 - Implement loop attribute on media elements. r=roc
authorMatthew Gregan <kinetik@flim.org>
Mon, 21 Nov 2011 11:59:01 +1300
changeset 80547 40b854ef9e16ab673619589b9cdf21ee611675cb
parent 80546 cf6e30a75b0a72c9d2e4aa271a4dcd08749e8002
child 80548 5d052a4d8f2b47ad091ba9d3fcc7d2a0010f09d1
push id21505
push usermbrubeck@mozilla.com
push dateMon, 21 Nov 2011 16:45:45 +0000
treeherdermozilla-central@9276e3274f18 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs449157
milestone11.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 449157 - Implement loop attribute on media elements. r=roc
content/base/src/nsGkAtomList.h
content/base/src/nsTreeSanitizer.cpp
content/html/content/src/nsHTMLMediaElement.cpp
content/media/test/Makefile.in
content/media/test/test_loop.html
dom/interfaces/html/nsIDOMHTMLMediaElement.idl
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -254,17 +254,16 @@ GK_ATOM(controls, "controls")
 GK_ATOM(coords, "coords")
 GK_ATOM(copy, "copy")
 GK_ATOM(copyOf, "copy-of")
 GK_ATOM(count, "count")
 GK_ATOM(crop, "crop")
 GK_ATOM(crossorigin, "crossorigin")
 GK_ATOM(curpos, "curpos")
 GK_ATOM(current, "current")
-GK_ATOM(currentloop, "currentloop")
 GK_ATOM(cycler, "cycler")
 GK_ATOM(data, "data")
 GK_ATOM(datalist, "datalist")
 GK_ATOM(dataType, "data-type")
 GK_ATOM(dateTime, "date-time")
 GK_ATOM(datasources, "datasources")
 GK_ATOM(datetime, "datetime")
 GK_ATOM(dblclick, "dblclick")
@@ -528,18 +527,16 @@ GK_ATOM(listing, "listing")
 GK_ATOM(listitem, "listitem")
 GK_ATOM(listrows, "listrows")
 GK_ATOM(load, "load")
 GK_ATOM(localedir, "localedir")
 GK_ATOM(localName, "local-name")
 GK_ATOM(longdesc, "longdesc")
 #ifdef MOZ_MEDIA
 GK_ATOM(loop, "loop")
-GK_ATOM(loopend, "loopend")
-GK_ATOM(loopstart, "loopstart")
 #endif
 GK_ATOM(low, "low")
 GK_ATOM(lowerFirst, "lower-first")
 GK_ATOM(lowest, "lowest")
 GK_ATOM(lowsrc, "lowsrc")
 GK_ATOM(ltr, "ltr")
 GK_ATOM(lwtheme, "lwtheme")
 GK_ATOM(lwthemetextcolor, "lwthemetextcolor")
@@ -776,24 +773,20 @@ GK_ATOM(parentfocused, "parentfocused")
 GK_ATOM(parsetype, "parsetype")
 GK_ATOM(pattern, "pattern")
 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(placeholder, "placeholder")
 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(popupalign, "popupalign")
 GK_ATOM(popupanchor, "popupanchor")
 GK_ATOM(popupgroup, "popupgroup")
--- a/content/base/src/nsTreeSanitizer.cpp
+++ b/content/base/src/nsTreeSanitizer.cpp
@@ -239,18 +239,16 @@ nsIAtom** const kAttributesHTML[] = {
   &nsGkAtoms::itemtype,
   &nsGkAtoms::kind,
   &nsGkAtoms::label,
   &nsGkAtoms::lang,
   &nsGkAtoms::list,
   &nsGkAtoms::longdesc,
 #ifdef MOZ_MEDIA
   &nsGkAtoms::loop,
-  &nsGkAtoms::loopend,
-  &nsGkAtoms::loopstart,
 #endif
   &nsGkAtoms::low,
   &nsGkAtoms::max,
   &nsGkAtoms::maxlength,
   &nsGkAtoms::media,
   &nsGkAtoms::method,
   &nsGkAtoms::min,
   &nsGkAtoms::mozdonotsend,
@@ -258,23 +256,19 @@ nsIAtom** const kAttributesHTML[] = {
   &nsGkAtoms::name,
   &nsGkAtoms::nohref,
   &nsGkAtoms::noshade,
   &nsGkAtoms::novalidate,
   &nsGkAtoms::nowrap,
   &nsGkAtoms::open,
   &nsGkAtoms::optimum,
   &nsGkAtoms::pattern,
-#ifdef MOZ_MEDIA
-  &nsGkAtoms::pixelratio,
-#endif
   &nsGkAtoms::placeholder,
 #ifdef MOZ_MEDIA
   &nsGkAtoms::playbackrate,
-  &nsGkAtoms::playcount,
 #endif
   &nsGkAtoms::pointSize,
 #ifdef MOZ_MEDIA
   &nsGkAtoms::poster,
   &nsGkAtoms::preload,
 #endif
   &nsGkAtoms::prompt,
   &nsGkAtoms::pubdate,
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -391,16 +391,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsHTMLMediaElement)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
 NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
 
 // nsIDOMHTMLMediaElement
 NS_IMPL_URI_ATTR(nsHTMLMediaElement, Src, src)
 NS_IMPL_BOOL_ATTR(nsHTMLMediaElement, Controls, controls)
 NS_IMPL_BOOL_ATTR(nsHTMLMediaElement, Autoplay, autoplay)
+NS_IMPL_BOOL_ATTR(nsHTMLMediaElement, Loop, loop)
 NS_IMPL_ENUM_ATTR_DEFAULT_VALUE(nsHTMLMediaElement, Preload, preload, NULL)
 
 /* readonly attribute nsIDOMHTMLMediaElement mozAutoplayEnabled; */
 NS_IMETHODIMP nsHTMLMediaElement::GetMozAutoplayEnabled(bool *aAutoplayEnabled)
 {
   *aAutoplayEnabled = mAutoplayEnabled;
 
   return NS_OK;
@@ -1417,22 +1418,16 @@ bool nsHTMLMediaElement::ParseAttribute(
     { "",         nsHTMLMediaElement::PRELOAD_ATTR_EMPTY },
     { "none",     nsHTMLMediaElement::PRELOAD_ATTR_NONE },
     { "metadata", nsHTMLMediaElement::PRELOAD_ATTR_METADATA },
     { "auto",     nsHTMLMediaElement::PRELOAD_ATTR_AUTO },
     { 0 }
   };
 
   if (aNamespaceID == kNameSpaceID_None) {
-    if (aAttribute == nsGkAtoms::loopstart ||
-        aAttribute == nsGkAtoms::loopend ||
-        aAttribute == nsGkAtoms::start ||
-        aAttribute == nsGkAtoms::end) {
-      return aResult.ParseDoubleValue(aValue);
-    }
     if (ParseImageAttribute(aAttribute, aValue, aResult)) {
       return true;
     }
     if (aAttribute == nsGkAtoms::preload) {
       return aResult.ParseEnumValue(aValue, kPreloadTable, false);
     }
   }
 
@@ -2077,16 +2072,21 @@ void nsHTMLMediaElement::PlaybackEnded()
   // We changed the state of IsPlaybackEnded which can affect AddRemoveSelfReference
   AddRemoveSelfReference();
 
   if (mDecoder && mDecoder->IsInfinite()) {
     LOG(PR_LOG_DEBUG, ("%p, got duration by reaching the end of the stream", this));
     DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
   }
 
+  if (HasAttr(kNameSpaceID_None, nsGkAtoms::loop)) {
+    SetCurrentTime(0);
+    return;
+  }
+
   FireTimeUpdate(false);
   DispatchAsyncEvent(NS_LITERAL_STRING("ended"));
 }
 
 void nsHTMLMediaElement::SeekStarted()
 {
   DispatchAsyncEvent(NS_LITERAL_STRING("seeking"));
   FireTimeUpdate(false);
--- a/content/media/test/Makefile.in
+++ b/content/media/test/Makefile.in
@@ -116,16 +116,17 @@ include $(topsrcdir)/config/rules.mk
 		test_decoder_disable.html \
 		test_delay_load.html \
 		test_error_on_404.html \
 		test_error_in_video_document.html \
 		test_info_leak.html \
 		test_load.html \
 		test_load_candidates.html \
 		test_load_source.html \
+		test_loop.html \
 		test_media_selection.html \
 		test_mozLoadFrom.html \
 		test_networkState.html \
 		test_new_audio.html \
 		test_paused.html \
 		test_paused_after_ended.html \
 		test_play_events.html \
 		test_play_events_2.html \
new file mode 100644
--- /dev/null
+++ b/content/media/test/test_loop.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test looping support</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="text/javascript" src="manifest.js"></script>
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+var manager = new MediaTestManager;
+
+function startTest(test, token) {
+  manager.started(token);
+  var v = document.createElement('video');
+  v.token = token;
+  v.src = test.name;
+  v.name = test.name;
+  v.playCount = 0;
+  v.seekingCount = 0;
+  v.seekedCount = 0;
+  v.loop = true;
+
+  v.addEventListener("play", function (e) {
+    e.target.playCount += 1;
+    ok(e.target.playCount == 1, "Should get exactly one play event.");
+  }, false);
+
+  v.addEventListener("seeking", function (e) {
+    e.target.seekingCount += 1;
+  }, false);
+
+  v.addEventListener("seeked", function (e) {
+    e.target.seekedCount += 1;
+    if (e.target.seekedCount == 3) {
+      ok(e.target.seekingCount == 3, "Expect matched pairs of seeking/seeked events.");
+      e.target.loop = false;
+    }
+  }, false);
+
+  v.addEventListener("ended", function (e) {
+    ok(!e.target.loop, "Shouldn't get ended event while looping.");
+    e.target.parentNode.removeChild(v);
+    manager.finished(e.target.token);
+  }, false);
+
+  document.body.appendChild(v);
+  v.play();
+}
+
+manager.runTests(gSmallTests, startTest);
+</script>
+</pre>
+</body>
+</html>
--- a/dom/interfaces/html/nsIDOMHTMLMediaElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLMediaElement.idl
@@ -52,17 +52,17 @@
 
 // undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK
 %{C++
 #ifdef GetCurrentTime
 #undef GetCurrentTime
 #endif
 %}
 
-[scriptable, uuid(642a3b85-4edb-4c01-a162-06b5d88171e7)]
+[scriptable, uuid(f6eddb8a-7480-4b15-af2c-cc6ce9a7c140)]
 interface nsIDOMHTMLMediaElement : nsIDOMHTMLElement
 {
   // error state
   readonly attribute nsIDOMMediaError error;
 
   // network state
            attribute DOMString src;
   readonly attribute DOMString currentSrc;
@@ -89,16 +89,17 @@ interface nsIDOMHTMLMediaElement : nsIDO
            attribute double currentTime;
   readonly attribute double initialTime;
   readonly attribute double duration;
   readonly attribute boolean paused;
   readonly attribute nsIDOMTimeRanges seekable;
   readonly attribute boolean ended;
   readonly attribute boolean mozAutoplayEnabled;
            attribute boolean autoplay;
+           attribute boolean loop;
   void play();
   void pause();
 
   // controls
            attribute boolean controls;
            attribute double volume;
            attribute boolean muted;