Scroll to make the video element visible when exiting fullscreen.
[WebKit-https.git] / Source / WebKit2 / WebProcess / ios / WebVideoFullscreenManager.mm
1 /*
2  * Copyright (C) 2014 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. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 #import "config.h"
26 #import "WebVideoFullscreenManager.h"
27
28 #if PLATFORM(IOS)
29
30 #import "Attachment.h"
31 #import "WebCoreArgumentCoders.h"
32 #import "WebPage.h"
33 #import "WebProcess.h"
34 #import "WebVideoFullscreenManagerMessages.h"
35 #import "WebVideoFullscreenManagerProxyMessages.h"
36 #import <QuartzCore/CoreAnimation.h>
37 #import <WebCore/Color.h>
38 #import <WebCore/Event.h>
39 #import <WebCore/EventNames.h>
40 #import <WebCore/FrameView.h>
41 #import <WebCore/HTMLVideoElement.h>
42 #import <WebCore/PlatformCALayer.h>
43 #import <WebCore/RenderLayer.h>
44 #import <WebCore/RenderLayerBacking.h>
45 #import <WebCore/RenderView.h>
46 #import <WebCore/Settings.h>
47 #import <WebCore/TimeRanges.h>
48 #import <WebCore/WebCoreThreadRun.h>
49 #import <mach/mach_port.h>
50
51 using namespace WebCore;
52
53 namespace WebKit {
54
55 static IntRect clientRectForElement(HTMLElement* element)
56 {
57     if (!element)
58         return IntRect();
59
60     return element->clientRect();
61 }
62
63 PassRefPtr<WebVideoFullscreenManager> WebVideoFullscreenManager::create(PassRefPtr<WebPage> page)
64 {
65     return adoptRef(new WebVideoFullscreenManager(page));
66 }
67
68 WebVideoFullscreenManager::WebVideoFullscreenManager(PassRefPtr<WebPage> page)
69     : m_page(page.get())
70     , m_isAnimating(false)
71     , m_targetIsFullscreen(false)
72     , m_fullscreenMode(HTMLMediaElement::VideoFullscreenModeNone)
73     , m_isFullscreen(false)
74 {
75     setWebVideoFullscreenInterface(this);
76     WebProcess::singleton().addMessageReceiver(Messages::WebVideoFullscreenManager::messageReceiverName(), page->pageID(), *this);
77 }
78
79 WebVideoFullscreenManager::~WebVideoFullscreenManager()
80 {
81     WebProcess::singleton().removeMessageReceiver(Messages::WebVideoFullscreenManager::messageReceiverName(), m_page->pageID());
82 }
83
84 bool WebVideoFullscreenManager::supportsVideoFullscreen() const
85 {
86     return Settings::avKitEnabled();
87 }
88
89 void WebVideoFullscreenManager::enterVideoFullscreenForVideoElement(HTMLVideoElement* videoElement, HTMLMediaElement::VideoFullscreenMode mode)
90 {
91     ASSERT(mode != HTMLMediaElement::VideoFullscreenModeNone);
92
93     m_videoElement = videoElement;
94
95     m_targetIsFullscreen = true;
96     m_fullscreenMode = mode;
97
98     if (m_isAnimating)
99         return;
100
101     m_isAnimating = true;
102     setVideoElement(videoElement);
103
104     m_layerHostingContext = LayerHostingContext::createForExternalHostingProcess();
105     bool allowOptimizedFullscreen = m_videoElement->mediaSession().allowsAlternateFullscreen(*m_videoElement.get());
106     
107     m_page->send(Messages::WebVideoFullscreenManagerProxy::SetupFullscreenWithID(m_layerHostingContext->contextID(), clientRectForElement(videoElement), m_page->deviceScaleFactor(), m_fullscreenMode, allowOptimizedFullscreen), m_page->pageID());
108 }
109
110 void WebVideoFullscreenManager::exitVideoFullscreen()
111 {
112     RefPtr<HTMLVideoElement> videoElement = m_videoElement.release();
113     m_targetIsFullscreen = false;
114
115     if (m_isAnimating)
116         return;
117
118     m_isAnimating = true;
119     m_page->send(Messages::WebVideoFullscreenManagerProxy::ExitFullscreen(clientRectForElement(videoElement.get())), m_page->pageID());
120 }
121
122 void WebVideoFullscreenManager::resetMediaState()
123 {
124     m_page->send(Messages::WebVideoFullscreenManagerProxy::ResetMediaState(), m_page->pageID());
125 }
126     
127 void WebVideoFullscreenManager::setDuration(double duration)
128 {
129     m_page->send(Messages::WebVideoFullscreenManagerProxy::SetDuration(duration), m_page->pageID());
130 }
131
132 void WebVideoFullscreenManager::setCurrentTime(double currentTime, double anchorTime)
133 {
134     m_page->send(Messages::WebVideoFullscreenManagerProxy::SetCurrentTime(currentTime, anchorTime), m_page->pageID());
135 }
136
137 void WebVideoFullscreenManager::setRate(bool isPlaying, float playbackRate)
138 {
139     m_page->send(Messages::WebVideoFullscreenManagerProxy::SetRate(isPlaying, playbackRate), m_page->pageID());
140 }
141
142 void WebVideoFullscreenManager::setVideoDimensions(bool hasVideo, float width, float height)
143 {
144     m_page->send(Messages::WebVideoFullscreenManagerProxy::SetVideoDimensions(hasVideo, width, height), m_page->pageID());
145 }
146     
147 void WebVideoFullscreenManager::setSeekableRanges(const WebCore::TimeRanges& timeRanges)
148 {
149     Vector<std::pair<double, double>> rangesVector;
150     
151     for (unsigned i = 0; i < timeRanges.length(); i++) {
152         ExceptionCode exceptionCode;
153         double start = timeRanges.start(i, exceptionCode);
154         double end = timeRanges.end(i, exceptionCode);
155         rangesVector.append(std::pair<double,double>(start, end));
156     }
157
158     m_page->send(Messages::WebVideoFullscreenManagerProxy::SetSeekableRangesVector(WTF::move(rangesVector)), m_page->pageID());
159 }
160
161 void WebVideoFullscreenManager::setCanPlayFastReverse(bool value)
162 {
163     m_page->send(Messages::WebVideoFullscreenManagerProxy::SetCanPlayFastReverse(value), m_page->pageID());
164 }
165
166 void WebVideoFullscreenManager::setAudioMediaSelectionOptions(const Vector<String>& options, uint64_t selectedIndex)
167 {
168     m_page->send(Messages::WebVideoFullscreenManagerProxy::SetAudioMediaSelectionOptions(options, selectedIndex), m_page->pageID());
169 }
170
171 void WebVideoFullscreenManager::setLegibleMediaSelectionOptions(const Vector<String>& options, uint64_t selectedIndex)
172 {
173     m_page->send(Messages::WebVideoFullscreenManagerProxy::SetLegibleMediaSelectionOptions(options, selectedIndex), m_page->pageID());
174 }
175
176 void WebVideoFullscreenManager::setExternalPlayback(bool enabled, WebVideoFullscreenInterface::ExternalPlaybackTargetType targetType, String localizedDeviceName)
177 {
178     m_page->send(Messages::WebVideoFullscreenManagerProxy::SetExternalPlaybackProperties(enabled, static_cast<uint32_t>(targetType), localizedDeviceName), m_page->pageID());
179 }
180     
181 void WebVideoFullscreenManager::didSetupFullscreen()
182 {
183     PlatformLayer* videoLayer = [CALayer layer];
184 #ifndef NDEBUG
185     [videoLayer setName:@"Web video fullscreen manager layer"];
186 #endif
187
188     [CATransaction begin];
189     [CATransaction setDisableActions:YES];
190
191     [videoLayer setPosition:CGPointMake(0, 0)];
192     [videoLayer setBackgroundColor:cachedCGColor(WebCore::Color::transparent, WebCore::ColorSpaceDeviceRGB)];
193
194     // Set a scale factor here to make convertRect:toLayer:nil take scale factor into account. <rdar://problem/18316542>.
195     // This scale factor is inverted in the hosting process.
196     float hostingScaleFactor = m_page->deviceScaleFactor();
197     [videoLayer setTransform:CATransform3DMakeScale(hostingScaleFactor, hostingScaleFactor, 1)];
198     m_layerHostingContext->setRootLayer(videoLayer);
199
200     setVideoFullscreenLayer(videoLayer);
201     [CATransaction commit];
202
203     m_page->send(Messages::WebVideoFullscreenManagerProxy::EnterFullscreen(), m_page->pageID());
204 }
205     
206 void WebVideoFullscreenManager::didEnterFullscreen()
207 {
208     m_isAnimating = false;
209     m_isFullscreen = false;
210
211     if (m_targetIsFullscreen)
212         return;
213
214     // exit fullscreen now if it was previously requested during an animation.
215     __block RefPtr<WebVideoFullscreenModelVideoElement> protect(this);
216     WebThreadRun(^ {
217         exitVideoFullscreen();
218         protect.clear();
219     });
220 }
221
222 void WebVideoFullscreenManager::didExitFullscreen()
223 {
224     setVideoFullscreenLayer(nil);
225     __block RefPtr<WebVideoFullscreenModelVideoElement> protect(this);
226
227     dispatch_async(dispatch_get_main_queue(), ^{
228         if (m_layerHostingContext) {
229             m_layerHostingContext->setRootLayer(nullptr);
230             m_layerHostingContext = nullptr;
231         }
232         if (m_page)
233             m_page->send(Messages::WebVideoFullscreenManagerProxy::CleanupFullscreen(), m_page->pageID());
234         protect.clear();
235     });
236 }
237     
238 void WebVideoFullscreenManager::didCleanupFullscreen()
239 {
240     m_isAnimating = false;
241     m_isFullscreen = false;
242     
243     setVideoElement(nullptr);
244
245     if (!m_targetIsFullscreen)
246         return;
247
248     // enter fullscreen now if it was previously requested during an animation.
249     __block RefPtr<WebVideoFullscreenModelVideoElement> protect(this);
250     WebThreadRun(^ {
251         enterVideoFullscreenForVideoElement(m_videoElement.get(), m_fullscreenMode);
252         protect.clear();
253     });
254 }
255     
256 void WebVideoFullscreenManager::setVideoLayerGravityEnum(unsigned gravity)
257 {
258     setVideoLayerGravity((WebVideoFullscreenModel::VideoGravity)gravity);
259 }
260     
261 void WebVideoFullscreenManager::fullscreenMayReturnToInline(bool isPageVisible)
262 {
263     if (!isPageVisible)
264         m_videoElement->scrollIntoViewIfNotVisible(false);
265     m_page->send(Messages::WebVideoFullscreenManagerProxy::PreparedToReturnToInline(true, clientRectForElement(m_videoElement.get())), m_page->pageID());
266 }
267     
268 void WebVideoFullscreenManager::setVideoLayerFrameFenced(WebCore::FloatRect bounds, IPC::Attachment fencePort)
269 {
270     if (m_layerHostingContext)
271         m_layerHostingContext->setFencePort(fencePort.port());
272     setVideoLayerFrame(bounds);
273     mach_port_deallocate(mach_task_self(), fencePort.port());
274 }
275
276 } // namespace WebKit
277
278 #endif // PLATFORM(IOS)