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