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"
33 #include "FrameView.h"
34 #include "GraphicsContext.h"
35 #include "HTMLNames.h"
36 #include "HTMLVideoElement.h"
37 #include "MediaPlayer.h"
39 #include "PaintInfo.h"
40 #include "RenderView.h"
42 #if ENABLE(FULLSCREEN_API)
43 #include "RenderFullScreen.h"
48 using namespace HTMLNames;
50 RenderVideo::RenderVideo(HTMLVideoElement* video)
53 setIntrinsicSize(calculateIntrinsicSize());
56 RenderVideo::~RenderVideo()
58 if (MediaPlayer* p = mediaElement()->player()) {
64 IntSize RenderVideo::defaultSize()
66 // These values are specified in the spec.
67 static const int cDefaultWidth = 300;
68 static const int cDefaultHeight = 150;
70 return IntSize(cDefaultWidth, cDefaultHeight);
73 void RenderVideo::intrinsicSizeChanged()
75 if (videoElement()->shouldDisplayPosterImage())
76 RenderMedia::intrinsicSizeChanged();
77 updateIntrinsicSize();
80 void RenderVideo::updateIntrinsicSize()
82 IntSize size = calculateIntrinsicSize();
83 size.scale(style()->effectiveZoom());
85 // Never set the element size to zero when in a media document.
86 if (size.isEmpty() && node()->ownerDocument() && node()->ownerDocument()->isMediaDocument())
89 if (size == intrinsicSize())
92 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
93 // Do not update IntrinsicSize while videoElement is suspended.
94 if (videoElement()->suspended())
98 setIntrinsicSize(size);
99 setPreferredLogicalWidthsDirty(true);
100 setNeedsLayout(true);
103 IntSize RenderVideo::calculateIntrinsicSize()
105 HTMLVideoElement* video = videoElement();
107 // Spec text from 4.8.6
109 // The intrinsic width of a video element's playback area is the intrinsic width
110 // of the video resource, if that is available; otherwise it is the intrinsic
111 // width of the poster frame, if that is available; otherwise it is 300 CSS pixels.
113 // The intrinsic height of a video element's playback area is the intrinsic height
114 // of the video resource, if that is available; otherwise it is the intrinsic
115 // height of the poster frame, if that is available; otherwise it is 150 CSS pixels.
116 MediaPlayer* player = mediaElement()->player();
117 if (player && video->readyState() >= HTMLVideoElement::HAVE_METADATA) {
118 IntSize size = player->naturalSize();
123 if (video->shouldDisplayPosterImage() && !m_cachedImageSize.isEmpty() && !imageResource()->errorOccurred())
124 return m_cachedImageSize;
126 // When the natural size of the video is unavailable, we use the provided
127 // width and height attributes of the video element as the intrinsic size until
128 // better values become available.
129 if (video->hasAttribute(widthAttr) && video->hasAttribute(heightAttr))
130 return IntSize(video->width(), video->height());
132 // <video> in standalone media documents should not use the default 300x150
133 // size since they also have audio-only files. By setting the intrinsic
134 // size to 300x1 the video will resize itself in these cases, and audio will
135 // have the correct height (it needs to be > 0 for controls to render properly).
136 #if ENABLE(TIZEN_GSTREAMER_VIDEO)
137 if (video->ownerDocument() && video->ownerDocument()->isMediaDocument() && player && !player->hasVideo())
139 if (video->ownerDocument() && video->ownerDocument()->isMediaDocument())
141 return IntSize(defaultSize().width(), 1);
143 return defaultSize();
146 void RenderVideo::imageChanged(WrappedImagePtr newImage, const IntRect* rect)
148 RenderMedia::imageChanged(newImage, rect);
150 // Cache the image intrinsic size so we can continue to use it to draw the image correctly
151 // even if we know the video intrinsic size but aren't able to draw video frames yet
152 // (we don't want to scale the poster to the video size without keeping aspect ratio).
153 if (videoElement()->shouldDisplayPosterImage())
154 m_cachedImageSize = intrinsicSize();
156 // The intrinsic size is now that of the image, but in case we already had the
157 // intrinsic size of the video we call this here to restore the video size.
158 updateIntrinsicSize();
161 IntRect RenderVideo::videoBox() const
163 if (m_cachedImageSize.isEmpty() && videoElement()->shouldDisplayPosterImage())
167 if (videoElement()->shouldDisplayPosterImage())
168 elementSize = m_cachedImageSize;
170 elementSize = intrinsicSize();
172 IntRect contentRect = pixelSnappedIntRect(contentBoxRect());
173 if (elementSize.isEmpty() || contentRect.isEmpty())
176 IntRect renderBox = contentRect;
177 int ratio = renderBox.width() * elementSize.height() - renderBox.height() * elementSize.width();
179 int newWidth = renderBox.height() * elementSize.width() / elementSize.height();
180 // Just fill the whole area if the difference is one pixel or less (in both sides)
181 if (renderBox.width() - newWidth > 2)
182 renderBox.setWidth(newWidth);
183 renderBox.move((contentRect.width() - renderBox.width()) / 2, 0);
184 } else if (ratio < 0) {
185 int newHeight = renderBox.width() * elementSize.height() / elementSize.width();
186 if (renderBox.height() - newHeight > 2)
187 renderBox.setHeight(newHeight);
188 renderBox.move(0, (contentRect.height() - renderBox.height()) / 2);
194 bool RenderVideo::shouldDisplayVideo() const
196 return !videoElement()->shouldDisplayPosterImage();
199 void RenderVideo::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
201 MediaPlayer* mediaPlayer = mediaElement()->player();
202 bool displayingPoster = videoElement()->shouldDisplayPosterImage();
205 if (Frame* frame = this->frame())
206 page = frame->page();
208 if (!displayingPoster) {
210 if (page && paintInfo.phase == PaintPhaseForeground)
211 page->addRelevantUnpaintedObject(this, visualOverflowRect());
217 LayoutRect rect = videoBox();
218 if (rect.isEmpty()) {
219 if (page && paintInfo.phase == PaintPhaseForeground)
220 page->addRelevantUnpaintedObject(this, visualOverflowRect());
223 rect.moveBy(paintOffset);
225 if (page && paintInfo.phase == PaintPhaseForeground)
226 page->addRelevantRepaintedObject(this, rect);
228 if (displayingPoster)
229 paintIntoRect(paintInfo.context, rect);
230 else if (document()->view() && document()->view()->paintBehavior() & PaintBehaviorFlattenCompositingLayers)
231 mediaPlayer->paintCurrentFrameInContext(paintInfo.context, pixelSnappedIntRect(rect));
233 mediaPlayer->paint(paintInfo.context, pixelSnappedIntRect(rect));
236 void RenderVideo::layout()
238 RenderMedia::layout();
242 HTMLVideoElement* RenderVideo::videoElement() const
244 ASSERT(node()->hasTagName(videoTag));
245 return static_cast<HTMLVideoElement*>(node());
248 void RenderVideo::updateFromElement()
250 RenderMedia::updateFromElement();
254 void RenderVideo::updatePlayer()
256 updateIntrinsicSize();
258 MediaPlayer* mediaPlayer = mediaElement()->player();
262 if (!videoElement()->inActiveDocument()) {
263 mediaPlayer->setVisible(false);
267 #if USE(ACCELERATED_COMPOSITING)
268 contentChanged(VideoChanged);
271 IntRect videoBounds = videoBox();
272 mediaPlayer->setFrameView(document()->view());
273 mediaPlayer->setSize(IntSize(videoBounds.width(), videoBounds.height()));
274 mediaPlayer->setVisible(true);
277 LayoutUnit RenderVideo::computeReplacedLogicalWidth(bool includeMaxWidth) const
279 return RenderReplaced::computeReplacedLogicalWidth(includeMaxWidth);
282 LayoutUnit RenderVideo::computeReplacedLogicalHeight() const
284 return RenderReplaced::computeReplacedLogicalHeight();
287 int RenderVideo::minimumReplacedHeight() const
289 return RenderReplaced::minimumReplacedHeight();
292 #if USE(ACCELERATED_COMPOSITING)
293 bool RenderVideo::supportsAcceleratedRendering() const
295 MediaPlayer* p = mediaElement()->player();
297 return p->supportsAcceleratedRendering();
302 void RenderVideo::acceleratedRenderingStateChanged()
304 MediaPlayer* p = mediaElement()->player();
306 p->acceleratedRenderingStateChanged();
308 #endif // USE(ACCELERATED_COMPOSITING)
310 #if ENABLE(FULLSCREEN_API)
311 static const RenderBlock* rendererPlaceholder(const RenderObject* renderer)
313 RenderObject* parent = renderer->parent();
317 RenderFullScreen* fullScreen = parent->isRenderFullScreen() ? toRenderFullScreen(parent) : 0;
321 return fullScreen->placeholder();
324 LayoutUnit RenderVideo::offsetLeft() const
326 if (const RenderBlock* block = rendererPlaceholder(this))
327 return block->offsetLeft();
328 return RenderMedia::offsetLeft();
331 LayoutUnit RenderVideo::offsetTop() const
333 if (const RenderBlock* block = rendererPlaceholder(this))
334 return block->offsetTop();
335 return RenderMedia::offsetTop();
338 LayoutUnit RenderVideo::offsetWidth() const
340 if (const RenderBlock* block = rendererPlaceholder(this))
341 return block->offsetWidth();
342 return RenderMedia::offsetWidth();
345 LayoutUnit RenderVideo::offsetHeight() const
347 if (const RenderBlock* block = rendererPlaceholder(this))
348 return block->offsetHeight();
349 return RenderMedia::offsetHeight();
353 } // namespace WebCore