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 44using namespace std; 45 46namespace WebCore { 47 48using namespace HTMLNames; 49 50static const int cDefaultWidth = 300; 51static const int cDefaultHeight = 150; 52 53RenderVideo::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 77RenderVideo::~RenderVideo() 78{ 79 if (MediaPlayer* p = player()) { 80 p->setVisible(false); 81 p->setFrameView(0); 82 } 83} 84 85void RenderVideo::intrinsicSizeChanged() 86{ 87 if (videoElement()->shouldDisplayPosterImage()) 88 RenderMedia::intrinsicSizeChanged(); 89 videoSizeChanged(); 90} 91 92 93void 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 105void 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 116IntRect 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 149void 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 173void RenderVideo::layout() 174{ 175 RenderMedia::layout(); 176 updatePlayer(); 177} 178 179HTMLVideoElement* RenderVideo::videoElement() const 180{ 181 ASSERT(node()->hasTagName(videoTag)); 182 return static_cast<HTMLVideoElement*>(node()); 183} 184 185void RenderVideo::updateFromElement() 186{ 187 RenderMedia::updateFromElement(); 188 updatePlayer(); 189} 190 191void 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 211int 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 225int 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 239int 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 248int 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 257int RenderVideo::minimumReplacedHeight() const 258{ 259 return 0; 260} 261 262#if USE(ACCELERATED_COMPOSITING) 263bool RenderVideo::supportsAcceleratedRendering() const 264{ 265 MediaPlayer* p = player(); 266 if (p) 267 return p->supportsAcceleratedRendering(); 268 269 return false; 270} 271 272void RenderVideo::acceleratedRenderingStateChanged() 273{ 274 MediaPlayer* p = player(); 275 if (p) 276 p->acceleratedRenderingStateChanged(); 277} 278 279GraphicsLayer* 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