Bug 481040 - video controls should indicate when an error occurs. r=enn

This commit is contained in:
Justin Dolske 2009-03-23 16:40:39 -07:00
parent 6e3ac6acb8
commit 5d2fec2163
8 changed files with 72 additions and 29 deletions

View File

@ -10,7 +10,7 @@
-moz-binding: url("chrome://global/content/bindings/videocontrols.xml#suppressChangeEvent");
}
.throbberOverlay {
.statusOverlay {
visibility: hidden;
opacity: 0.0;
}

View File

@ -52,8 +52,8 @@
<xbl:content xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<stack flex="1">
<vbox class="throbberOverlay">
<box class="throbber" flex="1"/>
<vbox class="statusOverlay">
<box class="statusIcon" flex="1"/>
</vbox>
<vbox>
@ -141,8 +141,9 @@
videoEvents : ["play", "pause", "ended", "volumechange", "loadeddata",
"loadstart", "durationchange", "timeupdate", "progress",
"playing", "waiting", "canplaythrough", "seeking",
"seeked", "emptied", "loadedmetadata"],
"seeked", "emptied", "loadedmetadata", "error"],
// controlFader holds the fade state for the control bar.
controlFader : {
name : "controls", // shorthand for debugging
element : null, // the element to fade in/out
@ -156,8 +157,9 @@
RUNTIME_STEP : 30 // ms
},
throbberFader : {
name : "throbber",
// statusFader holds the fade state for the status overlay (inc. throbber)
statusFader : {
name : "status",
element : null,
runtime : 0,
fadingIn : false,
@ -207,7 +209,7 @@
if (this.video.readyState >= this.video.HAVE_CURRENT_DATA)
this.firstFrameShown = true;
else
this.startFadeIn(this.throbberFader);
this.startFadeIn(this.statusFader);
// We can't determine the exact buffering status, but do know if it's
// fully loaded. (If it's still loading, it will fire a progress event
@ -217,6 +219,15 @@
this.bufferBar.setAttribute("value", 100);
else
this.bufferBar.setAttribute("value", 0);
// Set the current status icon. If the video is in an error state,
// show the status overlay now.
if (this.video.error) {
this.statusIcon.setAttribute("type", "error");
this.startFadeIn(this.statusFader, true);
} else {
this.statusIcon.setAttribute("type", "throbber");
}
},
get dynamicControls() {
@ -227,6 +238,11 @@
if (this.video.hasAttribute("mozNoDynamicControls"))
enabled = false;
// If the video hits an error, suppress controls if it
// hasn't managed to do anything else yet.
if (!this.firstFrameShown && this.video.error)
enabled = false;
return enabled;
},
@ -263,6 +279,7 @@
break;
case "loadstart":
this.maxCurrentTimeSeen = 0;
this.statusIcon.setAttribute("type", "throbber");
this.isAudioOnly = (this.video instanceof HTMLAudioElement);
break;
case "durationchange":
@ -297,17 +314,25 @@
break;
case "seeking":
case "waiting":
this.startFadeIn(this.throbberFader);
this.statusIcon.setAttribute("type", "throbber");
this.startFadeIn(this.statusFader);
break;
case "seeked":
// Normally we'd expect canplaythough to fire, but if we already
// have the data cached it shouldn't fire again.
if (this.video.readyState == this.video.HAVE_ENOUGH_DATA)
this.startFadeOut(this.throbberFader);
this.startFadeOut(this.statusFader);
break;
case "playing":
case "canplaythrough":
this.startFadeOut(this.throbberFader);
this.startFadeOut(this.statusFader);
break;
case "error":
this.statusIcon.setAttribute("type", "error");
this.startFadeIn(this.statusFader, true);
// If video hasn't shown anything yet, disable the controls.
if (!this.firstFrameShown)
this.startFadeOut(this.controlFader);
break;
default:
this.log("!!! event " + aEvent.type + " not handled!");
@ -322,10 +347,10 @@
clearInterval(this.controlFader.timer);
if (this.controlFader.delayTimer)
clearInterval(this.controlFader.delayTimer);
if (this.throbberFader.timer)
clearInterval(this.throbberFader.timer);
if (this.throbberFader.delayTimer)
clearTimeout(this.throbberFader.delayTimer);
if (this.statusFader.timer)
clearInterval(this.statusFader.timer);
if (this.statusFader.delayTimer)
clearTimeout(this.statusFader.delayTimer);
this.log("--- videocontrols terminated ---");
},
@ -403,12 +428,12 @@
this.startFade(this.controlFader, isMouseOver);
},
startFadeIn : function (fader) {
this.startFade(fader, true);
startFadeIn : function (fader, immediate) {
this.startFade(fader, true, immediate);
},
startFadeOut : function (fader) {
this.startFade(fader, false);
startFadeOut : function (fader, immediate) {
this.startFade(fader, false, immediate);
},
startFade : function (fader, fadeIn, immediate) {
@ -543,7 +568,8 @@
this.Utils.isAudioOnly = (video instanceof HTMLAudioElement);
this.Utils.controlFader.element = document.getAnonymousElementByAttribute(this, "class", "controlBar");
this.Utils.throbberFader.element = document.getAnonymousElementByAttribute(this, "class", "throbberOverlay");
this.Utils.statusFader.element = document.getAnonymousElementByAttribute(this, "class", "statusOverlay");
this.Utils.statusIcon = document.getAnonymousElementByAttribute(this, "class", "statusIcon");
this.Utils.playButton = document.getAnonymousElementByAttribute(this, "class", "playButton");
this.Utils.muteButton = document.getAnonymousElementByAttribute(this, "class", "muteButton");

View File

@ -179,6 +179,7 @@ classic.jar:
+ skin/classic/global/media/muteButton.png (media/muteButton.png)
+ skin/classic/global/media/unmuteButton.png (media/unmuteButton.png)
+ skin/classic/global/media/scrubberThumb.png (media/scrubberThumb.png)
+ skin/classic/global/media/error.png (media/error.png)
+ skin/classic/global/media/throbber.png (media/throbber.png)
+ skin/classic/global/menu/menu-arrow-dis.gif (menu/menu-arrow-dis.gif)
+ skin/classic/global/menu/menu-arrow-hov.gif (menu/menu-arrow-hov.gif)

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 B

View File

@ -80,13 +80,20 @@
min-height: 20px;
}
.throbberOverlay {
.statusOverlay {
background-color: rgba(0,0,0,0.55);
}
.throbber {
background: url(chrome://global/skin/media/throbber.png) no-repeat center;
height: 36px;
.statusIcon {
margin-bottom: 28px; /* same height as .controlBar, to keep icon centered above it */
width: 36px;
margin-bottom: 28px; /* same height as .controlBar, to keep throbber centered above it */
height: 36px;
}
.statusIcon[type="throbber"] {
background: url(chrome://global/skin/media/throbber.png) no-repeat center;
}
.statusIcon[type="error"] {
background: url(chrome://global/skin/media/error.png) no-repeat center;
}

View File

@ -153,6 +153,7 @@ classic.jar:
skin/classic/global/media/unmuteButton.png (media/unmuteButton.png)
skin/classic/global/media/scrubberThumb.png (media/scrubberThumb.png)
skin/classic/global/media/throbber.png (media/throbber.png)
skin/classic/global/media/error.png (media/error.png)
skin/classic/global/radio/radio-check.gif (radio/radio-check.gif)
skin/classic/global/radio/radio-check-dis.gif (radio/radio-check-dis.gif)
skin/classic/global/scrollbar/slider.gif (scrollbar/slider.gif)
@ -326,6 +327,7 @@ classic.jar:
skin/classic/aero/global/media/unmuteButton.png (media/unmuteButton.png)
skin/classic/aero/global/media/scrubberThumb.png (media/scrubberThumb.png)
skin/classic/aero/global/media/throbber.png (media/throbber.png)
skin/classic/aero/global/media/error.png (media/error.png)
skin/classic/aero/global/radio/radio-check.gif (radio/radio-check.gif)
skin/classic/aero/global/radio/radio-check-dis.gif (radio/radio-check-dis.gif)
skin/classic/aero/global/scrollbar/slider.gif (scrollbar/slider.gif)

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 B

View File

@ -87,13 +87,20 @@
min-height: 20px;
}
.throbberOverlay {
.statusOverlay {
background-color: rgba(0,0,0,0.55);
}
.throbber {
background: url(chrome://global/skin/media/throbber.png) no-repeat center;
height: 36px;
.statusIcon {
margin-bottom: 28px; /* same height as .controlBar, to keep icon centered above it */
width: 36px;
margin-bottom: 28px; /* same height as .controlBar, to keep throbber centered above it */
height: 36px;
}
.statusIcon[type="throbber"] {
background: url(chrome://global/skin/media/throbber.png) no-repeat center;
}
.statusIcon[type="error"] {
background: url(chrome://global/skin/media/error.png) no-repeat center;
}