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 USE(ACCELERATED_COMPOSITING)
41 #include "RenderLayer.h"
42 #include "RenderLayerBacking.h"
43 #endif
44 
45 using namespace std;
46 
47 namespace WebCore {
48 
49 using namespace HTMLNames;
50 
RenderVideo(HTMLVideoElement * video)51 RenderVideo::RenderVideo(HTMLVideoElement* video)
52     : RenderMedia(video)
53 {
54     setIntrinsicSize(calculateIntrinsicSize());
55 }
56 
~RenderVideo()57 RenderVideo::~RenderVideo()
58 {
59     if (MediaPlayer* p = mediaElement()->player()) {
60         p->setVisible(false);
61         p->setFrameView(0);
62     }
63 }
64 
defaultSize()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 
intrinsicSizeChanged()74 void RenderVideo::intrinsicSizeChanged()
75 {
76     if (videoElement()->shouldDisplayPosterImage())
77         RenderMedia::intrinsicSizeChanged();
78     updateIntrinsicSize();
79 }
80 
updateIntrinsicSize()81 void RenderVideo::updateIntrinsicSize()
82 {
83     IntSize 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() && node()->ownerDocument() && node()->ownerDocument()->isMediaDocument())
88         return;
89 
90     if (size == intrinsicSize())
91         return;
92 
93     setIntrinsicSize(size);
94     setPreferredLogicalWidthsDirty(true);
95     setNeedsLayout(true);
96 }
97 
calculateIntrinsicSize()98 IntSize RenderVideo::calculateIntrinsicSize()
99 {
100     HTMLVideoElement* video = videoElement();
101 
102     // Spec text from 4.8.6
103     //
104     // The intrinsic width of a video element's playback area is the intrinsic width
105     // of the video resource, if that is available; otherwise it is the intrinsic
106     // width of the poster frame, if that is available; otherwise it is 300 CSS pixels.
107     //
108     // The intrinsic height of a video element's playback area is the intrinsic height
109     // of the video resource, if that is available; otherwise it is the intrinsic
110     // height of the poster frame, if that is available; otherwise it is 150 CSS pixels.
111     MediaPlayer* player = mediaElement()->player();
112     if (player && video->readyState() >= HTMLVideoElement::HAVE_METADATA)
113         return player->naturalSize();
114 
115     if (video->shouldDisplayPosterImage() && !m_cachedImageSize.isEmpty() && !imageResource()->errorOccurred())
116         return m_cachedImageSize;
117 
118     // When the natural size of the video is unavailable, we use the provided
119     // width and height attributes of the video element as the intrinsic size until
120     // better values become available.
121     if (video->hasAttribute(widthAttr) && video->hasAttribute(heightAttr))
122         return IntSize(video->width(), video->height());
123 
124     // <video> in standalone media documents should not use the default 300x150
125     // size since they also have audio-only files. By setting the intrinsic
126     // size to 300x1 the video will resize itself in these cases, and audio will
127     // have the correct height (it needs to be > 0 for controls to render properly).
128     if (video->ownerDocument() && video->ownerDocument()->isMediaDocument())
129         return IntSize(defaultSize().width(), 1);
130 
131     return defaultSize();
132 }
133 
imageChanged(WrappedImagePtr newImage,const IntRect * rect)134 void RenderVideo::imageChanged(WrappedImagePtr newImage, const IntRect* rect)
135 {
136     RenderMedia::imageChanged(newImage, rect);
137 
138     // Cache the image intrinsic size so we can continue to use it to draw the image correctly
139     // even if we know the video intrinsic size but aren't able to draw video frames yet
140     // (we don't want to scale the poster to the video size without keeping aspect ratio).
141     if (videoElement()->shouldDisplayPosterImage())
142         m_cachedImageSize = intrinsicSize();
143 
144     // The intrinsic size is now that of the image, but in case we already had the
145     // intrinsic size of the video we call this here to restore the video size.
146     updateIntrinsicSize();
147 }
148 
videoBox() const149 IntRect RenderVideo::videoBox() const
150 {
151     if (m_cachedImageSize.isEmpty() && videoElement()->shouldDisplayPosterImage())
152         return IntRect();
153 
154     IntSize elementSize;
155     if (videoElement()->shouldDisplayPosterImage())
156         elementSize = m_cachedImageSize;
157     else
158         elementSize = intrinsicSize();
159 
160     IntRect contentRect = contentBoxRect();
161     if (elementSize.isEmpty() || contentRect.isEmpty())
162         return IntRect();
163 
164     IntRect renderBox = contentRect;
165     int ratio = renderBox.width() * elementSize.height() - renderBox.height() * elementSize.width();
166     if (ratio > 0) {
167         int newWidth = renderBox.height() * elementSize.width() / elementSize.height();
168         // Just fill the whole area if the difference is one pixel or less (in both sides)
169         if (renderBox.width() - newWidth > 2)
170             renderBox.setWidth(newWidth);
171         renderBox.move((contentRect.width() - renderBox.width()) / 2, 0);
172     } else if (ratio < 0) {
173         int newHeight = renderBox.width() * elementSize.height() / elementSize.width();
174         if (renderBox.height() - newHeight > 2)
175             renderBox.setHeight(newHeight);
176         renderBox.move(0, (contentRect.height() - renderBox.height()) / 2);
177     }
178 
179     return renderBox;
180 }
181 
shouldDisplayVideo() const182 bool RenderVideo::shouldDisplayVideo() const
183 {
184     return !videoElement()->shouldDisplayPosterImage();
185 }
186 
paintReplaced(PaintInfo & paintInfo,int tx,int ty)187 void RenderVideo::paintReplaced(PaintInfo& paintInfo, int tx, int ty)
188 {
189     MediaPlayer* mediaPlayer = mediaElement()->player();
190     bool displayingPoster = videoElement()->shouldDisplayPosterImage();
191 
192     if (!displayingPoster) {
193         if (!mediaPlayer)
194             return;
195         updatePlayer();
196     }
197 
198     IntRect rect = videoBox();
199     if (rect.isEmpty())
200         return;
201     rect.move(tx, ty);
202 
203     if (displayingPoster)
204         paintIntoRect(paintInfo.context, rect);
205     else if (document()->view() && document()->view()->paintBehavior() & PaintBehaviorFlattenCompositingLayers)
206         mediaPlayer->paintCurrentFrameInContext(paintInfo.context, rect);
207     else
208         mediaPlayer->paint(paintInfo.context, rect);
209 }
210 
layout()211 void RenderVideo::layout()
212 {
213     RenderMedia::layout();
214     updatePlayer();
215 }
216 
videoElement() const217 HTMLVideoElement* RenderVideo::videoElement() const
218 {
219     ASSERT(node()->hasTagName(videoTag));
220     return static_cast<HTMLVideoElement*>(node());
221 }
222 
updateFromElement()223 void RenderVideo::updateFromElement()
224 {
225     RenderMedia::updateFromElement();
226     updatePlayer();
227 }
228 
updatePlayer()229 void RenderVideo::updatePlayer()
230 {
231     updateIntrinsicSize();
232 
233     MediaPlayer* mediaPlayer = mediaElement()->player();
234     if (!mediaPlayer)
235         return;
236 
237     if (!videoElement()->inActiveDocument()) {
238         mediaPlayer->setVisible(false);
239         return;
240     }
241 
242 #if USE(ACCELERATED_COMPOSITING)
243     layer()->contentChanged(RenderLayer::VideoChanged);
244 #endif
245 
246     IntRect videoBounds = videoBox();
247     mediaPlayer->setFrameView(document()->view());
248     mediaPlayer->setSize(IntSize(videoBounds.width(), videoBounds.height()));
249     mediaPlayer->setVisible(true);
250 }
251 
computeReplacedLogicalWidth(bool includeMaxWidth) const252 int RenderVideo::computeReplacedLogicalWidth(bool includeMaxWidth) const
253 {
254     return RenderReplaced::computeReplacedLogicalWidth(includeMaxWidth);
255 }
256 
computeReplacedLogicalHeight() const257 int RenderVideo::computeReplacedLogicalHeight() const
258 {
259     return RenderReplaced::computeReplacedLogicalHeight();
260 }
261 
minimumReplacedHeight() const262 int RenderVideo::minimumReplacedHeight() const
263 {
264     return RenderReplaced::minimumReplacedHeight();
265 }
266 
267 #if USE(ACCELERATED_COMPOSITING)
supportsAcceleratedRendering() const268 bool RenderVideo::supportsAcceleratedRendering() const
269 {
270     MediaPlayer* p = mediaElement()->player();
271     if (p)
272         return p->supportsAcceleratedRendering();
273 
274     return false;
275 }
276 
acceleratedRenderingStateChanged()277 void RenderVideo::acceleratedRenderingStateChanged()
278 {
279     MediaPlayer* p = mediaElement()->player();
280     if (p)
281         p->acceleratedRenderingStateChanged();
282 }
283 #endif  // USE(ACCELERATED_COMPOSITING)
284 
285 } // namespace WebCore
286 
287 #endif
288