Modernize NavigationAction code
[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.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, WebCore::ResourceRequest&& request, const WebCore::WindowFeatures& windowFeatures, 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(WTFMove(navigationActionData), &sourceFrameInfo, nullptr, WTFMove(request), WebCore::URL(), shouldOpenAppLinks, WTFMove(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<WebPageProxy> UIDelegate::UIClient::createNewPage(WebPageProxy* page, API::FrameInfo& originatingFrameInfo, WebCore::ResourceRequest&& request, const WebCore::WindowFeatures& windowFeatures, 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, WTFMove(request), windowFeatures, WTFMove(navigationActionData), nullptr);
234 }
235
236 bool UIDelegate::UIClient::canCreateNewPageAsync()
237 {
238     return m_uiDelegate.m_delegateMethods.webViewCreateWebViewWithConfigurationForNavigationActionWindowFeaturesAsync
239         && m_uiDelegate.m_delegate.get();
240 }
241
242 void UIDelegate::UIClient::createNewPageAsync(WebPageProxy* page, API::FrameInfo& originatingFrameInfo, WebCore::ResourceRequest&& request, const WebCore::WindowFeatures& windowFeatures, NavigationActionData&& navigationActionData, WTF::Function<void(RefPtr<WebPageProxy>&&)>&& completionHandler)
243 {
244     ASSERT(canCreateNewPageAsync());
245     ASSERT(m_uiDelegate.m_delegateMethods.webViewCreateWebViewWithConfigurationForNavigationActionWindowFeaturesAsync);
246
247     auto delegate = m_uiDelegate.m_delegate.get();
248     ASSERT(delegate);
249
250     createNewPageCommon(page, originatingFrameInfo, WTFMove(request), windowFeatures, WTFMove(navigationActionData), WTFMove(completionHandler));
251 }
252
253 void UIDelegate::UIClient::runJavaScriptAlert(WebKit::WebPageProxy*, const WTF::String& message, WebKit::WebFrameProxy* webFrameProxy, const WebCore::SecurityOriginData& securityOriginData, Function<void ()>&& completionHandler)
254 {
255     if (!m_uiDelegate.m_delegateMethods.webViewRunJavaScriptAlertPanelWithMessageInitiatedByFrameCompletionHandler) {
256         completionHandler();
257         return;
258     }
259
260     auto delegate = m_uiDelegate.m_delegate.get();
261     if (!delegate) {
262         completionHandler();
263         return;
264     }
265
266     RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:));
267     [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)] {
268         if (checker->completionHandlerHasBeenCalled())
269             return;
270         completionHandler();
271         checker->didCallCompletionHandler();
272     }).get()];
273 }
274
275 void UIDelegate::UIClient::runJavaScriptConfirm(WebKit::WebPageProxy*, const WTF::String& message, WebKit::WebFrameProxy* webFrameProxy, const WebCore::SecurityOriginData& securityOriginData, Function<void (bool)>&& completionHandler)
276 {
277     if (!m_uiDelegate.m_delegateMethods.webViewRunJavaScriptConfirmPanelWithMessageInitiatedByFrameCompletionHandler) {
278         completionHandler(false);
279         return;
280     }
281
282     auto delegate = m_uiDelegate.m_delegate.get();
283     if (!delegate) {
284         completionHandler(false);
285         return;
286     }
287
288     RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(webView:runJavaScriptConfirmPanelWithMessage:initiatedByFrame:completionHandler:));
289     [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) {
290         if (checker->completionHandlerHasBeenCalled())
291             return;
292         completionHandler(result);
293         checker->didCallCompletionHandler();
294     }).get()];
295 }
296
297 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)
298 {
299     if (!m_uiDelegate.m_delegateMethods.webViewRunJavaScriptTextInputPanelWithPromptDefaultTextInitiatedByFrameCompletionHandler) {
300         completionHandler(String());
301         return;
302     }
303
304     auto delegate = m_uiDelegate.m_delegate.get();
305     if (!delegate) {
306         completionHandler(String());
307         return;
308     }
309
310     RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(webView:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:));
311     [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) {
312         if (checker->completionHandlerHasBeenCalled())
313             return;
314         completionHandler(result);
315         checker->didCallCompletionHandler();
316     }).get()];
317 }
318
319 bool UIDelegate::UIClient::canRunBeforeUnloadConfirmPanel() const
320 {
321     return m_uiDelegate.m_delegateMethods.webViewRunBeforeUnloadConfirmPanelWithMessageInitiatedByFrameCompletionHandler;
322 }
323
324 void UIDelegate::UIClient::runBeforeUnloadConfirmPanel(WebKit::WebPageProxy*, const WTF::String& message, WebKit::WebFrameProxy* webFrameProxy, const WebCore::SecurityOriginData& securityOriginData, Function<void (bool)>&& completionHandler)
325 {
326     if (!m_uiDelegate.m_delegateMethods.webViewRunBeforeUnloadConfirmPanelWithMessageInitiatedByFrameCompletionHandler) {
327         completionHandler(false);
328         return;
329     }
330
331     auto delegate = m_uiDelegate.m_delegate.get();
332     if (!delegate) {
333         completionHandler(false);
334         return;
335     }
336
337     RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame:completionHandler:));
338     [(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) {
339         if (checker->completionHandlerHasBeenCalled())
340             return;
341         completionHandler(result);
342         checker->didCallCompletionHandler();
343     }).get()];
344 }
345
346 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)
347 {
348     if (!m_uiDelegate.m_delegateMethods.webViewDecideDatabaseQuotaForSecurityOriginCurrentQuotaCurrentOriginUsageCurrentDatabaseUsageExpectedUsageDecisionHandler) {
349
350         // Use 50 MB as the default database quota.
351         unsigned long long defaultPerOriginDatabaseQuota = 50 * 1024 * 1024;
352
353         completionHandler(defaultPerOriginDatabaseQuota);
354         return;
355     }
356
357     auto delegate = m_uiDelegate.m_delegate.get();
358     if (!delegate) {
359         completionHandler(currentQuota);
360         return;
361     }
362
363     ASSERT(securityOrigin);
364     RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:decideDatabaseQuotaForSecurityOrigin:currentQuota:currentOriginUsage:currentDatabaseUsage:expectedUsage:decisionHandler:));
365     [(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) {
366         if (checker->completionHandlerHasBeenCalled())
367             return;
368         checker->didCallCompletionHandler();
369         completionHandler(newQuota);
370     }).get()];
371 }
372
373 #if PLATFORM(MAC)
374 bool UIDelegate::UIClient::runOpenPanel(WebPageProxy*, WebFrameProxy* webFrameProxy, const WebCore::SecurityOriginData& securityOriginData, API::OpenPanelParameters* openPanelParameters, WebOpenPanelResultListenerProxy* listener)
375 {
376     if (!m_uiDelegate.m_delegateMethods.webViewRunOpenPanelWithParametersInitiatedByFrameCompletionHandler)
377         return false;
378
379     auto delegate = m_uiDelegate.m_delegate.get();
380     if (!delegate)
381         return false;
382
383     auto frame = API::FrameInfo::create(*webFrameProxy, securityOriginData.securityOrigin());
384     RefPtr<WebOpenPanelResultListenerProxy> resultListener = listener;
385
386     RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler:));
387
388     [delegate webView:m_uiDelegate.m_webView runOpenPanelWithParameters:wrapper(*openPanelParameters) initiatedByFrame:wrapper(frame) completionHandler:[checker, resultListener](NSArray *URLs) {
389         if (checker->completionHandlerHasBeenCalled())
390             return;
391         checker->didCallCompletionHandler();
392
393         if (!URLs) {
394             resultListener->cancel();
395             return;
396         }
397
398         Vector<String> filenames;
399         for (NSURL *url in URLs)
400             filenames.append(url.path);
401
402         resultListener->chooseFiles(filenames);
403     }];
404
405     return true;
406 }
407 #endif
408
409 static void requestUserMediaAuthorizationForDevices(const WebKit::WebFrameProxy& frame, WebKit::UserMediaPermissionRequestProxy& request, id <WKUIDelegatePrivate> delegate, WKWebView& webView)
410 {
411     auto decisionHandler = BlockPtr<void(BOOL)>::fromCallable([protectedRequest = makeRef(request)](BOOL authorized) {
412         if (!authorized) {
413             protectedRequest->deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied);
414             return;
415         }
416         const String& videoDeviceUID = protectedRequest->requiresVideo() ? protectedRequest->videoDeviceUIDs().first() : String();
417         const String& audioDeviceUID = protectedRequest->requiresAudio() ? protectedRequest->audioDeviceUIDs().first() : String();
418         protectedRequest->allow(audioDeviceUID, videoDeviceUID);
419     });
420
421     const WebFrameProxy* mainFrame = frame.page()->mainFrame();
422     WebCore::URL requestFrameURL(WebCore::URL(), frame.url());
423     WebCore::URL mainFrameURL(WebCore::URL(), mainFrame->url());
424
425     _WKCaptureDevices devices = 0;
426     if (request.requiresAudio())
427         devices |= _WKCaptureDeviceMicrophone;
428     if (request.requiresVideo())
429         devices |= _WKCaptureDeviceCamera;
430
431     auto protectedWebView = RetainPtr<WKWebView>(&webView);
432     [delegate _webView:protectedWebView.get() requestUserMediaAuthorizationForDevices:devices url:requestFrameURL mainFrameURL:mainFrameURL decisionHandler:decisionHandler.get()];
433 }
434
435 bool UIDelegate::UIClient::decidePolicyForUserMediaPermissionRequest(WebKit::WebPageProxy& page, WebKit::WebFrameProxy& frame, API::SecurityOrigin& userMediaOrigin, API::SecurityOrigin& topLevelOrigin, WebKit::UserMediaPermissionRequestProxy& request)
436 {
437     auto delegate = m_uiDelegate.m_delegate.get();
438     if (!delegate || !m_uiDelegate.m_delegateMethods.webViewRequestUserMediaAuthorizationForDevicesURLMainFrameURLDecisionHandler) {
439         request.deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::UserMediaDisabled);
440         return true;
441     }
442
443     bool requiresAudio = request.requiresAudio();
444     bool requiresVideo = request.requiresVideo();
445     if (!requiresAudio && !requiresVideo) {
446         request.deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::NoConstraints);
447         return true;
448     }
449
450 #if PLATFORM(IOS)
451     auto requestCameraAuthorization = BlockPtr<void()>::fromCallable([this, &frame, protectedRequest = makeRef(request), webView = RetainPtr<WKWebView>(m_uiDelegate.m_webView)]() {
452
453         if (!protectedRequest->requiresVideo()) {
454             requestUserMediaAuthorizationForDevices(frame, protectedRequest, (id <WKUIDelegatePrivate>)m_uiDelegate.m_delegate.get(), *webView.get());
455             return;
456         }
457         AVAuthorizationStatus cameraAuthorizationStatus = [getAVCaptureDeviceClass() authorizationStatusForMediaType:getAVMediaTypeVideo()];
458         switch (cameraAuthorizationStatus) {
459         case AVAuthorizationStatusAuthorized:
460             requestUserMediaAuthorizationForDevices(frame, protectedRequest, (id <WKUIDelegatePrivate>)m_uiDelegate.m_delegate.get(), *webView.get());
461             break;
462         case AVAuthorizationStatusDenied:
463         case AVAuthorizationStatusRestricted:
464             protectedRequest->deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied);
465             return;
466         case AVAuthorizationStatusNotDetermined:
467             auto decisionHandler = BlockPtr<void(BOOL)>::fromCallable([this, &frame, protectedRequest = makeRef(protectedRequest.get()), webView = RetainPtr<WKWebView>(m_uiDelegate.m_webView)](BOOL authorized) {
468                 if (!authorized) {
469                     protectedRequest->deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied);
470                     return;
471                 }
472                 requestUserMediaAuthorizationForDevices(frame, protectedRequest, (id <WKUIDelegatePrivate>)m_uiDelegate.m_delegate.get(), *webView.get());
473             });
474
475             [getAVCaptureDeviceClass() requestAccessForMediaType:getAVMediaTypeVideo() completionHandler:decisionHandler.get()];
476             break;
477         }
478     });
479
480     if (requiresAudio) {
481         AVAuthorizationStatus microphoneAuthorizationStatus = [getAVCaptureDeviceClass() authorizationStatusForMediaType:getAVMediaTypeAudio()];
482         switch (microphoneAuthorizationStatus) {
483         case AVAuthorizationStatusAuthorized:
484             requestCameraAuthorization();
485             break;
486         case AVAuthorizationStatusDenied:
487         case AVAuthorizationStatusRestricted:
488             request.deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied);
489             return true;
490         case AVAuthorizationStatusNotDetermined:
491             auto decisionHandler = BlockPtr<void(BOOL)>::fromCallable([protectedRequest = makeRef(request), requestCameraAuthorization](BOOL authorized) {
492                 if (!authorized) {
493                     protectedRequest->deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied);
494                     return;
495                 }
496                 requestCameraAuthorization();
497             });
498
499             [getAVCaptureDeviceClass() requestAccessForMediaType:getAVMediaTypeAudio() completionHandler:decisionHandler.get()];
500             break;
501         }
502     } else
503         requestCameraAuthorization();
504 #else
505     requestUserMediaAuthorizationForDevices(frame, request, (id <WKUIDelegatePrivate>)m_uiDelegate.m_delegate.get(), *m_uiDelegate.m_webView);
506 #endif
507
508     return true;
509 }
510
511 bool UIDelegate::UIClient::checkUserMediaPermissionForOrigin(WebKit::WebPageProxy& page, WebKit::WebFrameProxy& frame, API::SecurityOrigin& userMediaOrigin, API::SecurityOrigin& topLevelOrigin, WebKit::UserMediaPermissionCheckProxy& request)
512 {
513     auto delegate = m_uiDelegate.m_delegate.get();
514     if (!delegate || !m_uiDelegate.m_delegateMethods.webViewCheckUserMediaPermissionForURLMainFrameURLFrameIdentifierDecisionHandler) {
515         request.setUserMediaAccessInfo(String(), false);
516         return true;
517     }
518
519     WKWebView *webView = m_uiDelegate.m_webView;
520     const WebFrameProxy* mainFrame = frame.page()->mainFrame();
521     WebCore::URL requestFrameURL(WebCore::URL(), frame.url());
522     WebCore::URL mainFrameURL(WebCore::URL(), mainFrame->url());
523
524     auto decisionHandler = BlockPtr<void(NSString *, BOOL)>::fromCallable([protectedRequest = makeRef(request)](NSString *salt, BOOL authorized) {
525         protectedRequest->setUserMediaAccessInfo(String(salt), authorized);
526     });
527
528     [(id <WKUIDelegatePrivate>)delegate _webView:webView checkUserMediaPermissionForURL:requestFrameURL mainFrameURL:mainFrameURL frameIdentifier:frame.frameID() decisionHandler:decisionHandler.get()];
529
530     return true;
531 }
532
533 void UIDelegate::UIClient::mediaCaptureStateDidChange(WebCore::MediaProducer::MediaStateFlags state)
534 {
535     WKWebView *webView = m_uiDelegate.m_webView;
536     auto delegate = m_uiDelegate.m_delegate.get();
537     if (!delegate || !m_uiDelegate.m_delegateMethods.webViewMediaCaptureStateDidChange)
538         return;
539
540     _WKMediaCaptureState mediaCaptureState = _WKMediaCaptureStateNone;
541     if (state & WebCore::MediaProducer::HasActiveAudioCaptureDevice)
542         mediaCaptureState |= _WKMediaCaptureStateActiveMicrophone;
543     if (state & WebCore::MediaProducer::HasActiveVideoCaptureDevice)
544         mediaCaptureState |= _WKMediaCaptureStateActiveCamera;
545     if (state & WebCore::MediaProducer::HasMutedAudioCaptureDevice)
546         mediaCaptureState |= _WKMediaCaptureStateMutedMicrophone;
547     if (state & WebCore::MediaProducer::HasMutedVideoCaptureDevice)
548         mediaCaptureState |= _WKMediaCaptureStateMutedCamera;
549
550     [(id <WKUIDelegatePrivate>)delegate _webView:webView mediaCaptureStateDidChange:mediaCaptureState];
551 }
552
553 void UIDelegate::UIClient::reachedApplicationCacheOriginQuota(WebPageProxy*, const WebCore::SecurityOrigin& securityOrigin, uint64_t currentQuota, uint64_t totalBytesNeeded, Function<void (unsigned long long)>&& completionHandler)
554 {
555     if (!m_uiDelegate.m_delegateMethods.webViewDecideWebApplicationCacheQuotaForSecurityOriginCurrentQuotaTotalBytesNeeded) {
556         completionHandler(currentQuota);
557         return;
558     }
559
560     auto delegate = m_uiDelegate.m_delegate.get();
561     if (!delegate) {
562         completionHandler(currentQuota);
563         return;
564     }
565
566     RefPtr<CompletionHandlerCallChecker> checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:decideWebApplicationCacheQuotaForSecurityOrigin:currentQuota:totalBytesNeeded:decisionHandler:));
567     RefPtr<API::SecurityOrigin> apiOrigin = API::SecurityOrigin::create(securityOrigin);
568     
569     [(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) {
570         if (checker->completionHandlerHasBeenCalled())
571             return;
572         checker->didCallCompletionHandler();
573         completionHandler(newQuota);
574     }).get()];
575 }
576
577 void UIDelegate::UIClient::printFrame(WebKit::WebPageProxy*, WebKit::WebFrameProxy* webFrameProxy)
578 {
579     ASSERT_ARG(webFrameProxy, webFrameProxy);
580
581     if (!m_uiDelegate.m_delegateMethods.webViewPrintFrame)
582         return;
583
584     auto delegate = m_uiDelegate.m_delegate.get();
585     if (!delegate)
586         return;
587
588     [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView printFrame:wrapper(API::FrameHandle::create(webFrameProxy->frameID()))];
589 }
590
591 void UIDelegate::UIClient::close(WebKit::WebPageProxy*)
592 {
593     if (m_uiDelegate.m_delegateMethods.webViewClose) {
594         auto delegate = m_uiDelegate.m_delegate.get();
595         if (!delegate)
596             return;
597
598         [(id <WKUIDelegatePrivate>)delegate _webViewClose:m_uiDelegate.m_webView];
599         return;
600     }
601
602     if (!m_uiDelegate.m_delegateMethods.webViewDidClose)
603         return;
604
605     auto delegate = m_uiDelegate.m_delegate.get();
606     if (!delegate)
607         return;
608
609     [delegate webViewDidClose:m_uiDelegate.m_webView];
610 }
611
612 void UIDelegate::UIClient::fullscreenMayReturnToInline(WebKit::WebPageProxy*)
613 {
614     if (!m_uiDelegate.m_delegateMethods.webViewFullscreenMayReturnToInline)
615         return;
616     
617     auto delegate = m_uiDelegate.m_delegate.get();
618     if (!delegate)
619         return;
620     
621     [(id <WKUIDelegatePrivate>)delegate _webViewFullscreenMayReturnToInline:m_uiDelegate.m_webView];
622 }
623
624 void UIDelegate::UIClient::didEnterFullscreen(WebKit::WebPageProxy*)
625 {
626     if (!m_uiDelegate.m_delegateMethods.webViewDidEnterFullscreen)
627         return;
628
629     auto delegate = m_uiDelegate.m_delegate.get();
630     if (!delegate)
631         return;
632
633     [(id <WKUIDelegatePrivate>)delegate _webViewDidEnterFullscreen:m_uiDelegate.m_webView];
634 }
635
636 void UIDelegate::UIClient::didExitFullscreen(WebKit::WebPageProxy*)
637 {
638     if (!m_uiDelegate.m_delegateMethods.webViewDidExitFullscreen)
639         return;
640
641     auto delegate = m_uiDelegate.m_delegate.get();
642     if (!delegate)
643         return;
644
645     [(id <WKUIDelegatePrivate>)delegate _webViewDidExitFullscreen:m_uiDelegate.m_webView];
646 }
647     
648 #if PLATFORM(IOS)
649 #if HAVE(APP_LINKS)
650 bool UIDelegate::UIClient::shouldIncludeAppLinkActionsForElement(_WKActivatedElementInfo *elementInfo)
651 {
652     if (!m_uiDelegate.m_delegateMethods.webViewShouldIncludeAppLinkActionsForElement)
653         return true;
654
655     auto delegate = m_uiDelegate.m_delegate.get();
656     if (!delegate)
657         return true;
658
659     return [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView shouldIncludeAppLinkActionsForElement:elementInfo];
660 }
661 #endif
662
663 RetainPtr<NSArray> UIDelegate::UIClient::actionsForElement(_WKActivatedElementInfo *elementInfo, RetainPtr<NSArray> defaultActions)
664 {
665     if (!m_uiDelegate.m_delegateMethods.webViewActionsForElementDefaultActions)
666         return defaultActions;
667
668     auto delegate = m_uiDelegate.m_delegate.get();
669     if (!delegate)
670         return defaultActions;
671
672     return [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate.m_webView actionsForElement:elementInfo defaultActions:defaultActions.get()];
673 }
674
675 void UIDelegate::UIClient::didNotHandleTapAsClick(const WebCore::IntPoint& point)
676 {
677     if (!m_uiDelegate.m_delegateMethods.webViewDidNotHandleTapAsClickAtPoint)
678         return;
679
680     auto delegate = m_uiDelegate.m_delegate.get();
681     if (!delegate)
682         return;
683
684     [static_cast<id <WKUIDelegatePrivate>>(delegate) _webView:m_uiDelegate.m_webView didNotHandleTapAsClickAtPoint:point];
685 }
686
687 UIViewController *UIDelegate::UIClient::presentingViewController()
688 {
689     if (!m_uiDelegate.m_delegateMethods.presentingViewControllerForWebView)
690         return nullptr;
691
692     auto delegate = m_uiDelegate.m_delegate.get();
693     if (!delegate)
694         return nullptr;
695
696     return [static_cast<id <WKUIDelegatePrivate>>(delegate) _presentingViewControllerForWebView:m_uiDelegate.m_webView];
697 }
698
699 #endif
700
701 NSDictionary *UIDelegate::UIClient::dataDetectionContext()
702 {
703     if (!m_uiDelegate.m_delegateMethods.dataDetectionContextForWebView)
704         return nullptr;
705
706     auto delegate = m_uiDelegate.m_delegate.get();
707     if (!delegate)
708         return nullptr;
709
710     return [static_cast<id <WKUIDelegatePrivate>>(delegate) _dataDetectionContextForWebView:m_uiDelegate.m_webView];
711 }
712
713 #if ENABLE(POINTER_LOCK)
714
715 void UIDelegate::UIClient::requestPointerLock(WebKit::WebPageProxy*)
716 {
717     if (!m_uiDelegate.m_delegateMethods.webViewRequestPointerLock)
718         return;
719
720     auto delegate = m_uiDelegate.m_delegate.get();
721     if (!delegate)
722         return;
723
724     [static_cast<id <WKUIDelegatePrivate>>(delegate) _webViewRequestPointerLock:m_uiDelegate.m_webView];
725 }
726
727 void UIDelegate::UIClient::didLosePointerLock(WebKit::WebPageProxy*)
728 {
729     if (!m_uiDelegate.m_delegateMethods.webViewDidLosePointerLock)
730         return;
731
732     auto delegate = m_uiDelegate.m_delegate.get();
733     if (!delegate)
734         return;
735
736     [static_cast<id <WKUIDelegatePrivate>>(delegate) _webViewDidLosePointerLock:m_uiDelegate.m_webView];
737 }
738
739 #endif
740     
741 void UIDelegate::UIClient::hasVideoInPictureInPictureDidChange(WebKit::WebPageProxy*, bool hasVideoInPictureInPicture)
742 {
743     if (!m_uiDelegate.m_delegateMethods.webViewHasVideoInPictureInPictureDidChange)
744         return;
745     
746     auto delegate = m_uiDelegate.m_delegate.get();
747     if (!delegate)
748         return;
749     
750     [static_cast<id <WKUIDelegatePrivate>>(delegate) _webView:m_uiDelegate.m_webView hasVideoInPictureInPictureDidChange:hasVideoInPictureInPicture];
751 }
752
753 void UIDelegate::UIClient::imageOrMediaDocumentSizeChanged(const WebCore::IntSize& newSize)
754 {
755     if (!m_uiDelegate.m_delegateMethods.webViewImageOrMediaDocumentSizeChanged)
756         return;
757
758     auto delegate = m_uiDelegate.m_delegate.get();
759     if (!delegate)
760         return;
761
762     [static_cast<id <WKUIDelegatePrivate>>(delegate) _webView:m_uiDelegate.m_webView imageOrMediaDocumentSizeChanged:newSize];
763 }
764
765 } // namespace WebKit
766
767 #endif // WK_API_ENABLED