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