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