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