summaryrefslogtreecommitdiff
path: root/chromium/third_party/WebKit/Source/core/rendering/RenderMediaControls.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/third_party/WebKit/Source/core/rendering/RenderMediaControls.cpp')
-rw-r--r--chromium/third_party/WebKit/Source/core/rendering/RenderMediaControls.cpp427
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