Bug Summary

File:Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm
Warning:line 459, column 1
The return value is +1 and results in a memory leak

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple arm64-apple-ios26.2.0-simulator -Os -Wundef-prefix=TARGET_OS_ -Werror=undef-prefix -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -Werror=implicit-function-declaration -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name UnifiedSource38-nonARC.mm -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=osx -analyzer-checker=security.insecureAPI.decodeValueOfObjCType -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -mframe-pointer=non-leaf -ffp-contract=on -fno-rounding-math -target-sdk-version=26.2 -fdepfile-entry=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/SDKSettings.json -fcompatibility-qualified-id-block-type-checking -fvisibility-inlines-hidden-static-local-var -fno-modulemap-allow-subdirectory-search -fdefine-target-os-macros -enable-tlsdesc -target-cpu apple-m1 -target-feature +v8.4a -target-feature +aes -target-feature +altnzcv -target-feature +ccdp -target-feature +ccpp -target-feature +complxnum -target-feature +crc -target-feature +dotprod -target-feature +flagm -target-feature +fp-armv8 -target-feature +fp16fml -target-feature +fptoint -target-feature +fullfp16 -target-feature +jsconv -target-feature +lse -target-feature +neon -target-feature +pauth -target-feature +perfmon -target-feature +predres -target-feature +ras -target-feature +rcpc -target-feature +rdm -target-feature +sb -target-feature +sha2 -target-feature +sha3 -target-feature +specrestrict -target-feature +ssbs -target-abi darwinpcs -dwarf-ext-refs -fmodule-format=obj -debug-info-kind=standalone -dwarf-version=5 -debugger-tuning=lldb -fdebug-compilation-dir=/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit -target-linker-version 1230.1 -fcoverage-compilation-dir=/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit -resource-dir /Users/buildbot/Library/Developer/Toolchains/swift-webkit.xctoolchain/usr/lib/clang/21 -dependency-file /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/scan-build-output/StaticAnalyzer/WebKit/WebKit/normal/arm64/UnifiedSource38-nonARC.d -skip-unused-modulemap-deps -MT dependencies -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk -iquote /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release-iphonesimulator/WebKit.build/WebKit-generated-files.hmap -iquote /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release-iphonesimulator/WebKit.build/WebKit-project-headers.hmap -isystem /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitLibraries/SDKs/iphonesimulator26.0-additions.sdk/usr/local/include -isystem /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/SDKAdditions/usr/include -iframework /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/SDKAdditions/System/Library/Frameworks -iframework /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/System/Library/PrivateFrameworks -isystem /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitLibraries/SDKs/iphonesimulator26.0-additions.sdk/usr/local/include -include-pch /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/PrecompiledHeaders/SharedPrecompiledHeaders/3938699508861868627/WebKit2Prefix.h.gch -D _LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE -D NDEBUG -D HAVE_CORE_PREDICTION -D ENABLE_LOWER_FORMATREADERBUNDLE_CODESIGNING_REQUIREMENTS -D WEBKIT_BUNDLE_VERSION="625.1.8+" -D FRAMEWORK_NAME=WebKit -D OBJC_OLD_DISPATCH_PROTOTYPES=0 -D __clang_analyzer__ -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release-iphonesimulator/WebKit.build/WebKit-own-target-headers.hmap -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release-iphonesimulator/WebKit.build/WebKit-all-target-headers.hmap -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/include -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/usr/local/include -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/WebCore.framework/PrivateHeaders/ForwardingHeaders -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/DerivedSources/WebKit -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/DerivedSources/WebKit/IPC -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/usr/local/include/pal/graphics/WebGPU -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/local/include/pal/graphics/WebGPU -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/usr/local/include/WebKitAdditions -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/local/include/WebKitAdditions -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/usr/local/include/webrtc -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/local/include/webrtc -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/usr/local/include/webm -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/local/include/webm -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release-iphonesimulator/WebKit.build/DerivedSources-normal/arm64 -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release-iphonesimulator/WebKit.build/DerivedSources/arm64 -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release-iphonesimulator/WebKit.build/DerivedSources -F/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator -F/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitLibraries/SDKs/iphonesimulator26.0-additions.sdk/System/Library/PrivateFrameworks -F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/System/Library/PrivateFrameworks -D __STDC_WANT_LIB_EXT1__=1 -I/usr/local/include -stdlib=libc++ -internal-isystem /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/include/c++/v1 -internal-isystem /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/local/include -internal-isystem /Users/buildbot/Library/Developer/Toolchains/swift-webkit.xctoolchain/usr/lib/clang/21/include -internal-externc-isystem /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/include -internal-iframework /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/System/Library/Frameworks -internal-iframework /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/System/Library/SubFrameworks -internal-iframework /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/Library/Frameworks -Wno-trigraphs -Werror -Wno-missing-field-initializers -Wmissing-prototypes -Wunreachable-code -Wno-implicit-atomic-properties -Wno-objc-interface-ivars -Wno-arc-repeated-use-of-weak -Wimplicit-retain-self -Wnon-virtual-dtor -Wno-overloaded-virtual -Wno-exit-time-destructors -Wduplicate-method-match -Wno-missing-braces -Wparentheses -Wswitch -Wunused-function -Wno-unused-label -Wno-unused-parameter -Wunused-variable -Wunused-value -Wempty-body -Wuninitialized -Wno-unknown-pragmas -Wno-shadow -Wno-four-char-constants -Wno-conversion -Wconstant-conversion -Wint-conversion -Wbool-conversion -Wenum-conversion -Wno-float-conversion -Wnon-literal-null-conversion -Wobjc-literal-conversion -Wsign-compare -Wno-shorten-64-to-32 -Wnewline-eof -Wno-selector -Wno-strict-selector-match -Wundeclared-selector -Wdeprecated-implementations -Wno-c++11-extensions -Wno-implicit-fallthrough -Wprotocol -Wdeprecated-declarations -Winvalid-offsetof -Wno-sign-conversion -Winfinite-recursion -Wmove -Wcomma -Wblock-capture-autoreleasing -Wstrict-prototypes -Wrange-loop-analysis -Wno-semicolon-before-method-body -Wall -Wc99-designator -Wconditional-uninitialized -Wextra -Wdeprecated-enum-enum-conversion -Wdeprecated-enum-float-conversion -Wenum-float-conversion -Wfinal-dtor-non-final-class -Wformat=2 -Wmisleading-indentation -Wpointer-to-int-cast -Wreorder-init-list -Wundef -Wunused-but-set-variable -Wvla -Werror=unguarded-availability -Wno-elaborated-enum-base -Wno-unused-parameter -Wcast-qual -Wchar-subscripts -Wextra-tokens -Winit-self -Wmissing-noreturn -Wpacked -Wpointer-arith -Wredundant-decls -Wwrite-strings -Wexit-time-destructors -Wglobal-constructors -Wtautological-compare -Wimplicit-fallthrough -Wvla -Wliteral-conversion -Wthread-safety -Wno-profile-instr-out-of-date -Wno-profile-instr-unprofiled -Wunsafe-buffer-usage -Wno-missing-field-initializers -Wno-nullability-completeness -std=c++2b -fdeprecated-macro -ferror-limit 19 -fmacro-backtrace-limit=0 -fvisibility=hidden -fvisibility-inlines-hidden -stack-protector 1 -fblocks -fencode-extended-block-signature -fno-rtti -fregister-global-dtors-with-atexit -fgnuc-version=4.2.1 -fno-cxx-modules -fno-implicit-modules -fskip-odr-check-in-gmf -fobjc-runtime=ios-26.2.0 -fobjc-weak -fobjc-exceptions -fexceptions -fpascal-strings -fmax-type-align=16 -fdiagnostics-show-note-include-stack -vectorize-loops -vectorize-slp -fsafe-buffer-usage-suggestions -analyzer-output=html -analyzer-config report-in-main-source-file=true -analyzer-config nullability:NoDiagnoseCallsToSystemHeaders=true -analyzer-checker optin.osx.cocoa.localizability.NonLocalizedStringChecker -analyzer-checker security.insecureAPI.UncheckedReturn -analyzer-checker security.insecureAPI.getpw -analyzer-checker security.insecureAPI.gets -analyzer-checker security.insecureAPI.mkstemp -analyzer-checker security.insecureAPI.mktemp -analyzer-disable-checker security.insecureAPI.rand -analyzer-disable-checker security.insecureAPI.strcpy -analyzer-checker security.insecureAPI.vfork -analyzer-disable-checker alpha,apiModeling,core,cplusplus,deadcode,debug,fuchsia,nullability,optin,osx,security,unix,webkit -analyzer-checker alpha.webkit.ForwardDeclChecker,alpha.webkit.MemoryUnsafeCastChecker,alpha.webkit.NoDeleteChecker,alpha.webkit.NoUncheckedPtrMemberChecker,alpha.webkit.NoUnretainedMemberChecker,alpha.webkit.RetainPtrCtorAdoptChecker,alpha.webkit.UncheckedCallArgsChecker,alpha.webkit.UncheckedLocalVarsChecker,alpha.webkit.UncountedCallArgsChecker,alpha.webkit.UncountedLocalVarsChecker,alpha.webkit.UnretainedCallArgsChecker,alpha.webkit.UnretainedLambdaCapturesChecker,alpha.webkit.UnretainedLocalVarsChecker,webkit.NoUncountedMemberChecker,webkit.RefCntblBaseVirtualDtor,webkit.UncountedLambdaCapturesChecker -analyzer-config max-nodes=10000000 -analyzer-config verbose-report-filename=true -fdwarf2-cfi-asm -o /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/scan-build-output/StaticAnalyzer/WebKit/WebKit/normal/arm64/UnifiedSource38-nonARC.plist -x objective-c++ /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/DerivedSources/WebKit/unified-sources/UnifiedSource38-nonARC.mm
1/*
2 * Copyright (C) 2014-2025 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#import "config.h"
27#import "WKWebViewIOS.h"
28
29#if PLATFORM(IOS_FAMILY)(defined 1 && 1)
30
31#import "APIFindClient.h"
32#import "DataDetectionResult.h"
33#import "FrontBoardServicesSPI.h"
34#import "ImageOptions.h"
35#import "LayerProperties.h"
36#import "NativeWebWheelEvent.h"
37#import "NavigationState.h"
38#import "PointerTouchCompatibilitySimulator.h"
39#import "RemoteLayerTreeCommitBundle.h"
40#import "RemoteLayerTreeDrawingAreaProxy.h"
41#import "RemoteLayerTreeScrollingPerformanceData.h"
42#import "RemoteLayerTreeViews.h"
43#import "RemoteScrollingCoordinatorProxyIOS.h"
44#import "ScrollingTreeScrollingNodeDelegateIOS.h"
45#import "TapHandlingResult.h"
46#import "TransactionID.h"
47#import "UIKitUtilities.h"
48#import "VideoPresentationManagerProxy.h"
49#import "ViewGestureController.h"
50#import "VisibleContentRectUpdateInfo.h"
51#import "WKAPICast.h"
52#import "WKBackForwardListItemInternal.h"
53#import "WKColorExtensionView.h"
54#import "WKContentViewInteraction.h"
55#import "WKDataDetectorTypesInternal.h"
56#import "WKErrorInternal.h"
57#import "WKPasswordView.h"
58#import "WKProcessPoolPrivate.h"
59#import "WKScrollView.h"
60#import "WKUIDelegatePrivate.h"
61#import "WKWebViewConfigurationInternal.h"
62#import "WKWebViewContentProvider.h"
63#import "WKWebViewContentProviderRegistry.h"
64#import "WKWebViewPrivate.h"
65#import "WKWebViewPrivateForTestingIOS.h"
66#import "WebBackForwardList.h"
67#import "WebIOSEventFactory.h"
68#import "WebPage.h"
69#import "WebPageProxy.h"
70#import "WebPreferences.h"
71#import "WebProcessPool.h"
72#import "_WKActivatedElementInfoInternal.h"
73#import "_WKWarningView.h"
74#import <CoreGraphics/CGColor.h>
75#import <WebCore/BoxSides.h>
76#import <WebCore/ColorCocoa.h>
77#import <WebCore/ContentsFormatCocoa.h>
78#import <WebCore/FloatConversion.h>
79#import <WebCore/GraphicsContextCG.h>
80#import <WebCore/IOSurfacePool.h>
81#import <WebCore/LocalCurrentTraitCollection.h>
82#import <WebCore/LocalFrameView.h>
83#import <WebCore/MIMETypeRegistry.h>
84#import <WebCore/UserInterfaceLayoutDirection.h>
85#import <WebCore/VelocityData.h>
86#import <pal/spi/cocoa/QuartzCoreSPI.h>
87#import <pal/spi/ios/GraphicsServicesSPI.h>
88#import <ranges>
89#import <wtf/BlockPtr.h>
90#import <wtf/Box.h>
91#import <wtf/EnumTraits.h>
92#import <wtf/FixedVector.h>
93#import <wtf/NeverDestroyed.h>
94#import <wtf/RefCounted.h>
95#import <wtf/RuntimeApplicationChecks.h>
96#import <wtf/SystemTracing.h>
97#import <wtf/cf/TypeCastsCF.h>
98#import <wtf/cocoa/Entitlements.h>
99#import <wtf/cocoa/RuntimeApplicationChecksCocoa.h>
100#import <wtf/cocoa/VectorCocoa.h>
101#import <wtf/text/MakeString.h>
102
103#if ENABLE(LOCKDOWN_MODE_API)(defined 1 && 1)
104#import "_WKSystemPreferencesInternal.h"
105#import <WebCore/LocalizedStrings.h>
106#import <wtf/spi/cf/CFBundleSPI.h>
107#endif
108
109#if HAVE(UI_EVENT_ATTRIBUTION)(defined 1 && 1)
110#import <UIKit/UIEventAttribution.h>
111#endif
112
113#if USE(APPLE_INTERNAL_SDK)(defined USE_APPLE_INTERNAL_SDK && USE_APPLE_INTERNAL_SDK
)
114#import <WebKitAdditions/WKWebViewIOSAdditionsBefore.mm>
115#endif
116
117#if HAVE(SUPPORT_HDR_DISPLAY_APIS)(defined HAVE_SUPPORT_HDR_DISPLAY_APIS && HAVE_SUPPORT_HDR_DISPLAY_APIS
)
118#import <UIKit/UITraitCollection.h>
119#endif
120
121#if ENABLE(BACK_FORWARD_LIST_SWIFT)(defined ENABLE_BACK_FORWARD_LIST_SWIFT && ENABLE_BACK_FORWARD_LIST_SWIFT
)
122#include "WebKit-Swift.h"
123#endif
124
125#import <pal/ios/ManagedConfigurationSoftLink.h>
126
127#define FORWARD_ACTION_TO_WKCONTENTVIEW(_action)- (void)_action:(id)sender { if (self.usesStandardContentView
) [_contentView _actionForWebView:sender]; }
\
128- (void)_action:(id)sender \
129{ \
130 if (self.usesStandardContentView) \
131 [_contentView _action ## ForWebView:sender]; \
132}
133
134#define WKWEBVIEW_RELEASE_LOG(...) RELEASE_LOG(ViewState, __VA_ARGS__)clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option"
clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic
ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic
ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang
::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogViewState
.osLogChannel); os_log_type_t _type_tmp = (OS_LOG_TYPE_DEFAULT
); if (os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__
({ clang diagnostic push clang diagnostic ignored "-Wvla" clang
diagnostic error "-Wformat" _Static_assert(__builtin_constant_p
(__VA_ARGS__), "format/label/description argument must be a string constant"
); __attribute__((section("__TEXT,__oslogstring,cstring_literals"
),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG53"
) = __VA_ARGS__; uint8_t _Alignas(16) __attribute__((uninitialized
)) _os_fmt_buf[__builtin_os_log_format_buffer_size(__VA_ARGS__
)]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str
, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, __VA_ARGS__
), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); }
}) clang diagnostic pop
135
136static const Seconds delayBeforeNoVisibleContentsRectsLogging = 1_s;
137static const Seconds delayBeforeNoCommitsLogging = 5_s;
138static constexpr Seconds delayBeforeUpdatingVisibleContentRectsWhenChangingObscuredInsetsInteractively = 100_ms;
139static const unsigned highlightMargin = 5;
140
141static WebCore::IntDegrees deviceOrientationForUIInterfaceOrientation(UIInterfaceOrientation orientation)
142{
143 switch (orientation) {
144 case UIInterfaceOrientationUnknown:
145 case UIInterfaceOrientationPortrait:
146 return 0;
147 case UIInterfaceOrientationPortraitUpsideDown:
148 return 180;
149 case UIInterfaceOrientationLandscapeLeft:
150 return -90;
151 case UIInterfaceOrientationLandscapeRight:
152 return 90;
153 }
154}
155
156@interface UIView (UIViewInternal)
157- (UIViewController *)_viewControllerForAncestor;
158@end
159
160@interface UIWindow (UIWindowInternal)
161- (BOOL)_isHostedInAnotherProcess;
162@end
163
164@implementation WKWebView (WKViewInternalIOS)
165
166- (void)setFrame:(CGRect)frame
167{
168 bool sizeChanged = !CGSizeEqualToSize__CGSizeEqualToSize(self.frame.size, frame.size);
169 if (sizeChanged)
170 [self _frameOrBoundsWillChange];
171
172 [super setFrame:frame];
173
174 if (sizeChanged) {
175 [self _frameOrBoundsMayHaveChanged];
176
177#if HAVE(UIKIT_RESIZABLE_WINDOWS)(defined 1 && 1)
178 [self _acquireResizeAssertionForReason:@"-[WKWebView setFrame:]"];
179#endif
180 }
181}
182
183- (void)setBounds:(CGRect)bounds
184{
185 bool sizeChanged = !CGSizeEqualToSize__CGSizeEqualToSize(self.bounds.size, bounds.size);
186 if (sizeChanged)
187 [self _frameOrBoundsWillChange];
188
189 [super setBounds:bounds];
190 [_customContentFixedOverlayView setFrame:self.bounds];
191
192 if (sizeChanged) {
193 [self _frameOrBoundsMayHaveChanged];
194
195#if HAVE(UIKIT_RESIZABLE_WINDOWS)(defined 1 && 1)
196 [self _acquireResizeAssertionForReason:@"-[WKWebView setBounds:]"];
197#endif
198 }
199}
200
201- (void)layoutSubviews
202{
203#if ENABLE(SCREEN_TIME)(defined ENABLE_SCREEN_TIME && ENABLE_SCREEN_TIME)
204 [self _updateScreenTimeViewGeometry];
205#endif
206
207 [_warningView setFrame:self.bounds];
208 [super layoutSubviews];
209 [self _frameOrBoundsMayHaveChanged];
210}
211
212#pragma mark - iOS implementation methods
213
214- (void)_setupScrollAndContentViews
215{
216 CGRect bounds = self.bounds;
217 _scrollView = adoptNS([[WKScrollView alloc] initWithFrame:bounds]);
218 [_scrollView setInternalDelegate:self];
219 [_scrollView setBaseScrollViewDelegate:self];
220 [_scrollView setBouncesZoom:YES__objc_yes];
221
222ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push GCC diagnostic ignored "-W" "deprecated-declarations"
223 if ([_scrollView respondsToSelector:@selector(_setAvoidsJumpOnInterruptedBounce:)]) {
224 [_scrollView setTracksImmediatelyWhileDecelerating:NO__objc_no];
225 [_scrollView _setAvoidsJumpOnInterruptedBounce:YES__objc_yes];
226 }
227ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop
228
229 _scrollViewDefaultAllowedTouchTypes = [_scrollView panGestureRecognizer].allowedTouchTypes;
230
231 [self _updateScrollViewInsetAdjustmentBehavior];
232
233 [self addSubview:_scrollView.get()];
234
235 [self _dispatchSetDeviceOrientation:[self _deviceOrientationIgnoringOverrides]];
236 [self _dispatchSetOrientationForMediaCapture:[self _deviceOrientationIgnoringOverrides]];
237
238 [_contentView layer].anchorPoint = CGPointZero;
239 [_contentView setFrame:bounds];
240 [_scrollView addSubview:_contentView.get()];
241 [_scrollView addSubview:[_contentView unscaledView]];
242
243#if PLATFORM(VISION)(defined WTF_PLATFORM_VISION && WTF_PLATFORM_VISION)
244 if ([[CARemoteExternalEffect class] respondsToSelector:@selector(rcp_requiresTwoHandedInteractionProcessorEffect)])
245 [_scrollView _requestRemoteEffects:@[[CARemoteExternalEffect rcp_requiresTwoHandedInteractionProcessorEffect]] forKey:@"input"];
246#endif
247}
248
249- (void)_setObscuredInsetsInternal:(UIEdgeInsets)obscuredInsets
250{
251 ASSERT(obscuredInsets.top >= 0)((void)0);
252 ASSERT(obscuredInsets.left >= 0)((void)0);
253 ASSERT(obscuredInsets.bottom >= 0)((void)0);
254 ASSERT(obscuredInsets.right >= 0)((void)0);
255
256 _haveSetObscuredInsets = YES__objc_yes;
257
258 if (UIEdgeInsetsEqualToEdgeInsets(_obscuredInsets, obscuredInsets))
259 return;
260
261 if (RefPtr page = _page) {
262 if (RefPtr drawingArea = page->drawingArea())
263 _perProcessState.firstTransactionIDAfterObscuredInsetChange = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*drawingArea).nextMainFrameLayerTreeTransactionID();
264 }
265
266#if ENABLE(CONTENT_INSET_BACKGROUND_FILL)(defined 1 && 1)
267 auto sidesWithInsets = [](UIEdgeInsets insets) {
268 WebCore::BoxSideSet sides;
269 if (insets.top > 0)
270 sides.add(WebCore::BoxSide::Top);
271 if (insets.left > 0)
272 sides.add(WebCore::BoxSide::Left);
273 if (insets.bottom > 0)
274 sides.add(WebCore::BoxSide::Bottom);
275 if (insets.right > 0)
276 sides.add(WebCore::BoxSide::Right);
277 return sides;
278 };
279 BOOL updateFixedColorExtensionViews = sidesWithInsets(_obscuredInsets) != sidesWithInsets(obscuredInsets);
280#endif // ENABLE(CONTENT_INSET_BACKGROUND_FILL)
281
282 _obscuredInsets = obscuredInsets;
283
284 [self _scheduleVisibleContentRectUpdate];
285#if ENABLE(CONTENT_INSET_BACKGROUND_FILL)(defined 1 && 1)
286 if (updateFixedColorExtensionViews)
287 [self _updateFixedColorExtensionViews];
288 else
289 [self _updateFixedColorExtensionViewFrames];
290#endif
291 [_warningView setContentInset:[self _computedObscuredInsetForWarningView]];
292}
293
294- (void)_registerForNotifications
295{
296 NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
297 [center addObserver:self selector:@selector(_keyboardWillChangeFrame:) name:UIKeyboardWillChangeFrameNotification object:nilnullptr];
298 [center addObserver:self selector:@selector(_keyboardDidChangeFrame:) name:UIKeyboardDidChangeFrameNotification object:nilnullptr];
299 [center addObserver:self selector:@selector(_keyboardWillShow:) name:UIKeyboardWillShowNotification object:nilnullptr];
300 [center addObserver:self selector:@selector(_keyboardDidShow:) name:UIKeyboardDidShowNotification object:nilnullptr];
301 [center addObserver:self selector:@selector(_keyboardWillHide:) name:UIKeyboardWillHideNotification object:nilnullptr];
302 [center addObserver:self selector:@selector(_windowDidRotate:) name:UIWindowDidRotateNotification object:nilnullptr];
303 [center addObserver:self selector:@selector(_contentSizeCategoryDidChange:) name:UIContentSizeCategoryDidChangeNotification object:nilnullptr];
304
305 [center addObserver:self selector:@selector(_accessibilitySettingsDidChange:) name:UIAccessibilityGrayscaleStatusDidChangeNotification object:nilnullptr];
306 [center addObserver:self selector:@selector(_accessibilitySettingsDidChange:) name:UIAccessibilityInvertColorsStatusDidChangeNotification object:nilnullptr];
307 [center addObserver:self selector:@selector(_accessibilitySettingsDidChange:) name:UIAccessibilityReduceMotionStatusDidChangeNotification object:nilnullptr];
308
309#if HAVE(UIKIT_RESIZABLE_WINDOWS)(defined 1 && 1)
310 [center addObserver:self selector:@selector(_enhancedWindowingToggled:) name:_UIWindowSceneEnhancedWindowingModeChanged object:nilnullptr];
311#endif
312
313#if HAVE(SUPPORT_HDR_DISPLAY_APIS)(defined HAVE_SUPPORT_HDR_DISPLAY_APIS && HAVE_SUPPORT_HDR_DISPLAY_APIS
)
314 [self registerForTraitChanges:@[[UITraitHDRHeadroomUsageLimit class]] withAction:@selector(_hdrHeadroomUsageLimitDidChange)];
315 [self _hdrHeadroomUsageLimitDidChange];
316#endif // HAVE(SUPPORT_HDR_DISPLAY_APIS)
317}
318
319- (BOOL)_isShowingVideoPictureInPicture
320{
321#if ENABLE(VIDEO_PRESENTATION_MODE)(defined 1 && 1)
322 if (!_page || !_page->videoPresentationManager())
323 return false;
324
325 return protect(_page->videoPresentationManager())->hasMode(WebCore::HTMLMediaElementEnums::VideoFullscreenModePictureInPicture);
326#else
327 return false;
328#endif
329}
330
331- (BOOL)_mayAutomaticallyShowVideoPictureInPicture
332{
333#if ENABLE(VIDEO_PRESENTATION_MODE)(defined 1 && 1)
334 if (!_page || !_page->videoPresentationManager())
335 return false;
336
337 return protect(_page->videoPresentationManager())->mayAutomaticallyShowVideoPictureInPicture();
338#else
339 return false;
340#endif
341}
342
343- (void)_incrementFocusPreservationCount
344{
345 ++_focusPreservationCount;
346}
347
348- (void)_decrementFocusPreservationCount
349{
350 if (_focusPreservationCount)
351 --_focusPreservationCount;
352}
353
354- (void)_resetFocusPreservationCountAndReleaseActiveFocusState
355{
356 if (std::exchange(_focusPreservationCount, 0))
357 RELEASE_LOG_ERROR(ViewState, "Keyboard dismissed with nonzero focus preservation count; check for unbalanced calls to -_incrementFocusPreservationCount")clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option"
clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic
ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic
ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang
::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogViewState
.osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if
(os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({
clang diagnostic push clang diagnostic ignored "-Wvla" clang
diagnostic error "-Wformat" _Static_assert(__builtin_constant_p
("Keyboard dismissed with nonzero focus preservation count; check for unbalanced calls to -_incrementFocusPreservationCount"
), "format/label/description argument must be a string constant"
); __attribute__((section("__TEXT,__oslogstring,cstring_literals"
),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG54"
) = "Keyboard dismissed with nonzero focus preservation count; check for unbalanced calls to -_incrementFocusPreservationCount"
; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf
[__builtin_os_log_format_buffer_size("Keyboard dismissed with nonzero focus preservation count; check for unbalanced calls to -_incrementFocusPreservationCount"
)]; _os_log_error_impl(&__dso_handle, _log_tmp, _type_tmp
, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf
, "Keyboard dismissed with nonzero focus preservation count; check for unbalanced calls to -_incrementFocusPreservationCount"
), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); }
}) clang diagnostic pop
;
358
359 if (std::exchange(_activeFocusedStateRetainCount, 0))
360 RELEASE_LOG_ERROR(ViewState, "Keyboard dismissed with nonzero active state retain count; make sure all callbacks returned from -_retainActiveFocusedState are invoked")clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option"
clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic
ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic
ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang
::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogViewState
.osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_ERROR; if
(os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({
clang diagnostic push clang diagnostic ignored "-Wvla" clang
diagnostic error "-Wformat" _Static_assert(__builtin_constant_p
("Keyboard dismissed with nonzero active state retain count; make sure all callbacks returned from -_retainActiveFocusedState are invoked"
), "format/label/description argument must be a string constant"
); __attribute__((section("__TEXT,__oslogstring,cstring_literals"
),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG55"
) = "Keyboard dismissed with nonzero active state retain count; make sure all callbacks returned from -_retainActiveFocusedState are invoked"
; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf
[__builtin_os_log_format_buffer_size("Keyboard dismissed with nonzero active state retain count; make sure all callbacks returned from -_retainActiveFocusedState are invoked"
)]; _os_log_error_impl(&__dso_handle, _log_tmp, _type_tmp
, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf
, "Keyboard dismissed with nonzero active state retain count; make sure all callbacks returned from -_retainActiveFocusedState are invoked"
), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); }
}) clang diagnostic pop
;
361}
362
363- (BOOL)_isRetainingActiveFocusedState
364{
365 // Focus preservation count fulfills the same role as active focus state count.
366 // However, unlike active focus state, it may be reset to 0 without impacting the
367 // behavior of -_retainActiveFocusedState, and it's harmless to invoke
368 // -_decrementFocusPreservationCount after resetting the count to 0.
369 return _focusPreservationCount || _activeFocusedStateRetainCount;
370}
371
372- (WebCore::IntDegrees)_deviceOrientationIgnoringOverrides
373{
374 return deviceOrientationForUIInterfaceOrientation([&] {
375 if (auto windowScene = self.window.windowScene)
376 return windowScene.effectiveGeometry.interfaceOrientation;
377ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push GCC diagnostic ignored "-W" "deprecated-declarations"
378 return UIApplication.sharedApplication.statusBarOrientation;
379ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop
380 }());
381}
382
383- (void)_dynamicUserInterfaceTraitDidChange
384{
385 if (!_page)
386 return;
387 _page->effectiveAppearanceDidChange();
388 [self _updateScrollViewBackground];
389}
390
391- (BOOL)_effectiveAppearanceIsDark
392{
393 return self.traitCollection.userInterfaceStyle == UIUserInterfaceStyleDark;
394}
395
396- (BOOL)_effectiveUserInterfaceLevelIsElevated
397{
398#if !PLATFORM(WATCHOS)(defined WTF_PLATFORM_WATCHOS && WTF_PLATFORM_WATCHOS
)
399 return self.traitCollection.userInterfaceLevel == UIUserInterfaceLevelElevated;
400#else
401 return NO__objc_no;
402#endif
403}
404
405- (void)_populateArchivedSubviews:(NSMutableSet *)encodedViews
406{
407 [super _populateArchivedSubviews:encodedViews];
408
409 if (_scrollView)
410 [encodedViews removeObject:_scrollView.get()];
411 if (_customContentFixedOverlayView)
412 [encodedViews removeObject:_customContentFixedOverlayView.get()];
413}
414
415- (BOOL)_isBackground
416{
417 if (![self usesStandardContentView] && [_customContentView respondsToSelector:@selector(web_isBackground)])
418 return [_customContentView web_isBackground];
419 return [_contentView isBackground];
420}
421
422- (BOOL)becomeFirstResponder
423{
424#if PLATFORM(VISION)(defined WTF_PLATFORM_VISION && WTF_PLATFORM_VISION)
425 if (_page)
426 _page->setSceneIdentifier(self.window.windowScene.session.persistentIdentifier);
427#endif
428 UIView *currentContentView = self._currentContentView;
429 if (currentContentView == _contentView && [_contentView superview])
430 return [_contentView becomeFirstResponderForWebView] || [super becomeFirstResponder];
431
432 return [currentContentView becomeFirstResponder] || [super becomeFirstResponder];
433}
434
435- (BOOL)canBecomeFirstResponder
436{
437 if (self._currentContentView == _contentView)
438 return [_contentView canBecomeFirstResponderForWebView];
439
440 return YES__objc_yes;
441}
442
443- (BOOL)resignFirstResponder
444{
445 if ([_contentView isFirstResponder])
446 return [_contentView resignFirstResponderForWebView];
447
448 return [super resignFirstResponder];
449}
450
451- (NSUndoManager *)undoManager
452{
453 if (self._currentContentView == _contentView)
454 return [_contentView undoManagerForWebView];
455
456 return [super undoManager];
457}
458
459FOR_EACH_WKCONTENTVIEW_ACTION(FORWARD_ACTION_TO_WKCONTENTVIEW)- (void)captureTextFromCamera:(id)sender { if (self.usesStandardContentView
) [_contentView captureTextFromCameraForWebView:sender]; } - (
void)useSelectionForFind:(id)sender { if (self.usesStandardContentView
) [_contentView useSelectionForFindForWebView:sender]; } - (void
)findSelected:(id)sender { if (self.usesStandardContentView) [
_contentView findSelectedForWebView:sender]; } - (void)_findSelected
:(id)sender { if (self.usesStandardContentView) [_contentView
_findSelectedForWebView:sender]; } - (void)addShortcut:(id)sender
{ if (self.usesStandardContentView) [_contentView addShortcutForWebView
:sender]; } - (void)_addShortcut:(id)sender { if (self.usesStandardContentView
) [_contentView _addShortcutForWebView:sender]; } - (void)define
:(id)sender { if (self.usesStandardContentView) [_contentView
defineForWebView:sender]; } - (void)_define:(id)sender { if (
self.usesStandardContentView) [_contentView _defineForWebView
:sender]; } - (void)lookup:(id)sender { if (self.usesStandardContentView
) [_contentView lookupForWebView:sender]; } - (void)_lookup:(
id)sender { if (self.usesStandardContentView) [_contentView _lookupForWebView
:sender]; } - (void)translate:(id)sender { if (self.usesStandardContentView
) [_contentView translateForWebView:sender]; } - (void)_translate
:(id)sender { if (self.usesStandardContentView) [_contentView
_translateForWebView:sender]; } - (void)promptForReplace:(id
)sender { if (self.usesStandardContentView) [_contentView promptForReplaceForWebView
:sender]; } - (void)_promptForReplace:(id)sender { if (self.usesStandardContentView
) [_contentView _promptForReplaceForWebView:sender]; } - (void
)share:(id)sender { if (self.usesStandardContentView) [_contentView
shareForWebView:sender]; } - (void)_share:(id)sender { if (self
.usesStandardContentView) [_contentView _shareForWebView:sender
]; } - (void)transliterateChinese:(id)sender { if (self.usesStandardContentView
) [_contentView transliterateChineseForWebView:sender]; } - (
void)_transliterateChinese:(id)sender { if (self.usesStandardContentView
) [_contentView _transliterateChineseForWebView:sender]; } - (
void)_nextAccessoryTab:(id)sender { if (self.usesStandardContentView
) [_contentView _nextAccessoryTabForWebView:sender]; } - (void
)_previousAccessoryTab:(id)sender { if (self.usesStandardContentView
) [_contentView _previousAccessoryTabForWebView:sender]; } - (
void)copy:(id)sender { if (self.usesStandardContentView) [_contentView
copyForWebView:sender]; } - (void)cut:(id)sender { if (self.
usesStandardContentView) [_contentView cutForWebView:sender];
} - (void)paste:(id)sender { if (self.usesStandardContentView
) [_contentView pasteForWebView:sender]; } - (void)replace:(id
)sender { if (self.usesStandardContentView) [_contentView replaceForWebView
:sender]; } - (void)select:(id)sender { if (self.usesStandardContentView
) [_contentView selectForWebView:sender]; } - (void)selectAll
:(id)sender { if (self.usesStandardContentView) [_contentView
selectAllForWebView:sender]; } - (void)toggleBoldface:(id)sender
{ if (self.usesStandardContentView) [_contentView toggleBoldfaceForWebView
:sender]; } - (void)toggleItalics:(id)sender { if (self.usesStandardContentView
) [_contentView toggleItalicsForWebView:sender]; } - (void)toggleUnderline
:(id)sender { if (self.usesStandardContentView) [_contentView
toggleUnderlineForWebView:sender]; } - (void)increaseSize:(id
)sender { if (self.usesStandardContentView) [_contentView increaseSizeForWebView
:sender]; } - (void)decreaseSize:(id)sender { if (self.usesStandardContentView
) [_contentView decreaseSizeForWebView:sender]; } - (void)pasteAndMatchStyle
:(id)sender { if (self.usesStandardContentView) [_contentView
pasteAndMatchStyleForWebView:sender]; } - (void)makeTextWritingDirectionNatural
:(id)sender { if (self.usesStandardContentView) [_contentView
makeTextWritingDirectionNaturalForWebView:sender]; } - (void
)makeTextWritingDirectionLeftToRight:(id)sender { if (self.usesStandardContentView
) [_contentView makeTextWritingDirectionLeftToRightForWebView
:sender]; } - (void)makeTextWritingDirectionRightToLeft:(id)sender
{ if (self.usesStandardContentView) [_contentView makeTextWritingDirectionRightToLeftForWebView
:sender]; } - (void)alignCenter:(id)sender { if (self.usesStandardContentView
) [_contentView alignCenterForWebView:sender]; } - (void)alignJustified
:(id)sender { if (self.usesStandardContentView) [_contentView
alignJustifiedForWebView:sender]; } - (void)alignLeft:(id)sender
{ if (self.usesStandardContentView) [_contentView alignLeftForWebView
:sender]; } - (void)alignRight:(id)sender { if (self.usesStandardContentView
) [_contentView alignRightForWebView:sender]; }
The return value is +1 and results in a memory leak
460
461- (BOOL)canPerformAction:(SEL)action withSender:(id)sender
462{
463#define FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_action) \
464 if (action == @selector(_action:)) \
465 return self.usesStandardContentView && [_contentView canPerformActionForWebView:action withSender:sender];
466
467 FOR_EACH_WKCONTENTVIEW_ACTION(FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW)FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(captureTextFromCamera
) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(useSelectionForFind
) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(findSelected) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW
(_findSelected) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(addShortcut
) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_addShortcut) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW
(define) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_define) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW
(lookup) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_lookup) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW
(translate) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_translate
) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(promptForReplace)
FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_promptForReplace)
FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(share) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW
(_share) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(transliterateChinese
) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_transliterateChinese
) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_nextAccessoryTab
) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_previousAccessoryTab
) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(copy) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW
(cut) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(paste) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW
(replace) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(select) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW
(selectAll) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(toggleBoldface
) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(toggleItalics) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW
(toggleUnderline) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(increaseSize
) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(decreaseSize) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW
(pasteAndMatchStyle) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW
(makeTextWritingDirectionNatural) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW
(makeTextWritingDirectionLeftToRight) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW
(makeTextWritingDirectionRightToLeft) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW
(alignCenter) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(alignJustified
) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(alignLeft) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW
(alignRight)
468 FOR_EACH_PRIVATE_WKCONTENTVIEW_ACTION(FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW)FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_indent) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW
(_outdent) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_toggleStrikeThrough
) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_insertOrderedList
) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_insertUnorderedList
) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_insertNestedOrderedList
) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_insertNestedUnorderedList
) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_increaseListLevel
) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_decreaseListLevel
) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_changeListType) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW
(_pasteAsQuotation) FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW
(_pasteAndMatchStyle)
469 FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_setTextColor:sender)
470 FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_setFontSize:sender)
471 FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_setFont:sender)
472
473#undef FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW
474
475#if HAVE(UIFINDINTERACTION)(defined 1 && 1)
476 if (action == @selector(find:) || action == @selector(findNext:) || action == @selector(findPrevious:))
477 return _findInteractionEnabled;
478
479 if (action == @selector(findAndReplace:))
480 return _findInteractionEnabled && self.supportsTextReplacement;
481#endif
482
483 return [super canPerformAction:action withSender:sender];
484}
485
486- (id)targetForAction:(SEL)action withSender:(id)sender
487{
488#define FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(_action) \
489 if (action == @selector(_action:) && self.usesStandardContentView) \
490 return [_contentView targetForActionForWebView:action withSender:sender];
491
492 FOR_EACH_WKCONTENTVIEW_ACTION(FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW)FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(captureTextFromCamera
) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(useSelectionForFind
) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(findSelected) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW
(_findSelected) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(addShortcut
) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(_addShortcut) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW
(define) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(_define) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW
(lookup) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(_lookup) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW
(translate) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(_translate
) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(promptForReplace) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW
(_promptForReplace) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(
share) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(_share) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW
(transliterateChinese) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW
(_transliterateChinese) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW
(_nextAccessoryTab) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(
_previousAccessoryTab) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW
(copy) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(cut) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW
(paste) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(replace) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW
(select) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(selectAll) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW
(toggleBoldface) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(toggleItalics
) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(toggleUnderline) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW
(increaseSize) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(decreaseSize
) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(pasteAndMatchStyle
) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(makeTextWritingDirectionNatural
) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(makeTextWritingDirectionLeftToRight
) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(makeTextWritingDirectionRightToLeft
) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(alignCenter) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW
(alignJustified) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(alignLeft
) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(alignRight)
493 FOR_EACH_PRIVATE_WKCONTENTVIEW_ACTION(FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW)FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(_indent) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW
(_outdent) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(_toggleStrikeThrough
) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(_insertOrderedList
) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(_insertUnorderedList
) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(_insertNestedOrderedList
) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(_insertNestedUnorderedList
) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(_increaseListLevel
) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(_decreaseListLevel
) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(_changeListType) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW
(_pasteAsQuotation) FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(
_pasteAndMatchStyle)
494 FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(_setTextColor:sender)
495 FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(_setFontSize:sender)
496 FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(_setFont:sender)
497
498#undef FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW
499
500 return [super targetForAction:action withSender:sender];
501}
502
503#if HAVE(UIFINDINTERACTION)(defined 1 && 1)
504
505- (void)find:(id)sender
506{
507 [_findInteraction presentFindNavigatorShowingReplace:NO__objc_no];
508}
509
510- (void)findNext:(id)sender
511{
512 [_findInteraction findNext];
513}
514
515- (void)findPrevious:(id)sender
516{
517 [_findInteraction findPrevious];
518}
519
520- (void)findAndReplace:(id)sender
521{
522 [_findInteraction presentFindNavigatorShowingReplace:YES__objc_yes];
523}
524
525#endif
526
527- (void)willFinishIgnoringCalloutBarFadeAfterPerformingAction
528{
529 [_contentView willFinishIgnoringCalloutBarFadeAfterPerformingAction];
530}
531
532static inline CGFloat floorToDevicePixel(CGFloat input, float deviceScaleFactor)
533{
534 return CGFloor(input * deviceScaleFactor)floor((input * deviceScaleFactor)) / deviceScaleFactor;
535}
536
537static inline bool pointsEqualInDevicePixels(CGPoint a, CGPoint b, float deviceScaleFactor)
538{
539 return std::abs(a.x * deviceScaleFactor - b.x * deviceScaleFactor) < std::numeric_limits<float>::epsilon()
540 && std::abs(a.y * deviceScaleFactor - b.y * deviceScaleFactor) < std::numeric_limits<float>::epsilon();
541}
542
543static CGSize roundScrollViewContentSize(const WebKit::WebPageProxy& page, CGSize contentSize)
544{
545 float deviceScaleFactor = page.deviceScaleFactor();
546 return CGSizeMake(floorToDevicePixel(contentSize.width, deviceScaleFactor), floorToDevicePixel(contentSize.height, deviceScaleFactor));
547}
548
549- (WKScrollView *)_wkScrollView
550{
551 return _scrollView.get();
552}
553
554- (UIView *)_currentContentView
555{
556 return _customContentView ? [_customContentView web_contentView] : _contentView.get();
557}
558
559- (WKWebViewContentProviderRegistry *)_contentProviderRegistry
560{
561 if (!self->_contentProviderRegistry)
562 self->_contentProviderRegistry = adoptNS([[WKWebViewContentProviderRegistry alloc] initWithConfiguration:self.configuration]);
563 return self->_contentProviderRegistry.get();
564}
565
566- (WKSelectionGranularity)_selectionGranularity
567{
568 return [_configuration selectionGranularity];
569}
570
571- (void)_setHasCustomContentView:(BOOL)pageHasCustomContentView loadedMIMEType:(const WTF::String&)mimeType
572{
573 Class representationClass = nilnullptr;
574 if (pageHasCustomContentView)
575 representationClass = [[self _contentProviderRegistry] providerForMIMEType:mimeType];
576
577 if (pageHasCustomContentView && representationClass) {
578 [_customContentView removeFromSuperview];
579 [_customContentFixedOverlayView removeFromSuperview];
580
581 // This is correct, static analysis gets confused by the `web_` prefix to the init method.
582 SUPPRESS_RETAINPTR_CTOR_ADOPT[[clang::suppress]] _customContentView = adoptNS([[representationClass alloc] web_initWithFrame:self.bounds webView:self mimeType:mimeType.createNSString().get()]);
583 _customContentFixedOverlayView = adoptNS([[UIView alloc] initWithFrame:self.bounds]);
584 [_customContentFixedOverlayView layer].name = @"CustomContentFixedOverlay";
585 [_customContentFixedOverlayView setUserInteractionEnabled:NO__objc_no];
586
587 [[_contentView unscaledView] removeFromSuperview];
588 [_contentView removeFromSuperview];
589 [_scrollView addSubview:_customContentView.get()];
590 [self addSubview:_customContentFixedOverlayView.get()];
591
592 [_customContentView web_setMinimumSize:self.bounds.size];
593 [_customContentView web_setFixedOverlayView:_customContentFixedOverlayView.get()];
594
595 _perProcessState.scrollViewBackgroundColor = WebCore::Color();
596 [self _resetContentOffset];
597 [_scrollView panGestureRecognizer].allowedTouchTypes = _scrollViewDefaultAllowedTouchTypes.get();
598 [_scrollView _setScrollEnabledInternal:YES__objc_yes];
599
600 [self _setAvoidsUnsafeArea:NO__objc_no];
601 } else if (_customContentView) {
602 [_customContentView removeFromSuperview];
603 _customContentView = nullptr;
604
605 [_customContentFixedOverlayView removeFromSuperview];
606 _customContentFixedOverlayView = nullptr;
607
608 [_scrollView addSubview:_contentView.get()];
609 [_scrollView addSubview:[_contentView unscaledView]];
610 [_scrollView setContentSize:roundScrollViewContentSize(*_page, [_contentView frame].size)];
611
612 [_customContentFixedOverlayView setFrame:self.bounds];
613 [self addSubview:_customContentFixedOverlayView.get()];
614 }
615
616 if (self.isFirstResponder) {
617 UIView *currentContentView = self._currentContentView;
618 if (currentContentView == _contentView ? [_contentView canBecomeFirstResponderForWebView] : currentContentView.canBecomeFirstResponder)
619 [currentContentView becomeFirstResponder];
620 }
621}
622
623- (void)_didFinishLoadingDataForCustomContentProviderWithSuggestedFilename:(const String&)suggestedFilename data:(NSData *)data
624{
625 ASSERT(_customContentView)((void)0);
626 [_customContentView web_setContentProviderData:data suggestedFilename:suggestedFilename.createNSString().get() completionHandler:^{
627 _page->didLayoutForCustomContentProvider();
628 }];
629}
630
631- (void)_willInvokeUIScrollViewDelegateCallback
632{
633 _perProcessState.invokingUIScrollViewDelegateCallback = YES__objc_yes;
634}
635
636- (void)_didInvokeUIScrollViewDelegateCallback
637{
638 _perProcessState.invokingUIScrollViewDelegateCallback = NO__objc_no;
639 if (_perProcessState.didDeferUpdateVisibleContentRectsForUIScrollViewDelegateCallback) {
640 _perProcessState.didDeferUpdateVisibleContentRectsForUIScrollViewDelegateCallback = NO__objc_no;
641 [self _scheduleVisibleContentRectUpdate];
642 }
643}
644
645static CGFloat contentZoomScale(WKWebView *webView)
646{
647 CGFloat scale = webView._currentContentView.layer.affineTransform.a;
648 ASSERT(webView->_resizeAnimationView || scale == [webView->_scrollView zoomScale])((void)0);
649 return scale;
650}
651
652enum class AllowPageBackgroundColorOverride : bool { No, Yes };
653
654static WebCore::Color baseScrollViewBackgroundColor(WKWebView *webView, AllowPageBackgroundColorOverride allowPageBackgroundColorOverride)
655{
656 if (webView->_customContentView)
657 return WebCore::roundAndClampToSRGBALossy(protect([webView->_customContentView backgroundColor].CGColor));
658
659 if (webView->_gestureController) {
660 WebCore::Color color = webView->_gestureController->backgroundColorForCurrentSnapshot();
661 if (color.isValid())
662 return color;
663 }
664
665 if (!webView->_page)
666 return { };
667
668 return allowPageBackgroundColorOverride == AllowPageBackgroundColorOverride::Yes ? webView->_page->underPageBackgroundColor() : webView->_page->pageExtendedBackgroundColor();
669}
670
671static WebCore::Color scrollViewBackgroundColor(WKWebView *webView, AllowPageBackgroundColorOverride allowPageBackgroundColorOverride)
672{
673 if (!webView.opaque)
674 return WebCore::Color::transparentBlack;
675
676 WebCore::Color color;
677 [protect(WebCore::traitCollectionWithAdjustedIdiomForSystemColors(webView.traitCollection)) performAsCurrentTraitCollection:[&, webView = RetainPtr { webView }] {
678 color = baseScrollViewBackgroundColor(webView.get(), allowPageBackgroundColorOverride);
679
680 if (!color.isValid() && webView->_contentView)
681 color = WebCore::roundAndClampToSRGBALossy(protect([webView->_contentView backgroundColor].CGColor));
682
683 if (!color.isValid())
684 color = WebCore::roundAndClampToSRGBALossy(protect(UIColor.systemBackgroundColor.CGColor));
685 }];
686
687 return color;
688}
689
690- (void)_resetCachedScrollViewBackgroundColor
691{
692 _perProcessState.scrollViewBackgroundColor = WebCore::Color();
693}
694
695- (void)_updateScrollViewBackground
696{
697 auto newScrollViewBackgroundColor = scrollViewBackgroundColor(self, AllowPageBackgroundColorOverride::Yes);
698 if (_perProcessState.scrollViewBackgroundColor != newScrollViewBackgroundColor) {
699 [_scrollView _setBackgroundColorInternal:cocoaColor(newScrollViewBackgroundColor).get()];
700 _perProcessState.scrollViewBackgroundColor = newScrollViewBackgroundColor;
701 }
702
703 [self _updateScrollViewIndicatorStyle];
704}
705
706- (void)_updateScrollViewIndicatorStyle
707{
708 // Update the indicator style based on the lightness/darkness of the background color.
709 auto newPageBackgroundColor = scrollViewBackgroundColor(self, AllowPageBackgroundColorOverride::No);
710 if (newPageBackgroundColor.lightness() <= .5f && newPageBackgroundColor.isVisible())
711 [_scrollView _setIndicatorStyleInternal:UIScrollViewIndicatorStyleWhite];
712 else
713 [_scrollView _setIndicatorStyleInternal:UIScrollViewIndicatorStyleBlack];
714}
715
716- (UIColor *)_insertionPointColor
717{
718 return self._textInputTraits.insertionPointColor ?: UIColor.insertionPointColor;
719}
720
721- (void)_videoControlsManagerDidChange
722{
723#if ENABLE(FULLSCREEN_API)(defined 1 && 1)
724 if (_fullScreenWindowController)
725 [_fullScreenWindowController videoControlsManagerDidChange];
726#endif
727}
728
729- (void)_videosInElementFullscreenChanged
730{
731#if ENABLE(FULLSCREEN_API)(defined 1 && 1)
732 if (_fullScreenWindowController)
733 [_fullScreenWindowController videosInElementFullscreenChanged];
734#endif
735}
736
737- (CGPoint)_initialContentOffsetForScrollView
738{
739 // FIXME: Should this use -[_scrollView adjustedContentInset]?
740 auto combinedUnobscuredAndScrollViewInset = [self _computedContentInset];
741 return CGPointMake(-combinedUnobscuredAndScrollViewInset.left, -combinedUnobscuredAndScrollViewInset.top);
742}
743
744- (CGPoint)_contentOffsetAdjustedForObscuredInset:(CGPoint)point
745{
746 CGPoint result = point;
747 UIEdgeInsets contentInset = [self _computedObscuredInset];
748
749 result.x -= contentInset.left;
750 result.y -= contentInset.top;
751
752 return result;
753}
754
755- (UIRectEdge)_effectiveObscuredInsetEdgesAffectedBySafeArea
756{
757 if (![self usesStandardContentView])
758 return UIRectEdgeAll;
759 return _obscuredInsetEdgesAffectedBySafeArea;
760}
761
762- (UIEdgeInsets)_computedObscuredInsetForWarningView
763{
764 if (_haveSetObscuredInsets)
765 return _obscuredInsets;
766
767 return UIEdgeInsetsAdd(UIEdgeInsetsZero, self._scrollViewSystemContentInset, self._effectiveObscuredInsetEdgesAffectedBySafeArea);
768}
769
770- (UIEdgeInsets)_contentInsetsFromSystemMinimumLayoutMargins
771{
772 if (auto controller = self._wk_viewControllerForFullScreenPresentation) {
773 auto margins = controller.systemMinimumLayoutMargins;
774 auto insets = UIEdgeInsetsMake(margins.top, margins.leading, margins.bottom, margins.trailing);
775 if (_page && _page->userInterfaceLayoutDirection() == WebCore::UserInterfaceLayoutDirection::RTL)
776 std::swap(insets.left, insets.right);
777
778 if (auto view = controller.viewIfLoaded) {
779 auto adjustInsetEdge = [](CGFloat& insetEdge, CGFloat distanceFromEdge) {
780 insetEdge -= std::max<CGFloat>(0, distanceFromEdge);
781 insetEdge = std::max<CGFloat>(0, insetEdge);
782 };
783
784 auto viewBounds = view.bounds;
785 auto webViewBoundsInView = [self convertRect:self.bounds toView:view];
786 adjustInsetEdge(insets.top, CGRectGetMinY(webViewBoundsInView) - CGRectGetMinY(viewBounds));
787 adjustInsetEdge(insets.left, CGRectGetMinX(webViewBoundsInView) - CGRectGetMinX(viewBounds));
788 adjustInsetEdge(insets.bottom, CGRectGetMaxY(viewBounds) - CGRectGetMaxY(webViewBoundsInView));
789 adjustInsetEdge(insets.right, CGRectGetMaxX(viewBounds) - CGRectGetMaxX(webViewBoundsInView));
790 }
791 return insets;
792 }
793 return UIEdgeInsetsZero;
794}
795
796- (UIEdgeInsets)_computedObscuredInset
797{
798 if (!linkedOnOrAfterSDKWithBehavior(SDKAlignedBehavior::ScrollViewContentInsetsAreNotObscuringInsets)) {
799 // For binary compability with third party apps, treat scroll view content insets as obscuring insets when the app is compiled
800 // against a WebKit version without the fix in r229641.
801 return [self _computedContentInset];
802 }
803
804 if (_haveSetObscuredInsets)
805 return _obscuredInsets;
806
807 if (self._safeAreaShouldAffectObscuredInsets)
808 return UIEdgeInsetsAdd(UIEdgeInsetsZero, self._scrollViewSystemContentInset, self._effectiveObscuredInsetEdgesAffectedBySafeArea);
809
810 return UIEdgeInsetsZero;
811}
812
813- (UIEdgeInsets)_computedContentInset
814{
815 if (_haveSetObscuredInsets)
816 return _obscuredInsets;
817
818 UIEdgeInsets insets = [_scrollView contentInset];
819 if (self._safeAreaShouldAffectObscuredInsets) {
820#if PLATFORM(WATCHOS)(defined WTF_PLATFORM_WATCHOS && WTF_PLATFORM_WATCHOS
)
821 // On watchOS, PepperUICore swizzles -[UIScrollView contentInset], such that it includes -_contentScrollInset as well.
822 // To avoid double-counting -_contentScrollInset when determining the total content inset, we only apply safe area insets here.
823 auto additionalScrollViewContentInset = self.safeAreaInsets;
824#else
825 auto additionalScrollViewContentInset = self._scrollViewSystemContentInset;
826#endif
827 insets = UIEdgeInsetsAdd(insets, additionalScrollViewContentInset, self._effectiveObscuredInsetEdgesAffectedBySafeArea);
828 }
829
830 return insets;
831}
832
833- (UIEdgeInsets)_computedUnobscuredSafeAreaInset
834{
835 if (_haveSetUnobscuredSafeAreaInsets)
836 return _unobscuredSafeAreaInsets;
837
838 if (!self._safeAreaShouldAffectObscuredInsets) {
839 auto safeAreaInsets = self.safeAreaInsets;
840#if PLATFORM(WATCHOS)(defined WTF_PLATFORM_WATCHOS && WTF_PLATFORM_WATCHOS
)
841 safeAreaInsets = UIEdgeInsetsAdd(safeAreaInsets, self._contentInsetsFromSystemMinimumLayoutMargins, self._effectiveObscuredInsetEdgesAffectedBySafeArea);
842#endif
843 if (_haveSetObscuredInsets) {
844 safeAreaInsets.top = std::max<CGFloat>(0., safeAreaInsets.top - _obscuredInsets.top);
845 safeAreaInsets.left = std::max<CGFloat>(0., safeAreaInsets.left - _obscuredInsets.left);
846 safeAreaInsets.bottom = std::max<CGFloat>(0., safeAreaInsets.bottom - _obscuredInsets.bottom);
847 safeAreaInsets.right = std::max<CGFloat>(0., safeAreaInsets.right - _obscuredInsets.right);
848 }
849 return safeAreaInsets;
850 }
851
852 return UIEdgeInsetsZero;
853}
854
855- (void)_processWillSwapOrDidExit
856{
857 // FIXME: Which ones of these need to be done in the process swap case and which ones in the exit case?
858 [self _hidePasswordView];
859 [self _cancelAnimatedResize];
860 [self _destroyResizeAnimationView];
861 [_contentView setHidden:NO__objc_no];
862
863#if HAVE(UIKIT_RESIZABLE_WINDOWS)(defined 1 && 1)
864 [self _invalidateResizeAssertions];
865#endif
866
867#if ENABLE(TEXT_EXTRACTION_FILTER)(defined ENABLE_TEXT_EXTRACTION_FILTER && ENABLE_TEXT_EXTRACTION_FILTER
)
868 [self _clearTextExtractionFilterCache];
869#endif
870
871 if (_gestureController)
872 protect(_gestureController)->disconnectFromProcess();
873
874 _perProcessState = { };
875}
876
877- (void)_processWillSwap
878{
879 WKWEBVIEW_RELEASE_LOG("%p -[WKWebView _processWillSwap]", self);
880 [self _processWillSwapOrDidExit];
881}
882
883- (void)_processDidExit
884{
885 WKWEBVIEW_RELEASE_LOG("%p -[WKWebView _processDidExit]", self);
886
887 [self _processWillSwapOrDidExit];
888
889 [_contentView setFrame:self.bounds];
890 [_scrollView _setBackgroundColorInternal:[_contentView backgroundColor]];
891 [self _resetContentOffset];
892 [_scrollView setZoomScale:1];
893}
894
895- (void)_didRelaunchProcess
896{
897 WKWEBVIEW_RELEASE_LOG("%p -[WKWebView _didRelaunchProcess] (pid=%d)", self, _page->legacyMainFrameProcessID());
898 _perProcessState.hasScheduledVisibleRectUpdate = NO__objc_no;
899 _viewStabilityWhenVisibleContentRectUpdateScheduled = { };
900 if (_gestureController)
901 protect(_gestureController)->connectToProcess();
902}
903
904- (void)_didCommitLoadForMainFrame
905{
906 _perProcessState.resetViewStateAfterTransactionID = protect(downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()))->nextMainFrameLayerTreeTransactionID();
907
908 _perProcessState.hasCommittedLoadForMainFrame = YES__objc_yes;
909
910 if (![self _scrollViewIsRubberBandingForRefreshControl])
911 [_scrollView _wk_stopScrollingAndZooming];
912
913#if HAVE(UIFINDINTERACTION)(defined 1 && 1)
914 if (_findInteractionEnabled) {
915 [_findInteraction dismissFindNavigator];
916
917 if (auto *findSession = dynamic_objc_cast<UITextSearchingFindSession>([_findInteraction activeFindSession]))
918 findSession.searchableObject = [self _searchableObject];
919 }
920#endif
921
922#if ENABLE(PAGE_LOAD_OBSERVER)(defined ENABLE_PAGE_LOAD_OBSERVER && ENABLE_PAGE_LOAD_OBSERVER
)
923 URL url { _page->currentURL() };
924 if (url.isValid() && url.protocolIsInHTTPFamily()) {
925 _pendingPageLoadObserverHost = url.hostAndPort().createNSString();
926 [self _updatePageLoadObserverState];
927 }
928#endif
929}
930
931static CGPoint contentOffsetBoundedInValidRange(UIScrollView *scrollView, CGPoint contentOffset)
932{
933// FIXME: Likely we can remove this special case for watchOS.
934#if !PLATFORM(WATCHOS)(defined WTF_PLATFORM_WATCHOS && WTF_PLATFORM_WATCHOS
)
935 UIEdgeInsets contentInsets = scrollView.adjustedContentInset;
936#else
937 UIEdgeInsets contentInsets = scrollView.contentInset;
938#endif
939
940 CGSize contentSize = scrollView.contentSize;
941 CGSize scrollViewSize = scrollView.bounds.size;
942
943 CGPoint minimumContentOffset = CGPointMake(-contentInsets.left, -contentInsets.top);
944 CGPoint maximumContentOffset = CGPointMake(std::max(minimumContentOffset.x, contentSize.width + contentInsets.right - scrollViewSize.width), std::max(minimumContentOffset.y, contentSize.height + contentInsets.bottom - scrollViewSize.height));
945
946 return CGPointMake(std::max(std::min(contentOffset.x, maximumContentOffset.x), minimumContentOffset.x), std::max(std::min(contentOffset.y, maximumContentOffset.y), minimumContentOffset.y));
947}
948
949static void changeContentOffsetBoundedInValidRange(UIScrollView *scrollView, WebCore::FloatPoint contentOffset)
950{
951 scrollView.contentOffset = contentOffsetBoundedInValidRange(scrollView, contentOffset);
952}
953
954- (WebCore::FloatRect)visibleRectInViewCoordinates
955{
956 WebCore::FloatRect bounds = self.bounds;
957 bounds.moveBy([_scrollView contentOffset]);
958 WebCore::FloatRect contentViewBounds = [_contentView bounds];
959 bounds.intersect(contentViewBounds);
960 return bounds;
961}
962
963- (void)_didCommitLayerTreeDuringAnimatedResize:(const WebKit::RemoteLayerTreeTransaction&)layerTreeTransaction mainFrameData:(const WebKit::MainFrameData&)mainFrameData
964{
965 auto updateID = mainFrameData.dynamicViewportSizeUpdateID;
966 if (updateID != _currentDynamicViewportSizeUpdateID)
967 return;
968
969 double pageScale = mainFrameData.pageScaleFactor;
970 WebCore::IntPoint scrollPosition = layerTreeTransaction.scrollPosition();
971
972 CGFloat animatingScaleTarget = [[_resizeAnimationView layer] transform].m11;
973 double currentTargetScale = animatingScaleTarget * [[_contentView layer] transform].m11;
974 double scale = pageScale / currentTargetScale;
975 _resizeAnimationTransformAdjustments = CATransform3DMakeScale(scale, scale, 1);
976
977 CGPoint newContentOffset = [self _contentOffsetAdjustedForObscuredInset:CGPointMake(scrollPosition.x() * pageScale, scrollPosition.y() * pageScale)];
978 CGPoint currentContentOffset = [_scrollView contentOffset];
979
980 _resizeAnimationTransformAdjustments.m41 = (currentContentOffset.x - newContentOffset.x) / animatingScaleTarget;
981 _resizeAnimationTransformAdjustments.m42 = (currentContentOffset.y - newContentOffset.y) / animatingScaleTarget;
982
983 [_resizeAnimationView layer].sublayerTransform = _resizeAnimationTransformAdjustments;
984
985 // If we've already passed endAnimatedResize, immediately complete
986 // the resize when we have an up-to-date layer tree. Otherwise,
987 // we will defer completion until endAnimatedResize.
988 _perProcessState.waitingForCommitAfterAnimatedResize = NO__objc_no;
989 if (!_perProcessState.waitingForEndAnimatedResize)
990 [self _didCompleteAnimatedResize];
991}
992
993- (void)_trackTransactionCommit:(const WebKit::TransactionID&)transactionID
994{
995 if (_perProcessState.didDeferUpdateVisibleContentRectsForUnstableScrollView) {
996 WKWEBVIEW_RELEASE_LOG("%p (pageProxyID=%llu) -[WKWebView _didCommitLayerTree:] - received a commit (%llu) while deferring visible content rect updates (dynamicViewportUpdateMode %d, resetViewStateAfterTransactionID %llu, sizeChangedSinceLastVisibleContentRectUpdate %d, [_scrollView isZoomBouncing] %d, currentlyAdjustingScrollViewInsetsForKeyboard %d)",
997 self, _page->identifier().toUInt64(), transactionID.object().toUInt64(), std::to_underlying(_perProcessState.dynamicViewportUpdateMode), _perProcessState.resetViewStateAfterTransactionID ? _perProcessState.resetViewStateAfterTransactionID->object().toUInt64() : 0, [_contentView sizeChangedSinceLastVisibleContentRectUpdate], [_scrollView isZoomBouncing], _perProcessState.currentlyAdjustingScrollViewInsetsForKeyboard);
998 }
999
1000 if (_timeOfFirstVisibleContentRectUpdateWithPendingCommit) {
1001 auto timeSinceFirstRequestWithPendingCommit = MonotonicTime::now() - *_timeOfFirstVisibleContentRectUpdateWithPendingCommit;
1002 if (timeSinceFirstRequestWithPendingCommit > delayBeforeNoCommitsLogging)
1003 WKWEBVIEW_RELEASE_LOG("%p (pageProxyID=%llu) -[WKWebView _didCommitLayerTree:] - finally received commit %.2fs after visible content rect update request; transactionID %llu", self, _page->identifier().toUInt64(), timeSinceFirstRequestWithPendingCommit.value(), transactionID.object().toUInt64());
1004 _timeOfFirstVisibleContentRectUpdateWithPendingCommit = std::nullopt;
1005 }
1006}
1007
1008- (void)_updateScrollViewForTransaction:(const WebKit::MainFrameData&)mainFrameData
1009{
1010 CGSize oldContentSize = [_scrollView contentSize];
1011 CGSize newContentSize = roundScrollViewContentSize(*_page, [_contentView frame].size);
1012 [_scrollView _setContentSizePreservingContentOffsetDuringRubberband:newContentSize];
1013
1014 CGFloat minimumScaleFactor = mainFrameData.minimumScaleFactor;
1015 CGFloat maximumScaleFactor = mainFrameData.maximumScaleFactor;
1016 CGFloat pageScaleFactor = mainFrameData.pageScaleFactor;
1017 BOOL allowsUserScaling = mainFrameData.allowsUserScaling;
1018
1019 if (_forcesInitialScaleFactor) {
1020 minimumScaleFactor = mainFrameData.initialScaleFactor;
1021 maximumScaleFactor = mainFrameData.initialScaleFactor;
1022 pageScaleFactor = mainFrameData.initialScaleFactor;
1023 allowsUserScaling = NO__objc_no;
1024 }
1025
1026 [_scrollView setMinimumZoomScale:minimumScaleFactor];
1027 [_scrollView setMaximumZoomScale:maximumScaleFactor];
1028 [_scrollView _setZoomEnabledInternal:allowsUserScaling && self._allowsMagnification];
1029
1030 CheckedPtr scrollingCoordinator = _page->scrollingCoordinatorProxy();
1031 if ([_scrollView showsHorizontalScrollIndicator] && [_scrollView showsVerticalScrollIndicator]) {
1032 [_scrollView setShowsHorizontalScrollIndicator:(scrollingCoordinator->mainFrameScrollbarWidth() != WebCore::ScrollbarWidth::None)];
1033 [_scrollView setShowsVerticalScrollIndicator:(scrollingCoordinator->mainFrameScrollbarWidth() != WebCore::ScrollbarWidth::None)];
1034 }
1035
1036#if HAVE(UIKIT_SCROLLBAR_COLOR_SPI)(defined 1 && 1)
1037 auto scrollbarColor = scrollingCoordinator->mainFrameScrollbarColor();
1038 if (scrollbarColor) {
1039 RetainPtr thumbColor = cocoaColor(scrollbarColor->thumbColor);
1040 [_scrollView _setHorizontalScrollIndicatorColor:thumbColor.get()];
1041 [_scrollView _setVerticalScrollIndicatorColor:thumbColor.get()];
1042 } else {
1043 [_scrollView _setHorizontalScrollIndicatorColor:nilnullptr];
1044 [_scrollView _setVerticalScrollIndicatorColor:nilnullptr];
1045 }
1046#endif
1047 auto horizontalOverscrollBehavior = scrollingCoordinator->mainFrameHorizontalOverscrollBehavior();
1048 auto verticalOverscrollBehavior = scrollingCoordinator->mainFrameVerticalOverscrollBehavior();
1049
1050 WebKit::ScrollingTreeScrollingNodeDelegateIOS::updateScrollViewForOverscrollBehavior(_scrollView.get(), horizontalOverscrollBehavior, verticalOverscrollBehavior, WebKit::ScrollingTreeScrollingNodeDelegateIOS::AllowOverscrollToPreventScrollPropagation::No);
1051
1052 bool hasDockedInputView = !CGRectIsEmpty(_inputViewBoundsInWindow);
1053 bool isZoomed = !WebKit::scalesAreEssentiallyEqual(pageScaleFactor, mainFrameData.initialScaleFactor) && (pageScaleFactor > mainFrameData.initialScaleFactor);
1054
1055 bool scrollingNeededToRevealUI = false;
1056 if (_overriddenLayoutParameters && protect(_page->preferences())->automaticallyForceEnableScrollingIfNeededToRevealUI()) {
1057 auto unobscuredContentRect = _page->unobscuredContentRect();
1058 auto maxUnobscuredSize = _page->maximumUnobscuredSize();
1059 auto minUnobscuredSize = _page->minimumUnobscuredSize();
1060 scrollingNeededToRevealUI = minUnobscuredSize != maxUnobscuredSize && maxUnobscuredSize == unobscuredContentRect.size();
1061 }
1062
1063 bool scrollingEnabled = scrollingCoordinator->hasScrollableOrZoomedMainFrame() || hasDockedInputView || isZoomed || scrollingNeededToRevealUI;
1064
1065 // FIXME: <rdar://99001670> Get the default list of allowed touch types from UIKit instead of caching the returned value.
1066 [_scrollView panGestureRecognizer].allowedTouchTypes = scrollingEnabled ? _scrollViewDefaultAllowedTouchTypes.get() : @[ ];
1067 [_scrollView _setScrollEnabledInternal:YES__objc_yes];
1068
1069 BOOL shouldUpdateZoomScale = !mainFrameData.scaleWasSetByUIProcess && ![_scrollView isZooming] && ![_scrollView isZoomBouncing] && ![_scrollView _wk_isZoomAnimating] && !WebKit::scalesAreEssentiallyEqual([_scrollView zoomScale], pageScaleFactor);
1070 BOOL shouldUpdateContentOffsetForFittingScale = !shouldUpdateZoomScale && !CGSizeEqualToSize__CGSizeEqualToSize(oldContentSize, newContentSize) && (oldContentSize.height > 0) && self._isDisplayingPDF;
1071
1072 if (shouldUpdateZoomScale || shouldUpdateContentOffsetForFittingScale) {
1073 UIEdgeInsets contentInsets = [_scrollView adjustedContentInset];
1074 CGPoint minimumContentOffset = CGPointMake(-contentInsets.left, -contentInsets.top);
1075 CGPoint contentOffset = [_scrollView contentOffset];
1076
1077 // When web-process-originated scale changes occur, pin the
1078 // scroll position to the top edge of the content,
1079 // instead of the center (which UIScrollView does by default).
1080 CGFloat scaleRatio = pageScaleFactor / [_scrollView zoomScale];
1081 if (shouldUpdateContentOffsetForFittingScale)
1082 scaleRatio = newContentSize.height / oldContentSize.height;
1083
1084 CGFloat contentOffsetX = contentOffset.x;
1085 if (shouldUpdateZoomScale)
1086 contentOffsetX = minimumContentOffset.x + ((contentOffsetX - minimumContentOffset.x) * scaleRatio);
1087
1088 CGFloat contentOffsetY = minimumContentOffset.y + ((contentOffset.y - minimumContentOffset.y) * scaleRatio);
1089
1090 if (shouldUpdateZoomScale) {
1091 LOG_WITH_STREAM(VisibleRects, stream << " updating scroll view with pageScaleFactor " << pageScaleFactor)((void)0);
1092 [_scrollView setZoomScale:pageScaleFactor];
1093 }
1094
1095 changeContentOffsetBoundedInValidRange(_scrollView.get(), CGPointMake(contentOffsetX, contentOffsetY));
1096 }
1097
1098#if HAVE(LIQUID_GLASS)(defined 1 && 1)
1099 if (!WTF::areEssentiallyEqual<CGFloat>(oldContentSize.width, newContentSize.width))
1100 [self _updateFixedColorExtensionViewFrames];
1101#endif
1102}
1103
1104- (BOOL)_restoreScrollAndZoomStateForTransaction:(const WebKit::TransactionID&)transactionID
1105{
1106 if (!_perProcessState.firstTransactionIDAfterPageRestore || transactionID.lessThanSameProcess(_perProcessState.firstTransactionIDAfterPageRestore.value()))
1107 return NO__objc_no;
1108
1109 _perProcessState.firstTransactionIDAfterPageRestore = std::nullopt;
1110
1111 BOOL needUpdateVisibleContentRects = NO__objc_no;
1112
1113 if (_perProcessState.scrollOffsetToRestore && ![self _scrollViewIsRubberBandingForRefreshControl]) {
1114 WebCore::FloatPoint scaledScrollOffset = _perProcessState.scrollOffsetToRestore.value();
1115 _perProcessState.scrollOffsetToRestore = std::nullopt;
1116
1117 if (WebKit::scalesAreEssentiallyEqual(contentZoomScale(self), _scaleToRestore)) {
1118 scaledScrollOffset.scale(_scaleToRestore);
1119 WebCore::FloatPoint contentOffsetInScrollViewCoordinates = scaledScrollOffset - WebCore::FloatSize(_obscuredInsetsWhenSaved.left(), _obscuredInsetsWhenSaved.top());
1120
1121 changeContentOffsetBoundedInValidRange(_scrollView.get(), contentOffsetInScrollViewCoordinates);
1122 _perProcessState.commitDidRestoreScrollPosition = YES__objc_yes;
1123 }
1124
1125 needUpdateVisibleContentRects = YES__objc_yes;
1126 }
1127
1128 if (_perProcessState.unobscuredCenterToRestore && ![self _scrollViewIsRubberBandingForRefreshControl]) {
1129 WebCore::FloatPoint unobscuredCenterToRestore = _perProcessState.unobscuredCenterToRestore.value();
1130 _perProcessState.unobscuredCenterToRestore = std::nullopt;
1131
1132 if (WebKit::scalesAreEssentiallyEqual(contentZoomScale(self), _scaleToRestore)) {
1133 CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, _obscuredInsets);
1134 WebCore::FloatSize unobscuredContentSizeAtNewScale = WebCore::FloatSize(unobscuredRect.size) / _scaleToRestore;
1135 WebCore::FloatPoint topLeftInDocumentCoordinates = unobscuredCenterToRestore - unobscuredContentSizeAtNewScale / 2;
1136
1137 topLeftInDocumentCoordinates.scale(_scaleToRestore);
1138 topLeftInDocumentCoordinates.moveBy(WebCore::FloatPoint(-_obscuredInsets.left, -_obscuredInsets.top));
1139
1140 changeContentOffsetBoundedInValidRange(_scrollView.get(), topLeftInDocumentCoordinates);
1141 }
1142
1143 needUpdateVisibleContentRects = YES__objc_yes;
1144 }
1145
1146 if (_gestureController)
1147 protect(_gestureController)->didRestoreScrollPosition();
1148
1149 return needUpdateVisibleContentRects;
1150}
1151
1152- (void)_didCommitLayerTree:(const WebKit::RemoteLayerTreeTransaction&)layerTreeTransaction mainFrameData:(const std::optional<WebKit::MainFrameData>&)mainFrameData pageData:(const WebKit::PageData&)pageData transactionID:(const WebKit::TransactionID&)transactionID
1153{
1154 if (mainFrameData) {
1155 const auto& mainFrameCommitData = *mainFrameData;
1156
1157 [self _trackTransactionCommit:transactionID];
1158
1159 _perProcessState.lastTransactionID = transactionID;
1160
1161#if HAVE(LIQUID_GLASS)(defined 1 && 1)
1162 bool isEnteringStableState = !std::exchange(_perProcessState.lastTransactionWasInStableState, mainFrameCommitData.isInStableState);
1163#endif
1164
1165 if (![self usesStandardContentView])
1166 return;
1167
1168 LOG_WITH_STREAM(VisibleRects, stream << "-[WKWebView " << _page->identifier() << " _didCommitLayerTree:] transactionID " << transactionID << " dynamicViewportUpdateMode " << std::to_underlying(_perProcessState.dynamicViewportUpdateMode))((void)0);
1169
1170 bool needUpdateVisibleContentRects = _page->updateLayoutViewportParameters(mainFrameCommitData);
1171
1172 bool isFirstTransactionAfterObscuredInsetChange = _perProcessState.firstTransactionIDAfterObscuredInsetChange.transform([&transactionID](auto expectedTransactionID) {
1173 return transactionID.greaterThanOrEqualSameProcess(expectedTransactionID);
1174 }).value_or(false);
1175
1176 if (isFirstTransactionAfterObscuredInsetChange) {
1177 _perProcessState.firstTransactionIDAfterObscuredInsetChange = std::nullopt;
1178 bool needsOverrideLayoutSizeUpdate = [&] {
1179 if (!_overriddenLayoutParameters)
1180 return false;
1181
1182 if (!self._shouldDeferGeometryUpdates)
1183 return true;
1184
1185 // rdar://157669095: SFSafariViewController may misplace content if geometry updates are deferred.
1186 // Dispatch updates as long as the scene isn't being interactively resized.
1187 if (WTF::IOSApplication::isSafariViewService() && !self.window.windowScene.effectiveGeometry.isInteractivelyResizing)
1188 return true;
1189
1190 return false;
1191 }();
1192
1193 if (needsOverrideLayoutSizeUpdate) {
1194 [self _dispatchSetViewLayoutSize:WebCore::FloatSize(_overriddenLayoutParameters->viewLayoutSize)];
1195 _page->setMinimumUnobscuredSize(WebCore::FloatSize(_overriddenLayoutParameters->minimumUnobscuredSize));
1196 _page->setDefaultUnobscuredSize(WebCore::FloatSize(_overriddenLayoutParameters->maximumUnobscuredSize));
1197 _page->setMaximumUnobscuredSize(WebCore::FloatSize(_overriddenLayoutParameters->maximumUnobscuredSize));
1198 needUpdateVisibleContentRects = true;
1199 }
1200 }
1201
1202 if (_perProcessState.dynamicViewportUpdateMode != WebKit::DynamicViewportUpdateMode::NotResizing) {
1203 [self _didCommitLayerTreeDuringAnimatedResize:layerTreeTransaction mainFrameData:mainFrameCommitData];
1204 return;
1205 }
1206
1207 if (_perProcessState.liveResizeParameters)
1208 return;
1209
1210 if (_resizeAnimationView)
1211 WKWEBVIEW_RELEASE_LOG("%p -[WKWebView _didCommitLayerTree:] - dynamicViewportUpdateMode is NotResizing, but still have a live resizeAnimationView (unpaired begin/endAnimatedResize?)", self);
1212
1213 [self _updateScrollViewForTransaction:mainFrameCommitData];
1214
1215#if ENABLE(CONTENT_INSET_BACKGROUND_FILL)(defined 1 && 1)
1216 [self _updateNeedsTopScrollPocketDueToVisibleContentInset];
1217#endif
1218
1219 _perProcessState.viewportMetaTagInteractiveWidget = mainFrameCommitData.viewportMetaTagInteractiveWidget;
1220 _perProcessState.viewportMetaTagWidth = mainFrameCommitData.viewportMetaTagWidth;
1221 _perProcessState.viewportMetaTagWidthWasExplicit = mainFrameCommitData.viewportMetaTagWidthWasExplicit;
1222 _perProcessState.viewportMetaTagCameFromImageDocument = mainFrameCommitData.viewportMetaTagCameFromImageDocument;
1223 _perProcessState.initialScaleFactor = mainFrameCommitData.initialScaleFactor;
1224
1225 if (_page->inStableState() && mainFrameCommitData.isInStableState && [_stableStatePresentationUpdateCallbacks count]) {
1226 for (dispatch_block_t action in _stableStatePresentationUpdateCallbacks.get())
1227 action();
1228
1229 [_stableStatePresentationUpdateCallbacks removeAllObjects];
1230 _stableStatePresentationUpdateCallbacks = nilnullptr;
1231 }
1232
1233 if (![_contentView _mayDisableDoubleTapGesturesDuringSingleTap])
1234 [_contentView _setDoubleTapGesturesEnabled:self._allowsDoubleTapGestures];
1235
1236 [self _updateScrollViewBackground];
1237 [self _setAvoidsUnsafeArea:mainFrameCommitData.avoidsUnsafeArea];
1238
1239 if (_gestureController)
1240 protect(_gestureController)->setRenderTreeSize(pageData.renderTreeSize);
1241
1242 if (_perProcessState.resetViewStateAfterTransactionID && transactionID.greaterThanOrEqualSameProcess(*_perProcessState.resetViewStateAfterTransactionID)) {
1243 _perProcessState.resetViewStateAfterTransactionID = std::nullopt;
1244 if (![self _scrollViewIsRubberBandingForRefreshControl])
1245 [self _resetContentOffset];
1246
1247 if (_observedRenderingProgressEvents & _WKRenderingProgressEventFirstPaint)
1248 _navigationState->didFirstPaint();
1249
1250 needUpdateVisibleContentRects = true;
1251 }
1252
1253 if ([self _restoreScrollAndZoomStateForTransaction:transactionID])
1254 needUpdateVisibleContentRects = true;
1255
1256 if (needUpdateVisibleContentRects)
1257 [self _scheduleVisibleContentRectUpdate];
1258
1259#if HAVE(LIQUID_GLASS)(defined 1 && 1)
1260 if (isEnteringStableState)
1261 [self _reinsertTopFixedColorExtensionViewIfNeeded];
1262#endif
1263
1264 if (WebKit::RemoteLayerTreeScrollingPerformanceData* scrollPerfData = _page->scrollingPerformanceData())
1265 scrollPerfData->didCommitLayerTree([self visibleRectInViewCoordinates]);
1266 }
1267
1268 if (_perProcessState.pendingFindLayerID) {
1269 RetainPtr layer = downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()).remoteLayerTreeHost().layerForID(*_perProcessState.pendingFindLayerID);
1270 if (layer.get().superlayer)
1271 [self _didAddLayerForFindOverlay:layer.get()];
1272 }
1273
1274}
1275
1276- (void)_layerTreeCommitComplete
1277{
1278 _perProcessState.commitDidRestoreScrollPosition = NO__objc_no;
1279}
1280
1281- (void)_couldNotRestorePageState
1282{
1283 // The gestureController may be waiting for the scroll position to be restored
1284 // in order to remove the swipe snapshot. Since the scroll position could not be
1285 // restored, tell the gestureController it was restored so that it no longer waits
1286 // for it.
1287 if (_gestureController)
1288 protect(_gestureController)->didRestoreScrollPosition();
1289}
1290
1291- (void)_restorePageScrollPosition:(std::optional<WebCore::FloatPoint>)scrollPosition scrollOrigin:(WebCore::FloatPoint)scrollOrigin previousObscuredInset:(WebCore::FloatBoxExtent)obscuredInsets scale:(double)scale
1292{
1293 if (self._shouldDeferGeometryUpdates) {
1294 // Defer scroll position restoration until after the current resize completes.
1295 RetainPtr<WKWebView> retainedSelf = self;
1296 _callbacksDeferredDuringResize.append([retainedSelf, scrollPosition, scrollOrigin, obscuredInsets, scale] {
1297 [retainedSelf _restorePageScrollPosition:scrollPosition scrollOrigin:scrollOrigin previousObscuredInset:obscuredInsets scale:scale];
1298 });
1299 return;
1300 }
1301
1302 if (![self usesStandardContentView])
1303 return;
1304
1305 _perProcessState.firstTransactionIDAfterPageRestore = protect(downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()))->nextMainFrameLayerTreeTransactionID();
1306 if (scrollPosition)
1307 _perProcessState.scrollOffsetToRestore = WebCore::ScrollableArea::scrollOffsetFromPosition(WebCore::FloatPoint(scrollPosition.value()), WebCore::toFloatSize(scrollOrigin));
1308 else
1309 _perProcessState.scrollOffsetToRestore = std::nullopt;
1310
1311 _obscuredInsetsWhenSaved = obscuredInsets;
1312 _scaleToRestore = scale;
1313}
1314
1315- (void)_restorePageStateToUnobscuredCenter:(std::optional<WebCore::FloatPoint>)center scale:(double)scale
1316{
1317 if (self._shouldDeferGeometryUpdates) {
1318 // Defer scroll position restoration until after the current resize completes.
1319 RetainPtr<WKWebView> retainedSelf = self;
1320 _callbacksDeferredDuringResize.append([retainedSelf, center, scale] {
1321 [retainedSelf _restorePageStateToUnobscuredCenter:center scale:scale];
1322 });
1323 return;
1324 }
1325
1326 if (![self usesStandardContentView])
1327 return;
1328
1329 _perProcessState.firstTransactionIDAfterPageRestore = protect(downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*_page->drawingArea()))->nextMainFrameLayerTreeTransactionID();
1330 _perProcessState.unobscuredCenterToRestore = center;
1331
1332 _scaleToRestore = scale;
1333}
1334
1335- (RefPtr<WebKit::ViewSnapshot>)_takeViewSnapshot
1336{
1337#if HAVE(CORE_ANIMATION_RENDER_SERVER)(defined 1 && 1)
1338 float deviceScale = WebCore::screenScaleFactor();
1339 WebCore::FloatSize snapshotSize(self.bounds.size);
1340 snapshotSize.scale(deviceScale);
1341
1342 if (snapshotSize.isEmpty())
1343 return nullptr;
1344
1345 auto transform = CATransform3DMakeScale(deviceScale, deviceScale, 1);
1346
1347 auto snapshotFormat = WebCore::convertToIOSurfaceFormat(WebCore::PlatformCALayer::contentsFormatForLayer());
1348 auto surface = WebCore::IOSurface::create(nullptr, WebCore::expandedIntSize(snapshotSize), WebCore::DestinationColorSpace::SRGB(), WebCore::IOSurface::Name::Snapshot, snapshotFormat);
1349 if (!surface)
1350 return nullptr;
1351
1352 CARenderServerSnapshot(MACH_PORT_NULL0, @{
1353 kCASnapshotMode: kCASnapshotModeLayer,
1354 kCASnapshotDisplayName: kCARenderServerDefaultDisplay@"defaultDisplay",
1355 kCASnapshotContextId: @(self.layer.context.contextId),
1356 kCASnapshotLayerId: @(reinterpret_cast<uint64_t>(self.layer)),
1357 kCASnapshotDestination: (__bridge id)surface->surface(),
1358 kCASnapshotOriginX: @(0),
1359 kCASnapshotOriginY: @(0),
1360 kCASnapshotTransform: @(transform),
1361 kCASnapshotTimeOffset: @(0),
1362 });
1363
1364#if HAVE(IOSURFACE_ACCELERATOR)(defined HAVE_IOSURFACE_ACCELERATOR && HAVE_IOSURFACE_ACCELERATOR
)
1365 WebCore::IOSurface::Format compressedFormat = WebCore::IOSurface::Format::YUV422;
1366 if (WebCore::IOSurface::allowConversionFromFormatToFormat(snapshotFormat, compressedFormat)) {
1367 auto viewSnapshot = WebKit::ViewSnapshot::create(nullptr);
1368 WebCore::IOSurface::convertToFormat(nullptr, WTF::move(surface), WebCore::IOSurface::Name::Snapshot, WebCore::IOSurface::Format::YUV422, [viewSnapshot](std::unique_ptr<WebCore::IOSurface> convertedSurface) {
1369 if (convertedSurface)
1370 viewSnapshot->setSurface(WTF::move(convertedSurface));
1371 });
1372
1373 return viewSnapshot;
1374 }
1375#endif // HAVE(IOSURFACE_ACCELERATOR)
1376
1377 return WebKit::ViewSnapshot::create(WTF::move(surface));
1378#else // HAVE(CORE_ANIMATION_RENDER_SERVER)
1379 return nullptr;
1380#endif
1381}
1382
1383- (void)_zoomToCenter:(WebCore::FloatPoint)center atScale:(double)scale animated:(BOOL)animated honorScrollability:(BOOL)honorScrollability
1384{
1385 CGFloat zoomScale = contentZoomScale(self);
1386
1387 auto scaleIsChanging = !WTF::areEssentiallyEqual<CGFloat>(scale, zoomScale);
1388 if (scaleIsChanging)
1389 _page->willStartUserTriggeredZooming();
1390
1391 if (honorScrollability && !scaleIsChanging && ![_scrollView isScrollEnabled])
1392 return;
1393
1394 LOG_WITH_STREAM(VisibleRects, stream << "_zoomToCenter:" << center << " scale: " << scale << " animated:" << animated)((void)0);
1395
1396 auto bounds = [_scrollView bounds];
1397 auto targetWidth = CGRectGetWidth(bounds) / scale;
1398 auto targetHeight = CGRectGetHeight(bounds) / scale;
1399 auto targetRect = CGRectMake(center.x() - targetWidth / 2, center.y() - targetHeight / 2, targetWidth, targetHeight);
1400 if (!animated) {
1401 [_scrollView zoomToRect:targetRect animated:NO__objc_no];
1402 return;
1403 }
1404
1405 if (scaleIsChanging) {
1406 [_scrollView zoomToRect:targetRect animated:YES__objc_yes];
1407 return;
1408 }
1409
1410 constexpr auto defaultAnimationDurationAtSameScale = 0.25;
1411 [UIView animateWithDuration:defaultAnimationDurationAtSameScale animations:^{
1412 [_scrollView zoomToRect:targetRect animated:NO__objc_no];
1413 }];
1414}
1415
1416- (void)_zoomToRect:(WebCore::FloatRect)targetRect atScale:(double)scale origin:(WebCore::FloatPoint)origin animated:(BOOL)animated
1417{
1418 // FIXME: Some of this could be shared with _scrollToRect.
1419 const double visibleRectScaleChange = contentZoomScale(self) / scale;
1420 const WebCore::FloatRect visibleRect([self convertRect:self.bounds toView:self._currentContentView]);
1421 const WebCore::FloatRect unobscuredRect([self _contentRectForUserInteraction]);
1422
1423 const WebCore::FloatSize topLeftObscuredInsetAfterZoom((unobscuredRect.minXMinYCorner() - visibleRect.minXMinYCorner()) * visibleRectScaleChange);
1424 const WebCore::FloatSize bottomRightObscuredInsetAfterZoom((visibleRect.maxXMaxYCorner() - unobscuredRect.maxXMaxYCorner()) * visibleRectScaleChange);
1425
1426 const WebCore::FloatSize unobscuredRectSizeAfterZoom(unobscuredRect.size() * visibleRectScaleChange);
1427
1428 // Center to the target rect.
1429 WebCore::FloatPoint unobscuredRectLocationAfterZoom = targetRect.location() - (unobscuredRectSizeAfterZoom - targetRect.size()) * 0.5;
1430
1431 // Center to the tap point instead in case the target rect won't fit in a direction.
1432 if (targetRect.width() > unobscuredRectSizeAfterZoom.width())
1433 unobscuredRectLocationAfterZoom.setX(origin.x() - unobscuredRectSizeAfterZoom.width() / 2);
1434 if (targetRect.height() > unobscuredRectSizeAfterZoom.height())
1435 unobscuredRectLocationAfterZoom.setY(origin.y() - unobscuredRectSizeAfterZoom.height() / 2);
1436
1437 // We have computed where we want the unobscured rect to be. Now adjust for the obscuring insets.
1438 WebCore::FloatRect visibleRectAfterZoom(unobscuredRectLocationAfterZoom, unobscuredRectSizeAfterZoom);
1439 visibleRectAfterZoom.move(-topLeftObscuredInsetAfterZoom);
1440 visibleRectAfterZoom.expand(topLeftObscuredInsetAfterZoom + bottomRightObscuredInsetAfterZoom);
1441
1442 [self _zoomToCenter:visibleRectAfterZoom.center() atScale:scale animated:animated honorScrollability:YES__objc_yes];
1443}
1444
1445static WebCore::FloatPoint constrainContentOffset(WebCore::FloatPoint contentOffset, WebCore::FloatPoint minimumContentOffset, WebCore::FloatSize contentSize, WebCore::FloatSize unobscuredContentSize)
1446{
1447 WebCore::FloatSize maximumContentOffset = contentSize - unobscuredContentSize;
1448 return contentOffset.constrainedBetween(minimumContentOffset, WebCore::FloatPoint(maximumContentOffset));
1449}
1450
1451- (void)_scrollToContentScrollPosition:(WebCore::FloatPoint)scrollPosition scrollOrigin:(WebCore::IntPoint)scrollOrigin animated:(BOOL)animated interruptAnimation:(BOOL)interruptAnimation
1452{
1453 if (_perProcessState.commitDidRestoreScrollPosition || self._shouldDeferGeometryUpdates)
1454 return;
1455
1456 // Don't allow content to do programmatic scrolls for non-scrollable pages when zoomed.
1457 if (!protect(_page->scrollingCoordinatorProxy())->hasScrollableMainFrame() && !WebKit::scalesAreEssentiallyEqual([_scrollView zoomScale], [_scrollView minimumZoomScale])) {
1458 [self _scheduleForcedVisibleContentRectUpdate];
1459 return;
1460 }
1461
1462 WebCore::FloatPoint contentOffset = WebCore::ScrollableArea::scrollOffsetFromPosition(scrollPosition, toFloatSize(scrollOrigin));
1463
1464 WebCore::FloatPoint scaledOffset = contentOffset;
1465 CGFloat zoomScale = contentZoomScale(self);
1466 scaledOffset.scale(zoomScale);
1467
1468 CGPoint contentOffsetInScrollViewCoordinates = [self _contentOffsetAdjustedForObscuredInset:scaledOffset];
1469 contentOffsetInScrollViewCoordinates = contentOffsetBoundedInValidRange(_scrollView.get(), contentOffsetInScrollViewCoordinates);
1470
1471 if (interruptAnimation)
1472 [_scrollView _wk_stopScrollingAndZooming];
1473
1474 CGPoint scrollViewContentOffset = [_scrollView contentOffset];
1475
1476 if (!CGPointEqualToPoint__CGPointEqualToPoint(contentOffsetInScrollViewCoordinates, scrollViewContentOffset)) {
1477 if (WTF::areEssentiallyEqual<float>(scrollPosition.x(), 0) && scrollViewContentOffset.x < 0)
1478 contentOffsetInScrollViewCoordinates.x = scrollViewContentOffset.x;
1479
1480 if (WTF::areEssentiallyEqual<float>(scrollPosition.y(), 0) && scrollViewContentOffset.y < 0)
1481 contentOffsetInScrollViewCoordinates.y = scrollViewContentOffset.y;
1482
1483 if (interruptAnimation)
1484 [_scrollView setContentOffset:contentOffsetInScrollViewCoordinates animated:animated];
1485 else
1486 [_scrollView setContentOffset:contentOffsetInScrollViewCoordinates];
1487
1488 if (!animated)
1489 [self _didFinishScrolling:_scrollView.get()];
1490
1491 } else if (!_perProcessState.didDeferUpdateVisibleContentRectsForAnyReason) {
1492 // If we haven't changed anything, and are not deferring updates, there would not be any VisibleContentRect update sent to the content.
1493 // The WebProcess would keep the invalid contentOffset as its scroll position.
1494 // To synchronize the WebProcess with what is on screen, we send the VisibleContentRect again.
1495 _page->resendLastVisibleContentRects();
1496 }
1497}
1498
1499- (float)_adjustScrollRectToAvoidHighlightOverlay:(WebCore::FloatRect)targetRect
1500{
1501#if ENABLE(APP_HIGHLIGHTS)(defined 1 && 1)
1502 WebCore::FloatRect overlayRect = [self convertRect:_page->appHighlightsOverlayRect() fromCoordinateSpace:self.window.screen.coordinateSpace];
1503
1504 if (CGRectIsNull(overlayRect))
1505 return 0;
1506
1507 overlayRect.expand(highlightMargin, highlightMargin);
1508
1509 if (!targetRect.intersects(overlayRect))
1510 return 0;
1511
1512 float topGap = overlayRect.y() - [self bounds].origin.y;
1513 float bottomGap = (self.bounds.size.height + self.bounds.origin.y) - overlayRect.maxY();
1514
1515 float midScreen = self.center.y;
1516
1517 if (topGap > bottomGap) {
1518 auto midGap = topGap / 2 + self.bounds.origin.y;
1519 auto diff = midScreen - midGap;
1520 return diff;
1521 }
1522
1523 auto midGap = bottomGap / 2 + self.bounds.origin.y;
1524 auto diff = midGap - midScreen;
1525 return diff;
1526#else
1527 return 0;
1528#endif
1529}
1530
1531
1532- (BOOL)_scrollToRect:(WebCore::FloatRect)targetRect origin:(WebCore::FloatPoint)origin minimumScrollDistance:(float)minimumScrollDistance
1533{
1534 if (![_scrollView isScrollEnabled])
1535 return NO__objc_no;
1536
1537 WebCore::FloatRect unobscuredContentRect([self _contentRectForUserInteraction]);
1538 WebCore::FloatPoint unobscuredContentOffset = unobscuredContentRect.location();
1539 WebCore::FloatSize contentSize([self._currentContentView bounds].size);
1540
1541 auto scrollViewInsets = [_scrollView adjustedContentInset];
1542 WebCore::FloatPoint minimumContentOffset(-scrollViewInsets.left, -scrollViewInsets.top);
1543 if (_haveSetObscuredInsets) {
1544 minimumContentOffset.move(_obscuredInsets.left, _obscuredInsets.top);
1545 minimumContentOffset = minimumContentOffset.constrainedBetween(minimumContentOffset, WebCore::FloatPoint());
1546 }
1547
1548 // Center the target rect in the scroll view.
1549 // If the target doesn't fit in the scroll view, center on the gesture location instead.
1550 WebCore::FloatPoint newUnobscuredContentOffset;
1551 if (targetRect.width() <= unobscuredContentRect.width())
1552 newUnobscuredContentOffset.setX(targetRect.x() - (unobscuredContentRect.width() - targetRect.width()) / 2);
1553 else
1554 newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
1555 if (targetRect.height() <= unobscuredContentRect.height())
1556 newUnobscuredContentOffset.setY(targetRect.y() - (unobscuredContentRect.height() - targetRect.height()) / 2);
1557 else
1558 newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
1559 newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, minimumContentOffset, contentSize, unobscuredContentRect.size());
1560
1561 if (unobscuredContentOffset == newUnobscuredContentOffset) {
1562 if (targetRect.width() > unobscuredContentRect.width())
1563 newUnobscuredContentOffset.setX(origin.x() - unobscuredContentRect.width() / 2);
1564 if (targetRect.height() > unobscuredContentRect.height())
1565 newUnobscuredContentOffset.setY(origin.y() - unobscuredContentRect.height() / 2);
1566 newUnobscuredContentOffset = constrainContentOffset(newUnobscuredContentOffset, minimumContentOffset, contentSize, unobscuredContentRect.size());
1567 }
1568
1569 WebCore::FloatSize scrollViewOffsetDelta = newUnobscuredContentOffset - unobscuredContentOffset;
1570 scrollViewOffsetDelta.scale(contentZoomScale(self));
1571
1572 float scrollDistance = scrollViewOffsetDelta.diagonalLength();
1573
1574 WebCore::FloatRect startRect = targetRect;
1575 WebCore::FloatRect convertedStartRect = [self convertRect:startRect fromView:self._currentContentView];
1576 convertedStartRect.move(-scrollViewOffsetDelta);
1577
1578 float additionalOffset = [self _adjustScrollRectToAvoidHighlightOverlay:convertedStartRect];
1579
1580 scrollViewOffsetDelta += WebCore::FloatSize(0, additionalOffset);
1581
1582 if (scrollDistance < minimumScrollDistance)
1583 return NO__objc_no;
1584
1585 [_contentView willStartZoomOrScroll];
1586
1587 LOG_WITH_STREAM(VisibleRects, stream << "_scrollToRect: scrolling to " << [_scrollView contentOffset] + scrollViewOffsetDelta)((void)0);
1588
1589 [_scrollView setContentOffset:([_scrollView contentOffset] + scrollViewOffsetDelta) animated:YES__objc_yes];
1590 return YES__objc_yes;
1591}
1592
1593- (void)_zoomOutWithOrigin:(WebCore::FloatPoint)origin animated:(BOOL)animated
1594{
1595 [self _zoomToCenter:origin atScale:[_scrollView minimumZoomScale] animated:animated honorScrollability:YES__objc_yes];
1596}
1597
1598- (void)_zoomToInitialScaleWithOrigin:(WebCore::FloatPoint)origin animated:(BOOL)animated
1599{
1600 ASSERT(_perProcessState.initialScaleFactor > 0)((void)0);
1601 [self _zoomToCenter:origin atScale:_perProcessState.initialScaleFactor animated:animated honorScrollability:YES__objc_yes];
1602}
1603
1604- (BOOL)_selectionRectIsFullyVisibleAndNonEmpty
1605{
1606 auto rect = _page->selectionBoundingRectInRootViewCoordinates();
1607 return !rect.isEmpty() && CGRectContainsRect(self._contentRectForUserInteraction, rect);
1608}
1609
1610- (void)_scrollToAndRevealSelectionIfNeeded
1611{
1612 if (![_scrollView isScrollEnabled])
1613 return;
1614
1615 auto selectionRect = _page->selectionBoundingRectInRootViewCoordinates();
1616 constexpr CGFloat selectionRectPadding = 4;
1617 selectionRect.inflate(selectionRectPadding);
1618 selectionRect.intersect([_contentView bounds]);
1619 if (selectionRect.isEmpty())
1620 return;
1621
1622 WebCore::FloatRect userInteractionContentRect = self._contentRectForUserInteraction;
1623 auto scrollDeltaInContentCoordinates = CGSizeZero;
1624
1625 if (userInteractionContentRect.maxY() < selectionRect.maxY())
1626 scrollDeltaInContentCoordinates.height = selectionRect.maxY() - userInteractionContentRect.maxY();
1627 else if (userInteractionContentRect.y() > selectionRect.y())
1628 scrollDeltaInContentCoordinates.height = selectionRect.y() - userInteractionContentRect.y();
1629
1630 if (userInteractionContentRect.maxX() < selectionRect.maxX())
1631 scrollDeltaInContentCoordinates.width = selectionRect.maxX() - userInteractionContentRect.maxX();
1632 else if (userInteractionContentRect.x() > selectionRect.x())
1633 scrollDeltaInContentCoordinates.width = selectionRect.x() - userInteractionContentRect.x();
1634
1635 if (CGSizeEqualToSize__CGSizeEqualToSize(scrollDeltaInContentCoordinates, CGSizeZero))
1636 return;
1637
1638 auto scale = contentZoomScale(self);
1639 auto newContentOffset = [_scrollView contentOffset];
1640 newContentOffset.x += scrollDeltaInContentCoordinates.width * scale;
1641 newContentOffset.y += scrollDeltaInContentCoordinates.height * scale;
1642 [_scrollView setContentOffset:newContentOffset animated:YES__objc_yes];
1643}
1644
1645- (void)_zoomToFocusRect:(const WebCore::FloatRect&)focusedElementRectInDocumentCoordinates selectionRect:(const WebCore::FloatRect&)selectionRectInDocumentCoordinates
1646 fontSize:(float)fontSize minimumScale:(double)minimumScale maximumScale:(double)maximumScale allowScaling:(BOOL)allowScaling forceScroll:(BOOL)forceScroll
1647{
1648 LOG_WITH_STREAM(VisibleRects, stream << "_zoomToFocusRect:" << focusedElementRectInDocumentCoordinates << " selectionRect:" << selectionRectInDocumentCoordinates)((void)0);
1649
1650 const double minimumHeightToShowContentAboveKeyboard = 106;
1651 const double caretOffsetFromWindowEdge = 8;
1652
1653 UIWindow *window = [_scrollView window];
1654
1655 CGRect unobscuredScrollViewRectInWebViewCoordinates = UIEdgeInsetsInsetRect([self bounds], _obscuredInsets);
1656 CGRect visibleScrollViewBoundsInWebViewCoordinates = CGRectIntersection(unobscuredScrollViewRectInWebViewCoordinates, [window convertRect:window.bounds toView:self]);
1657 CGRect formAssistantFrameInWebViewCoordinates = [window convertRect:_inputViewBoundsInWindow toView:self];
1658 CGRect intersectionBetweenScrollViewAndFormAssistant = CGRectIntersection(visibleScrollViewBoundsInWebViewCoordinates, formAssistantFrameInWebViewCoordinates);
1659 CGSize visibleSize = visibleScrollViewBoundsInWebViewCoordinates.size;
1660
1661 if (CGSizeEqualToSize__CGSizeEqualToSize(visibleSize, CGSizeZero) && CGRectIsEmpty(formAssistantFrameInWebViewCoordinates))
1662 return;
1663
1664 CGFloat visibleOffsetFromTop = 0;
1665 CGFloat minimumDistanceFromKeyboardToTriggerScroll = 0;
1666 if (!CGRectIsEmpty(intersectionBetweenScrollViewAndFormAssistant)) {
1667 CGFloat heightVisibleAboveFormAssistant = CGRectGetMinY(intersectionBetweenScrollViewAndFormAssistant) - CGRectGetMinY(visibleScrollViewBoundsInWebViewCoordinates);
1668 CGFloat heightVisibleBelowFormAssistant = CGRectGetMaxY(visibleScrollViewBoundsInWebViewCoordinates) - CGRectGetMaxY(intersectionBetweenScrollViewAndFormAssistant);
1669
1670 if (heightVisibleAboveFormAssistant >= minimumHeightToShowContentAboveKeyboard || heightVisibleBelowFormAssistant < heightVisibleAboveFormAssistant) {
1671 visibleSize.height = heightVisibleAboveFormAssistant;
1672 minimumDistanceFromKeyboardToTriggerScroll = 50;
1673 } else {
1674 visibleSize.height = heightVisibleBelowFormAssistant;
1675 visibleOffsetFromTop = CGRectGetMaxY(intersectionBetweenScrollViewAndFormAssistant) - CGRectGetMinY(visibleScrollViewBoundsInWebViewCoordinates);
1676 }
1677 }
1678
1679 // Zoom around the element's bounding frame. We use a "standard" size to determine the proper frame.
1680 double currentScale = contentZoomScale(self);
1681 double scale = currentScale;
1682 if (allowScaling) {
1683#if PLATFORM(WATCHOS)(defined WTF_PLATFORM_WATCHOS && WTF_PLATFORM_WATCHOS
)
1684 const CGFloat minimumMarginForZoomingToEntireFocusRectInWebViewCoordinates = 10;
1685 const CGFloat maximumMarginForZoomingToEntireFocusRectInWebViewCoordinates = 35;
1686
1687 CGRect minimumTargetRectInDocumentCoordinates = UIRectInsetEdges(focusedElementRectInDocumentCoordinates, UIRectEdgeAll, -minimumMarginForZoomingToEntireFocusRectInWebViewCoordinates / currentScale);
1688 CGRect maximumTargetRectInDocumentCoordinates = UIRectInsetEdges(focusedElementRectInDocumentCoordinates, UIRectEdgeAll, -maximumMarginForZoomingToEntireFocusRectInWebViewCoordinates / currentScale);
1689
1690 double clampedMaximumTargetScale = clampTo<double>(std::min(visibleSize.width / CGRectGetWidth(minimumTargetRectInDocumentCoordinates), visibleSize.height / CGRectGetHeight(minimumTargetRectInDocumentCoordinates)), minimumScale, maximumScale);
1691 double clampedMinimumTargetScale = clampTo<double>(std::min(visibleSize.width / CGRectGetWidth(maximumTargetRectInDocumentCoordinates), visibleSize.height / CGRectGetHeight(maximumTargetRectInDocumentCoordinates)), minimumScale, maximumScale);
1692 scale = clampTo<double>(currentScale, clampedMinimumTargetScale, clampedMaximumTargetScale);
1693#else
1694 const double webViewStandardFontSize = 16;
1695 scale = clampTo<double>(webViewStandardFontSize / fontSize, minimumScale, maximumScale);
1696#endif
1697 }
1698
1699 CGFloat documentWidth = [_contentView bounds].size.width;
1700 scale = CGRound(documentWidth * scale)round((documentWidth * scale)) / documentWidth;
1701
1702 WebCore::FloatRect focusedElementRectInNewScale = focusedElementRectInDocumentCoordinates;
1703 focusedElementRectInNewScale.scale(scale);
1704 focusedElementRectInNewScale.moveBy([_contentView frame].origin);
1705
1706 BOOL selectionRectIsNotNull = !selectionRectInDocumentCoordinates.isZero();
1707 BOOL doNotScrollWhenContentIsAlreadyVisible = !forceScroll || [_contentView _shouldAvoidScrollingWhenFocusedContentIsVisible];
1708 if (doNotScrollWhenContentIsAlreadyVisible) {
1709 CGRect currentlyVisibleRegionInWebViewCoordinates;
1710 currentlyVisibleRegionInWebViewCoordinates.origin = unobscuredScrollViewRectInWebViewCoordinates.origin;
1711 currentlyVisibleRegionInWebViewCoordinates.origin.y += visibleOffsetFromTop;
1712 currentlyVisibleRegionInWebViewCoordinates.size = visibleSize;
1713 currentlyVisibleRegionInWebViewCoordinates.size.height -= minimumDistanceFromKeyboardToTriggerScroll;
1714
1715 // Don't bother scrolling if the entire node is already visible, whether or not we got a selectionRect.
1716 if (CGRectContainsRect(currentlyVisibleRegionInWebViewCoordinates, [self convertRect:focusedElementRectInDocumentCoordinates fromView:_contentView.get()]))
1717 return;
1718
1719 // Don't bother scrolling if we have a valid selectionRect and it is already visible.
1720 if (selectionRectIsNotNull && CGRectContainsRect(currentlyVisibleRegionInWebViewCoordinates, [self convertRect:selectionRectInDocumentCoordinates fromView:_contentView.get()]))
1721 return;
1722 }
1723
1724 // We want to center the focused element within the viewport, with as much spacing on all sides as
1725 // we can get based on the visible area after zooming. The spacing in either dimension is half the
1726 // difference between the size of the DOM node and the size of the visible frame.
1727 // If the element is too wide to be horizontally centered or too tall to be vertically centered, we
1728 // instead scroll such that the left edge or top edge of the element is within the left half or top
1729 // half of the viewport, respectively.
1730 CGFloat horizontalSpaceInWebViewCoordinates = (visibleSize.width - focusedElementRectInNewScale.width()) / 2.0;
1731 CGFloat verticalSpaceInWebViewCoordinates = (visibleSize.height - focusedElementRectInNewScale.height()) / 2.0;
1732
1733 auto topLeft = CGPointZero;
1734 auto scrollViewInsets = [_scrollView adjustedContentInset];
1735 auto currentTopLeft = [_scrollView contentOffset];
1736
1737 if (_haveSetObscuredInsets) {
1738 currentTopLeft.x += _obscuredInsets.left;
1739 currentTopLeft.y += _obscuredInsets.top;
1740 }
1741
1742 if (horizontalSpaceInWebViewCoordinates > 0)
1743 topLeft.x = focusedElementRectInNewScale.x() - horizontalSpaceInWebViewCoordinates;
1744 else {
1745 auto minimumOffsetToRevealLeftEdge = std::max(-scrollViewInsets.left, focusedElementRectInNewScale.x() - visibleSize.width / 2);
1746 auto maximumOffsetToRevealLeftEdge = focusedElementRectInNewScale.x();
1747 topLeft.x = clampTo<double>(currentTopLeft.x, minimumOffsetToRevealLeftEdge, maximumOffsetToRevealLeftEdge);
1748 }
1749
1750 if (verticalSpaceInWebViewCoordinates > 0)
1751 topLeft.y = focusedElementRectInNewScale.y() - verticalSpaceInWebViewCoordinates;
1752 else {
1753 auto minimumOffsetToRevealTopEdge = std::max(-scrollViewInsets.top, focusedElementRectInNewScale.y() - visibleSize.height / 2);
1754 auto maximumOffsetToRevealTopEdge = focusedElementRectInNewScale.y();
1755 topLeft.y = clampTo<double>(currentTopLeft.y, minimumOffsetToRevealTopEdge, maximumOffsetToRevealTopEdge);
1756 }
1757
1758 topLeft.y -= visibleOffsetFromTop;
1759
1760 WebCore::FloatRect documentBoundsInNewScale = [_contentView bounds];
1761 documentBoundsInNewScale.scale(scale);
1762 documentBoundsInNewScale.moveBy([_contentView frame].origin);
1763
1764 CGFloat minimumAllowableHorizontalOffsetInWebViewCoordinates = -INFINITY__builtin_huge_valf();
1765 CGFloat minimumAllowableVerticalOffsetInWebViewCoordinates = -INFINITY__builtin_huge_valf();
1766 CGFloat maximumAllowableHorizontalOffsetInWebViewCoordinates = CGRectGetMaxX(documentBoundsInNewScale) - visibleSize.width;
1767 CGFloat maximumAllowableVerticalOffsetInWebViewCoordinates = CGRectGetMaxY(documentBoundsInNewScale) - visibleSize.height;
1768
1769 if (selectionRectIsNotNull) {
1770 WebCore::FloatRect selectionRectInNewScale = selectionRectInDocumentCoordinates;
1771 selectionRectInNewScale.scale(scale);
1772 selectionRectInNewScale.moveBy([_contentView frame].origin);
1773 // Adjust the min and max allowable scroll offsets, such that the selection rect remains visible.
1774 minimumAllowableHorizontalOffsetInWebViewCoordinates = CGRectGetMaxX(selectionRectInNewScale) + caretOffsetFromWindowEdge - visibleSize.width;
1775 minimumAllowableVerticalOffsetInWebViewCoordinates = CGRectGetMaxY(selectionRectInNewScale) + caretOffsetFromWindowEdge - visibleSize.height - visibleOffsetFromTop;
1776 maximumAllowableHorizontalOffsetInWebViewCoordinates = std::min<CGFloat>(maximumAllowableHorizontalOffsetInWebViewCoordinates, CGRectGetMinX(selectionRectInNewScale) - caretOffsetFromWindowEdge);
1777 maximumAllowableVerticalOffsetInWebViewCoordinates = std::min<CGFloat>(maximumAllowableVerticalOffsetInWebViewCoordinates, CGRectGetMinY(selectionRectInNewScale) - caretOffsetFromWindowEdge - visibleOffsetFromTop);
1778 }
1779
1780 // Constrain the left edge in document coordinates so that:
1781 // - it isn't so small that the scrollVisibleRect isn't visible on the screen
1782 // - it isn't so great that the document's right edge is less than the right edge of the screen
1783 topLeft.x = clampTo<CGFloat>(topLeft.x, minimumAllowableHorizontalOffsetInWebViewCoordinates, maximumAllowableHorizontalOffsetInWebViewCoordinates);
1784
1785 // Constrain the top edge in document coordinates so that:
1786 // - it isn't so small that the scrollVisibleRect isn't visible on the screen
1787 // - it isn't so great that the document's bottom edge is higher than the top of the form assistant
1788 topLeft.y = clampTo<CGFloat>(topLeft.y, minimumAllowableVerticalOffsetInWebViewCoordinates, maximumAllowableVerticalOffsetInWebViewCoordinates);
1789
1790 if (_haveSetObscuredInsets) {
1791 // This looks unintuitive, but is necessary in order to precisely center the focused element in the visible area.
1792 // The top left position already accounts for top and left obscured insets - i.e., a topLeft of (0, 0) corresponds
1793 // to the top- and left-most point below (and to the right of) the top inset area and left inset areas, respectively.
1794 // However, when telling WKScrollView to scroll to a given center position, this center position is computed relative
1795 // to the coordinate space of the scroll view. Thus, to compute our center position from the top left position, we
1796 // need to first move the top left position up and to the left, and then add half the width and height of the content
1797 // area (including obscured insets).
1798 topLeft.x -= _obscuredInsets.left;
1799 topLeft.y -= _obscuredInsets.top;
1800 }
1801
1802 WebCore::FloatPoint newCenter = CGPointMake(topLeft.x + CGRectGetWidth(self.bounds) / 2, topLeft.y + CGRectGetHeight(self.bounds) / 2);
1803
1804 if (scale != currentScale)
1805 _page->willStartUserTriggeredZooming();
1806
1807 LOG_WITH_STREAM(VisibleRects, stream << "_zoomToFocusRect: zooming to " << newCenter << " scale:" << scale)((void)0);
1808
1809 // The newCenter has been computed in the new scale, but _zoomToCenter expected the center to be in the original scale.
1810 newCenter.scale(1 / scale);
1811 [self _zoomToCenter:newCenter atScale:scale animated:YES__objc_yes honorScrollability:NO__objc_no];
1812}
1813
1814- (double)_initialScaleFactor
1815{
1816 return _perProcessState.initialScaleFactor;
1817}
1818
1819- (double)_contentZoomScale
1820{
1821 return contentZoomScale(self);
1822}
1823
1824- (double)_targetContentZoomScaleForRect:(const WebCore::FloatRect&)targetRect currentScale:(double)currentScale fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale
1825{
1826 WebCore::FloatSize unobscuredContentSize([self _contentRectForUserInteraction].size);
1827 double horizontalScale = unobscuredContentSize.width() * currentScale / targetRect.width();
1828 double verticalScale = unobscuredContentSize.height() * currentScale / targetRect.height();
1829
1830 horizontalScale = std::min(std::max(horizontalScale, minimumScale), maximumScale);
1831 verticalScale = std::min(std::max(verticalScale, minimumScale), maximumScale);
1832
1833 return fitEntireRect ? std::min(horizontalScale, verticalScale) : horizontalScale;
1834}
1835
1836- (BOOL)_zoomToRect:(WebCore::FloatRect)targetRect withOrigin:(WebCore::FloatPoint)origin fitEntireRect:(BOOL)fitEntireRect minimumScale:(double)minimumScale maximumScale:(double)maximumScale minimumScrollDistance:(float)minimumScrollDistance
1837{
1838 const float maximumScaleFactorDeltaForPanScroll = 0.02;
1839
1840 double currentScale = contentZoomScale(self);
1841 double targetScale = [self _targetContentZoomScaleForRect:targetRect currentScale:currentScale fitEntireRect:fitEntireRect minimumScale:minimumScale maximumScale:maximumScale];
1842
1843 if (std::abs(targetScale - currentScale) < maximumScaleFactorDeltaForPanScroll) {
1844 if ([self _scrollToRect:targetRect origin:origin minimumScrollDistance:minimumScrollDistance])
1845 return true;
1846 } else if (targetScale != currentScale) {
1847 [self _zoomToRect:targetRect atScale:targetScale origin:origin animated:YES__objc_yes];
1848 return true;
1849 }
1850
1851 return false;
1852}
1853
1854- (void)_updateLastKnownWindowSizeAndOrientation
1855{
1856 RetainPtr scene = [self.window windowScene];
1857 RetainPtr geometry = [scene effectiveGeometry];
1858 _lastKnownWindowSizeAndOrientation = {
1859#if HAVE(UI_WINDOW_SCENE_GEOMETRY_COORDINATE_SPACE)(defined 1 && 1)
1860 [geometry coordinateSpace].bounds.size,
1861#else
1862 [scene coordinateSpace].bounds.size,
1863#endif
1864 [geometry interfaceOrientation]
1865 };
1866}
1867
1868- (void)didMoveToWindow
1869{
1870 if (!_overridesInterfaceOrientation)
1871 [self _dispatchSetDeviceOrientation:[self _deviceOrientationIgnoringOverrides]];
1872 [self _dispatchSetOrientationForMediaCapture:[self _deviceOrientationIgnoringOverrides]];
1873 _page->activityStateDidChange(WebCore::allActivityStates());
1874 _page->webViewDidMoveToWindow();
1875 [self _presentLockdownModeAlertIfNeeded];
1876#if HAVE(UIKIT_RESIZABLE_WINDOWS)(defined 1 && 1)
1877 if (_page->hasRunningProcess() && self.window)
1878 _page->setIsWindowResizingEnabled(self._isWindowResizingEnabled);
1879 [self _invalidateResizeAssertions];
1880#endif
1881#if HAVE(UI_WINDOW_SCENE_LIVE_RESIZE)(defined 1 && 1)
1882 [self _endLiveResize];
1883#endif
1884 [self _updateLastKnownWindowSizeAndOrientation];
1885}
1886
1887#if HAVE(UIKIT_RESIZABLE_WINDOWS)(defined 1 && 1)
1888
1889- (BOOL)_isWindowResizingEnabled
1890{
1891#if PLATFORM(VISION)(defined WTF_PLATFORM_VISION && WTF_PLATFORM_VISION)
1892 // This is technically incorrect, but matches longstanding behavior, and avoids layout regressions on visionOS.
1893 return NO__objc_no;
1894#else
1895 return self.window.windowScene._enhancedWindowingEnabled;
1896#endif
1897}
1898
1899#endif // HAVE(UIKIT_RESIZABLE_WINDOWS)
1900
1901#if ENABLE(INTERACTION_REGIONS_IN_EVENT_REGION)(defined ENABLE_INTERACTION_REGIONS_IN_EVENT_REGION &&
ENABLE_INTERACTION_REGIONS_IN_EVENT_REGION)
1902- (void)_setInteractionRegionsEnabled:(BOOL)enabled
1903{
1904 WKWEBVIEW_RELEASE_LOG("%p -[WKWebView _setInteractionRegionsEnabled] page - %p", self, _page.get());
1905 if (_page && _page->preferences().interactionRegionsEnabled())
1906 _page->setInteractionRegionsEnabled(enabled);
1907}
1908#endif // ENABLE(INTERACTION_REGIONS_IN_EVENT_REGION)
1909
1910
1911- (void)_setOpaqueInternal:(BOOL)opaque
1912{
1913 [super setOpaque:opaque];
1914
1915 [_contentView setOpaque:opaque];
1916
1917#if PLATFORM(VISION)(defined WTF_PLATFORM_VISION && WTF_PLATFORM_VISION)
1918 static BOOL alternateRenderingAvailable = [UIScrollView instancesRespondToSelector:@selector(_setUseVisionAlternateScrollIndicatorRendering:)];
1919 if (alternateRenderingAvailable)
1920 [_scrollView _setUseVisionAlternateScrollIndicatorRendering:opaque];
1921#endif
1922
1923 if (!_page)
1924 return;
1925
1926 std::optional<WebCore::Color> backgroundColor;
1927 if (!opaque)
1928 backgroundColor = WebCore::Color(WebCore::Color::transparentBlack);
1929 _page->setBackgroundColor(backgroundColor);
1930
1931 [self _updateScrollViewBackground];
1932}
1933
1934- (void)setOpaque:(BOOL)opaque
1935{
1936 if (opaque == self.opaque)
1937 return;
1938
1939 [self _setOpaqueInternal:opaque];
1940}
1941
1942- (void)setBackgroundColor:(UIColor *)backgroundColor
1943{
1944 [super setBackgroundColor:backgroundColor];
1945 [_contentView setBackgroundColor:backgroundColor];
1946 [self _updateScrollViewBackground];
1947}
1948
1949- (BOOL)_allowsDoubleTapGestures
1950{
1951 if (_fastClickingIsDisabled)
1952 return YES__objc_yes;
1953
1954 // If the page is not user scalable, we don't allow double tap gestures.
1955 if (![_scrollView isZoomEnabled] || [_scrollView minimumZoomScale] >= [_scrollView maximumZoomScale] || WebKit::scalesAreEssentiallyEqual([_scrollView minimumZoomScale], [_scrollView maximumZoomScale]))
1956 return NO__objc_no;
1957
1958 // If the viewport width was not explicit, we allow double tap gestures.
1959 if (!_perProcessState.viewportMetaTagWidthWasExplicit || _perProcessState.viewportMetaTagCameFromImageDocument)
1960 return YES__objc_yes;
1961
1962 // If the page set a viewport width that wasn't the device width, then it was
1963 // scaled and thus will probably need to zoom.
1964 if (_perProcessState.viewportMetaTagWidth != WebCore::ViewportArguments::ValueDeviceWidth)
1965 return YES__objc_yes;
1966
1967 // At this point, we have a page that asked for width = device-width. However,
1968 // if the content's width and height were large, we might have had to shrink it.
1969 // We'll enable double tap zoom whenever we're not at the actual initial scale.
1970 return !WebKit::scalesAreEssentiallyEqual(contentZoomScale(self), _perProcessState.initialScaleFactor);
1971}
1972
1973#pragma mark UIScrollViewDelegate
1974
1975- (BOOL)usesStandardContentView
1976{
1977 return !_customContentView && !_passwordView;
1978}
1979
1980- (CGSize)scrollView:(UIScrollView*)scrollView contentSizeForZoomScale:(CGFloat)scale withProposedSize:(CGSize)proposedSize
1981{
1982 return roundScrollViewContentSize(*_page, proposedSize);
1983}
1984
1985- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView
1986{
1987 if (_resizeAnimationView)
1988 return nilnullptr;
1989
1990 ASSERT(_scrollView == scrollView)((void)0);
1991 return self._currentContentView;
1992}
1993
1994- (void)scrollViewWillBeginZooming:(UIScrollView *)scrollView withView:(UIView *)view
1995{
1996 if (![self usesStandardContentView])
1997 return;
1998
1999 if (scrollView.pinchGestureRecognizer.state == UIGestureRecognizerStateBegan) {
2000 _page->willStartUserTriggeredZooming();
2001
2002 [_contentView scrollViewWillStartPanOrPinchGesture];
2003 }
2004 [_contentView willStartZoomOrScroll];
2005
2006 [_contentView cancelPointersForGestureRecognizer:scrollView.pinchGestureRecognizer];
2007}
2008
2009- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView
2010{
2011 if (![self usesStandardContentView])
2012 return;
2013
2014 if (scrollView.panGestureRecognizer.state == UIGestureRecognizerStateBegan)
2015 [_contentView scrollViewWillStartPanOrPinchGesture];
2016
2017 [_contentView willStartZoomOrScroll];
2018
2019 // FIXME: We will want to detect whether snapping will occur before beginning to drag. See WebPageProxy::didCommitLayerTree.
2020 ASSERT(scrollView == _scrollView.get())((void)0);
2021 if (CheckedPtr coordinator = downcast<WebKit::RemoteScrollingCoordinatorProxyIOS>(_page->scrollingCoordinatorProxy())) {
2022 [_scrollView _setDecelerationRateInternal:(coordinator->shouldSetScrollViewDecelerationRateFast()) ? UIScrollViewDecelerationRateFast : UIScrollViewDecelerationRateNormal];
2023 coordinator->setRootNodeIsInUserScroll(true);
2024 }
2025}
2026
2027- (void)_didFinishScrolling:(UIScrollView *)scrollView
2028{
2029 if (scrollView != _scrollView.get())
2030 return;
2031
2032 if (![self usesStandardContentView])
2033 return;
2034
2035#if ENABLE(OVERLAY_REGIONS_IN_EVENT_REGION)(defined ENABLE_OVERLAY_REGIONS_IN_EVENT_REGION && ENABLE_OVERLAY_REGIONS_IN_EVENT_REGION
)
2036 _isScrollingWithOverlayRegion = NO__objc_no;
2037#endif
2038
2039 [self _scheduleVisibleContentRectUpdate];
2040 [_contentView didFinishScrolling];
2041
2042 if (CheckedPtr coordinator = _page->scrollingCoordinatorProxy())
2043 coordinator->setRootNodeIsInUserScroll(false);
2044}
2045
2046- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset
2047{
2048 // Work around <rdar://problem/16374753> by avoiding deceleration while
2049 // zooming. We'll animate to the right place once the zoom finishes.
2050 if ([scrollView isZooming])
2051 *targetContentOffset = [scrollView contentOffset];
2052 else {
2053 auto axesToPreventMomentumScrolling = dynamic_objc_cast<WKBaseScrollView>(scrollView).axesToPreventMomentumScrolling;
2054 if ([_contentView preventsPanningInXAxis] || (axesToPreventMomentumScrolling & UIAxisHorizontal))
2055 targetContentOffset->x = scrollView.contentOffset.x;
2056 if ([_contentView preventsPanningInYAxis] || (axesToPreventMomentumScrolling & UIAxisVertical))
2057 targetContentOffset->y = scrollView.contentOffset.y;
2058 }
2059
2060 if (CheckedPtr coordinator = downcast<WebKit::RemoteScrollingCoordinatorProxyIOS>(_page->scrollingCoordinatorProxy())) {
2061 // FIXME: Here, I'm finding the maximum horizontal/vertical scroll offsets. There's probably a better way to do this.
2062 CGSize maxScrollOffsets = CGSizeMake(scrollView.contentSize.width - scrollView.bounds.size.width, scrollView.contentSize.height - scrollView.bounds.size.height);
2063
2064 UIEdgeInsets obscuredInset;
2065
2066 id<WKUIDelegatePrivate> uiDelegatePrivate = static_cast<id <WKUIDelegatePrivate>>([self UIDelegate]);
2067 if ([uiDelegatePrivate respondsToSelector:@selector(_webView:finalObscuredInsetsForScrollView:withVelocity:targetContentOffset:)])
2068 obscuredInset = [uiDelegatePrivate _webView:self finalObscuredInsetsForScrollView:scrollView withVelocity:velocity targetContentOffset:targetContentOffset];
2069 else
2070 obscuredInset = [self _computedObscuredInset];
2071
2072 CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, obscuredInset);
2073
2074 coordinator->adjustTargetContentOffsetForSnapping(maxScrollOffsets, velocity, unobscuredRect.origin.y, scrollView.contentOffset, targetContentOffset);
2075 }
2076}
2077
2078- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate
2079{
2080 // If we're decelerating, scroll offset will be updated when scrollViewDidFinishDecelerating: is called.
2081 if (!decelerate)
2082 [self _didFinishScrolling:scrollView];
2083}
2084
2085- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
2086{
2087 [self _didFinishScrolling:scrollView];
2088}
2089
2090- (void)scrollViewDidScrollToTop:(UIScrollView *)scrollView
2091{
2092 [self _didFinishScrolling:scrollView];
2093}
2094
2095#if HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)(defined 1 && 1)
2096
2097#if !USE(BROWSERENGINEKIT)(defined 1 && 1)
2098
2099- (void)_scrollView:(WKScrollView *)scrollView asynchronouslyHandleScrollEvent:(WKBEScrollViewScrollUpdateBEScrollViewScrollUpdate *)scrollEvent completion:(void (^)(BOOL handled))completion
2100{
2101 [self scrollView:scrollView handleScrollUpdate:scrollEvent completion:completion];
2102}
2103
2104#endif // !USE(BROWSERENGINEKIT)
2105
2106- (void)scrollView:(WKBaseScrollView *)scrollView handleScrollUpdate:(WKBEScrollViewScrollUpdateBEScrollViewScrollUpdate *)update completion:(void (^)(BOOL handled))completion
2107{
2108 if (_pointerTouchCompatibilitySimulator->handleScrollUpdate(scrollView, update))
2109 return completion(YES__objc_yes);
2110
2111 BOOL isHandledByDefault = [self, update] -> BOOL {
2112 RetainPtr hitView = [_scrollView hitTest:[update locationInView:_scrollView.get()] withEvent:nilnullptr];
2113 return ![_contentView _hasEnclosingScrollView:hitView.get() matchingCriteria:[](UIScrollView *scroller) {
2114 return scroller.scrollEnabled;
2115 }];
2116 }();
2117
2118#if ENABLE(OVERLAY_REGIONS_IN_EVENT_REGION)(defined ENABLE_OVERLAY_REGIONS_IN_EVENT_REGION && ENABLE_OVERLAY_REGIONS_IN_EVENT_REGION
)
2119 if (update._scrollDeviceCategory == _UIScrollDeviceCategoryOverlayScroll) {
2120 _isScrollingWithOverlayRegion = YES__objc_yes;
2121 completion(isHandledByDefault);
2122 return;
2123 }
2124 _isScrollingWithOverlayRegion = NO__objc_no;
2125#endif
2126
2127#if !USE(BROWSERENGINEKIT)(defined 1 && 1)
2128 if (update.phase == UIScrollPhaseMayBegin) {
2129 completion(isHandledByDefault);
2130 return;
2131 }
2132#endif
2133
2134 if (update.phase == WKBEScrollViewScrollUpdatePhaseBeganBEScrollViewScrollUpdatePhaseBegan) {
2135 _currentScrollGestureState = std::nullopt;
2136 _wheelEventCountInCurrentScrollGesture = 0;
2137 }
2138
2139 WebCore::IntPoint scrollLocation = WebCore::roundedIntPoint([update locationInView:_contentView.get()]);
2140 auto eventListeners = WebKit::eventListenerTypesAtPoint(_contentView.get(), scrollLocation);
2141 bool hasWheelHandlers = eventListeners.contains(WebCore::EventListenerRegionType::Wheel);
2142 if (!hasWheelHandlers) {
2143 completion(isHandledByDefault);
2144 return;
2145 }
2146
2147 // Scroll events with zero delta are not dispatched to the page, so cannot be
2148 // cancelled, so we can short-circuit them here.
2149 // We make an exception for end-phase events, similar to the logic in
2150 // EventHandler::handleWheelEventInAppropriateEnclosingBox.
2151 if (WebKit::WebIOSEventFactory::translationInView(update, _contentView.get()).isZero() && update.phase != WKBEScrollViewScrollUpdatePhaseEndedBEScrollViewScrollUpdatePhaseEnded) {
2152 completion(isHandledByDefault);
2153 return;
2154 }
2155
2156 bool hasActiveWheelHandlers = eventListeners.contains(WebCore::EventListenerRegionType::NonPassiveWheel);
2157 bool isCancelable = hasActiveWheelHandlers && (!_currentScrollGestureState || _currentScrollGestureState == WebCore::WheelScrollGestureState::Blocking);
2158
2159 std::optional<WebKit::WebWheelEvent::Phase> overridePhase;
2160 // The first event with non-zero delta in a given gesture should be considered the
2161 // "Began" event in the WebCore sense (e.g. for deciding cancelability). Note that
2162 // this may not be a WKBEScrollViewScrollUpdatePhaseBegin event, nor even necessarily the first WKBEScrollViewScrollUpdatePhaseChanged event.
2163 if (!_wheelEventCountInCurrentScrollGesture)
2164 overridePhase = WebKit::WebWheelEvent::Phase::Began;
2165 auto event = WebKit::WebIOSEventFactory::createWebWheelEvent(update, _contentView.get(), overridePhase);
2166
2167 _wheelEventCountInCurrentScrollGesture++;
2168 _page->dispatchWheelEventWithoutScrolling(event, [weakSelf = WeakObjCPtr<WKWebView>(self), strongCompletion = makeBlockPtr(completion), isCancelable, isHandledByDefault](bool defaultPrevented) {
2169 RetainPtr strongSelf = weakSelf.get();
2170 if (!strongSelf) {
2171 if (isCancelable)
2172 strongCompletion(isHandledByDefault);
2173 return;
2174 }
2175
2176 if (isCancelable) {
2177 if (!strongSelf->_currentScrollGestureState)
2178 strongSelf->_currentScrollGestureState = defaultPrevented ? WebCore::WheelScrollGestureState::Blocking : WebCore::WheelScrollGestureState::NonBlocking;
2179 strongCompletion(isHandledByDefault || defaultPrevented);
2180 }
2181 });
2182
2183 if (!isCancelable)
2184 completion(isHandledByDefault);
2185}
2186
2187#endif // HAVE(UISCROLLVIEW_ASYNCHRONOUS_SCROLL_EVENT_HANDLING)
2188
2189#if ENABLE(CONTENT_INSET_BACKGROUND_FILL)(defined 1 && 1)
2190
2191- (void)_reinsertTopFixedColorExtensionViewIfNeeded
2192{
2193 RetainPtr topColorExtensionView = _fixedColorExtensionViews.at(WebCore::BoxSide::Top);
2194 if (!topColorExtensionView || [topColorExtensionView isHidden])
2195 return;
2196
2197 std::optional<NSInteger> refreshControlIndex;
2198 std::optional<NSInteger> topColorExtensionViewIndex;
2199 std::optional<NSInteger> contentViewIndex;
2200
2201 NSInteger index = 0;
2202 RetainPtr refreshControl = [_scrollView refreshControl];
2203 for (UIView *subview in [_scrollView subviews]) {
2204 if (subview == refreshControl)
2205 refreshControlIndex = index;
2206 else if (subview == topColorExtensionView)
2207 topColorExtensionViewIndex = index;
2208 else if (subview == _contentView)
2209 contentViewIndex = index;
2210
2211 if (refreshControlIndex && topColorExtensionViewIndex && contentViewIndex)
2212 break;
2213
2214 index++;
2215 }
2216
2217 if (!topColorExtensionViewIndex)
2218 return;
2219
2220 BOOL scrolledBeyondTopExtent = [_scrollView _wk_isScrolledBeyondTopExtent];
2221 if (scrolledBeyondTopExtent && refreshControlIndex && *refreshControlIndex < *topColorExtensionViewIndex)
2222 [_scrollView insertSubview:topColorExtensionView.get() belowSubview:refreshControl.get()];
2223 else if (!scrolledBeyondTopExtent && contentViewIndex && *topColorExtensionViewIndex < *contentViewIndex)
2224 [_scrollView insertSubview:topColorExtensionView.get() aboveSubview:_contentView.get()];
2225}
2226
2227- (void)_updateNeedsTopScrollPocketDueToVisibleContentInset
2228{
2229 if (!std::exchange(_shouldUpdateNeedsTopScrollPocketDueToVisibleContentInset, NO__objc_no))
2230 return;
2231
2232 BOOL value = [&] -> BOOL {
2233 if ([_scrollView adjustedContentInset].top <= self._computedObscuredInset.top + CGFLOAT_EPSILON2.2204460492503131e-16)
2234 return NO__objc_no;
2235
2236 if ([_scrollView _wk_isScrolledBeyondTopExtent])
2237 return NO__objc_no;
2238
2239 if ([_scrollView _usesHardTopScrollEdgeEffect])
2240 return NO__objc_no;
2241
2242 return [_scrollView contentOffset].y < 0;
2243 }();
2244
2245 if (_needsTopScrollPocketDueToVisibleContentInset == value)
2246 return;
2247
2248 _needsTopScrollPocketDueToVisibleContentInset = value;
2249
2250 [self _updateHiddenScrollPocketEdges];
2251 [self _updateTopScrollPocketCaptureColor];
2252}
2253
2254- (BOOL)_shouldHideTopScrollPocket
2255{
2256 if (_reasonsToHideTopScrollPocket)
2257 return YES__objc_yes;
2258
2259 if (_needsTopScrollPocketDueToVisibleContentInset)
2260 return NO__objc_no;
2261
2262 if ([_scrollView _usesHardTopScrollEdgeEffect])
2263 return NO__objc_no;
2264
2265 return [self _hasVisibleColorExtensionView:WebCore::BoxSide::Top];
2266}
2267
2268#endif // ENABLE(CONTENT_INSET_BACKGROUND_FILL)
2269
2270- (void)scrollViewDidChangeAdjustedContentInset:(UIScrollView *)scrollView
2271{
2272#if ENABLE(CONTENT_INSET_BACKGROUND_FILL)(defined 1 && 1)
2273 _shouldUpdateNeedsTopScrollPocketDueToVisibleContentInset = YES__objc_yes;
2274#endif
2275
2276#if !PLATFORM(WATCHOS)(defined WTF_PLATFORM_WATCHOS && WTF_PLATFORM_WATCHOS
)
2277 if (!self._shouldDeferGeometryUpdates && !_overriddenLayoutParameters)
2278 [self _dispatchSetViewLayoutSize:[self activeViewLayoutSize:self.bounds]];
2279#endif
2280}
2281
2282- (void)scrollViewDidScroll:(UIScrollView *)scrollView
2283{
2284 if (scrollView == _scrollView) {
2285 [_scrollView updateInteractiveScrollVelocity];
2286#if ENABLE(CONTENT_INSET_BACKGROUND_FILL)(defined 1 && 1)
2287 [self _updateFixedColorExtensionViewFrames];
2288 [self _reinsertTopFixedColorExtensionViewIfNeeded];
2289 _shouldUpdateNeedsTopScrollPocketDueToVisibleContentInset = YES__objc_yes;
2290#endif
2291 }
2292
2293#if HAVE(UIFINDINTERACTION)(defined 1 && 1)
2294 [self _updateFindOverlayPosition];
2295#endif
2296
2297 [self _scheduleVisibleContentRectUpdateAfterScrollInView:scrollView];
2298
2299 if (WebKit::RemoteLayerTreeScrollingPerformanceData* scrollPerfData = _page->scrollingPerformanceData())
2300 scrollPerfData->didScroll([self visibleRectInViewCoordinates]);
2301
2302 [_contentView updateSelection];
2303
2304#if ENABLE(PDF_PAGE_NUMBER_INDICATOR)(defined 1 && 1)
2305 [self _updatePDFPageNumberIndicatorIfNeeded];
2306#endif
2307}
2308
2309- (void)scrollViewDidZoom:(UIScrollView *)scrollView
2310{
2311 [self _updateScrollViewBackground];
2312 [self _scheduleVisibleContentRectUpdateAfterScrollInView:scrollView];
2313
2314#if ENABLE(MODEL_PROCESS)(defined 0 && 0)
2315 [_contentView _setTransform3DForModelViews:[scrollView zoomScale]];
2316#endif
2317}
2318
2319- (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(UIView *)view atScale:(CGFloat)scale
2320{
2321 ASSERT(scrollView == _scrollView)((void)0);
2322
2323 // FIXME: remove when rdar://problem/36065495 is fixed.
2324 // When rotating with two fingers down, UIScrollView can set a bogus content view position.
2325 // "Center" is top left because we set the anchorPoint to 0,0.
2326 [_contentView setCenter:self.bounds.origin];
2327
2328 [self _scheduleVisibleContentRectUpdateAfterScrollInView:scrollView];
2329 [_contentView didZoomToScale:scale];
2330
2331 _page->didEndUserTriggeredZooming();
2332}
2333
2334- (void)scrollViewDidEndScrollingAnimation:(UIScrollView *)scrollView
2335{
2336 [self _didFinishScrolling:scrollView];
2337}
2338
2339- (void)_scrollViewDidInterruptDecelerating:(UIScrollView *)scrollView
2340{
2341 if (![self usesStandardContentView])
2342 return;
2343
2344 _historicalKinematicData.clear();
2345 [self _scheduleVisibleContentRectUpdateAfterScrollInView:scrollView];
2346}
2347
2348#pragma mark end UIScrollViewDelegate
2349
2350- (CGRect)_visibleRectInEnclosingView:(UIView *)enclosingView
2351{
2352 if (!enclosingView)
2353 return self.bounds;
2354
2355 CGRect exposedRect = [enclosingView convertRect:enclosingView.bounds toView:self];
2356 return CGRectIntersectsRect(exposedRect, self.bounds) ? CGRectIntersection(exposedRect, self.bounds) : CGRectZero;
2357}
2358
2359- (CGRect)_visibleContentRect
2360{
2361 if (_perProcessState.frozenVisibleContentRect)
2362 return _perProcessState.frozenVisibleContentRect.value();
2363
2364 CGRect visibleRectInContentCoordinates = [self convertRect:self.bounds toView:_contentView.get()];
2365
2366 if (UIView *enclosingView = [self _enclosingViewForExposedRectComputation]) {
2367 CGRect viewVisibleRect = [self _visibleRectInEnclosingView:enclosingView];
2368 CGRect viewVisibleContentRect = [self convertRect:viewVisibleRect toView:_contentView.get()];
2369 visibleRectInContentCoordinates = CGRectIntersection(visibleRectInContentCoordinates, viewVisibleContentRect);
2370 }
2371
2372 return visibleRectInContentCoordinates;
2373}
2374
2375// Called when some ancestor UIScrollView scrolls.
2376- (void)_didScroll
2377{
2378 [self _scheduleVisibleContentRectUpdateAfterScrollInView:[self _scroller]];
2379
2380 const NSTimeInterval ScrollingEndedTimerInterval = 0.032;
2381 if (!_enclosingScrollViewScrollTimer) {
2382 _enclosingScrollViewScrollTimer = adoptNS([[NSTimer alloc] initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:ScrollingEndedTimerInterval]
2383 interval:0 target:self selector:@selector(_enclosingScrollerScrollingEnded:) userInfo:nilnullptr repeats:YES__objc_yes]);
2384 [[NSRunLoop mainRunLoop] addTimer:_enclosingScrollViewScrollTimer.get() forMode:NSDefaultRunLoopMode];
2385 }
2386 _didScrollSinceLastTimerFire = YES__objc_yes;
2387}
2388
2389- (void)_enclosingScrollerScrollingEnded:(NSTimer *)timer
2390{
2391 if (_didScrollSinceLastTimerFire) {
2392 _didScrollSinceLastTimerFire = NO__objc_no;
2393 return;
2394 }
2395
2396 [self _scheduleVisibleContentRectUpdate];
2397 [_enclosingScrollViewScrollTimer invalidate];
2398 _enclosingScrollViewScrollTimer = nilnullptr;
2399}
2400
2401- (UIEdgeInsets)_scrollViewSystemContentInset
2402{
2403 // It's not safe to access the scroll view's safeAreaInsets or _systemContentInset from
2404 // inside our layoutSubviews implementation, because they aren't updated until afterwards.
2405 // Instead, depend on the fact that the UIScrollView and WKWebView are in the same coordinate
2406 // space, and map the WKWebView's own insets into the scroll view manually.
2407 return UIEdgeInsetsAdd([_scrollView _contentScrollInset], self.safeAreaInsets, [_scrollView _edgesApplyingSafeAreaInsetsToContentInset]);
2408}
2409
2410- (WebCore::FloatSize)activeViewLayoutSize:(const CGRect&)bounds
2411{
2412 if (_overriddenLayoutParameters)
2413 return WebCore::FloatSize(_overriddenLayoutParameters->viewLayoutSize);
2414
2415 auto layoutInsets = self._scrollViewSystemContentInset;
2416 if (_automaticallyAdjustsViewLayoutSizesWithObscuredInset)
2417 layoutInsets = WebKit::maxEdgeInsets(layoutInsets, self._obscuredInsets);
2418
2419 return WebCore::FloatSize { UIEdgeInsetsInsetRect(bounds, layoutInsets).size };
2420}
2421
2422- (void)_dispatchSetViewLayoutSize:(WebCore::FloatSize)viewLayoutSize
2423{
2424 auto newMinimumEffectiveDeviceWidth = _page->minimumEffectiveDeviceWidth();
2425 if (_perProcessState.lastSentViewLayoutSize && CGSizeEqualToSize__CGSizeEqualToSize(_perProcessState.lastSentViewLayoutSize.value(), viewLayoutSize) && _perProcessState.lastSentMinimumEffectiveDeviceWidth && _perProcessState.lastSentMinimumEffectiveDeviceWidth == newMinimumEffectiveDeviceWidth)
2426 return;
2427
2428 LOG_WITH_STREAM(VisibleRects, stream << "-[WKWebView " << _page->identifier() << " _dispatchSetViewLayoutSize:] " << viewLayoutSize << " contentZoomScale " << contentZoomScale(self))((void)0);
2429 _page->setViewportConfigurationViewLayoutSize(viewLayoutSize, _page->layoutSizeScaleFactorFromClient(), newMinimumEffectiveDeviceWidth);
2430 _perProcessState.lastSentViewLayoutSize = viewLayoutSize;
2431 _perProcessState.lastSentMinimumEffectiveDeviceWidth = newMinimumEffectiveDeviceWidth;
2432}
2433
2434- (void)_dispatchSetDeviceOrientation:(WebCore::IntDegrees)deviceOrientation
2435{
2436 if (_perProcessState.lastSentDeviceOrientation && _perProcessState.lastSentDeviceOrientation.value() == deviceOrientation)
2437 return;
2438
2439 _page->setDeviceOrientation(deviceOrientation);
2440 _perProcessState.lastSentDeviceOrientation = deviceOrientation;
2441}
2442
2443- (void)_dispatchSetOrientationForMediaCapture:(WebCore::IntDegrees)orientationForMediaCapture
2444{
2445 if (_perProcessState.lastSentOrientationForMediaCapture && _perProcessState.lastSentOrientationForMediaCapture.value() == orientationForMediaCapture)
2446 return;
2447
2448 _page->setOrientationForMediaCapture(orientationForMediaCapture);
2449 _perProcessState.lastSentOrientationForMediaCapture = orientationForMediaCapture;
2450}
2451
2452- (BOOL)_updateScrollViewContentInsetsIfNecessary
2453{
2454#if PLATFORM(WATCHOS)(defined WTF_PLATFORM_WATCHOS && WTF_PLATFORM_WATCHOS
)
2455 return [_scrollView _setContentScrollInsetInternal:self._safeAreaShouldAffectObscuredInsets ? self._contentInsetsFromSystemMinimumLayoutMargins : UIEdgeInsetsZero];
2456#else
2457 return NO__objc_no;
2458#endif
2459}
2460
2461#if HAVE(UI_WINDOW_SCENE_LIVE_RESIZE)(defined 1 && 1)
2462
2463- (BOOL)_canBeginAutomaticLiveResize
2464{
2465 if (!_page)
2466 return false;
2467
2468 if (!protect(_page->preferences())->automaticLiveResizeEnabled())
2469 return false;
2470
2471 if (![self usesStandardContentView])
2472 return false;
2473
2474 if (!self.window)
2475 return false;
2476
2477 if (CGRectIsEmpty(self.bounds))
2478 return false;
2479
2480 return true;
2481}
2482
2483- (void)_beginAutomaticLiveResizeIfNeeded
2484{
2485 if (![self _canBeginAutomaticLiveResize])
2486 return;
2487
2488 if (_perProcessState.dynamicViewportUpdateMode != WebKit::DynamicViewportUpdateMode::NotResizing)
2489 return;
2490
2491 [self _rescheduleEndLiveResizeTimer];
2492
2493 if (_perProcessState.liveResizeParameters)
2494 return;
2495
2496 [self _beginLiveResize];
2497}
2498
2499- (void)_rescheduleEndLiveResizeTimer
2500{
2501 [_endLiveResizeTimer invalidate];
2502
2503 constexpr auto endLiveResizeHysteresis = 500_ms;
2504
2505 _endLiveResizeTimer = [NSTimer
2506 scheduledTimerWithTimeInterval:endLiveResizeHysteresis.seconds()
2507 repeats:NO__objc_no
2508 block:makeBlockPtr([weakSelf = WeakObjCPtr<WKWebView>(self)](NSTimer *) {
2509 auto strongSelf = weakSelf.get();
2510 [strongSelf _endLiveResize];
2511 }).get()];
2512}
2513
2514- (void)_updateLiveResizeTransform
2515{
2516 CGFloat scale = self.bounds.size.width / _perProcessState.liveResizeParameters->viewWidth;
2517 CGAffineTransform transform = CGAffineTransformMakeScale(scale, scale);
2518
2519 CGPoint newContentOffset = [self _contentOffsetAdjustedForObscuredInset:CGPointMake(_perProcessState.liveResizeParameters->initialScrollPosition.x * scale, _perProcessState.liveResizeParameters->initialScrollPosition.y * scale)];
2520 CGPoint currentContentOffset = [_scrollView contentOffset];
2521
2522 transform.tx = currentContentOffset.x - newContentOffset.x;
2523 transform.ty = currentContentOffset.y - newContentOffset.y;
2524
2525 [_resizeAnimationView setTransform:transform];
2526
2527#if HAVE(LIQUID_GLASS)(defined 1 && 1)
2528 [self _updateFixedColorExtensionViewFrames];
2529#endif
2530}
2531
2532#endif // HAVE(UI_WINDOW_SCENE_LIVE_RESIZE)
2533
2534- (void)_frameOrBoundsWillChange
2535{
2536#if HAVE(UI_WINDOW_SCENE_LIVE_RESIZE)(defined 1 && 1)
2537 auto [sizeBeforeUpdate, orientationBeforeUpdate] = _lastKnownWindowSizeAndOrientation;
2538 [self _updateLastKnownWindowSizeAndOrientation];
2539
2540 auto [sizeAfterUpdate, orientationAfterUpdate] = _lastKnownWindowSizeAndOrientation;
2541 auto shouldScheduleAutomaticLiveResize = [&] {
2542 if (sizeBeforeUpdate.width == sizeAfterUpdate.width)
2543 return NO__objc_no;
2544
2545 if (orientationBeforeUpdate != orientationAfterUpdate)
2546 return NO__objc_no;
2547
2548 return YES__objc_yes;
2549 };
2550
2551 if (shouldScheduleAutomaticLiveResize())
2552 [self _beginAutomaticLiveResizeIfNeeded];
2553#endif // HAVE(UI_WINDOW_SCENE_LIVE_RESIZE)
2554}
2555
2556- (void)_updateDrawingAreaSize
2557{
2558 BOOL sizeChanged = NO__objc_no;
2559 if (_page) {
2560 if (RefPtr drawingArea = _page->drawingArea())
2561 sizeChanged = drawingArea->setSize(WebCore::IntSize(self.bounds.size));
2562 }
2563
2564 if (sizeChanged && [self usesStandardContentView])
2565 [_contentView setSizeChangedSinceLastVisibleContentRectUpdate:YES__objc_yes];
2566}
2567
2568- (void)_frameOrBoundsMayHaveChanged
2569{
2570 CGRect bounds = self.bounds;
2571 [_scrollView setFrame:bounds];
2572
2573#if HAVE(UIFINDINTERACTION)(defined 1 && 1)
2574 [self _updateFindOverlayPosition];
2575#endif
2576
2577#if HAVE(UI_WINDOW_SCENE_LIVE_RESIZE)(defined 1 && 1)
2578 if (_perProcessState.liveResizeParameters)
2579 [self _updateLiveResizeTransform];
2580#endif
2581
2582 if (!self._shouldDeferGeometryUpdates) {
2583 if (!_overriddenLayoutParameters) {
2584 [self _dispatchSetViewLayoutSize:[self activeViewLayoutSize:self.bounds]];
2585 _page->setDefaultUnobscuredSize(WebCore::FloatSize(bounds.size));
2586 }
2587
2588 [self _recalculateViewportSizesWithMinimumViewportInset:_minimumViewportInset maximumViewportInset:_maximumViewportInset throwOnInvalidInput:NO__objc_no];
2589
2590 [self _updateDrawingAreaSize];
2591 }
2592
2593 [_customContentView web_setMinimumSize:bounds.size];
2594 [self _scheduleVisibleContentRectUpdate];
2595
2596#if ENABLE(PAGE_LOAD_OBSERVER)(defined ENABLE_PAGE_LOAD_OBSERVER && ENABLE_PAGE_LOAD_OBSERVER
)
2597 [self _updatePageLoadObserverState];
2598#endif
2599}
2600
2601#if HAVE(UIKIT_RESIZABLE_WINDOWS)(defined 1 && 1)
2602
2603- (void)_acquireResizeAssertionForReason:(NSString *)reason
2604{
2605 if (_page && protect(_page->preferences())->automaticLiveResizeEnabled())
2606 return;
2607
2608 UIWindowScene *windowScene = self.window.windowScene;
2609 if (!windowScene)
2610 return;
2611
2612 if (_resizeAssertions.isEmpty()) {
2613 auto didInvalidateResizeAssertions = Box<bool>::create(false);
2614
2615 [self _doAfterNextVisibleContentRectUpdate:makeBlockPtr([weakSelf = WeakObjCPtr<WKWebView>(self), didInvalidateResizeAssertions] {
2616 auto strongSelf = weakSelf.get();
2617 if (!strongSelf)
2618 return;
2619
2620 if (*didInvalidateResizeAssertions)
2621 return;
2622
2623 [strongSelf _invalidateResizeAssertions];
2624
2625 *didInvalidateResizeAssertions = true;
2626 }).get()];
2627
2628 RunLoop::mainSingleton().dispatchAfter(1_s, [weakSelf = WeakObjCPtr<WKWebView>(self), didInvalidateResizeAssertions] {
2629 if (*didInvalidateResizeAssertions)
2630 return;
2631
2632 auto strongSelf = weakSelf.get();
2633 if (!strongSelf)
2634 return;
2635
2636 WKWEBVIEW_RELEASE_LOG("WKWebView %p next visible content rect update took too long; clearing resize assertions", strongSelf.get());
2637 [strongSelf _invalidateResizeAssertions];
2638
2639 *didInvalidateResizeAssertions = true;
2640 });
2641 }
2642
2643 _resizeAssertions.append(retainPtr([windowScene _holdLiveResizeSnapshotForReason:reason]));
2644}
2645
2646- (void)_invalidateResizeAssertions
2647{
2648 for (auto resizeAssertion : std::exchange(_resizeAssertions, { }))
2649 [resizeAssertion _invalidate];
2650}
2651
2652#endif // HAVE(UIKIT_RESIZABLE_WINDOWS)
2653
2654// Unobscured content rect where the user can interact. When the keyboard is up, this should be the area above or below the keyboard, wherever there is enough space.
2655- (CGRect)_contentRectForUserInteraction
2656{
2657 // FIXME: handle split keyboard.
2658 UIEdgeInsets obscuredInsets = _obscuredInsets;
2659 obscuredInsets.bottom = std::max(_obscuredInsets.bottom, _inputViewBoundsInWindow.size.height);
2660 CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, obscuredInsets);
2661 return [self convertRect:unobscuredRect toView:self._currentContentView];
2662}
2663
2664- (WKBaseScrollView *)_scrollViewInternal
2665{
2666 return _scrollView.get();
2667}
2668
2669// Ideally UIScrollView would expose this for us: <rdar://problem/21394567>.
2670- (BOOL)_scrollViewIsRubberBanding:(UIScrollView *)scrollView
2671{
2672 float deviceScaleFactor = _page->deviceScaleFactor();
2673
2674 CGPoint contentOffset = [scrollView contentOffset];
2675 CGPoint boundedOffset = contentOffsetBoundedInValidRange(scrollView, contentOffset);
2676 return !pointsEqualInDevicePixels(contentOffset, boundedOffset, deviceScaleFactor);
2677}
2678
2679- (BOOL)_scrollViewIsRubberBandingForRefreshControl
2680{
2681 if (![_scrollView refreshControl])
2682 return NO__objc_no;
2683
2684 CGPoint contentOffset = [_scrollView contentOffset];
2685 UIEdgeInsets contentInsets = [_scrollView adjustedContentInset];
2686 return contentOffset.y < -contentInsets.top && [self _scrollViewIsRubberBanding:_scrollView.get()];
2687}
2688
2689// FIXME: Likely we can remove this special case for watchOS.
2690#if !PLATFORM(WATCHOS)(defined WTF_PLATFORM_WATCHOS && WTF_PLATFORM_WATCHOS
)
2691- (void)safeAreaInsetsDidChange
2692{
2693 [super safeAreaInsetsDidChange];
2694
2695 [self _scheduleVisibleContentRectUpdate];
2696 [_warningView setContentInset:[self _computedObscuredInsetForWarningView]];
2697}
2698#endif
2699
2700- (void)_scheduleVisibleContentRectUpdate
2701{
2702 // For visible rect updates not associated with a specific UIScrollView, just consider our own scroller.
2703 [self _scheduleVisibleContentRectUpdateAfterScrollInView:_scrollView.get()];
2704}
2705
2706- (void)_scheduleForcedVisibleContentRectUpdate
2707{
2708 _alwaysSendNextVisibleContentRectUpdate = YES__objc_yes;
2709 [self _cancelPendingVisibleContentRectUpdateTimer];
2710 [self _scheduleVisibleContentRectUpdate];
2711}
2712
2713- (void)_cancelPendingVisibleContentRectUpdateTimer
2714{
2715 if (!_pendingInteractiveObscuredInsetsChangeTimer)
2716 return;
2717
2718 _pendingInteractiveObscuredInsetsChangeTimer->stop();
2719 _pendingInteractiveObscuredInsetsChangeTimer = nullptr;
2720}
2721
2722- (void)_scheduleVisibleContentRectUpdateWithDelay:(Seconds)delay
2723{
2724 if (_pendingInteractiveObscuredInsetsChangeTimer && _pendingInteractiveObscuredInsetsChangeTimer->isActive())
2725 return;
2726
2727 _perProcessState.didDeferUpdateVisibleContentRectsForAnyReason = YES__objc_yes;
2728 _pendingInteractiveObscuredInsetsChangeTimer = RunLoop::mainSingleton().dispatchAfter(delay, [retainedSelf = retainPtr(self)] {
2729 retainedSelf->_pendingInteractiveObscuredInsetsChangeTimer = nullptr;
2730 [retainedSelf _scheduleVisibleContentRectUpdate];
2731 });
2732}
2733
2734- (BOOL)_isInStableState:(UIScrollView *)scrollView
2735{
2736 return [self _viewStabilityState:scrollView].isEmpty();
2737}
2738
2739- (OptionSet<WebKit::ViewStabilityFlag>)_viewStabilityState:(UIScrollView *)scrollView
2740{
2741 OptionSet<WebKit::ViewStabilityFlag> stabilityFlags;
2742
2743 if (scrollView.isDragging || scrollView.isZooming)
2744 stabilityFlags.add(WebKit::ViewStabilityFlag::ScrollViewInteracting);
2745 if (scrollView.isDecelerating || scrollView._wk_isZoomAnimating || scrollView._wk_isScrollAnimating || scrollView.isZoomBouncing)
2746 stabilityFlags.add(WebKit::ViewStabilityFlag::ScrollViewAnimatedScrollOrZoom);
2747
2748#if ENABLE(OVERLAY_REGIONS_IN_EVENT_REGION)(defined ENABLE_OVERLAY_REGIONS_IN_EVENT_REGION && ENABLE_OVERLAY_REGIONS_IN_EVENT_REGION
)
2749 if (_isScrollingWithOverlayRegion)
2750 stabilityFlags.add(WebKit::ViewStabilityFlag::ScrollViewAnimatedScrollOrZoom);
2751#endif
2752
2753 if (scrollView == _scrollView.get() && _isChangingObscuredInsetsInteractively)
2754 stabilityFlags.add(WebKit::ViewStabilityFlag::ChangingObscuredInsetsInteractively);
2755
2756 if ([self _scrollViewIsRubberBanding:scrollView])
2757 stabilityFlags.add(WebKit::ViewStabilityFlag::ScrollViewRubberBanding);
2758
2759 if (NSNumber *stableOverride = self._stableStateOverride) {
2760 if (stableOverride.boolValue)
2761 stabilityFlags = { };
2762 else
2763 stabilityFlags.add(WebKit::ViewStabilityFlag::UnstableForTesting);
2764 }
2765
2766 return stabilityFlags;
2767}
2768
2769- (void)_addUpdateVisibleContentRectPreCommitHandler
2770{
2771 auto retainedSelf = retainPtr(self);
2772 [CATransaction addCommitHandler:[retainedSelf] {
2773 WKWebView *webView = retainedSelf.get();
2774 if (![webView _isValid]) {
2775 WKWEBVIEW_RELEASE_LOG("In CATransaction preCommitHandler, WKWebView %p is invalid", webView);
2776 return;
2777 }
2778
2779 @try {
2780 [webView _updateVisibleContentRects];
2781 } @catch (NSException *exception) {
2782 WKWEBVIEW_RELEASE_LOG("In CATransaction preCommitHandler, -[WKWebView %p _updateVisibleContentRects] threw an exception", webView);
2783 }
2784 webView->_perProcessState.hasScheduledVisibleRectUpdate = NO__objc_no;
2785 } forPhase:kCATransactionPhasePreCommit];
2786}
2787
2788- (void)_scheduleVisibleContentRectUpdateAfterScrollInView:(UIScrollView *)scrollView
2789{
2790 _viewStabilityWhenVisibleContentRectUpdateScheduled = [self _viewStabilityState:scrollView];
2791
2792 if (_perProcessState.hasScheduledVisibleRectUpdate) {
2793 auto timeNow = MonotonicTime::now();
2794 if ((timeNow - _timeOfRequestForVisibleContentRectUpdate) > delayBeforeNoVisibleContentsRectsLogging) {
2795 WKWEBVIEW_RELEASE_LOG("-[WKWebView %p _scheduleVisibleContentRectUpdateAfterScrollInView]: hasScheduledVisibleRectUpdate is true but haven't updated visible content rects for %.2fs (last update was %.2fs ago) - valid %d",
2796 self, (timeNow - _timeOfRequestForVisibleContentRectUpdate).value(), (timeNow - _timeOfLastVisibleContentRectUpdate).value(), [self _isValid]);
2797 }
2798 return;
2799 }
2800
2801 _perProcessState.hasScheduledVisibleRectUpdate = YES__objc_yes;
2802 _timeOfRequestForVisibleContentRectUpdate = MonotonicTime::now();
2803
2804 CATransactionPhase transactionPhase = [CATransaction currentPhase];
2805 if (transactionPhase == kCATransactionPhaseNull || transactionPhase == kCATransactionPhasePreLayout) {
2806 [self _addUpdateVisibleContentRectPreCommitHandler];
2807 return;
2808 }
2809
2810 RunLoop::mainSingleton().dispatch([retainedSelf = retainPtr(self)] {
2811 WKWebView *webView = retainedSelf.get();
2812 if (![webView _isValid])
2813 return;
2814 [webView _addUpdateVisibleContentRectPreCommitHandler];
2815 });
2816}
2817
2818static bool scrollViewCanScroll(UIScrollView *scrollView)
2819{
2820 if (!scrollView)
2821 return NO__objc_no;
2822
2823 UIEdgeInsets contentInset = scrollView.contentInset;
2824 CGSize contentSize = scrollView.contentSize;
2825 CGSize boundsSize = scrollView.bounds.size;
2826
2827 return (contentSize.width + contentInset.left + contentInset.right) > boundsSize.width
2828 || (contentSize.height + contentInset.top + contentInset.bottom) > boundsSize.height;
2829}
2830
2831- (CGRect)_contentBoundsExtendedForRubberbandingWithScale:(CGFloat)scaleFactor
2832{
2833 CGPoint contentOffset = [_scrollView contentOffset];
2834 CGPoint boundedOffset = contentOffsetBoundedInValidRange(_scrollView.get(), contentOffset);
2835
2836 CGFloat horizontalRubberbandAmountInContentCoordinates = (contentOffset.x - boundedOffset.x) / scaleFactor;
2837 CGFloat verticalRubberbandAmountInContentCoordinates = (contentOffset.y - boundedOffset.y) / scaleFactor;
2838
2839 CGRect extendedBounds = [_contentView bounds];
2840
2841 if (horizontalRubberbandAmountInContentCoordinates < 0) {
2842 extendedBounds.origin.x += horizontalRubberbandAmountInContentCoordinates;
2843 extendedBounds.size.width -= horizontalRubberbandAmountInContentCoordinates;
2844 } else if (horizontalRubberbandAmountInContentCoordinates > 0)
2845 extendedBounds.size.width += horizontalRubberbandAmountInContentCoordinates;
2846
2847 if (verticalRubberbandAmountInContentCoordinates < 0) {
2848 extendedBounds.origin.y += verticalRubberbandAmountInContentCoordinates;
2849 extendedBounds.size.height -= verticalRubberbandAmountInContentCoordinates;
2850 } else if (verticalRubberbandAmountInContentCoordinates > 0)
2851 extendedBounds.size.height += verticalRubberbandAmountInContentCoordinates;
2852
2853 return extendedBounds;
2854}
2855
2856- (UIEdgeInsets)currentlyVisibleContentInsetsWithScale:(CGFloat)scaleFactor obscuredInsets:(UIEdgeInsets)obscuredInsets
2857{
2858 // The following logic computes the extent to which the bottom, top, left and right content insets are visible.
2859 auto scrollViewInsets = [_scrollView contentInset];
2860 auto scrollViewBounds = [_scrollView bounds];
2861 auto scrollViewContentSize = [_scrollView contentSize];
2862 auto scrollViewOriginIncludingInset = UIEdgeInsetsInsetRect(scrollViewBounds, obscuredInsets).origin;
2863 auto maximumVerticalScrollExtentWithoutRevealingBottomContentInset = scrollViewContentSize.height - CGRectGetHeight(scrollViewBounds);
2864 auto maximumHorizontalScrollExtentWithoutRevealingRightContentInset = scrollViewContentSize.width - CGRectGetWidth(scrollViewBounds);
2865 auto contentInsets = UIEdgeInsetsZero;
2866
2867 if (scrollViewInsets.left > 0 && scrollViewOriginIncludingInset.x < 0)
2868 contentInsets.left = std::min(-scrollViewOriginIncludingInset.x, scrollViewInsets.left) / scaleFactor;
2869
2870 if (scrollViewInsets.top > 0 && scrollViewOriginIncludingInset.y < 0)
2871 contentInsets.top = std::min(-scrollViewOriginIncludingInset.y, scrollViewInsets.top) / scaleFactor;
2872
2873 if (scrollViewInsets.right > 0 && scrollViewOriginIncludingInset.x > maximumHorizontalScrollExtentWithoutRevealingRightContentInset)
2874 contentInsets.right = std::min(scrollViewOriginIncludingInset.x - maximumHorizontalScrollExtentWithoutRevealingRightContentInset, scrollViewInsets.right) / scaleFactor;
2875
2876 if (scrollViewInsets.bottom > 0 && scrollViewOriginIncludingInset.y > maximumVerticalScrollExtentWithoutRevealingBottomContentInset)
2877 contentInsets.bottom = std::min(scrollViewOriginIncludingInset.y - maximumVerticalScrollExtentWithoutRevealingBottomContentInset, scrollViewInsets.bottom) / scaleFactor;
2878
2879 return contentInsets;
2880}
2881
2882- (BOOL)_shouldDeferGeometryUpdates
2883{
2884 return _perProcessState.liveResizeParameters || _perProcessState.dynamicViewportUpdateMode != WebKit::DynamicViewportUpdateMode::NotResizing || _perProcessState.isAnimatingFullScreenExit;
2885}
2886
2887- (std::optional<WebKit::VisibleContentRectUpdateInfo>)_createVisibleContentRectUpdate
2888{
2889 RefPtr drawingArea = _page->drawingArea();
2890 if (!drawingArea)
2891 return std::nullopt;
2892
2893 if (![self usesStandardContentView])
2894 return std::nullopt;
2895
2896 auto viewStability = _viewStabilityWhenVisibleContentRectUpdateScheduled;
2897
2898 CGRect visibleRectInContentCoordinates = [self _visibleContentRect];
2899
2900 UIEdgeInsets computedContentInsetUnadjustedForKeyboard = [self _computedObscuredInset];
2901 if (!_haveSetObscuredInsets)
2902 computedContentInsetUnadjustedForKeyboard.bottom -= _totalScrollViewBottomInsetAdjustmentForKeyboard;
2903
2904 CGFloat scaleFactor = contentZoomScale(self);
2905 CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, computedContentInsetUnadjustedForKeyboard);
2906 WebCore::FloatRect unobscuredRectInContentCoordinates = WebCore::FloatRect(_perProcessState.frozenUnobscuredContentRect ? _perProcessState.frozenUnobscuredContentRect.value() : [self convertRect:unobscuredRect toView:_contentView.get()]);
2907 if (![_contentView sizeChangedSinceLastVisibleContentRectUpdate])
2908 unobscuredRectInContentCoordinates.intersect([self _contentBoundsExtendedForRubberbandingWithScale:scaleFactor]);
2909
2910 auto contentInsets = [self currentlyVisibleContentInsetsWithScale:scaleFactor obscuredInsets:computedContentInsetUnadjustedForKeyboard];
2911
2912 if (viewStability.isEmpty()) {
2913 CheckedPtr coordinator = downcast<WebKit::RemoteScrollingCoordinatorProxyIOS>(_page->scrollingCoordinatorProxy());
2914 if (coordinator && coordinator->hasActiveSnapPoint()) {
2915 CGPoint currentPoint = [_scrollView contentOffset];
2916 CGPoint activePoint = coordinator->nearestActiveContentInsetAdjustedSnapOffset(unobscuredRect.origin.y, currentPoint);
2917
2918 if (!CGPointEqualToPoint__CGPointEqualToPoint(activePoint, currentPoint)) {
2919 RetainPtr<WKScrollView> strongScrollView = _scrollView;
2920 RunLoop::mainSingleton().dispatch([strongScrollView, activePoint] {
2921 [strongScrollView setContentOffset:activePoint animated:NO__objc_no];
2922 });
2923 }
2924 }
2925 }
2926
2927 MonotonicTime timestamp = MonotonicTime::now();
2928 WebCore::VelocityData velocityData;
2929 bool inStableState = viewStability.isEmpty();
2930 if (!inStableState)
2931 velocityData = _historicalKinematicData.velocityForNewData(visibleRectInContentCoordinates.origin, scaleFactor, timestamp);
2932 else {
2933 _historicalKinematicData.clear();
2934 velocityData = { 0, 0, 0, timestamp };
2935 }
2936
2937 CGRect unobscuredContentRectRespectingInputViewBounds = [_contentView _computeUnobscuredContentRectRespectingInputViewBounds:unobscuredRectInContentCoordinates inputViewBounds:_inputViewBoundsInWindow];
2938 WebCore::FloatRect fixedPositionRectForLayout = _page->computeLayoutViewportRect(unobscuredRectInContentCoordinates, unobscuredContentRectRespectingInputViewBounds, _page->layoutViewportRect(), scaleFactor, WebCore::LayoutViewportConstraint::ConstrainedToDocumentRect);
2939
2940 return { {
2941 visibleRectInContentCoordinates,
2942 unobscuredRectInContentCoordinates,
2943 WebKit::floatBoxExtent(contentInsets),
2944 unobscuredRect,
2945 unobscuredContentRectRespectingInputViewBounds,
2946 fixedPositionRectForLayout,
2947 WebKit::floatBoxExtent(_obscuredInsets),
2948 WebKit::floatBoxExtent([self _computedUnobscuredSafeAreaInset]),
2949 scaleFactor,
2950 viewStability,
2951 !![_contentView sizeChangedSinceLastVisibleContentRectUpdate],
2952 !!_allowsViewportShrinkToFit,
2953 scrollViewCanScroll([self _scroller]),
2954 !!_needsScrollend,
2955 velocityData,
2956 downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*drawingArea).lastCommittedMainFrameLayerTreeTransactionID()
2957 } };
2958}
2959
2960- (void)_updateVisibleContentRects
2961{
2962 if (![self usesStandardContentView]) {
2963 [_passwordView setFrame:self.bounds];
2964 [_customContentView web_computedContentInsetDidChange];
2965 _perProcessState.didDeferUpdateVisibleContentRectsForAnyReason = YES__objc_yes;
2966 WKWEBVIEW_RELEASE_LOG("%p (pageProxyID=%llu) -[WKWebView _updateVisibleContentRects:] - usesStandardContentView is NO, bailing", self, _page->identifier().toUInt64());
2967 return;
2968 }
2969
2970 auto timeNow = MonotonicTime::now();
2971 if (_timeOfFirstVisibleContentRectUpdateWithPendingCommit) {
2972 auto timeSinceFirstRequestWithPendingCommit = timeNow - *_timeOfFirstVisibleContentRectUpdateWithPendingCommit;
2973 if (timeSinceFirstRequestWithPendingCommit > delayBeforeNoCommitsLogging)
2974 WKWEBVIEW_RELEASE_LOG("%p (pageProxyID=%llu) -[WKWebView _updateVisibleContentRects:] - have not received a commit %.2fs after visible content rect update; lastTransactionID %llu", self, _page->identifier().toUInt64(), timeSinceFirstRequestWithPendingCommit.value(), _perProcessState.lastTransactionID ? _perProcessState.lastTransactionID->object().toUInt64() : 0);
2975 }
2976
2977 if (_perProcessState.invokingUIScrollViewDelegateCallback) {
2978 _perProcessState.didDeferUpdateVisibleContentRectsForUIScrollViewDelegateCallback = YES__objc_yes;
2979 _perProcessState.didDeferUpdateVisibleContentRectsForAnyReason = YES__objc_yes;
2980 WKWEBVIEW_RELEASE_LOG("%p (pageProxyID=%llu) -[WKWebView _updateVisibleContentRects:] - _invokingUIScrollViewDelegateCallback is YES, bailing", self, _page->identifier().toUInt64());
2981 return;
2982 }
2983
2984 if (!CGRectIsEmpty(_perProcessState.animatedResizeOldBounds))
2985 [self _cancelAnimatedResize];
2986
2987 if (self._shouldDeferGeometryUpdates
2988 || (_perProcessState.resetViewStateAfterTransactionID && ![_contentView sizeChangedSinceLastVisibleContentRectUpdate])
2989 || [_scrollView isZoomBouncing]
2990 || _perProcessState.currentlyAdjustingScrollViewInsetsForKeyboard) {
2991 _perProcessState.didDeferUpdateVisibleContentRectsForAnyReason = YES__objc_yes;
2992 _perProcessState.didDeferUpdateVisibleContentRectsForUnstableScrollView = YES__objc_yes;
2993 WKWEBVIEW_RELEASE_LOG("%p (pageProxyID=%llu) -[WKWebView _updateVisibleContentRects:] - scroll view state is non-stable, bailing (shouldDeferGeometryUpdates %d, dynamicViewportUpdateMode %d, resetViewStateAfterTransactionID %llu, sizeChangedSinceLastVisibleContentRectUpdate %d, [_scrollView isZoomBouncing] %d, currentlyAdjustingScrollViewInsetsForKeyboard %d)", self, _page->identifier().toUInt64(), self._shouldDeferGeometryUpdates, std::to_underlying(_perProcessState.dynamicViewportUpdateMode), _perProcessState.resetViewStateAfterTransactionID ? _perProcessState.resetViewStateAfterTransactionID->object().toUInt64() : 0, [_contentView sizeChangedSinceLastVisibleContentRectUpdate], [_scrollView isZoomBouncing], _perProcessState.currentlyAdjustingScrollViewInsetsForKeyboard);
2994 return;
2995 }
2996
2997 if (_isChangingObscuredInsetsInteractively) {
2998 auto timeSinceLastUpdate = timeNow - _timeOfLastVisibleContentRectUpdate;
2999 if (timeSinceLastUpdate < delayBeforeUpdatingVisibleContentRectsWhenChangingObscuredInsetsInteractively) {
3000 auto delay = delayBeforeUpdatingVisibleContentRectsWhenChangingObscuredInsetsInteractively - timeSinceLastUpdate;
3001 [self _scheduleVisibleContentRectUpdateWithDelay:delay];
3002
3003 // Reposition fixed/sticky layers even though the full update to WebContent is throttled.
3004 // Without taking this step, fixed or sticky elements will freeze in position and then visibly jump when WebContent updates.
3005 [self _updateScrollViewContentInsetsIfNecessary];
3006 if (auto info = [self _createVisibleContentRectUpdate]) {
3007 _page->updateVisibleContentRectsLocally(*info);
3008 auto layoutViewport = _page->unconstrainedLayoutViewportRect();
3009 _page->adjustLayersForLayoutViewport(_page->unobscuredContentRect().location(), layoutViewport, _page->displayedContentScale());
3010 }
3011 return;
3012 }
3013 }
3014
3015 [self _updateScrollViewContentInsetsIfNecessary];
3016
3017 auto visibleContentRectUpdateInfo = [self _createVisibleContentRectUpdate];
3018 if (!visibleContentRectUpdateInfo)
3019 return;
3020
3021 if (_perProcessState.didDeferUpdateVisibleContentRectsForAnyReason)
3022 WKWEBVIEW_RELEASE_LOG("%p (pageProxyID=%llu) -[WKWebView _updateVisibleContentRects:] - performing first visible content rect update after deferring updates", self, _page->identifier().toUInt64());
3023
3024 _perProcessState.didDeferUpdateVisibleContentRectsForUIScrollViewDelegateCallback = NO__objc_no;
3025 _perProcessState.didDeferUpdateVisibleContentRectsForUnstableScrollView = NO__objc_no;
3026 _perProcessState.didDeferUpdateVisibleContentRectsForAnyReason = NO__objc_no;
3027
3028 [_contentView didUpdateVisibleRect:*visibleContentRectUpdateInfo sendEvenIfUnchanged:_alwaysSendNextVisibleContentRectUpdate];
3029
3030 while (!_visibleContentRectUpdateCallbacks.isEmpty()) {
3031 auto callback = _visibleContentRectUpdateCallbacks.takeLast();
3032 callback();
3033 }
3034
3035#if ENABLE(PDF_PAGE_NUMBER_INDICATOR)(defined 1 && 1)
3036 [self _updatePDFPageNumberIndicatorIfNeeded];
3037#endif
3038
3039 if ((timeNow - _timeOfRequestForVisibleContentRectUpdate) > delayBeforeNoVisibleContentsRectsLogging)
3040 WKWEBVIEW_RELEASE_LOG("%p -[WKWebView _updateVisibleContentRects:] finally ran %.2fs after being scheduled", self, (timeNow - _timeOfRequestForVisibleContentRectUpdate).value());
3041
3042 _alwaysSendNextVisibleContentRectUpdate = NO__objc_no;
3043 _timeOfLastVisibleContentRectUpdate = timeNow;
3044 if (!_timeOfFirstVisibleContentRectUpdateWithPendingCommit)
3045 _timeOfFirstVisibleContentRectUpdateWithPendingCommit = timeNow;
3046}
3047
3048- (void)_didStartProvisionalLoadForMainFrame
3049{
3050 if (_gestureController)
3051 protect(_gestureController)->didStartProvisionalLoadForMainFrame();
3052}
3053
3054static WebCore::FloatSize activeMinimumUnobscuredSize(WKWebView *webView, const CGRect& bounds)
3055{
3056 if (!webView->_overriddenLayoutParameters)
3057 return WebCore::FloatSize(bounds.size);
3058
3059 return WebCore::FloatSize(webView->_overriddenLayoutParameters->minimumUnobscuredSize);
3060}
3061
3062static WebCore::FloatSize activeMaximumUnobscuredSize(WKWebView *webView, const CGRect& bounds)
3063{
3064 if (!webView->_overriddenLayoutParameters)
3065 return WebCore::FloatSize(bounds.size);
3066
3067 return WebCore::FloatSize(webView->_overriddenLayoutParameters->maximumUnobscuredSize);
3068}
3069
3070static WebCore::IntDegrees activeOrientation(WKWebView *webView)
3071{
3072 return webView->_overridesInterfaceOrientation ? deviceOrientationForUIInterfaceOrientation(webView->_interfaceOrientationOverride) : webView->_page->deviceOrientation();
3073}
3074
3075- (void)_ensureResizeAnimationView
3076{
3077 if (_resizeAnimationView)
3078 return;
3079
3080 NSUInteger indexOfContentView = [[_scrollView subviews] indexOfObject:_contentView.get()];
3081 _resizeAnimationView = adoptNS([[UIView alloc] init]);
3082 [_resizeAnimationView layer].name = @"ResizeAnimation";
3083 [_scrollView insertSubview:_resizeAnimationView.get() atIndex:indexOfContentView];
3084 [_resizeAnimationView addSubview:_contentView.get()];
3085 [_resizeAnimationView addSubview:[_contentView unscaledView]];
3086}
3087
3088- (void)_destroyResizeAnimationView
3089{
3090 if (!_resizeAnimationView)
3091 return;
3092
3093 NSUInteger indexOfResizeAnimationView = [[_scrollView subviews] indexOfObject:_resizeAnimationView.get()];
3094 [_scrollView insertSubview:_contentView.get() atIndex:indexOfResizeAnimationView];
3095 [_scrollView insertSubview:[_contentView unscaledView] atIndex:indexOfResizeAnimationView + 1];
3096 [_resizeAnimationView removeFromSuperview];
3097 _resizeAnimationView = nilnullptr;
3098}
3099
3100- (void)_cancelAnimatedResize
3101{
3102 WKWEBVIEW_RELEASE_LOG("%p (pageProxyID=%llu) -[WKWebView _cancelAnimatedResize] dynamicViewportUpdateMode %d", self, _page->identifier().toUInt64(), std::to_underlying(_perProcessState.dynamicViewportUpdateMode));
3103
3104 if (_perProcessState.dynamicViewportUpdateMode == WebKit::DynamicViewportUpdateMode::NotResizing)
3105 return;
3106
3107 if (!_customContentView) {
3108 [self _destroyResizeAnimationView];
3109
3110 [_contentView setHidden:NO__objc_no];
3111 _resizeAnimationTransformAdjustments = CATransform3DIdentity;
3112 }
3113
3114 _perProcessState.dynamicViewportUpdateMode = WebKit::DynamicViewportUpdateMode::NotResizing;
3115 _perProcessState.animatedResizeOldBounds = { };
3116 [self _scheduleVisibleContentRectUpdate];
3117}
3118
3119- (void)_didCompleteAnimatedResize
3120{
3121 if (_perProcessState.dynamicViewportUpdateMode == WebKit::DynamicViewportUpdateMode::NotResizing)
3122 return;
3123
3124 [_contentView setHidden:NO__objc_no];
3125
3126 if (!_resizeAnimationView) {
3127 // Paranoia. If _resizeAnimationView is null we'll end up setting a zero scale on the content view.
3128 WKWEBVIEW_RELEASE_LOG("%p -[WKWebView _didCompleteAnimatedResize:] - _resizeAnimationView is nil", self);
3129 [self _cancelAnimatedResize];
3130 return;
3131 }
3132
3133 WKWEBVIEW_RELEASE_LOG("%p (pageProxyID=%llu) -[WKWebView _didCompleteAnimatedResize]", self, _page->identifier().toUInt64());
3134
3135 NSUInteger indexOfResizeAnimationView = [[_scrollView subviews] indexOfObject:_resizeAnimationView.get()];
3136 [_scrollView insertSubview:_contentView.get() atIndex:indexOfResizeAnimationView];
3137 [_scrollView insertSubview:[_contentView unscaledView] atIndex:indexOfResizeAnimationView + 1];
3138
3139 CALayer *contentLayer = [_contentView layer];
3140 CGFloat adjustmentScale = _resizeAnimationTransformAdjustments.m11;
3141 contentLayer.sublayerTransform = CATransform3DIdentity;
3142
3143 CGFloat animatingScaleTarget = [[_resizeAnimationView layer] transform].m11;
3144 CATransform3D contentLayerTransform = contentLayer.transform;
3145 CGFloat currentScale = [[_resizeAnimationView layer] transform].m11 * contentLayerTransform.m11;
3146
3147 // We cannot use [UIScrollView setZoomScale:] directly because the UIScrollView delegate would get a callback with
3148 // an invalid contentOffset. The real content offset is only set below.
3149 // Since there is no public API for setting both the zoomScale and the contentOffset, we set the zoomScale manually
3150 // on the zoom layer and then only change the contentOffset.
3151 CGFloat adjustedScale = adjustmentScale * currentScale;
3152 contentLayerTransform.m11 = adjustedScale;
3153 contentLayerTransform.m22 = adjustedScale;
3154 contentLayer.transform = contentLayerTransform;
3155
3156 CGPoint currentScrollOffset = [_scrollView contentOffset];
3157 double horizontalScrollAdjustment = _resizeAnimationTransformAdjustments.m41 * animatingScaleTarget;
3158 double verticalScrollAdjustment = _resizeAnimationTransformAdjustments.m42 * animatingScaleTarget;
3159
3160 [_scrollView setContentSize:roundScrollViewContentSize(*_page, [_contentView frame].size)];
3161 [_scrollView setContentOffset:CGPointMake(currentScrollOffset.x - horizontalScrollAdjustment, currentScrollOffset.y - verticalScrollAdjustment)];
3162
3163 [_resizeAnimationView removeFromSuperview];
3164 _resizeAnimationView = nilnullptr;
3165 _resizeAnimationTransformAdjustments = CATransform3DIdentity;
3166
3167 _perProcessState.dynamicViewportUpdateMode = WebKit::DynamicViewportUpdateMode::NotResizing;
3168 _perProcessState.animatedResizeOldBounds = { };
3169
3170 [self _didStopDeferringGeometryUpdates];
3171
3172#if HAVE(LIQUID_GLASS)(defined 1 && 1)
3173 [self _updateFixedColorExtensionViewFrames];
3174#endif
3175}
3176
3177- (void)_didStopDeferringGeometryUpdates
3178{
3179 [self _scheduleVisibleContentRectUpdate];
3180
3181 CGRect newBounds = self.bounds;
3182 auto newViewLayoutSize = [self activeViewLayoutSize:newBounds];
3183 auto newMinimumUnobscuredSize = activeMinimumUnobscuredSize(self, newBounds);
3184 auto newMaximumUnobscuredSize = activeMaximumUnobscuredSize(self, newBounds);
3185 auto newOrientation = activeOrientation(self);
3186 auto newMinimumEffectiveDeviceWidth = [self _minimumEffectiveDeviceWidth];
3187
3188 if (!_perProcessState.lastSentViewLayoutSize || newViewLayoutSize != _perProcessState.lastSentViewLayoutSize.value() || newMinimumEffectiveDeviceWidth != _perProcessState.lastSentMinimumEffectiveDeviceWidth.value())
3189 [self _dispatchSetViewLayoutSize:newViewLayoutSize];
3190
3191 if (_overriddenLayoutParameters) {
3192 _page->setMinimumUnobscuredSize(WebCore::FloatSize(newMinimumUnobscuredSize));
3193 _page->setDefaultUnobscuredSize(WebCore::FloatSize(newMaximumUnobscuredSize));
3194 _page->setMaximumUnobscuredSize(WebCore::FloatSize(newMaximumUnobscuredSize));
3195 }
3196 [self _recalculateViewportSizesWithMinimumViewportInset:_minimumViewportInset maximumViewportInset:_maximumViewportInset throwOnInvalidInput:NO__objc_no];
3197
3198 if (!_perProcessState.lastSentDeviceOrientation || newOrientation != _perProcessState.lastSentDeviceOrientation.value())
3199 [self _dispatchSetDeviceOrientation:newOrientation];
3200
3201 [self _updateDrawingAreaSize];
3202
3203 while (!_callbacksDeferredDuringResize.isEmpty())
3204 _callbacksDeferredDuringResize.takeLast()();
3205}
3206
3207- (void)_didFinishNavigation:(API::Navigation*)navigation
3208{
3209 if (_gestureController)
3210 protect(_gestureController)->didFinishNavigation(navigation);
3211}
3212
3213- (void)_didFailNavigation:(API::Navigation*)navigation
3214{
3215 if (_gestureController)
3216 protect(_gestureController)->didFailNavigation(navigation);
3217}
3218
3219- (void)_didSameDocumentNavigationForMainFrame:(WebKit::SameDocumentNavigationType)navigationType
3220{
3221 [_customContentView web_didSameDocumentNavigation:toAPI(navigationType)];
3222
3223 if (_gestureController)
3224 protect(_gestureController)->didSameDocumentNavigationForMainFrame(navigationType);
3225}
3226
3227- (void)_keyboardChangedWithInfo:(NSDictionary *)keyboardInfo adjustScrollView:(BOOL)adjustScrollView
3228{
3229 NSValue *endFrameValue = [keyboardInfo objectForKey:UIKeyboardFrameEndUserInfoKey];
3230 if (!endFrameValue)
3231 return;
3232
3233 auto previousInputViewBounds = _inputViewBoundsInWindow;
3234 BOOL selectionWasVisible = self._selectionRectIsFullyVisibleAndNonEmpty;
3235
3236 _inputViewBoundsInWindow = ([&] {
3237 if (UIPeripheralHost.sharedInstance.isUndocked)
3238 return CGRectZero;
3239
3240 auto keyboardFrameInScreen = endFrameValue.CGRectValue;
3241 // The keyboard rect is always in screen coordinates. In the view services case the window does not
3242 // have the interface orientation rotation transformation; its host does. So, it makes no sense to
3243 // clip the keyboard rect against its screen.
3244 if (!self.window._isHostedInAnotherProcess)
3245 keyboardFrameInScreen = CGRectIntersection(keyboardFrameInScreen, self.window.screen.bounds);
3246
3247 return [self.window convertRect:keyboardFrameInScreen fromCoordinateSpace:self.window.screen.coordinateSpace];
3248 })();
3249
3250 if (adjustScrollView) {
3251 CGFloat bottomInsetBeforeAdjustment = [_scrollView contentInset].bottom;
3252 SetForScope insetAdjustmentGuard(_perProcessState.currentlyAdjustingScrollViewInsetsForKeyboard, YES__objc_yes);
3253 [_scrollView _adjustForAutomaticKeyboardInfo:keyboardInfo animated:YES__objc_yes lastAdjustment:&_lastAdjustmentForScroller];
3254 CGFloat bottomInsetAfterAdjustment = [_scrollView contentInset].bottom;
3255 // FIXME: This "total bottom content inset adjustment" mechanism hasn't worked since iOS 11, since -_adjustForAutomaticKeyboardInfo:animated:lastAdjustment:
3256 // no longer sets -[UIScrollView contentInset] for apps linked on or after iOS 11. We should consider removing this logic, since the original bug this was
3257 // intended to fix, <rdar://problem/23202254>, remains fixed through other means.
3258 if (bottomInsetBeforeAdjustment != bottomInsetAfterAdjustment)
3259 _totalScrollViewBottomInsetAdjustmentForKeyboard += bottomInsetAfterAdjustment - bottomInsetBeforeAdjustment;
3260 }
3261
3262 if (selectionWasVisible && [_contentView _hasFocusedElement] && !CGRectIsEmpty(previousInputViewBounds) && !CGRectIsEmpty(_inputViewBoundsInWindow) && !CGRectEqualToRect(previousInputViewBounds, _inputViewBoundsInWindow))
3263 [self _scrollToAndRevealSelectionIfNeeded];
3264
3265 [self _scheduleVisibleContentRectUpdate];
3266}
3267
3268- (BOOL)_shouldUpdateKeyboardWithInfo:(NSDictionary *)keyboardInfo
3269{
3270 if ([_contentView isFocusingElement])
3271 return YES__objc_yes;
3272
3273#if HAVE(UIFINDINTERACTION)(defined 1 && 1)
3274 if ([_findInteraction isFindNavigatorVisible])
3275 return YES__objc_yes;
3276#endif
3277
3278 NSNumber *isLocalKeyboard = [keyboardInfo valueForKey:UIKeyboardIsLocalUserInfoKey];
3279 return isLocalKeyboard && !isLocalKeyboard.boolValue;
3280}
3281
3282- (void)_keyboardWillChangeFrame:(NSNotification *)notification
3283{
3284 if ([self _shouldUpdateKeyboardWithInfo:notification.userInfo])
3285 [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES__objc_yes];
3286}
3287
3288- (void)_keyboardDidChangeFrame:(NSNotification *)notification
3289{
3290 [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:NO__objc_no];
3291}
3292
3293- (void)_keyboardWillShow:(NSNotification *)notification
3294{
3295 if ([self _shouldUpdateKeyboardWithInfo:notification.userInfo])
3296 [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES__objc_yes];
3297
3298 _page->setIsKeyboardAnimatingIn(true);
3299 [_contentView _keyboardWillShow];
3300}
3301
3302- (void)_keyboardDidShow:(NSNotification *)notification
3303{
3304 _page->setIsKeyboardAnimatingIn(false);
3305 [_contentView _keyboardDidShow];
3306}
3307
3308- (void)_keyboardWillHide:(NSNotification *)notification
3309{
3310 if ([_contentView shouldIgnoreKeyboardWillHideNotification])
3311 return;
3312
3313 [self _keyboardChangedWithInfo:notification.userInfo adjustScrollView:YES__objc_yes];
3314}
3315
3316- (void)_windowDidRotate:(NSNotification *)notification
3317{
3318 if (!_overridesInterfaceOrientation)
3319 [self _dispatchSetDeviceOrientation:[self _deviceOrientationIgnoringOverrides]];
3320 [self _dispatchSetOrientationForMediaCapture:[self _deviceOrientationIgnoringOverrides]];
3321}
3322
3323- (void)_contentSizeCategoryDidChange:(NSNotification *)notification
3324{
3325 _page->contentSizeCategoryDidChange([self _contentSizeCategory]);
3326}
3327
3328- (void)_accessibilitySettingsDidChange:(NSNotification *)notification
3329{
3330 _page->accessibilitySettingsDidChange();
3331}
3332
3333- (NSString *)_contentSizeCategory
3334{
3335 return [[UIApplication sharedApplication] preferredContentSizeCategory];
3336}
3337
3338- (BOOL)_isNavigationSwipeGestureRecognizer:(UIGestureRecognizer *)recognizer
3339{
3340 if (!_gestureController)
3341 return NO__objc_no;
3342 return protect(_gestureController)->isNavigationSwipeGestureRecognizer(recognizer);
3343}
3344
3345- (void)_navigationGestureDidBegin
3346{
3347 // During a back/forward swipe, there's a view interposed between this view and the content view that has
3348 // an offset and animation on it, which results in computing incorrect rectangles. Work around by using
3349 // frozen rects during swipes.
3350 CGRect fullViewRect = self.bounds;
3351 CGRect unobscuredRect = UIEdgeInsetsInsetRect(fullViewRect, [self _computedObscuredInset]);
3352
3353 _perProcessState.frozenVisibleContentRect = [self convertRect:fullViewRect toView:_contentView.get()];
3354 _perProcessState.frozenUnobscuredContentRect = [self convertRect:unobscuredRect toView:_contentView.get()];
3355 _contentViewShouldBecomeFirstResponderAfterNavigationGesture = [_contentView isFirstResponder];
3356
3357 LOG_WITH_STREAM(VisibleRects, stream << "_navigationGestureDidBegin: freezing visibleContentRect " << WebCore::FloatRect(_perProcessState.frozenVisibleContentRect.value()) << " UnobscuredContentRect " << WebCore::FloatRect(_perProcessState.frozenUnobscuredContentRect.value()))((void)0);
3358}
3359
3360- (void)_navigationGestureDidEnd
3361{
3362 _perProcessState.frozenVisibleContentRect = std::nullopt;
3363 _perProcessState.frozenUnobscuredContentRect = std::nullopt;
3364
3365 if (_contentViewShouldBecomeFirstResponderAfterNavigationGesture) {
3366 if (self.window && ![_contentView isFirstResponder])
3367 [_contentView becomeFirstResponder];
3368 _contentViewShouldBecomeFirstResponderAfterNavigationGesture = NO__objc_no;
3369 }
3370}
3371
3372- (void)_showPasswordViewWithDocumentName:(NSString *)documentName passwordHandler:(void (^)(NSString *))passwordHandler
3373{
3374 ASSERT(!_passwordView)((void)0);
3375 _passwordView = adoptNS([[WKPasswordView alloc] initWithFrame:self.bounds documentName:documentName]);
3376 [_passwordView setUserDidEnterPassword:passwordHandler];
3377 [_passwordView showInScrollView:_scrollView.get()];
3378 self._currentContentView.hidden = YES__objc_yes;
3379
3380 [self _didRequestPasswordForDocument];
3381}
3382
3383- (void)_hidePasswordView
3384{
3385 if (!_passwordView)
3386 return;
3387
3388 self._currentContentView.hidden = NO__objc_no;
3389 [_passwordView hide];
3390 _passwordView = nilnullptr;
3391
3392 [self _didStopRequestingPasswordForDocument];
3393}
3394
3395- (void)_didRequestPasswordForDocument
3396{
3397 _navigationState->didRequestPasswordForQuickLookDocument();
3398}
3399
3400- (void)_didStopRequestingPasswordForDocument
3401{
3402 _navigationState->didStopRequestingPasswordForQuickLookDocument();
3403}
3404
3405- (WKPasswordView *)_passwordView
3406{
3407 return _passwordView.get();
3408}
3409
3410- (void)_updateScrollViewInsetAdjustmentBehavior
3411{
3412// FIXME: Likely we can remove this special case for watchOS.
3413#if !PLATFORM(WATCHOS)(defined WTF_PLATFORM_WATCHOS && WTF_PLATFORM_WATCHOS
)
3414 if (![_scrollView _contentInsetAdjustmentBehaviorWasExternallyOverridden])
3415 [_scrollView _setContentInsetAdjustmentBehaviorInternal:self._safeAreaShouldAffectObscuredInsets ? UIScrollViewContentInsetAdjustmentAlways : UIScrollViewContentInsetAdjustmentNever];
3416#endif
3417}
3418
3419- (void)_resetScrollViewInsetAdjustmentBehavior
3420{
3421#if !PLATFORM(WATCHOS)(defined WTF_PLATFORM_WATCHOS && WTF_PLATFORM_WATCHOS
)
&& !PLATFORM(APPLETV)(defined WTF_PLATFORM_APPLETV && WTF_PLATFORM_APPLETV
)
3422 [_scrollView _resetContentInsetAdjustmentBehavior];
3423 [self _updateScrollViewInsetAdjustmentBehavior];
3424#endif
3425}
3426
3427- (void)_setAvoidsUnsafeArea:(BOOL)avoidsUnsafeArea
3428{
3429 if (_avoidsUnsafeArea == avoidsUnsafeArea)
3430 return;
3431
3432 _avoidsUnsafeArea = avoidsUnsafeArea;
3433
3434 if ([self _updateScrollViewContentInsetsIfNecessary] && !self._shouldDeferGeometryUpdates && !_overriddenLayoutParameters)
3435 [self _dispatchSetViewLayoutSize:[self activeViewLayoutSize:self.bounds]];
3436
3437 [self _updateScrollViewInsetAdjustmentBehavior];
3438 [self _scheduleVisibleContentRectUpdate];
3439
3440 id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)[self UIDelegate];
3441 if ([uiDelegate respondsToSelector:@selector(_webView:didChangeSafeAreaShouldAffectObscuredInsets:)])
3442 [uiDelegate _webView:self didChangeSafeAreaShouldAffectObscuredInsets:avoidsUnsafeArea];
3443}
3444
3445- (BOOL)_haveSetObscuredInsets
3446{
3447 return _haveSetObscuredInsets;
3448}
3449
3450- (void)_beginAnimatedFullScreenExit
3451{
3452 _perProcessState.isAnimatingFullScreenExit = YES__objc_yes;
3453}
3454
3455- (void)_endAnimatedFullScreenExit
3456{
3457 if (std::exchange(_perProcessState.isAnimatingFullScreenExit, NO__objc_no)) {
3458 if (!self._shouldDeferGeometryUpdates)
3459 [self _didStopDeferringGeometryUpdates];
3460 }
3461}
3462
3463#if ENABLE(FULLSCREEN_API)(defined 1 && 1)
3464- (void)removeFromSuperview
3465{
3466 [super removeFromSuperview];
3467
3468 if ([_fullScreenWindowController isFullScreen])
3469 [_fullScreenWindowController webViewDidRemoveFromSuperviewWhileInFullscreen];
3470}
3471#endif
3472
3473- (void)_firePresentationUpdateForPendingStableStatePresentationCallbacks
3474{
3475 RetainPtr<WKWebView> strongSelf = self;
3476 [self _doAfterNextPresentationUpdate:[strongSelf] {
3477 RunLoop::mainSingleton().dispatch([strongSelf] {
3478 if ([strongSelf->_stableStatePresentationUpdateCallbacks count])
3479 [strongSelf _firePresentationUpdateForPendingStableStatePresentationCallbacks];
3480 });
3481 }];
3482}
3483
3484static WebCore::UserInterfaceLayoutDirection toUserInterfaceLayoutDirection(UISemanticContentAttribute contentAttribute)
3485{
3486 auto direction = [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:contentAttribute];
3487 switch (direction) {
3488 case UIUserInterfaceLayoutDirectionLeftToRight:
3489 return WebCore::UserInterfaceLayoutDirection::LTR;
3490 case UIUserInterfaceLayoutDirectionRightToLeft:
3491 return WebCore::UserInterfaceLayoutDirection::RTL;
3492 }
3493
3494 ASSERT_NOT_REACHED()((void)0);
3495 return WebCore::UserInterfaceLayoutDirection::LTR;
3496}
3497
3498- (void)setSemanticContentAttribute:(UISemanticContentAttribute)contentAttribute
3499{
3500 [super setSemanticContentAttribute:contentAttribute];
3501
3502 if (_page)
3503 _page->setUserInterfaceLayoutDirection(toUserInterfaceLayoutDirection(contentAttribute));
3504}
3505
3506- (void)buildMenuWithBuilder:(id <UIMenuBuilder>)builder
3507{
3508 if (self.usesStandardContentView)
3509 [_contentView buildMenuForWebViewWithBuilder:builder];
3510
3511 [super buildMenuWithBuilder:builder];
3512}
3513
3514- (BOOL)_shouldAvoidSecurityHeuristicScoreUpdates
3515{
3516 return [_contentView _shouldAvoidSecurityHeuristicScoreUpdates];
3517}
3518
3519#if HAVE(UI_WINDOW_SCENE_LIVE_RESIZE)(defined 1 && 1)
3520
3521- (void)_beginLiveResize
3522{
3523 if (_perProcessState.liveResizeParameters) {
3524 RELEASE_LOG_FAULT(Resize, "Error: _beginLiveResize called with an outstanding live resize.")clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option"
clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic
ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic
ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang
::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogResize
.osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_FAULT; if
(os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({
clang diagnostic push clang diagnostic ignored "-Wvla" clang
diagnostic error "-Wformat" _Static_assert(__builtin_constant_p
("Error: _beginLiveResize called with an outstanding live resize."
), "format/label/description argument must be a string constant"
); __attribute__((section("__TEXT,__oslogstring,cstring_literals"
),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG56"
) = "Error: _beginLiveResize called with an outstanding live resize."
; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf
[__builtin_os_log_format_buffer_size("Error: _beginLiveResize called with an outstanding live resize."
)]; _os_log_fault_impl(&__dso_handle, _log_tmp, _type_tmp
, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf
, "Error: _beginLiveResize called with an outstanding live resize."
), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); }
}) clang diagnostic pop
;
3525 return;
3526 }
3527
3528 if (_perProcessState.dynamicViewportUpdateMode != WebKit::DynamicViewportUpdateMode::NotResizing) {
3529 RELEASE_LOG_FAULT(Resize, "Error: _beginLiveResize called during an animated resize.")clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option"
clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic
ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic
ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang
::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogResize
.osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_FAULT; if
(os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({
clang diagnostic push clang diagnostic ignored "-Wvla" clang
diagnostic error "-Wformat" _Static_assert(__builtin_constant_p
("Error: _beginLiveResize called during an animated resize.")
, "format/label/description argument must be a string constant"
); __attribute__((section("__TEXT,__oslogstring,cstring_literals"
),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG57"
) = "Error: _beginLiveResize called during an animated resize."
; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf
[__builtin_os_log_format_buffer_size("Error: _beginLiveResize called during an animated resize."
)]; _os_log_fault_impl(&__dso_handle, _log_tmp, _type_tmp
, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf
, "Error: _beginLiveResize called during an animated resize."
), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); }
}) clang diagnostic pop
;
3530 return;
3531 }
3532
3533 WKWEBVIEW_RELEASE_LOG("%p (pageProxyID=%llu) -[WKWebView _beginLiveResize]", self, _page->identifier().toUInt64());
3534
3535 CGPoint contentOffsetWithoutObscuredInset = self.scrollView.contentOffset;
3536 UIEdgeInsets contentInset = [self _computedObscuredInset];
3537 contentOffsetWithoutObscuredInset.x += contentInset.left;
3538 contentOffsetWithoutObscuredInset.y += contentInset.top;
3539
3540 _perProcessState.liveResizeParameters = { { self.bounds.size.width, contentOffsetWithoutObscuredInset } };
3541
3542 [self _ensureResizeAnimationView];
3543}
3544
3545- (void)_endLiveResize
3546{
3547 WKWEBVIEW_RELEASE_LOG("%p (pageProxyID=%llu) -[WKWebView _endLiveResize]", self, _page->identifier().toUInt64());
3548
3549 if (!_perProcessState.liveResizeParameters)
3550 return;
3551
3552 [_endLiveResizeTimer invalidate];
3553 _endLiveResizeTimer = nilnullptr;
3554
3555 RetainPtr liveResizeSnapshotView = [self snapshotViewAfterScreenUpdates:NO__objc_no];
3556 [liveResizeSnapshotView setFrame:self.bounds];
3557 [self addSubview:liveResizeSnapshotView.get()];
3558
3559 _perProcessState.liveResizeParameters = std::nullopt;
3560
3561 ASSERT(_perProcessState.dynamicViewportUpdateMode == WebKit::DynamicViewportUpdateMode::NotResizing)((void)0);
3562 [self _destroyResizeAnimationView];
3563 [self _didStopDeferringGeometryUpdates];
3564
3565 [self _doAfterNextVisibleContentRectUpdate:makeBlockPtr([liveResizeSnapshotView, weakSelf = WeakObjCPtr<WKWebView>(self)]() mutable {
3566 auto strongSelf = weakSelf.get();
3567 [strongSelf _doAfterNextPresentationUpdate:makeBlockPtr([liveResizeSnapshotView] {
3568 [liveResizeSnapshotView removeFromSuperview];
3569 }).get()];
3570 }).get()];
3571
3572 // Ensure that the live resize snapshot is eventually removed, even if the webpage is unresponsive.
3573 RunLoop::mainSingleton().dispatchAfter(1_s, [liveResizeSnapshotView] {
3574 [liveResizeSnapshotView removeFromSuperview];
3575 });
3576}
3577
3578#endif // HAVE(UI_WINDOW_SCENE_LIVE_RESIZE)
3579
3580#if HAVE(UIFINDINTERACTION)(defined 1 && 1)
3581
3582- (id<UITextSearching>)_searchableObject
3583{
3584 if ([_customContentView conformsToProtocol:@protocol(UITextSearching)])
3585 return (id<UITextSearching>)_customContentView.get();
3586
3587 return _contentView.get();
3588}
3589
3590- (BOOL)supportsTextReplacement
3591{
3592 if ([_customContentView respondsToSelector:@selector(supportsTextReplacement)])
3593 return [(id<UITextSearching>)_customContentView.get() supportsTextReplacement];
3594
3595 return [_contentView supportsTextReplacementForWebView];
3596}
3597
3598- (CABasicAnimation *)_animationForFindOverlay:(BOOL)animateIn
3599{
3600 CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
3601 animation.fromValue = animateIn ? @0 : @1;
3602 animation.toValue = animateIn ? @1 : @0;
3603 animation.duration = 0.2f;
3604 animation.timingFunction = [CAMediaTimingFunction functionWithControlPoints:0.445 :0.05 :0.55 :0.95];
3605 return animation;
3606}
3607
3608- (void)_updateFindOverlayPosition
3609{
3610 if (!_findOverlaysOutsideContentView)
3611 return;
3612
3613 RetainPtr scrollView = _scrollView.get();
3614 CGRect contentViewBounds = [_contentView bounds];
3615 CGRect contentViewFrame = [_contentView frame];
3616 CGFloat minX = std::min<CGFloat>(0, scrollView.get().contentOffset.x);
3617 CGFloat minY = std::min<CGFloat>(0, scrollView.get().contentOffset.y);
3618 CGFloat maxX = std::max<CGFloat>(scrollView.get().bounds.size.width + scrollView.get().contentOffset.x, contentViewBounds.size.width);
3619 CGFloat maxY = std::max<CGFloat>(scrollView.get().bounds.size.height + scrollView.get().contentOffset.y, contentViewBounds.size.height);
3620
3621 [_findOverlaysOutsideContentView->top setFrame:CGRectMake(
3622 CGRectGetMinX(contentViewFrame),
3623 minY,
3624 std::max<CGFloat>(maxX - CGRectGetMinX(contentViewFrame), 0),
3625 std::max<CGFloat>(CGRectGetMinY(contentViewFrame) - minY, 0))];
3626
3627 [_findOverlaysOutsideContentView->right setFrame:CGRectMake(
3628 CGRectGetMaxX(contentViewFrame),
3629 CGRectGetMinY(contentViewFrame),
3630 std::max<CGFloat>(maxX - CGRectGetMaxX(contentViewFrame), 0),
3631 std::max<CGFloat>(maxY - CGRectGetMinY(contentViewFrame), 0))];
3632
3633 [_findOverlaysOutsideContentView->bottom setFrame:CGRectMake(
3634 minX,
3635 CGRectGetMaxY(contentViewFrame),
3636 std::max<CGFloat>(CGRectGetMaxX(contentViewFrame) - minX, 0),
3637 std::max<CGFloat>(maxY - CGRectGetMaxY(contentViewFrame), 0))];
3638
3639 [_findOverlaysOutsideContentView->left setFrame:CGRectMake(
3640 minX,
3641 minY,
3642 std::max<CGFloat>(CGRectGetMinX(contentViewFrame) - minX, 0),
3643 std::max<CGFloat>(CGRectGetMaxY(contentViewFrame) - minY, 0))];
3644}
3645
3646- (void)_showFindOverlay
3647{
3648 if (!_findOverlaysOutsideContentView) {
3649 auto makeOverlay = [&]() {
3650 UIColor *overlayColor = [UIColor colorWithRed:(26. / 255) green:(26. / 255) blue:(26. / 255) alpha:(64. / 255)];
3651 auto newOverlay = adoptNS([[UIView alloc] init]);
3652 [newOverlay setBackgroundColor:overlayColor];
3653 [_scrollView insertSubview:newOverlay.get() belowSubview:_contentView.get()];
3654
3655 return newOverlay;
3656 };
3657 _findOverlaysOutsideContentView = { makeOverlay(), makeOverlay(), makeOverlay(), makeOverlay() };
3658 }
3659 [self _updateFindOverlayPosition];
3660
3661 if (CALayer *contentViewFindOverlayLayer = [self _layerForFindOverlay]) {
3662 [self _updateFindOverlaysOutsideContentView:^(UIView *view) {
3663 [[view layer] removeAllAnimations];
3664 [view setAlpha:1];
3665 }];
3666
3667 [contentViewFindOverlayLayer removeAllAnimations];
3668 [contentViewFindOverlayLayer setOpacity:1];
3669 } else {
3670 [self _updateFindOverlaysOutsideContentView:^(UIView *view) {
3671 [view setAlpha:0];
3672 }];
3673 [self _addLayerForFindOverlay];
3674 }
3675}
3676
3677- (void)_hideFindOverlay
3678{
3679 CALayer *contentViewFindOverlayLayer = [self _layerForFindOverlay];
3680 CALayer *findOverlayLayer = _findOverlaysOutsideContentView ? [_findOverlaysOutsideContentView->top layer] : nilnullptr;
3681
3682 if (!findOverlayLayer && !contentViewFindOverlayLayer)
3683 return;
3684
3685 if ([findOverlayLayer animationForKey:@"findOverlayFadeOut"])
3686 return;
3687
3688 [contentViewFindOverlayLayer removeAllAnimations];
3689 [self _updateFindOverlaysOutsideContentView:^(UIView *view) {
3690 [[view layer] removeAllAnimations];
3691 }];
3692
3693 [CATransaction begin];
3694
3695 CABasicAnimation *animation = [self _animationForFindOverlay:NO__objc_no];
3696
3697 [CATransaction setCompletionBlock:[weakSelf = WeakObjCPtr<WKWebView>(self)] {
3698 auto strongSelf = weakSelf.get();
3699 if (!strongSelf)
3700 return;
3701
3702 bool hasOverlaysOutsideContentView = strongSelf->_findOverlaysOutsideContentView.has_value();
3703 if (hasOverlaysOutsideContentView && [strongSelf->_findOverlaysOutsideContentView->top alpha])
3704 return;
3705
3706 [strongSelf _removeLayerForFindOverlay];
3707
3708 if (hasOverlaysOutsideContentView) {
3709 [strongSelf _updateFindOverlaysOutsideContentView:^(UIView *view) {
3710 [view removeFromSuperview];
3711 }];
3712 strongSelf->_findOverlaysOutsideContentView.reset();
3713 }
3714 }];
3715
3716 [contentViewFindOverlayLayer addAnimation:animation forKey:@"findOverlayFadeOut"];
3717 [self _updateFindOverlaysOutsideContentView:^(UIView *view) {
3718 [[view layer] addAnimation:animation forKey:@"findOverlayFadeOut"];
3719 }];
3720
3721 [CATransaction commit];
3722
3723 contentViewFindOverlayLayer.opacity = 0;
3724 [self _updateFindOverlaysOutsideContentView:^(UIView *view) {
3725 [view layer].opacity = 0;
3726 }];
3727}
3728
3729#endif // HAVE(UIFINDINTERACTION)
3730
3731#if HAVE(UIKIT_RESIZABLE_WINDOWS)(defined 1 && 1)
3732
3733- (void)_enhancedWindowingToggled:(NSNotification *)notification
3734{
3735 if (dynamic_objc_cast<UIWindowScene>(notification.object) != self.window.windowScene)
3736 return;
3737
3738 if (!_page || !_page->hasRunningProcess())
3739 return;
3740
3741 _page->setIsWindowResizingEnabled(self._isWindowResizingEnabled);
3742}
3743
3744#endif // HAVE(UIKIT_RESIZABLE_WINDOWS)
3745
3746#if HAVE(SUPPORT_HDR_DISPLAY_APIS)(defined HAVE_SUPPORT_HDR_DISPLAY_APIS && HAVE_SUPPORT_HDR_DISPLAY_APIS
)
3747- (void)_hdrHeadroomUsageLimitDidChange
3748{
3749 const UIHDRHeadroomUsageLimit hdrHeadroomUsageLimit = [[self traitCollection] hdrHeadroomUsageLimit];
3750 _page->setShouldSuppressHDR(hdrHeadroomUsageLimit == UIHDRHeadroomUsageLimitActive);
3751}
3752#endif // HAVE(SUPPORT_HDR_DISPLAY_APIS)
3753
3754#if ENABLE(LOCKDOWN_MODE_API)(defined 1 && 1)
3755
3756// Note: Use the legacy 'CaptivePortal' string to avoid losing users choice from earlier releases.
3757constexpr auto WebKitLockdownModeAlertShownKey = @"WebKitCaptivePortalModeAlertShown";
3758
3759static bool lockdownModeWarningNeeded = true;
3760
3761+ (void)_clearLockdownModeWarningNeeded
3762{
3763 lockdownModeWarningNeeded = true;
3764}
3765
3766static bool isLockdownModeWarningNeeded()
3767{
3768 // Only present the alert if the app is not Safari
3769 // and we've never presented the alert before
3770 if (WTF::IOSApplication::isMobileSafari())
3771 return false;
3772
3773 ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push GCC diagnostic ignored "-W" "deprecated-declarations"
3774 if (![WKProcessPool _lockdownModeEnabledGloballyForTesting] || [[NSUserDefaults standardUserDefaults] boolForKey:WebKitLockdownModeAlertShownKey])
3775 return false;
3776 ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop
3777
3778 return true;
3779}
3780
3781- (void)_presentLockdownMode
3782{
3783 lockdownModeWarningNeeded = isLockdownModeWarningNeeded();
3784 if (!lockdownModeWarningNeeded)
3785 return;
3786
3787#if PLATFORM(MACCATALYST)(defined WTF_PLATFORM_MACCATALYST && WTF_PLATFORM_MACCATALYST
)
3788 RetainPtr message = WEB_UI_NSSTRING(@"Certain experiences and features may not function as expected. You can manage Lockdown Mode in Settings.", "Lockdown Mode alert message (MacCatalyst)")WebCore::localizedNSString(@"Certain experiences and features may not function as expected. You can manage Lockdown Mode in Settings."
)
;
3789#else
3790 RetainPtr message = WEB_UI_NSSTRING(@"Certain experiences and features may not function as expected. You can turn off Lockdown Mode for this app in Settings.", "Lockdown Mode alert message")WebCore::localizedNSString(@"Certain experiences and features may not function as expected. You can turn off Lockdown Mode for this app in Settings."
)
;
3791#endif
3792
3793 auto decisionHandler = makeBlockPtr([message, protectedSelf = retainPtr(self)](WKDialogResult result) mutable {
3794 if (result == WKDialogResultAskAgain) {
3795 lockdownModeWarningNeeded = true;
3796 return;
3797 }
3798
3799 lockdownModeWarningNeeded = false;
3800
3801 if (result == WKDialogResultHandled) {
3802 [[NSUserDefaults standardUserDefaults] setBool:YES__objc_yes forKey:WebKitLockdownModeAlertShownKey];
3803 return;
3804 }
3805
3806 RunLoop::mainSingleton().dispatch([message = WTF::move(message), protectedSelf = WTF::move(protectedSelf)] {
3807 NSString *appDisplayName = [[NSBundle mainBundle] objectForInfoDictionaryKey:(__bridge NSString *)_kCFBundleDisplayNameKey];
3808 if (!appDisplayName)
3809 appDisplayName = [[NSBundle mainBundle] objectForInfoDictionaryKey:(__bridge NSString *)kCFBundleNameKey];
3810
3811 SUPPRESS_UNRETAINED_ARG[[clang::suppress]] RetainPtr title = adoptNS([[NSString alloc] initWithFormat:WEB_UI_NSSTRING(@"Lockdown Mode is Turned On For “%@“", "Lockdown Mode alert title")WebCore::localizedNSString(@"Lockdown Mode is Turned On For “%@“"
)
, appDisplayName]);
3812 auto alert = WebKit::createUIAlertController(title.get(), message.get());
3813
3814 [alert addAction:[UIAlertAction actionWithTitle:protect(WEB_UI_NSSTRING(@"OK", "Lockdown Mode alert OK button")WebCore::localizedNSString(@"OK")) style:UIAlertActionStyleDefault handler:nilnullptr]];
3815
3816 auto presentationViewController = [protectedSelf _wk_viewControllerForFullScreenPresentation];
3817 [presentationViewController presentViewController:alert.get() animated:YES__objc_yes completion:nilnullptr];
3818 [[NSUserDefaults standardUserDefaults] setBool:YES__objc_yes forKey:WebKitLockdownModeAlertShownKey];
3819 });
3820 });
3821
3822#if PLATFORM(IOS)(defined 1 && 1) || PLATFORM(VISION)(defined WTF_PLATFORM_VISION && WTF_PLATFORM_VISION)
3823 if ([self.UIDelegate respondsToSelector:@selector(webView:showLockdownModeFirstUseMessage:completionHandler:)]) {
3824 [self.UIDelegate webView:self showLockdownModeFirstUseMessage:message.get() completionHandler:decisionHandler.get()];
3825 return;
3826 }
3827#endif
3828
3829 decisionHandler(WKDialogResultShowDefault);
3830}
3831
3832- (void)_presentLockdownModeAlertIfNeeded
3833{
3834 static dispatch_once_t onceToken;
3835 dispatch_once_dispatch_once(&onceToken, ^{
3836 [self _presentLockdownMode];
3837 _needsToPresentLockdownModeMessage = NO__objc_no;
3838 });
3839
3840 if (!lockdownModeWarningNeeded)
3841 return;
3842
3843 if (!_needsToPresentLockdownModeMessage)
3844 return;
3845
3846 [self _presentLockdownMode];
3847 _needsToPresentLockdownModeMessage = NO__objc_no;
3848}
3849#else
3850- (void)_presentLockdownModeAlertIfNeeded
3851{
3852}
3853#endif
3854
3855- (_UIDataOwner)_effectiveDataOwner:(_UIDataOwner)clientSuppliedDataOwner
3856{
3857 auto isCurrentURLManaged = [&] {
3858#if PLATFORM(MACCATALYST)(defined WTF_PLATFORM_MACCATALYST && WTF_PLATFORM_MACCATALYST
)
3859 return NO__objc_no;
3860#else
3861 return [(MCProfileConnection *)[PAL::getMCProfileConnectionClassSingleton() sharedConnection] isURLManaged:self.URL];
3862#endif
3863 };
3864
3865 switch (clientSuppliedDataOwner) {
3866 case _UIDataOwnerUndefined:
3867 return isCurrentURLManaged() ? _UIDataOwnerEnterprise : _UIDataOwnerUser;
3868 case _UIDataOwnerUser:
3869 case _UIDataOwnerEnterprise:
3870 case _UIDataOwnerShared:
3871 return clientSuppliedDataOwner;
3872 }
3873 ASSERT_NOT_REACHED()((void)0);
3874 return _UIDataOwnerUndefined;
3875}
3876
3877#if HAVE(UIFINDINTERACTION)(defined 1 && 1)
3878- (void)_updateFindOverlaysOutsideContentView:(void(^)(UIView *))updateFindOverlay
3879{
3880 if (!_findOverlaysOutsideContentView)
3881 return;
3882 updateFindOverlay(_findOverlaysOutsideContentView->top.get());
3883 updateFindOverlay(_findOverlaysOutsideContentView->bottom.get());
3884 updateFindOverlay(_findOverlaysOutsideContentView->left.get());
3885 updateFindOverlay(_findOverlaysOutsideContentView->right.get());
3886}
3887#endif
3888
3889- (void)_didAddLayerForFindOverlay:(CALayer *)layer
3890{
3891 _perProcessState.committedFindLayerID = std::exchange(_perProcessState.pendingFindLayerID, std::nullopt);
3892 _page->findClient().didAddLayerForFindOverlay(_page.get(), layer);
3893
3894#if HAVE(UIFINDINTERACTION)(defined 1 && 1)
3895 CABasicAnimation *animation = [self _animationForFindOverlay:YES__objc_yes];
3896
3897 [layer addAnimation:animation forKey:@"findOverlayFadeIn"];
3898
3899 [self _updateFindOverlaysOutsideContentView:^(UIView *view) {
3900 CALayer *overlayLayer = [view layer];
3901 [overlayLayer addAnimation:animation forKey:@"findOverlayFadeIn"];
3902 overlayLayer.opacity = 1;
3903 }];
3904
3905 layer.opacity = 1;
3906#endif
3907}
3908
3909- (void)_didRemoveLayerForFindOverlay
3910{
3911 if (!_page)
3912 return;
3913
3914 _page->findClient().didRemoveLayerForFindOverlay(_page.get());
3915}
3916
3917- (BOOL)_haveSetUnobscuredSafeAreaInsets
3918{
3919 return _haveSetUnobscuredSafeAreaInsets;
3920}
3921
3922- (void)_resetContentOffset
3923{
3924 [_scrollView setContentOffset:[self _initialContentOffsetForScrollView]];
3925}
3926
3927- (void)_resetUnobscuredSafeAreaInsets
3928{
3929 _haveSetUnobscuredSafeAreaInsets = NO__objc_no;
3930 _unobscuredSafeAreaInsets = { };
3931 [self _scheduleVisibleContentRectUpdate];
3932}
3933
3934- (BOOL)_hasOverriddenLayoutParameters
3935{
3936 return _overriddenLayoutParameters.has_value();
3937}
3938
3939- (void)_resetObscuredInsets
3940{
3941 _haveSetObscuredInsets = NO__objc_no;
3942 _obscuredInsets = { };
3943 [self _scheduleVisibleContentRectUpdate];
3944}
3945
3946- (void)_setPointerTouchCompatibilitySimulatorEnabled:(BOOL)enabled
3947{
3948 _pointerTouchCompatibilitySimulator->setEnabled(enabled);
3949}
3950
3951- (BOOL)_isSimulatingCompatibilityPointerTouches
3952{
3953 return _pointerTouchCompatibilitySimulator->isSimulatingTouches();
3954}
3955
3956#pragma mark - WKBaseScrollViewDelegate
3957
3958- (BOOL)shouldAllowPanGestureRecognizerToReceiveTouchesInScrollView:(WKBaseScrollView *)scrollView
3959{
3960 return !self._isSimulatingCompatibilityPointerTouches;
3961}
3962
3963- (UIAxis)axesToPreventScrollingForPanGestureInScrollView:(WKBaseScrollView *)scrollView
3964{
3965 auto panGestureRecognizer = scrollView.panGestureRecognizer;
3966 if (![_contentView preventsPanningInXAxis] && ![_contentView preventsPanningInYAxis]) {
3967 [_contentView cancelPointersForGestureRecognizer:panGestureRecognizer];
3968 return UIAxisNeither;
3969 }
3970
3971 UIAxis axesToPrevent = UIAxisNeither;
3972 if ([_contentView preventsPanningInXAxis])
3973 axesToPrevent |= UIAxisHorizontal;
3974 if ([_contentView preventsPanningInYAxis])
3975 axesToPrevent |= UIAxisVertical;
3976
3977 auto translation = [panGestureRecognizer translationInView:scrollView];
3978 if ((![_contentView preventsPanningInXAxis] && std::abs(translation.x) > CGFLOAT_EPSILON2.2204460492503131e-16)
3979 || (![_contentView preventsPanningInYAxis] && std::abs(translation.y) > CGFLOAT_EPSILON2.2204460492503131e-16))
3980 [_contentView cancelPointersForGestureRecognizer:panGestureRecognizer];
3981
3982 return axesToPrevent;
3983}
3984
3985- (BOOL)_forcesInitialScaleFactor
3986{
3987 return _forcesInitialScaleFactor;
3988}
3989
3990- (void)_setForcesInitialScaleFactor:(BOOL)forcesInitialScaleFactor
3991{
3992 _forcesInitialScaleFactor = forcesInitialScaleFactor;
3993 [self _doAfterNextPresentationUpdate:^{ }];
3994}
3995
3996#if ENABLE(MODEL_PROCESS)(defined 0 && 0)
3997- (void)_willInvalidateDraggedModelWithContainerView:(UIView *)containerView
3998{
3999 [_contentView _willInvalidateDraggedModelWithContainerView:containerView];
4000}
4001#endif
4002
4003#if HAVE(UI_CONVERSATION_CONTEXT)(defined 1 && 1)
4004
4005- (UIConversationContext *)conversationContext
4006{
4007 return _conversationContextFromClient.get();
4008}
4009
4010- (void)setConversationContext:(UIConversationContext *)context
4011{
4012 _conversationContextFromClient = context;
4013
4014 [_contentView _setConversationContext:context];
4015}
4016
4017#endif // HAVE(UI_CONVERSATION_CONTEXT)
4018
4019#if USE(APPLE_INTERNAL_SDK)(defined USE_APPLE_INTERNAL_SDK && USE_APPLE_INTERNAL_SDK
)
&& __has_include(<WebKitAdditions/WKWebViewIOSInternalAdditionsAfter.mm>)
4020#import <WebKitAdditions/WKWebViewIOSInternalAdditionsAfter.mm>
4021#endif
4022
4023@end
4024
4025@implementation WKWebView (WKPrivateIOS)
4026
4027#if !PLATFORM(WATCHOS)(defined WTF_PLATFORM_WATCHOS && WTF_PLATFORM_WATCHOS
)
&& !PLATFORM(APPLETV)(defined WTF_PLATFORM_APPLETV && WTF_PLATFORM_APPLETV
)
4028- (void)_setUIEventAttribution:(UIEventAttribution *)attribution
4029{
4030#if HAVE(UI_EVENT_ATTRIBUTION)(defined 1 && 1)
4031 if (attribution) {
4032 WebCore::PrivateClickMeasurement measurement(
4033 WebCore::PrivateClickMeasurement::SourceID(attribution.sourceIdentifier),
4034 WebCore::PCM::SourceSite(attribution.reportEndpoint),
4035 WebCore::PCM::AttributionDestinationSite(attribution.destinationURL),
4036 applicationBundleIdentifier(),
4037 WallTime::now(),
4038 WebCore::PCM::AttributionEphemeral::No
4039 );
4040 _page->setPrivateClickMeasurement(WTF::move(measurement), attribution.sourceDescription, attribution.purchaser);
4041 } else
4042 _page->setPrivateClickMeasurement(std::nullopt);
4043#endif
4044}
4045
4046- (UIEventAttribution *)_uiEventAttribution
4047{
4048#if HAVE(UI_EVENT_ATTRIBUTION)(defined 1 && 1)
4049 auto attribution = _page->privateClickMeasurementEventAttribution();
4050 if (!attribution)
4051 return nilnullptr;
4052
4053 URL destinationURL { makeString("https://"_s, attribution->destinationDomain) };
4054 return adoptNS([[UIEventAttribution alloc] initWithSourceIdentifier:attribution->sourceID destinationURL:destinationURL.createNSURL().get() sourceDescription:attribution->sourceDescription.createNSString().get() purchaser:attribution->purchaser.createNSString().get()]).autorelease();
4055#else
4056 return nilnullptr;
4057#endif
4058}
4059
4060- (void)_setEphemeralUIEventAttribution:(UIEventAttribution *)attribution
4061{
4062 // FIXME: Deprecate and remove this version without a bundle ID.
4063 [self _setEphemeralUIEventAttribution:attribution forApplicationWithBundleID:@"com.apple.mobilesafari"];
4064}
4065
4066- (void)_setEphemeralUIEventAttribution:(UIEventAttribution *)attribution forApplicationWithBundleID:(NSString *)bundleID
4067{
4068#if HAVE(UI_EVENT_ATTRIBUTION)(defined 1 && 1)
4069 if (attribution) {
4070 WebCore::PrivateClickMeasurement measurement(
4071 WebCore::PrivateClickMeasurement::SourceID(attribution.sourceIdentifier),
4072 WebCore::PCM::SourceSite(attribution.reportEndpoint),
4073 WebCore::PCM::AttributionDestinationSite(attribution.destinationURL),
4074 bundleID,
4075 WallTime::now(),
4076 WebCore::PCM::AttributionEphemeral::Yes
4077 );
4078 _page->setPrivateClickMeasurement(WTF::move(measurement), attribution.sourceDescription, attribution.purchaser);
4079 } else
4080 _page->setPrivateClickMeasurement(std::nullopt);
4081#endif
4082}
4083
4084- (UIEventAttribution *)_ephemeralUIEventAttribution
4085{
4086 return self._uiEventAttribution;
4087}
4088
4089#endif // !PLATFORM(WATCHOS) && !PLATFORM(APPLETV)
4090
4091- (CGRect)_contentVisibleRect
4092{
4093 return [self convertRect:[self bounds] toView:self._currentContentView];
4094}
4095
4096// Deprecated SPI.
4097- (CGSize)_minimumLayoutSizeOverride
4098{
4099 if (!_overriddenLayoutParameters)
4100 return CGSizeZero;
4101
4102 return _overriddenLayoutParameters->viewLayoutSize;
4103}
4104
4105- (CGSize)_minimumUnobscuredSizeOverride
4106{
4107 if (!_overriddenLayoutParameters)
4108 return CGSizeZero;
4109
4110 return _overriddenLayoutParameters->minimumUnobscuredSize;
4111}
4112
4113// Deprecated SPI
4114- (CGSize)_maximumUnobscuredSizeOverride
4115{
4116 if (!_overriddenLayoutParameters)
4117 return CGSizeZero;
4118
4119 return _overriddenLayoutParameters->maximumUnobscuredSize;
4120}
4121
4122- (UIEdgeInsets)_obscuredInsets
4123{
4124 return _obscuredInsets;
4125}
4126
4127- (void)_setObscuredInsets:(UIEdgeInsets)obscuredInsets
4128{
4129 [self _setObscuredInsetsInternal:obscuredInsets];
4130
4131 _automaticallyAdjustsViewLayoutSizesWithObscuredInset = NO__objc_no;
4132}
4133
4134- (UIRectEdge)_obscuredInsetEdgesAffectedBySafeArea
4135{
4136 return _obscuredInsetEdgesAffectedBySafeArea;
4137}
4138
4139- (void)_setObscuredInsetEdgesAffectedBySafeArea:(UIRectEdge)edges
4140{
4141 if (edges == _obscuredInsetEdgesAffectedBySafeArea)
4142 return;
4143
4144 _obscuredInsetEdgesAffectedBySafeArea = edges;
4145
4146 [self _scheduleVisibleContentRectUpdate];
4147#if ENABLE(CONTENT_INSET_BACKGROUND_FILL)(defined 1 && 1)
4148 [self _updateFixedColorExtensionViewFrames];
4149#endif
4150}
4151
4152- (UIEdgeInsets)_unobscuredSafeAreaInsets
4153{
4154 return _unobscuredSafeAreaInsets;
4155}
4156
4157- (void)_setUnobscuredSafeAreaInsets:(UIEdgeInsets)unobscuredSafeAreaInsets
4158{
4159 ASSERT(unobscuredSafeAreaInsets.top >= 0)((void)0);
4160 ASSERT(unobscuredSafeAreaInsets.left >= 0)((void)0);
4161 ASSERT(unobscuredSafeAreaInsets.bottom >= 0)((void)0);
4162 ASSERT(unobscuredSafeAreaInsets.right >= 0)((void)0);
4163
4164 _haveSetUnobscuredSafeAreaInsets = YES__objc_yes;
4165
4166 if (UIEdgeInsetsEqualToEdgeInsets(_unobscuredSafeAreaInsets, unobscuredSafeAreaInsets))
4167 return;
4168
4169 _unobscuredSafeAreaInsets = unobscuredSafeAreaInsets;
4170
4171 [self _scheduleVisibleContentRectUpdate];
4172}
4173
4174- (BOOL)_safeAreaShouldAffectObscuredInsets
4175{
4176 if (![self usesStandardContentView])
4177 return NO__objc_no;
4178 return _avoidsUnsafeArea;
4179}
4180
4181- (BOOL)_allowsTouchPanningAtPoint:(CGPoint)point
4182{
4183 return [_contentView allowsTouchPanningAtPoint:[self _convertPointFromViewToContents:point]];
4184}
4185
4186- (UIView *)_enclosingViewForExposedRectComputation
4187{
4188 return [self _scroller];
4189}
4190
4191- (void)_setInterfaceOrientationOverride:(UIInterfaceOrientation)interfaceOrientation
4192{
4193 if (_overridesInterfaceOrientation && interfaceOrientation == _interfaceOrientationOverride)
4194 return;
4195
4196 _overridesInterfaceOrientation = YES__objc_yes;
4197 _interfaceOrientationOverride = interfaceOrientation;
4198
4199 if (!self._shouldDeferGeometryUpdates)
4200 [self _dispatchSetDeviceOrientation:deviceOrientationForUIInterfaceOrientation(_interfaceOrientationOverride)];
4201}
4202
4203- (UIInterfaceOrientation)_interfaceOrientationOverride
4204{
4205 ASSERT(_overridesInterfaceOrientation)((void)0);
4206 return _interfaceOrientationOverride;
4207}
4208
4209- (void)_clearInterfaceOrientationOverride
4210{
4211 _overridesInterfaceOrientation = NO__objc_no;
4212 _interfaceOrientationOverride = UIInterfaceOrientationPortrait;
4213}
4214
4215- (void)_setAllowsViewportShrinkToFit:(BOOL)allowShrinkToFit
4216{
4217 _allowsViewportShrinkToFit = allowShrinkToFit;
4218}
4219
4220- (BOOL)_allowsViewportShrinkToFit
4221{
4222 return _allowsViewportShrinkToFit;
4223}
4224
4225- (NSData *)_dataForDisplayedPDF
4226{
4227 return nilnullptr;
4228}
4229
4230- (NSString *)_suggestedFilenameForDisplayedPDF
4231{
4232 return nilnullptr;
4233}
4234
4235- (_WKWebViewPrintFormatter *)_webViewPrintFormatter
4236{
4237 return checked_objc_cast<_WKWebViewPrintFormatter>(self.viewPrintFormatter);
4238}
4239
4240- (_WKDragInteractionPolicy)_dragInteractionPolicy
4241{
4242 return _dragInteractionPolicy;
4243}
4244
4245- (void)_setDragInteractionPolicy:(_WKDragInteractionPolicy)policy
4246{
4247 if (_dragInteractionPolicy == policy)
4248 return;
4249
4250 _dragInteractionPolicy = policy;
4251#if ENABLE(DRAG_SUPPORT)(defined 1 && 1)
4252 [_contentView _didChangeDragInteractionPolicy];
4253#endif
4254}
4255
4256- (BOOL)_shouldAvoidResizingWhenInputViewBoundsChange
4257{
4258 return [_contentView _shouldAvoidResizingWhenInputViewBoundsChange];
4259}
4260
4261- (BOOL)_contentViewIsFirstResponder
4262{
4263 return self._currentContentView.isFirstResponder;
4264}
4265
4266- (CGRect)_uiTextCaretRect
4267{
4268 // Only here to maintain binary compatibility.
4269 return CGRectZero;
4270}
4271
4272- (UIView *)_safeBrowsingWarning
4273{
4274 return _warningView.get();
4275}
4276
4277- (CGPoint)_convertPointFromContentsToView:(CGPoint)point
4278{
4279 return [self convertPoint:point fromView:self._currentContentView];
4280}
4281
4282- (CGPoint)_convertPointFromViewToContents:(CGPoint)point
4283{
4284 return [self convertPoint:point toView:self._currentContentView];
4285}
4286
4287- (void)_doAfterNextStablePresentationUpdate:(dispatch_block_t)updateBlock
4288{
4289 if (![self usesStandardContentView]) {
4290 RunLoop::mainSingleton().dispatch([updateBlock = makeBlockPtr(updateBlock)] {
4291 updateBlock();
4292 });
4293 return;
4294 }
4295
4296 auto updateBlockCopy = makeBlockPtr(updateBlock);
4297
4298 if (_stableStatePresentationUpdateCallbacks)
4299 SUPPRESS_UNRETAINED_ARG[[clang::suppress]] [_stableStatePresentationUpdateCallbacks addObject:updateBlockCopy.get()];
4300 else {
4301 SUPPRESS_UNRETAINED_ARG[[clang::suppress]] _stableStatePresentationUpdateCallbacks = adoptNS([[NSMutableArray alloc] initWithObjects:updateBlockCopy.get(), nilnullptr]);
4302 [self _firePresentationUpdateForPendingStableStatePresentationCallbacks];
4303 }
4304}
4305
4306- (void)_setFont:(UIFont *)font sender:(id)sender
4307{
4308 if (self.usesStandardContentView)
4309 [_contentView _setFontForWebView:font sender:sender];
4310}
4311
4312- (void)_setFontSize:(CGFloat)fontSize sender:(id)sender
4313{
4314 if (self.usesStandardContentView)
4315 [_contentView _setFontSizeForWebView:fontSize sender:sender];
4316}
4317
4318- (void)_setTextColor:(UIColor *)color sender:(id)sender
4319{
4320 if (self.usesStandardContentView)
4321 [_contentView _setTextColorForWebView:color sender:sender];
4322}
4323
4324- (void)_detectDataWithTypes:(WKDataDetectorTypes)types completionHandler:(dispatch_block_t)completion
4325{
4326#if ENABLE(DATA_DETECTION)(defined 1 && 1)
4327 _page->detectDataInAllFrames(fromWKDataDetectorTypes(types), [completion = makeBlockPtr(completion), page = WeakPtr { _page.get() }] (auto&& result) {
4328 if (page)
4329 page->setDataDetectionResult(WTF::move(result));
4330 if (completion)
4331 completion();
4332 });
4333#else
4334 UNUSED_PARAM(types)(void)types;
4335 UNUSED_PARAM(completion)(void)completion;
4336#endif
4337}
4338
4339- (void)_requestActivatedElementAtPosition:(CGPoint)position completionBlock:(void (^)(_WKActivatedElementInfo *))block
4340{
4341 auto infoRequest = WebKit::InteractionInformationRequest(WebCore::roundedIntPoint(position));
4342 infoRequest.includeSnapshot = true;
4343
4344 [_contentView doAfterPositionInformationUpdate:[capturedBlock = makeBlockPtr(block)] (WebKit::InteractionInformationAtPosition information) {
4345 capturedBlock([_WKActivatedElementInfo activatedElementInfoWithInteractionInformationAtPosition:information userInfo:nilnullptr]);
4346 } forRequest:infoRequest];
4347}
4348
4349- (void)didStartFormControlInteraction
4350{
4351 // For subclasses to override.
4352}
4353
4354- (void)didEndFormControlInteraction
4355{
4356 // For subclasses to override.
4357}
4358
4359- (void)didEnsurePositionInformationIsUpToDate
4360{
4361 // For subclasses to override.
4362}
4363
4364- (void)_beginInteractiveObscuredInsetsChange
4365{
4366 ASSERT(!_isChangingObscuredInsetsInteractively)((void)0);
4367 _isChangingObscuredInsetsInteractively = YES__objc_yes;
4368}
4369
4370- (void)_endInteractiveObscuredInsetsChange
4371{
4372 ASSERT(_isChangingObscuredInsetsInteractively)((void)0);
4373 _isChangingObscuredInsetsInteractively = NO__objc_no;
4374 [self _cancelPendingVisibleContentRectUpdateTimer];
4375 [self _scheduleVisibleContentRectUpdate];
4376}
4377
4378- (void)_hideContentUntilNextUpdate
4379{
4380 if (RefPtr area = _page->drawingArea())
4381 area->hideContentUntilAnyUpdate();
4382}
4383
4384- (void)_beginAnimatedResizeWithUpdates:(void (^)(void))updateBlock
4385{
4386 bool hadPendingAnimatedResize = _perProcessState.dynamicViewportUpdateMode != WebKit::DynamicViewportUpdateMode::NotResizing;
4387 CGRect oldBounds = self.bounds;
4388 WebCore::FloatRect oldUnobscuredContentRect = _page->unobscuredContentRect();
4389
4390 auto isOldBoundsValid = !CGRectIsEmpty(oldBounds) || !CGRectIsEmpty(_perProcessState.animatedResizeOldBounds);
4391 bool shouldPerformAnimatedResize = [&] {
4392 if (![self usesStandardContentView])
4393 return false;
4394
4395 if (!_perProcessState.hasCommittedLoadForMainFrame)
4396 return false;
4397
4398 if (!isOldBoundsValid)
4399 return false;
4400
4401 if (oldUnobscuredContentRect.isEmpty())
4402 return false;
4403
4404 if (_perProcessState.liveResizeParameters)
4405 return false;
4406
4407#if HAVE(UI_WINDOW_SCENE_LIVE_RESIZE)(defined 1 && 1)
4408 if ([self _canBeginAutomaticLiveResize]) {
4409 auto sceneGeometry = self.window.windowScene.effectiveGeometry;
4410 if ([sceneGeometry respondsToSelector:@selector(isInteractivelyResizing)]) {
4411 if (sceneGeometry.isInteractivelyResizing)
4412 return false;
4413 }
4414 }
4415#endif
4416
4417 if (_page->isTakingSnapshotsForApplicationSuspension() && self._isDisplayingPDF)
4418 return false;
4419
4420 return true;
4421 }();
4422
4423 if (!shouldPerformAnimatedResize) {
4424 updateBlock();
4425 return;
4426 }
4427
4428 WKWEBVIEW_RELEASE_LOG("%p (pageProxyID=%llu) -[WKWebView _beginAnimatedResizeWithUpdates:]", self, _page->identifier().toUInt64());
4429
4430 _perProcessState.dynamicViewportUpdateMode = WebKit::DynamicViewportUpdateMode::ResizingWithAnimation;
4431
4432 CGFloat oldMinimumEffectiveDeviceWidth;
4433 WebCore::IntDegrees oldOrientation;
4434 UIEdgeInsets oldObscuredInsets;
4435 if (!CGRectIsEmpty(_perProcessState.animatedResizeOldBounds)) {
4436 oldBounds = _perProcessState.animatedResizeOldBounds;
4437 oldMinimumEffectiveDeviceWidth = _animatedResizeOldMinimumEffectiveDeviceWidth;
4438 oldOrientation = _animatedResizeOldOrientation;
4439 oldObscuredInsets = _animatedResizeOldObscuredInsets;
4440 _perProcessState.animatedResizeOldBounds = { };
4441 } else {
4442 oldMinimumEffectiveDeviceWidth = [self _minimumEffectiveDeviceWidth];
4443 oldOrientation = activeOrientation(self);
4444 oldObscuredInsets = _obscuredInsets;
4445 }
4446 auto oldViewLayoutSize = [self activeViewLayoutSize:oldBounds];
4447 auto oldMinimumUnobscuredSize = activeMinimumUnobscuredSize(self, oldBounds);
4448 auto oldMaximumUnobscuredSize = activeMaximumUnobscuredSize(self, oldBounds);
4449
4450 updateBlock();
4451
4452 CGRect newBounds = self.bounds;
4453 auto newMinimumEffectiveDeviceWidth = [self _minimumEffectiveDeviceWidth];
4454 auto newViewLayoutSize = [self activeViewLayoutSize:newBounds];
4455 auto newMinimumUnobscuredSize = activeMinimumUnobscuredSize(self, newBounds);
4456 auto newMaximumUnobscuredSize = activeMaximumUnobscuredSize(self, newBounds);
4457 auto newOrientation = activeOrientation(self);
4458 UIEdgeInsets newObscuredInsets = _obscuredInsets;
4459 CGRect futureUnobscuredRectInSelfCoordinates = UIEdgeInsetsInsetRect(newBounds, _obscuredInsets);
4460 CGRect contentViewBounds = [_contentView bounds];
4461
4462 if (CGRectIsEmpty(newBounds) || newViewLayoutSize.isEmpty() || CGRectIsEmpty(futureUnobscuredRectInSelfCoordinates) || CGRectIsEmpty(contentViewBounds)) {
4463 if (!CGRectIsEmpty(newBounds))
4464 [self _cancelAnimatedResize];
4465 else {
4466 _perProcessState.animatedResizeOldBounds = oldBounds;
4467 _animatedResizeOldMinimumEffectiveDeviceWidth = oldMinimumEffectiveDeviceWidth;
4468 _animatedResizeOldOrientation = oldOrientation;
4469 _animatedResizeOldObscuredInsets = oldObscuredInsets;
4470 _perProcessState.waitingForCommitAfterAnimatedResize = YES__objc_yes;
4471 _perProcessState.waitingForEndAnimatedResize = YES__objc_yes;
4472 }
4473
4474 [self _frameOrBoundsMayHaveChanged];
4475 if (_overriddenLayoutParameters) {
4476 [self _dispatchSetViewLayoutSize:newViewLayoutSize];
4477 _page->setMinimumUnobscuredSize(WebCore::FloatSize(newMinimumUnobscuredSize));
4478 _page->setDefaultUnobscuredSize(WebCore::FloatSize(newMaximumUnobscuredSize));
4479 _page->setMaximumUnobscuredSize(WebCore::FloatSize(newMaximumUnobscuredSize));
4480 }
4481 if (_overridesInterfaceOrientation)
4482 [self _dispatchSetDeviceOrientation:newOrientation];
4483
4484 return;
4485 }
4486
4487 if (!hadPendingAnimatedResize
4488 && CGRectEqualToRect(oldBounds, newBounds)
4489 && oldViewLayoutSize == newViewLayoutSize
4490 && oldMinimumUnobscuredSize == newMinimumUnobscuredSize
4491 && oldMaximumUnobscuredSize == newMaximumUnobscuredSize
4492 && oldOrientation == newOrientation
4493 && oldMinimumEffectiveDeviceWidth == newMinimumEffectiveDeviceWidth
4494 && UIEdgeInsetsEqualToEdgeInsets(oldObscuredInsets, newObscuredInsets)) {
4495 [self _cancelAnimatedResize];
4496 return;
4497 }
4498
4499 _resizeAnimationTransformAdjustments = CATransform3DIdentity;
4500
4501 [self _ensureResizeAnimationView];
4502
4503 CGSize contentSizeInContentViewCoordinates = contentViewBounds.size;
4504 [_scrollView setMinimumZoomScale:std::min(newViewLayoutSize.width() / contentSizeInContentViewCoordinates.width, [_scrollView minimumZoomScale])];
4505 [_scrollView setMaximumZoomScale:std::max(newViewLayoutSize.width() / contentSizeInContentViewCoordinates.width, [_scrollView maximumZoomScale])];
4506
4507 // Compute the new scale to keep the current content width in the scrollview.
4508 CGFloat oldWebViewWidthInContentViewCoordinates = oldUnobscuredContentRect.width();
4509 _perProcessState.animatedResizeOriginalContentWidth = [&] {
4510#if HAVE(UIKIT_RESIZABLE_WINDOWS)(defined 1 && 1)
4511 if (self._isWindowResizingEnabled && !self._isDisplayingPDF)
4512 return contentSizeInContentViewCoordinates.width;
4513#endif
4514 return std::min(contentSizeInContentViewCoordinates.width, oldWebViewWidthInContentViewCoordinates);
4515 }();
4516 CGFloat targetScale = newViewLayoutSize.width() / _perProcessState.animatedResizeOriginalContentWidth;
4517 CGFloat resizeAnimationViewAnimationScale = targetScale / contentZoomScale(self);
4518 [_resizeAnimationView setTransform:CGAffineTransformMakeScale(resizeAnimationViewAnimationScale, resizeAnimationViewAnimationScale)];
4519
4520 // Compute a new position to keep the content centered.
4521 CGPoint originalContentCenter = oldUnobscuredContentRect.center();
4522 CGPoint originalContentCenterInSelfCoordinates = [self convertPoint:originalContentCenter fromView:_contentView.get()];
4523 CGPoint futureUnobscuredRectCenterInSelfCoordinates = CGPointMake(futureUnobscuredRectInSelfCoordinates.origin.x + futureUnobscuredRectInSelfCoordinates.size.width / 2, futureUnobscuredRectInSelfCoordinates.origin.y + futureUnobscuredRectInSelfCoordinates.size.height / 2);
4524
4525 CGPoint originalContentOffset = [_scrollView contentOffset];
4526 CGPoint contentOffset = originalContentOffset;
4527 contentOffset.x += (originalContentCenterInSelfCoordinates.x - futureUnobscuredRectCenterInSelfCoordinates.x);
4528 contentOffset.y += (originalContentCenterInSelfCoordinates.y - futureUnobscuredRectCenterInSelfCoordinates.y);
4529
4530 // Limit the new offset within the scrollview, we do not want to rubber band programmatically.
4531 CGSize futureContentSizeInSelfCoordinates = CGSizeMake(contentSizeInContentViewCoordinates.width * targetScale, contentSizeInContentViewCoordinates.height * targetScale);
4532 CGFloat maxHorizontalOffset = futureContentSizeInSelfCoordinates.width - newBounds.size.width + _obscuredInsets.right;
4533 contentOffset.x = std::min(contentOffset.x, maxHorizontalOffset);
4534 CGFloat maxVerticalOffset = futureContentSizeInSelfCoordinates.height - newBounds.size.height + _obscuredInsets.bottom;
4535 contentOffset.y = std::min(contentOffset.y, maxVerticalOffset);
4536
4537 contentOffset.x = std::max(contentOffset.x, -_obscuredInsets.left);
4538 contentOffset.y = std::max(contentOffset.y, -_obscuredInsets.top);
4539
4540 // Make the top/bottom edges "sticky" within 1 pixel.
4541 if (oldUnobscuredContentRect.maxY() > contentSizeInContentViewCoordinates.height - 1)
4542 contentOffset.y = maxVerticalOffset;
4543 if (oldUnobscuredContentRect.y() < 1)
4544 contentOffset.y = [self _initialContentOffsetForScrollView].y;
4545
4546 // FIXME: if we have content centered after double tap to zoom, we should also try to keep that rect in view.
4547 [_scrollView setContentSize:roundScrollViewContentSize(*_page, futureContentSizeInSelfCoordinates)];
4548 [_scrollView setContentOffset:contentOffset];
4549
4550 CGRect visibleRectInContentCoordinates = [self convertRect:newBounds toView:_contentView.get()];
4551 CGRect unobscuredRectInContentCoordinates = [self convertRect:futureUnobscuredRectInSelfCoordinates toView:_contentView.get()];
4552
4553 UIEdgeInsets unobscuredSafeAreaInsets = [self _computedUnobscuredSafeAreaInset];
4554 WebCore::FloatBoxExtent unobscuredSafeAreaInsetsExtent { WebCore::narrowPrecisionToFloatFromCGFloat(unobscuredSafeAreaInsets.top), WebCore::narrowPrecisionToFloatFromCGFloat(unobscuredSafeAreaInsets.right), WebCore::narrowPrecisionToFloatFromCGFloat(unobscuredSafeAreaInsets.bottom), WebCore::narrowPrecisionToFloatFromCGFloat(unobscuredSafeAreaInsets.left) };
4555
4556 _perProcessState.lastSentViewLayoutSize = newViewLayoutSize;
4557 _perProcessState.lastSentDeviceOrientation = newOrientation;
4558 _perProcessState.lastSentMinimumEffectiveDeviceWidth = newMinimumEffectiveDeviceWidth;
4559
4560 _page->dynamicViewportSizeUpdate({ newViewLayoutSize, newMinimumUnobscuredSize, newMaximumUnobscuredSize, visibleRectInContentCoordinates, unobscuredRectInContentCoordinates, futureUnobscuredRectInSelfCoordinates, unobscuredSafeAreaInsetsExtent, targetScale, newOrientation, newMinimumEffectiveDeviceWidth, ++_currentDynamicViewportSizeUpdateID });
4561 if (RefPtr drawingArea = _page->drawingArea())
4562 drawingArea->setSize(WebCore::IntSize(newBounds.size));
4563
4564 _perProcessState.waitingForCommitAfterAnimatedResize = YES__objc_yes;
4565 _perProcessState.waitingForEndAnimatedResize = YES__objc_yes;
4566
4567#if HAVE(LIQUID_GLASS)(defined 1 && 1)
4568 [self _updateFixedColorExtensionViewFrames];
4569#endif
4570}
4571
4572- (void)_endAnimatedResize
4573{
4574 WKWEBVIEW_RELEASE_LOG("%p (pageProxyID=%llu) -[WKWebView _endAnimatedResize] dynamicViewportUpdateMode %d", self, _page->identifier().toUInt64(), std::to_underlying(_perProcessState.dynamicViewportUpdateMode));
4575
4576 // If we already have an up-to-date layer tree, immediately complete
4577 // the resize. Otherwise, we will defer completion until we do.
4578 _perProcessState.waitingForEndAnimatedResize = NO__objc_no;
4579 if (!_perProcessState.waitingForCommitAfterAnimatedResize)
4580 [self _didCompleteAnimatedResize];
4581}
4582
4583- (void)_resizeWhileHidingContentWithUpdates:(void (^)(void))updateBlock
4584{
4585 WKWEBVIEW_RELEASE_LOG("%p (pageProxyID=%llu) -[WKWebView _resizeWhileHidingContentWithUpdates:]", self, _page->identifier().toUInt64());
4586
4587 [self _beginAnimatedResizeWithUpdates:updateBlock];
4588 if (_perProcessState.dynamicViewportUpdateMode == WebKit::DynamicViewportUpdateMode::ResizingWithAnimation) {
4589 [_contentView setHidden:YES__objc_yes];
4590 _perProcessState.dynamicViewportUpdateMode = WebKit::DynamicViewportUpdateMode::ResizingWithDocumentHidden;
4591
4592 // _resizeWhileHidingContentWithUpdates is used by itself; the client will
4593 // not call endAnimatedResize, so we can't wait for it.
4594 _perProcessState.waitingForEndAnimatedResize = NO__objc_no;
4595 }
4596}
4597
4598- (void)_setSuppressSoftwareKeyboard:(BOOL)suppressSoftwareKeyboard
4599{
4600 super._suppressSoftwareKeyboard = suppressSoftwareKeyboard;
4601 [_contentView updateSoftwareKeyboardSuppressionStateFromWebView];
4602}
4603
4604// FIXME (<rdar://problem/80986330>): This method should be updated to take an image
4605// width in points (for consistency) and a completion handler with a UIImage parameter
4606// (to avoid redundant copies for PDFs), once it is no longer in use by internal clients.
4607- (void)_snapshotRectAfterScreenUpdates:(BOOL)afterScreenUpdates rectInViewCoordinates:(CGRect)rectInViewCoordinates intoImageOfWidth:(CGFloat)imageWidth completionHandler:(void(^)(CGImageRef))completionHandler
4608{
4609 if (self._shouldDeferGeometryUpdates) {
4610 // Defer snapshotting until after the current resize completes.
4611 _callbacksDeferredDuringResize.append([retainedSelf = retainPtr(self), afterScreenUpdates, rectInViewCoordinates, imageWidth, completionHandler = makeBlockPtr(completionHandler)] {
4612 [retainedSelf _snapshotRectAfterScreenUpdates:afterScreenUpdates rectInViewCoordinates:rectInViewCoordinates intoImageOfWidth:imageWidth completionHandler:completionHandler.get()];
4613 });
4614 return;
4615 }
4616
4617 CGRect snapshotRectInContentCoordinates = [self convertRect:rectInViewCoordinates toView:self._currentContentView];
4618 CGFloat imageScale = imageWidth / snapshotRectInContentCoordinates.size.width;
4619 CGFloat imageHeight = imageScale * snapshotRectInContentCoordinates.size.height;
4620 CGSize imageSize = CGSizeMake(imageWidth, imageHeight);
4621
4622#if HAVE(CORE_ANIMATION_RENDER_SERVER)(defined 1 && 1)
4623 // If we are parented and not hidden, and thus won't incur a significant penalty from paging in tiles, snapshot the view hierarchy directly.
4624 NSString *displayName = self.window.screen.displayConfiguration.name;
4625 if (displayName && !self.window.hidden) {
4626 TraceScope snapshotScope(RenderServerSnapshotStart, RenderServerSnapshotEnd);
4627 auto surface = WebCore::IOSurface::create(nullptr, WebCore::expandedIntSize(WebCore::FloatSize(imageSize)), WebCore::DestinationColorSpace::SRGB(), WebCore::IOSurface::Name::Snapshot);
4628 if (!surface) {
4629 completionHandler(nullptr);
4630 return;
4631 }
4632 if (afterScreenUpdates)
4633 [CATransaction synchronize];
4634 CGFloat imageScaleInViewCoordinates = imageWidth / rectInViewCoordinates.size.width;
4635 CATransform3D transform = CATransform3DMakeScale(imageScaleInViewCoordinates, imageScaleInViewCoordinates, 1);
4636 transform = CATransform3DTranslate(transform, -rectInViewCoordinates.origin.x, -rectInViewCoordinates.origin.y, 0);
4637
4638 CARenderServerSnapshot(MACH_PORT_NULL0, @{
4639 kCASnapshotMode: kCASnapshotModeLayer,
4640 kCASnapshotDisplayName: displayName,
4641 kCASnapshotContextId: @(self.layer.context.contextId),
4642 kCASnapshotLayerId: @(reinterpret_cast<uint64_t>(self.layer)),
4643 kCASnapshotDestination: (__bridge id)surface->surface(),
4644 kCASnapshotOriginX: @(0),
4645 kCASnapshotOriginY: @(0),
4646 kCASnapshotTransform: @(transform),
4647 kCASnapshotTimeOffset: @(0),
4648 });
4649
4650 completionHandler(WebCore::IOSurface::sinkIntoImage(WTF::move(surface)).get());
4651 return;
4652 }
4653#endif
4654
4655 if (_customContentView) {
4656 UIGraphicsBeginImageContextWithOptions(imageSize, YES__objc_yes, 1);
4657
4658 RetainPtr customContentView = _customContentView.get();
4659 [customContentView.get().backgroundColor set];
4660 UIRectFill(CGRectMake(0, 0, imageWidth, imageHeight));
4661
4662 RetainPtr context = UIGraphicsGetCurrentContext();
4663 CGContextTranslateCTM(context.get(), -snapshotRectInContentCoordinates.origin.x * imageScale, -snapshotRectInContentCoordinates.origin.y * imageScale);
4664 CGContextScaleCTM(context.get(), imageScale, imageScale);
4665 [customContentView.get().layer renderInContext:context.get()];
4666
4667 completionHandler([protect(UIGraphicsGetImageFromCurrentImageContext()) CGImage]);
4668
4669 UIGraphicsEndImageContext();
4670 return;
4671 }
4672
4673 _page->takeSnapshot(WebCore::enclosingIntRect(snapshotRectInContentCoordinates), WebCore::expandedIntSize(WebCore::FloatSize(imageSize)), WebKit::SnapshotOption::ExcludeDeviceScaleFactor, [completionHandler = makeBlockPtr(completionHandler)](CGImageRef image) {
4674 completionHandler(image);
4675 });
4676}
4677
4678- (void)_snapshotRect:(CGRect)rectInViewCoordinates intoImageOfWidth:(CGFloat)imageWidth completionHandler:(void(^)(CGImageRef))completionHandler
4679{
4680 [self _snapshotRectAfterScreenUpdates:NO__objc_no rectInViewCoordinates:rectInViewCoordinates intoImageOfWidth:imageWidth completionHandler:completionHandler];
4681}
4682
4683- (void)_overrideLayoutParametersWithMinimumLayoutSize:(CGSize)minimumLayoutSize maximumUnobscuredSizeOverride:(CGSize)maximumUnobscuredSizeOverride
4684{
4685 [self _overrideLayoutParametersWithMinimumLayoutSize:minimumLayoutSize minimumUnobscuredSizeOverride:minimumLayoutSize maximumUnobscuredSizeOverride:maximumUnobscuredSizeOverride];
4686}
4687
4688- (void)_overrideLayoutParametersWithMinimumLayoutSize:(CGSize)minimumLayoutSize minimumUnobscuredSizeOverride:(CGSize)minimumUnobscuredSizeOverride maximumUnobscuredSizeOverride:(CGSize)maximumUnobscuredSizeOverride
4689{
4690 LOG_WITH_STREAM(VisibleRects, stream << "-[WKWebView " << _page->identifier() << " _overrideLayoutParametersWithMinimumLayoutSize:" << WebCore::FloatSize(minimumLayoutSize) << " minimumUnobscuredSizeOverride:" << WebCore::FloatSize(minimumUnobscuredSizeOverride) << " maximumUnobscuredSizeOverride:" << WebCore::FloatSize(maximumUnobscuredSizeOverride) << "]")((void)0);
4691
4692 if (minimumLayoutSize.width < 0 || minimumLayoutSize.height < 0)
4693 RELEASE_LOG_FAULT(VisibleRects, "%s: Error: attempting to override layout parameters with negative width or height: %@", __PRETTY_FUNCTION__, NSStringFromCGSize(minimumLayoutSize))clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option"
clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic
ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic
ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang
::suppress]] __extension__({ os_log_t _log_tmp = (WebKit2LogVisibleRects
.osLogChannel); os_log_type_t _type_tmp = OS_LOG_TYPE_FAULT; if
(os_log_type_enabled(_log_tmp, _type_tmp)) { __extension__({
clang diagnostic push clang diagnostic ignored "-Wvla" clang
diagnostic error "-Wformat" _Static_assert(__builtin_constant_p
("%s: Error: attempting to override layout parameters with negative width or height: %@"
), "format/label/description argument must be a string constant"
); __attribute__((section("__TEXT,__oslogstring,cstring_literals"
),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG58"
) = "%s: Error: attempting to override layout parameters with negative width or height: %@"
; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf
[__builtin_os_log_format_buffer_size("%s: Error: attempting to override layout parameters with negative width or height: %@"
, __PRETTY_FUNCTION__, NSStringFromCGSize(minimumLayoutSize))
]; _os_log_fault_impl(&__dso_handle, _log_tmp, _type_tmp,
_os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf,
"%s: Error: attempting to override layout parameters with negative width or height: %@"
, __PRETTY_FUNCTION__, NSStringFromCGSize(minimumLayoutSize))
, (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); }
}) clang diagnostic pop
;
4694
4695 OverriddenLayoutParameters overriddenLayoutParameters;
4696
4697 overriddenLayoutParameters.viewLayoutSize = CGSizeMake(std::max<CGFloat>(0, minimumLayoutSize.width), std::max<CGFloat>(0, minimumLayoutSize.height));
4698
4699 ASSERT((!self.bounds.size.width || minimumUnobscuredSizeOverride.width <= self.bounds.size.width) && (!self.bounds.size.height || minimumUnobscuredSizeOverride.height <= self.bounds.size.height))((void)0);
4700 overriddenLayoutParameters.minimumUnobscuredSize = minimumUnobscuredSizeOverride;
4701
4702 ASSERT((!self.bounds.size.width || maximumUnobscuredSizeOverride.width <= self.bounds.size.width) && (!self.bounds.size.height || maximumUnobscuredSizeOverride.height <= self.bounds.size.height))((void)0);
4703 overriddenLayoutParameters.maximumUnobscuredSize = maximumUnobscuredSizeOverride;
4704
4705 _overriddenLayoutParameters = overriddenLayoutParameters;
4706
4707 if (self._shouldDispatchOverridenLayoutParametersImmediately) {
4708 [self _dispatchSetViewLayoutSize:WebCore::FloatSize(overriddenLayoutParameters.viewLayoutSize)];
4709 _page->setMinimumUnobscuredSize(WebCore::FloatSize(overriddenLayoutParameters.minimumUnobscuredSize));
4710 _page->setDefaultUnobscuredSize(WebCore::FloatSize(overriddenLayoutParameters.maximumUnobscuredSize));
4711 _page->setMaximumUnobscuredSize(WebCore::FloatSize(overriddenLayoutParameters.maximumUnobscuredSize));
4712 }
4713}
4714
4715- (BOOL)_shouldDispatchOverridenLayoutParametersImmediately
4716{
4717 return !self._shouldDeferGeometryUpdates && !_perProcessState.firstTransactionIDAfterObscuredInsetChange;
4718}
4719
4720- (void)_clearOverrideLayoutParameters
4721{
4722 _overriddenLayoutParameters = std::nullopt;
4723
4724 _page->setMinimumUnobscuredSize({ });
4725 _page->setDefaultUnobscuredSize({ });
4726 _page->setMaximumUnobscuredSize({ });
4727}
4728
4729static std::optional<WebCore::ViewportArguments> viewportArgumentsFromDictionary(NSDictionary<NSString *, NSString *> *viewportArgumentPairs, bool metaViewportInteractiveWidgetEnabled)
4730{
4731 if (!viewportArgumentPairs)
4732 return std::nullopt;
4733
4734 WebCore::ViewportArguments viewportArguments(WebCore::ViewportArguments::Type::ViewportMeta);
4735
4736 [viewportArgumentPairs enumerateKeysAndObjectsUsingBlock:makeBlockPtr([&] (NSString *key, NSString *value, BOOL* stop) {
4737 if (![key isKindOfClass:[NSString class]] || ![value isKindOfClass:[NSString class]])
4738 [NSException raise:NSInvalidArgumentException format:@"-[WKWebView _overrideViewportWithArguments:]: Keys and values must all be NSStrings."];
4739 String keyString = key;
4740 String valueString = value;
4741 WebCore::setViewportFeature(viewportArguments, keyString, valueString, metaViewportInteractiveWidgetEnabled, [] (WebCore::ViewportErrorCode, const String& errorMessage) {
4742 NSLog(@"-[WKWebView _overrideViewportWithArguments:]: Error parsing viewport argument: %s", errorMessage.utf8().data());
4743 });
4744 }).get()];
4745
4746 return viewportArguments;
4747}
4748
4749- (void)_overrideViewportWithArguments:(NSDictionary<NSString *, NSString *> *)arguments
4750{
4751 if (!_page)
4752 return;
4753
4754 _page->setOverrideViewportArguments(viewportArgumentsFromDictionary(arguments, protect(_page->preferences())->metaViewportInteractiveWidgetEnabled()));
4755}
4756
4757- (UIView *)_viewForFindUI
4758{
4759 return [self viewForZoomingInScrollView:[self scrollView]];
4760}
4761
4762- (void)_setOverlaidAccessoryViewsInset:(CGSize)inset
4763{
4764 [_customContentView web_setOverlaidAccessoryViewsInset:inset];
4765}
4766
4767- (void (^)(void))_retainActiveFocusedState
4768{
4769 ++_activeFocusedStateRetainCount;
4770 // FIXME: Use something like CompletionHandlerCallChecker to ensure that the returned block is called before it's released.
4771 return adoptNS([[weakSelf = WeakObjCPtr { self }] {
4772 RetainPtr strongSelf = weakSelf.get();
4773 if (!strongSelf)
4774 return;
4775 if (strongSelf->_activeFocusedStateRetainCount)
4776 --strongSelf->_activeFocusedStateRetainCount;
4777 } copy]).autorelease();
4778}
4779
4780- (void)_becomeFirstResponderWithSelectionMovingForward:(BOOL)selectingForward completionHandler:(void (^)(BOOL didBecomeFirstResponder))completionHandler
4781{
4782 decltype(completionHandler) completionHandlerCopy = nilnullptr;
4783 if (completionHandler)
4784 completionHandlerCopy = Block_copy(completionHandler)((__typeof(completionHandler))_Block_copy(((const void *)((const
void *)(completionHandler)))))
;
4785
4786 [_contentView _becomeFirstResponderWithSelectionMovingForward:selectingForward completionHandler:[completionHandlerCopy](BOOL didBecomeFirstResponder) {
4787 if (!completionHandlerCopy)
4788 return;
4789
4790 completionHandlerCopy(didBecomeFirstResponder);
4791 Block_release(completionHandlerCopy)_Block_release(((const void *)((const void *)(completionHandlerCopy
))))
;
4792 }];
4793}
4794
4795- (id)_snapshotLayerContentsForBackForwardListItem:(WKBackForwardListItem *)item
4796{
4797 if (_page->backForwardList().currentItem() == &item._item)
4798 _page->recordNavigationSnapshot(*protect(_page->backForwardList().currentItem()));
4799
4800 if (RefPtr viewSnapshot = item._item.snapshot())
4801 return viewSnapshot->asLayerContents();
4802
4803 return nilnullptr;
4804}
4805
4806- (NSArray *)_dataDetectionResults
4807{
4808#if ENABLE(DATA_DETECTION)(defined 1 && 1)
4809 return [_contentView _dataDetectionResults];
4810#else
4811 return nilnullptr;
4812#endif
4813}
4814
4815- (void)_accessibilityRetrieveRectsAtSelectionOffset:(NSInteger)offset withText:(NSString *)text completionHandler:(void (^)(NSArray<NSValue *> *rects))completionHandler
4816{
4817 [_contentView _accessibilityRetrieveRectsAtSelectionOffset:offset withText:text completionHandler:[capturedCompletionHandler = makeBlockPtr(completionHandler)] (const Vector<WebCore::SelectionGeometry>& selectionRects) {
4818 if (!capturedCompletionHandler)
4819 return;
4820 capturedCompletionHandler(createNSArray(selectionRects, [] (auto& rect) {
4821 return [NSValue valueWithCGRect:rect.rect()];
4822 }).get());
4823 }];
4824}
4825
4826- (void)_accessibilityStoreSelection
4827{
4828 [_contentView _accessibilityStoreSelection];
4829}
4830
4831- (void)_accessibilityClearSelection
4832{
4833 [_contentView _accessibilityClearSelection];
4834}
4835
4836- (void)_accessibilityRetrieveSpeakSelectionContent
4837{
4838 [_contentView accessibilityRetrieveSpeakSelectionContent];
4839}
4840
4841// This method is for subclasses to override.
4842// Currently it's only in TestRunnerWKWebView.
4843- (void)_accessibilityDidGetSpeakSelectionContent:(NSString *)content
4844{
4845}
4846
4847- (UIView *)_fullScreenPlaceholderView
4848{
4849#if ENABLE(FULLSCREEN_API)(defined 1 && 1)
4850 return [_fullScreenWindowController webViewPlaceholder];
4851#endif // ENABLE(FULLSCREEN_API)
4852 return nilnullptr;
4853}
4854
4855- (void)_willOpenAppLink
4856{
4857 if (_page)
4858 _page->willOpenAppLink();
4859}
4860
4861- (void)_isNavigatingToAppBoundDomain:(void(^)(BOOL))completionHandler
4862{
4863 _page->isNavigatingToAppBoundDomainTesting([completionHandler = makeBlockPtr(completionHandler)] (bool isAppBound) {
4864 completionHandler(isAppBound);
4865 });
4866}
4867
4868- (void)_isForcedIntoAppBoundMode:(void(^)(BOOL))completionHandler
4869{
4870 _page->isForcedIntoAppBoundModeTesting([completionHandler = makeBlockPtr(completionHandler)] (bool isForcedIntoAppBoundMode) {
4871 completionHandler(isForcedIntoAppBoundMode);
4872 });
4873}
4874
4875#if HAVE(UIFINDINTERACTION)(defined 1 && 1)
4876
4877- (BOOL)_findInteractionEnabled
4878{
4879 return _findInteractionEnabled;
4880}
4881
4882- (void)_setFindInteractionEnabled:(BOOL)enabled
4883{
4884 [self setFindInteractionEnabled:enabled];
4885}
4886
4887- (_UIFindInteraction *)_findInteraction
4888{
4889 return (_UIFindInteraction *)_findInteraction.get();
4890}
4891
4892- (UITextRange *)selectedTextRange
4893{
4894 return nilnullptr;
4895}
4896
4897ALLOW_DEPRECATED_IMPLEMENTATIONS_BEGINGCC diagnostic push GCC diagnostic ignored "-W" "deprecated-implementations"
4898- (NSInteger)offsetFromPosition:(UITextPosition *)from toPosition:(UITextPosition *)toPosition inDocument:(UITextSearchDocumentIdentifier)document
4899{
4900 return [_contentView offsetFromPosition:from toPosition:toPosition inDocument:document];
4901}
4902ALLOW_DEPRECATED_IMPLEMENTATIONS_ENDGCC diagnostic pop
4903
4904- (NSComparisonResult)compareFoundRange:(UITextRange *)fromRange toRange:(UITextRange *)toRange inDocument:(UITextSearchDocumentIdentifier)document
4905{
4906 return [_contentView compareFoundRange:fromRange toRange:toRange inDocument:document];
4907}
4908
4909- (void)performTextSearchWithQueryString:(NSString *)string usingOptions:(UITextSearchOptions *)options resultAggregator:(id<UITextSearchAggregator>)aggregator
4910{
4911 [[self _searchableObject] performTextSearchWithQueryString:string usingOptions:options resultAggregator:aggregator];
4912}
4913
4914- (void)replaceFoundTextInRange:(UITextRange *)range inDocument:(UITextSearchDocumentIdentifier)document withText:(NSString *)replacementText
4915{
4916 [_contentView replaceFoundTextInRange:range inDocument:document withText:replacementText];
4917}
4918
4919- (void)decorateFoundTextRange:(UITextRange *)range inDocument:(UITextSearchDocumentIdentifier)document usingStyle:(UITextSearchFoundTextStyle)style
4920{
4921 [_contentView decorateFoundTextRange:range inDocument:document usingStyle:style];
4922}
4923
4924- (void)scrollRangeToVisible:(UITextRange *)range inDocument:(UITextSearchDocumentIdentifier)document
4925{
4926 [_contentView scrollRangeToVisible:range inDocument:document];
4927}
4928
4929- (void)clearAllDecoratedFoundText
4930{
4931 [_contentView clearAllDecoratedFoundText];
4932}
4933
4934- (void)didBeginTextSearchOperation
4935{
4936 [_contentView didBeginTextSearchOperation];
4937}
4938
4939- (void)didEndTextSearchOperation
4940{
4941 [_contentView didEndTextSearchOperation];
4942}
4943
4944- (void)_requestRectForFoundTextRange:(UITextRange *)ranges completionHandler:(void (^)(CGRect rect))completionHandler
4945{
4946 [_contentView requestRectForFoundTextRange:ranges completionHandler:completionHandler];
4947}
4948
4949- (UIFindSession *)findInteraction:(UIFindInteraction *)interaction sessionForView:(UIView *)view
4950{
4951 auto findSession = adoptNS([[UITextSearchingFindSession alloc] initWithSearchableObject:[self _searchableObject]]);
4952 return findSession.autorelease();
4953}
4954
4955- (void)findInteraction:(UIFindInteraction *)interaction didBeginFindSession:(UIFindSession *)session
4956{
4957 if ([self _searchableObject] == _contentView.get())
4958 [_contentView didBeginTextSearchOperation];
4959}
4960
4961- (void)findInteraction:(UIFindInteraction *)interaction didEndFindSession:(UIFindSession *)session
4962{
4963 if ([self _searchableObject] == _contentView.get())
4964 [_contentView didEndTextSearchOperation];
4965}
4966
4967- (void)_addLayerForFindOverlay
4968{
4969 if (!_page || _perProcessState.pendingFindLayerID || _perProcessState.committedFindLayerID)
4970 return;
4971
4972 _page->addLayerForFindOverlay([weakSelf = WeakObjCPtr<WKWebView>(self)] (std::optional<WebCore::PlatformLayerIdentifier> layerID) {
4973 auto strongSelf = weakSelf.get();
4974 if (strongSelf)
4975 strongSelf->_perProcessState.pendingFindLayerID = layerID;
4976 });
4977}
4978
4979- (void)_removeLayerForFindOverlay
4980{
4981 if (!_page)
4982 return;
4983
4984 if (!_perProcessState.pendingFindLayerID && !_perProcessState.committedFindLayerID)
4985 return;
4986
4987 _perProcessState.pendingFindLayerID = std::nullopt;
4988 _perProcessState.committedFindLayerID = std::nullopt;
4989
4990 _page->removeLayerForFindOverlay([weakSelf = WeakObjCPtr<WKWebView>(self)] {
4991 auto strongSelf = weakSelf.get();
4992 if (!strongSelf)
4993 return;
4994
4995 [strongSelf _didRemoveLayerForFindOverlay];
4996 });
4997}
4998
4999- (CALayer *)_layerForFindOverlay
5000{
5001 if (!_page || !_perProcessState.committedFindLayerID)
5002 return nilnullptr;
5003
5004 if (RefPtr drawingArea = _page->drawingArea())
5005 return downcast<WebKit::RemoteLayerTreeDrawingAreaProxy>(*drawingArea).remoteLayerTreeHost().layerForID(*_perProcessState.committedFindLayerID);
5006
5007 return nilnullptr;
5008}
5009
5010#endif // HAVE(UIFINDINTERACTION)
5011
5012- (UITextInputTraits *)_textInputTraits
5013{
5014 if (self.usesStandardContentView)
5015 return [_contentView textInputTraitsForWebView];
5016
5017 return nilnullptr;
5018}
5019
5020@end // WKWebView (WKPrivateIOS)
5021
5022@implementation WKWebView (WKViewInternalIOS_SwiftNonObjCxxSupport)
5023
5024- (BOOL)_allowsMagnification
5025{
5026 return _allowsMagnification;
5027}
5028
5029- (void)_setAllowsMagnification:(BOOL)allowsMagnification
5030{
5031 _allowsMagnification = allowsMagnification;
5032 [_contentView _updateDoubleTapGestureRecognizerEnablement];
5033}
5034
5035@end
5036
5037#if ENABLE(FULLSCREEN_API)(defined 1 && 1)
5038
5039@implementation WKWebView (FullScreenAPI_Private)
5040
5041- (BOOL)hasFullScreenWindowController
5042{
5043 return !!_fullScreenWindowController;
5044}
5045
5046- (void)closeFullScreenWindowController
5047{
5048 if (!_fullScreenWindowController)
5049 return;
5050
5051 [_fullScreenWindowController close];
5052 _fullScreenWindowController = nullptr;
5053}
5054
5055@end
5056
5057@implementation WKWebView (FullScreenAPI_Internal)
5058
5059- (WKFullScreenWindowController *)fullScreenWindowController
5060{
5061 if (!_fullScreenWindowController)
5062 _fullScreenWindowController = adoptNS([[WKFullScreenWindowController alloc] initWithWebView:self]);
5063
5064 return _fullScreenWindowController.get();
5065}
5066
5067#if PLATFORM(VISION)(defined WTF_PLATFORM_VISION && WTF_PLATFORM_VISION)
5068
5069- (UIMenu *)fullScreenWindowSceneDimmingAction
5070{
5071 UIDeferredMenuElement *deferredMenu = [UIDeferredMenuElement elementWithUncachedProvider:[weakSelf = WeakObjCPtr<WKWebView>(self)] (void (^completion)(NSArray<UIMenuElement *> *)) {
5072 auto strongSelf = weakSelf.get();
5073 if (!strongSelf) {
5074 completion(@[ ]);
5075 return;
5076 }
5077
5078 UIAction *dimmingAction = [UIAction actionWithTitle:WEB_UI_STRING("Auto Dimming", "Menu item label to toggle automatic scene dimming in fullscreen (visionOS only)")WebCore::localizedString(((CFStringRef) __builtin___CFStringMakeConstantString
("" "Auto Dimming" "")))
.createNSString().get() image:[UIImage _systemImageNamed:@"circle.lefthalf.dotted.inset.half.filled"] identifier:nilnullptr handler:[weakSelf] (UIAction *) {
5079 auto strongSelf = weakSelf.get();
5080 if (!strongSelf)
5081 return;
5082
5083 [strongSelf->_fullScreenWindowController toggleSceneDimming];
5084 }];
5085 dimmingAction.state = [strongSelf->_fullScreenWindowController prefersSceneDimming] ? UIMenuElementStateOn : UIMenuElementStateOff;
5086
5087 completion(@[ dimmingAction ]);
5088 }];
5089
5090 return [UIMenu menuWithTitle:@"" image:nilnullptr identifier:nilnullptr options:UIMenuOptionsDisplayInline children:@[ deferredMenu ]];
5091}
5092
5093#endif // PLATFORM(VISION)
5094
5095@end
5096
5097
5098#if PLATFORM(VISION)(defined WTF_PLATFORM_VISION && WTF_PLATFORM_VISION)
5099@implementation WKWebView(WKPrivateVision)
5100- (NSString *)_defaultSTSLabel
5101{
5102 return nsStringNilIfNull(_page->defaultSpatialTrackingLabel()).autorelease();
5103}
5104
5105- (void)_setDefaultSTSLabel:(NSString *)defaultSTSLabel
5106{
5107 _page->setDefaultSpatialTrackingLabel(defaultSTSLabel);
5108}
5109
5110- (void)_enterExternalPlaybackForNowPlayingMediaSessionWithEnterCompletionHandler:(void (^)(UIViewController *nowPlayingViewController, NSError *error))enterHandler exitCompletionHandler:(void (^)(NSError *error))exitHandler
5111{
5112 if (!_page) {
5113 enterHandler(nilnullptr, [NSError errorWithDomain:WKErrorDomain code:WKErrorUnknown userInfo:nilnullptr]);
5114 exitHandler([NSError errorWithDomain:WKErrorDomain code:WKErrorUnknown userInfo:nilnullptr]);
5115 return;
5116 }
5117
5118 _page->enterExternalPlaybackForNowPlayingMediaSession([handler = makeBlockPtr(enterHandler)](bool entered, UIViewController *viewController) {
5119 if (entered)
5120 handler(viewController, nilnullptr);
5121 else
5122 handler(nilnullptr, createNSError(WKErrorUnknown).get());
5123 }, [handler = makeBlockPtr(exitHandler)](bool success) {
5124 handler(success ? nilnullptr : createNSError(WKErrorUnknown).get());
5125 });
5126}
5127
5128- (void)_exitExternalPlayback
5129{
5130 if (!_page)
5131 return;
5132
5133 _page->exitExternalPlayback();
5134}
5135@end
5136#endif
5137
5138#endif // ENABLE(FULLSCREEN_API)
5139
5140@implementation WKWebView (_WKWebViewPrintFormatter)
5141
5142- (Class)_printFormatterClass
5143{
5144 return [_WKWebViewPrintFormatter class];
5145}
5146
5147- (id <_WKWebViewPrintProvider>)_printProvider
5148{
5149 RetainPtr printProvider = _customContentView ? _customContentView.get() : _contentView.get();
5150 if ([printProvider conformsToProtocol:@protocol(_WKWebViewPrintProvider)])
5151 return (id<_WKWebViewPrintProvider>)printProvider.autorelease();
5152 return nilnullptr;
5153}
5154
5155@end
5156
5157#if ENABLE(TWO_PHASE_CLICKS)(defined 1 && 1)
5158
5159@implementation WKWebView (TwoPhaseClicks)
5160
5161- (void)_didNotHandleTapAsClick:(const WebCore::IntPoint&)point
5162{
5163 [_contentView _didNotHandleTapAsClick:point];
5164}
5165
5166- (void)_didHandleTapAsHover
5167{
5168 [_contentView _didHandleTapAsHover];
5169}
5170
5171- (void)_didCompleteSyntheticClick
5172{
5173 [_contentView _didCompleteSyntheticClick];
5174}
5175
5176- (void)_commitPotentialTapFailed
5177{
5178 [_contentView _commitPotentialTapFailed];
5179}
5180
5181- (void)_didGetTapHighlightGeometries:(WebKit::TapIdentifier)requestID color:(const WebCore::Color&)color quads:(const Vector<WebCore::FloatQuad>&)highlightedQuads topLeftRadius:(const WebCore::IntSize&)topLeftRadius topRightRadius:(const WebCore::IntSize&)topRightRadius bottomLeftRadius:(const WebCore::IntSize&)bottomLeftRadius bottomRightRadius:(const WebCore::IntSize&)bottomRightRadius nodeHasBuiltInClickHandling:(BOOL)nodeHasBuiltInClickHandling
5182{
5183 [_contentView _didGetTapHighlightForRequest:requestID color:color quads:highlightedQuads topLeftRadius:topLeftRadius topRightRadius:topRightRadius bottomLeftRadius:bottomLeftRadius bottomRightRadius:bottomRightRadius nodeHasBuiltInClickHandling:nodeHasBuiltInClickHandling];
5184}
5185
5186- (BOOL)_isPotentialTapInProgress
5187{
5188 return [_contentView isPotentialTapInProgress];
5189}
5190
5191- (void)_disableDoubleTapGesturesDuringTapIfNecessary:(WebKit::TapIdentifier)requestID
5192{
5193 [_contentView _disableDoubleTapGesturesDuringTapIfNecessary:requestID];
5194}
5195
5196- (void)_handleSmartMagnificationInformationForPotentialTap:(WebKit::TapIdentifier)requestID renderRect:(const WebCore::FloatRect&)renderRect fitEntireRect:(BOOL)fitEntireRect viewportMinimumScale:(double)viewportMinimumScale viewportMaximumScale:(double)viewportMaximumScale nodeIsRootLevel:(BOOL)nodeIsRootLevel nodeIsPluginElement:(BOOL)nodeIsPluginElement
5197{
5198 [_contentView _handleSmartMagnificationInformationForPotentialTap:requestID renderRect:renderRect fitEntireRect:fitEntireRect viewportMinimumScale:viewportMinimumScale viewportMaximumScale:viewportMaximumScale nodeIsRootLevel:nodeIsRootLevel nodeIsPluginElement:nodeIsPluginElement];
5199}
5200
5201@end
5202
5203#endif // ENABLE(TWO_PHASE_CLICKS)
5204
5205#undef WKWEBVIEW_RELEASE_LOG
5206
5207_WKTapHandlingResult wkTapHandlingResult(WebKit::TapHandlingResult result)
5208{
5209 switch (result) {
5210 case WebKit::TapHandlingResult::DidNotHandleTapAsClick:
5211 return _WKTapHandlingResultDidNotHandleTapAsClick;
5212 case WebKit::TapHandlingResult::NonMeaningfulClick:
5213 return _WKTapHandlingResultNonMeaningfulClick;
5214 case WebKit::TapHandlingResult::MeaningfulClick:
5215 return _WKTapHandlingResultMeaningfulClick;
5216 }
5217 ASSERT_NOT_REACHED()((void)0);
5218 return _WKTapHandlingResultDidNotHandleTapAsClick;
5219}
5220
5221#endif // PLATFORM(IOS_FAMILY)