2 * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
13 * THIS SOFTWARE IS PROVIDED BY APPLE 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 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.
29 #include "RenderVideo.h"
33 #include "FrameView.h"
34 #include "GraphicsContext.h"
35 #include "HTMLNames.h"
36 #include "HTMLVideoElement.h"
37 #include "MediaPlayer.h"
39 #include "PaintInfo.h"
40 #include "RenderView.h"
41 #include <wtf/StackStats.h>
43 #if ENABLE(FULLSCREEN_API)
44 #include "RenderFullScreen.h"
49 using namespace HTMLNames;
51 RenderVideo::RenderVideo(HTMLVideoElement& element, Ref<RenderStyle>&& style)
52 : RenderMedia(element, WTFMove(style))
54 setIntrinsicSize(calculateIntrinsicSize());
57 RenderVideo::~RenderVideo()
59 if (MediaPlayer* player = videoElement().player())
60 player->setVisible(false);
63 IntSize RenderVideo::defaultSize()
65 // These values are specified in the spec.
66 static const int cDefaultWidth = 300;
67 static const int cDefaultHeight = 150;
69 return IntSize(cDefaultWidth, cDefaultHeight);
72 void RenderVideo::intrinsicSizeChanged()
74 if (videoElement().shouldDisplayPosterImage())
75 RenderMedia::intrinsicSizeChanged();
76 updateIntrinsicSize();
79 void RenderVideo::updateIntrinsicSize()
81 LayoutSize size = calculateIntrinsicSize();
82 size.scale(style().effectiveZoom());
84 // Never set the element size to zero when in a media document.
85 if (size.isEmpty() && document().isMediaDocument())
88 if (size == intrinsicSize())
91 setIntrinsicSize(size);
92 setPreferredLogicalWidthsDirty(true);
96 LayoutSize RenderVideo::calculateIntrinsicSize()
98 // Spec text from 4.8.6
100 // The intrinsic width of a video element's playback area is the intrinsic width
101 // of the video resource, if that is available; otherwise it is the intrinsic
102 // width of the poster frame, if that is available; otherwise it is 300 CSS pixels.
104 // The intrinsic height of a video element's playback area is the intrinsic height
105 // of the video resource, if that is available; otherwise it is the intrinsic
106 // height of the poster frame, if that is available; otherwise it is 150 CSS pixels.
107 MediaPlayer* player = videoElement().player();
108 if (player && videoElement().readyState() >= HTMLVideoElement::HAVE_METADATA) {
109 LayoutSize size(player->naturalSize());
114 if (videoElement().shouldDisplayPosterImage() && !m_cachedImageSize.isEmpty() && !imageResource().errorOccurred())
115 return m_cachedImageSize;
117 // <video> in standalone media documents should not use the default 300x150
118 // size since they also have audio-only files. By setting the intrinsic
119 // size to 300x1 the video will resize itself in these cases, and audio will
120 // have the correct height (it needs to be > 0 for controls to render properly).
121 if (videoElement().document().isMediaDocument())
122 return LayoutSize(defaultSize().width(), 1);
124 return defaultSize();
127 void RenderVideo::imageChanged(WrappedImagePtr newImage, const IntRect* rect)
129 RenderMedia::imageChanged(newImage, rect);
131 // Cache the image intrinsic size so we can continue to use it to draw the image correctly
132 // even if we know the video intrinsic size but aren't able to draw video frames yet
133 // (we don't want to scale the poster to the video size without keeping aspect ratio).
134 if (videoElement().shouldDisplayPosterImage())
135 m_cachedImageSize = intrinsicSize();
137 // The intrinsic size is now that of the image, but in case we already had the
138 // intrinsic size of the video we call this here to restore the video size.
139 updateIntrinsicSize();
142 IntRect RenderVideo::videoBox() const
144 LayoutSize intrinsicSize = this->intrinsicSize();
146 if (videoElement().shouldDisplayPosterImage())
147 intrinsicSize = m_cachedImageSize;
149 return snappedIntRect(replacedContentRect(intrinsicSize));
152 bool RenderVideo::shouldDisplayVideo() const
154 return !videoElement().shouldDisplayPosterImage();
157 void RenderVideo::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
159 MediaPlayer* mediaPlayer = videoElement().player();
160 bool displayingPoster = videoElement().shouldDisplayPosterImage();
162 Page* page = frame().page();
164 if (!displayingPoster && !mediaPlayer) {
165 if (page && paintInfo.phase == PaintPhaseForeground)
166 page->addRelevantUnpaintedObject(this, visualOverflowRect());
170 LayoutRect rect = videoBox();
171 if (rect.isEmpty()) {
172 if (page && paintInfo.phase == PaintPhaseForeground)
173 page->addRelevantUnpaintedObject(this, visualOverflowRect());
176 rect.moveBy(paintOffset);
178 if (page && paintInfo.phase == PaintPhaseForeground)
179 page->addRelevantRepaintedObject(this, rect);
181 LayoutRect contentRect = contentBoxRect();
182 contentRect.moveBy(paintOffset);
183 GraphicsContext& context = paintInfo.context();
184 bool clip = !contentRect.contains(rect);
185 GraphicsContextStateSaver stateSaver(context, clip);
187 context.clip(contentRect);
189 if (displayingPoster)
190 paintIntoRect(context, rect);
191 else if (!videoElement().isFullscreen() || !mediaPlayer->supportsAcceleratedRendering()) {
192 if (view().frameView().paintBehavior() & PaintBehaviorFlattenCompositingLayers)
193 mediaPlayer->paintCurrentFrameInContext(context, rect);
195 mediaPlayer->paint(context, rect);
199 void RenderVideo::layout()
201 StackStats::LayoutCheckPoint layoutCheckPoint;
202 RenderMedia::layout();
206 HTMLVideoElement& RenderVideo::videoElement() const
208 return downcast<HTMLVideoElement>(RenderMedia::mediaElement());
211 void RenderVideo::updateFromElement()
213 RenderMedia::updateFromElement();
217 void RenderVideo::updatePlayer()
219 if (documentBeingDestroyed())
222 updateIntrinsicSize();
224 MediaPlayer* mediaPlayer = videoElement().player();
228 if (!videoElement().inActiveDocument()) {
229 mediaPlayer->setVisible(false);
233 contentChanged(VideoChanged);
235 IntRect videoBounds = videoBox();
236 mediaPlayer->setSize(IntSize(videoBounds.width(), videoBounds.height()));
237 mediaPlayer->setVisible(true);
238 mediaPlayer->setShouldMaintainAspectRatio(style().objectFit() != ObjectFitFill);
241 LayoutUnit RenderVideo::computeReplacedLogicalWidth(ShouldComputePreferred shouldComputePreferred) const
243 return RenderReplaced::computeReplacedLogicalWidth(shouldComputePreferred);
246 LayoutUnit RenderVideo::computeReplacedLogicalHeight() const
248 return RenderReplaced::computeReplacedLogicalHeight();
251 LayoutUnit RenderVideo::minimumReplacedHeight() const
253 return RenderReplaced::minimumReplacedHeight();
256 bool RenderVideo::supportsAcceleratedRendering() const
258 if (MediaPlayer* player = videoElement().player())
259 return player->supportsAcceleratedRendering();
263 void RenderVideo::acceleratedRenderingStateChanged()
265 if (MediaPlayer* player = videoElement().player())
266 player->acceleratedRenderingStateChanged();
269 bool RenderVideo::requiresImmediateCompositing() const
271 MediaPlayer* player = videoElement().player();
272 return player && player->requiresImmediateCompositing();
275 #if ENABLE(FULLSCREEN_API)
276 static const RenderBlock* rendererPlaceholder(const RenderObject* renderer)
278 RenderObject* parent = renderer->parent();
279 return is<RenderFullScreen>(parent) ? downcast<RenderFullScreen>(*parent).placeholder() : nullptr;
282 LayoutUnit RenderVideo::offsetLeft() const
284 if (const RenderBlock* block = rendererPlaceholder(this))
285 return block->offsetLeft();
286 return RenderMedia::offsetLeft();
289 LayoutUnit RenderVideo::offsetTop() const
291 if (const RenderBlock* block = rendererPlaceholder(this))
292 return block->offsetTop();
293 return RenderMedia::offsetTop();
296 LayoutUnit RenderVideo::offsetWidth() const
298 if (const RenderBlock* block = rendererPlaceholder(this))
299 return block->offsetWidth();
300 return RenderMedia::offsetWidth();
303 LayoutUnit RenderVideo::offsetHeight() const
305 if (const RenderBlock* block = rendererPlaceholder(this))
306 return block->offsetHeight();
307 return RenderMedia::offsetHeight();
311 bool RenderVideo::foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect, unsigned maxDepthToTest) const
313 if (videoElement().shouldDisplayPosterImage())
314 return RenderImage::foregroundIsKnownToBeOpaqueInRect(localRect, maxDepthToTest);
316 if (!videoBox().contains(enclosingIntRect(localRect)))
319 if (MediaPlayer* player = videoElement().player())
320 return player->hasAvailableVideoFrame();
325 } // namespace WebCore