Add the default video poster if it doesn't exist in video tag
[WebKit-https.git] / Source / WebCore / html / HTMLVideoElement.cpp
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 #if ENABLE(VIDEO)
28 #include "HTMLVideoElement.h"
29
30 #include "Attribute.h"
31 #include "CSSPropertyNames.h"
32 #include "Chrome.h"
33 #include "ChromeClient.h"
34 #include "Document.h"
35 #include "ExceptionCode.h"
36 #include "Frame.h"
37 #include "HTMLImageLoader.h"
38 #include "HTMLNames.h"
39 #include "HTMLParserIdioms.h"
40 #include "Page.h"
41 #include "RenderImage.h"
42 #include "RenderVideo.h"
43 #include "ScriptController.h"
44 #include "Settings.h"
45
46 namespace WebCore {
47
48 using namespace HTMLNames;
49
50 inline HTMLVideoElement::HTMLVideoElement(const QualifiedName& tagName, Document* document, bool createdByParser)
51     : HTMLMediaElement(tagName, document, createdByParser)
52 {
53     ASSERT(hasTagName(videoTag));
54     if (document->settings())
55         m_defaultPosterURL = document->settings()->defaultVideoPosterURL();
56 }
57
58 PassRefPtr<HTMLVideoElement> HTMLVideoElement::create(const QualifiedName& tagName, Document* document, bool createdByParser)
59 {
60     RefPtr<HTMLVideoElement> videoElement(adoptRef(new HTMLVideoElement(tagName, document, createdByParser)));
61     videoElement->suspendIfNeeded();
62     return videoElement.release();
63 }
64
65 bool HTMLVideoElement::rendererIsNeeded(const NodeRenderingContext& context) 
66 {
67     return HTMLElement::rendererIsNeeded(context); 
68 }
69
70 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
71 RenderObject* HTMLVideoElement::createRenderer(RenderArena* arena, RenderStyle*)
72 {
73     return new (arena) RenderVideo(this);
74 }
75 #endif
76
77 void HTMLVideoElement::attach()
78 {
79     HTMLMediaElement::attach();
80
81 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
82     updateDisplayState();
83     if (shouldDisplayPosterImage()) {
84         if (!m_imageLoader)
85             m_imageLoader = adoptPtr(new HTMLImageLoader(this));
86         m_imageLoader->updateFromElement();
87         if (renderer())
88             toRenderImage(renderer())->imageResource()->setCachedImage(m_imageLoader->image()); 
89     }
90 #endif
91 }
92
93 void HTMLVideoElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStylePropertySet* style)
94 {
95     if (name == widthAttr)
96         addHTMLLengthToStyle(style, CSSPropertyWidth, value);
97     else if (name == heightAttr)
98         addHTMLLengthToStyle(style, CSSPropertyHeight, value);
99     else
100         HTMLMediaElement::collectStyleForPresentationAttribute(name, value, style);
101 }
102
103 bool HTMLVideoElement::isPresentationAttribute(const QualifiedName& name) const
104 {
105     if (name == widthAttr || name == heightAttr)
106         return true;
107     return HTMLMediaElement::isPresentationAttribute(name);
108 }
109
110 void HTMLVideoElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
111 {
112     if (name == posterAttr) {
113         // Force a poster recalc by setting m_displayMode to Unknown directly before calling updateDisplayState.
114         HTMLMediaElement::setDisplayMode(Unknown);
115         updateDisplayState();
116 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
117         if (shouldDisplayPosterImage()) {
118             if (!m_imageLoader)
119                 m_imageLoader = adoptPtr(new HTMLImageLoader(this));
120             m_imageLoader->updateFromElementIgnoringPreviousError();
121         } else {
122             if (renderer())
123                 toRenderImage(renderer())->imageResource()->setCachedImage(0); 
124         }
125 #endif
126     } else
127         HTMLMediaElement::parseAttribute(name, value);
128 }
129
130 bool HTMLVideoElement::supportsFullscreen() const
131 {
132     Page* page = document() ? document()->page() : 0;
133     if (!page) 
134         return false;
135
136     if (!player() || !player()->supportsFullscreen())
137         return false;
138
139 #if ENABLE(FULLSCREEN_API)
140     // If the full screen API is enabled and is supported for the current element
141     // do not require that the player has a video track to enter full screen.
142     if (page->chrome()->client()->supportsFullScreenForElement(this, false))
143         return true;
144 #endif
145
146     if (!player()->hasVideo())
147         return false;
148
149     return page->chrome()->client()->supportsFullscreenForNode(this);
150 }
151
152 unsigned HTMLVideoElement::videoWidth() const
153 {
154     if (!player())
155         return 0;
156     return player()->naturalSize().width();
157 }
158
159 unsigned HTMLVideoElement::videoHeight() const
160 {
161     if (!player())
162         return 0;
163     return player()->naturalSize().height();
164 }
165
166 unsigned HTMLVideoElement::width() const
167 {
168     bool ok;
169     unsigned w = getAttribute(widthAttr).string().toUInt(&ok);
170     return ok ? w : 0;
171 }
172     
173 unsigned HTMLVideoElement::height() const
174 {
175     bool ok;
176     unsigned h = getAttribute(heightAttr).string().toUInt(&ok);
177     return ok ? h : 0;
178 }
179     
180 bool HTMLVideoElement::isURLAttribute(const Attribute& attribute) const
181 {
182     return attribute.name() == posterAttr || HTMLMediaElement::isURLAttribute(attribute);
183 }
184
185 const AtomicString& HTMLVideoElement::imageSourceURL() const
186 {
187     const AtomicString& url = getAttribute(posterAttr);
188     if (!stripLeadingAndTrailingHTMLSpaces(url).isEmpty())
189         return url;
190     return m_defaultPosterURL;
191 }
192
193 void HTMLVideoElement::setDisplayMode(DisplayMode mode)
194 {
195     DisplayMode oldMode = displayMode();
196     KURL poster = posterImageURL();
197
198     if (!poster.isEmpty()) {
199         // We have a poster path, but only show it until the user triggers display by playing or seeking and the
200         // media engine has something to display.
201         if (mode == Video) {
202             if (oldMode != Video && player())
203                 player()->prepareForRendering();
204             if (!hasAvailableVideoFrame())
205                 mode = PosterWaitingForVideo;
206         }
207     } else if (oldMode != Video && player())
208         player()->prepareForRendering();
209
210     HTMLMediaElement::setDisplayMode(mode);
211
212     if (player() && player()->canLoadPoster()) {
213         bool canLoad = true;
214         if (!poster.isEmpty()) {
215             Frame* frame = document()->frame();
216             FrameLoader* loader = frame ? frame->loader() : 0;
217             canLoad = loader && loader->willLoadMediaElementURL(poster);
218         }
219         if (canLoad)
220             player()->setPoster(poster);
221     }
222
223 #if !ENABLE(PLUGIN_PROXY_FOR_VIDEO)
224     if (renderer() && displayMode() != oldMode)
225         renderer()->updateFromElement();
226 #endif
227 }
228
229 void HTMLVideoElement::updateDisplayState()
230 {
231     if (posterImageURL().isEmpty())
232         setDisplayMode(Video);
233     else if (displayMode() < Poster)
234         setDisplayMode(Poster);
235 }
236
237 void HTMLVideoElement::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& destRect)
238 {
239     MediaPlayer* player = HTMLMediaElement::player();
240     if (!player)
241         return;
242     
243     player->setVisible(true); // Make player visible or it won't draw.
244     player->paintCurrentFrameInContext(context, destRect);
245 }
246
247 bool HTMLVideoElement::copyVideoTextureToPlatformTexture(GraphicsContext3D* context, Platform3DObject texture, GC3Dint level, GC3Denum type, GC3Denum internalFormat, bool premultiplyAlpha, bool flipY)
248 {
249     if (!player())
250         return false;
251     return player()->copyVideoTextureToPlatformTexture(context, texture, level, type, internalFormat, premultiplyAlpha, flipY);
252 }
253
254 bool HTMLVideoElement::hasAvailableVideoFrame() const
255 {
256     if (!player())
257         return false;
258     
259     return player()->hasVideo() && player()->hasAvailableVideoFrame();
260 }
261
262 void HTMLVideoElement::webkitEnterFullscreen(ExceptionCode& ec)
263 {
264     if (isFullscreen())
265         return;
266
267     // Generate an exception if this isn't called in response to a user gesture, or if the 
268     // element does not support fullscreen.
269     if ((userGestureRequiredForFullscreen() && !ScriptController::processingUserGesture()) || !supportsFullscreen()) {
270         ec = INVALID_STATE_ERR;
271         return;
272     }
273
274     enterFullscreen();
275 }
276
277 void HTMLVideoElement::webkitExitFullscreen()
278 {
279     if (isFullscreen())
280         exitFullscreen();
281 }
282
283 bool HTMLVideoElement::webkitSupportsFullscreen()
284 {
285     return supportsFullscreen();
286 }
287
288 bool HTMLVideoElement::webkitDisplayingFullscreen()
289 {
290     return isFullscreen();
291 }
292
293 void HTMLVideoElement::didMoveToNewDocument(Document* oldDocument)
294 {
295     if (m_imageLoader)
296         m_imageLoader->elementDidMoveToNewDocument();
297     HTMLMediaElement::didMoveToNewDocument(oldDocument);
298 }
299
300 #if ENABLE(MEDIA_STATISTICS)
301 unsigned HTMLVideoElement::webkitDecodedFrameCount() const
302 {
303     if (!player())
304         return 0;
305
306     return player()->decodedFrameCount();
307 }
308
309 unsigned HTMLVideoElement::webkitDroppedFrameCount() const
310 {
311     if (!player())
312         return 0;
313
314     return player()->droppedFrameCount();
315 }
316 #endif
317
318 KURL HTMLVideoElement::posterImageURL() const
319 {
320     const AtomicString& url = stripLeadingAndTrailingHTMLSpaces(imageSourceURL());
321     if (url.isEmpty())
322         return KURL();
323     return document()->completeURL(url);
324 }
325
326 }
327
328 #endif