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