CTTE: ImageLoader is always owned by an Element.
[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 "Frame.h"
36 #include "HTMLImageLoader.h"
37 #include "HTMLNames.h"
38 #include "HTMLParserIdioms.h"
39 #include "Page.h"
40 #include "RenderImage.h"
41 #include "RenderVideo.h"
42 #include "ScriptController.h"
43 #include "Settings.h"
44
45 namespace WebCore {
46
47 using namespace HTMLNames;
48
49 inline HTMLVideoElement::HTMLVideoElement(const QualifiedName& tagName, Document& document, bool createdByParser)
50     : HTMLMediaElement(tagName, document, createdByParser)
51 {
52     ASSERT(hasTagName(videoTag));
53     setHasCustomStyleResolveCallbacks();
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 RenderStyle& style) 
66 {
67     return HTMLElement::rendererIsNeeded(style); 
68 }
69
70 RenderPtr<RenderElement> HTMLVideoElement::createElementRenderer(PassRef<RenderStyle> style)
71 {
72 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
73     if (shouldUseVideoPluginProxy())
74         return HTMLMediaElement::createElementRenderer(std::move(style));
75 #endif
76     return createRenderer<RenderVideo>(*this, std::move(style));
77 }
78
79 void HTMLVideoElement::didAttachRenderers()
80 {
81     HTMLMediaElement::didAttachRenderers();
82
83 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
84     if (!shouldUseVideoPluginProxy()) {
85 #endif
86         updateDisplayState();
87         if (shouldDisplayPosterImage()) {
88             if (!m_imageLoader)
89                 m_imageLoader = adoptPtr(new HTMLImageLoader(*this));
90             m_imageLoader->updateFromElement();
91             if (renderer())
92                 toRenderImage(renderer())->imageResource().setCachedImage(m_imageLoader->image());
93         }
94 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
95     }
96 #endif
97 }
98
99 void HTMLVideoElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStyleProperties& style)
100 {
101     if (name == widthAttr)
102         addHTMLLengthToStyle(style, CSSPropertyWidth, value);
103     else if (name == heightAttr)
104         addHTMLLengthToStyle(style, CSSPropertyHeight, value);
105     else
106         HTMLMediaElement::collectStyleForPresentationAttribute(name, value, style);
107 }
108
109 bool HTMLVideoElement::isPresentationAttribute(const QualifiedName& name) const
110 {
111     if (name == widthAttr || name == heightAttr)
112         return true;
113     return HTMLMediaElement::isPresentationAttribute(name);
114 }
115
116 void HTMLVideoElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
117 {
118     if (name == posterAttr) {
119         // Force a poster recalc by setting m_displayMode to Unknown directly before calling updateDisplayState.
120         HTMLMediaElement::setDisplayMode(Unknown);
121         updateDisplayState();
122 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
123         if (shouldUseVideoPluginProxy())
124             return;
125 #endif
126         if (shouldDisplayPosterImage()) {
127             if (!m_imageLoader)
128                 m_imageLoader = adoptPtr(new HTMLImageLoader(*this));
129             m_imageLoader->updateFromElementIgnoringPreviousError();
130         } else {
131             if (renderer())
132                 toRenderImage(renderer())->imageResource().setCachedImage(0);
133         }
134     }
135 #if ENABLE(IOS_AIRPLAY)
136     else if (name == webkitwirelessvideoplaybackdisabledAttr) {
137         if (player())
138             player()->setWirelessVideoPlaybackDisabled(webkitWirelessVideoPlaybackDisabled());
139     } else {
140         HTMLMediaElement::parseAttribute(name, value);
141
142         if (name == webkitairplayAttr) {
143             if (player())
144                 player()->setWirelessVideoPlaybackDisabled(webkitWirelessVideoPlaybackDisabled());
145         }
146     }
147 #else
148     else
149         HTMLMediaElement::parseAttribute(name, value);    
150 #endif
151 }
152
153 bool HTMLVideoElement::supportsFullscreen() const
154 {
155     Page* page = document().page();
156     if (!page) 
157         return false;
158
159     if (!player() || !player()->supportsFullscreen())
160         return false;
161
162 #if PLATFORM(IOS)
163     // Fullscreen implemented by player.
164     return true;
165 #else
166 #if ENABLE(FULLSCREEN_API)
167     // If the full screen API is enabled and is supported for the current element
168     // do not require that the player has a video track to enter full screen.
169     if (page->chrome().client().supportsFullScreenForElement(this, false))
170         return true;
171 #endif
172
173     if (!player()->hasVideo())
174         return false;
175
176     return page->chrome().client().supportsFullscreenForNode(this);
177 #endif // PLATFORM(IOS)
178 }
179
180 unsigned HTMLVideoElement::videoWidth() const
181 {
182     if (!player())
183         return 0;
184     return player()->naturalSize().width();
185 }
186
187 unsigned HTMLVideoElement::videoHeight() const
188 {
189     if (!player())
190         return 0;
191     return player()->naturalSize().height();
192 }
193
194 bool HTMLVideoElement::isURLAttribute(const Attribute& attribute) const
195 {
196     return attribute.name() == posterAttr || HTMLMediaElement::isURLAttribute(attribute);
197 }
198
199 const AtomicString& HTMLVideoElement::imageSourceURL() const
200 {
201     const AtomicString& url = getAttribute(posterAttr);
202     if (!stripLeadingAndTrailingHTMLSpaces(url).isEmpty())
203         return url;
204     return m_defaultPosterURL;
205 }
206
207 void HTMLVideoElement::setDisplayMode(DisplayMode mode)
208 {
209     DisplayMode oldMode = displayMode();
210     URL poster = posterImageURL();
211
212     if (!poster.isEmpty()) {
213         // We have a poster path, but only show it until the user triggers display by playing or seeking and the
214         // media engine has something to display.
215         if (mode == Video) {
216             if (oldMode != Video && player())
217                 player()->prepareForRendering();
218             if (!hasAvailableVideoFrame())
219                 mode = PosterWaitingForVideo;
220         }
221     } else if (oldMode != Video && player())
222         player()->prepareForRendering();
223
224     HTMLMediaElement::setDisplayMode(mode);
225
226     if (player() && player()->canLoadPoster()) {
227         bool canLoad = true;
228         if (!poster.isEmpty()) {
229             if (Frame* frame = document().frame())
230                 canLoad = frame->loader().willLoadMediaElementURL(poster);
231         }
232         if (canLoad)
233             player()->setPoster(poster);
234     }
235
236 #if ENABLE(PLUGIN_PROXY_FOR_VIDEO)
237     if (shouldUseVideoPluginProxy())
238         return;
239 #endif
240     if (renderer() && displayMode() != oldMode)
241         renderer()->updateFromElement();
242 }
243
244 void HTMLVideoElement::updateDisplayState()
245 {
246     if (posterImageURL().isEmpty())
247         setDisplayMode(Video);
248     else if (displayMode() < Poster)
249         setDisplayMode(Poster);
250 }
251
252 void HTMLVideoElement::paintCurrentFrameInContext(GraphicsContext* context, const IntRect& destRect)
253 {
254     MediaPlayer* player = HTMLMediaElement::player();
255     if (!player)
256         return;
257     
258     player->setVisible(true); // Make player visible or it won't draw.
259     player->paintCurrentFrameInContext(context, destRect);
260 }
261
262 bool HTMLVideoElement::copyVideoTextureToPlatformTexture(GraphicsContext3D* context, Platform3DObject texture, GC3Dint level, GC3Denum type, GC3Denum internalFormat, bool premultiplyAlpha, bool flipY)
263 {
264     if (!player())
265         return false;
266     return player()->copyVideoTextureToPlatformTexture(context, texture, level, type, internalFormat, premultiplyAlpha, flipY);
267 }
268
269 bool HTMLVideoElement::hasAvailableVideoFrame() const
270 {
271     if (!player())
272         return false;
273     
274     return player()->hasVideo() && player()->hasAvailableVideoFrame();
275 }
276
277 PassNativeImagePtr HTMLVideoElement::nativeImageForCurrentTime()
278 {
279     if (!player())
280         return 0;
281
282     return player()->nativeImageForCurrentTime();
283 }
284
285 void HTMLVideoElement::webkitEnterFullscreen(ExceptionCode& ec)
286 {
287     if (isFullscreen())
288         return;
289
290     // Generate an exception if this isn't called in response to a user gesture, or if the 
291     // element does not support fullscreen.
292     if (!mediaSession().fullscreenPermitted(*this) || !supportsFullscreen()) {
293         ec = INVALID_STATE_ERR;
294         return;
295     }
296
297     enterFullscreen();
298 }
299
300 void HTMLVideoElement::webkitExitFullscreen()
301 {
302     if (isFullscreen())
303         exitFullscreen();
304 }
305
306 bool HTMLVideoElement::webkitSupportsFullscreen()
307 {
308     return supportsFullscreen();
309 }
310
311 bool HTMLVideoElement::webkitDisplayingFullscreen()
312 {
313     return isFullscreen();
314 }
315
316 #if ENABLE(IOS_AIRPLAY)
317 bool HTMLVideoElement::webkitWirelessVideoPlaybackDisabled() const
318 {
319     Settings* settings = document().settings();
320     if (!settings || !settings->mediaPlaybackAllowsAirPlay())
321         return true;
322
323     String legacyAirplayAttributeValue = fastGetAttribute(webkitairplayAttr);
324     if (equalIgnoringCase(legacyAirplayAttributeValue, "deny"))
325         return true;
326     return fastHasAttribute(webkitwirelessvideoplaybackdisabledAttr);
327 }
328
329 void HTMLVideoElement::setWebkitWirelessVideoPlaybackDisabled(bool disabled)
330 {
331     setBooleanAttribute(webkitwirelessvideoplaybackdisabledAttr, disabled);
332 }
333 #endif
334
335 void HTMLVideoElement::didMoveToNewDocument(Document* oldDocument)
336 {
337     if (m_imageLoader)
338         m_imageLoader->elementDidMoveToNewDocument();
339     HTMLMediaElement::didMoveToNewDocument(oldDocument);
340 }
341
342 #if ENABLE(MEDIA_STATISTICS)
343 unsigned HTMLVideoElement::webkitDecodedFrameCount() const
344 {
345     if (!player())
346         return 0;
347
348     return player()->decodedFrameCount();
349 }
350
351 unsigned HTMLVideoElement::webkitDroppedFrameCount() const
352 {
353     if (!player())
354         return 0;
355
356     return player()->droppedFrameCount();
357 }
358 #endif
359
360 URL HTMLVideoElement::posterImageURL() const
361 {
362     String url = stripLeadingAndTrailingHTMLSpaces(imageSourceURL());
363     if (url.isEmpty())
364         return URL();
365     return document().completeURL(url);
366 }
367
368 }
369
370 #endif