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 36 namespace WebCore { 37 38 #if ENABLE(VIDEO) 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).releaseRef()) { 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, Image* image) 64 { 65 IntRect imageRect = image->rect(); 66 context->drawImage(image, DeviceColorSpace, rect); 67 return true; 68 } 69 70 static bool paintMediaMuteButton(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) 71 { 72 HTMLMediaElement* mediaElement = toParentMediaElement(object); 73 if (!mediaElement) 74 return false; 75 76 static Image* soundFull = platformResource("mediaSoundFull"); 77 static Image* soundNone = platformResource("mediaSoundNone"); 78 static Image* soundDisabled = platformResource("mediaSoundDisabled"); 79 80 if (!hasSource(mediaElement) || !mediaElement->hasAudio()) 81 return paintMediaButton(paintInfo.context, rect, soundDisabled); 82 83 return paintMediaButton(paintInfo.context, rect, mediaElement->muted() ? soundNone: soundFull); 84 } 85 86 static bool paintMediaPlayButton(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) 87 { 88 HTMLMediaElement* mediaElement = toParentMediaElement(object); 89 if (!mediaElement) 90 return false; 91 92 static Image* mediaPlay = platformResource("mediaPlay"); 93 static Image* mediaPause = platformResource("mediaPause"); 94 static Image* mediaPlayDisabled = platformResource("mediaPlayDisabled"); 95 96 if (!hasSource(mediaElement)) 97 return paintMediaButton(paintInfo.context, rect, mediaPlayDisabled); 98 99 return paintMediaButton(paintInfo.context, rect, mediaElement->paused() ? mediaPlay : mediaPause); 100 } 101 102 static bool paintMediaSlider(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) 103 { 104 HTMLMediaElement* mediaElement = toParentMediaElement(object); 105 if (!mediaElement) 106 return false; 107 108 RenderStyle* style = object->style(); 109 GraphicsContext* context = paintInfo.context; 110 111 // Draw the border of the time bar. 112 // FIXME: this should be a rounded rect but need to fix GraphicsContextSkia first. 113 // https://bugs.webkit.org/show_bug.cgi?id=30143 114 context->save(); 115 context->setShouldAntialias(true); 116 context->setStrokeStyle(SolidStroke); 117 context->setStrokeColor(style->borderLeftColor(), DeviceColorSpace); 118 context->setStrokeThickness(style->borderLeftWidth()); 119 context->setFillColor(style->backgroundColor(), DeviceColorSpace); 120 context->drawRect(rect); 121 context->restore(); 122 123 // Draw the buffered ranges. 124 // FIXME: Draw multiple ranges if there are multiple buffered ranges. 125 IntRect bufferedRect = rect; 126 bufferedRect.inflate(-style->borderLeftWidth()); 127 bufferedRect.setWidth((bufferedRect.width() * mediaElement->percentLoaded())); 128 129 // Don't bother drawing an empty area. 130 if (!bufferedRect.isEmpty()) { 131 IntPoint sliderTopLeft = bufferedRect.location(); 132 IntPoint sliderTopRight = sliderTopLeft; 133 sliderTopRight.move(0, bufferedRect.height()); 134 135 RefPtr<Gradient> gradient = Gradient::create(sliderTopLeft, sliderTopRight); 136 Color startColor = object->style()->color(); 137 gradient->addColorStop(0.0, startColor); 138 gradient->addColorStop(1.0, Color(startColor.red() / 2, startColor.green() / 2, startColor.blue() / 2, startColor.alpha())); 139 140 context->save(); 141 context->setStrokeStyle(NoStroke); 142 context->setFillGradient(gradient); 143 context->fillRect(bufferedRect); 144 context->restore(); 145 } 146 147 return true; 148 } 149 150 static bool paintMediaSliderThumb(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) 151 { 152 if (!object->parent()->isSlider()) 153 return false; 154 155 HTMLMediaElement* mediaElement = toParentMediaElement(object->parent()); 156 if (!mediaElement) 157 return false; 158 159 if (!hasSource(mediaElement)) 160 return true; 161 162 static Image* mediaSliderThumb = platformResource("mediaSliderThumb"); 163 return paintMediaButton(paintInfo.context, rect, mediaSliderThumb); 164 } 165 166 static bool paintMediaVolumeSlider(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) 167 { 168 HTMLMediaElement* mediaElement = toParentMediaElement(object); 169 if (!mediaElement) 170 return false; 171 172 GraphicsContext* context = paintInfo.context; 173 Color originalColor = context->strokeColor(); 174 if (originalColor != Color::white) 175 context->setStrokeColor(Color::white, DeviceColorSpace); 176 177 int x = rect.x() + rect.width() / 2; 178 context->drawLine(IntPoint(x, rect.y()), IntPoint(x, rect.y() + rect.height())); 179 180 if (originalColor != Color::white) 181 context->setStrokeColor(originalColor, DeviceColorSpace); 182 return true; 183 } 184 185 static bool paintMediaVolumeSliderThumb(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) 186 { 187 if (!object->parent()->isSlider()) 188 return false; 189 190 static Image* mediaVolumeSliderThumb = platformResource("mediaVolumeSliderThumb"); 191 return paintMediaButton(paintInfo.context, rect, mediaVolumeSliderThumb); 192 } 193 194 static bool paintMediaTimelineContainer(RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) 195 { 196 HTMLMediaElement* mediaElement = toParentMediaElement(object); 197 if (!mediaElement) 198 return false; 199 200 if (!rect.isEmpty()) { 201 GraphicsContext* context = paintInfo.context; 202 Color originalColor = context->strokeColor(); 203 float originalThickness = context->strokeThickness(); 204 StrokeStyle originalStyle = context->strokeStyle(); 205 206 context->setStrokeStyle(SolidStroke); 207 208 // Draw the left border using CSS defined width and color. 209 context->setStrokeThickness(object->style()->borderLeftWidth()); 210 context->setStrokeColor(object->style()->borderLeftColor().rgb(), DeviceColorSpace); 211 context->drawLine(IntPoint(rect.x() + 1, rect.y()), 212 IntPoint(rect.x() + 1, rect.y() + rect.height())); 213 214 // Draw the right border using CSS defined width and color. 215 context->setStrokeThickness(object->style()->borderRightWidth()); 216 context->setStrokeColor(object->style()->borderRightColor().rgb(), DeviceColorSpace); 217 context->drawLine(IntPoint(rect.x() + rect.width() - 1, rect.y()), 218 IntPoint(rect.x() + rect.width() - 1, rect.y() + rect.height())); 219 220 context->setStrokeColor(originalColor, DeviceColorSpace); 221 context->setStrokeThickness(originalThickness); 222 context->setStrokeStyle(originalStyle); 223 } 224 return true; 225 } 226 227 bool RenderMediaControlsChromium::shouldRenderMediaControlPart(ControlPart part, Element* e) 228 { 229 UNUSED_PARAM(e); 230 231 switch (part) { 232 case MediaMuteButtonPart: 233 case MediaPlayButtonPart: 234 case MediaSliderPart: 235 case MediaSliderThumbPart: 236 case MediaVolumeSliderContainerPart: 237 case MediaVolumeSliderPart: 238 case MediaVolumeSliderThumbPart: 239 case MediaControlsBackgroundPart: 240 case MediaCurrentTimePart: 241 case MediaTimeRemainingPart: 242 return true; 243 default: 244 ; 245 } 246 return false; 247 } 248 249 bool RenderMediaControlsChromium::paintMediaControlsPart(MediaControlElementType part, RenderObject* object, const RenderObject::PaintInfo& paintInfo, const IntRect& rect) 250 { 251 switch (part) { 252 case MediaMuteButton: 253 case MediaUnMuteButton: 254 return paintMediaMuteButton(object, paintInfo, rect); 255 case MediaPauseButton: 256 case MediaPlayButton: 257 return paintMediaPlayButton(object, paintInfo, rect); 258 case MediaSlider: 259 return paintMediaSlider(object, paintInfo, rect); 260 case MediaSliderThumb: 261 return paintMediaSliderThumb(object, paintInfo, rect); 262 case MediaVolumeSlider: 263 return paintMediaVolumeSlider(object, paintInfo, rect); 264 case MediaVolumeSliderThumb: 265 return paintMediaVolumeSliderThumb(object, paintInfo, rect); 266 case MediaTimelineContainer: 267 return paintMediaTimelineContainer(object, paintInfo, rect); 268 case MediaFullscreenButton: 269 case MediaSeekBackButton: 270 case MediaSeekForwardButton: 271 case MediaVolumeSliderContainer: 272 case MediaCurrentTimeDisplay: 273 case MediaTimeRemainingDisplay: 274 case MediaControlsPanel: 275 case MediaRewindButton: 276 case MediaReturnToRealtimeButton: 277 case MediaStatusDisplay: 278 case MediaShowClosedCaptionsButton: 279 case MediaHideClosedCaptionsButton: 280 ASSERT_NOT_REACHED(); 281 break; 282 } 283 return false; 284 } 285 286 void RenderMediaControlsChromium::adjustMediaSliderThumbSize(RenderObject* object) 287 { 288 static Image* mediaSliderThumb = platformResource("mediaSliderThumb"); 289 static Image* mediaVolumeSliderThumb = platformResource("mediaVolumeSliderThumb"); 290 291 Image* thumbImage = 0; 292 if (object->style()->appearance() == MediaSliderThumbPart) 293 thumbImage = mediaSliderThumb; 294 else if (object->style()->appearance() == MediaVolumeSliderThumbPart) 295 thumbImage = mediaVolumeSliderThumb; 296 297 float zoomLevel = object->style()->effectiveZoom(); 298 if (thumbImage) { 299 object->style()->setWidth(Length(static_cast<int>(thumbImage->width() * zoomLevel), Fixed)); 300 object->style()->setHeight(Length(static_cast<int>(thumbImage->height() * zoomLevel), Fixed)); 301 } 302 } 303 304 #endif // #if ENABLE(VIDEO) 305 306 } // namespace WebCore 307