Home | History | Annotate | Download | only in rendering
      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 
     44 RenderVideo::RenderVideo(HTMLVideoElement* video)
     45     : RenderMedia(video)
     46 {
     47     setIntrinsicSize(calculateIntrinsicSize());
     48 }
     49 
     50 RenderVideo::~RenderVideo()
     51 {
     52 }
     53 
     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 
     63 void RenderVideo::intrinsicSizeChanged()
     64 {
     65     if (videoElement()->shouldDisplayPosterImage())
     66         RenderMedia::intrinsicSizeChanged();
     67     updateIntrinsicSize();
     68 }
     69 
     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 
     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 
    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 
    135 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 
    144 bool RenderVideo::shouldDisplayVideo() const
    145 {
    146     return !videoElement()->shouldDisplayPosterImage();
    147 }
    148 
    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 
    179 bool RenderVideo::acceleratedRenderingInUse()
    180 {
    181     blink::WebLayer* webLayer = mediaElement()->platformLayer();
    182     return webLayer && !webLayer->isOrphan();
    183 }
    184 
    185 void RenderVideo::layout()
    186 {
    187     updatePlayer();
    188     RenderMedia::layout();
    189 }
    190 
    191 HTMLVideoElement* RenderVideo::videoElement() const
    192 {
    193     return toHTMLVideoElement(node());
    194 }
    195 
    196 void RenderVideo::updateFromElement()
    197 {
    198     RenderMedia::updateFromElement();
    199     updatePlayer();
    200 }
    201 
    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 
    216 LayoutUnit RenderVideo::computeReplacedLogicalWidth(ShouldComputePreferred shouldComputePreferred) const
    217 {
    218     return RenderReplaced::computeReplacedLogicalWidth(shouldComputePreferred);
    219 }
    220 
    221 LayoutUnit RenderVideo::computeReplacedLogicalHeight() const
    222 {
    223     return RenderReplaced::computeReplacedLogicalHeight();
    224 }
    225 
    226 LayoutUnit RenderVideo::minimumReplacedHeight() const
    227 {
    228     return RenderReplaced::minimumReplacedHeight();
    229 }
    230 
    231 bool RenderVideo::supportsAcceleratedRendering() const
    232 {
    233     return !!mediaElement()->platformLayer();
    234 }
    235 
    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 
    249 LayoutUnit RenderVideo::offsetLeft() const
    250 {
    251     if (const RenderBlock* block = rendererPlaceholder(this))
    252         return block->offsetLeft();
    253     return RenderMedia::offsetLeft();
    254 }
    255 
    256 LayoutUnit RenderVideo::offsetTop() const
    257 {
    258     if (const RenderBlock* block = rendererPlaceholder(this))
    259         return block->offsetTop();
    260     return RenderMedia::offsetTop();
    261 }
    262 
    263 LayoutUnit RenderVideo::offsetWidth() const
    264 {
    265     if (const RenderBlock* block = rendererPlaceholder(this))
    266         return block->offsetWidth();
    267     return RenderMedia::offsetWidth();
    268 }
    269 
    270 LayoutUnit RenderVideo::offsetHeight() const
    271 {
    272     if (const RenderBlock* block = rendererPlaceholder(this))
    273         return block->offsetHeight();
    274     return RenderMedia::offsetHeight();
    275 }
    276 
    277 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