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