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