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 "PaintInfo.h" 38 #include "RenderView.h" 39 40 #if USE(ACCELERATED_COMPOSITING) 41 #include "RenderLayer.h" 42 #include "RenderLayerBacking.h" 43 #endif 44 45 using namespace std; 46 47 namespace WebCore { 48 49 using namespace HTMLNames; 50 51 RenderVideo::RenderVideo(HTMLVideoElement* video) 52 : RenderMedia(video) 53 { 54 setIntrinsicSize(calculateIntrinsicSize()); 55 } 56 57 RenderVideo::~RenderVideo() 58 { 59 if (MediaPlayer* p = mediaElement()->player()) { 60 p->setVisible(false); 61 p->setFrameView(0); 62 } 63 } 64 65 IntSize RenderVideo::defaultSize() 66 { 67 // These values are specified in the spec. 68 static const int cDefaultWidth = 300; 69 static const int cDefaultHeight = 150; 70 71 return IntSize(cDefaultWidth, cDefaultHeight); 72 } 73 74 void RenderVideo::intrinsicSizeChanged() 75 { 76 if (videoElement()->shouldDisplayPosterImage()) 77 RenderMedia::intrinsicSizeChanged(); 78 updateIntrinsicSize(); 79 } 80 81 void RenderVideo::updateIntrinsicSize() 82 { 83 IntSize size = calculateIntrinsicSize(); 84 size.scale(style()->effectiveZoom()); 85 86 // Never set the element size to zero when in a media document. 87 if (size.isEmpty() && node()->ownerDocument() && node()->ownerDocument()->isMediaDocument()) 88 return; 89 90 if (size == intrinsicSize()) 91 return; 92 93 setIntrinsicSize(size); 94 setPreferredLogicalWidthsDirty(true); 95 setNeedsLayout(true); 96 } 97 98 IntSize RenderVideo::calculateIntrinsicSize() 99 { 100 HTMLVideoElement* video = videoElement(); 101 102 // Spec text from 4.8.6 103 // 104 // The intrinsic width of a video element's playback area is the intrinsic width 105 // of the video resource, if that is available; otherwise it is the intrinsic 106 // width of the poster frame, if that is available; otherwise it is 300 CSS pixels. 107 // 108 // The intrinsic height of a video element's playback area is the intrinsic height 109 // of the video resource, if that is available; otherwise it is the intrinsic 110 // height of the poster frame, if that is available; otherwise it is 150 CSS pixels. 111 MediaPlayer* player = mediaElement()->player(); 112 if (player && video->readyState() >= HTMLVideoElement::HAVE_METADATA) 113 return player->naturalSize(); 114 115 if (video->shouldDisplayPosterImage() && !m_cachedImageSize.isEmpty() && !imageResource()->errorOccurred()) 116 return m_cachedImageSize; 117 118 // When the natural size of the video is unavailable, we use the provided 119 // width and height attributes of the video element as the intrinsic size until 120 // better values become available. 121 if (video->hasAttribute(widthAttr) && video->hasAttribute(heightAttr)) 122 return IntSize(video->width(), video->height()); 123 124 // <video> in standalone media documents should not use the default 300x150 125 // size since they also have audio-only files. By setting the intrinsic 126 // size to 300x1 the video will resize itself in these cases, and audio will 127 // have the correct height (it needs to be > 0 for controls to render properly). 128 if (video->ownerDocument() && video->ownerDocument()->isMediaDocument()) 129 return IntSize(defaultSize().width(), 1); 130 131 return defaultSize(); 132 } 133 134 void RenderVideo::imageChanged(WrappedImagePtr newImage, const IntRect* rect) 135 { 136 RenderMedia::imageChanged(newImage, rect); 137 138 // Cache the image intrinsic size so we can continue to use it to draw the image correctly 139 // even if we know the video intrinsic size but aren't able to draw video frames yet 140 // (we don't want to scale the poster to the video size without keeping aspect ratio). 141 if (videoElement()->shouldDisplayPosterImage()) 142 m_cachedImageSize = intrinsicSize(); 143 144 // The intrinsic size is now that of the image, but in case we already had the 145 // intrinsic size of the video we call this here to restore the video size. 146 updateIntrinsicSize(); 147 } 148 149 IntRect RenderVideo::videoBox() const 150 { 151 if (m_cachedImageSize.isEmpty() && videoElement()->shouldDisplayPosterImage()) 152 return IntRect(); 153 154 IntSize elementSize; 155 if (videoElement()->shouldDisplayPosterImage()) 156 elementSize = m_cachedImageSize; 157 else 158 elementSize = intrinsicSize(); 159 160 IntRect contentRect = contentBoxRect(); 161 if (elementSize.isEmpty() || contentRect.isEmpty()) 162 return IntRect(); 163 164 IntRect renderBox = contentRect; 165 int ratio = renderBox.width() * elementSize.height() - renderBox.height() * elementSize.width(); 166 if (ratio > 0) { 167 int newWidth = renderBox.height() * elementSize.width() / elementSize.height(); 168 // Just fill the whole area if the difference is one pixel or less (in both sides) 169 if (renderBox.width() - newWidth > 2) 170 renderBox.setWidth(newWidth); 171 renderBox.move((contentRect.width() - renderBox.width()) / 2, 0); 172 } else if (ratio < 0) { 173 int newHeight = renderBox.width() * elementSize.height() / elementSize.width(); 174 if (renderBox.height() - newHeight > 2) 175 renderBox.setHeight(newHeight); 176 renderBox.move(0, (contentRect.height() - renderBox.height()) / 2); 177 } 178 179 return renderBox; 180 } 181 182 bool RenderVideo::shouldDisplayVideo() const 183 { 184 return !videoElement()->shouldDisplayPosterImage(); 185 } 186 187 void RenderVideo::paintReplaced(PaintInfo& paintInfo, int tx, int ty) 188 { 189 MediaPlayer* mediaPlayer = mediaElement()->player(); 190 bool displayingPoster = videoElement()->shouldDisplayPosterImage(); 191 192 if (!displayingPoster) { 193 if (!mediaPlayer) 194 return; 195 updatePlayer(); 196 } 197 198 IntRect rect = videoBox(); 199 if (rect.isEmpty()) 200 return; 201 rect.move(tx, ty); 202 203 if (displayingPoster) 204 paintIntoRect(paintInfo.context, rect); 205 else if (document()->view() && document()->view()->paintBehavior() & PaintBehaviorFlattenCompositingLayers) 206 mediaPlayer->paintCurrentFrameInContext(paintInfo.context, rect); 207 else 208 mediaPlayer->paint(paintInfo.context, rect); 209 } 210 211 void RenderVideo::layout() 212 { 213 RenderMedia::layout(); 214 updatePlayer(); 215 } 216 217 HTMLVideoElement* RenderVideo::videoElement() const 218 { 219 ASSERT(node()->hasTagName(videoTag)); 220 return static_cast<HTMLVideoElement*>(node()); 221 } 222 223 void RenderVideo::updateFromElement() 224 { 225 RenderMedia::updateFromElement(); 226 updatePlayer(); 227 } 228 229 void RenderVideo::updatePlayer() 230 { 231 updateIntrinsicSize(); 232 233 MediaPlayer* mediaPlayer = mediaElement()->player(); 234 if (!mediaPlayer) 235 return; 236 237 if (!videoElement()->inActiveDocument()) { 238 mediaPlayer->setVisible(false); 239 return; 240 } 241 242 #if USE(ACCELERATED_COMPOSITING) 243 layer()->contentChanged(RenderLayer::VideoChanged); 244 #endif 245 246 IntRect videoBounds = videoBox(); 247 mediaPlayer->setFrameView(document()->view()); 248 mediaPlayer->setSize(IntSize(videoBounds.width(), videoBounds.height())); 249 mediaPlayer->setVisible(true); 250 } 251 252 int RenderVideo::computeReplacedLogicalWidth(bool includeMaxWidth) const 253 { 254 return RenderReplaced::computeReplacedLogicalWidth(includeMaxWidth); 255 } 256 257 int RenderVideo::computeReplacedLogicalHeight() const 258 { 259 return RenderReplaced::computeReplacedLogicalHeight(); 260 } 261 262 int RenderVideo::minimumReplacedHeight() const 263 { 264 return RenderReplaced::minimumReplacedHeight(); 265 } 266 267 #if USE(ACCELERATED_COMPOSITING) 268 bool RenderVideo::supportsAcceleratedRendering() const 269 { 270 MediaPlayer* p = mediaElement()->player(); 271 if (p) 272 return p->supportsAcceleratedRendering(); 273 274 return false; 275 } 276 277 void RenderVideo::acceleratedRenderingStateChanged() 278 { 279 MediaPlayer* p = mediaElement()->player(); 280 if (p) 281 p->acceleratedRenderingStateChanged(); 282 } 283 #endif // USE(ACCELERATED_COMPOSITING) 284 285 } // namespace WebCore 286 287 #endif 288