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