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