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 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 58 static bool hasSource(const HTMLMediaElement* mediaElement) 59 { 60 return mediaElement->networkState() != HTMLMediaElement::NETWORK_EMPTY 61 && mediaElement->networkState() != HTMLMediaElement::NETWORK_NO_SOURCE; 62 } 63 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 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 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 103 static Image* getMediaSliderThumb() 104 { 105 static Image* mediaSliderThumb = platformResource("mediaSliderThumb"); 106 return mediaSliderThumb; 107 } 108 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 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 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 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 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 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 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