blob: cacaeb41eb26559ac59051b412d8e1a20db31144 [file] [log] [blame]
/*
* Copyright (C) 2014-2021 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 "WKWebViewInternal.h"
#import "APIFormClient.h"
#import "APIFrameTreeNode.h"
#import "APIPageConfiguration.h"
#import "APISerializedScriptValue.h"
#import "CocoaImage.h"
#import "CompletionHandlerCallChecker.h"
#import "ContentAsStringIncludesChildFrames.h"
#import "DiagnosticLoggingClient.h"
#import "FindClient.h"
#import "FullscreenClient.h"
#import "GlobalFindInPageState.h"
#import "IconLoadingDelegate.h"
#import "LegacySessionStateCoding.h"
#import "Logging.h"
#import "MediaUtilities.h"
#import "NavigationState.h"
#import "ObjCObjectGraph.h"
#import "PageClient.h"
#import "ProvisionalPageProxy.h"
#import "QuickLookThumbnailLoader.h"
#import "RemoteLayerTreeScrollingPerformanceData.h"
#import "RemoteObjectRegistry.h"
#import "RemoteObjectRegistryMessages.h"
#import "ResourceLoadDelegate.h"
#import "SafeBrowsingWarning.h"
#import "SessionStateCoding.h"
#import "SharedBufferCopy.h"
#import "UIDelegate.h"
#import "VideoFullscreenManagerProxy.h"
#import "ViewGestureController.h"
#import "WKBackForwardListInternal.h"
#import "WKBackForwardListItemInternal.h"
#import "WKBrowsingContextHandleInternal.h"
#import "WKContentWorldInternal.h"
#import "WKDownloadInternal.h"
#import "WKErrorInternal.h"
#import "WKFindConfiguration.h"
#import "WKFindResultInternal.h"
#import "WKFrameInfoPrivate.h"
#import "WKHistoryDelegatePrivate.h"
#import "WKLayoutMode.h"
#import "WKNSData.h"
#import "WKNSURLExtras.h"
#import "WKNavigationDelegate.h"
#import "WKNavigationInternal.h"
#import "WKPDFConfiguration.h"
#import "WKPreferencesInternal.h"
#import "WKProcessPoolInternal.h"
#import "WKSafeBrowsingWarning.h"
#import "WKSharedAPICast.h"
#import "WKSnapshotConfiguration.h"
#import "WKUIDelegate.h"
#import "WKUIDelegatePrivate.h"
#import "WKUserContentControllerInternal.h"
#import "WKWebViewConfigurationInternal.h"
#import "WKWebViewContentProvider.h"
#import "WKWebViewMac.h"
#import "WKWebpagePreferencesInternal.h"
#import "WKWebsiteDataStoreInternal.h"
#import "WebBackForwardList.h"
#import "WebCertificateInfo.h"
#import "WebFullScreenManagerProxy.h"
#import "WebPageGroup.h"
#import "WebPageInspectorController.h"
#import "WebPageProxy.h"
#import "WebPreferences.h"
#import "WebProcessPool.h"
#import "WebProcessProxy.h"
#import "WebURLSchemeHandlerCocoa.h"
#import "WebViewImpl.h"
#import "_WKActivatedElementInfoInternal.h"
#import "_WKAppHighlightDelegate.h"
#import "_WKAppHighlightInternal.h"
#import "_WKDiagnosticLoggingDelegate.h"
#import "_WKFindDelegate.h"
#import "_WKFrameHandleInternal.h"
#import "_WKFrameTreeNodeInternal.h"
#import "_WKFullscreenDelegate.h"
#import "_WKHitTestResultInternal.h"
#import "_WKInputDelegate.h"
#import "_WKInspectorInternal.h"
#import "_WKRemoteObjectRegistryInternal.h"
#import "_WKSessionStateInternal.h"
#import "_WKTextInputContextInternal.h"
#import "_WKTextManipulationConfiguration.h"
#import "_WKTextManipulationDelegate.h"
#import "_WKTextManipulationExclusionRule.h"
#import "_WKTextManipulationItem.h"
#import "_WKTextManipulationToken.h"
#import "_WKVisitedLinkStoreInternal.h"
#import <WebCore/AppHighlight.h>
#import <WebCore/AttributedString.h>
#import <WebCore/ColorCocoa.h>
#import <WebCore/ColorSerialization.h>
#import <WebCore/ContentExtensionsBackend.h>
#import <WebCore/ElementContext.h>
#import <WebCore/JSDOMBinding.h>
#import <WebCore/JSDOMExceptionHandling.h>
#import <WebCore/LegacySchemeRegistry.h>
#import <WebCore/MIMETypeRegistry.h>
#import <WebCore/PlatformScreen.h>
#import <WebCore/RuntimeApplicationChecks.h>
#import <WebCore/RuntimeEnabledFeatures.h>
#import <WebCore/Settings.h>
#import <WebCore/SharedBuffer.h>
#import <WebCore/StringUtilities.h>
#import <WebCore/TextManipulationController.h>
#import <WebCore/ViewportArguments.h>
#import <WebCore/WebCoreObjCExtras.h>
#import <WebCore/WebViewVisualIdentificationOverlay.h>
#import <WebCore/WritingMode.h>
#import <wtf/BlockPtr.h>
#import <wtf/CallbackAggregator.h>
#import <wtf/HashMap.h>
#import <wtf/MathExtras.h>
#import <wtf/NeverDestroyed.h>
#import <wtf/RetainPtr.h>
#import <wtf/SystemTracing.h>
#import <wtf/UUID.h>
#import <wtf/cocoa/RuntimeApplicationChecksCocoa.h>
#import <wtf/cocoa/VectorCocoa.h>
#import <wtf/spi/darwin/dyldSPI.h>
#import <wtf/text/TextStream.h>
#if ENABLE(APPLICATION_MANIFEST)
#import "_WKApplicationManifestInternal.h"
#endif
#if ENABLE(DATA_DETECTION)
#import "WKDataDetectorTypesInternal.h"
#endif
#if PLATFORM(IOS_FAMILY)
#import "RemoteLayerTreeDrawingAreaProxy.h"
#import "RemoteScrollingCoordinatorProxy.h"
#import "UIKitSPI.h"
#import "WKContentViewInteraction.h"
#import "WKScrollView.h"
#import "WKWebViewContentProviderRegistry.h"
#import "WKWebViewIOS.h"
#import "WKWebViewPrivateForTestingIOS.h"
#import <MobileCoreServices/MobileCoreServices.h>
#import <UIKit/UIApplication.h>
#import <pal/spi/cf/CFNotificationCenterSPI.h>
#import <pal/spi/cocoa/QuartzCoreSPI.h>
#import <pal/spi/ios/GraphicsServicesSPI.h>
#import <wtf/cocoa/Entitlements.h>
#define WKWEBVIEW_RELEASE_LOG(...) RELEASE_LOG(ViewState, __VA_ARGS__)
#endif // PLATFORM(IOS_FAMILY)
#if PLATFORM(MAC)
#import "AppKitSPI.h"
#import "WKContentViewMac.h"
#import "WKTextFinderClient.h"
#import "WKViewInternal.h"
#import <WebCore/ColorMac.h>
#endif
#if PLATFORM(WATCHOS)
static const BOOL defaultAllowsViewportShrinkToFit = YES;
static const BOOL defaultFastClickingEnabled = YES;
#elif PLATFORM(IOS_FAMILY)
static const BOOL defaultAllowsViewportShrinkToFit = NO;
static const BOOL defaultFastClickingEnabled = NO;
#endif
#define THROW_IF_SUSPENDED if (UNLIKELY(_page && _page->isSuspended())) \
[NSException raise:NSInternalInconsistencyException format:@"The WKWebView is suspended"]
RetainPtr<NSError> nsErrorFromExceptionDetails(const WebCore::ExceptionDetails& details)
{
auto userInfo = adoptNS([[NSMutableDictionary alloc] init]);
WKErrorCode errorCode;
switch (details.type) {
case WebCore::ExceptionDetails::Type::InvalidTargetFrame:
errorCode = WKErrorJavaScriptInvalidFrameTarget;
break;
case WebCore::ExceptionDetails::Type::Script:
errorCode = WKErrorJavaScriptExceptionOccurred;
break;
case WebCore::ExceptionDetails::Type::AppBoundDomain:
errorCode = WKErrorJavaScriptAppBoundDomain;
break;
}
[userInfo setObject:localizedDescriptionForErrorCode(errorCode) forKey:NSLocalizedDescriptionKey];
[userInfo setObject:details.message forKey:_WKJavaScriptExceptionMessageErrorKey];
[userInfo setObject:@(details.lineNumber) forKey:_WKJavaScriptExceptionLineNumberErrorKey];
[userInfo setObject:@(details.columnNumber) forKey:_WKJavaScriptExceptionColumnNumberErrorKey];
if (!details.sourceURL.isEmpty())
[userInfo setObject:[NSURL _web_URLWithWTFString:details.sourceURL] forKey:_WKJavaScriptExceptionSourceURLErrorKey];
return adoptNS([[NSError alloc] initWithDomain:WKErrorDomain code:errorCode userInfo:userInfo.get()]);
}
@implementation WKWebView
- (instancetype)initWithFrame:(CGRect)frame
{
return [self initWithFrame:frame configuration:adoptNS([[WKWebViewConfiguration alloc] init]).get()];
}
- (BOOL)_isValid
{
return _page && _page->hasRunningProcess();
}
#if PLATFORM(IOS_FAMILY)
static bool shouldAllowPictureInPictureMediaPlayback()
{
static bool shouldAllowPictureInPictureMediaPlayback = linkedOnOrAfter(SDKVersion::FirstWithPictureInPictureMediaPlayback);
return shouldAllowPictureInPictureMediaPlayback;
}
static bool shouldAllowSettingAnyXHRHeaderFromFileURLs()
{
static bool shouldAllowSettingAnyXHRHeaderFromFileURLs = (WebCore::IOSApplication::isCardiogram() || WebCore::IOSApplication::isNike()) && !linkedOnOrAfter(SDKVersion::FirstThatDisallowsSettingAnyXHRHeaderFromFileURLs);
return shouldAllowSettingAnyXHRHeaderFromFileURLs;
}
#endif // PLATFORM(IOS_FAMILY)
static bool shouldRequireUserGestureToLoadVideo()
{
#if PLATFORM(IOS_FAMILY)
static bool shouldRequireUserGestureToLoadVideo = linkedOnOrAfter(SDKVersion::FirstThatRequiresUserGestureToLoadVideo);
return shouldRequireUserGestureToLoadVideo;
#else
return false;
#endif
}
static bool shouldRestrictBaseURLSchemes()
{
static bool shouldRestrictBaseURLSchemes = linkedOnOrAfter(SDKVersion::FirstThatRestrictsBaseURLSchemes);
return shouldRestrictBaseURLSchemes;
}
#if PLATFORM(MAC)
static uint32_t convertUserInterfaceDirectionPolicy(WKUserInterfaceDirectionPolicy policy)
{
switch (policy) {
case WKUserInterfaceDirectionPolicyContent:
return static_cast<uint32_t>(WebCore::UserInterfaceDirectionPolicy::Content);
case WKUserInterfaceDirectionPolicySystem:
return static_cast<uint32_t>(WebCore::UserInterfaceDirectionPolicy::System);
}
return static_cast<uint32_t>(WebCore::UserInterfaceDirectionPolicy::Content);
}
static uint32_t convertSystemLayoutDirection(NSUserInterfaceLayoutDirection direction)
{
switch (direction) {
case NSUserInterfaceLayoutDirectionLeftToRight:
return static_cast<uint32_t>(WebCore::UserInterfaceLayoutDirection::LTR);
case NSUserInterfaceLayoutDirectionRightToLeft:
return static_cast<uint32_t>(WebCore::UserInterfaceLayoutDirection::RTL);
}
return static_cast<uint32_t>(WebCore::UserInterfaceLayoutDirection::LTR);
}
#endif // PLATFORM(MAC)
static void validate(WKWebViewConfiguration *configuration)
{
if (!configuration.processPool)
[NSException raise:NSInvalidArgumentException format:@"configuration.processPool is nil"];
if (!configuration.preferences)
[NSException raise:NSInvalidArgumentException format:@"configuration.preferences is nil"];
if (!configuration.userContentController)
[NSException raise:NSInvalidArgumentException format:@"configuration.userContentController is nil"];
if (!configuration.websiteDataStore)
[NSException raise:NSInvalidArgumentException format:@"configuration.websiteDataStore is nil"];
if (!configuration._visitedLinkStore)
[NSException raise:NSInvalidArgumentException format:@"configuration._visitedLinkStore is nil"];
#if PLATFORM(IOS_FAMILY)
if (!configuration._contentProviderRegistry)
[NSException raise:NSInvalidArgumentException format:@"configuration._contentProviderRegistry is nil"];
#endif
}
#if PLATFORM(IOS_FAMILY)
static void hardwareKeyboardAvailabilityChangedCallback(CFNotificationCenterRef, void* observer, CFStringRef, const void*, CFDictionaryRef)
{
ASSERT(observer);
RetainPtr webView { (__bridge WKWebView *)observer };
if (!webView)
return;
[webView->_contentView _hardwareKeyboardAvailabilityChanged];
webView->_page->hardwareKeyboardAvailabilityChanged(GSEventIsHardwareKeyboardAttached());
}
#endif
- (void)_initializeWithConfiguration:(WKWebViewConfiguration *)configuration
{
if (!configuration)
[NSException raise:NSInvalidArgumentException format:@"Configuration cannot be nil"];
_configuration = adoptNS([configuration copy]);
if (WKWebView *relatedWebView = [_configuration _relatedWebView]) {
WKProcessPool *processPool = [_configuration processPool];
WKProcessPool *relatedWebViewProcessPool = [relatedWebView->_configuration processPool];
if (processPool && processPool != relatedWebViewProcessPool)
[NSException raise:NSInvalidArgumentException format:@"Related web view %@ has process pool %@ but configuration specifies a different process pool %@", relatedWebView, relatedWebViewProcessPool, configuration.processPool];
if ([relatedWebView->_configuration websiteDataStore] != [_configuration websiteDataStore] && linkedOnOrAfter(SDKVersion::FirstWithExceptionsForRelatedWebViewsUsingDifferentDataStores))
[NSException raise:NSInvalidArgumentException format:@"Related web view %@ has data store %@ but configuration specifies a different data store %@", relatedWebView, [relatedWebView->_configuration websiteDataStore], [_configuration websiteDataStore]];
[_configuration setProcessPool:relatedWebViewProcessPool];
}
validate(_configuration.get());
WebKit::WebProcessPool& processPool = *[_configuration processPool]->_processPool;
auto pageConfiguration = [configuration copyPageConfiguration];
pageConfiguration->setProcessPool(&processPool);
[self _setupPageConfiguration:pageConfiguration];
_usePlatformFindUI = YES;
#if PLATFORM(IOS_FAMILY)
_avoidsUnsafeArea = YES;
_obscuredInsetEdgesAffectedBySafeArea = UIRectEdgeTop | UIRectEdgeLeft | UIRectEdgeRight;
_viewportMetaTagWidth = WebCore::ViewportArguments::ValueAuto;
_initialScaleFactor = 1;
_allowsViewportShrinkToFit = defaultAllowsViewportShrinkToFit;
_allowsLinkPreview = linkedOnOrAfter(SDKVersion::FirstWithLinkPreviewEnabledByDefault);
#if HAVE(UIFINDINTERACTION)
_findInteractionEnabled = NO;
#endif
auto fastClickingEnabled = []() {
if (NSNumber *enabledValue = [[NSUserDefaults standardUserDefaults] objectForKey:@"WebKitFastClickingDisabled"])
return enabledValue.boolValue;
return defaultFastClickingEnabled;
};
_fastClickingIsDisabled = fastClickingEnabled();
_dragInteractionPolicy = _WKDragInteractionPolicyDefault;
_contentView = adoptNS([[WKContentView alloc] initWithFrame:self.bounds processPool:processPool configuration:pageConfiguration.copyRef() webView:self]);
_page = [_contentView page];
[[_configuration _contentProviderRegistry] addPage:*_page];
[self _setupScrollAndContentViews];
if (!self.opaque || !pageConfiguration->drawsBackground())
[self _setOpaqueInternal:NO];
else
[self _updateScrollViewBackground];
[self _frameOrBoundsChanged];
[self _registerForNotifications];
_page->contentSizeCategoryDidChange([self _contentSizeCategory]);
auto notificationName = adoptNS([[NSString alloc] initWithCString:kGSEventHardwareKeyboardAvailabilityChangedNotification encoding:NSUTF8StringEncoding]);
auto notificationBehavior = static_cast<CFNotificationSuspensionBehavior>(CFNotificationSuspensionBehaviorCoalesce | _CFNotificationObserverIsObjC);
CFNotificationCenterAddObserver(CFNotificationCenterGetDarwinNotifyCenter(), (__bridge const void *)(self), hardwareKeyboardAvailabilityChangedCallback, (__bridge CFStringRef)notificationName.get(), nullptr, notificationBehavior);
#endif // PLATFORM(IOS_FAMILY)
#if ENABLE(META_VIEWPORT)
_page->setForceAlwaysUserScalable([_configuration ignoresViewportScaleLimits]);
#endif
#if PLATFORM(MAC)
_impl = makeUnique<WebKit::WebViewImpl>(self, self, processPool, pageConfiguration.copyRef());
_page = &_impl->page();
if (_impl->isUsingUISideCompositing())
_contentView = adoptNS([[WKContentView alloc] initWithFrame:self.bounds page:_page.copyRef()]);
_impl->setAutomaticallyAdjustsContentInsets(true);
[self _setupScrollAndContentViews];
#endif
if (NSString *applicationNameForUserAgent = configuration.applicationNameForUserAgent)
_page->setApplicationNameForUserAgent(applicationNameForUserAgent);
_page->setApplicationNameForDesktopUserAgent(configuration._applicationNameForDesktopUserAgent);
_navigationState = makeUnique<WebKit::NavigationState>(self);
_page->setNavigationClient(_navigationState->createNavigationClient());
_uiDelegate = makeUnique<WebKit::UIDelegate>(self);
_page->setFindClient(makeUnique<WebKit::FindClient>(self));
_page->setDiagnosticLoggingClient(makeUnique<WebKit::DiagnosticLoggingClient>(self));
_iconLoadingDelegate = makeUnique<WebKit::IconLoadingDelegate>(self);
_resourceLoadDelegate = makeUnique<WebKit::ResourceLoadDelegate>(self);
for (auto& pair : pageConfiguration->urlSchemeHandlers())
_page->setURLSchemeHandlerForScheme(pair.value.get(), pair.key);
_page->setCocoaView(self);
[WebViewVisualIdentificationOverlay installForWebViewIfNeeded:self kind:@"WKWebView" deprecated:NO];
#if PLATFORM(IOS_FAMILY)
auto timeNow = MonotonicTime::now();
_timeOfRequestForVisibleContentRectUpdate = timeNow;
_timeOfLastVisibleContentRectUpdate = timeNow;
_timeOfFirstVisibleContentRectUpdateWithPendingCommit = timeNow;
#endif
}
- (void)_setupPageConfiguration:(Ref<API::PageConfiguration>&)pageConfiguration
{
pageConfiguration->setPreferences([_configuration preferences]->_preferences.get());
if (WKWebView *relatedWebView = [_configuration _relatedWebView])
pageConfiguration->setRelatedPage(relatedWebView->_page.get());
pageConfiguration->setUserContentController([_configuration userContentController]->_userContentControllerProxy.get());
pageConfiguration->setVisitedLinkStore([_configuration _visitedLinkStore]->_visitedLinkStore.get());
pageConfiguration->setWebsiteDataStore([_configuration websiteDataStore]->_websiteDataStore.get());
pageConfiguration->setDefaultWebsitePolicies([_configuration defaultWebpagePreferences]->_websitePolicies.get());
#if PLATFORM(MAC)
if (auto pageGroup = WebKit::toImpl([_configuration _pageGroup]))
pageConfiguration->setPageGroup(pageGroup);
else
#endif
{
NSString *groupIdentifier = [_configuration _groupIdentifier];
if (groupIdentifier.length)
pageConfiguration->setPageGroup(WebKit::WebPageGroup::create(groupIdentifier).ptr());
}
pageConfiguration->setAdditionalSupportedImageTypes(makeVector<String>([_configuration _additionalSupportedImageTypes]));
pageConfiguration->preferences()->setSuppressesIncrementalRendering(!![_configuration suppressesIncrementalRendering]);
pageConfiguration->preferences()->setShouldRespectImageOrientation(!![_configuration _respectsImageOrientation]);
#if !PLATFORM(MAC)
// FIXME: Expose WKPreferences._shouldPrintBackgrounds on iOS, adopt it in all iOS clients, and remove this and WKWebViewConfiguration._printsBackgrounds.
pageConfiguration->preferences()->setShouldPrintBackgrounds(!![_configuration _printsBackgrounds]);
#endif
pageConfiguration->preferences()->setIncrementalRenderingSuppressionTimeout([_configuration _incrementalRenderingSuppressionTimeout]);
pageConfiguration->preferences()->setJavaScriptMarkupEnabled(!![_configuration _allowsJavaScriptMarkup]);
pageConfiguration->preferences()->setShouldConvertPositionStyleOnCopy(!![_configuration _convertsPositionStyleOnCopy]);
pageConfiguration->preferences()->setHTTPEquivEnabled(!![_configuration _allowsMetaRefresh]);
pageConfiguration->preferences()->setAllowUniversalAccessFromFileURLs(!![_configuration _allowUniversalAccessFromFileURLs]);
pageConfiguration->preferences()->setAllowTopNavigationToDataURLs(!![_configuration _allowTopNavigationToDataURLs]);
pageConfiguration->setWaitsForPaintAfterViewDidMoveToWindow([_configuration _waitsForPaintAfterViewDidMoveToWindow]);
pageConfiguration->setDrawsBackground([_configuration _drawsBackground]);
pageConfiguration->setControlledByAutomation([_configuration _isControlledByAutomation]);
pageConfiguration->preferences()->setIncompleteImageBorderEnabled(!![_configuration _incompleteImageBorderEnabled]);
pageConfiguration->preferences()->setShouldDeferAsynchronousScriptsUntilAfterDocumentLoadOrFirstPaint(!![_configuration _shouldDeferAsynchronousScriptsUntilAfterDocumentLoad]);
pageConfiguration->preferences()->setShouldRestrictBaseURLSchemes(shouldRestrictBaseURLSchemes());
#if PLATFORM(MAC)
pageConfiguration->preferences()->setShowsURLsInToolTipsEnabled(!![_configuration _showsURLsInToolTips]);
pageConfiguration->preferences()->setServiceControlsEnabled(!![_configuration _serviceControlsEnabled]);
pageConfiguration->preferences()->setImageControlsEnabled(!![_configuration _imageControlsEnabled]);
pageConfiguration->preferences()->setUserInterfaceDirectionPolicy(convertUserInterfaceDirectionPolicy([_configuration userInterfaceDirectionPolicy]));
// We are in the View's initialization routine, so our client hasn't had time to set our user interface direction.
// Therefore, according to the docs[1], "this property contains the value reported by the app's userInterfaceLayoutDirection property."
// [1] http://developer.apple.com/library/mac/documentation/Cocoa/Reference/ApplicationKit/Classes/NSView_Class/index.html#//apple_ref/doc/uid/20000014-SW222
pageConfiguration->preferences()->setSystemLayoutDirection(convertSystemLayoutDirection(self.userInterfaceLayoutDirection));
#endif
#if PLATFORM(IOS_FAMILY)
pageConfiguration->preferences()->setAllowsInlineMediaPlayback(!![_configuration allowsInlineMediaPlayback]);
pageConfiguration->preferences()->setAllowsInlineMediaPlaybackAfterFullscreen(!![_configuration _allowsInlineMediaPlaybackAfterFullscreen]);
pageConfiguration->preferences()->setInlineMediaPlaybackRequiresPlaysInlineAttribute(!![_configuration _inlineMediaPlaybackRequiresPlaysInlineAttribute]);
pageConfiguration->preferences()->setAllowsPictureInPictureMediaPlayback(!![_configuration allowsPictureInPictureMediaPlayback] && shouldAllowPictureInPictureMediaPlayback());
pageConfiguration->preferences()->setUserInterfaceDirectionPolicy(static_cast<uint32_t>(WebCore::UserInterfaceDirectionPolicy::Content));
pageConfiguration->preferences()->setSystemLayoutDirection(static_cast<uint32_t>(WebCore::TextDirection::LTR));
pageConfiguration->preferences()->setAllowSettingAnyXHRHeaderFromFileURLs(shouldAllowSettingAnyXHRHeaderFromFileURLs());
pageConfiguration->preferences()->setShouldDecidePolicyBeforeLoadingQuickLookPreview(!![_configuration _shouldDecidePolicyBeforeLoadingQuickLookPreview]);
#if ENABLE(DEVICE_ORIENTATION)
pageConfiguration->preferences()->setDeviceOrientationPermissionAPIEnabled(linkedOnOrAfter(SDKVersion::FirstWithDeviceOrientationAndMotionPermissionAPI));
#endif
#if USE(SYSTEM_PREVIEW)
pageConfiguration->preferences()->setSystemPreviewEnabled(!![_configuration _systemPreviewEnabled]);
#endif
#endif // PLATFORM(IOS_FAMILY)
WKAudiovisualMediaTypes mediaTypesRequiringUserGesture = [_configuration mediaTypesRequiringUserActionForPlayback];
pageConfiguration->preferences()->setRequiresUserGestureForVideoPlayback((mediaTypesRequiringUserGesture & WKAudiovisualMediaTypeVideo) == WKAudiovisualMediaTypeVideo);
pageConfiguration->preferences()->setRequiresUserGestureForAudioPlayback(((mediaTypesRequiringUserGesture & WKAudiovisualMediaTypeAudio) == WKAudiovisualMediaTypeAudio));
pageConfiguration->preferences()->setRequiresUserGestureToLoadVideo(shouldRequireUserGestureToLoadVideo());
pageConfiguration->preferences()->setMainContentUserGestureOverrideEnabled(!![_configuration _mainContentUserGestureOverrideEnabled]);
pageConfiguration->preferences()->setInvisibleAutoplayNotPermitted(!![_configuration _invisibleAutoplayNotPermitted]);
pageConfiguration->preferences()->setMediaDataLoadsAutomatically(!![_configuration _mediaDataLoadsAutomatically]);
pageConfiguration->preferences()->setAttachmentElementEnabled(!![_configuration _attachmentElementEnabled]);
#if ENABLE(DATA_DETECTION) && PLATFORM(IOS_FAMILY)
pageConfiguration->preferences()->setDataDetectorTypes(fromWKDataDetectorTypes([_configuration dataDetectorTypes]).toRaw());
#endif
#if ENABLE(WIRELESS_PLAYBACK_TARGET)
pageConfiguration->preferences()->setAllowsAirPlayForMediaPlayback(!![_configuration allowsAirPlayForMediaPlayback]);
#endif
#if ENABLE(APPLE_PAY)
pageConfiguration->preferences()->setApplePayEnabled(!![_configuration _applePayEnabled]);
#endif
pageConfiguration->preferences()->setNeedsStorageAccessFromFileURLsQuirk(!![_configuration _needsStorageAccessFromFileURLsQuirk]);
pageConfiguration->preferences()->setMediaContentTypesRequiringHardwareSupport(String([_configuration _mediaContentTypesRequiringHardwareSupport]));
pageConfiguration->preferences()->setAllowMediaContentTypesRequiringHardwareSupportAsFallback(!![_configuration _allowMediaContentTypesRequiringHardwareSupportAsFallback]);
if (!pageConfiguration->preferences()->mediaDevicesEnabled())
pageConfiguration->preferences()->setMediaDevicesEnabled(!![_configuration _mediaCaptureEnabled]);
pageConfiguration->preferences()->setColorFilterEnabled(!![_configuration _colorFilterEnabled]);
pageConfiguration->preferences()->setUndoManagerAPIEnabled(!![_configuration _undoManagerAPIEnabled]);
#if ENABLE(APP_HIGHLIGHTS)
pageConfiguration->preferences()->setAppHighlightsEnabled(!![_configuration _appHighlightsEnabled]);
#endif
#if ENABLE(LEGACY_ENCRYPTED_MEDIA)
pageConfiguration->preferences()->setLegacyEncryptedMediaAPIEnabled(!![_configuration _legacyEncryptedMediaAPIEnabled]);
#endif
#if PLATFORM(IOS_FAMILY) && ENABLE(SERVICE_WORKER)
bool hasServiceWorkerEntitlement = (WTF::processHasEntitlement("com.apple.developer.WebKit.ServiceWorkers") || WTF::processHasEntitlement("com.apple.developer.web-browser")) && ![_configuration preferences]._serviceWorkerEntitlementDisabledForTesting;
if (!hasServiceWorkerEntitlement && ![_configuration limitsNavigationsToAppBoundDomains])
pageConfiguration->preferences()->setServiceWorkersEnabled(false);
pageConfiguration->preferences()->setServiceWorkerEntitlementDisabledForTesting(!![_configuration preferences]._serviceWorkerEntitlementDisabledForTesting);
#endif
pageConfiguration->preferences()->setSampledPageTopColorMaxDifference([_configuration _sampledPageTopColorMaxDifference]);
pageConfiguration->preferences()->setSampledPageTopColorMinHeight([_configuration _sampledPageTopColorMinHeight]);
if (!linkedOnOrAfter(SDKVersion::FirstWhereSiteSpecificQuirksAreEnabledByDefault))
pageConfiguration->preferences()->setNeedsSiteSpecificQuirks(false);
#if PLATFORM(IOS_FAMILY)
pageConfiguration->preferences()->setAlternateFormControlDesignEnabled(WebKit::defaultAlternateFormControlDesignEnabled());
#endif
}
- (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration
{
if (!(self = [super initWithFrame:frame]))
return nil;
[self _initializeWithConfiguration:configuration];
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
if (!(self = [super initWithCoder:coder]))
return nil;
WKWebViewConfiguration *configuration = [coder decodeObjectOfClass:[WKWebViewConfiguration class] forKey:@"configuration"];
[self _initializeWithConfiguration:configuration];
self.allowsBackForwardNavigationGestures = [coder decodeBoolForKey:@"allowsBackForwardNavigationGestures"];
self.customUserAgent = [coder decodeObjectOfClass:[NSString class] forKey:@"customUserAgent"];
self.allowsLinkPreview = [coder decodeBoolForKey:@"allowsLinkPreview"];
#if PLATFORM(MAC)
self.allowsMagnification = [coder decodeBoolForKey:@"allowsMagnification"];
self.magnification = [coder decodeDoubleForKey:@"magnification"];
#endif
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder
{
[super encodeWithCoder:coder];
[coder encodeObject:_configuration.get() forKey:@"configuration"];
[coder encodeBool:self.allowsBackForwardNavigationGestures forKey:@"allowsBackForwardNavigationGestures"];
[coder encodeObject:self.customUserAgent forKey:@"customUserAgent"];
[coder encodeBool:self.allowsLinkPreview forKey:@"allowsLinkPreview"];
#if PLATFORM(MAC)
[coder encodeBool:self.allowsMagnification forKey:@"allowsMagnification"];
[coder encodeDouble:self.magnification forKey:@"magnification"];
#endif
}
- (void)dealloc
{
if (WebCoreObjCScheduleDeallocateOnMainRunLoop(WKWebView.class, self))
return;
#if PLATFORM(MAC)
[_textFinderClient willDestroyView:self];
#endif
#if PLATFORM(IOS_FAMILY)
[_contentView _webViewDestroyed];
if (_page && _remoteObjectRegistry)
_page->process().processPool().removeMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), _page->identifier());
#endif
if (_page)
_page->close();
#if PLATFORM(IOS_FAMILY)
[_remoteObjectRegistry _invalidate];
[[_configuration _contentProviderRegistry] removePage:*_page];
[[NSNotificationCenter defaultCenter] removeObserver:self];
[_scrollView setInternalDelegate:nil];
auto notificationName = adoptNS([[NSString alloc] initWithCString:kGSEventHardwareKeyboardAvailabilityChangedNotification encoding:NSUTF8StringEncoding]);
CFNotificationCenterRemoveObserver(CFNotificationCenterGetDarwinNotifyCenter(), (__bridge const void *)(self), (__bridge CFStringRef)notificationName.get(), nullptr);
#endif
[super dealloc];
}
- (id)valueForUndefinedKey:(NSString *)key
{
if ([key isEqualToString:@"serverTrust"])
return (__bridge id)[self serverTrust];
return [super valueForUndefinedKey:key];
}
#pragma mark - macOS/iOS API
- (WKWebViewConfiguration *)configuration
{
return adoptNS([_configuration copy]).autorelease();
}
- (WKBackForwardList *)backForwardList
{
return wrapper(_page->backForwardList());
}
- (id <WKNavigationDelegate>)navigationDelegate
{
return _navigationState->navigationDelegate().autorelease();
}
- (void)setNavigationDelegate:(id <WKNavigationDelegate>)navigationDelegate
{
_page->setNavigationClient(_navigationState->createNavigationClient());
_navigationState->setNavigationDelegate(navigationDelegate);
}
- (id <WKUIDelegate>)UIDelegate
{
return _uiDelegate->delegate().autorelease();
}
- (void)setUIDelegate:(id<WKUIDelegate>)UIDelegate
{
_uiDelegate->setDelegate(UIDelegate);
#if ENABLE(CONTEXT_MENUS)
_page->setContextMenuClient(_uiDelegate->createContextMenuClient());
#endif
_page->setUIClient(_uiDelegate->createUIClient());
}
- (id <_WKIconLoadingDelegate>)_iconLoadingDelegate
{
return _iconLoadingDelegate->delegate().autorelease();
}
- (void)_setIconLoadingDelegate:(id<_WKIconLoadingDelegate>)iconLoadingDelegate
{
_page->setIconLoadingClient(_iconLoadingDelegate->createIconLoadingClient());
_iconLoadingDelegate->setDelegate(iconLoadingDelegate);
}
- (id <_WKResourceLoadDelegate>)_resourceLoadDelegate
{
return _resourceLoadDelegate->delegate().autorelease();
}
- (void)_setResourceLoadDelegate:(id<_WKResourceLoadDelegate>)delegate
{
if (delegate) {
_page->setResourceLoadClient(_resourceLoadDelegate->createResourceLoadClient());
_resourceLoadDelegate->setDelegate(delegate);
} else {
_page->setResourceLoadClient(nullptr);
_resourceLoadDelegate->setDelegate(nil);
}
}
- (WKNavigation *)loadRequest:(NSURLRequest *)request
{
THROW_IF_SUSPENDED;
if (_page->isServiceWorkerPage())
[NSException raise:NSInternalInconsistencyException format:@"The WKWebView was used to load a service worker"];
return wrapper(_page->loadRequest(request));
}
- (WKNavigation *)loadFileURL:(NSURL *)URL allowingReadAccessToURL:(NSURL *)readAccessURL
{
THROW_IF_SUSPENDED;
if (_page->isServiceWorkerPage())
[NSException raise:NSInternalInconsistencyException format:@"The WKWebView was used to load a service worker"];
if (![URL isFileURL])
[NSException raise:NSInvalidArgumentException format:@"%@ is not a file URL", URL];
if (![readAccessURL isFileURL])
[NSException raise:NSInvalidArgumentException format:@"%@ is not a file URL", readAccessURL];
return wrapper(_page->loadFile(URL.absoluteString, readAccessURL.absoluteString));
}
- (WKNavigation *)loadHTMLString:(NSString *)string baseURL:(NSURL *)baseURL
{
THROW_IF_SUSPENDED;
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
return [self loadData:data MIMEType:@"text/html" characterEncodingName:@"UTF-8" baseURL:baseURL];
}
- (WKNavigation *)loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL
{
THROW_IF_SUSPENDED;
if (_page->isServiceWorkerPage())
[NSException raise:NSInternalInconsistencyException format:@"The WKWebView was used to load a service worker"];
return wrapper(_page->loadData({ static_cast<const uint8_t*>(data.bytes), data.length }, MIMEType, characterEncodingName, baseURL.absoluteString));
}
- (void)startDownloadUsingRequest:(NSURLRequest *)request completionHandler:(void(^)(WKDownload *))completionHandler
{
THROW_IF_SUSPENDED;
_page->downloadRequest(request, [completionHandler = makeBlockPtr(completionHandler)] (auto* download) {
if (download)
completionHandler(wrapper(download));
else
ASSERT_NOT_REACHED();
});
}
- (void)resumeDownloadFromResumeData:(NSData *)resumeData completionHandler:(void(^)(WKDownload *))completionHandler
{
THROW_IF_SUSPENDED;
auto unarchiver = adoptNS([[NSKeyedUnarchiver alloc] initForReadingFromData:resumeData error:nil]);
[unarchiver setDecodingFailurePolicy:NSDecodingFailurePolicyRaiseException];
NSDictionary *dictionary = [unarchiver decodeObjectOfClasses:[NSSet setWithObjects:[NSDictionary class], [NSArray class], [NSString class], [NSNumber class], [NSData class], [NSURL class], [NSURLRequest class], nil] forKey:@"NSKeyedArchiveRootObjectKey"];
[unarchiver finishDecoding];
NSString *path = [dictionary objectForKey:@"NSURLSessionResumeInfoLocalPath"];
if (!path)
[NSException raise:NSInvalidArgumentException format:@"Invalid resume data"];
_page->resumeDownload(API::Data::createWithoutCopying(resumeData), path, [completionHandler = makeBlockPtr(completionHandler)] (auto* download) {
if (download)
completionHandler(wrapper(download));
else
ASSERT_NOT_REACHED();
});
}
- (WKNavigation *)goToBackForwardListItem:(WKBackForwardListItem *)item
{
THROW_IF_SUSPENDED;
return wrapper(_page->goToBackForwardItem(item._item));
}
- (NSString *)title
{
return _page->pageLoadState().title();
}
- (NSURL *)URL
{
return [NSURL _web_URLWithWTFString:_page->pageLoadState().activeURL()];
}
- (NSURL *)_resourceDirectoryURL
{
return _page->currentResourceDirectoryURL();
}
- (BOOL)isLoading
{
return _page->pageLoadState().isLoading();
}
- (double)estimatedProgress
{
return _page->pageLoadState().estimatedProgress();
}
- (BOOL)hasOnlySecureContent
{
return _page->pageLoadState().hasOnlySecureContent();
}
- (SecTrustRef)serverTrust
{
#if HAVE(SEC_TRUST_SERIALIZATION)
auto certificateInfo = _page->pageLoadState().certificateInfo();
if (!certificateInfo)
return nil;
return certificateInfo->certificateInfo().trust();
#else
return nil;
#endif
}
- (BOOL)canGoBack
{
return _page->pageLoadState().canGoBack();
}
- (BOOL)canGoForward
{
return _page->pageLoadState().canGoForward();
}
- (WKNavigation *)goBack
{
THROW_IF_SUSPENDED;
if (self._safeBrowsingWarning)
return [self reload];
return wrapper(_page->goBack());
}
- (WKNavigation *)goForward
{
THROW_IF_SUSPENDED;
return wrapper(_page->goForward());
}
- (WKNavigation *)reload
{
THROW_IF_SUSPENDED;
OptionSet<WebCore::ReloadOption> reloadOptions;
if (linkedOnOrAfter(SDKVersion::FirstWithExpiredOnlyReloadBehavior))
reloadOptions.add(WebCore::ReloadOption::ExpiredOnly);
return wrapper(_page->reload(reloadOptions));
}
- (WKNavigation *)reloadFromOrigin
{
THROW_IF_SUSPENDED;
return wrapper(_page->reload(WebCore::ReloadOption::FromOrigin));
}
- (void)stopLoading
{
THROW_IF_SUSPENDED;
_page->stopLoading();
}
- (void)evaluateJavaScript:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler
{
THROW_IF_SUSPENDED;
[self _evaluateJavaScript:javaScriptString asAsyncFunction:NO withSourceURL:nil withArguments:nil forceUserGesture:YES inFrame:nil inWorld:WKContentWorld.pageWorld completionHandler:completionHandler];
}
- (void)evaluateJavaScript:(NSString *)javaScriptString inFrame:(WKFrameInfo *)frame inContentWorld:(WKContentWorld *)contentWorld completionHandler:(void (^)(id, NSError *))completionHandler
{
THROW_IF_SUSPENDED;
[self _evaluateJavaScript:javaScriptString asAsyncFunction:NO withSourceURL:nil withArguments:nil forceUserGesture:YES inFrame:frame inWorld:contentWorld completionHandler:completionHandler];
}
- (void)callAsyncJavaScript:(NSString *)javaScriptString arguments:(NSDictionary<NSString *, id> *)arguments inFrame:(WKFrameInfo *)frame inContentWorld:(WKContentWorld *)contentWorld completionHandler:(void (^)(id, NSError *error))completionHandler
{
THROW_IF_SUSPENDED;
[self _evaluateJavaScript:javaScriptString asAsyncFunction:YES withSourceURL:nil withArguments:arguments forceUserGesture:YES inFrame:frame inWorld:contentWorld completionHandler:completionHandler];
}
static bool validateArgument(id argument)
{
if ([argument isKindOfClass:[NSString class]] || [argument isKindOfClass:[NSNumber class]] || [argument isKindOfClass:[NSDate class]] || [argument isKindOfClass:[NSNull class]])
return true;
if ([argument isKindOfClass:[NSArray class]]) {
__block BOOL valid = true;
[argument enumerateObjectsUsingBlock:^(id object, NSUInteger, BOOL *stop) {
if (!validateArgument(object)) {
valid = false;
*stop = YES;
}
}];
return valid;
}
if ([argument isKindOfClass:[NSDictionary class]]) {
__block bool valid = true;
[argument enumerateKeysAndObjectsUsingBlock:^(id key, id value, BOOL *stop) {
if (!validateArgument(key) || !validateArgument(value)) {
valid = false;
*stop = YES;
}
}];
return valid;
}
return false;
}
- (void)closeAllMediaPresentations
{
[self closeAllMediaPresentationsWithCompletionHandler:^{ }];
}
- (void)closeAllMediaPresentations:(void (^)(void))completionHandler
{
[self closeAllMediaPresentationsWithCompletionHandler:completionHandler];
}
- (void)closeAllMediaPresentationsWithCompletionHandler:(void (^)(void))completionHandler
{
THROW_IF_SUSPENDED;
auto callbackAggregator = CallbackAggregator::create(WTFMove(completionHandler));
#if ENABLE(FULLSCREEN_API)
if (auto videoFullscreenManager = _page->videoFullscreenManager()) {
videoFullscreenManager->forEachSession([callbackAggregator] (auto& model, auto& interface) mutable {
model.requestCloseAllMediaPresentations(false, [callbackAggregator] { });
});
}
if (auto fullScreenManager = _page->fullScreenManager(); fullScreenManager && fullScreenManager->isFullScreen())
fullScreenManager->closeWithCallback([callbackAggregator] { });
#endif
}
- (void)pauseAllMediaPlayback:(void (^)(void))completionHandler
{
[self pauseAllMediaPlaybackWithCompletionHandler:completionHandler];
}
- (void)pauseAllMediaPlaybackWithCompletionHandler:(void (^)(void))completionHandler
{
THROW_IF_SUSPENDED;
if (!completionHandler) {
_page->pauseAllMediaPlayback([] { });
return;
}
_page->pauseAllMediaPlayback(makeBlockPtr(completionHandler));
}
- (void)resumeAllMediaPlayback:(void (^)(void))completionHandler
{
[self setAllMediaPlaybackSuspended:NO completionHandler:completionHandler];
}
- (void)suspendAllMediaPlayback:(void (^)(void))completionHandler
{
[self setAllMediaPlaybackSuspended:YES completionHandler:completionHandler];
}
- (void)setAllMediaPlaybackSuspended:(BOOL)suspended completionHandler:(void (^)(void))completionHandler
{
THROW_IF_SUSPENDED;
if (!completionHandler)
completionHandler = [] { };
if (suspended) {
_page->suspendAllMediaPlayback(makeBlockPtr(completionHandler));
return;
}
_page->resumeAllMediaPlayback(makeBlockPtr(completionHandler));
}
static WKMediaPlaybackState toWKMediaPlaybackState(WebKit::MediaPlaybackState mediaPlaybackState)
{
switch (mediaPlaybackState) {
case WebKit::MediaPlaybackState::NoMediaPlayback:
return WKMediaPlaybackStateNone;
case WebKit::MediaPlaybackState::MediaPlaybackPlaying:
return WKMediaPlaybackStatePlaying;
case WebKit::MediaPlaybackState::MediaPlaybackPaused:
return WKMediaPlaybackStatePaused;
case WebKit::MediaPlaybackState::MediaPlaybackSuspended:
return WKMediaPlaybackStateSuspended;
default:
break;
}
ASSERT_NOT_REACHED();
return WKMediaPlaybackStateNone;
}
- (void)requestMediaPlaybackState:(void (^)(WKMediaPlaybackState))completionHandler
{
[self requestMediaPlaybackStateWithCompletionHandler:completionHandler];
}
- (void)requestMediaPlaybackStateWithCompletionHandler:(void (^)(WKMediaPlaybackState))completionHandler
{
THROW_IF_SUSPENDED;
if (!completionHandler)
return;
return _page->requestMediaPlaybackState([completionHandler = makeBlockPtr(completionHandler)] (auto&& mediaPlaybackState) {
completionHandler(toWKMediaPlaybackState(mediaPlaybackState));
});
}
- (WKMediaCaptureState)cameraCaptureState
{
auto state = _page->reportedMediaState();
if (state & WebCore::MediaProducerMediaState::HasActiveVideoCaptureDevice)
return WKMediaCaptureStateActive;
if (state & WebCore::MediaProducerMediaState::HasMutedVideoCaptureDevice)
return WKMediaCaptureStateMuted;
return WKMediaCaptureStateNone;
}
- (WKMediaCaptureState)microphoneCaptureState
{
auto state = _page->reportedMediaState();
if (state & WebCore::MediaProducerMediaState::HasActiveAudioCaptureDevice)
return WKMediaCaptureStateActive;
if (state & WebCore::MediaProducerMediaState::HasMutedAudioCaptureDevice)
return WKMediaCaptureStateMuted;
return WKMediaCaptureStateNone;
}
- (void)setMicrophoneCaptureState:(WKMediaCaptureState)state completionHandler:(void (^)(void))completionHandler
{
THROW_IF_SUSPENDED;
if (!completionHandler)
completionHandler = [] { };
if (state == WKMediaCaptureStateNone) {
_page->stopMediaCapture(WebCore::MediaProducerMediaCaptureKind::Audio, [completionHandler = makeBlockPtr(completionHandler)] {
completionHandler();
});
return;
}
auto mutedState = _page->mutedStateFlags();
if (state == WKMediaCaptureStateActive)
mutedState.remove(WebCore::MediaProducerMutedState::AudioCaptureIsMuted);
else if (state == WKMediaCaptureStateMuted)
mutedState.add(WebCore::MediaProducerMutedState::AudioCaptureIsMuted);
_page->setMuted(mutedState, [completionHandler = makeBlockPtr(completionHandler)] {
completionHandler();
});
}
- (void)setCameraCaptureState:(WKMediaCaptureState)state completionHandler:(void (^)(void))completionHandler
{
THROW_IF_SUSPENDED;
if (!completionHandler)
completionHandler = [] { };
if (state == WKMediaCaptureStateNone) {
_page->stopMediaCapture(WebCore::MediaProducerMediaCaptureKind::Video, [completionHandler = makeBlockPtr(completionHandler)] {
completionHandler();
});
return;
}
auto mutedState = _page->mutedStateFlags();
if (state == WKMediaCaptureStateActive)
mutedState.remove(WebCore::MediaProducerMutedState::VideoCaptureIsMuted);
else if (state == WKMediaCaptureStateMuted)
mutedState.add(WebCore::MediaProducerMutedState::VideoCaptureIsMuted);
_page->setMuted(mutedState, [completionHandler = makeBlockPtr(completionHandler)] {
completionHandler();
});
}
- (void)_evaluateJavaScript:(NSString *)javaScriptString asAsyncFunction:(BOOL)asAsyncFunction withSourceURL:(NSURL *)sourceURL withArguments:(NSDictionary<NSString *, id> *)arguments forceUserGesture:(BOOL)forceUserGesture inFrame:(WKFrameInfo *)frame inWorld:(WKContentWorld *)world completionHandler:(void (^)(id, NSError *))completionHandler
{
THROW_IF_SUSPENDED;
auto handler = adoptNS([completionHandler copy]);
std::optional<WebCore::ArgumentWireBytesMap> argumentsMap;
if (asAsyncFunction)
argumentsMap = WebCore::ArgumentWireBytesMap { };
NSString *errorMessage = nil;
for (id key in arguments) {
id value = [arguments objectForKey:key];
auto serializedValue = API::SerializedScriptValue::createFromNSObject(value);
if (!serializedValue) {
errorMessage = @"Function argument values must be one of the following types, or contain only the following types: NSNumber, NSNull, NSDate, NSString, NSArray, and NSDictionary";
break;
}
argumentsMap->set(key, serializedValue->internalRepresentation().toWireBytes());
}
if (errorMessage && handler) {
RetainPtr<NSMutableDictionary> userInfo = adoptNS([[NSMutableDictionary alloc] init]);
[userInfo setObject:localizedDescriptionForErrorCode(WKErrorJavaScriptExceptionOccurred) forKey:NSLocalizedDescriptionKey];
[userInfo setObject:errorMessage forKey:_WKJavaScriptExceptionMessageErrorKey];
auto error = adoptNS([[NSError alloc] initWithDomain:WKErrorDomain code:WKErrorJavaScriptExceptionOccurred userInfo:userInfo.get()]);
RunLoop::main().dispatch([handler, error] {
auto rawHandler = (void (^)(id, NSError *))handler.get();
rawHandler(nil, error.get());
});
return;
}
std::optional<WebCore::FrameIdentifier> frameID;
if (frame) {
if (uint64_t identifier = frame._handle.frameID)
frameID = makeObjectIdentifier<WebCore::FrameIdentifierType>(identifier);
}
_page->runJavaScriptInFrameInScriptWorld({ javaScriptString, sourceURL, !!asAsyncFunction, WTFMove(argumentsMap), !!forceUserGesture }, frameID, *world->_contentWorld.get(), [handler] (auto&& result) {
if (!handler)
return;
auto rawHandler = (void (^)(id, NSError *))handler.get();
if (!result.has_value()) {
rawHandler(nil, nsErrorFromExceptionDetails(result.error()).get());
return;
}
if (!result.value()) {
rawHandler(nil, createNSError(WKErrorJavaScriptResultTypeIsUnsupported).get());
return;
}
id body = API::SerializedScriptValue::deserialize(result.value()->internalRepresentation(), 0);
rawHandler(body, nil);
});
}
- (void)takeSnapshotWithConfiguration:(WKSnapshotConfiguration *)snapshotConfiguration completionHandler:(void(^)(CocoaImage *, NSError *))completionHandler
{
THROW_IF_SUSPENDED;
constexpr bool snapshotFailedTraceValue = false;
tracePoint(TakeSnapshotStart);
CGRect rectInViewCoordinates = snapshotConfiguration && !CGRectIsNull(snapshotConfiguration.rect) ? snapshotConfiguration.rect : self.bounds;
CGFloat snapshotWidth;
if (snapshotConfiguration)
snapshotWidth = snapshotConfiguration.snapshotWidth.doubleValue ?: rectInViewCoordinates.size.width;
else
snapshotWidth = self.bounds.size.width;
auto handler = makeBlockPtr(completionHandler);
if (CGRectIsEmpty(rectInViewCoordinates) || !snapshotWidth) {
RunLoop::main().dispatch([handler = WTFMove(handler)] {
#if USE(APPKIT)
auto image = adoptNS([[NSImage alloc] initWithSize:NSMakeSize(0, 0)]);
#else
auto image = adoptNS([[UIImage alloc] init]);
#endif
handler(image.get(), nil);
});
return;
}
#if USE(APPKIT)
CGFloat imageScale = snapshotWidth / rectInViewCoordinates.size.width;
CGFloat imageHeight = imageScale * rectInViewCoordinates.size.height;
// Need to scale by device scale factor or the image will be distorted.
CGFloat deviceScale = _page->deviceScaleFactor();
WebCore::IntSize bitmapSize(snapshotWidth, imageHeight);
bitmapSize.scale(deviceScale, deviceScale);
// Software snapshot will not capture elements rendered with hardware acceleration (WebGL, video, etc).
// This code doesn't consider snapshotConfiguration.afterScreenUpdates since the software snapshot always
// contains recent updates. If we ever have a UI-side snapshot mechanism on macOS, we will need to factor
// in snapshotConfiguration.afterScreenUpdates at that time.
_page->takeSnapshot(WebCore::enclosingIntRect(rectInViewCoordinates), bitmapSize, WebKit::SnapshotOptionsInViewCoordinates, [handler, snapshotWidth, imageHeight](const WebKit::ShareableBitmap::Handle& imageHandle) {
if (imageHandle.isNull()) {
tracePoint(TakeSnapshotEnd, snapshotFailedTraceValue);
handler(nil, createNSError(WKErrorUnknown).get());
return;
}
auto bitmap = WebKit::ShareableBitmap::create(imageHandle, WebKit::SharedMemory::Protection::ReadOnly);
RetainPtr<CGImageRef> cgImage = bitmap ? bitmap->makeCGImage() : nullptr;
auto image = adoptNS([[NSImage alloc] initWithCGImage:cgImage.get() size:NSMakeSize(snapshotWidth, imageHeight)]);
tracePoint(TakeSnapshotEnd, true);
handler(image.get(), nil);
});
#else
auto useIntrinsicDeviceScaleFactor = [[_customContentView class] web_requiresCustomSnapshotting];
CGFloat deviceScale = useIntrinsicDeviceScaleFactor ? UIScreen.mainScreen.scale : _page->deviceScaleFactor();
CGFloat imageWidth = useIntrinsicDeviceScaleFactor ? snapshotWidth : snapshotWidth * deviceScale;
RetainPtr<WKWebView> strongSelf = self;
BOOL afterScreenUpdates = snapshotConfiguration && snapshotConfiguration.afterScreenUpdates;
auto callSnapshotRect = [strongSelf, afterScreenUpdates, rectInViewCoordinates, imageWidth, deviceScale, handler] {
[strongSelf _snapshotRectAfterScreenUpdates:afterScreenUpdates rectInViewCoordinates:rectInViewCoordinates intoImageOfWidth:imageWidth completionHandler:[strongSelf, handler, deviceScale](CGImageRef snapshotImage) {
RetainPtr<NSError> error;
RetainPtr<UIImage> image;
if (!snapshotImage)
error = createNSError(WKErrorUnknown);
else
image = adoptNS([[UIImage alloc] initWithCGImage:snapshotImage scale:deviceScale orientation:UIImageOrientationUp]);
tracePoint(TakeSnapshotEnd, !!snapshotImage);
handler(image.get(), error.get());
}];
};
if ((snapshotConfiguration && !snapshotConfiguration.afterScreenUpdates) || !linkedOnOrAfter(SDKVersion::FirstWithSnapshotAfterScreenUpdates)) {
callSnapshotRect();
return;
}
_page->callAfterNextPresentationUpdate([callSnapshotRect = WTFMove(callSnapshotRect), handler](WebKit::CallbackBase::Error error) mutable {
if (error != WebKit::CallbackBase::Error::None) {
tracePoint(TakeSnapshotEnd, snapshotFailedTraceValue);
handler(nil, createNSError(WKErrorUnknown).get());
return;
}
// Create an implicit transaction to ensure a commit will happen next.
[CATransaction activate];
// Wait for the next flush to ensure the latest IOSurfaces are pushed to backboardd before taking the snapshot.
[CATransaction addCommitHandler:[callSnapshotRect = WTFMove(callSnapshotRect)]() mutable {
// callSnapshotRect() calls the client callback which may call directly or indirectly addCommitHandler.
// It is prohibited by CA to add a commit handler while processing a registered commit handler.
// So postpone calling callSnapshotRect() till CATransaction processes its commit handlers.
dispatch_async(dispatch_get_main_queue(), [callSnapshotRect = WTFMove(callSnapshotRect)] {
callSnapshotRect();
});
} forPhase:kCATransactionPhasePostCommit];
});
#endif
}
- (void)setAllowsBackForwardNavigationGestures:(BOOL)allowsBackForwardNavigationGestures
{
THROW_IF_SUSPENDED;
#if PLATFORM(MAC)
_impl->setAllowsBackForwardNavigationGestures(allowsBackForwardNavigationGestures);
#elif PLATFORM(IOS_FAMILY)
if (_allowsBackForwardNavigationGestures == allowsBackForwardNavigationGestures)
return;
_allowsBackForwardNavigationGestures = allowsBackForwardNavigationGestures;
if (allowsBackForwardNavigationGestures && !_gestureController) {
_gestureController = makeUnique<WebKit::ViewGestureController>(*_page);
_gestureController->installSwipeHandler(self, [self scrollView]);
if (WKWebView *alternateWebView = [_configuration _alternateWebViewForNavigationGestures])
_gestureController->setAlternateBackForwardListSourcePage(alternateWebView->_page.get());
}
if (_gestureController)
_gestureController->setSwipeGestureEnabled(allowsBackForwardNavigationGestures);
_page->setShouldRecordNavigationSnapshots(allowsBackForwardNavigationGestures);
#endif
}
- (BOOL)allowsBackForwardNavigationGestures
{
#if PLATFORM(MAC)
return _impl->allowsBackForwardNavigationGestures();
#elif PLATFORM(IOS_FAMILY)
return _allowsBackForwardNavigationGestures;
#endif
}
- (NSString *)customUserAgent
{
return _page->customUserAgent();
}
- (void)setCustomUserAgent:(NSString *)customUserAgent
{
THROW_IF_SUSPENDED;
_page->setCustomUserAgent(customUserAgent);
}
- (BOOL)allowsLinkPreview
{
#if PLATFORM(MAC)
return _impl->allowsLinkPreview();
#elif PLATFORM(IOS_FAMILY)
return _allowsLinkPreview;
#endif
}
- (void)setAllowsLinkPreview:(BOOL)allowsLinkPreview
{
THROW_IF_SUSPENDED;
#if PLATFORM(MAC)
_impl->setAllowsLinkPreview(allowsLinkPreview);
return;
#elif PLATFORM(IOS_FAMILY)
if (_allowsLinkPreview == allowsLinkPreview)
return;
_allowsLinkPreview = allowsLinkPreview;
#if HAVE(LINK_PREVIEW)
if (_allowsLinkPreview)
[_contentView _registerPreview];
else
[_contentView _unregisterPreview];
[_contentView _didChangeLinkPreviewAvailability];
#endif // HAVE(LINK_PREVIEW)
#endif // PLATFORM(IOS_FAMILY)
}
- (void)setPageZoom:(CGFloat)pageZoom
{
THROW_IF_SUSPENDED;
_page->setPageZoomFactor(pageZoom);
}
- (CGFloat)pageZoom
{
return _page->pageZoomFactor();
}
inline OptionSet<WebKit::FindOptions> toFindOptions(WKFindConfiguration *configuration)
{
OptionSet<WebKit::FindOptions> findOptions;
if (!configuration.caseSensitive)
findOptions.add(WebKit::FindOptions::CaseInsensitive);
if (configuration.backwards)
findOptions.add(WebKit::FindOptions::Backwards);
if (configuration.wraps)
findOptions.add(WebKit::FindOptions::WrapAround);
return findOptions;
}
- (void)findString:(NSString *)string withConfiguration:(WKFindConfiguration *)configuration completionHandler:(void (^)(WKFindResult *result))completionHandler
{
THROW_IF_SUSPENDED;
if (!string.length) {
completionHandler(adoptNS([[WKFindResult alloc] _initWithMatchFound:NO]).get());
return;
}
_page->findString(string, toFindOptions(configuration), 1, [handler = makeBlockPtr(completionHandler)](bool found) {
handler(adoptNS([[WKFindResult alloc] _initWithMatchFound:found]).get());
});
}
+ (BOOL)handlesURLScheme:(NSString *)urlScheme
{
return WebCore::LegacySchemeRegistry::isBuiltinScheme(urlScheme);
}
- (void)setMediaType:(NSString *)mediaStyle
{
THROW_IF_SUSPENDED;
_page->setOverriddenMediaType(mediaStyle);
}
- (NSString *)mediaType
{
return _page->overriddenMediaType().isNull() ? nil : (NSString *)_page->overriddenMediaType();
}
- (id)interactionState
{
return WebKit::encodeSessionState(_page->sessionState()).autorelease();
}
- (void)setInteractionState:(id)interactionState
{
THROW_IF_SUSPENDED;
if (![(id)interactionState isKindOfClass:[NSData class]])
return;
WebKit::SessionState sessionState;
if (!WebKit::decodeSessionState((NSData *)(interactionState), sessionState))
return;
_page->restoreFromSessionState(sessionState, true);
}
#pragma mark - iOS API
#if PLATFORM(IOS_FAMILY)
- (UIScrollView *)scrollView
{
return _scrollView.get();
}
#endif // PLATFORM(IOS_FAMILY)
#pragma mark - macOS API
#if PLATFORM(MAC)
- (void)setAllowsMagnification:(BOOL)allowsMagnification
{
THROW_IF_SUSPENDED;
_impl->setAllowsMagnification(allowsMagnification);
}
- (BOOL)allowsMagnification
{
return _impl->allowsMagnification();
}
- (void)setMagnification:(double)magnification centeredAtPoint:(NSPoint)point
{
THROW_IF_SUSPENDED;
_impl->setMagnification(magnification, NSPointToCGPoint(point));
}
- (void)setMagnification:(double)magnification
{
THROW_IF_SUSPENDED;
_impl->setMagnification(magnification);
}
- (double)magnification
{
return _impl->magnification();
}
- (NSPrintOperation *)printOperationWithPrintInfo:(NSPrintInfo *)printInfo
{
THROW_IF_SUSPENDED;
if (auto webFrameProxy = _page->mainFrame())
return _impl->printOperationWithPrintInfo(printInfo, *webFrameProxy);
return nil;
}
#endif // PLATFORM(MAC)
#pragma mark - macOS/iOS internal
- (void)_showSafeBrowsingWarning:(const WebKit::SafeBrowsingWarning&)warning completionHandler:(CompletionHandler<void(std::variant<WebKit::ContinueUnsafeLoad, URL>&&)>&&)completionHandler
{
_safeBrowsingWarning = adoptNS([[WKSafeBrowsingWarning alloc] initWithFrame:self.bounds safeBrowsingWarning:warning completionHandler:[weakSelf = WeakObjCPtr<WKWebView>(self), completionHandler = WTFMove(completionHandler)] (auto&& result) mutable {
completionHandler(WTFMove(result));
auto strongSelf = weakSelf.get();
if (!strongSelf)
return;
bool navigatesFrame = WTF::switchOn(result,
[] (WebKit::ContinueUnsafeLoad continueUnsafeLoad) { return continueUnsafeLoad == WebKit::ContinueUnsafeLoad::Yes; },
[] (const URL&) { return true; }
);
bool forMainFrameNavigation = [strongSelf->_safeBrowsingWarning forMainFrameNavigation];
if (navigatesFrame && forMainFrameNavigation) {
// The safe browsing warning will be hidden once the next page is shown.
return;
}
if (!navigatesFrame && strongSelf->_safeBrowsingWarning && !forMainFrameNavigation) {
strongSelf->_page->goBack();
return;
}
[std::exchange(strongSelf->_safeBrowsingWarning, nullptr) removeFromSuperview];
}]);
[self addSubview:_safeBrowsingWarning.get()];
}
- (void)_clearSafeBrowsingWarning
{
[std::exchange(_safeBrowsingWarning, nullptr) removeFromSuperview];
}
- (void)_clearSafeBrowsingWarningIfForMainFrameNavigation
{
if ([_safeBrowsingWarning forMainFrameNavigation])
[self _clearSafeBrowsingWarning];
}
- (void)_internalDoAfterNextPresentationUpdate:(void (^)(void))updateBlock withoutWaitingForPainting:(BOOL)withoutWaitingForPainting withoutWaitingForAnimatedResize:(BOOL)withoutWaitingForAnimatedResize
{
#if PLATFORM(IOS_FAMILY)
if (![self usesStandardContentView]) {
RunLoop::main().dispatch([updateBlock = makeBlockPtr(updateBlock)] {
updateBlock();
});
return;
}
#endif
if (withoutWaitingForPainting)
_page->setShouldSkipWaitingForPaintAfterNextViewDidMoveToWindow(true);
auto updateBlockCopy = makeBlockPtr(updateBlock);
RetainPtr<WKWebView> strongSelf = self;
_page->callAfterNextPresentationUpdate([updateBlockCopy, withoutWaitingForAnimatedResize, strongSelf](WebKit::CallbackBase::Error error) {
if (!updateBlockCopy)
return;
#if PLATFORM(IOS_FAMILY)
if (!withoutWaitingForAnimatedResize && strongSelf->_dynamicViewportUpdateMode != WebKit::DynamicViewportUpdateMode::NotResizing) {
strongSelf->_callbacksDeferredDuringResize.append([updateBlockCopy] {
updateBlockCopy();
});
return;
}
#else
UNUSED_PARAM(withoutWaitingForAnimatedResize);
#endif
updateBlockCopy();
});
}
#if ENABLE(ATTACHMENT_ELEMENT)
- (void)_didInsertAttachment:(API::Attachment&)attachment withSource:(NSString *)source
{
id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate;
if ([uiDelegate respondsToSelector:@selector(_webView:didInsertAttachment:withSource:)])
[uiDelegate _webView:self didInsertAttachment:wrapper(attachment) withSource:source];
}
- (void)_didRemoveAttachment:(API::Attachment&)attachment
{
id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate;
if ([uiDelegate respondsToSelector:@selector(_webView:didRemoveAttachment:)])
[uiDelegate _webView:self didRemoveAttachment:wrapper(attachment)];
}
- (void)_didInvalidateDataForAttachment:(API::Attachment&)attachment
{
id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate;
if ([uiDelegate respondsToSelector:@selector(_webView:didInvalidateDataForAttachment:)])
[uiDelegate _webView:self didInvalidateDataForAttachment:wrapper(attachment)];
}
#endif // ENABLE(ATTACHMENT_ELEMENT)
- (id <_WKAppHighlightDelegate>)_appHighlightDelegate
{
#if ENABLE(APP_HIGHLIGHTS)
return _appHighlightDelegate.getAutoreleased();
#else
return nil;
#endif
}
- (void)_setAppHighlightDelegate:(id <_WKAppHighlightDelegate>)delegate
{
#if ENABLE(APP_HIGHLIGHTS)
_appHighlightDelegate = delegate;
#endif
}
#if ENABLE(APP_HIGHLIGHTS)
- (void)_storeAppHighlight:(const WebCore::AppHighlight&)highlight
{
auto delegate = self._appHighlightDelegate;
if (!delegate)
return;
if (![delegate respondsToSelector:@selector(_webView:storeAppHighlight:inNewGroup:requestOriginatedInApp:)])
return;
NSString *text = nil;
if (highlight.text)
text = highlight.text.value();
auto wkHighlight = adoptNS([[_WKAppHighlight alloc] initWithHighlight:highlight.highlight->makeContiguous()->createNSData().get() text:text image:nil]);
if ([delegate respondsToSelector:@selector(_webView:storeAppHighlight:inNewGroup:requestOriginatedInApp:)])
[delegate _webView:self storeAppHighlight:wkHighlight.get() inNewGroup:highlight.isNewGroup == WebCore::CreateNewGroupForHighlight::Yes requestOriginatedInApp:highlight.requestOriginatedInApp == WebCore::HighlightRequestOriginatedInApp::Yes];
}
#endif
- (WKPageRef)_pageForTesting
{
return toAPI(_page.get());
}
- (NakedPtr<WebKit::WebPageProxy>)_page
{
return _page.get();
}
- (id <WKURLSchemeHandler>)urlSchemeHandlerForURLScheme:(NSString *)urlScheme
{
auto* handler = static_cast<WebKit::WebURLSchemeHandlerCocoa*>(_page->urlSchemeHandlerForScheme(urlScheme));
return handler ? handler->apiHandler() : nil;
}
- (std::optional<BOOL>)_resolutionForShareSheetImmediateCompletionForTesting
{
return _resolutionForShareSheetImmediateCompletionForTesting;
}
- (void)createPDFWithConfiguration:(WKPDFConfiguration *)pdfConfiguration completionHandler:(void (^)(NSData *pdfDocumentData, NSError *error))completionHandler
{
THROW_IF_SUSPENDED;
WebCore::FrameIdentifier frameID;
if (auto mainFrame = _page->mainFrame())
frameID = mainFrame->frameID();
else {
completionHandler(nil, createNSError(WKErrorUnknown).get());
return;
}
std::optional<WebCore::FloatRect> floatRect;
if (pdfConfiguration && !CGRectIsNull(pdfConfiguration.rect))
floatRect = WebCore::FloatRect(pdfConfiguration.rect);
_page->drawToPDF(frameID, floatRect, [handler = makeBlockPtr(completionHandler)](const IPC::SharedBufferCopy& pdfData) {
if (pdfData.isEmpty()) {
handler(nil, createNSError(WKErrorUnknown).get());
return;
}
auto data = pdfData.buffer()->createCFData();
handler((NSData *)data.get(), nil);
});
}
- (void)createWebArchiveDataWithCompletionHandler:(void (^)(NSData *, NSError *))completionHandler
{
THROW_IF_SUSPENDED;
_page->getWebArchiveOfFrame(_page->mainFrame(), [completionHandler = makeBlockPtr(completionHandler)](API::Data* data) {
completionHandler(wrapper(data), nil);
});
}
static NSDictionary *dictionaryRepresentationForEditorState(const WebKit::EditorState& state)
{
if (state.isMissingPostLayoutData)
return @{ @"post-layout-data" : @NO };
auto& postLayoutData = state.postLayoutData();
return @{
@"post-layout-data" : @YES,
@"bold": postLayoutData.typingAttributes & WebKit::AttributeBold ? @YES : @NO,
@"italic": postLayoutData.typingAttributes & WebKit::AttributeItalics ? @YES : @NO,
@"underline": postLayoutData.typingAttributes & WebKit::AttributeUnderline ? @YES : @NO,
@"text-alignment": @(nsTextAlignment(static_cast<WebKit::TextAlignment>(postLayoutData.textAlignment))),
@"text-color": (NSString *)serializationForCSS(postLayoutData.textColor)
};
}
static NSTextAlignment nsTextAlignment(WebKit::TextAlignment alignment)
{
switch (alignment) {
case WebKit::NoAlignment:
return NSTextAlignmentNatural;
case WebKit::LeftAlignment:
return NSTextAlignmentLeft;
case WebKit::RightAlignment:
return NSTextAlignmentRight;
case WebKit::CenterAlignment:
return NSTextAlignmentCenter;
case WebKit::JustifiedAlignment:
return NSTextAlignmentJustified;
}
ASSERT_NOT_REACHED();
return NSTextAlignmentNatural;
}
static _WKSelectionAttributes selectionAttributes(const WebKit::EditorState& editorState, _WKSelectionAttributes previousAttributes)
{
_WKSelectionAttributes attributes = _WKSelectionAttributeNoSelection;
if (editorState.selectionIsNone)
return attributes;
if (editorState.selectionIsRange)
attributes |= _WKSelectionAttributeIsRange;
else
attributes |= _WKSelectionAttributeIsCaret;
return attributes;
}
- (void)_didChangeEditorState
{
auto newSelectionAttributes = selectionAttributes(_page->editorState(), _selectionAttributes);
if (_selectionAttributes != newSelectionAttributes) {
NSString *selectionAttributesKey = NSStringFromSelector(@selector(_selectionAttributes));
[self willChangeValueForKey:selectionAttributesKey];
_selectionAttributes = newSelectionAttributes;
[self didChangeValueForKey:selectionAttributesKey];
}
// FIXME: We should either rename -_webView:editorStateDidChange: to clarify that it's only intended for use when testing,
// or remove it entirely and use -_webView:didChangeFontAttributes: instead once text alignment is supported in the set of
// font attributes.
id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate;
if ([uiDelegate respondsToSelector:@selector(_webView:editorStateDidChange:)])
[uiDelegate _webView:self editorStateDidChange:dictionaryRepresentationForEditorState(_page->editorState())];
}
- (WKNavigation *)loadSimulatedRequest:(NSURLRequest *)request response:(NSURLResponse *)response responseData:(NSData *)data
{
THROW_IF_SUSPENDED;
return wrapper(_page->loadSimulatedRequest(request, response, { static_cast<const uint8_t*>(data.bytes), data.length }));
}
// FIXME(223658): Remove this once adopters have moved to the final API.
- (WKNavigation *)loadSimulatedRequest:(NSURLRequest *)request withResponse:(NSURLResponse *)response responseData:(NSData *)data
{
THROW_IF_SUSPENDED;
return [self loadSimulatedRequest:request response:response responseData:data];
}
- (WKNavigation *)loadSimulatedRequest:(NSURLRequest *)request responseHTMLString:(NSString *)string
{
THROW_IF_SUSPENDED;
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
auto response = adoptNS([[NSURLResponse alloc] initWithURL:request.URL MIMEType:@"text/html" expectedContentLength:string.length textEncodingName:@"UTF-8"]);
return [self loadSimulatedRequest:request response:response.get() responseData:data];
}
// FIXME(223658): Remove this once adopters have moved to the final API.
- (WKNavigation *)loadSimulatedRequest:(NSURLRequest *)request withResponseHTMLString:(NSString *)string
{
THROW_IF_SUSPENDED;
return [self loadSimulatedRequest:request responseHTMLString:string];
}
- (WKNavigation *)loadFileRequest:(NSURLRequest *)request allowingReadAccessToURL:(NSURL *)readAccessURL
{
THROW_IF_SUSPENDED;
auto URL = request.URL;
if (![URL isFileURL])
[NSException raise:NSInvalidArgumentException format:@"%@ is not a file URL", URL];
if (![readAccessURL isFileURL])
[NSException raise:NSInvalidArgumentException format:@"%@ is not a file URL", readAccessURL];
bool isAppInitiated = true;
#if ENABLE(APP_PRIVACY_REPORT)
isAppInitiated = request.attribution == NSURLRequestAttributionDeveloper;
#endif
return wrapper(_page->loadFile(URL.absoluteString, readAccessURL.absoluteString, isAppInitiated));
}
- (WebCore::CocoaColor *)themeColor
{
return cocoaColorOrNil(_page->themeColor()).autorelease();
}
- (WebCore::CocoaColor *)underPageBackgroundColor
{
return cocoaColor(_page->underPageBackgroundColor()).autorelease();
}
- (void)setUnderPageBackgroundColor:(WebCore::CocoaColor *)underPageBackgroundColorOverride
{
_page->setUnderPageBackgroundColorOverride(WebCore::roundAndClampToSRGBALossy(underPageBackgroundColorOverride.CGColor));
}
+ (BOOL)automaticallyNotifiesObserversOfUnderPageBackgroundColor
{
return NO;
}
- (WKFullscreenState)fullscreenState
{
#if ENABLE(FULLSCREEN_API)
auto* fullscreenManager = _page->fullScreenManager();
if (!fullscreenManager)
return WKFullscreenStateNotInFullscreen;
WKFullscreenState state = WKFullscreenStateNotInFullscreen;
switch (fullscreenManager->fullscreenState()) {
case WebKit::WebFullScreenManagerProxy::FullscreenState::EnteringFullscreen:
state = WKFullscreenStateEnteringFullscreen;
break;
case WebKit::WebFullScreenManagerProxy::FullscreenState::InFullscreen:
state = WKFullscreenStateInFullscreen;
break;
case WebKit::WebFullScreenManagerProxy::FullscreenState::ExitingFullscreen:
state = WKFullscreenStateExitingFullscreen;
break;
default:
state = WKFullscreenStateNotInFullscreen;
break;
}
return state;
#else
return WKFullscreenStateNotInFullscreen;
#endif
}
@end
#pragma mark -
@implementation WKWebView (WKPrivate)
#pragma mark - macOS WKPrivate
#if PLATFORM(MAC)
#define WEBCORE_PRIVATE_COMMAND(command) - (void)_##command:(id)sender { THROW_IF_SUSPENDED; _page->executeEditCommand(#command ## _s); }
WEBCORE_PRIVATE_COMMAND(alignCenter)
WEBCORE_PRIVATE_COMMAND(alignJustified)
WEBCORE_PRIVATE_COMMAND(alignLeft)
WEBCORE_PRIVATE_COMMAND(alignRight)
WEBCORE_PRIVATE_COMMAND(insertOrderedList)
WEBCORE_PRIVATE_COMMAND(insertUnorderedList)
WEBCORE_PRIVATE_COMMAND(insertNestedOrderedList)
WEBCORE_PRIVATE_COMMAND(insertNestedUnorderedList)
WEBCORE_PRIVATE_COMMAND(indent)
WEBCORE_PRIVATE_COMMAND(outdent)
WEBCORE_PRIVATE_COMMAND(pasteAsQuotation)
WEBCORE_PRIVATE_COMMAND(pasteAndMatchStyle)
#undef WEBCORE_PRIVATE_COMMAND
- (void)_toggleStrikeThrough:(id)sender
{
THROW_IF_SUSPENDED;
_page->executeEditCommand("strikethrough"_s);
}
- (void)_increaseListLevel:(id)sender
{
THROW_IF_SUSPENDED;
_page->increaseListLevel();
}
- (void)_decreaseListLevel:(id)sender
{
THROW_IF_SUSPENDED;
_page->decreaseListLevel();
}
- (void)_changeListType:(id)sender
{
THROW_IF_SUSPENDED;
_page->changeListType();
}
#endif // PLATFORM(MAC)
#pragma mark - iOS WKPrivate
#if PLATFORM(IOS_FAMILY)
#define FORWARD_ACTION_TO_WKCONTENTVIEW(_action) \
- (void)_action:(id)sender \
{ \
if (self.usesStandardContentView) \
[_contentView _action ## ForWebView:sender]; \
}
FOR_EACH_PRIVATE_WKCONTENTVIEW_ACTION(FORWARD_ACTION_TO_WKCONTENTVIEW)
#undef FORWARD_ACTION_TO_WKCONTENTVIEW
- (UIView *)inputAccessoryView
{
return [_contentView inputAccessoryViewForWebView];
}
- (UIView *)inputView
{
return [_contentView inputViewForWebView];
}
- (UITextInputAssistantItem *)inputAssistantItem
{
return [_contentView inputAssistantItemForWebView];
}
#endif // PLATFORM(IOS_FAMILY)
#pragma mark - macOS/iOS WKPrivate
- (_WKSelectionAttributes)_selectionAttributes
{
return _selectionAttributes;
}
- (CGSize)_viewportSizeForCSSViewportUnits
{
return _page->viewportSizeForCSSViewportUnits();
}
- (void)_setViewportSizeForCSSViewportUnits:(CGSize)viewportSize
{
THROW_IF_SUSPENDED;
auto viewportSizeForViewportUnits = WebCore::FloatSize(viewportSize);
if (viewportSizeForViewportUnits.isEmpty())
[NSException raise:NSInvalidArgumentException format:@"Viewport size should not be empty"];
_page->setViewportSizeForCSSViewportUnits(viewportSizeForViewportUnits);
}
- (BOOL)_isBeingInspected
{
return _page && _page->hasInspectorFrontend();
}
- (_WKInspector *)_inspector
{
if (auto* inspector = _page->inspector())
return wrapper(*inspector);
return nil;
}
- (void)_didEnableBrowserExtensions:(NSDictionary<NSString *, NSString *> *)extensionIDToNameMap
{
THROW_IF_SUSPENDED;
HashMap<String, String> transformed;
transformed.reserveInitialCapacity(extensionIDToNameMap.count);
[extensionIDToNameMap enumerateKeysAndObjectsUsingBlock:[&](NSString *extensionID, NSString *extensionName, BOOL *) {
transformed.set(extensionID, extensionName);
}];
_page->inspectorController().browserExtensionsEnabled(WTFMove(transformed));
}
- (void)_didDisableBrowserExtensions:(NSSet<NSString *> *)extensionIDs
{
THROW_IF_SUSPENDED;
HashSet<String> transformed;
transformed.reserveInitialCapacity(extensionIDs.count);
for (NSString *extensionID in extensionIDs)
transformed.addVoid(extensionID);
_page->inspectorController().browserExtensionsDisabled(WTFMove(transformed));
}
#if HAVE(SAFARI_FOR_WEBKIT_DEVELOPMENT_REQUIRING_EXTRA_SYMBOLS)
- (id <_WKInspectorDelegate>)_inspectorDelegate
{
// This is needed to launch SafariForWebKitDevelopment on Big Sur with an open source WebKit build.
// FIXME: Remove this after we release a Safari after Safari 14.
return nil;
}
- (void)_setInspectorDelegate:(id<_WKInspectorDelegate>)delegate
{
// This is needed to launch SafariForWebKitDevelopment on Big Sur with an open source WebKit build.
// FIXME: Remove this after we release a Safari after Safari 14.
}
#endif
- (_WKFrameHandle *)_mainFrame
{
if (auto* frame = _page->mainFrame())
return wrapper(API::FrameHandle::create(frame->frameID()));
return nil;
}
- (BOOL)_negotiatedLegacyTLS
{
return _page->pageLoadState().hasNegotiatedLegacyTLS();
}
- (void)_frames:(void (^)(_WKFrameTreeNode *))completionHandler
{
_page->getAllFrames([completionHandler = makeBlockPtr(completionHandler), page = Ref { *_page.get() }] (WebKit::FrameTreeNodeData&& data) {
completionHandler(wrapper(API::FrameTreeNode::create(WTFMove(data), page.get())));
});
}
- (BOOL)_isEditable
{
return _page && _page->isEditable();
}
- (void)_setEditable:(BOOL)editable
{
THROW_IF_SUSPENDED;
bool wasEditable = _page->isEditable();
_page->setEditable(editable);
#if PLATFORM(MAC)
if (editable)
_impl->didBecomeEditable();
#endif
if (wasEditable == editable)
return;
#if PLATFORM(IOS_FAMILY)
[_contentView _didChangeWebViewEditability];
#endif
}
- (void)_executeEditCommand:(NSString *)command argument:(NSString *)argument completion:(void (^)(BOOL))completion
{
THROW_IF_SUSPENDED;
_page->executeEditCommand(command, argument, [capturedCompletionBlock = makeBlockPtr(completion)] {
if (capturedCompletionBlock)
capturedCompletionBlock(YES);
});
}
- (id <_WKTextManipulationDelegate>)_textManipulationDelegate
{
return _textManipulationDelegate.getAutoreleased();
}
- (void)_setTextManipulationDelegate:(id <_WKTextManipulationDelegate>)delegate
{
_textManipulationDelegate = delegate;
}
static RetainPtr<NSDictionary<NSString *, id>> createUserInfo(const std::optional<WebCore::TextManipulationController::ManipulationTokenInfo>& info)
{
if (!info)
return { };
auto result = adoptNS([[NSMutableDictionary alloc] initWithCapacity:3]);
if (!info->documentURL.isNull())
[result setObject:(NSURL *)info->documentURL forKey:_WKTextManipulationTokenUserInfoDocumentURLKey];
if (!info->tagName.isNull())
[result setObject:(NSString *)info->tagName forKey:_WKTextManipulationTokenUserInfoTagNameKey];
if (!info->roleAttribute.isNull())
[result setObject:(NSString *)info->roleAttribute forKey:_WKTextManipulationTokenUserInfoRoleAttributeKey];
[result setObject:@(info->isVisible) forKey:_WKTextManipulationTokenUserInfoVisibilityKey];
return result;
}
- (void)_startTextManipulationsWithConfiguration:(_WKTextManipulationConfiguration *)configuration completion:(void(^)())completionHandler
{
THROW_IF_SUSPENDED;
using ExclusionRule = WebCore::TextManipulationController::ExclusionRule;
if (!_textManipulationDelegate || !_page) {
completionHandler();
return;
}
Vector<WebCore::TextManipulationController::ExclusionRule> exclusionRules;
if (configuration) {
for (_WKTextManipulationExclusionRule *wkRule in configuration.exclusionRules) {
auto type = wkRule.isExclusion ? ExclusionRule::Type::Exclude : ExclusionRule::Type::Include;
if (wkRule.attributeName)
exclusionRules.append({type, ExclusionRule::AttributeRule { wkRule.attributeName, wkRule.attributeValue } });
else if (wkRule.className)
exclusionRules.append({type, ExclusionRule::ClassRule { wkRule.className } });
else
exclusionRules.append({type, ExclusionRule::ElementRule { wkRule.elementName } });
}
}
_page->startTextManipulations(exclusionRules, [weakSelf = WeakObjCPtr<WKWebView>(self)] (const Vector<WebCore::TextManipulationController::ManipulationItem>& itemReferences) {
if (!weakSelf)
return;
auto retainedSelf = weakSelf.get();
auto delegate = [retainedSelf _textManipulationDelegate];
if (!delegate)
return;
auto createWKItem = [] (const WebCore::TextManipulationController::ManipulationItem& item) {
auto tokens = createNSArray(item.tokens, [] (auto& token) {
auto wkToken = adoptNS([[_WKTextManipulationToken alloc] init]);
[wkToken setIdentifier:String::number(token.identifier.toUInt64())];
[wkToken setContent:token.content];
[wkToken setExcluded:token.isExcluded];
[wkToken setUserInfo:createUserInfo(token.info).get()];
return wkToken;
});
return adoptNS([[_WKTextManipulationItem alloc] initWithIdentifier:String::number(item.identifier.toUInt64()) tokens:tokens.get()]);
};
if ([delegate respondsToSelector:@selector(_webView:didFindTextManipulationItems:)])
[delegate _webView:retainedSelf.get() didFindTextManipulationItems:createNSArray(itemReferences, createWKItem).get()];
else {
for (auto& item : itemReferences)
[delegate _webView:retainedSelf.get() didFindTextManipulationItem:createWKItem(item).get()];
}
}, [capturedCompletionBlock = makeBlockPtr(completionHandler)] () {
capturedCompletionBlock();
});
}
static WebCore::TextManipulationController::ItemIdentifier coreTextManipulationItemIdentifierFromString(NSString *identifier)
{
return makeObjectIdentifier<WebCore::TextManipulationController::ItemIdentifierType>(identifier.longLongValue);
}
static WebCore::TextManipulationController::TokenIdentifier coreTextManipulationTokenIdentifierFromString(NSString *identifier)
{
return makeObjectIdentifier<WebCore::TextManipulationController::TokenIdentifierType>(identifier.longLongValue);
}
- (void)_completeTextManipulation:(_WKTextManipulationItem *)item completion:(void(^)(BOOL success))completionHandler
{
THROW_IF_SUSPENDED;
if (!_page) {
completionHandler(false);
return;
}
auto itemID = coreTextManipulationItemIdentifierFromString(item.identifier);
Vector<WebCore::TextManipulationController::ManipulationToken> tokens;
for (_WKTextManipulationToken *wkToken in item.tokens)
tokens.append(WebCore::TextManipulationController::ManipulationToken { coreTextManipulationTokenIdentifierFromString(wkToken.identifier), wkToken.content, std::nullopt });
Vector<WebCore::TextManipulationController::ManipulationItem> coreItems;
coreItems.reserveInitialCapacity(1);
coreItems.uncheckedAppend(WebCore::TextManipulationController::ManipulationItem { itemID, WTFMove(tokens) });
_page->completeTextManipulation(coreItems, [capturedCompletionBlock = makeBlockPtr(completionHandler)] (bool allFailed, auto& failures) {
capturedCompletionBlock(!allFailed && failures.isEmpty());
});
}
static RetainPtr<NSMutableArray> makeFailureSetForAllTextManipulationItems(NSArray<_WKTextManipulationItem *> *items)
{
RetainPtr<NSMutableArray> wkFailures = adoptNS([[NSMutableArray alloc] initWithCapacity:items.count]);
for (_WKTextManipulationItem *item in items)
[wkFailures addObject:[NSError errorWithDomain:_WKTextManipulationItemErrorDomain code:_WKTextManipulationItemErrorNotAvailable userInfo:@{_WKTextManipulationItemErrorItemKey: item}]];
return wkFailures;
};
static RetainPtr<NSArray> wkTextManipulationErrors(NSArray<_WKTextManipulationItem *> *items, const Vector<WebCore::TextManipulationController::ManipulationFailure>& failures)
{
if (failures.isEmpty())
return nil;
return createNSArray(failures, [&] (auto& coreFailure) -> NSError * {
ASSERT(coreFailure.index < items.count);
if (coreFailure.index >= items.count)
return nil;
auto errorCode = static_cast<NSInteger>(([&coreFailure] {
using Type = WebCore::TextManipulationController::ManipulationFailureType;
switch (coreFailure.type) {
case Type::ContentChanged:
return _WKTextManipulationItemErrorContentChanged;
case Type::InvalidItem:
return _WKTextManipulationItemErrorInvalidItem;
case Type::InvalidToken:
return _WKTextManipulationItemErrorInvalidToken;
case Type::ExclusionViolation:
return _WKTextManipulationItemErrorExclusionViolation;
}
})());
auto item = items[coreFailure.index];
ASSERT(coreTextManipulationItemIdentifierFromString(item.identifier) == coreFailure.identifier);
return [NSError errorWithDomain:_WKTextManipulationItemErrorDomain code:errorCode userInfo:@{_WKTextManipulationItemErrorItemKey: item}];
});
}
- (void)_completeTextManipulationForItems:(NSArray<_WKTextManipulationItem *> *)items completion:(void(^)(NSArray<NSError *> *errors))completionHandler
{
THROW_IF_SUSPENDED;
if (!_page) {
completionHandler(makeFailureSetForAllTextManipulationItems(items).get());
return;
}
Vector<WebCore::TextManipulationController::ManipulationItem> coreItems;
coreItems.reserveInitialCapacity(items.count);
for (_WKTextManipulationItem *wkItem in items) {
Vector<WebCore::TextManipulationController::ManipulationToken> coreTokens;
coreTokens.reserveInitialCapacity(wkItem.tokens.count);
for (_WKTextManipulationToken *wkToken in wkItem.tokens)
coreTokens.uncheckedAppend(WebCore::TextManipulationController::ManipulationToken { coreTextManipulationTokenIdentifierFromString(wkToken.identifier), wkToken.content, std::nullopt });
coreItems.uncheckedAppend(WebCore::TextManipulationController::ManipulationItem { coreTextManipulationItemIdentifierFromString(wkItem.identifier), WTFMove(coreTokens) });
}
RetainPtr<NSArray<_WKTextManipulationItem *>> retainedItems = items;
_page->completeTextManipulation(coreItems, [capturedItems = retainedItems, capturedCompletionBlock = makeBlockPtr(completionHandler)](bool allFailed, auto& failures) {
if (allFailed) {
capturedCompletionBlock(makeFailureSetForAllTextManipulationItems(capturedItems.get()).get());
return;
}
capturedCompletionBlock(wkTextManipulationErrors(capturedItems.get(), failures).get());
});
}
- (void)_startImageAnalysis:(NSString *)identifier
{
#if ENABLE(IMAGE_ANALYSIS)
THROW_IF_SUSPENDED;
if (!_page || !_page->preferences().textRecognitionEnhancementsEnabled())
return;
_page->startImageAnalysis(identifier);
#endif
}
- (void)_requestResource:(NSURLRequest *)request completionHandler:(void(^)(NSData *, NSURLResponse *, NSError *))completionHandler
{
_page->requestResource(request, [completionHandler = makeBlockPtr(completionHandler)] (Ref<WebCore::SharedBuffer>&& buffer, WebCore::ResourceResponse&& response, WebCore::ResourceError&& error) {
if (error.isNull())
return completionHandler(buffer->createNSData().get(), response.nsURLResponse(), nil);
completionHandler(nil, nil, error.nsError());
});
}
- (void)_takeFindStringFromSelection:(id)sender
{
THROW_IF_SUSPENDED;
#if PLATFORM(MAC)
[self _takeFindStringFromSelectionInternal:sender];
#else
_page->executeEditCommand("TakeFindStringFromSelection"_s);
#endif
}
+ (NSString *)_stringForFind
{
return WebKit::stringForFind();
}
+ (void)_setStringForFind:(NSString *)findString
{
WebKit::updateStringForFind(findString);
}
- (_WKRemoteObjectRegistry *)_remoteObjectRegistry
{
#if PLATFORM(MAC)
return _impl->remoteObjectRegistry();
#else
if (!_remoteObjectRegistry) {
_remoteObjectRegistry = adoptNS([[_WKRemoteObjectRegistry alloc] _initWithWebPageProxy:*_page]);
_page->process().processPool().addMessageReceiver(Messages::RemoteObjectRegistry::messageReceiverName(), _page->identifier(), [_remoteObjectRegistry remoteObjectRegistry]);
}
return _remoteObjectRegistry.get();
#endif
}
- (WKBrowsingContextHandle *)_handle
{
return adoptNS([[WKBrowsingContextHandle alloc] _initWithPageProxy:*_page]).autorelease();
}
- (_WKRenderingProgressEvents)_observedRenderingProgressEvents
{
return _observedRenderingProgressEvents;
}
- (id <WKHistoryDelegatePrivate>)_historyDelegate
{
return _navigationState->historyDelegate().autorelease();
}
- (void)_setHistoryDelegate:(id <WKHistoryDelegatePrivate>)historyDelegate
{
_page->setHistoryClient(_navigationState->createHistoryClient());
_navigationState->setHistoryDelegate(historyDelegate);
}
- (void)_updateMediaPlaybackControlsManager
{
THROW_IF_SUSPENDED;
#if HAVE(TOUCH_BAR) && ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER)
_impl->updateMediaPlaybackControlsManager();
#endif
}
- (BOOL)_canTogglePictureInPicture
{
#if HAVE(TOUCH_BAR)
return _impl->canTogglePictureInPicture();
#else
return NO;
#endif
}
- (BOOL)_isPictureInPictureActive
{
#if HAVE(TOUCH_BAR) && ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER)
return _impl->isPictureInPictureActive();
#else
return NO;
#endif
}
- (void)_togglePictureInPicture
{
THROW_IF_SUSPENDED;
#if HAVE(TOUCH_BAR) && ENABLE(WEB_PLAYBACK_CONTROLS_MANAGER)
_impl->togglePictureInPicture();
#endif
}
- (_WKMediaMutedState)_mediaMutedState
{
return WebKit::toWKMediaMutedState(_page->mutedStateFlags());
}
- (void)_closeAllMediaPresentations
{
THROW_IF_SUSPENDED;
[self closeAllMediaPresentationsWithCompletionHandler:^{ }];
}
- (void)_stopMediaCapture
{
THROW_IF_SUSPENDED;
_page->stopMediaCapture(WebCore::MediaProducerMediaCaptureKind::AudioVideo);
}
- (void)_stopAllMediaPlayback
{
THROW_IF_SUSPENDED;
[self pauseAllMediaPlaybackWithCompletionHandler:nil];
}
- (void)_suspendAllMediaPlayback
{
THROW_IF_SUSPENDED;
[self setAllMediaPlaybackSuspended:YES completionHandler:nil];
}
- (void)_resumeAllMediaPlayback
{
THROW_IF_SUSPENDED;
[self setAllMediaPlaybackSuspended:NO completionHandler:nil];
}
#if ENABLE(APP_HIGHLIGHTS)
static void convertAndAddHighlight(Vector<Ref<WebKit::SharedMemory>>& buffers, NSData *highlight)
{
auto sharedMemory = WebKit::SharedMemory::allocate(highlight.length);
if (sharedMemory) {
[highlight getBytes:sharedMemory->data() length:highlight.length];
buffers.append(*sharedMemory);
}
}
#endif
- (void)_restoreAppHighlights:(NSArray<NSData *> *)highlights
{
THROW_IF_SUSPENDED;
#if ENABLE(APP_HIGHLIGHTS)
Vector<Ref<WebKit::SharedMemory>> buffers;
for (NSData *highlight in highlights)
convertAndAddHighlight(buffers, highlight);
_page->restoreAppHighlightsAndScrollToIndex(buffers, std::nullopt);
#else
UNUSED_PARAM(highlights);
#endif
}
- (void)_restoreAndScrollToAppHighlight:(NSData *)highlight
{
THROW_IF_SUSPENDED;
#if ENABLE(APP_HIGHLIGHTS)
Vector<Ref<WebKit::SharedMemory>> buffers;
convertAndAddHighlight(buffers, highlight);
_page->restoreAppHighlightsAndScrollToIndex(buffers, 0);
#else
UNUSED_PARAM(highlight);
#endif
}
- (void)_addAppHighlight
{
THROW_IF_SUSPENDED;
[self _addAppHighlightInNewGroup:NO originatedInApp:YES];
}
- (void)_addAppHighlightInNewGroup:(BOOL)newGroup originatedInApp:(BOOL)originatedInApp
{
THROW_IF_SUSPENDED;
#if ENABLE(APP_HIGHLIGHTS)
_page->createAppHighlightInSelectedRange(newGroup ? WebCore::CreateNewGroupForHighlight::Yes : WebCore::CreateNewGroupForHighlight::No, originatedInApp ? WebCore::HighlightRequestOriginatedInApp::Yes : WebCore::HighlightRequestOriginatedInApp::No);
#endif
}
- (NSURL *)_unreachableURL
{
return [NSURL _web_URLWithWTFString:_page->pageLoadState().unreachableURL()];
}
- (NSURL *)_mainFrameURL
{
if (auto* frame = _page->mainFrame())
return frame->url();
return nil;
}
- (void)_loadAlternateHTMLString:(NSString *)string baseURL:(NSURL *)baseURL forUnreachableURL:(NSURL *)unreachableURL
{
THROW_IF_SUSPENDED;
NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];
_page->loadAlternateHTML({ static_cast<const uint8_t*>(data.bytes), data.length }, "UTF-8"_s, baseURL, unreachableURL);
}
- (WKNavigation *)_loadData:(NSData *)data MIMEType:(NSString *)MIMEType characterEncodingName:(NSString *)characterEncodingName baseURL:(NSURL *)baseURL userData:(id)userData
{
THROW_IF_SUSPENDED;
return wrapper(_page->loadData({ static_cast<const uint8_t*>(data.bytes), data.length }, MIMEType, characterEncodingName, baseURL.absoluteString, WebKit::ObjCObjectGraph::create(userData).ptr()));
}
- (WKNavigation *)_loadRequest:(NSURLRequest *)request shouldOpenExternalURLs:(BOOL)shouldOpenExternalURLs
{
THROW_IF_SUSPENDED;
_WKShouldOpenExternalURLsPolicy policy = shouldOpenExternalURLs ? _WKShouldOpenExternalURLsPolicyAllow : _WKShouldOpenExternalURLsPolicyNotAllow;
return [self _loadRequest:request shouldOpenExternalURLsPolicy:policy];
}
- (WKNavigation *)_loadRequest:(NSURLRequest *)request shouldOpenExternalURLsPolicy:(_WKShouldOpenExternalURLsPolicy)shouldOpenExternalURLsPolicy
{
THROW_IF_SUSPENDED;
WebCore::ShouldOpenExternalURLsPolicy policy;
switch (shouldOpenExternalURLsPolicy) {
case _WKShouldOpenExternalURLsPolicyNotAllow:
policy = WebCore::ShouldOpenExternalURLsPolicy::ShouldNotAllow;
break;
case _WKShouldOpenExternalURLsPolicyAllow:
policy = WebCore::ShouldOpenExternalURLsPolicy::ShouldAllow;
break;
case _WKShouldOpenExternalURLsPolicyAllowExternalSchemesButNotAppLinks:
policy = WebCore::ShouldOpenExternalURLsPolicy::ShouldAllowExternalSchemesButNotAppLinks;
break;
}
return wrapper(_page->loadRequest(request, policy));
}
- (void)_loadServiceWorker:(NSURL *)url completionHandler:(void (^)(BOOL success))completionHandler
{
THROW_IF_SUSPENDED;
if (_page->isServiceWorkerPage())
[NSException raise:NSInternalInconsistencyException format:@"The WKWebView was already used to load a service worker"];
_page->loadServiceWorker(url, [completionHandler = makeBlockPtr(completionHandler)](bool success) mutable {
completionHandler(success);
});
}
- (void)_grantAccessToAssetServices
{
THROW_IF_SUSPENDED;
if (_page)
_page->grantAccessToAssetServices();
}
- (void)_revokeAccessToAssetServices
{
THROW_IF_SUSPENDED;
if (_page)
_page->revokeAccessToAssetServices();
}
- (void)_switchFromStaticFontRegistryToUserFontRegistry
{
THROW_IF_SUSPENDED;
if (_page)
_page->switchFromStaticFontRegistryToUserFontRegistry();
}
- (void)_didLoadAppInitiatedRequest:(void (^)(BOOL result))completionHandler
{
THROW_IF_SUSPENDED;
_page->appPrivacyReportTestingData([completionHandler = makeBlockPtr(completionHandler)] (auto&& appPrivacyReportTestingData) mutable {
completionHandler(appPrivacyReportTestingData.hasLoadedAppInitiatedRequestTesting);
});
}
- (void)_didLoadNonAppInitiatedRequest:(void (^)(BOOL result))completionHandler
{
THROW_IF_SUSPENDED;
_page->appPrivacyReportTestingData([completionHandler = makeBlockPtr(completionHandler)] (auto&& appPrivacyReportTestingData) mutable {
completionHandler(appPrivacyReportTestingData.hasLoadedNonAppInitiatedRequestTesting);
});
}
- (void)_suspendPage:(void (^)(BOOL))completionHandler
{
if (!_page) {
completionHandler(NO);
return;
}
_page->suspend([completionHandler = makeBlockPtr(completionHandler)](bool success) {
completionHandler(success);
});
}
- (void)_resumePage:(void (^)(BOOL))completionHandler
{
if (!_page) {
completionHandler(NO);
return;
}
_page->resume([completionHandler = makeBlockPtr(completionHandler)](bool success) {
completionHandler(success);
});
}
- (NSArray *)_certificateChain
{
if (WebKit::WebFrameProxy* mainFrame = _page->mainFrame())
return mainFrame->certificateInfo() ? (__bridge NSArray *)mainFrame->certificateInfo()->certificateInfo().certificateChain() : nil;
return nil;
}
- (NSURL *)_committedURL
{
return [NSURL _web_URLWithWTFString:_page->pageLoadState().url()];
}
- (NSString *)_MIMEType
{
if (_page->mainFrame())
return _page->mainFrame()->mimeType();
return nil;
}
- (NSString *)_userAgent
{
return _page->userAgent();
}
- (NSString *)_applicationNameForUserAgent
{
return _page->applicationNameForUserAgent();
}
- (void)_setApplicationNameForUserAgent:(NSString *)applicationNameForUserAgent
{
THROW_IF_SUSPENDED;
_page->setApplicationNameForUserAgent(applicationNameForUserAgent);
_page->setApplicationNameForDesktopUserAgent(applicationNameForUserAgent);
}
- (NSString *)_customUserAgent
{
return self.customUserAgent;
}
- (void)_setCustomUserAgent:(NSString *)customUserAgent
{
self.customUserAgent = customUserAgent;
}
- (void)_setUserContentExtensionsEnabled:(BOOL)userContentExtensionsEnabled
{
// This is kept for binary compatibility with iOS 9.
}
- (BOOL)_userContentExtensionsEnabled
{
// This is kept for binary compatibility with iOS 9.
return true;
}
- (pid_t)_webProcessIdentifier
{
if (![self _isValid])
return 0;
return _page->processIdentifier();
}
- (pid_t)_provisionalWebProcessIdentifier
{
if (![self _isValid])
return 0;
auto* provisionalPage = _page->provisionalPageProxy();
if (!provisionalPage)
return 0;
return provisionalPage->process().processIdentifier();
}
- (BOOL)_webProcessIsResponsive
{
return _page->process().isResponsive();
}
- (void)_killWebContentProcess
{
THROW_IF_SUSPENDED;
if (![self _isValid])
return;
_page->process().terminate();
}
- (WKNavigation *)_reloadWithoutContentBlockers
{
THROW_IF_SUSPENDED;
return wrapper(_page->reload(WebCore::ReloadOption::DisableContentBlockers));
}
- (WKNavigation *)_reloadExpiredOnly
{
THROW_IF_SUSPENDED;
return wrapper(_page->reload(WebCore::ReloadOption::ExpiredOnly));
}
- (void)_killWebContentProcessAndResetState
{
THROW_IF_SUSPENDED;
Ref<WebKit::WebProcessProxy> protectedProcessProxy(_page->process());
protectedProcessProxy->requestTermination(WebKit::ProcessTerminationReason::RequestedByClient);
if (auto* provisionalPageProxy = _page->provisionalPageProxy()) {
Ref<WebKit::WebProcessProxy> protectedProcessProxy(provisionalPageProxy->process());
protectedProcessProxy->requestTermination(WebKit::ProcessTerminationReason::RequestedByClient);
}
}
- (void)_takePDFSnapshotWithConfiguration:(WKSnapshotConfiguration *)snapshotConfiguration completionHandler:(void (^)(NSData *, NSError *))completionHandler
{
THROW_IF_SUSPENDED;
RetainPtr<WKPDFConfiguration> pdfConfiguration;
if (snapshotConfiguration) {
pdfConfiguration = adoptNS([[WKPDFConfiguration alloc] init]);
[pdfConfiguration setRect:snapshotConfiguration.rect];
}
[self createPDFWithConfiguration:pdfConfiguration.get() completionHandler:completionHandler];
}
- (void)_getPDFFirstPageSizeInFrame:(_WKFrameHandle *)frame completionHandler:(void(^)(CGSize))completionHandler
{
THROW_IF_SUSPENDED;
_page->getPDFFirstPageSize(frame->_frameHandle->frameID(), [completionHandler = makeBlockPtr(completionHandler)](WebCore::FloatSize size) {
completionHandler(static_cast<CGSize>(size));
});
}
- (NSData *)_sessionStateData
{
// FIXME: This should not use the legacy session state encoder.
return wrapper(WebKit::encodeLegacySessionState(_page->sessionState()));
}
- (_WKSessionState *)_sessionState
{
return adoptNS([[_WKSessionState alloc] _initWithSessionState:_page->sessionState()]).autorelease();
}
- (_WKSessionState *)_sessionStateWithFilter:(BOOL (^)(WKBackForwardListItem *item))filter
{
WebKit::SessionState sessionState = _page->sessionState([filter](WebKit::WebBackForwardListItem& item) {
if (!filter)
return true;
return (bool)filter(wrapper(item));
});
return adoptNS([[_WKSessionState alloc] _initWithSessionState:sessionState]).autorelease();
}
- (void)_restoreFromSessionStateData:(NSData *)sessionStateData
{
THROW_IF_SUSPENDED;
// FIXME: This should not use the legacy session state decoder.
WebKit::SessionState sessionState;
if (!WebKit::decodeLegacySessionState(static_cast<const uint8_t*>(sessionStateData.bytes), sessionStateData.length, sessionState))
return;
_page->restoreFromSessionState(WTFMove(sessionState), true);
}
- (WKNavigation *)_restoreSessionState:(_WKSessionState *)sessionState andNavigate:(BOOL)navigate
{
THROW_IF_SUSPENDED;
return wrapper(_page->restoreFromSessionState(sessionState ? sessionState->_sessionState : WebKit::SessionState { }, navigate));
}
- (void)_close
{
_page->close();
}
- (BOOL)_tryClose
{
THROW_IF_SUSPENDED;
return _page->tryClose();
}
- (BOOL)_isClosed
{
return _page->isClosed();
}
- (_WKAttachment *)_insertAttachmentWithFilename:(NSString *)filename contentType:(NSString *)contentType data:(NSData *)data options:(_WKAttachmentDisplayOptions *)options completion:(void(^)(BOOL success))completionHandler
{
THROW_IF_SUSPENDED;
UNUSED_PARAM(options);
auto fileWrapper = adoptNS([[NSFileWrapper alloc] initRegularFileWithContents:data]);
if (filename)
[fileWrapper setPreferredFilename:filename];
return [self _insertAttachmentWithFileWrapper:fileWrapper.get() contentType:contentType completion:completionHandler];
}
- (_WKAttachment *)_insertAttachmentWithFileWrapper:(NSFileWrapper *)fileWrapper contentType:(NSString *)contentType options:(_WKAttachmentDisplayOptions *)options completion:(void(^)(BOOL success))completionHandler
{
THROW_IF_SUSPENDED;
UNUSED_PARAM(options);
return [self _insertAttachmentWithFileWrapper:fileWrapper contentType:contentType completion:completionHandler];
}
- (_WKAttachment *)_insertAttachmentWithFileWrapper:(NSFileWrapper *)fileWrapper contentType:(NSString *)contentType completion:(void(^)(BOOL success))completionHandler
{
THROW_IF_SUSPENDED;
#if ENABLE(ATTACHMENT_ELEMENT)
auto identifier = createCanonicalUUIDString();
auto attachment = API::Attachment::create(identifier, *_page);
attachment->setFileWrapperAndUpdateContentType(fileWrapper, contentType);
_page->insertAttachment(attachment.copyRef(), [capturedHandler = makeBlockPtr(completionHandler)] {
if (capturedHandler)
capturedHandler(true);
});
#if HAVE(QUICKLOOK_THUMBNAILING)
_page->requestThumbnailWithFileWrapper(fileWrapper, identifier);
#endif
return wrapper(attachment);
#else
return nil;
#endif
}
- (_WKAttachment *)_attachmentForIdentifier:(NSString *)identifier
{
THROW_IF_SUSPENDED;
#if ENABLE(ATTACHMENT_ELEMENT)
if (auto attachment = _page->attachmentForIdentifier(identifier))
return wrapper(attachment);
#endif
return nil;
}
- (void)_simulateDeviceOrientationChangeWithAlpha:(double)alpha beta:(double)beta gamma:(double)gamma
{
THROW_IF_SUSPENDED;
_page->simulateDeviceOrientationChange(alpha, beta, gamma);
}
+ (BOOL)_handlesSafeBrowsing
{
return true;
}
+ (BOOL)_willUpgradeToHTTPS:(NSURL *)url
{
#if ENABLE(CONTENT_EXTENSIONS)
return WebCore::ContentExtensions::ContentExtensionsBackend::shouldBeMadeSecure(url);
#else
return NO;
#endif
}
- (void)_showSafeBrowsingWarningWithTitle:(NSString *)title warning:(NSString *)warning details:(NSAttributedString *)details completionHandler:(void(^)(BOOL))completionHandler
{
THROW_IF_SUSPENDED;
[self _showSafeBrowsingWarningWithURL:nil title:title warning:warning detailsWithLinks:details completionHandler:^(BOOL continueUnsafeLoad, NSURL *url) {
ASSERT(!url);
completionHandler(continueUnsafeLoad);
}];
}
- (void)_showSafeBrowsingWarningWithURL:(NSURL *)url title:(NSString *)title warning:(NSString *)warning details:(NSAttributedString *)details completionHandler:(void(^)(BOOL))completionHandler
{
THROW_IF_SUSPENDED;
[self _showSafeBrowsingWarningWithURL:nil title:title warning:warning detailsWithLinks:details completionHandler:^(BOOL continueUnsafeLoad, NSURL *url) {
ASSERT(!url);
completionHandler(continueUnsafeLoad);
}];
}
- (void)_showSafeBrowsingWarningWithURL:(NSURL *)url title:(NSString *)title warning:(NSString *)warning detailsWithLinks:(NSAttributedString *)details completionHandler:(void(^)(BOOL, NSURL *))completionHandler
{
THROW_IF_SUSPENDED;
auto safeBrowsingWarning = WebKit::SafeBrowsingWarning::create(url, title, warning, details);
auto wrapper = [completionHandler = makeBlockPtr(completionHandler)] (std::variant<WebKit::ContinueUnsafeLoad, URL>&& variant) {
switchOn(variant, [&] (WebKit::ContinueUnsafeLoad continueUnsafeLoad) {
switch (continueUnsafeLoad) {
case WebKit::ContinueUnsafeLoad::Yes:
return completionHandler(YES, nil);
case WebKit::ContinueUnsafeLoad::No:
return completionHandler(NO, nil);
}
}, [&] (URL url) {
completionHandler(NO, url);
});
};
#if PLATFORM(MAC)
_impl->showSafeBrowsingWarning(safeBrowsingWarning, WTFMove(wrapper));
#else
[self _showSafeBrowsingWarning:safeBrowsingWarning completionHandler:WTFMove(wrapper)];
#endif
}
+ (NSURL *)_confirmMalwareSentinel
{
return WebKit::SafeBrowsingWarning::confirmMalwareSentinel();
}
+ (NSURL *)_visitUnsafeWebsiteSentinel
{
return WebKit::SafeBrowsingWarning::visitUnsafeWebsiteSentinel();
}
- (void)_isJITEnabled:(void(^)(BOOL))completionHandler
{
THROW_IF_SUSPENDED;
_page->isJITEnabled([completionHandler = makeBlockPtr(completionHandler)] (bool enabled) {
completionHandler(enabled);
});
}
- (void)_evaluateJavaScriptWithoutUserGesture:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler
{
THROW_IF_SUSPENDED;
[self _evaluateJavaScript:javaScriptString asAsyncFunction:NO withSourceURL:nil withArguments:nil forceUserGesture:NO inFrame:nil inWorld:WKContentWorld.pageWorld completionHandler:completionHandler];
}
- (void)_callAsyncJavaScript:(NSString *)functionBody arguments:(NSDictionary<NSString *, id> *)arguments inFrame:(WKFrameInfo *)frame inContentWorld:(WKContentWorld *)contentWorld completionHandler:(void (^)(id, NSError *error))completionHandler
{
THROW_IF_SUSPENDED;
[self _evaluateJavaScript:functionBody asAsyncFunction:YES withSourceURL:nil withArguments:arguments forceUserGesture:YES inFrame:frame inWorld:contentWorld completionHandler:completionHandler];
}
- (BOOL)_allMediaPresentationsClosed
{
#if ENABLE(FULLSCREEN_API)
bool hasOpenMediaPresentations = false;
if (auto videoFullscreenManager = _page->videoFullscreenManager()) {
hasOpenMediaPresentations = videoFullscreenManager->hasMode(WebCore::HTMLMediaElementEnums::VideoFullscreenModePictureInPicture)
|| videoFullscreenManager->hasMode(WebCore::HTMLMediaElementEnums::VideoFullscreenModeStandard);
}
if (!hasOpenMediaPresentations && _page->fullScreenManager() && _page->fullScreenManager()->isFullScreen())
hasOpenMediaPresentations = true;
return !hasOpenMediaPresentations;
#else
return true;
#endif
}
- (void)_evaluateJavaScript:(NSString *)javaScriptString inFrame:(WKFrameInfo *)frame inContentWorld:(WKContentWorld *)contentWorld completionHandler:(void (^)(id, NSError *error))completionHandler
{
THROW_IF_SUSPENDED;
[self _evaluateJavaScript:javaScriptString asAsyncFunction:NO withSourceURL:nil withArguments:nil forceUserGesture:YES inFrame:frame inWorld:contentWorld completionHandler:completionHandler];
}
- (void)_evaluateJavaScript:(NSString *)javaScriptString withSourceURL:(NSURL *)url inFrame:(WKFrameInfo *)frame inContentWorld:(WKContentWorld *)contentWorld completionHandler:(void (^)(id, NSError *error))completionHandler
{
THROW_IF_SUSPENDED;
[self _evaluateJavaScript:javaScriptString asAsyncFunction:NO withSourceURL:url withArguments:nil forceUserGesture:YES inFrame:frame inWorld:contentWorld completionHandler:completionHandler];
}
- (void)_updateWebpagePreferences:(WKWebpagePreferences *)webpagePreferences
{
THROW_IF_SUSPENDED;
if (webpagePreferences._websiteDataStore)
[NSException raise:NSInvalidArgumentException format:@"Updating WKWebsiteDataStore is only supported during decidePolicyForNavigationAction."];
if (webpagePreferences._userContentController)
[NSException raise:NSInvalidArgumentException format:@"Updating WKUserContentController is only supported during decidePolicyForNavigationAction."];
auto data = webpagePreferences->_websitePolicies->data();
_page->updateWebsitePolicies(WTFMove(data));
}
- (void)_notifyUserScripts
{
THROW_IF_SUSPENDED;
_page->notifyUserScripts();
}
- (BOOL)_deferrableUserScriptsNeedNotification
{
THROW_IF_SUSPENDED;
return _page->userScriptsNeedNotification();
}
- (BOOL)_allowsRemoteInspection
{
#if ENABLE(REMOTE_INSPECTOR)
return _page->allowsRemoteInspection();
#else
return NO;
#endif
}
- (void)_setAllowsRemoteInspection:(BOOL)allow
{
THROW_IF_SUSPENDED;
#if ENABLE(REMOTE_INSPECTOR)
_page->setAllowsRemoteInspection(allow);
#endif
}
- (NSString *)_remoteInspectionNameOverride
{
#if ENABLE(REMOTE_INSPECTOR)
return _page->remoteInspectionNameOverride();
#else
return nil;
#endif
}
- (void)_setRemoteInspectionNameOverride:(NSString *)name
{
THROW_IF_SUSPENDED;
#if ENABLE(REMOTE_INSPECTOR)
_page->setRemoteInspectionNameOverride(name);
#endif
}
- (BOOL)_addsVisitedLinks
{
return _page->addsVisitedLinks();
}
- (void)_setAddsVisitedLinks:(BOOL)addsVisitedLinks
{
THROW_IF_SUSPENDED;
_page->setAddsVisitedLinks(addsVisitedLinks);
}
- (BOOL)_networkRequestsInProgress
{
return _page->pageLoadState().networkRequestsInProgress();
}
static inline OptionSet<WebCore::LayoutMilestone> layoutMilestones(_WKRenderingProgressEvents events)
{
OptionSet<WebCore::LayoutMilestone> milestones;
if (events & _WKRenderingProgressEventFirstLayout)
milestones.add(WebCore::DidFirstLayout);
if (events & _WKRenderingProgressEventFirstVisuallyNonEmptyLayout)
milestones.add(WebCore::DidFirstVisuallyNonEmptyLayout);
if (events & _WKRenderingProgressEventFirstPaintWithSignificantArea)
milestones.add(WebCore::DidHitRelevantRepaintedObjectsAreaThreshold);
if (events & _WKRenderingProgressEventReachedSessionRestorationRenderTreeSizeThreshold)
milestones.add(WebCore::ReachedSessionRestorationRenderTreeSizeThreshold);
if (events & _WKRenderingProgressEventFirstLayoutAfterSuppressedIncrementalRendering)
milestones.add(WebCore::DidFirstLayoutAfterSuppressedIncrementalRendering);
if (events & _WKRenderingProgressEventFirstPaintAfterSuppressedIncrementalRendering)
milestones.add(WebCore::DidFirstPaintAfterSuppressedIncrementalRendering);
if (events & _WKRenderingProgressEventDidRenderSignificantAmountOfText)
milestones.add(WebCore::DidRenderSignificantAmountOfText);
if (events & _WKRenderingProgressEventFirstMeaningfulPaint)
milestones.add(WebCore::DidFirstMeaningfulPaint);
return milestones;
}
- (void)_setObservedRenderingProgressEvents:(_WKRenderingProgressEvents)observedRenderingProgressEvents
{
_observedRenderingProgressEvents = observedRenderingProgressEvents;
_page->listenForLayoutMilestones(layoutMilestones(observedRenderingProgressEvents));
}
- (void)_getMainResourceDataWithCompletionHandler:(void (^)(NSData *, NSError *))completionHandler
{
THROW_IF_SUSPENDED;
_page->getMainResourceDataOfFrame(_page->mainFrame(), [completionHandler = makeBlockPtr(completionHandler)](API::Data* data) {
completionHandler(wrapper(data), nil);
});
}
- (void)_getWebArchiveDataWithCompletionHandler:(void (^)(NSData *, NSError *))completionHandler
{
THROW_IF_SUSPENDED;
[self createWebArchiveDataWithCompletionHandler:completionHandler];
}
- (void)_getContentsAsStringWithCompletionHandler:(void (^)(NSString *, NSError *))completionHandler
{
THROW_IF_SUSPENDED;
_page->getContentsAsString(WebKit::ContentAsStringIncludesChildFrames::No, [handler = makeBlockPtr(completionHandler)](String string) {
handler(string, nil);
});
}
- (void)_getContentsAsStringWithCompletionHandlerKeepIPCConnectionAliveForTesting:(void (^)(NSString *, NSError *))completionHandler
{
THROW_IF_SUSPENDED;
_page->getContentsAsString(WebKit::ContentAsStringIncludesChildFrames::No, [handler = makeBlockPtr(completionHandler), connection = RefPtr { _page->process().connection() }](String string) {
handler(string, nil);
});
}
- (void)_getContentsOfAllFramesAsStringWithCompletionHandler:(void (^)(NSString *))completionHandler
{
THROW_IF_SUSPENDED;
_page->getContentsAsString(WebKit::ContentAsStringIncludesChildFrames::Yes, [handler = makeBlockPtr(completionHandler)](String string) {
handler(string);
});
}
- (void)_getContentsAsAttributedStringWithCompletionHandler:(void (^)(NSAttributedString *, NSDictionary<NSAttributedStringDocumentAttributeKey, id> *, NSError *))completionHandler
{
THROW_IF_SUSPENDED;
_page->getContentsAsAttributedString([handler = makeBlockPtr(completionHandler)](auto& attributedString) {
if (attributedString.string)
handler(attributedString.string.get(), attributedString.documentAttributes.get(), nil);
else
handler(nil, nil, createNSError(WKErrorUnknown).get());
});
}
- (void)_getApplicationManifestWithCompletionHandler:(void (^)(_WKApplicationManifest *))completionHandler
{
THROW_IF_SUSPENDED;
#if ENABLE(APPLICATION_MANIFEST)
_page->getApplicationManifest([completionHandler = makeBlockPtr(completionHandler)](const std::optional<WebCore::ApplicationManifest>& manifest) {
if (completionHandler) {
if (manifest) {
auto apiManifest = API::ApplicationManifest::create(*manifest);
completionHandler(wrapper(apiManifest));
} else
completionHandler(nil);
}
});
#else
if (completionHandler)
completionHandler(nil);
#endif
}
- (_WKPaginationMode)_paginationMode
{
switch (_page->paginationMode()) {
case WebCore::Pagination::Unpaginated:
return _WKPaginationModeUnpaginated;
case WebCore::Pagination::LeftToRightPaginated:
return _WKPaginationModeLeftToRight;
case WebCore::Pagination::RightToLeftPaginated:
return _WKPaginationModeRightToLeft;
case WebCore::Pagination::TopToBottomPaginated:
return _WKPaginationModeTopToBottom;
case WebCore::Pagination::BottomToTopPaginated:
return _WKPaginationModeBottomToTop;
}
ASSERT_NOT_REACHED();
return _WKPaginationModeUnpaginated;
}
- (void)_setPaginationMode:(_WKPaginationMode)paginationMode
{
THROW_IF_SUSPENDED;
WebCore::Pagination::Mode mode;
switch (paginationMode) {
case _WKPaginationModeUnpaginated:
mode = WebCore::Pagination::Unpaginated;
break;
case _WKPaginationModeLeftToRight:
mode = WebCore::Pagination::LeftToRightPaginated;
break;
case _WKPaginationModeRightToLeft:
mode = WebCore::Pagination::RightToLeftPaginated;
break;
case _WKPaginationModeTopToBottom:
mode = WebCore::Pagination::TopToBottomPaginated;
break;
case _WKPaginationModeBottomToTop:
mode = WebCore::Pagination::BottomToTopPaginated;
break;
default:
return;
}
_page->setPaginationMode(mode);
}
- (BOOL)_paginationBehavesLikeColumns
{
return _page->paginationBehavesLikeColumns();
}
- (void)_setPaginationBehavesLikeColumns:(BOOL)behavesLikeColumns
{
THROW_IF_SUSPENDED;
_page->setPaginationBehavesLikeColumns(behavesLikeColumns);
}
- (CGFloat)_pageLength
{
return _page->pageLength();
}
- (void)_setPageLength:(CGFloat)pageLength
{
THROW_IF_SUSPENDED;
_page->setPageLength(pageLength);
}
- (CGFloat)_gapBetweenPages
{
return _page->gapBetweenPages();
}
- (void)_setGapBetweenPages:(CGFloat)gapBetweenPages
{
THROW_IF_SUSPENDED;
_page->setGapBetweenPages(gapBetweenPages);
}
- (BOOL)_paginationLineGridEnabled
{
return _page->paginationLineGridEnabled();
}
- (void)_setPaginationLineGridEnabled:(BOOL)lineGridEnabled
{
THROW_IF_SUSPENDED;
_page->setPaginationLineGridEnabled(lineGridEnabled);
}
- (NSUInteger)_pageCount
{
return _page->pageCount();
}
- (BOOL)_supportsTextZoom
{
return _page->supportsTextZoom();
}
- (double)_textZoomFactor
{
return _page->textZoomFactor();
}
- (void)_setTextZoomFactor:(double)zoomFactor
{
THROW_IF_SUSPENDED;
_page->setTextZoomFactor(zoomFactor);
}
- (double)_pageZoomFactor
{
return [self pageZoom];
}
- (void)_setPageZoomFactor:(double)zoomFactor
{
THROW_IF_SUSPENDED;
[self setPageZoom:zoomFactor];
}
- (id <_WKDiagnosticLoggingDelegate>)_diagnosticLoggingDelegate
{
auto* diagnosticLoggingClient = _page->diagnosticLoggingClient();
if (!diagnosticLoggingClient)
return nil;
return static_cast<WebKit::DiagnosticLoggingClient&>(*diagnosticLoggingClient).delegate().autorelease();
}
- (void)_setDiagnosticLoggingDelegate:(id<_WKDiagnosticLoggingDelegate>)diagnosticLoggingDelegate
{
auto* diagnosticLoggingClient = _page->diagnosticLoggingClient();
if (!diagnosticLoggingClient)
return;
static_cast<WebKit::DiagnosticLoggingClient&>(*diagnosticLoggingClient).setDelegate(diagnosticLoggingDelegate);
}
- (id <_WKFindDelegate>)_findDelegate
{
return static_cast<WebKit::FindClient&>(_page->findClient()).delegate().autorelease();
}
- (void)_setFindDelegate:(id<_WKFindDelegate>)findDelegate
{
static_cast<WebKit::FindClient&>(_page->findClient()).setDelegate(findDelegate);
}
static inline OptionSet<WebKit::FindOptions> toFindOptions(_WKFindOptions wkFindOptions)
{
OptionSet<WebKit::FindOptions> findOptions;
if (wkFindOptions & _WKFindOptionsCaseInsensitive)
findOptions.add(WebKit::FindOptions::CaseInsensitive);
if (wkFindOptions & _WKFindOptionsAtWordStarts)
findOptions.add(WebKit::FindOptions::AtWordStarts);
if (wkFindOptions & _WKFindOptionsTreatMedialCapitalAsWordStart)
findOptions.add(WebKit::FindOptions::TreatMedialCapitalAsWordStart);
if (wkFindOptions & _WKFindOptionsBackwards)
findOptions.add(WebKit::FindOptions::Backwards);
if (wkFindOptions & _WKFindOptionsWrapAround)
findOptions.add(WebKit::FindOptions::WrapAround);
if (wkFindOptions & _WKFindOptionsShowOverlay)
findOptions.add(WebKit::FindOptions::ShowOverlay);
if (wkFindOptions & _WKFindOptionsShowFindIndicator)
findOptions.add(WebKit::FindOptions::ShowFindIndicator);
if (wkFindOptions & _WKFindOptionsShowHighlight)
findOptions.add(WebKit::FindOptions::ShowHighlight);
if (wkFindOptions & _WKFindOptionsNoIndexChange)
findOptions.add(WebKit::FindOptions::NoIndexChange);
if (wkFindOptions & _WKFindOptionsDetermineMatchIndex)
findOptions.add(WebKit::FindOptions::DetermineMatchIndex);
return findOptions;
}
- (void)_countStringMatches:(NSString *)string options:(_WKFindOptions)options maxCount:(NSUInteger)maxCount
{
THROW_IF_SUSPENDED;
#if PLATFORM(IOS_FAMILY)
if (_customContentView) {
[_customContentView web_countStringMatches:string options:options maxCount:maxCount];
return;
}
#endif
_page->countStringMatches(string, toFindOptions(options), maxCount);
}
- (void)_findString:(NSString *)string options:(_WKFindOptions)options maxCount:(NSUInteger)maxCount
{
THROW_IF_SUSPENDED;
#if PLATFORM(IOS_FAMILY)
// While AppKit contains logic in NSBarTextFinder to automatically update the find pasteboard
// when the find string changes, this (along with the find pasteboard itself) are both missing
// from iOS; thus, on iOS, we update the current find-in-page string here.
WebKit::updateStringForFind(string);
if (_customContentView) {
[_customContentView web_findString:string options:options maxCount:maxCount];
return;
}
#endif
_page->findString(string, toFindOptions(options), maxCount);
}
- (void)_hideFindUI
{
THROW_IF_SUSPENDED;
#if PLATFORM(IOS_FAMILY)
if (_customContentView) {
[_customContentView web_hideFindUI];
return;
}
#endif
_page->hideFindUI();
}
- (void)_saveBackForwardSnapshotForItem:(WKBackForwardListItem *)item
{
THROW_IF_SUSPENDED;
if (!item)
return;
_page->recordNavigationSnapshot(item._item);
}
- (void)_serviceWorkersEnabled:(void(^)(BOOL))completionHandler
{
auto enabled = [_configuration preferences]->_preferences.get()->serviceWorkersEnabled() || WebCore::RuntimeEnabledFeatures::sharedFeatures().serviceWorkerEnabled();
completionHandler(enabled);
}
- (void)_clearServiceWorkerEntitlementOverride:(void (^)(void))completionHandler
{
THROW_IF_SUSPENDED;
_page->clearServiceWorkerEntitlementOverride([completionHandler = makeBlockPtr(completionHandler)] {
completionHandler();
});
}
- (void)_preconnectToServer:(NSURL *)url
{
THROW_IF_SUSPENDED;
_page->preconnectTo(url);
}
- (BOOL)_canUseCredentialStorage
{
return _page->canUseCredentialStorage();
}
- (void)_setCanUseCredentialStorage:(BOOL)canUseCredentialStorage
{
THROW_IF_SUSPENDED;
_page->setCanUseCredentialStorage(canUseCredentialStorage);
}
// FIXME: Remove old `-[WKWebView _themeColor]` SPI <rdar://76662644>
- (WebCore::CocoaColor *)_themeColor
{
return [self themeColor];
}
// FIXME: Remove old `-[WKWebView _pageExtendedBackgroundColor]` SPI <rdar://77789732>
- (WebCore::CocoaColor *)_pageExtendedBackgroundColor
{
return cocoaColorOrNil(_page->pageExtendedBackgroundColor()).autorelease();
}
- (WebCore::CocoaColor *)_sampledPageTopColor
{
return cocoaColorOrNil(_page->sampledPageTopColor()).autorelease();
}
- (id <_WKInputDelegate>)_inputDelegate
{
return _inputDelegate.getAutoreleased();
}
- (void)_setInputDelegate:(id <_WKInputDelegate>)inputDelegate
{
_inputDelegate = inputDelegate;
class FormClient : public API::FormClient {
WTF_MAKE_FAST_ALLOCATED;
public:
explicit FormClient(WKWebView *webView)
: m_webView(webView)
{
}
virtual ~FormClient() { }
void willSubmitForm(WebKit::WebPageProxy&, WebKit::WebFrameProxy&, WebKit::WebFrameProxy& sourceFrame, const Vector<std::pair<WTF::String, WTF::String>>& textFieldValues, API::Object* userData, WTF::Function<void(void)>&& completionHandler) override
{
if (userData && userData->type() != API::Object::Type::Data) {
ASSERT(!userData || userData->type() == API::Object::Type::Data);
m_webView->_page->process().connection()->markCurrentlyDispatchedMessageAsInvalid();
completionHandler();
return;
}
auto inputDelegate = m_webView->_inputDelegate.get();
if (![inputDelegate respondsToSelector:@selector(_webView:willSubmitFormValues:userObject:submissionHandler:)]) {
completionHandler();
return;
}
auto valueMap = adoptNS([[NSMutableDictionary alloc] initWithCapacity:textFieldValues.size()]);
for (const auto& pair : textFieldValues)
[valueMap setObject:pair.second forKey:pair.first];
NSObject <NSSecureCoding> *userObject = nil;
if (API::Data* data = static_cast<API::Data*>(userData)) {
auto nsData = adoptNS([[NSData alloc] initWithBytesNoCopy:const_cast<unsigned char*>(data->bytes()) length:data->size() freeWhenDone:NO]);
auto unarchiver = adoptNS([[NSKeyedUnarchiver alloc] initForReadingFromData:nsData.get() error:nullptr]);
unarchiver.get().decodingFailurePolicy = NSDecodingFailurePolicyRaiseException;
@try {
auto* allowedClasses = m_webView->_page->process().processPool().allowedClassesForParameterCoding();
userObject = [unarchiver decodeObjectOfClasses:allowedClasses forKey:@"userObject"];
} @catch (NSException *exception) {
LOG_ERROR("Failed to decode user data: %@", exception);
}
}
auto checker = WebKit::CompletionHandlerCallChecker::create(inputDelegate.get(), @selector(_webView:willSubmitFormValues:userObject:submissionHandler:));
[inputDelegate _webView:m_webView willSubmitFormValues:valueMap.get() userObject:userObject submissionHandler:makeBlockPtr([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)] {
if (checker->completionHandlerHasBeenCalled())
return;
checker->didCallCompletionHandler();
completionHandler();
}).get()];
}
private:
WKWebView *m_webView;
};
if (inputDelegate)
_page->setFormClient(makeUnique<FormClient>(self));
else
_page->setFormClient(nullptr);
}
- (BOOL)_isDisplayingStandaloneImageDocument
{
if (auto* mainFrame = _page->mainFrame())
return mainFrame->isDisplayingStandaloneImageDocument();
return NO;
}
- (BOOL)_isDisplayingStandaloneMediaDocument
{
if (auto* mainFrame = _page->mainFrame())
return mainFrame->isDisplayingStandaloneMediaDocument();
return NO;
}
- (BOOL)_isPlayingAudio
{
return _page->isPlayingAudio();
}
- (BOOL)_isShowingNavigationGestureSnapshot
{
return _page->isShowingNavigationGestureSnapshot();
}
- (_WKLayoutMode)_layoutMode
{
#if PLATFORM(MAC)
switch (_impl->layoutMode()) {
case kWKLayoutModeFixedSize:
return _WKLayoutModeFixedSize;
case kWKLayoutModeDynamicSizeComputedFromViewScale:
return _WKLayoutModeDynamicSizeComputedFromViewScale;
case kWKLayoutModeDynamicSizeComputedFromMinimumDocumentSize:
return _WKLayoutModeDynamicSizeComputedFromMinimumDocumentSize;
case kWKLayoutModeViewSize:
default:
return _WKLayoutModeViewSize;
}
#else
return _page->useFixedLayout() ? _WKLayoutModeFixedSize : _WKLayoutModeViewSize;
#endif
}
- (void)_setLayoutMode:(_WKLayoutMode)layoutMode
{
THROW_IF_SUSPENDED;
#if PLATFORM(MAC)
WKLayoutMode wkViewLayoutMode;
switch (layoutMode) {
case _WKLayoutModeFixedSize:
wkViewLayoutMode = kWKLayoutModeFixedSize;
break;
case _WKLayoutModeDynamicSizeComputedFromViewScale:
wkViewLayoutMode = kWKLayoutModeDynamicSizeComputedFromViewScale;
break;
case _WKLayoutModeDynamicSizeComputedFromMinimumDocumentSize:
wkViewLayoutMode = kWKLayoutModeDynamicSizeComputedFromMinimumDocumentSize;
break;
case _WKLayoutModeViewSize:
default:
wkViewLayoutMode = kWKLayoutModeViewSize;
break;
}
_impl->setLayoutMode(wkViewLayoutMode);
#else
_page->setUseFixedLayout(layoutMode == _WKLayoutModeFixedSize || layoutMode == _WKLayoutModeDynamicSizeComputedFromViewScale);
#endif
}
- (CGSize)_fixedLayoutSize
{
return _page->fixedLayoutSize();
}
- (void)_setFixedLayoutSize:(CGSize)fixedLayoutSize
{
THROW_IF_SUSPENDED;
_page->setFixedLayoutSize(WebCore::expandedIntSize(WebCore::FloatSize(fixedLayoutSize)));
}
- (void)_setBackgroundExtendsBeyondPage:(BOOL)backgroundExtends
{
THROW_IF_SUSPENDED;
_page->setBackgroundExtendsBeyondPage(backgroundExtends);
}
- (BOOL)_backgroundExtendsBeyondPage
{
return _page->backgroundExtendsBeyondPage();
}
- (CGFloat)_viewScale
{
#if PLATFORM(MAC)
return _page->viewScaleFactor();
#else
return _page->layoutSizeScaleFactor();
#endif
}
- (void)_setViewScale:(CGFloat)viewScale
{
THROW_IF_SUSPENDED;
if (viewScale <= 0 || isnan(viewScale) || isinf(viewScale))
[NSException raise:NSInvalidArgumentException format:@"View scale should be a positive number"];
#if PLATFORM(MAC)
_impl->setViewScale(viewScale);
#else
if (_page->layoutSizeScaleFactor() == viewScale)
return;
_page->setViewportConfigurationViewLayoutSize(_page->viewLayoutSize(), viewScale, _page->minimumEffectiveDeviceWidth());
#endif
}
- (NSArray<NSString *> *)_corsDisablingPatterns
{
return createNSArray(_page->corsDisablingPatterns()).autorelease();
}
- (void)_setCORSDisablingPatterns:(NSArray<NSString *> *)patterns
{
THROW_IF_SUSPENDED;
_page->setCORSDisablingPatterns(makeVector<String>(patterns));
}
- (void)_getProcessDisplayNameWithCompletionHandler:(void (^)(NSString *))completionHandler
{
THROW_IF_SUSPENDED;
_page->getProcessDisplayName([handler = makeBlockPtr(completionHandler)](auto&& name) {
handler(name);
});
}
- (void)_setMinimumEffectiveDeviceWidth:(CGFloat)minimumEffectiveDeviceWidth
{
THROW_IF_SUSPENDED;
#if PLATFORM(IOS_FAMILY)
if (_page->minimumEffectiveDeviceWidth() == minimumEffectiveDeviceWidth)
return;
if (_dynamicViewportUpdateMode == WebKit::DynamicViewportUpdateMode::NotResizing)
_page->setViewportConfigurationViewLayoutSize(_page->viewLayoutSize(), _page->layoutSizeScaleFactor(), minimumEffectiveDeviceWidth);
else
_page->setMinimumEffectiveDeviceWidthWithoutViewportConfigurationUpdate(minimumEffectiveDeviceWidth);
#endif
}
- (CGFloat)_minimumEffectiveDeviceWidth
{
#if PLATFORM(IOS_FAMILY)
return _page->minimumEffectiveDeviceWidth();
#else
return 0;
#endif
}
- (void)_grantAccessToPreferenceService
{
THROW_IF_SUSPENDED;
if (_page)
_page->grantAccessToPreferenceService();
}
#pragma mark - scrollPerformanceData
- (void)_setScrollPerformanceDataCollectionEnabled:(BOOL)enabled
{
THROW_IF_SUSPENDED;
_page->setScrollPerformanceDataCollectionEnabled(enabled);
}
- (BOOL)_scrollPerformanceDataCollectionEnabled
{
return _page->scrollPerformanceDataCollectionEnabled();
}
- (NSArray *)_scrollPerformanceData
{
#if PLATFORM(IOS_FAMILY)
if (WebKit::RemoteLayerTreeScrollingPerformanceData* scrollPerfData = _page->scrollingPerformanceData())
return scrollPerfData->data();
#endif
return nil;
}
#pragma mark - Media playback restrictions
- (BOOL)_allowsMediaDocumentInlinePlayback
{
#if PLATFORM(IOS_FAMILY)
return _page->allowsMediaDocumentInlinePlayback();
#else
return NO;
#endif
}
- (void)_setAllowsMediaDocumentInlinePlayback:(BOOL)flag
{
THROW_IF_SUSPENDED;
#if PLATFORM(IOS_FAMILY)
_page->setAllowsMediaDocumentInlinePlayback(flag);
#endif
}
// FIXME: Remove this after Safari adopts the new API
- (void)_setFullscreenDelegate:(id<_WKFullscreenDelegate>)delegate
{
#if ENABLE(FULLSCREEN_API)
if (is<WebKit::FullscreenClient>(_page->fullscreenClient()))
downcast<WebKit::FullscreenClient>(_page->fullscreenClient()).setDelegate(delegate);
#endif
}
// FIXME: Remove this after Safari adopts the new API
- (id<_WKFullscreenDelegate>)_fullscreenDelegate
{
#if ENABLE(FULLSCREEN_API)
if (is<WebKit::FullscreenClient>(_page->fullscreenClient()))
return downcast<WebKit::FullscreenClient>(_page->fullscreenClient()).delegate().autorelease();
#endif
return nil;
}
// FIXME: Remove this after Safari adopts the new API
- (BOOL)_isInFullscreen
{
#if ENABLE(FULLSCREEN_API)
return _page->fullScreenManager() && _page->fullScreenManager()->isFullScreen();
#else
return false;
#endif
}
- (_WKMediaCaptureStateDeprecated)_mediaCaptureState
{
return WebKit::toWKMediaCaptureStateDeprecated(_page->reportedMediaState());
}
- (void)_setMediaCaptureEnabled:(BOOL)enabled
{
THROW_IF_SUSPENDED;
_page->setMediaCaptureEnabled(enabled);
}
- (BOOL)_mediaCaptureEnabled
{
return _page->mediaCaptureEnabled();
}
- (void)_setPageMuted:(_WKMediaMutedState)mutedState
{
THROW_IF_SUSPENDED;
WebCore::MediaProducerMutedStateFlags coreState;
if (mutedState & _WKMediaAudioMuted)
coreState.add(WebCore::MediaProducerMutedState::AudioIsMuted);
if (mutedState & _WKMediaCaptureDevicesMuted)
coreState.add(WebCore::MediaProducer::AudioAndVideoCaptureIsMuted);
if (mutedState & _WKMediaScreenCaptureMuted)
coreState.add(WebCore::MediaProducerMutedState::ScreenCaptureIsMuted);
_page->setMuted(coreState);
}
- (void)_removeDataDetectedLinks:(dispatch_block_t)completion
{
THROW_IF_SUSPENDED;
#if ENABLE(DATA_DETECTION)
_page->removeDataDetectedLinks([completion = makeBlockPtr(completion), page = WeakPtr { _page.get() }] (auto& result) {
if (page)
page->setDataDetectionResult(result);
if (completion)
completion();
});
#else
UNUSED_PARAM(completion);
#endif
}
// Execute the supplied block after the next transaction from the WebProcess.
- (void)_doAfterNextPresentationUpdate:(void (^)(void))updateBlock
{
THROW_IF_SUSPENDED;
[self _internalDoAfterNextPresentationUpdate:updateBlock withoutWaitingForPainting:NO withoutWaitingForAnimatedResize:NO];
}
- (void)_doAfterNextPresentationUpdateWithoutWaitingForPainting:(void (^)(void))updateBlock
{
THROW_IF_SUSPENDED;
[self _internalDoAfterNextPresentationUpdate:updateBlock withoutWaitingForPainting:YES withoutWaitingForAnimatedResize:NO];
}
@end
@implementation WKWebView (WKDeprecated)
- (NSArray *)certificateChain
{
auto certificateInfo = _page->pageLoadState().certificateInfo();
if (!certificateInfo)
return @[ ];
return (__bridge NSArray *)certificateInfo->certificateInfo().certificateChain() ?: @[ ];
}
@end
@implementation WKWebView (WKBinaryCompatibilityWithIOS10)
- (id <_WKInputDelegate>)_formDelegate
{
return self._inputDelegate;
}
- (void)_setFormDelegate:(id <_WKInputDelegate>)formDelegate
{
self._inputDelegate = formDelegate;
}
@end
#undef WKWEBVIEW_RELEASE_LOG