blob: f9d63139c03f815a6e700e9b3cc9193a7d4ed64c [file] [log] [blame]
/*
* Copyright (C) 2014-2018 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#import "config.h"
#import "UIDelegate.h"
#import "APIArray.h"
#import "APIFrameInfo.h"
#import "APIHitTestResult.h"
#import "APIInspectorConfiguration.h"
#import "CompletionHandlerCallChecker.h"
#import "MediaPermissionUtilities.h"
#import "MediaUtilities.h"
#import "NativeWebWheelEvent.h"
#import "NavigationActionData.h"
#import "UserMediaPermissionCheckProxy.h"
#import "UserMediaPermissionRequestManagerProxy.h"
#import "UserMediaPermissionRequestProxy.h"
#import "WKFrameInfoInternal.h"
#import "WKNSData.h"
#import "WKNSDictionary.h"
#import "WKNavigationActionInternal.h"
#import "WKOpenPanelParametersInternal.h"
#import "WKSecurityOriginInternal.h"
#import "WKStorageAccessAlert.h"
#import "WKUIDelegatePrivate.h"
#import "WKWebViewConfigurationInternal.h"
#import "WKWebViewInternal.h"
#import "WKWindowFeaturesInternal.h"
#import "WebEventFactory.h"
#import "WebOpenPanelResultListenerProxy.h"
#import "WebProcessProxy.h"
#import "_WKContextMenuElementInfo.h"
#import "_WKFrameHandleInternal.h"
#import "_WKHitTestResultInternal.h"
#import "_WKInspectorConfigurationInternal.h"
#import "_WKInspectorInternal.h"
#import "_WKModalContainerInfoInternal.h"
#import "_WKWebAuthenticationPanelInternal.h"
#import <AVFoundation/AVCaptureDevice.h>
#import <AVFoundation/AVMediaFormat.h>
#import <WebCore/FontAttributes.h>
#import <WebCore/SecurityOrigin.h>
#import <wtf/BlockPtr.h>
#import <wtf/URL.h>
#if PLATFORM(IOS_FAMILY)
#import "TapHandlingResult.h"
#import "WKWebViewIOS.h"
#endif
#import <pal/cocoa/AVFoundationSoftLink.h>
namespace WebKit {
UIDelegate::UIDelegate(WKWebView *webView)
: m_webView(webView)
{
}
UIDelegate::~UIDelegate()
{
}
#if ENABLE(CONTEXT_MENUS)
std::unique_ptr<API::ContextMenuClient> UIDelegate::createContextMenuClient()
{
return makeUnique<ContextMenuClient>(*this);
}
#endif
std::unique_ptr<API::UIClient> UIDelegate::createUIClient()
{
return makeUnique<UIClient>(*this);
}
RetainPtr<id <WKUIDelegate> > UIDelegate::delegate()
{
return m_delegate.get();
}
void UIDelegate::setDelegate(id <WKUIDelegate> delegate)
{
m_delegate = delegate;
m_delegateMethods.webViewCreateWebViewWithConfigurationForNavigationActionWindowFeatures = [delegate respondsToSelector:@selector(webView:createWebViewWithConfiguration:forNavigationAction:windowFeatures:)];
m_delegateMethods.webViewCreateWebViewWithConfigurationForNavigationActionWindowFeaturesAsync = [delegate respondsToSelector:@selector(_webView:createWebViewWithConfiguration:forNavigationAction:windowFeatures:completionHandler:)];
m_delegateMethods.webViewRunJavaScriptAlertPanelWithMessageInitiatedByFrameCompletionHandler = [delegate respondsToSelector:@selector(webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:)];
m_delegateMethods.webViewRunJavaScriptConfirmPanelWithMessageInitiatedByFrameCompletionHandler = [delegate respondsToSelector:@selector(webView:runJavaScriptConfirmPanelWithMessage:initiatedByFrame:completionHandler:)];
m_delegateMethods.webViewRunJavaScriptTextInputPanelWithPromptDefaultTextInitiatedByFrameCompletionHandler = [delegate respondsToSelector:@selector(webView:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:)];
m_delegateMethods.webViewRequestStorageAccessPanelUnderFirstPartyCompletionHandler = [delegate respondsToSelector:@selector(_webView:requestStorageAccessPanelForDomain:underCurrentDomain:completionHandler:)];
m_delegateMethods.webViewRunBeforeUnloadConfirmPanelWithMessageInitiatedByFrameCompletionHandler = [delegate respondsToSelector:@selector(_webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame:completionHandler:)];
m_delegateMethods.webViewRequestGeolocationPermissionForOriginDecisionHandler = [delegate respondsToSelector:@selector(_webView:requestGeolocationPermissionForOrigin:initiatedByFrame:decisionHandler:)];
m_delegateMethods.webViewRequestGeolocationPermissionForFrameDecisionHandler = [delegate respondsToSelector:@selector(_webView:requestGeolocationPermissionForFrame:decisionHandler:)];
m_delegateMethods.webViewDidResignInputElementStrongPasswordAppearanceWithUserInfo = [delegate respondsToSelector:@selector(_webView:didResignInputElementStrongPasswordAppearanceWithUserInfo:)];
m_delegateMethods.webViewTakeFocus = [delegate respondsToSelector:@selector(_webView:takeFocus:)];
m_delegateMethods.webViewHandleAutoplayEventWithFlags = [delegate respondsToSelector:@selector(_webView:handleAutoplayEvent:withFlags:)];
#if PLATFORM(MAC)
m_delegateMethods.showWebView = [delegate respondsToSelector:@selector(_showWebView:)];
m_delegateMethods.focusWebView = [delegate respondsToSelector:@selector(_focusWebView:)];
m_delegateMethods.unfocusWebView = [delegate respondsToSelector:@selector(_unfocusWebView:)];
m_delegateMethods.webViewRunModal = [delegate respondsToSelector:@selector(_webViewRunModal:)];
m_delegateMethods.webViewDidScroll = [delegate respondsToSelector:@selector(_webViewDidScroll:)];
m_delegateMethods.webViewGetToolbarsAreVisibleWithCompletionHandler = [delegate respondsToSelector:@selector(_webView:getToolbarsAreVisibleWithCompletionHandler:)];
m_delegateMethods.webViewDidNotHandleWheelEvent = [delegate respondsToSelector:@selector(_webView:didNotHandleWheelEvent:)];
m_delegateMethods.webViewSetResizable = [delegate respondsToSelector:@selector(_webView:setResizable:)];
m_delegateMethods.webViewGetWindowFrameWithCompletionHandler = [delegate respondsToSelector:@selector(_webView:getWindowFrameWithCompletionHandler:)];
m_delegateMethods.webViewSetWindowFrame = [delegate respondsToSelector:@selector(_webView:setWindowFrame:)];
m_delegateMethods.webViewUnavailablePlugInButtonClicked = [delegate respondsToSelector:@selector(_webView:unavailablePlugInButtonClickedWithReason:plugInInfo:)];
m_delegateMethods.webViewDidClickAutoFillButtonWithUserInfo = [delegate respondsToSelector:@selector(_webView:didClickAutoFillButtonWithUserInfo:)];
m_delegateMethods.webViewDrawHeaderInRectForPageWithTitleURL = [delegate respondsToSelector:@selector(_webView:drawHeaderInRect:forPageWithTitle:URL:)];
m_delegateMethods.webViewDrawFooterInRectForPageWithTitleURL = [delegate respondsToSelector:@selector(_webView:drawFooterInRect:forPageWithTitle:URL:)];
m_delegateMethods.webViewHeaderHeight = [delegate respondsToSelector:@selector(_webViewHeaderHeight:)];
m_delegateMethods.webViewFooterHeight = [delegate respondsToSelector:@selector(_webViewFooterHeight:)];
m_delegateMethods.webViewMouseDidMoveOverElementWithFlagsUserInfo = [delegate respondsToSelector:@selector(_webView:mouseDidMoveOverElement:withFlags:userInfo:)];
m_delegateMethods.webViewDidExceedBackgroundResourceLimitWhileInForeground = [delegate respondsToSelector:@selector(_webView:didExceedBackgroundResourceLimitWhileInForeground:)];
m_delegateMethods.webViewSaveDataToFileSuggestedFilenameMimeTypeOriginatingURL = [delegate respondsToSelector:@selector(_webView:saveDataToFile:suggestedFilename:mimeType:originatingURL:)];
m_delegateMethods.webViewRunOpenPanelWithParametersInitiatedByFrameCompletionHandler = [delegate respondsToSelector:@selector(webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler:)];
m_delegateMethods.webViewConfigurationForLocalInspector = [delegate respondsToSelector:@selector(_webView:configurationForLocalInspector:)];
m_delegateMethods.webViewDidAttachLocalInspector = [delegate respondsToSelector:@selector(_webView:didAttachLocalInspector:)];
m_delegateMethods.webViewWillCloseLocalInspector = [delegate respondsToSelector:@selector(_webView:willCloseLocalInspector:)];
#endif
#if ENABLE(DEVICE_ORIENTATION)
m_delegateMethods.webViewRequestDeviceOrientationAndMotionPermissionForOriginDecisionHandler = [delegate respondsToSelector:@selector(webView:requestDeviceOrientationAndMotionPermissionForOrigin:initiatedByFrame:decisionHandler:)];
#endif
m_delegateMethods.webViewDecideDatabaseQuotaForSecurityOriginCurrentQuotaCurrentOriginUsageCurrentDatabaseUsageExpectedUsageDecisionHandler = [delegate respondsToSelector:@selector(_webView:decideDatabaseQuotaForSecurityOrigin:currentQuota:currentOriginUsage:currentDatabaseUsage:expectedUsage:decisionHandler:)];
m_delegateMethods.webViewDecideDatabaseQuotaForSecurityOriginDatabaseNameDisplayNameCurrentQuotaCurrentOriginUsageCurrentDatabaseUsageExpectedUsageDecisionHandler = [delegate respondsToSelector:@selector(_webView:decideDatabaseQuotaForSecurityOrigin:databaseName:displayName:currentQuota:currentOriginUsage:currentDatabaseUsage:expectedUsage:decisionHandler:)];
m_delegateMethods.webViewDecideWebApplicationCacheQuotaForSecurityOriginCurrentQuotaTotalBytesNeeded = [delegate respondsToSelector:@selector(_webView:decideWebApplicationCacheQuotaForSecurityOrigin:currentQuota:totalBytesNeeded:decisionHandler:)];
m_delegateMethods.webViewPrintFrame = [delegate respondsToSelector:@selector(_webView:printFrame:)];
m_delegateMethods.webViewPrintFramePDFFirstPageSizeCompletionHandler = [delegate respondsToSelector:@selector(_webView:printFrame:pdfFirstPageSize:completionHandler:)];
m_delegateMethods.webViewDidClose = [delegate respondsToSelector:@selector(webViewDidClose:)];
m_delegateMethods.webViewClose = [delegate respondsToSelector:@selector(_webViewClose:)];
m_delegateMethods.webViewFullscreenMayReturnToInline = [delegate respondsToSelector:@selector(_webViewFullscreenMayReturnToInline:)];
m_delegateMethods.webViewDidEnterFullscreen = [delegate respondsToSelector:@selector(_webViewDidEnterFullscreen:)];
m_delegateMethods.webViewDidExitFullscreen = [delegate respondsToSelector:@selector(_webViewDidExitFullscreen:)];
#if PLATFORM(IOS_FAMILY)
#if HAVE(APP_LINKS)
m_delegateMethods.webViewShouldIncludeAppLinkActionsForElement = [delegate respondsToSelector:@selector(_webView:shouldIncludeAppLinkActionsForElement:)];
#endif
m_delegateMethods.webViewActionsForElementDefaultActions = [delegate respondsToSelector:@selector(_webView:actionsForElement:defaultActions:)];
m_delegateMethods.webViewDidNotHandleTapAsClickAtPoint = [delegate respondsToSelector:@selector(_webView:didNotHandleTapAsClickAtPoint:)];
#endif
m_delegateMethods.presentingViewControllerForWebView = [delegate respondsToSelector:@selector(_presentingViewControllerForWebView:)];
m_delegateMethods.webViewIsMediaCaptureAuthorizedForFrameDecisionHandler = [delegate respondsToSelector:@selector(_webView:checkUserMediaPermissionForURL:mainFrameURL:frameIdentifier:decisionHandler:)] || [delegate respondsToSelector:@selector(_webView:includeSensitiveMediaDeviceDetails:)];
m_delegateMethods.webViewMediaCaptureStateDidChange = [delegate respondsToSelector:@selector(_webView:mediaCaptureStateDidChange:)];
m_delegateMethods.webViewDidChangeFontAttributes = [delegate respondsToSelector:@selector(_webView:didChangeFontAttributes:)];
m_delegateMethods.dataDetectionContextForWebView = [delegate respondsToSelector:@selector(_dataDetectionContextForWebView:)];
m_delegateMethods.webViewImageOrMediaDocumentSizeChanged = [delegate respondsToSelector:@selector(_webView:imageOrMediaDocumentSizeChanged:)];
#if ENABLE(POINTER_LOCK)
m_delegateMethods.webViewRequestPointerLock = [delegate respondsToSelector:@selector(_webViewRequestPointerLock:)];
m_delegateMethods.webViewDidRequestPointerLockCompletionHandler = [delegate respondsToSelector:@selector(_webViewDidRequestPointerLock:completionHandler:)];
m_delegateMethods.webViewDidLosePointerLock = [delegate respondsToSelector:@selector(_webViewDidLosePointerLock:)];
#endif
#if ENABLE(CONTEXT_MENUS)
m_delegateMethods.webViewContextMenuForElement = [delegate respondsToSelector:@selector(_webView:contextMenu:forElement:)];
m_delegateMethods.webViewContextMenuForElementUserInfo = [delegate respondsToSelector:@selector(_webView:contextMenu:forElement:userInfo:)];
m_delegateMethods.webViewGetContextMenuFromProposedMenuForElementUserInfoCompletionHandler = [delegate respondsToSelector:@selector(_webView:getContextMenuFromProposedMenu:forElement:userInfo:completionHandler:)];
#endif
m_delegateMethods.webViewHasVideoInPictureInPictureDidChange = [delegate respondsToSelector:@selector(_webView:hasVideoInPictureInPictureDidChange:)];
m_delegateMethods.webViewDidShowSafeBrowsingWarning = [delegate respondsToSelector:@selector(_webViewDidShowSafeBrowsingWarning:)];
m_delegateMethods.webViewShouldAllowPDFAtURLToOpenFromFrameCompletionHandler = [delegate respondsToSelector:@selector(_webView:shouldAllowPDFAtURL:toOpenFromFrame:completionHandler:)];
#if ENABLE(WEB_AUTHN)
m_delegateMethods.webViewRunWebAuthenticationPanelInitiatedByFrameCompletionHandler = [delegate respondsToSelector:@selector(_webView:runWebAuthenticationPanel:initiatedByFrame:completionHandler:)];
m_delegateMethods.webViewRequestWebAuthenticationNoGestureForOriginCompletionHandler = [delegate respondsToSelector:@selector(_webView:requestWebAuthenticationNoGestureForOrigin:completionHandler:)];
#endif
m_delegateMethods.webViewDidEnableInspectorBrowserDomain = [delegate respondsToSelector:@selector(_webViewDidEnableInspectorBrowserDomain:)];
m_delegateMethods.webViewDidDisableInspectorBrowserDomain = [delegate respondsToSelector:@selector(_webViewDidDisableInspectorBrowserDomain:)];
#if ENABLE(WEBXR)
m_delegateMethods.webViewRequestPermissionForXRSessionOriginModeAndFeaturesWithCompletionHandler = [delegate respondsToSelector:@selector(_webView:requestPermissionForXRSessionOrigin:mode:grantedFeatures:consentRequiredFeatures:consentOptionalFeatures:completionHandler:)];
m_delegateMethods.webViewStartXRSessionWithCompletionHandler = [delegate respondsToSelector:@selector(_webView:startXRSessionWithCompletionHandler:)];
#endif
m_delegateMethods.webViewRequestNotificationPermissionForSecurityOriginDecisionHandler = [delegate respondsToSelector:@selector(_webView:requestNotificationPermissionForSecurityOrigin:decisionHandler:)];
m_delegateMethods.webViewRequestCookieConsentWithMoreInfoHandlerDecisionHandler = [delegate respondsToSelector:@selector(_webView:requestCookieConsentWithMoreInfoHandler:decisionHandler:)];
m_delegateMethods.webViewDecidePolicyForModalContainerDecisionHandler = [delegate respondsToSelector:@selector(_webView:decidePolicyForModalContainer:decisionHandler:)];
}
#if ENABLE(CONTEXT_MENUS)
UIDelegate::ContextMenuClient::ContextMenuClient(UIDelegate& uiDelegate)
: m_uiDelegate(uiDelegate)
{
}
UIDelegate::ContextMenuClient::~ContextMenuClient()
{
}
void UIDelegate::ContextMenuClient::menuFromProposedMenu(WebPageProxy&, NSMenu *menu, const WebHitTestResultData&, API::Object* userInfo, CompletionHandler<void(RetainPtr<NSMenu>&&)>&& completionHandler)
{
if (!m_uiDelegate)
return completionHandler(menu);
if (!m_uiDelegate->m_delegateMethods.webViewContextMenuForElement
&& !m_uiDelegate->m_delegateMethods.webViewContextMenuForElementUserInfo
&& !m_uiDelegate->m_delegateMethods.webViewGetContextMenuFromProposedMenuForElementUserInfoCompletionHandler)
return completionHandler(menu);
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return completionHandler(menu);
auto contextMenuElementInfo = adoptNS([[_WKContextMenuElementInfo alloc] init]);
if (m_uiDelegate->m_delegateMethods.webViewGetContextMenuFromProposedMenuForElementUserInfoCompletionHandler) {
auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:getContextMenuFromProposedMenu:forElement:userInfo:completionHandler:));
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() getContextMenuFromProposedMenu:menu forElement:contextMenuElementInfo.get() userInfo:userInfo ? static_cast<id <NSSecureCoding>>(userInfo->wrapper()) : nil completionHandler:makeBlockPtr([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)] (NSMenu *menu) mutable {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
completionHandler(menu);
}).get()];
return;
}
ALLOW_DEPRECATED_DECLARATIONS_BEGIN
if (m_uiDelegate->m_delegateMethods.webViewContextMenuForElement)
return completionHandler([(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() contextMenu:menu forElement:contextMenuElementInfo.get()]);
completionHandler([(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() contextMenu:menu forElement:contextMenuElementInfo.get() userInfo:userInfo ? static_cast<id <NSSecureCoding>>(userInfo->wrapper()) : nil]);
ALLOW_DEPRECATED_DECLARATIONS_END
}
#endif
UIDelegate::UIClient::UIClient(UIDelegate& uiDelegate)
: m_uiDelegate(uiDelegate)
{
}
UIDelegate::UIClient::~UIClient()
{
}
void UIDelegate::UIClient::createNewPage(WebKit::WebPageProxy&, WebCore::WindowFeatures&& windowFeatures, Ref<API::NavigationAction>&& navigationAction, CompletionHandler<void(RefPtr<WebPageProxy>&&)>&& completionHandler)
{
if (!m_uiDelegate)
return completionHandler(nullptr);
auto delegate = m_uiDelegate->m_delegate.get();
ASSERT(delegate);
auto configuration = adoptNS([m_uiDelegate->m_webView.get()->_configuration copy]);
[configuration _setRelatedWebView:m_uiDelegate->m_webView.get().get()];
auto apiWindowFeatures = API::WindowFeatures::create(windowFeatures);
if (m_uiDelegate->m_delegateMethods.webViewCreateWebViewWithConfigurationForNavigationActionWindowFeaturesAsync) {
auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:createWebViewWithConfiguration:forNavigationAction:windowFeatures:completionHandler:));
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() createWebViewWithConfiguration:configuration.get() forNavigationAction:wrapper(navigationAction) windowFeatures:wrapper(apiWindowFeatures) completionHandler:makeBlockPtr([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker), relatedWebView = m_uiDelegate->m_webView.get()] (WKWebView *webView) mutable {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
if (!webView) {
completionHandler(nullptr);
return;
}
if ([webView->_configuration _relatedWebView] != relatedWebView.get())
[NSException raise:NSInternalInconsistencyException format:@"Returned WKWebView was not created with the given configuration."];
completionHandler(webView->_page.get());
}).get()];
return;
}
if (!m_uiDelegate->m_delegateMethods.webViewCreateWebViewWithConfigurationForNavigationActionWindowFeatures)
return completionHandler(nullptr);
RetainPtr<WKWebView> webView = [delegate webView:m_uiDelegate->m_webView.get().get() createWebViewWithConfiguration:configuration.get() forNavigationAction:wrapper(navigationAction) windowFeatures:wrapper(apiWindowFeatures)];
if (!webView)
return completionHandler(nullptr);
if ([webView.get()->_configuration _relatedWebView] != m_uiDelegate->m_webView.get().get())
[NSException raise:NSInternalInconsistencyException format:@"Returned WKWebView was not created with the given configuration."];
completionHandler(webView->_page.get());
}
void UIDelegate::UIClient::runJavaScriptAlert(WebPageProxy& page, const WTF::String& message, WebFrameProxy*, FrameInfoData&& frameInfo, Function<void()>&& completionHandler)
{
if (!m_uiDelegate)
return completionHandler();
if (!m_uiDelegate->m_delegateMethods.webViewRunJavaScriptAlertPanelWithMessageInitiatedByFrameCompletionHandler) {
completionHandler();
return;
}
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate) {
completionHandler();
return;
}
page.makeViewBlankIfUnpaintedSinceLastLoadCommit();
auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(webView:runJavaScriptAlertPanelWithMessage:initiatedByFrame:completionHandler:));
[delegate webView:m_uiDelegate->m_webView.get().get() runJavaScriptAlertPanelWithMessage:message initiatedByFrame:wrapper(API::FrameInfo::create(WTFMove(frameInfo), &page)) completionHandler:makeBlockPtr([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)] {
if (checker->completionHandlerHasBeenCalled())
return;
completionHandler();
checker->didCallCompletionHandler();
}).get()];
}
void UIDelegate::UIClient::runJavaScriptConfirm(WebPageProxy& page, const WTF::String& message, WebFrameProxy* webFrameProxy, FrameInfoData&& frameInfo, Function<void(bool)>&& completionHandler)
{
if (!m_uiDelegate)
return completionHandler(false);
if (!m_uiDelegate->m_delegateMethods.webViewRunJavaScriptConfirmPanelWithMessageInitiatedByFrameCompletionHandler) {
completionHandler(false);
return;
}
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate) {
completionHandler(false);
return;
}
page.makeViewBlankIfUnpaintedSinceLastLoadCommit();
auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(webView:runJavaScriptConfirmPanelWithMessage:initiatedByFrame:completionHandler:));
[delegate webView:m_uiDelegate->m_webView.get().get() runJavaScriptConfirmPanelWithMessage:message initiatedByFrame:wrapper(API::FrameInfo::create(WTFMove(frameInfo), &page)) completionHandler:makeBlockPtr([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)] (BOOL result) mutable {
if (checker->completionHandlerHasBeenCalled())
return;
completionHandler(result);
checker->didCallCompletionHandler();
}).get()];
}
void UIDelegate::UIClient::runJavaScriptPrompt(WebPageProxy& page, const WTF::String& message, const WTF::String& defaultValue, WebFrameProxy*, FrameInfoData&& frameInfo, Function<void(const WTF::String&)>&& completionHandler)
{
if (!m_uiDelegate)
return completionHandler({ });
if (!m_uiDelegate->m_delegateMethods.webViewRunJavaScriptTextInputPanelWithPromptDefaultTextInitiatedByFrameCompletionHandler) {
completionHandler(String());
return;
}
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate) {
completionHandler(String());
return;
}
page.makeViewBlankIfUnpaintedSinceLastLoadCommit();
auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(webView:runJavaScriptTextInputPanelWithPrompt:defaultText:initiatedByFrame:completionHandler:));
[delegate webView:m_uiDelegate->m_webView.get().get() runJavaScriptTextInputPanelWithPrompt:message defaultText:defaultValue initiatedByFrame:wrapper(API::FrameInfo::create(WTFMove(frameInfo), &page)) completionHandler:makeBlockPtr([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)] (NSString *result) mutable {
if (checker->completionHandlerHasBeenCalled())
return;
completionHandler(result);
checker->didCallCompletionHandler();
}).get()];
}
void UIDelegate::UIClient::requestStorageAccessConfirm(WebPageProxy& webPageProxy, WebFrameProxy*, const WebCore::RegistrableDomain& requestingDomain, const WebCore::RegistrableDomain& currentDomain, CompletionHandler<void(bool)>&& completionHandler)
{
if (!m_uiDelegate)
return completionHandler(false);
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate) {
completionHandler(false);
return;
}
// Some sites have quirks where multiple login domains require storage access.
if (auto additionalLoginDomain = WebCore::NetworkStorageSession::findAdditionalLoginDomain(currentDomain, requestingDomain)) {
#if !PLATFORM(WATCHOS) && !PLATFORM(APPLETV)
presentStorageAccessAlertQuirk(m_uiDelegate->m_webView.get().get(), requestingDomain, *additionalLoginDomain, currentDomain, WTFMove(completionHandler));
#endif
return;
}
if (!m_uiDelegate->m_delegateMethods.webViewRequestStorageAccessPanelUnderFirstPartyCompletionHandler) {
#if !PLATFORM(WATCHOS) && !PLATFORM(APPLETV)
presentStorageAccessAlert(m_uiDelegate->m_webView.get().get(), requestingDomain, currentDomain, WTFMove(completionHandler));
#endif
return;
}
auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:requestStorageAccessPanelForDomain:underCurrentDomain:completionHandler:));
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() requestStorageAccessPanelForDomain:requestingDomain.string() underCurrentDomain:currentDomain.string() completionHandler:makeBlockPtr([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)] (BOOL result) mutable {
if (checker->completionHandlerHasBeenCalled())
return;
completionHandler(result);
checker->didCallCompletionHandler();
}).get()];
}
void UIDelegate::UIClient::decidePolicyForGeolocationPermissionRequest(WebKit::WebPageProxy& page, WebKit::WebFrameProxy& frame, const FrameInfoData& frameInfo, Function<void(bool)>& completionHandler)
{
if (!m_uiDelegate || (!m_uiDelegate->m_delegateMethods.webViewRequestGeolocationPermissionForFrameDecisionHandler && !m_uiDelegate->m_delegateMethods.webViewRequestGeolocationPermissionForOriginDecisionHandler))
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
if (m_uiDelegate->m_delegateMethods.webViewRequestGeolocationPermissionForOriginDecisionHandler) {
auto securityOrigin = WebCore::SecurityOrigin::createFromString(page.pageLoadState().activeURL());
auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:requestGeolocationPermissionForOrigin:initiatedByFrame:decisionHandler:));
auto decisionHandler = makeBlockPtr([completionHandler = std::exchange(completionHandler, nullptr), securityOrigin = securityOrigin->data(), checker = WTFMove(checker), page = WeakPtr { page }] (WKPermissionDecision decision) mutable {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
if (!page) {
completionHandler(false);
return;
}
switch (decision) {
case WKPermissionDecisionPrompt:
alertForPermission(*page, MediaPermissionReason::Geolocation, securityOrigin, WTFMove(completionHandler));
break;
case WKPermissionDecisionGrant:
completionHandler(true);
break;
case WKPermissionDecisionDeny:
completionHandler(false);
break;
}
});
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() requestGeolocationPermissionForOrigin:wrapper(API::SecurityOrigin::create(securityOrigin.get())) initiatedByFrame:wrapper(API::FrameInfo::create(FrameInfoData { frameInfo }, &page)) decisionHandler:decisionHandler.get()];
return;
}
auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:requestGeolocationPermissionForFrame:decisionHandler:));
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() requestGeolocationPermissionForFrame:wrapper(API::FrameInfo::create(FrameInfoData { frameInfo }, &page)) decisionHandler:makeBlockPtr([completionHandler = std::exchange(completionHandler, nullptr), checker = WTFMove(checker)] (BOOL result) mutable {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
completionHandler(result);
}).get()];
}
void UIDelegate::UIClient::didResignInputElementStrongPasswordAppearance(WebPageProxy&, API::Object* userInfo)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.webViewDidResignInputElementStrongPasswordAppearanceWithUserInfo)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() didResignInputElementStrongPasswordAppearanceWithUserInfo:userInfo ? static_cast<id <NSSecureCoding>>(userInfo->wrapper()) : nil];
}
bool UIDelegate::UIClient::canRunBeforeUnloadConfirmPanel() const
{
if (!m_uiDelegate)
return false;
return m_uiDelegate->m_delegateMethods.webViewRunBeforeUnloadConfirmPanelWithMessageInitiatedByFrameCompletionHandler;
}
void UIDelegate::UIClient::runBeforeUnloadConfirmPanel(WebPageProxy& page, const WTF::String& message, WebFrameProxy*, FrameInfoData&& frameInfo, Function<void(bool)>&& completionHandler)
{
if (!m_uiDelegate)
return completionHandler(false);
if (!m_uiDelegate->m_delegateMethods.webViewRunBeforeUnloadConfirmPanelWithMessageInitiatedByFrameCompletionHandler) {
completionHandler(false);
return;
}
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate) {
completionHandler(false);
return;
}
auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:runBeforeUnloadConfirmPanelWithMessage:initiatedByFrame:completionHandler:));
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() runBeforeUnloadConfirmPanelWithMessage:message initiatedByFrame:wrapper(API::FrameInfo::create(WTFMove(frameInfo), &page)) completionHandler:makeBlockPtr([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)] (BOOL result) mutable {
if (checker->completionHandlerHasBeenCalled())
return;
completionHandler(result);
checker->didCallCompletionHandler();
}).get()];
}
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)
{
// Use 50 MB as the default database quota.
unsigned long long defaultPerOriginDatabaseQuota = 50 * 1024 * 1024;
if (!m_uiDelegate)
return completionHandler(defaultPerOriginDatabaseQuota);
if (!m_uiDelegate->m_delegateMethods.webViewDecideDatabaseQuotaForSecurityOriginCurrentQuotaCurrentOriginUsageCurrentDatabaseUsageExpectedUsageDecisionHandler && !m_uiDelegate->m_delegateMethods.webViewDecideDatabaseQuotaForSecurityOriginDatabaseNameDisplayNameCurrentQuotaCurrentOriginUsageCurrentDatabaseUsageExpectedUsageDecisionHandler) {
completionHandler(defaultPerOriginDatabaseQuota);
return;
}
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate) {
completionHandler(defaultPerOriginDatabaseQuota);
return;
}
ASSERT(securityOrigin);
if (m_uiDelegate->m_delegateMethods.webViewDecideDatabaseQuotaForSecurityOriginDatabaseNameDisplayNameCurrentQuotaCurrentOriginUsageCurrentDatabaseUsageExpectedUsageDecisionHandler) {
auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:decideDatabaseQuotaForSecurityOrigin:databaseName:displayName:currentQuota:currentOriginUsage:currentDatabaseUsage:expectedUsage:decisionHandler:));
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() decideDatabaseQuotaForSecurityOrigin:wrapper(*securityOrigin) databaseName:databaseName displayName:displayName currentQuota:currentQuota currentOriginUsage:currentOriginUsage currentDatabaseUsage:currentUsage expectedUsage:expectedUsage decisionHandler:makeBlockPtr([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)](unsigned long long newQuota) {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
completionHandler(newQuota);
}).get()];
return;
}
auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:decideDatabaseQuotaForSecurityOrigin:currentQuota:currentOriginUsage:currentDatabaseUsage:expectedUsage:decisionHandler:));
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() decideDatabaseQuotaForSecurityOrigin:wrapper(*securityOrigin) currentQuota:currentQuota currentOriginUsage:currentOriginUsage currentDatabaseUsage:currentUsage expectedUsage:expectedUsage decisionHandler:makeBlockPtr([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)](unsigned long long newQuota) {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
completionHandler(newQuota);
}).get()];
}
static inline _WKFocusDirection toWKFocusDirection(WKFocusDirection direction)
{
switch (direction) {
case kWKFocusDirectionBackward:
return _WKFocusDirectionBackward;
case kWKFocusDirectionForward:
return _WKFocusDirectionForward;
}
ASSERT_NOT_REACHED();
return _WKFocusDirectionForward;
}
bool UIDelegate::UIClient::takeFocus(WebPageProxy*, WKFocusDirection direction)
{
if (!m_uiDelegate)
return false;
if (!m_uiDelegate->m_delegateMethods.webViewTakeFocus)
return false;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return false;
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() takeFocus:toWKFocusDirection(direction)];
return true;
}
static _WKAutoplayEventFlags toWKAutoplayEventFlags(OptionSet<WebCore::AutoplayEventFlags> flags)
{
_WKAutoplayEventFlags wkFlags = _WKAutoplayEventFlagsNone;
if (flags.contains(WebCore::AutoplayEventFlags::HasAudio))
wkFlags |= _WKAutoplayEventFlagsHasAudio;
if (flags.contains(WebCore::AutoplayEventFlags::PlaybackWasPrevented))
wkFlags |= _WKAutoplayEventFlagsPlaybackWasPrevented;
if (flags.contains(WebCore::AutoplayEventFlags::MediaIsMainContent))
wkFlags |= _WKAutoplayEventFlagsMediaIsMainContent;
return wkFlags;
}
static _WKAutoplayEvent toWKAutoplayEvent(WebCore::AutoplayEvent event)
{
switch (event) {
case WebCore::AutoplayEvent::DidPreventMediaFromPlaying:
return _WKAutoplayEventDidPreventFromAutoplaying;
case WebCore::AutoplayEvent::DidPlayMediaWithUserGesture:
return _WKAutoplayEventDidPlayMediaWithUserGesture;
case WebCore::AutoplayEvent::DidAutoplayMediaPastThresholdWithoutUserInterference:
return _WKAutoplayEventDidAutoplayMediaPastThresholdWithoutUserInterference;
case WebCore::AutoplayEvent::UserDidInterfereWithPlayback:
return _WKAutoplayEventUserDidInterfereWithPlayback;
}
ASSERT_NOT_REACHED();
return _WKAutoplayEventDidPlayMediaWithUserGesture;
}
void UIDelegate::UIClient::handleAutoplayEvent(WebPageProxy&, WebCore::AutoplayEvent event, OptionSet<WebCore::AutoplayEventFlags> flags)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.webViewHandleAutoplayEventWithFlags)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() handleAutoplayEvent:toWKAutoplayEvent(event) withFlags:toWKAutoplayEventFlags(flags)];
}
void UIDelegate::UIClient::decidePolicyForNotificationPermissionRequest(WebKit::WebPageProxy&, API::SecurityOrigin& securityOrigin, CompletionHandler<void(bool allowed)>&& completionHandler)
{
if (!m_uiDelegate)
return completionHandler(false);
if (!m_uiDelegate->m_delegateMethods.webViewRequestNotificationPermissionForSecurityOriginDecisionHandler)
return completionHandler(false);
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return completionHandler(false);
auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:requestNotificationPermissionForSecurityOrigin:decisionHandler:));
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() requestNotificationPermissionForSecurityOrigin:wrapper(securityOrigin) decisionHandler:makeBlockPtr([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)] (BOOL result) mutable {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
completionHandler(result);
}).get()];
}
void UIDelegate::UIClient::requestCookieConsent(CompletionHandler<void(WebCore::CookieConsentDecisionResult)>&& completion)
{
if (!m_uiDelegate)
return completion(WebCore::CookieConsentDecisionResult::NotSupported);
if (!m_uiDelegate->m_delegateMethods.webViewRequestCookieConsentWithMoreInfoHandlerDecisionHandler)
return completion(WebCore::CookieConsentDecisionResult::NotSupported);
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return completion(WebCore::CookieConsentDecisionResult::NotSupported);
// FIXME: Add support for the 'more info' handler.
auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:requestCookieConsentWithMoreInfoHandler:decisionHandler:));
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() requestCookieConsentWithMoreInfoHandler:nil decisionHandler:makeBlockPtr([completion = WTFMove(completion), checker = WTFMove(checker)] (BOOL decision) mutable {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
completion(decision ? WebCore::CookieConsentDecisionResult::Consent : WebCore::CookieConsentDecisionResult::Dissent);
}).get()];
}
static WebCore::ModalContainerDecision coreModalContainerDecision(_WKModalContainerDecision decision)
{
switch (decision) {
case _WKModalContainerDecisionShow:
return WebCore::ModalContainerDecision::Show;
case _WKModalContainerDecisionHideAndIgnore:
return WebCore::ModalContainerDecision::HideAndIgnore;
case _WKModalContainerDecisionHideAndAllow:
return WebCore::ModalContainerDecision::HideAndAllow;
case _WKModalContainerDecisionHideAndDisallow:
return WebCore::ModalContainerDecision::HideAndDisallow;
}
return WebCore::ModalContainerDecision::Show;
}
void UIDelegate::UIClient::decidePolicyForModalContainer(OptionSet<WebCore::ModalContainerControlType> controlTypes, CompletionHandler<void(WebCore::ModalContainerDecision)>&& completion)
{
if (!m_uiDelegate)
return completion(WebCore::ModalContainerDecision::Show);
if (!m_uiDelegate->m_delegateMethods.webViewDecidePolicyForModalContainerDecisionHandler)
return completion(WebCore::ModalContainerDecision::Show);
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return completion(WebCore::ModalContainerDecision::Show);
auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:decidePolicyForModalContainer:decisionHandler:));
auto info = adoptNS([[_WKModalContainerInfo alloc] initWithTypes:controlTypes]);
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() decidePolicyForModalContainer:info.get() decisionHandler:makeBlockPtr([completion = WTFMove(completion), checker = WTFMove(checker)] (_WKModalContainerDecision decision) mutable {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
completion(coreModalContainerDecision(decision));
}).get()];
}
#if PLATFORM(MAC)
bool UIDelegate::UIClient::canRunModal() const
{
if (!m_uiDelegate)
return false;
return m_uiDelegate->m_delegateMethods.webViewRunModal;
}
void UIDelegate::UIClient::runModal(WebPageProxy&)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.webViewRunModal)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[(id <WKUIDelegatePrivate>)delegate _webViewRunModal:m_uiDelegate->m_webView.get().get()];
}
float UIDelegate::UIClient::headerHeight(WebPageProxy&, WebFrameProxy& webFrameProxy)
{
if (!m_uiDelegate)
return 0;
if (!m_uiDelegate->m_delegateMethods.webViewHeaderHeight)
return 0;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return 0;
return [(id <WKUIDelegatePrivate>)delegate _webViewHeaderHeight:m_uiDelegate->m_webView.get().get()];
}
float UIDelegate::UIClient::footerHeight(WebPageProxy&, WebFrameProxy&)
{
if (!m_uiDelegate)
return 0;
if (!m_uiDelegate->m_delegateMethods.webViewFooterHeight)
return 0;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return 0;
return [(id <WKUIDelegatePrivate>)delegate _webViewFooterHeight:m_uiDelegate->m_webView.get().get()];
}
void UIDelegate::UIClient::drawHeader(WebPageProxy&, WebFrameProxy& frame, WebCore::FloatRect&& rect)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.webViewDrawHeaderInRectForPageWithTitleURL)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() drawHeaderInRect:rect forPageWithTitle:frame.title() URL:frame.url()];
}
void UIDelegate::UIClient::drawFooter(WebPageProxy&, WebFrameProxy& frame, WebCore::FloatRect&& rect)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.webViewDrawFooterInRectForPageWithTitleURL)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() drawFooterInRect:rect forPageWithTitle:frame.title() URL:frame.url()];
}
void UIDelegate::UIClient::pageDidScroll(WebPageProxy*)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.webViewDidScroll)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[(id <WKUIDelegatePrivate>)delegate _webViewDidScroll:m_uiDelegate->m_webView.get().get()];
}
void UIDelegate::UIClient::focus(WebPageProxy*)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.focusWebView)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[(id <WKUIDelegatePrivate>)delegate _focusWebView:m_uiDelegate->m_webView.get().get()];
}
void UIDelegate::UIClient::unfocus(WebPageProxy*)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.unfocusWebView)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[(id <WKUIDelegatePrivate>)delegate _unfocusWebView:m_uiDelegate->m_webView.get().get()];
}
static _WKResourceLimit toWKResourceLimit(WKResourceLimit limit)
{
switch (limit) {
case kWKResourceLimitMemory:
return _WKResourceLimitMemory;
case kWKResourceLimitCPU:
return _WKResourceLimitCPU;
}
ASSERT_NOT_REACHED();
return _WKResourceLimitMemory;
}
void UIDelegate::UIClient::didExceedBackgroundResourceLimitWhileInForeground(WebPageProxy&, WKResourceLimit limit)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.webViewDidExceedBackgroundResourceLimitWhileInForeground)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[static_cast<id <WKUIDelegatePrivate>>(delegate) _webView:m_uiDelegate->m_webView.get().get() didExceedBackgroundResourceLimitWhileInForeground:toWKResourceLimit(limit)];
}
void UIDelegate::UIClient::didNotHandleWheelEvent(WebPageProxy*, const NativeWebWheelEvent& event)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.webViewDidNotHandleWheelEvent)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() didNotHandleWheelEvent:event.nativeEvent()];
}
void UIDelegate::UIClient::setIsResizable(WebKit::WebPageProxy&, bool resizable)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.webViewSetResizable)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() setResizable:resizable];
}
void UIDelegate::UIClient::setWindowFrame(WebKit::WebPageProxy&, const WebCore::FloatRect& frame)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.webViewSetWindowFrame)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() setWindowFrame:frame];
}
void UIDelegate::UIClient::windowFrame(WebKit::WebPageProxy&, Function<void(WebCore::FloatRect)>&& completionHandler)
{
if (!m_uiDelegate)
return completionHandler({ });
if (!m_uiDelegate->m_delegateMethods.webViewGetWindowFrameWithCompletionHandler)
return completionHandler({ });
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return completionHandler({ });
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() getWindowFrameWithCompletionHandler:makeBlockPtr([completionHandler = WTFMove(completionHandler), checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:getWindowFrameWithCompletionHandler:))](CGRect frame) {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
completionHandler(frame);
}).get()];
}
void UIDelegate::UIClient::mouseDidMoveOverElement(WebPageProxy&, const WebHitTestResultData& data, OptionSet<WebEvent::Modifier> modifiers, API::Object* userInfo)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.webViewMouseDidMoveOverElementWithFlagsUserInfo)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
auto apiHitTestResult = API::HitTestResult::create(data);
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() mouseDidMoveOverElement:wrapper(apiHitTestResult.get()) withFlags:WebEventFactory::toNSEventModifierFlags(modifiers) userInfo:userInfo ? static_cast<id <NSSecureCoding>>(userInfo->wrapper()) : nil];
}
void UIDelegate::UIClient::toolbarsAreVisible(WebPageProxy&, Function<void(bool)>&& completionHandler)
{
if (!m_uiDelegate)
return completionHandler(true);
if (!m_uiDelegate->m_delegateMethods.webViewGetToolbarsAreVisibleWithCompletionHandler)
return completionHandler(true);
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return completionHandler(true);
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() getToolbarsAreVisibleWithCompletionHandler:makeBlockPtr([completionHandler = WTFMove(completionHandler), checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:getToolbarsAreVisibleWithCompletionHandler:))](BOOL visible) {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
completionHandler(visible);
}).get()];
}
void UIDelegate::UIClient::didClickAutoFillButton(WebPageProxy&, API::Object* userInfo)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.webViewDidClickAutoFillButtonWithUserInfo)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() didClickAutoFillButtonWithUserInfo:userInfo ? static_cast<id <NSSecureCoding>>(userInfo->wrapper()) : nil];
}
void UIDelegate::UIClient::showPage(WebPageProxy*)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.showWebView)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[(id <WKUIDelegatePrivate>)delegate _showWebView:m_uiDelegate->m_webView.get().get()];
}
void UIDelegate::UIClient::saveDataToFileInDownloadsFolder(WebPageProxy*, const WTF::String& suggestedFilename, const WTF::String& mimeType, const URL& originatingURL, API::Data& data)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.webViewSaveDataToFileSuggestedFilenameMimeTypeOriginatingURL)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() saveDataToFile:wrapper(data) suggestedFilename:suggestedFilename mimeType:mimeType originatingURL:originatingURL];
}
Ref<API::InspectorConfiguration> UIDelegate::UIClient::configurationForLocalInspector(WebPageProxy&, WebInspectorUIProxy& inspector)
{
if (!m_uiDelegate)
return API::InspectorConfiguration::create();
if (!m_uiDelegate->m_delegateMethods.webViewConfigurationForLocalInspector)
return API::InspectorConfiguration::create();
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return API::InspectorConfiguration::create();
return static_cast<API::InspectorConfiguration&>([[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() configurationForLocalInspector:wrapper(inspector)] _apiObject]);
}
void UIDelegate::UIClient::didAttachLocalInspector(WebPageProxy&, WebInspectorUIProxy& inspector)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.webViewDidAttachLocalInspector)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() didAttachLocalInspector:wrapper(inspector)];
}
void UIDelegate::UIClient::willCloseLocalInspector(WebPageProxy&, WebInspectorUIProxy& inspector)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.webViewWillCloseLocalInspector)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() willCloseLocalInspector:wrapper(inspector)];
}
bool UIDelegate::UIClient::runOpenPanel(WebPageProxy& page, WebFrameProxy* webFrameProxy, FrameInfoData&& frameInfo, API::OpenPanelParameters* openPanelParameters, WebOpenPanelResultListenerProxy* listener)
{
if (!m_uiDelegate)
return false;
if (!m_uiDelegate->m_delegateMethods.webViewRunOpenPanelWithParametersInitiatedByFrameCompletionHandler)
return false;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return false;
auto frame = API::FrameInfo::create(WTFMove(frameInfo), &page);
auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(webView:runOpenPanelWithParameters:initiatedByFrame:completionHandler:));
[delegate webView:m_uiDelegate->m_webView.get().get() runOpenPanelWithParameters:wrapper(*openPanelParameters) initiatedByFrame:wrapper(frame) completionHandler:makeBlockPtr([checker = WTFMove(checker), openPanelParameters = Ref { *openPanelParameters }, listener = RefPtr { listener }] (NSArray *URLs) mutable {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
if (!URLs) {
listener->cancel();
return;
}
Vector<String> filenames;
for (NSURL *url in URLs)
filenames.append(url.path);
listener->chooseFiles(filenames, openPanelParameters->allowedMIMETypes()->toStringVector());
}).get()];
return true;
}
#endif
#if ENABLE(DEVICE_ORIENTATION)
void UIDelegate::UIClient::shouldAllowDeviceOrientationAndMotionAccess(WebKit::WebPageProxy& page, WebFrameProxy& webFrameProxy, FrameInfoData&& frameInfo, CompletionHandler<void(bool)>&& completionHandler)
{
auto securityOrigin = WebCore::SecurityOrigin::createFromString(page.pageLoadState().activeURL());
if (!m_uiDelegate || !m_uiDelegate->m_delegate.get() || !m_uiDelegate->m_delegateMethods.webViewRequestDeviceOrientationAndMotionPermissionForOriginDecisionHandler) {
alertForPermission(page, MediaPermissionReason::DeviceOrientation, securityOrigin->data(), WTFMove(completionHandler));
return;
}
auto delegate = m_uiDelegate->m_delegate.get();
auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(webView:requestDeviceOrientationAndMotionPermissionForOrigin:initiatedByFrame:decisionHandler:));
auto decisionHandler = makeBlockPtr([completionHandler = WTFMove(completionHandler), securityOrigin = securityOrigin->data(), checker = WTFMove(checker), page = WeakPtr { page }](WKPermissionDecision decision) mutable {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
if (!page) {
completionHandler(false);
return;
}
switch (decision) {
case WKPermissionDecisionPrompt:
alertForPermission(*page, MediaPermissionReason::DeviceOrientation, securityOrigin, WTFMove(completionHandler));
break;
case WKPermissionDecisionGrant:
completionHandler(true);
break;
case WKPermissionDecisionDeny:
completionHandler(false);
break;
}
});
[delegate webView:m_uiDelegate->m_webView.get().get() requestDeviceOrientationAndMotionPermissionForOrigin:wrapper(API::SecurityOrigin::create(securityOrigin.get())) initiatedByFrame:wrapper(API::FrameInfo::create(WTFMove(frameInfo), &page)) decisionHandler:decisionHandler.get()];
}
#endif
void UIDelegate::UIClient::didChangeFontAttributes(const WebCore::FontAttributes& fontAttributes)
{
if (!m_uiDelegate)
return;
if (!needsFontAttributes())
return;
auto privateUIDelegate = (id <WKUIDelegatePrivate>)m_uiDelegate->m_delegate.get();
[privateUIDelegate _webView:m_uiDelegate->m_webView.get().get() didChangeFontAttributes:fontAttributes.createDictionary().get()];
}
void UIDelegate::UIClient::promptForDisplayCapturePermission(WebPageProxy& page, WebFrameProxy& frame, API::SecurityOrigin& userMediaOrigin, API::SecurityOrigin& topLevelOrigin, UserMediaPermissionRequestProxy& request)
{
auto delegate = (id <WKUIDelegatePrivate>)m_uiDelegate->m_delegate.get();
ASSERT([delegate respondsToSelector:@selector(_webView:requestDisplayCapturePermissionForOrigin:initiatedByFrame:withSystemAudio:decisionHandler:)]);
ASSERT(request.canPromptForGetDisplayMedia());
auto checker = CompletionHandlerCallChecker::create(delegate, @selector(_webView:requestDisplayCapturePermissionForOrigin:initiatedByFrame:withSystemAudio:decisionHandler:));
auto decisionHandler = makeBlockPtr([protectedRequest = Ref { request }, checker = WTFMove(checker)](WKDisplayCapturePermissionDecision decision) {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
switch (decision) {
case WKDisplayCapturePermissionDecisionScreenPrompt:
protectedRequest->promptForGetDisplayMedia(UserMediaPermissionRequestProxy::UserMediaDisplayCapturePromptType::Screen);
break;
case WKDisplayCapturePermissionDecisionWindowPrompt: {
protectedRequest->promptForGetDisplayMedia(UserMediaPermissionRequestProxy::UserMediaDisplayCapturePromptType::Window);
break;
}
case WKDisplayCapturePermissionDecisionDeny:
protectedRequest->deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied);
break;
}
});
std::optional<WebCore::FrameIdentifier> mainFrameID;
if (auto* mainFrame = frame.page() ? frame.page()->mainFrame() : nullptr)
mainFrameID = mainFrame->frameID();
FrameInfoData frameInfo { frame.isMainFrame(), { }, userMediaOrigin.securityOrigin(), { }, frame.frameID(), mainFrameID };
RetainPtr<WKFrameInfo> frameInfoWrapper = wrapper(API::FrameInfo::create(WTFMove(frameInfo), frame.page()));
BOOL requestSystemAudio = !!request.requiresDisplayCaptureWithAudio();
[delegate _webView:m_uiDelegate->m_webView.get().get() requestDisplayCapturePermissionForOrigin:wrapper(topLevelOrigin) initiatedByFrame:frameInfoWrapper.get() withSystemAudio:requestSystemAudio decisionHandler:decisionHandler.get()];
}
void UIDelegate::UIClient::decidePolicyForUserMediaPermissionRequest(WebPageProxy& page, WebFrameProxy& frame, API::SecurityOrigin& userMediaOrigin, API::SecurityOrigin& topLevelOrigin, UserMediaPermissionRequestProxy& request)
{
#if ENABLE(MEDIA_STREAM)
if (!m_uiDelegate)
return;
auto delegate = (id <WKUIDelegatePrivate>)m_uiDelegate->m_delegate.get();
if (!delegate) {
request.doDefaultAction();
return;
}
bool respondsToRequestMediaCapturePermission = [delegate respondsToSelector:@selector(webView:requestMediaCapturePermissionForOrigin:initiatedByFrame:type:decisionHandler:)];
bool respondsToRequestUserMediaAuthorizationForDevices = [delegate respondsToSelector:@selector(_webView:requestUserMediaAuthorizationForDevices:url:mainFrameURL:decisionHandler:)];
bool respondsToRequestDisplayCapturePermissionForOrigin = [delegate respondsToSelector:@selector(_webView:requestDisplayCapturePermissionForOrigin:initiatedByFrame:withSystemAudio:decisionHandler:)];
if (!respondsToRequestMediaCapturePermission && !respondsToRequestUserMediaAuthorizationForDevices && !respondsToRequestDisplayCapturePermissionForOrigin) {
request.doDefaultAction();
return;
}
// FIXME: Provide a specific delegate for display capture.
if (!request.requiresDisplayCapture() && respondsToRequestMediaCapturePermission) {
auto checker = CompletionHandlerCallChecker::create(delegate, @selector(webView:requestMediaCapturePermissionForOrigin:initiatedByFrame:type:decisionHandler:));
auto decisionHandler = makeBlockPtr([protectedRequest = Ref { request }, checker = WTFMove(checker)](WKPermissionDecision decision) {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
switch (decision) {
case WKPermissionDecisionPrompt:
protectedRequest->promptForGetUserMedia();
break;
case WKPermissionDecisionGrant: {
const String& videoDeviceUID = protectedRequest->requiresVideoCapture() ? protectedRequest->videoDeviceUIDs().first() : String();
const String& audioDeviceUID = protectedRequest->requiresAudioCapture() ? protectedRequest->audioDeviceUIDs().first() : String();
protectedRequest->allow(audioDeviceUID, videoDeviceUID);
break;
}
case WKPermissionDecisionDeny:
protectedRequest->deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied);
break;
}
});
std::optional<WebCore::FrameIdentifier> mainFrameID;
if (auto* mainFrame = frame.page() ? frame.page()->mainFrame() : nullptr)
mainFrameID = mainFrame->frameID();
FrameInfoData frameInfo { frame.isMainFrame(), { }, userMediaOrigin.securityOrigin(), { }, frame.frameID(), mainFrameID };
RetainPtr<WKFrameInfo> frameInfoWrapper = wrapper(API::FrameInfo::create(WTFMove(frameInfo), frame.page()));
WKMediaCaptureType type = WKMediaCaptureTypeCamera;
if (request.requiresAudioCapture())
type = request.requiresVideoCapture() ? WKMediaCaptureTypeCameraAndMicrophone : WKMediaCaptureTypeMicrophone;
[delegate webView:m_uiDelegate->m_webView.get().get() requestMediaCapturePermissionForOrigin:wrapper(topLevelOrigin) initiatedByFrame:frameInfoWrapper.get() type:type decisionHandler:decisionHandler.get()];
return;
}
if (request.requiresDisplayCapture() && request.canPromptForGetDisplayMedia()) {
if (respondsToRequestDisplayCapturePermissionForOrigin)
promptForDisplayCapturePermission(page, frame, userMediaOrigin, topLevelOrigin, request);
else
request.promptForGetDisplayMedia();
return;
}
if (!respondsToRequestUserMediaAuthorizationForDevices) {
request.doDefaultAction();
return;
}
URL requestFrameURL { frame.url() };
URL mainFrameURL { frame.page()->mainFrame()->url() };
_WKCaptureDevices devices = 0;
if (request.requiresAudioCapture())
devices |= _WKCaptureDeviceMicrophone;
if (request.requiresVideoCapture())
devices |= _WKCaptureDeviceCamera;
if (request.requiresDisplayCapture()) {
devices |= _WKCaptureDeviceDisplay;
ASSERT(!(devices & _WKCaptureDeviceCamera));
}
auto checker = CompletionHandlerCallChecker::create(delegate, @selector(_webView:requestUserMediaAuthorizationForDevices:url:mainFrameURL:decisionHandler:));
auto decisionHandler = makeBlockPtr([protectedRequest = Ref { request }, checker = WTFMove(checker)](BOOL authorized) {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
if (!authorized) {
protectedRequest->deny(UserMediaPermissionRequestProxy::UserMediaAccessDenialReason::PermissionDenied);
return;
}
const String& videoDeviceUID = (protectedRequest->requiresVideoCapture() || protectedRequest->requiresDisplayCapture()) ? protectedRequest->videoDeviceUIDs().first() : String();
const String& audioDeviceUID = protectedRequest->requiresAudioCapture() ? protectedRequest->audioDeviceUIDs().first() : String();
protectedRequest->allow(audioDeviceUID, videoDeviceUID);
});
[delegate _webView:m_uiDelegate->m_webView.get().get() requestUserMediaAuthorizationForDevices:devices url:requestFrameURL mainFrameURL:mainFrameURL decisionHandler:decisionHandler.get()];
#endif
}
void UIDelegate::UIClient::checkUserMediaPermissionForOrigin(WebPageProxy& page, WebFrameProxy& frame, API::SecurityOrigin& userMediaOrigin, API::SecurityOrigin& topLevelOrigin, UserMediaPermissionCheckProxy& request)
{
if (!m_uiDelegate)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate || !m_uiDelegate->m_delegateMethods.webViewIsMediaCaptureAuthorizedForFrameDecisionHandler) {
request.setUserMediaAccessInfo(false);
return;
}
const auto* mainFrame = frame.page()->mainFrame();
if ([delegate respondsToSelector:@selector(_webView:includeSensitiveMediaDeviceDetails:)]) {
auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:includeSensitiveMediaDeviceDetails:));
auto decisionHandler = makeBlockPtr([protectedRequest = Ref { request }, checker = WTFMove(checker)](BOOL includeSensitiveDetails) {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
protectedRequest->setUserMediaAccessInfo(includeSensitiveDetails);
});
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() includeSensitiveMediaDeviceDetails:decisionHandler.get()];
return;
}
auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:checkUserMediaPermissionForURL:mainFrameURL:frameIdentifier:decisionHandler:));
auto decisionHandler = makeBlockPtr([protectedRequest = Ref { request }, checker = WTFMove(checker)](NSString*, BOOL authorized) {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
protectedRequest->setUserMediaAccessInfo(authorized);
});
URL requestFrameURL { frame.url() };
URL mainFrameURL { mainFrame->url() };
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() checkUserMediaPermissionForURL:requestFrameURL mainFrameURL:mainFrameURL frameIdentifier:frame.frameID().toUInt64() decisionHandler:decisionHandler.get()];
}
void UIDelegate::UIClient::mediaCaptureStateDidChange(WebCore::MediaProducerMediaStateFlags state)
{
if (!m_uiDelegate)
return;
auto webView = m_uiDelegate->m_webView;
[webView didChangeValueForKey:@"mediaCaptureState"];
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate || !m_uiDelegate->m_delegateMethods.webViewMediaCaptureStateDidChange)
return;
[(id <WKUIDelegatePrivate>)delegate _webView:webView.get().get() mediaCaptureStateDidChange:toWKMediaCaptureStateDeprecated(state)];
}
void UIDelegate::UIClient::reachedApplicationCacheOriginQuota(WebPageProxy*, const WebCore::SecurityOrigin& securityOrigin, uint64_t currentQuota, uint64_t totalBytesNeeded, Function<void (unsigned long long)>&& completionHandler)
{
if (!m_uiDelegate)
return completionHandler(currentQuota);
if (!m_uiDelegate->m_delegateMethods.webViewDecideWebApplicationCacheQuotaForSecurityOriginCurrentQuotaTotalBytesNeeded) {
completionHandler(currentQuota);
return;
}
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate) {
completionHandler(currentQuota);
return;
}
auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:decideWebApplicationCacheQuotaForSecurityOrigin:currentQuota:totalBytesNeeded:decisionHandler:));
auto apiOrigin = API::SecurityOrigin::create(securityOrigin);
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() decideWebApplicationCacheQuotaForSecurityOrigin:wrapper(apiOrigin.get()) currentQuota:currentQuota totalBytesNeeded:totalBytesNeeded decisionHandler:makeBlockPtr([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)](unsigned long long newQuota) {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
completionHandler(newQuota);
}).get()];
}
void UIDelegate::UIClient::printFrame(WebPageProxy&, WebFrameProxy& webFrameProxy, const WebCore::FloatSize& pdfFirstPageSize, CompletionHandler<void()>&& completionHandler)
{
if (!m_uiDelegate)
return completionHandler();
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return completionHandler();
auto handle = API::FrameHandle::create(webFrameProxy.frameID());
if (m_uiDelegate->m_delegateMethods.webViewPrintFramePDFFirstPageSizeCompletionHandler) {
auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:printFrame:pdfFirstPageSize:completionHandler:));
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() printFrame:wrapper(handle) pdfFirstPageSize:static_cast<CGSize>(pdfFirstPageSize) completionHandler:makeBlockPtr([checker = WTFMove(checker), completionHandler = WTFMove(completionHandler)] () mutable {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
completionHandler();
}).get()];
return;
}
if (m_uiDelegate->m_delegateMethods.webViewPrintFrame)
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() printFrame:wrapper(handle)];
completionHandler();
}
void UIDelegate::UIClient::close(WebPageProxy*)
{
if (!m_uiDelegate)
return;
if (m_uiDelegate->m_delegateMethods.webViewClose) {
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[(id <WKUIDelegatePrivate>)delegate _webViewClose:m_uiDelegate->m_webView.get().get()];
return;
}
if (!m_uiDelegate->m_delegateMethods.webViewDidClose)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[delegate webViewDidClose:m_uiDelegate->m_webView.get().get()];
}
void UIDelegate::UIClient::fullscreenMayReturnToInline(WebPageProxy*)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.webViewFullscreenMayReturnToInline)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[(id <WKUIDelegatePrivate>)delegate _webViewFullscreenMayReturnToInline:m_uiDelegate->m_webView.get().get()];
}
void UIDelegate::UIClient::didEnterFullscreen(WebPageProxy*)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.webViewDidEnterFullscreen)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[(id <WKUIDelegatePrivate>)delegate _webViewDidEnterFullscreen:m_uiDelegate->m_webView.get().get()];
}
void UIDelegate::UIClient::didExitFullscreen(WebPageProxy*)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.webViewDidExitFullscreen)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[(id <WKUIDelegatePrivate>)delegate _webViewDidExitFullscreen:m_uiDelegate->m_webView.get().get()];
}
#if PLATFORM(IOS_FAMILY)
#if HAVE(APP_LINKS)
bool UIDelegate::UIClient::shouldIncludeAppLinkActionsForElement(_WKActivatedElementInfo *elementInfo)
{
if (!m_uiDelegate)
return true;
if (!m_uiDelegate->m_delegateMethods.webViewShouldIncludeAppLinkActionsForElement)
return true;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return true;
return [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() shouldIncludeAppLinkActionsForElement:elementInfo];
}
#endif
RetainPtr<NSArray> UIDelegate::UIClient::actionsForElement(_WKActivatedElementInfo *elementInfo, RetainPtr<NSArray> defaultActions)
{
if (!m_uiDelegate)
return defaultActions;
if (!m_uiDelegate->m_delegateMethods.webViewActionsForElementDefaultActions)
return defaultActions;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return defaultActions;
return [(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() actionsForElement:elementInfo defaultActions:defaultActions.get()];
}
void UIDelegate::UIClient::didNotHandleTapAsClick(const WebCore::IntPoint& point)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.webViewDidNotHandleTapAsClickAtPoint)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[static_cast<id <WKUIDelegatePrivate>>(delegate) _webView:m_uiDelegate->m_webView.get().get() didNotHandleTapAsClickAtPoint:point];
}
#endif // PLATFORM(IOS_FAMILY)
PlatformViewController *UIDelegate::UIClient::presentingViewController()
{
if (!m_uiDelegate)
return nullptr;
if (!m_uiDelegate->m_delegateMethods.presentingViewControllerForWebView)
return nullptr;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return nullptr;
return [static_cast<id <WKUIDelegatePrivate>>(delegate) _presentingViewControllerForWebView:m_uiDelegate->m_webView.get().get()];
}
NSDictionary *UIDelegate::UIClient::dataDetectionContext()
{
if (!m_uiDelegate)
return nullptr;
if (!m_uiDelegate->m_delegateMethods.dataDetectionContextForWebView)
return nullptr;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return nullptr;
return [static_cast<id <WKUIDelegatePrivate>>(delegate) _dataDetectionContextForWebView:m_uiDelegate->m_webView.get().get()];
}
#if ENABLE(POINTER_LOCK)
void UIDelegate::UIClient::requestPointerLock(WebPageProxy* page)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.webViewRequestPointerLock && !m_uiDelegate->m_delegateMethods.webViewDidRequestPointerLockCompletionHandler)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
if (m_uiDelegate->m_delegateMethods.webViewRequestPointerLock) {
[static_cast<id <WKUIDelegatePrivate>>(delegate) _webViewRequestPointerLock:m_uiDelegate->m_webView.get().get()];
return;
}
auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webViewDidRequestPointerLock:completionHandler:));
[static_cast<id <WKUIDelegatePrivate>>(delegate) _webViewDidRequestPointerLock:m_uiDelegate->m_webView.get().get() completionHandler:makeBlockPtr([checker = WTFMove(checker), page = RefPtr { page }] (BOOL allow) {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
if (allow)
page->didAllowPointerLock();
else
page->didDenyPointerLock();
}).get()];
}
void UIDelegate::UIClient::didLosePointerLock(WebPageProxy*)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.webViewDidLosePointerLock)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[static_cast<id <WKUIDelegatePrivate>>(delegate) _webViewDidLosePointerLock:m_uiDelegate->m_webView.get().get()];
}
#endif
void UIDelegate::UIClient::didShowSafeBrowsingWarning()
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.webViewDidShowSafeBrowsingWarning)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[static_cast<id <WKUIDelegatePrivate>>(delegate) _webViewDidShowSafeBrowsingWarning:m_uiDelegate->m_webView.get().get()];
}
void UIDelegate::UIClient::confirmPDFOpening(WebPageProxy& page, const WTF::URL& fileURL, FrameInfoData&& frameInfo, CompletionHandler<void(bool)>&& completionHandler)
{
if (!m_uiDelegate)
return completionHandler(true);
if (!m_uiDelegate->m_delegateMethods.webViewShouldAllowPDFAtURLToOpenFromFrameCompletionHandler)
return completionHandler(true);
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return completionHandler(true);
auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:shouldAllowPDFAtURL:toOpenFromFrame:completionHandler:));
[static_cast<id <WKUIDelegatePrivate>>(delegate) _webView:m_uiDelegate->m_webView.get().get() shouldAllowPDFAtURL:fileURL toOpenFromFrame:wrapper(API::FrameInfo::create(WTFMove(frameInfo), &page)) completionHandler:makeBlockPtr([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)] (BOOL result) mutable {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
completionHandler(result);
}).get()];
}
#if ENABLE(WEB_AUTHN)
static WebAuthenticationPanelResult webAuthenticationPanelResult(_WKWebAuthenticationPanelResult result)
{
switch (result) {
case _WKWebAuthenticationPanelResultUnavailable:
return WebAuthenticationPanelResult::Unavailable;
case _WKWebAuthenticationPanelResultPresented:
return WebAuthenticationPanelResult::Presented;
case _WKWebAuthenticationPanelResultDidNotPresent:
return WebAuthenticationPanelResult::DidNotPresent;
}
ASSERT_NOT_REACHED();
return WebAuthenticationPanelResult::Unavailable;
}
void UIDelegate::UIClient::runWebAuthenticationPanel(WebPageProxy& page, API::WebAuthenticationPanel& panel, WebFrameProxy&, FrameInfoData&& frameInfo, CompletionHandler<void(WebAuthenticationPanelResult)>&& completionHandler)
{
if (!m_uiDelegate)
return completionHandler(WebAuthenticationPanelResult::Unavailable);
if (!m_uiDelegate->m_delegateMethods.webViewRunWebAuthenticationPanelInitiatedByFrameCompletionHandler) {
completionHandler(WebAuthenticationPanelResult::Unavailable);
return;
}
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate) {
completionHandler(WebAuthenticationPanelResult::Unavailable);
return;
}
auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:runWebAuthenticationPanel:initiatedByFrame:completionHandler:));
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() runWebAuthenticationPanel:wrapper(panel) initiatedByFrame:wrapper(API::FrameInfo::create(WTFMove(frameInfo), &page)) completionHandler:makeBlockPtr([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)] (_WKWebAuthenticationPanelResult result) mutable {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
completionHandler(webAuthenticationPanelResult(result));
}).get()];
}
void UIDelegate::UIClient::requestWebAuthenticationNoGesture(API::SecurityOrigin& origin, CompletionHandler<void(bool)>&& completionHandler)
{
if (!m_uiDelegate)
return completionHandler(false);
if (!m_uiDelegate->m_delegateMethods.webViewRequestWebAuthenticationNoGestureForOriginCompletionHandler)
return completionHandler(false);
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return completionHandler(false);
auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(_webView:requestWebAuthenticationNoGestureForOrigin:completionHandler:));
[(id <WKUIDelegatePrivate>)delegate _webView:m_uiDelegate->m_webView.get().get() requestWebAuthenticationNoGestureForOrigin: wrapper(origin) completionHandler:makeBlockPtr([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)] (BOOL result) mutable {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
completionHandler(result);
}).get()];
}
#endif // ENABLE(WEB_AUTHN)
void UIDelegate::UIClient::hasVideoInPictureInPictureDidChange(WebPageProxy*, bool hasVideoInPictureInPicture)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.webViewHasVideoInPictureInPictureDidChange)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[static_cast<id <WKUIDelegatePrivate>>(delegate) _webView:m_uiDelegate->m_webView.get().get() hasVideoInPictureInPictureDidChange:hasVideoInPictureInPicture];
}
void UIDelegate::UIClient::imageOrMediaDocumentSizeChanged(const WebCore::IntSize& newSize)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.webViewImageOrMediaDocumentSizeChanged)
return;
auto delegate = m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[static_cast<id <WKUIDelegatePrivate>>(delegate) _webView:m_uiDelegate->m_webView.get().get() imageOrMediaDocumentSizeChanged:newSize];
}
void UIDelegate::UIClient::decidePolicyForSpeechRecognitionPermissionRequest(WebKit::WebPageProxy& page, API::SecurityOrigin& origin, CompletionHandler<void(bool)>&& completionHandler)
{
if (!m_uiDelegate) {
page.requestSpeechRecognitionPermissionByDefaultAction(origin.securityOrigin(), WTFMove(completionHandler));
return;
}
auto delegate = (id <WKUIDelegatePrivate>)m_uiDelegate->m_delegate.get();
if (!delegate) {
page.requestSpeechRecognitionPermissionByDefaultAction(origin.securityOrigin(), WTFMove(completionHandler));
return;
}
if (![delegate respondsToSelector:@selector(_webView:requestSpeechRecognitionPermissionForOrigin:decisionHandler:)]) {
page.requestSpeechRecognitionPermissionByDefaultAction(origin.securityOrigin(), WTFMove(completionHandler));
return;
}
auto checker = CompletionHandlerCallChecker::create(delegate, @selector(_webView:requestSpeechRecognitionPermissionForOrigin:decisionHandler:));
[delegate _webView:m_uiDelegate->m_webView.get().get() requestSpeechRecognitionPermissionForOrigin:wrapper(origin) decisionHandler:makeBlockPtr([completionHandler = std::exchange(completionHandler, { }), checker = WTFMove(checker)] (BOOL granted) mutable {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
completionHandler(granted);
}).get()];
}
void UIDelegate::UIClient::didEnableInspectorBrowserDomain(WebPageProxy&)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.webViewDidEnableInspectorBrowserDomain)
return;
auto delegate = (id <WKUIDelegatePrivate>)m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[delegate _webViewDidEnableInspectorBrowserDomain:m_uiDelegate->m_webView.get().get()];
}
void UIDelegate::UIClient::didDisableInspectorBrowserDomain(WebPageProxy&)
{
if (!m_uiDelegate)
return;
if (!m_uiDelegate->m_delegateMethods.webViewDidDisableInspectorBrowserDomain)
return;
auto delegate = (id <WKUIDelegatePrivate>)m_uiDelegate->m_delegate.get();
if (!delegate)
return;
[delegate _webViewDidDisableInspectorBrowserDomain:m_uiDelegate->m_webView.get().get()];
}
#if ENABLE(WEBXR)
static _WKXRSessionMode toWKXRSessionMode(PlatformXR::SessionMode mode)
{
switch (mode) {
case PlatformXR::SessionMode::Inline:
return _WKXRSessionModeInline;
case PlatformXR::SessionMode::ImmersiveVr:
return _WKXRSessionModeImmersiveVr;
case PlatformXR::SessionMode::ImmersiveAr:
return _WKXRSessionModeImmersiveAr;
}
}
static _WKXRSessionFeatureFlags toWKXRSessionFeatureFlags(PlatformXR::ReferenceSpaceType feature)
{
switch (feature) {
case PlatformXR::ReferenceSpaceType::Viewer:
return _WKXRSessionFeatureFlagsReferenceSpaceTypeViewer;
case PlatformXR::ReferenceSpaceType::Local:
return _WKXRSessionFeatureFlagsReferenceSpaceTypeLocal;
case PlatformXR::ReferenceSpaceType::LocalFloor:
return _WKXRSessionFeatureFlagsReferenceSpaceTypeLocalFloor;
case PlatformXR::ReferenceSpaceType::BoundedFloor:
return _WKXRSessionFeatureFlagsReferenceSpaceTypeBoundedFloor;
case PlatformXR::ReferenceSpaceType::Unbounded:
return _WKXRSessionFeatureFlagsReferenceSpaceTypeUnbounded;
}
}
static _WKXRSessionFeatureFlags toWKXRSessionFeatureFlags(const PlatformXR::Device::FeatureList& features)
{
_WKXRSessionFeatureFlags flags = _WKXRSessionFeatureFlagsNone;
for (auto feature : features)
flags |= toWKXRSessionFeatureFlags(feature);
return flags;
}
static std::optional<PlatformXR::Device::FeatureList> toPlatformXRFeatures(_WKXRSessionFeatureFlags featureFlags)
{
if (featureFlags == _WKXRSessionFeatureFlagsNone)
return std::nullopt;
PlatformXR::Device::FeatureList features;
if (featureFlags & _WKXRSessionFeatureFlagsReferenceSpaceTypeViewer)
features.append(PlatformXR::ReferenceSpaceType::Viewer);
if (featureFlags & _WKXRSessionFeatureFlagsReferenceSpaceTypeLocal)
features.append(PlatformXR::ReferenceSpaceType::Local);
if (featureFlags & _WKXRSessionFeatureFlagsReferenceSpaceTypeLocalFloor)
features.append(PlatformXR::ReferenceSpaceType::LocalFloor);
if (featureFlags & _WKXRSessionFeatureFlagsReferenceSpaceTypeBoundedFloor)
features.append(PlatformXR::ReferenceSpaceType::BoundedFloor);
if (featureFlags & _WKXRSessionFeatureFlagsReferenceSpaceTypeUnbounded)
features.append(PlatformXR::ReferenceSpaceType::Unbounded);
return features;
}
void UIDelegate::UIClient::requestPermissionOnXRSessionFeatures(WebPageProxy&, const WebCore::SecurityOriginData& securityOriginData, PlatformXR::SessionMode mode, const PlatformXR::Device::FeatureList& granted, const PlatformXR::Device::FeatureList& consentRequired, const PlatformXR::Device::FeatureList& consentOptional, CompletionHandler<void(std::optional<PlatformXR::Device::FeatureList>&&)>&& completionHandler)
{
if (!m_uiDelegate || !m_uiDelegate->m_delegateMethods.webViewRequestPermissionForXRSessionOriginModeAndFeaturesWithCompletionHandler) {
completionHandler(granted);
return;
}
auto delegate = (id <WKUIDelegatePrivate>)m_uiDelegate->m_delegate.get();
if (!delegate) {
completionHandler(granted);
return;
}
auto checker = CompletionHandlerCallChecker::create(delegate, @selector(_webView:requestPermissionForXRSessionOrigin:mode:grantedFeatures:consentRequiredFeatures:consentOptionalFeatures:completionHandler:));
[delegate _webView:m_uiDelegate->m_webView.get().get() requestPermissionForXRSessionOrigin:securityOriginData.toString() mode:toWKXRSessionMode(mode) grantedFeatures:toWKXRSessionFeatureFlags(granted) consentRequiredFeatures:toWKXRSessionFeatureFlags(consentRequired) consentOptionalFeatures:toWKXRSessionFeatureFlags(consentOptional) completionHandler:makeBlockPtr([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)] (_WKXRSessionFeatureFlags userGrantedFeatures) mutable {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
completionHandler(toPlatformXRFeatures(userGrantedFeatures));
}).get()];
}
void UIDelegate::UIClient::startXRSession(WebPageProxy&, CompletionHandler<void(RetainPtr<id>)>&& completionHandler)
{
if (!m_uiDelegate || !m_uiDelegate->m_delegateMethods.webViewStartXRSessionWithCompletionHandler) {
completionHandler(nil);
return;
}
auto delegate = (id <WKUIDelegatePrivate>)m_uiDelegate->m_delegate.get();
if (!delegate) {
completionHandler(nil);
return;
}
auto checker = CompletionHandlerCallChecker::create(delegate, @selector(_webView:startXRSessionWithCompletionHandler:));
[delegate _webView:m_uiDelegate->m_webView.get().get() startXRSessionWithCompletionHandler:makeBlockPtr([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)] (id result) mutable {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
completionHandler(result);
}).get()];
}
#endif
} // namespace WebKit