1/*
2 * Copyright (C) 2009, 2013 Apple Inc. All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27
28#if ENABLE(VIDEO)
29
30#include "RenderMediaControls.h"
31
32#include "GraphicsContext.h"
33#include "HTMLMediaElement.h"
34#include "HTMLNames.h"
35#include "PaintInfo.h"
36#include "RenderTheme.h"
37
38// FIXME: Unify more of the code for Mac and Win.
39#if PLATFORM(WIN) && USE(CG)
40
41#include <CoreGraphics/CoreGraphics.h>
42#include <WebKitSystemInterface/WebKitSystemInterface.h>
43
44// The Windows version of WKSI defines these functions as capitalized, while the Mac version defines them as lower case.
45// FIXME: Is this necessary anymore?
46inline bool wkHitTestMediaUIPart(int part, const CGRect& bounds, const CGPoint& point)
47{
48    WKHitTestMediaUIPart(part, bounds, point);
49}
50
51inline void wkMeasureMediaUIPart(int part, CGRect* bounds, CGSize* naturalSize)
52{
53    WKMeasureMediaUIPart(part, bounds, naturalSize);
54}
55
56inline void wkDrawMediaUIPart(int part, CGContextRef context, const CGRect& rect, unsigned state)
57{
58    WKDrawMediaUIPart(part, context, rect, state);
59}
60
61inline void wkDrawMediaSliderTrack(CGContextRef context, const CGRect& rect, float timeLoaded, float currentTime, float duration, unsigned state)
62{
63    WKDrawMediaSliderTrack(context, rect, timeLoaded, currentTime, duration, state);
64}
65
66#endif
67
68
69namespace WebCore {
70
71#if PLATFORM(WIN) && USE(CG)
72
73static WKMediaControllerThemeState determineState(const RenderObject& o)
74{
75    int result = 0;
76    const RenderTheme& theme = o.theme();
77    if (!theme.isEnabled(o) || theme.isReadOnlyControl(o))
78        result |= WKMediaControllerFlagDisabled;
79    if (theme.isPressed(o))
80        result |= WKMediaControllerFlagPressed;
81    if (theme.isFocused(o))
82        result |= WKMediaControllerFlagFocused;
83    return static_cast<WKMediaControllerThemeState>(result);
84}
85
86// Utility to scale when the UI part are not scaled by wkDrawMediaUIPart
87static FloatRect getUnzoomedRectAndAdjustCurrentContext(const RenderObject& o, const PaintInfo& paintInfo, const IntRect &originalRect)
88{
89    float zoomLevel = o.style().effectiveZoom();
90    FloatRect unzoomedRect(originalRect);
91    if (zoomLevel != 1.0f) {
92        unzoomedRect.setWidth(unzoomedRect.width() / zoomLevel);
93        unzoomedRect.setHeight(unzoomedRect.height() / zoomLevel);
94        paintInfo.context->translate(unzoomedRect.x(), unzoomedRect.y());
95        paintInfo.context->scale(FloatSize(zoomLevel, zoomLevel));
96        paintInfo.context->translate(-unzoomedRect.x(), -unzoomedRect.y());
97    }
98    return unzoomedRect;
99}
100
101static const int mediaSliderThumbWidth = 13;
102static const int mediaSliderThumbHeight = 14;
103
104void RenderMediaControls::adjustMediaSliderThumbSize(RenderStyle& style)
105{
106    int part;
107    switch (style.appearance()) {
108    case MediaSliderThumbPart:
109        part = MediaSliderThumb;
110        break;
111    case MediaVolumeSliderThumbPart:
112        part = MediaVolumeSliderThumb;
113        break;
114    case MediaFullScreenVolumeSliderThumbPart:
115        part = MediaFullScreenVolumeSliderThumb;
116        break;
117    default:
118        return;
119    }
120
121    CGSize size;
122    wkMeasureMediaUIPart(part, 0, &size);
123
124    float zoomLevel = style.effectiveZoom();
125    style.setWidth(Length(static_cast<int>(size.width * zoomLevel), Fixed));
126    style.setHeight(Length(static_cast<int>(size.height * zoomLevel), Fixed));
127}
128
129bool RenderMediaControls::paintMediaControlsPart(MediaControlElementType part, const RenderObject& o, const PaintInfo& paintInfo, const IntRect& r)
130{
131    GraphicsContextStateSaver stateSaver(*paintInfo.context);
132
133    switch (part) {
134    case MediaEnterFullscreenButton:
135    case MediaExitFullscreenButton:
136        if (MediaControlFullscreenButtonElement* btn = static_cast<MediaControlFullscreenButtonElement*>(o.node())) {
137            bool enterButton = btn->displayType() == MediaEnterFullscreenButton;
138            wkDrawMediaUIPart(enterButton ? WKMediaUIPartFullscreenButton : WKMediaUIPartExitFullscreenButton, paintInfo.context->platformContext(), r, determineState(o));
139        }
140        break;
141    case MediaShowClosedCaptionsButton:
142    case MediaHideClosedCaptionsButton:
143        if (MediaControlToggleClosedCaptionsButtonElement* btn = static_cast<MediaControlToggleClosedCaptionsButtonElement*>(o.node())) {
144            bool captionsVisible = btn->displayType() == MediaHideClosedCaptionsButton;
145            wkDrawMediaUIPart(captionsVisible ? WKMediaUIPartHideClosedCaptionsButton : WKMediaUIPartShowClosedCaptionsButton, paintInfo.context->platformContext(), r, determineState(o));
146        }
147        break;
148    case MediaMuteButton:
149    case MediaUnMuteButton:
150        if (MediaControlMuteButtonElement* btn = static_cast<MediaControlMuteButtonElement*>(o.node())) {
151            bool audioEnabled = btn->displayType() == MediaMuteButton;
152            wkDrawMediaUIPart(audioEnabled ? WKMediaUIPartMuteButton : WKMediaUIPartUnMuteButton, paintInfo.context->platformContext(), r, determineState(o));
153        }
154        break;
155    case MediaPauseButton:
156    case MediaPlayButton:
157        if (MediaControlPlayButtonElement* btn = static_cast<MediaControlPlayButtonElement*>(o.node())) {
158            bool canPlay = btn->displayType() == MediaPlayButton;
159            wkDrawMediaUIPart(canPlay ? WKMediaUIPartPlayButton : WKMediaUIPartPauseButton, paintInfo.context->platformContext(), r, determineState(o));
160        }
161        break;
162    case MediaRewindButton:
163        wkDrawMediaUIPart(WKMediaUIPartRewindButton, paintInfo.context->platformContext(), r, determineState(o));
164        break;
165    case MediaReturnToRealtimeButton:
166        wkDrawMediaUIPart(WKMediaUIPartSeekToRealtimeButton, paintInfo.context->platformContext(), r, determineState(o));
167        break;
168    case MediaSeekBackButton:
169        wkDrawMediaUIPart(WKMediaUIPartSeekBackButton, paintInfo.context->platformContext(), r, determineState(o));
170        break;
171    case MediaSeekForwardButton:
172        wkDrawMediaUIPart(WKMediaUIPartSeekForwardButton, paintInfo.context->platformContext(), r, determineState(o));
173        break;
174    case MediaSlider: {
175        if (HTMLMediaElement* mediaElement = parentMediaElement(o)) {
176            FloatRect unzoomedRect = getUnzoomedRectAndAdjustCurrentContext(o, paintInfo, r);
177            wkDrawMediaSliderTrack(paintInfo.context->platformContext(), unzoomedRect, mediaElement->percentLoaded() * mediaElement->duration(), mediaElement->currentTime(), mediaElement->duration(), determineState(o));
178        }
179        break;
180    }
181    case MediaSliderThumb:
182        wkDrawMediaUIPart(WKMediaUIPartTimelineSliderThumb, paintInfo.context->platformContext(), r, determineState(o));
183        break;
184    case MediaVolumeSliderContainer:
185        wkDrawMediaUIPart(WKMediaUIPartVolumeSliderContainer, paintInfo.context->platformContext(), r, determineState(o));
186        break;
187    case MediaVolumeSlider:
188        wkDrawMediaUIPart(WKMediaUIPartVolumeSlider, paintInfo.context->platformContext(), r, determineState(o));
189        break;
190    case MediaVolumeSliderThumb:
191        wkDrawMediaUIPart(WKMediaUIPartVolumeSliderThumb, paintInfo.context->platformContext(), r, determineState(o));
192        break;
193    case MediaFullScreenVolumeSlider:
194        wkDrawMediaUIPart(WKMediaUIPartFullScreenVolumeSlider, paintInfo.context->platformContext(), r, determineState(o));
195        break;
196    case MediaFullScreenVolumeSliderThumb:
197        wkDrawMediaUIPart(WKMediaUIPartFullScreenVolumeSliderThumb, paintInfo.context->platformContext(), r, determineState(o));
198        break;
199    case MediaTimelineContainer:
200        wkDrawMediaUIPart(WKMediaUIPartBackground, paintInfo.context->platformContext(), r, determineState(o));
201        break;
202    case MediaCurrentTimeDisplay:
203        ASSERT_NOT_REACHED();
204        break;
205    case MediaTimeRemainingDisplay:
206        ASSERT_NOT_REACHED();
207        break;
208    case MediaControlsPanel:
209        ASSERT_NOT_REACHED();
210    case MediaTextTrackDisplayContainer:
211    case MediaTextTrackDisplay:
212    case MediaClosedCaptionsContainer:
213    case MediaClosedCaptionsTrackList:
214        ASSERT_NOT_REACHED();
215        break;
216    }
217
218    return false;
219}
220
221#endif
222
223IntPoint RenderMediaControls::volumeSliderOffsetFromMuteButton(RenderBox* muteButtonBox, const IntSize& size)
224{
225    static const int xOffset = -4;
226    static const int yOffset = 5;
227
228    float zoomLevel = muteButtonBox->style().effectiveZoom();
229    int y = yOffset * zoomLevel + muteButtonBox->pixelSnappedOffsetHeight() - size.height();
230    FloatPoint absPoint = muteButtonBox->localToAbsolute(FloatPoint(muteButtonBox->pixelSnappedOffsetLeft(), y), IsFixed | UseTransforms);
231    if (absPoint.y() < 0)
232        y = muteButtonBox->pixelSnappedHeight();
233    return IntPoint(xOffset * zoomLevel, y);
234}
235
236}
237
238#endif
239