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