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
37 namespace WebCore {
38
39 #if ENABLE(VIDEO)
40
41 typedef WTF::HashMap<const char*, Image*> MediaControlImageMap;
42 static MediaControlImageMap* gMediaControlImageMap = 0;
43
platformResource(const char * name)44 static 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
hasSource(const HTMLMediaElement * mediaElement)58 static bool hasSource(const HTMLMediaElement* mediaElement)
59 {
60 return mediaElement->networkState() != HTMLMediaElement::NETWORK_EMPTY
61 && mediaElement->networkState() != HTMLMediaElement::NETWORK_NO_SOURCE;
62 }
63
paintMediaButton(GraphicsContext * context,const IntRect & rect,Image * image)64 static 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
paintMediaMuteButton(RenderObject * object,const PaintInfo & paintInfo,const IntRect & rect)71 static 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
paintMediaPlayButton(RenderObject * object,const PaintInfo & paintInfo,const IntRect & rect)87 static 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
getMediaSliderThumb()103 static Image* getMediaSliderThumb()
104 {
105 static Image* mediaSliderThumb = platformResource("mediaSliderThumb");
106 return mediaSliderThumb;
107 }
108
paintMediaSlider(RenderObject * object,const PaintInfo & paintInfo,const IntRect & rect)109 static 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
paintMediaSliderThumb(RenderObject * object,const PaintInfo & paintInfo,const IntRect & rect)168 static 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
paintMediaVolumeSlider(RenderObject * object,const PaintInfo & paintInfo,const IntRect & rect)184 static 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
paintMediaVolumeSliderThumb(RenderObject * object,const PaintInfo & paintInfo,const IntRect & rect)203 static 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
paintMediaTimelineContainer(RenderObject * object,const PaintInfo & paintInfo,const IntRect & rect)212 static 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
paintMediaControlsPart(MediaControlElementType part,RenderObject * object,const PaintInfo & paintInfo,const IntRect & rect)245 bool 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
adjustMediaSliderThumbSize(RenderObject * object)283 void 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