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/page/Frame.h"
     34 #include "core/page/FrameView.h"
     35 #include "core/page/Page.h"
     36 #include "core/platform/graphics/MediaPlayer.h"
     37 #include "core/rendering/PaintInfo.h"
     38 #include "core/rendering/RenderFullScreen.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(true);
     84     setNeedsLayout();
     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     MediaPlayer* player = mediaElement()->player();
    101     if (player && video->readyState() >= HTMLVideoElement::HAVE_METADATA) {
    102         LayoutSize size = player->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     // When the natural size of the video is unavailable, we use the provided
    111     // width and height attributes of the video element as the intrinsic size until
    112     // better values become available.
    113     if (video->hasAttribute(widthAttr) && video->hasAttribute(heightAttr))
    114         return LayoutSize(video->width(), video->height());
    115 
    116     // <video> in standalone media documents should not use the default 300x150
    117     // size since they also have audio-only files. By setting the intrinsic
    118     // size to 300x1 the video will resize itself in these cases, and audio will
    119     // have the correct height (it needs to be > 0 for controls to render properly).
    120     if (video->ownerDocument() && video->ownerDocument()->isMediaDocument())
    121         return LayoutSize(defaultSize().width(), 1);
    122 
    123     return defaultSize();
    124 }
    125 
    126 void RenderVideo::imageChanged(WrappedImagePtr newImage, const IntRect* rect)
    127 {
    128     RenderMedia::imageChanged(newImage, rect);
    129 
    130     // Cache the image intrinsic size so we can continue to use it to draw the image correctly
    131     // even if we know the video intrinsic size but aren't able to draw video frames yet
    132     // (we don't want to scale the poster to the video size without keeping aspect ratio).
    133     if (videoElement()->shouldDisplayPosterImage())
    134         m_cachedImageSize = intrinsicSize();
    135 
    136     // The intrinsic size is now that of the image, but in case we already had the
    137     // intrinsic size of the video we call this here to restore the video size.
    138     updateIntrinsicSize();
    139 }
    140 
    141 IntRect RenderVideo::videoBox() const
    142 {
    143     if (m_cachedImageSize.isEmpty() && videoElement()->shouldDisplayPosterImage())
    144         return IntRect();
    145 
    146     LayoutSize elementSize;
    147     if (videoElement()->shouldDisplayPosterImage())
    148         elementSize = m_cachedImageSize;
    149     else
    150         elementSize = intrinsicSize();
    151 
    152     IntRect contentRect = pixelSnappedIntRect(contentBoxRect());
    153     if (elementSize.isEmpty() || contentRect.isEmpty())
    154         return IntRect();
    155 
    156     LayoutRect renderBox = contentRect;
    157     LayoutUnit ratio = renderBox.width() * elementSize.height() - renderBox.height() * elementSize.width();
    158     if (ratio > 0) {
    159         LayoutUnit newWidth = renderBox.height() * elementSize.width() / elementSize.height();
    160         // Just fill the whole area if the difference is one pixel or less (in both sides)
    161         if (renderBox.width() - newWidth > 2)
    162             renderBox.setWidth(newWidth);
    163         renderBox.move((contentRect.width() - renderBox.width()) / 2, 0);
    164     } else if (ratio < 0) {
    165         LayoutUnit newHeight = renderBox.width() * elementSize.height() / elementSize.width();
    166         if (renderBox.height() - newHeight > 2)
    167             renderBox.setHeight(newHeight);
    168         renderBox.move(0, (contentRect.height() - renderBox.height()) / 2);
    169     }
    170 
    171     return pixelSnappedIntRect(renderBox);
    172 }
    173 
    174 bool RenderVideo::shouldDisplayVideo() const
    175 {
    176     return !videoElement()->shouldDisplayPosterImage();
    177 }
    178 
    179 void RenderVideo::paintReplaced(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
    180 {
    181     MediaPlayer* mediaPlayer = mediaElement()->player();
    182     bool displayingPoster = videoElement()->shouldDisplayPosterImage();
    183 
    184     Page* page = 0;
    185     if (Frame* frame = this->frame())
    186         page = frame->page();
    187 
    188     if (!displayingPoster && !mediaPlayer) {
    189         if (page && paintInfo.phase == PaintPhaseForeground)
    190             page->addRelevantUnpaintedObject(this, visualOverflowRect());
    191         return;
    192     }
    193 
    194     LayoutRect rect = videoBox();
    195     if (rect.isEmpty()) {
    196         if (page && paintInfo.phase == PaintPhaseForeground)
    197             page->addRelevantUnpaintedObject(this, visualOverflowRect());
    198         return;
    199     }
    200     rect.moveBy(paintOffset);
    201 
    202     if (page && paintInfo.phase == PaintPhaseForeground)
    203         page->addRelevantRepaintedObject(this, rect);
    204 
    205     if (displayingPoster)
    206         paintIntoRect(paintInfo.context, rect);
    207     else if (document()->view() && document()->view()->paintBehavior() & PaintBehaviorFlattenCompositingLayers)
    208         mediaPlayer->paintCurrentFrameInContext(paintInfo.context, pixelSnappedIntRect(rect));
    209     else
    210         mediaPlayer->paint(paintInfo.context, pixelSnappedIntRect(rect));
    211 }
    212 
    213 void RenderVideo::layout()
    214 {
    215     StackStats::LayoutCheckPoint layoutCheckPoint;
    216     RenderMedia::layout();
    217     updatePlayer();
    218 }
    219 
    220 HTMLVideoElement* RenderVideo::videoElement() const
    221 {
    222     ASSERT(isHTMLVideoElement(node()));
    223     return toHTMLVideoElement(node());
    224 }
    225 
    226 void RenderVideo::updateFromElement()
    227 {
    228     RenderMedia::updateFromElement();
    229     updatePlayer();
    230 }
    231 
    232 void RenderVideo::updatePlayer()
    233 {
    234     updateIntrinsicSize();
    235 
    236     MediaPlayer* mediaPlayer = mediaElement()->player();
    237     if (!mediaPlayer)
    238         return;
    239 
    240     if (!videoElement()->inActiveDocument())
    241         return;
    242 
    243     contentChanged(VideoChanged);
    244 }
    245 
    246 LayoutUnit RenderVideo::computeReplacedLogicalWidth(ShouldComputePreferred shouldComputePreferred) const
    247 {
    248     return RenderReplaced::computeReplacedLogicalWidth(shouldComputePreferred);
    249 }
    250 
    251 LayoutUnit RenderVideo::computeReplacedLogicalHeight() const
    252 {
    253     return RenderReplaced::computeReplacedLogicalHeight();
    254 }
    255 
    256 LayoutUnit RenderVideo::minimumReplacedHeight() const
    257 {
    258     return RenderReplaced::minimumReplacedHeight();
    259 }
    260 
    261 bool RenderVideo::supportsAcceleratedRendering() const
    262 {
    263     MediaPlayer* p = mediaElement()->player();
    264     if (p)
    265         return p->supportsAcceleratedRendering();
    266 
    267     return false;
    268 }
    269 
    270 static const RenderBlock* rendererPlaceholder(const RenderObject* renderer)
    271 {
    272     RenderObject* parent = renderer->parent();
    273     if (!parent)
    274         return 0;
    275 
    276     RenderFullScreen* fullScreen = parent->isRenderFullScreen() ? toRenderFullScreen(parent) : 0;
    277     if (!fullScreen)
    278         return 0;
    279 
    280     return fullScreen->placeholder();
    281 }
    282 
    283 LayoutUnit RenderVideo::offsetLeft() const
    284 {
    285     if (const RenderBlock* block = rendererPlaceholder(this))
    286         return block->offsetLeft();
    287     return RenderMedia::offsetLeft();
    288 }
    289 
    290 LayoutUnit RenderVideo::offsetTop() const
    291 {
    292     if (const RenderBlock* block = rendererPlaceholder(this))
    293         return block->offsetTop();
    294     return RenderMedia::offsetTop();
    295 }
    296 
    297 LayoutUnit RenderVideo::offsetWidth() const
    298 {
    299     if (const RenderBlock* block = rendererPlaceholder(this))
    300         return block->offsetWidth();
    301     return RenderMedia::offsetWidth();
    302 }
    303 
    304 LayoutUnit RenderVideo::offsetHeight() const
    305 {
    306     if (const RenderBlock* block = rendererPlaceholder(this))
    307         return block->offsetHeight();
    308     return RenderMedia::offsetHeight();
    309 }
    310 
    311 } // namespace WebCore
    312