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