diff options
Diffstat (limited to 'chromium/third_party/WebKit/Source/core/rendering/RenderMediaControls.cpp')
-rw-r--r-- | chromium/third_party/WebKit/Source/core/rendering/RenderMediaControls.cpp | 427 |
1 files changed, 422 insertions, 5 deletions
diff --git a/chromium/third_party/WebKit/Source/core/rendering/RenderMediaControls.cpp b/chromium/third_party/WebKit/Source/core/rendering/RenderMediaControls.cpp index ae146c0338a..13d1620367a 100644 --- a/chromium/third_party/WebKit/Source/core/rendering/RenderMediaControls.cpp +++ b/chromium/third_party/WebKit/Source/core/rendering/RenderMediaControls.cpp @@ -1,5 +1,7 @@ /* - * Copyright (C) 2009 Apple Inc. All Rights Reserved. + * Copyright (C) 2009 Apple Inc. + * Copyright (C) 2009 Google Inc. + * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -24,12 +26,427 @@ */ #include "config.h" - #include "core/rendering/RenderMediaControls.h" -// FIXME: Unify more of the code for Mac and Win. - -using namespace std; +#include "bindings/v8/ExceptionStatePlaceholder.h" +#include "core/html/HTMLMediaElement.h" +#include "core/html/TimeRanges.h" +#include "core/rendering/PaintInfo.h" +#include "platform/graphics/Gradient.h" +#include "platform/graphics/GraphicsContext.h" namespace WebCore { + +typedef WTF::HashMap<const char*, Image*> MediaControlImageMap; +static MediaControlImageMap* gMediaControlImageMap = 0; + +static Image* platformResource(const char* name) +{ + if (!gMediaControlImageMap) + gMediaControlImageMap = new MediaControlImageMap(); + if (Image* image = gMediaControlImageMap->get(name)) + return image; + if (Image* image = Image::loadPlatformResource(name).leakRef()) { + gMediaControlImageMap->set(name, image); + return image; + } + ASSERT_NOT_REACHED(); + return 0; +} + +static bool hasSource(const HTMLMediaElement* mediaElement) +{ + return mediaElement->networkState() != HTMLMediaElement::NETWORK_EMPTY + && mediaElement->networkState() != HTMLMediaElement::NETWORK_NO_SOURCE; +} + +static bool paintMediaButton(GraphicsContext* context, const IntRect& rect, Image* image) +{ + context->drawImage(image, rect); + return true; +} + +static bool paintMediaMuteButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) +{ + HTMLMediaElement* mediaElement = toParentMediaElement(object); + if (!mediaElement) + return false; + + static Image* soundLevel3 = platformResource("mediaplayerSoundLevel3"); + static Image* soundLevel2 = platformResource("mediaplayerSoundLevel2"); + static Image* soundLevel1 = platformResource("mediaplayerSoundLevel1"); + static Image* soundLevel0 = platformResource("mediaplayerSoundLevel0"); + static Image* soundDisabled = platformResource("mediaplayerSoundDisabled"); + + if (!hasSource(mediaElement) || !mediaElement->hasAudio()) + return paintMediaButton(paintInfo.context, rect, soundDisabled); + + if (mediaElement->muted() || mediaElement->volume() <= 0) + return paintMediaButton(paintInfo.context, rect, soundLevel0); + + if (mediaElement->volume() <= 0.33) + return paintMediaButton(paintInfo.context, rect, soundLevel1); + + if (mediaElement->volume() <= 0.66) + return paintMediaButton(paintInfo.context, rect, soundLevel2); + + return paintMediaButton(paintInfo.context, rect, soundLevel3); +} + +static bool paintMediaPlayButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) +{ + HTMLMediaElement* mediaElement = toParentMediaElement(object); + if (!mediaElement) + return false; + + static Image* mediaPlay = platformResource("mediaplayerPlay"); + static Image* mediaPause = platformResource("mediaplayerPause"); + static Image* mediaPlayDisabled = platformResource("mediaplayerPlayDisabled"); + + if (!hasSource(mediaElement)) + return paintMediaButton(paintInfo.context, rect, mediaPlayDisabled); + + return paintMediaButton(paintInfo.context, rect, mediaElement->canPlay() ? mediaPlay : mediaPause); +} + +static bool paintMediaOverlayPlayButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) +{ + HTMLMediaElement* mediaElement = toParentMediaElement(object); + if (!mediaElement) + return false; + + if (!hasSource(mediaElement) || !mediaElement->canPlay()) + return false; + + static Image* mediaOverlayPlay = platformResource("mediaplayerOverlayPlay"); + return paintMediaButton(paintInfo.context, rect, mediaOverlayPlay); } + +static Image* getMediaSliderThumb() +{ + static Image* mediaSliderThumb = platformResource("mediaplayerSliderThumb"); + return mediaSliderThumb; +} + +static void paintRoundedSliderBackground(const IntRect& rect, const RenderStyle* style, GraphicsContext* context) +{ + int borderRadius = rect.height() / 2; + IntSize radii(borderRadius, borderRadius); + Color sliderBackgroundColor = Color(11, 11, 11); + context->save(); + context->fillRoundedRect(rect, radii, radii, radii, radii, sliderBackgroundColor); + context->restore(); +} + +static void paintSliderRangeHighlight(const IntRect& rect, const RenderStyle* style, GraphicsContext* context, int startPosition, int endPosition, Color startColor, Color endColor) +{ + // Calculate border radius; need to avoid being smaller than half the slider height + // because of https://bugs.webkit.org/show_bug.cgi?id=30143. + int borderRadius = rect.height() / 2; + IntSize radii(borderRadius, borderRadius); + + // Calculate highlight rectangle and edge dimensions. + int startOffset = startPosition; + int endOffset = rect.width() - endPosition; + int rangeWidth = endPosition - startPosition; + + if (rangeWidth <= 0) + return; + + // Make sure the range width is bigger than border radius at the edges to retain rounded corners. + if (startOffset < borderRadius && rangeWidth < borderRadius) + rangeWidth = borderRadius; + if (endOffset < borderRadius && rangeWidth < borderRadius) { + startPosition -= borderRadius - rangeWidth; + rangeWidth = borderRadius; + } + + // Set rectangle to highlight range. + IntRect highlightRect = rect; + highlightRect.move(startOffset, 0); + highlightRect.setWidth(rangeWidth); + + // Don't bother drawing an empty area. + if (highlightRect.isEmpty()) + return; + + // Calculate white-grey gradient. + IntPoint sliderTopLeft = highlightRect.location(); + IntPoint sliderBottomLeft = sliderTopLeft; + sliderBottomLeft.move(0, highlightRect.height()); + RefPtr<Gradient> gradient = Gradient::create(sliderTopLeft, sliderBottomLeft); + gradient->addColorStop(0.0, startColor); + gradient->addColorStop(1.0, endColor); + + // Fill highlight rectangle with gradient, potentially rounded if on left or right edge. + context->save(); + context->setFillGradient(gradient); + + if (startOffset < borderRadius && endOffset < borderRadius) + context->fillRoundedRect(highlightRect, radii, radii, radii, radii, startColor); + else if (startOffset < borderRadius) + context->fillRoundedRect(highlightRect, radii, IntSize(0, 0), radii, IntSize(0, 0), startColor); + else if (endOffset < borderRadius) + context->fillRoundedRect(highlightRect, IntSize(0, 0), radii, IntSize(0, 0), radii, startColor); + else + context->fillRect(highlightRect); + + context->restore(); +} + +const int mediaSliderThumbWidth = 32; + +static bool paintMediaSlider(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) +{ + HTMLMediaElement* mediaElement = toParentMediaElement(object); + if (!mediaElement) + return false; + + RenderStyle* style = object->style(); + GraphicsContext* context = paintInfo.context; + + paintRoundedSliderBackground(rect, style, context); + + // Draw the buffered range. Since the element may have multiple buffered ranges and it'd be + // distracting/'busy' to show all of them, show only the buffered range containing the current play head. + RefPtr<TimeRanges> bufferedTimeRanges = mediaElement->buffered(); + float duration = mediaElement->duration(); + float currentTime = mediaElement->currentTime(); + if (std::isnan(duration) || std::isinf(duration) || !duration || std::isnan(currentTime)) + return true; + + for (unsigned i = 0; i < bufferedTimeRanges->length(); ++i) { + float start = bufferedTimeRanges->start(i, ASSERT_NO_EXCEPTION); + float end = bufferedTimeRanges->end(i, ASSERT_NO_EXCEPTION); + if (std::isnan(start) || std::isnan(end) || start > currentTime || end < currentTime) + continue; + int startPosition = int(start * rect.width() / duration); + int currentPosition = int(currentTime * rect.width() / duration); + int endPosition = int(end * rect.width() / duration); + + // Add half the thumb width proportionally adjusted to the current painting position. + int thumbCenter = mediaSliderThumbWidth / 2; + int addWidth = thumbCenter * (1.0 - 2.0 * currentPosition / rect.width()); + currentPosition += addWidth; + + // Draw white-ish highlight before current time. + Color startColor = Color(195, 195, 195); + Color endColor = Color(217, 217, 217); + if (currentPosition > startPosition) + paintSliderRangeHighlight(rect, style, context, startPosition, currentPosition, startColor, endColor); + + // Draw grey-ish highlight after current time. + startColor = Color(60, 60, 60); + endColor = Color(76, 76, 76); + + if (endPosition > currentPosition) + paintSliderRangeHighlight(rect, style, context, currentPosition, endPosition, startColor, endColor); + + return true; + } + + return true; +} + +static bool paintMediaSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) +{ + ASSERT(object->node()); + HTMLMediaElement* mediaElement = toParentMediaElement(object->node()->shadowHost()); + if (!mediaElement) + return false; + + if (!hasSource(mediaElement)) + return true; + + Image* mediaSliderThumb = getMediaSliderThumb(); + return paintMediaButton(paintInfo.context, rect, mediaSliderThumb); +} + +const int mediaVolumeSliderThumbWidth = 24; + +static bool paintMediaVolumeSlider(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) +{ + HTMLMediaElement* mediaElement = toParentMediaElement(object); + if (!mediaElement) + return false; + + GraphicsContext* context = paintInfo.context; + RenderStyle* style = object->style(); + + paintRoundedSliderBackground(rect, style, context); + + // Calculate volume position for white background rectangle. + float volume = mediaElement->volume(); + if (std::isnan(volume) || volume < 0) + return true; + if (volume > 1) + volume = 1; + if (!hasSource(mediaElement) || !mediaElement->hasAudio() || mediaElement->muted()) + volume = 0; + + // Calculate the position relative to the center of the thumb. + float fillWidth = 0; + if (volume > 0) { + float thumbCenter = mediaVolumeSliderThumbWidth / 2; + float zoomLevel = style->effectiveZoom(); + float positionWidth = volume * (rect.width() - (zoomLevel * thumbCenter)); + fillWidth = positionWidth + (zoomLevel * thumbCenter / 2); + } + + Color startColor = Color(195, 195, 195); + Color endColor = Color(217, 217, 217); + + paintSliderRangeHighlight(rect, style, context, 0.0, fillWidth, startColor, endColor); + + return true; +} + +static bool paintMediaVolumeSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) +{ + ASSERT(object->node()); + HTMLMediaElement* mediaElement = toParentMediaElement(object->node()->shadowHost()); + if (!mediaElement) + return false; + + if (!hasSource(mediaElement) || !mediaElement->hasAudio()) + return true; + + static Image* mediaVolumeSliderThumb = platformResource("mediaplayerVolumeSliderThumb"); + return paintMediaButton(paintInfo.context, rect, mediaVolumeSliderThumb); +} + +static bool paintMediaFullscreenButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) +{ + HTMLMediaElement* mediaElement = toParentMediaElement(object); + if (!mediaElement) + return false; + + static Image* mediaFullscreenButton = platformResource("mediaplayerFullscreen"); + return paintMediaButton(paintInfo.context, rect, mediaFullscreenButton); +} + +static bool paintMediaToggleClosedCaptionsButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) +{ + HTMLMediaElement* mediaElement = toParentMediaElement(object); + if (!mediaElement) + return false; + + static Image* mediaClosedCaptionButton = platformResource("mediaplayerClosedCaption"); + static Image* mediaClosedCaptionButtonDisabled = platformResource("mediaplayerClosedCaptionDisabled"); + + if (mediaElement->closedCaptionsVisible()) + return paintMediaButton(paintInfo.context, rect, mediaClosedCaptionButton); + + return paintMediaButton(paintInfo.context, rect, mediaClosedCaptionButtonDisabled); +} + + +bool RenderMediaControls::paintMediaControlsPart(MediaControlElementType part, RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) +{ + switch (part) { + case MediaMuteButton: + case MediaUnMuteButton: + return paintMediaMuteButton(object, paintInfo, rect); + case MediaPauseButton: + case MediaPlayButton: + return paintMediaPlayButton(object, paintInfo, rect); + case MediaShowClosedCaptionsButton: + return paintMediaToggleClosedCaptionsButton(object, paintInfo, rect); + case MediaSlider: + return paintMediaSlider(object, paintInfo, rect); + case MediaSliderThumb: + return paintMediaSliderThumb(object, paintInfo, rect); + case MediaVolumeSlider: + return paintMediaVolumeSlider(object, paintInfo, rect); + case MediaVolumeSliderThumb: + return paintMediaVolumeSliderThumb(object, paintInfo, rect); + case MediaEnterFullscreenButton: + case MediaExitFullscreenButton: + return paintMediaFullscreenButton(object, paintInfo, rect); + case MediaOverlayPlayButton: + return paintMediaOverlayPlayButton(object, paintInfo, rect); + case MediaVolumeSliderMuteButton: + case MediaSeekBackButton: + case MediaSeekForwardButton: + case MediaVolumeSliderContainer: + case MediaTimelineContainer: + case MediaCurrentTimeDisplay: + case MediaTimeRemainingDisplay: + case MediaControlsPanel: + case MediaRewindButton: + case MediaReturnToRealtimeButton: + case MediaStatusDisplay: + case MediaHideClosedCaptionsButton: + case MediaTextTrackDisplayContainer: + case MediaTextTrackDisplay: + case MediaFullScreenVolumeSlider: + case MediaFullScreenVolumeSliderThumb: + ASSERT_NOT_REACHED(); + break; + } + return false; +} + +const int mediaSliderThumbHeight = 24; +const int mediaVolumeSliderThumbHeight = 24; + +void RenderMediaControls::adjustMediaSliderThumbSize(RenderStyle* style) +{ + static Image* mediaSliderThumb = platformResource("mediaplayerSliderThumb"); + static Image* mediaVolumeSliderThumb = platformResource("mediaplayerVolumeSliderThumb"); + int width = 0; + int height = 0; + + Image* thumbImage = 0; + if (style->appearance() == MediaSliderThumbPart) { + thumbImage = mediaSliderThumb; + width = mediaSliderThumbWidth; + height = mediaSliderThumbHeight; + } else if (style->appearance() == MediaVolumeSliderThumbPart) { + thumbImage = mediaVolumeSliderThumb; + width = mediaVolumeSliderThumbWidth; + height = mediaVolumeSliderThumbHeight; + } + + float zoomLevel = style->effectiveZoom(); + if (thumbImage) { + style->setWidth(Length(static_cast<int>(width * zoomLevel), Fixed)); + style->setHeight(Length(static_cast<int>(height * zoomLevel), Fixed)); + } +} + +static String formatChromiumMediaControlsTime(float time, float duration) +{ + if (!std::isfinite(time)) + time = 0; + if (!std::isfinite(duration)) + duration = 0; + int seconds = static_cast<int>(fabsf(time)); + int hours = seconds / (60 * 60); + int minutes = (seconds / 60) % 60; + seconds %= 60; + + // duration defines the format of how the time is rendered + int durationSecs = static_cast<int>(fabsf(duration)); + int durationHours = durationSecs / (60 * 60); + int durationMins = (durationSecs / 60) % 60; + + if (durationHours || hours) + return String::format("%s%01d:%02d:%02d", (time < 0 ? "-" : ""), hours, minutes, seconds); + if (durationMins > 9) + return String::format("%s%02d:%02d", (time < 0 ? "-" : ""), minutes, seconds); + + return String::format("%s%01d:%02d", (time < 0 ? "-" : ""), minutes, seconds); +} + +String RenderMediaControls::formatMediaControlsTime(float time) +{ + return formatChromiumMediaControlsTime(time, time); +} + +String RenderMediaControls::formatMediaControlsCurrentTime(float currentTime, float duration) +{ + return formatChromiumMediaControlsTime(currentTime, duration); +} + +} // namespace WebCore |