Bug Summary

File:Source/WebKit/WebProcess/WebPage/Cocoa/WebPageCocoa.mm
Warning:line 631, column 47
Call argument for parameter 'wrapper' is unretained 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 (result.isLocal()) {
631 if (auto token = tokenFromWrapper(result.localObject()->wrapper()))
Call argument for parameter 'wrapper' is unretained and unsafe
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
648 // Get the web area for this frame as the anchor object.
649 RefPtr webArea = cache ? cache->rootObjectForFrame(*coreFrame) : nullptr;
650
651 if (!webArea) {
652 completionHandler({ });
653 return;
654 }
655
656 // Convert IPC criteria to local criteria with this frame's web area as anchor.
657 auto localCriteria = criteria.toSearchCriteria(webArea.get());
658
659 // Since this frame is being searched by a parent frame (via performAccessibilitySearchInRemoteFrame),
660 // only search this frame and its nested child frames - do NOT coordinate with the parent.
661 // The parent already handles its own elements and will merge our results appropriately.
662 auto searchResults = WebCore::performSearchWithCrossProcessCoordination(*webArea, WTF::move(localCriteria));
663
664 completionHandler(convertSearchResultsToRemoteTokens(WTF::move(searchResults)));
665}
666
667void WebPage::continueAccessibilitySearchInParentFrame(WebCore::FrameIdentifier childFrameID, WebCore::AccessibilitySearchCriteriaIPC criteria, CompletionHandler<void(Vector<WebCore::AccessibilityRemoteToken>&&)>&& completionHandler)
668{
669 // Get the main frame's document and AXObjectCache for this (parent) process.
670 RefPtr coreFrame = m_mainFrame->coreLocalFrame();
671 RefPtr document = coreFrame ? coreFrame->document() : nullptr;
672 CheckedPtr cache = document ? document->axObjectCache() : nullptr;
673 if (!cache) {
674 completionHandler({ });
675 return;
676 }
677
678 // Find the AXRemoteFrame via the frame tree and AccessibilityScrollView.
679 RefPtr remoteFrame = dynamicDowncast<WebCore::RemoteFrame>(coreFrame->tree().descendantByFrameID(childFrameID));
680 RefPtr remoteFrameView = remoteFrame ? remoteFrame->view() : nullptr;
681 RefPtr scrollView = dynamicDowncast<WebCore::AccessibilityScrollView>(cache->get(remoteFrameView.get()));
682 RefPtr childRemoteFrame = scrollView ? scrollView->remoteFrame() : nullptr;
683
684 if (!childRemoteFrame) {
685 completionHandler({ });
686 return;
687 }
688
689 // Save the original limit.
690 unsigned originalLimit = criteria.resultsLimit;
691
692 // Get the web area as the anchor and use the child remote frame as the start object.
693 RefPtr webArea = cache->rootObjectForFrame(*coreFrame);
694 if (!webArea) {
695 completionHandler({ });
696 return;
697 }
698
699 // Create local criteria with webArea as anchor and AXRemoteFrame as start object.
700 auto localCriteria = criteria.toSearchCriteria(webArea.get());
701 localCriteria.startObject = childRemoteFrame.get();
702
703 // Perform the search.
704 auto stream = WebCore::AXSearchManager().findMatchingObjectsAsStream(WTF::move(localCriteria));
705
706 // Perform cross-process search coordination if there are nested remote frames in this process.
707 // Pass childFrameID as the excluded frame to prevent re-searching the frame that requested continuation.
708 auto searchResults = WebCore::performCrossProcessSearch(WTF::move(stream), criteria, webArea->treeID(), originalLimit, childFrameID);
709
710 completionHandler(convertSearchResultsToRemoteTokens(WTF::move(searchResults)));
711}
712#endif // PLATFORM(MAC)
713
714#if PLATFORM(MAC)(defined 1 && 1)
715void WebPage::getAccessibilityWebProcessDebugInfo(CompletionHandler<void(WebCore::AXDebugInfo)>&& completionHandler)
716{
717 if (!AXObjectCache::isAppleInternalInstall()) {
718 completionHandler({ });
719 return;
720 }
721
722 bool isAXThreadInitialized = false;
723#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)(defined 1 && 1)
724 isAXThreadInitialized = WebCore::AXObjectCache::isAXThreadInitialized();
725#endif
726 Vector<String> warnings;
727
728 RefPtr focusedFrame = [m_mockAccessibilityElement focusedLocalFrame];
729 RefPtr document = focusedFrame ? focusedFrame->document() : nullptr;
730
731 if (document) {
732 if (CheckedPtr cache = document->axObjectCache()) {
733 auto treeData = cache->treeData();
734 warnings = WTF::move(treeData.warnings);
735 completionHandler({ WebCore::AXObjectCache::accessibilityEnabled(), isAXThreadInitialized, WTF::move(treeData.liveTree), WTF::move(treeData.isolatedTree), WTF::move(warnings), [m_mockAccessibilityElement remoteTokenHash], [accessibilityRemoteTokenData() hash] });
736 return;
737 }
738 warnings.append("No AXObjectCache"_s);
739 } else if (!focusedFrame)
740 warnings.append("No focused LocalFrame found"_s);
741 else
742 warnings.append("Focused LocalFrame has no document"_s);
743
744 completionHandler({ WebCore::AXObjectCache::accessibilityEnabled(), isAXThreadInitialized, emptyString(), emptyString(), WTF::move(warnings), [m_mockAccessibilityElement remoteTokenHash], [accessibilityRemoteTokenData() hash] });
745}
746
747#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)(defined 1 && 1)
748void WebPage::clearAccessibilityIsolatedTree()
749{
750 if (RefPtr page = m_page)
751 page->clearAccessibilityIsolatedTree();
752}
753#endif
754
755#endif // PLATFORM(MAC)
756
757#if ENABLE(APPLE_PAY)(defined 1 && 1)
758WebPaymentCoordinator* WebPage::paymentCoordinator()
759{
760 if (!m_page)
761 return nullptr;
762 return dynamicDowncast<WebPaymentCoordinator>(m_page->paymentCoordinator().client());
763}
764#endif
765
766void WebPage::getContentsAsAttributedString(CompletionHandler<void(const WebCore::AttributedString&)>&& completionHandler)
767{
768 RefPtr localFrame = protect(corePage())->localMainFrame();
769 completionHandler(localFrame ? attributedString(makeRangeSelectingNodeContents(*protect(localFrame->document())), IgnoreUserSelectNone::No) : AttributedString { });
770}
771
772void WebPage::setRemoteObjectRegistry(WebRemoteObjectRegistry* registry)
773{
774 m_remoteObjectRegistry = registry;
775}
776
777WebRemoteObjectRegistry* WebPage::remoteObjectRegistry()
778{
779 return m_remoteObjectRegistry.get();
780}
781
782void WebPage::updateMockAccessibilityElementAfterCommittingLoad()
783{
784 RefPtr mainFrame = dynamicDowncast<WebCore::LocalFrame>(this->mainFrame());
785 RefPtr document = mainFrame ? mainFrame->document() : nullptr;
786 [m_mockAccessibilityElement setHasMainFramePlugin:document ? document->isPluginDocument() : false];
787}
788
789void WebPage::getProcessDisplayName(CompletionHandler<void(String&&)>&& completionHandler)
790{
791#if PLATFORM(MAC)(defined 1 && 1)
792#if ENABLE(SET_WEBCONTENT_PROCESS_INFORMATION_IN_NETWORK_PROCESS)(defined 1 && 1)
793 WebProcess::singleton().getProcessDisplayName(WTF::move(completionHandler));
794#else
795 completionHandler(adoptCF((CFStringRef)_LSCopyApplicationInformationItem(kLSDefaultSessionID, _LSGetCurrentApplicationASN(), _kLSDisplayNameKey)).get());
796#endif
797#else
798 completionHandler({ });
799#endif
800}
801
802static bool rendererIsTransparentOrFullyClipped(const RenderObject& renderer)
803{
804 CheckedPtr enclosingLayer = renderer.enclosingLayer();
805 if (enclosingLayer && enclosingLayer->isTransparentRespectingParentFrames())
806 return true;
807
808 return renderer.hasEmptyVisibleRectRespectingParentFrames();
809}
810
811bool WebPage::isTransparentOrFullyClipped(const Node& node) const
812{
813 CheckedPtr renderer = node.renderer();
814 if (!renderer)
815 return false;
816 return rendererIsTransparentOrFullyClipped(*renderer);
817}
818
819static bool selectionIsTransparentOrFullyClipped(const VisibleSelection& selection)
820{
821 RefPtr startContainer = selection.start().containerNode();
822 if (!startContainer)
823 return false;
824
825 RefPtr endContainer = selection.end().containerNode();
826 if (!endContainer)
827 return false;
828
829 CheckedPtr startRenderer = startContainer->renderer();
830 if (!startRenderer)
831 return false;
832
833 CheckedPtr endRenderer = endContainer->renderer();
834 if (!endRenderer)
835 return false;
836
837 if (!rendererIsTransparentOrFullyClipped(*startRenderer))
838 return false;
839
840 return startRenderer == endRenderer || rendererIsTransparentOrFullyClipped(*endRenderer);
841}
842
843static void convertContentToRootView(const LocalFrameView& view, Vector<SelectionGeometry>& geometries)
844{
845 for (auto& geometry : geometries)
846 geometry.setQuad(view.contentsToRootView(geometry.quad()));
847}
848
849void WebPage::getPlatformEditorStateCommon(LocalFrame& frame, EditorState& result) const
850{
851 if (!result.hasPostLayoutAndVisualData())
852 return;
853
854 const auto& selection = frame.selection().selection();
855
856 if (selection.isNone())
857 return;
858
859 ASSERT(frame.view())((void)0);
860 Ref view = *frame.view();
861
862 auto& postLayoutData = *result.postLayoutData;
863 auto& visualData = *result.visualData;
864
865 if (result.isContentEditable) {
866 if (auto editingStyle = EditingStyle::styleAtSelectionStart(selection, false, EditingStyle::PropertiesToInclude::PostLayoutProperties)) {
867 if (editingStyle->fontWeightIsBold())
868 postLayoutData.typingAttributes.add(TypingAttribute::Bold);
869
870 if (editingStyle->fontStyleIsItalic())
871 postLayoutData.typingAttributes.add(TypingAttribute::Italics);
872
873 if (editingStyle->webkitTextDecorationsInEffectIsUnderline())
874 postLayoutData.typingAttributes.add(TypingAttribute::Underline);
875
876 if (RefPtr styleProperties = editingStyle->style()) {
877 bool isLeftToRight = styleProperties->propertyAsValueID(CSSPropertyDirection) == CSSValueLtr;
878 switch (styleProperties->propertyAsValueID(CSSPropertyTextAlign).value_or(CSSValueInvalid)) {
879 case CSSValueRight:
880 case CSSValueWebkitRight:
881 postLayoutData.textAlignment = TextAlignment::Right;
882 break;
883 case CSSValueLeft:
884 case CSSValueWebkitLeft:
885 postLayoutData.textAlignment = TextAlignment::Left;
886 break;
887 case CSSValueCenter:
888 case CSSValueWebkitCenter:
889 postLayoutData.textAlignment = TextAlignment::Center;
890 break;
891 case CSSValueJustify:
892 postLayoutData.textAlignment = TextAlignment::Justified;
893 break;
894 case CSSValueStart:
895 postLayoutData.textAlignment = isLeftToRight ? TextAlignment::Left : TextAlignment::Right;
896 break;
897 case CSSValueEnd:
898 postLayoutData.textAlignment = isLeftToRight ? TextAlignment::Right : TextAlignment::Left;
899 break;
900 default:
901 break;
902 }
903 if (auto textColor = styleProperties->propertyAsColor(CSSPropertyColor))
904 postLayoutData.textColor = *textColor;
905 }
906 }
907
908 if (RefPtr enclosingListElement = enclosingList(RefPtr { selection.start().containerNode() }.get())) {
909 if (is<HTMLUListElement>(*enclosingListElement))
910 postLayoutData.enclosingListType = ListType::UnorderedList;
911 else if (is<HTMLOListElement>(*enclosingListElement))
912 postLayoutData.enclosingListType = ListType::OrderedList;
913 else
914 ASSERT_NOT_REACHED()((void)0);
915 }
916
917 postLayoutData.baseWritingDirection = protect(frame.editor())->baseWritingDirectionForSelectionStart();
918 postLayoutData.canEnableWritingSuggestions = [&] {
919 if (!selection.canEnableWritingSuggestions())
920 return false;
921
922 if (!m_lastNodeBeforeWritingSuggestions)
923 return true;
924
925 RefPtr currentNode = protect(frame.editor())->nodeBeforeWritingSuggestions();
926 return !currentNode || m_lastNodeBeforeWritingSuggestions == currentNode.get();
927 }();
928 }
929
930 RefPtr enclosingFormControl = enclosingTextFormControl(selection.start());
931 if (RefPtr editableRootOrFormControl = enclosingFormControl.get() ?: selection.rootEditableElement()) {
932 postLayoutData.selectionIsTransparentOrFullyClipped = result.isContentEditable && isTransparentOrFullyClipped(*editableRootOrFormControl);
933#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
934 result.visualData->editableRootBounds = rootViewInteractionBounds(Ref { *editableRootOrFormControl });
935#endif
936 } else if (result.selectionIsRange)
937 postLayoutData.selectionIsTransparentOrFullyClipped = selectionIsTransparentOrFullyClipped(selection);
938
939#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
940 if (enclosingFormControl || !m_page->settings().selectionHonorsOverflowScrolling())
941 result.visualData->selectionClipRect = result.visualData->editableRootBounds;
942#endif
943
944 bool startNodeIsInsideFixedPosition = false;
945 bool endNodeIsInsideFixedPosition = false;
946
947 if (selection.isCaret()) {
948 visualData.caretRectAtStart = view->contentsToRootView(WTF::protect(frame.selection())->absoluteCaretBounds(&startNodeIsInsideFixedPosition));
949 endNodeIsInsideFixedPosition = startNodeIsInsideFixedPosition;
950 visualData.caretRectAtEnd = visualData.caretRectAtStart;
951 } else if (selection.isRange()) {
952 visualData.caretRectAtStart = view->contentsToRootView(VisiblePosition(selection.start()).absoluteCaretBounds(&startNodeIsInsideFixedPosition));
953 visualData.caretRectAtEnd = view->contentsToRootView(VisiblePosition(selection.end()).absoluteCaretBounds(&endNodeIsInsideFixedPosition));
954
955 auto selectedRange = selection.toNormalizedRange();
956 if (selectedRange) {
957 auto [selectionGeometries, intersectingLayerIDs] = RenderObject::collectSelectionGeometries(*selectedRange);
958 convertContentToRootView(view, selectionGeometries);
959
960 visualData.selectionGeometries = WTF::move(selectionGeometries);
961 visualData.intersectingLayerIDs = WTF::move(intersectingLayerIDs);
962 }
963 }
964
965 postLayoutData.insideFixedPosition = startNodeIsInsideFixedPosition || endNodeIsInsideFixedPosition;
966}
967
968void WebPage::getPDFFirstPageSize(WebCore::FrameIdentifier frameID, CompletionHandler<void(WebCore::FloatSize)>&& completionHandler)
969{
970 RefPtr webFrame = WebProcess::singleton().webFrame(frameID);
971 if (!webFrame)
972 return completionHandler({ });
973
974#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
975 if (RefPtr pluginView = pluginViewForFrame(protect(webFrame->coreLocalFrame()).get()))
976 return completionHandler(pluginView->pdfDocumentSizeForPrinting());
977#endif
978
979 completionHandler({ });
980}
981
982#if ENABLE(DATA_DETECTION)(defined 1 && 1)
983
984void WebPage::handleClickForDataDetectionResult(const DataDetectorElementInfo& info, const IntPoint& clickLocation)
985{
986 send(Messages::WebPageProxy::HandleClickForDataDetectionResult(info, clickLocation));
987}
988
989#endif
990
991static String& replaceSelectionPasteboardName()
992{
993 static NeverDestroyed<String> string("ReplaceSelectionPasteboard"_s);
994 return string;
995}
996
997class OverridePasteboardForSelectionReplacement {
998 WTF_MAKE_NONCOPYABLE(OverridePasteboardForSelectionReplacement)OverridePasteboardForSelectionReplacement(const OverridePasteboardForSelectionReplacement
&) = delete; OverridePasteboardForSelectionReplacement&
operator=(const OverridePasteboardForSelectionReplacement&
) = delete;
;
999 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
;
1000public:
1001 OverridePasteboardForSelectionReplacement(const Vector<String>& types, std::span<const uint8_t> data)
1002 : m_types(types)
1003 {
1004 for (auto& type : types)
1005 WebPasteboardOverrides::sharedPasteboardOverrides().addOverride(replaceSelectionPasteboardName(), type, { data });
1006 }
1007
1008 ~OverridePasteboardForSelectionReplacement()
1009 {
1010 for (auto& type : m_types)
1011 WebPasteboardOverrides::sharedPasteboardOverrides().removeOverride(replaceSelectionPasteboardName(), type);
1012 }
1013
1014private:
1015 Vector<String> m_types;
1016};
1017
1018#if ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)(defined 1 && 1)
1019
1020void WebPage::replaceImageForRemoveBackground(const ElementContext& elementContext, const Vector<String>& types, std::span<const uint8_t> data)
1021{
1022 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
1023 if (!frame)
1024 return;
1025
1026 auto element = elementForContext(elementContext);
1027 if (!element || !element->isContentEditable())
1028 return;
1029
1030 Ref document = element->document();
1031 if (frame->document() != document.ptr())
1032 return;
1033
1034 auto originalSelection = frame->selection().selection();
1035 RefPtr selectionHost = originalSelection.rootEditableElement() ?: document->body();
1036 if (!selectionHost)
1037 return;
1038
1039 constexpr OptionSet iteratorOptions = TextIteratorBehavior::EmitsCharactersBetweenAllVisiblePositions;
1040 std::optional<CharacterRange> rangeToRestore;
1041 uint64_t numberOfCharactersInSelectionHost = 0;
1042 if (auto range = originalSelection.range()) {
1043 auto selectionHostRangeBeforeReplacement = makeRangeSelectingNodeContents(*selectionHost);
1044 rangeToRestore = characterRange(selectionHostRangeBeforeReplacement, *range, iteratorOptions);
1045 numberOfCharactersInSelectionHost = characterCount(selectionHostRangeBeforeReplacement, iteratorOptions);
1046 }
1047
1048 {
1049 OverridePasteboardForSelectionReplacement overridePasteboard { types, data };
1050 IgnoreSelectionChangeForScope ignoreSelectionChanges { *frame };
1051 protect(frame->editor())->replaceNodeFromPasteboard(*element, replaceSelectionPasteboardName(), EditAction::RemoveBackground);
1052
1053 auto position = frame->selection().selection().visibleStart();
1054 if (auto imageRange = makeSimpleRange(WebCore::VisiblePositionRange { position.previous(), position })) {
1055 for (WebCore::TextIterator iterator { *imageRange, { } }; !iterator.atEnd(); iterator.advance()) {
1056 if (RefPtr image = dynamicDowncast<HTMLImageElement>(iterator.node())) {
1057 m_elementsToExcludeFromRemoveBackground.add(*image);
1058 break;
1059 }
1060 }
1061 }
1062 }
1063
1064 constexpr auto restoreSelectionOptions = FrameSelection::defaultSetSelectionOptions(UserTriggered::Yes);
1065 if (!originalSelection.isNoneOrOrphaned()) {
1066 protect(frame->selection())->setSelection(originalSelection, restoreSelectionOptions);
1067 return;
1068 }
1069
1070 if (!rangeToRestore || !selectionHost->isConnected())
1071 return;
1072
1073 auto selectionHostRange = makeRangeSelectingNodeContents(*selectionHost);
1074 if (numberOfCharactersInSelectionHost != characterCount(selectionHostRange, iteratorOptions)) {
1075 // FIXME: We don't attempt to restore the selection if the replaced element contains a different
1076 // character count than the content that replaces it, since this codepath is currently only used
1077 // to replace a single non-text element with another. If this is used to replace text content in
1078 // the future, we should adjust the `rangeToRestore` to fit the newly inserted content.
1079 return;
1080 }
1081
1082 // The node replacement may have orphaned the original selection range; in this case, try to restore
1083 // the original selected character range.
1084 auto newSelectionRange = resolveCharacterRange(selectionHostRange, *rangeToRestore, iteratorOptions);
1085 protect(frame->selection())->setSelection(newSelectionRange, restoreSelectionOptions);
1086}
1087
1088#endif // ENABLE(IMAGE_ANALYSIS_ENHANCEMENTS)
1089
1090void WebPage::replaceSelectionWithPasteboardData(const Vector<String>& types, std::span<const uint8_t> data)
1091{
1092 OverridePasteboardForSelectionReplacement overridePasteboard { types, data };
1093 readSelectionFromPasteboard(replaceSelectionPasteboardName(), [](bool) { });
1094}
1095
1096void WebPage::readSelectionFromPasteboard(const String& pasteboardName, CompletionHandler<void(bool&&)>&& completionHandler)
1097{
1098 RefPtr frame = m_page->focusController().focusedOrMainFrame();
1099 if (!frame)
1100 return completionHandler(false);
1101 if (frame->selection().isNone())
1102 return completionHandler(false);
1103 protect(frame->editor())->readSelectionFromPasteboard(pasteboardName);
1104 completionHandler(true);
1105}
1106
1107#if ENABLE(MULTI_REPRESENTATION_HEIC)(defined ENABLE_MULTI_REPRESENTATION_HEIC && ENABLE_MULTI_REPRESENTATION_HEIC
)
1108void WebPage::insertMultiRepresentationHEIC(std::span<const uint8_t> data, const String& altText)
1109{
1110 RefPtr frame = m_page->focusController().focusedOrMainFrame();
1111 if (!frame)
1112 return;
1113 if (frame->selection().isNone())
1114 return;
1115 frame->editor().insertMultiRepresentationHEIC(data, altText);
1116}
1117#endif
1118
1119std::pair<URL, DidFilterLinkDecoration> WebPage::applyLinkDecorationFilteringWithResult(const URL& url, LinkDecorationFilteringTrigger trigger)
1120{
1121#if ENABLE(ADVANCED_PRIVACY_PROTECTIONS)(defined 1 && 1)
1122 if (m_internals->linkDecorationFilteringData.isEmpty()) {
1123 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
;
1124 return { url, DidFilterLinkDecoration::No };
1125 }
1126
1127 RefPtr mainFrame = m_mainFrame->coreLocalFrame();
1128 if (!mainFrame || !WebCore::DeprecatedGlobalSettings::trackingPreventionEnabled())
1129 return { url, DidFilterLinkDecoration::No };
1130
1131 auto isLinkDecorationFilteringEnabled = [&](const DocumentLoader* loader) {
1132 if (!loader)
1133 return false;
1134 auto effectivePolicies = trigger == LinkDecorationFilteringTrigger::Navigation ? loader->navigationalAdvancedPrivacyProtections() : loader->advancedPrivacyProtections();
1135 return effectivePolicies.contains(AdvancedPrivacyProtections::LinkDecorationFiltering);
1136 };
1137
1138 bool hasOptedInToLinkDecorationFiltering = [&] {
1139 if (isLinkDecorationFilteringEnabled(RefPtr { mainFrame->loader().activeDocumentLoader() }.get()))
1140 return true;
1141
1142 return isLinkDecorationFilteringEnabled(RefPtr { mainFrame->loader().policyDocumentLoader() }.get());
1143 }();
1144
1145 RefPtr document = mainFrame ? mainFrame->document() : nullptr;
1146 bool isConsistentQueryParameterFilteringQuirkEnabled = document && (document->quirks().needsConsistentQueryParameterFilteringQuirk(document->url()) || document->quirks().needsConsistentQueryParameterFilteringQuirk(url));
1147 if (!hasOptedInToLinkDecorationFiltering && !m_page->settings().filterLinkDecorationByDefaultEnabled() && !isConsistentQueryParameterFilteringQuirkEnabled)
1148 return { url, DidFilterLinkDecoration::No };
1149
1150 if (!url.hasQuery())
1151 return { url, DidFilterLinkDecoration::No };
1152
1153 auto sanitizedURL = url;
1154 bool allowLowEntropyException = !(hasOptedInToLinkDecorationFiltering || isConsistentQueryParameterFilteringQuirkEnabled);
1155 auto removedParameters = WTF::removeQueryParameters(sanitizedURL, [&](auto& key, auto& value) {
1156 auto it = m_internals->linkDecorationFilteringData.find(key);
1157 if (it == m_internals->linkDecorationFilteringData.end())
1158 return false;
1159
1160 constexpr auto base = 10;
1161 if (value.length() == 3 && allowLowEntropyException && WTF::parseInteger<uint8_t>(value, base, WTF::ParseIntegerWhitespacePolicy::Disallow))
1162 return false;
1163
1164 const auto& conditionals = it->value;
1165 bool isEmptyOrFoundDomain = conditionals.domains.isEmpty() || conditionals.domains.contains(RegistrableDomain { url });
1166 bool isEmptyOrFoundPath = conditionals.paths.isEmpty() || std::ranges::any_of(conditionals.paths,
1167 [&url](auto& path) {
1168 return url.path().contains(path);
1169 });
1170
1171 return isEmptyOrFoundDomain && isEmptyOrFoundPath;
1172 });
1173
1174 if (!removedParameters.isEmpty() && trigger != LinkDecorationFilteringTrigger::Unspecified) {
1175 if (trigger == LinkDecorationFilteringTrigger::Navigation)
1176 send(Messages::WebPageProxy::DidApplyLinkDecorationFiltering(url, sanitizedURL));
1177 auto removedParametersString = makeStringByJoining(removedParameters, ", "_s);
1178 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
;
1179 }
1180
1181 return { sanitizedURL, DidFilterLinkDecoration::Yes };
1182#else
1183 return { url, DidFilterLinkDecoration::No };
1184#endif
1185}
1186
1187URL WebPage::allowedQueryParametersForAdvancedPrivacyProtections(const URL& url)
1188{
1189#if ENABLE(ADVANCED_PRIVACY_PROTECTIONS)(defined 1 && 1)
1190 if (m_internals->allowedQueryParametersForAdvancedPrivacyProtections.isEmpty()) {
1191 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
;
1192 return url;
1193 }
1194
1195 if (!url.hasQuery() && !url.hasFragmentIdentifier())
1196 return url;
1197
1198 auto sanitizedURL = url;
1199
1200 auto allowedParameters = m_internals->allowedQueryParametersForAdvancedPrivacyProtections.get(RegistrableDomain { sanitizedURL });
1201
1202 if (!allowedParameters.contains("#"_s))
1203 sanitizedURL.removeFragmentIdentifier();
1204
1205 WTF::removeQueryParameters(sanitizedURL, [&](auto& key, auto&) {
1206 return !allowedParameters.contains(key);
1207 });
1208
1209 return sanitizedURL;
1210#else
1211 return url;
1212#endif
1213}
1214
1215#if ENABLE(EXTENSION_CAPABILITIES)(defined ENABLE_EXTENSION_CAPABILITIES && ENABLE_EXTENSION_CAPABILITIES
)
1216void WebPage::setMediaPlaybackEnvironment(const String& environment)
1217{
1218 m_mediaPlaybackEnvironment = environment;
1219 if (RefPtr gpuProcessConnection = WebProcess::singleton().existingGPUProcessConnection())
1220 gpuProcessConnection->setMediaPlaybackEnvironment(identifier(), environment);
1221}
1222
1223void WebPage::setDisplayCaptureEnvironment(const String& environment)
1224{
1225 m_displayCaptureEnvironment = environment;
1226 if (RefPtr gpuProcessConnection = WebProcess::singleton().existingGPUProcessConnection())
1227 gpuProcessConnection->setDisplayCaptureEnvironment(identifier(), environment);
1228}
1229#endif
1230
1231#if ENABLE(WRITING_TOOLS)(defined 1 && 1)
1232void WebPage::willBeginWritingToolsSession(const std::optional<WebCore::WritingTools::Session>& session, CompletionHandler<void(const Vector<WebCore::WritingTools::Context>&)>&& completionHandler)
1233{
1234 protect(corePage())->willBeginWritingToolsSession(session, WTF::move(completionHandler));
1235}
1236
1237void WebPage::didBeginWritingToolsSession(const WebCore::WritingTools::Session& session, const Vector<WebCore::WritingTools::Context>& contexts)
1238{
1239 protect(corePage())->didBeginWritingToolsSession(session, contexts);
1240}
1241
1242void 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)
1243{
1244 protect(corePage())->proofreadingSessionDidReceiveSuggestions(session, suggestions, processedRange, context, finished);
1245 completionHandler();
1246}
1247
1248void WebPage::proofreadingSessionDidUpdateStateForSuggestion(const WebCore::WritingTools::Session& session, WebCore::WritingTools::TextSuggestion::State state, const WebCore::WritingTools::TextSuggestion& suggestion, const WebCore::WritingTools::Context& context)
1249{
1250 protect(corePage())->proofreadingSessionDidUpdateStateForSuggestion(session, state, suggestion, context);
1251}
1252
1253void WebPage::willEndWritingToolsSession(const WebCore::WritingTools::Session& session, bool accepted, CompletionHandler<void()>&& completionHandler)
1254{
1255 protect(corePage())->willEndWritingToolsSession(session, accepted);
1256 completionHandler();
1257}
1258
1259void WebPage::didEndWritingToolsSession(const WebCore::WritingTools::Session& session, bool accepted)
1260{
1261 protect(corePage())->didEndWritingToolsSession(session, accepted);
1262}
1263
1264void 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)
1265{
1266 protect(corePage())->compositionSessionDidReceiveTextWithReplacementRange(session, attributedText, range, context, finished);
1267 completionHandler();
1268}
1269
1270void WebPage::writingToolsSessionDidReceiveAction(const WritingTools::Session& session, WebCore::WritingTools::Action action)
1271{
1272 protect(corePage())->writingToolsSessionDidReceiveAction(session, action);
1273}
1274
1275void WebPage::proofreadingSessionShowDetailsForSuggestionWithIDRelativeToRect(const WebCore::WritingTools::TextSuggestion::ID& replacementID, WebCore::IntRect rect)
1276{
1277 send(Messages::WebPageProxy::ProofreadingSessionShowDetailsForSuggestionWithIDRelativeToRect(replacementID, rect));
1278}
1279
1280void WebPage::proofreadingSessionUpdateStateForSuggestionWithID(WebCore::WritingTools::TextSuggestion::State state, const WebCore::WritingTools::TextSuggestion::ID& replacementID)
1281{
1282 send(Messages::WebPageProxy::ProofreadingSessionUpdateStateForSuggestionWithID(state, replacementID));
1283}
1284
1285void WebPage::addTextAnimationForAnimationID(const WTF::UUID& uuid, const WebCore::TextAnimationData& styleData, const RefPtr<WebCore::TextIndicator> textIndicator, CompletionHandler<void(WebCore::TextAnimationRunMode)>&& completionHandler)
1286{
1287 if (completionHandler)
1288 sendWithAsyncReply(Messages::WebPageProxy::AddTextAnimationForAnimationIDWithCompletionHandler(uuid, styleData, textIndicator), WTF::move(completionHandler));
1289 else
1290 send(Messages::WebPageProxy::AddTextAnimationForAnimationID(uuid, styleData, textIndicator));
1291}
1292
1293void WebPage::removeTextAnimationForAnimationID(const WTF::UUID& uuid)
1294{
1295 send(Messages::WebPageProxy::RemoveTextAnimationForAnimationID(uuid));
1296}
1297
1298void WebPage::removeInitialTextAnimationForActiveWritingToolsSession()
1299{
1300 m_textAnimationController->removeInitialTextAnimationForActiveWritingToolsSession();
1301}
1302
1303void WebPage::addInitialTextAnimationForActiveWritingToolsSession()
1304{
1305 m_textAnimationController->addInitialTextAnimationForActiveWritingToolsSession();
1306}
1307
1308void WebPage::addSourceTextAnimationForActiveWritingToolsSession(const WTF::UUID& sourceAnimationUUID, const WTF::UUID& destinationAnimationUUID, bool finished, const CharacterRange& range, const String& string, CompletionHandler<void(WebCore::TextAnimationRunMode)>&& completionHandler)
1309{
1310 m_textAnimationController->addSourceTextAnimationForActiveWritingToolsSession(sourceAnimationUUID, destinationAnimationUUID, finished, range, string, WTF::move(completionHandler));
1311}
1312
1313void WebPage::addDestinationTextAnimationForActiveWritingToolsSession(const WTF::UUID& sourceAnimationUUID, const WTF::UUID& destinationAnimationUUID, const std::optional<CharacterRange>& range, const String& string)
1314{
1315 m_textAnimationController->addDestinationTextAnimationForActiveWritingToolsSession(sourceAnimationUUID, destinationAnimationUUID, range, string);
1316}
1317
1318void WebPage::saveSnapshotOfTextPlaceholderForAnimation(const WebCore::SimpleRange& placeholderRange)
1319{
1320 m_textAnimationController->saveSnapshotOfTextPlaceholderForAnimation(placeholderRange);
1321}
1322
1323void WebPage::clearAnimationsForActiveWritingToolsSession()
1324{
1325 m_textAnimationController->clearAnimationsForActiveWritingToolsSession();
1326}
1327
1328void WebPage::createTextIndicatorForTextAnimationID(const WTF::UUID& uuid, CompletionHandler<void(RefPtr<WebCore::TextIndicator>&&)>&& completionHandler)
1329{
1330 m_textAnimationController->createTextIndicatorForTextAnimationID(uuid, WTF::move(completionHandler));
1331}
1332
1333void WebPage::updateUnderlyingTextVisibilityForTextAnimationID(const WTF::UUID& uuid, bool visible, CompletionHandler<void()>&& completionHandler)
1334{
1335 m_textAnimationController->updateUnderlyingTextVisibilityForTextAnimationID(uuid, visible, WTF::move(completionHandler));
1336}
1337
1338void WebPage::proofreadingSessionSuggestionTextRectsInRootViewCoordinates(const WebCore::CharacterRange& enclosingRangeRelativeToSessionRange, CompletionHandler<void(Vector<FloatRect>&&)>&& completionHandler) const
1339{
1340 auto rects = protect(corePage())->proofreadingSessionSuggestionTextRectsInRootViewCoordinates(enclosingRangeRelativeToSessionRange);
1341 completionHandler(WTF::move(rects));
1342}
1343
1344void WebPage::updateTextVisibilityForActiveWritingToolsSession(const WebCore::CharacterRange& rangeRelativeToSessionRange, bool visible, const WTF::UUID& identifier, CompletionHandler<void()>&& completionHandler)
1345{
1346 protect(corePage())->updateTextVisibilityForActiveWritingToolsSession(rangeRelativeToSessionRange, visible, identifier);
1347 completionHandler();
1348}
1349
1350void WebPage::textPreviewDataForActiveWritingToolsSession(const WebCore::CharacterRange& rangeRelativeToSessionRange, CompletionHandler<void(RefPtr<WebCore::TextIndicator>&&)>&& completionHandler)
1351{
1352 RefPtr textIndicator = protect(corePage())->textPreviewDataForActiveWritingToolsSession(rangeRelativeToSessionRange);
1353 completionHandler(WTF::move(textIndicator));
1354}
1355
1356void WebPage::decorateTextReplacementsForActiveWritingToolsSession(const WebCore::CharacterRange& rangeRelativeToSessionRange, CompletionHandler<void(void)>&& completionHandler)
1357{
1358 protect(corePage())->decorateTextReplacementsForActiveWritingToolsSession(rangeRelativeToSessionRange);
1359 completionHandler();
1360}
1361
1362void WebPage::setSelectionForActiveWritingToolsSession(const WebCore::CharacterRange& rangeRelativeToSessionRange, CompletionHandler<void(void)>&& completionHandler)
1363{
1364 protect(corePage())->setSelectionForActiveWritingToolsSession(rangeRelativeToSessionRange);
1365 completionHandler();
1366}
1367
1368void WebPage::intelligenceTextAnimationsDidComplete()
1369{
1370 protect(corePage())->intelligenceTextAnimationsDidComplete();
1371}
1372
1373void WebPage::didEndPartialIntelligenceTextAnimation()
1374{
1375 send(Messages::WebPageProxy::DidEndPartialIntelligenceTextAnimation());
1376}
1377
1378#endif
1379
1380static std::optional<bool> elementHasHiddenVisibility(StyledElement* styledElement)
1381{
1382 RefPtr inlineStyle = styledElement->inlineStyle();
1383 if (!inlineStyle)
1384 return std::nullopt;
1385
1386 RefPtr value = inlineStyle->getPropertyCSSValue(CSSPropertyVisibility);
1387 if (!value)
1388 return false;
1389
1390 return value->valueID() == CSSValueHidden;
1391}
1392
1393void WebPage::createTextIndicatorForElementWithID(const String& elementID, CompletionHandler<void(RefPtr<WebCore::TextIndicator>&&)>&& completionHandler)
1394{
1395 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
1396 if (!frame) {
1397 ASSERT_NOT_REACHED()((void)0);
1398 completionHandler(nilnullptr);
1399 return;
1400 }
1401
1402 RefPtr document = frame->document();
1403 if (!document) {
1404 ASSERT_NOT_REACHED()((void)0);
1405 completionHandler(nilnullptr);
1406 return;
1407 }
1408
1409 RefPtr element = document->getElementById(elementID);
1410 if (!element) {
1411 ASSERT_NOT_REACHED()((void)0);
1412 completionHandler(nilnullptr);
1413 return;
1414 }
1415
1416 RefPtr styledElement = dynamicDowncast<StyledElement>(element.get());
1417 if (!styledElement) {
1418 ASSERT_NOT_REACHED()((void)0);
1419 completionHandler(nilnullptr);
1420 return;
1421 }
1422
1423 // Temporarily force the content to be visible so that it can be snapshotted.
1424
1425 auto isHiddenInitially = elementHasHiddenVisibility(styledElement.get());
1426
1427 styledElement->setInlineStyleProperty(CSSPropertyVisibility, CSSValueVisible, IsImportant::Yes);
1428
1429 auto elementRange = WebCore::makeRangeSelectingNodeContents(*styledElement);
1430
1431 std::optional<WebCore::TextIndicatorData> textIndicatorData;
1432 constexpr OptionSet textIndicatorOptions {
1433 WebCore::TextIndicatorOption::IncludeSnapshotOfAllVisibleContentWithoutSelection,
1434 WebCore::TextIndicatorOption::ExpandClipBeyondVisibleRect,
1435 WebCore::TextIndicatorOption::SkipReplacedContent,
1436 WebCore::TextIndicatorOption::RespectTextColor,
1437#if PLATFORM(VISION)(defined WTF_PLATFORM_VISION && WTF_PLATFORM_VISION)
1438 WebCore::TextIndicatorOption::SnapshotContentAt3xBaseScale,
1439#endif
1440 };
1441
1442 RefPtr textIndicator = WebCore::TextIndicator::createWithRange(elementRange, textIndicatorOptions, WebCore::TextIndicatorPresentationTransition::None, { });
1443 if (!textIndicator) {
1444 completionHandler(nilnullptr);
1445 return;
1446 }
1447
1448 // If `initialVisibility` is an empty optional, this means there was no initial inline style.
1449 // Ensure the state is idempotent after by removing the inline style if this is the case.
1450
1451 if (isHiddenInitially.has_value())
1452 styledElement->setInlineStyleProperty(CSSPropertyVisibility, *isHiddenInitially ? CSSValueHidden : CSSValueVisible, IsImportant::Yes);
1453 else
1454 styledElement->removeInlineStyleProperty(CSSPropertyVisibility);
1455
1456 completionHandler(WTF::move(textIndicator));
1457}
1458
1459void WebPage::createBitmapsFromImageData(Ref<WebCore::SharedBuffer>&& buffer, const Vector<unsigned>& lengths, CompletionHandler<void(Vector<Ref<WebCore::ShareableBitmap>>&&)>&& completionHandler)
1460{
1461 WebCore::createBitmapsFromImageData(buffer->span(), lengths.span(), WTF::move(completionHandler));
1462}
1463
1464void WebPage::decodeImageData(Ref<WebCore::SharedBuffer>&& buffer, std::optional<WebCore::FloatSize> preferredSize, CompletionHandler<void(RefPtr<WebCore::ShareableBitmap>&&)>&& completionHandler)
1465{
1466 decodeImageWithSize(buffer->span(), preferredSize, WTF::move(completionHandler));
1467}
1468
1469#if HAVE(PDFKIT)(defined 1 && 1)
1470
1471void WebPage::computePagesForPrintingPDFDocument(WebCore::FrameIdentifier frameID, const PrintInfo& printInfo, Vector<IntRect>& resultPageRects)
1472{
1473 ASSERT(resultPageRects.isEmpty())((void)0);
1474 RefPtr frame = WebProcess::singleton().webFrame(frameID);
1475 RefPtr coreFrame = frame ? frame->coreLocalFrame() : nullptr;
1476 RetainPtr<PDFDocument> pdfDocument = coreFrame ? pdfDocumentForPrintingFrame(coreFrame.get()) : 0;
1477 if ([pdfDocument allowsPrinting]) {
1478 NSUInteger pageCount = [pdfDocument pageCount];
1479 IntRect pageRect(0, 0, ceilf(printInfo.availablePaperWidth), ceilf(printInfo.availablePaperHeight));
1480 for (NSUInteger i = 1; i <= pageCount; ++i) {
1481 resultPageRects.append(pageRect);
1482 pageRect.move(0, pageRect.height());
1483 }
1484 }
1485}
1486
1487static inline CGFloat roundCGFloat(CGFloat f)
1488{
1489 if (sizeof(CGFloat) == sizeof(float))
1490 return roundf(static_cast<float>(f));
1491 return static_cast<CGFloat>(round(f));
1492}
1493
1494static void drawPDFPage(PDFDocument *pdfDocument, CFIndex pageIndex, CGContextRef context, CGFloat pageSetupScaleFactor, CGSize paperSize)
1495{
1496 CGContextSaveGState(context);
1497
1498 CGContextScaleCTM(context, pageSetupScaleFactor, pageSetupScaleFactor);
1499
1500 RetainPtr<PDFPage> pdfPage = [pdfDocument pageAtIndex:pageIndex];
1501 NSRect cropBox = [pdfPage boundsForBox:kPDFDisplayBoxCropBox];
1502 if (NSIsEmptyRect(cropBox))
1503 cropBox = [pdfPage boundsForBox:kPDFDisplayBoxMediaBox];
1504 else
1505 cropBox = NSIntersectionRect(cropBox, [pdfPage boundsForBox:kPDFDisplayBoxMediaBox]);
1506
1507 // Always auto-rotate PDF content regardless of the paper orientation.
1508 NSInteger rotation = [pdfPage rotation];
1509 if (rotation == 90 || rotation == 270)
1510 std::swap(cropBox.size.width, cropBox.size.height);
1511
1512 bool shouldRotate = (paperSize.width < paperSize.height) != (cropBox.size.width < cropBox.size.height);
1513 if (shouldRotate)
1514 std::swap(cropBox.size.width, cropBox.size.height);
1515
1516 // Center.
1517 CGFloat widthDifference = paperSize.width / pageSetupScaleFactor - cropBox.size.width;
1518 CGFloat heightDifference = paperSize.height / pageSetupScaleFactor - cropBox.size.height;
1519 if (widthDifference || heightDifference)
1520 CGContextTranslateCTM(context, roundCGFloat(widthDifference / 2), roundCGFloat(heightDifference / 2));
1521
1522 if (shouldRotate) {
1523 CGContextRotateCTM(context, static_cast<CGFloat>(piOverTwoDouble));
1524 CGContextTranslateCTM(context, 0, -cropBox.size.width);
1525 }
1526
1527 [pdfPage drawWithBox:kPDFDisplayBoxCropBox toContext:context];
1528
1529 CGAffineTransform transform = CGContextGetCTM(context);
1530
1531 for (PDFAnnotation *annotation in [pdfPage annotations]) {
1532 if (![[annotation valueForAnnotationKey:get_PDFKit_PDFAnnotationKeySubtypeSingleton()] isEqualToString:get_PDFKit_PDFAnnotationSubtypeLinkSingleton()])
1533 continue;
1534
1535 RetainPtr<NSURL> url = annotation.URL;
1536 if (!url)
1537 continue;
1538
1539 CGRect transformedRect = CGRectApplyAffineTransform(annotation.bounds, transform);
1540 CGPDFContextSetURLForRect(context, (CFURLRef)url.get(), transformedRect);
1541 }
1542
1543 CGContextRestoreGState(context);
1544}
1545
1546void WebPage::drawPDFDocument(CGContextRef context, PDFDocument *pdfDocument, const PrintInfo& printInfo, const WebCore::IntRect& rect)
1547{
1548 NSUInteger pageCount = [pdfDocument pageCount];
1549 IntSize paperSize(ceilf(printInfo.availablePaperWidth), ceilf(printInfo.availablePaperHeight));
1550 IntRect pageRect(IntPoint(), paperSize);
1551 for (NSUInteger i = 0; i < pageCount; ++i) {
1552 if (pageRect.intersects(rect)) {
1553 CGContextSaveGState(context);
1554
1555 CGContextTranslateCTM(context, pageRect.x() - rect.x(), pageRect.y() - rect.y());
1556 drawPDFPage(pdfDocument, i, context, printInfo.pageSetupScaleFactor, paperSize);
1557
1558 CGContextRestoreGState(context);
1559 }
1560 pageRect.move(0, pageRect.height());
1561 }
1562}
1563
1564void WebPage::drawPagesToPDFFromPDFDocument(GraphicsContext& context, PDFDocument *pdfDocument, const PrintInfo& printInfo, const WebCore::FloatRect& mediaBox, uint32_t first, uint32_t count)
1565{
1566 NSUInteger pageCount = [pdfDocument pageCount];
1567 for (uint32_t page = first; page < first + count; ++page) {
1568 if (page >= pageCount)
1569 break;
1570
1571 context.beginPage(mediaBox);
1572 drawPDFPage(pdfDocument, page, context.protectedPlatformContext().get(), printInfo.pageSetupScaleFactor, CGSizeMake(printInfo.availablePaperWidth, printInfo.availablePaperHeight));
1573 context.endPage();
1574 }
1575}
1576
1577#else
1578
1579void WebPage::drawPDFDocument(CGContextRef, PDFDocument *, const PrintInfo&, const WebCore::IntRect&)
1580{
1581 notImplemented()((void)0);
1582}
1583
1584void WebPage::computePagesForPrintingPDFDocument(WebCore::FrameIdentifier, const PrintInfo&, Vector<IntRect>&)
1585{
1586 notImplemented()((void)0);
1587}
1588
1589void WebPage::drawPagesToPDFFromPDFDocument(GraphicsContext&, PDFDocument *, const PrintInfo&, const WebCore::FloatRect&, uint32_t, uint32_t)
1590{
1591 notImplemented()((void)0);
1592}
1593
1594#endif
1595
1596BoxSideSet WebPage::sidesRequiringFixedContainerEdges() const
1597{
1598 if (!m_page->settings().contentInsetBackgroundFillEnabled())
1599 return { };
1600
1601#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
1602 auto obscuredInsets = m_page->obscuredInsets();
1603#else
1604 auto obscuredInsets = m_page->obscuredContentInsets();
1605#endif
1606
1607#if PLATFORM(MAC)(defined 1 && 1)
1608 auto additionalHeight = m_overflowHeightForTopScrollEdgeEffect;
1609#else
1610 auto additionalHeight = 0;
1611#endif
1612
1613 auto sides = m_page->fixedContainerEdges().fixedEdges();
1614
1615 if ((additionalHeight + obscuredInsets.top()) > 0)
1616 sides.add(BoxSide::Top);
1617
1618 if (obscuredInsets.left() > 0)
1619 sides.add(BoxSide::Left);
1620
1621 if (obscuredInsets.right() > 0)
1622 sides.add(BoxSide::Right);
1623
1624 if (obscuredInsets.bottom() > 0)
1625 sides.add(BoxSide::Bottom);
1626
1627 return sides;
1628}
1629
1630void WebPage::getWebArchivesForFrames(const Vector<WebCore::FrameIdentifier>& frameIdentifiers, CompletionHandler<void(HashMap<WebCore::FrameIdentifier, Ref<WebCore::LegacyWebArchive>>&&)>&& completionHandler)
1631{
1632 if (!m_page)
1633 return completionHandler({ });
1634
1635 HashMap<WebCore::FrameIdentifier, Ref<LegacyWebArchive>> result;
1636 for (auto& frameIdentifier : frameIdentifiers) {
1637 RefPtr frame = WebFrame::webFrame(frameIdentifier);
1638 if (!frame)
1639 continue;
1640
1641 RefPtr localFrame = frame->coreLocalFrame();
1642 if (!localFrame)
1643 continue;
1644
1645 RefPtr document = localFrame->document();
1646 if (!document)
1647 continue;
1648
1649 WebCore::LegacyWebArchive::ArchiveOptions options {
1650 LegacyWebArchive::ShouldSaveScriptsFromMemoryCache::Yes,
1651 LegacyWebArchive::ShouldArchiveSubframes::No
1652 };
1653 if (RefPtr archive = WebCore::LegacyWebArchive::create(*document, WTF::move(options)))
1654 result.add(localFrame->frameID(), archive.releaseNonNull());
1655 }
1656 completionHandler(WTF::move(result));
1657}
1658
1659void WebPage::getWebArchiveData(CompletionHandler<void(const std::optional<IPC::SharedBufferReference>&)>&& completionHandler)
1660{
1661 RetainPtr<CFDataRef> data = m_mainFrame->webArchiveData(nullptr, nullptr);
1662 completionHandler(IPC::SharedBufferReference(SharedBuffer::create(data.get())));
1663}
1664
1665void WebPage::processSystemWillSleep() const
1666{
1667 if (RefPtr manager = mediaSessionManagerIfExists())
1668 manager->processSystemWillSleep();
1669}
1670
1671void WebPage::processSystemDidWake() const
1672{
1673 if (RefPtr manager = mediaSessionManagerIfExists())
1674 manager->processSystemDidWake();
1675}
1676
1677NSObject *WebPage::accessibilityObjectForMainFramePlugin()
1678{
1679#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
1680 if (!m_page)
1681 return nilnullptr;
1682
1683 if (RefPtr pluginView = mainFramePlugIn())
1684 return pluginView->accessibilityObject();
1685#endif
1686
1687 return nilnullptr;
1688}
1689
1690bool WebPage::shouldFallbackToWebContentAXObjectForMainFramePlugin() const
1691{
1692#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
1693 RefPtr pluginView = mainFramePlugIn();
1694 return pluginView && pluginView->isPresentingLockedContent();
1695#else
1696 return false;
1697#endif
1698}
1699
1700WKAccessibilityWebPageObject* WebPage::accessibilityRemoteObject()
1701{
1702 return m_mockAccessibilityElement.get();
1703}
1704
1705RetainPtr<PDFDocument> WebPage::pdfDocumentForPrintingFrame(LocalFrame* coreFrame)
1706{
1707#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
1708 if (RefPtr pluginView = pluginViewForFrame(coreFrame))
1709 return pluginView->pdfDocumentForPrinting();
1710#endif
1711 return nullptr;
1712}
1713
1714void WebPage::drawToPDF(const std::optional<FloatRect>& rect, bool allowTransparentBackground, CompletionHandler<void(RefPtr<WebCore::SharedBuffer>&&)>&& completionHandler)
1715{
1716 RefPtr localMainFrame = this->localMainFrame();
1717 if (!localMainFrame)
1718 return;
1719
1720 Ref frameView = *localMainFrame->view();
1721 auto snapshotRect = IntRect { rect.value_or(FloatRect { { }, frameView->contentsSize() }) };
1722
1723 RefPtr buffer = ImageBuffer::create(snapshotRect.size(), RenderingMode::PDFDocument, RenderingPurpose::Snapshot, 1, DestinationColorSpace::SRGB(), PixelFormat::BGRA8);
1724 if (!buffer)
1725 return;
1726
1727 drawMainFrameToPDF(*localMainFrame, buffer->context(), snapshotRect, allowTransparentBackground);
1728 completionHandler(buffer->sinkIntoPDFDocument());
1729}
1730
1731void WebPage::drawRectToImage(FrameIdentifier frameID, const PrintInfo& printInfo, const IntRect& rect, const WebCore::IntSize& imageSize, CompletionHandler<void(std::optional<WebCore::ShareableBitmap::Handle>&&)>&& completionHandler)
1732{
1733 PrintContextAccessScope scope { *this };
1734 RefPtr frame = WebProcess::singleton().webFrame(frameID);
1735 RefPtr coreFrame = frame ? frame->coreLocalFrame() : 0;
1736
1737 RefPtr<WebImage> image;
1738
1739#if USE(CG)(defined 1 && 1)
1740 if (coreFrame) {
1741 ASSERT(coreFrame->document()->printing() || pdfDocumentForPrintingFrame(coreFrame.get()))((void)0);
1742 image = WebImage::create(imageSize, ImageOption::Local, DestinationColorSpace::SRGB(), &m_page->chrome().client());
1743 if (!image || !image->context()) {
1744 ASSERT_NOT_REACHED()((void)0);
1745 return completionHandler({ });
1746 }
1747
1748 auto& graphicsContext = *image->context();
1749 float printingScale = static_cast<float>(imageSize.width()) / rect.width();
1750 graphicsContext.scale(printingScale);
1751
1752 if (RetainPtr<PDFDocument> pdfDocument = pdfDocumentForPrintingFrame(coreFrame.get())) {
1753 ASSERT(!m_printContext)((void)0);
1754 graphicsContext.scale(FloatSize(1, -1));
1755 graphicsContext.translate(0, -rect.height());
1756 drawPDFDocument(graphicsContext.protectedPlatformContext().get(), pdfDocument.get(), printInfo, rect);
1757 } else
1758 Ref { *m_printContext }->spoolRect(graphicsContext, rect);
1759 }
1760#endif
1761
1762 std::optional<ShareableBitmap::Handle> handle;
1763 if (image)
1764 handle = image->createHandle(SharedMemory::Protection::ReadOnly);
1765
1766 completionHandler(WTF::move(handle));
1767}
1768
1769void WebPage::drawPagesToPDF(FrameIdentifier frameID, const PrintInfo& printInfo, uint32_t first, uint32_t count, CompletionHandler<void(RefPtr<WebCore::SharedBuffer>&&)>&& callback)
1770{
1771 PrintContextAccessScope scope { *this };
1772 RefPtr<SharedBuffer> pdfPageData;
1773 drawPagesToPDFImpl(frameID, printInfo, first, count, pdfPageData);
1774 callback(WTF::move(pdfPageData));
1775}
1776
1777void WebPage::drawPagesToPDFImpl(FrameIdentifier frameID, const PrintInfo& printInfo, uint32_t first, uint32_t count, RefPtr<SharedBuffer>& pdfPageData)
1778{
1779 RefPtr frame = WebProcess::singleton().webFrame(frameID);
1780 RefPtr coreFrame = frame ? frame->coreLocalFrame() : 0;
1781
1782#if USE(CG)(defined 1 && 1)
1783 if (coreFrame) {
1784 ASSERT(coreFrame->document()->printing() || pdfDocumentForPrintingFrame(coreFrame.get()))((void)0);
1785
1786 FloatRect mediaBox = (m_printContext && m_printContext->pageCount()) ? m_printContext->pageRect(0) : FloatRect { 0, 0, printInfo.availablePaperWidth, printInfo.availablePaperHeight };
1787
1788 RefPtr buffer = ImageBuffer::create(mediaBox.size(), RenderingMode::PDFDocument, RenderingPurpose::Snapshot, 1, DestinationColorSpace::SRGB(), PixelFormat::BGRA8);
1789 if (!buffer)
1790 return;
1791 GraphicsContext& context = buffer->context();
1792
1793 if (RetainPtr<PDFDocument> pdfDocument = pdfDocumentForPrintingFrame(coreFrame.get())) {
1794 ASSERT(!m_printContext)((void)0);
1795 drawPagesToPDFFromPDFDocument(context, pdfDocument.get(), printInfo, mediaBox, first, count);
1796 } else {
1797 if (!m_printContext)
1798 return;
1799
1800 drawPrintContextPagesToGraphicsContext(context, mediaBox, first, count);
1801 }
1802 pdfPageData = ImageBuffer::sinkIntoPDFDocument(WTF::move(buffer));
1803 }
1804#endif
1805}
1806
1807void WebPage::drawPrintContextPagesToGraphicsContext(GraphicsContext& context, const FloatRect& pageRect, uint32_t first, uint32_t count)
1808{
1809 RefPtr printContext = m_printContext;
1810 for (uint32_t page = first; page < first + count; ++page) {
1811 if (page >= printContext->pageCount())
1812 break;
1813
1814 context.beginPage(pageRect);
1815
1816 context.scale(FloatSize(1, -1));
1817 context.translate(0, -printContext->pageRect(page).height());
1818 printContext->spoolPage(context, page, printContext->pageRect(page).width());
1819
1820 context.endPage();
1821 }
1822}
1823
1824void WebPage::drawPrintingRectToSnapshot(RemoteSnapshotIdentifier snapshotIdentifier, WebCore::FrameIdentifier frameID, const PrintInfo& printInfo, const WebCore::IntRect& rect, const WebCore::IntSize& imageSize, CompletionHandler<void(bool)>&& completionHandler)
1825{
1826 RefPtr frame = WebProcess::singleton().webFrame(frameID);
1827 if (!frame) {
1828 completionHandler(false);
1829 return;
1830 }
1831
1832 RefPtr coreFrame = frame->coreLocalFrame();
1833 if (!coreFrame) {
1834 completionHandler(false);
1835 return;
1836 }
1837
1838 if (pdfDocumentForPrintingFrame(coreFrame.get())) {
1839 // Can't do this remotely.
1840 completionHandler(false);
1841 return;
1842 }
1843 ASSERT(coreFrame->document()->printing())((void)0);
1844 PrintContextAccessScope scope { *this };
1845
1846 Ref remoteRenderingBackend = ensureRemoteRenderingBackendProxy();
1847 m_remoteSnapshotState = {
1848 snapshotIdentifier,
1849 remoteRenderingBackend->createSnapshotRecorder(snapshotIdentifier),
1850 MainRunLoopSuccessCallbackAggregator::create([completionHandler = WTF::move(completionHandler)] (bool success) mutable {
1851 completionHandler(success);
1852 })
1853 };
1854 GraphicsContext& context = m_remoteSnapshotState->recorder.get();
1855
1856 float printingScale = static_cast<float>(imageSize.width()) / rect.width();
1857 context.scale(printingScale);
1858
1859 Ref { *m_printContext }->spoolRect(context, rect);
1860
1861 remoteRenderingBackend->sinkSnapshotRecorderIntoSnapshotFrame(WTF::move(m_remoteSnapshotState->recorder), frameID, Ref { m_remoteSnapshotState->callback }->chain());
1862 m_remoteSnapshotState = std::nullopt;
1863}
1864
1865void WebPage::drawPrintingPagesToSnapshot(RemoteSnapshotIdentifier snapshotIdentifier, FrameIdentifier frameID, const PrintInfo& printInfo, uint32_t first, uint32_t count, CompletionHandler<void(std::optional<WebCore::FloatSize>)>&& completionHandler)
1866{
1867 RefPtr frame = WebProcess::singleton().webFrame(frameID);
1868 if (!frame) {
1869 completionHandler({ });
1870 return;
1871 }
1872
1873 RefPtr coreFrame = frame->coreLocalFrame();
1874 if (!coreFrame) {
1875 completionHandler({ });
1876 return;
1877 }
1878
1879 if (pdfDocumentForPrintingFrame(coreFrame.get())) {
1880 // Can't do this remotely.
1881 completionHandler({ });
1882 return;
1883 }
1884
1885 if (!m_printContext) {
1886 completionHandler({ });
1887 return;
1888 }
1889
1890 PrintContextAccessScope scope { *this };
1891 ASSERT(coreFrame->document()->printing())((void)0);
1892
1893 FloatRect mediaBox = (m_printContext && m_printContext->pageCount()) ? m_printContext->pageRect(0) : FloatRect { 0, 0, printInfo.availablePaperWidth, printInfo.availablePaperHeight };
1894
1895 Ref remoteRenderingBackend = ensureRemoteRenderingBackendProxy();
1896 m_remoteSnapshotState = {
1897 snapshotIdentifier,
1898 remoteRenderingBackend->createSnapshotRecorder(snapshotIdentifier),
1899 MainRunLoopSuccessCallbackAggregator::create([completionHandler = WTF::move(completionHandler), snapshotSize = mediaBox.size()] (bool success) mutable {
1900 completionHandler(success ? std::optional<FloatSize>(snapshotSize) : std::nullopt);
1901 })
1902 };
1903
1904 GraphicsContext& context = m_remoteSnapshotState->recorder.get();
1905
1906 drawPrintContextPagesToGraphicsContext(context, mediaBox, first, count);
1907
1908 remoteRenderingBackend->sinkSnapshotRecorderIntoSnapshotFrame(WTF::move(m_remoteSnapshotState->recorder), frameID, Ref { m_remoteSnapshotState->callback }->chain());
1909 m_remoteSnapshotState = std::nullopt;
1910}
1911
1912void WebPage::handleAlternativeTextUIResult(const String& result)
1913{
1914 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
1915 if (!frame)
1916 return;
1917
1918 protect(frame->editor())->handleAlternativeTextUIResult(result);
1919}
1920
1921void WebPage::setTextAsync(const String& text)
1922{
1923 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
1924 if (!frame)
1925 return;
1926
1927 if (frame->selection().selection().isContentEditable()) {
1928 UserTypingGestureIndicator indicator(*frame);
1929 protect(frame->selection())->selectAll();
1930 if (text.isEmpty())
1931 protect(frame->editor())->deleteSelectionWithSmartDelete(false);
1932 else
1933 protect(frame->editor())->insertText(text, nullptr, TextEventInputKeyboard);
1934 return;
1935 }
1936
1937 if (RefPtr input = dynamicDowncast<HTMLInputElement>(m_focusedElement)) {
1938 input->setValueForUser(text);
1939 return;
1940 }
1941
1942 ASSERT_NOT_REACHED()((void)0);
1943}
1944
1945void WebPage::insertTextAsync(const String& text, const EditingRange& replacementEditingRange, InsertTextOptions&& options)
1946{
1947 platformWillPerformEditingCommand();
1948
1949 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
1950 if (!frame)
1951 return;
1952
1953 UserGestureIndicator gestureIndicator { options.processingUserGesture ? IsProcessingUserGesture::Yes : IsProcessingUserGesture::No, frame->document() };
1954 std::optional<UserTypingGestureIndicator> userTypingGestureIndicator;
1955 if (options.processingUserGesture)
1956 userTypingGestureIndicator.emplace(*frame);
1957
1958 bool replacesText = false;
1959 if (replacementEditingRange.location != notFound) {
1960 if (auto replacementRange = EditingRange::toRange(*frame, replacementEditingRange, options.editingRangeIsRelativeTo)) {
1961 SetForScope isSelectingTextWhileInsertingAsynchronously(m_isSelectingTextWhileInsertingAsynchronously, options.suppressSelectionUpdate);
1962 protect(frame->selection())->setSelection(VisibleSelection(*replacementRange));
1963 replacesText = replacementEditingRange.length;
1964 }
1965 }
1966
1967 if (options.registerUndoGroup)
1968 send(Messages::WebPageProxy::RegisterInsertionUndoGrouping());
1969
1970 RefPtr focusedElement = frame->document() ? frame->document()->focusedElement() : nullptr;
1971 if (focusedElement && options.shouldSimulateKeyboardInput)
1972 focusedElement->dispatchEvent(Event::create(eventNames().keydownEvent, Event::CanBubble::Yes, Event::IsCancelable::Yes));
1973
1974 Ref editor = frame->editor();
1975 if (!editor->hasComposition()) {
1976 if (text.isEmpty() && frame->selection().isRange())
1977 editor->deleteWithDirection(SelectionDirection::Backward, TextGranularity::CharacterGranularity, false, true);
1978 else {
1979 // An insertText: might be handled by other responders in the chain if we don't handle it.
1980 // One example is space bar that results in scrolling down the page.
1981 editor->insertText(text, nullptr, replacesText ? TextEventInputAutocompletion : TextEventInputKeyboard);
1982 }
1983 } else
1984 editor->confirmComposition(text);
1985
1986 auto baseWritingDirectionFromInputMode = [&] -> std::optional<WritingDirection> {
1987 auto direction = options.directionFromCurrentInputMode;
1988 if (!direction)
1989 return { };
1990
1991 if (text != "\n"_s)
1992 return { };
1993
1994 auto selection = frame->selection().selection();
1995 if (!selection.isCaret() || !selection.isContentEditable())
1996 return { };
1997
1998 auto start = selection.visibleStart();
1999 if (!isStartOfLine(start) || !isEndOfLine(start))
2000 return { };
2001
2002 if (direction == directionOfEnclosingBlock(start.deepEquivalent()))
2003 return { };
2004
2005 return { direction == TextDirection::LTR ? WritingDirection::LeftToRight : WritingDirection::RightToLeft };
2006 }();
2007
2008 if (baseWritingDirectionFromInputMode) {
2009 editor->setBaseWritingDirection(*baseWritingDirectionFromInputMode);
2010 editor->setTextAlignmentForChangedBaseWritingDirection(*baseWritingDirectionFromInputMode);
2011 }
2012
2013 if (focusedElement && options.shouldSimulateKeyboardInput) {
2014 focusedElement->dispatchEvent(Event::create(eventNames().keyupEvent, Event::CanBubble::Yes, Event::IsCancelable::Yes));
2015 focusedElement->dispatchEvent(Event::create(eventNames().changeEvent, Event::CanBubble::Yes, Event::IsCancelable::Yes));
2016 }
2017}
2018
2019void WebPage::hasMarkedText(CompletionHandler<void(bool)>&& completionHandler)
2020{
2021 RefPtr focusedOrMainFrame = corePage()->focusController().focusedOrMainFrame();
2022 if (!focusedOrMainFrame)
2023 return completionHandler(false);
2024 completionHandler(focusedOrMainFrame->editor().hasComposition());
2025}
2026
2027void WebPage::getMarkedRangeAsync(CompletionHandler<void(const EditingRange&)>&& completionHandler)
2028{
2029 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
2030 if (!frame)
2031 return completionHandler({ });
2032
2033 completionHandler(EditingRange::fromRange(*frame, protect(frame->editor())->compositionRange()));
2034}
2035
2036void WebPage::getSelectedRangeAsync(CompletionHandler<void(const EditingRange& selectedRange, const EditingRange& compositionRange)>&& completionHandler)
2037{
2038 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
2039 if (!frame)
2040 return completionHandler({ }, { });
2041
2042 completionHandler(EditingRange::fromRange(*frame, frame->selection().selection().toNormalizedRange()),
2043 EditingRange::fromRange(*frame, protect(frame->editor())->compositionRange()));
2044}
2045
2046void WebPage::characterIndexForPointAsync(const WebCore::IntPoint& point, CompletionHandler<void(uint64_t)>&& completionHandler)
2047{
2048 RefPtr localMainFrame = this->localMainFrame();
2049 if (!localMainFrame)
2050 return;
2051 constexpr OptionSet<HitTestRequest::Type> hitType { HitTestRequest::Type::ReadOnly, HitTestRequest::Type::Active, HitTestRequest::Type::DisallowUserAgentShadowContent, HitTestRequest::Type::AllowChildFrameContent };
2052 auto result = localMainFrame->eventHandler().hitTestResultAtPoint(point, hitType);
2053 RefPtr frame = result.innerNonSharedNode() ? result.innerNodeFrame() : corePage()->focusController().focusedOrMainFrame();
2054 if (!frame)
2055 return completionHandler({ });
2056 auto range = frame->rangeForPoint(result.roundedPointInInnerNodeFrame());
2057 auto editingRange = EditingRange::fromRange(*frame, range);
2058 completionHandler(editingRange.location);
2059}
2060
2061void WebPage::firstRectForCharacterRangeAsync(const EditingRange& editingRange, CompletionHandler<void(const WebCore::IntRect&, const EditingRange&)>&& completionHandler)
2062{
2063 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
2064 if (!frame)
2065 return completionHandler({ }, { });
2066
2067 auto range = EditingRange::toRange(*frame, editingRange);
2068 if (!range)
2069 return completionHandler({ }, editingRange);
2070
2071 auto rect = RefPtr(frame->view())->contentsToWindow(protect(frame->editor())->firstRectForRange(*range));
2072 auto startPosition = makeContainerOffsetPosition(range->start);
2073
2074 auto endPosition = endOfLine(startPosition);
2075 if (endPosition.isNull())
2076 endPosition = startPosition;
2077 else if (endPosition.affinity() == Affinity::Downstream && inSameLine(startPosition, endPosition)) {
2078 auto nextLineStartPosition = positionOfNextBoundaryOfGranularity(endPosition, TextGranularity::LineGranularity, SelectionDirection::Forward);
2079 if (nextLineStartPosition.isNotNull() && endPosition < nextLineStartPosition)
2080 endPosition = nextLineStartPosition;
2081 }
2082
2083 auto endBoundary = makeBoundaryPoint(endPosition);
2084 if (!endBoundary)
2085 return completionHandler({ }, editingRange);
2086
2087 auto rangeForFirstLine = EditingRange::fromRange(*frame, makeSimpleRange(range->start, WTF::move(endBoundary)));
2088
2089 rangeForFirstLine.location = std::min(std::max(rangeForFirstLine.location, editingRange.location), editingRange.location + editingRange.length);
2090 rangeForFirstLine.length = std::min(rangeForFirstLine.location + rangeForFirstLine.length, editingRange.location + editingRange.length) - rangeForFirstLine.location;
2091
2092 completionHandler(rect, rangeForFirstLine);
2093}
2094
2095void 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)
2096{
2097 platformWillPerformEditingCommand();
2098
2099 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
2100 if (!frame)
2101 return;
2102
2103 if (frame->selection().selection().isContentEditable()) {
2104 if (replacementEditingRange.location != notFound) {
2105 if (auto replacementRange = EditingRange::toRange(*frame, replacementEditingRange))
2106 protect(frame->selection())->setSelection(VisibleSelection(*replacementRange));
2107 }
2108 protect(frame->editor())->setComposition(text, underlines, highlights, annotations, selection.location, selection.location + selection.length);
2109 }
2110}
2111
2112void WebPage::setWritingSuggestion(const String& fullTextWithPrediction, const EditingRange& selection)
2113{
2114 platformWillPerformEditingCommand();
2115
2116 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
2117 if (!frame)
2118 return;
2119
2120 protect(frame->editor())->setWritingSuggestion(fullTextWithPrediction, { selection.location, selection.length });
2121}
2122
2123void WebPage::confirmCompositionAsync()
2124{
2125 platformWillPerformEditingCommand();
2126
2127 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
2128 if (!frame)
2129 return;
2130
2131 protect(frame->editor())->confirmComposition();
2132}
2133
2134void WebPage::getInformationFromImageData(const Vector<uint8_t>& data, CompletionHandler<void(Expected<std::pair<String, Vector<IntSize>>, WebCore::ImageDecodingError>&&)>&& completionHandler)
2135{
2136 if (m_isClosed)
2137 return completionHandler(makeUnexpected(ImageDecodingError::Internal));
2138
2139 if (SVGImage::isDataDecodable(m_page->settings(), data.span()))
2140 return completionHandler(std::make_pair(String { "public.svg-image"_s }, Vector<IntSize> { }));
2141
2142 completionHandler(utiAndAvailableSizesFromImageData(data.span()));
2143}
2144
2145void WebPage::insertTextPlaceholder(const IntSize& size, CompletionHandler<void(const std::optional<WebCore::ElementContext>&)>&& completionHandler)
2146{
2147 // Inserting the placeholder may run JavaScript, which can do anything, including frame destruction.
2148 RefPtr frame = corePage()->focusController().focusedOrMainFrame();
2149 if (!frame)
2150 return completionHandler({ });
2151
2152 auto placeholder = protect(frame->editor())->insertTextPlaceholder(size);
2153 completionHandler(placeholder ? contextForElement(*placeholder) : std::nullopt);
2154}
2155
2156void WebPage::removeTextPlaceholder(const ElementContext& placeholder, CompletionHandler<void()>&& completionHandler)
2157{
2158 if (auto element = elementForContext(placeholder)) {
2159 if (RefPtr frame = element->document().frame())
2160 protect(frame->editor())->removeTextPlaceholder(downcast<TextPlaceholderElement>(*element));
2161 }
2162 completionHandler();
2163}
2164
2165void WebPage::setObscuredContentInsetsFenced(const FloatBoxExtent& obscuredContentInsets, const WTF::MachSendRight& machSendRight)
2166{
2167 protect(drawingArea())->addFence(machSendRight);
2168 setObscuredContentInsets(obscuredContentInsets);
2169}
2170
2171void WebPage::willCommitLayerTree(RemoteLayerTreeTransaction& layerTransaction, WebCore::FrameIdentifier rootFrameID)
2172{
2173 RefPtr rootFrame = WebProcess::singleton().webFrame(rootFrameID);
2174 if (!rootFrame)
2175 return;
2176
2177 RefPtr localRootFrame = rootFrame->coreLocalFrame();
2178 if (!localRootFrame)
2179 return;
2180
2181 RefPtr frameView = localRootFrame->view();
2182 if (!frameView)
2183 return;
2184
2185 Ref page = *corePage();
2186
2187#if ENABLE(THREADED_ANIMATIONS)(defined 1 && 1)
2188 if (auto* acceleratedTimelinesUpdater = page->acceleratedTimelinesUpdater())
2189 layerTransaction.setTimelinesUpdate(acceleratedTimelinesUpdater->takeTimelinesUpdate());
2190#endif
2191
2192 layerTransaction.setContentsSize(frameView->contentsSize());
2193 layerTransaction.setScrollGeometryContentSize(frameView->scrollGeometryContentSize());
2194 layerTransaction.setScrollOrigin(frameView->scrollOrigin());
2195 layerTransaction.setScrollPosition(frameView->scrollPosition());
2196
2197 m_pendingThemeColorChange = false;
2198 m_pendingPageExtendedBackgroundColorChange = false;
2199 m_pendingSampledPageTopColorChange = false;
2200}
2201
2202void WebPage::willCommitMainFrameData(MainFrameData& data, const TransactionID& transactionID)
2203{
2204 RefPtr mainFrameView = localMainFrameView();
2205 if (!mainFrameView)
2206 return;
2207
2208 Ref page = *corePage();
2209 data.pageScaleFactor = page->pageScaleFactor();
2210 data.themeColor = page->themeColor();
2211 data.pageExtendedBackgroundColor = page->pageExtendedBackgroundColor();
2212 data.sampledPageTopColor = page->sampledPageTopColor();
2213
2214 if (std::exchange(m_needsFixedContainerEdgesUpdate, false)) {
2215 page->updateFixedContainerEdges(sidesRequiringFixedContainerEdges());
2216 data.fixedContainerEdges = page->fixedContainerEdges();
2217 }
2218
2219 data.baseLayoutViewportSize = mainFrameView->baseLayoutViewportSize();
2220 data.minStableLayoutViewportOrigin = mainFrameView->minStableLayoutViewportOrigin();
2221 data.maxStableLayoutViewportOrigin = mainFrameView->maxStableLayoutViewportOrigin();
2222
2223#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
2224 data.scaleWasSetByUIProcess = scaleWasSetByUIProcess();
2225 data.minimumScaleFactor = m_viewportConfiguration.minimumScale();
2226 data.maximumScaleFactor = m_viewportConfiguration.maximumScale();
2227 data.initialScaleFactor = m_viewportConfiguration.initialScale();
2228 data.viewportMetaTagInteractiveWidget = m_viewportConfiguration.viewportArguments().interactiveWidget;
2229 data.viewportMetaTagWidth = m_viewportConfiguration.viewportArguments().width;
2230 data.viewportMetaTagWidthWasExplicit = m_viewportConfiguration.viewportArguments().widthWasExplicit;
2231 data.viewportMetaTagCameFromImageDocument = m_viewportConfiguration.viewportArguments().type == ViewportArguments::Type::ImageDocument;
2232 data.avoidsUnsafeArea = m_viewportConfiguration.avoidsUnsafeArea();
2233 data.isInStableState = m_isInStableState;
2234 data.allowsUserScaling = allowsUserScaling();
2235 if (m_pendingDynamicViewportSizeUpdateID) {
2236 data.dynamicViewportSizeUpdateID = *m_pendingDynamicViewportSizeUpdateID;
2237 m_pendingDynamicViewportSizeUpdateID = std::nullopt;
2238 }
2239 if (m_lastTransactionPageScaleFactor != data.pageScaleFactor) {
2240 m_lastTransactionPageScaleFactor = data.pageScaleFactor;
2241 m_internals->lastTransactionIDWithScaleChange = transactionID;
2242 }
2243#endif
2244
2245 if (hasPendingEditorStateUpdate() || m_needsEditorStateVisualDataUpdate) {
2246 data.editorState = editorState();
2247 m_pendingEditorStateUpdateStatus = PendingEditorStateUpdateStatus::NotScheduled;
2248 m_needsEditorStateVisualDataUpdate = false;
2249 }
2250
2251#if ENABLE(SCROLL_STRETCH_NOTIFICATIONS)(defined ENABLE_SCROLL_STRETCH_NOTIFICATIONS && ENABLE_SCROLL_STRETCH_NOTIFICATIONS
)
2252 auto scrollOrigin = mainFrameView->scrollOrigin();
2253 auto scrollPosition = mainFrameView->scrollPosition();
2254 data.topScrollStretch = static_cast<uint64_t>(std::max(0, -scrollOrigin.y() - scrollPosition.y()));
2255#endif
2256}
2257
2258void WebPage::didFlushLayerTreeAtTime(MonotonicTime timestamp, bool flushSucceeded)
2259{
2260#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
2261 if (m_oldestNonStableUpdateVisibleContentRectsTimestamp != MonotonicTime()) {
2262 Seconds elapsed = timestamp - m_oldestNonStableUpdateVisibleContentRectsTimestamp;
2263 m_oldestNonStableUpdateVisibleContentRectsTimestamp = MonotonicTime();
2264
2265 m_estimatedLatency = m_estimatedLatency * 0.80 + elapsed * 0.20;
2266 }
2267#else
2268 UNUSED_PARAM(timestamp)(void)timestamp;
2269#endif
2270#if ENABLE(GPU_PROCESS)(defined 1 && 1)
2271 if (!flushSucceeded) {
2272 if (RefPtr proxy = m_remoteRenderingBackendProxy)
2273 proxy->didBecomeUnresponsive();
2274 }
2275#endif
2276}
2277
2278bool WebPage::isSpeaking() const
2279{
2280 auto sendResult = const_cast<WebPage*>(this)->sendSync(Messages::WebPageProxy::GetIsSpeaking());
2281 auto [result] = sendResult.takeReplyOr(false);
2282 return result;
2283}
2284
2285bool WebPage::shouldAllowSingleClickToChangeSelection(WebCore::Node& targetNode, const WebCore::VisibleSelection& newSelection)
2286{
2287#if !PLATFORM(MAC)(defined 1 && 1) || HAVE(APPKIT_GESTURES_SUPPORT)(defined HAVE_APPKIT_GESTURES_SUPPORT && HAVE_APPKIT_GESTURES_SUPPORT
)
2288#if HAVE(APPKIT_GESTURES_SUPPORT)(defined HAVE_APPKIT_GESTURES_SUPPORT && HAVE_APPKIT_GESTURES_SUPPORT
)
2289 if (!m_page->settings().useAppKitGestures())
2290 return true;
2291#endif
2292
2293 if (RefPtr editableRoot = newSelection.rootEditableElement(); editableRoot && editableRoot == targetNode.rootEditableElement()) {
2294 // FIXME: This logic should be made consistent for both macOS and iOS.
2295#if PLATFORM(MAC)(defined 1 && 1)
2296 return false;
2297#else
2298 // Text interaction gestures will handle selection in the case where we are already editing the node. In the case where we're
2299 // just starting to focus an editable element by tapping on it, only change the selection if we weren't already showing an
2300 // input view prior to handling the tap.
2301 return !(m_completingSyntheticClick ? m_wasShowingInputViewForFocusedElementDuringLastPotentialTap : m_isShowingInputViewForFocusedElement);
2302#endif
2303 }
2304#endif // !PLATFORM(MAC) || HAVE(APPKIT_GESTURES_SUPPORT)
2305
2306 return true;
2307}
2308
2309void WebPage::selectWithGesture(const IntPoint& point, GestureType gestureType, GestureRecognizerState gestureState, bool isInteractingWithFocusedElement, CompletionHandler<void(const WebCore::IntPoint&, GestureType, GestureRecognizerState, OptionSet<SelectionFlags>)>&& completionHandler)
2310{
2311 if (gestureState == GestureRecognizerState::Began)
2312 updateFocusBeforeSelectingTextAtLocation(point);
2313
2314 RefPtr frame = m_page->focusController().focusedOrMainFrame();
2315 if (!frame) {
2316 completionHandler({ }, gestureType, gestureState, { });
2317 return;
2318 }
2319
2320 VisiblePosition position = visiblePositionInFocusedNodeForPoint(*frame, point, isInteractingWithFocusedElement);
2321
2322 if (position.isNull()) {
2323 completionHandler(point, gestureType, gestureState, { });
2324 return;
2325 }
2326 std::optional<SimpleRange> range;
2327 OptionSet<SelectionFlags> flags;
2328 GestureRecognizerState wkGestureState = gestureState;
2329 switch (gestureType) {
2330 case GestureType::PhraseBoundary: {
2331 if (!frame->editor().hasComposition())
2332 break;
2333 auto markedRange = WTF::protect(frame->editor())->compositionRange();
2334 auto startPosition = VisiblePosition { makeDeprecatedLegacyPosition(markedRange->start) };
2335 position = std::clamp(position, startPosition, VisiblePosition { makeDeprecatedLegacyPosition(markedRange->end) });
2336 if (wkGestureState != GestureRecognizerState::Began)
2337 flags = distanceBetweenPositions(startPosition, frame->selection().selection().start()) != distanceBetweenPositions(startPosition, position) ? SelectionFlags::PhraseBoundaryChanged : OptionSet<SelectionFlags> { };
2338 else
2339 flags = SelectionFlags::PhraseBoundaryChanged;
2340 range = makeSimpleRange(position);
2341 break;
2342 }
2343
2344 case GestureType::OneFingerTap: {
2345 auto [adjustedPosition, withinWordBoundary] = wordBoundaryForPositionWithoutCrossingLine(position);
2346 if (withinWordBoundary == WithinWordBoundary::Yes)
2347 flags = SelectionFlags::WordIsNearTap;
2348 range = makeSimpleRange(adjustedPosition);
2349 break;
2350 }
2351
2352 case GestureType::Loupe:
2353 if (position.rootEditableElement())
2354 range = makeSimpleRange(position);
2355 else {
2356#if !PLATFORM(MACCATALYST)(defined WTF_PLATFORM_MACCATALYST && WTF_PLATFORM_MACCATALYST
)
2357 range = wordRangeFromPosition(position);
2358#else
2359 switch (wkGestureState) {
2360 case GestureRecognizerState::Began:
2361 m_startingGestureRange = makeSimpleRange(position);
2362 break;
2363 case GestureRecognizerState::Changed:
2364 if (m_startingGestureRange) {
2365 auto& start = m_startingGestureRange->start;
2366 if (makeDeprecatedLegacyPosition(start) < position)
2367 range = makeSimpleRange(start, position);
2368 else
2369 range = makeSimpleRange(position, start);
2370 }
2371 break;
2372 case GestureRecognizerState::Ended:
2373 case GestureRecognizerState::Cancelled:
2374 m_startingGestureRange = std::nullopt;
2375 break;
2376 case GestureRecognizerState::Failed:
2377 case GestureRecognizerState::Possible:
2378 ASSERT_NOT_REACHED()((void)0);
2379 break;
2380 }
2381#endif
2382 }
2383 break;
2384
2385 case GestureType::TapAndAHalf:
2386 switch (wkGestureState) {
2387 case GestureRecognizerState::Began:
2388 range = wordRangeFromPosition(position);
2389 if (range)
2390 m_currentWordRange = { { *range } };
2391 else
2392 m_currentWordRange = std::nullopt;
2393 break;
2394 case GestureRecognizerState::Changed:
2395 if (!m_currentWordRange)
2396 break;
2397 range = m_currentWordRange;
2398 if (position < makeDeprecatedLegacyPosition(range->start))
2399 range->start = *makeBoundaryPoint(position);
2400 if (position > makeDeprecatedLegacyPosition(range->end))
2401 range->end = *makeBoundaryPoint(position);
2402 break;
2403 case GestureRecognizerState::Ended:
2404 case GestureRecognizerState::Cancelled:
2405 m_currentWordRange = std::nullopt;
2406 break;
2407 case GestureRecognizerState::Failed:
2408 case GestureRecognizerState::Possible:
2409 ASSERT_NOT_REACHED()((void)0);
2410 break;
2411 }
2412 break;
2413
2414 case GestureType::OneFingerDoubleTap:
2415 if (atBoundaryOfGranularity(position, TextGranularity::LineGranularity, SelectionDirection::Forward)) {
2416 // Double-tap at end of line only places insertion point there.
2417 // This helps to get the callout for pasting at ends of lines,
2418 // paragraphs, and documents.
2419 range = makeSimpleRange(position);
2420 } else
2421 range = wordRangeFromPosition(position);
2422 break;
2423
2424 case GestureType::TwoFingerSingleTap:
2425 // Single tap with two fingers selects the entire paragraph.
2426 range = enclosingTextUnitOfGranularity(position, TextGranularity::ParagraphGranularity, SelectionDirection::Forward);
2427 break;
2428
2429 case GestureType::OneFingerTripleTap:
2430 if (atBoundaryOfGranularity(position, TextGranularity::LineGranularity, SelectionDirection::Forward)) {
2431 // Triple-tap at end of line only places insertion point there.
2432 // This helps to get the callout for pasting at ends of lines, paragraphs, and documents.
2433 range = makeSimpleRange(position);
2434 } else
2435 range = enclosingTextUnitOfGranularity(position, TextGranularity::ParagraphGranularity, SelectionDirection::Forward);
2436 break;
2437
2438 default:
2439 break;
2440 }
2441 if (range)
2442 WTF::protect(frame->selection())->setSelectedRange(range, position.affinity(), WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered::Yes);
2443
2444 completionHandler(point, gestureType, gestureState, flags);
2445}
2446
2447void WebPage::updateFocusBeforeSelectingTextAtLocation(const IntPoint& point)
2448{
2449 static constexpr OptionSet hitType { HitTestRequest::Type::ReadOnly, HitTestRequest::Type::Active, HitTestRequest::Type::AllowVisibleChildFrameContentOnly };
2450 RefPtr localMainFrame = WTF::protect(m_page)->localMainFrame();
2451 if (!localMainFrame)
2452 return;
2453
2454 auto result = localMainFrame->eventHandler().hitTestResultAtPoint(point, hitType);
2455 RefPtr hitNode = result.innerNode();
2456 if (!hitNode || !hitNode->renderer())
2457 return;
2458
2459 RefPtr frame = result.innerNodeFrame();
2460 m_page->focusController().setFocusedFrame(frame.get());
2461
2462 if (!result.isOverWidget())
2463 return;
2464
2465#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
2466 if (RefPtr pluginView = pluginViewForFrame(frame.get()))
2467 pluginView->focusPluginElement();
2468#endif
2469}
2470
2471IntRect WebPage::rootViewInteractionBounds(const Node& node)
2472{
2473 RefPtr frame = node.document().frame();
2474 if (!frame)
2475 return { };
2476
2477 RefPtr view = frame->view();
2478 if (!view)
2479 return { };
2480
2481 return view->contentsToRootView(absoluteInteractionBounds(node));
2482}
2483
2484IntRect WebPage::absoluteInteractionBounds(const Node& node)
2485{
2486 RefPtr frame = node.document().frame();
2487 if (!frame)
2488 return { };
2489
2490 RefPtr view = frame->view();
2491 if (!view)
2492 return { };
2493
2494 CheckedPtr renderer = node.renderer();
2495 if (!renderer)
2496 return { };
2497
2498 if (CheckedPtr box = dynamicDowncast<RenderBox>(*renderer)) {
2499 FloatRect rect;
2500 // FIXME: want borders or not?
2501 if (box->style().isOverflowVisible())
2502 rect = box->layoutOverflowRect();
2503 else
2504 rect = box->clientBoxRect();
2505 return box->localToAbsoluteQuad(rect).enclosingBoundingBox();
2506 }
2507
2508 CheckedRef style = renderer->style();
2509 FloatRect boundingBox = renderer->absoluteBoundingBoxRect(true /* use transforms*/);
2510 // This is wrong. It's subtracting borders after converting to absolute coords on something that probably doesn't represent a rectangular element.
2511 boundingBox.move(WebCore::Style::evaluate<float>(style->usedBorderLeftWidth(), WebCore::Style::ZoomNeeded { }), WebCore::Style::evaluate<float>(style->usedBorderTopWidth(), WebCore::Style::ZoomNeeded { }));
2512 boundingBox.setWidth(boundingBox.width() - WebCore::Style::evaluate<float>(style->usedBorderLeftWidth(), WebCore::Style::ZoomNeeded { }) - WebCore::Style::evaluate<float>(style->usedBorderRightWidth(), WebCore::Style::ZoomNeeded { }));
2513 boundingBox.setHeight(boundingBox.height() - WebCore::Style::evaluate<float>(style->usedBorderBottomWidth(), WebCore::Style::ZoomNeeded { }) - WebCore::Style::evaluate<float>(style->usedBorderTopWidth(), WebCore::Style::ZoomNeeded { }));
2514 return enclosingIntRect(boundingBox);
2515}
2516
2517static IntRect elementBoundsInFrame(const LocalFrame& frame, const Element& focusedElement)
2518{
2519 WTF::protect(frame.document())->updateLayout(LayoutOptions::IgnorePendingStylesheets);
2520
2521 if (focusedElement.hasTagName(HTMLNames::textareaTag) || focusedElement.hasTagName(HTMLNames::inputTag) || focusedElement.hasTagName(HTMLNames::selectTag))
2522 return WebPage::absoluteInteractionBounds(focusedElement);
2523
2524 if (RefPtr rootEditableElement = focusedElement.rootEditableElement())
2525 return WebPage::absoluteInteractionBounds(*rootEditableElement);
2526
2527 return { };
2528}
2529
2530IntPoint WebPage::constrainPoint(const IntPoint& point, const LocalFrame& frame, const Element& focusedElement)
2531{
2532 ASSERT(&focusedElement.document() == frame.document())((void)0);
2533 const int DEFAULT_CONSTRAIN_INSET = 2;
2534 IntRect innerFrame = elementBoundsInFrame(frame, focusedElement);
2535 IntPoint constrainedPoint = point;
2536
2537 int minX = innerFrame.x() + DEFAULT_CONSTRAIN_INSET;
2538 int maxX = innerFrame.maxX() - DEFAULT_CONSTRAIN_INSET;
2539 int minY = innerFrame.y() + DEFAULT_CONSTRAIN_INSET;
2540 int maxY = innerFrame.maxY() - DEFAULT_CONSTRAIN_INSET;
2541
2542 if (point.x() < minX)
2543 constrainedPoint.setX(minX);
2544 else if (point.x() > maxX)
2545 constrainedPoint.setX(maxX);
2546
2547 if (point.y() < minY)
2548 constrainedPoint.setY(minY);
2549 else if (point.y() >= maxY)
2550 constrainedPoint.setY(maxY);
2551
2552 return constrainedPoint;
2553}
2554
2555VisiblePosition WebPage::visiblePositionInFocusedNodeForPoint(const LocalFrame& frame, const IntPoint& point, bool isInteractingWithFocusedElement)
2556{
2557 IntPoint adjustedPoint(WTF::protect(frame.view())->rootViewToContents(point));
2558 IntPoint constrainedPoint = m_focusedElement && isInteractingWithFocusedElement ? WebPage::constrainPoint(adjustedPoint, frame, WTF::protect(*m_focusedElement)) : adjustedPoint;
2559 return frame.visiblePositionForPoint(constrainedPoint);
2560}
2561
2562InteractionInformationAtPosition WebPage::positionInformation(const InteractionInformationRequest& request)
2563{
2564 return WebKit::positionInformationForWebPage(*this, request);
2565}
2566
2567void WebPage::requestPositionInformation(const InteractionInformationRequest& request)
2568{
2569 sendEditorStateUpdate();
2570 send(Messages::WebPageProxy::DidReceivePositionInformation(positionInformation(request)));
2571}
2572
2573bool WebPage::isAssistableElement(Element& element)
2574{
2575 if (is<HTMLSelectElement>(element))
2576 return true;
2577 if (is<HTMLTextAreaElement>(element))
2578 return true;
2579 if (RefPtr inputElement = dynamicDowncast<HTMLInputElement>(element)) {
2580 // FIXME: This laundry list of types is not a good way to factor this. Need a suitable function on HTMLInputElement itself.
2581#if ENABLE(INPUT_TYPE_WEEK_PICKER)(defined 0 && 0)
2582 if (inputElement->isWeekField())
2583 return true;
2584#endif
2585 return inputElement->isTextField() || inputElement->isDateField() || inputElement->isDateTimeLocalField() || inputElement->isMonthField() || inputElement->isTimeField() || inputElement->isColorControl();
2586 }
2587 if (is<HTMLIFrameElement>(element))
2588 return false;
2589 return element.isContentEditable();
2590}
2591
2592RefPtr<HTMLAnchorElement> WebPage::containingLinkAnchorElement(Element& element)
2593{
2594 // FIXME: There is code in the drag controller that supports any link, even if it's not an HTMLAnchorElement. Why is this different?
2595 for (Ref currentElement : lineageOfType<HTMLAnchorElement>(element)) {
2596 if (currentElement->isLink())
2597 return currentElement;
2598 }
2599 return nullptr;
2600}
2601
2602void WebPage::selectPositionAtPoint(WebCore::IntPoint point, bool isInteractingWithFocusedElement, CompletionHandler<void()>&& completionHandler)
2603{
2604 SetForScope userIsInteractingChange { m_userIsInteracting, true };
2605
2606 updateFocusBeforeSelectingTextAtLocation(point);
2607
2608 RefPtr frame = m_page->focusController().focusedOrMainFrame();
2609 if (!frame)
2610 return completionHandler();
2611
2612 VisiblePosition position = visiblePositionInFocusedNodeForPoint(*frame, point, isInteractingWithFocusedElement);
2613
2614 if (position.isNotNull())
2615 WTF::protect(frame->selection())->setSelectedRange(makeSimpleRange(position), position.affinity(), WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered::Yes);
2616 completionHandler();
2617}
2618
2619std::optional<SimpleRange> WebPage::rangeForGranularityAtPoint(LocalFrame& frame, const WebCore::IntPoint& point, WebCore::TextGranularity granularity, bool isInteractingWithFocusedElement)
2620{
2621 auto position = visiblePositionInFocusedNodeForPoint(frame, point, isInteractingWithFocusedElement);
2622 switch (granularity) {
2623 case TextGranularity::CharacterGranularity:
2624 return makeSimpleRange(position);
2625 case TextGranularity::WordGranularity:
2626 return wordRangeFromPosition(position);
2627 case TextGranularity::SentenceGranularity:
2628 case TextGranularity::ParagraphGranularity:
2629 return enclosingTextUnitOfGranularity(position, granularity, SelectionDirection::Forward);
2630 case TextGranularity::DocumentGranularity:
2631 // FIXME: Makes no sense that this mutates the current selection and returns null.
2632 protect(frame.selection())->selectAll();
2633 return std::nullopt;
2634 case TextGranularity::LineGranularity:
2635 case TextGranularity::LineBoundary:
2636 case TextGranularity::SentenceBoundary:
2637 case TextGranularity::ParagraphBoundary:
2638 case TextGranularity::DocumentBoundary:
2639 ASSERT_NOT_REACHED()((void)0);
2640 return std::nullopt;
2641 }
2642 ASSERT_NOT_REACHED()((void)0);
2643 return std::nullopt;
2644}
2645
2646void WebPage::setSelectionRange(WebCore::IntPoint point, WebCore::TextGranularity granularity, bool isInteractingWithFocusedElement)
2647{
2648 updateFocusBeforeSelectingTextAtLocation(point);
2649
2650 RefPtr frame = m_page->focusController().focusedOrMainFrame();
2651 if (!frame)
2652 return;
2653
2654#if ENABLE(PDF_PLUGIN)(defined 1 && 1) && PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
2655 // FIXME: Support text selection in embedded PDFs.
2656 if (RefPtr pluginView = focusedPluginViewForFrame(*frame)) {
2657 pluginView->setSelectionRange(point, granularity);
2658 return;
2659 }
2660#endif // ENABLE(PDF_PLUGIN) && PLATFORM(IOS_FAMILY)
2661
2662 auto range = rangeForGranularityAtPoint(*frame, point, granularity, isInteractingWithFocusedElement);
2663 if (range)
2664 protect(frame->selection())->setSelectedRange(*range, Affinity::Upstream, WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered::Yes);
2665
2666 m_initialSelection = range;
2667}
2668
2669void WebPage::updateSelectionWithExtentPointAndBoundary(WebCore::IntPoint point, WebCore::TextGranularity granularity, bool isInteractingWithFocusedElement, TextInteractionSource source, CompletionHandler<void(bool)>&& callback)
2670{
2671 RefPtr frame = m_page->focusController().focusedOrMainFrame();
2672 if (!frame)
2673 return callback(false);
2674
2675#if ENABLE(PDF_PLUGIN)(defined 1 && 1) && PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
2676 if (RefPtr pluginView = focusedPluginViewForFrame(*frame)) {
2677 auto movedEndpoint = pluginView->extendInitialSelection(point, granularity);
2678 return callback(movedEndpoint == SelectionEndpoint::End);
2679 }
2680#endif // ENABLE(PDF_PLUGIN) && PLATFORM(IOS_FAMILY)
2681
2682 auto position = visiblePositionInFocusedNodeForPoint(*frame, point, isInteractingWithFocusedElement);
2683 auto newRange = rangeForGranularityAtPoint(*frame, point, granularity, isInteractingWithFocusedElement);
2684
2685 if (position.isNull() || !m_initialSelection || !newRange)
2686 return callback(false);
2687
2688#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
2689 addTextInteractionSources(source);
2690#endif
2691
2692 auto initialSelectionStartPosition = makeDeprecatedLegacyPosition(m_initialSelection->start);
2693 auto initialSelectionEndPosition = makeDeprecatedLegacyPosition(m_initialSelection->end);
2694
2695 VisiblePosition selectionStart = initialSelectionStartPosition;
2696 VisiblePosition selectionEnd = initialSelectionEndPosition;
2697 if (position > initialSelectionEndPosition)
2698 selectionEnd = makeDeprecatedLegacyPosition(newRange->end);
2699 else if (position < initialSelectionStartPosition)
2700 selectionStart = makeDeprecatedLegacyPosition(newRange->start);
2701
2702 if (auto range = makeSimpleRange(selectionStart, selectionEnd))
2703 protect(frame->selection())->setSelectedRange(range, Affinity::Upstream, WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered::Yes);
2704
2705#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
2706 if (!m_hasAnyActiveTouchPoints) {
2707 // Ensure that `Touch` doesn't linger around in `m_activeTextInteractionSources` after
2708 // the user has ended all active touches.
2709 removeTextInteractionSources(TextInteractionSource::Touch);
2710 }
2711#endif // PLATFORM(IOS_FAMILY)
2712
2713 callback(selectionStart == initialSelectionStartPosition);
2714}
2715
2716void WebPage::updateSelectionWithExtentPoint(WebCore::IntPoint point, bool isInteractingWithFocusedElement, RespectSelectionAnchor respectSelectionAnchor, CompletionHandler<void(bool)>&& callback)
2717{
2718 RefPtr frame = m_page->focusController().focusedOrMainFrame();
2719 if (!frame)
2720 return callback(false);
2721
2722 auto position = visiblePositionInFocusedNodeForPoint(*frame, point, isInteractingWithFocusedElement);
2723
2724 if (position.isNull())
2725 return callback(false);
2726
2727 VisiblePosition selectionStart;
2728 VisiblePosition selectionEnd;
2729
2730 if (respectSelectionAnchor == RespectSelectionAnchor::Yes) {
2731#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
2732 if (m_selectionAnchor == SelectionAnchor::Start) {
2733 selectionStart = frame->selection().selection().visibleStart();
2734 selectionEnd = position;
2735 if (position <= selectionStart) {
2736 selectionStart = selectionStart.previous();
2737 selectionEnd = frame->selection().selection().visibleEnd();
2738 m_selectionAnchor = SelectionAnchor::End;
2739 }
2740 } else {
2741 selectionStart = position;
2742 selectionEnd = frame->selection().selection().visibleEnd();
2743 if (position >= selectionEnd) {
2744 selectionStart = frame->selection().selection().visibleStart();
2745 selectionEnd = selectionEnd.next();
2746 m_selectionAnchor = SelectionAnchor::Start;
2747 }
2748 }
2749#endif // PLATFORM(IOS_FAMILY)
2750 } else {
2751 auto currentStart = frame->selection().selection().visibleStart();
2752 auto currentEnd = frame->selection().selection().visibleEnd();
2753 if (position <= currentStart) {
2754 selectionStart = position;
2755 selectionEnd = currentEnd;
2756 } else if (position >= currentEnd) {
2757 selectionStart = currentStart;
2758 selectionEnd = position;
2759 }
2760 }
2761
2762 if (auto range = makeSimpleRange(selectionStart, selectionEnd))
2763 protect(frame->selection())->setSelectedRange(range, Affinity::Upstream, WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered::Yes);
2764
2765#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
2766 callback(m_selectionAnchor == SelectionAnchor::Start);
2767#else
2768 callback(true);
2769#endif
2770}
2771
2772void WebPage::selectTextWithGranularityAtPoint(WebCore::IntPoint point, WebCore::TextGranularity granularity, bool isInteractingWithFocusedElement, CompletionHandler<void()>&& completionHandler)
2773{
2774#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
2775 if (!m_potentialTapNode) {
2776 setSelectionRange(point, granularity, isInteractingWithFocusedElement);
2777 completionHandler();
2778 return;
2779 }
2780
2781 ASSERT(!m_selectionChangedHandler)((void)0);
2782 if (auto selectionChangedHandler = std::exchange(m_selectionChangedHandler, { }))
2783 selectionChangedHandler();
2784
2785 m_selectionChangedHandler = [point, granularity, isInteractingWithFocusedElement, completionHandler = WTF::move(completionHandler), weakThis = WeakPtr { *this }]() mutable {
2786 RefPtr protectedThis = weakThis.get();
2787 if (!protectedThis) {
2788 completionHandler();
2789 return;
2790 }
2791 protectedThis->setSelectionRange(point, granularity, isInteractingWithFocusedElement);
2792 completionHandler();
2793 };
2794#else
2795 setSelectionRange(point, granularity, isInteractingWithFocusedElement);
2796 completionHandler();
2797#endif
2798}
2799
2800#if ENABLE(TWO_PHASE_CLICKS)(defined 0 && 0)
2801
2802static void dispatchSyntheticMouseMove(LocalFrame& localFrame, const WebCore::FloatPoint& location, OptionSet<WebEventModifier> modifiers, WebCore::PointerID pointerId, WebCore::MouseEventInputSource inputSource)
2803{
2804 auto roundedAdjustedPoint = roundedIntPoint(location);
2805 auto mouseEvent = PlatformMouseEvent(
2806 roundedAdjustedPoint, roundedAdjustedPoint,
2807 MouseButton::None, PlatformEvent::Type::MouseMoved, 0,
2808 platform(modifiers), MonotonicTime::now(),
2809 WebCore::ForceAtClick, WebCore::SyntheticClickType::OneFingerTap,
2810 inputSource,
2811 pointerId
2812 );
2813 localFrame.eventHandler().dispatchSyntheticMouseMove(mouseEvent);
2814}
2815
2816void WebPage::handleSyntheticClick(std::optional<WebCore::FrameIdentifier> frameID, Node& nodeRespondingToClick, const WebCore::FloatPoint& location, OptionSet<WebEventModifier> modifiers, WebCore::PointerID pointerId)
2817{
2818 Ref respondingDocument = nodeRespondingToClick.document();
2819 m_hasHandledSyntheticClick = true;
2820
2821 if (!respondingDocument->settings().contentChangeObserverEnabled() || respondingDocument->quirks().shouldIgnoreContentObservationForClick(nodeRespondingToClick)) {
2822 completeSyntheticClick(frameID, nodeRespondingToClick, location, modifiers, WebCore::SyntheticClickType::OneFingerTap, pointerId);
2823 return;
2824 }
2825
2826 Ref contentChangeObserver = respondingDocument->contentChangeObserver();
2827 contentChangeObserver->setClickTarget(nodeRespondingToClick);
2828 auto targetNodeWentFromHiddenToVisible = contentChangeObserver->hiddenTouchTarget() == &nodeRespondingToClick && ContentChangeObserver::isConsideredVisible(nodeRespondingToClick);
2829 {
2830 LOG_WITH_STREAM(ContentObservation, stream << "handleSyntheticClick: node(" << &nodeRespondingToClick << ") " << location)((void)0);
2831 ContentChangeObserver::MouseMovedScope observingScope(respondingDocument);
2832 RefPtr localRootFrame = this->localRootFrame(frameID);
2833 if (!localRootFrame)
2834 return;
2835 dispatchSyntheticMouseMove(*localRootFrame, location, modifiers, pointerId, m_potentialTapInputSource);
2836 protect(localRootFrame->document())->updateStyleIfNeeded();
2837 if (m_isClosed)
2838 return;
2839 }
2840
2841 if (targetNodeWentFromHiddenToVisible) {
2842 LOG(ContentObservation, "handleSyntheticClick: target node was hidden and now is visible -> hover.")((void)0);
2843 didHandleTapAsHover();
2844 return;
2845 }
2846
2847 auto nodeTriggersFastPath = [&](auto& targetNode) {
2848 RefPtr element = dynamicDowncast<Element>(targetNode);
2849 if (!element)
2850 return false;
2851 if (is<HTMLFormControlElement>(*element))
2852 return true;
2853 if (element->document().quirks().shouldIgnoreAriaForFastPathContentObservationCheck())
2854 return false;
2855 auto ariaRole = AccessibilityObject::ariaRoleToWebCoreRole(element->getAttribute(HTMLNames::roleAttr));
2856 return AccessibilityObject::isARIAControl(ariaRole);
2857 };
2858 auto targetNodeTriggersFastPath = nodeTriggersFastPath(nodeRespondingToClick);
2859
2860 auto observedContentChange = contentChangeObserver->observedContentChange();
2861 auto continueContentObservation = !(observedContentChange == WebCore::ContentChange::Visibility || targetNodeTriggersFastPath);
2862 if (continueContentObservation) {
2863 // Wait for callback to didFinishContentChangeObserving() to decide whether to send the click event.
2864 const Seconds observationDuration = 32_ms;
2865 contentChangeObserver->startContentObservationForDuration(observationDuration);
2866 LOG(ContentObservation, "handleSyntheticClick: Can't decide it yet -> wait.")((void)0);
2867 m_pendingSyntheticClickNode = nodeRespondingToClick;
2868 m_pendingSyntheticClickLocation = location;
2869 m_pendingSyntheticClickModifiers = modifiers;
2870 m_pendingSyntheticClickPointerId = pointerId;
2871 return;
2872 }
2873 contentChangeObserver->stopContentObservation();
2874 callOnMainRunLoop([protectedThis = Ref { *this }, targetNode = Ref<Node>(nodeRespondingToClick), location, modifiers, observedContentChange, pointerId, frameID] {
2875 if (protectedThis->m_isClosed || !protectedThis->corePage())
2876 return;
2877
2878 auto shouldStayAtHoverState = observedContentChange == WebCore::ContentChange::Visibility;
2879 if (shouldStayAtHoverState) {
2880 // 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.
2881 if (RefPtr localRootFrame = protectedThis->localRootFrame(frameID))
2882 dispatchSyntheticMouseMove(*localRootFrame, location, modifiers, pointerId, protectedThis->m_potentialTapInputSource);
2883 LOG(ContentObservation, "handleSyntheticClick: Observed meaningful visible change -> hover.")((void)0);
2884 protectedThis->didHandleTapAsHover();
2885 return;
2886 }
2887 LOG(ContentObservation, "handleSyntheticClick: calling completeSyntheticClick -> click.")((void)0);
2888 protectedThis->completeSyntheticClick(frameID, targetNode, location, modifiers, WebCore::SyntheticClickType::OneFingerTap, pointerId);
2889 });
2890}
2891
2892Awaitable<std::optional<WebCore::RemoteUserInputEventData>> WebPage::potentialTapAtPosition(std::optional<WebCore::FrameIdentifier> frameID, WebKit::TapIdentifier requestID, WebCore::FloatPoint position, bool shouldRequestMagnificationInformation, WebKit::WebMouseEventInputSource inputSource)
2893{
2894 m_potentialTapInputSource = platform(inputSource);
2895
2896 RefPtr localMainFrame = protect(*m_page)->localMainFrame();
2897
2898 if (RefPtr localRootFrame = this->localRootFrame(frameID))
2899 m_potentialTapNode = localRootFrame->nodeRespondingToClickEvents(position, m_potentialTapLocation, m_potentialTapSecurityOrigin.get());
2900
2901 RefPtr frameOwner = dynamicDowncast<HTMLFrameOwnerElement>(m_potentialTapNode.get());
2902 if (RefPtr remoteFrame = frameOwner ? dynamicDowncast<RemoteFrame>(frameOwner->contentFrame()) : nullptr) {
2903 RefPtr localFrame = frameOwner->document().frame();
2904 if (RefPtr frameView = localFrame ? localFrame->view() : nullptr) {
2905 if (RefPtr remoteFrameView = remoteFrame->view()) {
2906 RemoteFrameGeometryTransformer transformer(remoteFrameView.releaseNonNull(), frameView.releaseNonNull(), remoteFrame->frameID());
2907 co_return WebCore::RemoteUserInputEventData {
2908 remoteFrame->frameID(),
2909 transformer.transformToRemoteFrameCoordinates(position)
2910 };
2911 }
2912 }
2913 }
2914
2915#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
2916 auto lastTouchLocation = std::exchange(m_lastTouchLocationBeforeTap, { });
2917 bool ignorePotentialTap = [&] {
2918 if (!m_potentialTapNode)
2919 return false;
2920
2921 if (!localMainFrame)
2922 return false;
2923
2924 if (!lastTouchLocation)
2925 return false;
2926
2927 static constexpr auto maxAllowedMovementSquared = 200 * 200;
2928 if ((position - *lastTouchLocation).diagonalLengthSquared() <= maxAllowedMovementSquared)
2929 return false;
2930
2931 FloatPoint adjustedLocation;
2932 RefPtr lastTouchedNode = localMainFrame->nodeRespondingToClickEvents(*lastTouchLocation, adjustedLocation, m_potentialTapSecurityOrigin.get());
2933 return lastTouchedNode != m_potentialTapNode;
2934 }();
2935
2936 if (ignorePotentialTap) {
2937 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
;
2938 m_potentialTapNode = nullptr;
2939 co_return std::nullopt;
2940 }
2941
2942 m_wasShowingInputViewForFocusedElementDuringLastPotentialTap = m_isShowingInputViewForFocusedElement;
2943
2944 RefPtr viewGestureGeometryCollector = m_viewGestureGeometryCollector;
2945
2946 if (shouldRequestMagnificationInformation && m_potentialTapNode && viewGestureGeometryCollector) {
2947 FloatPoint origin = position;
2948 FloatRect absoluteBoundingRect;
2949 bool fitEntireRect;
2950 double viewportMinimumScale;
2951 double viewportMaximumScale;
2952
2953 viewGestureGeometryCollector->computeZoomInformationForNode(*protect(m_potentialTapNode), origin, absoluteBoundingRect, fitEntireRect, viewportMinimumScale, viewportMaximumScale);
2954
2955 bool nodeIsRootLevel = is<WebCore::Document>(*m_potentialTapNode) || is<WebCore::HTMLBodyElement>(*m_potentialTapNode);
2956 bool nodeIsPluginElement = is<WebCore::HTMLPlugInElement>(*m_potentialTapNode);
2957 send(Messages::WebPageProxy::HandleSmartMagnificationInformationForPotentialTap(requestID, absoluteBoundingRect, fitEntireRect, viewportMinimumScale, viewportMaximumScale, nodeIsRootLevel, nodeIsPluginElement));
2958 }
2959
2960 sendTapHighlightForNodeIfNecessary(requestID, m_potentialTapNode.get(), position);
2961#if ENABLE(TWO_PHASE_CLICKS)(defined 0 && 0)
2962 if (RefPtr potentialTapNode = m_potentialTapNode; potentialTapNode && !potentialTapNode->allowsDoubleTapGesture())
2963 send(Messages::WebPageProxy::DisableDoubleTapGesturesDuringTapIfNecessary(requestID));
2964#endif
2965#endif // PLATFORM(IOS_FAMILY)
2966
2967 co_return std::nullopt;
2968}
2969
2970Awaitable<std::optional<WebCore::FrameIdentifier>> WebPage::commitPotentialTap(std::optional<WebCore::FrameIdentifier> frameID, OptionSet<WebEventModifier> modifiers, TransactionID lastLayerTreeTransactionId, WebCore::PointerID pointerId)
2971{
2972 RefPtr frameOwner = dynamicDowncast<HTMLFrameOwnerElement>(m_potentialTapNode.get());
2973 RefPtr remoteFrame = frameOwner ? dynamicDowncast<RemoteFrame>(frameOwner->contentFrame()) : nullptr;
2974 if (remoteFrame)
2975 co_return remoteFrame->frameID();
2976
2977#if ENABLE(TWO_PHASE_CLICKS)(defined 0 && 0)
2978 auto invalidTargetForSingleClick = !m_potentialTapNode;
2979 if (!invalidTargetForSingleClick) {
2980 bool targetRenders = m_potentialTapNode->renderer();
2981 if (RefPtr element = dynamicDowncast<Element>(m_potentialTapNode); element && !targetRenders)
2982 targetRenders = element->renderOrDisplayContentsStyle();
2983 if (RefPtr shadowRoot = dynamicDowncast<ShadowRoot>(m_potentialTapNode); shadowRoot && !targetRenders)
2984 targetRenders = protect(shadowRoot->host())->renderOrDisplayContentsStyle();
2985 invalidTargetForSingleClick = !targetRenders && !is<HTMLAreaElement>(m_potentialTapNode);
2986 }
2987
2988 RefPtr localRootFrame = this->localRootFrame(frameID);
2989
2990 if (invalidTargetForSingleClick) {
2991#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
2992 if (localRootFrame) {
2993 constexpr OptionSet hitType { HitTestRequest::Type::ReadOnly, HitTestRequest::Type::Active, HitTestRequest::Type::AllowVisibleChildFrameContentOnly };
2994 auto roundedPoint = IntPoint { m_potentialTapLocation };
2995 auto result = localRootFrame->eventHandler().hitTestResultAtPoint(roundedPoint, hitType);
2996 localRootFrame->eventHandler().setLastTouchedNode(result.innerNode());
2997 }
2998#endif
2999
3000 commitPotentialTapFailed();
3001 co_return std::nullopt;
3002 }
3003
3004#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
3005 if (localRootFrame)
3006 localRootFrame->eventHandler().setLastTouchedNode(nullptr);
3007#endif
3008
3009 FloatPoint adjustedPoint;
3010 RefPtr nodeRespondingToClick = localRootFrame ? localRootFrame->nodeRespondingToClickEvents(m_potentialTapLocation, adjustedPoint, m_potentialTapSecurityOrigin.get()) : nullptr;
3011 RefPtr frameRespondingToClick = nodeRespondingToClick ? nodeRespondingToClick->document().frame() : nullptr;
3012
3013 if (!frameRespondingToClick) {
3014 commitPotentialTapFailed();
3015 co_return std::nullopt;
3016 }
3017
3018 auto firstTransactionID = WebFrame::fromCoreFrame(*frameRespondingToClick)->firstLayerTreeTransactionIDAfterDidCommitLoad();
3019 if (firstTransactionID
3020 && lastLayerTreeTransactionId.processIdentifier() == firstTransactionID->processIdentifier()
3021 && lastLayerTreeTransactionId.lessThanSameProcess(*firstTransactionID)) {
3022 commitPotentialTapFailed();
3023 co_return std::nullopt;
3024 }
3025
3026 if (m_potentialTapNode == nodeRespondingToClick)
3027 handleSyntheticClick(frameID, *nodeRespondingToClick, adjustedPoint, modifiers, pointerId);
3028 else
3029 commitPotentialTapFailed();
3030#endif // ENABLE(TWO_PHASE_CLICKS)
3031
3032 m_potentialTapNode = nullptr;
3033 m_potentialTapLocation = FloatPoint();
3034 m_potentialTapSecurityOrigin = nullptr;
3035
3036 co_return std::nullopt;
3037}
3038
3039void WebPage::cancelPotentialTapInFrame(WebFrame& frame)
3040{
3041 if (auto selectionChangedHandler = std::exchange(m_selectionChangedHandler, { }))
3042 selectionChangedHandler();
3043
3044 if (m_potentialTapNode) {
3045 RefPtr potentialTapFrame = m_potentialTapNode->document().frame();
3046 if (potentialTapFrame && !potentialTapFrame->tree().isDescendantOf(protect(frame.coreLocalFrame())))
3047 return;
3048 }
3049
3050 m_potentialTapNode = nullptr;
3051 m_potentialTapLocation = FloatPoint();
3052 m_potentialTapSecurityOrigin = nullptr;
3053}
3054
3055void WebPage::cancelPotentialTap()
3056{
3057#if ENABLE(CONTENT_CHANGE_OBSERVER)(defined 0 && 0)
3058 if (RefPtr localMainFrame = protect(*m_page)->localMainFrame())
3059 ContentChangeObserver::didCancelPotentialTap(*localMainFrame);
3060#endif
3061 cancelPotentialTapInFrame(m_mainFrame);
3062}
3063
3064void WebPage::didHandleTapAsHover()
3065{
3066 invokePendingSyntheticClickCallback(SyntheticClickResult::Hover);
3067 send(Messages::WebPageProxy::DidHandleTapAsHover());
3068}
3069
3070void WebPage::sendTapHighlightForNodeIfNecessary(WebKit::TapIdentifier requestID, Node* node, FloatPoint point)
3071{
3072#if ENABLE(TWO_PHASE_CLICKS)(defined 0 && 0)
3073 if (!node)
3074 return;
3075
3076 RefPtr localMainFrame = m_page->localMainFrame();
3077 if (!localMainFrame)
3078 return;
3079
3080 if (m_page->isEditable() && node == protect(localMainFrame->document())->body())
3081 return;
3082
3083 if (RefPtr element = dynamicDowncast<Element>(*node)) {
3084 ASSERT(m_page)((void)0);
3085 localMainFrame->loader().prefetchDNSIfNeeded(element->absoluteLinkURL());
3086 }
3087
3088 RefPtr updatedNode = node;
3089 if (RefPtr area = dynamicDowncast<HTMLAreaElement>(*node)) {
3090 updatedNode = area->imageElement();
3091 if (!updatedNode)
3092 return;
3093 }
3094
3095#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
3096 if (RefPtr pluginView = pluginViewForFrame(protect(updatedNode->document().frame()))) {
3097 if (auto rect = pluginView->highlightRectForTapAtPoint(point)) {
3098#if ENABLE(CSS_TAP_HIGHLIGHT_COLOR)(defined ENABLE_CSS_TAP_HIGHLIGHT_COLOR && ENABLE_CSS_TAP_HIGHLIGHT_COLOR
)
3099 auto highlightColor = RenderTheme::singleton().platformTapHighlightColor();
3100#else
3101 auto highlightColor = Color::transparentBlack;
3102#endif
3103 auto highlightQuads = Vector { FloatQuad { WTF::move(*rect) } };
3104 send(Messages::WebPageProxy::DidGetTapHighlightGeometries(requestID, WTF::move(highlightColor), WTF::move(highlightQuads), { }, { }, { }, { }, true));
3105 return;
3106 }
3107 }
3108#endif // ENABLE(PDF_PLUGIN)
3109
3110 Vector<FloatQuad> quads;
3111 if (CheckedPtr renderer = updatedNode->renderer()) {
3112 renderer->absoluteQuads(quads);
3113#if ENABLE(CSS_TAP_HIGHLIGHT_COLOR)(defined ENABLE_CSS_TAP_HIGHLIGHT_COLOR && ENABLE_CSS_TAP_HIGHLIGHT_COLOR
)
3114 auto highlightColor = renderer->style().tapHighlightColorResolvingCurrentColor();
3115#else
3116 auto highlightColor = Color::transparentBlack;
3117#endif
3118 if (!updatedNode->document().frame()->isMainFrame()) {
3119 RefPtr view = updatedNode->document().frame()->view();
3120 for (auto& quad : quads)
3121 quad = view->contentsToRootView(quad);
3122 }
3123
3124 LayoutRoundedRect::Radii borderRadii;
3125 if (CheckedPtr renderBox = dynamicDowncast<RenderBox>(*renderer))
3126 borderRadii = renderBox->borderRadii();
3127
3128 RefPtr element = dynamicDowncast<Element>(*updatedNode);
3129 bool nodeHasBuiltInClickHandling = element && (is<HTMLFormControlElement>(*element) || is<HTMLAnchorElement>(*element) || is<HTMLLabelElement>(*element) || is<HTMLSummaryElement>(*element) || element->isLink());
3130 send(Messages::WebPageProxy::DidGetTapHighlightGeometries(requestID, highlightColor, quads, roundedIntSize(borderRadii.topLeft()), roundedIntSize(borderRadii.topRight()), roundedIntSize(borderRadii.bottomLeft()), roundedIntSize(borderRadii.bottomRight()), nodeHasBuiltInClickHandling));
3131 }
3132#else
3133 UNUSED_PARAM(requestID)(void)requestID;
3134 UNUSED_PARAM(node)(void)node;
3135 UNUSED_PARAM(point)(void)point;
3136#endif
3137}
3138
3139#if ENABLE(CONTENT_CHANGE_OBSERVER)(defined 0 && 0)
3140void WebPage::didFinishContentChangeObserving(WebCore::FrameIdentifier frameID, WebCore::ContentChange observedContentChange)
3141{
3142 LOG_WITH_STREAM(ContentObservation, stream << "didFinishContentChangeObserving: pending target node(" << m_pendingSyntheticClickNode << ")")((void)0);
3143 if (!m_pendingSyntheticClickNode)
3144 return;
3145 callOnMainRunLoop([
3146 protectedThis = Ref { *this },
3147 targetNode = Ref<Node>(*m_pendingSyntheticClickNode),
3148 originalDocument = WeakPtr<Document, WeakPtrImplWithEventTargetData> { m_pendingSyntheticClickNode->document() },
3149 observedContentChange,
3150 location = m_pendingSyntheticClickLocation,
3151 modifiers = m_pendingSyntheticClickModifiers,
3152 pointerId = m_pendingSyntheticClickPointerId,
3153 inputSource = m_potentialTapInputSource,
3154 frameID
3155 ] {
3156 if (protectedThis->m_isClosed || !protectedThis->corePage())
3157 return;
3158 if (!originalDocument || &targetNode->document() != originalDocument)
3159 return;
3160
3161 // Only dispatch the click if the document didn't get changed by any timers started by the move event.
3162 if (observedContentChange == WebCore::ContentChange::None) {
3163 LOG(ContentObservation, "No change was observed -> click.")((void)0);
3164 protectedThis->completeSyntheticClick(frameID, targetNode, location, modifiers, WebCore::SyntheticClickType::OneFingerTap, pointerId);
3165 return;
3166 }
3167 // Ensure that the mouse is on the most recent content.
3168 LOG(ContentObservation, "Observed meaningful visible change -> hover.")((void)0);
3169 if (RefPtr localRootFrame = protectedThis->localRootFrame(frameID))
3170 dispatchSyntheticMouseMove(*localRootFrame, location, modifiers, pointerId, inputSource);
3171
3172 protectedThis->didHandleTapAsHover();
3173 });
3174 m_pendingSyntheticClickNode = nullptr;
3175 m_pendingSyntheticClickLocation = { };
3176 m_pendingSyntheticClickModifiers = { };
3177 m_pendingSyntheticClickPointerId = 0;
3178}
3179#endif // ENABLE(CONTENT_CHANGE_OBSERVER)
3180
3181void WebPage::invokePendingSyntheticClickCallback(SyntheticClickResult result)
3182{
3183 if (auto callback = std::exchange(m_pendingSyntheticClickCallback, { }))
3184 callback(result);
3185}
3186
3187void WebPage::commitPotentialTapFailed()
3188{
3189 if (auto selectionChangedHandler = std::exchange(m_selectionChangedHandler, { }))
3190 selectionChangedHandler();
3191
3192#if ENABLE(CONTENT_CHANGE_OBSERVER)(defined 0 && 0)
3193 if (RefPtr localMainFrame = protect(*m_page)->localMainFrame())
3194 ContentChangeObserver::didCancelPotentialTap(*localMainFrame);
3195#endif
3196#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
3197 clearSelectionAfterTapIfNeeded();
3198#endif
3199 invokePendingSyntheticClickCallback(SyntheticClickResult::Failed);
3200
3201 send(Messages::WebPageProxy::CommitPotentialTapFailed());
3202 send(Messages::WebPageProxy::DidNotHandleTapAsClick(roundedIntPoint(m_potentialTapLocation)));
3203}
3204
3205void WebPage::completeSyntheticClick(std::optional<WebCore::FrameIdentifier> frameID, Node& nodeRespondingToClick, const WebCore::FloatPoint& location, OptionSet<WebEventModifier> modifiers, SyntheticClickType syntheticClickType, WebCore::PointerID pointerId)
3206{
3207 SetForScope completeSyntheticClickScope { m_completingSyntheticClick, true };
3208 IntPoint roundedAdjustedPoint = roundedIntPoint(location);
3209
3210 // 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.
3211 auto rootFrameID = frameID;
3212 if (RefPtr webFrame = WebProcess::singleton().webFrame(frameID)) {
3213 if (RefPtr frame = webFrame->coreLocalFrame(); frame && !frame->isRootFrame())
3214 rootFrameID = WebFrame::fromCoreFrame(protect(frame->rootFrame()))->frameID();
3215 }
3216
3217 RefPtr localRootFrame = this->localRootFrame(rootFrameID);
3218 if (!localRootFrame) {
3219 invokePendingSyntheticClickCallback(SyntheticClickResult::PageInvalid);
3220 return;
3221 }
3222
3223 RefPtr oldFocusedFrame = m_page->focusController().focusedLocalFrame();
3224 RefPtr<Element> oldFocusedElement = oldFocusedFrame ? oldFocusedFrame->document()->focusedElement() : nullptr;
3225
3226 SetForScope userIsInteractingChange { m_userIsInteracting, true };
3227
3228#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
3229 m_lastInteractionLocation = roundedAdjustedPoint;
3230#endif
3231
3232 // FIXME: Pass caps lock state.
3233 auto platformModifiers = platform(modifiers);
3234
3235 bool handledPress = localRootFrame->eventHandler().handleMousePressEvent(PlatformMouseEvent(roundedAdjustedPoint, roundedAdjustedPoint, MouseButton::Left, PlatformEvent::Type::MousePressed, 1, platformModifiers, MonotonicTime::now(), WebCore::ForceAtClick, syntheticClickType, m_potentialTapInputSource, pointerId)).wasHandled();
3236 if (m_isClosed)
3237 return;
3238
3239 if (auto selectionChangedHandler = std::exchange(m_selectionChangedHandler, { }))
3240 selectionChangedHandler();
3241#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
3242 else if (!handledPress)
3243 clearSelectionAfterTapIfNeeded();
3244#endif
3245
3246 auto releaseEvent = PlatformMouseEvent { roundedAdjustedPoint, roundedAdjustedPoint, MouseButton::Left, PlatformEvent::Type::MouseReleased, 1, platformModifiers, MonotonicTime::now(), ForceAtClick, syntheticClickType, m_potentialTapInputSource, pointerId };
3247 bool handledRelease = localRootFrame->eventHandler().handleMouseReleaseEvent(releaseEvent).wasHandled();
3248 if (m_isClosed)
3249 return;
3250
3251 RefPtr newFocusedFrame = m_page->focusController().focusedLocalFrame();
3252 RefPtr<Element> newFocusedElement = newFocusedFrame ? newFocusedFrame->document()->focusedElement() : nullptr;
3253
3254 if (nodeRespondingToClick.document().settings().contentChangeObserverEnabled()) {
3255 Ref document = nodeRespondingToClick.document();
3256 // Dispatch mouseOut to dismiss tooltip content when tapping on the control bar buttons (cc, settings).
3257 if (document->quirks().needsYouTubeMouseOutQuirk()) {
3258 if (RefPtr frame = document->frame()) {
3259 PlatformMouseEvent event { roundedAdjustedPoint, roundedAdjustedPoint, MouseButton::Left, PlatformEvent::Type::NoType, 0, platformModifiers, MonotonicTime::now(), 0, WebCore::SyntheticClickType::NoTap, m_potentialTapInputSource, pointerId };
3260 if (!nodeRespondingToClick.isConnected())
3261 frame->eventHandler().dispatchSyntheticMouseMove(event);
3262 frame->eventHandler().dispatchSyntheticMouseOut(event);
3263 }
3264 }
3265 }
3266
3267 if (m_isClosed)
3268 return;
3269
3270#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
3271 if (RefPtr pluginElement = dynamicDowncast<HTMLPlugInElement>(nodeRespondingToClick)) {
3272 if (RefPtr pluginWidget = downcast<PluginView>(pluginElement->pluginWidget()))
3273 pluginWidget->handleSyntheticClick(WTF::move(releaseEvent));
3274 }
3275#endif
3276
3277 invokePendingSyntheticClickCallback(SyntheticClickResult::Click);
3278
3279 if ((!handledPress && !handledRelease) || !nodeRespondingToClick.isElementNode())
3280 send(Messages::WebPageProxy::DidNotHandleTapAsClick(roundedIntPoint(location)));
3281
3282 send(Messages::WebPageProxy::DidCompleteSyntheticClick());
3283
3284#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
3285 scheduleLayoutViewportHeightExpansionUpdate();
3286#endif
3287}
3288
3289#endif // ENABLE(TWO_PHASE_CLICKS)
3290
3291} // namespace WebKit
3292
3293#endif // PLATFORM(COCOA)