Replace WTF::move with WTFMove
[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 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 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 "CSSPropertyNames.h"
31 #include "Chrome.h"
32 #include "ChromeClient.h"
33 #include "Document.h"
34 #include "Frame.h"
35 #include "HTMLImageLoader.h"
36 #include "HTMLNames.h"
37 #include "HTMLParserIdioms.h"
38 #include "Logging.h"
39 #include "Page.h"
40 #include "RenderImage.h"
41 #include "RenderVideo.h"
42 #include "ScriptController.h"
43 #include "Settings.h"
44 #include "TextStream.h"
45 #include <wtf/NeverDestroyed.h>
46
47 #if ENABLE(VIDEO_PRESENTATION_MODE)
48 #include "WebVideoFullscreenInterfaceAVKit.h"
49 #endif
50
51 namespace WebCore {
52
53 using namespace HTMLNames;
54
55 inline HTMLVideoElement::HTMLVideoElement(const QualifiedName& tagName, Document& document, bool createdByParser)
56     : HTMLMediaElement(tagName, document, createdByParser)
57 {
58     ASSERT(hasTagName(videoTag));
59     setHasCustomStyleResolveCallbacks();
60     if (document.settings())
61         m_defaultPosterURL = document.settings()->defaultVideoPosterURL();
62 }
63
64 Ref<HTMLVideoElement> HTMLVideoElement::create(const QualifiedName& tagName, Document& document, bool createdByParser)
65 {
66     Ref<HTMLVideoElement> videoElement = adoptRef(*new HTMLVideoElement(tagName, document, createdByParser));
67     videoElement->suspendIfNeeded();
68     return videoElement;
69 }
70
71 bool HTMLVideoElement::rendererIsNeeded(const RenderStyle& style) 
72 {
73     return HTMLElement::rendererIsNeeded(style); 
74 }
75
76 RenderPtr<RenderElement> HTMLVideoElement::createElementRenderer(Ref<RenderStyle>&& style, const RenderTreePosition&)
77 {
78     return createRenderer<RenderVideo>(*this, WTFMove(style));
79 }
80
81 void HTMLVideoElement::didAttachRenderers()
82 {
83     HTMLMediaElement::didAttachRenderers();
84
85     updateDisplayState();
86     if (shouldDisplayPosterImage()) {
87         if (!m_imageLoader)
88             m_imageLoader = std::make_unique<HTMLImageLoader>(*this);
89         m_imageLoader->updateFromElement();
90         if (renderer())
91             downcast<RenderImage>(*renderer()).imageResource().setCachedImage(m_imageLoader->image());
92     }
93 }
94
95 void HTMLVideoElement::collectStyleForPresentationAttribute(const QualifiedName& name, const AtomicString& value, MutableStyleProperties& style)
96 {
97     if (name == widthAttr)
98         addHTMLLengthToStyle(style, CSSPropertyWidth, value);
99     else if (name == heightAttr)
100         addHTMLLengthToStyle(style, CSSPropertyHeight, value);
101     else
102         HTMLMediaElement::collectStyleForPresentationAttribute(name, value, style);
103 }
104
105 bool HTMLVideoElement::isPresentationAttribute(const QualifiedName& name) const
106 {
107     if (name == widthAttr || name == heightAttr)
108         return true;
109     return HTMLMediaElement::isPresentationAttribute(name);
110 }
111
112 void HTMLVideoElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
113 {
114     if (name == posterAttr) {
115         // Force a poster recalc by setting m_displayMode to Unknown directly before calling updateDisplayState.
116         HTMLMediaElement::setDisplayMode(Unknown);
117         updateDisplayState();
118
119         if (shouldDisplayPosterImage()) {
120             if (!m_imageLoader)
121                 m_imageLoader = std::make_unique<HTMLImageLoader>(*this);
122             m_imageLoader->updateFromElementIgnoringPreviousError();
123         } else {
124             if (renderer())
125                 downcast<RenderImage>(*renderer()).imageResource().setCachedImage(nullptr);
126         }
127     }
128 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
129     else if (name == webkitwirelessvideoplaybackdisabledAttr)
130         mediaSession().setWirelessVideoPlaybackDisabled(*this, true);
131 #endif
132     else {
133         HTMLMediaElement::parseAttribute(name, value);    
134
135 #if PLATFORM(IOS) && ENABLE(WIRELESS_PLAYBACK_TARGET)
136         if (name == webkitairplayAttr) {
137             bool disabled = false;
138             if (equalIgnoringCase(fastGetAttribute(HTMLNames::webkitairplayAttr), "deny"))
139                 disabled = true;
140             mediaSession().setWirelessVideoPlaybackDisabled(*this, disabled);
141         }
142 #endif
143     }
144
145 }
146
147 bool HTMLVideoElement::supportsFullscreen() const
148 {
149     Page* page = document().page();
150     if (!page) 
151         return false;
152
153     if (!player() || !player()->supportsFullscreen())
154         return false;
155
156 #if PLATFORM(IOS)
157     // Fullscreen implemented by player.
158     return true;
159 #else
160 #if ENABLE(FULLSCREEN_API)
161     // If the full screen API is enabled and is supported for the current element
162     // do not require that the player has a video track to enter full screen.
163     if (page->chrome().client().supportsFullScreenForElement(this, false))
164         return true;
165 #endif
166
167     if (!player()->hasVideo())
168         return false;
169
170     return page->chrome().client().supportsVideoFullscreen();
171 #endif // PLATFORM(IOS)
172 }
173
174 unsigned HTMLVideoElement::videoWidth() const
175 {
176     if (!player())
177         return 0;
178     return clampToUnsigned(player()->naturalSize().width());
179 }
180
181 unsigned HTMLVideoElement::videoHeight() const
182 {
183     if (!player())
184         return 0;
185     return clampToUnsigned(player()->naturalSize().height());
186 }
187
188 bool HTMLVideoElement::isURLAttribute(const Attribute& attribute) const
189 {
190     return attribute.name() == posterAttr || HTMLMediaElement::isURLAttribute(attribute);
191 }
192
193 const AtomicString& HTMLVideoElement::imageSourceURL() const
194 {
195     const AtomicString& url = getAttribute(posterAttr);
196     if (!stripLeadingAndTrailingHTMLSpaces(url).isEmpty())
197         return url;
198     return m_defaultPosterURL;
199 }
200
201 void HTMLVideoElement::setDisplayMode(DisplayMode mode)
202 {
203     DisplayMode oldMode = displayMode();
204     URL poster = posterImageURL();
205
206     if (!poster.isEmpty()) {
207         // We have a poster path, but only show it until the user triggers display by playing or seeking and the
208         // media engine has something to display.
209         if (mode == Video) {
210             if (oldMode != Video && player())
211                 player()->prepareForRendering();
212             if (!hasAvailableVideoFrame())
213                 mode = PosterWaitingForVideo;
214         }
215     } else if (oldMode != Video && player())
216         player()->prepareForRendering();
217
218     HTMLMediaElement::setDisplayMode(mode);
219
220     if (player() && player()->canLoadPoster()) {
221         bool canLoad = true;
222         if (!poster.isEmpty()) {
223             if (Frame* frame = document().frame())
224                 canLoad = frame->loader().willLoadMediaElementURL(poster);
225         }
226         if (canLoad)
227             player()->setPoster(poster);
228     }
229
230     if (renderer() && displayMode() != oldMode)
231         renderer()->updateFromElement();
232 }
233
234 void HTMLVideoElement::updateDisplayState()
235 {
236     if (posterImageURL().isEmpty())
237         setDisplayMode(Video);
238     else if (displayMode() < Poster)
239         setDisplayMode(Poster);
240 }
241
242 void HTMLVideoElement::paintCurrentFrameInContext(GraphicsContext& context, const FloatRect& destRect)
243 {
244     MediaPlayer* player = HTMLMediaElement::player();
245     if (!player)
246         return;
247     
248     player->setVisible(true); // Make player visible or it won't draw.
249     player->paintCurrentFrameInContext(context, destRect);
250 }
251
252 bool HTMLVideoElement::copyVideoTextureToPlatformTexture(GraphicsContext3D* context, Platform3DObject texture, GC3Denum target, GC3Dint level, GC3Denum internalFormat, GC3Denum format, GC3Denum type, bool premultiplyAlpha, bool flipY)
253 {
254     if (!player())
255         return false;
256     return player()->copyVideoTextureToPlatformTexture(context, texture, target, level, internalFormat, format, type, premultiplyAlpha, flipY);
257 }
258
259 bool HTMLVideoElement::hasAvailableVideoFrame() const
260 {
261     if (!player())
262         return false;
263     
264     return player()->hasVideo() && player()->hasAvailableVideoFrame();
265 }
266
267 PassNativeImagePtr HTMLVideoElement::nativeImageForCurrentTime()
268 {
269     if (!player())
270         return nullptr;
271
272     return player()->nativeImageForCurrentTime();
273 }
274
275 void HTMLVideoElement::webkitEnterFullscreen(ExceptionCode& ec)
276 {
277     if (isFullscreen())
278         return;
279
280     // Generate an exception if this isn't called in response to a user gesture, or if the 
281     // element does not support fullscreen.
282     if (!mediaSession().fullscreenPermitted(*this) || !supportsFullscreen()) {
283         ec = INVALID_STATE_ERR;
284         return;
285     }
286
287     enterFullscreen();
288 }
289
290 void HTMLVideoElement::webkitExitFullscreen()
291 {
292     if (isFullscreen())
293         exitFullscreen();
294 }
295
296 bool HTMLVideoElement::webkitSupportsFullscreen()
297 {
298     return supportsFullscreen();
299 }
300
301 bool HTMLVideoElement::webkitDisplayingFullscreen()
302 {
303     return isFullscreen();
304 }
305
306 #if ENABLE(WIRELESS_PLAYBACK_TARGET)
307 bool HTMLVideoElement::webkitWirelessVideoPlaybackDisabled() const
308 {
309     return mediaSession().wirelessVideoPlaybackDisabled(*this);
310 }
311
312 void HTMLVideoElement::setWebkitWirelessVideoPlaybackDisabled(bool disabled)
313 {
314     setBooleanAttribute(webkitwirelessvideoplaybackdisabledAttr, disabled);
315 }
316 #endif
317
318 void HTMLVideoElement::didMoveToNewDocument(Document* oldDocument)
319 {
320     if (m_imageLoader)
321         m_imageLoader->elementDidMoveToNewDocument();
322     HTMLMediaElement::didMoveToNewDocument(oldDocument);
323 }
324
325 #if ENABLE(MEDIA_STATISTICS)
326 unsigned HTMLVideoElement::webkitDecodedFrameCount() const
327 {
328     if (!player())
329         return 0;
330
331     return player()->decodedFrameCount();
332 }
333
334 unsigned HTMLVideoElement::webkitDroppedFrameCount() const
335 {
336     if (!player())
337         return 0;
338
339     return player()->droppedFrameCount();
340 }
341 #endif
342
343 URL HTMLVideoElement::posterImageURL() const
344 {
345     String url = stripLeadingAndTrailingHTMLSpaces(imageSourceURL());
346     if (url.isEmpty())
347         return URL();
348     return document().completeURL(url);
349 }
350
351 #if ENABLE(VIDEO_PRESENTATION_MODE)
352
353 static const AtomicString& presentationModeFullscreen()
354 {
355     static NeverDestroyed<AtomicString> fullscreen("fullscreen", AtomicString::ConstructFromLiteral);
356     return fullscreen;
357 }
358
359 static const AtomicString& presentationModePictureInPicture()
360 {
361     static NeverDestroyed<AtomicString> pictureInPicture("picture-in-picture", AtomicString::ConstructFromLiteral);
362     return pictureInPicture;
363 }
364
365 static const AtomicString& presentationModeInline()
366 {
367     static NeverDestroyed<AtomicString> inlineMode("inline", AtomicString::ConstructFromLiteral);
368     return inlineMode;
369 }
370
371 bool HTMLVideoElement::webkitSupportsPresentationMode(const String& mode) const
372 {
373     if (mode == presentationModeFullscreen())
374         return mediaSession().fullscreenPermitted(*this) && supportsFullscreen();
375
376     if (mode == presentationModePictureInPicture())
377         return supportsPictureInPicture() && mediaSession().allowsPictureInPicture(*this) && supportsFullscreen();
378
379     if (mode == presentationModeInline())
380         return !mediaSession().requiresFullscreenForVideoPlayback(*this);
381
382     return false;
383 }
384
385 static bool presentationModeToFullscreenMode(const String& presentationMode, HTMLMediaElementEnums::VideoFullscreenMode& fullscreenMode)
386 {
387     if (presentationMode == presentationModeFullscreen()) {
388         fullscreenMode = HTMLMediaElementEnums::VideoFullscreenModeStandard;
389         return true;
390     }
391
392     if (presentationMode == presentationModePictureInPicture()) {
393         fullscreenMode = HTMLMediaElementEnums::VideoFullscreenModePictureInPicture;
394         return true;
395     }
396
397     if (presentationMode == presentationModeInline()) {
398         fullscreenMode = HTMLMediaElementEnums::VideoFullscreenModeNone;
399         return true;
400     }
401     return false;
402 }
403
404 void HTMLVideoElement::webkitSetPresentationMode(const String& mode)
405 {
406     VideoFullscreenMode fullscreenMode = VideoFullscreenModeNone;
407     if (!presentationModeToFullscreenMode(mode, fullscreenMode))
408         return;
409
410     LOG_WITH_STREAM(Media, stream << "HTMLVideoElement::webkitSetPresentationMode(" << this << ") - setting to \"" << mode << "\"");
411     setFullscreenMode(fullscreenMode);
412 }
413
414 void HTMLVideoElement::setFullscreenMode(HTMLMediaElementEnums::VideoFullscreenMode mode)
415 {
416     if (mode == VideoFullscreenModeNone && isFullscreen()) {
417         exitFullscreen();
418         return;
419     }
420
421     if (!mediaSession().fullscreenPermitted(*this) || !supportsFullscreen())
422         return;
423
424     enterFullscreen(mode);
425 }
426
427 String HTMLVideoElement::webkitPresentationMode() const
428 {
429     HTMLMediaElement::VideoFullscreenMode mode = fullscreenMode();
430
431     if (mode == VideoFullscreenModeStandard)
432         return presentationModeFullscreen();
433
434     if (mode & VideoFullscreenModePictureInPicture)
435         return presentationModePictureInPicture();
436
437     if (mode == VideoFullscreenModeNone)
438         return presentationModeInline();
439
440     ASSERT_NOT_REACHED();
441     return presentationModeInline();
442 }
443
444 void HTMLVideoElement::fullscreenModeChanged(VideoFullscreenMode mode)
445 {
446     if (mode != fullscreenMode()) {
447         LOG(Media, "HTMLVideoElement::fullscreenModeChanged(%p) - mode changed from %i to %i", this, fullscreenMode(), mode);
448         scheduleEvent(eventNames().webkitpresentationmodechangedEvent);
449     }
450
451     if (player())
452         player()->setVideoFullscreenMode(mode);
453
454     HTMLMediaElement::fullscreenModeChanged(mode);
455 }
456
457 #endif
458
459 }
460
461 #endif