Bug Summary

File:Source/WebKit/WebProcess/WebPage/Cocoa/WebPageCocoa.mm
Warning:line 630, column 19
Local variable 'object' is uncounted and unsafe

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple arm64-apple-macosx15.5.0 -Os -Wundef-prefix=TARGET_OS_ -Werror=undef-prefix -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name UnifiedSource75-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=15.5 -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/macOS-Safer-CPP-Checks-EWS/build/Source/WebKit -target-linker-version 1167.5 -fcoverage-compilation-dir=/Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/Source/WebKit -resource-dir /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/llvm-project/build/lib/clang/21 -dependency-file /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/scan-build-output/StaticAnalyzer/WebKit/WebKit/normal/arm64/UnifiedSource75-nonARC.d -skip-unused-modulemap-deps -MT dependencies -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.5.sdk -iquote /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release/WebKit.build/WebKit-generated-files.hmap -iquote /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release/WebKit.build/WebKit-project-headers.hmap -isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.5.sdk/usr/local/include -iframework /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.5.sdk/System/Library/PrivateFrameworks -iframework /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.5.sdk/System/Library/PrivateFrameworks -isystem /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitLibraries/SDKs/macosx15.0-additions.sdk/usr/local/include -include-pch /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/PrecompiledHeaders/SharedPrecompiledHeaders/6538014812039595989/WebKit2Prefix.h.gch -D _LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE -D CLANG_WEBKIT_BRANCH=1 -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/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release/WebKit.build/WebKit-own-target-headers.hmap -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release/WebKit.build/WebKit-all-target-headers.hmap -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/Release/include -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/Release/usr/local/include -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/Release/WebCore.framework/Versions/A/PrivateHeaders/ForwardingHeaders -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/Release/DerivedSources/WebKit -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/Release/usr/local/include/pal/graphics/WebGPU -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.5.sdk/usr/local/include/pal/graphics/WebGPU -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/Release/usr/local/include/WebKitAdditions -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.5.sdk/usr/local/include/WebKitAdditions -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/Release/usr/local/include/webrtc -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.5.sdk/usr/local/include/webrtc -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/Release/usr/local/include/webm -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.5.sdk/usr/local/include/webm -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/Source/WebKit -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release/WebKit.build/DerivedSources-normal/arm64 -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release/WebKit.build/DerivedSources/arm64 -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release/WebKit.build/DerivedSources -F/Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/Release -F/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.5.sdk/System/Library/Frameworks/WebKit.framework/Versions/A/Frameworks -D __STDC_WANT_LIB_EXT1__=1 -I/usr/local/include -stdlib=libc++ -internal-isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.5.sdk/usr/include/c++/v1 -internal-isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.5.sdk/usr/local/include -internal-isystem /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/llvm-project/build/lib/clang/21/include -internal-externc-isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.5.sdk/usr/include -internal-iframework /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.5.sdk/System/Library/Frameworks -internal-iframework /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.5.sdk/System/Library/SubFrameworks -internal-iframework /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.5.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=macosx-15.5.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/macOS-Safer-CPP-Checks-EWS/build/scan-build-output/StaticAnalyzer/WebKit/WebKit/normal/arm64/UnifiedSource75-nonARC.plist -x objective-c++ /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/Release/DerivedSources/WebKit/unified-sources/UnifiedSource75-nonARC.mm
1/*
2 * Copyright (C) 2016-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 "WebPage.h"
28
29#import "DrawingArea.h"
30#import "EditorState.h"
31#import "GPUProcessConnection.h"
32#import "InsertTextOptions.h"
33#import "InteractionInformationAtPosition.h"
34#import "LoadParameters.h"
35#import "MessageSenderInlines.h"
36#import "PDFPlugin.h"
37#import "PluginView.h"
38#import "PositionInformationForWebPage.h"
39#import "PrintInfo.h"
40#import "RemoteLayerTreeCommitBundle.h"
41#import "RemoteLayerTreeTransaction.h"
42#import "RemoteRenderingBackendProxy.h"
43#import "RemoteSnapshotRecorderProxy.h"
44#import "SharedBufferReference.h"
45#import "TextAnimationController.h"
46#import "UserMediaCaptureManager.h"
47#import "ViewGestureGeometryCollector.h"
48#import "WKAccessibilityWebPageObjectBase.h"
49#import "WebEventConversion.h"
50#import "WebFrame.h"
51#import "WebImage.h"
52#import "WebPageInternals.h"
53#import "WebPageProxyMessages.h"
54#import "WebPasteboardOverrides.h"
55#import "WebPaymentCoordinator.h"
56#import "WebPreferencesKeys.h"
57#import "WebProcess.h"
58#import "WebRemoteObjectRegistry.h"
59#import <WebCore/AXCrossProcessSearch.h>
60#import <WebCore/AXObjectCache.h>
61#import <WebCore/AXRemoteFrame.h>
62#import <WebCore/AXSearchManager.h>
63#import <WebCore/AccessibilityObject.h>
64#import <WebCore/AccessibilityScrollView.h>
65#import <WebCore/AnimationTimelinesController.h>
66#import <WebCore/Chrome.h>
67#import <WebCore/ChromeClient.h>
68#if ENABLE(CONTENT_CHANGE_OBSERVER)(defined 0 && 0)
69#import <WebCore/ContentChangeObserver.h>
70#endif
71#import <WebCore/DeprecatedGlobalSettings.h>
72#import <WebCore/DictionaryLookup.h>
73#import <WebCore/DocumentMarkerController.h>
74#import <WebCore/DocumentMarkers.h>
75#import <WebCore/DocumentQuirks.h>
76#import <WebCore/DocumentView.h>
77#import <WebCore/DragImage.h>
78#import <WebCore/Editing.h>
79#import <WebCore/EditingHTMLConverter.h>
80#import <WebCore/Editor.h>
81#import <WebCore/ElementAncestorIteratorInlines.h>
82#import <WebCore/EventHandler.h>
83#import <WebCore/EventNames.h>
84#import <WebCore/FixedContainerEdges.h>
85#import <WebCore/FocusController.h>
86#import <WebCore/FrameDestructionObserverInlines.h>
87#import <WebCore/FrameLoader.h>
88#import <WebCore/FrameView.h>
89#import <WebCore/GraphicsContextCG.h>
90#import <WebCore/HTMLAnchorElement.h>
91#import <WebCore/HTMLAreaElement.h>
92#import <WebCore/HTMLBodyElement.h>
93#import <WebCore/HTMLFrameOwnerElement.h>
94#import <WebCore/HTMLIFrameElement.h>
95#import <WebCore/HTMLImageElement.h>
96#import <WebCore/HTMLLabelElement.h>
97#import <WebCore/HTMLOListElement.h>
98#import <WebCore/HTMLPlugInElement.h>
99#import <WebCore/HTMLSelectElement.h>
100#import <WebCore/HTMLSummaryElement.h>
101#import <WebCore/HTMLTextAreaElement.h>
102#import <WebCore/HTMLTextFormControlElement.h>
103#import <WebCore/HTMLUListElement.h>
104#import <WebCore/HandleUserInputEventResult.h>
105#import <WebCore/HitTestResult.h>
106#import <WebCore/ImageOverlay.h>
107#import <WebCore/ImageUtilities.h>
108#import <WebCore/LegacyWebArchive.h>
109#import <WebCore/LocalFrameInlines.h>
110#import <WebCore/LocalFrameView.h>
111#import <WebCore/MIMETypeRegistry.h>
112#import <WebCore/MutableStyleProperties.h>
113#import <WebCore/NetworkExtensionContentFilter.h>
114#import <WebCore/NodeDocument.h>
115#import <WebCore/NodeHTMLConverter.h>
116#import <WebCore/NodeRenderStyle.h>
117#import <WebCore/NotImplemented.h>
118#import <WebCore/NowPlayingInfo.h>
119#import <WebCore/PaymentCoordinator.h>
120#import <WebCore/PlatformMediaSessionManager.h>
121#import <WebCore/PlatformMouseEvent.h>
122#import <WebCore/PrintContext.h>
123#import <WebCore/Range.h>
124#import <WebCore/RemoteFrame.h>
125#import <WebCore/RemoteFrameGeometryTransformer.h>
126#import <WebCore/RemoteFrameView.h>
127#import <WebCore/RemoteUserInputEventData.h>
128#import <WebCore/RenderBoxInlines.h>
129#import <WebCore/RenderElement.h>
130#import <WebCore/RenderLayer.h>
131#import <WebCore/RenderObjectInlines.h>
132#import <WebCore/RenderTheme.h>
133#import <WebCore/RenderedDocumentMarker.h>
134#import <WebCore/SVGImage.h>
135#import <WebCore/Settings.h>
136#import <WebCore/StylePropertiesInlines.h>
137#import <WebCore/TextIterator.h>
138#import <WebCore/TextPlaceholderElement.h>
139#import <WebCore/UTIRegistry.h>
140#import <WebCore/UTIUtilities.h>
141#import <WebCore/UserTypingGestureIndicator.h>
142#import <WebCore/WebAccessibilityObjectWrapperMac.h>
143#import <WebCore/markup.h>
144#import <pal/spi/cocoa/LaunchServicesSPI.h>
145#import <pal/spi/cocoa/NSAccessibilitySPI.h>
146#import <pal/spi/cocoa/QuartzCoreSPI.h>
147#import <wtf/CoroutineUtilities.h>
148#import <wtf/MonotonicTime.h>
149#import <wtf/TZoneMallocInlines.h>
150#import <wtf/cf/VectorCF.h>
151#import <wtf/cocoa/SpanCocoa.h>
152#import <wtf/spi/darwin/SandboxSPI.h>
153#import <wtf/text/StringToIntegerConversion.h>
154
155#if ENABLE(GPU_PROCESS)(defined 1 && 1) && PLATFORM(COCOA)(defined 1 && 1)
156#include "LibWebRTCCodecs.h"
157#endif
158
159#if PLATFORM(IOS)(defined WTF_PLATFORM_IOS && WTF_PLATFORM_IOS) || PLATFORM(VISION)(defined WTF_PLATFORM_VISION && WTF_PLATFORM_VISION)
160#import <WebCore/ParentalControlsContentFilter.h>
161#endif
162
163#if USE(EXTENSIONKIT)(defined USE_EXTENSIONKIT && USE_EXTENSIONKIT)
164#import "WKProcessExtension.h"
165#endif
166
167#if ENABLE(THREADED_ANIMATIONS)(defined 1 && 1)
168#import <WebCore/AcceleratedEffectStackUpdater.h>
169#endif
170
171#if HAVE(PDFKIT)(defined 1 && 1)
172#import "PDFKitSPI.h"
173#endif
174
175#if ENABLE(WK_WEB_EXTENSIONS)(defined 1 && 1)
176#include "WebExtensionControllerProxy.h"
177#endif
178
179#import "PDFKitSoftLink.h"
180
181#define WEBPAGE_RELEASE_LOG(channel, fmt, ...)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 = (WebKit2Logchannel
.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
("%p - [webPageID=%" "ll" "u" "] WebPage::" fmt), "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_LOG27"
) = "%p - [webPageID=%" "ll" "u" "] WebPage::" fmt; uint8_t _Alignas
(16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size
("%p - [webPageID=%" "ll" "u" "] WebPage::" fmt, this, m_identifier
.toUInt64(), ...)]; _os_log_impl(&__dso_handle, _log_tmp,
_type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf
, "%p - [webPageID=%" "ll" "u" "] WebPage::" fmt, this, m_identifier
.toUInt64(), ...), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic
pop ; }); } }) clang diagnostic pop
RELEASE_LOG(channel, "%p - [webPageID=%" PRIu64 "] WebPage::" fmt, this, m_identifier.toUInt64(), ##__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 = (WebKit2Logchannel
.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
("%p - [webPageID=%" "ll" "u" "] WebPage::" fmt), "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_LOG28"
) = "%p - [webPageID=%" "ll" "u" "] WebPage::" fmt; uint8_t _Alignas
(16) __attribute__((uninitialized)) _os_fmt_buf[__builtin_os_log_format_buffer_size
("%p - [webPageID=%" "ll" "u" "] WebPage::" fmt, this, m_identifier
.toUInt64(), ##__VA_ARGS__)]; _os_log_impl(&__dso_handle,
_log_tmp, _type_tmp, _os_fmt_str, (uint8_t *)__builtin_os_log_format
(_os_fmt_buf, "%p - [webPageID=%" "ll" "u" "] WebPage::" fmt,
this, m_identifier.toUInt64(), ##__VA_ARGS__), (uint32_t)sizeof
(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic
pop
182
183#if PLATFORM(COCOA)(defined 1 && 1)
184
185namespace WebKit {
186
187using namespace WebCore;
188
189// FIXME: Unclear if callers in this file are correctly choosing which of these two functions to use.
190
191String plainTextForContext(const SimpleRange& range)
192{
193 return WebCore::plainTextReplacingNoBreakSpace(range);
194}
195
196String plainTextForContext(const std::optional<SimpleRange>& range)
197{
198 return range ? plainTextForContext(*range) : emptyString();
199}
200
201String plainTextForDisplay(const SimpleRange& range)
202{
203 return WebCore::plainTextReplacingNoBreakSpace(range, { }, true);
204}
205
206String plainTextForDisplay(const std::optional<SimpleRange>& range)
207{
208 return range ? plainTextForDisplay(*range) : emptyString();
209}
210
211void WebPage::platformInitialize(const WebPageCreationParameters& parameters)
212{
213#if ENABLE(INITIALIZE_ACCESSIBILITY_ON_DEMAND)(defined 1 && 1)
214 bool shouldInitializeAccessibility = WebProcess::singleton().shouldInitializeAccessibility() || !parameters.store.getBoolValueForKey(WebPreferencesKey::enableAccessibilityOnDemandKey());
215#else
216 bool shouldInitializeAccessibility = false;
217#endif
218
219 platformInitializeAccessibility(shouldInitializeAccessibility ? ShouldInitializeNSAccessibility::Yes : ShouldInitializeNSAccessibility::No);
220
221#if ENABLE(MEDIA_STREAM)(defined 1 && 1)
222 protect(WebProcess::singleton().userMediaCaptureManager())->setupCaptureProcesses(parameters.shouldCaptureAudioInUIProcess, parameters.shouldCaptureAudioInGPUProcess, parameters.shouldCaptureVideoInUIProcess, parameters.shouldCaptureVideoInGPUProcess, parameters.shouldCaptureDisplayInUIProcess, parameters.shouldCaptureDisplayInGPUProcess,
223#if ENABLE(WEB_RTC)(defined 1 && 1)
224 m_page->settings().webRTCRemoteVideoFrameEnabled()
225#else
226 false
227#endif // ENABLE(WEB_RTC)
228 );
229#endif // ENABLE(MEDIA_STREAM)
230#if USE(LIBWEBRTC)(defined 1 && 1)
231 LibWebRTCCodecs::setCallbacks(m_page->settings().webRTCPlatformCodecsInGPUProcessEnabled(), m_page->settings().webRTCRemoteVideoFrameEnabled());
232 LibWebRTCCodecs::setWebRTCMediaPipelineAdditionalLoggingEnabled(m_page->settings().webRTCMediaPipelineAdditionalLoggingEnabled());
233#endif
234
235#if PLATFORM(MAC)(defined 1 && 1)
236 // In order to be able to block launchd on macOS, we need to eagerly open up a connection to CARenderServer here.
237 // This is because PDF rendering on macOS requires access to CARenderServer, unless unified PDF is enabled.
238 // In Lockdown mode we always block access to CARenderServer.
239 bool pdfRenderingRequiresRenderServerAccess = true;
240#if ENABLE(UNIFIED_PDF)(defined 1 && 1)
241 pdfRenderingRequiresRenderServerAccess = !m_page->settings().unifiedPDFEnabled();
242#endif
243 if (pdfRenderingRequiresRenderServerAccess && !WebProcess::singleton().isLockdownModeEnabled())
244 CARenderServerGetServerPort(nullptr);
245#endif // PLATFORM(MAC)
246
247#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
248 setInsertionPointColor(parameters.insertionPointColor);
249 setHardwareKeyboardState(parameters.hardwareKeyboardState);
250#endif
251 if (!WebProcess::singleton().isLockdownModeEnabled()) {
252 WebCore::setAdditionalSupportedImageTypes(parameters.additionalSupportedImageTypes);
253 WebCore::setImageSourceAllowableTypes(WebCore::allowableImageTypes());
254 }
255}
256
257#if HAVE(SANDBOX_STATE_FLAGS)(defined 1 && 1)
258void WebPage::setHasLaunchedWebContentProcess()
259{
260 static bool hasSetLaunchVariable = false;
261 if (!hasSetLaunchVariable) {
262 auto auditToken = WebProcess::singleton().auditTokenForSelf();
263#if USE(EXTENSIONKIT)(defined USE_EXTENSIONKIT && USE_EXTENSIONKIT)
264 if (WKProcessExtension.sharedInstance)
265 [WKProcessExtension.sharedInstance lockdownSandbox:@"2.0"];
266#endif
267 sandbox_enable_state_flag("local:WebContentProcessLaunched", *auditToken);
268 hasSetLaunchVariable = true;
269 }
270}
271#endif
272
273void WebPage::platformDidReceiveLoadParameters(const LoadParameters& parameters)
274{
275 WebCore::PublicSuffixStore::singleton().addPublicSuffix(parameters.publicSuffix);
276 m_dataDetectionReferenceDate = parameters.dataDetectionReferenceDate;
277}
278
279void WebPage::requestActiveNowPlayingSessionInfo(CompletionHandler<void(bool, WebCore::NowPlayingInfo&&)>&& completionHandler)
280{
281 if (RefPtr manager = mediaSessionManagerIfExists()) {
282 if (auto nowPlayingInfo = manager->nowPlayingInfo()) {
283 bool registeredAsNowPlayingApplication = manager->registeredAsNowPlayingApplication();
284 completionHandler(registeredAsNowPlayingApplication, WTF::move(*nowPlayingInfo));
285 return;
286 }
287 }
288
289 completionHandler(false, { });
290}
291
292#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
293bool WebPage::shouldUsePDFPlugin(const String& contentType, StringView path) const
294{
295#if ENABLE(PDFJS)(defined 0 && 0)
296 if (corePage()->settings().pdfJSViewerEnabled())
297 return false;
298#endif
299
300 bool pluginEnabled = false;
301#if ENABLE(LEGACY_PDFKIT_PLUGIN)(defined ENABLE_LEGACY_PDFKIT_PLUGIN && ENABLE_LEGACY_PDFKIT_PLUGIN
)
302 pluginEnabled |= pdfPluginEnabled() && PDFPlugin::pdfKitLayerControllerIsAvailable();
303#endif
304#if ENABLE(UNIFIED_PDF)(defined 1 && 1)
305 pluginEnabled |= corePage()->settings().unifiedPDFEnabled();
306#endif
307 if (!pluginEnabled)
308 return false;
309
310 return MIMETypeRegistry::isPDFMIMEType(contentType) || (contentType.isEmpty() && path.endsWithIgnoringASCIICase(".pdf"_s));
311}
312#endif
313
314void WebPage::performDictionaryLookupAtLocation(const FloatPoint& floatPoint)
315{
316#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
317 if (RefPtr pluginView = mainFramePlugIn()) {
318 if (pluginView->performDictionaryLookupAtLocation(floatPoint))
319 return;
320 }
321#endif
322
323 RefPtr localMainFrame = protect(corePage())->localMainFrame();
324 if (!localMainFrame)
325 return;
326 // Find the frame the point is over.
327 constexpr OptionSet<HitTestRequest::Type> hitType { HitTestRequest::Type::ReadOnly, HitTestRequest::Type::Active, HitTestRequest::Type::DisallowUserAgentShadowContent, HitTestRequest::Type::AllowChildFrameContent };
328 auto result = localMainFrame->eventHandler().hitTestResultAtPoint(protect(localMainFrame->view())->windowToContents(roundedIntPoint(floatPoint)), hitType);
329
330 RefPtr frame = result.innerNonSharedNode() ? result.innerNonSharedNode()->document().frame() : corePage()->focusController().focusedOrMainFrame();
331 if (!frame)
332 return;
333
334 auto rangeResult = DictionaryLookup::rangeAtHitTestResult(result);
335 if (!rangeResult)
336 return;
337
338 performDictionaryLookupForRange(*frame, *rangeResult, TextIndicatorPresentationTransition::Bounce);
339}
340
341void WebPage::performDictionaryLookupForSelection(LocalFrame& frame, const VisibleSelection& selection, TextIndicatorPresentationTransition presentationTransition)
342{
343 auto range = DictionaryLookup::rangeForSelection(selection);
344 if (!range)
345 return;
346
347 performDictionaryLookupForRange(frame, *range, presentationTransition);
348}
349
350void WebPage::performDictionaryLookupForRange(LocalFrame& frame, const SimpleRange& range, TextIndicatorPresentationTransition presentationTransition)
351{
352 send(Messages::WebPageProxy::DidPerformDictionaryLookup(dictionaryPopupInfoForRange(frame, range, presentationTransition)));
353}
354
355DictionaryPopupInfo WebPage::dictionaryPopupInfoForRange(LocalFrame& frame, const SimpleRange& range, TextIndicatorPresentationTransition presentationTransition)
356{
357 Ref editor = frame.editor();
358 editor->setIsGettingDictionaryPopupInfo(true);
359
360 if (plainText(range).find(deprecatedIsNotSpaceOrNewline) == notFound) {
361 editor->setIsGettingDictionaryPopupInfo(false);
362 return { };
363 }
364
365 auto quads = RenderObject::absoluteTextQuads(range);
366 if (quads.isEmpty()) {
367 editor->setIsGettingDictionaryPopupInfo(false);
368 return { };
369 }
370
371 DictionaryPopupInfo dictionaryPopupInfo;
372
373 IntRect rangeRect = protect(frame.view())->contentsToWindow(quads[0].enclosingBoundingBox());
374
375 const CheckedPtr style = protect(range.startContainer())->renderStyle();
376 float scaledAscent = style ? style->metricsOfPrimaryFont().intAscent() * pageScaleFactor() : 0;
377 dictionaryPopupInfo.origin = FloatPoint(rangeRect.x(), rangeRect.y() + scaledAscent);
378
379#if PLATFORM(MAC)(defined 1 && 1)
380 auto attributedString = editingAttributedString(range, { }).nsAttributedString();
381 auto scaledAttributedString = adoptNS([[NSMutableAttributedString alloc] initWithString:[attributedString string]]);
382 NSFontManager *fontManager = [NSFontManager sharedFontManager];
383 [attributedString enumerateAttributesInRange:NSMakeRange(0, [attributedString length]) options:0 usingBlock:^(NSDictionary *attributes, NSRange range, BOOL *stop) {
384 RetainPtr<NSMutableDictionary> scaledAttributes = adoptNS([attributes mutableCopy]);
385 RetainPtr<NSFont> font = [scaledAttributes objectForKey:NSFontAttributeName];
386 if (font)
387 font = [fontManager convertFont:font.get() toSize:font.get().pointSize * pageScaleFactor()];
388 if (font)
389 [scaledAttributes setObject:font.get() forKey:NSFontAttributeName];
390 [scaledAttributedString addAttributes:scaledAttributes.get() range:range];
391 }];
392#endif // PLATFORM(MAC)
393
394 OptionSet<TextIndicatorOption> indicatorOptions { TextIndicatorOption::UseBoundingRectAndPaintAllContentForComplexRanges };
395 if (ImageOverlay::isInsideOverlay(range))
396 indicatorOptions.add({ TextIndicatorOption::PaintAllContent, TextIndicatorOption::PaintBackgrounds });
397
398 if (presentationTransition == TextIndicatorPresentationTransition::BounceAndCrossfade)
399 indicatorOptions.add(TextIndicatorOption::IncludeSnapshotWithSelectionHighlight);
400
401 auto textIndicator = TextIndicator::createWithRange(range, indicatorOptions, presentationTransition);
402 if (!textIndicator) {
403 editor->setIsGettingDictionaryPopupInfo(false);
404 return dictionaryPopupInfo;
405 }
406
407 dictionaryPopupInfo.textIndicator = textIndicator;
408#if PLATFORM(MAC)(defined 1 && 1)
409#if ENABLE(LEGACY_PDFKIT_PLUGIN)(defined ENABLE_LEGACY_PDFKIT_PLUGIN && ENABLE_LEGACY_PDFKIT_PLUGIN
)
410 dictionaryPopupInfo.platformData.attributedString = WebCore::AttributedString::fromNSAttributedString(scaledAttributedString);
411#else
412 dictionaryPopupInfo.text = [scaledAttributedString string];
413#endif
414
415#elif PLATFORM(MACCATALYST)(defined WTF_PLATFORM_MACCATALYST && WTF_PLATFORM_MACCATALYST
)
416#if ENABLE(LEGACY_PDFKIT_PLUGIN)(defined ENABLE_LEGACY_PDFKIT_PLUGIN && ENABLE_LEGACY_PDFKIT_PLUGIN
)
417 dictionaryPopupInfo.platformData.attributedString = WebCore::AttributedString::fromNSAttributedString(adoptNS([[NSMutableAttributedString alloc] initWithString:plainText(range).createNSString().get()]));
418#else
419 dictionaryPopupInfo.text = plainText(range);
420#endif
421
422#endif
423
424 editor->setIsGettingDictionaryPopupInfo(false);
425 return dictionaryPopupInfo;
426}
427
428void WebPage::insertDictatedTextAsync(const String& text, const EditingRange& replacementEditingRange, const Vector<WebCore::DictationAlternative>& dictationAlternativeLocations, InsertTextOptions&& options)
429{
430 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
431 if (!frame)
432 return;
433
434 if (replacementEditingRange.location != notFound) {
435 auto replacementRange = EditingRange::toRange(*frame, replacementEditingRange);
436 if (replacementRange)
437 protect(frame->selection())->setSelection(VisibleSelection { *replacementRange });
438 }
439
440 if (options.registerUndoGroup)
441 send(Messages::WebPageProxy::RegisterInsertionUndoGrouping { });
442
443 RefPtr<Element> focusedElement = frame->document() ? frame->document()->focusedElement() : nullptr;
444 if (focusedElement && options.shouldSimulateKeyboardInput)
445 focusedElement->dispatchEvent(Event::create(eventNames().keydownEvent, Event::CanBubble::Yes, Event::IsCancelable::Yes));
446
447 if (frame->editor().hasComposition())
448 return;
449
450 protect(frame->editor())->insertDictatedText(text, dictationAlternativeLocations, nullptr /* triggeringEvent */);
451
452 if (focusedElement && options.shouldSimulateKeyboardInput) {
453 focusedElement->dispatchEvent(Event::create(eventNames().keyupEvent, Event::CanBubble::Yes, Event::IsCancelable::Yes));
454 focusedElement->dispatchEvent(Event::create(eventNames().changeEvent, Event::CanBubble::Yes, Event::IsCancelable::Yes));
455 }
456}
457
458void WebPage::addDictationAlternative(const String& text, DictationContext context, CompletionHandler<void(bool)>&& completion)
459{
460 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
461 if (!frame)
462 return;
463
464 RefPtr document = frame->document();
465 if (!document) {
466 completion(false);
467 return;
468 }
469
470 auto selection = frame->selection().selection();
471 RefPtr editableRoot = selection.rootEditableElement();
472 if (!editableRoot) {
473 completion(false);
474 return;
475 }
476
477 auto firstEditablePosition = firstPositionInNode(editableRoot.get());
478 auto selectionEnd = selection.end();
479 auto searchRange = makeSimpleRange(firstEditablePosition, selectionEnd);
480 if (!searchRange) {
481 completion(false);
482 return;
483 }
484
485 auto targetOffset = characterCount(*searchRange);
486 targetOffset -= std::min<uint64_t>(targetOffset, text.length());
487 auto matchRange = findClosestPlainText(*searchRange, text, { FindOption::Backwards, FindOption::DoNotRevealSelection }, targetOffset);
488 if (matchRange.collapsed()) {
489 completion(false);
490 return;
491 }
492
493 protect(document->markers())->addMarker(matchRange, DocumentMarkerType::DictationAlternatives, { DocumentMarker::DictationData { context, text } });
494 completion(true);
495}
496
497void WebPage::dictationAlternativesAtSelection(CompletionHandler<void(Vector<DictationContext>&&)>&& completion)
498{
499 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
500 if (!frame)
501 return;
502
503 RefPtr document = frame->document();
504 if (!document) {
505 completion({ });
506 return;
507 }
508
509 auto selection = frame->selection().selection();
510 auto expandedSelectionRange = VisibleSelection { selection.visibleStart().previous(CannotCrossEditingBoundary), selection.visibleEnd().next(CannotCrossEditingBoundary) }.range();
511 if (!expandedSelectionRange) {
512 completion({ });
513 return;
514 }
515
516 auto markers = protect(document->markers())->markersInRange(*expandedSelectionRange, DocumentMarkerType::DictationAlternatives);
517 auto contexts = WTF::compactMap(markers, [](auto& marker) -> std::optional<DictationContext> {
518 if (std::holds_alternative<DocumentMarker::DictationData>(marker->data()))
519 return std::get<DocumentMarker::DictationData>(marker->data()).context;
520 return std::nullopt;
521 });
522 completion(WTF::move(contexts));
523}
524
525void WebPage::clearDictationAlternatives(Vector<DictationContext>&& contexts)
526{
527 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
528 if (!frame)
529 return;
530
531 RefPtr document = frame->document();
532 if (!document)
533 return;
534
535 HashSet<DictationContext> setOfContextsToRemove;
536 setOfContextsToRemove.reserveInitialCapacity(contexts.size());
537 for (auto context : contexts)
538 setOfContextsToRemove.add(context);
539
540 auto documentRange = makeRangeSelectingNodeContents(*document);
541 protect(document->markers())->filterMarkers(documentRange, [&] (auto& marker) {
542 if (!std::holds_alternative<DocumentMarker::DictationData>(marker.data()))
543 return FilterMarkerResult::Keep;
544 return setOfContextsToRemove.contains(std::get<WebCore::DocumentMarker::DictationData>(marker.data()).context) ? FilterMarkerResult::Remove : FilterMarkerResult::Keep;
545 }, DocumentMarkerType::DictationAlternatives);
546}
547
548void WebPage::accessibilityTransferRemoteToken(RetainPtr<NSData> remoteToken)
549{
550 send(Messages::WebPageProxy::RegisterWebProcessAccessibilityToken(span(remoteToken.get())));
551}
552
553void WebPage::accessibilityManageRemoteElementStatus(bool registerStatus, int processIdentifier)
554{
555#if PLATFORM(MAC)(defined 1 && 1)
556 if (registerStatus)
557 [NSAccessibilityRemoteUIElement registerRemoteUIProcessIdentifier:processIdentifier];
558 else
559 [NSAccessibilityRemoteUIElement unregisterRemoteUIProcessIdentifier:processIdentifier];
560#else
561 UNUSED_PARAM(registerStatus)(void)registerStatus;
562 UNUSED_PARAM(processIdentifier)(void)processIdentifier;
563#endif
564}
565
566void WebPage::bindRemoteAccessibilityFrames(int processIdentifier, WebCore::FrameIdentifier frameID, WebCore::AccessibilityRemoteToken dataToken, CompletionHandler<void(WebCore::AccessibilityRemoteToken, int)>&& completionHandler)
567{
568 RefPtr webFrame = WebProcess::singleton().webFrame(frameID);
569 if (!webFrame) {
570 ASSERT_NOT_REACHED()((void)0);
571 return completionHandler({ }, 0);
572 }
573
574 RefPtr coreLocalFrame = webFrame->coreLocalFrame();
575 if (!coreLocalFrame) {
576 ASSERT_NOT_REACHED()((void)0);
577 return completionHandler({ }, 0);
578 }
579
580 if (!coreLocalFrame->contentRenderer()) {
581 ASSERT_NOT_REACHED()((void)0);
582 return completionHandler({ }, 0);
583 }
584
585 registerRemoteFrameAccessibilityTokens(processIdentifier, dataToken, frameID);
586
587 // Get our remote token data and send back to the RemoteFrame.
588#if PLATFORM(MAC)(defined 1 && 1)
589 completionHandler({ makeVector(accessibilityRemoteTokenData().get()) }, getpid());
590#else
591 completionHandler({ dataToken }, getpid());
592#endif
593}
594
595void WebPage::resolveAccessibilityHitTestForTesting(WebCore::FrameIdentifier frameID, const WebCore::IntPoint& point, CompletionHandler<void(String)>&& completionHandler)
596{
597 RefPtr webFrame = WebProcess::singleton().webFrame(frameID);
598 if (!webFrame)
599 return completionHandler("NULL"_s);
600#if PLATFORM(MAC)(defined 1 && 1)
601 if (RetainPtr coreObject = [m_mockAccessibilityElement accessibilityRootObjectWrapper:protect(webFrame->coreLocalFrame()).get()]) {
602 if (RetainPtr hitTestResult = [coreObject accessibilityHitTest:point]) {
603 ALLOW_DEPRECATED_DECLARATIONS_BEGINGCC diagnostic push GCC diagnostic ignored "-W" "deprecated-declarations"
604 completionHandler([hitTestResult accessibilityAttributeValue:@"AXInfoStringForTesting"]);
605 ALLOW_DEPRECATED_DECLARATIONS_ENDGCC diagnostic pop
606 return;
607 }
608 }
609#endif
610 UNUSED_PARAM(point)(void)point;
611 completionHandler("NULL"_s);
612}
613
614#if PLATFORM(MAC)(defined 1 && 1)
615// Creates an AccessibilityRemoteToken from an accessibility wrapper.
616// Returns std::nullopt if the wrapper is nil or token creation fails.
617static std::optional<WebCore::AccessibilityRemoteToken> tokenFromWrapper(id wrapper)
618{
619 if (!wrapper)
620 return std::nullopt;
621 if (RetainPtr tokenData = [NSAccessibilityRemoteUIElement remoteTokenForLocalUIElement:wrapper])
622 return WebCore::AccessibilityRemoteToken { makeVector(tokenData.get()) };
623 return std::nullopt;
624}
625
626static Vector<WebCore::AccessibilityRemoteToken> convertSearchResultsToRemoteTokens(WebCore::AccessibilitySearchResults&& searchResults)
627{
628 Vector<WebCore::AccessibilityRemoteToken> results;
629 for (auto& result : searchResults) {
630 if (auto* object = result.objectIfLocalResult()) {
Local variable 'object' is uncounted and unsafe
631 if (auto token = tokenFromWrapper(protect(object->wrapper())))
632 results.append(WTF::move(*token));
633 } else if (result.isRemote())
634 results.append(*result.remoteToken());
635 }
636 return results;
637}
638
639void WebPage::performAccessibilitySearchInRemoteFrame(WebCore::FrameIdentifier frameID, WebCore::AccessibilitySearchCriteriaIPC criteria, CompletionHandler<void(Vector<WebCore::AccessibilityRemoteToken>&&)>&& completionHandler)
640{
641 AX_ASSERT(isMainRunLoop())((void)0);
642
643 RefPtr webFrame = WebProcess::singleton().webFrame(frameID);
644 RefPtr coreFrame = webFrame ? webFrame->coreLocalFrame() : nullptr;
645 RefPtr document = coreFrame ? coreFrame->document() : nullptr;
646 CheckedPtr cache = document ? document->axObjectCache() : nullptr;
647 // Get the web area for this frame as the anchor object.
648 RefPtr webArea = cache ? cache->rootObjectForFrame(*coreFrame) : nullptr;
649 if (!webArea) {
650 completionHandler({ });
651 return;
652 }
653
654 // Convert IPC criteria to local criteria with this frame's web area as anchor.
655 auto localCriteria = criteria.toSearchCriteria(webArea.get());
656
657 // Since this frame is being searched by a parent frame (via performAccessibilitySearchInRemoteFrame),
658 // only search this frame and its nested child frames - do NOT coordinate with the parent.
659 // The parent already handles its own elements and will merge our results appropriately.
660 auto searchResults = WebCore::performSearchWithCrossProcessCoordination(*webArea, WTF::move(localCriteria));
661
662 completionHandler(convertSearchResultsToRemoteTokens(WTF::move(searchResults)));
663}
664
665void WebPage::continueAccessibilitySearchInParentFrame(WebCore::FrameIdentifier childFrameID, WebCore::AccessibilitySearchCriteriaIPC criteria, CompletionHandler<void(Vector<WebCore::AccessibilityRemoteToken>&&)>&& completionHandler)
666{
667 // Get the main frame's document and AXObjectCache for this (parent) process.
668 RefPtr coreFrame = m_mainFrame->coreLocalFrame();
669 RefPtr document = coreFrame ? coreFrame->document() : nullptr;
670 CheckedPtr cache = document ? document->axObjectCache() : nullptr;
671 if (!cache) {
672 completionHandler({ });
673 return;
674 }
675
676 // Find the AXRemoteFrame via the frame tree and AccessibilityScrollView.
677 RefPtr remoteFrame = dynamicDowncast<WebCore::RemoteFrame>(coreFrame->tree().descendantByFrameID(childFrameID));
678 RefPtr remoteFrameView = remoteFrame ? remoteFrame->view() : nullptr;
679 RefPtr scrollView = dynamicDowncast<WebCore::AccessibilityScrollView>(cache->get(remoteFrameView.get()));
680 RefPtr childRemoteFrame = scrollView ? scrollView->remoteFrame() : nullptr;
681
682 if (!childRemoteFrame) {
683 completionHandler({ });
684 return;
685 }
686
687 unsigned originalLimit = criteria.resultsLimit;
688 // Get the web area as the anchor and use the child remote frame as the start object.
689 RefPtr webArea = cache->rootObjectForFrame(*coreFrame);
690 if (!webArea) {
691 completionHandler({ });
692 return;
693 }
694
695 // Create local criteria with webArea as anchor and AXRemoteFrame as start object.
696 auto localCriteria = criteria.toSearchCriteria(webArea.get());
697 localCriteria.startObject = childRemoteFrame.get();
698
699 auto stream = WebCore::AXSearchManager().findMatchingObjectsAsStream(WTF::move(localCriteria));
700 // Perform cross-process search coordination if there are nested remote frames in this process.
701 // Pass childFrameID as the excluded frame to prevent re-searching the frame that requested continuation.
702 auto searchResults = WebCore::performCrossProcessSearch(WTF::move(stream), criteria, webArea->treeID(), originalLimit, childFrameID);
703
704 completionHandler(convertSearchResultsToRemoteTokens(WTF::move(searchResults)));
705}
706#endif // PLATFORM(MAC)
707
708#if PLATFORM(MAC)(defined 1 && 1)
709void WebPage::getAccessibilityWebProcessDebugInfo(CompletionHandler<void(WebCore::AXDebugInfo)>&& completionHandler)
710{
711 if (!AXObjectCache::isAppleInternalInstall()) {
712 completionHandler({ });
713 return;
714 }
715
716 bool isAXThreadInitialized = false;
717#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)(defined 1 && 1)
718 isAXThreadInitialized = WebCore::AXObjectCache::isAXThreadInitialized();
719#endif
720 Vector<String> warnings;
721
722 RefPtr focusedFrame = [m_mockAccessibilityElement focusedLocalFrame];
723 RefPtr document = focusedFrame ? focusedFrame->document() : nullptr;
724
725 if (document) {
726 if (CheckedPtr cache = document->axObjectCache()) {
727 auto treeData = cache->treeData();
728 warnings = WTF::move(treeData.warnings);
729 completionHandler({ WebCore::AXObjectCache::accessibilityEnabled(), isAXThreadInitialized, WTF::move(treeData.liveTree), WTF::move(treeData.isolatedTree), WTF::move(warnings), [m_mockAccessibilityElement remoteTokenHash], [accessibilityRemoteTokenData() hash] });
730 return;
731 }
732 warnings.append("No AXObjectCache"_s);
733 } else if (!focusedFrame)
734 warnings.append("No focused LocalFrame found"_s);
735 else
736 warnings.append("Focused LocalFrame has no document"_s);
737
738 completionHandler({ WebCore::AXObjectCache::accessibilityEnabled(), isAXThreadInitialized, emptyString(), emptyString(), WTF::move(warnings), [m_mockAccessibilityElement remoteTokenHash], [accessibilityRemoteTokenData() hash] });
739}
740
741#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)(defined 1 && 1)
742void WebPage::clearAccessibilityIsolatedTree()
743{
744 if (RefPtr page = m_page)
745 page->clearAccessibilityIsolatedTree();
746}
747#endif
748
749#endif // PLATFORM(MAC)
750
751#if ENABLE(APPLE_PAY)(defined 1 && 1)
752WebPaymentCoordinator* WebPage::paymentCoordinator()
753{
754 if (!m_page)
755 return nullptr;
756 return dynamicDowncast<WebPaymentCoordinator>(m_page->paymentCoordinator().client());
757}
758#endif
759
760void WebPage::getContentsAsAttributedString(CompletionHandler<void(const WebCore::AttributedString&)>&& completionHandler)
761{
762 RefPtr localFrame = protect(corePage())->localMainFrame();
763 completionHandler(localFrame ? attributedString(makeRangeSelectingNodeContents(*protect(localFrame->document())), IgnoreUserSelectNone::No) : AttributedString { });
764}
765
766void WebPage::setRemoteObjectRegistry(WebRemoteObjectRegistry* registry)
767{
768 m_remoteObjectRegistry = registry;
769}
770
771WebRemoteObjectRegistry* WebPage::remoteObjectRegistry()
772{
773 return m_remoteObjectRegistry.get();
774}
775
776void WebPage::updateMockAccessibilityElementAfterCommittingLoad()
777{
778 RefPtr mainFrame = dynamicDowncast<WebCore::LocalFrame>(this->mainFrame());
779 RefPtr document = mainFrame ? mainFrame->document() : nullptr;
780 [m_mockAccessibilityElement setHasMainFramePlugin:document ? document->isPluginDocument() : false];
781}
782
783void WebPage::getProcessDisplayName(CompletionHandler<void(String&&)>&& completionHandler)
784{
785#if PLATFORM(MAC)(defined 1 && 1)
786#if ENABLE(SET_WEBCONTENT_PROCESS_INFORMATION_IN_NETWORK_PROCESS)(defined 1 && 1)
787 WebProcess::singleton().getProcessDisplayName(WTF::move(completionHandler));
788#else
789 completionHandler(adoptCF((CFStringRef)_LSCopyApplicationInformationItem(kLSDefaultSessionID, _LSGetCurrentApplicationASN(), _kLSDisplayNameKey)).get());
790#endif
791#else
792 completionHandler({ });
793#endif
794}
795
796static bool rendererIsTransparentOrFullyClipped(const RenderObject& renderer)
797{
798 CheckedPtr enclosingLayer = renderer.enclosingLayer();
799 if (enclosingLayer && enclosingLayer->isTransparentRespectingParentFrames())
800 return true;
801
802 return renderer.hasEmptyVisibleRectRespectingParentFrames();
803}
804
805bool WebPage::isTransparentOrFullyClipped(const Node& node) const
806{
807 CheckedPtr renderer = node.renderer();
808 if (!renderer)
809 return false;
810 return rendererIsTransparentOrFullyClipped(*renderer);
811}
812
813static bool selectionIsTransparentOrFullyClipped(const VisibleSelection& selection)
814{
815 RefPtr startContainer = selection.start().containerNode();
816 if (!startContainer)
817 return false;
818
819 RefPtr endContainer = selection.end().containerNode();
820 if (!endContainer)
821 return false;
822
823 CheckedPtr startRenderer = startContainer->renderer();
824 if (!startRenderer)
825 return false;
826
827 CheckedPtr endRenderer = endContainer->renderer();
828 if (!endRenderer)
829 return false;
830
831 if (!rendererIsTransparentOrFullyClipped(*startRenderer))
832 return false;
833
834 return startRenderer == endRenderer || rendererIsTransparentOrFullyClipped(*endRenderer);
835}
836
837static void convertContentToRootView(const LocalFrameView& view, Vector<SelectionGeometry>& geometries)
838{
839 for (auto& geometry : geometries)
840 geometry.setQuad(view.contentsToRootView(geometry.quad()));
841}
842
843void WebPage::getPlatformEditorStateCommon(LocalFrame& frame, EditorState& result) const
844{
845 if (!result.hasPostLayoutAndVisualData())
846 return;
847
848 const auto& selection = frame.selection().selection();
849
850 if (selection.isNone())
851 return;
852
853 ASSERT(frame.view())((void)0);
854 Ref view = *frame.view();
855
856 auto& postLayoutData = *result.postLayoutData;
857 auto& visualData = *result.visualData;
858
859 if (result.isContentEditable) {
860 if (auto editingStyle = EditingStyle::styleAtSelectionStart(selection, false, EditingStyle::PropertiesToInclude::PostLayoutProperties)) {
861 if (editingStyle->fontWeightIsBold())
862 postLayoutData.typingAttributes.add(TypingAttribute::Bold);
863
864 if (editingStyle->fontStyleIsItalic())
865 postLayoutData.typingAttributes.add(TypingAttribute::Italics);
866
867 if (editingStyle->webkitTextDecorationsInEffectIsUnderline())
868 postLayoutData.typingAttributes.add(TypingAttribute::Underline);
869
870 if (RefPtr styleProperties = editingStyle->style()) {
871 bool isLeftToRight = styleProperties->propertyAsValueID(CSSPropertyDirection) == CSSValueLtr;
872 switch (styleProperties->propertyAsValueID(CSSPropertyTextAlign).value_or(CSSValueInvalid)) {
873 case CSSValueRight:
874 case CSSValueWebkitRight:
875 postLayoutData.textAlignment = TextAlignment::Right;
876 break;
877 case CSSValueLeft:
878 case CSSValueWebkitLeft:
879 postLayoutData.textAlignment = TextAlignment::Left;
880 break;
881 case CSSValueCenter:
882 case CSSValueWebkitCenter:
883 postLayoutData.textAlignment = TextAlignment::Center;
884 break;
885 case CSSValueJustify:
886 postLayoutData.textAlignment = TextAlignment::Justified;
887 break;
888 case CSSValueStart:
889 postLayoutData.textAlignment = isLeftToRight ? TextAlignment::Left : TextAlignment::Right;
890 break;
891 case CSSValueEnd:
892 postLayoutData.textAlignment = isLeftToRight ? TextAlignment::Right : TextAlignment::Left;
893 break;
894 default:
895 break;
896 }
897 if (auto textColor = styleProperties->propertyAsColor(CSSPropertyColor))
898 postLayoutData.textColor = *textColor;
899 }
900 }
901
902 if (RefPtr enclosingListElement = enclosingList(RefPtr { selection.start().containerNode() }.get())) {
903 if (is<HTMLUListElement>(*enclosingListElement))
904 postLayoutData.enclosingListType = ListType::UnorderedList;
905 else if (is<HTMLOListElement>(*enclosingListElement))
906 postLayoutData.enclosingListType = ListType::OrderedList;
907 else
908 ASSERT_NOT_REACHED()((void)0);
909 }
910
911 postLayoutData.baseWritingDirection = protect(frame.editor())->baseWritingDirectionForSelectionStart();
912 postLayoutData.canEnableWritingSuggestions = [&] {
913 if (!selection.canEnableWritingSuggestions())
914 return false;
915
916 if (!m_lastNodeBeforeWritingSuggestions)
917 return true;
918
919 RefPtr currentNode = protect(frame.editor())->nodeBeforeWritingSuggestions();
920 return !currentNode || m_lastNodeBeforeWritingSuggestions == currentNode.get();
921 }();
922 }
923
924 RefPtr enclosingFormControl = enclosingTextFormControl(selection.start());
925 if (RefPtr editableRootOrFormControl = enclosingFormControl.get() ?: selection.rootEditableElement()) {
926 postLayoutData.selectionIsTransparentOrFullyClipped = result.isContentEditable && isTransparentOrFullyClipped(*editableRootOrFormControl);
927#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
928 result.visualData->editableRootBounds = rootViewInteractionBounds(Ref { *editableRootOrFormControl });
929#endif
930 } else if (result.selectionIsRange)
931 postLayoutData.selectionIsTransparentOrFullyClipped = selectionIsTransparentOrFullyClipped(selection);
932
933#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
934 if (enclosingFormControl || !m_page->settings().selectionHonorsOverflowScrolling())
935 result.visualData->selectionClipRect = result.visualData->editableRootBounds;
936#endif
937
938 bool startNodeIsInsideFixedPosition = false;
939 bool endNodeIsInsideFixedPosition = false;
940
941 if (selection.isCaret()) {
942 visualData.caretRectAtStart = view->contentsToRootView(WTF::protect(frame.selection())->absoluteCaretBounds(&startNodeIsInsideFixedPosition));
943 endNodeIsInsideFixedPosition = startNodeIsInsideFixedPosition;
944 visualData.caretRectAtEnd = visualData.caretRectAtStart;
945 } else if (selection.isRange()) {
946 visualData.caretRectAtStart = view->contentsToRootView(VisiblePosition(selection.start()).absoluteCaretBounds(&startNodeIsInsideFixedPosition));
947 visualData.caretRectAtEnd = view->contentsToRootView(VisiblePosition(selection.end()).absoluteCaretBounds(&endNodeIsInsideFixedPosition));
948
949 auto selectedRange = selection.toNormalizedRange();
950 if (selectedRange) {
951 auto [selectionGeometries, intersectingLayerIDs] = RenderObject::collectSelectionGeometries(*selectedRange);
952 convertContentToRootView(view, selectionGeometries);
953
954 visualData.selectionGeometries = WTF::move(selectionGeometries);
955 visualData.intersectingLayerIDs = WTF::move(intersectingLayerIDs);
956 }
957 }
958
959 postLayoutData.insideFixedPosition = startNodeIsInsideFixedPosition || endNodeIsInsideFixedPosition;
960}
961
962void WebPage::getPDFFirstPageSize(WebCore::FrameIdentifier frameID, CompletionHandler<void(WebCore::FloatSize)>&& completionHandler)
963{
964 RefPtr webFrame = WebProcess::singleton().webFrame(frameID);
965 if (!webFrame)
966 return completionHandler({ });
967
968#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
969 if (RefPtr pluginView = pluginViewForFrame(protect(webFrame->coreLocalFrame()).get()))
970 return completionHandler(pluginView->pdfDocumentSizeForPrinting());
971#endif
972
973 completionHandler({ });
974}
975
976#if ENABLE(DATA_DETECTION)(defined 1 && 1)
977
978void WebPage::handleClickForDataDetectionResult(const DataDetectorElementInfo& info, const IntPoint& clickLocation)
979{
980 send(Messages::WebPageProxy::HandleClickForDataDetectionResult(info, clickLocation));
981}
982
983#endif
984
985static String& replaceSelectionPasteboardName()
986{
987 static NeverDestroyed<String> string("ReplaceSelectionPasteboard"_s);
988 return string;
989}
990
991class OverridePasteboardForSelectionReplacement {
992 WTF_MAKE_NONCOPYABLE(OverridePasteboardForSelectionReplacement)OverridePasteboardForSelectionReplacement(const OverridePasteboardForSelectionReplacement
&) = delete; OverridePasteboardForSelectionReplacement&
operator=(const OverridePasteboardForSelectionReplacement&
) = delete;
;
993 WTF_MAKE_TZONE_ALLOCATED_INLINE(OverridePasteboardForSelectionReplacement)public: public: using HeapRef = ::bmalloc::api::HeapRef; using
TZoneDescriptor = ::bmalloc::api::TZoneDescriptor; using TZoneMallocFallback
= ::bmalloc::api::TZoneMallocFallback; using CompactAllocationMode
= ::bmalloc::CompactAllocationMode; static constexpr bool usesTZoneHeap
() { return true; } static constexpr unsigned inheritedSizeClass
() { return ::bmalloc::TZone::sizeClass<OverridePasteboardForSelectionReplacement
>(); } static constexpr unsigned inheritedAlignment() { return
::bmalloc::TZone::alignment<OverridePasteboardForSelectionReplacement
>(); } __attribute__((always_inline)) inline void* operator
new(size_t, void* p) { return p; } __attribute__((always_inline
)) inline void* operator new[](size_t, void* p) { return p; }
void* operator new[](size_t size) = delete; void operator delete
[](void* p) = delete; __attribute__((always_inline)) inline void
* operator new(size_t, NotNullTag, void* location) { ((void)0
); return location; } void* operator new(size_t size) { static
HeapRef s_heapRef; static const TZoneSpecification s_heapSpec
= { &s_heapRef, TZoneSpecification::encodeSize<OverridePasteboardForSelectionReplacement
>(), TZoneSpecification::encodeAlignment<OverridePasteboardForSelectionReplacement
>(), TZoneSpecification::encodeCategory<OverridePasteboardForSelectionReplacement
>(), ::bmalloc::api::compactAllocationMode<OverridePasteboardForSelectionReplacement
>(), TZoneSpecification::encodeDescriptor<OverridePasteboardForSelectionReplacement
>(), }; if (!s_heapRef || size != sizeof(OverridePasteboardForSelectionReplacement
)) { [[unlikely]] if constexpr (::bmalloc::api::compactAllocationMode
<OverridePasteboardForSelectionReplacement>() == CompactAllocationMode
::Compact) return ::bmalloc::api::tzoneAllocateCompactSlow(size
, s_heapSpec); return ::bmalloc::api::tzoneAllocateNonCompactSlow
(size, s_heapSpec); } ; if constexpr (::bmalloc::api::compactAllocationMode
<OverridePasteboardForSelectionReplacement>() == CompactAllocationMode
::Compact) return ::bmalloc::api::tzoneAllocateCompact(s_heapRef
); return ::bmalloc::api::tzoneAllocateNonCompact(s_heapRef);
} __attribute__((always_inline)) inline void operator delete
(void* p) { ::bmalloc::api::tzoneFree(p); } __attribute__((always_inline
)) inline static void freeAfterDestruction(void* p) { ::bmalloc
::api::tzoneFree(p); } using WTFIsFastMallocAllocated = int; private
: using __makeBtzoneMallocedInlineMacroSemicolonifier __attribute__
((unused)) = int
;
994public:
995 OverridePasteboardForSelectionReplacement(const Vector<String>& types, std::span<const uint8_t> data)
996 : m_types(types)
997 {
998 for (auto& type : types)
999 WebPasteboardOverrides::sharedPasteboardOverrides().addOverride(replaceSelectionPasteboardName(), type, { data });
1000 }
1001
1002 ~OverridePasteboardForSelectionReplacement()
1003 {
1004 for (auto& type : m_types)
1005 WebPasteboardOverrides::sharedPasteboardOverrides().removeOverride(replaceSelectionPasteboardName(), type);
1006 }
1007
1008private:
1009 Vector<String> m_types;
1010};
1011
1012#if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1)
1013
1014void WebPage::replaceImageForRemoveBackground(const ElementContext& elementContext, const Vector<String>& types, std::span<const uint8_t> data)
1015{
1016 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
1017 if (!frame)
1018 return;
1019
1020 auto element = elementForContext(elementContext);
1021 if (!element || !element->isContentEditable())
1022 return;
1023
1024 Ref document = element->document();
1025 if (frame->document() != document.ptr())
1026 return;
1027
1028 auto originalSelection = frame->selection().selection();
1029 RefPtr selectionHost = originalSelection.rootEditableElement() ?: document->body();
1030 if (!selectionHost)
1031 return;
1032
1033 constexpr OptionSet iteratorOptions = TextIteratorBehavior::EmitsCharactersBetweenAllVisiblePositions;
1034 std::optional<CharacterRange> rangeToRestore;
1035 uint64_t numberOfCharactersInSelectionHost = 0;
1036 if (auto range = originalSelection.range()) {
1037 auto selectionHostRangeBeforeReplacement = makeRangeSelectingNodeContents(*selectionHost);
1038 rangeToRestore = characterRange(selectionHostRangeBeforeReplacement, *range, iteratorOptions);
1039 numberOfCharactersInSelectionHost = characterCount(selectionHostRangeBeforeReplacement, iteratorOptions);
1040 }
1041
1042 {
1043 OverridePasteboardForSelectionReplacement overridePasteboard { types, data };
1044 IgnoreSelectionChangeForScope ignoreSelectionChanges { *frame };
1045 protect(frame->editor())->replaceNodeFromPasteboard(*element, replaceSelectionPasteboardName(), EditAction::RemoveBackground);
1046
1047 auto position = frame->selection().selection().visibleStart();
1048 if (auto imageRange = makeSimpleRange(WebCore::VisiblePositionRange { position.previous(), position })) {
1049 for (WebCore::TextIterator iterator { *imageRange, { } }; !iterator.atEnd(); iterator.advance()) {
1050 if (RefPtr image = dynamicDowncast<HTMLImageElement>(iterator.node())) {
1051 m_elementsToExcludeFromRemoveBackground.add(*image);
1052 break;
1053 }
1054 }
1055 }
1056 }
1057
1058 constexpr auto restoreSelectionOptions = FrameSelection::defaultSetSelectionOptions(UserTriggered::Yes);
1059 if (!originalSelection.isNoneOrOrphaned()) {
1060 protect(frame->selection())->setSelection(originalSelection, restoreSelectionOptions);
1061 return;
1062 }
1063
1064 if (!rangeToRestore || !selectionHost->isConnected())
1065 return;
1066
1067 auto selectionHostRange = makeRangeSelectingNodeContents(*selectionHost);
1068 if (numberOfCharactersInSelectionHost != characterCount(selectionHostRange, iteratorOptions)) {
1069 // FIXME: We don't attempt to restore the selection if the replaced element contains a different
1070 // character count than the content that replaces it, since this codepath is currently only used
1071 // to replace a single non-text element with another. If this is used to replace text content in
1072 // the future, we should adjust the `rangeToRestore` to fit the newly inserted content.
1073 return;
1074 }
1075
1076 // The node replacement may have orphaned the original selection range; in this case, try to restore
1077 // the original selected character range.
1078 auto newSelectionRange = resolveCharacterRange(selectionHostRange, *rangeToRestore, iteratorOptions);
1079 protect(frame->selection())->setSelection(newSelectionRange, restoreSelectionOptions);
1080}
1081
1082#endif // ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)
1083
1084void WebPage::replaceSelectionWithPasteboardData(const Vector<String>& types, std::span<const uint8_t> data)
1085{
1086 OverridePasteboardForSelectionReplacement overridePasteboard { types, data };
1087 readSelectionFromPasteboard(replaceSelectionPasteboardName(), [](bool) { });
1088}
1089
1090void WebPage::readSelectionFromPasteboard(const String& pasteboardName, CompletionHandler<void(bool&&)>&& completionHandler)
1091{
1092 RefPtr frame = m_page->focusController().focusedOrMainFrame();
1093 if (!frame)
1094 return completionHandler(false);
1095 if (frame->selection().isNone())
1096 return completionHandler(false);
1097 protect(frame->editor())->readSelectionFromPasteboard(pasteboardName);
1098 completionHandler(true);
1099}
1100
1101#if ENABLE(MULTI_REPRESENTATION_HEIC)(defined ENABLE_MULTI_REPRESENTATION_HEIC && ENABLE_MULTI_REPRESENTATION_HEIC
)
1102void WebPage::insertMultiRepresentationHEIC(std::span<const uint8_t> data, const String& altText)
1103{
1104 RefPtr frame = m_page->focusController().focusedOrMainFrame();
1105 if (!frame)
1106 return;
1107 if (frame->selection().isNone())
1108 return;
1109 frame->editor().insertMultiRepresentationHEIC(data, altText);
1110}
1111#endif
1112
1113std::pair<URL, DidFilterLinkDecoration> WebPage::applyLinkDecorationFilteringWithResult(const URL& url, LinkDecorationFilteringTrigger trigger)
1114{
1115#if ENABLE(ADVANCED_PRIVACY_PROTECTIONS)(defined 1 && 1)
1116 if (m_internals->linkDecorationFilteringData.isEmpty()) {
1117 RELEASE_LOG_ERROR(ResourceLoadStatistics, "Unable to filter tracking query parameters (missing data)")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 = (WebKit2LogResourceLoadStatistics
.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
("Unable to filter tracking query parameters (missing data)")
, "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_LOG29"
) = "Unable to filter tracking query parameters (missing data)"
; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf
[__builtin_os_log_format_buffer_size("Unable to filter tracking query parameters (missing data)"
)]; _os_log_error_impl(&__dso_handle, _log_tmp, _type_tmp
, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf
, "Unable to filter tracking query parameters (missing data)"
), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); }
}) clang diagnostic pop
;
1118 return { url, DidFilterLinkDecoration::No };
1119 }
1120
1121 RefPtr mainFrame = m_mainFrame->coreLocalFrame();
1122 if (!mainFrame || !WebCore::DeprecatedGlobalSettings::trackingPreventionEnabled())
1123 return { url, DidFilterLinkDecoration::No };
1124
1125 auto isLinkDecorationFilteringEnabled = [&](const DocumentLoader* loader) {
1126 if (!loader)
1127 return false;
1128 auto effectivePolicies = trigger == LinkDecorationFilteringTrigger::Navigation ? loader->navigationalAdvancedPrivacyProtections() : loader->advancedPrivacyProtections();
1129 return effectivePolicies.contains(AdvancedPrivacyProtections::LinkDecorationFiltering);
1130 };
1131
1132 bool hasOptedInToLinkDecorationFiltering = [&] {
1133 if (isLinkDecorationFilteringEnabled(RefPtr { mainFrame->loader().activeDocumentLoader() }.get()))
1134 return true;
1135
1136 return isLinkDecorationFilteringEnabled(RefPtr { mainFrame->loader().policyDocumentLoader() }.get());
1137 }();
1138
1139 RefPtr document = mainFrame ? mainFrame->document() : nullptr;
1140 bool isConsistentQueryParameterFilteringQuirkEnabled = document && (document->quirks().needsConsistentQueryParameterFilteringQuirk(document->url()) || document->quirks().needsConsistentQueryParameterFilteringQuirk(url));
1141 if (!hasOptedInToLinkDecorationFiltering && !m_page->settings().filterLinkDecorationByDefaultEnabled() && !isConsistentQueryParameterFilteringQuirkEnabled)
1142 return { url, DidFilterLinkDecoration::No };
1143
1144 if (!url.hasQuery())
1145 return { url, DidFilterLinkDecoration::No };
1146
1147 auto sanitizedURL = url;
1148 bool allowLowEntropyException = !(hasOptedInToLinkDecorationFiltering || isConsistentQueryParameterFilteringQuirkEnabled);
1149 auto removedParameters = WTF::removeQueryParameters(sanitizedURL, [&](auto& key, auto& value) {
1150 auto it = m_internals->linkDecorationFilteringData.find(key);
1151 if (it == m_internals->linkDecorationFilteringData.end())
1152 return false;
1153
1154 constexpr auto base = 10;
1155 if (value.length() == 3 && allowLowEntropyException && WTF::parseInteger<uint8_t>(value, base, WTF::ParseIntegerWhitespacePolicy::Disallow))
1156 return false;
1157
1158 const auto& conditionals = it->value;
1159 bool isEmptyOrFoundDomain = conditionals.domains.isEmpty() || conditionals.domains.contains(RegistrableDomain { url });
1160 bool isEmptyOrFoundPath = conditionals.paths.isEmpty() || std::ranges::any_of(conditionals.paths,
1161 [&url](auto& path) {
1162 return url.path().contains(path);
1163 });
1164
1165 return isEmptyOrFoundDomain && isEmptyOrFoundPath;
1166 });
1167
1168 if (!removedParameters.isEmpty() && trigger != LinkDecorationFilteringTrigger::Unspecified) {
1169 if (trigger == LinkDecorationFilteringTrigger::Navigation)
1170 send(Messages::WebPageProxy::DidApplyLinkDecorationFiltering(url, sanitizedURL));
1171 auto removedParametersString = makeStringByJoining(removedParameters, ", "_s);
1172 WEBPAGE_RELEASE_LOG(ResourceLoadStatistics, "applyLinkDecorationFilteringWithResult: Blocked known tracking query parameters: %s", removedParametersString.utf8().data())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 = (WebKit2LogResourceLoadStatistics
.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
("%p - [webPageID=%" "ll" "u" "] WebPage::" "applyLinkDecorationFilteringWithResult: Blocked known tracking query parameters: %s"
), "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_LOG30"
) = "%p - [webPageID=%" "ll" "u" "] WebPage::" "applyLinkDecorationFilteringWithResult: Blocked known tracking query parameters: %s"
; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf
[__builtin_os_log_format_buffer_size("%p - [webPageID=%" "ll"
"u" "] WebPage::" "applyLinkDecorationFilteringWithResult: Blocked known tracking query parameters: %s"
, this, m_identifier.toUInt64(), removedParametersString.utf8
().data())]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp
, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf
, "%p - [webPageID=%" "ll" "u" "] WebPage::" "applyLinkDecorationFilteringWithResult: Blocked known tracking query parameters: %s"
, this, m_identifier.toUInt64(), removedParametersString.utf8
().data()), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop
; }); } }) clang diagnostic pop
;
1173 }
1174
1175 return { sanitizedURL, DidFilterLinkDecoration::Yes };
1176#else
1177 return { url, DidFilterLinkDecoration::No };
1178#endif
1179}
1180
1181URL WebPage::allowedQueryParametersForAdvancedPrivacyProtections(const URL& url)
1182{
1183#if ENABLE(ADVANCED_PRIVACY_PROTECTIONS)(defined 1 && 1)
1184 if (m_internals->allowedQueryParametersForAdvancedPrivacyProtections.isEmpty()) {
1185 RELEASE_LOG_ERROR(ResourceLoadStatistics, "Unable to hide query parameters from script (missing data)")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 = (WebKit2LogResourceLoadStatistics
.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
("Unable to hide query parameters from script (missing data)"
), "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_LOG31"
) = "Unable to hide query parameters from script (missing data)"
; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf
[__builtin_os_log_format_buffer_size("Unable to hide query parameters from script (missing data)"
)]; _os_log_error_impl(&__dso_handle, _log_tmp, _type_tmp
, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf
, "Unable to hide query parameters from script (missing data)"
), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); }
}) clang diagnostic pop
;
1186 return url;
1187 }
1188
1189 if (!url.hasQuery() && !url.hasFragmentIdentifier())
1190 return url;
1191
1192 auto sanitizedURL = url;
1193
1194 auto allowedParameters = m_internals->allowedQueryParametersForAdvancedPrivacyProtections.get(RegistrableDomain { sanitizedURL });
1195
1196 if (!allowedParameters.contains("#"_s))
1197 sanitizedURL.removeFragmentIdentifier();
1198
1199 WTF::removeQueryParameters(sanitizedURL, [&](auto& key, auto&) {
1200 return !allowedParameters.contains(key);
1201 });
1202
1203 return sanitizedURL;
1204#else
1205 return url;
1206#endif
1207}
1208
1209#if ENABLE(EXTENSION_CAPABILITIES)(defined ENABLE_EXTENSION_CAPABILITIES && ENABLE_EXTENSION_CAPABILITIES
)
1210void WebPage::setMediaPlaybackEnvironment(const String& environment)
1211{
1212 m_mediaPlaybackEnvironment = environment;
1213 if (RefPtr gpuProcessConnection = WebProcess::singleton().existingGPUProcessConnection())
1214 gpuProcessConnection->setMediaPlaybackEnvironment(identifier(), environment);
1215}
1216
1217void WebPage::setDisplayCaptureEnvironment(const String& environment)
1218{
1219 m_displayCaptureEnvironment = environment;
1220 if (RefPtr gpuProcessConnection = WebProcess::singleton().existingGPUProcessConnection())
1221 gpuProcessConnection->setDisplayCaptureEnvironment(identifier(), environment);
1222}
1223#endif
1224
1225#if ENABLE(WRITING_TOOLS)(defined 1 && 1)
1226void WebPage::willBeginWritingToolsSession(const std::optional<WebCore::WritingTools::Session>& session, CompletionHandler<void(const Vector<WebCore::WritingTools::Context>&)>&& completionHandler)
1227{
1228 protect(corePage())->willBeginWritingToolsSession(session, WTF::move(completionHandler));
1229}
1230
1231void WebPage::didBeginWritingToolsSession(const WebCore::WritingTools::Session& session, const Vector<WebCore::WritingTools::Context>& contexts)
1232{
1233 protect(corePage())->didBeginWritingToolsSession(session, contexts);
1234}
1235
1236void WebPage::proofreadingSessionDidReceiveSuggestions(const WebCore::WritingTools::Session& session, const Vector<WebCore::WritingTools::TextSuggestion>& suggestions, const WebCore::CharacterRange& processedRange, const WebCore::WritingTools::Context& context, bool finished, CompletionHandler<void()>&& completionHandler)
1237{
1238 protect(corePage())->proofreadingSessionDidReceiveSuggestions(session, suggestions, processedRange, context, finished);
1239 completionHandler();
1240}
1241
1242void WebPage::proofreadingSessionDidUpdateStateForSuggestion(const WebCore::WritingTools::Session& session, WebCore::WritingTools::TextSuggestion::State state, const WebCore::WritingTools::TextSuggestion& suggestion, const WebCore::WritingTools::Context& context)
1243{
1244 protect(corePage())->proofreadingSessionDidUpdateStateForSuggestion(session, state, suggestion, context);
1245}
1246
1247void WebPage::willEndWritingToolsSession(const WebCore::WritingTools::Session& session, bool accepted, CompletionHandler<void()>&& completionHandler)
1248{
1249 protect(corePage())->willEndWritingToolsSession(session, accepted);
1250 completionHandler();
1251}
1252
1253void WebPage::didEndWritingToolsSession(const WebCore::WritingTools::Session& session, bool accepted)
1254{
1255 protect(corePage())->didEndWritingToolsSession(session, accepted);
1256}
1257
1258void WebPage::compositionSessionDidReceiveTextWithReplacementRange(const WebCore::WritingTools::Session& session, const WebCore::AttributedString& attributedText, const WebCore::CharacterRange& range, const WebCore::WritingTools::Context& context, bool finished, CompletionHandler<void()>&& completionHandler)
1259{
1260 protect(corePage())->compositionSessionDidReceiveTextWithReplacementRange(session, attributedText, range, context, finished);
1261 completionHandler();
1262}
1263
1264void WebPage::writingToolsSessionDidReceiveAction(const WritingTools::Session& session, WebCore::WritingTools::Action action)
1265{
1266 protect(corePage())->writingToolsSessionDidReceiveAction(session, action);
1267}
1268
1269void WebPage::proofreadingSessionShowDetailsForSuggestionWithIDRelativeToRect(const WebCore::WritingTools::TextSuggestion::ID& replacementID, WebCore::IntRect rect)
1270{
1271 send(Messages::WebPageProxy::ProofreadingSessionShowDetailsForSuggestionWithIDRelativeToRect(replacementID, rect));
1272}
1273
1274void WebPage::proofreadingSessionUpdateStateForSuggestionWithID(WebCore::WritingTools::TextSuggestion::State state, const WebCore::WritingTools::TextSuggestion::ID& replacementID)
1275{
1276 send(Messages::WebPageProxy::ProofreadingSessionUpdateStateForSuggestionWithID(state, replacementID));
1277}
1278
1279void WebPage::addTextAnimationForAnimationID(const WTF::UUID& uuid, const WebCore::TextAnimationData& styleData, const RefPtr<WebCore::TextIndicator> textIndicator, CompletionHandler<void(WebCore::TextAnimationRunMode)>&& completionHandler)
1280{
1281 if (completionHandler)
1282 sendWithAsyncReply(Messages::WebPageProxy::AddTextAnimationForAnimationIDWithCompletionHandler(uuid, styleData, textIndicator), WTF::move(completionHandler));
1283 else
1284 send(Messages::WebPageProxy::AddTextAnimationForAnimationID(uuid, styleData, textIndicator));
1285}
1286
1287void WebPage::removeTextAnimationForAnimationID(const WTF::UUID& uuid)
1288{
1289 send(Messages::WebPageProxy::RemoveTextAnimationForAnimationID(uuid));
1290}
1291
1292void WebPage::removeInitialTextAnimationForActiveWritingToolsSession()
1293{
1294 m_textAnimationController->removeInitialTextAnimationForActiveWritingToolsSession();
1295}
1296
1297void WebPage::addInitialTextAnimationForActiveWritingToolsSession()
1298{
1299 m_textAnimationController->addInitialTextAnimationForActiveWritingToolsSession();
1300}
1301
1302void WebPage::addSourceTextAnimationForActiveWritingToolsSession(const WTF::UUID& sourceAnimationUUID, const WTF::UUID& destinationAnimationUUID, bool finished, const CharacterRange& range, const String& string, CompletionHandler<void(WebCore::TextAnimationRunMode)>&& completionHandler)
1303{
1304 m_textAnimationController->addSourceTextAnimationForActiveWritingToolsSession(sourceAnimationUUID, destinationAnimationUUID, finished, range, string, WTF::move(completionHandler));
1305}
1306
1307void WebPage::addDestinationTextAnimationForActiveWritingToolsSession(const WTF::UUID& sourceAnimationUUID, const WTF::UUID& destinationAnimationUUID, const std::optional<CharacterRange>& range, const String& string)
1308{
1309 m_textAnimationController->addDestinationTextAnimationForActiveWritingToolsSession(sourceAnimationUUID, destinationAnimationUUID, range, string);
1310}
1311
1312void WebPage::saveSnapshotOfTextPlaceholderForAnimation(const WebCore::SimpleRange& placeholderRange)
1313{
1314 m_textAnimationController->saveSnapshotOfTextPlaceholderForAnimation(placeholderRange);
1315}
1316
1317void WebPage::clearAnimationsForActiveWritingToolsSession()
1318{
1319 m_textAnimationController->clearAnimationsForActiveWritingToolsSession();
1320}
1321
1322void WebPage::createTextIndicatorForTextAnimationID(const WTF::UUID& uuid, CompletionHandler<void(RefPtr<WebCore::TextIndicator>&&)>&& completionHandler)
1323{
1324 m_textAnimationController->createTextIndicatorForTextAnimationID(uuid, WTF::move(completionHandler));
1325}
1326
1327void WebPage::updateUnderlyingTextVisibilityForTextAnimationID(const WTF::UUID& uuid, bool visible, CompletionHandler<void()>&& completionHandler)
1328{
1329 m_textAnimationController->updateUnderlyingTextVisibilityForTextAnimationID(uuid, visible, WTF::move(completionHandler));
1330}
1331
1332void WebPage::proofreadingSessionSuggestionTextRectsInRootViewCoordinates(const WebCore::CharacterRange& enclosingRangeRelativeToSessionRange, CompletionHandler<void(Vector<FloatRect>&&)>&& completionHandler) const
1333{
1334 auto rects = protect(corePage())->proofreadingSessionSuggestionTextRectsInRootViewCoordinates(enclosingRangeRelativeToSessionRange);
1335 completionHandler(WTF::move(rects));
1336}
1337
1338void WebPage::updateTextVisibilityForActiveWritingToolsSession(const WebCore::CharacterRange& rangeRelativeToSessionRange, bool visible, const WTF::UUID& identifier, CompletionHandler<void()>&& completionHandler)
1339{
1340 protect(corePage())->updateTextVisibilityForActiveWritingToolsSession(rangeRelativeToSessionRange, visible, identifier);
1341 completionHandler();
1342}
1343
1344void WebPage::textPreviewDataForActiveWritingToolsSession(const WebCore::CharacterRange& rangeRelativeToSessionRange, CompletionHandler<void(RefPtr<WebCore::TextIndicator>&&)>&& completionHandler)
1345{
1346 RefPtr textIndicator = protect(corePage())->textPreviewDataForActiveWritingToolsSession(rangeRelativeToSessionRange);
1347 completionHandler(WTF::move(textIndicator));
1348}
1349
1350void WebPage::decorateTextReplacementsForActiveWritingToolsSession(const WebCore::CharacterRange& rangeRelativeToSessionRange, CompletionHandler<void(void)>&& completionHandler)
1351{
1352 protect(corePage())->decorateTextReplacementsForActiveWritingToolsSession(rangeRelativeToSessionRange);
1353 completionHandler();
1354}
1355
1356void WebPage::setSelectionForActiveWritingToolsSession(const WebCore::CharacterRange& rangeRelativeToSessionRange, CompletionHandler<void(void)>&& completionHandler)
1357{
1358 protect(corePage())->setSelectionForActiveWritingToolsSession(rangeRelativeToSessionRange);
1359 completionHandler();
1360}
1361
1362void WebPage::intelligenceTextAnimationsDidComplete()
1363{
1364 protect(corePage())->intelligenceTextAnimationsDidComplete();
1365}
1366
1367void WebPage::didEndPartialIntelligenceTextAnimation()
1368{
1369 send(Messages::WebPageProxy::DidEndPartialIntelligenceTextAnimation());
1370}
1371
1372#endif
1373
1374static std::optional<bool> elementHasHiddenVisibility(StyledElement* styledElement)
1375{
1376 RefPtr inlineStyle = styledElement->inlineStyle();
1377 if (!inlineStyle)
1378 return std::nullopt;
1379
1380 RefPtr value = inlineStyle->getPropertyCSSValue(CSSPropertyVisibility);
1381 if (!value)
1382 return false;
1383
1384 return value->valueID() == CSSValueHidden;
1385}
1386
1387void WebPage::createTextIndicatorForElementWithID(const String& elementID, CompletionHandler<void(RefPtr<WebCore::TextIndicator>&&)>&& completionHandler)
1388{
1389 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
1390 if (!frame) {
1391 ASSERT_NOT_REACHED()((void)0);
1392 completionHandler(nilnullptr);
1393 return;
1394 }
1395
1396 RefPtr document = frame->document();
1397 if (!document) {
1398 ASSERT_NOT_REACHED()((void)0);
1399 completionHandler(nilnullptr);
1400 return;
1401 }
1402
1403 RefPtr element = document->getElementById(elementID);
1404 if (!element) {
1405 ASSERT_NOT_REACHED()((void)0);
1406 completionHandler(nilnullptr);
1407 return;
1408 }
1409
1410 RefPtr styledElement = dynamicDowncast<StyledElement>(element.get());
1411 if (!styledElement) {
1412 ASSERT_NOT_REACHED()((void)0);
1413 completionHandler(nilnullptr);
1414 return;
1415 }
1416
1417 // Temporarily force the content to be visible so that it can be snapshotted.
1418
1419 auto isHiddenInitially = elementHasHiddenVisibility(styledElement.get());
1420
1421 styledElement->setInlineStyleProperty(CSSPropertyVisibility, CSSValueVisible, IsImportant::Yes);
1422
1423 auto elementRange = WebCore::makeRangeSelectingNodeContents(*styledElement);
1424
1425 std::optional<WebCore::TextIndicatorData> textIndicatorData;
1426 constexpr OptionSet textIndicatorOptions {
1427 WebCore::TextIndicatorOption::IncludeSnapshotOfAllVisibleContentWithoutSelection,
1428 WebCore::TextIndicatorOption::ExpandClipBeyondVisibleRect,
1429 WebCore::TextIndicatorOption::SkipReplacedContent,
1430 WebCore::TextIndicatorOption::RespectTextColor,
1431#if PLATFORM(VISION)(defined WTF_PLATFORM_VISION && WTF_PLATFORM_VISION)
1432 WebCore::TextIndicatorOption::SnapshotContentAt3xBaseScale,
1433#endif
1434 };
1435
1436 RefPtr textIndicator = WebCore::TextIndicator::createWithRange(elementRange, textIndicatorOptions, WebCore::TextIndicatorPresentationTransition::None, { });
1437 if (!textIndicator) {
1438 completionHandler(nilnullptr);
1439 return;
1440 }
1441
1442 // If `initialVisibility` is an empty optional, this means there was no initial inline style.
1443 // Ensure the state is idempotent after by removing the inline style if this is the case.
1444
1445 if (isHiddenInitially.has_value())
1446 styledElement->setInlineStyleProperty(CSSPropertyVisibility, *isHiddenInitially ? CSSValueHidden : CSSValueVisible, IsImportant::Yes);
1447 else
1448 styledElement->removeInlineStyleProperty(CSSPropertyVisibility);
1449
1450 completionHandler(WTF::move(textIndicator));
1451}
1452
1453void WebPage::createBitmapsFromImageData(Ref<WebCore::SharedBuffer>&& buffer, const Vector<unsigned>& lengths, CompletionHandler<void(Vector<Ref<WebCore::ShareableBitmap>>&&)>&& completionHandler)
1454{
1455 WebCore::createBitmapsFromImageData(buffer->span(), lengths.span(), WTF::move(completionHandler));
1456}
1457
1458void WebPage::decodeImageData(Ref<WebCore::SharedBuffer>&& buffer, std::optional<WebCore::FloatSize> preferredSize, CompletionHandler<void(RefPtr<WebCore::ShareableBitmap>&&)>&& completionHandler)
1459{
1460 decodeImageWithSize(buffer->span(), preferredSize, WTF::move(completionHandler));
1461}
1462
1463#if HAVE(PDFKIT)(defined 1 && 1)
1464
1465void WebPage::computePagesForPrintingPDFDocument(WebCore::FrameIdentifier frameID, const PrintInfo& printInfo, Vector<IntRect>& resultPageRects)
1466{
1467 ASSERT(resultPageRects.isEmpty())((void)0);
1468 RefPtr frame = WebProcess::singleton().webFrame(frameID);
1469 RefPtr coreFrame = frame ? frame->coreLocalFrame() : nullptr;
1470 RetainPtr<PDFDocument> pdfDocument = coreFrame ? pdfDocumentForPrintingFrame(coreFrame.get()) : 0;
1471 if ([pdfDocument allowsPrinting]) {
1472 NSUInteger pageCount = [pdfDocument pageCount];
1473 IntRect pageRect(0, 0, ceilf(printInfo.availablePaperWidth), ceilf(printInfo.availablePaperHeight));
1474 for (NSUInteger i = 1; i <= pageCount; ++i) {
1475 resultPageRects.append(pageRect);
1476 pageRect.move(0, pageRect.height());
1477 }
1478 }
1479}
1480
1481static inline CGFloat roundCGFloat(CGFloat f)
1482{
1483 if (sizeof(CGFloat) == sizeof(float))
1484 return roundf(static_cast<float>(f));
1485 return static_cast<CGFloat>(round(f));
1486}
1487
1488static void drawPDFPage(PDFDocument *pdfDocument, CFIndex pageIndex, CGContextRef context, CGFloat pageSetupScaleFactor, CGSize paperSize)
1489{
1490 CGContextSaveGState(context);
1491
1492 CGContextScaleCTM(context, pageSetupScaleFactor, pageSetupScaleFactor);
1493
1494 RetainPtr<PDFPage> pdfPage = [pdfDocument pageAtIndex:pageIndex];
1495 NSRect cropBox = [pdfPage boundsForBox:kPDFDisplayBoxCropBox];
1496 if (NSIsEmptyRect(cropBox))
1497 cropBox = [pdfPage boundsForBox:kPDFDisplayBoxMediaBox];
1498 else
1499 cropBox = NSIntersectionRect(cropBox, [pdfPage boundsForBox:kPDFDisplayBoxMediaBox]);
1500
1501 // Always auto-rotate PDF content regardless of the paper orientation.
1502 NSInteger rotation = [pdfPage rotation];
1503 if (rotation == 90 || rotation == 270)
1504 std::swap(cropBox.size.width, cropBox.size.height);
1505
1506 bool shouldRotate = (paperSize.width < paperSize.height) != (cropBox.size.width < cropBox.size.height);
1507 if (shouldRotate)
1508 std::swap(cropBox.size.width, cropBox.size.height);
1509
1510 // Center.
1511 CGFloat widthDifference = paperSize.width / pageSetupScaleFactor - cropBox.size.width;
1512 CGFloat heightDifference = paperSize.height / pageSetupScaleFactor - cropBox.size.height;
1513 if (widthDifference || heightDifference)
1514 CGContextTranslateCTM(context, roundCGFloat(widthDifference / 2), roundCGFloat(heightDifference / 2));
1515
1516 if (shouldRotate) {
1517 CGContextRotateCTM(context, static_cast<CGFloat>(piOverTwoDouble));
1518 CGContextTranslateCTM(context, 0, -cropBox.size.width);
1519 }
1520
1521 [pdfPage drawWithBox:kPDFDisplayBoxCropBox toContext:context];
1522
1523 CGAffineTransform transform = CGContextGetCTM(context);
1524
1525 for (PDFAnnotation *annotation in [pdfPage annotations]) {
1526 if (![[annotation valueForAnnotationKey:get_PDFKit_PDFAnnotationKeySubtypeSingleton()] isEqualToString:get_PDFKit_PDFAnnotationSubtypeLinkSingleton()])
1527 continue;
1528
1529 RetainPtr<NSURL> url = annotation.URL;
1530 if (!url)
1531 continue;
1532
1533 CGRect transformedRect = CGRectApplyAffineTransform(annotation.bounds, transform);
1534 CGPDFContextSetURLForRect(context, (CFURLRef)url.get(), transformedRect);
1535 }
1536
1537 CGContextRestoreGState(context);
1538}
1539
1540void WebPage::drawPDFDocument(CGContextRef context, PDFDocument *pdfDocument, const PrintInfo& printInfo, const WebCore::IntRect& rect)
1541{
1542 NSUInteger pageCount = [pdfDocument pageCount];
1543 IntSize paperSize(ceilf(printInfo.availablePaperWidth), ceilf(printInfo.availablePaperHeight));
1544 IntRect pageRect(IntPoint(), paperSize);
1545 for (NSUInteger i = 0; i < pageCount; ++i) {
1546 if (pageRect.intersects(rect)) {
1547 CGContextSaveGState(context);
1548
1549 CGContextTranslateCTM(context, pageRect.x() - rect.x(), pageRect.y() - rect.y());
1550 drawPDFPage(pdfDocument, i, context, printInfo.pageSetupScaleFactor, paperSize);
1551
1552 CGContextRestoreGState(context);
1553 }
1554 pageRect.move(0, pageRect.height());
1555 }
1556}
1557
1558void WebPage::drawPagesToPDFFromPDFDocument(GraphicsContext& context, PDFDocument *pdfDocument, const PrintInfo& printInfo, const WebCore::FloatRect& mediaBox, uint32_t first, uint32_t count)
1559{
1560 NSUInteger pageCount = [pdfDocument pageCount];
1561 for (uint32_t page = first; page < first + count; ++page) {
1562 if (page >= pageCount)
1563 break;
1564
1565 context.beginPage(mediaBox);
1566 drawPDFPage(pdfDocument, page, context.protectedPlatformContext().get(), printInfo.pageSetupScaleFactor, CGSizeMake(printInfo.availablePaperWidth, printInfo.availablePaperHeight));
1567 context.endPage();
1568 }
1569}
1570
1571#else
1572
1573void WebPage::drawPDFDocument(CGContextRef, PDFDocument *, const PrintInfo&, const WebCore::IntRect&)
1574{
1575 notImplemented()((void)0);
1576}
1577
1578void WebPage::computePagesForPrintingPDFDocument(WebCore::FrameIdentifier, const PrintInfo&, Vector<IntRect>&)
1579{
1580 notImplemented()((void)0);
1581}
1582
1583void WebPage::drawPagesToPDFFromPDFDocument(GraphicsContext&, PDFDocument *, const PrintInfo&, const WebCore::FloatRect&, uint32_t, uint32_t)
1584{
1585 notImplemented()((void)0);
1586}
1587
1588#endif
1589
1590BoxSideSet WebPage::sidesRequiringFixedContainerEdges() const
1591{
1592 if (!m_page->settings().contentInsetBackgroundFillEnabled())
1593 return { };
1594
1595#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
1596 auto obscuredInsets = m_page->obscuredInsets();
1597#else
1598 auto obscuredInsets = m_page->obscuredContentInsets();
1599#endif
1600
1601#if PLATFORM(MAC)(defined 1 && 1)
1602 auto additionalHeight = m_overflowHeightForTopScrollEdgeEffect;
1603#else
1604 auto additionalHeight = 0;
1605#endif
1606
1607 auto sides = m_page->fixedContainerEdges().fixedEdges();
1608
1609 if ((additionalHeight + obscuredInsets.top()) > 0)
1610 sides.add(BoxSide::Top);
1611
1612 if (obscuredInsets.left() > 0)
1613 sides.add(BoxSide::Left);
1614
1615 if (obscuredInsets.right() > 0)
1616 sides.add(BoxSide::Right);
1617
1618 if (obscuredInsets.bottom() > 0)
1619 sides.add(BoxSide::Bottom);
1620
1621 return sides;
1622}
1623
1624void WebPage::getWebArchivesForFrames(const Vector<WebCore::FrameIdentifier>& frameIdentifiers, CompletionHandler<void(HashMap<WebCore::FrameIdentifier, Ref<WebCore::LegacyWebArchive>>&&)>&& completionHandler)
1625{
1626 if (!m_page)
1627 return completionHandler({ });
1628
1629 HashMap<WebCore::FrameIdentifier, Ref<LegacyWebArchive>> result;
1630 for (auto& frameIdentifier : frameIdentifiers) {
1631 RefPtr frame = WebFrame::webFrame(frameIdentifier);
1632 if (!frame)
1633 continue;
1634
1635 RefPtr localFrame = frame->coreLocalFrame();
1636 if (!localFrame)
1637 continue;
1638
1639 RefPtr document = localFrame->document();
1640 if (!document)
1641 continue;
1642
1643 WebCore::LegacyWebArchive::ArchiveOptions options {
1644 LegacyWebArchive::ShouldSaveScriptsFromMemoryCache::Yes,
1645 LegacyWebArchive::ShouldArchiveSubframes::No
1646 };
1647 if (RefPtr archive = WebCore::LegacyWebArchive::create(*document, WTF::move(options)))
1648 result.add(localFrame->frameID(), archive.releaseNonNull());
1649 }
1650 completionHandler(WTF::move(result));
1651}
1652
1653void WebPage::getWebArchiveData(CompletionHandler<void(const std::optional<IPC::SharedBufferReference>&)>&& completionHandler)
1654{
1655 RetainPtr<CFDataRef> data = m_mainFrame->webArchiveData(nullptr, nullptr);
1656 completionHandler(IPC::SharedBufferReference(SharedBuffer::create(data.get())));
1657}
1658
1659void WebPage::processSystemWillSleep() const
1660{
1661 if (RefPtr manager = mediaSessionManagerIfExists())
1662 manager->processSystemWillSleep();
1663}
1664
1665void WebPage::processSystemDidWake() const
1666{
1667 if (RefPtr manager = mediaSessionManagerIfExists())
1668 manager->processSystemDidWake();
1669}
1670
1671NSObject *WebPage::accessibilityObjectForMainFramePlugin()
1672{
1673#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
1674 if (!m_page)
1675 return nilnullptr;
1676
1677 if (RefPtr pluginView = mainFramePlugIn())
1678 return pluginView->accessibilityObject();
1679#endif
1680
1681 return nilnullptr;
1682}
1683
1684bool WebPage::shouldFallbackToWebContentAXObjectForMainFramePlugin() const
1685{
1686#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
1687 RefPtr pluginView = mainFramePlugIn();
1688 return pluginView && pluginView->isPresentingLockedContent();
1689#else
1690 return false;
1691#endif
1692}
1693
1694WKAccessibilityWebPageObject* WebPage::accessibilityRemoteObject()
1695{
1696 return m_mockAccessibilityElement.get();
1697}
1698
1699RetainPtr<PDFDocument> WebPage::pdfDocumentForPrintingFrame(LocalFrame* coreFrame)
1700{
1701#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
1702 if (RefPtr pluginView = pluginViewForFrame(coreFrame))
1703 return pluginView->pdfDocumentForPrinting();
1704#endif
1705 return nullptr;
1706}
1707
1708void WebPage::drawToPDF(const std::optional<FloatRect>& rect, bool allowTransparentBackground, CompletionHandler<void(RefPtr<WebCore::SharedBuffer>&&)>&& completionHandler)
1709{
1710 RefPtr localMainFrame = this->localMainFrame();
1711 if (!localMainFrame)
1712 return;
1713
1714 Ref frameView = *localMainFrame->view();
1715 auto snapshotRect = IntRect { rect.value_or(FloatRect { { }, frameView->contentsSize() }) };
1716
1717 RefPtr buffer = ImageBuffer::create(snapshotRect.size(), RenderingMode::PDFDocument, RenderingPurpose::Snapshot, 1, DestinationColorSpace::SRGB(), PixelFormat::BGRA8);
1718 if (!buffer)
1719 return;
1720
1721 drawMainFrameToPDF(*localMainFrame, buffer->context(), snapshotRect, allowTransparentBackground);
1722 completionHandler(buffer->sinkIntoPDFDocument());
1723}
1724
1725void WebPage::drawRectToImage(FrameIdentifier frameID, const PrintInfo& printInfo, const IntRect& rect, const WebCore::IntSize& imageSize, CompletionHandler<void(std::optional<WebCore::ShareableBitmap::Handle>&&)>&& completionHandler)
1726{
1727 PrintContextAccessScope scope { *this };
1728 RefPtr frame = WebProcess::singleton().webFrame(frameID);
1729 RefPtr coreFrame = frame ? frame->coreLocalFrame() : 0;
1730
1731 RefPtr<WebImage> image;
1732
1733#if USE(CG)(defined 1 && 1)
1734 if (coreFrame) {
1735 ASSERT(coreFrame->document()->printing() || pdfDocumentForPrintingFrame(coreFrame.get()))((void)0);
1736 image = WebImage::create(imageSize, ImageOption::Local, DestinationColorSpace::SRGB(), &m_page->chrome().client());
1737 if (!image || !image->context()) {
1738 ASSERT_NOT_REACHED()((void)0);
1739 return completionHandler({ });
1740 }
1741
1742 auto& graphicsContext = *image->context();
1743 float printingScale = static_cast<float>(imageSize.width()) / rect.width();
1744 graphicsContext.scale(printingScale);
1745
1746 if (RetainPtr<PDFDocument> pdfDocument = pdfDocumentForPrintingFrame(coreFrame.get())) {
1747 ASSERT(!m_printContext)((void)0);
1748 graphicsContext.scale(FloatSize(1, -1));
1749 graphicsContext.translate(0, -rect.height());
1750 drawPDFDocument(graphicsContext.protectedPlatformContext().get(), pdfDocument.get(), printInfo, rect);
1751 } else
1752 Ref { *m_printContext }->spoolRect(graphicsContext, rect);
1753 }
1754#endif
1755
1756 std::optional<ShareableBitmap::Handle> handle;
1757 if (image)
1758 handle = image->createHandle(SharedMemory::Protection::ReadOnly);
1759
1760 completionHandler(WTF::move(handle));
1761}
1762
1763void WebPage::drawPagesToPDF(FrameIdentifier frameID, const PrintInfo& printInfo, uint32_t first, uint32_t count, CompletionHandler<void(RefPtr<WebCore::SharedBuffer>&&)>&& callback)
1764{
1765 PrintContextAccessScope scope { *this };
1766 RefPtr<SharedBuffer> pdfPageData;
1767 drawPagesToPDFImpl(frameID, printInfo, first, count, pdfPageData);
1768 callback(WTF::move(pdfPageData));
1769}
1770
1771void WebPage::drawPagesToPDFImpl(FrameIdentifier frameID, const PrintInfo& printInfo, uint32_t first, uint32_t count, RefPtr<SharedBuffer>& pdfPageData)
1772{
1773 RefPtr frame = WebProcess::singleton().webFrame(frameID);
1774 RefPtr coreFrame = frame ? frame->coreLocalFrame() : 0;
1775
1776#if USE(CG)(defined 1 && 1)
1777 if (coreFrame) {
1778 ASSERT(coreFrame->document()->printing() || pdfDocumentForPrintingFrame(coreFrame.get()))((void)0);
1779
1780 FloatRect mediaBox = (m_printContext && m_printContext->pageCount()) ? m_printContext->pageRect(0) : FloatRect { 0, 0, printInfo.availablePaperWidth, printInfo.availablePaperHeight };
1781
1782 RefPtr buffer = ImageBuffer::create(mediaBox.size(), RenderingMode::PDFDocument, RenderingPurpose::Snapshot, 1, DestinationColorSpace::SRGB(), PixelFormat::BGRA8);
1783 if (!buffer)
1784 return;
1785 GraphicsContext& context = buffer->context();
1786
1787 if (RetainPtr<PDFDocument> pdfDocument = pdfDocumentForPrintingFrame(coreFrame.get())) {
1788 ASSERT(!m_printContext)((void)0);
1789 drawPagesToPDFFromPDFDocument(context, pdfDocument.get(), printInfo, mediaBox, first, count);
1790 } else {
1791 if (!m_printContext)
1792 return;
1793
1794 drawPrintContextPagesToGraphicsContext(context, mediaBox, first, count);
1795 }
1796 pdfPageData = ImageBuffer::sinkIntoPDFDocument(WTF::move(buffer));
1797 }
1798#endif
1799}
1800
1801void WebPage::drawPrintContextPagesToGraphicsContext(GraphicsContext& context, const FloatRect& pageRect, uint32_t first, uint32_t count)
1802{
1803 RefPtr printContext = m_printContext;
1804 for (uint32_t page = first; page < first + count; ++page) {
1805 if (page >= printContext->pageCount())
1806 break;
1807
1808 context.beginPage(pageRect);
1809
1810 context.scale(FloatSize(1, -1));
1811 context.translate(0, -printContext->pageRect(page).height());
1812 printContext->spoolPage(context, page, printContext->pageRect(page).width());
1813
1814 context.endPage();
1815 }
1816}
1817
1818void WebPage::drawPrintingRectToSnapshot(RemoteSnapshotIdentifier snapshotIdentifier, WebCore::FrameIdentifier frameID, const PrintInfo& printInfo, const WebCore::IntRect& rect, const WebCore::IntSize& imageSize, CompletionHandler<void(bool)>&& completionHandler)
1819{
1820 RefPtr frame = WebProcess::singleton().webFrame(frameID);
1821 if (!frame) {
1822 completionHandler(false);
1823 return;
1824 }
1825
1826 RefPtr coreFrame = frame->coreLocalFrame();
1827 if (!coreFrame) {
1828 completionHandler(false);
1829 return;
1830 }
1831
1832 if (pdfDocumentForPrintingFrame(coreFrame.get())) {
1833 // Can't do this remotely.
1834 completionHandler(false);
1835 return;
1836 }
1837 ASSERT(coreFrame->document()->printing())((void)0);
1838 PrintContextAccessScope scope { *this };
1839
1840 Ref remoteRenderingBackend = ensureRemoteRenderingBackendProxy();
1841 m_remoteSnapshotState = {
1842 snapshotIdentifier,
1843 remoteRenderingBackend->createSnapshotRecorder(snapshotIdentifier),
1844 MainRunLoopSuccessCallbackAggregator::create([completionHandler = WTF::move(completionHandler)] (bool success) mutable {
1845 completionHandler(success);
1846 })
1847 };
1848 GraphicsContext& context = m_remoteSnapshotState->recorder.get();
1849
1850 float printingScale = static_cast<float>(imageSize.width()) / rect.width();
1851 context.scale(printingScale);
1852
1853 Ref { *m_printContext }->spoolRect(context, rect);
1854
1855 remoteRenderingBackend->sinkSnapshotRecorderIntoSnapshotFrame(WTF::move(m_remoteSnapshotState->recorder), frameID, Ref { m_remoteSnapshotState->callback }->chain());
1856 m_remoteSnapshotState = std::nullopt;
1857}
1858
1859void WebPage::drawPrintingPagesToSnapshot(RemoteSnapshotIdentifier snapshotIdentifier, FrameIdentifier frameID, const PrintInfo& printInfo, uint32_t first, uint32_t count, CompletionHandler<void(std::optional<WebCore::FloatSize>)>&& completionHandler)
1860{
1861 RefPtr frame = WebProcess::singleton().webFrame(frameID);
1862 if (!frame) {
1863 completionHandler({ });
1864 return;
1865 }
1866
1867 RefPtr coreFrame = frame->coreLocalFrame();
1868 if (!coreFrame) {
1869 completionHandler({ });
1870 return;
1871 }
1872
1873 if (pdfDocumentForPrintingFrame(coreFrame.get())) {
1874 // Can't do this remotely.
1875 completionHandler({ });
1876 return;
1877 }
1878
1879 if (!m_printContext) {
1880 completionHandler({ });
1881 return;
1882 }
1883
1884 PrintContextAccessScope scope { *this };
1885 ASSERT(coreFrame->document()->printing())((void)0);
1886
1887 FloatRect mediaBox = (m_printContext && m_printContext->pageCount()) ? m_printContext->pageRect(0) : FloatRect { 0, 0, printInfo.availablePaperWidth, printInfo.availablePaperHeight };
1888
1889 Ref remoteRenderingBackend = ensureRemoteRenderingBackendProxy();
1890 m_remoteSnapshotState = {
1891 snapshotIdentifier,
1892 remoteRenderingBackend->createSnapshotRecorder(snapshotIdentifier),
1893 MainRunLoopSuccessCallbackAggregator::create([completionHandler = WTF::move(completionHandler), snapshotSize = mediaBox.size()] (bool success) mutable {
1894 completionHandler(success ? std::optional<FloatSize>(snapshotSize) : std::nullopt);
1895 })
1896 };
1897
1898 GraphicsContext& context = m_remoteSnapshotState->recorder.get();
1899
1900 drawPrintContextPagesToGraphicsContext(context, mediaBox, first, count);
1901
1902 remoteRenderingBackend->sinkSnapshotRecorderIntoSnapshotFrame(WTF::move(m_remoteSnapshotState->recorder), frameID, Ref { m_remoteSnapshotState->callback }->chain());
1903 m_remoteSnapshotState = std::nullopt;
1904}
1905
1906void WebPage::handleAlternativeTextUIResult(const String& result)
1907{
1908 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
1909 if (!frame)
1910 return;
1911
1912 protect(frame->editor())->handleAlternativeTextUIResult(result);
1913}
1914
1915void WebPage::setTextAsync(const String& text)
1916{
1917 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
1918 if (!frame)
1919 return;
1920
1921 if (frame->selection().selection().isContentEditable()) {
1922 UserTypingGestureIndicator indicator(*frame);
1923 protect(frame->selection())->selectAll();
1924 if (text.isEmpty())
1925 protect(frame->editor())->deleteSelectionWithSmartDelete(false);
1926 else
1927 protect(frame->editor())->insertText(text, nullptr, TextEventInputKeyboard);
1928 return;
1929 }
1930
1931 if (RefPtr input = dynamicDowncast<HTMLInputElement>(m_focusedElement)) {
1932 input->setValueForUser(text);
1933 return;
1934 }
1935
1936 ASSERT_NOT_REACHED()((void)0);
1937}
1938
1939void WebPage::insertTextAsync(const String& text, const EditingRange& replacementEditingRange, InsertTextOptions&& options)
1940{
1941 platformWillPerformEditingCommand();
1942
1943 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
1944 if (!frame)
1945 return;
1946
1947 UserGestureIndicator gestureIndicator { options.processingUserGesture ? IsProcessingUserGesture::Yes : IsProcessingUserGesture::No, frame->document() };
1948 std::optional<UserTypingGestureIndicator> userTypingGestureIndicator;
1949 if (options.processingUserGesture)
1950 userTypingGestureIndicator.emplace(*frame);
1951
1952 bool replacesText = false;
1953 if (replacementEditingRange.location != notFound) {
1954 if (auto replacementRange = EditingRange::toRange(*frame, replacementEditingRange, options.editingRangeIsRelativeTo)) {
1955 SetForScope isSelectingTextWhileInsertingAsynchronously(m_isSelectingTextWhileInsertingAsynchronously, options.suppressSelectionUpdate);
1956 protect(frame->selection())->setSelection(VisibleSelection(*replacementRange));
1957 replacesText = replacementEditingRange.length;
1958 }
1959 }
1960
1961 if (options.registerUndoGroup)
1962 send(Messages::WebPageProxy::RegisterInsertionUndoGrouping());
1963
1964 RefPtr focusedElement = frame->document() ? frame->document()->focusedElement() : nullptr;
1965 if (focusedElement && options.shouldSimulateKeyboardInput)
1966 focusedElement->dispatchEvent(Event::create(eventNames().keydownEvent, Event::CanBubble::Yes, Event::IsCancelable::Yes));
1967
1968 Ref editor = frame->editor();
1969 if (!editor->hasComposition()) {
1970 if (text.isEmpty() && frame->selection().isRange())
1971 editor->deleteWithDirection(SelectionDirection::Backward, TextGranularity::CharacterGranularity, false, true);
1972 else {
1973 // An insertText: might be handled by other responders in the chain if we don't handle it.
1974 // One example is space bar that results in scrolling down the page.
1975 editor->insertText(text, nullptr, replacesText ? TextEventInputAutocompletion : TextEventInputKeyboard);
1976 }
1977 } else
1978 editor->confirmComposition(text);
1979
1980 auto baseWritingDirectionFromInputMode = [&] -> std::optional<WritingDirection> {
1981 auto direction = options.directionFromCurrentInputMode;
1982 if (!direction)
1983 return { };
1984
1985 if (text != "\n"_s)
1986 return { };
1987
1988 auto selection = frame->selection().selection();
1989 if (!selection.isCaret() || !selection.isContentEditable())
1990 return { };
1991
1992 auto start = selection.visibleStart();
1993 if (!isStartOfLine(start) || !isEndOfLine(start))
1994 return { };
1995
1996 if (direction == directionOfEnclosingBlock(start.deepEquivalent()))
1997 return { };
1998
1999 return { direction == TextDirection::LTR ? WritingDirection::LeftToRight : WritingDirection::RightToLeft };
2000 }();
2001
2002 if (baseWritingDirectionFromInputMode) {
2003 editor->setBaseWritingDirection(*baseWritingDirectionFromInputMode);
2004 editor->setTextAlignmentForChangedBaseWritingDirection(*baseWritingDirectionFromInputMode);
2005 }
2006
2007 if (focusedElement && options.shouldSimulateKeyboardInput) {
2008 focusedElement->dispatchEvent(Event::create(eventNames().keyupEvent, Event::CanBubble::Yes, Event::IsCancelable::Yes));
2009 focusedElement->dispatchEvent(Event::create(eventNames().changeEvent, Event::CanBubble::Yes, Event::IsCancelable::Yes));
2010 }
2011}
2012
2013void WebPage::hasMarkedText(CompletionHandler<void(bool)>&& completionHandler)
2014{
2015 RefPtr focusedOrMainFrame = corePage()->focusController().focusedOrMainFrame();
2016 if (!focusedOrMainFrame)
2017 return completionHandler(false);
2018 completionHandler(focusedOrMainFrame->editor().hasComposition());
2019}
2020
2021void WebPage::getMarkedRangeAsync(CompletionHandler<void(const EditingRange&)>&& completionHandler)
2022{
2023 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
2024 if (!frame)
2025 return completionHandler({ });
2026
2027 completionHandler(EditingRange::fromRange(*frame, protect(frame->editor())->compositionRange()));
2028}
2029
2030void WebPage::getSelectedRangeAsync(CompletionHandler<void(const EditingRange& selectedRange, const EditingRange& compositionRange)>&& completionHandler)
2031{
2032 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
2033 if (!frame)
2034 return completionHandler({ }, { });
2035
2036 completionHandler(EditingRange::fromRange(*frame, frame->selection().selection().toNormalizedRange()),
2037 EditingRange::fromRange(*frame, protect(frame->editor())->compositionRange()));
2038}
2039
2040void WebPage::characterIndexForPointAsync(const WebCore::IntPoint& point, CompletionHandler<void(uint64_t)>&& completionHandler)
2041{
2042 RefPtr localMainFrame = this->localMainFrame();
2043 if (!localMainFrame)
2044 return;
2045 constexpr OptionSet<HitTestRequest::Type> hitType { HitTestRequest::Type::ReadOnly, HitTestRequest::Type::Active, HitTestRequest::Type::DisallowUserAgentShadowContent, HitTestRequest::Type::AllowChildFrameContent };
2046 auto result = localMainFrame->eventHandler().hitTestResultAtPoint(point, hitType);
2047 RefPtr frame = result.innerNonSharedNode() ? result.innerNodeFrame() : corePage()->focusController().focusedOrMainFrame();
2048 if (!frame)
2049 return completionHandler({ });
2050 auto range = frame->rangeForPoint(result.roundedPointInInnerNodeFrame());
2051 auto editingRange = EditingRange::fromRange(*frame, range);
2052 completionHandler(editingRange.location);
2053}
2054
2055void WebPage::firstRectForCharacterRangeAsync(const EditingRange& editingRange, CompletionHandler<void(const WebCore::IntRect&, const EditingRange&)>&& completionHandler)
2056{
2057 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
2058 if (!frame)
2059 return completionHandler({ }, { });
2060
2061 auto range = EditingRange::toRange(*frame, editingRange);
2062 if (!range)
2063 return completionHandler({ }, editingRange);
2064
2065 auto rect = RefPtr(frame->view())->contentsToWindow(protect(frame->editor())->firstRectForRange(*range));
2066 auto startPosition = makeContainerOffsetPosition(range->start);
2067
2068 auto endPosition = endOfLine(startPosition);
2069 if (endPosition.isNull())
2070 endPosition = startPosition;
2071 else if (endPosition.affinity() == Affinity::Downstream && inSameLine(startPosition, endPosition)) {
2072 auto nextLineStartPosition = positionOfNextBoundaryOfGranularity(endPosition, TextGranularity::LineGranularity, SelectionDirection::Forward);
2073 if (nextLineStartPosition.isNotNull() && endPosition < nextLineStartPosition)
2074 endPosition = nextLineStartPosition;
2075 }
2076
2077 auto endBoundary = makeBoundaryPoint(endPosition);
2078 if (!endBoundary)
2079 return completionHandler({ }, editingRange);
2080
2081 auto rangeForFirstLine = EditingRange::fromRange(*frame, makeSimpleRange(range->start, WTF::move(endBoundary)));
2082
2083 rangeForFirstLine.location = std::min(std::max(rangeForFirstLine.location, editingRange.location), editingRange.location + editingRange.length);
2084 rangeForFirstLine.length = std::min(rangeForFirstLine.location + rangeForFirstLine.length, editingRange.location + editingRange.length) - rangeForFirstLine.location;
2085
2086 completionHandler(rect, rangeForFirstLine);
2087}
2088
2089void WebPage::setCompositionAsync(const String& text, const Vector<CompositionUnderline>& underlines, const Vector<CompositionHighlight>& highlights, const HashMap<String, Vector<CharacterRange>>& annotations, const EditingRange& selection, const EditingRange& replacementEditingRange)
2090{
2091 platformWillPerformEditingCommand();
2092
2093 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
2094 if (!frame)
2095 return;
2096
2097 if (frame->selection().selection().isContentEditable()) {
2098 if (replacementEditingRange.location != notFound) {
2099 if (auto replacementRange = EditingRange::toRange(*frame, replacementEditingRange))
2100 protect(frame->selection())->setSelection(VisibleSelection(*replacementRange));
2101 }
2102 protect(frame->editor())->setComposition(text, underlines, highlights, annotations, selection.location, selection.location + selection.length);
2103 }
2104}
2105
2106void WebPage::setWritingSuggestion(const String& fullTextWithPrediction, const EditingRange& selection)
2107{
2108 platformWillPerformEditingCommand();
2109
2110 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
2111 if (!frame)
2112 return;
2113
2114 protect(frame->editor())->setWritingSuggestion(fullTextWithPrediction, { selection.location, selection.length });
2115}
2116
2117void WebPage::confirmCompositionAsync()
2118{
2119 platformWillPerformEditingCommand();
2120
2121 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
2122 if (!frame)
2123 return;
2124
2125 protect(frame->editor())->confirmComposition();
2126}
2127
2128void WebPage::getInformationFromImageData(const Vector<uint8_t>& data, CompletionHandler<void(Expected<std::pair<String, Vector<IntSize>>, WebCore::ImageDecodingError>&&)>&& completionHandler)
2129{
2130 if (m_isClosed)
2131 return completionHandler(makeUnexpected(ImageDecodingError::Internal));
2132
2133 if (SVGImage::isDataDecodable(m_page->settings(), data.span()))
2134 return completionHandler(std::make_pair(String { "public.svg-image"_s }, Vector<IntSize> { }));
2135
2136 completionHandler(utiAndAvailableSizesFromImageData(data.span()));
2137}
2138
2139void WebPage::insertTextPlaceholder(const IntSize& size, CompletionHandler<void(const std::optional<WebCore::ElementContext>&)>&& completionHandler)
2140{
2141 // Inserting the placeholder may run JavaScript, which can do anything, including frame destruction.
2142 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
2143 if (!frame)
2144 return completionHandler({ });
2145
2146 auto placeholder = protect(frame->editor())->insertTextPlaceholder(size);
2147 completionHandler(placeholder ? contextForElement(*placeholder) : std::nullopt);
2148}
2149
2150void WebPage::removeTextPlaceholder(const ElementContext& placeholder, CompletionHandler<void()>&& completionHandler)
2151{
2152 if (auto element = elementForContext(placeholder)) {
2153 if (RefPtr frame = element->document().frame())
2154 protect(frame->editor())->removeTextPlaceholder(downcast<TextPlaceholderElement>(*element));
2155 }
2156 completionHandler();
2157}
2158
2159void WebPage::setObscuredContentInsetsFenced(const FloatBoxExtent& obscuredContentInsets, const WTF::MachSendRight& machSendRight)
2160{
2161 protect(drawingArea())->addFence(machSendRight);
2162 setObscuredContentInsets(obscuredContentInsets);
2163}
2164
2165void WebPage::willCommitLayerTree(RemoteLayerTreeTransaction& layerTransaction, WebCore::FrameIdentifier rootFrameID)
2166{
2167 RefPtr rootFrame = WebProcess::singleton().webFrame(rootFrameID);
2168 if (!rootFrame)
2169 return;
2170
2171 RefPtr localRootFrame = rootFrame->coreLocalFrame();
2172 if (!localRootFrame)
2173 return;
2174
2175 RefPtr frameView = localRootFrame->view();
2176 if (!frameView)
2177 return;
2178
2179 Ref page = *corePage();
2180
2181#if ENABLE(THREADED_ANIMATIONS)(defined 1 && 1)
2182 if (auto* acceleratedTimelinesUpdater = page->acceleratedTimelinesUpdater())
2183 layerTransaction.setTimelinesUpdate(acceleratedTimelinesUpdater->takeTimelinesUpdate());
2184#endif
2185
2186 layerTransaction.setContentsSize(frameView->contentsSize());
2187 layerTransaction.setScrollGeometryContentSize(frameView->scrollGeometryContentSize());
2188 layerTransaction.setScrollOrigin(frameView->scrollOrigin());
2189 layerTransaction.setScrollPosition(frameView->scrollPosition());
2190
2191 m_pendingThemeColorChange = false;
2192 m_pendingPageExtendedBackgroundColorChange = false;
2193 m_pendingSampledPageTopColorChange = false;
2194}
2195
2196void WebPage::willCommitMainFrameData(MainFrameData& data, const TransactionID& transactionID)
2197{
2198 RefPtr mainFrameView = localMainFrameView();
2199 if (!mainFrameView)
2200 return;
2201
2202 Ref page = *corePage();
2203 data.pageScaleFactor = page->pageScaleFactor();
2204 data.themeColor = page->themeColor();
2205 data.pageExtendedBackgroundColor = page->pageExtendedBackgroundColor();
2206 data.sampledPageTopColor = page->sampledPageTopColor();
2207
2208 if (std::exchange(m_needsFixedContainerEdgesUpdate, false)) {
2209 page->updateFixedContainerEdges(sidesRequiringFixedContainerEdges());
2210 data.fixedContainerEdges = page->fixedContainerEdges();
2211 }
2212
2213 data.baseLayoutViewportSize = mainFrameView->baseLayoutViewportSize();
2214 data.minStableLayoutViewportOrigin = mainFrameView->minStableLayoutViewportOrigin();
2215 data.maxStableLayoutViewportOrigin = mainFrameView->maxStableLayoutViewportOrigin();
2216
2217#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
2218 data.scaleWasSetByUIProcess = scaleWasSetByUIProcess();
2219 data.minimumScaleFactor = m_viewportConfiguration.minimumScale();
2220 data.maximumScaleFactor = m_viewportConfiguration.maximumScale();
2221 data.initialScaleFactor = m_viewportConfiguration.initialScale();
2222 data.viewportMetaTagInteractiveWidget = m_viewportConfiguration.viewportArguments().interactiveWidget;
2223 data.viewportMetaTagWidth = m_viewportConfiguration.viewportArguments().width;
2224 data.viewportMetaTagWidthWasExplicit = m_viewportConfiguration.viewportArguments().widthWasExplicit;
2225 data.viewportMetaTagCameFromImageDocument = m_viewportConfiguration.viewportArguments().type == ViewportArguments::Type::ImageDocument;
2226 data.avoidsUnsafeArea = m_viewportConfiguration.avoidsUnsafeArea();
2227 data.isInStableState = m_isInStableState;
2228 data.allowsUserScaling = allowsUserScaling();
2229 if (m_pendingDynamicViewportSizeUpdateID) {
2230 data.dynamicViewportSizeUpdateID = *m_pendingDynamicViewportSizeUpdateID;
2231 m_pendingDynamicViewportSizeUpdateID = std::nullopt;
2232 }
2233 if (m_lastTransactionPageScaleFactor != data.pageScaleFactor) {
2234 m_lastTransactionPageScaleFactor = data.pageScaleFactor;
2235 m_internals->lastTransactionIDWithScaleChange = transactionID;
2236 }
2237#endif
2238
2239 if (hasPendingEditorStateUpdate() || m_needsEditorStateVisualDataUpdate) {
2240 data.editorState = editorState();
2241 m_pendingEditorStateUpdateStatus = PendingEditorStateUpdateStatus::NotScheduled;
2242 m_needsEditorStateVisualDataUpdate = false;
2243 }
2244
2245#if ENABLE(SCROLL_STRETCH_NOTIFICATIONS)(defined ENABLE_SCROLL_STRETCH_NOTIFICATIONS && ENABLE_SCROLL_STRETCH_NOTIFICATIONS
)
2246 auto scrollOrigin = mainFrameView->scrollOrigin();
2247 auto scrollPosition = mainFrameView->scrollPosition();
2248 data.topScrollStretch = static_cast<uint64_t>(std::max(0, -scrollOrigin.y() - scrollPosition.y()));
2249#endif
2250}
2251
2252void WebPage::didFlushLayerTreeAtTime(MonotonicTime timestamp, bool flushSucceeded)
2253{
2254#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
2255 if (m_oldestNonStableUpdateVisibleContentRectsTimestamp != MonotonicTime()) {
2256 Seconds elapsed = timestamp - m_oldestNonStableUpdateVisibleContentRectsTimestamp;
2257 m_oldestNonStableUpdateVisibleContentRectsTimestamp = MonotonicTime();
2258
2259 m_estimatedLatency = m_estimatedLatency * 0.80 + elapsed * 0.20;
2260 }
2261#else
2262 UNUSED_PARAM(timestamp)(void)timestamp;
2263#endif
2264#if ENABLE(GPU_PROCESS)(defined 1 && 1)
2265 if (!flushSucceeded) {
2266 if (RefPtr proxy = m_remoteRenderingBackendProxy)
2267 proxy->didBecomeUnresponsive();
2268 }
2269#endif
2270}
2271
2272bool WebPage::isSpeaking() const
2273{
2274 auto sendResult = const_cast<WebPage*>(this)->sendSync(Messages::WebPageProxy::GetIsSpeaking());
2275 auto [result] = sendResult.takeReplyOr(false);
2276 return result;
2277}
2278
2279bool WebPage::shouldAllowSingleClickToChangeSelection(WebCore::Node& targetNode, const WebCore::VisibleSelection& newSelection)
2280{
2281#if !PLATFORM(MAC)(defined 1 && 1) || HAVE(APPKIT_GESTURES_SUPPORT)(defined HAVE_APPKIT_GESTURES_SUPPORT && HAVE_APPKIT_GESTURES_SUPPORT
)
2282#if HAVE(APPKIT_GESTURES_SUPPORT)(defined HAVE_APPKIT_GESTURES_SUPPORT && HAVE_APPKIT_GESTURES_SUPPORT
)
2283 if (!m_page->settings().useAppKitGestures())
2284 return true;
2285#endif
2286
2287 if (RefPtr editableRoot = newSelection.rootEditableElement(); editableRoot && editableRoot == targetNode.rootEditableElement()) {
2288 // FIXME: This logic should be made consistent for both macOS and iOS.
2289#if PLATFORM(MAC)(defined 1 && 1)
2290 return false;
2291#else
2292 // Text interaction gestures will handle selection in the case where we are already editing the node. In the case where we're
2293 // just starting to focus an editable element by tapping on it, only change the selection if we weren't already showing an
2294 // input view prior to handling the tap.
2295 return !(m_completingSyntheticClick ? m_wasShowingInputViewForFocusedElementDuringLastPotentialTap : m_isShowingInputViewForFocusedElement);
2296#endif
2297 }
2298#endif // !PLATFORM(MAC) || HAVE(APPKIT_GESTURES_SUPPORT)
2299
2300 return true;
2301}
2302
2303void WebPage::selectWithGesture(const IntPoint& point, GestureType gestureType, GestureRecognizerState gestureState, bool isInteractingWithFocusedElement, CompletionHandler<void(const WebCore::IntPoint&, GestureType, GestureRecognizerState, OptionSet<SelectionFlags>)>&& completionHandler)
2304{
2305 if (gestureState == GestureRecognizerState::Began)
2306 updateFocusBeforeSelectingTextAtLocation(point);
2307
2308 RefPtr frame = m_page->focusController().focusedOrMainFrame();
2309 if (!frame) {
2310 completionHandler({ }, gestureType, gestureState, { });
2311 return;
2312 }
2313
2314 VisiblePosition position = visiblePositionInFocusedNodeForPoint(*frame, point, isInteractingWithFocusedElement);
2315
2316 if (position.isNull()) {
2317 completionHandler(point, gestureType, gestureState, { });
2318 return;
2319 }
2320 std::optional<SimpleRange> range;
2321 OptionSet<SelectionFlags> flags;
2322 GestureRecognizerState wkGestureState = gestureState;
2323 switch (gestureType) {
2324 case GestureType::PhraseBoundary: {
2325 if (!frame->editor().hasComposition())
2326 break;
2327 auto markedRange = WTF::protect(frame->editor())->compositionRange();
2328 auto startPosition = VisiblePosition { makeDeprecatedLegacyPosition(markedRange->start) };
2329 position = std::clamp(position, startPosition, VisiblePosition { makeDeprecatedLegacyPosition(markedRange->end) });
2330 if (wkGestureState != GestureRecognizerState::Began)
2331 flags = distanceBetweenPositions(startPosition, frame->selection().selection().start()) != distanceBetweenPositions(startPosition, position) ? SelectionFlags::PhraseBoundaryChanged : OptionSet<SelectionFlags> { };
2332 else
2333 flags = SelectionFlags::PhraseBoundaryChanged;
2334 range = makeSimpleRange(position);
2335 break;
2336 }
2337
2338 case GestureType::OneFingerTap: {
2339 auto [adjustedPosition, withinWordBoundary] = wordBoundaryForPositionWithoutCrossingLine(position);
2340 if (withinWordBoundary == WithinWordBoundary::Yes)
2341 flags = SelectionFlags::WordIsNearTap;
2342 range = makeSimpleRange(adjustedPosition);
2343 break;
2344 }
2345
2346 case GestureType::Loupe:
2347 if (position.rootEditableElement())
2348 range = makeSimpleRange(position);
2349 else {
2350#if !PLATFORM(MACCATALYST)(defined WTF_PLATFORM_MACCATALYST && WTF_PLATFORM_MACCATALYST
)
2351 range = wordRangeFromPosition(position);
2352#else
2353 switch (wkGestureState) {
2354 case GestureRecognizerState::Began:
2355 m_startingGestureRange = makeSimpleRange(position);
2356 break;
2357 case GestureRecognizerState::Changed:
2358 if (m_startingGestureRange) {
2359 auto& start = m_startingGestureRange->start;
2360 if (makeDeprecatedLegacyPosition(start) < position)
2361 range = makeSimpleRange(start, position);
2362 else
2363 range = makeSimpleRange(position, start);
2364 }
2365 break;
2366 case GestureRecognizerState::Ended:
2367 case GestureRecognizerState::Cancelled:
2368 m_startingGestureRange = std::nullopt;
2369 break;
2370 case GestureRecognizerState::Failed:
2371 case GestureRecognizerState::Possible:
2372 ASSERT_NOT_REACHED()((void)0);
2373 break;
2374 }
2375#endif
2376 }
2377 break;
2378
2379 case GestureType::TapAndAHalf:
2380 switch (wkGestureState) {
2381 case GestureRecognizerState::Began:
2382 range = wordRangeFromPosition(position);
2383 if (range)
2384 m_currentWordRange = { { *range } };
2385 else
2386 m_currentWordRange = std::nullopt;
2387 break;
2388 case GestureRecognizerState::Changed:
2389 if (!m_currentWordRange)
2390 break;
2391 range = m_currentWordRange;
2392 if (position < makeDeprecatedLegacyPosition(range->start))
2393 range->start = *makeBoundaryPoint(position);
2394 if (position > makeDeprecatedLegacyPosition(range->end))
2395 range->end = *makeBoundaryPoint(position);
2396 break;
2397 case GestureRecognizerState::Ended:
2398 case GestureRecognizerState::Cancelled:
2399 m_currentWordRange = std::nullopt;
2400 break;
2401 case GestureRecognizerState::Failed:
2402 case GestureRecognizerState::Possible:
2403 ASSERT_NOT_REACHED()((void)0);
2404 break;
2405 }
2406 break;
2407
2408 case GestureType::OneFingerDoubleTap:
2409 if (atBoundaryOfGranularity(position, TextGranularity::LineGranularity, SelectionDirection::Forward)) {
2410 // Double-tap at end of line only places insertion point there.
2411 // This helps to get the callout for pasting at ends of lines,
2412 // paragraphs, and documents.
2413 range = makeSimpleRange(position);
2414 } else
2415 range = wordRangeFromPosition(position);
2416 break;
2417
2418 case GestureType::TwoFingerSingleTap:
2419 // Single tap with two fingers selects the entire paragraph.
2420 range = enclosingTextUnitOfGranularity(position, TextGranularity::ParagraphGranularity, SelectionDirection::Forward);
2421 break;
2422
2423 case GestureType::OneFingerTripleTap:
2424 if (atBoundaryOfGranularity(position, TextGranularity::LineGranularity, SelectionDirection::Forward)) {
2425 // Triple-tap at end of line only places insertion point there.
2426 // This helps to get the callout for pasting at ends of lines, paragraphs, and documents.
2427 range = makeSimpleRange(position);
2428 } else
2429 range = enclosingTextUnitOfGranularity(position, TextGranularity::ParagraphGranularity, SelectionDirection::Forward);
2430 break;
2431
2432 default:
2433 break;
2434 }
2435 if (range)
2436 WTF::protect(frame->selection())->setSelectedRange(range, position.affinity(), WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered::Yes);
2437
2438 completionHandler(point, gestureType, gestureState, flags);
2439}
2440
2441void WebPage::updateFocusBeforeSelectingTextAtLocation(const IntPoint& point)
2442{
2443 static constexpr OptionSet hitType { HitTestRequest::Type::ReadOnly, HitTestRequest::Type::Active, HitTestRequest::Type::AllowVisibleChildFrameContentOnly };
2444 RefPtr localMainFrame = WTF::protect(m_page)->localMainFrame();
2445 if (!localMainFrame)
2446 return;
2447
2448 auto result = localMainFrame->eventHandler().hitTestResultAtPoint(point, hitType);
2449 RefPtr hitNode = result.innerNode();
2450 if (!hitNode || !hitNode->renderer())
2451 return;
2452
2453 RefPtr frame = result.innerNodeFrame();
2454 m_page->focusController().setFocusedFrame(frame.get());
2455
2456 if (!result.isOverWidget())
2457 return;
2458
2459#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
2460 if (RefPtr pluginView = pluginViewForFrame(frame.get()))
2461 pluginView->focusPluginElement();
2462#endif
2463}
2464
2465IntRect WebPage::rootViewInteractionBounds(const Node& node)
2466{
2467 RefPtr frame = node.document().frame();
2468 if (!frame)
2469 return { };
2470
2471 RefPtr view = frame->view();
2472 if (!view)
2473 return { };
2474
2475 return view->contentsToRootView(absoluteInteractionBounds(node));
2476}
2477
2478IntRect WebPage::absoluteInteractionBounds(const Node& node)
2479{
2480 RefPtr frame = node.document().frame();
2481 if (!frame)
2482 return { };
2483
2484 RefPtr view = frame->view();
2485 if (!view)
2486 return { };
2487
2488 CheckedPtr renderer = node.renderer();
2489 if (!renderer)
2490 return { };
2491
2492 if (CheckedPtr box = dynamicDowncast<RenderBox>(*renderer)) {
2493 FloatRect rect;
2494 // FIXME: want borders or not?
2495 if (box->style().isOverflowVisible())
2496 rect = box->layoutOverflowRect();
2497 else
2498 rect = box->clientBoxRect();
2499 return box->localToAbsoluteQuad(rect).enclosingBoundingBox();
2500 }
2501
2502 CheckedRef style = renderer->style();
2503 FloatRect boundingBox = renderer->absoluteBoundingBoxRect(true /* use transforms*/);
2504 // This is wrong. It's subtracting borders after converting to absolute coords on something that probably doesn't represent a rectangular element.
2505 boundingBox.move(WebCore::Style::evaluate<float>(style->usedBorderLeftWidth(), WebCore::Style::ZoomNeeded { }), WebCore::Style::evaluate<float>(style->usedBorderTopWidth(), WebCore::Style::ZoomNeeded { }));
2506 boundingBox.setWidth(boundingBox.width() - WebCore::Style::evaluate<float>(style->usedBorderLeftWidth(), WebCore::Style::ZoomNeeded { }) - WebCore::Style::evaluate<float>(style->usedBorderRightWidth(), WebCore::Style::ZoomNeeded { }));
2507 boundingBox.setHeight(boundingBox.height() - WebCore::Style::evaluate<float>(style->usedBorderBottomWidth(), WebCore::Style::ZoomNeeded { }) - WebCore::Style::evaluate<float>(style->usedBorderTopWidth(), WebCore::Style::ZoomNeeded { }));
2508 return enclosingIntRect(boundingBox);
2509}
2510
2511static IntRect elementBoundsInFrame(const LocalFrame& frame, const Element& focusedElement)
2512{
2513 WTF::protect(frame.document())->updateLayout(LayoutOptions::IgnorePendingStylesheets);
2514
2515 if (focusedElement.hasTagName(HTMLNames::textareaTag) || focusedElement.hasTagName(HTMLNames::inputTag) || focusedElement.hasTagName(HTMLNames::selectTag))
2516 return WebPage::absoluteInteractionBounds(focusedElement);
2517
2518 if (RefPtr rootEditableElement = focusedElement.rootEditableElement())
2519 return WebPage::absoluteInteractionBounds(*rootEditableElement);
2520
2521 return { };
2522}
2523
2524IntPoint WebPage::constrainPoint(const IntPoint& point, const LocalFrame& frame, const Element& focusedElement)
2525{
2526 ASSERT(&focusedElement.document() == frame.document())((void)0);
2527 const int DEFAULT_CONSTRAIN_INSET = 2;
2528 IntRect innerFrame = elementBoundsInFrame(frame, focusedElement);
2529 IntPoint constrainedPoint = point;
2530
2531 int minX = innerFrame.x() + DEFAULT_CONSTRAIN_INSET;
2532 int maxX = innerFrame.maxX() - DEFAULT_CONSTRAIN_INSET;
2533 int minY = innerFrame.y() + DEFAULT_CONSTRAIN_INSET;
2534 int maxY = innerFrame.maxY() - DEFAULT_CONSTRAIN_INSET;
2535
2536 if (point.x() < minX)
2537 constrainedPoint.setX(minX);
2538 else if (point.x() > maxX)
2539 constrainedPoint.setX(maxX);
2540
2541 if (point.y() < minY)
2542 constrainedPoint.setY(minY);
2543 else if (point.y() >= maxY)
2544 constrainedPoint.setY(maxY);
2545
2546 return constrainedPoint;
2547}
2548
2549VisiblePosition WebPage::visiblePositionInFocusedNodeForPoint(const LocalFrame& frame, const IntPoint& point, bool isInteractingWithFocusedElement)
2550{
2551 IntPoint adjustedPoint(WTF::protect(frame.view())->rootViewToContents(point));
2552 IntPoint constrainedPoint = m_focusedElement && isInteractingWithFocusedElement ? WebPage::constrainPoint(adjustedPoint, frame, WTF::protect(*m_focusedElement)) : adjustedPoint;
2553 return frame.visiblePositionForPoint(constrainedPoint);
2554}
2555
2556InteractionInformationAtPosition WebPage::positionInformation(const InteractionInformationRequest& request)
2557{
2558 return WebKit::positionInformationForWebPage(*this, request);
2559}
2560
2561void WebPage::requestPositionInformation(const InteractionInformationRequest& request)
2562{
2563 sendEditorStateUpdate();
2564 send(Messages::WebPageProxy::DidReceivePositionInformation(positionInformation(request)));
2565}
2566
2567bool WebPage::isAssistableElement(Element& element)
2568{
2569 if (is<HTMLSelectElement>(element))
2570 return true;
2571 if (is<HTMLTextAreaElement>(element))
2572 return true;
2573 if (RefPtr inputElement = dynamicDowncast<HTMLInputElement>(element)) {
2574 // FIXME: This laundry list of types is not a good way to factor this. Need a suitable function on HTMLInputElement itself.
2575#if ENABLE(INPUT_TYPE_WEEK_PICKER)(defined 0 && 0)
2576 if (inputElement->isWeekField())
2577 return true;
2578#endif
2579 return inputElement->isTextField() || inputElement->isDateField() || inputElement->isDateTimeLocalField() || inputElement->isMonthField() || inputElement->isTimeField() || inputElement->isColorControl();
2580 }
2581 if (is<HTMLIFrameElement>(element))
2582 return false;
2583 return element.isContentEditable();
2584}
2585
2586RefPtr<HTMLAnchorElement> WebPage::containingLinkAnchorElement(Element& element)
2587{
2588 // FIXME: There is code in the drag controller that supports any link, even if it's not an HTMLAnchorElement. Why is this different?
2589 for (Ref currentElement : lineageOfType<HTMLAnchorElement>(element)) {
2590 if (currentElement->isLink())
2591 return currentElement;
2592 }
2593 return nullptr;
2594}
2595
2596void WebPage::selectPositionAtPoint(WebCore::IntPoint point, bool isInteractingWithFocusedElement, CompletionHandler<void()>&& completionHandler)
2597{
2598 SetForScope userIsInteractingChange { m_userIsInteracting, true };
2599
2600 updateFocusBeforeSelectingTextAtLocation(point);
2601
2602 RefPtr frame = m_page->focusController().focusedOrMainFrame();
2603 if (!frame)
2604 return completionHandler();
2605
2606 VisiblePosition position = visiblePositionInFocusedNodeForPoint(*frame, point, isInteractingWithFocusedElement);
2607
2608 if (position.isNotNull())
2609 WTF::protect(frame->selection())->setSelectedRange(makeSimpleRange(position), position.affinity(), WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered::Yes);
2610 completionHandler();
2611}
2612
2613std::optional<SimpleRange> WebPage::rangeForGranularityAtPoint(LocalFrame& frame, const WebCore::IntPoint& point, WebCore::TextGranularity granularity, bool isInteractingWithFocusedElement)
2614{
2615 auto position = visiblePositionInFocusedNodeForPoint(frame, point, isInteractingWithFocusedElement);
2616 switch (granularity) {
2617 case TextGranularity::CharacterGranularity:
2618 return makeSimpleRange(position);
2619 case TextGranularity::WordGranularity:
2620 return wordRangeFromPosition(position);
2621 case TextGranularity::SentenceGranularity:
2622 case TextGranularity::ParagraphGranularity:
2623 return enclosingTextUnitOfGranularity(position, granularity, SelectionDirection::Forward);
2624 case TextGranularity::DocumentGranularity:
2625 // FIXME: Makes no sense that this mutates the current selection and returns null.
2626 protect(frame.selection())->selectAll();
2627 return std::nullopt;
2628 case TextGranularity::LineGranularity:
2629 case TextGranularity::LineBoundary:
2630 case TextGranularity::SentenceBoundary:
2631 case TextGranularity::ParagraphBoundary:
2632 case TextGranularity::DocumentBoundary:
2633 ASSERT_NOT_REACHED()((void)0);
2634 return std::nullopt;
2635 }
2636 ASSERT_NOT_REACHED()((void)0);
2637 return std::nullopt;
2638}
2639
2640void WebPage::setSelectionRange(WebCore::IntPoint point, WebCore::TextGranularity granularity, bool isInteractingWithFocusedElement)
2641{
2642 updateFocusBeforeSelectingTextAtLocation(point);
2643
2644 RefPtr frame = m_page->focusController().focusedOrMainFrame();
2645 if (!frame)
2646 return;
2647
2648#if ENABLE(PDF_PLUGIN)(defined 1 && 1) && PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
2649 // FIXME: Support text selection in embedded PDFs.
2650 if (RefPtr pluginView = focusedPluginViewForFrame(*frame)) {
2651 pluginView->setSelectionRange(point, granularity);
2652 return;
2653 }
2654#endif // ENABLE(PDF_PLUGIN) && PLATFORM(IOS_FAMILY)
2655
2656 auto range = rangeForGranularityAtPoint(*frame, point, granularity, isInteractingWithFocusedElement);
2657 if (range)
2658 protect(frame->selection())->setSelectedRange(*range, Affinity::Upstream, WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered::Yes);
2659
2660 m_initialSelection = range;
2661}
2662
2663void WebPage::updateSelectionWithExtentPointAndBoundary(WebCore::IntPoint point, WebCore::TextGranularity granularity, bool isInteractingWithFocusedElement, TextInteractionSource source, CompletionHandler<void(bool)>&& callback)
2664{
2665 RefPtr frame = m_page->focusController().focusedOrMainFrame();
2666 if (!frame)
2667 return callback(false);
2668
2669#if ENABLE(PDF_PLUGIN)(defined 1 && 1) && PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
2670 if (RefPtr pluginView = focusedPluginViewForFrame(*frame)) {
2671 auto movedEndpoint = pluginView->extendInitialSelection(point, granularity);
2672 return callback(movedEndpoint == SelectionEndpoint::End);
2673 }
2674#endif // ENABLE(PDF_PLUGIN) && PLATFORM(IOS_FAMILY)
2675
2676 auto position = visiblePositionInFocusedNodeForPoint(*frame, point, isInteractingWithFocusedElement);
2677 auto newRange = rangeForGranularityAtPoint(*frame, point, granularity, isInteractingWithFocusedElement);
2678
2679 if (position.isNull() || !m_initialSelection || !newRange)
2680 return callback(false);
2681
2682#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
2683 addTextInteractionSources(source);
2684#endif
2685
2686 auto initialSelectionStartPosition = makeDeprecatedLegacyPosition(m_initialSelection->start);
2687 auto initialSelectionEndPosition = makeDeprecatedLegacyPosition(m_initialSelection->end);
2688
2689 VisiblePosition selectionStart = initialSelectionStartPosition;
2690 VisiblePosition selectionEnd = initialSelectionEndPosition;
2691 if (position > initialSelectionEndPosition)
2692 selectionEnd = makeDeprecatedLegacyPosition(newRange->end);
2693 else if (position < initialSelectionStartPosition)
2694 selectionStart = makeDeprecatedLegacyPosition(newRange->start);
2695
2696 if (auto range = makeSimpleRange(selectionStart, selectionEnd))
2697 protect(frame->selection())->setSelectedRange(range, Affinity::Upstream, WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered::Yes);
2698
2699#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
2700 if (!m_hasAnyActiveTouchPoints) {
2701 // Ensure that `Touch` doesn't linger around in `m_activeTextInteractionSources` after
2702 // the user has ended all active touches.
2703 removeTextInteractionSources(TextInteractionSource::Touch);
2704 }
2705#endif // PLATFORM(IOS_FAMILY)
2706
2707 callback(selectionStart == initialSelectionStartPosition);
2708}
2709
2710void WebPage::updateSelectionWithExtentPoint(WebCore::IntPoint point, bool isInteractingWithFocusedElement, RespectSelectionAnchor respectSelectionAnchor, CompletionHandler<void(bool)>&& callback)
2711{
2712 RefPtr frame = m_page->focusController().focusedOrMainFrame();
2713 if (!frame)
2714 return callback(false);
2715
2716 auto position = visiblePositionInFocusedNodeForPoint(*frame, point, isInteractingWithFocusedElement);
2717
2718 if (position.isNull())
2719 return callback(false);
2720
2721 VisiblePosition selectionStart;
2722 VisiblePosition selectionEnd;
2723
2724 if (respectSelectionAnchor == RespectSelectionAnchor::Yes) {
2725#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
2726 if (m_selectionAnchor == SelectionAnchor::Start) {
2727 selectionStart = frame->selection().selection().visibleStart();
2728 selectionEnd = position;
2729 if (position <= selectionStart) {
2730 selectionStart = selectionStart.previous();
2731 selectionEnd = frame->selection().selection().visibleEnd();
2732 m_selectionAnchor = SelectionAnchor::End;
2733 }
2734 } else {
2735 selectionStart = position;
2736 selectionEnd = frame->selection().selection().visibleEnd();
2737 if (position >= selectionEnd) {
2738 selectionStart = frame->selection().selection().visibleStart();
2739 selectionEnd = selectionEnd.next();
2740 m_selectionAnchor = SelectionAnchor::Start;
2741 }
2742 }
2743#endif // PLATFORM(IOS_FAMILY)
2744 } else {
2745 auto currentStart = frame->selection().selection().visibleStart();
2746 auto currentEnd = frame->selection().selection().visibleEnd();
2747 if (position <= currentStart) {
2748 selectionStart = position;
2749 selectionEnd = currentEnd;
2750 } else if (position >= currentEnd) {
2751 selectionStart = currentStart;
2752 selectionEnd = position;
2753 }
2754 }
2755
2756 if (auto range = makeSimpleRange(selectionStart, selectionEnd))
2757 protect(frame->selection())->setSelectedRange(range, Affinity::Upstream, WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered::Yes);
2758
2759#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
2760 callback(m_selectionAnchor == SelectionAnchor::Start);
2761#else
2762 callback(true);
2763#endif
2764}
2765
2766void WebPage::selectTextWithGranularityAtPoint(WebCore::IntPoint point, WebCore::TextGranularity granularity, bool isInteractingWithFocusedElement, CompletionHandler<void()>&& completionHandler)
2767{
2768#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
2769 if (!m_potentialTapNode) {
2770 setSelectionRange(point, granularity, isInteractingWithFocusedElement);
2771 completionHandler();
2772 return;
2773 }
2774
2775 ASSERT(!m_selectionChangedHandler)((void)0);
2776 if (auto selectionChangedHandler = std::exchange(m_selectionChangedHandler, { }))
2777 selectionChangedHandler();
2778
2779 m_selectionChangedHandler = [point, granularity, isInteractingWithFocusedElement, completionHandler = WTF::move(completionHandler), weakThis = WeakPtr { *this }]() mutable {
2780 RefPtr protectedThis = weakThis.get();
2781 if (!protectedThis) {
2782 completionHandler();
2783 return;
2784 }
2785 protectedThis->setSelectionRange(point, granularity, isInteractingWithFocusedElement);
2786 completionHandler();
2787 };
2788#else
2789 setSelectionRange(point, granularity, isInteractingWithFocusedElement);
2790 completionHandler();
2791#endif
2792}
2793
2794#if ENABLE(TWO_PHASE_CLICKS)(defined 0 && 0)
2795
2796static void dispatchSyntheticMouseMove(LocalFrame& localFrame, const WebCore::FloatPoint& location, OptionSet<WebEventModifier> modifiers, WebCore::PointerID pointerId, WebCore::MouseEventInputSource inputSource)
2797{
2798 auto roundedAdjustedPoint = roundedIntPoint(location);
2799 auto mouseEvent = PlatformMouseEvent(
2800 roundedAdjustedPoint, roundedAdjustedPoint,
2801 MouseButton::None, PlatformEvent::Type::MouseMoved, 0,
2802 platform(modifiers), MonotonicTime::now(),
2803 WebCore::ForceAtClick, WebCore::SyntheticClickType::OneFingerTap,
2804 inputSource,
2805 pointerId
2806 );
2807 localFrame.eventHandler().dispatchSyntheticMouseMove(mouseEvent);
2808}
2809
2810void WebPage::handleSyntheticClick(std::optional<WebCore::FrameIdentifier> frameID, Node& nodeRespondingToClick, const WebCore::FloatPoint& location, OptionSet<WebEventModifier> modifiers, WebCore::PointerID pointerId)
2811{
2812 Ref respondingDocument = nodeRespondingToClick.document();
2813 m_hasHandledSyntheticClick = true;
2814
2815 if (!respondingDocument->settings().contentChangeObserverEnabled() || respondingDocument->quirks().shouldIgnoreContentObservationForClick(nodeRespondingToClick)) {
2816 completeSyntheticClick(frameID, nodeRespondingToClick, location, modifiers, WebCore::SyntheticClickType::OneFingerTap, pointerId);
2817 return;
2818 }
2819
2820 Ref contentChangeObserver = respondingDocument->contentChangeObserver();
2821 contentChangeObserver->setClickTarget(nodeRespondingToClick);
2822 auto targetNodeWentFromHiddenToVisible = contentChangeObserver->hiddenTouchTarget() == &nodeRespondingToClick && ContentChangeObserver::isConsideredVisible(nodeRespondingToClick);
2823 {
2824 LOG_WITH_STREAM(ContentObservation, stream << "handleSyntheticClick: node(" << &nodeRespondingToClick << ") " << location)((void)0);
2825 ContentChangeObserver::MouseMovedScope observingScope(respondingDocument);
2826 RefPtr localRootFrame = this->localRootFrame(frameID);
2827 if (!localRootFrame)
2828 return;
2829 dispatchSyntheticMouseMove(*localRootFrame, location, modifiers, pointerId, m_potentialTapInputSource);
2830 protect(localRootFrame->document())->updateStyleIfNeeded();
2831 if (m_isClosed)
2832 return;
2833 }
2834
2835 if (targetNodeWentFromHiddenToVisible) {
2836 LOG(ContentObservation, "handleSyntheticClick: target node was hidden and now is visible -> hover.")((void)0);
2837 didHandleTapAsHover();
2838 return;
2839 }
2840
2841 auto nodeTriggersFastPath = [&](auto& targetNode) {
2842 RefPtr element = dynamicDowncast<Element>(targetNode);
2843 if (!element)
2844 return false;
2845 if (is<HTMLFormControlElement>(*element))
2846 return true;
2847 if (element->document().quirks().shouldIgnoreAriaForFastPathContentObservationCheck())
2848 return false;
2849 auto ariaRole = AccessibilityObject::ariaRoleToWebCoreRole(element->getAttribute(HTMLNames::roleAttr));
2850 return AccessibilityObject::isARIAControl(ariaRole);
2851 };
2852 auto targetNodeTriggersFastPath = nodeTriggersFastPath(nodeRespondingToClick);
2853
2854 auto observedContentChange = contentChangeObserver->observedContentChange();
2855 auto continueContentObservation = !(observedContentChange == WebCore::ContentChange::Visibility || targetNodeTriggersFastPath);
2856 if (continueContentObservation) {
2857 // Wait for callback to didFinishContentChangeObserving() to decide whether to send the click event.
2858 const Seconds observationDuration = 32_ms;
2859 contentChangeObserver->startContentObservationForDuration(observationDuration);
2860 LOG(ContentObservation, "handleSyntheticClick: Can't decide it yet -> wait.")((void)0);
2861 m_pendingSyntheticClickNode = nodeRespondingToClick;
2862 m_pendingSyntheticClickLocation = location;
2863 m_pendingSyntheticClickModifiers = modifiers;
2864 m_pendingSyntheticClickPointerId = pointerId;
2865 return;
2866 }
2867 contentChangeObserver->stopContentObservation();
2868 callOnMainRunLoop([protectedThis = Ref { *this }, targetNode = Ref<Node>(nodeRespondingToClick), location, modifiers, observedContentChange, pointerId, frameID] {
2869 if (protectedThis->m_isClosed || !protectedThis->corePage())
2870 return;
2871
2872 auto shouldStayAtHoverState = observedContentChange == WebCore::ContentChange::Visibility;
2873 if (shouldStayAtHoverState) {
2874 // The move event caused new contents to appear. Don't send synthetic click event, but just ensure that the mouse is on the most recent content.
2875 if (RefPtr localRootFrame = protectedThis->localRootFrame(frameID))
2876 dispatchSyntheticMouseMove(*localRootFrame, location, modifiers, pointerId, protectedThis->m_potentialTapInputSource);
2877 LOG(ContentObservation, "handleSyntheticClick: Observed meaningful visible change -> hover.")((void)0);
2878 protectedThis->didHandleTapAsHover();
2879 return;
2880 }
2881 LOG(ContentObservation, "handleSyntheticClick: calling completeSyntheticClick -> click.")((void)0);
2882 protectedThis->completeSyntheticClick(frameID, targetNode, location, modifiers, WebCore::SyntheticClickType::OneFingerTap, pointerId);
2883 });
2884}
2885
2886Awaitable<std::optional<WebCore::RemoteUserInputEventData>> WebPage::potentialTapAtPosition(std::optional<WebCore::FrameIdentifier> frameID, WebKit::TapIdentifier requestID, WebCore::FloatPoint position, bool shouldRequestMagnificationInformation, WebKit::WebMouseEventInputSource inputSource)
2887{
2888 m_potentialTapInputSource = platform(inputSource);
2889
2890 RefPtr localMainFrame = protect(*m_page)->localMainFrame();
2891
2892 if (RefPtr localRootFrame = this->localRootFrame(frameID))
2893 m_potentialTapNode = localRootFrame->nodeRespondingToClickEvents(position, m_potentialTapLocation, m_potentialTapSecurityOrigin.get());
2894
2895 RefPtr frameOwner = dynamicDowncast<HTMLFrameOwnerElement>(m_potentialTapNode.get());
2896 if (RefPtr remoteFrame = frameOwner ? dynamicDowncast<RemoteFrame>(frameOwner->contentFrame()) : nullptr) {
2897 RefPtr localFrame = frameOwner->document().frame();
2898 if (RefPtr frameView = localFrame ? localFrame->view() : nullptr) {
2899 if (RefPtr remoteFrameView = remoteFrame->view()) {
2900 RemoteFrameGeometryTransformer transformer(remoteFrameView.releaseNonNull(), frameView.releaseNonNull(), remoteFrame->frameID());
2901 co_return WebCore::RemoteUserInputEventData {
2902 remoteFrame->frameID(),
2903 transformer.transformToRemoteFrameCoordinates(position)
2904 };
2905 }
2906 }
2907 }
2908
2909#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
2910 auto lastTouchLocation = std::exchange(m_lastTouchLocationBeforeTap, { });
2911 bool ignorePotentialTap = [&] {
2912 if (!m_potentialTapNode)
2913 return false;
2914
2915 if (!localMainFrame)
2916 return false;
2917
2918 if (!lastTouchLocation)
2919 return false;
2920
2921 static constexpr auto maxAllowedMovementSquared = 200 * 200;
2922 if ((position - *lastTouchLocation).diagonalLengthSquared() <= maxAllowedMovementSquared)
2923 return false;
2924
2925 FloatPoint adjustedLocation;
2926 RefPtr lastTouchedNode = localMainFrame->nodeRespondingToClickEvents(*lastTouchLocation, adjustedLocation, m_potentialTapSecurityOrigin.get());
2927 return lastTouchedNode != m_potentialTapNode;
2928 }();
2929
2930 if (ignorePotentialTap) {
2931 RELEASE_LOG(ViewGestures, "Ignoring potential tap (distance from last touch: %.0f)", (position - *lastTouchLocation).diagonalLength())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 = (WebKit2LogViewGestures
.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
("Ignoring potential tap (distance from last touch: %.0f)"), "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_LOG32"
) = "Ignoring potential tap (distance from last touch: %.0f)"
; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf
[__builtin_os_log_format_buffer_size("Ignoring potential tap (distance from last touch: %.0f)"
, (position - *lastTouchLocation).diagonalLength())]; _os_log_impl
(&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t
*)__builtin_os_log_format(_os_fmt_buf, "Ignoring potential tap (distance from last touch: %.0f)"
, (position - *lastTouchLocation).diagonalLength()), (uint32_t
)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic
pop
;
2932 m_potentialTapNode = nullptr;
2933 co_return std::nullopt;
2934 }
2935
2936 m_wasShowingInputViewForFocusedElementDuringLastPotentialTap = m_isShowingInputViewForFocusedElement;
2937
2938 RefPtr viewGestureGeometryCollector = m_viewGestureGeometryCollector;
2939
2940 if (shouldRequestMagnificationInformation && m_potentialTapNode && viewGestureGeometryCollector) {
2941 FloatPoint origin = position;
2942 FloatRect absoluteBoundingRect;
2943 bool fitEntireRect;
2944 double viewportMinimumScale;
2945 double viewportMaximumScale;
2946
2947 viewGestureGeometryCollector->computeZoomInformationForNode(*protect(m_potentialTapNode), origin, absoluteBoundingRect, fitEntireRect, viewportMinimumScale, viewportMaximumScale);
2948
2949 bool nodeIsRootLevel = is<WebCore::Document>(*m_potentialTapNode) || is<WebCore::HTMLBodyElement>(*m_potentialTapNode);
2950 bool nodeIsPluginElement = is<WebCore::HTMLPlugInElement>(*m_potentialTapNode);
2951 send(Messages::WebPageProxy::HandleSmartMagnificationInformationForPotentialTap(requestID, absoluteBoundingRect, fitEntireRect, viewportMinimumScale, viewportMaximumScale, nodeIsRootLevel, nodeIsPluginElement));
2952 }
2953
2954 sendTapHighlightForNodeIfNecessary(requestID, m_potentialTapNode.get(), position);
2955#if ENABLE(TWO_PHASE_CLICKS)(defined 0 && 0)
2956 if (RefPtr potentialTapNode = m_potentialTapNode; potentialTapNode && !potentialTapNode->allowsDoubleTapGesture())
2957 send(Messages::WebPageProxy::DisableDoubleTapGesturesDuringTapIfNecessary(requestID));
2958#endif
2959#endif // PLATFORM(IOS_FAMILY)
2960
2961 co_return std::nullopt;
2962}
2963
2964Awaitable<std::optional<WebCore::FrameIdentifier>> WebPage::commitPotentialTap(std::optional<WebCore::FrameIdentifier> frameID, OptionSet<WebEventModifier> modifiers, TransactionID lastLayerTreeTransactionId, WebCore::PointerID pointerId)
2965{
2966 RefPtr frameOwner = dynamicDowncast<HTMLFrameOwnerElement>(m_potentialTapNode.get());
2967 RefPtr remoteFrame = frameOwner ? dynamicDowncast<RemoteFrame>(frameOwner->contentFrame()) : nullptr;
2968 if (remoteFrame)
2969 co_return remoteFrame->frameID();
2970
2971#if ENABLE(TWO_PHASE_CLICKS)(defined 0 && 0)
2972 auto invalidTargetForSingleClick = !m_potentialTapNode;
2973 if (!invalidTargetForSingleClick) {
2974 bool targetRenders = m_potentialTapNode->renderer();
2975 if (RefPtr element = dynamicDowncast<Element>(m_potentialTapNode); element && !targetRenders)
2976 targetRenders = element->renderOrDisplayContentsStyle();
2977 if (RefPtr shadowRoot = dynamicDowncast<ShadowRoot>(m_potentialTapNode); shadowRoot && !targetRenders)
2978 targetRenders = protect(shadowRoot->host())->renderOrDisplayContentsStyle();
2979 invalidTargetForSingleClick = !targetRenders && !is<HTMLAreaElement>(m_potentialTapNode);
2980 }
2981
2982 RefPtr localRootFrame = this->localRootFrame(frameID);
2983
2984 if (invalidTargetForSingleClick) {
2985#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
2986 if (localRootFrame) {
2987 constexpr OptionSet hitType { HitTestRequest::Type::ReadOnly, HitTestRequest::Type::Active, HitTestRequest::Type::AllowVisibleChildFrameContentOnly };
2988 auto roundedPoint = IntPoint { m_potentialTapLocation };
2989 auto result = localRootFrame->eventHandler().hitTestResultAtPoint(roundedPoint, hitType);
2990 localRootFrame->eventHandler().setLastTouchedNode(result.innerNode());
2991 }
2992#endif
2993
2994 commitPotentialTapFailed();
2995 co_return std::nullopt;
2996 }
2997
2998#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
2999 if (localRootFrame)
3000 localRootFrame->eventHandler().setLastTouchedNode(nullptr);
3001#endif
3002
3003 FloatPoint adjustedPoint;
3004 RefPtr nodeRespondingToClick = localRootFrame ? localRootFrame->nodeRespondingToClickEvents(m_potentialTapLocation, adjustedPoint, m_potentialTapSecurityOrigin.get()) : nullptr;
3005 RefPtr frameRespondingToClick = nodeRespondingToClick ? nodeRespondingToClick->document().frame() : nullptr;
3006
3007 if (!frameRespondingToClick) {
3008 commitPotentialTapFailed();
3009 co_return std::nullopt;
3010 }
3011
3012 auto firstTransactionID = WebFrame::fromCoreFrame(*frameRespondingToClick)->firstLayerTreeTransactionIDAfterDidCommitLoad();
3013 if (firstTransactionID
3014 && lastLayerTreeTransactionId.processIdentifier() == firstTransactionID->processIdentifier()
3015 && lastLayerTreeTransactionId.lessThanSameProcess(*firstTransactionID)) {
3016 commitPotentialTapFailed();
3017 co_return std::nullopt;
3018 }
3019
3020 if (m_potentialTapNode == nodeRespondingToClick)
3021 handleSyntheticClick(frameID, *nodeRespondingToClick, adjustedPoint, modifiers, pointerId);
3022 else
3023 commitPotentialTapFailed();
3024#endif // ENABLE(TWO_PHASE_CLICKS)
3025
3026 m_potentialTapNode = nullptr;
3027 m_potentialTapLocation = FloatPoint();
3028 m_potentialTapSecurityOrigin = nullptr;
3029
3030 co_return std::nullopt;
3031}
3032
3033void WebPage::cancelPotentialTapInFrame(WebFrame& frame)
3034{
3035 if (auto selectionChangedHandler = std::exchange(m_selectionChangedHandler, { }))
3036 selectionChangedHandler();
3037
3038 if (m_potentialTapNode) {
3039 RefPtr potentialTapFrame = m_potentialTapNode->document().frame();
3040 if (potentialTapFrame && !potentialTapFrame->tree().isDescendantOf(protect(frame.coreLocalFrame())))
3041 return;
3042 }
3043
3044 m_potentialTapNode = nullptr;
3045 m_potentialTapLocation = FloatPoint();
3046 m_potentialTapSecurityOrigin = nullptr;
3047}
3048
3049void WebPage::cancelPotentialTap()
3050{
3051#if ENABLE(CONTENT_CHANGE_OBSERVER)(defined 0 && 0)
3052 if (RefPtr localMainFrame = protect(*m_page)->localMainFrame())
3053 ContentChangeObserver::didCancelPotentialTap(*localMainFrame);
3054#endif
3055 cancelPotentialTapInFrame(m_mainFrame);
3056}
3057
3058void WebPage::didHandleTapAsHover()
3059{
3060 invokePendingSyntheticClickCallback(SyntheticClickResult::Hover);
3061 send(Messages::WebPageProxy::DidHandleTapAsHover());
3062}
3063
3064void WebPage::sendTapHighlightForNodeIfNecessary(WebKit::TapIdentifier requestID, Node* node, FloatPoint point)
3065{
3066#if ENABLE(TWO_PHASE_CLICKS)(defined 0 && 0)
3067 if (!node)
3068 return;
3069
3070 RefPtr localMainFrame = m_page->localMainFrame();
3071 if (!localMainFrame)
3072 return;
3073
3074 if (m_page->isEditable() && node == protect(localMainFrame->document())->body())
3075 return;
3076
3077 if (RefPtr element = dynamicDowncast<Element>(*node)) {
3078 ASSERT(m_page)((void)0);
3079 localMainFrame->loader().prefetchDNSIfNeeded(element->absoluteLinkURL());
3080 }
3081
3082 RefPtr updatedNode = node;
3083 if (RefPtr area = dynamicDowncast<HTMLAreaElement>(*node)) {
3084 updatedNode = area->imageElement();
3085 if (!updatedNode)
3086 return;
3087 }
3088
3089#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
3090 if (RefPtr pluginView = pluginViewForFrame(protect(updatedNode->document().frame()))) {
3091 if (auto rect = pluginView->highlightRectForTapAtPoint(point)) {
3092#if ENABLE(CSS_TAP_HIGHLIGHT_COLOR)(defined ENABLE_CSS_TAP_HIGHLIGHT_COLOR && ENABLE_CSS_TAP_HIGHLIGHT_COLOR
)
3093 auto highlightColor = RenderTheme::singleton().platformTapHighlightColor();
3094#else
3095 auto highlightColor = Color::transparentBlack;
3096#endif
3097 auto highlightQuads = Vector { FloatQuad { WTF::move(*rect) } };
3098 send(Messages::WebPageProxy::DidGetTapHighlightGeometries(requestID, WTF::move(highlightColor), WTF::move(highlightQuads), { }, { }, { }, { }, true));
3099 return;
3100 }
3101 }
3102#endif // ENABLE(PDF_PLUGIN)
3103
3104 Vector<FloatQuad> quads;
3105 if (CheckedPtr renderer = updatedNode->renderer()) {
3106 renderer->absoluteQuads(quads);
3107#if ENABLE(CSS_TAP_HIGHLIGHT_COLOR)(defined ENABLE_CSS_TAP_HIGHLIGHT_COLOR && ENABLE_CSS_TAP_HIGHLIGHT_COLOR
)
3108 auto highlightColor = renderer->style().tapHighlightColorResolvingCurrentColor();
3109#else
3110 auto highlightColor = Color::transparentBlack;
3111#endif
3112 if (!updatedNode->document().frame()->isMainFrame()) {
3113 RefPtr view = updatedNode->document().frame()->view();
3114 for (auto& quad : quads)
3115 quad = view->contentsToRootView(quad);
3116 }
3117
3118 LayoutRoundedRect::Radii borderRadii;
3119 if (CheckedPtr renderBox = dynamicDowncast<RenderBox>(*renderer))
3120 borderRadii = renderBox->borderRadii();
3121
3122 RefPtr element = dynamicDowncast<Element>(*updatedNode);
3123 bool nodeHasBuiltInClickHandling = element && (is<HTMLFormControlElement>(*element) || is<HTMLAnchorElement>(*element) || is<HTMLLabelElement>(*element) || is<HTMLSummaryElement>(*element) || element->isLink());
3124 send(Messages::WebPageProxy::DidGetTapHighlightGeometries(requestID, highlightColor, quads, roundedIntSize(borderRadii.topLeft()), roundedIntSize(borderRadii.topRight()), roundedIntSize(borderRadii.bottomLeft()), roundedIntSize(borderRadii.bottomRight()), nodeHasBuiltInClickHandling));
3125 }
3126#else
3127 UNUSED_PARAM(requestID)(void)requestID;
3128 UNUSED_PARAM(node)(void)node;
3129 UNUSED_PARAM(point)(void)point;
3130#endif
3131}
3132
3133#if ENABLE(CONTENT_CHANGE_OBSERVER)(defined 0 && 0)
3134void WebPage::didFinishContentChangeObserving(WebCore::FrameIdentifier frameID, WebCore::ContentChange observedContentChange)
3135{
3136 LOG_WITH_STREAM(ContentObservation, stream << "didFinishContentChangeObserving: pending target node(" << m_pendingSyntheticClickNode << ")")((void)0);
3137 if (!m_pendingSyntheticClickNode)
3138 return;
3139 callOnMainRunLoop([
3140 protectedThis = Ref { *this },
3141 targetNode = Ref<Node>(*m_pendingSyntheticClickNode),
3142 originalDocument = WeakPtr<Document, WeakPtrImplWithEventTargetData> { m_pendingSyntheticClickNode->document() },
3143 observedContentChange,
3144 location = m_pendingSyntheticClickLocation,
3145 modifiers = m_pendingSyntheticClickModifiers,
3146 pointerId = m_pendingSyntheticClickPointerId,
3147 inputSource = m_potentialTapInputSource,
3148 frameID
3149 ] {
3150 if (protectedThis->m_isClosed || !protectedThis->corePage())
3151 return;
3152 if (!originalDocument || &targetNode->document() != originalDocument)
3153 return;
3154
3155 // Only dispatch the click if the document didn't get changed by any timers started by the move event.
3156 if (observedContentChange == WebCore::ContentChange::None) {
3157 LOG(ContentObservation, "No change was observed -> click.")((void)0);
3158 protectedThis->completeSyntheticClick(frameID, targetNode, location, modifiers, WebCore::SyntheticClickType::OneFingerTap, pointerId);
3159 return;
3160 }
3161 // Ensure that the mouse is on the most recent content.
3162 LOG(ContentObservation, "Observed meaningful visible change -> hover.")((void)0);
3163 if (RefPtr localRootFrame = protectedThis->localRootFrame(frameID))
3164 dispatchSyntheticMouseMove(*localRootFrame, location, modifiers, pointerId, inputSource);
3165
3166 protectedThis->didHandleTapAsHover();
3167 });
3168 m_pendingSyntheticClickNode = nullptr;
3169 m_pendingSyntheticClickLocation = { };
3170 m_pendingSyntheticClickModifiers = { };
3171 m_pendingSyntheticClickPointerId = 0;
3172}
3173#endif // ENABLE(CONTENT_CHANGE_OBSERVER)
3174
3175void WebPage::invokePendingSyntheticClickCallback(SyntheticClickResult result)
3176{
3177 if (auto callback = std::exchange(m_pendingSyntheticClickCallback, { }))
3178 callback(result);
3179}
3180
3181void WebPage::commitPotentialTapFailed()
3182{
3183 if (auto selectionChangedHandler = std::exchange(m_selectionChangedHandler, { }))
3184 selectionChangedHandler();
3185
3186#if ENABLE(CONTENT_CHANGE_OBSERVER)(defined 0 && 0)
3187 if (RefPtr localMainFrame = protect(*m_page)->localMainFrame())
3188 ContentChangeObserver::didCancelPotentialTap(*localMainFrame);
3189#endif
3190#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
3191 clearSelectionAfterTapIfNeeded();
3192#endif
3193 invokePendingSyntheticClickCallback(SyntheticClickResult::Failed);
3194
3195 send(Messages::WebPageProxy::CommitPotentialTapFailed());
3196 send(Messages::WebPageProxy::DidNotHandleTapAsClick(roundedIntPoint(m_potentialTapLocation)));
3197}
3198
3199void WebPage::completeSyntheticClick(std::optional<WebCore::FrameIdentifier> frameID, Node& nodeRespondingToClick, const WebCore::FloatPoint& location, OptionSet<WebEventModifier> modifiers, SyntheticClickType syntheticClickType, WebCore::PointerID pointerId)
3200{
3201 SetForScope completeSyntheticClickScope { m_completingSyntheticClick, true };
3202 IntPoint roundedAdjustedPoint = roundedIntPoint(location);
3203
3204 // FIXME: Make this function take a root frame's ID instead of taking a frame ID of a non-root frame and replacing it with the root frame.
3205 auto rootFrameID = frameID;
3206 if (RefPtr webFrame = WebProcess::singleton().webFrame(frameID)) {
3207 if (RefPtr frame = webFrame->coreLocalFrame(); frame && !frame->isRootFrame())
3208 rootFrameID = WebFrame::fromCoreFrame(protect(frame->rootFrame()))->frameID();
3209 }
3210
3211 RefPtr localRootFrame = this->localRootFrame(rootFrameID);
3212 if (!localRootFrame) {
3213 invokePendingSyntheticClickCallback(SyntheticClickResult::PageInvalid);
3214 return;
3215 }
3216
3217 RefPtr oldFocusedFrame = m_page->focusController().focusedLocalFrame();
3218 RefPtr<Element> oldFocusedElement = oldFocusedFrame ? oldFocusedFrame->document()->focusedElement() : nullptr;
3219
3220 SetForScope userIsInteractingChange { m_userIsInteracting, true };
3221
3222#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
3223 m_lastInteractionLocation = roundedAdjustedPoint;
3224#endif
3225
3226 // FIXME: Pass caps lock state.
3227 auto platformModifiers = platform(modifiers);
3228
3229 bool handledPress = localRootFrame->eventHandler().handleMousePressEvent(PlatformMouseEvent(roundedAdjustedPoint, roundedAdjustedPoint, MouseButton::Left, PlatformEvent::Type::MousePressed, 1, platformModifiers, MonotonicTime::now(), WebCore::ForceAtClick, syntheticClickType, m_potentialTapInputSource, pointerId)).wasHandled();
3230 if (m_isClosed)
3231 return;
3232
3233 if (auto selectionChangedHandler = std::exchange(m_selectionChangedHandler, { }))
3234 selectionChangedHandler();
3235#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
3236 else if (!handledPress)
3237 clearSelectionAfterTapIfNeeded();
3238#endif
3239
3240 auto releaseEvent = PlatformMouseEvent { roundedAdjustedPoint, roundedAdjustedPoint, MouseButton::Left, PlatformEvent::Type::MouseReleased, 1, platformModifiers, MonotonicTime::now(), ForceAtClick, syntheticClickType, m_potentialTapInputSource, pointerId };
3241 bool handledRelease = localRootFrame->eventHandler().handleMouseReleaseEvent(releaseEvent).wasHandled();
3242 if (m_isClosed)
3243 return;
3244
3245 RefPtr newFocusedFrame = m_page->focusController().focusedLocalFrame();
3246 RefPtr<Element> newFocusedElement = newFocusedFrame ? newFocusedFrame->document()->focusedElement() : nullptr;
3247
3248 if (nodeRespondingToClick.document().settings().contentChangeObserverEnabled()) {
3249 Ref document = nodeRespondingToClick.document();
3250 // Dispatch mouseOut to dismiss tooltip content when tapping on the control bar buttons (cc, settings).
3251 if (document->quirks().needsYouTubeMouseOutQuirk()) {
3252 if (RefPtr frame = document->frame()) {
3253 PlatformMouseEvent event { roundedAdjustedPoint, roundedAdjustedPoint, MouseButton::Left, PlatformEvent::Type::NoType, 0, platformModifiers, MonotonicTime::now(), 0, WebCore::SyntheticClickType::NoTap, m_potentialTapInputSource, pointerId };
3254 if (!nodeRespondingToClick.isConnected())
3255 frame->eventHandler().dispatchSyntheticMouseMove(event);
3256 frame->eventHandler().dispatchSyntheticMouseOut(event);
3257 }
3258 }
3259 }
3260
3261 if (m_isClosed)
3262 return;
3263
3264#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
3265 if (RefPtr pluginElement = dynamicDowncast<HTMLPlugInElement>(nodeRespondingToClick)) {
3266 if (RefPtr pluginWidget = downcast<PluginView>(pluginElement->pluginWidget()))
3267 pluginWidget->handleSyntheticClick(WTF::move(releaseEvent));
3268 }
3269#endif
3270
3271 invokePendingSyntheticClickCallback(SyntheticClickResult::Click);
3272
3273 if ((!handledPress && !handledRelease) || !nodeRespondingToClick.isElementNode())
3274 send(Messages::WebPageProxy::DidNotHandleTapAsClick(roundedIntPoint(location)));
3275
3276 send(Messages::WebPageProxy::DidCompleteSyntheticClick());
3277
3278#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
3279 scheduleLayoutViewportHeightExpansionUpdate();
3280#endif
3281}
3282
3283#endif // ENABLE(TWO_PHASE_CLICKS)
3284
3285} // namespace WebKit
3286
3287#endif // PLATFORM(COCOA)