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