OLD | NEW |
| (Empty) |
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 "core/rendering/RenderMediaControlsChromium.h" | |
30 | |
31 #include "bindings/v8/ExceptionStatePlaceholder.h" | |
32 #include "core/html/HTMLMediaElement.h" | |
33 #include "core/html/TimeRanges.h" | |
34 #include "core/platform/graphics/Gradient.h" | |
35 #include "core/platform/graphics/GraphicsContext.h" | |
36 #include "core/rendering/PaintInfo.h" | |
37 | |
38 namespace WebCore { | |
39 | |
40 typedef WTF::HashMap<const char*, Image*> MediaControlImageMap; | |
41 static MediaControlImageMap* gMediaControlImageMap = 0; | |
42 | |
43 static Image* platformResource(const char* name) | |
44 { | |
45 if (!gMediaControlImageMap) | |
46 gMediaControlImageMap = new MediaControlImageMap(); | |
47 if (Image* image = gMediaControlImageMap->get(name)) | |
48 return image; | |
49 if (Image* image = Image::loadPlatformResource(name).leakRef()) { | |
50 gMediaControlImageMap->set(name, image); | |
51 return image; | |
52 } | |
53 ASSERT_NOT_REACHED(); | |
54 return 0; | |
55 } | |
56 | |
57 static bool hasSource(const HTMLMediaElement* mediaElement) | |
58 { | |
59 return mediaElement->networkState() != HTMLMediaElement::NETWORK_EMPTY | |
60 && mediaElement->networkState() != HTMLMediaElement::NETWORK_NO_SOURCE; | |
61 } | |
62 | |
63 static bool paintMediaButton(GraphicsContext* context, const IntRect& rect, Imag
e* image) | |
64 { | |
65 context->drawImage(image, rect); | |
66 return true; | |
67 } | |
68 | |
69 static bool paintMediaMuteButton(RenderObject* object, const PaintInfo& paintInf
o, const IntRect& rect) | |
70 { | |
71 HTMLMediaElement* mediaElement = toParentMediaElement(object); | |
72 if (!mediaElement) | |
73 return false; | |
74 | |
75 static Image* soundLevel3 = platformResource("mediaplayerSoundLevel3"); | |
76 static Image* soundLevel2 = platformResource("mediaplayerSoundLevel2"); | |
77 static Image* soundLevel1 = platformResource("mediaplayerSoundLevel1"); | |
78 static Image* soundLevel0 = platformResource("mediaplayerSoundLevel0"); | |
79 static Image* soundDisabled = platformResource("mediaplayerSoundDisabled"); | |
80 | |
81 if (!hasSource(mediaElement) || !mediaElement->hasAudio()) | |
82 return paintMediaButton(paintInfo.context, rect, soundDisabled); | |
83 | |
84 if (mediaElement->muted() || mediaElement->volume() <= 0) | |
85 return paintMediaButton(paintInfo.context, rect, soundLevel0); | |
86 | |
87 if (mediaElement->volume() <= 0.33) | |
88 return paintMediaButton(paintInfo.context, rect, soundLevel1); | |
89 | |
90 if (mediaElement->volume() <= 0.66) | |
91 return paintMediaButton(paintInfo.context, rect, soundLevel2); | |
92 | |
93 return paintMediaButton(paintInfo.context, rect, soundLevel3); | |
94 } | |
95 | |
96 static bool paintMediaPlayButton(RenderObject* object, const PaintInfo& paintInf
o, const IntRect& rect) | |
97 { | |
98 HTMLMediaElement* mediaElement = toParentMediaElement(object); | |
99 if (!mediaElement) | |
100 return false; | |
101 | |
102 static Image* mediaPlay = platformResource("mediaplayerPlay"); | |
103 static Image* mediaPause = platformResource("mediaplayerPause"); | |
104 static Image* mediaPlayDisabled = platformResource("mediaplayerPlayDisabled"
); | |
105 | |
106 if (!hasSource(mediaElement)) | |
107 return paintMediaButton(paintInfo.context, rect, mediaPlayDisabled); | |
108 | |
109 return paintMediaButton(paintInfo.context, rect, mediaElement->canPlay() ? m
ediaPlay : mediaPause); | |
110 } | |
111 | |
112 static bool paintMediaOverlayPlayButton(RenderObject* object, const PaintInfo& p
aintInfo, const IntRect& rect) | |
113 { | |
114 HTMLMediaElement* mediaElement = toParentMediaElement(object); | |
115 if (!mediaElement) | |
116 return false; | |
117 | |
118 if (!hasSource(mediaElement) || !mediaElement->canPlay()) | |
119 return false; | |
120 | |
121 static Image* mediaOverlayPlay = platformResource("mediaplayerOverlayPlay"); | |
122 return paintMediaButton(paintInfo.context, rect, mediaOverlayPlay); | |
123 } | |
124 | |
125 static Image* getMediaSliderThumb() | |
126 { | |
127 static Image* mediaSliderThumb = platformResource("mediaplayerSliderThumb"); | |
128 return mediaSliderThumb; | |
129 } | |
130 | |
131 static void paintRoundedSliderBackground(const IntRect& rect, const RenderStyle*
style, GraphicsContext* context) | |
132 { | |
133 int borderRadius = rect.height() / 2; | |
134 IntSize radii(borderRadius, borderRadius); | |
135 Color sliderBackgroundColor = Color(11, 11, 11); | |
136 context->save(); | |
137 context->fillRoundedRect(rect, radii, radii, radii, radii, sliderBackgroundC
olor); | |
138 context->restore(); | |
139 } | |
140 | |
141 static void paintSliderRangeHighlight(const IntRect& rect, const RenderStyle* st
yle, GraphicsContext* context, int startPosition, int endPosition, Color startCo
lor, Color endColor) | |
142 { | |
143 // Calculate border radius; need to avoid being smaller than half the slider
height | |
144 // because of https://bugs.webkit.org/show_bug.cgi?id=30143. | |
145 int borderRadius = rect.height() / 2; | |
146 IntSize radii(borderRadius, borderRadius); | |
147 | |
148 // Calculate highlight rectangle and edge dimensions. | |
149 int startOffset = startPosition; | |
150 int endOffset = rect.width() - endPosition; | |
151 int rangeWidth = endPosition - startPosition; | |
152 | |
153 if (rangeWidth <= 0) | |
154 return; | |
155 | |
156 // Make sure the range width is bigger than border radius at the edges to re
tain rounded corners. | |
157 if (startOffset < borderRadius && rangeWidth < borderRadius) | |
158 rangeWidth = borderRadius; | |
159 if (endOffset < borderRadius && rangeWidth < borderRadius) { | |
160 startPosition -= borderRadius - rangeWidth; | |
161 rangeWidth = borderRadius; | |
162 } | |
163 | |
164 // Set rectangle to highlight range. | |
165 IntRect highlightRect = rect; | |
166 highlightRect.move(startOffset, 0); | |
167 highlightRect.setWidth(rangeWidth); | |
168 | |
169 // Don't bother drawing an empty area. | |
170 if (highlightRect.isEmpty()) | |
171 return; | |
172 | |
173 // Calculate white-grey gradient. | |
174 IntPoint sliderTopLeft = highlightRect.location(); | |
175 IntPoint sliderBottomLeft = sliderTopLeft; | |
176 sliderBottomLeft.move(0, highlightRect.height()); | |
177 RefPtr<Gradient> gradient = Gradient::create(sliderTopLeft, sliderBottomLeft
); | |
178 gradient->addColorStop(0.0, startColor); | |
179 gradient->addColorStop(1.0, endColor); | |
180 | |
181 // Fill highlight rectangle with gradient, potentially rounded if on left or
right edge. | |
182 context->save(); | |
183 context->setFillGradient(gradient); | |
184 | |
185 if (startOffset < borderRadius && endOffset < borderRadius) | |
186 context->fillRoundedRect(highlightRect, radii, radii, radii, radii, star
tColor); | |
187 else if (startOffset < borderRadius) | |
188 context->fillRoundedRect(highlightRect, radii, IntSize(0, 0), radii, Int
Size(0, 0), startColor); | |
189 else if (endOffset < borderRadius) | |
190 context->fillRoundedRect(highlightRect, IntSize(0, 0), radii, IntSize(0,
0), radii, startColor); | |
191 else | |
192 context->fillRect(highlightRect); | |
193 | |
194 context->restore(); | |
195 } | |
196 | |
197 const int mediaSliderThumbWidth = 32; | |
198 | |
199 static bool paintMediaSlider(RenderObject* object, const PaintInfo& paintInfo, c
onst IntRect& rect) | |
200 { | |
201 HTMLMediaElement* mediaElement = toParentMediaElement(object); | |
202 if (!mediaElement) | |
203 return false; | |
204 | |
205 RenderStyle* style = object->style(); | |
206 GraphicsContext* context = paintInfo.context; | |
207 | |
208 paintRoundedSliderBackground(rect, style, context); | |
209 | |
210 // Draw the buffered range. Since the element may have multiple buffered ran
ges and it'd be | |
211 // distracting/'busy' to show all of them, show only the buffered range cont
aining the current play head. | |
212 RefPtr<TimeRanges> bufferedTimeRanges = mediaElement->buffered(); | |
213 float duration = mediaElement->duration(); | |
214 float currentTime = mediaElement->currentTime(); | |
215 if (std::isnan(duration) || std::isinf(duration) || !duration || std::isnan(
currentTime)) | |
216 return true; | |
217 | |
218 for (unsigned i = 0; i < bufferedTimeRanges->length(); ++i) { | |
219 float start = bufferedTimeRanges->start(i, ASSERT_NO_EXCEPTION); | |
220 float end = bufferedTimeRanges->end(i, ASSERT_NO_EXCEPTION); | |
221 if (std::isnan(start) || std::isnan(end) || start > currentTime || end <
currentTime) | |
222 continue; | |
223 int startPosition = int(start * rect.width() / duration); | |
224 int currentPosition = int(currentTime * rect.width() / duration); | |
225 int endPosition = int(end * rect.width() / duration); | |
226 | |
227 // Add half the thumb width proportionally adjusted to the current paint
ing position. | |
228 int thumbCenter = mediaSliderThumbWidth / 2; | |
229 int addWidth = thumbCenter * (1.0 - 2.0 * currentPosition / rect.width()
); | |
230 currentPosition += addWidth; | |
231 | |
232 // Draw white-ish highlight before current time. | |
233 Color startColor = Color(195, 195, 195); | |
234 Color endColor = Color(217, 217, 217); | |
235 if (currentPosition > startPosition) | |
236 paintSliderRangeHighlight(rect, style, context, startPosition, curre
ntPosition, startColor, endColor); | |
237 | |
238 // Draw grey-ish highlight after current time. | |
239 startColor = Color(60, 60, 60); | |
240 endColor = Color(76, 76, 76); | |
241 | |
242 if (endPosition > currentPosition) | |
243 paintSliderRangeHighlight(rect, style, context, currentPosition, end
Position, startColor, endColor); | |
244 | |
245 return true; | |
246 } | |
247 | |
248 return true; | |
249 } | |
250 | |
251 static bool paintMediaSliderThumb(RenderObject* object, const PaintInfo& paintIn
fo, const IntRect& rect) | |
252 { | |
253 ASSERT(object->node()); | |
254 HTMLMediaElement* mediaElement = toParentMediaElement(object->node()->shadow
Host()); | |
255 if (!mediaElement) | |
256 return false; | |
257 | |
258 if (!hasSource(mediaElement)) | |
259 return true; | |
260 | |
261 Image* mediaSliderThumb = getMediaSliderThumb(); | |
262 return paintMediaButton(paintInfo.context, rect, mediaSliderThumb); | |
263 } | |
264 | |
265 const int mediaVolumeSliderThumbWidth = 24; | |
266 | |
267 static bool paintMediaVolumeSlider(RenderObject* object, const PaintInfo& paintI
nfo, const IntRect& rect) | |
268 { | |
269 HTMLMediaElement* mediaElement = toParentMediaElement(object); | |
270 if (!mediaElement) | |
271 return false; | |
272 | |
273 GraphicsContext* context = paintInfo.context; | |
274 RenderStyle* style = object->style(); | |
275 | |
276 paintRoundedSliderBackground(rect, style, context); | |
277 | |
278 // Calculate volume position for white background rectangle. | |
279 float volume = mediaElement->volume(); | |
280 if (std::isnan(volume) || volume < 0) | |
281 return true; | |
282 if (volume > 1) | |
283 volume = 1; | |
284 if (!hasSource(mediaElement) || !mediaElement->hasAudio() || mediaElement->m
uted()) | |
285 volume = 0; | |
286 | |
287 // Calculate the position relative to the center of the thumb. | |
288 float fillWidth = 0; | |
289 if (volume > 0) { | |
290 float thumbCenter = mediaVolumeSliderThumbWidth / 2; | |
291 float zoomLevel = style->effectiveZoom(); | |
292 float positionWidth = volume * (rect.width() - (zoomLevel * thumbCenter)
); | |
293 fillWidth = positionWidth + (zoomLevel * thumbCenter / 2); | |
294 } | |
295 | |
296 Color startColor = Color(195, 195, 195); | |
297 Color endColor = Color(217, 217, 217); | |
298 | |
299 paintSliderRangeHighlight(rect, style, context, 0.0, fillWidth, startColor,
endColor); | |
300 | |
301 return true; | |
302 } | |
303 | |
304 static bool paintMediaVolumeSliderThumb(RenderObject* object, const PaintInfo& p
aintInfo, const IntRect& rect) | |
305 { | |
306 ASSERT(object->node()); | |
307 HTMLMediaElement* mediaElement = toParentMediaElement(object->node()->shadow
Host()); | |
308 if (!mediaElement) | |
309 return false; | |
310 | |
311 if (!hasSource(mediaElement) || !mediaElement->hasAudio()) | |
312 return true; | |
313 | |
314 static Image* mediaVolumeSliderThumb = platformResource("mediaplayerVolumeSl
iderThumb"); | |
315 return paintMediaButton(paintInfo.context, rect, mediaVolumeSliderThumb); | |
316 } | |
317 | |
318 static bool paintMediaFullscreenButton(RenderObject* object, const PaintInfo& pa
intInfo, const IntRect& rect) | |
319 { | |
320 HTMLMediaElement* mediaElement = toParentMediaElement(object); | |
321 if (!mediaElement) | |
322 return false; | |
323 | |
324 static Image* mediaFullscreenButton = platformResource("mediaplayerFullscree
n"); | |
325 return paintMediaButton(paintInfo.context, rect, mediaFullscreenButton); | |
326 } | |
327 | |
328 static bool paintMediaToggleClosedCaptionsButton(RenderObject* object, const Pai
ntInfo& paintInfo, const IntRect& rect) | |
329 { | |
330 HTMLMediaElement* mediaElement = toParentMediaElement(object); | |
331 if (!mediaElement) | |
332 return false; | |
333 | |
334 static Image* mediaClosedCaptionButton = platformResource("mediaplayerClosed
Caption"); | |
335 static Image* mediaClosedCaptionButtonDisabled = platformResource("mediaplay
erClosedCaptionDisabled"); | |
336 | |
337 if (mediaElement->webkitClosedCaptionsVisible()) | |
338 return paintMediaButton(paintInfo.context, rect, mediaClosedCaptionButto
n); | |
339 | |
340 return paintMediaButton(paintInfo.context, rect, mediaClosedCaptionButtonDis
abled); | |
341 } | |
342 | |
343 | |
344 bool RenderMediaControlsChromium::paintMediaControlsPart(MediaControlElementType
part, RenderObject* object, const PaintInfo& paintInfo, const IntRect& rect) | |
345 { | |
346 switch (part) { | |
347 case MediaMuteButton: | |
348 case MediaUnMuteButton: | |
349 return paintMediaMuteButton(object, paintInfo, rect); | |
350 case MediaPauseButton: | |
351 case MediaPlayButton: | |
352 return paintMediaPlayButton(object, paintInfo, rect); | |
353 case MediaShowClosedCaptionsButton: | |
354 return paintMediaToggleClosedCaptionsButton(object, paintInfo, rect); | |
355 case MediaSlider: | |
356 return paintMediaSlider(object, paintInfo, rect); | |
357 case MediaSliderThumb: | |
358 return paintMediaSliderThumb(object, paintInfo, rect); | |
359 case MediaVolumeSlider: | |
360 return paintMediaVolumeSlider(object, paintInfo, rect); | |
361 case MediaVolumeSliderThumb: | |
362 return paintMediaVolumeSliderThumb(object, paintInfo, rect); | |
363 case MediaEnterFullscreenButton: | |
364 case MediaExitFullscreenButton: | |
365 return paintMediaFullscreenButton(object, paintInfo, rect); | |
366 case MediaOverlayPlayButton: | |
367 return paintMediaOverlayPlayButton(object, paintInfo, rect); | |
368 case MediaVolumeSliderMuteButton: | |
369 case MediaSeekBackButton: | |
370 case MediaSeekForwardButton: | |
371 case MediaVolumeSliderContainer: | |
372 case MediaTimelineContainer: | |
373 case MediaCurrentTimeDisplay: | |
374 case MediaTimeRemainingDisplay: | |
375 case MediaControlsPanel: | |
376 case MediaRewindButton: | |
377 case MediaReturnToRealtimeButton: | |
378 case MediaStatusDisplay: | |
379 case MediaHideClosedCaptionsButton: | |
380 case MediaTextTrackDisplayContainer: | |
381 case MediaTextTrackDisplay: | |
382 case MediaFullScreenVolumeSlider: | |
383 case MediaFullScreenVolumeSliderThumb: | |
384 ASSERT_NOT_REACHED(); | |
385 break; | |
386 } | |
387 return false; | |
388 } | |
389 | |
390 const int mediaSliderThumbHeight = 24; | |
391 const int mediaVolumeSliderThumbHeight = 24; | |
392 | |
393 void RenderMediaControlsChromium::adjustMediaSliderThumbSize(RenderStyle* style) | |
394 { | |
395 static Image* mediaSliderThumb = platformResource("mediaplayerSliderThumb"); | |
396 static Image* mediaVolumeSliderThumb = platformResource("mediaplayerVolumeSl
iderThumb"); | |
397 int width = 0; | |
398 int height = 0; | |
399 | |
400 Image* thumbImage = 0; | |
401 if (style->appearance() == MediaSliderThumbPart) { | |
402 thumbImage = mediaSliderThumb; | |
403 width = mediaSliderThumbWidth; | |
404 height = mediaSliderThumbHeight; | |
405 } else if (style->appearance() == MediaVolumeSliderThumbPart) { | |
406 thumbImage = mediaVolumeSliderThumb; | |
407 width = mediaVolumeSliderThumbWidth; | |
408 height = mediaVolumeSliderThumbHeight; | |
409 } | |
410 | |
411 float zoomLevel = style->effectiveZoom(); | |
412 if (thumbImage) { | |
413 style->setWidth(Length(static_cast<int>(width * zoomLevel), Fixed)); | |
414 style->setHeight(Length(static_cast<int>(height * zoomLevel), Fixed)); | |
415 } | |
416 } | |
417 | |
418 static String formatChromiumMediaControlsTime(float time, float duration) | |
419 { | |
420 if (!std::isfinite(time)) | |
421 time = 0; | |
422 if (!std::isfinite(duration)) | |
423 duration = 0; | |
424 int seconds = static_cast<int>(fabsf(time)); | |
425 int hours = seconds / (60 * 60); | |
426 int minutes = (seconds / 60) % 60; | |
427 seconds %= 60; | |
428 | |
429 // duration defines the format of how the time is rendered | |
430 int durationSecs = static_cast<int>(fabsf(duration)); | |
431 int durationHours = durationSecs / (60 * 60); | |
432 int durationMins = (durationSecs / 60) % 60; | |
433 | |
434 if (durationHours || hours) | |
435 return String::format("%s%01d:%02d:%02d", (time < 0 ? "-" : ""), hours,
minutes, seconds); | |
436 if (durationMins > 9) | |
437 return String::format("%s%02d:%02d", (time < 0 ? "-" : ""), minutes, sec
onds); | |
438 | |
439 return String::format("%s%01d:%02d", (time < 0 ? "-" : ""), minutes, seconds
); | |
440 } | |
441 | |
442 String RenderMediaControlsChromium::formatMediaControlsTime(float time) | |
443 { | |
444 return formatChromiumMediaControlsTime(time, time); | |
445 } | |
446 | |
447 String RenderMediaControlsChromium::formatMediaControlsCurrentTime(float current
Time, float duration) | |
448 { | |
449 return formatChromiumMediaControlsTime(currentTime, duration); | |
450 } | |
451 | |
452 } // namespace WebCore | |
OLD | NEW |