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#include "core/rendering/RenderVideo.h"
29
30#include "core/HTMLNames.h"
31#include "core/dom/Document.h"
32#include "core/frame/FrameView.h"
33#include "core/frame/LocalFrame.h"
34#include "core/html/HTMLVideoElement.h"
35#include "core/paint/VideoPainter.h"
36#include "core/rendering/PaintInfo.h"
37#include "core/rendering/RenderFullScreen.h"
38#include "platform/graphics/media/MediaPlayer.h"
39#include "public/platform/WebLayer.h"
40
41namespace blink {
42
43using namespace HTMLNames;
44
45RenderVideo::RenderVideo(HTMLVideoElement* video)
46    : RenderMedia(video)
47{
48    setIntrinsicSize(calculateIntrinsicSize());
49}
50
51RenderVideo::~RenderVideo()
52{
53}
54
55IntSize RenderVideo::defaultSize()
56{
57    return IntSize(defaultWidth, defaultHeight);
58}
59
60void RenderVideo::intrinsicSizeChanged()
61{
62    if (videoElement()->shouldDisplayPosterImage())
63        RenderMedia::intrinsicSizeChanged();
64    updateIntrinsicSize();
65}
66
67void RenderVideo::updateIntrinsicSize()
68{
69    LayoutSize size = calculateIntrinsicSize();
70    size.scale(style()->effectiveZoom());
71
72    // Never set the element size to zero when in a media document.
73    if (size.isEmpty() && node()->ownerDocument() && node()->ownerDocument()->isMediaDocument())
74        return;
75
76    if (size == intrinsicSize())
77        return;
78
79    setIntrinsicSize(size);
80    setPreferredLogicalWidthsDirty();
81    setNeedsLayoutAndFullPaintInvalidation();
82}
83
84LayoutSize RenderVideo::calculateIntrinsicSize()
85{
86    HTMLVideoElement* video = videoElement();
87
88    // Spec text from 4.8.6
89    //
90    // The intrinsic width of a video element's playback area is the intrinsic width
91    // of the video resource, if that is available; otherwise it is the intrinsic
92    // width of the poster frame, if that is available; otherwise it is 300 CSS pixels.
93    //
94    // The intrinsic height of a video element's playback area is the intrinsic height
95    // of the video resource, if that is available; otherwise it is the intrinsic
96    // height of the poster frame, if that is available; otherwise it is 150 CSS pixels.
97    WebMediaPlayer* webMediaPlayer = mediaElement()->webMediaPlayer();
98    if (webMediaPlayer && video->readyState() >= HTMLVideoElement::HAVE_METADATA) {
99        IntSize size = webMediaPlayer->naturalSize();
100        if (!size.isEmpty())
101            return size;
102    }
103
104    if (video->shouldDisplayPosterImage() && !m_cachedImageSize.isEmpty() && !imageResource()->errorOccurred())
105        return m_cachedImageSize;
106
107    // <video> in standalone media documents should not use the default 300x150
108    // size since they also have audio-only files. By setting the intrinsic
109    // size to 300x1 the video will resize itself in these cases, and audio will
110    // have the correct height (it needs to be > 0 for controls to render properly).
111    if (video->ownerDocument() && video->ownerDocument()->isMediaDocument())
112        return LayoutSize(defaultSize().width(), 1);
113
114    return defaultSize();
115}
116
117void RenderVideo::imageChanged(WrappedImagePtr newImage, const IntRect* rect)
118{
119    RenderMedia::imageChanged(newImage, rect);
120
121    // Cache the image intrinsic size so we can continue to use it to draw the image correctly
122    // even if we know the video intrinsic size but aren't able to draw video frames yet
123    // (we don't want to scale the poster to the video size without keeping aspect ratio).
124    if (videoElement()->shouldDisplayPosterImage())
125        m_cachedImageSize = intrinsicSize();
126
127    // The intrinsic size is now that of the image, but in case we already had the
128    // intrinsic size of the video we call this here to restore the video size.
129    updateIntrinsicSize();
130}
131
132IntRect RenderVideo::videoBox() const
133{
134    const LayoutSize* overriddenIntrinsicSize = 0;
135    if (videoElement()->shouldDisplayPosterImage())
136        overriddenIntrinsicSize = &m_cachedImageSize;
137
138    return pixelSnappedIntRect(replacedContentRect(overriddenIntrinsicSize));
139}
140
141bool RenderVideo::shouldDisplayVideo() const
142{
143    return !videoElement()->shouldDisplayPosterImage();
144}
145
146void RenderVideo::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
147{
148    VideoPainter(*this).paintReplaced(paintInfo, paintOffset);
149}
150
151bool RenderVideo::acceleratedRenderingInUse()
152{
153    WebLayer* webLayer = mediaElement()->platformLayer();
154    return webLayer && !webLayer->isOrphan();
155}
156
157void RenderVideo::layout()
158{
159    updatePlayer();
160    RenderMedia::layout();
161}
162
163HTMLVideoElement* RenderVideo::videoElement() const
164{
165    return toHTMLVideoElement(node());
166}
167
168void RenderVideo::updateFromElement()
169{
170    RenderMedia::updateFromElement();
171    updatePlayer();
172}
173
174void RenderVideo::updatePlayer()
175{
176    updateIntrinsicSize();
177
178    WebMediaPlayer* mediaPlayer = mediaElement()->webMediaPlayer();
179    if (!mediaPlayer)
180        return;
181
182    if (!videoElement()->isActive())
183        return;
184
185    videoElement()->setNeedsCompositingUpdate();
186}
187
188LayoutUnit RenderVideo::computeReplacedLogicalWidth(ShouldComputePreferred shouldComputePreferred) const
189{
190    return RenderReplaced::computeReplacedLogicalWidth(shouldComputePreferred);
191}
192
193LayoutUnit RenderVideo::computeReplacedLogicalHeight() const
194{
195    return RenderReplaced::computeReplacedLogicalHeight();
196}
197
198LayoutUnit RenderVideo::minimumReplacedHeight() const
199{
200    return RenderReplaced::minimumReplacedHeight();
201}
202
203bool RenderVideo::supportsAcceleratedRendering() const
204{
205    return !!mediaElement()->platformLayer();
206}
207
208static const RenderBlock* rendererPlaceholder(const RenderObject* renderer)
209{
210    RenderObject* parent = renderer->parent();
211    if (!parent)
212        return 0;
213
214    RenderFullScreen* fullScreen = parent->isRenderFullScreen() ? toRenderFullScreen(parent) : 0;
215    if (!fullScreen)
216        return 0;
217
218    return fullScreen->placeholder();
219}
220
221LayoutUnit RenderVideo::offsetLeft() const
222{
223    if (const RenderBlock* block = rendererPlaceholder(this))
224        return block->offsetLeft();
225    return RenderMedia::offsetLeft();
226}
227
228LayoutUnit RenderVideo::offsetTop() const
229{
230    if (const RenderBlock* block = rendererPlaceholder(this))
231        return block->offsetTop();
232    return RenderMedia::offsetTop();
233}
234
235LayoutUnit RenderVideo::offsetWidth() const
236{
237    if (const RenderBlock* block = rendererPlaceholder(this))
238        return block->offsetWidth();
239    return RenderMedia::offsetWidth();
240}
241
242LayoutUnit RenderVideo::offsetHeight() const
243{
244    if (const RenderBlock* block = rendererPlaceholder(this))
245        return block->offsetHeight();
246    return RenderMedia::offsetHeight();
247}
248
249CompositingReasons RenderVideo::additionalCompositingReasons() const
250{
251    if (RuntimeEnabledFeatures::overlayFullscreenVideoEnabled()) {
252        HTMLMediaElement* media = toHTMLMediaElement(node());
253        if (media->isFullscreen())
254            return CompositingReasonVideo;
255    }
256
257    if (shouldDisplayVideo() && supportsAcceleratedRendering())
258        return CompositingReasonVideo;
259
260    return CompositingReasonNone;
261}
262
263} // namespace blink
264