Use enum classes and OptionSets for PaintPhase and PaintBehavior
[WebKit-https.git] / Source / WebCore / rendering / RenderVideo.cpp
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 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. 
24  */
25
26 #include "config.h"
27
28 #if ENABLE(VIDEO)
29 #include "RenderVideo.h"
30
31 #include "Document.h"
32 #include "Frame.h"
33 #include "FrameView.h"
34 #include "GraphicsContext.h"
35 #include "HTMLNames.h"
36 #include "HTMLVideoElement.h"
37 #include "MediaPlayer.h"
38 #include "Page.h"
39 #include "PaintInfo.h"
40 #include "RenderView.h"
41 #include <wtf/IsoMallocInlines.h>
42 #include <wtf/StackStats.h>
43
44 #if ENABLE(FULLSCREEN_API)
45 #include "RenderFullScreen.h"
46 #endif
47
48 namespace WebCore {
49
50 using namespace HTMLNames;
51
52 WTF_MAKE_ISO_ALLOCATED_IMPL(RenderVideo);
53
54 RenderVideo::RenderVideo(HTMLVideoElement& element, RenderStyle&& style)
55     : RenderMedia(element, WTFMove(style))
56 {
57     setIntrinsicSize(calculateIntrinsicSize());
58 }
59
60 RenderVideo::~RenderVideo()
61 {
62     // Do not add any code here. Add it to willBeDestroyed() instead.
63 }
64
65 void RenderVideo::willBeDestroyed()
66 {
67     visibleInViewportStateChanged();
68     if (auto player = videoElement().player())
69         player->setVisible(false);
70
71     RenderMedia::willBeDestroyed();
72 }
73
74 void RenderVideo::visibleInViewportStateChanged()
75 {
76     videoElement().isVisibleInViewportChanged();
77 }
78
79 IntSize RenderVideo::defaultSize()
80 {
81     // These values are specified in the spec.
82     static const int cDefaultWidth = 300;
83     static const int cDefaultHeight = 150;
84
85     return IntSize(cDefaultWidth, cDefaultHeight);
86 }
87
88 void RenderVideo::intrinsicSizeChanged()
89 {
90     if (videoElement().shouldDisplayPosterImage())
91         RenderMedia::intrinsicSizeChanged();
92     updateIntrinsicSize(); 
93 }
94
95 bool RenderVideo::updateIntrinsicSize()
96 {
97     LayoutSize size = calculateIntrinsicSize();
98     size.scale(style().effectiveZoom());
99
100     // Never set the element size to zero when in a media document.
101     if (size.isEmpty() && document().isMediaDocument())
102         return false;
103
104     if (size == intrinsicSize())
105         return false;
106
107     setIntrinsicSize(size);
108     setPreferredLogicalWidthsDirty(true);
109     setNeedsLayout();
110     return true;
111 }
112     
113 LayoutSize RenderVideo::calculateIntrinsicSize()
114 {
115     // Spec text from 4.8.6
116     //
117     // The intrinsic width of a video element's playback area is the intrinsic width 
118     // of the video resource, if that is available; otherwise it is the intrinsic 
119     // width of the poster frame, if that is available; otherwise it is 300 CSS pixels.
120     //
121     // The intrinsic height of a video element's playback area is the intrinsic height 
122     // of the video resource, if that is available; otherwise it is the intrinsic 
123     // height of the poster frame, if that is available; otherwise it is 150 CSS pixels.
124     auto player = videoElement().player();
125     if (player && videoElement().readyState() >= HTMLVideoElement::HAVE_METADATA) {
126         LayoutSize size(player->naturalSize());
127         if (!size.isEmpty())
128             return size;
129     }
130
131     if (videoElement().shouldDisplayPosterImage() && !m_cachedImageSize.isEmpty() && !imageResource().errorOccurred())
132         return m_cachedImageSize;
133
134     // <video> in standalone media documents should not use the default 300x150
135     // size since they also have audio-only files. By setting the intrinsic
136     // size to 300x1 the video will resize itself in these cases, and audio will
137     // have the correct height (it needs to be > 0 for controls to render properly).
138     if (videoElement().document().isMediaDocument())
139         return LayoutSize(defaultSize().width(), 1);
140
141     return defaultSize();
142 }
143
144 void RenderVideo::imageChanged(WrappedImagePtr newImage, const IntRect* rect)
145 {
146     RenderMedia::imageChanged(newImage, rect);
147
148     // Cache the image intrinsic size so we can continue to use it to draw the image correctly
149     // even if we know the video intrinsic size but aren't able to draw video frames yet
150     // (we don't want to scale the poster to the video size without keeping aspect ratio).
151     if (videoElement().shouldDisplayPosterImage())
152         m_cachedImageSize = intrinsicSize();
153
154     // The intrinsic size is now that of the image, but in case we already had the
155     // intrinsic size of the video we call this here to restore the video size.
156     updateIntrinsicSize();
157 }
158
159 IntRect RenderVideo::videoBox() const
160 {
161     LayoutSize intrinsicSize = this->intrinsicSize();
162
163     if (videoElement().shouldDisplayPosterImage())
164         intrinsicSize = m_cachedImageSize;
165
166     return snappedIntRect(replacedContentRect(intrinsicSize));
167 }
168
169 bool RenderVideo::shouldDisplayVideo() const
170 {
171     return !videoElement().shouldDisplayPosterImage();
172 }
173
174 void RenderVideo::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
175 {
176     auto mediaPlayer = videoElement().player();
177     bool displayingPoster = videoElement().shouldDisplayPosterImage();
178
179     if (!displayingPoster && !mediaPlayer) {
180         if (paintInfo.phase == PaintPhase::Foreground)
181             page().addRelevantUnpaintedObject(this, visualOverflowRect());
182         return;
183     }
184
185     LayoutRect rect = videoBox();
186     if (rect.isEmpty()) {
187         if (paintInfo.phase == PaintPhase::Foreground)
188             page().addRelevantUnpaintedObject(this, visualOverflowRect());
189         return;
190     }
191     rect.moveBy(paintOffset);
192
193     if (paintInfo.phase == PaintPhase::Foreground)
194         page().addRelevantRepaintedObject(this, rect);
195
196     LayoutRect contentRect = contentBoxRect();
197     contentRect.moveBy(paintOffset);
198     GraphicsContext& context = paintInfo.context();
199     bool clip = !contentRect.contains(rect);
200     GraphicsContextStateSaver stateSaver(context, clip);
201     if (clip)
202         context.clip(contentRect);
203
204     if (displayingPoster)
205         paintIntoRect(paintInfo, rect);
206     else if (!videoElement().isFullscreen() || !mediaPlayer->supportsAcceleratedRendering()) {
207         if (paintInfo.paintBehavior.contains(PaintBehavior::FlattenCompositingLayers))
208             mediaPlayer->paintCurrentFrameInContext(context, rect);
209         else
210             mediaPlayer->paint(context, rect);
211     }
212 }
213
214 void RenderVideo::layout()
215 {
216     StackStats::LayoutCheckPoint layoutCheckPoint;
217     updateIntrinsicSize();
218     RenderMedia::layout();
219     updatePlayer();
220 }
221     
222 HTMLVideoElement& RenderVideo::videoElement() const
223 {
224     return downcast<HTMLVideoElement>(RenderMedia::mediaElement());
225 }
226
227 void RenderVideo::updateFromElement()
228 {
229     RenderMedia::updateFromElement();
230     updatePlayer();
231 }
232
233 void RenderVideo::updatePlayer()
234 {
235     if (renderTreeBeingDestroyed())
236         return;
237
238     bool intrinsicSizeChanged;
239     intrinsicSizeChanged = updateIntrinsicSize();
240     ASSERT_UNUSED(intrinsicSizeChanged, !intrinsicSizeChanged || !view().frameView().layoutContext().isInRenderTreeLayout());
241
242     auto mediaPlayer = videoElement().player();
243     if (!mediaPlayer)
244         return;
245
246     if (!videoElement().inActiveDocument()) {
247         mediaPlayer->setVisible(false);
248         return;
249     }
250
251     contentChanged(VideoChanged);
252     
253     IntRect videoBounds = videoBox(); 
254     mediaPlayer->setSize(IntSize(videoBounds.width(), videoBounds.height()));
255     mediaPlayer->setVisible(!videoElement().elementIsHidden());
256     mediaPlayer->setShouldMaintainAspectRatio(style().objectFit() != ObjectFit::Fill);
257 }
258
259 LayoutUnit RenderVideo::computeReplacedLogicalWidth(ShouldComputePreferred shouldComputePreferred) const
260 {
261     return RenderReplaced::computeReplacedLogicalWidth(shouldComputePreferred);
262 }
263
264 LayoutUnit RenderVideo::minimumReplacedHeight() const 
265 {
266     return RenderReplaced::minimumReplacedHeight(); 
267 }
268
269 bool RenderVideo::supportsAcceleratedRendering() const
270 {
271     if (auto player = videoElement().player())
272         return player->supportsAcceleratedRendering();
273     return false;
274 }
275
276 void RenderVideo::acceleratedRenderingStateChanged()
277 {
278     if (auto player = videoElement().player())
279         player->acceleratedRenderingStateChanged();
280 }
281
282 bool RenderVideo::requiresImmediateCompositing() const
283 {
284     auto player = videoElement().player();
285     return player && player->requiresImmediateCompositing();
286 }
287
288 #if ENABLE(FULLSCREEN_API)
289
290 static const RenderBlock* placeholder(const RenderVideo& renderer)
291 {
292     auto* parent = renderer.parent();
293     return is<RenderFullScreen>(parent) ? downcast<RenderFullScreen>(*parent).placeholder() : nullptr;
294 }
295
296 LayoutUnit RenderVideo::offsetLeft() const
297 {
298     if (auto* block = placeholder(*this))
299         return block->offsetLeft();
300     return RenderMedia::offsetLeft();
301 }
302
303 LayoutUnit RenderVideo::offsetTop() const
304 {
305     if (auto* block = placeholder(*this))
306         return block->offsetTop();
307     return RenderMedia::offsetTop();
308 }
309
310 LayoutUnit RenderVideo::offsetWidth() const
311 {
312     if (auto* block = placeholder(*this))
313         return block->offsetWidth();
314     return RenderMedia::offsetWidth();
315 }
316
317 LayoutUnit RenderVideo::offsetHeight() const
318 {
319     if (auto* block = placeholder(*this))
320         return block->offsetHeight();
321     return RenderMedia::offsetHeight();
322 }
323
324 #endif
325
326 bool RenderVideo::foregroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect, unsigned maxDepthToTest) const
327 {
328     if (videoElement().shouldDisplayPosterImage())
329         return RenderImage::foregroundIsKnownToBeOpaqueInRect(localRect, maxDepthToTest);
330
331     if (!videoBox().contains(enclosingIntRect(localRect)))
332         return false;
333
334     if (auto player = videoElement().player())
335         return player->hasAvailableVideoFrame();
336
337     return false;
338 }
339
340 } // namespace WebCore
341
342 #endif