b14f1356c593fe7081492146f500fb28cfe1a76f
[WebKit-https.git] / Source / WebKit2 / UIProcess / Cocoa / WebVideoFullscreenManagerProxy.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
26 #import "config.h"
27 #import "WebVideoFullscreenManagerProxy.h"
28
29 #if PLATFORM(IOS) && HAVE(AVKIT) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))
30
31 #import "WebPageProxy.h"
32 #import "WebPlaybackSessionManagerProxy.h"
33 #import "WebProcessProxy.h"
34 #import "WebVideoFullscreenManagerMessages.h"
35 #import "WebVideoFullscreenManagerProxyMessages.h"
36 #import <QuartzCore/CoreAnimation.h>
37 #import <WebCore/MachSendRight.h>
38 #import <WebCore/QuartzCoreSPI.h>
39 #import <WebCore/TimeRanges.h>
40 #import <WebKitSystemInterface.h>
41
42 #if PLATFORM(IOS)
43 #import "RemoteLayerTreeDrawingAreaProxy.h"
44 #import "UIKitSPI.h"
45 #endif
46
47 @interface WKLayerHostView : PlatformView
48 @property (nonatomic, assign) uint32_t contextID;
49 @end
50
51 @implementation WKLayerHostView
52
53 #if PLATFORM(IOS)
54 + (Class)layerClass {
55     return [CALayerHost class];
56 }
57 #else
58 - (CALayer *)makeBackingLayer
59 {
60     return [[CALayerHost alloc] init];
61 }
62 #endif
63
64 - (uint32_t)contextID {
65     return [[self layerHost] contextId];
66 }
67
68 - (void)setContextID:(uint32_t)contextID {
69     [[self layerHost] setContextId:contextID];
70 }
71
72 - (CALayerHost *)layerHost {
73     return (CALayerHost *)[self layer];
74 }
75
76 @end
77
78 using namespace WebCore;
79
80 namespace WebKit {
81
82 #if PLATFORM(IOS) && !HAVE(AVKIT)
83
84 RefPtr<WebVideoFullscreenManagerProxy> WebVideoFullscreenManagerProxy::create(WebPageProxy&)
85 {
86     return nullptr;
87 }
88
89 void WebVideoFullscreenManagerProxy::invalidate()
90 {
91 }
92
93 bool WebVideoFullscreenManagerProxy::hasMode(HTMLMediaElementEnums::VideoFullscreenMode) const
94 {
95     return false;
96 }
97
98 bool WebVideoFullscreenManagerProxy::mayAutomaticallyShowVideoPictureInPicture() const
99 {
100     return false;
101 }
102
103 void WebVideoFullscreenManagerProxy::requestHideAndExitFullscreen()
104 {
105
106 }
107
108 void WebVideoFullscreenManagerProxy::applicationDidBecomeActive()
109 {
110
111 }
112 #else
113
114 #pragma mark - WebVideoFullscreenModelContext
115
116 WebVideoFullscreenModelContext::WebVideoFullscreenModelContext(WebVideoFullscreenManagerProxy& manager, WebPlaybackSessionModelContext& playbackSessionModel, uint64_t contextId)
117     : m_manager(&manager)
118     , m_playbackSessionModel(playbackSessionModel)
119     , m_contextId(contextId)
120 {
121 }
122
123 WebVideoFullscreenModelContext::~WebVideoFullscreenModelContext()
124 {
125 }
126
127 void WebVideoFullscreenModelContext::requestFullscreenMode(HTMLMediaElementEnums::VideoFullscreenMode mode)
128 {
129     if (m_manager)
130         m_manager->requestFullscreenMode(m_contextId, mode);
131 }
132
133 void WebVideoFullscreenModelContext::setVideoLayerFrame(WebCore::FloatRect frame)
134 {
135     if (m_manager)
136         m_manager->setVideoLayerFrame(m_contextId, frame);
137 }
138
139 void WebVideoFullscreenModelContext::setVideoLayerGravity(WebCore::WebVideoFullscreenModel::VideoGravity gravity)
140 {
141     if (m_manager)
142         m_manager->setVideoLayerGravity(m_contextId, gravity);
143 }
144
145 void WebVideoFullscreenModelContext::fullscreenModeChanged(WebCore::HTMLMediaElementEnums::VideoFullscreenMode mode)
146 {
147     if (m_manager)
148         m_manager->fullscreenModeChanged(m_contextId, mode);
149 }
150
151 bool WebVideoFullscreenModelContext::isVisible() const
152 {
153     return m_manager ? m_manager->isVisible() : false;
154 }
155
156 void WebVideoFullscreenModelContext::didSetupFullscreen()
157 {
158     if (m_manager)
159         m_manager->didSetupFullscreen(m_contextId);
160 }
161
162 void WebVideoFullscreenModelContext::didEnterFullscreen()
163 {
164     if (m_manager)
165         m_manager->didEnterFullscreen(m_contextId);
166 }
167
168 void WebVideoFullscreenModelContext::didExitFullscreen()
169 {
170     if (m_manager)
171         m_manager->didExitFullscreen(m_contextId);
172 }
173
174 void WebVideoFullscreenModelContext::didCleanupFullscreen()
175 {
176     if (m_manager)
177         m_manager->didCleanupFullscreen(m_contextId);
178 }
179
180 void WebVideoFullscreenModelContext::fullscreenMayReturnToInline()
181 {
182     if (m_manager)
183         m_manager->fullscreenMayReturnToInline(m_contextId);
184 }
185
186 #pragma mark - WebVideoFullscreenManagerProxy
187
188 RefPtr<WebVideoFullscreenManagerProxy> WebVideoFullscreenManagerProxy::create(WebPageProxy& page, WebPlaybackSessionManagerProxy& playbackSessionManagerProxy)
189 {
190     return adoptRef(new WebVideoFullscreenManagerProxy(page, playbackSessionManagerProxy));
191 }
192
193 WebVideoFullscreenManagerProxy::WebVideoFullscreenManagerProxy(WebPageProxy& page, WebPlaybackSessionManagerProxy& playbackSessionManagerProxy)
194     : m_page(&page)
195     , m_playbackSessionManagerProxy(playbackSessionManagerProxy)
196 {
197     m_page->process().addMessageReceiver(Messages::WebVideoFullscreenManagerProxy::messageReceiverName(), m_page->pageID(), *this);
198 }
199
200 WebVideoFullscreenManagerProxy::~WebVideoFullscreenManagerProxy()
201 {
202     if (!m_page)
203         return;
204     invalidate();
205 }
206
207 void WebVideoFullscreenManagerProxy::invalidate()
208 {
209     m_page->process().removeMessageReceiver(Messages::WebVideoFullscreenManagerProxy::messageReceiverName(), m_page->pageID());
210     m_page = nullptr;
211
212     for (auto& tuple : m_contextMap.values()) {
213         RefPtr<WebVideoFullscreenModelContext> model;
214         RefPtr<PlatformWebVideoFullscreenInterface> interface;
215         std::tie(model, interface) = tuple;
216
217         interface->invalidate();
218         [model->layerHostView() removeFromSuperview];
219         model->setLayerHostView(nullptr);
220     }
221
222     m_contextMap.clear();
223     m_clientCounts.clear();
224 }
225
226 void WebVideoFullscreenManagerProxy::requestHideAndExitFullscreen()
227 {
228     for (auto& tuple : m_contextMap.values())
229         std::get<1>(tuple)->requestHideAndExitFullscreen();
230 }
231
232 bool WebVideoFullscreenManagerProxy::hasMode(HTMLMediaElementEnums::VideoFullscreenMode mode) const
233 {
234     for (auto& tuple : m_contextMap.values()) {
235         if (std::get<1>(tuple)->hasMode(mode))
236             return true;
237     }
238     return false;
239 }
240
241 bool WebVideoFullscreenManagerProxy::mayAutomaticallyShowVideoPictureInPicture() const
242 {
243     for (auto& tuple : m_contextMap.values()) {
244         if (std::get<1>(tuple)->mayAutomaticallyShowVideoPictureInPicture())
245             return true;
246     }
247     return false;
248 }
249
250 void WebVideoFullscreenManagerProxy::applicationDidBecomeActive()
251 {
252     for (auto& tuple : m_contextMap.values())
253         std::get<1>(tuple)->applicationDidBecomeActive();
254 }
255
256 WebVideoFullscreenManagerProxy::ModelInterfaceTuple WebVideoFullscreenManagerProxy::createModelAndInterface(uint64_t contextId)
257 {
258     auto& playbackSessionModel = m_playbackSessionManagerProxy->ensureModel(contextId);
259     Ref<WebVideoFullscreenModelContext> model = WebVideoFullscreenModelContext::create(*this, playbackSessionModel, contextId);
260     auto& playbackSessionInterface = m_playbackSessionManagerProxy->ensureInterface(contextId);
261     Ref<PlatformWebVideoFullscreenInterface> interface = PlatformWebVideoFullscreenInterface::create(playbackSessionInterface);
262     m_playbackSessionManagerProxy->addClientForContext(contextId);
263
264     interface->setWebVideoFullscreenModel(&model.get());
265     interface->setWebVideoFullscreenChangeObserver(&model.get());
266
267     return std::make_tuple(WTFMove(model), WTFMove(interface));
268 }
269
270 WebVideoFullscreenManagerProxy::ModelInterfaceTuple& WebVideoFullscreenManagerProxy::ensureModelAndInterface(uint64_t contextId)
271 {
272     auto addResult = m_contextMap.add(contextId, ModelInterfaceTuple());
273     if (addResult.isNewEntry)
274         addResult.iterator->value = createModelAndInterface(contextId);
275     return addResult.iterator->value;
276 }
277
278 WebVideoFullscreenModelContext& WebVideoFullscreenManagerProxy::ensureModel(uint64_t contextId)
279 {
280     return *std::get<0>(ensureModelAndInterface(contextId));
281 }
282
283 PlatformWebVideoFullscreenInterface& WebVideoFullscreenManagerProxy::ensureInterface(uint64_t contextId)
284 {
285     return *std::get<1>(ensureModelAndInterface(contextId));
286 }
287
288 void WebVideoFullscreenManagerProxy::addClientForContext(uint64_t contextId)
289 {
290     auto addResult = m_clientCounts.add(contextId, 1);
291     if (!addResult.isNewEntry)
292         addResult.iterator->value++;
293 }
294
295 void WebVideoFullscreenManagerProxy::removeClientForContext(uint64_t contextId)
296 {
297     ASSERT(m_clientCounts.contains(contextId));
298
299     int clientCount = m_clientCounts.get(contextId);
300     ASSERT(clientCount > 0);
301     clientCount--;
302
303     if (clientCount <= 0) {
304         m_playbackSessionManagerProxy->removeClientForContext(contextId);
305         m_clientCounts.remove(contextId);
306         m_contextMap.remove(contextId);
307         return;
308     }
309
310     m_clientCounts.set(contextId, clientCount);
311 }
312
313 #pragma mark Messages from WebVideoFullscreenManager
314
315 void WebVideoFullscreenManagerProxy::setupFullscreenWithID(uint64_t contextId, uint32_t videoLayerID, const WebCore::IntRect& initialRect, float hostingDeviceScaleFactor, HTMLMediaElementEnums::VideoFullscreenMode videoFullscreenMode, bool allowsPictureInPicture)
316 {
317     ASSERT(videoLayerID);
318     RefPtr<WebVideoFullscreenModelContext> model;
319     RefPtr<PlatformWebVideoFullscreenInterface> interface;
320
321     std::tie(model, interface) = ensureModelAndInterface(contextId);
322     addClientForContext(contextId);
323
324     RetainPtr<WKLayerHostView> view = static_cast<WKLayerHostView*>(model->layerHostView());
325     if (!view) {
326         view = adoptNS([[WKLayerHostView alloc] init]);
327 #if PLATFORM(MAC)
328         [view setWantsLayer:YES];
329 #endif
330         model->setLayerHostView(view);
331     }
332     [view setContextID:videoLayerID];
333     if (hostingDeviceScaleFactor != 1) {
334         // Invert the scale transform added in the WebProcess to fix <rdar://problem/18316542>.
335         float inverseScale = 1 / hostingDeviceScaleFactor;
336         [[view layer] setSublayerTransform:CATransform3DMakeScale(inverseScale, inverseScale, 1)];
337     }
338
339 #if PLATFORM(IOS)
340     UIView *parentView = downcast<RemoteLayerTreeDrawingAreaProxy>(*m_page->drawingArea()).remoteLayerTreeHost().rootLayer();
341     interface->setupFullscreen(*model->layerHostView(), initialRect, parentView, videoFullscreenMode, allowsPictureInPicture);
342 #else
343     IntRect initialWindowRect;
344     m_page->rootViewToWindow(initialRect, initialWindowRect);
345     interface->setupFullscreen(*model->layerHostView(), initialWindowRect, m_page->platformWindow(), videoFullscreenMode, allowsPictureInPicture);
346 #endif
347 }
348
349 void WebVideoFullscreenManagerProxy::setVideoDimensions(uint64_t contextId, bool hasVideo, unsigned width, unsigned height)
350 {
351     ensureInterface(contextId).setVideoDimensions(hasVideo, width, height);
352 }
353
354 void WebVideoFullscreenManagerProxy::enterFullscreen(uint64_t contextId)
355 {
356     auto& interface = ensureInterface(contextId);
357     interface.enterFullscreen();
358
359     // Only one context can be in a given full screen mode at a time:
360     for (auto& contextPair : m_contextMap) {
361         auto& otherContextId = contextPair.key;
362         if (contextId == otherContextId)
363             continue;
364
365         auto& otherInterface = std::get<1>(contextPair.value);
366         if (otherInterface->hasMode(interface.mode()))
367             otherInterface->requestHideAndExitFullscreen();
368     }
369 }
370
371 void WebVideoFullscreenManagerProxy::exitFullscreen(uint64_t contextId, WebCore::IntRect finalRect)
372 {
373 #if PLATFORM(IOS)
374     ensureInterface(contextId).exitFullscreen(finalRect);
375 #else
376     IntRect finalWindowRect;
377     m_page->rootViewToWindow(finalRect, finalWindowRect);
378     ensureInterface(contextId).exitFullscreen(finalWindowRect, m_page->platformWindow());
379 #endif
380 }
381
382 #if PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE)
383 void WebVideoFullscreenManagerProxy::exitFullscreenWithoutAnimationToMode(uint64_t contextId, WebCore::HTMLMediaElementEnums::VideoFullscreenMode targetMode)
384 {
385     ensureInterface(contextId).exitFullscreenWithoutAnimationToMode(targetMode);
386 }
387 #endif
388
389 void WebVideoFullscreenManagerProxy::cleanupFullscreen(uint64_t contextId)
390 {
391     ensureInterface(contextId).cleanupFullscreen();
392 }
393
394 void WebVideoFullscreenManagerProxy::preparedToReturnToInline(uint64_t contextId, bool visible, WebCore::IntRect inlineRect)
395 {
396     m_page->fullscreenMayReturnToInline();
397
398 #if PLATFORM(IOS)
399     ensureInterface(contextId).preparedToReturnToInline(visible, inlineRect);
400 #else
401     IntRect inlineWindowRect;
402     m_page->rootViewToWindow(inlineRect, inlineWindowRect);
403     ensureInterface(contextId).preparedToReturnToInline(visible, inlineWindowRect, m_page->platformWindow());
404 #endif
405 }
406
407 #pragma mark Messages to WebVideoFullscreenManager
408
409 void WebVideoFullscreenManagerProxy::requestFullscreenMode(uint64_t contextId, WebCore::HTMLMediaElementEnums::VideoFullscreenMode mode)
410 {
411     m_page->send(Messages::WebVideoFullscreenManager::RequestFullscreenMode(contextId, mode), m_page->pageID());
412 }
413
414 void WebVideoFullscreenManagerProxy::didSetupFullscreen(uint64_t contextId)
415 {
416     m_page->send(Messages::WebVideoFullscreenManager::DidSetupFullscreen(contextId), m_page->pageID());
417 }
418
419 void WebVideoFullscreenManagerProxy::didExitFullscreen(uint64_t contextId)
420 {
421     m_page->send(Messages::WebVideoFullscreenManager::DidExitFullscreen(contextId), m_page->pageID());
422     m_page->didExitFullscreen();
423 }
424
425 void WebVideoFullscreenManagerProxy::didEnterFullscreen(uint64_t contextId)
426 {
427     m_page->send(Messages::WebVideoFullscreenManager::DidEnterFullscreen(contextId), m_page->pageID());
428     m_page->didEnterFullscreen();
429 }
430
431 void WebVideoFullscreenManagerProxy::didCleanupFullscreen(uint64_t contextId)
432 {
433     RefPtr<WebVideoFullscreenModelContext> model;
434     RefPtr<PlatformWebVideoFullscreenInterface> interface;
435
436     std::tie(model, interface) = ensureModelAndInterface(contextId);
437
438     [CATransaction flush];
439     [model->layerHostView() removeFromSuperview];
440     model->setLayerHostView(nullptr);
441     m_page->send(Messages::WebVideoFullscreenManager::DidCleanupFullscreen(contextId), m_page->pageID());
442
443     interface->setMode(HTMLMediaElementEnums::VideoFullscreenModeNone);
444     removeClientForContext(contextId);
445 }
446
447 void WebVideoFullscreenManagerProxy::setVideoLayerFrame(uint64_t contextId, WebCore::FloatRect frame)
448 {
449     @autoreleasepool {
450 #if PLATFORM(IOS)
451         mach_port_name_t fencePort = [UIWindow _synchronizeDrawingAcrossProcesses];
452 #else
453         MachSendRight fenceSendRight;
454         if (DrawingAreaProxy* drawingArea = m_page->drawingArea())
455             fenceSendRight = drawingArea->createFence();
456         mach_port_name_t fencePort = fenceSendRight.leakSendRight();
457 #endif
458
459         m_page->send(Messages::WebVideoFullscreenManager::SetVideoLayerFrameFenced(contextId, frame, IPC::Attachment(fencePort, MACH_MSG_TYPE_MOVE_SEND)), m_page->pageID());
460     }
461 }
462
463 void WebVideoFullscreenManagerProxy::setVideoLayerGravity(uint64_t contextId, WebCore::WebVideoFullscreenModel::VideoGravity gravity)
464 {
465     m_page->send(Messages::WebVideoFullscreenManager::SetVideoLayerGravityEnum(contextId, (unsigned)gravity), m_page->pageID());
466 }
467
468 void WebVideoFullscreenManagerProxy::fullscreenModeChanged(uint64_t contextId, WebCore::HTMLMediaElementEnums::VideoFullscreenMode mode)
469 {
470     m_page->send(Messages::WebVideoFullscreenManager::FullscreenModeChanged(contextId, mode), m_page->pageID());
471 }
472
473 bool WebVideoFullscreenManagerProxy::isVisible() const
474 {
475     return m_page->isViewVisible() && m_page->isInWindow();
476 }
477
478 void WebVideoFullscreenManagerProxy::fullscreenMayReturnToInline(uint64_t contextId)
479 {
480     bool isViewVisible = m_page->isViewVisible();
481     m_page->send(Messages::WebVideoFullscreenManager::FullscreenMayReturnToInline(contextId, isViewVisible), m_page->pageID());
482 }
483
484 #endif
485
486 } // namespace WebKit
487
488 #endif // PLATFORM(IOS) || (PLATFORM(MAC) && ENABLE(VIDEO_PRESENTATION_MODE))