Add UIDelegatePrivate SPI corresponding to WKPageUIClient.showPage
[WebKit-https.git] / Source / WebKit / UIProcess / Cocoa / UIDelegate.mm
1 /*
2  * Copyright (C) 2014-2016 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 "UIDelegate.h"
28
29 #if WK_API_ENABLED
30
31 #import "APIFrameInfo.h"
32 #import "CompletionHandlerCallChecker.h"
33 #import "NavigationActionData.h"
34 #import "UserMediaPermissionCheckProxy.h"
35 #import "UserMediaPermissionRequestProxy.h"
36 #import "WKFrameInfoInternal.h"
37 #import "WKNavigationActionInternal.h"
38 #import "WKOpenPanelParametersInternal.h"
39 #import "WKSecurityOriginInternal.h"
40 #import "WKUIDelegatePrivate.h"
41 #import "WKWebViewConfigurationInternal.h"
42 #import "WKWebViewInternal.h"
43 #import "WKWindowFeaturesInternal.h"
44 #import "WebOpenPanelResultListenerProxy.h"
45 #import "WebProcessProxy.h"
46 #import "_WKContextMenuElementInfo.h"
47 #import "_WKFrameHandleInternal.h"
48 #import <WebCore/SecurityOriginData.h>
49 #import <WebCore/URL.h>
50 #import <wtf/BlockPtr.h>
51
52 #if PLATFORM(IOS)
53 #import <AVFoundation/AVCaptureDevice.h>
54 #import <AVFoundation/AVMediaFormat.h>
55 #import <wtf/SoftLinking.h>
56
57 SOFT_LINK_FRAMEWORK(AVFoundation);
58 SOFT_LINK_CLASS(AVFoundation, AVCaptureDevice);
59 SOFT_LINK_CONSTANT(AVFoundation, AVMediaTypeAudio, NSString *);
60 SOFT_LINK_CONSTANT(AVFoundation, AVMediaTypeVideo, NSString *);
61 #endif
62
63 namespace WebKit {
64
65 UIDelegate::UIDelegate(WKWebView *webView)
66     : m_webView(webView)
67 {
68 }
69
70 UIDelegate::~UIDelegate()
71 {
72 }
73
74 #if ENABLE(CONTEXT_MENUS)
75 std::unique_ptr<API::ContextMenuClient> UIDelegate::createContextMenuClient()
76 {
77     return std::make_unique<ContextMenuClient>(*this);
78 }
79 #endif
80
81 std::unique_ptr<API::UIClient> UIDelegate::createUIClient()
82 {
83     return std::make_unique<UIClient>(*this);
84 }
85
86 RetainPtr<id <WKUIDelegate> > UIDelegate::delegate()
87 {
88     return m_delegate.get();
89 }
90
91 void UIDelegate::setDelegate(id <WKUIDelegate> delegate)
92 {
93     m_delegate = delegate;
94
95     m_delegateMethods.webViewCreateWebViewWithConfigurationForNavigationActionWindowFeatures = [delegate respondsToSelector:@selector(webView:createWebViewWithConfiguration:forNavigationAction:windowFeatures:)];
96     m_delegateMethods.webViewCreateWebViewWithConfigurationForNavigationActionWindowFeaturesAsync = [delegate respondsToSelector:@selector(_webView:createWebViewWithConfiguration:forNavigationAction:windowFeatures:completionHandler:)];
97     m_delegateMethods.showPage = [delegate respondsToSelector:@selector(_showPage:)];
98     m_delegateMethods.webViewRunJavaScriptAlertPanelWithMessageInitiatedByFrameCompletionHandler = [delegate respondsToSelector:@selector(webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:)];
99     m_delegateMethods.webViewRunJavaScriptConfirmPanelWithMessageInitiatedByFrameCompletionHandler = [delegate respondsToSelector:@selector(webView:runJavaScriptConfirmPanelWithMessage:initiatedByFrame:completionHandler:)];
100     m_delegateMethods.webViewRunJavaScriptTextInputPanelWithPromptDefaultTextInitiatedByFrameCompletionHandler = [delegate respondsToSelector:@selector(webView:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:)];
101     m_delegateMethods.webViewRunBeforeUnloadConfirmPanelWithMessageInitiatedByFrameCompletionHandler = [delegate respondsToSelector:@selector(_webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame:completionHandler:)];
102
103 #if PLATFORM(MAC)
104     m_delegateMethods.webViewRunOpenPanelWithParametersInitiatedByFrameCompletionHandler = [delegate respondsToSelector:@selector(webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler:)];
105 #endif
106
107     m_delegateMethods.webViewDecideDatabaseQuotaForSecurityOriginCurrentQuotaCurrentOriginUsageCurrentDatabaseUsageExpectedUsageDecisionHandler = [delegate respondsToSelector:@selector(_webView:decideDatabaseQuotaForSecurityOrigin:currentQuota:currentOriginUsage:currentDatabaseUsage:expectedUsage:decisionHandler:)];
108     m_delegateMethods.webViewDecideWebApplicationCacheQuotaForSecurityOriginCurrentQuotaTotalBytesNeeded = [delegate respondsToSelector:@selector(_webView:decideWebApplicationCacheQuotaForSecurityOrigin:currentQuota:totalBytesNeeded:decisionHandler:)];
109     m_delegateMethods.webViewPrintFrame = [delegate respondsToSelector:@selector(_webView:printFrame:)];
110     m_delegateMethods.webViewDidClose = [delegate respondsToSelector:@selector(webViewDidClose:)];
111     m_delegateMethods.webViewClose = [delegate respondsToSelector:@selector(_webViewClose:)];
112     m_delegateMethods.webViewFullscreenMayReturnToInline = [delegate respondsToSelector:@selector(_webViewFullscreenMayReturnToInline:)];
113     m_delegateMethods.webViewDidEnterFullscreen = [delegate respondsToSelector:@selector(_webViewDidEnterFullscreen:)];
114     m_delegateMethods.webViewDidExitFullscreen = [delegate respondsToSelector:@selector(_webViewDidExitFullscreen:)];
115 #if PLATFORM(IOS)
116 #if HAVE(APP_LINKS)
117     m_delegateMethods.webViewShouldIncludeAppLinkActionsForElement = [delegate respondsToSelector:@selector(_webView:shouldIncludeAppLinkActionsForElement:)];
118 #endif
119     m_delegateMethods.webViewActionsForElementDefaultActions = [delegate respondsToSelector:@selector(_webView:actionsForElement:defaultActions:)];
120     m_delegateMethods.webViewDidNotHandleTapAsClickAtPoint = [delegate respondsToSelector:@selector(_webView:didNotHandleTapAsClickAtPoint:)];
121     m_delegateMethods.presentingViewControllerForWebView = [delegate respondsToSelector:@selector(_presentingViewControllerForWebView:)];
122 #endif
123     m_delegateMethods.webViewRequestUserMediaAuthorizationForDevicesURLMainFrameURLDecisionHandler = [delegate respondsToSelector:@selector(_webView:requestUserMediaAuthorizationForDevices:url:mainFrameURL:decisionHandler:)];
124     m_delegateMethods.webViewCheckUserMediaPermissionForURLMainFrameURLFrameIdentifierDecisionHandler = [delegate respondsToSelector:@selector(_webView:checkUserMediaPermissionForURL:mainFrameURL:frameIdentifier:decisionHandler:)];
125     m_delegateMethods.webViewMediaCaptureStateDidChange = [delegate respondsToSelector:@selector(_webView:mediaCaptureStateDidChange:)];
126     m_delegateMethods.dataDetectionContextForWebView = [delegate respondsToSelector:@selector(_dataDetectionContextForWebView:)];
127     m_delegateMethods.webViewImageOrMediaDocumentSizeChanged = [delegate respondsToSelector:@selector(_webView:imageOrMediaDocumentSizeChanged:)];
128
129 #if ENABLE(POINTER_LOCK)
130     m_delegateMethods.webViewRequestPointerLock = [delegate respondsToSelector:@selector(_webViewRequestPointerLock:)];
131     m_delegateMethods.webViewDidLosePointerLock = [delegate respondsToSelector:@selector(_webViewDidLosePointerLock:)];
132 #endif
133 #if ENABLE(CONTEXT_MENUS)
134     m_delegateMethods.webViewContextMenuForElement = [delegate respondsToSelector:@selector(_webView:contextMenu:forElement:)];
135     m_delegateMethods.webViewContextMenuForElementUserInfo = [delegate respondsToSelector:@selector(_webView:contextMenu:forElement:userInfo:)];
136 #endif
137     
138     m_delegateMethods.webViewHasVideoInPictureInPictureDidChange = [delegate respondsToSelector:@selector(_webView:hasVideoInPictureInPictureDidChange:)];
139 }
140
141 #if ENABLE(CONTEXT_MENUS)
142 UIDelegate::ContextMenuClient::ContextMenuClient(UIDelegate& uiDelegate)
143     : m_uiDelegate(uiDelegate)
144 {
145 }
146
147 UIDelegate::ContextMenuClient::~ContextMenuClient()
148 {
149 }
150
151 RetainPtr<NSMenu> UIDelegate::ContextMenuClient::menuFromProposedMenu(WebKit::WebPageProxy&, NSMenu *menu, const WebKit::WebHitTestResultData&, API::Object* userInfo)
152 {
153     if (!m_uiDelegate.m_delegateMethods.webViewContextMenuForElement && !m_uiDelegate.m_delegateMethods.webViewContextMenuForElementUserInfo)
154         return menu;
155
156     auto delegate = m_uiDelegate.m_delegate.get();
157     if (!delegate)
158         return menu;
159
160     auto contextMenuElementInfo = adoptNS([[_WKContextMenuElementInfo alloc] init]);
161
162     if (m_uiDelegate.m_delegateMethods.webViewContextMenuForElement)
163         return [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView contextMenu:menu forElement:contextMenuElementInfo.get()];
164
165     return [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView contextMenu:menu forElement:contextMenuElementInfo.get() userInfo:static_cast<id <NSSecureCoding>>(userInfo->wrapper())];
166 }
167 #endif
168
169 UIDelegate::UIClient::UIClient(UIDelegate& uiDelegate)
170     : m_uiDelegate(uiDelegate)
171 {
172 }
173
174 UIDelegate::UIClient::~UIClient()
175 {
176 }
177
178 RefPtr<WebKit::WebPageProxy> UIDelegate::UIClient::createNewPageCommon(WebKit::WebPageProxy* page, API::FrameInfo& sourceFrameInfo, WebCore::ResourceRequest&& request, const WebCore::WindowFeatures& windowFeatures, WebKit::NavigationActionData&& navigationActionData, WTF::Function<void(RefPtr<WebKit::WebPageProxy>&&)>&& completionHandler)
179 {
180     auto delegate = m_uiDelegate.m_delegate.get();
181     ASSERT(delegate);
182
183     auto configuration = adoptNS([m_uiDelegate.m_webView->_configuration copy]);
184     [configuration _setRelatedWebView:m_uiDelegate.m_webView];
185
186     auto userInitiatedActivity = page->process().userInitiatedActivity(navigationActionData.userGestureTokenIdentifier);
187     bool shouldOpenAppLinks = !hostsAreEqual(sourceFrameInfo.request().url(), request.url());
188     auto apiNavigationAction = API::NavigationAction::create(WTFMove(navigationActionData), &sourceFrameInfo, nullptr, WTFMove(request), WebCore::URL(), shouldOpenAppLinks, WTFMove(userInitiatedActivity));
189
190     auto apiWindowFeatures = API::WindowFeatures::create(windowFeatures);
191
192     if (completionHandler) {
193         RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:createWebViewWithConfiguration:forNavigationAction:windowFeatures:completionHandler:));
194
195         [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView createWebViewWithConfiguration:configuration.get() forNavigationAction:wrapper(apiNavigationAction) windowFeatures:wrapper(apiWindowFeatures) completionHandler:BlockPtr<void (WKWebView *)>::fromCallable([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker), relatedWebView = RetainPtr<WKWebView>(m_uiDelegate.m_webView)](WKWebView *webView) {
196             if (checker->completionHandlerHasBeenCalled())
197                 return;
198             checker->didCallCompletionHandler();
199
200             if (!webView) {
201                 completionHandler(nullptr);
202                 return;
203             }
204
205             if ([webView->_configuration _relatedWebView] != relatedWebView.get())
206                 [NSException raise:NSInternalInconsistencyException format:@"Returned WKWebView was not created with the given configuration."];
207
208             completionHandler(webView->_page.get());
209         }).get()];
210
211         return nullptr;
212     }
213
214     RetainPtr<WKWebView> webView = [delegate webView:m_uiDelegate.m_webView createWebViewWithConfiguration:configuration.get() forNavigationAction:wrapper(apiNavigationAction) windowFeatures:wrapper(apiWindowFeatures)];
215
216     if (!webView)
217         return nullptr;
218
219     if ([webView->_configuration _relatedWebView] != m_uiDelegate.m_webView)
220         [NSException raise:NSInternalInconsistencyException format:@"Returned WKWebView was not created with the given configuration."];
221
222     return webView->_page.get();
223 }
224
225 RefPtr<WebPageProxy> UIDelegate::UIClient::createNewPage(WebPageProxy* page, API::FrameInfo& originatingFrameInfo, WebCore::ResourceRequest&& request, const WebCore::WindowFeatures& windowFeatures, NavigationActionData&& navigationActionData)
226 {
227     if (!m_uiDelegate.m_delegateMethods.webViewCreateWebViewWithConfigurationForNavigationActionWindowFeatures)
228         return nullptr;
229
230     auto delegate = m_uiDelegate.m_delegate.get();
231     if (!delegate)
232         return nullptr;
233
234     return createNewPageCommon(page, originatingFrameInfo, WTFMove(request), windowFeatures, WTFMove(navigationActionData), nullptr);
235 }
236
237 bool UIDelegate::UIClient::canCreateNewPageAsync()
238 {
239     return m_uiDelegate.m_delegateMethods.webViewCreateWebViewWithConfigurationForNavigationActionWindowFeaturesAsync
240         && m_uiDelegate.m_delegate.get();
241 }
242
243 void UIDelegate::UIClient::createNewPageAsync(WebPageProxy* page, API::FrameInfo& originatingFrameInfo, WebCore::ResourceRequest&& request, const WebCore::WindowFeatures& windowFeatures, NavigationActionData&& navigationActionData, WTF::Function<void(RefPtr<WebPageProxy>&&)>&& completionHandler)
244 {
245     ASSERT(canCreateNewPageAsync());
246     ASSERT(m_uiDelegate.m_delegateMethods.webViewCreateWebViewWithConfigurationForNavigationActionWindowFeaturesAsync);
247
248     auto delegate = m_uiDelegate.m_delegate.get();
249     ASSERT(delegate);
250
251     createNewPageCommon(page, originatingFrameInfo, WTFMove(request), windowFeatures, WTFMove(navigationActionData), WTFMove(completionHandler));
252 }
253
254 void UIDelegate::UIClient::showPage(WebPageProxy*)
255 {
256     if (!m_uiDelegate.m_delegateMethods.showPage)
257         return;
258     auto delegate = m_uiDelegate.m_delegate.get();
259     ASSERT(delegate);
260     [(id <WKUIDelegatePrivate>)delegate _showPage:m_uiDelegate.m_webView];
261 }
262
263 void UIDelegate::UIClient::runJavaScriptAlert(WebKit::WebPageProxy*, const WTF::String& message, WebKit::WebFrameProxy* webFrameProxy, const WebCore::SecurityOriginData& securityOriginData, Function<void ()>&& completionHandler)
264 {
265     if (!m_uiDelegate.m_delegateMethods.webViewRunJavaScriptAlertPanelWithMessageInitiatedByFrameCompletionHandler) {
266         completionHandler();
267         return;
268     }
269
270     auto delegate = m_uiDelegate.m_delegate.get();
271     if (!delegate) {
272         completionHandler();
273         return;
274     }
275
276     RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:));
277     [delegate webView:m_uiDelegate.m_webView runJavaScriptAlertPanelWithMessage:message initiatedByFrame:wrapper(API::FrameInfo::create(*webFrameProxy, securityOriginData.securityOrigin())) completionHandler:BlockPtr<void ()>::fromCallable([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)] {
278         if (checker->completionHandlerHasBeenCalled())
279             return;
280         completionHandler();
281         checker->didCallCompletionHandler();
282     }).get()];
283 }
284
285 void UIDelegate::UIClient::runJavaScriptConfirm(WebKit::WebPageProxy*, const WTF::String& message, WebKit::WebFrameProxy* webFrameProxy, const WebCore::SecurityOriginData& securityOriginData, Function<void (bool)>&& completionHandler)
286 {
287     if (!m_uiDelegate.m_delegateMethods.webViewRunJavaScriptConfirmPanelWithMessageInitiatedByFrameCompletionHandler) {
288         completionHandler(false);
289         return;
290     }
291
292     auto delegate = m_uiDelegate.m_delegate.get();
293     if (!delegate) {
294         completionHandler(false);
295         return;
296     }
297
298     RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(webView:runJavaScriptConfirmPanelWithMessage:initiatedByFrame:completionHandler:));
299     [delegate webView:m_uiDelegate.m_webView runJavaScriptConfirmPanelWithMessage:message initiatedByFrame:wrapper(API::FrameInfo::create(*webFrameProxy, securityOriginData.securityOrigin())) completionHandler:BlockPtr<void (BOOL)>::fromCallable([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)](BOOL result) {
300         if (checker->completionHandlerHasBeenCalled())
301             return;
302         completionHandler(result);
303         checker->didCallCompletionHandler();
304     }).get()];
305 }
306
307 void UIDelegate::UIClient::runJavaScriptPrompt(WebKit::WebPageProxy*, const WTF::String& message, const WTF::String& defaultValue, WebKit::WebFrameProxy* webFrameProxy, const WebCore::SecurityOriginData& securityOriginData, Function<void (const WTF::String&)>&& completionHandler)
308 {
309     if (!m_uiDelegate.m_delegateMethods.webViewRunJavaScriptTextInputPanelWithPromptDefaultTextInitiatedByFrameCompletionHandler) {
310         completionHandler(String());
311         return;
312     }
313
314     auto delegate = m_uiDelegate.m_delegate.get();
315     if (!delegate) {
316         completionHandler(String());
317         return;
318     }
319
320     RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(webView:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:));
321     [delegate webView:m_uiDelegate.m_webView runJavaScriptTextInputPanelWithPrompt:message defaultText:defaultValue initiatedByFrame:wrapper(API::FrameInfo::create(*webFrameProxy, securityOriginData.securityOrigin())) completionHandler:BlockPtr<void (NSString *)>::fromCallable([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)](NSString *result) {
322         if (checker->completionHandlerHasBeenCalled())
323             return;
324         completionHandler(result);
325         checker->didCallCompletionHandler();
326     }).get()];
327 }
328
329 bool UIDelegate::UIClient::canRunBeforeUnloadConfirmPanel() const
330 {
331     return m_uiDelegate.m_delegateMethods.webViewRunBeforeUnloadConfirmPanelWithMessageInitiatedByFrameCompletionHandler;
332 }
333
334 void UIDelegate::UIClient::runBeforeUnloadConfirmPanel(WebKit::WebPageProxy*, const WTF::String& message, WebKit::WebFrameProxy* webFrameProxy, const WebCore::SecurityOriginData& securityOriginData, Function<void (bool)>&& completionHandler)
335 {
336     if (!m_uiDelegate.m_delegateMethods.webViewRunBeforeUnloadConfirmPanelWithMessageInitiatedByFrameCompletionHandler) {
337         completionHandler(false);
338         return;
339     }
340
341     auto delegate = m_uiDelegate.m_delegate.get();
342     if (!delegate) {
343         completionHandler(false);
344         return;
345     }
346
347     RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame:completionHandler:));
348     [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView runBeforeUnloadConfirmPanelWithMessage:message initiatedByFrame:wrapper(API::FrameInfo::create(*webFrameProxy, securityOriginData.securityOrigin())) completionHandler:BlockPtr<void (BOOL)>::fromCallable([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)](BOOL result) {
349         if (checker->completionHandlerHasBeenCalled())
350             return;
351         completionHandler(result);
352         checker->didCallCompletionHandler();
353     }).get()];
354 }
355
356 void UIDelegate::UIClient::exceededDatabaseQuota(WebPageProxy*, WebFrameProxy*, API::SecurityOrigin* securityOrigin, const WTF::String& databaseName, const WTF::String& displayName, unsigned long long currentQuota, unsigned long long currentOriginUsage, unsigned long long currentUsage, unsigned long long expectedUsage, Function<void (unsigned long long)>&& completionHandler)
357 {
358     if (!m_uiDelegate.m_delegateMethods.webViewDecideDatabaseQuotaForSecurityOriginCurrentQuotaCurrentOriginUsageCurrentDatabaseUsageExpectedUsageDecisionHandler) {
359
360         // Use 50 MB as the default database quota.
361         unsigned long long defaultPerOriginDatabaseQuota = 50 * 1024 * 1024;
362
363         completionHandler(defaultPerOriginDatabaseQuota);
364         return;
365     }
366
367     auto delegate = m_uiDelegate.m_delegate.get();
368     if (!delegate) {
369         completionHandler(currentQuota);
370         return;
371     }
372
373     ASSERT(securityOrigin);
374     RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:decideDatabaseQuotaForSecurityOrigin:currentQuota:currentOriginUsage:currentDatabaseUsage:expectedUsage:decisionHandler:));
375     [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView decideDatabaseQuotaForSecurityOrigin:wrapper(*securityOrigin) currentQuota:currentQuota currentOriginUsage:currentOriginUsage currentDatabaseUsage:currentUsage expectedUsage:expectedUsage decisionHandler:BlockPtr<void (unsigned long long newQuota)>::fromCallable([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)](unsigned long long newQuota) {
376         if (checker->completionHandlerHasBeenCalled())
377             return;
378         checker->didCallCompletionHandler();
379         completionHandler(newQuota);
380     }).get()];
381 }
382
383 #if PLATFORM(MAC)
384 bool UIDelegate::UIClient::runOpenPanel(WebPageProxy*, WebFrameProxy* webFrameProxy, const WebCore::SecurityOriginData& securityOriginData, API::OpenPanelParameters* openPanelParameters, WebOpenPanelResultListenerProxy* listener)
385 {
386     if (!m_uiDelegate.m_delegateMethods.webViewRunOpenPanelWithParametersInitiatedByFrameCompletionHandler)
387         return false;
388
389     auto delegate = m_uiDelegate.m_delegate.get();
390     if (!delegate)
391         return false;
392
393     auto frame = API::FrameInfo::create(*webFrameProxy, securityOriginData.securityOrigin());
394     RefPtr<WebOpenPanelResultListenerProxy> resultListener = listener;
395
396     RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler:));
397
398     [delegate webView:m_uiDelegate.m_webView runOpenPanelWithParameters:wrapper(*openPanelParameters) initiatedByFrame:wrapper(frame) completionHandler:[checker, resultListener](NSArray *URLs) {
399         if (checker->completionHandlerHasBeenCalled())
400             return;
401         checker->didCallCompletionHandler();
402
403         if (!URLs) {
404             resultListener->cancel();
405             return;
406         }
407
408         Vector<String> filenames;
409         for (NSURL *url in URLs)
410             filenames.append(url.path);
411
412         resultListener->chooseFiles(filenames);
413     }];
414
415     return true;
416 }
417 #endif
418
419 static void requestUserMediaAuthorizationForDevices(const WebKit::WebFrameProxy& frame, WebKit::UserMediaPermissionRequestProxy& request, id <WKUIDelegatePrivate> delegate, WKWebView& webView)
420 {
421     auto decisionHandler = BlockPtr<void(BOOL)>::fromCallable([protectedRequest = makeRef(request)](BOOL authorized) {
422         if (!authorized) {
423             protectedRequest->deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied);
424             return;
425         }
426         const String& videoDeviceUID = protectedRequest->requiresVideo() ? protectedRequest->videoDeviceUIDs().first() : String();
427         const String& audioDeviceUID = protectedRequest->requiresAudio() ? protectedRequest->audioDeviceUIDs().first() : String();
428         protectedRequest->allow(audioDeviceUID, videoDeviceUID);
429     });
430
431     const WebFrameProxy* mainFrame = frame.page()->mainFrame();
432     WebCore::URL requestFrameURL(WebCore::URL(), frame.url());
433     WebCore::URL mainFrameURL(WebCore::URL(), mainFrame->url());
434
435     _WKCaptureDevices devices = 0;
436     if (request.requiresAudio())
437         devices |= _WKCaptureDeviceMicrophone;
438     if (request.requiresVideo())
439         devices |= _WKCaptureDeviceCamera;
440
441     auto protectedWebView = RetainPtr<WKWebView>(&webView);
442     [delegate _webView:protectedWebView.get() requestUserMediaAuthorizationForDevices:devices url:requestFrameURL mainFrameURL:mainFrameURL decisionHandler:decisionHandler.get()];
443 }
444
445 bool UIDelegate::UIClient::decidePolicyForUserMediaPermissionRequest(WebKit::WebPageProxy& page, WebKit::WebFrameProxy& frame, API::SecurityOrigin& userMediaOrigin, API::SecurityOrigin& topLevelOrigin, WebKit::UserMediaPermissionRequestProxy& request)
446 {
447     auto delegate = m_uiDelegate.m_delegate.get();
448     if (!delegate || !m_uiDelegate.m_delegateMethods.webViewRequestUserMediaAuthorizationForDevicesURLMainFrameURLDecisionHandler) {
449         request.deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::UserMediaDisabled);
450         return true;
451     }
452
453     bool requiresAudio = request.requiresAudio();
454     bool requiresVideo = request.requiresVideo();
455     if (!requiresAudio && !requiresVideo) {
456         request.deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::NoConstraints);
457         return true;
458     }
459
460 #if PLATFORM(IOS)
461     auto requestCameraAuthorization = BlockPtr<void()>::fromCallable([this, &frame, protectedRequest = makeRef(request), webView = RetainPtr<WKWebView>(m_uiDelegate.m_webView)]() {
462
463         if (!protectedRequest->requiresVideo()) {
464             requestUserMediaAuthorizationForDevices(frame, protectedRequest, (id <WKUIDelegatePrivate>)m_uiDelegate.m_delegate.get(), *webView.get());
465             return;
466         }
467         AVAuthorizationStatus cameraAuthorizationStatus = [getAVCaptureDeviceClass() authorizationStatusForMediaType:getAVMediaTypeVideo()];
468         switch (cameraAuthorizationStatus) {
469         case AVAuthorizationStatusAuthorized:
470             requestUserMediaAuthorizationForDevices(frame, protectedRequest, (id <WKUIDelegatePrivate>)m_uiDelegate.m_delegate.get(), *webView.get());
471             break;
472         case AVAuthorizationStatusDenied:
473         case AVAuthorizationStatusRestricted:
474             protectedRequest->deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied);
475             return;
476         case AVAuthorizationStatusNotDetermined:
477             auto decisionHandler = BlockPtr<void(BOOL)>::fromCallable([this, &frame, protectedRequest = makeRef(protectedRequest.get()), webView = RetainPtr<WKWebView>(m_uiDelegate.m_webView)](BOOL authorized) {
478                 if (!authorized) {
479                     protectedRequest->deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied);
480                     return;
481                 }
482                 requestUserMediaAuthorizationForDevices(frame, protectedRequest, (id <WKUIDelegatePrivate>)m_uiDelegate.m_delegate.get(), *webView.get());
483             });
484
485             [getAVCaptureDeviceClass() requestAccessForMediaType:getAVMediaTypeVideo() completionHandler:decisionHandler.get()];
486             break;
487         }
488     });
489
490     if (requiresAudio) {
491         AVAuthorizationStatus microphoneAuthorizationStatus = [getAVCaptureDeviceClass() authorizationStatusForMediaType:getAVMediaTypeAudio()];
492         switch (microphoneAuthorizationStatus) {
493         case AVAuthorizationStatusAuthorized:
494             requestCameraAuthorization();
495             break;
496         case AVAuthorizationStatusDenied:
497         case AVAuthorizationStatusRestricted:
498             request.deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied);
499             return true;
500         case AVAuthorizationStatusNotDetermined:
501             auto decisionHandler = BlockPtr<void(BOOL)>::fromCallable([protectedRequest = makeRef(request), requestCameraAuthorization](BOOL authorized) {
502                 if (!authorized) {
503                     protectedRequest->deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied);
504                     return;
505                 }
506                 requestCameraAuthorization();
507             });
508
509             [getAVCaptureDeviceClass() requestAccessForMediaType:getAVMediaTypeAudio() completionHandler:decisionHandler.get()];
510             break;
511         }
512     } else
513         requestCameraAuthorization();
514 #else
515     requestUserMediaAuthorizationForDevices(frame, request, (id <WKUIDelegatePrivate>)m_uiDelegate.m_delegate.get(), *m_uiDelegate.m_webView);
516 #endif
517
518     return true;
519 }
520
521 bool UIDelegate::UIClient::checkUserMediaPermissionForOrigin(WebKit::WebPageProxy& page, WebKit::WebFrameProxy& frame, API::SecurityOrigin& userMediaOrigin, API::SecurityOrigin& topLevelOrigin, WebKit::UserMediaPermissionCheckProxy& request)
522 {
523     auto delegate = m_uiDelegate.m_delegate.get();
524     if (!delegate || !m_uiDelegate.m_delegateMethods.webViewCheckUserMediaPermissionForURLMainFrameURLFrameIdentifierDecisionHandler) {
525         request.setUserMediaAccessInfo(String(), false);
526         return true;
527     }
528
529     WKWebView *webView = m_uiDelegate.m_webView;
530     const WebFrameProxy* mainFrame = frame.page()->mainFrame();
531     WebCore::URL requestFrameURL(WebCore::URL(), frame.url());
532     WebCore::URL mainFrameURL(WebCore::URL(), mainFrame->url());
533
534     auto decisionHandler = BlockPtr<void(NSString *, BOOL)>::fromCallable([protectedRequest = makeRef(request)](NSString *salt, BOOL authorized) {
535         protectedRequest->setUserMediaAccessInfo(String(salt), authorized);
536     });
537
538     [(id <WKUIDelegatePrivate>)delegate _webView:webView checkUserMediaPermissionForURL:requestFrameURL mainFrameURL:mainFrameURL frameIdentifier:frame.frameID() decisionHandler:decisionHandler.get()];
539
540     return true;
541 }
542
543 void UIDelegate::UIClient::mediaCaptureStateDidChange(WebCore::MediaProducer::MediaStateFlags state)
544 {
545     WKWebView *webView = m_uiDelegate.m_webView;
546     auto delegate = m_uiDelegate.m_delegate.get();
547     if (!delegate || !m_uiDelegate.m_delegateMethods.webViewMediaCaptureStateDidChange)
548         return;
549
550     _WKMediaCaptureState mediaCaptureState = _WKMediaCaptureStateNone;
551     if (state & WebCore::MediaProducer::HasActiveAudioCaptureDevice)
552         mediaCaptureState |= _WKMediaCaptureStateActiveMicrophone;
553     if (state & WebCore::MediaProducer::HasActiveVideoCaptureDevice)
554         mediaCaptureState |= _WKMediaCaptureStateActiveCamera;
555     if (state & WebCore::MediaProducer::HasMutedAudioCaptureDevice)
556         mediaCaptureState |= _WKMediaCaptureStateMutedMicrophone;
557     if (state & WebCore::MediaProducer::HasMutedVideoCaptureDevice)
558         mediaCaptureState |= _WKMediaCaptureStateMutedCamera;
559
560     [(id <WKUIDelegatePrivate>)delegate _webView:webView mediaCaptureStateDidChange:mediaCaptureState];
561 }
562
563 void UIDelegate::UIClient::reachedApplicationCacheOriginQuota(WebPageProxy*, const WebCore::SecurityOrigin& securityOrigin, uint64_t currentQuota, uint64_t totalBytesNeeded, Function<void (unsigned long long)>&& completionHandler)
564 {
565     if (!m_uiDelegate.m_delegateMethods.webViewDecideWebApplicationCacheQuotaForSecurityOriginCurrentQuotaTotalBytesNeeded) {
566         completionHandler(currentQuota);
567         return;
568     }
569
570     auto delegate = m_uiDelegate.m_delegate.get();
571     if (!delegate) {
572         completionHandler(currentQuota);
573         return;
574     }
575
576     RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:decideWebApplicationCacheQuotaForSecurityOrigin:currentQuota:totalBytesNeeded:decisionHandler:));
577     RefPtr<API::SecurityOrigin> apiOrigin = API::SecurityOrigin::create(securityOrigin);
578     
579     [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView decideWebApplicationCacheQuotaForSecurityOrigin:wrapper(*apiOrigin) currentQuota:currentQuota totalBytesNeeded:totalBytesNeeded decisionHandler:BlockPtr<void (unsigned long long)>::fromCallable([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)](unsigned long long newQuota) {
580         if (checker->completionHandlerHasBeenCalled())
581             return;
582         checker->didCallCompletionHandler();
583         completionHandler(newQuota);
584     }).get()];
585 }
586
587 void UIDelegate::UIClient::printFrame(WebKit::WebPageProxy*, WebKit::WebFrameProxy* webFrameProxy)
588 {
589     ASSERT_ARG(webFrameProxy, webFrameProxy);
590
591     if (!m_uiDelegate.m_delegateMethods.webViewPrintFrame)
592         return;
593
594     auto delegate = m_uiDelegate.m_delegate.get();
595     if (!delegate)
596         return;
597
598     [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView printFrame:wrapper(API::FrameHandle::create(webFrameProxy->frameID()))];
599 }
600
601 void UIDelegate::UIClient::close(WebKit::WebPageProxy*)
602 {
603     if (m_uiDelegate.m_delegateMethods.webViewClose) {
604         auto delegate = m_uiDelegate.m_delegate.get();
605         if (!delegate)
606             return;
607
608         [(id <WKUIDelegatePrivate>)delegate _webViewClose:m_uiDelegate.m_webView];
609         return;
610     }
611
612     if (!m_uiDelegate.m_delegateMethods.webViewDidClose)
613         return;
614
615     auto delegate = m_uiDelegate.m_delegate.get();
616     if (!delegate)
617         return;
618
619     [delegate webViewDidClose:m_uiDelegate.m_webView];
620 }
621
622 void UIDelegate::UIClient::fullscreenMayReturnToInline(WebKit::WebPageProxy*)
623 {
624     if (!m_uiDelegate.m_delegateMethods.webViewFullscreenMayReturnToInline)
625         return;
626     
627     auto delegate = m_uiDelegate.m_delegate.get();
628     if (!delegate)
629         return;
630     
631     [(id <WKUIDelegatePrivate>)delegate _webViewFullscreenMayReturnToInline:m_uiDelegate.m_webView];
632 }
633
634 void UIDelegate::UIClient::didEnterFullscreen(WebKit::WebPageProxy*)
635 {
636     if (!m_uiDelegate.m_delegateMethods.webViewDidEnterFullscreen)
637         return;
638
639     auto delegate = m_uiDelegate.m_delegate.get();
640     if (!delegate)
641         return;
642
643     [(id <WKUIDelegatePrivate>)delegate _webViewDidEnterFullscreen:m_uiDelegate.m_webView];
644 }
645
646 void UIDelegate::UIClient::didExitFullscreen(WebKit::WebPageProxy*)
647 {
648     if (!m_uiDelegate.m_delegateMethods.webViewDidExitFullscreen)
649         return;
650
651     auto delegate = m_uiDelegate.m_delegate.get();
652     if (!delegate)
653         return;
654
655     [(id <WKUIDelegatePrivate>)delegate _webViewDidExitFullscreen:m_uiDelegate.m_webView];
656 }
657     
658 #if PLATFORM(IOS)
659 #if HAVE(APP_LINKS)
660 bool UIDelegate::UIClient::shouldIncludeAppLinkActionsForElement(_WKActivatedElementInfo *elementInfo)
661 {
662     if (!m_uiDelegate.m_delegateMethods.webViewShouldIncludeAppLinkActionsForElement)
663         return true;
664
665     auto delegate = m_uiDelegate.m_delegate.get();
666     if (!delegate)
667         return true;
668
669     return [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView shouldIncludeAppLinkActionsForElement:elementInfo];
670 }
671 #endif
672
673 RetainPtr<NSArray> UIDelegate::UIClient::actionsForElement(_WKActivatedElementInfo *elementInfo, RetainPtr<NSArray> defaultActions)
674 {
675     if (!m_uiDelegate.m_delegateMethods.webViewActionsForElementDefaultActions)
676         return defaultActions;
677
678     auto delegate = m_uiDelegate.m_delegate.get();
679     if (!delegate)
680         return defaultActions;
681
682     return [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView actionsForElement:elementInfo defaultActions:defaultActions.get()];
683 }
684
685 void UIDelegate::UIClient::didNotHandleTapAsClick(const WebCore::IntPoint& point)
686 {
687     if (!m_uiDelegate.m_delegateMethods.webViewDidNotHandleTapAsClickAtPoint)
688         return;
689
690     auto delegate = m_uiDelegate.m_delegate.get();
691     if (!delegate)
692         return;
693
694     [static_cast<id <WKUIDelegatePrivate>>(delegate) _webView:m_uiDelegate.m_webView didNotHandleTapAsClickAtPoint:point];
695 }
696
697 UIViewController *UIDelegate::UIClient::presentingViewController()
698 {
699     if (!m_uiDelegate.m_delegateMethods.presentingViewControllerForWebView)
700         return nullptr;
701
702     auto delegate = m_uiDelegate.m_delegate.get();
703     if (!delegate)
704         return nullptr;
705
706     return [static_cast<id <WKUIDelegatePrivate>>(delegate) _presentingViewControllerForWebView:m_uiDelegate.m_webView];
707 }
708
709 #endif
710
711 NSDictionary *UIDelegate::UIClient::dataDetectionContext()
712 {
713     if (!m_uiDelegate.m_delegateMethods.dataDetectionContextForWebView)
714         return nullptr;
715
716     auto delegate = m_uiDelegate.m_delegate.get();
717     if (!delegate)
718         return nullptr;
719
720     return [static_cast<id <WKUIDelegatePrivate>>(delegate) _dataDetectionContextForWebView:m_uiDelegate.m_webView];
721 }
722
723 #if ENABLE(POINTER_LOCK)
724
725 void UIDelegate::UIClient::requestPointerLock(WebKit::WebPageProxy*)
726 {
727     if (!m_uiDelegate.m_delegateMethods.webViewRequestPointerLock)
728         return;
729
730     auto delegate = m_uiDelegate.m_delegate.get();
731     if (!delegate)
732         return;
733
734     [static_cast<id <WKUIDelegatePrivate>>(delegate) _webViewRequestPointerLock:m_uiDelegate.m_webView];
735 }
736
737 void UIDelegate::UIClient::didLosePointerLock(WebKit::WebPageProxy*)
738 {
739     if (!m_uiDelegate.m_delegateMethods.webViewDidLosePointerLock)
740         return;
741
742     auto delegate = m_uiDelegate.m_delegate.get();
743     if (!delegate)
744         return;
745
746     [static_cast<id <WKUIDelegatePrivate>>(delegate) _webViewDidLosePointerLock:m_uiDelegate.m_webView];
747 }
748
749 #endif
750     
751 void UIDelegate::UIClient::hasVideoInPictureInPictureDidChange(WebKit::WebPageProxy*, bool hasVideoInPictureInPicture)
752 {
753     if (!m_uiDelegate.m_delegateMethods.webViewHasVideoInPictureInPictureDidChange)
754         return;
755     
756     auto delegate = m_uiDelegate.m_delegate.get();
757     if (!delegate)
758         return;
759     
760     [static_cast<id <WKUIDelegatePrivate>>(delegate) _webView:m_uiDelegate.m_webView hasVideoInPictureInPictureDidChange:hasVideoInPictureInPicture];
761 }
762
763 void UIDelegate::UIClient::imageOrMediaDocumentSizeChanged(const WebCore::IntSize& newSize)
764 {
765     if (!m_uiDelegate.m_delegateMethods.webViewImageOrMediaDocumentSizeChanged)
766         return;
767
768     auto delegate = m_uiDelegate.m_delegate.get();
769     if (!delegate)
770         return;
771
772     [static_cast<id <WKUIDelegatePrivate>>(delegate) _webView:m_uiDelegate.m_webView imageOrMediaDocumentSizeChanged:newSize];
773 }
774
775 } // namespace WebKit
776
777 #endif // WK_API_ENABLED