1 /*
2 * Copyright (C) 2007 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
38 #if USE(ACCELERATED_COMPOSITING)
39 #include "RenderLayer.h"
40 #include "RenderLayerBacking.h"
41 #endif
42
43 using namespace std;
44
45 namespace WebCore {
46
47 using namespace HTMLNames;
48
49 static const int cDefaultWidth = 300;
50 static const int cDefaultHeight = 150;
51
RenderVideo(HTMLMediaElement * video)52 RenderVideo::RenderVideo(HTMLMediaElement* video)
53 : RenderMedia(video)
54 {
55 if (video->player())
56 setIntrinsicSize(video->player()->naturalSize());
57 else {
58 // Video in standalone media documents should not use the default 300x150
59 // size since they also have audio thrown at them. By setting the intrinsic
60 // size to 300x1 the video will resize itself in these cases, and audio will
61 // have the correct height (it needs to be > 0 for controls to render properly).
62 if (video->ownerDocument() && video->ownerDocument()->isMediaDocument())
63 setIntrinsicSize(IntSize(cDefaultWidth, 1));
64 else
65 setIntrinsicSize(IntSize(cDefaultWidth, cDefaultHeight));
66 }
67 }
68
~RenderVideo()69 RenderVideo::~RenderVideo()
70 {
71 if (MediaPlayer* p = player()) {
72 p->setVisible(false);
73 p->setFrameView(0);
74 }
75 }
76
videoSizeChanged()77 void RenderVideo::videoSizeChanged()
78 {
79 if (!player())
80 return;
81 IntSize size = player()->naturalSize();
82 if (!size.isEmpty() && size != intrinsicSize()) {
83 setIntrinsicSize(size);
84 setPrefWidthsDirty(true);
85 setNeedsLayout(true);
86 }
87 }
88
videoBox() const89 IntRect RenderVideo::videoBox() const
90 {
91 IntRect contentRect = contentBoxRect();
92
93 if (intrinsicSize().isEmpty() || contentRect.isEmpty())
94 return IntRect();
95
96 IntRect resultRect = contentRect;
97 int ratio = contentRect.width() * intrinsicSize().height() - contentRect.height() * intrinsicSize().width();
98 if (ratio > 0) {
99 int newWidth = contentRect.height() * intrinsicSize().width() / intrinsicSize().height();
100 // Just fill the whole area if the difference is one pixel or less (in both sides)
101 if (resultRect.width() - newWidth > 2)
102 resultRect.setWidth(newWidth);
103 resultRect.move((contentRect.width() - resultRect.width()) / 2, 0);
104 } else if (ratio < 0) {
105 int newHeight = contentRect.width() * intrinsicSize().height() / intrinsicSize().width();
106 if (resultRect.height() - newHeight > 2)
107 resultRect.setHeight(newHeight);
108 resultRect.move(0, (contentRect.height() - resultRect.height()) / 2);
109 }
110 return resultRect;
111 }
112
paintReplaced(PaintInfo & paintInfo,int tx,int ty)113 void RenderVideo::paintReplaced(PaintInfo& paintInfo, int tx, int ty)
114 {
115 MediaPlayer* mediaPlayer = player();
116 if (!mediaPlayer)
117 return;
118 updatePlayer();
119 IntRect rect = videoBox();
120 if (rect.isEmpty())
121 return;
122 rect.move(tx, ty);
123 mediaPlayer->paint(paintInfo.context, rect);
124 }
125
layout()126 void RenderVideo::layout()
127 {
128 RenderMedia::layout();
129 updatePlayer();
130 }
131
updateFromElement()132 void RenderVideo::updateFromElement()
133 {
134 RenderMedia::updateFromElement();
135 updatePlayer();
136 }
137
updatePlayer()138 void RenderVideo::updatePlayer()
139 {
140 MediaPlayer* mediaPlayer = player();
141 if (!mediaPlayer)
142 return;
143 if (!mediaElement()->inActiveDocument()) {
144 mediaPlayer->setVisible(false);
145 return;
146 }
147
148 #if USE(ACCELERATED_COMPOSITING)
149 layer()->rendererContentChanged();
150 #endif
151
152 IntRect videoBounds = videoBox();
153 mediaPlayer->setFrameView(document()->view());
154 mediaPlayer->setSize(IntSize(videoBounds.width(), videoBounds.height()));
155 mediaPlayer->setVisible(true);
156 }
157
isWidthSpecified() const158 bool RenderVideo::isWidthSpecified() const
159 {
160 switch (style()->width().type()) {
161 case Fixed:
162 case Percent:
163 return true;
164 case Auto:
165 case Relative: // FIXME: Shouldn't this case return true? It doesn't for images.
166 case Static:
167 case Intrinsic:
168 case MinIntrinsic:
169 return false;
170 }
171 ASSERT(false);
172 return false;
173 }
174
isHeightSpecified() const175 bool RenderVideo::isHeightSpecified() const
176 {
177 switch (style()->height().type()) {
178 case Fixed:
179 case Percent:
180 return true;
181 case Auto:
182 case Relative: // FIXME: Shouldn't this case return true? It doesn't for images.
183 case Static:
184 case Intrinsic:
185 case MinIntrinsic:
186 return false;
187 }
188 ASSERT(false);
189 return false;
190 }
191
calcReplacedWidth(bool includeMaxWidth) const192 int RenderVideo::calcReplacedWidth(bool includeMaxWidth) const
193 {
194 int width;
195 if (isWidthSpecified())
196 width = calcReplacedWidthUsing(style()->width());
197 else
198 width = calcAspectRatioWidth() * style()->effectiveZoom();
199
200 int minW = calcReplacedWidthUsing(style()->minWidth());
201 int maxW = !includeMaxWidth || style()->maxWidth().isUndefined() ? width : calcReplacedWidthUsing(style()->maxWidth());
202
203 return max(minW, min(width, maxW));
204 }
205
calcReplacedHeight() const206 int RenderVideo::calcReplacedHeight() const
207 {
208 int height;
209 if (isHeightSpecified())
210 height = calcReplacedHeightUsing(style()->height());
211 else
212 height = calcAspectRatioHeight() * style()->effectiveZoom();
213
214 int minH = calcReplacedHeightUsing(style()->minHeight());
215 int maxH = style()->maxHeight().isUndefined() ? height : calcReplacedHeightUsing(style()->maxHeight());
216
217 return max(minH, min(height, maxH));
218 }
219
calcAspectRatioWidth() const220 int RenderVideo::calcAspectRatioWidth() const
221 {
222 int intrinsicWidth = intrinsicSize().width();
223 int intrinsicHeight = intrinsicSize().height();
224 if (!intrinsicHeight)
225 return 0;
226 return RenderBox::calcReplacedHeight() * intrinsicWidth / intrinsicHeight;
227 }
228
calcAspectRatioHeight() const229 int RenderVideo::calcAspectRatioHeight() const
230 {
231 int intrinsicWidth = intrinsicSize().width();
232 int intrinsicHeight = intrinsicSize().height();
233 if (!intrinsicWidth)
234 return 0;
235 return RenderBox::calcReplacedWidth() * intrinsicHeight / intrinsicWidth;
236 }
237
calcPrefWidths()238 void RenderVideo::calcPrefWidths()
239 {
240 ASSERT(prefWidthsDirty());
241
242 int paddingAndBorders = paddingLeft() + paddingRight() + borderLeft() + borderRight();
243 m_maxPrefWidth = calcReplacedWidth(false) + paddingAndBorders;
244
245 if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength)
246 m_maxPrefWidth = min(m_maxPrefWidth, style()->maxWidth().value() + (style()->boxSizing() == CONTENT_BOX ? paddingAndBorders : 0));
247
248 if (style()->width().isPercent() || style()->height().isPercent() ||
249 style()->maxWidth().isPercent() || style()->maxHeight().isPercent() ||
250 style()->minWidth().isPercent() || style()->minHeight().isPercent())
251 m_minPrefWidth = 0;
252 else
253 m_minPrefWidth = m_maxPrefWidth;
254
255 setPrefWidthsDirty(false);
256 }
257
258 #if USE(ACCELERATED_COMPOSITING)
supportsAcceleratedRendering() const259 bool RenderVideo::supportsAcceleratedRendering() const
260 {
261 MediaPlayer* p = player();
262 if (p)
263 return p->supportsAcceleratedRendering();
264
265 return false;
266 }
267
acceleratedRenderingStateChanged()268 void RenderVideo::acceleratedRenderingStateChanged()
269 {
270 MediaPlayer* p = player();
271 if (p)
272 p->acceleratedRenderingStateChanged();
273 }
274
videoGraphicsLayer() const275 GraphicsLayer* RenderVideo::videoGraphicsLayer() const
276 {
277 if (hasLayer() && layer()->isComposited())
278 return layer()->backing()->graphicsLayer();
279
280 return 0;
281 }
282 #endif // USE(ACCELERATED_COMPOSITING)
283
284 } // namespace WebCore
285
286 #endif
287