1 /* 2 * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 28 #if ENABLE(VIDEO) 29 #include "RenderVideo.h" 30 31 #include "Document.h" 32 #include "FrameView.h" 33 #include "GraphicsContext.h" 34 #include "HTMLNames.h" 35 #include "HTMLVideoElement.h" 36 #include "MediaPlayer.h" 37 #include "RenderView.h" 38 39 #if USE(ACCELERATED_COMPOSITING) 40 #include "RenderLayer.h" 41 #include "RenderLayerBacking.h" 42 #endif 43 44 using namespace std; 45 46 namespace WebCore { 47 48 using namespace HTMLNames; 49 50 static const int cDefaultWidth = 300; 51 static const int cDefaultHeight = 150; 52 53 RenderVideo::RenderVideo(HTMLVideoElement* video) 54 : RenderMedia(video) 55 { 56 if (video->player()) 57 setIntrinsicSize(video->player()->naturalSize()); 58 else { 59 // When the natural size of the video is unavailable, we use the provided 60 // width and height attributes of the video element as the intrinsic size until 61 // better values become available. If these attributes are not set, we fall back 62 // to a default video size (300x150). 63 if (video->hasAttribute(widthAttr) && video->hasAttribute(heightAttr)) 64 setIntrinsicSize(IntSize(video->width(), video->height())); 65 else if (video->ownerDocument() && video->ownerDocument()->isMediaDocument()) { 66 // Video in standalone media documents should not use the default 300x150 67 // size since they also have audio thrown at them. By setting the intrinsic 68 // size to 300x1 the video will resize itself in these cases, and audio will 69 // have the correct height (it needs to be > 0 for controls to render properly). 70 setIntrinsicSize(IntSize(cDefaultWidth, 1)); 71 } 72 else 73 setIntrinsicSize(IntSize(cDefaultWidth, cDefaultHeight)); 74 } 75 } 76 77 RenderVideo::~RenderVideo() 78 { 79 if (MediaPlayer* p = player()) { 80 p->setVisible(false); 81 p->setFrameView(0); 82 } 83 } 84 85 void RenderVideo::intrinsicSizeChanged() 86 { 87 if (videoElement()->shouldDisplayPosterImage()) 88 RenderMedia::intrinsicSizeChanged(); 89 videoSizeChanged(); 90 } 91 92 93 void RenderVideo::videoSizeChanged() 94 { 95 if (!player()) 96 return; 97 IntSize size = player()->naturalSize(); 98 if (!size.isEmpty() && size != intrinsicSize()) { 99 setIntrinsicSize(size); 100 setPrefWidthsDirty(true); 101 setNeedsLayout(true); 102 } 103 } 104 105 void RenderVideo::imageChanged(WrappedImagePtr newImage, const IntRect* rect) 106 { 107 RenderMedia::imageChanged(newImage, rect); 108 109 // Cache the image intrinsic size so we can continue to use it to draw the image correctly 110 // even after we know the video intrisic size but aren't able to draw video frames yet 111 // (we don't want to scale the poster to the video size). 112 if (videoElement()->shouldDisplayPosterImage()) 113 m_cachedImageSize = intrinsicSize(); 114 } 115 116 IntRect RenderVideo::videoBox() const 117 { 118 if (m_cachedImageSize.isEmpty() && videoElement()->shouldDisplayPosterImage()) 119 return IntRect(); 120 121 IntSize elementSize; 122 if (videoElement()->shouldDisplayPosterImage()) 123 elementSize = m_cachedImageSize; 124 else 125 elementSize = intrinsicSize(); 126 127 IntRect contentRect = contentBoxRect(); 128 if (elementSize.isEmpty() || contentRect.isEmpty()) 129 return IntRect(); 130 131 IntRect renderBox = contentRect; 132 int ratio = renderBox.width() * elementSize.height() - renderBox.height() * elementSize.width(); 133 if (ratio > 0) { 134 int newWidth = renderBox.height() * elementSize.width() / elementSize.height(); 135 // Just fill the whole area if the difference is one pixel or less (in both sides) 136 if (renderBox.width() - newWidth > 2) 137 renderBox.setWidth(newWidth); 138 renderBox.move((contentRect.width() - renderBox.width()) / 2, 0); 139 } else if (ratio < 0) { 140 int newHeight = renderBox.width() * elementSize.height() / elementSize.width(); 141 if (renderBox.height() - newHeight > 2) 142 renderBox.setHeight(newHeight); 143 renderBox.move(0, (contentRect.height() - renderBox.height()) / 2); 144 } 145 146 return renderBox; 147 } 148 149 void RenderVideo::paintReplaced(PaintInfo& paintInfo, int tx, int ty) 150 { 151 MediaPlayer* mediaPlayer = player(); 152 bool displayingPoster = videoElement()->shouldDisplayPosterImage(); 153 154 if (displayingPoster && document()->printing() && !view()->printImages()) 155 return; 156 157 if (!displayingPoster) { 158 if (!mediaPlayer) 159 return; 160 updatePlayer(); 161 } 162 163 IntRect rect = videoBox(); 164 if (rect.isEmpty()) 165 return; 166 rect.move(tx, ty); 167 if (displayingPoster) 168 paintIntoRect(paintInfo.context, rect); 169 else 170 mediaPlayer->paint(paintInfo.context, rect); 171 } 172 173 void RenderVideo::layout() 174 { 175 RenderMedia::layout(); 176 updatePlayer(); 177 } 178 179 HTMLVideoElement* RenderVideo::videoElement() const 180 { 181 ASSERT(node()->hasTagName(videoTag)); 182 return static_cast<HTMLVideoElement*>(node()); 183 } 184 185 void RenderVideo::updateFromElement() 186 { 187 RenderMedia::updateFromElement(); 188 updatePlayer(); 189 } 190 191 void RenderVideo::updatePlayer() 192 { 193 MediaPlayer* mediaPlayer = player(); 194 if (!mediaPlayer) 195 return; 196 if (!videoElement()->inActiveDocument()) { 197 mediaPlayer->setVisible(false); 198 return; 199 } 200 201 #if USE(ACCELERATED_COMPOSITING) 202 layer()->rendererContentChanged(); 203 #endif 204 205 IntRect videoBounds = videoBox(); 206 mediaPlayer->setFrameView(document()->view()); 207 mediaPlayer->setSize(IntSize(videoBounds.width(), videoBounds.height())); 208 mediaPlayer->setVisible(true); 209 } 210 211 int RenderVideo::calcReplacedWidth(bool includeMaxWidth) const 212 { 213 int width; 214 if (isWidthSpecified()) 215 width = calcReplacedWidthUsing(style()->width()); 216 else 217 width = calcAspectRatioWidth() * style()->effectiveZoom(); 218 219 int minW = calcReplacedWidthUsing(style()->minWidth()); 220 int maxW = !includeMaxWidth || style()->maxWidth().isUndefined() ? width : calcReplacedWidthUsing(style()->maxWidth()); 221 222 return max(minW, min(width, maxW)); 223 } 224 225 int RenderVideo::calcReplacedHeight() const 226 { 227 int height; 228 if (isHeightSpecified()) 229 height = calcReplacedHeightUsing(style()->height()); 230 else 231 height = calcAspectRatioHeight() * style()->effectiveZoom(); 232 233 int minH = calcReplacedHeightUsing(style()->minHeight()); 234 int maxH = style()->maxHeight().isUndefined() ? height : calcReplacedHeightUsing(style()->maxHeight()); 235 236 return max(minH, min(height, maxH)); 237 } 238 239 int RenderVideo::calcAspectRatioWidth() const 240 { 241 int intrinsicWidth = intrinsicSize().width(); 242 int intrinsicHeight = intrinsicSize().height(); 243 if (!intrinsicHeight) 244 return 0; 245 return RenderBox::calcReplacedHeight() * intrinsicWidth / intrinsicHeight; 246 } 247 248 int RenderVideo::calcAspectRatioHeight() const 249 { 250 int intrinsicWidth = intrinsicSize().width(); 251 int intrinsicHeight = intrinsicSize().height(); 252 if (!intrinsicWidth) 253 return 0; 254 return RenderBox::calcReplacedWidth() * intrinsicHeight / intrinsicWidth; 255 } 256 257 int RenderVideo::minimumReplacedHeight() const 258 { 259 return 0; 260 } 261 262 #if USE(ACCELERATED_COMPOSITING) 263 bool RenderVideo::supportsAcceleratedRendering() const 264 { 265 MediaPlayer* p = player(); 266 if (p) 267 return p->supportsAcceleratedRendering(); 268 269 return false; 270 } 271 272 void RenderVideo::acceleratedRenderingStateChanged() 273 { 274 MediaPlayer* p = player(); 275 if (p) 276 p->acceleratedRenderingStateChanged(); 277 } 278 279 GraphicsLayer* RenderVideo::videoGraphicsLayer() const 280 { 281 if (hasLayer() && layer()->isComposited()) 282 return layer()->backing()->graphicsLayer(); 283 284 return 0; 285 } 286 #endif // USE(ACCELERATED_COMPOSITING) 287 288 } // namespace WebCore 289 290 #endif 291