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#include "core/rendering/RenderVideo.h" 29 30#include "core/HTMLNames.h" 31#include "core/dom/Document.h" 32#include "core/frame/FrameView.h" 33#include "core/frame/LocalFrame.h" 34#include "core/html/HTMLVideoElement.h" 35#include "core/paint/VideoPainter.h" 36#include "core/rendering/PaintInfo.h" 37#include "core/rendering/RenderFullScreen.h" 38#include "platform/graphics/media/MediaPlayer.h" 39#include "public/platform/WebLayer.h" 40 41namespace blink { 42 43using namespace HTMLNames; 44 45RenderVideo::RenderVideo(HTMLVideoElement* video) 46 : RenderMedia(video) 47{ 48 setIntrinsicSize(calculateIntrinsicSize()); 49} 50 51RenderVideo::~RenderVideo() 52{ 53} 54 55IntSize RenderVideo::defaultSize() 56{ 57 return IntSize(defaultWidth, defaultHeight); 58} 59 60void RenderVideo::intrinsicSizeChanged() 61{ 62 if (videoElement()->shouldDisplayPosterImage()) 63 RenderMedia::intrinsicSizeChanged(); 64 updateIntrinsicSize(); 65} 66 67void RenderVideo::updateIntrinsicSize() 68{ 69 LayoutSize size = calculateIntrinsicSize(); 70 size.scale(style()->effectiveZoom()); 71 72 // Never set the element size to zero when in a media document. 73 if (size.isEmpty() && node()->ownerDocument() && node()->ownerDocument()->isMediaDocument()) 74 return; 75 76 if (size == intrinsicSize()) 77 return; 78 79 setIntrinsicSize(size); 80 setPreferredLogicalWidthsDirty(); 81 setNeedsLayoutAndFullPaintInvalidation(); 82} 83 84LayoutSize RenderVideo::calculateIntrinsicSize() 85{ 86 HTMLVideoElement* video = videoElement(); 87 88 // Spec text from 4.8.6 89 // 90 // The intrinsic width of a video element's playback area is the intrinsic width 91 // of the video resource, if that is available; otherwise it is the intrinsic 92 // width of the poster frame, if that is available; otherwise it is 300 CSS pixels. 93 // 94 // The intrinsic height of a video element's playback area is the intrinsic height 95 // of the video resource, if that is available; otherwise it is the intrinsic 96 // height of the poster frame, if that is available; otherwise it is 150 CSS pixels. 97 WebMediaPlayer* webMediaPlayer = mediaElement()->webMediaPlayer(); 98 if (webMediaPlayer && video->readyState() >= HTMLVideoElement::HAVE_METADATA) { 99 IntSize size = webMediaPlayer->naturalSize(); 100 if (!size.isEmpty()) 101 return size; 102 } 103 104 if (video->shouldDisplayPosterImage() && !m_cachedImageSize.isEmpty() && !imageResource()->errorOccurred()) 105 return m_cachedImageSize; 106 107 // <video> in standalone media documents should not use the default 300x150 108 // size since they also have audio-only files. By setting the intrinsic 109 // size to 300x1 the video will resize itself in these cases, and audio will 110 // have the correct height (it needs to be > 0 for controls to render properly). 111 if (video->ownerDocument() && video->ownerDocument()->isMediaDocument()) 112 return LayoutSize(defaultSize().width(), 1); 113 114 return defaultSize(); 115} 116 117void RenderVideo::imageChanged(WrappedImagePtr newImage, const IntRect* rect) 118{ 119 RenderMedia::imageChanged(newImage, rect); 120 121 // Cache the image intrinsic size so we can continue to use it to draw the image correctly 122 // even if we know the video intrinsic size but aren't able to draw video frames yet 123 // (we don't want to scale the poster to the video size without keeping aspect ratio). 124 if (videoElement()->shouldDisplayPosterImage()) 125 m_cachedImageSize = intrinsicSize(); 126 127 // The intrinsic size is now that of the image, but in case we already had the 128 // intrinsic size of the video we call this here to restore the video size. 129 updateIntrinsicSize(); 130} 131 132IntRect RenderVideo::videoBox() const 133{ 134 const LayoutSize* overriddenIntrinsicSize = 0; 135 if (videoElement()->shouldDisplayPosterImage()) 136 overriddenIntrinsicSize = &m_cachedImageSize; 137 138 return pixelSnappedIntRect(replacedContentRect(overriddenIntrinsicSize)); 139} 140 141bool RenderVideo::shouldDisplayVideo() const 142{ 143 return !videoElement()->shouldDisplayPosterImage(); 144} 145 146void RenderVideo::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 147{ 148 VideoPainter(*this).paintReplaced(paintInfo, paintOffset); 149} 150 151bool RenderVideo::acceleratedRenderingInUse() 152{ 153 WebLayer* webLayer = mediaElement()->platformLayer(); 154 return webLayer && !webLayer->isOrphan(); 155} 156 157void RenderVideo::layout() 158{ 159 updatePlayer(); 160 RenderMedia::layout(); 161} 162 163HTMLVideoElement* RenderVideo::videoElement() const 164{ 165 return toHTMLVideoElement(node()); 166} 167 168void RenderVideo::updateFromElement() 169{ 170 RenderMedia::updateFromElement(); 171 updatePlayer(); 172} 173 174void RenderVideo::updatePlayer() 175{ 176 updateIntrinsicSize(); 177 178 WebMediaPlayer* mediaPlayer = mediaElement()->webMediaPlayer(); 179 if (!mediaPlayer) 180 return; 181 182 if (!videoElement()->isActive()) 183 return; 184 185 videoElement()->setNeedsCompositingUpdate(); 186} 187 188LayoutUnit RenderVideo::computeReplacedLogicalWidth(ShouldComputePreferred shouldComputePreferred) const 189{ 190 return RenderReplaced::computeReplacedLogicalWidth(shouldComputePreferred); 191} 192 193LayoutUnit RenderVideo::computeReplacedLogicalHeight() const 194{ 195 return RenderReplaced::computeReplacedLogicalHeight(); 196} 197 198LayoutUnit RenderVideo::minimumReplacedHeight() const 199{ 200 return RenderReplaced::minimumReplacedHeight(); 201} 202 203bool RenderVideo::supportsAcceleratedRendering() const 204{ 205 return !!mediaElement()->platformLayer(); 206} 207 208static const RenderBlock* rendererPlaceholder(const RenderObject* renderer) 209{ 210 RenderObject* parent = renderer->parent(); 211 if (!parent) 212 return 0; 213 214 RenderFullScreen* fullScreen = parent->isRenderFullScreen() ? toRenderFullScreen(parent) : 0; 215 if (!fullScreen) 216 return 0; 217 218 return fullScreen->placeholder(); 219} 220 221LayoutUnit RenderVideo::offsetLeft() const 222{ 223 if (const RenderBlock* block = rendererPlaceholder(this)) 224 return block->offsetLeft(); 225 return RenderMedia::offsetLeft(); 226} 227 228LayoutUnit RenderVideo::offsetTop() const 229{ 230 if (const RenderBlock* block = rendererPlaceholder(this)) 231 return block->offsetTop(); 232 return RenderMedia::offsetTop(); 233} 234 235LayoutUnit RenderVideo::offsetWidth() const 236{ 237 if (const RenderBlock* block = rendererPlaceholder(this)) 238 return block->offsetWidth(); 239 return RenderMedia::offsetWidth(); 240} 241 242LayoutUnit RenderVideo::offsetHeight() const 243{ 244 if (const RenderBlock* block = rendererPlaceholder(this)) 245 return block->offsetHeight(); 246 return RenderMedia::offsetHeight(); 247} 248 249CompositingReasons RenderVideo::additionalCompositingReasons() const 250{ 251 if (RuntimeEnabledFeatures::overlayFullscreenVideoEnabled()) { 252 HTMLMediaElement* media = toHTMLMediaElement(node()); 253 if (media->isFullscreen()) 254 return CompositingReasonVideo; 255 } 256 257 if (shouldDisplayVideo() && supportsAcceleratedRendering()) 258 return CompositingReasonVideo; 259 260 return CompositingReasonNone; 261} 262 263} // namespace blink 264