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