2 * Copyright (C) 2007, 2008, 2009, 2010 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
29 #include "RenderVideo.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"
40 #if ENABLE(FULLSCREEN_API)
41 #include "RenderFullScreen.h"
44 #if USE(ACCELERATED_COMPOSITING)
45 #include "RenderLayer.h"
46 #include "RenderLayerBacking.h"
53 using namespace HTMLNames;
55 RenderVideo::RenderVideo(HTMLVideoElement* video)
58 setIntrinsicSize(calculateIntrinsicSize());
61 RenderVideo::~RenderVideo()
63 if (MediaPlayer* p = mediaElement()->player()) {
69 IntSize RenderVideo::defaultSize()
71 // These values are specified in the spec.
72 static const int cDefaultWidth = 300;
73 static const int cDefaultHeight = 150;
75 return IntSize(cDefaultWidth, cDefaultHeight);
78 void RenderVideo::intrinsicSizeChanged()
80 if (videoElement()->shouldDisplayPosterImage())
81 RenderMedia::intrinsicSizeChanged();
82 updateIntrinsicSize();
85 void RenderVideo::updateIntrinsicSize()
87 IntSize size = calculateIntrinsicSize();
88 size.scale(style()->effectiveZoom());
90 // Never set the element size to zero when in a media document.
91 if (size.isEmpty() && node()->ownerDocument() && node()->ownerDocument()->isMediaDocument())
94 if (size == intrinsicSize())
97 setIntrinsicSize(size);
98 setPreferredLogicalWidthsDirty(true);
102 IntSize RenderVideo::calculateIntrinsicSize()
104 HTMLVideoElement* video = videoElement();
106 // Spec text from 4.8.6
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.
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();
119 if (video->shouldDisplayPosterImage() && !m_cachedImageSize.isEmpty() && !imageResource()->errorOccurred())
120 return m_cachedImageSize;
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());
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);
135 return defaultSize();
138 void RenderVideo::imageChanged(WrappedImagePtr newImage, const IntRect* rect)
140 RenderMedia::imageChanged(newImage, rect);
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();
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();
153 IntRect RenderVideo::videoBox() const
155 if (m_cachedImageSize.isEmpty() && videoElement()->shouldDisplayPosterImage())
159 if (videoElement()->shouldDisplayPosterImage())
160 elementSize = m_cachedImageSize;
162 elementSize = intrinsicSize();
164 IntRect contentRect = contentBoxRect();
165 if (elementSize.isEmpty() || contentRect.isEmpty())
168 IntRect renderBox = contentRect;
169 int ratio = renderBox.width() * elementSize.height() - renderBox.height() * elementSize.width();
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);
186 bool RenderVideo::shouldDisplayVideo() const
188 return !videoElement()->shouldDisplayPosterImage();
191 void RenderVideo::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
193 MediaPlayer* mediaPlayer = mediaElement()->player();
194 bool displayingPoster = videoElement()->shouldDisplayPosterImage();
196 if (!displayingPoster) {
202 LayoutRect rect = videoBox();
205 rect.moveBy(paintOffset);
207 if (displayingPoster)
208 paintIntoRect(paintInfo.context, rect);
209 else if (document()->view() && document()->view()->paintBehavior() & PaintBehaviorFlattenCompositingLayers)
210 mediaPlayer->paintCurrentFrameInContext(paintInfo.context, rect);
212 mediaPlayer->paint(paintInfo.context, rect);
215 void RenderVideo::layout()
217 RenderMedia::layout();
221 HTMLVideoElement* RenderVideo::videoElement() const
223 ASSERT(node()->hasTagName(videoTag));
224 return static_cast<HTMLVideoElement*>(node());
227 void RenderVideo::updateFromElement()
229 RenderMedia::updateFromElement();
233 void RenderVideo::updatePlayer()
235 updateIntrinsicSize();
237 MediaPlayer* mediaPlayer = mediaElement()->player();
241 if (!videoElement()->inActiveDocument()) {
242 mediaPlayer->setVisible(false);
246 #if USE(ACCELERATED_COMPOSITING)
247 layer()->contentChanged(RenderLayer::VideoChanged);
250 IntRect videoBounds = videoBox();
251 mediaPlayer->setFrameView(document()->view());
252 mediaPlayer->setSize(IntSize(videoBounds.width(), videoBounds.height()));
253 mediaPlayer->setVisible(true);
256 LayoutUnit RenderVideo::computeReplacedLogicalWidth(bool includeMaxWidth) const
258 return RenderReplaced::computeReplacedLogicalWidth(includeMaxWidth);
261 LayoutUnit RenderVideo::computeReplacedLogicalHeight() const
263 return RenderReplaced::computeReplacedLogicalHeight();
266 int RenderVideo::minimumReplacedHeight() const
268 return RenderReplaced::minimumReplacedHeight();
271 #if USE(ACCELERATED_COMPOSITING)
272 bool RenderVideo::supportsAcceleratedRendering() const
274 MediaPlayer* p = mediaElement()->player();
276 return p->supportsAcceleratedRendering();
281 void RenderVideo::acceleratedRenderingStateChanged()
283 MediaPlayer* p = mediaElement()->player();
285 p->acceleratedRenderingStateChanged();
287 #endif // USE(ACCELERATED_COMPOSITING)
289 #if ENABLE(FULLSCREEN_API)
290 static const RenderBlock* rendererPlaceholder(const RenderObject* renderer)
292 RenderObject* parent = renderer->parent();
296 RenderFullScreen* fullScreen = parent->isRenderFullScreen() ? toRenderFullScreen(parent) : 0;
300 return fullScreen->placeholder();
303 LayoutUnit RenderVideo::offsetLeft() const
305 if (const RenderBlock* block = rendererPlaceholder(this))
306 return block->offsetLeft();
307 return RenderMedia::offsetLeft();
310 LayoutUnit RenderVideo::offsetTop() const
312 if (const RenderBlock* block = rendererPlaceholder(this))
313 return block->offsetTop();
314 return RenderMedia::offsetTop();
317 LayoutUnit RenderVideo::offsetWidth() const
319 if (const RenderBlock* block = rendererPlaceholder(this))
320 return block->offsetWidth();
321 return RenderMedia::offsetWidth();
324 LayoutUnit RenderVideo::offsetHeight() const
326 if (const RenderBlock* block = rendererPlaceholder(this))
327 return block->offsetHeight();
328 return RenderMedia::offsetHeight();
332 } // namespace WebCore