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 "HTMLVideoElement.h"
30
31#include "Attribute.h"
32#include "CSSPropertyNames.h"
33#include "Chrome.h"
34#include "ChromeClient.h"
35#include "Document.h"
36#include "ExceptionCode.h"
37#include "HTMLImageLoader.h"
38#include "HTMLNames.h"
39#include "Page.h"
40#include "RenderImage.h"
41#include "RenderVideo.h"
42
43namespace WebCore {
44
45using namespace HTMLNames;
46
47inline HTMLVideoElement::HTMLVideoElement(const QualifiedName& tagName, Document* document)
48    : HTMLMediaElement(tagName, document)
49{
50    ASSERT(hasTagName(videoTag));
51}
52
53PassRefPtr<HTMLVideoElement> HTMLVideoElement::create(const QualifiedName& tagName, Document* document)
54{
55    return adoptRef(new HTMLVideoElement(tagName, document));
56}
57
58bool HTMLVideoElement::rendererIsNeeded(RenderStyle* style)
59{
60    return HTMLElement::rendererIsNeeded(style);
61}
62
63#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
64RenderObject* HTMLVideoElement::createRenderer(RenderArena* arena, RenderStyle*)
65{
66    return new (arena) RenderVideo(this);
67}
68#endif
69
70void HTMLVideoElement::attach()
71{
72    HTMLMediaElement::attach();
73
74#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
75    updateDisplayState();
76    if (shouldDisplayPosterImage()) {
77        if (!m_imageLoader)
78            m_imageLoader = adoptPtr(new HTMLImageLoader(this));
79        m_imageLoader->updateFromElement();
80        if (renderer())
81            toRenderImage(renderer())->imageResource()->setCachedImage(m_imageLoader->image());
82    }
83#endif
84}
85
86void HTMLVideoElement::detach()
87{
88    HTMLMediaElement::detach();
89
90    if (!shouldDisplayPosterImage() && m_imageLoader)
91        m_imageLoader.clear();
92}
93
94void HTMLVideoElement::parseMappedAttribute(Attribute* attr)
95{
96    const QualifiedName& attrName = attr->name();
97
98    if (attrName == posterAttr) {
99        // Force a poster recalc by setting m_displayMode to Unknown directly before calling updateDisplayState.
100        HTMLMediaElement::setDisplayMode(Unknown);
101        updateDisplayState();
102#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
103        if (shouldDisplayPosterImage()) {
104            if (!m_imageLoader)
105                m_imageLoader = adoptPtr(new HTMLImageLoader(this));
106            m_imageLoader->updateFromElementIgnoringPreviousError();
107        } else {
108            if (m_imageLoader)
109                m_imageLoader.clear();
110            if (renderer())
111                toRenderImage(renderer())->imageResource()->setCachedImage(0);
112        }
113#endif
114    } else if (attrName == widthAttr)
115        addCSSLength(attr, CSSPropertyWidth, attr->value());
116    else if (attrName == heightAttr)
117        addCSSLength(attr, CSSPropertyHeight, attr->value());
118    else
119        HTMLMediaElement::parseMappedAttribute(attr);
120}
121
122bool HTMLVideoElement::supportsFullscreen() const
123{
124    Page* page = document() ? document()->page() : 0;
125    if (!page)
126        return false;
127
128    if (!player() || !player()->supportsFullscreen() || !player()->hasVideo())
129        return false;
130
131    // Check with the platform client.
132#if ENABLE(FULLSCREEN_API)
133    if (page->chrome()->client()->supportsFullScreenForElement(this, false))
134        return true;
135#endif
136
137    return page->chrome()->client()->supportsFullscreenForNode(this);
138}
139
140unsigned HTMLVideoElement::videoWidth() const
141{
142    if (!player())
143        return 0;
144    return player()->naturalSize().width();
145}
146
147unsigned HTMLVideoElement::videoHeight() const
148{
149    if (!player())
150        return 0;
151    return player()->naturalSize().height();
152}
153
154unsigned HTMLVideoElement::width() const
155{
156    bool ok;
157    unsigned w = getAttribute(widthAttr).string().toUInt(&ok);
158    return ok ? w : 0;
159}
160
161unsigned HTMLVideoElement::height() const
162{
163    bool ok;
164    unsigned h = getAttribute(heightAttr).string().toUInt(&ok);
165    return ok ? h : 0;
166}
167
168bool HTMLVideoElement::isURLAttribute(Attribute* attribute) const
169{
170    return HTMLMediaElement::isURLAttribute(attribute)
171        || attribute->name() == posterAttr;
172}
173
174const QualifiedName& HTMLVideoElement::imageSourceAttributeName() const
175{
176    return posterAttr;
177}
178
179void HTMLVideoElement::setDisplayMode(DisplayMode mode)
180{
181    DisplayMode oldMode = displayMode();
182    KURL poster = getNonEmptyURLAttribute(posterAttr);
183
184    if (!poster.isEmpty()) {
185        // We have a poster path, but only show it until the user triggers display by playing or seeking and the
186        // media engine has something to display.
187        if (mode == Video) {
188            if (oldMode != Video && player())
189                player()->prepareForRendering();
190            if (!hasAvailableVideoFrame())
191                mode = PosterWaitingForVideo;
192        }
193    } else if (oldMode != Video && player())
194        player()->prepareForRendering();
195
196    HTMLMediaElement::setDisplayMode(mode);
197
198    if (player() && player()->canLoadPoster())
199        player()->setPoster(poster);
200
201#if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
202    if (renderer() && displayMode() != oldMode)
203        renderer()->updateFromElement();
204#endif
205}
206
207void HTMLVideoElement::updateDisplayState()
208{
209    if (getNonEmptyURLAttribute(posterAttr).isEmpty())
210        setDisplayMode(Video);
211    else if (displayMode() < Poster)
212        setDisplayMode(Poster);
213}
214
215void HTMLVideoElement::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& destRect)
216{
217    MediaPlayer* player = HTMLMediaElement::player();
218    if (!player)
219        return;
220
221    player->setVisible(true); // Make player visible or it won't draw.
222    player->paintCurrentFrameInContext(context, destRect);
223}
224
225bool HTMLVideoElement::hasAvailableVideoFrame() const
226{
227    if (!player())
228        return false;
229
230    return player()->hasAvailableVideoFrame();
231}
232
233void HTMLVideoElement::webkitEnterFullscreen(bool isUserGesture, ExceptionCode& ec)
234{
235    if (isFullscreen())
236        return;
237
238    // Generate an exception if this isn't called in response to a user gesture, or if the
239    // element does not support fullscreen.
240    if ((requireUserGestureForFullScreen() && !isUserGesture) || !supportsFullscreen()) {
241        ec = INVALID_STATE_ERR;
242        return;
243    }
244
245    enterFullscreen();
246}
247
248void HTMLVideoElement::webkitExitFullscreen()
249{
250    if (isFullscreen())
251        exitFullscreen();
252}
253
254bool HTMLVideoElement::webkitSupportsFullscreen()
255{
256    return supportsFullscreen();
257}
258
259bool HTMLVideoElement::webkitDisplayingFullscreen()
260{
261    return isFullscreen();
262}
263
264void HTMLVideoElement::willMoveToNewOwnerDocument()
265{
266    if (m_imageLoader)
267        m_imageLoader->elementWillMoveToNewOwnerDocument();
268    HTMLMediaElement::willMoveToNewOwnerDocument();
269}
270
271#if ENABLE(MEDIA_STATISTICS)
272unsigned HTMLVideoElement::webkitDecodedFrameCount() const
273{
274    if (!player())
275        return 0;
276
277    return player()->decodedFrameCount();
278}
279
280unsigned HTMLVideoElement::webkitDroppedFrameCount() const
281{
282    if (!player())
283        return 0;
284
285    return player()->droppedFrameCount();
286}
287#endif
288
289}
290
291#endif
292