Bug Summary

File:Source/WebKit/WebProcess/WebPage/FindController.cpp
Warning:line 291, column 51
Call argument for 'this' parameter is uncounted and unsafe

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple arm64-apple-ios26.2.0-simulator -Os -Wundef-prefix=TARGET_OS_ -Werror=undef-prefix -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -Werror=implicit-function-declaration -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name UnifiedSource143.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=osx -analyzer-checker=security.insecureAPI.decodeValueOfObjCType -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -mframe-pointer=non-leaf -ffp-contract=on -fno-rounding-math -target-sdk-version=26.2 -fdepfile-entry=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/SDKSettings.json -fcompatibility-qualified-id-block-type-checking -fvisibility-inlines-hidden-static-local-var -fno-modulemap-allow-subdirectory-search -fdefine-target-os-macros -enable-tlsdesc -target-cpu apple-m1 -target-feature +v8.4a -target-feature +aes -target-feature +altnzcv -target-feature +ccdp -target-feature +ccpp -target-feature +complxnum -target-feature +crc -target-feature +dotprod -target-feature +flagm -target-feature +fp-armv8 -target-feature +fp16fml -target-feature +fptoint -target-feature +fullfp16 -target-feature +jsconv -target-feature +lse -target-feature +neon -target-feature +pauth -target-feature +perfmon -target-feature +predres -target-feature +ras -target-feature +rcpc -target-feature +rdm -target-feature +sb -target-feature +sha2 -target-feature +sha3 -target-feature +specrestrict -target-feature +ssbs -target-abi darwinpcs -dwarf-ext-refs -fmodule-format=obj -debug-info-kind=standalone -dwarf-version=5 -debugger-tuning=lldb -fdebug-compilation-dir=/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit -target-linker-version 1230.1 -fcoverage-compilation-dir=/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit -resource-dir /Users/buildbot/Library/Developer/Toolchains/swift-webkit.xctoolchain/usr/lib/clang/21 -dependency-file /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/scan-build-output/StaticAnalyzer/WebKit/WebKit/normal/arm64/UnifiedSource143.d -skip-unused-modulemap-deps -MT dependencies -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk -iquote /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release-iphonesimulator/WebKit.build/WebKit-generated-files.hmap -iquote /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release-iphonesimulator/WebKit.build/WebKit-project-headers.hmap -isystem /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitLibraries/SDKs/iphonesimulator26.0-additions.sdk/usr/local/include -isystem /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/SDKAdditions/usr/include -iframework /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/SDKAdditions/System/Library/Frameworks -iframework /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/System/Library/PrivateFrameworks -isystem /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitLibraries/SDKs/iphonesimulator26.0-additions.sdk/usr/local/include -include-pch /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/PrecompiledHeaders/SharedPrecompiledHeaders/12151610001510045616/WebKit2Prefix.h.gch -D _LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE -D NDEBUG -D HAVE_CORE_PREDICTION -D ENABLE_LOWER_FORMATREADERBUNDLE_CODESIGNING_REQUIREMENTS -D WEBKIT_BUNDLE_VERSION="625.1.10+" -D FRAMEWORK_NAME=WebKit -D __clang_analyzer__ -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release-iphonesimulator/WebKit.build/WebKit-own-target-headers.hmap -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release-iphonesimulator/WebKit.build/WebKit-all-target-headers.hmap -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/include -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/usr/local/include -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/WebCore.framework/PrivateHeaders/ForwardingHeaders -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/DerivedSources/WebKit -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/DerivedSources/WebKit/IPC -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/usr/local/include/pal/graphics/WebGPU -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/local/include/pal/graphics/WebGPU -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/usr/local/include/WebKitAdditions -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/local/include/WebKitAdditions -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/usr/local/include/webrtc -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/local/include/webrtc -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/usr/local/include/webm -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/local/include/webm -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebKit -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release-iphonesimulator/WebKit.build/DerivedSources-normal/arm64 -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release-iphonesimulator/WebKit.build/DerivedSources/arm64 -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebKit.build/Release-iphonesimulator/WebKit.build/DerivedSources -F/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator -F/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitLibraries/SDKs/iphonesimulator26.0-additions.sdk/System/Library/PrivateFrameworks -F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/System/Library/PrivateFrameworks -D __STDC_WANT_LIB_EXT1__=1 -I/usr/local/include -stdlib=libc++ -internal-isystem /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/include/c++/v1 -internal-isystem /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/local/include -internal-isystem /Users/buildbot/Library/Developer/Toolchains/swift-webkit.xctoolchain/usr/lib/clang/21/include -internal-externc-isystem /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/include -internal-iframework /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/System/Library/Frameworks -internal-iframework /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/System/Library/SubFrameworks -internal-iframework /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/Library/Frameworks -Wno-trigraphs -Werror -Wno-missing-field-initializers -Wmissing-prototypes -Wunreachable-code -Wnon-virtual-dtor -Wno-overloaded-virtual -Wno-exit-time-destructors -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-c++11-extensions -Wno-implicit-fallthrough -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 -Wno-backend-plugin -Wunsafe-buffer-usage -Wno-unknown-warning-option -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 -fpascal-strings -fmax-type-align=16 -fdiagnostics-show-note-include-stack -vectorize-loops -vectorize-slp -fsafe-buffer-usage-suggestions -analyzer-output=html -analyzer-config report-in-main-source-file=true -analyzer-config nullability:NoDiagnoseCallsToSystemHeaders=true -analyzer-checker optin.osx.cocoa.localizability.NonLocalizedStringChecker -analyzer-checker security.insecureAPI.UncheckedReturn -analyzer-checker security.insecureAPI.getpw -analyzer-checker security.insecureAPI.gets -analyzer-checker security.insecureAPI.mkstemp -analyzer-checker security.insecureAPI.mktemp -analyzer-disable-checker security.insecureAPI.rand -analyzer-disable-checker security.insecureAPI.strcpy -analyzer-checker security.insecureAPI.vfork -analyzer-disable-checker alpha,apiModeling,core,cplusplus,deadcode,debug,fuchsia,nullability,optin,osx,security,unix,webkit -analyzer-checker alpha.webkit.ForwardDeclChecker,alpha.webkit.MemoryUnsafeCastChecker,alpha.webkit.NoDeleteChecker,alpha.webkit.NoUncheckedPtrMemberChecker,alpha.webkit.NoUnretainedMemberChecker,alpha.webkit.RetainPtrCtorAdoptChecker,alpha.webkit.UncheckedCallArgsChecker,alpha.webkit.UncheckedLocalVarsChecker,alpha.webkit.UncountedCallArgsChecker,alpha.webkit.UncountedLocalVarsChecker,alpha.webkit.UnretainedCallArgsChecker,alpha.webkit.UnretainedLambdaCapturesChecker,alpha.webkit.UnretainedLocalVarsChecker,webkit.NoUncountedMemberChecker,webkit.RefCntblBaseVirtualDtor,webkit.UncountedLambdaCapturesChecker -analyzer-config max-nodes=10000000 -analyzer-config verbose-report-filename=true -fdwarf2-cfi-asm -o /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/scan-build-output/StaticAnalyzer/WebKit/WebKit/normal/arm64/UnifiedSource143.plist -x c++ /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/DerivedSources/WebKit/unified-sources/UnifiedSource143.cpp
1/*
2 * Copyright (C) 2010-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#include "config.h"
27#include "FindController.h"
28
29#include "DrawingArea.h"
30#include "FindIndicatorStrategy.h"
31#include "MessageSenderInlines.h"
32#include "PluginView.h"
33#include "WKPage.h"
34#include "WebFrame.h"
35#include "WebImage.h"
36#include "WebPage.h"
37#include "WebPageProxyMessages.h"
38#include <WebCore/BoundaryPointInlines.h>
39#include <WebCore/DocumentMarkerController.h>
40#include <WebCore/DocumentMarkers.h>
41#include <WebCore/DocumentView.h>
42#include <WebCore/FindOptions.h>
43#include <WebCore/FindRevealAlgorithms.h>
44#include <WebCore/FloatQuad.h>
45#include <WebCore/FocusController.h>
46#include <WebCore/FrameDestructionObserverInlines.h>
47#include <WebCore/FrameSelection.h>
48#include <WebCore/GeometryUtilities.h>
49#include <WebCore/GraphicsContext.h>
50#include <WebCore/ImageAnalysisQueue.h>
51#include <WebCore/ImageBuffer.h>
52#include <WebCore/ImageOverlay.h>
53#include <WebCore/LocalFrameInlines.h>
54#include <WebCore/LocalFrameView.h>
55#include <WebCore/Page.h>
56#include <WebCore/PageOverlayController.h>
57#include <WebCore/PathUtilities.h>
58#include <WebCore/PlatformMouseEvent.h>
59#include <WebCore/PluginDocument.h>
60#include <WebCore/Range.h>
61#include <WebCore/RenderObject.h>
62#include <WebCore/ShareableBitmap.h>
63#include <WebCore/SimpleRange.h>
64#include <wtf/Forward.h>
65#include <wtf/TZoneMallocInlines.h>
66
67namespace WebKit {
68using namespace WebCore;
69
70WTF_MAKE_TZONE_ALLOCATED_IMPL(FindController)::bmalloc::api::HeapRef FindController::s_heapRef; const TZoneSpecification
FindController::s_heapSpec = { &FindController::s_heapRef
, TZoneSpecification::encodeSize<FindController>(), TZoneSpecification
::encodeAlignment<FindController>(), TZoneSpecification
::encodeCategory<FindController>(), ::bmalloc::api::compactAllocationMode
<FindController>(), TZoneSpecification::encodeDescriptor
<FindController>(), }; void* FindController::operatorNewSlow
(size_t size) { if constexpr (::bmalloc::api::compactAllocationMode
<FindController>() == CompactAllocationMode::Compact) return
::bmalloc::api::tzoneAllocateCompactSlow(size, s_heapSpec); return
::bmalloc::api::tzoneAllocateNonCompactSlow(size, s_heapSpec
); } using __makeBtzoneMallocedInlineMacroSemicolonifier __attribute__
((unused)) = int
;
71
72FindController::FindController(WebPage* webPage)
73 : m_webPage(webPage)
74#if PLATFORM(IOS_FAMILY)(defined 1 && 1)
75 , m_indicatorStrategy(WTF::makeUniqueRef<IOSFindIndicatorStrategy>(*this))
76#else
77 , m_indicatorStrategy(WTF::makeUniqueRef<DefaultFindIndicatorStrategy>(*this))
78#endif
79{
80}
81
82FindController::~FindController() = default;
83
84#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
85
86PluginView* FindController::mainFramePlugIn()
87{
88 return protect(m_webPage.get())->mainFramePlugIn();
89}
90
91#endif
92
93void FindController::countStringMatches(const String& string, OptionSet<FindOptions> options, unsigned maxMatchCount, CompletionHandler<void(uint32_t)>&& completionHandler)
94{
95 if (maxMatchCount == std::numeric_limits<unsigned>::max())
96 --maxMatchCount;
97
98 unsigned matchCount;
99#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
100 if (RefPtr pluginView = mainFramePlugIn())
101 matchCount = pluginView->countFindMatches(string, core(options), maxMatchCount + 1);
102 else
103#endif
104 {
105 RefPtr webPage { m_webPage.get() };
106 matchCount = protect(webPage->corePage())->countFindMatches(string, core(options), maxMatchCount + 1);
107 protect(webPage->corePage())->unmarkAllTextMatches();
108 }
109
110 if (matchCount > maxMatchCount)
111 matchCount = static_cast<unsigned>(kWKMoreThanMaximumMatchCount);
112
113 completionHandler(matchCount);
114}
115
116uint32_t FindController::replaceMatches(const Vector<uint32_t>& matchIndices, const String& replacementText, bool selectionOnly)
117{
118 RefPtr webPage { m_webPage.get() };
119 if (matchIndices.isEmpty())
120 return protect(webPage->corePage())->replaceSelectionWithText(replacementText);
121
122 // FIXME: This is an arbitrary cap on the maximum number of matches to try and replace, to prevent the web process from
123 // hanging while replacing an enormous amount of matches. In the future, we should handle replacement in batches, and
124 // periodically update an NSProgress in the UI process when a batch of find-in-page matches are replaced.
125 constexpr uint32_t maximumNumberOfMatchesToReplace = 1000;
126
127 Vector<SimpleRange> rangesToReplace;
128 rangesToReplace.reserveInitialCapacity(std::min<uint32_t>(maximumNumberOfMatchesToReplace, matchIndices.size()));
129 for (auto index : matchIndices) {
130 if (index < m_findMatches.size())
131 rangesToReplace.append(m_findMatches[index]);
132 if (rangesToReplace.size() >= maximumNumberOfMatchesToReplace)
133 break;
134 }
135 return protect(webPage->corePage())->replaceRangesWithText(rangesToReplace, replacementText, selectionOnly);
136}
137
138RefPtr<LocalFrame> FindController::frameWithSelection(Page* page)
139{
140 for (RefPtr<Frame> frame = page->mainFrame(); frame; frame = frame->tree().traverseNext()) {
141 auto* localFrame = dynamicDowncast<LocalFrame>(frame.get());
142 if (!localFrame)
143 continue;
144
145 if (localFrame->selection().isCaretOrRange())
146 return localFrame;
147 }
148 return nullptr;
149}
150
151void FindController::updateFindUIAfterIncrementalFind(bool found, const String& string, OptionSet<FindOptions> options, unsigned maxMatchCount, WebCore::DidWrap didWrap, std::optional<FrameIdentifier> idOfFrameContainingString, CompletionHandler<void(std::optional<WebCore::FrameIdentifier>, Vector<IntRect>&&, uint32_t, int32_t, bool)>&& completionHandler)
152{
153 RefPtr webPage { m_webPage.get() };
154 RefPtr selectedFrame = frameWithSelection(protect(webPage->corePage()).get());
155
156#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
157 RefPtr pluginView = mainFramePlugIn();
158#endif
159
160 bool shouldShowOverlay = found && options.contains(FindOptions::ShowOverlay);
161 unsigned matchCount = 0;
162 Vector<IntRect> matchRects;
163 if (!found) {
164 if (selectedFrame && !options.contains(FindOptions::DoNotSetSelection))
165 protect(selectedFrame->selection())->clear();
166
167 hideFindUI();
168
169 completionHandler(idOfFrameContainingString, WTF::move(matchRects), matchCount, m_foundStringMatchIndex.value_or(-1), didWrap == WebCore::DidWrap::Yes);
170 return;
171 }
172
173 bool needsCount = options.contains(FindOptions::DetermineMatchIndex);
174 bool needsMarking = shouldShowOverlay || options.contains(FindOptions::ShowHighlight);
175 matchCount = 1;
176
177 // marking implicitly counts, so we try not to double count here
178 if (needsMarking)
179 matchCount = markMatches(string, options, maxMatchCount);
180 else if (needsCount)
181 matchCount = getMatchCount(string, options, maxMatchCount);
182
183 if (matchCount > maxMatchCount)
184 matchCount = static_cast<unsigned>(kWKMoreThanMaximumMatchCount);
185
186 updateMatchIndex(matchCount, options);
187
188 if (auto range = protect(webPage->corePage())->selection().firstRange())
189 matchRects = RenderObject::absoluteTextRects(*range);
190
191 updateFindPageOverlay(shouldShowOverlay);
192 updateFindIndicatorIfNeeded(found, options, shouldShowOverlay);
193 completionHandler(idOfFrameContainingString, WTF::move(matchRects), matchCount, m_foundStringMatchIndex.value_or(0), didWrap == WebCore::DidWrap::Yes);
194}
195
196unsigned FindController::markMatches(const String& string, OptionSet<FindOptions> options, unsigned maxMatchCount)
197{
198 maxMatchCount = std::min(std::numeric_limits<unsigned>::max() - 1, maxMatchCount);
199
200#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
201 if (RefPtr pluginView = mainFramePlugIn())
202 return pluginView->countFindMatches(string, core(options), maxMatchCount + 1);
203#endif
204
205 RefPtr webPage { m_webPage.get() };
206 bool shouldShowHighlight = options.contains(FindOptions::ShowHighlight);
207
208 protect(webPage->corePage())->unmarkAllTextMatches();
209 return protect(webPage->corePage())->markAllMatchesForText(string, core(options), shouldShowHighlight, maxMatchCount + 1);
210}
211
212unsigned FindController::getMatchCount(const String& string, OptionSet<FindOptions> options, unsigned maxMatchCount)
213{
214#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
215 if (RefPtr pluginView = mainFramePlugIn(); pluginView)
216 return pluginView->countFindMatches(string, core(options), maxMatchCount + 1);
217#endif
218
219 RefPtr webPage { m_webPage.get() };
220 return protect(webPage->corePage())->countFindMatches(string, core(options), maxMatchCount + 1);
221}
222
223void FindController::updateMatchIndex(unsigned matchCount, OptionSet<FindOptions> options)
224{
225 if (options.contains(FindOptions::DetermineMatchIndex)) {
226 if (matchCount == static_cast<unsigned>(kWKMoreThanMaximumMatchCount))
227 m_foundStringMatchIndex = std::nullopt;
228 else {
229 if (!m_foundStringMatchIndex)
230 m_foundStringMatchIndex = matchCount;
231 else if (*m_foundStringMatchIndex >= matchCount)
232 *m_foundStringMatchIndex -= matchCount;
233 }
234 }
235}
236
237void FindController::updateFindUIAfterFindingAllMatches(bool found, const String& string, OptionSet<FindOptions> options, unsigned maxMatchCount)
238{
239 RefPtr webPage { m_webPage.get() };
240 RefPtr selectedFrame = frameWithSelection(protect(webPage->corePage()).get());
241
242#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
243 RefPtr pluginView = mainFramePlugIn();
244#endif
245
246 bool shouldShowOverlay = found && options.contains(FindOptions::ShowOverlay);
247 if (!found) {
248 if (selectedFrame && !options.contains(FindOptions::DoNotSetSelection))
249 protect(selectedFrame->selection())->clear();
250 hideFindUI();
251 return;
252 }
253
254 bool shouldShowHighlight = options.contains(FindOptions::ShowHighlight);
255 if (shouldShowOverlay || shouldShowHighlight) {
256 protect(webPage->corePage())->unmarkAllTextMatches();
257 protect(webPage->corePage())->markAllMatchesForText(string, core(options), shouldShowHighlight, maxMatchCount + 1);
258 }
259
260 updateFindPageOverlay(shouldShowOverlay);
261 updateFindIndicatorIfNeeded(found, options, shouldShowOverlay);
262}
263
264void FindController::updateFindPageOverlay(bool shouldShowOverlay)
265{
266 if (!shouldShowOverlay) {
267 if (RefPtr findPageOverlay = m_findPageOverlay.get())
268 m_webPage->corePage()->pageOverlayController().uninstallPageOverlay(*findPageOverlay, PageOverlay::FadeMode::Fade);
269 return;
270 }
271
272 RefPtr findPageOverlay = m_findPageOverlay.get();
273 if (!findPageOverlay) {
274 findPageOverlay = PageOverlay::create(*this, PageOverlay::OverlayType::Document);
275 m_findPageOverlay = findPageOverlay.get();
276#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
277 if (RefPtr pluginView = mainFramePlugIn(); pluginView && !pluginView->drawsFindOverlay())
278 findPageOverlay->setNeedsSynchronousScrolling(true);
279#endif
280 m_webPage->corePage()->pageOverlayController().installPageOverlay(*findPageOverlay, PageOverlay::FadeMode::Fade);
281 }
282 findPageOverlay->setNeedsDisplay();
283}
284
285void FindController::updateFindIndicatorIfNeeded(bool found, OptionSet<FindOptions> options, bool shouldShowOverlay)
286{
287 bool shouldSetSelection = !options.contains(FindOptions::DoNotSetSelection);
288 bool wantsFindIndicator = found && options.contains(FindOptions::ShowFindIndicator);
289 bool canShowFindIndicator = frameWithSelection(protect(protect(m_webPage.get())->corePage()).get());
290#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
291 canShowFindIndicator |= mainFramePlugIn() && !mainFramePlugIn()->drawsFindOverlay();
Call argument for 'this' parameter is uncounted and unsafe
292#endif
293 if (shouldSetSelection && (!wantsFindIndicator || !canShowFindIndicator || !m_indicatorStrategy->updateFindIndicator(shouldShowOverlay)))
294 m_indicatorStrategy->hideFindIndicator();
295}
296
297#if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1)
298void FindController::findStringIncludingImages(const String& string, OptionSet<FindOptions> options, unsigned maxMatchCount, CompletionHandler<void(std::optional<FrameIdentifier>, Vector<IntRect>&&, uint32_t, int32_t, bool)>&& completionHandler)
299{
300 protect(protect(m_webPage.get())->corePage())->analyzeImagesForFindInPage([weakPage = WeakPtr { m_webPage }, string, options, maxMatchCount, completionHandler = WTF::move(completionHandler)]() mutable {
301 if (weakPage)
302 weakPage->findController().findString(string, options, maxMatchCount, WTF::move(completionHandler));
303 else
304 completionHandler({ }, { }, { }, { }, { });
305 });
306}
307#endif
308
309void FindController::findString(const String& string, OptionSet<FindOptions> options, unsigned maxMatchCount, CompletionHandler<void(std::optional<FrameIdentifier>, Vector<IntRect>&&, uint32_t, int32_t, bool)>&& completionHandler)
310{
311#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
312 RefPtr pluginView = mainFramePlugIn();
313#endif
314
315 WebCore::FindOptions coreOptions = core(options);
316
317 // iOS will reveal the selection through a different mechanism, and
318 // we need to avoid sending the non-painted selection change to the UI process
319 // so that it does not clear the selection out from under us.
320 //
321 // To share logic between platforms, prevent Editor from revealing the selection
322 // and reveal the selection in FindIndicatorStrategy::didFindString.
323 coreOptions.add(FindOption::DoNotRevealSelection);
324
325 m_indicatorStrategy->willFindString();
326
327 bool foundStringStartsAfterSelection = false;
328 RefPtr webPage { m_webPage.get() };
329#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
330 if (!pluginView)
331#endif
332 {
333 if (RefPtr selectedFrame = frameWithSelection(protect(webPage->corePage()).get())) {
334 if (protect(selectedFrame->selection())->selectionBounds().isEmpty()) {
335 auto result = protect(webPage->corePage())->findTextMatches(string, coreOptions, maxMatchCount);
336 m_foundStringMatchIndex = result.indexForSelection;
337 foundStringStartsAfterSelection = true;
338 }
339 }
340 }
341
342 bool found;
343 std::optional<FrameIdentifier> idOfFrameContainingString;
344 auto didWrap = WebCore::DidWrap::No;
345#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
346 if (pluginView) {
347 found = pluginView->findString(string, coreOptions, maxMatchCount);
348 if (RefPtr frame = pluginView->frame(); frame && found)
349 idOfFrameContainingString = frame->frameID();
350 } else
351#endif
352 {
353 auto [frameID, _] = protect(webPage->corePage())->findString(string, coreOptions, &didWrap);
354 idOfFrameContainingString = frameID;
355 found = idOfFrameContainingString.has_value();
356 }
357
358 if (found && !options.contains(FindOptions::DoNotSetSelection)) {
359 m_indicatorStrategy->didFindString();
360
361 if (!foundStringStartsAfterSelection && m_foundStringMatchIndex) {
362 if (options.contains(FindOptions::Backwards))
363 *m_foundStringMatchIndex -= 1;
364 else if (!options.contains(FindOptions::NoIndexChange))
365 *m_foundStringMatchIndex += 1;
366 }
367 }
368
369 protect(webPage->drawingArea())->dispatchAfterEnsuringUpdatedScrollPosition([webPage, found, string, options, maxMatchCount, didWrap, idOfFrameContainingString, completionHandler = WTF::move(completionHandler)]() mutable {
370 webPage->findController().updateFindUIAfterIncrementalFind(found, string, options, maxMatchCount, didWrap, idOfFrameContainingString, WTF::move(completionHandler));
371 });
372}
373
374void FindController::findStringMatches(const String& string, OptionSet<FindOptions> options, unsigned maxMatchCount, CompletionHandler<void(Vector<Vector<WebCore::IntRect>>, int32_t)>&& completionHandler)
375{
376 RefPtr webPage { m_webPage.get() };
377 auto result = protect(webPage->corePage())->findTextMatches(string, core(options), maxMatchCount);
378 m_findMatches = WTF::move(result.ranges);
379
380 auto matchRects = m_findMatches.map([](auto& range) {
381 return RenderObject::absoluteTextRects(range);
382 });
383 completionHandler(matchRects, result.indexForSelection.value_or(-1));
384
385 if (!options.contains(FindOptions::ShowOverlay) && !options.contains(FindOptions::ShowFindIndicator))
386 return;
387
388 bool found = !m_findMatches.isEmpty();
389 protect(webPage->drawingArea())->dispatchAfterEnsuringUpdatedScrollPosition([webPage, found, string, options, maxMatchCount]() {
390 webPage->findController().updateFindUIAfterFindingAllMatches(found, string, options, maxMatchCount);
391 });
392}
393
394void FindController::getImageForFindMatch(uint32_t matchIndex)
395{
396 if (matchIndex >= m_findMatches.size())
397 return;
398 RefPtr frame = m_findMatches[matchIndex].start.document().frame();
399 if (!frame)
400 return;
401
402 CheckedRef frameSelection = frame->selection();
403 auto oldSelection = frameSelection->selection();
404 frameSelection->setSelection(m_findMatches[matchIndex]);
405
406 auto selectionSnapshot = protect(WebFrame::fromCoreFrame(*frame))->createSelectionSnapshot();
407
408 frameSelection->setSelection(oldSelection);
409
410 if (!selectionSnapshot)
411 return;
412
413 auto handle = selectionSnapshot->createHandle();
414 if (!handle || !selectionSnapshot->parameters())
415 return;
416
417 m_webPage->send(Messages::WebPageProxy::DidGetImageForFindMatch(*selectionSnapshot->parameters(), WTF::move(*handle), matchIndex));
418}
419
420void FindController::selectFindMatch(uint32_t matchIndex)
421{
422 if (matchIndex >= m_findMatches.size())
423 return;
424 RefPtr frame = m_findMatches[matchIndex].start.document().frame();
425 if (!frame)
426 return;
427 protect(frame->selection())->setSelection(m_findMatches[matchIndex]);
428}
429
430void FindController::indicateFindMatch(uint32_t matchIndex)
431{
432 m_indicatorStrategy->willFindString();
433
434 selectFindMatch(matchIndex);
435
436 if (!frameWithSelection(protect(protect(m_webPage.get())->corePage()).get()))
437 return;
438
439 m_indicatorStrategy->didFindString();
440
441 m_indicatorStrategy->updateFindIndicator(!!m_findPageOverlay);
442}
443
444void FindController::hideFindUI()
445{
446 m_findMatches.clear();
447 if (RefPtr findPageOverlay = m_findPageOverlay.get())
448 m_webPage->corePage()->pageOverlayController().uninstallPageOverlay(*findPageOverlay, PageOverlay::FadeMode::Fade);
449
450#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
451 if (RefPtr pluginView = mainFramePlugIn())
452 pluginView->findString(emptyString(), { }, 0);
453 else
454#endif
455 protect(protect(m_webPage.get())->corePage())->unmarkAllTextMatches();
456
457 m_indicatorStrategy->hideFindIndicator();
458 resetMatchIndex();
459
460#if ENABLE(IMAGE_ANALYSIS)(defined 1 && 1)
461 if (RefPtr imageAnalysisQueue = m_webPage->corePage()->imageAnalysisQueueIfExists())
462 imageAnalysisQueue->clearDidBecomeEmptyCallback();
463#endif
464}
465
466void FindController::hideFindIndicator()
467{
468 m_indicatorStrategy->hideFindIndicator();
469}
470
471void FindController::resetMatchIndex()
472{
473 m_foundStringMatchIndex = std::nullopt;
474}
475
476void FindController::showFindIndicatorInSelection()
477{
478 m_indicatorStrategy->updateFindIndicator(false);
479}
480
481void FindController::deviceScaleFactorDidChange()
482{
483 ASSERT(isShowingOverlay())((void)0);
484
485 m_indicatorStrategy->updateFindIndicator(true, false);
486}
487
488void FindController::redraw()
489{
490 if (!m_isShowingFindIndicator)
491 return;
492
493 m_indicatorStrategy->updateFindIndicator(isShowingOverlay(), false);
494}
495
496Vector<FloatRect> FindController::rectsForTextMatchesInRect(IntRect clipRect)
497{
498#if ENABLE(PDF_PLUGIN)(defined 1 && 1)
499 if (RefPtr pluginView = mainFramePlugIn())
500 return pluginView->rectsForTextMatchesInRect(clipRect);
501#endif
502
503 Vector<FloatRect> rects;
504 RefPtr mainFrameView = protect(protect(protect(m_webPage.get())->corePage())->mainFrame())->virtualView();
505 for (RefPtr frame = m_webPage->corePage()->mainFrame(); frame; frame = frame->tree().traverseNext()) {
506 auto* localFrame = dynamicDowncast<LocalFrame>(frame.get());
507 if (!localFrame)
508 continue;
509 RefPtr document = localFrame->document();
510 if (!document)
511 continue;
512
513 for (FloatRect rect : protect(document->markers())->renderedRectsForMarkers(DocumentMarkerType::TextMatch)) {
514 if (!localFrame->isMainFrame())
515 rect = mainFrameView->windowToContents(protect(localFrame->view())->contentsToWindow(enclosingIntRect(rect)));
516
517 if (rect.isEmpty() || !rect.intersects(clipRect))
518 continue;
519
520 rects.append(rect);
521 }
522 }
523
524 return rects;
525}
526
527void FindController::willMoveToPage(PageOverlay&, Page* page)
528{
529 if (page)
530 return;
531
532 ASSERT(m_findPageOverlay)((void)0);
533 m_findPageOverlay = nullptr;
534}
535
536void FindController::didMoveToPage(PageOverlay&, Page*)
537{
538}
539
540constexpr float shadowOffsetX = 0;
541constexpr float shadowOffsetY = 0;
542constexpr float shadowBlurRadius = 1;
543void FindController::drawRect(PageOverlay&, GraphicsContext& graphicsContext, const IntRect& dirtyRect)
544{
545 constexpr int borderWidth = 1;
546
547 constexpr auto overlayBackgroundColor = SRGBA<uint8_t> { 26, 26, 26, 64 };
548 constexpr auto shadowColor = Color::black.colorWithAlphaByte(128);
549
550 IntRect borderInflatedDirtyRect = dirtyRect;
551 borderInflatedDirtyRect.inflate(borderWidth);
552 Vector<FloatRect> rects = rectsForTextMatchesInRect(borderInflatedDirtyRect);
553
554 // Draw the background.
555 graphicsContext.fillRect(dirtyRect, overlayBackgroundColor);
556
557 Vector<Path> whiteFramePaths = PathUtilities::pathsWithShrinkWrappedRects(rects, m_indicatorStrategy->findIndicatorRadius());
558
559 GraphicsContextStateSaver stateSaver(graphicsContext);
560
561 // Draw white frames around the holes.
562 // We double the thickness because half of the stroke will be erased when we clear the holes.
563 graphicsContext.setDropShadow({ { shadowOffsetX, shadowOffsetY }, shadowBlurRadius, shadowColor, ShadowRadiusMode::Default });
564 graphicsContext.setStrokeColor(Color::white);
565 graphicsContext.setStrokeThickness(borderWidth * 2);
566 for (auto& path : whiteFramePaths)
567 graphicsContext.strokePath(path);
568
569 graphicsContext.clearDropShadow();
570
571 // Clear out the holes.
572 graphicsContext.setCompositeOperation(CompositeOperator::Clear);
573 for (auto& path : whiteFramePaths)
574 graphicsContext.fillPath(path);
575
576 if (!m_isShowingFindIndicator)
577 return;
578
579 if (RefPtr selectedFrame = frameWithSelection(protect(protect(m_webPage)->corePage()))) {
580 auto findIndicatorRect = protect(selectedFrame->view())->contentsToRootView(enclosingIntRect(protect(selectedFrame->selection())->selectionBounds(FrameSelection::ClipToVisibleContent::No)));
581
582 if (findIndicatorRect != m_findIndicatorRect) {
583 // We are underneath painting, so it's not safe to mutate the layer tree synchronously.
584 callOnMainRunLoop([weakWebPage = WeakPtr { m_webPage }] {
585 if (!weakWebPage)
586 return;
587 weakWebPage->findController().didScrollAffectingFindIndicatorPosition();
588 });
589 }
590 }
591}
592
593void FindController::didScrollAffectingFindIndicatorPosition()
594{
595 if (m_indicatorStrategy->shouldHideFindIndicatorOnScroll())
596 m_indicatorStrategy->hideFindIndicator();
597 else
598 m_indicatorStrategy->updateFindIndicator(true, false);
599}
600
601bool FindController::mouseEvent(PageOverlay&, const PlatformMouseEvent& mouseEvent)
602{
603 if (mouseEvent.type() == PlatformEvent::Type::MousePressed)
604 hideFindUI();
605
606 return false;
607}
608
609void FindController::didInvalidateFindRects()
610{
611 if (RefPtr findPageOverlay = m_findPageOverlay.get())
612 findPageOverlay->setNeedsDisplay();
613}
614
615} // namespace WebKit