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 41 namespace blink { 42 43 using namespace HTMLNames; 44 45 RenderVideo::RenderVideo(HTMLVideoElement* video) 46 : RenderMedia(video) 47 { 48 setIntrinsicSize(calculateIntrinsicSize()); 49 } 50 51 RenderVideo::~RenderVideo() 52 { 53 } 54 55 IntSize RenderVideo::defaultSize() 56 { 57 return IntSize(defaultWidth, defaultHeight); 58 } 59 60 void RenderVideo::intrinsicSizeChanged() 61 { 62 if (videoElement()->shouldDisplayPosterImage()) 63 RenderMedia::intrinsicSizeChanged(); 64 updateIntrinsicSize(); 65 } 66 67 void 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 84 LayoutSize 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 117 void 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 132 IntRect 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 141 bool RenderVideo::shouldDisplayVideo() const 142 { 143 return !videoElement()->shouldDisplayPosterImage(); 144 } 145 146 void RenderVideo::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 147 { 148 VideoPainter(*this).paintReplaced(paintInfo, paintOffset); 149 } 150 151 bool RenderVideo::acceleratedRenderingInUse() 152 { 153 WebLayer* webLayer = mediaElement()->platformLayer(); 154 return webLayer && !webLayer->isOrphan(); 155 } 156 157 void RenderVideo::layout() 158 { 159 updatePlayer(); 160 RenderMedia::layout(); 161 } 162 163 HTMLVideoElement* RenderVideo::videoElement() const 164 { 165 return toHTMLVideoElement(node()); 166 } 167 168 void RenderVideo::updateFromElement() 169 { 170 RenderMedia::updateFromElement(); 171 updatePlayer(); 172 } 173 174 void 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 188 LayoutUnit RenderVideo::computeReplacedLogicalWidth(ShouldComputePreferred shouldComputePreferred) const 189 { 190 return RenderReplaced::computeReplacedLogicalWidth(shouldComputePreferred); 191 } 192 193 LayoutUnit RenderVideo::computeReplacedLogicalHeight() const 194 { 195 return RenderReplaced::computeReplacedLogicalHeight(); 196 } 197 198 LayoutUnit RenderVideo::minimumReplacedHeight() const 199 { 200 return RenderReplaced::minimumReplacedHeight(); 201 } 202 203 bool RenderVideo::supportsAcceleratedRendering() const 204 { 205 return !!mediaElement()->platformLayer(); 206 } 207 208 static 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 221 LayoutUnit RenderVideo::offsetLeft() const 222 { 223 if (const RenderBlock* block = rendererPlaceholder(this)) 224 return block->offsetLeft(); 225 return RenderMedia::offsetLeft(); 226 } 227 228 LayoutUnit RenderVideo::offsetTop() const 229 { 230 if (const RenderBlock* block = rendererPlaceholder(this)) 231 return block->offsetTop(); 232 return RenderMedia::offsetTop(); 233 } 234 235 LayoutUnit RenderVideo::offsetWidth() const 236 { 237 if (const RenderBlock* block = rendererPlaceholder(this)) 238 return block->offsetWidth(); 239 return RenderMedia::offsetWidth(); 240 } 241 242 LayoutUnit RenderVideo::offsetHeight() const 243 { 244 if (const RenderBlock* block = rendererPlaceholder(this)) 245 return block->offsetHeight(); 246 return RenderMedia::offsetHeight(); 247 } 248 249 CompositingReasons 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