[BlackBerry] Some media controls are mispositioned for dynamic live streams (HLS)
[WebKit-https.git] / Source / WebCore / platform / blackberry / RenderThemeBlackBerry.cpp
index d95984dc6cde34bc048848f40ce5eafa76544bc8..85b2e175f88070f9abe7b2e1d3461bdfd5475e99 100644 (file)
 
 #include "CSSValueKeywords.h"
 #include "Frame.h"
+#include "HTMLMediaElement.h"
+#include "HostWindow.h"
 #include "MediaControlElements.h"
 #include "MediaPlayerPrivateBlackBerry.h"
+#include "Page.h"
 #include "PaintInfo.h"
+#include "RenderFullScreen.h"
 #include "RenderProgress.h"
 #include "RenderSlider.h"
 #include "RenderView.h"
@@ -37,12 +41,13 @@ namespace WebCore {
 const unsigned smallRadius = 1;
 const unsigned largeRadius = 3;
 const unsigned lineWidth = 1;
-const int marginSize = 4;
-const int mediaSliderThumbWidth = 40;
-const int mediaSliderThumbHeight = 13;
-const int mediaSliderThumbRadius = 5;
-const int sliderThumbWidth = 15;
-const int sliderThumbHeight = 25;
+const float marginSize = 4;
+const float mediaControlsHeight = 32;
+const float mediaSliderThumbWidth = 40;
+const float mediaSliderThumbHeight = 13;
+const float mediaSliderThumbRadius = 5;
+const float sliderThumbWidth = 15;
+const float sliderThumbHeight = 25;
 
 // Checkbox check scalers
 const float checkboxLeftX = 7 / 40.0;
@@ -58,6 +63,8 @@ const float radioButtonCheckStateScaler = 7 / 30.0;
 
 // Multipliers
 const unsigned paddingDivisor = 5;
+const unsigned fullScreenEnlargementFactor = 2;
+const float scaleFactorThreshold = 2.0;
 
 // Colors
 const RGBA32 caretBottom = 0xff2163bf;
@@ -144,6 +151,37 @@ static Path roundedRectForBorder(RenderObject* object, const IntRect& rect)
     return roundedRect;
 }
 
+static RenderSlider* determineRenderSlider(RenderObject* object)
+{
+    ASSERT(object->isSliderThumb());
+    // The RenderSlider is an ancestor of the slider thumb.
+    while (object && !object->isSlider())
+        object = object->parent();
+    return toRenderSlider(object);
+}
+
+static float determineFullScreenMultiplier(Element* element)
+{
+    float fullScreenMultiplier = 1.0;
+#if ENABLE(FULLSCREEN_API) && ENABLE(VIDEO)
+    if (element && element->document()->webkitIsFullScreen() && element->document()->webkitCurrentFullScreenElement() == toParentMediaElement(element)) {
+        if (element->document()->page()->deviceScaleFactor() < scaleFactorThreshold)
+            fullScreenMultiplier = fullScreenEnlargementFactor;
+
+        // The way the BlackBerry port implements the FULLSCREEN_API for media elements
+        // might result in the controls being oversized, proportionally to the current page
+        // scale. That happens because the fullscreen element gets sized to be as big as the
+        // viewport size, and the viewport size might get outstretched to fit to the screen dimensions.
+        // To fix that, lets strips out the Page scale factor from the media controls multiplier.
+        float scaleFactor = element->document()->view()->hostWindow()->platformPageClient()->currentZoomFactor();
+        static ViewportArguments defaultViewportArguments;
+        float scaleFactorFudge = 1 / element->document()->page()->deviceScaleFactor();
+        fullScreenMultiplier /= scaleFactor * scaleFactorFudge;
+    }
+#endif
+    return fullScreenMultiplier;
+}
+
 PassRefPtr<RenderTheme> RenderTheme::themeForPage(Page* page)
 {
     static RenderTheme* theme = RenderThemeBlackBerry::create().leakRef();
@@ -214,13 +252,13 @@ void RenderThemeBlackBerry::setButtonStyle(RenderStyle* style) const
     style->setPaddingBottom(vertPadding);
 }
 
-void RenderThemeBlackBerry::adjustButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+void RenderThemeBlackBerry::adjustButtonStyle(StyleResolver*, RenderStyle* style, Element*) const
 {
     setButtonStyle(style);
     style->setCursor(CURSOR_WEBKIT_GRAB);
 }
 
-void RenderThemeBlackBerry::adjustTextAreaStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+void RenderThemeBlackBerry::adjustTextAreaStyle(StyleResolver*, RenderStyle* style, Element*) const
 {
     setButtonStyle(style);
 }
@@ -230,7 +268,7 @@ bool RenderThemeBlackBerry::paintTextArea(RenderObject* object, const PaintInfo&
     return paintTextFieldOrTextAreaOrSearchField(object, info, rect);
 }
 
-void RenderThemeBlackBerry::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+void RenderThemeBlackBerry::adjustTextFieldStyle(StyleResolver*, RenderStyle* style, Element*) const
 {
     setButtonStyle(style);
 }
@@ -256,7 +294,8 @@ bool RenderThemeBlackBerry::paintTextFieldOrTextAreaOrSearchField(RenderObject*
     if (object->style()->appearance() == SearchFieldPart) {
         // We force the fill color to White so as to match the background color of the search cancel button graphic.
         context->setFillColor(Color::white, ColorSpaceDeviceRGB);
-        context->drawPath(textFieldRoundedRectangle);
+        context->fillPath(textFieldRoundedRectangle);
+        context->strokePath(textFieldRoundedRectangle);
     } else
         context->strokePath(textFieldRoundedRectangle);
     context->restore();
@@ -268,12 +307,12 @@ bool RenderThemeBlackBerry::paintTextField(RenderObject* object, const PaintInfo
     return paintTextFieldOrTextAreaOrSearchField(object, info, rect);
 }
 
-void RenderThemeBlackBerry::adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+void RenderThemeBlackBerry::adjustSearchFieldStyle(StyleResolver*, RenderStyle* style, Element*) const
 {
     setButtonStyle(style);
 }
 
-void RenderThemeBlackBerry::adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+void RenderThemeBlackBerry::adjustSearchFieldCancelButtonStyle(StyleResolver*, RenderStyle* style, Element*) const
 {
     static const float defaultControlFontPixelSize = 13;
     static const float defaultCancelButtonSize = 9;
@@ -318,7 +357,7 @@ bool RenderThemeBlackBerry::paintSearchFieldCancelButton(RenderObject* object, c
     return false;
 }
 
-void RenderThemeBlackBerry::adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+void RenderThemeBlackBerry::adjustMenuListButtonStyle(StyleResolver*, RenderStyle* style, Element*) const
 {
     // These seem to be reasonable padding values from observation.
     const int paddingLeft = 8;
@@ -405,7 +444,8 @@ bool RenderThemeBlackBerry::paintButton(RenderObject* object, const PaintInfo& i
         FloatSize smallCorner(smallRadius, smallRadius);
         Path path;
         path.addRoundedRect(rect, smallCorner);
-        info.context->drawPath(path);
+        info.context->fillPath(path);
+        info.context->strokePath(path);
 
         if (isChecked(object)) {
             Path checkPath;
@@ -417,7 +457,8 @@ bool RenderThemeBlackBerry::paintButton(RenderObject* object, const PaintInfo& i
             info.context->setLineCap(RoundCap);
             info.context->setStrokeColor(blackPen, ColorSpaceDeviceRGB);
             info.context->setStrokeThickness(rect2.width() / checkboxStrokeThickness);
-            info.context->drawPath(checkPath);
+            info.context->fillPath(checkPath);
+            info.context->strokePath(checkPath);
         }
         break;
     }
@@ -436,13 +477,15 @@ bool RenderThemeBlackBerry::paintButton(RenderObject* object, const PaintInfo& i
         FloatSize largeCorner(largeRadius, largeRadius);
         Path path;
         path.addRoundedRect(rect, largeCorner);
-        info.context->drawPath(path);
+        info.context->fillPath(path);
+        info.context->strokePath(path);
         break;
     }
     case SquareButtonPart: {
         Path path;
         path.addRect(rect);
-        info.context->drawPath(path);
+        info.context->fillPath(path);
+        info.context->strokePath(path);
         break;
     }
     default:
@@ -454,12 +497,12 @@ bool RenderThemeBlackBerry::paintButton(RenderObject* object, const PaintInfo& i
     return false;
 }
 
-void RenderThemeBlackBerry::adjustMenuListStyle(CSSStyleSelector* css, RenderStyle* style, Element* element) const
+void RenderThemeBlackBerry::adjustMenuListStyle(StyleResolver* css, RenderStyle* style, Element* element) const
 {
     adjustMenuListButtonStyle(css, style, element);
 }
 
-void RenderThemeBlackBerry::adjustCheckboxStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+void RenderThemeBlackBerry::adjustCheckboxStyle(StyleResolver*, RenderStyle* style, Element*) const
 {
     setCheckboxSize(style);
     style->setBoxShadow(nullptr);
@@ -469,7 +512,7 @@ void RenderThemeBlackBerry::adjustCheckboxStyle(CSSStyleSelector*, RenderStyle*
     style->setCursor(CURSOR_WEBKIT_GRAB);
 }
 
-void RenderThemeBlackBerry::adjustRadioStyle(CSSStyleSelector*, RenderStyle* style, Element*) const
+void RenderThemeBlackBerry::adjustRadioStyle(StyleResolver*, RenderStyle* style, Element*) const
 {
     setRadioSize(style);
     style->setBoxShadow(nullptr);
@@ -596,15 +639,23 @@ bool RenderThemeBlackBerry::paintMenuListButton(RenderObject* object, const Pain
     return false;
 }
 
-void RenderThemeBlackBerry::adjustSliderThumbSize(RenderStyle* style) const
+void RenderThemeBlackBerry::adjustSliderThumbSize(RenderStyle* style, Element* element) const
 {
+    float fullScreenMultiplier = 1;
     ControlPart part = style->appearance();
+
+    if (part == MediaSliderThumbPart || part == MediaVolumeSliderThumbPart) {
+        RenderSlider* slider = determineRenderSlider(element->renderer());
+        if (slider)
+            fullScreenMultiplier = determineFullScreenMultiplier(toElement(slider->node()));
+    }
+
     if (part == MediaVolumeSliderThumbPart || part == SliderThumbHorizontalPart || part == SliderThumbVerticalPart) {
-        style->setWidth(Length(part == SliderThumbVerticalPart ? sliderThumbHeight : sliderThumbWidth, Fixed));
-        style->setHeight(Length(part == SliderThumbVerticalPart ? sliderThumbWidth : sliderThumbHeight, Fixed));
+        style->setWidth(Length((part == SliderThumbVerticalPart ? sliderThumbHeight : sliderThumbWidth) * fullScreenMultiplier, Fixed));
+        style->setHeight(Length((part == SliderThumbVerticalPart ? sliderThumbWidth : sliderThumbHeight) * fullScreenMultiplier, Fixed));
     } else if (part == MediaSliderThumbPart) {
-        style->setWidth(Length(mediaSliderThumbWidth, Fixed));
-        style->setHeight(Length(mediaSliderThumbHeight, Fixed));
+        style->setWidth(Length(mediaSliderThumbWidth * fullScreenMultiplier, Fixed));
+        style->setHeight(Length(mediaSliderThumbHeight * fullScreenMultiplier, Fixed));
     }
 }
 
@@ -709,6 +760,89 @@ bool RenderThemeBlackBerry::paintSliderThumb(RenderObject* object, const PaintIn
     return false;
 }
 
+void RenderThemeBlackBerry::adjustMediaControlStyle(StyleResolver*, RenderStyle* style, Element* element) const
+{
+    float fullScreenMultiplier = determineFullScreenMultiplier(element);
+    HTMLMediaElement* mediaElement = toParentMediaElement(element);
+    if (!mediaElement)
+        return;
+
+    // We use multiples of mediaControlsHeight to make all objects scale evenly
+    Length zero(0, Fixed);
+    Length controlsHeight(mediaControlsHeight * fullScreenMultiplier, Fixed);
+    Length timeWidth(mediaControlsHeight * 3 / 2 * fullScreenMultiplier, Fixed);
+    Length volumeHeight(mediaControlsHeight * 4 * fullScreenMultiplier, Fixed);
+    Length padding(mediaControlsHeight / 8 * fullScreenMultiplier, Fixed);
+    float fontSize = mediaControlsHeight / 2 * fullScreenMultiplier;
+
+    switch (style->appearance()) {
+    case MediaPlayButtonPart:
+    case MediaEnterFullscreenButtonPart:
+    case MediaExitFullscreenButtonPart:
+    case MediaMuteButtonPart:
+        style->setWidth(controlsHeight);
+        style->setHeight(controlsHeight);
+        break;
+    case MediaCurrentTimePart:
+    case MediaTimeRemainingPart:
+        style->setWidth(timeWidth);
+        style->setHeight(controlsHeight);
+        style->setPaddingRight(padding);
+        style->setBlendedFontSize(fontSize);
+        break;
+    case MediaVolumeSliderContainerPart:
+        style->setWidth(controlsHeight);
+        style->setHeight(volumeHeight);
+        style->setBottom(controlsHeight);
+        break;
+    default:
+        break;
+    }
+
+    if (!isfinite(mediaElement->duration())) {
+        // Live streams have infinite duration with no timeline. Force the mute
+        // and fullscreen buttons to the right. This is needed when webkit does
+        // not render the timeline container because it has a webkit-box-flex
+        // of 1 and normally allows those buttons to be on the right.
+        switch (style->appearance()) {
+        case MediaEnterFullscreenButtonPart:
+        case MediaExitFullscreenButtonPart:
+            style->setPosition(AbsolutePosition);
+            style->setBottom(zero);
+            style->setRight(controlsHeight);
+            break;
+        case MediaMuteButtonPart:
+            style->setPosition(AbsolutePosition);
+            style->setBottom(zero);
+            style->setRight(zero);
+            break;
+        default:
+            break;
+        }
+    }
+}
+
+void RenderThemeBlackBerry::adjustSliderTrackStyle(StyleResolver*, RenderStyle* style, Element* element) const
+{
+    float fullScreenMultiplier = determineFullScreenMultiplier(element);
+
+    // We use multiples of mediaControlsHeight to make all objects scale evenly
+    Length controlsHeight(mediaControlsHeight * fullScreenMultiplier, Fixed);
+    Length volumeHeight(mediaControlsHeight * 4 * fullScreenMultiplier, Fixed);
+    switch (style->appearance()) {
+    case MediaSliderPart:
+        style->setHeight(controlsHeight);
+        break;
+    case MediaVolumeSliderPart:
+        style->setWidth(controlsHeight);
+        style->setHeight(volumeHeight);
+        break;
+    case MediaFullScreenVolumeSliderPart:
+    default:
+        break;
+    }
+}
+
 static bool paintMediaButton(GraphicsContext* context, const IntRect& rect, Image* image)
 {
     context->drawImage(image, ColorSpaceDeviceRGB, rect);
@@ -758,12 +892,19 @@ bool RenderThemeBlackBerry::paintMediaMuteButton(RenderObject* object, const Pai
 bool RenderThemeBlackBerry::paintMediaFullscreenButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
 {
 #if ENABLE(VIDEO)
-    if (!toParentMediaElement(object))
+    HTMLMediaElement* mediaElement = toParentMediaElement(object);
+    if (!mediaElement)
         return false;
 
-    static Image* mediaFullscreen = Image::loadPlatformResource("fullscreen").leakRef();
+    static Image* mediaEnterFullscreen = Image::loadPlatformResource("fullscreen").leakRef();
+    static Image* mediaExitFullscreen = Image::loadPlatformResource("exit_fullscreen").leakRef();
 
-    return paintMediaButton(paintInfo.context, rect, mediaFullscreen);
+    Image* buttonImage = mediaEnterFullscreen;
+#if ENABLE(FULLSCREEN_API)
+    if (mediaElement->document()->webkitIsFullScreen() && mediaElement->document()->webkitCurrentFullScreenElement() == mediaElement)
+        buttonImage = mediaExitFullscreen;
+#endif
+    return paintMediaButton(paintInfo.context, rect, buttonImage);
 #else
     UNUSED_PARAM(object);
     UNUSED_PARAM(paintInfo);
@@ -775,28 +916,27 @@ bool RenderThemeBlackBerry::paintMediaFullscreenButton(RenderObject* object, con
 bool RenderThemeBlackBerry::paintMediaSliderTrack(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
 {
 #if ENABLE(VIDEO)
-    IntRect rect2(rect.x() + 3, rect.y() + 14, rect.width() - 6, 2);
-
     HTMLMediaElement* mediaElement = toParentMediaElement(object);
-
     if (!mediaElement)
         return false;
 
+    float fullScreenMultiplier = determineFullScreenMultiplier(mediaElement);
     float loaded = 0;
     // FIXME: replace loaded with commented out one when buffer bug is fixed (see comment in
     // MediaPlayerPrivateMMrenderer::percentLoaded).
     // loaded = mediaElement->percentLoaded();
-    if (mediaElement->player())
+    if (mediaElement->player() && mediaElement->player()->implementation())
         loaded = static_cast<MediaPlayerPrivate *>(mediaElement->player()->implementation())->percentLoaded();
     float position = mediaElement->duration() > 0 ? (mediaElement->currentTime() / mediaElement->duration()) : 0;
 
-    int x = rect.x() + 3;
-    int y = rect.y() + 14;
-    int w = rect.width() - 6;
-    int h = 2;
+    int x = ceil(rect.x() + 2 * fullScreenMultiplier - fullScreenMultiplier / 2);
+    int y = ceil(rect.y() + 14 * fullScreenMultiplier + fullScreenMultiplier / 2);
+    int w = ceil(rect.width() - 4 * fullScreenMultiplier + fullScreenMultiplier / 2);
+    int h = ceil(2 * fullScreenMultiplier);
+    IntRect rect2(x, y, w, h);
 
-    int wPlayed = (w * position);
-    int wLoaded = (w - mediaSliderThumbWidth) * loaded + mediaSliderThumbWidth;
+    int wPlayed = ceil(w * position);
+    int wLoaded = ceil((w - mediaSliderThumbWidth * fullScreenMultiplier) * loaded + mediaSliderThumbWidth * fullScreenMultiplier);
 
     IntRect played(x, y, wPlayed, h);
     IntRect buffered(x, y, wLoaded, h);
@@ -824,16 +964,15 @@ bool RenderThemeBlackBerry::paintMediaSliderTrack(RenderObject* object, const Pa
 bool RenderThemeBlackBerry::paintMediaSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
 {
 #if ENABLE(VIDEO)
-    if (!object->parent())
-        return false;
-
-    RenderSlider* slider = toRenderSlider(object->parent()->parent()->parent());
+    RenderSlider* slider = determineRenderSlider(object);
     if (!slider)
         return false;
 
+    float fullScreenMultiplier = determineFullScreenMultiplier(toElement(slider->node()));
+
     paintInfo.context->save();
     Path mediaThumbRoundedRectangle;
-    mediaThumbRoundedRectangle.addRoundedRect(rect, FloatSize(mediaSliderThumbRadius, mediaSliderThumbRadius));
+    mediaThumbRoundedRectangle.addRoundedRect(rect, FloatSize(mediaSliderThumbRadius * fullScreenMultiplier, mediaSliderThumbRadius * fullScreenMultiplier));
     paintInfo.context->setStrokeStyle(SolidStroke);
     paintInfo.context->setStrokeThickness(0.5);
     paintInfo.context->setStrokeColor(Color::black, ColorSpaceDeviceRGB);
@@ -899,14 +1038,13 @@ Color RenderThemeBlackBerry::platformFocusRingColor() const
 #if ENABLE(TOUCH_EVENTS)
 Color RenderThemeBlackBerry::platformTapHighlightColor() const
 {
-    // Same color as 'focusRingPen' + 80 of alpha channel.
-    return Color(163, 200, 254, 80);
+    return Color(0, 168, 223, 50);
 }
 #endif
 
 Color RenderThemeBlackBerry::platformActiveSelectionBackgroundColor() const
 {
-    return Color(selection);
+    return Color(0, 168, 223, 50);
 }
 
 double RenderThemeBlackBerry::animationRepeatIntervalForProgressBar(RenderProgress* renderProgress) const
@@ -965,4 +1103,14 @@ bool RenderThemeBlackBerry::paintProgressBar(RenderObject* object, const PaintIn
     return false;
 }
 
+Color RenderThemeBlackBerry::platformActiveTextSearchHighlightColor() const
+{
+    return Color(255, 150, 50); // Orange.
+}
+
+Color RenderThemeBlackBerry::platformInactiveTextSearchHighlightColor() const
+{
+    return Color(255, 255, 0); // Yellow.
+}
+
 } // namespace WebCore