1/*
2 * Copyright (C) 2009 Apple Inc.
3 * Copyright (C) 2009 Google Inc.
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include "config.h"
29#include "RenderMediaControlsChromium.h"
30
31#include "Gradient.h"
32#include "GraphicsContext.h"
33#include "HTMLMediaElement.h"
34#include "HTMLNames.h"
35#include "PaintInfo.h"
36
37namespace WebCore {
38
39#if ENABLE(VIDEO)
40
41typedef WTF::HashMap<const char*, Image*> MediaControlImageMap;
42static MediaControlImageMap* gMediaControlImageMap = 0;
43
44static Image* platformResource(const char* name)
45{
46    if (!gMediaControlImageMap)
47        gMediaControlImageMap = new MediaControlImageMap();
48    if (Image* image = gMediaControlImageMap->get(name))
49        return image;
50    if (Image* image = Image::loadPlatformResource(name).releaseRef()) {
51        gMediaControlImageMap->set(name, image);
52        return image;
53    }
54    ASSERT_NOT_REACHED();
55    return 0;
56}
57
58static bool hasSource(const HTMLMediaElement* mediaElement)
59{
60    return mediaElement->networkState() != HTMLMediaElement::NETWORK_EMPTY
61        && mediaElement->networkState() != HTMLMediaElement::NETWORK_NO_SOURCE;
62}
63
64static bool paintMediaButton(GraphicsContext* context, const IntRect& rect, Image* image)
65{
66    IntRect imageRect = image->rect();
67    context->drawImage(image, ColorSpaceDeviceRGB, rect);
68    return true;
69}
70
71static bool paintMediaMuteButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
72{
73    HTMLMediaElement* mediaElement = toParentMediaElement(object);
74    if (!mediaElement)
75      return false;
76
77    static Image* soundFull = platformResource("mediaSoundFull");
78    static Image* soundNone = platformResource("mediaSoundNone");
79    static Image* soundDisabled = platformResource("mediaSoundDisabled");
80
81    if (!hasSource(mediaElement) || !mediaElement->hasAudio())
82        return paintMediaButton(paintInfo.context, rect, soundDisabled);
83
84    return paintMediaButton(paintInfo.context, rect, mediaElement->muted() ? soundNone: soundFull);
85}
86
87static bool paintMediaPlayButton(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
88{
89    HTMLMediaElement* mediaElement = toParentMediaElement(object);
90    if (!mediaElement)
91        return false;
92
93    static Image* mediaPlay = platformResource("mediaPlay");
94    static Image* mediaPause = platformResource("mediaPause");
95    static Image* mediaPlayDisabled = platformResource("mediaPlayDisabled");
96
97    if (!hasSource(mediaElement))
98        return paintMediaButton(paintInfo.context, rect, mediaPlayDisabled);
99
100    return paintMediaButton(paintInfo.context, rect, mediaElement->canPlay() ? mediaPlay : mediaPause);
101}
102
103static Image* getMediaSliderThumb()
104{
105    static Image* mediaSliderThumb = platformResource("mediaSliderThumb");
106    return mediaSliderThumb;
107}
108
109static bool paintMediaSlider(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
110{
111    HTMLMediaElement* mediaElement = toParentMediaElement(object);
112    if (!mediaElement)
113        return false;
114
115    RenderStyle* style = object->style();
116    GraphicsContext* context = paintInfo.context;
117
118    // Draw the border of the time bar.
119    // FIXME: this should be a rounded rect but need to fix GraphicsContextSkia first.
120    // https://bugs.webkit.org/show_bug.cgi?id=30143
121    context->save();
122    context->setShouldAntialias(true);
123    context->setStrokeStyle(SolidStroke);
124    context->setStrokeColor(style->visitedDependentColor(CSSPropertyBorderLeftColor), ColorSpaceDeviceRGB);
125    context->setStrokeThickness(style->borderLeftWidth());
126    context->setFillColor(style->visitedDependentColor(CSSPropertyBackgroundColor), ColorSpaceDeviceRGB);
127    context->drawRect(rect);
128    context->restore();
129
130    // Draw the buffered ranges.
131    // FIXME: Draw multiple ranges if there are multiple buffered ranges.
132    IntRect bufferedRect = rect;
133    bufferedRect.inflate(-style->borderLeftWidth());
134
135    double bufferedWidth = 0.0;
136    if (mediaElement->percentLoaded() > 0.0) {
137        // Account for the width of the slider thumb.
138        Image* mediaSliderThumb = getMediaSliderThumb();
139        double thumbWidth = mediaSliderThumb->width() / 2.0 + 1.0;
140        double rectWidth = bufferedRect.width() - thumbWidth;
141        if (rectWidth < 0.0)
142            rectWidth = 0.0;
143        bufferedWidth = rectWidth * mediaElement->percentLoaded() + thumbWidth;
144    }
145    bufferedRect.setWidth(bufferedWidth);
146
147    // Don't bother drawing an empty area.
148    if (!bufferedRect.isEmpty()) {
149        IntPoint sliderTopLeft = bufferedRect.location();
150        IntPoint sliderTopRight = sliderTopLeft;
151        sliderTopRight.move(0, bufferedRect.height());
152
153        RefPtr<Gradient> gradient = Gradient::create(sliderTopLeft, sliderTopRight);
154        Color startColor = object->style()->visitedDependentColor(CSSPropertyColor);
155        gradient->addColorStop(0.0, startColor);
156        gradient->addColorStop(1.0, Color(startColor.red() / 2, startColor.green() / 2, startColor.blue() / 2, startColor.alpha()));
157
158        context->save();
159        context->setStrokeStyle(NoStroke);
160        context->setFillGradient(gradient);
161        context->fillRect(bufferedRect);
162        context->restore();
163    }
164
165    return true;
166}
167
168static bool paintMediaSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
169{
170    if (!object->parent()->isSlider())
171        return false;
172
173    HTMLMediaElement* mediaElement = toParentMediaElement(object->parent());
174    if (!mediaElement)
175        return false;
176
177    if (!hasSource(mediaElement))
178        return true;
179
180    Image* mediaSliderThumb = getMediaSliderThumb();
181    return paintMediaButton(paintInfo.context, rect, mediaSliderThumb);
182}
183
184static bool paintMediaVolumeSlider(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
185{
186    HTMLMediaElement* mediaElement = toParentMediaElement(object);
187    if (!mediaElement)
188        return false;
189
190    GraphicsContext* context = paintInfo.context;
191    Color originalColor = context->strokeColor();
192    if (originalColor != Color::white)
193        context->setStrokeColor(Color::white, ColorSpaceDeviceRGB);
194
195    int x = rect.x() + rect.width() / 2;
196    context->drawLine(IntPoint(x, rect.y()),  IntPoint(x, rect.y() + rect.height()));
197
198    if (originalColor != Color::white)
199        context->setStrokeColor(originalColor, ColorSpaceDeviceRGB);
200    return true;
201}
202
203static bool paintMediaVolumeSliderThumb(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
204{
205    if (!object->parent()->isSlider())
206        return false;
207
208    static Image* mediaVolumeSliderThumb = platformResource("mediaVolumeSliderThumb");
209    return paintMediaButton(paintInfo.context, rect, mediaVolumeSliderThumb);
210}
211
212static bool paintMediaTimelineContainer(RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
213{
214    HTMLMediaElement* mediaElement = toParentMediaElement(object);
215    if (!mediaElement)
216        return false;
217
218    if (!rect.isEmpty()) {
219        GraphicsContext* context = paintInfo.context;
220        Color originalColor = context->strokeColor();
221        float originalThickness = context->strokeThickness();
222        StrokeStyle originalStyle = context->strokeStyle();
223
224        context->setStrokeStyle(SolidStroke);
225
226        // Draw the left border using CSS defined width and color.
227        context->setStrokeThickness(object->style()->borderLeftWidth());
228        context->setStrokeColor(object->style()->visitedDependentColor(CSSPropertyBorderLeftColor).rgb(), ColorSpaceDeviceRGB);
229        context->drawLine(IntPoint(rect.x() + 1, rect.y()),
230                          IntPoint(rect.x() + 1, rect.y() + rect.height()));
231
232        // Draw the right border using CSS defined width and color.
233        context->setStrokeThickness(object->style()->borderRightWidth());
234        context->setStrokeColor(object->style()->visitedDependentColor(CSSPropertyBorderRightColor).rgb(), ColorSpaceDeviceRGB);
235        context->drawLine(IntPoint(rect.x() + rect.width() - 1, rect.y()),
236                          IntPoint(rect.x() + rect.width() - 1, rect.y() + rect.height()));
237
238        context->setStrokeColor(originalColor, ColorSpaceDeviceRGB);
239        context->setStrokeThickness(originalThickness);
240        context->setStrokeStyle(originalStyle);
241    }
242    return true;
243}
244
245bool RenderMediaControlsChromium::paintMediaControlsPart(MediaControlElementType part, RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect)
246{
247    switch (part) {
248    case MediaMuteButton:
249    case MediaUnMuteButton:
250        return paintMediaMuteButton(object, paintInfo, rect);
251    case MediaPauseButton:
252    case MediaPlayButton:
253        return paintMediaPlayButton(object, paintInfo, rect);
254    case MediaSlider:
255        return paintMediaSlider(object, paintInfo, rect);
256    case MediaSliderThumb:
257        return paintMediaSliderThumb(object, paintInfo, rect);
258    case MediaVolumeSlider:
259        return paintMediaVolumeSlider(object, paintInfo, rect);
260    case MediaVolumeSliderThumb:
261        return paintMediaVolumeSliderThumb(object, paintInfo, rect);
262    case MediaTimelineContainer:
263        return paintMediaTimelineContainer(object, paintInfo, rect);
264    case MediaVolumeSliderMuteButton:
265    case MediaFullscreenButton:
266    case MediaSeekBackButton:
267    case MediaSeekForwardButton:
268    case MediaVolumeSliderContainer:
269    case MediaCurrentTimeDisplay:
270    case MediaTimeRemainingDisplay:
271    case MediaControlsPanel:
272    case MediaRewindButton:
273    case MediaReturnToRealtimeButton:
274    case MediaStatusDisplay:
275    case MediaShowClosedCaptionsButton:
276    case MediaHideClosedCaptionsButton:
277        ASSERT_NOT_REACHED();
278        break;
279    }
280    return false;
281}
282
283void RenderMediaControlsChromium::adjustMediaSliderThumbSize(RenderObject* object)
284{
285    static Image* mediaSliderThumb = platformResource("mediaSliderThumb");
286    static Image* mediaVolumeSliderThumb = platformResource("mediaVolumeSliderThumb");
287
288    Image* thumbImage = 0;
289    if (object->style()->appearance() == MediaSliderThumbPart)
290        thumbImage = mediaSliderThumb;
291    else if (object->style()->appearance() == MediaVolumeSliderThumbPart)
292        thumbImage = mediaVolumeSliderThumb;
293
294    float zoomLevel = object->style()->effectiveZoom();
295    if (thumbImage) {
296        object->style()->setWidth(Length(static_cast<int>(thumbImage->width() * zoomLevel), Fixed));
297        object->style()->setHeight(Length(static_cast<int>(thumbImage->height() * zoomLevel), Fixed));
298    }
299}
300
301#endif  // #if ENABLE(VIDEO)
302
303} // namespace WebCore
304