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