Bug Summary

File:Source/WebKit/UIProcess/WebBackForwardList.cpp
Warning:line 357, column 5
A function 'forwardListCountForAPI' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple arm64-apple-macosx26.2.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 UnifiedSource85.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/MacOSX.platform/Developer/SDKs/MacOSX26.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/macOS-Safer-CPP-Checks-EWS/build/Source/WebKit -target-linker-version 1230.1 -fcoverage-compilation-dir=/Volumes/Data/worker/macOS-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/macOS-Safer-CPP-Checks-EWS/build/scan-build-output/StaticAnalyzer/WebKit/WebKit/normal/arm64/UnifiedSource85.d -skip-unused-modulemap-deps -MT dependencies -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.2.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/MacOSX26.2.sdk/usr/local/include -iframework /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.2.sdk/System/Library/PrivateFrameworks -iframework /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.2.sdk/System/Library/PrivateFrameworks -isystem /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitLibraries/SDKs/macosx26.0-additions.sdk/usr/local/include -include-pch /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/PrecompiledHeaders/SharedPrecompiledHeaders/7226384000658175008/WebKitPrefix.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.19+" -D FRAMEWORK_NAME=WebKit -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/DerivedSources/WebKit/IPC -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/MacOSX26.2.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/MacOSX26.2.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/MacOSX26.2.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/MacOSX26.2.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/MacOSX26.2.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/MacOSX26.2.sdk/usr/include/c++/v1 -internal-isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.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/MacOSX.platform/Developer/SDKs/MacOSX26.2.sdk/usr/include -internal-iframework /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.2.sdk/System/Library/Frameworks -internal-iframework /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.2.sdk/System/Library/SubFrameworks -internal-iframework /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.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 -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/UnifiedSource85.plist -x c++ /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/Release/DerivedSources/WebKit/unified-sources/UnifiedSource85.cpp
1/*
2 * Copyright (C) 2010 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 "WebBackForwardList.h"
28
29#include "APIArray.h"
30#include "BrowsingContextGroup.h"
31#include "LoadedWebArchive.h"
32#include "Logging.h"
33#include "SessionState.h"
34#include "WebBackForwardCache.h"
35#include "WebBackForwardListCounts.h"
36#include "WebBackForwardListFrameItem.h"
37#include "WebBackForwardListSwiftUtilities.h"
38#include "WebFrameProxy.h"
39#include "WebInspectorUtilities.h"
40#include "WebPageProxy.h"
41#include <WebCore/DiagnosticLoggingClient.h>
42#include <WebCore/DiagnosticLoggingKeys.h>
43#include <wtf/Borrow.h>
44#include <wtf/DebugUtilities.h>
45#include <wtf/HexNumber.h>
46#include <wtf/text/StringBuilder.h>
47
48#if PLATFORM(COCOA)(defined 1 && 1)
49#include <wtf/cocoa/RuntimeApplicationChecksCocoa.h>
50#endif
51
52// FIXME: https://bugs.webkit.org/show_bug.cgi?id=306415
53#include "WebKit-Swift.h"
54
55namespace WebKit {
56using namespace WebCore;
57
58static inline void setBackForwardItemIdentifiers(FrameState& frameState, BackForwardItemIdentifier itemID)
59{
60 frameState.itemID = itemID;
61 frameState.frameItemID = BackForwardFrameItemIdentifier::generate();
62 for (auto& child : frameState.children)
63 setBackForwardItemIdentifiers(child, itemID);
64}
65
66#if !ENABLE(BACK_FORWARD_LIST_SWIFT)(defined 0 && 0)
67
68static bool shouldSkipItemsWithoutUserGestureForWebKitAPI()
69{
70#if PLATFORM(COCOA)(defined 1 && 1)
71 return linkedOnOrAfterSDKWithBehavior(SDKAlignedBehavior::AllBackForwardItemsWithoutUserGestureInvisibleToUI);
72#endif
73 return false;
74}
75
76static const unsigned DefaultCapacity = 100;
77
78WebBackForwardList::WebBackForwardList(WebPageProxy& page)
79 : m_page(&page)
80{
81 LOG(BackForward, "(Back/Forward) Created WebBackForwardList %p", this)((void)0);
82}
83
84WebBackForwardList::~WebBackForwardList()
85{
86 LOG(BackForward, "(Back/Forward) Destroying WebBackForwardList %p", this)((void)0);
87
88 // A WebBackForwardList should never be destroyed unless it's associated page has been closed or is invalid.
89 ASSERT((!m_page && !m_currentIndex) || !m_page->hasRunningProcess())((void)0);
90}
91
92WebBackForwardListItem* WebBackForwardList::itemForID(BackForwardItemIdentifier identifier)
93{
94 if (!m_page)
95 return nullptr;
96
97 ASSERT(!WebBackForwardListItem::itemForID(identifier) || WebBackForwardListItem::itemForID(identifier)->pageID() == m_page->identifier())((void)0);
98 return WebBackForwardListItem::itemForID(identifier);
99}
100
101void WebBackForwardList::pageClosed()
102{
103 LOG(BackForward, "(Back/Forward) WebBackForwardList %p had its page closed with current size %zu", this, m_entries.size())((void)0);
104
105 // We should have always started out with an m_page and we should never close the page twice.
106 ASSERT(m_page)((void)0);
107
108 if (m_page) {
109 size_t size = m_entries.size();
110 for (size_t i = 0; i < size; ++i)
111 didRemoveItem(m_entries[i]);
112 }
113
114 m_page.clear();
115 m_entries.clear();
116 m_currentIndex = std::nullopt;
117}
118
119void WebBackForwardList::addItem(Ref<WebBackForwardListItem>&& newItem)
120{
121 ASSERT(!m_currentIndex || *m_currentIndex < m_entries.size())((void)0);
122
123 RefPtr page = m_page.get();
124 if (!page)
125 return;
126
127 Vector<Ref<WebBackForwardListItem>> removedItems;
128
129 if (m_currentIndex) {
130 page->recordAutomaticNavigationSnapshot();
131
132 // Toss everything in the forward list.
133 unsigned targetSize = *m_currentIndex + 1;
134 removedItems.reserveInitialCapacity(m_entries.size() - targetSize);
135 while (m_entries.size() > targetSize) {
136 didRemoveItem(m_entries.last());
137 removedItems.append(WTF::move(m_entries.last()));
138 m_entries.removeLast();
139 }
140
141 // Toss the first item if the list is getting too big, as long as we're not using it
142 // (or even if we are, if we only want 1 entry).
143 if (m_entries.size() >= DefaultCapacity && (*m_currentIndex)) {
144 didRemoveItem(m_entries[0]);
145 removedItems.append(WTF::move(m_entries[0]));
146 m_entries.removeAt(0);
147
148 if (m_entries.isEmpty())
149 m_currentIndex = std::nullopt;
150 else
151 --*m_currentIndex;
152 }
153 } else {
154 // If we have no current item index we should also not have any entries.
155 ASSERT(m_entries.isEmpty())((void)0);
156
157 // But just in case it does happen in practice we'll get back in to a consistent state now before adding the new item.
158 size_t size = m_entries.size();
159 for (size_t i = 0; i < size; ++i) {
160 didRemoveItem(m_entries[i]);
161 removedItems.append(WTF::move(m_entries[i]));
162 }
163 m_entries.clear();
164 }
165
166 bool shouldKeepCurrentItem = true;
167
168 if (!m_currentIndex) {
169 ASSERT(m_entries.isEmpty())((void)0);
170 m_currentIndex = 0;
171 } else {
172 shouldKeepCurrentItem = page->shouldKeepCurrentBackForwardListItemInList(m_entries[*m_currentIndex]);
173 if (shouldKeepCurrentItem)
174 ++*m_currentIndex;
175 }
176
177 auto* newItemPtr = newItem.ptr();
178 if (!shouldKeepCurrentItem) {
179 // m_current should never be pointing past the end of the entries Vector.
180 // If it is, something has gone wrong and we should not try to swap in the new item.
181 ASSERT(*m_currentIndex < m_entries.size())((void)0);
182
183 removedItems.append(m_entries[*m_currentIndex].copyRef());
184 m_entries[*m_currentIndex] = WTF::move(newItem);
185 } else {
186 // m_current should never be pointing more than 1 past the end of the entries Vector.
187 // If it is, something has gone wrong and we should not try to insert the new item.
188 ASSERT(*m_currentIndex <= m_entries.size())((void)0);
189
190 if (*m_currentIndex <= m_entries.size())
191 m_entries.insert(*m_currentIndex, WTF::move(newItem));
192 }
193
194 LOG(BackForward, "(Back/Forward) WebBackForwardList %p added an item. Current size %zu, current index %zu, threw away %zu items", this, m_entries.size(), *m_currentIndex, removedItems.size())((void)0);
195 page->didChangeBackForwardList(newItemPtr, WTF::move(removedItems));
196}
197
198void WebBackForwardList::addChildItem(FrameIdentifier parentFrameID, Ref<FrameState>&& frameState)
199{
200 RefPtr currentItem = this->currentItem();
201 if (!currentItem)
202 return;
203
204 RefPtr parentItem = currentItem->mainFrameItem().childItemForFrameID(parentFrameID);
205 if (!parentItem)
206 return;
207
208 parentItem->setChild(WTF::move(frameState));
209}
210
211void WebBackForwardList::goToItem(WebBackForwardListItem& item)
212{
213 ASSERT(!m_currentIndex || *m_currentIndex < m_entries.size())((void)0);
214
215 RefPtr page = m_page.get();
216 if (!m_entries.size() || !page || !m_currentIndex)
217 return;
218
219 size_t targetIndex = notFound;
220 for (size_t i = 0; i < m_entries.size(); ++i) {
221 if (m_entries[i].ptr() == &item) {
222 targetIndex = i;
223 break;
224 }
225 }
226
227 // If the target item wasn't even in the list, there's nothing else to do.
228 if (targetIndex == notFound) {
229 LOG(BackForward, "(Back/Forward) WebBackForwardList %p could not go to item %s (%s) because it was not found", this, item.identifier().toString().utf8().data(), item.url().utf8().data())((void)0);
230 return;
231 }
232
233 if (targetIndex < *m_currentIndex) {
234 unsigned delta = m_entries.size() - targetIndex - 1;
235 String deltaValue = delta > 10 ? "over10"_s : String::number(delta);
236 page->logDiagnosticMessage(WebCore::DiagnosticLoggingKeys::backNavigationDeltaKey(), deltaValue, ShouldSample::No);
237 }
238
239 // If we're going to an item different from the current item, ask the client if the current
240 // item should remain in the list.
241 auto& currentItem = m_entries[*m_currentIndex];
242 bool shouldKeepCurrentItem = true;
243 if (currentItem.ptr() != &item) {
244 page->recordAutomaticNavigationSnapshot();
245 shouldKeepCurrentItem = page->shouldKeepCurrentBackForwardListItemInList(m_entries[*m_currentIndex]);
246 }
247
248 // If the client said to remove the current item, remove it and then update the target index.
249 Vector<Ref<WebBackForwardListItem>> removedItems;
250 if (!shouldKeepCurrentItem) {
251 removedItems.append(currentItem.copyRef());
252 m_entries.removeAt(*m_currentIndex);
253 targetIndex = notFound;
254 for (size_t i = 0; i < m_entries.size(); ++i) {
255 if (m_entries[i].ptr() == &item) {
256 targetIndex = i;
257 break;
258 }
259 }
260 ASSERT(targetIndex != notFound)((void)0);
261 }
262
263 m_currentIndex = targetIndex;
264
265 LOG(BackForward, "(Back/Forward) WebBackForwardList %p going to item %s, is now at index %zu", this, item.identifier().toString().utf8().data(), targetIndex)((void)0);
266 page->didChangeBackForwardList(nullptr, WTF::move(removedItems));
267}
268
269WebBackForwardListItem* WebBackForwardList::currentItem() const
270{
271 ASSERT(!m_currentIndex || *m_currentIndex < m_entries.size())((void)0);
272
273 return m_page && m_currentIndex ? m_entries[*m_currentIndex].ptr() : nullptr;
274}
275
276RefPtr<WebBackForwardListItem> WebBackForwardList::backItem() const
277{
278 ASSERT(!m_currentIndex || *m_currentIndex < m_entries.size())((void)0);
279 if (!m_page || !m_currentIndex)
280 return nullptr;
281
282 if (shouldSkipItemsWithoutUserGestureForWebKitAPI())
283 return itemStartingAtIndexSkippingItemsAddedByJSWithoutUserGesture(NavigationDirection::Backward, *m_currentIndex).first;
284
285 return *m_currentIndex ? m_entries[*m_currentIndex - 1].ptr() : nullptr;
286}
287
288RefPtr<WebBackForwardListItem> WebBackForwardList::forwardItem() const
289{
290 ASSERT(!m_currentIndex || *m_currentIndex < m_entries.size())((void)0);
291 if (!m_page || !m_currentIndex)
292 return nullptr;
293
294 if (shouldSkipItemsWithoutUserGestureForWebKitAPI())
295 return itemStartingAtIndexSkippingItemsAddedByJSWithoutUserGesture(NavigationDirection::Forward, *m_currentIndex).first;
296
297 return m_entries.size() && *m_currentIndex < m_entries.size() - 1 ? m_entries[*m_currentIndex + 1].ptr() : nullptr;
298}
299
300RefPtr<WebBackForwardListItem> WebBackForwardList::itemAtDeltaFromCurrentIndex(int delta, AllowSkippingBackForwardItems allowSkippingBackForwardItems) const
301{
302 if (!m_currentIndex || (int)*m_currentIndex + delta < 0)
303 return nullptr;
304
305 // API requests to get the current item will always get the current item without any skipping logic.
306 if (!delta)
307 return itemAtIndexWithoutSkipping(*m_currentIndex).first;
308
309 if (allowSkippingBackForwardItems == AllowSkippingBackForwardItems::No || !shouldSkipItemsWithoutUserGestureForWebKitAPI())
310 return itemAtIndexWithoutSkipping(*m_currentIndex + delta).first;
311
312 auto direction = delta < 0 ? NavigationDirection::Backward : NavigationDirection::Forward;
313 size_t stepsLeft = abs(delta);
314 size_t nextIndex = *m_currentIndex;
315 while (stepsLeft) {
316 auto item = itemStartingAtIndexSkippingItemsAddedByJSWithoutUserGesture(direction, nextIndex);
317 if (!item.first || !--stepsLeft)
318 return item.first;
319 nextIndex = item.second;
320 }
321
322 return nullptr;
323}
324
325std::pair<RefPtr<WebBackForwardListItem>, size_t> WebBackForwardList::itemAtIndexWithoutSkipping(size_t index) const
326{
327 if (!m_page)
328 return { nullptr, index };
329
330 if (index >= m_entries.size())
331 return { nullptr, index };
332
333 return { { m_entries[index] }, index };
334}
335
336unsigned WebBackForwardList::rawBackListEntryCount() const
337{
338 ASSERT(!m_currentIndex || *m_currentIndex < m_entries.size())((void)0);
339
340 return m_page && m_currentIndex ? *m_currentIndex : 0;
341}
342
343unsigned WebBackForwardList::rawForwardListEntryCount() const
344{
345 ASSERT(!m_currentIndex || *m_currentIndex < m_entries.size())((void)0);
346
347 return m_page && m_currentIndex ? m_entries.size() - (*m_currentIndex + 1) : 0;
348}
349
350unsigned WebBackForwardList::backListCountForAPI() const
351{
352 return backList()->size();
353}
354
355unsigned WebBackForwardList::forwardListCountForAPI() const
356{
357 return forwardList()->size();
A function 'forwardListCountForAPI' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object
358}
359
360WebBackForwardListCounts WebBackForwardList::counts() const
361{
362 return WebBackForwardListCounts { rawBackListEntryCount(), rawForwardListEntryCount() };
363}
364
365Ref<API::Array> WebBackForwardList::backList() const
366{
367 return backListAsAPIArrayWithLimit(rawBackListEntryCount());
368}
369
370Ref<API::Array> WebBackForwardList::forwardList() const
371{
372 return forwardListAsAPIArrayWithLimit(rawForwardListEntryCount());
373}
374
375Ref<API::Array> WebBackForwardList::backListAsAPIArrayWithLimit(unsigned limit) const
376{
377 ASSERT(!m_currentIndex || *m_currentIndex < m_entries.size())((void)0);
378
379 if (!m_page || !m_currentIndex)
380 return API::Array::create();
381
382 unsigned backListSize = rawBackListEntryCount();
383 unsigned size = std::min(backListSize, limit);
384 if (!size)
385 return API::Array::create();
386
387 ASSERT(backListSize >= size)((void)0);
388
389 if (shouldSkipItemsWithoutUserGestureForWebKitAPI()) {
390 Vector<RefPtr<API::Object>> vector;
391
392 size_t nextStartingIndex = *m_currentIndex;
393 while (backListSize) {
394 auto item = itemStartingAtIndexSkippingItemsAddedByJSWithoutUserGesture(NavigationDirection::Backward, nextStartingIndex);
395 if (item.first)
396 vector.append(item.first);
397
398 if (!item.first || !--backListSize || !item.second)
399 break;
400 nextStartingIndex = item.second;
401 }
402 vector.reverse();
403
404 return API::Array::create(WTF::move(vector));
405 }
406
407 size_t startIndex = backListSize - size;
408 Vector<RefPtr<API::Object>> vector(size, [&](size_t i) -> RefPtr<API::Object> {
409 // FIXME: Remove SUPPRESS_UNCOUNTED_LAMBDA_CAPTURE when the false positive
410 // in the static analyzer is fixed.
411 SUPPRESS_UNCOUNTED_LAMBDA_CAPTURE[[clang::suppress]] return m_entries[startIndex + i].ptr();
412 });
413
414 return API::Array::create(WTF::move(vector));
415}
416
417Ref<API::Array> WebBackForwardList::forwardListAsAPIArrayWithLimit(unsigned limit) const
418{
419 ASSERT(!m_currentIndex || *m_currentIndex < m_entries.size())((void)0);
420
421 if (!m_page || !m_currentIndex)
422 return API::Array::create();
423
424 unsigned size = std::min(rawForwardListEntryCount(), limit);
425 if (!size)
426 return API::Array::create();
427
428 if (shouldSkipItemsWithoutUserGestureForWebKitAPI()) {
429 Vector<RefPtr<API::Object>> vector;
430
431 size_t nextStartingIndex = *m_currentIndex;
432 while (size) {
433 auto item = itemStartingAtIndexSkippingItemsAddedByJSWithoutUserGesture(NavigationDirection::Forward, nextStartingIndex);
434 if (item.first)
435 vector.append(item.first);
436
437 if (!item.first || !--size || !item.second)
438 break;
439 nextStartingIndex = item.second;
440 }
441
442 return API::Array::create(WTF::move(vector));
443 }
444
445 size_t startIndex = *m_currentIndex + 1;
446 Vector<RefPtr<API::Object>> vector(size, [&](size_t i) -> RefPtr<API::Object> {
447 return m_entries[startIndex + i].ptr();
448 });
449
450 return API::Array::create(WTF::move(vector));
451}
452
453void WebBackForwardList::removeAllItems()
454{
455 ASSERT(!m_currentIndex || *m_currentIndex < m_entries.size())((void)0);
456
457 LOG(BackForward, "(Back/Forward) WebBackForwardList %p removeAllItems (has %zu of them)", this, m_entries.size())((void)0);
458
459 for (auto& entry : m_entries)
460 didRemoveItem(entry);
461
462 m_currentIndex = std::nullopt;
463 protect(m_page)->didChangeBackForwardList(nullptr, std::exchange(m_entries, { }));
464}
465
466void WebBackForwardList::clear()
467{
468 ASSERT(!m_currentIndex || *m_currentIndex < m_entries.size())((void)0);
469
470 LOG(BackForward, "(Back/Forward) WebBackForwardList %p clear (has %zu of them)", this, m_entries.size())((void)0);
471
472 RefPtr page = m_page.get();
473 size_t size = m_entries.size();
474 if (!page || size <= 1)
475 return;
476
477 RefPtr<WebBackForwardListItem> currentItem = this->currentItem();
478
479 if (!currentItem) {
480 // We should only ever have no current item if we also have no current item index.
481 ASSERT(!m_currentIndex)((void)0);
482
483 // But just in case it does happen in practice we should get back into a consistent state now.
484 for (auto& entry : m_entries)
485 didRemoveItem(entry);
486
487 m_currentIndex = std::nullopt;
488 page->didChangeBackForwardList(nullptr, std::exchange(m_entries, { }));
489
490 return;
491 }
492
493 for (size_t i = 0; i < size; ++i) {
494 if (m_entries[i].ptr() != currentItem)
495 didRemoveItem(m_entries[i]);
496 }
497
498 Vector<Ref<WebBackForwardListItem>> removedItems;
499 removedItems.reserveInitialCapacity(size - 1);
500 for (size_t i = 0; i < size; ++i) {
501 if (m_currentIndex && i != *m_currentIndex)
502 removedItems.append(WTF::move(m_entries[i]));
503 }
504
505 m_currentIndex = 0;
506
507 m_entries.clear();
508 if (currentItem)
509 m_entries.append(currentItem.releaseNonNull());
510 else
511 m_currentIndex = std::nullopt;
512 page->didChangeBackForwardList(nullptr, WTF::move(removedItems));
513}
514
515BackForwardListState WebBackForwardList::backForwardListState(WTF::Function<bool (WebBackForwardListItem&)>&& filter) const
516{
517 ASSERT(!m_currentIndex || *m_currentIndex < m_entries.size())((void)0);
518
519 BackForwardListState backForwardListState;
520 if (m_currentIndex)
521 backForwardListState.currentIndex = *m_currentIndex;
522
523 Borrow entries = m_entries;
524 for (size_t i = 0; i < entries->size(); ++i) {
525 auto& entry = entries.get()[i];
526
527 if (filter && !filter(entry)) {
528 auto& currentIndex = backForwardListState.currentIndex;
529 if (currentIndex && i <= currentIndex.value() && currentIndex.value())
530 --currentIndex.value();
531
532 continue;
533 }
534
535 backForwardListState.items.append({ entry->copyMainFrameStateWithChildren(), entry->navigatedFrameID() });
536 }
537
538 if (backForwardListState.items.isEmpty())
539 backForwardListState.currentIndex = std::nullopt;
540 else if (backForwardListState.items.size() <= backForwardListState.currentIndex.value())
541 backForwardListState.currentIndex = backForwardListState.items.size() - 1;
542
543 return backForwardListState;
544}
545
546void WebBackForwardList::restoreFromState(BackForwardListState backForwardListState)
547{
548 if (!m_page)
549 return;
550
551 // FIXME: Enable restoring resourceDirectoryURL.
552 m_entries = WTF::map(WTF::move(backForwardListState.items), [this](auto&& itemState) {
553 Ref stateCopy = itemState.frameState->copy();
554 setBackForwardItemIdentifiers(stateCopy, BackForwardItemIdentifier::generate());
555 m_currentIndex = m_entries.isEmpty() ? std::nullopt : std::optional(m_entries.size() - 1);
556 return WebBackForwardListItem::create(WTF::move(stateCopy), m_page->identifier(), itemState.navigatedFrameID);
557 });
558 m_currentIndex = backForwardListState.currentIndex ? std::optional<size_t>(*backForwardListState.currentIndex) : std::nullopt;
559
560 LOG(BackForward, "(Back/Forward) WebBackForwardList %p restored from state (has %zu entries)", this, m_entries.size())((void)0);
561}
562
563void WebBackForwardList::setItemsAsRestoredFromSession()
564{
565 setItemsAsRestoredFromSessionIf([](WebBackForwardListItem&) {
566 return true;
567 });
568}
569
570void WebBackForwardList::setItemsAsRestoredFromSessionIf(NOESCAPE[[clang::noescape]] Function<bool(WebBackForwardListItem&)>&& functor)
571{
572 for (auto& entry : m_entries) {
573 if (functor(entry))
574 entry->setWasRestoredFromSession();
575 }
576}
577
578void WebBackForwardList::didRemoveItem(WebBackForwardListItem& backForwardListItem)
579{
580 backForwardListItem.wasRemovedFromBackForwardList();
581
582 protect(m_page)->backForwardRemovedItem(backForwardListItem.mainFrameItem().identifier());
583
584#if PLATFORM(COCOA)(defined 1 && 1) || PLATFORM(GTK)(defined WTF_PLATFORM_GTK && WTF_PLATFORM_GTK)
585 backForwardListItem.setSnapshot(nullptr);
586#endif
587}
588
589std::pair<RefPtr<WebBackForwardListItem>, size_t> WebBackForwardList::itemStartingAtIndexSkippingItemsAddedByJSWithoutUserGesture(NavigationDirection direction, size_t startingIndex) const
590{
591 if (direction == NavigationDirection::Backward && !startingIndex)
592 return { nullptr, 0 };
593
594 auto delta = direction == NavigationDirection::Backward ? -1 : 1;
595 size_t itemIndex = startingIndex + delta;
596
597 if (itemIndex >= m_entries.size())
598 return { nullptr, 0 };
599
600 auto startingItem = itemAtIndexWithoutSkipping(startingIndex);
601 RELEASE_ASSERT(startingItem.first)do { if (__builtin_expect(!!(!(startingItem.first)), 0)) do {
WTF::isIntegralOrPointerType(); compilerFenceForCrash(); WTFCrashWithInfo
(601, "/Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/WebBackForwardList.cpp"
, __PRETTY_FUNCTION__ ); } while (false); } while (0)
;
602
603 auto item = itemAtIndexWithoutSkipping(itemIndex);
604
605#if PLATFORM(COCOA)(defined 1 && 1)
606 if (!linkedOnOrAfterSDKWithBehavior(SDKAlignedBehavior::UIBackForwardSkipsHistoryItemsWithoutUserGesture))
607 return item;
608#endif
609
610 if (!item.first)
611 return item;
612
613 // For example:
614 // A -> A#a (no userInteraction) -> B -> B#a (no user interaction) -> B#b (no user interaction)
615 // If we're on B and navigate back, we don't want to skip anything and load A#a.
616 // However, if we're on A and navigate forward, we do want to skip items and end up on B#b.
617 // The forward logic comes later.
618 if (direction == NavigationDirection::Backward && !startingItem.first->wasCreatedByJSWithoutUserInteraction())
619 return item;
620
621 // If every item from this point back to the start of the list was created by JS without user interaction,
622 // we ignore them all.
623 if (direction == NavigationDirection::Backward && startingItem.first->wasCreatedByJSWithoutUserInteraction()) {
624 auto innerItem = item;
625 while (innerItem.first->wasCreatedByJSWithoutUserInteraction()) {
626 if (innerItem.second)
627 innerItem = itemAtIndexWithoutSkipping(innerItem.second - 1);
628 else
629 return { };
630 ASSERT(innerItem.first)((void)0);
631 }
632 }
633
634 // For example:
635 // Yahoo -> Yahoo#a (no userInteraction) -> Google -> Google#a (no user interaction) -> Google#b (no user interaction)
636 // If we are on Google#b and navigate backwards, we want to skip over Google#a and Google, to end up on Yahoo#a.
637 // If we are on Yahoo#a and navigate forwards, we want to skip over Google and Google#a, to end up on Google#b.
638 auto originalitem = item;
639 while (item.first->wasCreatedByJSWithoutUserInteraction()) {
640 itemIndex += delta;
641 item = itemAtIndexWithoutSkipping(itemIndex);
642 if (!item.first) {
643 // If there are no more back items that ever had a user gesture, then we should not enable going back.
644 // This happens when e.g. a new window is created by JavaScript then client redirects occur that create
645 // a sequence of history items, each without user interaction.
646 RELEASE_LOG(Loading, "UI Navigation is disabling going back because no more WebBackForwardListItem items in the back list had user interaction")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 = (WebKit2LogLoading
.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
("UI Navigation is disabling going back because no more WebBackForwardListItem items in the back list had user interaction"
), "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_LOG13"
) = "UI Navigation is disabling going back because no more WebBackForwardListItem items in the back list had user interaction"
; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf
[__builtin_os_log_format_buffer_size("UI Navigation is disabling going back because no more WebBackForwardListItem items in the back list had user interaction"
)]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str
, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "UI Navigation is disabling going back because no more WebBackForwardListItem items in the back list had user interaction"
), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); }
}) clang diagnostic pop
;
647 return { };
648 }
649
650 RELEASE_LOG(Loading, "UI Navigation is skipping a WebBackForwardListItem because it was added by JavaScript without user interaction")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 = (WebKit2LogLoading
.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
("UI Navigation is skipping a WebBackForwardListItem because it was added by JavaScript without user interaction"
), "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_LOG14"
) = "UI Navigation is skipping a WebBackForwardListItem because it was added by JavaScript without user interaction"
; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf
[__builtin_os_log_format_buffer_size("UI Navigation is skipping a WebBackForwardListItem because it was added by JavaScript without user interaction"
)]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str
, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "UI Navigation is skipping a WebBackForwardListItem because it was added by JavaScript without user interaction"
), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); }
}) clang diagnostic pop
;
651 }
652
653 // We are now on the next item that has user interaction.
654 ASSERT(!item.first->wasCreatedByJSWithoutUserInteraction())((void)0);
655
656 if (direction == NavigationDirection::Backward) {
657 // If going backwards, skip over next item with user iteraction since this is the one the user
658 // thinks they're on.
659 --itemIndex;
660 item = itemAtIndexWithoutSkipping(itemIndex);
661 if (!item.first)
662 return originalitem;
663 RELEASE_LOG(Loading, "UI Navigation is skipping a WebBackForwardListItem that has user interaction because we started on an item that didn't have interaction")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 = (WebKit2LogLoading
.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
("UI Navigation is skipping a WebBackForwardListItem that has user interaction because we started on an item that didn't have interaction"
), "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_LOG15"
) = "UI Navigation is skipping a WebBackForwardListItem that has user interaction because we started on an item that didn't have interaction"
; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf
[__builtin_os_log_format_buffer_size("UI Navigation is skipping a WebBackForwardListItem that has user interaction because we started on an item that didn't have interaction"
)]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str
, (uint8_t *)__builtin_os_log_format(_os_fmt_buf, "UI Navigation is skipping a WebBackForwardListItem that has user interaction because we started on an item that didn't have interaction"
), (uint32_t)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); }
}) clang diagnostic pop
;
664 } else {
665 // If going forward and there are items that we created by JS without user interaction, move forward to the last
666 // one in the series.
667 auto nextItem = itemAtIndexWithoutSkipping(itemIndex + 1);
668 while (nextItem.first && nextItem.first->wasCreatedByJSWithoutUserInteraction())
669 item = std::exchange(nextItem, itemAtIndexWithoutSkipping(++itemIndex));
670 }
671 return item;
672}
673
674RefPtr<WebBackForwardListItem> WebBackForwardList::goBackItemSkippingItemsWithoutUserGesture() const
675{
676 if (!m_currentIndex || !*m_currentIndex)
677 return nullptr;
678 return itemStartingAtIndexSkippingItemsAddedByJSWithoutUserGesture(NavigationDirection::Backward, *m_currentIndex).first;
679}
680
681RefPtr<WebBackForwardListItem> WebBackForwardList::goForwardItemSkippingItemsWithoutUserGesture() const
682{
683 if (!m_currentIndex || *m_currentIndex >= m_entries.size())
684 return nullptr;
685 return itemStartingAtIndexSkippingItemsAddedByJSWithoutUserGesture(NavigationDirection::Forward, *m_currentIndex).first;
686}
687
688static inline void NODELETE[[clang::annotate_type("webkit.nodelete")]] setBackForwardItemIdentifier(FrameState& frameState, BackForwardItemIdentifier itemID)
689{
690 frameState.itemID = itemID;
691 for (auto& child : frameState.children)
692 setBackForwardItemIdentifier(child, itemID);
693}
694
695Ref<FrameState> WebBackForwardList::completeFrameStateForNavigation(Ref<FrameState>&& navigatedFrameState)
696{
697 RefPtr currentItem = this->currentItem();
698 if (!currentItem)
699 return navigatedFrameState;
700
701 auto navigatedFrameID = navigatedFrameState->frameID;
702 if (!navigatedFrameID)
703 return navigatedFrameState;
704
705 Ref mainFrameItem = currentItem->mainFrameItem();
706 if (mainFrameItem->frameID() == navigatedFrameID)
707 return navigatedFrameState;
708
709 if (!mainFrameItem->childItemForFrameID(*navigatedFrameID))
710 return navigatedFrameState;
711
712 Ref frameState = currentItem->copyMainFrameStateWithChildren();
713 setBackForwardItemIdentifier(frameState, *navigatedFrameState->itemID);
714 frameState->replaceChildFrameState(WTF::move(navigatedFrameState));
715 return frameState;
716}
717
718#define MESSAGE_CHECK(process, assertion)do { if (!(assertion)) [[unlikely]] { os_fault_with_payload(31
, 0, nullptr, 0, makeString("/Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/WebBackForwardList.cpp"
" " "718" ": Invalid message dispatched "_s, unsafeSpan(__PRETTY_FUNCTION__
)).utf8().data(), 0); IPC::markCurrentlyDispatchedMessageAsInvalid
(process->connection(), "Message check failed: " "assertion"_s
); if (IPC::Connection::shouldCrashOnMessageCheckFailure()) {
do { __asm__ volatile ("brk #0xc471"); __builtin_unreachable
(); } while (0); } { (void)0; } return; } } while (0)
MESSAGE_CHECK_BASE(assertion, process->connection())do { if (!(assertion)) [[unlikely]] { os_fault_with_payload(31
, 0, nullptr, 0, makeString("/Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/WebBackForwardList.cpp"
" " "718" ": Invalid message dispatched "_s, unsafeSpan(__PRETTY_FUNCTION__
)).utf8().data(), 0); IPC::markCurrentlyDispatchedMessageAsInvalid
(process->connection(), "Message check failed: " "assertion"_s
); if (IPC::Connection::shouldCrashOnMessageCheckFailure()) {
do { __asm__ volatile ("brk #0xc471"); __builtin_unreachable
(); } while (0); } { (void)0; } return; } } while (0)
719#define MESSAGE_CHECK_COMPLETION(process, assertion, completion)do { if (!(assertion)) [[unlikely]] { os_fault_with_payload(31
, 0, nullptr, 0, makeString("/Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/WebBackForwardList.cpp"
" " "719" ": Invalid message dispatched "_s, unsafeSpan(__PRETTY_FUNCTION__
)).utf8().data(), 0); IPC::markCurrentlyDispatchedMessageAsInvalid
(process->connection(), "Message check failed: " "assertion"_s
); if (IPC::Connection::shouldCrashOnMessageCheckFailure()) {
do { __asm__ volatile ("brk #0xc471"); __builtin_unreachable
(); } while (0); } { completion; } return; } } while (0)
MESSAGE_CHECK_COMPLETION_BASE(assertion, process->connection(), completion)do { if (!(assertion)) [[unlikely]] { os_fault_with_payload(31
, 0, nullptr, 0, makeString("/Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/WebBackForwardList.cpp"
" " "719" ": Invalid message dispatched "_s, unsafeSpan(__PRETTY_FUNCTION__
)).utf8().data(), 0); IPC::markCurrentlyDispatchedMessageAsInvalid
(process->connection(), "Message check failed: " "assertion"_s
); if (IPC::Connection::shouldCrashOnMessageCheckFailure()) {
do { __asm__ volatile ("brk #0xc471"); __builtin_unreachable
(); } while (0); } { completion; } return; } } while (0)
720
721void WebBackForwardList::backForwardAddItem(IPC::Connection& connection, Ref<FrameState>&& navigatedFrameState)
722{
723 if (RefPtr webPageProxy = m_page.get())
724 backForwardAddItemShared(connection, WTF::move(navigatedFrameState), webPageProxy->didLoadWebArchive() ? LoadedWebArchive::Yes : LoadedWebArchive::No);
725}
726
727void WebBackForwardList::backForwardAddItemShared(IPC::Connection& connection, Ref<FrameState>&& navigatedFrameState, LoadedWebArchive loadedWebArchive)
728{
729 Ref process = WebProcessProxy::fromConnection(connection);
730
731 URL itemURL { navigatedFrameState->urlString };
732 URL itemOriginalURL { navigatedFrameState->originalURLString };
733#if PLATFORM(COCOA)(defined 1 && 1)
734 if (linkedOnOrAfterSDKWithBehavior(SDKAlignedBehavior::PushStateFilePathRestriction)
735#if PLATFORM(MAC)(defined 1 && 1)
736 && !WTF::MacApplication::isMimeoPhotoProject() // rdar://112445672.
737#endif // PLATFORM(MAC)
738 ) {
739#endif // PLATFORM(COCOA)
740 ASSERT(!itemURL.protocolIsFile() || process->wasPreviouslyApprovedFileURL(itemURL))((void)0);
741 MESSAGE_CHECK(process, !itemURL.protocolIsFile() || process->wasPreviouslyApprovedFileURL(itemURL))do { if (!(!itemURL.protocolIsFile() || process->wasPreviouslyApprovedFileURL
(itemURL))) [[unlikely]] { os_fault_with_payload(31, 0, nullptr
, 0, makeString("/Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/WebBackForwardList.cpp"
" " "741" ": Invalid message dispatched "_s, unsafeSpan(__PRETTY_FUNCTION__
)).utf8().data(), 0); IPC::markCurrentlyDispatchedMessageAsInvalid
(process->connection(), "Message check failed: " "!itemURL.protocolIsFile() || process->wasPreviouslyApprovedFileURL(itemURL)"_s
); if (IPC::Connection::shouldCrashOnMessageCheckFailure()) {
do { __asm__ volatile ("brk #0xc471"); __builtin_unreachable
(); } while (0); } { (void)0; } return; } } while (0)
;
742 MESSAGE_CHECK(process, !itemOriginalURL.protocolIsFile() || process->wasPreviouslyApprovedFileURL(itemOriginalURL))do { if (!(!itemOriginalURL.protocolIsFile() || process->wasPreviouslyApprovedFileURL
(itemOriginalURL))) [[unlikely]] { os_fault_with_payload(31, 0
, nullptr, 0, makeString("/Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/WebBackForwardList.cpp"
" " "742" ": Invalid message dispatched "_s, unsafeSpan(__PRETTY_FUNCTION__
)).utf8().data(), 0); IPC::markCurrentlyDispatchedMessageAsInvalid
(process->connection(), "Message check failed: " "!itemOriginalURL.protocolIsFile() || process->wasPreviouslyApprovedFileURL(itemOriginalURL)"_s
); if (IPC::Connection::shouldCrashOnMessageCheckFailure()) {
do { __asm__ volatile ("brk #0xc471"); __builtin_unreachable
(); } while (0); } { (void)0; } return; } } while (0)
;
743#if PLATFORM(COCOA)(defined 1 && 1)
744 }
745#endif
746
747 if (RefPtr targetFrame = WebFrameProxy::webFrame(navigatedFrameState->frameID)) {
748 if (targetFrame->isPendingInitialHistoryItem()) {
749 targetFrame->setIsPendingInitialHistoryItem(false);
750 if (RefPtr parent = targetFrame->parentFrame())
751 addChildItem(parent->frameID(), WTF::move(navigatedFrameState));
752 return;
753 }
754 } else
755 return;
756
757 if (RefPtr webPageProxy = m_page.get()) {
758 auto navigatedFrameID = navigatedFrameState->frameID;
759 Ref item = WebBackForwardListItem::create(completeFrameStateForNavigation(WTF::move(navigatedFrameState)), webPageProxy->identifier(), navigatedFrameID, protect(webPageProxy->browsingContextGroup()).ptr());
760 item->setResourceDirectoryURL(webPageProxy->currentResourceDirectoryURL());
761 item->setEnhancedSecurity(process->enhancedSecurity());
762 if (loadedWebArchive == LoadedWebArchive::Yes)
763 item->setDataStoreForWebArchive(protect(process->websiteDataStore()));
764 addItem(WTF::move(item));
765 }
766}
767
768void WebBackForwardList::backForwardSetChildItem(BackForwardFrameItemIdentifier frameItemID, Ref<FrameState>&& frameState)
769{
770 RefPtr item = currentItem();
771 if (!item)
772 return;
773
774 if (RefPtr frameItem = WebBackForwardListFrameItem::itemForID(item->identifier(), frameItemID))
775 frameItem->setChild(WTF::move(frameState));
776}
777
778void WebBackForwardList::backForwardClearChildren(BackForwardItemIdentifier itemID, BackForwardFrameItemIdentifier frameItemID)
779{
780 if (RefPtr frameItem = WebBackForwardListFrameItem::itemForID(itemID, frameItemID))
781 frameItem->clearChildren();
782}
783
784void WebBackForwardList::backForwardUpdateItem(IPC::Connection& connection, Ref<FrameState>&& frameState)
785{
786 RefPtr frameItem = frameState->itemID && frameState->frameItemID ? WebBackForwardListFrameItem::itemForID(*frameState->itemID, *frameState->frameItemID) : nullptr;
787 if (!frameItem)
788 return;
789
790 RefPtr item = frameItem->backForwardListItem();
791 if (!item)
792 return;
793
794 if (RefPtr webPageProxy = m_page.get()) {
795 ASSERT(webPageProxy->identifier() == item->pageID() && frameState->itemID == item->identifier())((void)0);
796
797 Ref process = *downcast<WebProcessProxy>(AuxiliaryProcessProxy::fromConnection(connection));
798 if (!!item->backForwardCacheEntry() != frameState->hasCachedPage) {
799 if (frameState->hasCachedPage)
800 protect(webPageProxy->backForwardCache())->addEntry(*item, process->coreProcessIdentifier());
801 else if (!item->suspendedPage())
802 protect(webPageProxy->backForwardCache())->removeEntry(*item);
803 }
804
805 auto oldFrameID = frameItem->frameID();
806 frameItem->setFrameState(WTF::move(frameState));
807 auto newFrameID = frameItem->frameID();
808
809 if (oldFrameID && newFrameID && oldFrameID != newFrameID)
810 updateFrameIdentifier(*oldFrameID, *newFrameID);
811
812 webPageProxy->updateCanGoBackAndForward();
813 }
814}
815
816void WebBackForwardList::updateFrameIdentifier(FrameIdentifier oldFrameID, FrameIdentifier newFrameID)
817{
818 for (auto& entry : m_entries)
819 entry->updateFrameID(oldFrameID, newFrameID);
820}
821
822void WebBackForwardList::backForwardGoToItem(BackForwardItemIdentifier itemID, CompletionHandler<void(const WebBackForwardListCounts&)>&& completionHandler)
823{
824 // On process swap, we tell the previous process to ignore the load, which causes it to restore its current back forward item to its previous
825 // value. Since the load is really going on in a new provisional process, we want to ignore such requests from the committed process.
826 // Any real new load in the committed process would have cleared m_provisionalPage.
827 if (RefPtr webPageProxy = m_page.get()) {
828 if (webPageProxy->hasProvisionalPage())
829 return completionHandler(counts());
830 }
831
832 backForwardGoToItemShared(itemID, WTF::move(completionHandler));
833}
834
835void WebBackForwardList::backForwardListContainsItem(WebCore::BackForwardItemIdentifier itemID, CompletionHandler<void(bool)>&& completionHandler)
836{
837 completionHandler(itemForID(itemID));
838}
839
840void WebBackForwardList::backForwardGoToItemShared(BackForwardItemIdentifier itemID, CompletionHandler<void(const WebBackForwardListCounts&)>&& completionHandler)
841{
842 if (RefPtr webPageProxy = m_page.get())
843 MESSAGE_CHECK_COMPLETION(Ref { webPageProxy->legacyMainFrameProcess() }, !WebKit::isInspectorPage(*webPageProxy), completionHandler(counts()))do { if (!(!WebKit::isInspectorPage(*webPageProxy))) [[unlikely
]] { os_fault_with_payload(31, 0, nullptr, 0, makeString("/Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/WebBackForwardList.cpp"
" " "843" ": Invalid message dispatched "_s, unsafeSpan(__PRETTY_FUNCTION__
)).utf8().data(), 0); IPC::markCurrentlyDispatchedMessageAsInvalid
(Ref { webPageProxy->legacyMainFrameProcess() }->connection
(), "Message check failed: " "!WebKit::isInspectorPage(*webPageProxy)"_s
); if (IPC::Connection::shouldCrashOnMessageCheckFailure()) {
do { __asm__ volatile ("brk #0xc471"); __builtin_unreachable
(); } while (0); } { completionHandler(counts()); } return; }
} while (0)
;
844
845 RefPtr item = itemForID(itemID);
846 if (!item)
847 return completionHandler(counts());
848
849 goToItem(*item);
850 completionHandler(counts());
851}
852
853void WebBackForwardList::backForwardAllItems(FrameIdentifier frameID, CompletionHandler<void(Vector<Ref<FrameState>>&&)>&& completionHandler)
854{
855 auto frameItems = WTF::compactMap(entries(), [frameID](const auto& item) -> RefPtr<WebBackForwardListFrameItem> {
856 return item->mainFrameItem().childItemForFrameID(frameID);
857 });
858
859 completionHandler(WTF::map(WTF::move(frameItems), [](const auto& frameItem) {
860 return frameItem->copyFrameStateWithChildren();
861 }));
862}
863
864void WebBackForwardList::backForwardItemAtIndexForWebContent(int32_t delta, FrameIdentifier frameID, CompletionHandler<void(RefPtr<FrameState>&&)>&& completionHandler)
865{
866 // FIXME: This should verify that the web process requesting the item hosts the specified frame.
867 if (RefPtr item = itemAtDeltaFromCurrentIndex(delta, AllowSkippingBackForwardItems::No)) {
868 if (RefPtr frameItem = item->mainFrameItem().childItemForFrameID(frameID))
869 return completionHandler(frameItem->copyFrameStateWithChildren());
870 completionHandler(item->copyMainFrameStateWithChildren());
871 } else
872 completionHandler(nullptr);
873}
874
875void WebBackForwardList::backForwardListCounts(CompletionHandler<void(WebBackForwardListCounts&&)>&& completionHandler)
876{
877 completionHandler(counts());
878}
879
880FrameState* WebBackForwardList::findFrameStateInItem(WebCore::BackForwardItemIdentifier itemID, WebCore::FrameIdentifier parentFrameID, uint64_t childFrameIndex)
881{
882 RefPtr targetItem = itemForID(itemID);
883 if (!targetItem)
884 return nullptr;
885
886 RefPtr parentFrameItem = targetItem->mainFrameItem().childItemForFrameID(parentFrameID);
887 if (!parentFrameItem) {
888 // FIXME: After session restore, the back/forward list's frame identifiers don't match
889 // the current WebView's frames because the original identifiers are unavailable.
890 // Fall back to the mainFrameItem if the parentFrameID isn't found.
891 // This only works correctly for direct children of the main frame; nested frames
892 // (e.g., subframe > nestedframe) will get the wrong FrameState.
893 parentFrameItem = &targetItem->mainFrameItem();
894 }
895
896 RefPtr childFrameItem = parentFrameItem->childItemAtIndex(childFrameIndex);
897 if (!childFrameItem)
898 return nullptr;
899
900 return &childFrameItem->frameState();
901}
902
903String WebBackForwardList::loggingString() const
904{
905 StringBuilder builder;
906
907 String currentIndexString = m_currentIndex ? String::number(*m_currentIndex) : String::number(-1);
908 builder.append("\nWebBackForwardList 0x"_s, hex(reinterpret_cast<uintptr_t>(this)), " - "_s, m_entries.size(), " entries, currentIndex is "_s, currentIndexString, "\n"_s);
909
910 for (size_t i = 0; i < m_entries.size(); ++i) {
911 Ref entry = m_entries[i];
912 String itemIdentifier = entry->identifier().loggingString();
913 auto entryString = entry->loggingString();
914 builder.append(String::number(i), " - ItemID:"_s, itemIdentifier, ", "_s, entryString);
915 }
916
917 return builder.toString();
918}
919
920#else // ENABLE(BACK_FORWARD_LIST_SWIFT)
921
922WebBackForwardListWrapper::WebBackForwardListWrapper(WebPageProxy& webPageProxy)
923 : m_impl(WTF::makeUniqueWithoutFastMallocCheck<WebBackForwardList>(WebBackForwardList::init(webPageProxy)))
924{
925}
926
927WebBackForwardListWrapper::~WebBackForwardListWrapper() = default;
928
929WebBackForwardListItem* WebBackForwardListWrapper::currentItem() const
930{
931 return m_impl->currentItem();
932}
933
934RefPtr<WebBackForwardListItem> WebBackForwardListWrapper::backItem() const
935{
936 return m_impl->backItem();
937}
938
939RefPtr<WebBackForwardListItem> WebBackForwardListWrapper::forwardItem() const
940{
941 return m_impl->forwardItem();
942}
943
944RefPtr<WebBackForwardListItem> WebBackForwardListWrapper::itemAtDeltaFromCurrentIndex(int index, AllowSkippingBackForwardItems allowSkipping) const
945{
946 return m_impl->itemAtDeltaFromCurrentIndex(index, allowSkipping == AllowSkippingBackForwardItems::Yes ? true : false);
947}
948
949unsigned WebBackForwardListWrapper::backListCountForAPI() const
950{
951 return m_impl->backListCountForAPI();
952}
953
954unsigned WebBackForwardListWrapper::forwardListCountForAPI() const
955{
956 return m_impl->forwardListCountForAPI();
957}
958
959Ref<API::Array> WebBackForwardListWrapper::backList() const
960{
961 return backListAsAPIArrayWithLimit(backListCountForAPI());
962}
963
964Ref<API::Array> WebBackForwardListWrapper::forwardList() const
965{
966 return forwardListAsAPIArrayWithLimit(forwardListCountForAPI());
967}
968
969Ref<API::Array> WebBackForwardListWrapper::backListAsAPIArrayWithLimit(unsigned limit) const
970{
971 return m_impl->backListAsAPIArrayWithLimit(limit);
972}
973
974Ref<API::Array> WebBackForwardListWrapper::forwardListAsAPIArrayWithLimit(unsigned limit) const
975{
976 return m_impl->forwardListAsAPIArrayWithLimit(limit);
977}
978
979void WebBackForwardListWrapper::removeAllItems()
980{
981 m_impl->removeAllItems();
982}
983
984void WebBackForwardListWrapper::clear()
985{
986 m_impl->clear();
987}
988
989String WebBackForwardListWrapper::loggingString()
990{
991 return String::fromUTF8WithLatin1Fallback(std::string(m_impl->loggingString()));
992}
993
994#endif // ENABLE(BACK_FORWARD_LIST_SWIFT)
995
996} // namespace WebKit
997
998#if ENABLE(BACK_FORWARD_LIST_SWIFT)(defined 0 && 0)
999
1000WebCore::BackForwardFrameItemIdentifier generateBackForwardFrameItemIdentifier()
1001{
1002 return WebCore::BackForwardFrameItemIdentifier::generate();
1003}
1004
1005// rdar://168139823 is the task of doing a productionized version of WebKit Swift logging
1006void doLog(const WTF::String& msg)
1007{
1008 LOG(BackForward, "%s", msg.utf8().data())((void)0);
1009}
1010
1011void doLoadingReleaseLog(const WTF::String& msg)
1012{
1013 RELEASE_LOG(Loading, "%s", msg.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 = (WebKit2LogLoading
.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
("%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_LOG16"
) = "%s"; uint8_t _Alignas(16) __attribute__((uninitialized))
_os_fmt_buf[__builtin_os_log_format_buffer_size("%s", msg.utf8
().data())]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp
, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf
, "%s", msg.utf8().data()), (uint32_t)sizeof(_os_fmt_buf)) clang
diagnostic pop ; }); } }) clang diagnostic pop
;
1014}
1015// rdar://168139740 is the task of doing a productionized Swift MESSAGE_CHECK
1016void messageCheckFailed(Ref<WebKit::WebProcessProxy> process)
1017{
1018 MESSAGE_CHECK_BASE(false, process->connection())do { if (!(false)) [[unlikely]] { os_fault_with_payload(31, 0
, nullptr, 0, makeString("/Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/Source/WebKit/UIProcess/WebBackForwardList.cpp"
" " "1018" ": Invalid message dispatched "_s, unsafeSpan(__PRETTY_FUNCTION__
)).utf8().data(), 0); IPC::markCurrentlyDispatchedMessageAsInvalid
(process->connection(), "Message check failed: " "false"_s
); if (IPC::Connection::shouldCrashOnMessageCheckFailure()) {
do { __asm__ volatile ("brk #0xc471"); __builtin_unreachable
(); } while (0); } { (void)0; } return; } } while (0)
;
1019}
1020
1021// Workarounds for rdar://171011011
1022void appendToBackForwardStateItems(Vector<WebKit::BackForwardListItemState>& items, const WebKit::WebBackForwardListItem& entry)
1023{
1024 items.append({ entry.copyMainFrameStateWithChildren(), entry.navigatedFrameID() });
1025}
1026
1027Ref<WebKit::WebBackForwardListItem> createItemFromState(const WebKit::BackForwardListItemState& itemState, WebKit::WebPageProxyIdentifier pageIdentifier)
1028{
1029 Ref stateCopy = itemState.frameState->copy();
1030 setBackForwardItemIdentifiers(stateCopy, WebCore::BackForwardItemIdentifier::generate());
1031 return WebKit::WebBackForwardListItem::create(WTF::move(stateCopy), pageIdentifier, itemState.navigatedFrameID);
1032}
1033
1034
1035#endif // ENABLE(BACK_FORWARD_LIST_SWIFT)