Bug Summary

File:Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/page/LocalFrameView.cpp
Warning:line 6050, column 5
A function 'isViewForDocumentInFrame' 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-ios26.2.0-simulator -O3 -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 UnifiedSource299.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/WebCore -target-linker-version 1230.1 -fcoverage-compilation-dir=/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore -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/WebCore/WebCore/normal/arm64/UnifiedSource299.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/WebCore.build/Release-iphonesimulator/WebCore.build/WebCore-generated-files.hmap -iquote /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release-iphonesimulator/WebCore.build/WebCore-project-headers.hmap -isystem /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/PAL/ThirdParty/dav1d/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 /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitLibraries/SDKs/iphonesimulator26.0-additions.sdk/System/Library/PrivateFrameworks -iframework /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/System/Library/Frameworks -include-pch /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/PrecompiledHeaders/SharedPrecompiledHeaders/11721353671419897710/WebCorePrefix.h.gch -D _LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE -D NDEBUG -D BUILDING_WEBKIT -D GL_SILENCE_DEPRECATION=1 -D GLES_SILENCE_DEPRECATION=1 -D __clang_analyzer__ -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release-iphonesimulator/WebCore.build/WebCore-own-target-headers.hmap -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release-iphonesimulator/WebCore.build/WebCore-all-target-headers.hmap -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/include -I PAL -I ForwardingHeaders -I /usr/include/libxslt -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/include/libxml2 -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/DerivedSources/WebCore -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/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 -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitLibraries/SDKs/iphonesimulator26.0-additions.sdk/usr/local/include -I /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator26.2.sdk/usr/local/include -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/WebKitBuild/PAL.build/PAL.build/DerivedSources -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/PAL.build/Release-iphonesimulator/PAL.build/DerivedSources -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release-iphonesimulator/WebCore.build/DerivedSources-normal/arm64 -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release-iphonesimulator/WebCore.build/DerivedSources/arm64 -I /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release-iphonesimulator/WebCore.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/WebKitBuild/Release-iphonesimulator -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 -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 -Wno-unknown-warning-option -Wliteral-conversion -Wthread-safety -Wno-profile-instr-out-of-date -Wno-profile-instr-unprofiled -Wno-backend-plugin -Wunsafe-buffer-usage -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/WebCore/WebCore/normal/arm64/UnifiedSource299.plist -x c++ /Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/WebKitBuild/Release-iphonesimulator/DerivedSources/WebCore/unified-sources/UnifiedSource299.cpp
1/*
2 * Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
3 * 1999 Lars Knoll <knoll@kde.org>
4 * 1999 Antti Koivisto <koivisto@kde.org>
5 * 2000 Dirk Mueller <mueller@kde.org>
6 * Copyright (C) 2004-2025 Apple Inc. All rights reserved.
7 * (C) 2006 Graham Dennis (graham.dennis@gmail.com)
8 * (C) 2006 Alexey Proskuryakov (ap@nypop.com)
9 * Copyright (C) 2009 Google Inc. All rights reserved.
10 *
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Library General Public
13 * License as published by the Free Software Foundation; either
14 * version 2 of the License, or (at your option) any later version.
15 *
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Library General Public License for more details.
20 *
21 * You should have received a copy of the GNU Library General Public License
22 * along with this library; see the file COPYING.LIB. If not, write to
23 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 * Boston, MA 02110-1301, USA.
25 */
26
27#include "config.h"
28#include "LocalFrameView.h"
29
30#include "AXObjectCache.h"
31#include "AccessibilityRegionContext.h"
32#include "AnchorPositionEvaluator.h"
33#include "BackForwardCache.h"
34#include "BackForwardController.h"
35#include "BorderValue.h"
36#include "CachedImage.h"
37#include "Chrome.h"
38#include "ChromeClient.h"
39#include "ColorBlending.h"
40#include "ContainerNodeInlines.h"
41#include "ContentVisibilityDocumentState.h"
42#include "DebugPageOverlays.h"
43#include "DocumentFullscreen.h"
44#include "DocumentInlines.h"
45#include "DocumentLoader.h"
46#include "DocumentMarkerController.h"
47#include "DocumentQuirks.h"
48#include "DocumentResourceLoader.h"
49#include "DocumentSVG.h"
50#include "DocumentView.h"
51#include "Editor.h"
52#include "EventHandler.h"
53#include "EventLoop.h"
54#include "EventTargetInlines.h"
55#include "EventNames.h"
56#include "FindRevealAlgorithms.h"
57#include "FixedContainerEdges.h"
58#include "FloatRect.h"
59#include "FocusController.h"
60#include "FragmentDirectiveParser.h"
61#include "FragmentDirectiveRangeFinder.h"
62#include "FragmentDirectiveUtilities.h"
63#include "FrameInlines.h"
64#include "FrameLoader.h"
65#include "FrameSelection.h"
66#include "FrameTree.h"
67#include "GraphicsContext.h"
68#include "HTMLBodyElement.h"
69#include "HTMLEmbedElement.h"
70#include "HTMLFrameElement.h"
71#include "HTMLFrameSetElement.h"
72#include "HTMLHtmlElement.h"
73#include "HTMLIFrameElement.h"
74#include "HTMLNames.h"
75#include "HTMLObjectElement.h"
76#include "HTMLPlugInElement.h"
77#include "HighlightRegistry.h"
78#include "ImageDocument.h"
79#include "InspectorBackendClient.h"
80#include "InspectorInstrumentation.h"
81#include "LegacyRenderSVGRoot.h"
82#include "LocalDOMWindow.h"
83#include "LocalFrame.h"
84#include "LocalFrameLoaderClient.h"
85#include "Logging.h"
86#include "MemoryCache.h"
87#include "NodeInlines.h"
88#include "NodeRenderStyle.h"
89#include "NullGraphicsContext.h"
90#include "Page.h"
91#include "PageColorSampler.h"
92#include "PageInspectorController.h"
93#include "PageOverlayController.h"
94#include "PerformanceLoggingClient.h"
95#include "ProgressTracker.h"
96#include "Quirks.h"
97#include "RenderAncestorIterator.h"
98#include "RenderBoxInlines.h"
99#include "RenderElementStyleInlines.h"
100#include "RenderEmbeddedObject.h"
101#include "RenderFlexibleBox.h"
102#include "RenderIFrame.h"
103#include "RenderInline.h"
104#include "RenderLayer.h"
105#include "RenderLayerBacking.h"
106#include "RenderLayerCompositor.h"
107#include "RenderLayerScrollableArea.h"
108#include "RenderListBox.h"
109#include "RenderObjectInlines.h"
110#include "RenderSVGRoot.h"
111#include "RenderScrollbar.h"
112#include "RenderScrollbarPart.h"
113#include "RenderStyle+SettersInlines.h"
114#include "RenderText.h"
115#include "RenderTheme.h"
116#include "RenderTreeAsText.h"
117#include "RenderView.h"
118#include "RenderWidgetInlines.h"
119#include "ResizeObserver.h"
120#include "SVGDocument.h"
121#include "SVGElementTypeHelpers.h"
122#include "SVGSVGElement.h"
123#include "ScriptDisallowedScope.h"
124#include "ScriptRunner.h"
125#include "ScriptedAnimationController.h"
126#include "ScrollAnchoringController.h"
127#include "ScrollAnimator.h"
128#include "ScrollSnapOffsetsInfo.h"
129#include "ScrollbarTheme.h"
130#include "ScrollbarsController.h"
131#include "ScrollingCoordinator.h"
132#include "Settings.h"
133#include "ShadowRoot.h"
134#include "SimpleRange.h"
135#include "StyleResolver.h"
136#include "StyleScope.h"
137#include "TextIndicator.h"
138#include "TextIterator.h"
139#include "TextResourceDecoder.h"
140#include "TiledBacking.h"
141#include "VelocityData.h"
142#include "VisualViewport.h"
143#include "WheelEventTestMonitor.h"
144#include <wtf/HexNumber.h>
145#include <wtf/MemoryPressureHandler.h>
146#include <wtf/Ref.h>
147#include <wtf/SetForScope.h>
148#include <wtf/SystemTracing.h>
149#include <wtf/TZoneMallocInlines.h>
150#include <wtf/text/MakeString.h>
151#include <wtf/text/TextStream.h>
152
153#if PLATFORM(IOS_FAMILY)(defined 1 && 1)
154#include "DocumentLoader.h"
155#include "LegacyTileCache.h"
156#endif
157
158#include "LayoutContext.h"
159
160#define PAGE_ID (m_frame->pageID() ? m_frame->pageID()->toUInt64() : 0)
161#define FRAME_ID m_frame->frameID().toUInt64()
162#define FRAMEVIEW_RELEASE_LOG(channel, fmt, ...)do { if (auto& client = logClient()) client->fmt(PAGE_ID
, FRAME_ID, m_frame->isMainFrame(), ...); else 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 = (Logchannel.
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
(MESSAGE_fmt), "format/label/description argument must be a string constant"
); __attribute__((section("__TEXT,__oslogstring,cstring_literals"
),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG47"
) = MESSAGE_fmt; uint8_t _Alignas(16) __attribute__((uninitialized
)) _os_fmt_buf[__builtin_os_log_format_buffer_size(MESSAGE_fmt
, WTF::safePrintfType(PAGE_ID) , WTF::safePrintfType(FRAME_ID
) , WTF::safePrintfType(m_frame->isMainFrame()) , WTF::safePrintfType
(...))]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp,
_os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf,
MESSAGE_fmt, WTF::safePrintfType(PAGE_ID) , WTF::safePrintfType
(FRAME_ID) , WTF::safePrintfType(m_frame->isMainFrame()) ,
WTF::safePrintfType(...)), (uint32_t)sizeof(_os_fmt_buf)) clang
diagnostic pop ; }); } }) clang diagnostic pop ; } while (0)
RELEASE_LOG_FORWARDABLE(channel, fmt, PAGE_ID, FRAME_ID, m_frame->isMainFrame(), ##__VA_ARGS__)do { if (auto& client = logClient()) client->fmt(PAGE_ID
, FRAME_ID, m_frame->isMainFrame(), ##__VA_ARGS__); else 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 = (Logchannel.
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
(MESSAGE_fmt), "format/label/description argument must be a string constant"
); __attribute__((section("__TEXT,__oslogstring,cstring_literals"
),internal_linkage)) static const char _os_fmt_str[] __asm("LOS_LOG48"
) = MESSAGE_fmt; uint8_t _Alignas(16) __attribute__((uninitialized
)) _os_fmt_buf[__builtin_os_log_format_buffer_size(MESSAGE_fmt
, WTF::safePrintfType(PAGE_ID) , WTF::safePrintfType(FRAME_ID
) , WTF::safePrintfType(m_frame->isMainFrame()) , WTF::safePrintfType
(##__VA_ARGS__))]; _os_log_impl(&__dso_handle, _log_tmp, _type_tmp
, _os_fmt_str, (uint8_t *)__builtin_os_log_format(_os_fmt_buf
, MESSAGE_fmt, WTF::safePrintfType(PAGE_ID) , WTF::safePrintfType
(FRAME_ID) , WTF::safePrintfType(m_frame->isMainFrame()) ,
WTF::safePrintfType(##__VA_ARGS__)), (uint32_t)sizeof(_os_fmt_buf
)) clang diagnostic pop ; }); } }) clang diagnostic pop ; } while
(0)
163
164namespace WebCore {
165
166using namespace HTMLNames;
167
168WTF_MAKE_TZONE_ALLOCATED_IMPL(LocalFrameView)::bmalloc::api::HeapRef LocalFrameView::s_heapRef; const TZoneSpecification
LocalFrameView::s_heapSpec = { &LocalFrameView::s_heapRef
, TZoneSpecification::encodeSize<LocalFrameView>(), TZoneSpecification
::encodeAlignment<LocalFrameView>(), TZoneSpecification
::encodeCategory<LocalFrameView>(), ::bmalloc::api::compactAllocationMode
<LocalFrameView>(), TZoneSpecification::encodeDescriptor
<LocalFrameView>(), }; void* LocalFrameView::operatorNewSlow
(size_t size) { if constexpr (::bmalloc::api::compactAllocationMode
<LocalFrameView>() == CompactAllocationMode::Compact) return
::bmalloc::api::tzoneAllocateCompactSlow(size, s_heapSpec); return
::bmalloc::api::tzoneAllocateNonCompactSlow(size, s_heapSpec
); } using __makeBtzoneMallocedInlineMacroSemicolonifier __attribute__
((unused)) = int
;
169
170MonotonicTime LocalFrameView::sCurrentPaintTimeStamp { };
171
172// The maximum number of updateEmbeddedObjects iterations that should be done before returning.
173static const unsigned maxUpdateEmbeddedObjectsIterations = 2;
174
175static constexpr unsigned defaultSignificantRenderedTextCharacterThreshold = 3000;
176static constexpr float defaultSignificantRenderedTextMeanLength = 50;
177static constexpr unsigned mainArticleSignificantRenderedTextCharacterThreshold = 1500;
178static constexpr float mainArticleSignificantRenderedTextMeanLength = 25;
179
180Pagination::Mode paginationModeForRenderStyle(const RenderStyle& style)
181{
182 auto overflow = style.overflowY();
183 if (overflow != Overflow::PagedX && overflow != Overflow::PagedY)
184 return Pagination::Mode::Unpaginated;
185
186 // paged-x always corresponds to LeftToRightPaginated or RightToLeftPaginated. If the WritingMode
187 // is horizontal, then we use TextDirection to choose between those options. If the WritingMode
188 // is vertical, then the block flow direction dictates the choice.
189 if (overflow == Overflow::PagedX) {
190 return style.writingMode().isAnyLeftToRight() ? Pagination::Mode::LeftToRightPaginated : Pagination::Mode::RightToLeftPaginated;
191 }
192
193 // paged-y always corresponds to TopToBottomPaginated or BottomToTopPaginated. If the WritingMode
194 // is horizontal, then the block flow direction dictates the choice. If the WritingMode
195 // is vertical, then we use TextDirection to choose between those options.
196 return style.writingMode().isAnyTopToBottom() ? Pagination::Mode::TopToBottomPaginated : Pagination::Mode::BottomToTopPaginated;
197}
198
199LocalFrameView::LocalFrameView(LocalFrame& frame)
200 : m_frame(frame)
201 , m_layoutContext(*this)
202 , m_updateEmbeddedObjectsTimer(*this, &LocalFrameView::updateEmbeddedObjectsTimerFired)
203 , m_updateWidgetPositionsTimer(*this, &LocalFrameView::updateWidgetPositionsTimerFired)
204 , m_delayedScrollEventTimer(*this, &LocalFrameView::scheduleScrollEvent)
205 , m_delayedScrollToFocusedElementTimer(*this, &LocalFrameView::scrollToFocusedElementTimerFired)
206 , m_speculativeTilingEnableTimer(*this, &LocalFrameView::speculativeTilingEnableTimerFired)
207 , m_delayedTextFragmentIndicatorTimer(*this, &LocalFrameView::textFragmentIndicatorTimerFired)
208 , m_scrollToTextFragmentRetryTimer(*this, &LocalFrameView::scrollToTextFragmentRetryTimerFired)
209 , m_mediaType(screenAtom())
210{
211 init();
212
213#if HAVE(RUBBER_BANDING)(defined HAVE_RUBBER_BANDING && HAVE_RUBBER_BANDING)
214 auto verticalElasticity = ScrollElasticity::None;
215 auto horizontalElasticity = ScrollElasticity::None;
216 if (m_frame->isMainFrame()) {
217 verticalElasticity = m_frame->page() ? m_frame->page()->verticalScrollElasticity() : ScrollElasticity::Allowed;
218 horizontalElasticity = m_frame->page() ? m_frame->page()->horizontalScrollElasticity() : ScrollElasticity::Allowed;
219 } else if (m_frame->settings().rubberBandingForSubScrollableRegionsEnabled()) {
220 verticalElasticity = ScrollElasticity::Automatic;
221 horizontalElasticity = ScrollElasticity::Automatic;
222 }
223
224 ScrollableArea::setVerticalScrollElasticity(verticalElasticity);
225 ScrollableArea::setHorizontalScrollElasticity(horizontalElasticity);
226#endif
227
228 if (m_frame->document() && m_frame->document()->settings().cssScrollAnchoringEnabled())
229 m_scrollAnchoringController = WTF::makeUnique<ScrollAnchoringController>(*this);
230}
231
232Ref<LocalFrameView> LocalFrameView::create(LocalFrame& frame)
233{
234 Ref<LocalFrameView> view = adoptRef(*new LocalFrameView(frame));
235 if (frame.page() && frame.page()->isVisible())
236 view->show();
237 return view;
238}
239
240Ref<LocalFrameView> LocalFrameView::create(LocalFrame& frame, const IntSize& initialSize)
241{
242 Ref<LocalFrameView> view = adoptRef(*new LocalFrameView(frame));
243 view->Widget::setFrameRect(IntRect(view->location(), initialSize));
244 if (frame.page() && frame.page()->isVisible())
245 view->show();
246 return view;
247}
248
249LocalFrameView::~LocalFrameView()
250{
251 removeFromAXObjectCache();
252 resetScrollbars();
253
254 // Custom scrollbars should already be destroyed at this point
255 ASSERT(!horizontalScrollbar() || !horizontalScrollbar()->isCustomScrollbar())((void)0);
256 ASSERT(!verticalScrollbar() || !verticalScrollbar()->isCustomScrollbar())((void)0);
257
258 setHasHorizontalScrollbar(false); // Remove native scrollbars now before we lose the connection to the HostWindow.
259 setHasVerticalScrollbar(false);
260
261 ASSERT(!m_scrollCorner)((void)0);
262
263 ASSERT(m_frame->view() != this || !m_frame->contentRenderer())((void)0);
264}
265
266void LocalFrameView::reset()
267{
268 m_cannotBlitToWindow = false;
269 m_isOverlapped = false;
270 m_contentIsOpaque = false;
271 m_updateEmbeddedObjectsTimer.stop();
272 m_lastUserScrollType = std::nullopt;
273 m_wasEverScrolledExplicitlyByUser = false;
274 m_wasEverScrolledExplicitlyByUserBelowTopEdge = false;
275 m_delayedScrollEventTimer.stop();
276 m_shouldScrollToFocusedElement = false;
277 m_delayedScrollToFocusedElementTimer.stop();
278 m_delayedTextFragmentIndicatorTimer.stop();
279 m_scrollToTextFragmentRetryTimer.stop();
280 m_scrollToTextFragmentInitialAttemptTime = std::nullopt;
281 m_pendingTextFragmentIndicatorRange.reset();
282 m_pendingTextFragmentIndicatorText = String();
283 m_haveCreatedTextIndicator = false;
284 m_lastViewportSize = IntSize();
285 m_lastUsedZoomFactor = 1.0f;
286 m_isTrackingRepaints = false;
287 m_trackedRepaintRects.clear();
288 m_lastPaintTime = MonotonicTime();
289 m_paintBehavior = PaintBehavior::Normal;
290 m_isPainting = false;
291 m_needsDeferredScrollbarsUpdate = false;
292 m_needsDeferredPositionScrollbarLayers = false;
293 m_maintainScrollPositionAnchor = nullptr;
294 resetLayoutMilestones();
295 layoutContext().reset();
296}
297
298void LocalFrameView::resetLayoutMilestones()
299{
300 m_firstLayoutCallbackPending = false;
301 m_firstVisuallyNonEmptyLayoutMilestoneIsPending = true;
302 m_contentQualifiesAsVisuallyNonEmpty = false;
303 m_hasReachedSignificantRenderedTextThreshold = false;
304 m_renderedSignificantAmountOfText = false;
305 m_visuallyNonEmptyCharacterCount = 0;
306 m_visuallyNonEmptyPixelCount = 0;
307 m_textRendererCountForVisuallyNonEmptyCharacters = 0;
308}
309
310void LocalFrameView::removeFromAXObjectCache()
311{
312 if (CheckedPtr cache = axObjectCache()) {
313 RefPtr owner = m_frame->ownerElement();
314 if (CheckedPtr renderer = owner ? owner->renderer() : nullptr)
315 cache->childrenChanged(*renderer);
316 cache->remove(*this);
317 }
318}
319
320void LocalFrameView::resetScrollbars()
321{
322 // FIXME: Do we really need this?
323 layoutContext().resetFirstLayoutFlag();
324 // Reset the document's scrollbars back to our defaults before we yield the floor.
325 setScrollbarsSuppressed(true);
326 if (m_canHaveScrollbars)
327 setScrollbarModes(ScrollbarMode::Auto, ScrollbarMode::Auto);
328 else
329 setScrollbarModes(ScrollbarMode::AlwaysOff, ScrollbarMode::AlwaysOff);
330 setScrollbarsSuppressed(false);
331}
332
333void LocalFrameView::resetScrollbarsAndClearContentsSize()
334{
335 resetScrollbars();
336
337 LOG(Layout, "LocalFrameView %p resetScrollbarsAndClearContentsSize", this)((void)0);
338
339 setScrollbarsSuppressed(true);
340 setContentsSize(IntSize());
341 setScrollbarsSuppressed(false);
342}
343
344void LocalFrameView::init()
345{
346 reset();
347
348 m_lastUsedSizeForLayout = { };
349
350 // Propagate the scrolling mode to the view.
351 if (m_frame->scrollingMode() == ScrollbarMode::AlwaysOff)
352 setCanHaveScrollbars(false);
353
354 RefPtr page = m_frame->page();
355 if (page && page->chrome().client().shouldPaintEntireContents())
356 setPaintsEntireContents(true);
357}
358
359void LocalFrameView::prepareForDetach()
360{
361 detachCustomScrollbars();
362 // When the view is no longer associated with a frame, it needs to be removed from the ax object cache
363 // right now, otherwise it won't be able to reach the topDocument()'s axObject cache later.
364 removeFromAXObjectCache();
365}
366
367void LocalFrameView::detachCustomScrollbars()
368{
369 RefPtr horizontalBar = horizontalScrollbar();
370 if (horizontalBar && horizontalBar->isCustomScrollbar())
371 setHasHorizontalScrollbar(false);
372
373 RefPtr verticalBar = verticalScrollbar();
374 if (verticalBar && verticalBar->isCustomScrollbar())
375 setHasVerticalScrollbar(false);
376
377 m_scrollCorner = nullptr;
378}
379
380void LocalFrameView::willBeDestroyed()
381{
382 setHasHorizontalScrollbar(false);
383 setHasVerticalScrollbar(false);
384 m_scrollCorner = nullptr;
385}
386
387void LocalFrameView::recalculateScrollbarOverlayStyle()
388{
389 auto style = [this] {
390 if (RefPtr page = m_frame->page()) {
391 if (auto clientStyle = page->chrome().client().preferredScrollbarOverlayStyle())
392 return *clientStyle;
393 }
394 auto background = documentBackgroundColor();
395 if (background.isVisible()) {
396 if (background.lightness() <= .5f)
397 return ScrollbarOverlayStyle::Light;
398 } else {
399 if (useDarkAppearance())
400 return ScrollbarOverlayStyle::Light;
401 }
402 return ScrollbarOverlayStyle::Default;
403 }();
404 if (scrollbarOverlayStyle() != style) {
405 setScrollbarOverlayStyle(style);
406 setNeedsCompositingGeometryUpdate();
407 // The scroll corner must be repainted to match the new scrollbar appearance.
408 invalidateScrollCorner(scrollCornerRect());
409 }
410}
411
412void LocalFrameView::clear()
413{
414 setCanBlitOnScroll(true);
415
416 reset();
417
418 setScrollbarsSuppressed(true);
419
420#if PLATFORM(IOS_FAMILY)(defined 1 && 1)
421 // To avoid flashes of white, disable tile updates immediately when view is cleared at the beginning of a page load.
422 // Tiling will be re-enabled from UIKit via [WAKWindow setTilingMode:] when we have content to draw.
423 if (LegacyTileCache* tileCache = legacyTileCache())
424 tileCache->setTilingMode(LegacyTileCache::Disabled);
425#endif
426}
427
428#if PLATFORM(IOS_FAMILY)(defined 1 && 1)
429void LocalFrameView::didReplaceMultipartContent()
430{
431 // Re-enable tile updates that were disabled in clear().
432 if (LegacyTileCache* tileCache = legacyTileCache())
433 tileCache->setTilingMode(LegacyTileCache::Normal);
434}
435#endif
436
437bool LocalFrameView::didFirstLayout() const
438{
439 return layoutContext().didFirstLayout();
440}
441
442void LocalFrameView::setFrameRect(const IntRect& newRect)
443{
444 Ref<LocalFrameView> protectedThis(*this);
445 IntRect oldRect = frameRect();
446 if (newRect == oldRect)
447 return;
448
449 // Every scroll that happens as the result of frame size change is programmatic.
450 auto scrollTypeScope = ScrollTypeScope(*this, ScrollType::Programmatic);
451 ScrollView::setFrameRect(newRect);
452
453 updateScrollableAreaSet();
454
455 if (CheckedPtr renderView = this->renderView()) {
456 if (renderView->usesCompositing())
457 renderView->compositor().frameViewDidChangeSize();
458 }
459
460 if (m_frame->isMainFrame() && m_frame->page())
461 m_frame->page()->pageOverlayController().didChangeViewSize();
462
463 if (RefPtr document = m_frame->document())
464 document->didChangeViewSize();
465
466 viewportContentsChanged();
467}
468
469void LocalFrameView::setCanHaveScrollbars(bool canHaveScrollbars)
470{
471 m_canHaveScrollbars = canHaveScrollbars;
472 ScrollView::setCanHaveScrollbars(canHaveScrollbars);
473}
474
475void LocalFrameView::updateCanHaveScrollbars()
476{
477 ScrollbarMode hMode;
478 ScrollbarMode vMode;
479 scrollbarModes(hMode, vMode);
480 if (hMode == ScrollbarMode::AlwaysOff && vMode == ScrollbarMode::AlwaysOff)
481 setCanHaveScrollbars(false);
482 else
483 setCanHaveScrollbars(true);
484}
485
486RefPtr<Element> LocalFrameView::rootElementForCustomScrollbarPartStyle() const
487{
488 // FIXME: We need to update the scrollbar dynamically as documents change (or as doc elements and bodies get discovered that have custom styles).
489 RefPtr document = m_frame->document();
490 if (!document)
491 return nullptr;
492
493 // Try the <body> element first as a scrollbar source.
494 RefPtr body = document->bodyOrFrameset();
495 // scrollbar-width on root element should override custom scrollbars declared on body element, so check that here..
496 if (body && body->renderer() && body->renderer()->style().usesLegacyScrollbarStyle() && scrollbarWidthStyle() == ScrollbarWidth::Auto)
497 return body;
498
499 // If the <body> didn't have a custom style, then the root element might.
500 RefPtr docElement = document->documentElement();
501 if (docElement && docElement->renderer() && docElement->renderer()->style().usesLegacyScrollbarStyle())
502 return docElement;
503
504 return nullptr;
505}
506
507Ref<Scrollbar> LocalFrameView::createScrollbar(ScrollbarOrientation orientation)
508{
509 if (auto element = rootElementForCustomScrollbarPartStyle())
510 return RenderScrollbar::createCustomScrollbar(*this, orientation, element.get());
511
512 // If we have an owning iframe/frame element, then it can set the custom scrollbar also.
513 // FIXME: Seems bad to do this for cross-origin frames.
514 RefPtr frameRenderer = m_frame->ownerRenderer();
515 if (frameRenderer && frameRenderer->style().usesLegacyScrollbarStyle())
516 return RenderScrollbar::createCustomScrollbar(*this, orientation, nullptr, m_frame.ptr());
517
518 // Nobody set a custom style, so we just use a native scrollbar.
519 return Scrollbar::createNativeScrollbar(*this, orientation, scrollbarWidthStyle());
520}
521
522void LocalFrameView::didRestoreFromBackForwardCache()
523{
524 // When restoring from back/forward cache, the main frame stays in place while subframes get swapped in.
525 // We update the scrollable area set to ensure that scrolling data structures get invalidated.
526 updateScrollableAreaSet();
527}
528
529void LocalFrameView::willDestroyRenderTree()
530{
531 detachCustomScrollbars();
532 layoutContext().clearSubtreeLayoutRoot();
533}
534
535void LocalFrameView::didDestroyRenderTree()
536{
537 ASSERT(!layoutContext().subtreeLayoutRoot())((void)0);
538 ASSERT(m_widgetsInRenderTree.isEmpty())((void)0);
539
540 ASSERT(!m_embeddedObjectsToUpdate || m_embeddedObjectsToUpdate->isEmpty())((void)0);
541
542 ASSERT(!m_viewportConstrainedObjects || m_viewportConstrainedObjects->isEmptyIgnoringNullReferences())((void)0);
543 ASSERT(!m_slowRepaintObjects || m_slowRepaintObjects->isEmptyIgnoringNullReferences())((void)0);
544}
545
546void LocalFrameView::setContentsSize(const IntSize& size)
547{
548 if (size == contentsSize())
549 return;
550
551 layoutContext().disableSetNeedsLayout();
552
553 ScrollView::setContentsSize(size);
554 contentsResized();
555
556 RefPtr page = m_frame->page();
557 if (!page)
558 return;
559
560 updateScrollableAreaSet();
561
562 page->chrome().contentsSizeChanged(m_frame.get(), size); // Notify only.
563
564 if (m_frame->isMainFrame()) {
565 page->pageOverlayController().didChangeDocumentSize();
566#if HAVE(RUBBER_BANDING)(defined HAVE_RUBBER_BANDING && HAVE_RUBBER_BANDING)
567 if (CheckedPtr renderView = this->renderView())
568 renderView->compositor().updateSizeAndPositionForTopOverhangColorExtensionLayer();
569#endif
570 BackForwardCache::singleton().markPagesForContentsSizeChanged(*page);
571 }
572 layoutContext().enableSetNeedsLayout();
573}
574
575void LocalFrameView::adjustViewSize()
576{
577 CheckedPtr renderView = this->renderView();
578 if (!renderView)
579 return;
580
581 ASSERT(m_frame->view() == this)((void)0);
582
583 const IntRect rect = renderView->documentRect();
584 const IntSize& size = rect.size();
585 ScrollView::setScrollOrigin(IntPoint(-rect.x(), -rect.y()), !m_frame->document()->printing(), size == contentsSize());
586
587 LOG_WITH_STREAM(Layout, stream << "LocalFrameView " << this << " adjustViewSize: unscaled document rect changed to " << renderView->unscaledDocumentRect() << " (scaled to " << size << ")")((void)0);
588
589 setContentsSize(size);
590}
591
592void LocalFrameView::applyOverflowToViewport(const RenderElement& renderer, ScrollbarMode& hMode, ScrollbarMode& vMode)
593{
594 // Handle the overflow:hidden/scroll case for the body/html elements. WinIE treats
595 // overflow:hidden and overflow:scroll on <body> as applying to the document's
596 // scrollbars. The CSS2.1 draft states that HTML UAs should use the <html> or <body> element and XML/XHTML UAs should
597 // use the root element.
598
599 // To combat the inability to scroll on a page with overflow:hidden on the root when scaled, disregard hidden when
600 // there is a frameScaleFactor that is greater than one on the main frame. Also disregard hidden if there is a
601 // header or footer.
602
603 bool overrideHidden = m_frame->isMainFrame() && ((m_frame->frameScaleFactor() > 1) || headerHeight() || footerHeight());
604
605 Overflow overflowX = renderer.style().overflowX();
606 Overflow overflowY = renderer.style().overflowY();
607
608 if (CheckedPtr renderSVGRoot = dynamicDowncast<RenderSVGRoot>(renderer)) {
609 // FIXME: evaluate if we can allow overflow for these cases too.
610 // Overflow is always hidden when stand-alone SVG documents are embedded.
611 if (renderSVGRoot->isEmbeddedThroughFrameContainingSVGDocument()) {
612 overflowX = Overflow::Hidden;
613 overflowY = Overflow::Hidden;
614 }
615 }
616
617 if (CheckedPtr legacyRenderSVGRoot = dynamicDowncast<LegacyRenderSVGRoot>(renderer)) {
618 // FIXME: evaluate if we can allow overflow for these cases too.
619 // Overflow is always hidden when stand-alone SVG documents are embedded.
620 if (legacyRenderSVGRoot->isEmbeddedThroughFrameContainingSVGDocument()) {
621 overflowX = Overflow::Hidden;
622 overflowY = Overflow::Hidden;
623 }
624 }
625
626 switch (overflowX) {
627 case Overflow::Hidden:
628 case Overflow::Clip:
629 if (overrideHidden)
630 hMode = ScrollbarMode::Auto;
631 else
632 hMode = ScrollbarMode::AlwaysOff;
633 break;
634 case Overflow::Scroll:
635 hMode = ScrollbarMode::AlwaysOn;
636 break;
637 case Overflow::Auto:
638 hMode = ScrollbarMode::Auto;
639 break;
640 default:
641 // Don't set it at all.
642 ;
643 }
644
645 switch (overflowY) {
646 case Overflow::Hidden:
647 case Overflow::Clip:
648 if (overrideHidden)
649 vMode = ScrollbarMode::Auto;
650 else
651 vMode = ScrollbarMode::AlwaysOff;
652 break;
653 case Overflow::Scroll:
654 vMode = ScrollbarMode::AlwaysOn;
655 break;
656 case Overflow::Auto:
657 vMode = ScrollbarMode::Auto;
658 break;
659 default:
660 // Don't set it at all. Values of Overflow::PagedX and Overflow::PagedY are handled by applyPaginationToViewPort().
661 ;
662 }
663}
664
665void LocalFrameView::applyPaginationToViewport()
666{
667 RefPtr document = m_frame->document();
668 RefPtr documentElement = document ? document->documentElement() : nullptr;
669 if (!documentElement || !documentElement->renderer()) {
670 setPagination(Pagination());
671 return;
672 }
673
674 CheckedRef documentRenderer = *documentElement->renderer();
675 CheckedPtr documentOrBodyRenderer = documentRenderer.ptr();
676
677 RefPtr body = document->body();
678 if (body && body->renderer()) {
679 documentOrBodyRenderer = documentRenderer->effectiveOverflowX() == Overflow::Visible && is<HTMLHtmlElement>(*documentElement) ?
680 body->renderer() : documentRenderer.ptr();
681 }
682
683 Pagination pagination;
684 Overflow overflowY = documentOrBodyRenderer->effectiveOverflowY();
685 if (overflowY == Overflow::PagedX || overflowY == Overflow::PagedY) {
686 pagination.mode = WebCore::paginationModeForRenderStyle(documentOrBodyRenderer->style());
687 auto columnGap = documentOrBodyRenderer->style().columnGap();
688 pagination.gap = 0;
689 if (!columnGap.isNormal()) {
690 CheckedPtr renderBox = dynamicDowncast<RenderBox>(documentOrBodyRenderer.get());
691 if (CheckedPtr containerForPaginationGap = renderBox ? renderBox : documentOrBodyRenderer->containingBlock())
692 pagination.gap = Style::evaluate<LayoutUnit>(columnGap, containerForPaginationGap->contentBoxLogicalWidth(), Style::ZoomNeeded { }).toUnsigned();
693 }
694 }
695 setPagination(pagination);
696}
697
698void LocalFrameView::calculateScrollbarModesForLayout(ScrollbarMode& hMode, ScrollbarMode& vMode, ScrollbarModesCalculationStrategy strategy)
699{
700 m_viewportRendererType = ViewportRendererType::None;
701
702 if (m_frame->scrollingMode() == ScrollbarMode::AlwaysOff) {
703 hMode = ScrollbarMode::AlwaysOff;
704 vMode = ScrollbarMode::AlwaysOff;
705 return;
706 }
707
708 if (m_canHaveScrollbars || strategy == RulesFromWebContentOnly) {
709 hMode = ScrollbarMode::Auto;
710 vMode = ScrollbarMode::Auto;
711 } else {
712 hMode = ScrollbarMode::AlwaysOff;
713 vMode = ScrollbarMode::AlwaysOff;
714 }
715
716 if (layoutContext().subtreeLayoutRoot())
717 return;
718
719 RefPtr document = m_frame->document();
720 if (!document)
721 return;
722
723 RefPtr documentElement = document->documentElement();
724 if (!documentElement)
725 return;
726
727 RefPtr bodyOrFrameset = document->bodyOrFrameset();
728 CheckedPtr rootRenderer = documentElement->renderer();
729 if (!bodyOrFrameset || !bodyOrFrameset->renderer()) {
730 if (rootRenderer) {
731 applyOverflowToViewport(*rootRenderer, hMode, vMode);
732 m_viewportRendererType = ViewportRendererType::Document;
733 }
734 return;
735 }
736
737 if (is<HTMLFrameSetElement>(*bodyOrFrameset)) {
738 vMode = ScrollbarMode::AlwaysOff;
739 hMode = ScrollbarMode::AlwaysOff;
740 return;
741 }
742
743 if (is<HTMLBodyElement>(*bodyOrFrameset) && rootRenderer) {
744 // It's sufficient to just check the X overflow,
745 // since it's illegal to have visible in only one direction.
746 if (rootRenderer->effectiveOverflowX() == Overflow::Visible && is<HTMLHtmlElement>(documentElement)) {
747 CheckedPtr bodyRenderer = bodyOrFrameset->renderer();
748 if (bodyRenderer) {
749 applyOverflowToViewport(*bodyRenderer, hMode, vMode);
750 m_viewportRendererType = ViewportRendererType::Body;
751 }
752 } else {
753 applyOverflowToViewport(*rootRenderer, hMode, vMode);
754 m_viewportRendererType = ViewportRendererType::Document;
755 }
756 }
757}
758
759void LocalFrameView::willRecalcStyle()
760{
761 CheckedPtr renderView = this->renderView();
762 if (!renderView)
763 return;
764
765 renderView->compositor().willRecalcStyle();
766}
767
768void LocalFrameView::styleAndRenderTreeDidChange()
769{
770 ScrollView::styleAndRenderTreeDidChange();
771 if (!renderView())
772 return;
773
774 checkAndDispatchDidReachVisuallyNonEmptyState();
775}
776
777void LocalFrameView::invalidateScrollbarsForAllScrollableAreas()
778{
779 if (!m_scrollableAreas)
780 return;
781
782 for (CheckedRef area : *m_scrollableAreas) {
783 CheckedPtr<ScrollableArea> scrollableArea(area);
784 scrollableArea->invalidateScrollbars();
785 }
786}
787
788GraphicsLayer* LocalFrameView::layerForHorizontalScrollbar() const
789{
790 CheckedPtr renderView = this->renderView();
791 if (!renderView)
792 return nullptr;
793 return renderView->compositor().layerForHorizontalScrollbar();
794}
795
796GraphicsLayer* LocalFrameView::layerForVerticalScrollbar() const
797{
798 CheckedPtr renderView = this->renderView();
799 if (!renderView)
800 return nullptr;
801 return renderView->compositor().layerForVerticalScrollbar();
802}
803
804GraphicsLayer* LocalFrameView::layerForScrollCorner() const
805{
806 CheckedPtr renderView = this->renderView();
807 if (!renderView)
808 return nullptr;
809 return renderView->compositor().layerForScrollCorner();
810}
811
812TiledBacking* LocalFrameView::tiledBacking() const
813{
814 CheckedPtr renderView = this->renderView();
815 if (!renderView)
816 return nullptr;
817
818 RenderLayerBacking* backing = renderView->layer()->backing();
819 if (!backing)
820 return nullptr;
821
822 return backing->tiledBacking();
823}
824
825std::optional<ScrollingNodeID> LocalFrameView::scrollingNodeID() const
826{
827 CheckedPtr renderView = this->renderView();
828 if (!renderView)
829 return std::nullopt;
830
831 RenderLayerBacking* backing = renderView->layer()->backing();
832 if (!backing)
833 return std::nullopt;
834
835 return backing->scrollingNodeIDForRole(ScrollCoordinationRole::Scrolling);
836}
837
838ScrollableArea* LocalFrameView::scrollableAreaForScrollingNodeID(ScrollingNodeID nodeID) const
839{
840 CheckedPtr renderView = this->renderView();
841 if (!renderView)
842 return nullptr;
843
844 if (auto area = m_scrollingNodeIDToPluginScrollableAreaMap.get(nodeID))
845 return area;
846
847 return renderView->compositor().scrollableAreaForScrollingNodeID(nodeID);
848}
849
850#if HAVE(RUBBER_BANDING)(defined HAVE_RUBBER_BANDING && HAVE_RUBBER_BANDING)
851GraphicsLayer* LocalFrameView::layerForOverhangAreas() const
852{
853 CheckedPtr renderView = this->renderView();
854 if (!renderView)
855 return nullptr;
856 return renderView->compositor().layerForOverhangAreas();
857}
858
859GraphicsLayer* LocalFrameView::setWantsLayerForTopOverhangColorExtension(bool wantsLayer) const
860{
861 CheckedPtr renderView = this->renderView();
862 if (!renderView)
863 return nullptr;
864
865 return renderView->compositor().updateLayerForTopOverhangColorExtension(wantsLayer);
866}
867
868GraphicsLayer* LocalFrameView::setWantsLayerForTopOverhangImage(bool wantsLayer) const
869{
870 CheckedPtr renderView = this->renderView();
871 if (!renderView)
872 return nullptr;
873
874 return renderView->compositor().updateLayerForTopOverhangImage(wantsLayer);
875}
876
877GraphicsLayer* LocalFrameView::setWantsLayerForBottomOverHangArea(bool wantsLayer) const
878{
879 CheckedPtr renderView = this->renderView();
880 if (!renderView)
881 return nullptr;
882
883 return renderView->compositor().updateLayerForBottomOverhangArea(wantsLayer);
884}
885
886#endif // HAVE(RUBBER_BANDING)
887
888void LocalFrameView::updateSnapOffsets()
889{
890 if (!m_frame->document())
891 return;
892
893 RefPtr documentElement = m_frame->document()->documentElement();
894 CheckedPtr rootRenderer = documentElement ? documentElement->renderBox() : nullptr;
895
896 const RenderStyle* styleToUse = nullptr;
897 if (rootRenderer && !rootRenderer->style().scrollSnapType().isNone())
898 styleToUse = &rootRenderer->style();
899
900 if (!styleToUse || !documentElement) {
901 clearSnapOffsets();
902 return;
903 }
904
905 // updateSnapOffsetsForScrollableArea calculates scroll offsets with all rectangles having their origin at the
906 // padding box rectangle of the scrollable element. Unlike for overflow:scroll, the FrameView viewport includes
907 // the root element margins. This means that we need to offset the viewport rectangle to make it relative to
908 // the padding box of the root element.
909 LayoutRect viewport = LayoutRect(IntPoint(), baseLayoutViewportSize());
910 viewport.move(-rootRenderer->marginLeft(), -rootRenderer->marginTop());
911
912 updateSnapOffsetsForScrollableArea(*this, *rootRenderer, *styleToUse, viewport, rootRenderer->style().writingMode(), protect(m_frame->document()->focusedElement()).get());
913}
914
915bool LocalFrameView::isScrollSnapInProgress() const
916{
917 if (scrollbarsSuppressed())
918 return false;
919
920 // If the scrolling thread updates the scroll position for this FrameView, then we should return
921 // ScrollingCoordinator::isScrollSnapInProgress().
922 if (RefPtr scrollingCoordinator = this->scrollingCoordinator()) {
923 if (scrollingCoordinator->isScrollSnapInProgress(scrollingNodeID()))
924 return true;
925 }
926
927 // If the main thread updates the scroll position for this FrameView, we should return
928 // ScrollAnimator::isScrollSnapInProgress().
929 if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
930 return scrollAnimator->isScrollSnapInProgress();
931
932 return false;
933}
934
935void LocalFrameView::updateScrollingCoordinatorScrollSnapProperties() const
936{
937 renderView()->compositor().updateScrollSnapPropertiesWithFrameView(*this);
938}
939
940bool LocalFrameView::flushCompositingStateForThisFrame(const LocalFrame& rootFrameForFlush)
941{
942 CheckedPtr renderView = this->renderView();
943 if (!renderView)
944 return true; // We don't want to keep trying to update layers if we have no renderer.
945
946 ASSERT(m_frame->view() == this)((void)0);
947
948 // If we sync compositing layers when a layout is pending, we may cause painting of compositing
949 // layer content to occur before layout has happened, which will cause paintContents() to bail.
950 if (needsLayout())
951 return false;
952
953#if PLATFORM(IOS_FAMILY)(defined 1 && 1)
954 if (LegacyTileCache* tileCache = legacyTileCache())
955 tileCache->doPendingRepaints();
956#endif
957
958 renderView->compositor().flushPendingLayerChanges(&rootFrameForFlush == m_frame.ptr());
959
960 return true;
961}
962
963void LocalFrameView::setNeedsOneShotDrawingSynchronization()
964{
965 if (RefPtr page = m_frame->page())
966 page->chrome().client().setNeedsOneShotDrawingSynchronization();
967}
968
969GraphicsLayer* LocalFrameView::graphicsLayerForPlatformWidget(PlatformWidget platformWidget)
970{
971 // To find the Widget that corresponds with platformWidget we have to do a linear
972 // search of our child widgets.
973 RefPtr<const Widget> foundWidget = nullptr;
974 for (auto& widget : children()) {
975 if (widget->platformWidget() != platformWidget)
976 continue;
977 foundWidget = widget.ptr();
978 break;
979 }
980
981 if (!foundWidget)
982 return nullptr;
983
984 RefPtr renderWidget = RenderWidget::find(*foundWidget);
985 if (!renderWidget)
986 return nullptr;
987
988 CheckedPtr widgetLayer = renderWidget->layer();
989 if (!widgetLayer || !widgetLayer->isComposited())
990 return nullptr;
991
992 return widgetLayer->backing()->parentForSublayers();
993}
994
995GraphicsLayer* LocalFrameView::graphicsLayerForPageScale()
996{
997 RefPtr page = m_frame->page();
998 if (!page)
999 return nullptr;
1000
1001 if (page->delegatesScaling()) {
1002 ASSERT_NOT_REACHED()((void)0);
1003 return nullptr;
1004 }
1005
1006 CheckedPtr renderView = this->renderView();
1007 if (!renderView)
1008 return nullptr;
1009
1010 if (!renderView->hasLayer() || !renderView->layer()->isComposited())
1011 return nullptr;
1012
1013 auto* backing = renderView->layer()->backing();
1014 if (auto* contentsContainmentLayer = backing->contentsContainmentLayer())
1015 return contentsContainmentLayer;
1016
1017 return backing->graphicsLayer();
1018}
1019
1020GraphicsLayer* LocalFrameView::graphicsLayerForScrolledContents()
1021{
1022 if (auto* renderView = m_frame->contentRenderer())
1023 return renderView->compositor().scrolledContentsLayer();
1024 return nullptr;
1025}
1026
1027GraphicsLayer* LocalFrameView::clipLayer() const
1028{
1029 if (CheckedPtr renderView = m_frame->contentRenderer())
1030 return renderView->compositor().clipLayer();
1031 return nullptr;
1032}
1033
1034#if HAVE(RUBBER_BANDING)(defined HAVE_RUBBER_BANDING && HAVE_RUBBER_BANDING)
1035GraphicsLayer* LocalFrameView::graphicsLayerForTransientZoomShadow()
1036{
1037 RefPtr page = m_frame->page();
1038 if (!page)
1039 return nullptr;
1040
1041 if (page->delegatesScaling()) {
1042 ASSERT_NOT_REACHED()((void)0);
1043 return nullptr;
1044 }
1045
1046 CheckedPtr renderView = this->renderView();
1047 if (!renderView)
1048 return nullptr;
1049
1050 return renderView->compositor().layerForContentShadow();
1051}
1052#endif
1053
1054LayoutRect LocalFrameView::fixedScrollableAreaBoundsInflatedForScrolling(const LayoutRect& uninflatedBounds) const
1055{
1056 LayoutPoint scrollPosition;
1057 LayoutSize topLeftExpansion;
1058 LayoutSize bottomRightExpansion;
1059
1060 if (m_frame->settings().visualViewportEnabled()) {
1061 // FIXME: this is wrong under zooming; uninflatedBounds is scaled but the scroll positions are not.
1062 scrollPosition = layoutViewportRect().location();
1063 topLeftExpansion = scrollPosition - unscaledMinimumScrollPosition();
1064 bottomRightExpansion = unscaledMaximumScrollPosition() - scrollPosition;
1065 } else {
1066 scrollPosition = scrollPositionRespectingCustomFixedPosition();
1067 topLeftExpansion = scrollPosition - minimumScrollPosition();
1068 bottomRightExpansion = maximumScrollPosition() - scrollPosition;
1069 }
1070
1071 return LayoutRect(uninflatedBounds.location() - topLeftExpansion, uninflatedBounds.size() + topLeftExpansion + bottomRightExpansion);
1072}
1073
1074LayoutPoint LocalFrameView::scrollPositionRespectingCustomFixedPosition() const
1075{
1076#if PLATFORM(IOS_FAMILY)(defined 1 && 1)
1077 if (!m_frame->settings().visualViewportEnabled())
1078 return useCustomFixedPositionLayoutRect() ? customFixedPositionLayoutRect().location() : scrollPosition();
1079#endif
1080
1081 return scrollPositionForFixedPosition();
1082}
1083
1084void LocalFrameView::clearObscuredInsetsAdjustmentsIfNeeded()
1085{
1086 if (CheckedPtr tiledBacking = this->tiledBacking())
1087 tiledBacking->clearObscuredInsetsAdjustments();
1088}
1089
1090void LocalFrameView::obscuredInsetsWillChange(FloatBoxExtent&& obscuredInsetsDelta)
1091{
1092 if (CheckedPtr tiledBacking = this->tiledBacking())
1093 tiledBacking->obscuredInsetsWillChange(WTF::move(obscuredInsetsDelta));
1094}
1095
1096void LocalFrameView::obscuredContentInsetsDidChange(const FloatBoxExtent& newObscuredContentInsets)
1097{
1098 CheckedPtr renderView = this->renderView();
1099 if (!renderView)
1100 return;
1101
1102 if (platformWidget())
1103 platformSetContentInsets(newObscuredContentInsets);
1104
1105 if (RefPtr document = m_frame->document())
1106 document->updateViewportUnitsOnResize();
1107
1108 renderView->setNeedsLayout();
1109 layoutContext().layout();
1110
1111 {
1112 // Every scroll that happens as the result of content inset change is programmatic.
1113 auto scrollTypeScope = ScrollTypeScope(*this, ScrollType::Programmatic);
1114
1115 updateScrollbars(scrollPosition());
1116 if (renderView->usesCompositing())
1117 renderView->compositor().frameViewDidChangeSize();
1118
1119 if (CheckedPtr tiledBacking = this->tiledBacking())
1120 tiledBacking->setObscuredContentInsets(newObscuredContentInsets);
1121 }
1122
1123 if (RefPtr page = m_frame->page())
1124 page->chrome().client().setNeedsFixedContainerEdgesUpdate();
1125}
1126
1127void LocalFrameView::topContentDirectionDidChange()
1128{
1129 m_needsDeferredScrollbarsUpdate = true;
1130 m_needsDeferredPositionScrollbarLayers = true;
1131}
1132
1133void LocalFrameView::handleDeferredScrollbarsUpdate()
1134{
1135 if (!m_needsDeferredScrollbarsUpdate)
1136 return;
1137
1138 m_needsDeferredScrollbarsUpdate = false;
1139 updateScrollbars(scrollPosition());
1140}
1141
1142void LocalFrameView::handleDeferredPositionScrollbarLayers()
1143{
1144 if (!m_needsDeferredPositionScrollbarLayers)
1145 return;
1146
1147 m_needsDeferredPositionScrollbarLayers = false;
1148 positionScrollbarLayers();
1149}
1150
1151// Sometimes (for plug-ins) we need to eagerly go into compositing mode.
1152void LocalFrameView::enterCompositingMode()
1153{
1154 if (CheckedPtr renderView = this->renderView()) {
1155 renderView->compositor().enableCompositingMode();
1156 if (!needsLayout())
1157 renderView->compositor().scheduleCompositingLayerUpdate();
1158 }
1159}
1160
1161bool LocalFrameView::isEnclosedInCompositingLayer() const
1162{
1163 RefPtr frameOwnerRenderer = m_frame->ownerRenderer();
1164 if (frameOwnerRenderer && frameOwnerRenderer->containerForRepaint().renderer)
1165 return true;
1166
1167 if (RefPtr parentView = parentFrameView())
1168 return parentView->isEnclosedInCompositingLayer();
1169 return false;
1170}
1171
1172bool LocalFrameView::flushCompositingStateIncludingSubframes()
1173{
1174 bool allFramesFlushed = flushCompositingStateForThisFrame(m_frame.get());
1175
1176 for (RefPtr child = m_frame->tree().firstRenderedChild(); child; child = child->tree().traverseNextRendered(m_frame.ptr())) {
1177 RefPtr localChild = dynamicDowncast<LocalFrame>(child);
1178 if (!localChild)
1179 continue;
1180 RefPtr frameView = localChild->view();
1181 if (!frameView)
1182 continue;
1183 bool flushed = frameView->flushCompositingStateForThisFrame(m_frame.get());
1184 allFramesFlushed &= flushed;
1185 }
1186 return allFramesFlushed;
1187}
1188
1189bool LocalFrameView::isSoftwareRenderable() const
1190{
1191 CheckedPtr renderView = this->renderView();
1192 return !renderView || !renderView->compositor().has3DContent();
1193}
1194
1195void LocalFrameView::setIsInWindow(bool isInWindow)
1196{
1197 if (CheckedPtr renderView = this->renderView())
1198 renderView->setIsInWindow(isInWindow);
1199}
1200
1201void LocalFrameView::forceLayoutParentViewIfNeeded()
1202{
1203 RefPtr ownerRenderer = m_frame->ownerRenderer();
1204 if (!ownerRenderer)
1205 return;
1206
1207 CheckedPtr contentBox = embeddedContentBox();
1208 if (!contentBox)
1209 return;
1210
1211 if (auto* svgRoot = dynamicDowncast<LegacyRenderSVGRoot>(contentBox.get())) {
1212 if (svgRoot->everHadLayout() && !svgRoot->needsLayout())
1213 return;
1214 }
1215
1216 if (auto* svgRoot = dynamicDowncast<RenderSVGRoot>(contentBox.get())) {
1217 if (svgRoot->everHadLayout() && !svgRoot->needsLayout())
1218 return;
1219 }
1220
1221 LOG(Layout, "LocalFrameView %p forceLayoutParentViewIfNeeded scheduling layout on parent LocalFrameView %p", this, &ownerRenderer->view().frameView())((void)0);
1222
1223 // If the embedded SVG document appears the first time, the ownerRenderer has already finished
1224 // layout without knowing about the existence of the embedded SVG document, because RenderReplaced
1225 // embeddedContentBox() returns nullptr, as long as the embedded document isn't loaded yet. Before
1226 // bothering to lay out the SVG document, mark the ownerRenderer needing layout and ask its
1227 // LocalFrameView for a layout. After that the RenderEmbeddedObject (ownerRenderer) carries the
1228 // correct size, which LegacyRenderSVGRoot::computeReplacedLogicalWidth/Height rely on, when laying
1229 // out for the first time, or when the LegacyRenderSVGRoot size has changed dynamically (eg. via <script>).
1230
1231 ownerRenderer->setNeedsLayoutAndPreferredWidthsUpdate();
1232 ownerRenderer->view().frameView().layoutContext().scheduleLayout();
1233}
1234
1235void LocalFrameView::markRootOrBodyRendererDirty() const
1236{
1237 Ref document = *m_frame->document();
1238 CheckedPtr rootRenderer = document->documentElement() ? document->documentElement()->renderBox() : nullptr;
1239 RefPtr body = document->bodyOrFrameset();
1240 CheckedPtr bodyRenderer = rootRenderer && body ? body->renderBox() : nullptr;
1241 if (bodyRenderer && bodyRenderer->stretchesToViewport())
1242 bodyRenderer->setChildNeedsLayout();
1243 else if (rootRenderer && rootRenderer->stretchesToViewport())
1244 rootRenderer->setChildNeedsLayout();
1245}
1246
1247void LocalFrameView::adjustScrollbarsForLayout(bool isFirstLayout)
1248{
1249 ScrollbarMode hMode;
1250 ScrollbarMode vMode;
1251 calculateScrollbarModesForLayout(hMode, vMode);
1252
1253 LOG_WITH_STREAM(Layout, stream << "LocalFrameView " << this << " adjustScrollbarsForLayout - first layout " << isFirstLayout << " horizontal scrollbar mode:" << hMode << " vertical scrollbar mode:" << vMode)((void)0);
1254
1255 if (isFirstLayout && !layoutContext().isLayoutNested()) {
1256 setScrollbarsSuppressed(true);
1257 // Set the initial vMode to AlwaysOn if we're auto.
1258 if (vMode == ScrollbarMode::Auto)
1259 setVerticalScrollbarMode(ScrollbarMode::AlwaysOn); // This causes a vertical scrollbar to appear.
1260 // Set the initial hMode to AlwaysOff if we're auto.
1261 if (hMode == ScrollbarMode::Auto)
1262 setHorizontalScrollbarMode(ScrollbarMode::AlwaysOff); // This causes a horizontal scrollbar to disappear.
1263 ASSERT(m_frame->page())((void)0);
1264 if (m_frame->page()->isMonitoringWheelEvents())
1265 scrollAnimator().setWheelEventTestMonitor(m_frame->page()->wheelEventTestMonitor());
1266 setScrollbarModes(hMode, vMode);
1267 setScrollbarsSuppressed(false, true);
1268 } else if (hMode != horizontalScrollbarMode() || vMode != verticalScrollbarMode())
1269 setScrollbarModes(hMode, vMode);
1270}
1271
1272void LocalFrameView::willDoLayout(SingleThreadWeakPtr<RenderElement> layoutRoot)
1273{
1274 if (!m_frame->document()->isInStyleInterleavedLayout() && !layoutContext().isLayoutNested())
1275 updateScrollAnchoringBeforeLayoutForScrollableAreas();
1276
1277 bool subtreeLayout = !is<RenderView>(*layoutRoot);
1278 if (subtreeLayout)
1279 return;
1280
1281 if (RefPtr body = m_frame->document()->bodyOrFrameset()) {
1282 if (is<HTMLFrameSetElement>(*body) && body->renderer())
1283 body->renderer()->setChildNeedsLayout();
1284 }
1285 auto firstLayout = !layoutContext().didFirstLayout();
1286 if (firstLayout) {
1287 m_lastViewportSize = sizeForResizeEvent();
1288 m_lastUsedZoomFactor = layoutRoot->style().usedZoom();
1289 m_firstLayoutCallbackPending = true;
1290 }
1291 adjustScrollbarsForLayout(firstLayout);
1292
1293 auto oldSize = m_lastUsedSizeForLayout;
1294 auto newSize = layoutSize();
1295 if (oldSize != newSize) {
1296 m_lastUsedSizeForLayout = newSize;
1297 LOG_WITH_STREAM(Layout, stream << " size for layout changed from " << oldSize << " to " << newSize)((void)0);
1298 layoutContext().setNeedsFullRepaint();
1299 if (!firstLayout)
1300 markRootOrBodyRendererDirty();
1301 }
1302 forceLayoutParentViewIfNeeded();
1303}
1304
1305void LocalFrameView::didLayout(SingleThreadWeakPtr<RenderElement> layoutRoot, bool canDeferUpdateLayerPositions)
1306{
1307 ScriptDisallowedScope::InMainThread scriptDisallowedScope;
1308
1309 layoutContext().didLayout(canDeferUpdateLayerPositions);
1310
1311 Ref document = *m_frame->document();
1312
1313#if PLATFORM(COCOA)(defined 1 && 1) || PLATFORM(WIN)(defined WTF_PLATFORM_WIN && WTF_PLATFORM_WIN) || PLATFORM(GTK)(defined WTF_PLATFORM_GTK && WTF_PLATFORM_GTK)
1314 if (CheckedPtr cache = document->existingAXObjectCache())
1315 cache->onLayoutComplete(*layoutRoot.get());
1316#else
1317 UNUSED_PARAM(layoutRoot)(void)layoutRoot;
1318#endif
1319
1320 m_frame->invalidateContentEventRegionsIfNeeded(LocalFrame::InvalidateContentEventRegionsReason::Layout);
1321 document->invalidateRenderingDependentRegions();
1322
1323 updateCanBlitOnScrollRecursively();
1324
1325 updateScrollGeometryContentSize();
1326
1327 handleDeferredScrollUpdateAfterContentSizeChange();
1328
1329 handleDeferredScrollbarsUpdate();
1330 handleDeferredPositionScrollbarLayers();
1331
1332 if (CheckedPtr markers = document->markersIfExists())
1333 markers->invalidateRectsForAllMarkers();
1334}
1335
1336void LocalFrameView::updateScrollGeometryContentSize()
1337{
1338 if (!m_frame->isMainFrame())
1339 return;
1340
1341 RefPtr page = m_frame->page();
1342 if (!page || !page->chrome().client().needsScrollGeometryUpdates())
1343 return;
1344
1345 RefPtr document = m_frame->document();
1346 if (!document)
1347 return;
1348
1349 IntSize scrollGeometryContentSize;
1350 bool heightDeterminingBoxIsPositioned = false;
1351 bool hasViewportConstrainedHeight = false;
1352
1353 auto updateScrollGeometryContentSizeWithBoxAndPosition = [&scrollGeometryContentSize, &heightDeterminingBoxIsPositioned](const RenderBox& box, IntPoint position) {
1354 scrollGeometryContentSize.setWidth(std::max(scrollGeometryContentSize.width(), position.x()));
1355
1356 auto oldHeight = scrollGeometryContentSize.height();
1357 scrollGeometryContentSize.setHeight(std::max(scrollGeometryContentSize.height(), position.y()));
1358
1359 if (oldHeight != scrollGeometryContentSize.height())
1360 heightDeterminingBoxIsPositioned = box.isPositioned();
1361 };
1362
1363 auto maxPositionForBox = [](const RenderBox& box) {
1364 auto rect = snappedIntRect(box.borderBoxRect());
1365
1366 auto borderBoxRectMaxCorner = rect.maxXMaxYCorner();
1367
1368 auto layoutOverflowRect = snappedIntRect(box.layoutOverflowRect());
1369 auto layoutOverflowRectMaxCorner = layoutOverflowRect.maxXMaxYCorner();
1370
1371 if (box.effectiveOverflowX() == Overflow::Visible && layoutOverflowRectMaxCorner.x() > borderBoxRectMaxCorner.x())
1372 rect.setWidth(rect.width() + layoutOverflowRectMaxCorner.x() - borderBoxRectMaxCorner.x());
1373
1374 if (box.effectiveOverflowY() == Overflow::Visible && layoutOverflowRectMaxCorner.y() > borderBoxRectMaxCorner.y())
1375 rect.setHeight(rect.height() + layoutOverflowRectMaxCorner.y() - borderBoxRectMaxCorner.y());
1376
1377 auto point = roundedIntPoint(box.localToAbsolute(rect.location()));
1378 rect.setLocation(point);
1379
1380 return rect.maxXMaxYCorner();
1381 };
1382
1383 IntSize documentSize = layoutSize();
1384 CheckedPtr<RenderBox> documentRenderer;
1385
1386 IntSize bodySize;
1387 CheckedPtr<RenderBox> bodyRenderer;
1388
1389 auto adjustMaxPositionForBodyOrDocument = [&](RenderBox& box, IntPoint& maxPosition) {
1390 if (box.isBody()) {
1391 bodySize = { maxPosition.x(), maxPosition.y() };
1392 bodyRenderer = &box;
1393 } else
1394 documentRenderer = &box;
1395
1396 CheckedRef style = box.style();
1397
1398 if (style->width().isAuto())
1399 maxPosition.setX(0);
1400
1401 if (style->height().isAuto())
1402 maxPosition.setY(0);
1403 };
1404
1405 auto determineScrollGeometryForRendererWithNonVisibleOverflow = [&](const RenderBox& renderer) {
1406 CheckedPtr descendant = renderer.firstChild();
1407 CheckedPtr stop = renderer.nextInPreOrderAfterChildren();
1408
1409 while (descendant && descendant != stop) {
1410 CheckedPtr descendantBox = dynamicDowncast<RenderBox>(*descendant);
1411 if (!descendantBox) {
1412 descendant = descendant->nextInPreOrder();
1413 continue;
1414 }
1415
1416 if (!descendantBox->isOutOfFlowPositioned()) {
1417 descendant = descendant->nextInPreOrder();
1418 continue;
1419 }
1420
1421 auto position = maxPositionForBox(*descendantBox);
1422 updateScrollGeometryContentSizeWithBoxAndPosition(*descendantBox, position);
1423 descendant = descendant->nextInPreOrderAfterChildren();
1424 }
1425 };
1426
1427 auto constrainScrollGeometryContentSizeToViewportSizeIfNeeded = [&] {
1428 auto viewportContrainedSize = scrollGeometryContentSize;
1429
1430 if (documentRenderer->effectiveOverflowX() != Overflow::Visible) {
1431 if (bodyRenderer->effectiveOverflowX() != Overflow::Visible)
1432 viewportContrainedSize.setWidth(bodySize.width());
1433 else if (documentRenderer->effectiveOverflowX() == Overflow::Clip || documentRenderer->effectiveOverflowX() == Overflow::Hidden)
1434 viewportContrainedSize.setWidth(documentSize.width());
1435 }
1436
1437 if (documentRenderer->effectiveOverflowY() != Overflow::Visible) {
1438 if (bodyRenderer->effectiveOverflowY() != Overflow::Visible)
1439 viewportContrainedSize.setHeight(bodySize.height());
1440 else if (documentRenderer->effectiveOverflowY() == Overflow::Clip || documentRenderer->effectiveOverflowY() == Overflow::Hidden)
1441 viewportContrainedSize.setHeight(documentSize.height());
1442 }
1443
1444 hasViewportConstrainedHeight = scrollGeometryContentSize.height() != viewportContrainedSize.height();
1445
1446 scrollGeometryContentSize.setWidth(std::min(scrollGeometryContentSize.width(), viewportContrainedSize.width()));
1447 scrollGeometryContentSize.setHeight(std::min(scrollGeometryContentSize.height(), viewportContrainedSize.height()));
1448 };
1449
1450 auto applyBodyMarginToScrollGeometryContentSizeIfNeeded = [&] {
1451 if (!hasViewportConstrainedHeight && !scrollGeometryContentSize.isZero() && scrollGeometryContentSize.height() <= bodySize.height() && !heightDeterminingBoxIsPositioned)
1452 scrollGeometryContentSize.setHeight(scrollGeometryContentSize.height() + bodyRenderer->collapsedMarginAfter());
1453 };
1454
1455 // Determine the minimum view size needed such that no scrolling is necessary to reach
1456 // any content.
1457 //
1458 // This is achieved by traversing the render tree and determining the bottom right corner
1459 // of each renderer, after accounting for overflow.
1460
1461 CheckedPtr renderer = renderView()->firstChild();
1462 while (renderer) {
1463 CheckedPtr box = dynamicDowncast<RenderBox>(*renderer);
1464 if (!box) {
1465 renderer = renderer->nextInPreOrder();
1466 continue;
1467 }
1468
1469 auto maxPosition = maxPositionForBox(*box);
1470
1471 // The body and document renderers are special since their height is sized to fit
1472 // the view in quirks mode. In this case, their size should not determine the
1473 // scroll geometry content size, which could be smaller.
1474 bool isBodyOrDocumentRenderer = box->isBody() || box->isDocumentElementRenderer();
1475 if (isBodyOrDocumentRenderer)
1476 adjustMaxPositionForBodyOrDocument(*box, maxPosition);
1477
1478 updateScrollGeometryContentSizeWithBoxAndPosition(*box, maxPosition);
1479
1480 if (!isBodyOrDocumentRenderer) {
1481 // The children of positioned elements may be skipped, since the renderer itself
1482 // already has the necessary geometry information.
1483 if (box->isPositioned()) {
1484 renderer = renderer->nextInPreOrderAfterChildren();
1485 continue;
1486 }
1487
1488 // Only out-of-flow positioned children need to be consulted if the current
1489 // renderer has non-visible overflow. In-flow children would contribute to
1490 // the current renderer's geometry information and may be ignored.
1491 bool hasNonVisibleOverflow = isNonVisibleOverflow(box->effectiveOverflowX()) || isNonVisibleOverflow(box->effectiveOverflowY());
1492 if (hasNonVisibleOverflow) {
1493 determineScrollGeometryForRendererWithNonVisibleOverflow(*box);
1494 renderer = renderer->nextInPreOrderAfterChildren();
1495 continue;
1496 }
1497 }
1498
1499 renderer = renderer->nextInPreOrder();
1500 }
1501
1502 constrainScrollGeometryContentSizeToViewportSizeIfNeeded();
1503
1504 applyBodyMarginToScrollGeometryContentSizeIfNeeded();
1505
1506 m_scrollGeometryContentSize = scrollGeometryContentSize;
1507}
1508
1509bool LocalFrameView::shouldDeferScrollUpdateAfterContentSizeChange()
1510{
1511 return (layoutContext().layoutPhase() < LocalFrameViewLayoutContext::LayoutPhase::InPostLayout) && (layoutContext().layoutPhase() != LocalFrameViewLayoutContext::LayoutPhase::OutsideLayout);
1512}
1513
1514RenderBox* LocalFrameView::embeddedContentBox() const
1515{
1516 CheckedPtr renderView = this->renderView();
1517 if (!renderView)
1518 return nullptr;
1519
1520 RenderObject* firstChild = renderView->firstChild();
1521
1522 // Curently only embedded SVG documents participate in the size-negotiation logic.
1523 if (auto* svgRoot = dynamicDowncast<LegacyRenderSVGRoot>(firstChild))
1524 return svgRoot;
1525
1526 if (auto* svgRoot = dynamicDowncast<RenderSVGRoot>(firstChild))
1527 return svgRoot;
1528
1529 return nullptr;
1530}
1531
1532void LocalFrameView::addEmbeddedObjectToUpdate(RenderEmbeddedObject& embeddedObject)
1533{
1534 if (!m_embeddedObjectsToUpdate)
1535 m_embeddedObjectsToUpdate = makeUnique<ListHashSet<SingleThreadWeakRef<RenderEmbeddedObject>>>();
1536
1537 Ref element = embeddedObject.frameOwnerElement();
1538 if (RefPtr embedOrObject = dynamicDowncast<HTMLPlugInElement>(element.ptr()))
1539 embedOrObject->setNeedsWidgetUpdate(true);
1540
1541 m_embeddedObjectsToUpdate->add(embeddedObject);
1542}
1543
1544void LocalFrameView::removeEmbeddedObjectToUpdate(RenderEmbeddedObject& embeddedObject)
1545{
1546 if (!m_embeddedObjectsToUpdate)
1547 return;
1548
1549 m_embeddedObjectsToUpdate->remove(&embeddedObject);
1550}
1551
1552void LocalFrameView::setMediaType(const AtomString& mediaType)
1553{
1554 m_mediaType = mediaType;
1555}
1556
1557AtomString LocalFrameView::mediaType() const
1558{
1559 // See if we have an override type.
1560 auto overrideType = m_frame->loader().client().overrideMediaType();
1561 InspectorInstrumentation::applyEmulatedMedia(m_frame.get(), overrideType);
1562 if (!overrideType.isNull())
1563 return overrideType;
1564 return m_mediaType;
1565}
1566
1567void LocalFrameView::adjustMediaTypeForPrinting(bool printing)
1568{
1569 if (printing) {
1570 if (m_mediaTypeWhenNotPrinting.isNull())
1571 m_mediaTypeWhenNotPrinting = mediaType();
1572 setMediaType(printAtom());
1573 } else {
1574 if (!m_mediaTypeWhenNotPrinting.isNull())
1575 setMediaType(m_mediaTypeWhenNotPrinting);
1576 m_mediaTypeWhenNotPrinting = nullAtom();
1577 }
1578}
1579
1580bool LocalFrameView::useSlowRepaints(bool considerOverlap) const
1581{
1582 bool mustBeSlow = hasSlowRepaintObjects() || (platformWidget() && hasViewportConstrainedObjects());
1583
1584 // FIXME: WidgetMac.mm makes the assumption that useSlowRepaints ==
1585 // m_contentIsOpaque, so don't take the fast path for composited layers
1586 // if they are a platform widget in order to get painting correctness
1587 // for transparent layers. See the comment in WidgetMac::paint.
1588 if (usesCompositedScrolling() && !platformWidget())
1589 return mustBeSlow;
1590
1591 bool isOverlapped = m_isOverlapped && considerOverlap;
1592
1593 if (mustBeSlow || m_cannotBlitToWindow || isOverlapped || !m_contentIsOpaque)
1594 return true;
1595
1596 if (RefPtr parentView = parentFrameView())
1597 return parentView->useSlowRepaints(considerOverlap);
1598
1599 return false;
1600}
1601
1602bool LocalFrameView::useSlowRepaintsIfNotOverlapped() const
1603{
1604 return useSlowRepaints(false);
1605}
1606
1607void LocalFrameView::updateCanBlitOnScrollRecursively()
1608{
1609 for (RefPtr<Frame> frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr())) {
1610 RefPtr localFrame = dynamicDowncast<LocalFrame>(frame);
1611 if (!localFrame)
1612 continue;
1613 if (RefPtr view = localFrame->view())
1614 view->setCanBlitOnScroll(!view->useSlowRepaints());
1615 }
1616}
1617
1618bool LocalFrameView::usesCompositedScrolling() const
1619{
1620 CheckedPtr renderView = this->renderView();
1621 if (renderView && renderView->isComposited()) {
1622 GraphicsLayer* layer = renderView->layer()->backing()->graphicsLayer();
1623 if (layer && layer->drawsContent())
1624 return true;
1625 }
1626
1627 return false;
1628}
1629
1630bool LocalFrameView::usesAsyncScrolling() const
1631{
1632#if ENABLE(ASYNC_SCROLLING)(defined 1 && 1)
1633 if (RefPtr scrollingCoordinator = this->scrollingCoordinator())
1634 return scrollingCoordinator->coordinatesScrollingForFrameView(*this);
1635#endif
1636 return false;
1637}
1638
1639bool LocalFrameView::mockScrollbarsControllerEnabled() const
1640{
1641 return m_frame->settings().mockScrollbarsControllerEnabled();
1642}
1643
1644void LocalFrameView::logMockScrollbarsControllerMessage(const String& message) const
1645{
1646 RefPtr document = m_frame->document();
1647 if (!document)
1648 return;
1649 document->addConsoleMessage(MessageSource::Other, MessageLevel::Debug,
1650 makeString(m_frame->isMainFrame() ? "Main"_s : ""_s, "LocalFrameView: "_s, message));
1651}
1652
1653String LocalFrameView::debugDescription() const
1654{
1655 return makeString("LocalFrameView 0x"_s, hex(reinterpret_cast<uintptr_t>(this), Lowercase), ' ', m_frame->debugDescription());
1656}
1657
1658bool LocalFrameView::canShowNonOverlayScrollbars() const
1659{
1660 auto usesLegacyScrollbarStyle = [&] {
1661 RefPtr element = rootElementForCustomScrollbarPartStyle();
1662 if (!element)
1663 return false;
1664
1665 if (CheckedPtr renderBox = dynamicDowncast<RenderBox>(element->renderer()))
1666 return renderBox->style().usesLegacyScrollbarStyle();
1667
1668 return false;
1669 }();
1670
1671 return canHaveScrollbars() && (usesLegacyScrollbarStyle || !ScrollbarTheme::theme().usesOverlayScrollbars());
1672}
1673
1674bool LocalFrameView::styleHidesScrollbarWithOrientation(ScrollbarOrientation orientation) const
1675{
1676 auto element = rootElementForCustomScrollbarPartStyle();
1677 if (!element)
1678 return false;
1679 auto* renderer = element->renderer();
1680 ASSERT(renderer)((void)0); // rootElementForCustomScrollbarPart assures that it's not null.
1681
1682 StyleScrollbarState scrollbarState;
1683 scrollbarState.scrollbarPart = ScrollbarBGPart;
1684 scrollbarState.orientation = orientation;
1685 auto scrollbarStyle = renderer->getUncachedPseudoStyle({ PseudoElementType::WebKitScrollbar, scrollbarState }, &renderer->style());
1686 return scrollbarStyle && scrollbarStyle->display() == Style::DisplayType::None;
1687}
1688
1689NativeScrollbarVisibility LocalFrameView::horizontalNativeScrollbarVisibility() const
1690{
1691 if (managesScrollbars()) {
1692 RefPtr scrollbar = horizontalScrollbar();
1693 return Scrollbar::nativeScrollbarVisibility(scrollbar.get());
1694 }
1695
1696 return styleHidesScrollbarWithOrientation(ScrollbarOrientation::Horizontal) ? NativeScrollbarVisibility::HiddenByStyle : NativeScrollbarVisibility::Visible;
1697}
1698
1699NativeScrollbarVisibility LocalFrameView::verticalNativeScrollbarVisibility() const
1700{
1701 if (managesScrollbars()) {
1702 RefPtr scrollbar = verticalScrollbar();
1703 return Scrollbar::nativeScrollbarVisibility(scrollbar.get());
1704 }
1705
1706 return styleHidesScrollbarWithOrientation(ScrollbarOrientation::Vertical) ? NativeScrollbarVisibility::HiddenByStyle : NativeScrollbarVisibility::Visible;
1707}
1708
1709void LocalFrameView::setCannotBlitToWindow()
1710{
1711 m_cannotBlitToWindow = true;
1712 updateCanBlitOnScrollRecursively();
1713}
1714
1715void LocalFrameView::addSlowRepaintObject(RenderElement& renderer)
1716{
1717 bool hadSlowRepaintObjects = hasSlowRepaintObjects();
1718
1719 if (!m_slowRepaintObjects)
1720 m_slowRepaintObjects = makeUnique<SingleThreadWeakKeyHashSet<RenderElement>>();
1721
1722 auto addResult = m_slowRepaintObjects->add(renderer);
1723 if (addResult.isNewEntry) {
1724 if (CheckedPtr layer = renderer.enclosingLayer())
1725 layer->setNeedsScrollingTreeUpdate();
1726 }
1727
1728 if (hadSlowRepaintObjects)
1729 return;
1730
1731 updateCanBlitOnScrollRecursively();
1732}
1733
1734void LocalFrameView::removeSlowRepaintObject(RenderElement& renderer)
1735{
1736 if (!m_slowRepaintObjects)
1737 return;
1738
1739 bool removed = m_slowRepaintObjects->remove(renderer);
1740 if (removed) {
1741 if (CheckedPtr layer = renderer.enclosingLayer())
1742 layer->setNeedsScrollingTreeUpdate();
1743 }
1744
1745 if (!m_slowRepaintObjects->isEmptyIgnoringNullReferences())
1746 return;
1747
1748 m_slowRepaintObjects = nullptr;
1749 updateCanBlitOnScrollRecursively();
1750}
1751
1752bool LocalFrameView::hasSlowRepaintObject(const RenderElement& renderer) const
1753{
1754 return m_slowRepaintObjects && m_slowRepaintObjects->contains(renderer);
1755}
1756
1757bool LocalFrameView::hasSlowRepaintObjects() const
1758{
1759 return m_slowRepaintObjects && !m_slowRepaintObjects->isEmptyIgnoringNullReferences();
1760}
1761
1762bool LocalFrameView::hasViewportConstrainedObjects() const
1763{
1764 return m_viewportConstrainedObjects && !m_viewportConstrainedObjects->isEmptyIgnoringNullReferences();
1765}
1766
1767void LocalFrameView::addViewportConstrainedObject(RenderLayerModelObject& object)
1768{
1769 if (!m_viewportConstrainedObjects)
1770 m_viewportConstrainedObjects = makeUnique<SingleThreadWeakHashSet<RenderLayerModelObject>>();
1771
1772 if (!m_viewportConstrainedObjects->contains(object)) {
1773 m_viewportConstrainedObjects->add(object);
1774 if (platformWidget())
1775 updateCanBlitOnScrollRecursively();
1776
1777 if (RefPtr scrollingCoordinator = this->scrollingCoordinator())
1778 scrollingCoordinator->frameViewFixedObjectsDidChange(*this);
1779
1780 if (RefPtr page = m_frame->page())
1781 page->chrome().client().didAddOrRemoveViewportConstrainedObjects();
1782
1783 clearCachedHasAnchorPositionedViewportConstrainedObjects();
1784 }
1785}
1786
1787void LocalFrameView::removeViewportConstrainedObject(RenderLayerModelObject& object)
1788{
1789 if (m_viewportConstrainedObjects && m_viewportConstrainedObjects->remove(object)) {
1790 if (RefPtr scrollingCoordinator = this->scrollingCoordinator())
1791 scrollingCoordinator->frameViewFixedObjectsDidChange(*this);
1792
1793 // FIXME: In addFixedObject() we only call this if there's a platform widget,
1794 // why isn't the same check being made here?
1795 updateCanBlitOnScrollRecursively();
1796
1797 if (RefPtr<Page> page = m_frame->page())
1798 page->chrome().client().didAddOrRemoveViewportConstrainedObjects();
1799
1800 clearCachedHasAnchorPositionedViewportConstrainedObjects();
1801 }
1802}
1803
1804bool LocalFrameView::hasAnchorPositionedViewportConstrainedObjects() const
1805{
1806 auto compute = [&] {
1807 if (!m_viewportConstrainedObjects)
1808 return false;
1809 for (CheckedRef renderer : *m_viewportConstrainedObjects) {
1810 if (Style::AnchorPositionEvaluator::isAnchorPositioned(renderer->style()))
1811 return true;
1812 }
1813 return false;
1814 };
1815
1816 if (!m_hasAnchorPositionedViewportConstrainedObjects)
1817 m_hasAnchorPositionedViewportConstrainedObjects = compute();
1818
1819 return *m_hasAnchorPositionedViewportConstrainedObjects;
1820}
1821
1822void LocalFrameView::clearCachedHasAnchorPositionedViewportConstrainedObjects()
1823{
1824 m_hasAnchorPositionedViewportConstrainedObjects = { };
1825}
1826
1827LayoutSize LocalFrameView::expandedLayoutViewportSize(const LayoutSize& baseLayoutViewportSize, const LayoutSize& documentSize, double heightExpansionFactor)
1828{
1829 if (!heightExpansionFactor)
1830 return baseLayoutViewportSize;
1831
1832 auto documentHeight = documentSize.height();
1833 auto layoutViewportHeight = baseLayoutViewportSize.height();
1834 if (layoutViewportHeight > documentHeight)
1835 return baseLayoutViewportSize;
1836
1837 if (auto expandedHeight = LayoutUnit((1 + heightExpansionFactor) * layoutViewportHeight); expandedHeight < documentHeight)
1838 layoutViewportHeight = expandedHeight;
1839
1840 return { baseLayoutViewportSize.width(), std::min(documentHeight, layoutViewportHeight) };
1841}
1842
1843LayoutRect LocalFrameView::computeUpdatedLayoutViewportRect(const LayoutRect& layoutViewport, const LayoutRect& documentRect, const LayoutSize& unobscuredContentSize, const LayoutRect& unobscuredContentRect, const LayoutSize& baseLayoutViewportSize, const LayoutPoint& stableLayoutViewportOriginMin, const LayoutPoint& stableLayoutViewportOriginMax, LayoutViewportConstraint constraint)
1844{
1845 LayoutRect layoutViewportRect = layoutViewport;
1846
1847 // The layout viewport is never smaller than baseLayoutViewportSize, and never be smaller than the unobscuredContentRect.
1848 LayoutSize constrainedSize = baseLayoutViewportSize;
1849 layoutViewportRect.setSize(constrainedSize.expandedTo(unobscuredContentSize));
1850
1851 LayoutPoint layoutViewportOrigin = computeLayoutViewportOrigin(unobscuredContentRect, stableLayoutViewportOriginMin, stableLayoutViewportOriginMax, layoutViewportRect, ScrollBehaviorForFixedElements::StickToViewportBounds);
1852
1853 // FIXME: Is this equivalent to calling computeLayoutViewportOrigin() with StickToDocumentBounds?
1854 if (constraint == LayoutViewportConstraint::ConstrainedToDocumentRect) {
1855 // The max stable layout viewport origin really depends on the size of the layout viewport itself, so we need to adjust the location of the layout viewport one final time to make sure it does not end up out of bounds of the document.
1856 // Without this adjustment (and with using the non-constrained unobscuredContentRect's size as the size of the layout viewport) the layout viewport can be pushed past the bounds of the document during rubber-banding, and cannot be pushed
1857 // back in until the user scrolls back in the other direction.
1858 layoutViewportOrigin.setX(clampTo<float>(layoutViewportOrigin.x().toFloat(), 0, documentRect.width() - layoutViewportRect.width()));
1859 layoutViewportOrigin.setY(clampTo<float>(layoutViewportOrigin.y().toFloat(), 0, documentRect.height() - layoutViewportRect.height()));
1860 }
1861 layoutViewportRect.setLocation(layoutViewportOrigin);
1862
1863 return layoutViewportRect;
1864}
1865
1866// visualViewport and layoutViewport are both in content coordinates (unzoomed).
1867LayoutPoint LocalFrameView::computeLayoutViewportOrigin(const LayoutRect& visualViewport, const LayoutPoint& stableLayoutViewportOriginMin, const LayoutPoint& stableLayoutViewportOriginMax, const LayoutRect& layoutViewport, ScrollBehaviorForFixedElements fixedBehavior)
1868{
1869 LayoutPoint layoutViewportOrigin = layoutViewport.location();
1870 bool allowRubberBanding = fixedBehavior == ScrollBehaviorForFixedElements::StickToViewportBounds;
1871
1872 if (visualViewport.width() > layoutViewport.width()) {
1873 layoutViewportOrigin.setX(visualViewport.x());
1874 if (!allowRubberBanding) {
1875 if (layoutViewportOrigin.x() < stableLayoutViewportOriginMin.x())
1876 layoutViewportOrigin.setX(stableLayoutViewportOriginMin.x());
1877 else if (layoutViewportOrigin.x() > stableLayoutViewportOriginMax.x())
1878 layoutViewportOrigin.setX(stableLayoutViewportOriginMax.x());
1879 }
1880 } else {
1881 bool rubberbandingAtLeft = allowRubberBanding && visualViewport.x() < stableLayoutViewportOriginMin.x();
1882 bool rubberbandingAtRight = allowRubberBanding && (visualViewport.maxX() - layoutViewport.width()) > stableLayoutViewportOriginMax.x();
1883
1884 if (visualViewport.x() < layoutViewport.x() || rubberbandingAtLeft)
1885 layoutViewportOrigin.setX(visualViewport.x());
1886
1887 if (visualViewport.maxX() > layoutViewport.maxX() || rubberbandingAtRight)
1888 layoutViewportOrigin.setX(visualViewport.maxX() - layoutViewport.width());
1889
1890 if (!rubberbandingAtLeft && layoutViewportOrigin.x() < stableLayoutViewportOriginMin.x())
1891 layoutViewportOrigin.setX(stableLayoutViewportOriginMin.x());
1892
1893 if (!rubberbandingAtRight && layoutViewportOrigin.x() > stableLayoutViewportOriginMax.x())
1894 layoutViewportOrigin.setX(stableLayoutViewportOriginMax.x());
1895 }
1896
1897 if (visualViewport.height() > layoutViewport.height()) {
1898 layoutViewportOrigin.setY(visualViewport.y());
1899 if (!allowRubberBanding) {
1900 if (layoutViewportOrigin.y() < stableLayoutViewportOriginMin.y())
1901 layoutViewportOrigin.setY(stableLayoutViewportOriginMin.y());
1902 else if (layoutViewportOrigin.y() > stableLayoutViewportOriginMax.y())
1903 layoutViewportOrigin.setY(stableLayoutViewportOriginMax.y());
1904 }
1905 } else {
1906 bool rubberbandingAtTop = allowRubberBanding && visualViewport.y() < stableLayoutViewportOriginMin.y();
1907 bool rubberbandingAtBottom = allowRubberBanding && (visualViewport.maxY() - layoutViewport.height()) > stableLayoutViewportOriginMax.y();
1908
1909 if (visualViewport.y() < layoutViewport.y() || rubberbandingAtTop)
1910 layoutViewportOrigin.setY(visualViewport.y());
1911
1912 if (visualViewport.maxY() > layoutViewport.maxY() || rubberbandingAtBottom)
1913 layoutViewportOrigin.setY(visualViewport.maxY() - layoutViewport.height());
1914
1915 if (!rubberbandingAtTop && layoutViewportOrigin.y() < stableLayoutViewportOriginMin.y())
1916 layoutViewportOrigin.setY(stableLayoutViewportOriginMin.y());
1917
1918 if (!rubberbandingAtBottom && layoutViewportOrigin.y() > stableLayoutViewportOriginMax.y())
1919 layoutViewportOrigin.setY(stableLayoutViewportOriginMax.y());
1920 }
1921
1922 return layoutViewportOrigin;
1923}
1924
1925void LocalFrameView::setBaseLayoutViewportOrigin(LayoutPoint origin, TriggerLayoutOrNot layoutTriggering)
1926{
1927 ASSERT(m_frame->settings().visualViewportEnabled())((void)0);
1928
1929 if (origin == m_layoutViewportOrigin)
1930 return;
1931
1932 m_layoutViewportOrigin = origin;
1933 if (layoutTriggering == TriggerLayoutOrNot::Yes)
1934 setViewportConstrainedObjectsNeedLayout();
1935
1936 if (TiledBacking* tiledBacking = this->tiledBacking()) {
1937 FloatRect layoutViewport = layoutViewportRect();
1938 layoutViewport.moveBy(unscaledScrollOrigin()); // tiledBacking deals in top-left relative coordinates.
1939 tiledBacking->setLayoutViewportRect(layoutViewport);
1940 }
1941}
1942
1943void LocalFrameView::setLayoutViewportOverrideRect(std::optional<LayoutRect> rect, TriggerLayoutOrNot layoutTriggering)
1944{
1945 if (rect == m_layoutViewportOverrideRect)
1946 return;
1947
1948 LayoutRect oldRect = layoutViewportRect();
1949 m_layoutViewportOverrideRect = rect;
1950 LayoutRect newRect = layoutViewportRect();
1951
1952 // Triggering layout on height changes is necessary to make bottom-fixed elements behave correctly.
1953 if (oldRect.height() != newRect.height())
1954 layoutTriggering = TriggerLayoutOrNot::Yes;
1955
1956 LOG_WITH_STREAM(Scrolling, stream << "\nFrameView " << this << " setLayoutViewportOverrideRect() - changing override layout viewport from " << oldRect << " to " << valueOrDefault(m_layoutViewportOverrideRect) << " layoutTriggering " << (layoutTriggering == TriggerLayoutOrNot::Yes ? "yes" : "no"))((void)0);
1957
1958 if (layoutTriggering == TriggerLayoutOrNot::Yes) {
1959 if (oldRect != newRect)
1960 setViewportConstrainedObjectsNeedLayout();
1961
1962 if (oldRect.size() != newRect.size()) {
1963 if (RefPtr document = m_frame->document())
1964 document->updateViewportUnitsOnResize();
1965 }
1966 }
1967}
1968
1969void LocalFrameView::setVisualViewportOverrideRect(std::optional<LayoutRect> rect)
1970{
1971 m_visualViewportOverrideRect = rect;
1972}
1973
1974LayoutSize LocalFrameView::baseLayoutViewportSize() const
1975{
1976 return renderView() ? renderView()->size() : size();
1977}
1978
1979void LocalFrameView::updateLayoutViewport()
1980{
1981 if (!m_frame->settings().visualViewportEnabled())
1982 return;
1983
1984 // Don't update the layout viewport if we're in the middle of adjusting scrollbars. We'll get another call
1985 // as a post-layout task.
1986 if (layoutContext().layoutPhase() == LocalFrameViewLayoutContext::LayoutPhase::InViewSizeAdjust)
1987 return;
1988
1989 LayoutRect layoutViewport = layoutViewportRect();
1990
1991 LOG_WITH_STREAM(Scrolling, stream << "\nFrameView " << this << " updateLayoutViewport() totalContentSize " << totalContentsSize() << " unscaledDocumentRect " << (renderView() ? renderView()->unscaledDocumentRect() : IntRect()) << " header height " << headerHeight() << " footer height " << footerHeight() << " fixed behavior " << scrollBehaviorForFixedElements())((void)0);
1992 LOG_WITH_STREAM(Scrolling, stream << "layoutViewport: " << layoutViewport)((void)0);
1993 LOG_WITH_STREAM(Scrolling, stream << "visualViewport: " << visualViewportRect() << " (is override " << (bool)m_visualViewportOverrideRect << ")")((void)0);
1994 LOG_WITH_STREAM(Scrolling, stream << "stable origins: min: " << minStableLayoutViewportOrigin() << " max: "<< maxStableLayoutViewportOrigin())((void)0);
1995
1996 if (m_layoutViewportOverrideRect) {
1997 if (currentScrollType() == ScrollType::Programmatic) {
1998 LOG_WITH_STREAM(Scrolling, stream << "computing new override layout viewport because of programmatic scrolling")((void)0);
1999 LayoutPoint newOrigin = computeLayoutViewportOrigin(visualViewportRect(), minStableLayoutViewportOrigin(), maxStableLayoutViewportOrigin(), layoutViewport, ScrollBehaviorForFixedElements::StickToDocumentBounds);
2000 setLayoutViewportOverrideRect(LayoutRect(newOrigin, m_layoutViewportOverrideRect.value().size()));
2001 }
2002 layoutOrVisualViewportChanged();
2003 return;
2004 }
2005
2006 LayoutPoint newLayoutViewportOrigin = computeLayoutViewportOrigin(visualViewportRect(), minStableLayoutViewportOrigin(), maxStableLayoutViewportOrigin(), layoutViewport, scrollBehaviorForFixedElements());
2007 if (newLayoutViewportOrigin != m_layoutViewportOrigin) {
2008 setBaseLayoutViewportOrigin(newLayoutViewportOrigin);
2009 LOG_WITH_STREAM(Scrolling, stream << "layoutViewport changed to " << layoutViewportRect())((void)0);
2010 }
2011 layoutOrVisualViewportChanged();
2012}
2013
2014LayoutPoint LocalFrameView::minStableLayoutViewportOrigin() const
2015{
2016 return unscaledMinimumScrollPosition();
2017}
2018
2019LayoutPoint LocalFrameView::maxStableLayoutViewportOrigin() const
2020{
2021 LayoutPoint maxPosition = unscaledMaximumScrollPosition();
2022 maxPosition = (maxPosition - LayoutSize(0, headerHeight() + footerHeight())).expandedTo({ });
2023 return maxPosition;
2024}
2025
2026IntPoint LocalFrameView::unscaledScrollOrigin() const
2027{
2028 if (CheckedPtr renderView = this->renderView())
2029 return -renderView->unscaledDocumentRect().location(); // Akin to code in adjustViewSize().
2030
2031 return { };
2032}
2033
2034LayoutRect LocalFrameView::layoutViewportRect() const
2035{
2036 if (m_layoutViewportOverrideRect)
2037 return m_layoutViewportOverrideRect.value();
2038
2039 // Size of initial containing block, anchored at scroll position, in document coordinates (unchanged by scale factor).
2040 return LayoutRect(m_layoutViewportOrigin, baseLayoutViewportSize());
2041}
2042
2043void LocalFrameView::updateLayoutViewportRect()
2044{
2045 m_frame->loader().client().broadcastFrameLayoutViewportRectToOtherProcesses(layoutViewportRect());
2046}
2047
2048// visibleContentRect is in the bounds of the scroll view content. That consists of an
2049// optional header, the document, and an optional footer. Only the document is scaled,
2050// so we have to compute the visible part of the document in unscaled document coordinates.
2051// On iOS, pageScaleFactor is always 1 here, and we never have headers and footers.
2052LayoutRect LocalFrameView::visibleDocumentRect(const FloatRect& visibleContentRect, float headerHeight, float footerHeight, const FloatSize& totalContentsSize, float pageScaleFactor)
2053{
2054 float contentsHeight = totalContentsSize.height() - headerHeight - footerHeight;
2055
2056 float rubberBandTop = std::min<float>(visibleContentRect.y(), 0);
2057 float visibleScaledDocumentTop = std::max<float>(visibleContentRect.y() - headerHeight, 0) + rubberBandTop;
2058
2059 float rubberBandBottom = std::min<float>((totalContentsSize.height() - visibleContentRect.y()) - visibleContentRect.height(), 0);
2060 float visibleScaledDocumentBottom = std::min<float>(visibleContentRect.maxY() - headerHeight, contentsHeight) - rubberBandBottom;
2061
2062 FloatRect visibleDocumentRect = visibleContentRect;
2063 visibleDocumentRect.setY(visibleScaledDocumentTop);
2064 visibleDocumentRect.setHeight(std::max<float>(visibleScaledDocumentBottom - visibleScaledDocumentTop, 0));
2065 visibleDocumentRect.scale(1 / pageScaleFactor);
2066
2067 return LayoutRect(visibleDocumentRect);
2068}
2069
2070LayoutRect LocalFrameView::visualViewportRect() const
2071{
2072 if (m_visualViewportOverrideRect)
2073 return m_visualViewportOverrideRect.value();
2074
2075 FloatRect visibleContentRect = this->visibleContentRect(LegacyIOSDocumentVisibleRect);
2076 return visibleDocumentRect(visibleContentRect, headerHeight(), footerHeight(), totalContentsSize(), frameScaleFactor());
2077}
2078
2079LayoutRect LocalFrameView::layoutViewportRectIncludingObscuredInsets() const
2080{
2081 auto layoutViewportRect = this->layoutViewportRect();
2082 if (!m_frame->isMainFrame())
2083 return layoutViewportRect;
2084
2085 FloatBoxExtent insets;
2086 if (RefPtr page = m_frame->page()) {
2087#if PLATFORM(IOS_FAMILY)(defined 1 && 1)
2088 insets = page->obscuredInsets();
2089#else
2090 insets = page->obscuredContentInsets();
2091#endif
2092 }
2093 layoutViewportRect.expand({
2094 LayoutUnit::fromFloatRound(insets.top()),
2095 LayoutUnit::fromFloatRound(insets.right()),
2096 LayoutUnit::fromFloatRound(insets.bottom()),
2097 LayoutUnit::fromFloatRound(insets.left()),
2098 });
2099 return layoutViewportRect;
2100}
2101
2102LayoutRect LocalFrameView::viewportConstrainedVisibleContentRect() const
2103{
2104 ASSERT(!m_frame->settings().visualViewportEnabled())((void)0);
2105
2106#if PLATFORM(IOS_FAMILY)(defined 1 && 1)
2107 if (useCustomFixedPositionLayoutRect())
2108 return customFixedPositionLayoutRect();
2109#endif
2110 LayoutRect viewportRect = visibleContentRect();
2111
2112 viewportRect.setLocation(scrollPositionForFixedPosition());
2113 return viewportRect;
2114}
2115
2116std::optional<LayoutRect> LocalFrameView::visibleRectOfChild(const Frame& child) const
2117{
2118 RefPtr childOwnerRenderer = child.ownerRenderer();
2119 if (!childOwnerRenderer)
2120 return std::nullopt;
2121
2122 // Ensure |child| is a child of this frame.
2123 ASSERT(child.tree().parent()->frameID() == m_frame->frameID())((void)0);
2124 ASSERT(childOwnerRenderer->frame().frameID() == m_frame->frameID())((void)0);
2125
2126 auto rects = childOwnerRenderer->computeVisibleRectsInContainer(
2127 { childOwnerRenderer->frameRect() },
2128 &childOwnerRenderer->view(),
2129 {
2130 .hasPositionFixedDescendant = false,
2131 .dirtyRectIsFlipped = false,
2132 .descendantNeedsEnclosingIntRect = false,
2133 .options = {
2134 VisibleRectContext::Option::UseEdgeInclusiveIntersection,
2135 VisibleRectContext::Option::ApplyCompositedClips,
2136 VisibleRectContext::Option::ApplyCompositedContainerScrolls
2137 },
2138 }
2139 );
2140
2141 return rects.transform([] (const auto& repaintRects) { return repaintRects.clippedOverflowRect; });
2142}
2143
2144bool LocalFrameView::ownerElementOfChildFrameUsesDarkAppearance(const Frame& child) const
2145{
2146 RefPtr childOwnerRenderer = child.ownerRenderer();
2147 if (!childOwnerRenderer)
2148 return false;
2149
2150 // Ensure |child| is a child of this frame.
2151 ASSERT(child.tree().parent()->frameID() == m_frame->frameID())((void)0);
2152 ASSERT(childOwnerRenderer->frame().frameID() == m_frame->frameID())((void)0);
2153
2154 return childOwnerRenderer->useDarkAppearance();
2155}
2156
2157LayoutRect LocalFrameView::rectForFixedPositionLayout() const
2158{
2159 if (m_frame->settings().visualViewportEnabled())
2160 return layoutViewportRect();
2161
2162 return viewportConstrainedVisibleContentRect();
2163}
2164
2165float LocalFrameView::frameScaleFactor() const
2166{
2167 return m_frame->frameScaleFactor();
2168}
2169
2170LayoutPoint LocalFrameView::scrollPositionForFixedPosition() const
2171{
2172 if (m_frame->settings().visualViewportEnabled())
2173 return layoutViewportRect().location();
2174
2175 return scrollPositionForFixedPosition(visibleContentRect(), totalContentsSize(), scrollPosition(), scrollOrigin(), frameScaleFactor(), fixedElementsLayoutRelativeToFrame(), scrollBehaviorForFixedElements(), headerHeight(), footerHeight());
2176}
2177
2178LayoutPoint LocalFrameView::scrollPositionForFixedPosition(const LayoutRect& visibleContentRect, const LayoutSize& totalContentsSize, const LayoutPoint& scrollPosition, const LayoutPoint& scrollOrigin, float frameScaleFactor, bool fixedElementsLayoutRelativeToFrame, ScrollBehaviorForFixedElements behaviorForFixed, int headerHeight, int footerHeight)
2179{
2180 LayoutPoint position;
2181 if (behaviorForFixed == ScrollBehaviorForFixedElements::StickToDocumentBounds)
2182 position = ScrollableArea::constrainScrollPositionForOverhang(visibleContentRect, totalContentsSize, scrollPosition, scrollOrigin, headerHeight, footerHeight);
2183 else {
2184 position = scrollPosition;
2185 position.setY(position.y() - headerHeight);
2186 }
2187
2188 LayoutSize maxSize = totalContentsSize - visibleContentRect.size();
2189
2190 float dragFactorX = (fixedElementsLayoutRelativeToFrame || !maxSize.width()) ? 1 : (totalContentsSize.width() - visibleContentRect.width() * frameScaleFactor) / maxSize.width();
2191 float dragFactorY = (fixedElementsLayoutRelativeToFrame || !maxSize.height()) ? 1 : (totalContentsSize.height() - visibleContentRect.height() * frameScaleFactor) / maxSize.height();
2192
2193 return LayoutPoint(position.x() * dragFactorX / frameScaleFactor, position.y() * dragFactorY / frameScaleFactor);
2194}
2195
2196static bool isHiddenOrNearlyTransparent(const RenderBox& box)
2197{
2198 if (CheckedPtr layer = box.layer(); layer && layer->isVisibilityHiddenOrOpacityZero())
2199 return true;
2200
2201 if (box.opacity() < PageColorSampler::nearlyTransparentAlphaThreshold)
2202 return true;
2203
2204 if (!box.hasBackground() && !box.hasBackdropFilter() && !box.firstChild() && !is<RenderReplaced>(box))
2205 return true;
2206
2207 return false;
2208}
2209
2210std::pair<FixedContainerEdges, WeakElementEdges> LocalFrameView::fixedContainerEdges(BoxSideSet sides) const
2211{
2212 WeakElementEdges containers;
2213 FixedContainerEdges edges;
2214 if (sides.isEmpty())
2215 return { WTF::move(edges), WTF::move(containers) };
2216
2217 RefPtr page = m_frame->page();
2218 if (!page)
2219 return { WTF::move(edges), WTF::move(containers) };
2220
2221 if (!hasViewportConstrainedObjects())
2222 return { WTF::move(edges), WTF::move(containers) };
2223
2224 RefPtr document = m_frame->document();
2225 if (!document)
2226 return { WTF::move(edges), WTF::move(containers) };
2227
2228 TraceScope tracingScope { FixedContainerEdgeSamplingStart, FixedContainerEdgeSamplingEnd };
2229
2230 static constexpr auto sampleRectThickness = 2;
2231 static constexpr auto sampleRectMargin = 4;
2232 static constexpr auto thinBorderWidth = 10;
2233
2234 struct FixedContainerResult {
2235 RefPtr<Element> container;
2236 bool foundBackdropFilter : 1 { false };
2237 bool retryHonoringPointerEvents : 1 { false };
2238 bool isViewportSized : 1 { false };
2239 bool isDimmingLayer : 1 { false };
2240 bool isSidebar : 1 { false };
2241 Color backgroundColor;
2242 };
2243
2244 auto sizeForViewportUnits = LayoutSize { sizeForCSSDefaultViewportUnits() };
2245 auto unclampedFixedRect = rectForFixedPositionLayout();
2246 auto fixedRect = unclampedFixedRect;
2247 if (CheckedPtr view = renderView())
2248 fixedRect.intersect(view->unscaledDocumentRect());
2249 fixedRect.contract({ sampleRectMargin });
2250 unclampedFixedRect.contract({ sampleRectMargin });
2251
2252 auto adjacentSideInClockwiseOrder = [](BoxSide side) {
2253 switch (side) {
2254 case BoxSide::Left:
2255 return BoxSide::Top;
2256 case BoxSide::Top:
2257 return BoxSide::Right;
2258 case BoxSide::Right:
2259 return BoxSide::Bottom;
2260 case BoxSide::Bottom:
2261 return BoxSide::Left;
2262 }
2263 ASSERT_NOT_REACHED()((void)0);
2264 return side;
2265 };
2266
2267 auto lengthOnSide = [](BoxSide side, auto& rectOrSize) -> LayoutUnit {
2268 switch (side) {
2269 case BoxSide::Top:
2270 case BoxSide::Bottom:
2271 return rectOrSize.width();
2272 case BoxSide::Right:
2273 case BoxSide::Left:
2274 return rectOrSize.height();
2275 }
2276 return { };
2277 };
2278
2279 enum class ViewportComparison : uint8_t {
2280 Smaller,
2281 Similar,
2282 Larger,
2283 };
2284
2285 auto compareWithViewportSize = [&](BoxSide side, const RenderObject& renderer) {
2286 using enum ViewportComparison;
2287 static constexpr auto minimumRatio = 0.9;
2288 static constexpr auto maximumRatio = 1.05;
2289 auto elementRect = enclosingLayoutRect(renderer.absoluteBoundingBoxRect());
2290 auto containerLength = lengthOnSide(side, elementRect);
2291 auto viewportLength = std::min(lengthOnSide(side, fixedRect), lengthOnSide(side, sizeForViewportUnits));
2292 if (containerLength < viewportLength * minimumRatio)
2293 return Smaller;
2294
2295 if (containerLength < viewportLength * maximumRatio)
2296 return Similar;
2297
2298 return Larger;
2299 };
2300
2301 auto midpointOnSide = [&](BoxSide side, const LayoutRect& rect) -> LayoutPoint {
2302 switch (side) {
2303 case BoxSide::Top:
2304 return { (rect.x() + rect.maxX()) / 2, rect.y() };
2305 case BoxSide::Left:
2306 return { rect.x(), (rect.y() + rect.maxY()) / 2 };
2307 case BoxSide::Right:
2308 return { rect.maxX(), (rect.y() + rect.maxY()) / 2 };
2309 case BoxSide::Bottom:
2310 return { (rect.x() + rect.maxX()) / 2, rect.maxY() };
2311 default:
2312 ASSERT_NOT_REACHED()((void)0);
2313 return { };
2314 }
2315 };
2316
2317 auto computeSampleRectIgnoringBorderWidth = [](LayoutRect rect, BoxSide side) -> LayoutRect {
2318 switch (side) {
2319 case BoxSide::Top:
2320 return { rect.minXMinYCorner(), LayoutPoint { rect.maxX(), rect.y() + sampleRectThickness } };
2321 case BoxSide::Left:
2322 return { rect.minXMinYCorner(), LayoutPoint { rect.x() + sampleRectThickness, rect.maxY() } };
2323 case BoxSide::Right:
2324 return { LayoutPoint { rect.maxX() - sampleRectThickness, rect.y() }, rect.maxXMaxYCorner() };
2325 case BoxSide::Bottom:
2326 return { LayoutPoint { rect.x(), rect.maxY() - sampleRectThickness }, rect.maxXMaxYCorner() };
2327 default:
2328 ASSERT_NOT_REACHED()((void)0);
2329 return { };
2330 }
2331 };
2332
2333 auto primaryBackgroundColorForRenderer = [&](BoxSide side, const RenderElement& renderer) -> Color {
2334 CheckedPtr box = dynamicDowncast<RenderBox>(renderer);
2335 if (!box)
2336 return { };
2337
2338 if (box->width() <= thinBorderWidth || box->height() <= thinBorderWidth)
2339 return { };
2340
2341 if (isHiddenOrNearlyTransparent(*box))
2342 return { };
2343
2344 auto& styleColor = renderer.style().backgroundColor();
2345 if (!styleColor.isResolvedColor())
2346 return { };
2347
2348 if (compareWithViewportSize(side, renderer) == ViewportComparison::Smaller)
2349 return { };
2350
2351 return styleColor.resolvedColor();
2352 };
2353
2354 enum class ContainerEdgeCandidateResult : uint8_t {
2355 NoLayer,
2356 NotFixedOrSticky,
2357 IsHiddenOrTransparent,
2358 TooSmall,
2359 TooLarge,
2360 NegativeZIndex,
2361 IsViewportSizedCandidate,
2362 IsSidebar,
2363 IsDimmingLayer,
2364 IsCandidate,
2365 };
2366 using enum ContainerEdgeCandidateResult;
2367
2368 auto containerEdgeCandidateResult = [&](BoxSide side, const RenderElement& renderer) {
2369 if (!renderer.hasLayer())
2370 return NoLayer;
2371
2372 if (!renderer.isFixedPositioned() && !renderer.isStickilyPositioned())
2373 return NotFixedOrSticky;
2374
2375 auto lengthOnSide = compareWithViewportSize(side, renderer);
2376 auto lengthOnAdjacentSide = compareWithViewportSize(adjacentSideInClockwiseOrder(side), renderer);
2377 bool isProbablyDimmingContainer = false;
2378 if (CheckedPtr box = dynamicDowncast<RenderBox>(renderer)) {
2379 if (isHiddenOrNearlyTransparent(*box))
2380 return IsHiddenOrTransparent;
2381
2382 isProbablyDimmingContainer = [&] {
2383 if (lengthOnSide == ViewportComparison::Smaller)
2384 return false;
2385
2386 if (lengthOnAdjacentSide == ViewportComparison::Smaller)
2387 return false;
2388
2389 if (!box->hasBackground())
2390 return false;
2391
2392 if (box->firstChild())
2393 return false;
2394
2395 if (box->isTransparent())
2396 return true;
2397
2398 auto backgroundColor = primaryBackgroundColorForRenderer(side, *box);
2399 return backgroundColor.isVisible() && backgroundColor.alphaAsFloat() < 1;
2400 }();
2401 }
2402
2403 if (lengthOnSide == ViewportComparison::Smaller)
2404 return lengthOnAdjacentSide != ViewportComparison::Smaller ? IsSidebar : TooSmall;
2405
2406 if (!isProbablyDimmingContainer && lengthOnAdjacentSide == ViewportComparison::Larger)
2407 return TooLarge;
2408
2409 if (isProbablyDimmingContainer)
2410 return IsDimmingLayer;
2411
2412 if (lengthOnSide == ViewportComparison::Similar && lengthOnAdjacentSide == ViewportComparison::Similar) {
2413 if (auto zIndex = renderer.style().usedZIndex().tryValue(); zIndex && zIndex->isNegative())
2414 return NegativeZIndex;
2415
2416 return IsViewportSizedCandidate;
2417 }
2418
2419 return IsCandidate;
2420 };
2421
2422 enum class IgnoreCSSPointerEvents : bool { No, Yes };
2423 auto findFixedContainer = [&](BoxSide side, IgnoreCSSPointerEvents ignoreCSSPointerEvents) -> FixedContainerResult {
2424 using enum HitTestRequest::Type;
2425 static constexpr OptionSet commonHitTestOptions {
2426 ReadOnly,
2427 DisallowUserAgentShadowContent,
2428 IgnoreClipping,
2429 ForFixedContainerSampling,
2430 };
2431
2432 HitTestResult result { midpointOnSide(side, fixedRect) };
2433 auto hitTestOptions = commonHitTestOptions;
2434 if (ignoreCSSPointerEvents == IgnoreCSSPointerEvents::Yes)
2435 hitTestOptions.add(IgnoreCSSPointerEventsProperty);
2436
2437 document->hitTest({ HitTestSource::User, hitTestOptions }, result);
2438
2439 RefPtr hitNode = result.innerNonSharedNode();
2440 if (!hitNode)
2441 return { };
2442
2443 CheckedPtr renderer = hitNode->renderer();
2444 if (!renderer)
2445 return { };
2446
2447 auto containerResultFromBackdrop = [&] -> std::optional<FixedContainerResult> {
2448 RefPtr container = dynamicDowncast<Element>(*hitNode);
2449 if (!container)
2450 return std::nullopt;
2451
2452 CheckedPtr elementRenderer = dynamicDowncast<RenderElement>(*renderer);
2453 if (!elementRenderer)
2454 return std::nullopt;
2455
2456 auto backdropRenderer = elementRenderer->pseudoElementRenderer(PseudoElementType::Backdrop);
2457 if (!backdropRenderer)
2458 return std::nullopt;
2459
2460 auto backgroundColor = primaryBackgroundColorForRenderer(side, *backdropRenderer);
2461 if (!backgroundColor.isVisible())
2462 backgroundColor = page->pageExtendedBackgroundColor();
2463
2464 return { {
2465 .container = container.releaseNonNull(),
2466 .foundBackdropFilter = false,
2467 .retryHonoringPointerEvents = false,
2468 .isViewportSized = true,
2469 .isDimmingLayer = true,
2470 .isSidebar = false,
2471 .backgroundColor = WTF::move(backgroundColor),
2472 } };
2473 }();
2474
2475 if (containerResultFromBackdrop)
2476 return WTF::move(*containerResultFromBackdrop);
2477
2478 bool hasMultipleBackgroundColors = false;
2479 Color primaryBackgroundColor;
2480 bool foundBackdropFilter = false;
2481 bool hitInvisiblePointerEventsNoneContainer = false;
2482 for (CheckedRef ancestor : lineageOfType<RenderElement>(*renderer)) {
2483 auto candidateType = containerEdgeCandidateResult(side, ancestor);
2484 if (candidateType != IsHiddenOrTransparent) {
2485 if (ancestor->hasBackdropFilter())
2486 foundBackdropFilter = true;
2487 else if (auto color = primaryBackgroundColorForRenderer(side, ancestor); color.isVisible()) {
2488 if (!primaryBackgroundColor.isVisible())
2489 primaryBackgroundColor = WTF::move(color);
2490 else if (primaryBackgroundColor != color)
2491 hasMultipleBackgroundColors = true;
2492 }
2493 }
2494
2495 switch (candidateType) {
2496 case NoLayer:
2497 case NotFixedOrSticky:
2498 case TooSmall:
2499 break;
2500 case NegativeZIndex:
2501 case TooLarge:
2502 case IsHiddenOrTransparent: {
2503 hitInvisiblePointerEventsNoneContainer = ancestor->usedPointerEvents() == PointerEvents::None;
2504 break;
2505 }
2506 case IsViewportSizedCandidate:
2507 case IsDimmingLayer:
2508 case IsSidebar:
2509 case IsCandidate: {
2510 return {
2511 .container = { ancestor->element() },
2512 .foundBackdropFilter = foundBackdropFilter,
2513 .retryHonoringPointerEvents = false,
2514 .isViewportSized = candidateType == IsViewportSizedCandidate,
2515 .isDimmingLayer = candidateType == IsDimmingLayer,
2516 .isSidebar = candidateType == IsSidebar,
2517 .backgroundColor = hasMultipleBackgroundColors ? Color { } : WTF::move(primaryBackgroundColor),
2518 };
2519 }
2520 }
2521 }
2522
2523 return {
2524 .container = { },
2525 .foundBackdropFilter = foundBackdropFilter,
2526 .retryHonoringPointerEvents = hitInvisiblePointerEventsNoneContainer,
2527 .isViewportSized = false,
2528 .isDimmingLayer = false,
2529 .isSidebar = false,
2530 .backgroundColor = { },
2531 };
2532 };
2533
2534 auto computeSamplingRect = [&](const RenderStyle* style, BoxSide side) -> LayoutRect {
2535 auto samplingRect = computeSampleRectIgnoringBorderWidth(fixedRect, side);
2536 if (!style)
2537 return samplingRect;
2538
2539 const BorderValue* border = nullptr;
2540 switch (side) {
2541 case BoxSide::Top:
2542 border = &style->borderTop();
2543 break;
2544 case BoxSide::Left:
2545 border = &style->borderLeft();
2546 break;
2547 case BoxSide::Right:
2548 border = &style->borderRight();
2549 break;
2550 case BoxSide::Bottom:
2551 border = &style->borderBottom();
2552 break;
2553 }
2554
2555 if (!border)
2556 return samplingRect;
2557
2558 if (!border->isVisible())
2559 return samplingRect;
2560
2561 auto borderWidth = Style::evaluate<float>(border->width, Style::ZoomNeeded { });
2562 if (borderWidth > thinBorderWidth)
2563 return samplingRect;
2564
2565 auto offset = LayoutUnit::fromFloatRound(borderWidth);
2566
2567 switch (side) {
2568 case BoxSide::Top:
2569 samplingRect.move(0, offset);
2570 break;
2571 case BoxSide::Left:
2572 samplingRect.move(offset, 0);
2573 break;
2574 case BoxSide::Right:
2575 samplingRect.move(-offset, 0);
2576 break;
2577 case BoxSide::Bottom:
2578 samplingRect.move(0, -offset);
2579 break;
2580 }
2581
2582 return samplingRect;
2583 };
2584
2585 static constexpr auto minimumOpacityThresholdToClampToSolidColor = 0.75;
2586
2587 auto pageBackgroundColor = page->pageExtendedBackgroundColor();
2588 auto blendAgainstPageBackground = [pageBackgroundColor](const Color& color) {
2589 if (color.isOpaque())
2590 return color;
2591
2592 return blendSourceOver(pageBackgroundColor, color);
2593 };
2594
2595 for (auto side : sides) {
2596 auto result = findFixedContainer(side, IgnoreCSSPointerEvents::Yes);
2597 if (result.retryHonoringPointerEvents)
2598 result = findFixedContainer(side, IgnoreCSSPointerEvents::No);
2599
2600 if (!result.container)
2601 continue;
2602
2603 containers.setAt(side, *result.container);
2604
2605 if (result.foundBackdropFilter) {
2606 edges.colors.setAt(side, PredominantColorType::Multiple);
2607 continue;
2608 }
2609
2610 bool preferExistingColor = result.isDimmingLayer || result.isViewportSized || result.isSidebar;
2611 if (preferExistingColor && page->fixedContainerEdges().hasFixedEdge(side)) {
2612 edges.colors.setAt(side, page->fixedContainerEdges().colors.at(side));
2613 continue;
2614 }
2615
2616 if (result.backgroundColor.isVisible()) {
2617 edges.colors.setAt(side, [&] {
2618 if (result.isDimmingLayer || result.backgroundColor.alphaAsFloat() < minimumOpacityThresholdToClampToSolidColor)
2619 return blendAgainstPageBackground(result.backgroundColor);
2620 return result.backgroundColor.colorWithAlpha(1);
2621 }());
2622 continue;
2623 }
2624
2625 if (page->lastFixedContainer(side) == result.container && page->fixedContainerEdges().hasFixedEdge(side)) {
2626 edges.colors.setAt(side, page->fixedContainerEdges().colors.at(side));
2627 continue;
2628 }
2629
2630 edges.colors.setAt(side, [&] -> FixedContainerEdge {
2631 auto samplingResult = PageColorSampler::predominantColor(*page, computeSamplingRect(result.container->renderStyle(), side));
2632 if (!std::holds_alternative<Color>(samplingResult))
2633 return samplingResult;
2634
2635 auto color = std::get<Color>(samplingResult);
2636 if (result.isDimmingLayer || color.alphaAsFloat() < minimumOpacityThresholdToClampToSolidColor)
2637 return blendAgainstPageBackground(color);
2638
2639 return color.colorWithAlpha(1);
2640 }());
2641 }
2642
2643 return { WTF::move(edges), WTF::move(containers) };
2644}
2645
2646FloatRect LocalFrameView::insetClipLayerRect(const FloatPoint& scrollPosition, const FloatBoxExtent& obscuredContentInset, const FloatSize& sizeForVisibleContent)
2647{
2648 auto computeOffset = [](float scrollAmount, float insetAmount) {
2649 if (!insetAmount)
2650 return 0.f;
2651
2652 // The insetClipLayer should not move for negative scroll values.
2653 auto clampedScrollPosition = std::max<float>(0, scrollAmount);
2654 if (clampedScrollPosition >= insetAmount)
2655 return 0.f;
2656
2657 return insetAmount - clampedScrollPosition;
2658 };
2659
2660 FloatPoint position { computeOffset(scrollPosition.x(), obscuredContentInset.left()), computeOffset(scrollPosition.y(), obscuredContentInset.top()) };
2661
2662 auto adjustedSize = sizeForVisibleContent;
2663 if (obscuredContentInset.top())
2664 adjustedSize.setHeight(std::max(0.f, sizeForVisibleContent.height() - position.y()));
2665
2666 return { position, adjustedSize };
2667}
2668
2669float LocalFrameView::yPositionForHeaderLayer(const FloatPoint& scrollPosition, float topInset)
2670{
2671 if (!topInset)
2672 return 0;
2673
2674 float scrollY = std::max<float>(0, scrollPosition.y());
2675
2676 if (scrollY >= topInset)
2677 return topInset;
2678
2679 return scrollY;
2680}
2681
2682float LocalFrameView::yPositionForFooterLayer(const FloatPoint& scrollPosition, float topInset, float totalContentsHeight, float footerHeight)
2683{
2684 return yPositionForHeaderLayer(scrollPosition, topInset) + totalContentsHeight - footerHeight;
2685}
2686
2687FloatPoint LocalFrameView::positionForRootContentLayer(const FloatPoint& scrollPosition, const FloatPoint& scrollOrigin, const FloatBoxExtent& obscuredContentInsets, float headerHeight)
2688{
2689 return {
2690 (scrollPosition.x() >= obscuredContentInsets.left() ? obscuredContentInsets.left() : std::max<float>(0, scrollPosition.x())) - scrollOrigin.x(),
2691 yPositionForHeaderLayer(scrollPosition, obscuredContentInsets.top()) + headerHeight - scrollOrigin.y()
2692 };
2693}
2694
2695FloatPoint LocalFrameView::positionForRootContentLayer() const
2696{
2697 return positionForRootContentLayer(scrollPosition(), scrollOrigin(), obscuredContentInsets(), headerHeight());
2698}
2699
2700#if PLATFORM(IOS_FAMILY)(defined 1 && 1)
2701LayoutRect LocalFrameView::rectForViewportConstrainedObjects(const LayoutRect& visibleContentRect, const LayoutSize& totalContentsSize, float frameScaleFactor, bool fixedElementsLayoutRelativeToFrame, ScrollBehaviorForFixedElements scrollBehavior)
2702{
2703 if (fixedElementsLayoutRelativeToFrame)
2704 return visibleContentRect;
2705
2706 if (totalContentsSize.isEmpty())
2707 return visibleContentRect;
2708
2709 // We impose an lower limit on the size (so an upper limit on the scale) of
2710 // the rect used to position fixed objects so that they don't crowd into the
2711 // center of the screen at larger scales.
2712 const LayoutUnit maxContentWidthForZoomThreshold = 1024_lu;
2713 float zoomedOutScale = frameScaleFactor * visibleContentRect.width() / std::min(maxContentWidthForZoomThreshold, totalContentsSize.width());
2714 float constraintThresholdScale = 1.5 * zoomedOutScale;
2715 float maxPostionedObjectsRectScale = std::min(frameScaleFactor, constraintThresholdScale);
2716
2717 LayoutRect viewportConstrainedObjectsRect = visibleContentRect;
2718
2719 if (frameScaleFactor > constraintThresholdScale) {
2720 FloatRect contentRect(FloatPoint(), totalContentsSize);
2721 FloatRect viewportRect = visibleContentRect;
2722
2723 // Scale the rect up from a point that is relative to its position in the viewport.
2724 FloatSize sizeDelta = contentRect.size() - viewportRect.size();
2725
2726 FloatPoint scaleOrigin;
2727 scaleOrigin.setX(contentRect.x() + sizeDelta.width() > 0 ? contentRect.width() * (viewportRect.x() - contentRect.x()) / sizeDelta.width() : 0);
2728 scaleOrigin.setY(contentRect.y() + sizeDelta.height() > 0 ? contentRect.height() * (viewportRect.y() - contentRect.y()) / sizeDelta.height() : 0);
2729
2730 AffineTransform rescaleTransform = AffineTransform::makeTranslation(toFloatSize(scaleOrigin));
2731 rescaleTransform.scale(frameScaleFactor / maxPostionedObjectsRectScale, frameScaleFactor / maxPostionedObjectsRectScale);
2732 rescaleTransform = CGAffineTransformTranslate(rescaleTransform, -scaleOrigin.x(), -scaleOrigin.y());
2733
2734 viewportConstrainedObjectsRect = enclosingLayoutRect(rescaleTransform.mapRect(visibleContentRect));
2735 }
2736
2737 if (scrollBehavior == ScrollBehaviorForFixedElements::StickToDocumentBounds) {
2738 LayoutRect documentBounds(LayoutPoint(), totalContentsSize);
2739 viewportConstrainedObjectsRect.intersect(documentBounds);
2740 }
2741
2742 return viewportConstrainedObjectsRect;
2743}
2744
2745LayoutRect LocalFrameView::viewportConstrainedObjectsRect() const
2746{
2747 return rectForViewportConstrainedObjects(visibleContentRect(), totalContentsSize(), m_frame->frameScaleFactor(), fixedElementsLayoutRelativeToFrame(), scrollBehaviorForFixedElements());
2748}
2749#endif
2750
2751ScrollPosition LocalFrameView::minimumScrollPosition() const
2752{
2753 ScrollPosition minimumPosition = ScrollView::minimumScrollPosition();
2754
2755 if (m_frame->isMainFrame() && m_scrollPinningBehavior == ScrollPinningBehavior::PinToBottom)
2756 minimumPosition.setY(maximumScrollPosition().y());
2757
2758 return minimumPosition;
2759}
2760
2761ScrollPosition LocalFrameView::maximumScrollPosition() const
2762{
2763 ScrollPosition maximumPosition = ScrollView::maximumScrollPosition();
2764
2765 if (m_frame->isMainFrame() && m_scrollPinningBehavior == ScrollPinningBehavior::PinToTop)
2766 maximumPosition.setY(minimumScrollPosition().y());
2767
2768 return maximumPosition;
2769}
2770
2771ScrollPosition LocalFrameView::unscaledMinimumScrollPosition() const
2772{
2773 if (CheckedPtr renderView = this->renderView()) {
2774 IntRect unscaledDocumentRect = renderView->unscaledDocumentRect();
2775 ScrollPosition minimumPosition = unscaledDocumentRect.location();
2776
2777 if (m_frame->isMainFrame() && m_scrollPinningBehavior == ScrollPinningBehavior::PinToBottom)
2778 minimumPosition.setY(unscaledMaximumScrollPosition().y());
2779
2780 return minimumPosition;
2781 }
2782
2783 return minimumScrollPosition();
2784}
2785
2786ScrollPosition LocalFrameView::unscaledMaximumScrollPosition() const
2787{
2788 if (CheckedPtr renderView = this->renderView()) {
2789 IntRect unscaledDocumentRect = renderView->unscaledDocumentRect();
2790 unscaledDocumentRect.expand(0, headerHeight() + footerHeight());
2791 ScrollPosition maximumPosition = ScrollPosition(unscaledDocumentRect.maxXMaxYCorner() - visibleSize()).expandedTo({ 0, 0 });
2792 if (m_frame->isMainFrame() && m_scrollPinningBehavior == ScrollPinningBehavior::PinToTop)
2793 maximumPosition.setY(unscaledMinimumScrollPosition().y());
2794
2795 return maximumPosition;
2796 }
2797
2798 return maximumScrollPosition();
2799}
2800
2801void LocalFrameView::viewportContentsChanged()
2802{
2803 if (!m_frame->view()) {
2804 // The frame is being destroyed.
2805 return;
2806 }
2807
2808 if (RefPtr page = m_frame->page())
2809 page->updateValidationBubbleStateIfNeeded();
2810
2811 // When the viewport contents changes (scroll, resize, style recalc, layout, ...),
2812 // check if we should resume animated images or unthrottle DOM timers.
2813 applyRecursivelyWithVisibleRect([] (LocalFrameView& frameView, const IntRect& visibleRect) {
2814 frameView.resumeVisibleImageAnimations(visibleRect);
2815 frameView.updateScriptedAnimationsAndTimersThrottlingState(visibleRect);
2816
2817 if (auto* renderView = frameView.m_frame->contentRenderer())
2818 renderView->updateVisibleViewportRect(visibleRect);
2819 });
2820}
2821
2822IntRect LocalFrameView::viewRectExpandedByContentInsets() const
2823{
2824 FloatRect viewRect;
2825 if (delegatesScrollingToNativeView() && platformWidget())
2826 viewRect = unobscuredContentRect();
2827 else
2828 viewRect = visualViewportRect();
2829
2830 if (auto* page = m_frame->page())
2831 viewRect.expand(page->contentInsets());
2832
2833 return IntRect(viewRect);
2834}
2835
2836bool LocalFrameView::fixedElementsLayoutRelativeToFrame() const
2837{
2838 return m_frame->settings().fixedElementsLayoutRelativeToFrame();
2839}
2840
2841IntPoint LocalFrameView::lastKnownMousePositionInView() const
2842{
2843 return flooredIntPoint(convertFromContainingWindow(m_frame->eventHandler().lastKnownMousePosition()));
2844}
2845
2846bool LocalFrameView::isHandlingWheelEvent() const
2847{
2848 return m_frame->eventHandler().isHandlingWheelEvent();
2849}
2850
2851bool LocalFrameView::shouldSetCursor() const
2852{
2853 Page* page = m_frame->page();
2854 return page && page->isVisible() && page->focusController().isActive();
2855}
2856
2857#if ENABLE(DARK_MODE_CSS)(defined 1 && 1)
2858RenderElement* LocalFrameView::rendererForColorScheme() const
2859{
2860 auto* document = m_frame->document();
2861 auto* documentElement = document ? document->documentElement() : nullptr;
2862 auto* documentElementRenderer = documentElement ? documentElement->renderer() : nullptr;
2863 if (documentElementRenderer && documentElementRenderer->style().hasExplicitlySetColorScheme())
2864 return documentElementRenderer;
2865 return { };
2866}
2867#endif
2868
2869bool LocalFrameView::useDarkAppearance() const
2870{
2871#if ENABLE(DARK_MODE_CSS)(defined 1 && 1)
2872 if (auto* renderer = rendererForColorScheme())
2873 return renderer->useDarkAppearance();
2874#endif
2875 if (RefPtr document = m_frame->document())
2876 return document->useDarkAppearance(static_cast<const Style::ComputedStyle*>(nullptr));
2877 return false;
2878}
2879
2880OptionSet<StyleColorOptions> LocalFrameView::styleColorOptions() const
2881{
2882#if ENABLE(DARK_MODE_CSS)(defined 1 && 1)
2883 if (auto* renderer = rendererForColorScheme())
2884 return renderer->styleColorOptions();
2885#endif
2886 if (RefPtr document = m_frame->document())
2887 return document->styleColorOptions(static_cast<const Style::ComputedStyle*>(nullptr));
2888 return { };
2889}
2890
2891bool LocalFrameView::scrollContentsFastPath(const IntSize& scrollDelta, const IntRect& rectToScroll, const IntRect& clipRect)
2892{
2893 if (!m_viewportConstrainedObjects || m_viewportConstrainedObjects->isEmptyIgnoringNullReferences()) {
2894 m_frame->page()->chrome().scroll(scrollDelta, rectToScroll, clipRect);
2895 return true;
2896 }
2897
2898 bool isCompositedContentLayer = usesCompositedScrolling();
2899
2900 // Get the rects of the fixed objects visible in the rectToScroll
2901 Region regionToUpdate;
2902 for (CheckedRef renderer : *m_viewportConstrainedObjects) {
2903 if (!renderer->style().hasViewportConstrainedPosition())
2904 continue;
2905 if (renderer->isComposited())
2906 continue;
2907
2908 // Fixed items should always have layers.
2909 ASSERT(renderer->hasLayer())((void)0);
2910 CheckedPtr layer = downcast<RenderBoxModelObject>(renderer).layer();
2911
2912 if (layer->viewportConstrainedNotCompositedReason() == RenderLayer::NotCompositedForBoundsOutOfView
2913 || layer->viewportConstrainedNotCompositedReason() == RenderLayer::NotCompositedForNoVisibleContent) {
2914 // Don't invalidate for invisible fixed layers.
2915 continue;
2916 }
2917
2918 if (layer->hasAncestorWithFilterOutsets()) {
2919 // If the fixed layer has a blur/drop-shadow filter applied on at least one of its parents, we cannot
2920 // scroll using the fast path, otherwise the outsets of the filter will be moved around the page.
2921 return false;
2922 }
2923
2924 // FIXME: use pixel snapping instead of enclosing when ScrollView has finished transitioning from IntRect to Float/LayoutRect.
2925 IntRect updateRect = enclosingIntRect(layer->repaintRectIncludingNonCompositingDescendants());
2926 updateRect = contentsToRootView(updateRect);
2927 if (!isCompositedContentLayer)
2928 updateRect.intersect(rectToScroll);
2929 if (!updateRect.isEmpty())
2930 regionToUpdate.unite(updateRect);
2931 }
2932
2933 // 1) scroll
2934 m_frame->page()->chrome().scroll(scrollDelta, rectToScroll, clipRect);
2935
2936 // 2) update the area of fixed objects that has been invalidated
2937 for (auto& updateRect : regionToUpdate.rects()) {
2938 IntRect scrolledRect = updateRect;
2939 scrolledRect.move(scrollDelta);
2940 updateRect.unite(scrolledRect);
2941 if (isCompositedContentLayer) {
2942 updateRect = rootViewToContents(updateRect);
2943 ASSERT(renderView())((void)0);
2944 renderView()->layer()->setBackingNeedsRepaintInRect(updateRect);
2945 continue;
2946 }
2947 updateRect.intersect(rectToScroll);
2948 m_frame->page()->chrome().invalidateContentsAndRootView(updateRect);
2949 }
2950
2951 return true;
2952}
2953
2954void LocalFrameView::scrollContentsSlowPath(const IntRect& updateRect)
2955{
2956 repaintSlowRepaintObjects();
2957
2958 if (!usesCompositedScrolling() && isEnclosedInCompositingLayer()) {
2959 if (RefPtr frameRenderer = m_frame->ownerRenderer()) {
2960 LayoutRect rect(frameRenderer->borderLeft() + frameRenderer->paddingLeft(), frameRenderer->borderTop() + frameRenderer->paddingTop(),
2961 visibleWidth(), visibleHeight());
2962 frameRenderer->repaintRectangle(rect);
2963 return;
2964 }
2965 }
2966
2967 ScrollView::scrollContentsSlowPath(updateRect);
2968}
2969
2970void LocalFrameView::repaintSlowRepaintObjects()
2971{
2972 if (!m_slowRepaintObjects)
2973 return;
2974
2975 // Renderers with fixed backgrounds may be in compositing layers, so we need to explicitly
2976 // repaint them after scrolling.
2977 for (CheckedRef renderer : *m_slowRepaintObjects | dereferenceView)
2978 renderer->repaintSlowRepaintObject();
2979}
2980
2981// Note that this gets called at painting time.
2982void LocalFrameView::setIsOverlapped(bool isOverlapped)
2983{
2984 if (isOverlapped == m_isOverlapped)
2985 return;
2986
2987 m_isOverlapped = isOverlapped;
2988 updateCanBlitOnScrollRecursively();
2989}
2990
2991void LocalFrameView::setContentIsOpaque(bool contentIsOpaque)
2992{
2993 if (contentIsOpaque == m_contentIsOpaque)
2994 return;
2995
2996 m_contentIsOpaque = contentIsOpaque;
2997 updateCanBlitOnScrollRecursively();
2998}
2999
3000void LocalFrameView::restoreScrollbar()
3001{
3002 setScrollbarsSuppressed(false);
3003}
3004
3005bool LocalFrameView::scrollToFragment(const URL& url)
3006{
3007 ASSERT(m_frame->document())((void)0);
3008 Ref document = *m_frame->document();
3009
3010 if (scrollToTextFragment())
3011 return true;
3012
3013 auto fragmentIdentifier = url.fragmentIdentifier();
3014 if (scrollToAnchorFragment(fragmentIdentifier))
3015 return true;
3016
3017 if (scrollToAnchorFragment(PAL::decodeURLEscapeSequences(fragmentIdentifier)))
3018 return true;
3019
3020 resetScrollAnchor();
3021 return false;
3022}
3023
3024bool LocalFrameView::scrollToTextFragment(IsRetry isRetry)
3025{
3026 static constexpr auto scrollToTextFragmentRetryInterval = 500_ms;
3027 static constexpr auto scrollToTextFragmentRetryTimeout = 3_s;
3028
3029 ASSERT(m_frame->document())((void)0);
3030 Ref document = *m_frame->document();
3031
3032 if (!document->settings().scrollToTextFragmentEnabled())
3033 return false;
3034
3035 if (!m_frame->isMainFrame())
3036 return false;
3037
3038 document->fragmentHighlightRegistry().clear();
3039
3040 auto fragmentDirective = document->fragmentDirective();
3041 if (fragmentDirective.isEmpty())
3042 return false;
3043
3044 FragmentDirectiveParser fragmentDirectiveParser(fragmentDirective);
3045 if (!fragmentDirectiveParser.isValid())
3046 return false;
3047
3048 auto parsedTextDirectives = fragmentDirectiveParser.parsedTextDirectives();
3049 auto highlightRanges = FragmentDirectiveRangeFinder::findRangesFromTextDirectives(parsedTextDirectives, document);
3050 if (m_frame->settings().scrollToTextFragmentMarkingEnabled()) {
3051 for (auto range : highlightRanges)
3052 document->fragmentHighlightRegistry().addAnnotationHighlightWithRange(StaticRange::create(range));
3053 }
3054
3055 // If we didn't find the expected range, retry every 500ms, until the first of:
3056 // - we successfully find the range
3057 // - the user scrolls the view
3058 // - the 3s timeout expires
3059 if (highlightRanges.isEmpty()) {
3060 if (isRetry == IsRetry::No) {
3061 m_scrollToTextFragmentRetryTimer.startRepeating(scrollToTextFragmentRetryInterval);
3062 m_scrollToTextFragmentInitialAttemptTime = MonotonicTime::now();
3063 } else if (!m_scrollToTextFragmentInitialAttemptTime || (MonotonicTime::now() - *m_scrollToTextFragmentInitialAttemptTime >= scrollToTextFragmentRetryTimeout) || m_wasEverScrolledExplicitlyByUser) {
3064 m_scrollToTextFragmentRetryTimer.stop();
3065 }
3066
3067 return false;
3068 }
3069
3070 m_scrollToTextFragmentRetryTimer.stop();
3071
3072 auto range = highlightRanges.first();
3073 RefPtr commonAncestor = commonInclusiveAncestor<ComposedTree>(range);
3074 if (commonAncestor && !is<Element>(commonAncestor))
3075 commonAncestor = commonAncestor->parentElement();
3076 if (commonAncestor) {
3077 document->setCSSTarget(downcast<Element>(commonAncestor.get()));
3078 revealClosedDetailsAndHiddenUntilFoundAncestors(*commonAncestor);
3079 }
3080
3081 // FIXME: <http://webkit.org/b/245262> (Scroll To Text Fragment should use DelegateMainFrameScroll)
3082 auto selectionChange = revealRangeWithTemporarySelection(range);
3083 if (m_frame->settings().scrollToTextFragmentIndicatorEnabled() && !m_frame->page()->isControlledByAutomation())
3084 m_delayedTextFragmentIndicatorTimer.startOneShot(100_ms);
3085
3086 maintainScrollPositionAtScrollToTextFragmentRange(range);
3087
3088 return true;
3089}
3090
3091void LocalFrameView::scrollToTextFragmentRetryTimerFired()
3092{
3093 scrollToTextFragment(IsRetry::Yes);
3094}
3095
3096bool LocalFrameView::scrollToAnchorFragment(StringView fragmentIdentifier)
3097{
3098 // If our URL has no ref, then we have no place we need to jump to.
3099 if (fragmentIdentifier.isNull())
3100 return false;
3101
3102 LOG_WITH_STREAM(Scrolling, stream << *this << " scrollToAnchorFragment " << fragmentIdentifier)((void)0);
3103
3104 ASSERT(m_frame->document())((void)0);
3105 Ref document = *m_frame->document();
3106 RELEASE_ASSERT(document->haveStylesheetsLoaded())do { if (__builtin_expect(!!(!(document->haveStylesheetsLoaded
())), 0)) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash
(); WTFCrashWithInfo(3106, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/page/LocalFrameView.cpp"
, __PRETTY_FUNCTION__ ); } while (false); } while (0)
;
3107
3108 RefPtr anchorElement = document->findAnchor(fragmentIdentifier);
3109
3110 LOG(Scrolling, " anchorElement is %p", anchorElement.get())((void)0);
3111
3112 // Setting to null will clear the current target.
3113 document->setCSSTarget(anchorElement.get());
3114
3115 if (is<SVGDocument>(document.get())) {
3116 if (fragmentIdentifier.isEmpty())
3117 return false;
3118 if (auto rootElement = DocumentSVG::rootElement(document.get())) {
3119 if (rootElement->scrollToFragment(fragmentIdentifier))
3120 return true;
3121 // If SVG failed to scrollToAnchor() and anchorElement is null, no other scrolling will be possible.
3122 if (!anchorElement)
3123 return false;
3124 }
3125 } else if (!anchorElement && !(fragmentIdentifier.isEmpty() || equalLettersIgnoringASCIICase(fragmentIdentifier, "top"_s))) {
3126 // Implement the rule that "" and "top" both mean top of page as in other browsers.
3127 return false;
3128 }
3129
3130 if (anchorElement)
3131 revealClosedDetailsAndHiddenUntilFoundAncestors(*anchorElement);
3132
3133 RefPtr<ContainerNode> scrollPositionAnchor = anchorElement;
3134 if (!scrollPositionAnchor)
3135 scrollPositionAnchor = m_frame->document();
3136 maintainScrollPositionAtAnchor(scrollPositionAnchor.get());
3137
3138 if (anchorElement) {
3139 // If the anchor accepts keyboard focus, move focus there to aid users relying on keyboard navigation.
3140 if (anchorElement->isFocusable())
3141 document->setFocusedElement(anchorElement.get(), { { }, { }, { }, { }, { }, { }, FocusVisibility::Visible });
3142 else {
3143 document->setFocusedElement(nullptr);
3144 document->setFocusNavigationStartingNode(anchorElement.get());
3145 }
3146 }
3147
3148 return true;
3149}
3150
3151void LocalFrameView::maintainScrollPositionAtAnchor(ContainerNode* anchorNode)
3152{
3153 LOG(Scrolling, "LocalFrameView::maintainScrollPositionAtAnchor at %p", anchorNode)((void)0);
3154
3155 m_maintainScrollPositionAnchor = anchorNode;
3156 if (!m_maintainScrollPositionAnchor)
3157 return;
3158
3159 cancelScheduledScrolls();
3160
3161 if (RefPtr element = dynamicDowncast<Element>(anchorNode))
3162 m_frame->document()->updateContentRelevancyForScrollIfNeeded(*element);
3163
3164 // We need to update the layout before scrolling, otherwise we could
3165 // really mess things up if an anchor scroll comes at a bad moment.
3166 m_frame->document()->updateStyleIfNeeded();
3167 // Only do a layout if changes have occurred that make it necessary.
3168 CheckedPtr renderView = this->renderView();
3169 if (renderView && renderView->needsLayout())
3170 layoutContext().layout();
3171 else
3172 scheduleScrollToAnchorAndTextFragment();
3173
3174 scrollToAnchorAndTextFragmentNowIfNeeded();
3175}
3176
3177void LocalFrameView::maintainScrollPositionAtScrollToTextFragmentRange(SimpleRange& range)
3178{
3179 m_pendingTextFragmentIndicatorRange = range;
3180 m_pendingTextFragmentIndicatorText = plainText(range);
3181 if (!m_pendingTextFragmentIndicatorRange)
3182 return;
3183
3184 scrollToPendingTextFragmentRange();
3185}
3186
3187void LocalFrameView::scrollElementToRect(const Element& element, const IntRect& rect)
3188{
3189 protect(m_frame->document())->updateLayoutIgnorePendingStylesheets();
3190
3191 LayoutRect bounds;
3192 if (RenderElement* renderer = element.renderer())
3193 bounds = renderer->absoluteAnchorRect();
3194 int centeringOffsetX = (rect.width() - bounds.width()) / 2;
3195 int centeringOffsetY = (rect.height() - bounds.height()) / 2;
3196 setScrollPosition(IntPoint(bounds.x() - centeringOffsetX - rect.x(), bounds.y() - centeringOffsetY - rect.y()));
3197}
3198
3199ScrollableArea* LocalFrameView::scrollableAreaForNode(ContainerNode& node)
3200{
3201 if (node.isDocumentNode())
3202 return this;
3203
3204 if (CheckedPtr renderer = node.renderer()) {
3205 if (auto* renderListBox = dynamicDowncast<RenderListBox>(*renderer))
3206 return renderListBox;
3207
3208 if (CheckedPtr layer = renderer->enclosingLayer()) {
3209 if (CheckedPtr scrollableLayer = layer->enclosingScrollableLayer(IncludeSelfOrNot::IncludeSelf, CrossFrameBoundaries::No))
3210 return scrollableLayer->scrollableArea();
3211 }
3212 }
3213 return nullptr;
3214}
3215
3216void LocalFrameView::setScrollOffsetWithOptions(std::optional<int> x, std::optional<int> y, const ScrollPositionChangeOptions& options)
3217{
3218 if (x && y) {
3219 setScrollOffsetWithOptions({ *x, *y }, options);
3220 return;
3221 }
3222
3223 auto offset = scrollOffsetFromPosition(scrollPosition());
3224
3225 if (x)
3226 offset.setX(*x);
3227
3228 if (y)
3229 offset.setY(*y);
3230
3231 setScrollOffsetWithOptions(offset, options);
3232}
3233
3234void LocalFrameView::setScrollOffsetWithOptions(const ScrollOffset& scrollOffset, const ScrollPositionChangeOptions& options)
3235{
3236 LOG_WITH_STREAM(Scrolling, stream << "LocalFrameView::setScrollOffset " << scrollOffset << " animated " << (options.animated == ScrollIsAnimated::Yes) << ", clearing anchor")((void)0);
3237
3238 auto scrollTypeScope = ScrollTypeScope(*this, options.type);
3239
3240 m_maintainScrollPositionAnchor = nullptr;
3241 cancelScheduledScrolls();
3242
3243 RefPtr page = m_frame->page();
3244 if (page && page->isMonitoringWheelEvents())
3245 scrollAnimator().setWheelEventTestMonitor(page->wheelEventTestMonitor());
3246
3247 ScrollOffset snappedOffset = ceiledIntPoint(scrollAnimator().scrollOffsetAdjustedForSnapping(scrollOffset, options.snapPointSelectionMethod));
3248 auto snappedPosition = scrollPositionFromOffset(snappedOffset);
3249
3250 if (options.animated == ScrollIsAnimated::Yes)
3251 scrollToPositionWithAnimation(snappedPosition, options);
3252 else
3253 ScrollView::setScrollPosition(snappedPosition, options);
3254}
3255
3256void LocalFrameView::scrollToEdgeWithOptions(RectEdges<bool> edges, const ScrollPositionChangeOptions& options)
3257{
3258 LOG_WITH_STREAM(Scrolling, stream << "LocalFrameView::scrollToEdgeWithOptions " << edges << " animated " << (options.animated == ScrollIsAnimated::Yes) << ", clearing anchor")((void)0);
3259
3260 ASSERT(std::ranges::count_if(WebCore::allBoxSides, [edges](auto side) { return edges[side]; }) == 1)((void)0);
3261
3262 auto currentOffset = scrollOffsetFromPosition(scrollPosition());
3263 auto minimumScrollOffset = scrollOffsetFromPosition(minimumScrollPosition());
3264 auto maximumScrollOffset = scrollOffsetFromPosition(maximumScrollPosition());
3265
3266 if (edges.top())
3267 currentOffset.setY(minimumScrollOffset.y());
3268 else if (edges.left())
3269 currentOffset.setX(minimumScrollOffset.x());
3270 else if (edges.bottom())
3271 currentOffset.setY(maximumScrollOffset.y());
3272 else if (edges.right())
3273 currentOffset.setX(maximumScrollOffset.x());
3274
3275 setScrollOffsetWithOptions(currentOffset, options);
3276}
3277
3278void LocalFrameView::setScrollPosition(const ScrollPosition& scrollPosition, const ScrollPositionChangeOptions& options)
3279{
3280 LOG_WITH_STREAM(Scrolling, stream << "LocalFrameView::setScrollPosition " << scrollPosition << " animated " << (options.animated == ScrollIsAnimated::Yes) << ", clearing anchor")((void)0);
3281
3282 setScrollOffsetWithOptions(scrollOffsetFromPosition(scrollPosition), options);
3283}
3284
3285void LocalFrameView::resetScrollAnchor()
3286{
3287 ASSERT(m_frame->document())((void)0);
3288 Ref document = *m_frame->document();
3289
3290 // If CSS target was set previously, we want to set it to 0, recalc
3291 // and possibly repaint because :target pseudo class may have been
3292 // set (see bug 11321).
3293 document->setCSSTarget(nullptr);
3294
3295 if (is<SVGDocument>(document.get())) {
3296 if (auto rootElement = DocumentSVG::rootElement(document.get())) {
3297 // We need to update the layout before resetScrollAnchor(), otherwise we
3298 // could really mess things up if resetting the anchor comes at a bad moment.
3299 document->updateStyleIfNeeded();
3300 rootElement->resetScrollAnchor();
3301 }
3302 }
3303}
3304
3305void LocalFrameView::scheduleScrollToFocusedElement(SelectionRevealMode selectionRevealMode)
3306{
3307 if (selectionRevealMode == SelectionRevealMode::DoNotReveal)
3308 return;
3309
3310 m_selectionRevealModeForFocusedElement = selectionRevealMode;
3311 if (m_shouldScrollToFocusedElement)
3312 return;
3313 m_shouldScrollToFocusedElement = true;
3314 m_delayedScrollToFocusedElementTimer.startOneShot(0_s);
3315}
3316
3317void LocalFrameView::cancelScheduledScrolls()
3318{
3319 cancelScheduledScrollToFocusedElement();
3320 cancelScheduledTextFragmentIndicatorTimer();
3321}
3322
3323void LocalFrameView::cancelScheduledScrollToFocusedElement()
3324{
3325 m_shouldScrollToFocusedElement = false;
3326 m_delayedScrollToFocusedElementTimer.stop();
3327}
3328
3329void LocalFrameView::scrollToFocusedElementImmediatelyIfNeeded()
3330{
3331 if (!m_shouldScrollToFocusedElement)
3332 return;
3333
3334 m_delayedScrollToFocusedElementTimer.stop();
3335 scrollToFocusedElementInternal();
3336}
3337
3338void LocalFrameView::scrollToFocusedElementTimerFired()
3339{
3340 scrollToFocusedElementInternal();
3341}
3342
3343void LocalFrameView::scrollToFocusedElementInternal()
3344{
3345 RELEASE_ASSERT(m_shouldScrollToFocusedElement)do { if (__builtin_expect(!!(!(m_shouldScrollToFocusedElement
)), 0)) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash
(); WTFCrashWithInfo(3345, "/Volumes/Data/worker/Apple-iOS-26-Safer-CPP-Checks-EWS/build/Source/WebCore/page/LocalFrameView.cpp"
, __PRETTY_FUNCTION__ ); } while (false); } while (0)
;
3346 RefPtr document = m_frame->document();
3347 if (!document)
3348 return;
3349
3350 document->updateLayoutIgnorePendingStylesheets();
3351 if (!m_shouldScrollToFocusedElement || m_delayedScrollToFocusedElementTimer.isActive())
3352 return; // Updating the layout may have ran scripts.
3353
3354 m_shouldScrollToFocusedElement = false;
3355
3356 RefPtr focusedElement = document->focusedElement();
3357 if (!focusedElement)
3358 return;
3359 auto updateTarget = focusedElement->focusAppearanceUpdateTarget();
3360 if (!updateTarget)
3361 return;
3362
3363 // Get the scroll-margin of the shadow host when we're inside a user agent shadow root.
3364 if (updateTarget->containingShadowRoot() && updateTarget->containingShadowRoot()->mode() == ShadowRootMode::UserAgent)
3365 updateTarget = updateTarget->shadowHost();
3366
3367 CheckedPtr renderer = updateTarget->renderer();
3368 if (!renderer || renderer->isRenderWidget())
3369 return;
3370
3371 bool insideFixed;
3372 auto absoluteBounds = renderer->absoluteAnchorRectWithScrollMargin(&insideFixed);
3373 auto anchorRectWithScrollMargin = absoluteBounds.marginRect;
3374 auto anchorRect = absoluteBounds.anchorRect;
3375 LocalFrameView::scrollRectToVisible(anchorRectWithScrollMargin, *renderer, insideFixed, { m_selectionRevealModeForFocusedElement, ScrollAlignment::alignCenterIfNeeded, ScrollAlignment::alignCenterIfNeeded, ShouldAllowCrossOriginScrolling::No, ScrollBehavior::Auto, OnlyAllowForwardScrolling::No, AllowScrollingOverflowHidden::Yes, anchorRect });
3376}
3377
3378void LocalFrameView::textFragmentIndicatorTimerFired()
3379{
3380 ASSERT(m_frame->document())((void)0);
3381 Ref document = *m_frame->document();
3382
3383 m_delayedTextFragmentIndicatorTimer.stop();
3384
3385 if (!m_pendingTextFragmentIndicatorRange)
3386 return;
3387
3388 if (m_pendingTextFragmentIndicatorText != plainText(m_pendingTextFragmentIndicatorRange.value()))
3389 return;
3390
3391 auto range = m_pendingTextFragmentIndicatorRange.value();
3392
3393 // FIXME: <http://webkit.org/b/245262> (Scroll To Text Fragment should use DelegateMainFrameScroll)
3394 auto selectionChange = revealRangeWithTemporarySelection(range);
3395
3396 maintainScrollPositionAtScrollToTextFragmentRange(range);
3397}
3398
3399void LocalFrameView::cancelScheduledTextFragmentIndicatorTimer()
3400{
3401 if (m_skipScrollResetOfScrollToTextFragmentRange)
3402 return;
3403 m_pendingTextFragmentIndicatorRange.reset();
3404 m_pendingTextFragmentIndicatorText = String();
3405 m_haveCreatedTextIndicator = false;
3406 m_delayedTextFragmentIndicatorTimer.stop();
3407}
3408
3409static void NODELETE[[clang::annotate_type("webkit.nodelete")]] adjustScrollRectToVisibleOptionsForHiddenOverflow(ScrollRectToVisibleOptions& options, const RenderStyle& style)
3410{
3411 if (options.allowScrollingOverflowHidden == AllowScrollingOverflowHidden::Yes)
3412 return;
3413 if (style.overflowX() == Overflow::Hidden)
3414 options.alignX = ScrollAlignment::noScroll;
3415 if (style.overflowY() == Overflow::Hidden)
3416 options.alignY = ScrollAlignment::noScroll;
3417}
3418
3419static void adjustForScrollByAnchor(const RenderLayerModelObject& renderer, EnumSet<BoxAxis>& isFixed)
3420{
3421 if (!renderer.isFixedPositioned())
3422 return;
3423 auto* renderBox = dynamicDowncast<RenderBox>(&renderer);
3424 ASSERT(renderBox)((void)0);
3425
3426 if (!renderBox || !Style::AnchorPositionEvaluator::isAnchorPositioned(renderBox->style()))
3427 return;
3428 auto adjuster = renderBox->layoutContext().anchorScrollAdjusterFor(*renderBox);
3429 if (!adjuster || !adjuster->hasViewportSnapshot())
3430 return;
3431
3432 if (adjuster->mayNeedXAdjustment())
3433 isFixed.remove(BoxAxis::Horizontal);
3434 if (adjuster->mayNeedYAdjustment())
3435 isFixed.remove(BoxAxis::Vertical);
3436}
3437
3438bool LocalFrameView::scrollRectToVisible(const LayoutRect& absoluteRect, const RenderObject& renderer, bool insideFixed, const ScrollRectToVisibleOptions& options)
3439{
3440 if (options.revealMode == SelectionRevealMode::DoNotReveal)
3441 return false;
3442
3443 if (renderer.isSkippedContent())
3444 return false;
3445
3446 CheckedPtr layer = renderer.enclosingLayer();
3447 if (!layer)
3448 return false;
3449
3450 // FIXME: It would be nice to use RenderLayer::enclosingScrollableLayer here, but that seems to skip overflow:hidden layers.
3451 auto adjustedRect = absoluteRect;
3452 ScrollRectToVisibleOptions adjustedOptions = options;
3453 EnumSet<BoxAxis> isFixed(insideFixed ? EnumSet<BoxAxis> { BoxAxis::Horizontal, BoxAxis::Vertical } : EnumSet<BoxAxis> { });
3454
3455 for (; layer; layer = layer->enclosingContainingBlockLayer(CrossFrameBoundaries::No)) {
3456 if (layer->shouldTryToScrollForScrollIntoView(adjustedOptions)) {
3457 adjustScrollRectToVisibleOptionsForHiddenOverflow(adjustedOptions, layer->renderer().style());
3458 adjustedRect = layer->ensureLayerScrollableArea()->scrollRectToVisible(adjustedRect, adjustedOptions);
3459 if (adjustedOptions.visibilityCheckRect)
3460 adjustedOptions.visibilityCheckRect->setLocation(adjustedRect.location());
3461 }
3462 // FIXME: Make scroll adjustments work for more than just fixedpos elements.
3463 if (insideFixed)
3464 adjustForScrollByAnchor(layer->renderer(), isFixed);
3465 }
3466
3467 auto& frameView = renderer.view().frameView();
3468 RefPtr ownerElement = frameView.m_frame->document() ? frameView.m_frame->document()->ownerElement() : nullptr;
3469 if (ownerElement && ownerElement->renderer())
3470 frameView.scrollRectToVisibleInChildView(adjustedRect, isFixed, adjustedOptions, ownerElement.get());
3471 else
3472 frameView.scrollRectToVisibleInTopLevelView(adjustedRect, isFixed, adjustedOptions);
3473 return true;
3474}
3475
3476TemporarySelectionChange LocalFrameView::revealRangeWithTemporarySelection(const SimpleRange& range, OptionSet<TemporarySelectionOption> extraOptions)
3477{
3478 static constexpr OptionSet defaultOptions {
3479 TemporarySelectionOption::RevealSelection,
3480 TemporarySelectionOption::RevealSelectionBounds,
3481 TemporarySelectionOption::UserTriggered,
3482 TemporarySelectionOption::ForceCenterScroll
3483 };
3484 return { protect(range.startContainer().document()), { range }, defaultOptions | extraOptions };
3485}
3486
3487static ScrollPositionChangeOptions scrollPositionChangeOptionsForElement(const LocalFrameView& frameView, Element* element, const ScrollRectToVisibleOptions& options)
3488{
3489 auto scrollPositionOptions = ScrollPositionChangeOptions::createProgrammatic();
3490 if (!frameView.frame().eventHandler().autoscrollInProgress()
3491 && element
3492 && useSmoothScrolling(options.behavior, element))
3493 scrollPositionOptions.animated = ScrollIsAnimated::Yes;
3494 return scrollPositionOptions;
3495};
3496
3497void LocalFrameView::scrollRectToVisibleInChildView(const LayoutRect& absoluteRect, EnumSet<BoxAxis> isFixed, const ScrollRectToVisibleOptions& options, const HTMLFrameOwnerElement* ownerElement)
3498{
3499 // If scrollbars aren't explicitly forbidden, permit scrolling.
3500 if (m_frame->scrollingMode() == ScrollbarMode::AlwaysOff) {
3501 // If scrollbars are forbidden, user initiated scrolls should obviously be ignored.
3502 if (m_lastUserScrollType == UserScrollType::Explicit)
3503 return;
3504 // Forbid autoscrolls when scrollbars are off, but permits other programmatic scrolls, like navigation to an anchor.
3505 if (m_frame->eventHandler().autoscrollInProgress())
3506 return;
3507 }
3508
3509 // If this assertion fires we need to protect the ownerElement from being destroyed.
3510 ScriptDisallowedScope::InMainThread scriptDisallowedScope;
3511 auto viewRect = visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect);
3512
3513 // scroll-padding applies to the scroll container, but expand the rectangle that we want to expose in order
3514 // simulate padding the scroll container. This rectangle is passed up the tree of scrolling elements to
3515 // ensure that the padding on this scroll container is maintained.
3516 auto targetRect = absoluteRect;
3517 RefPtr element = ownerElement->contentDocument() ? ownerElement->contentDocument()->documentElement() : nullptr;
3518 if (auto* renderer = element ? element->renderBox() : nullptr)
3519 targetRect.expand(renderer->scrollPaddingForViewportRect(viewRect));
3520
3521 auto revealRect = getPossiblyFixedRectToExpose(viewRect, targetRect, isFixed, options.alignX, options.alignY);
3522 auto scrollPosition = roundedIntPoint(revealRect.location());
3523 scrollPosition = constrainedScrollPosition(scrollPosition);
3524
3525 // FIXME: Should we use contentDocument()->scrollingElement()?
3526 // See https://bugs.webkit.org/show_bug.cgi?id=205059
3527 setScrollPosition(scrollPosition, scrollPositionChangeOptionsForElement(*this, element.get(), options));
3528
3529 if (options.shouldAllowCrossOriginScrolling == ShouldAllowCrossOriginScrolling::No && !safeToPropagateScrollToParent())
3530 return;
3531
3532 // FIXME: ideally need to determine if this <iframe> is inside position:fixed.
3533 if (auto* ownerRenderer = ownerElement->renderer())
3534 scrollRectToVisible(contentsToContainingViewContents(enclosingIntRect(targetRect)), *ownerRenderer, false /* insideFixed */, options);
3535}
3536
3537void LocalFrameView::scrollRectToVisibleInTopLevelView(const LayoutRect& absoluteRect, EnumSet<BoxAxis> isFixed, const ScrollRectToVisibleOptions& options)
3538{
3539 if (options.revealMode == SelectionRevealMode::RevealUpToMainFrame && m_frame->isMainFrame())
3540 return;
3541
3542 RefPtr page = m_frame->page();
3543 if (!page)
3544 return;
3545
3546 if (options.revealMode == SelectionRevealMode::DelegateMainFrameScroll && m_frame->isMainFrame()) {
3547 page->chrome().scrollMainFrameToRevealRect(snappedIntRect(absoluteRect));
3548 return;
3549 }
3550
3551 auto minScrollPosition = minimumScrollPosition();
3552 auto maxScrollPosition = maximumScrollPosition();
3553
3554#if !PLATFORM(IOS_FAMILY)(defined 1 && 1)
3555 auto viewRect = visibleContentRect();
3556#else
3557 // FIXME: ContentInsets should be taken care of in UI process side. webkit.org/b/199682
3558 // To do that, getRectToExposeForScrollIntoView needs to return the additional scrolling to do beyond content rect.
3559 LayoutRect viewRect = viewRectExpandedByContentInsets();
3560
3561 // FIXME: webkit.org/b/199683 LocalFrameView::visibleContentRect is wrong when content insets are present
3562 maxScrollPosition = scrollPositionFromOffset(ScrollPosition(totalContentsSize() - flooredIntSize(viewRect.size())));
3563
3564 auto contentInsets = page->contentInsets();
3565 minScrollPosition.move(-contentInsets.left(), -contentInsets.top());
3566 maxScrollPosition.move(contentInsets.right(), contentInsets.bottom());
3567#endif
3568 // Move the target rect into "scrollView contents" coordinates.
3569 auto targetRect = absoluteRect;
3570 targetRect.move(0, headerHeight());
3571
3572 // scroll-padding applies to the scroll container, but expand the rectangle that we want to expose in order
3573 // simulate padding the scroll container. This rectangle is passed up the tree of scrolling elements to
3574 // ensure that the padding on this scroll container is maintained.
3575 RefPtr element = m_frame->document() ? m_frame->document()->documentElement() : nullptr;
3576 if (auto* renderBox = element ? element->renderBox() : nullptr)
3577 targetRect.expand(renderBox->scrollPaddingForViewportRect(viewRect));
3578
3579 LayoutRect revealRect = getPossiblyFixedRectToExpose(viewRect, targetRect, isFixed, options.alignX, options.alignY);
3580
3581 // Avoid scrolling to the rounded value of revealRect.location() if we don't actually need to scroll
3582 if (revealRect != viewRect) {
3583 if (options.onlyAllowForwardScrolling == OnlyAllowForwardScrolling::No || (revealRect.y() > viewRect.y())) {
3584
3585 // FIXME: Should we use document()->scrollingElement()?
3586 // See https://bugs.webkit.org/show_bug.cgi?id=205059
3587 ScrollOffset clampedScrollPosition = roundedIntPoint(revealRect.location()).constrainedBetween(minScrollPosition, maxScrollPosition);
3588 setScrollPosition(clampedScrollPosition, scrollPositionChangeOptionsForElement(*this, element.get(), options));
3589 }
3590 }
3591
3592 // This is the outermost view of a web page, so after scrolling this view we
3593 // scroll its container by calling Page::scrollMainFrameToRevealRect.
3594 // This only has an effect on the Mac platform in applications
3595 // that put web views into scrolling containers, such as macOS Mail.
3596 // The canAutoscroll function in EventHandler also knows about this.
3597 page->chrome().scrollContainingScrollViewsToRevealRect(snappedIntRect(absoluteRect));
3598}
3599
3600void LocalFrameView::contentsResized()
3601{
3602 // For non-delegated scrolling, updateTiledBackingAdaptiveSizing() is called via addedOrRemovedScrollbar() which occurs less often.
3603 if (delegatesScrollingToNativeView())
3604 updateTiledBackingAdaptiveSizing();
3605}
3606
3607void LocalFrameView::delegatedScrollingModeDidChange()
3608{
3609 CheckedPtr renderView = this->renderView();
3610 if (!renderView)
3611 return;
3612
3613 RenderLayerCompositor& compositor = renderView->compositor();
3614 // When we switch to delegatesScrolling mode, we should destroy the scrolling/clipping layers in RenderLayerCompositor.
3615 // FIXME: Is this right? What turns compositing back on?
3616 if (compositor.usesCompositing()) {
3617 ASSERT(compositor.usesCompositing())((void)0);
3618 compositor.enableCompositingMode(false);
3619 compositor.clearBackingForAllLayers();
3620 }
3621}
3622
3623void LocalFrameView::setViewportConstrainedObjectsNeedLayout()
3624{
3625 if (!hasViewportConstrainedObjects())
3626 return;
3627
3628 for (CheckedRef renderer : *m_viewportConstrainedObjects) {
3629 renderer->setNeedsLayout();
3630 if (renderer->hasLayer()) {
3631 CheckedPtr layer = downcast<RenderBoxModelObject>(renderer).layer();
3632 layer->setNeedsCompositingGeometryUpdate();
3633 }
3634 }
3635}
3636
3637void LocalFrameView::didChangeScrollOffset()
3638{
3639 if (RefPtr page = m_frame->page()) {
3640 page->pageOverlayController().didScrollFrame(m_frame.get());
3641 InspectorInstrumentation::didScroll(*page);
3642 }
3643
3644 m_frame->loader().client().didChangeScrollOffset();
3645}
3646
3647void LocalFrameView::scrollOffsetChangedViaPlatformWidgetImpl(const ScrollOffset& oldOffset, const ScrollOffset& newOffset)
3648{
3649#if PLATFORM(COCOA)(defined 1 && 1)
3650 auto deferrer = WheelEventTestMonitorCompletionDeferrer { m_frame->page()->wheelEventTestMonitor().get(), scrollingNodeIDForTesting(), WheelEventTestMonitor::DeferReason::ContentScrollInProgress };
3651#endif
3652
3653 updateLayerPositionsAfterScrolling();
3654 updateCompositingLayersAfterScrolling();
3655 repaintSlowRepaintObjects();
3656 scrollPositionChanged(scrollPositionFromOffset(oldOffset), scrollPositionFromOffset(newOffset));
3657 clearScrollAnchor();
3658
3659 if (auto* renderView = this->renderView()) {
3660 if (renderView->usesCompositing())
3661 renderView->compositor().didChangeVisibleRect();
3662 }
3663}
3664
3665// These scroll positions are affected by zooming.
3666void LocalFrameView::scrollPositionChanged(const ScrollPosition& oldPosition, const ScrollPosition& newPosition)
3667{
3668 UNUSED_PARAM(oldPosition)(void)oldPosition;
3669 UNUSED_PARAM(newPosition)(void)newPosition;
3670
3671 RefPtr page = m_frame->page();
3672 Seconds throttlingDelay = page ? page->chrome().client().eventThrottlingDelay() : 0_s;
3673
3674 if (throttlingDelay == 0_s) {
3675 m_delayedScrollEventTimer.stop();
3676 scheduleScrollEvent();
3677 } else if (!m_delayedScrollEventTimer.isActive())
3678 m_delayedScrollEventTimer.startOneShot(throttlingDelay);
3679
3680 if (CheckedPtr renderView = this->renderView()) {
3681 if (renderView->usesCompositing())
3682 renderView->compositor().frameViewDidScroll();
3683 }
3684
3685 LOG_WITH_STREAM(Scrolling, stream << "LocalFrameView " << this << " scrollPositionChanged from " << oldPosition << " to " << newPosition << " (scale " << frameScaleFactor() << " )")((void)0);
3686
3687 updateLayoutViewport();
3688 viewportContentsChanged();
3689
3690 if (auto* renderView = this->renderView()) {
3691 if (CheckedPtr layer = renderView->layer())
3692 m_frame->editor().renderLayerDidScroll(*layer);
3693 }
3694}
3695
3696void LocalFrameView::applyRecursivelyWithVisibleRect(NOESCAPE[[clang::noescape]] const Function<void(LocalFrameView& frameView, const IntRect& visibleRect)>& apply)
3697{
3698 IntRect windowClipRect = this->windowClipRect();
3699 auto visibleRect = windowToContents(windowClipRect);
3700 apply(*this, visibleRect);
3701
3702 // Recursive call for subframes. We cache the current LocalFrameView's windowClipRect to avoid recomputing it for every subframe.
3703 SetForScope windowClipRectCache(m_cachedWindowClipRect, &windowClipRect);
3704 for (RefPtr childFrame = m_frame->tree().firstChild(); childFrame; childFrame = childFrame->tree().nextSibling()) {
3705 RefPtr localFrame = dynamicDowncast<LocalFrame>(childFrame);
3706 if (!localFrame)
3707 continue;
3708 if (RefPtr childView = localFrame->view())
3709 childView->applyRecursivelyWithVisibleRect(apply);
3710 }
3711}
3712
3713void LocalFrameView::resumeVisibleImageAnimations(const IntRect& visibleRect)
3714{
3715 if (visibleRect.isEmpty())
3716 return;
3717
3718 if (auto* renderView = m_frame->contentRenderer())
3719 renderView->resumePausedImageAnimationsIfNeeded(visibleRect);
3720}
3721
3722void LocalFrameView::updateScriptedAnimationsAndTimersThrottlingState(const IntRect& visibleRect)
3723{
3724 if (m_frame->isMainFrame())
3725 return;
3726
3727 RefPtr document = m_frame->document();
3728 if (!document)
3729 return;
3730
3731 // We don't throttle zero-size or display:none frames because those are usually utility frames.
3732 bool shouldThrottle = visibleRect.isEmpty() && !m_lastUsedSizeForLayout.isEmpty() && m_frame->ownerRenderer();
3733 document->setTimerThrottlingEnabled(shouldThrottle);
3734
3735 RefPtr page = m_frame->page();
3736 if (!page || !page->canUpdateThrottlingReason(ThrottlingReason::OutsideViewport))
3737 return;
3738
3739 RefPtr scriptedAnimationController = document->scriptedAnimationController();
3740 if (!scriptedAnimationController)
3741 return;
3742
3743 if (shouldThrottle)
3744 scriptedAnimationController->addThrottlingReason(ThrottlingReason::OutsideViewport);
3745 else
3746 scriptedAnimationController->removeThrottlingReason(ThrottlingReason::OutsideViewport);
3747}
3748
3749
3750void LocalFrameView::resumeVisibleImageAnimationsIncludingSubframes()
3751{
3752 applyRecursivelyWithVisibleRect([] (LocalFrameView& frameView, const IntRect& visibleRect) {
3753 frameView.resumeVisibleImageAnimations(visibleRect);
3754 });
3755}
3756
3757#if ENABLE(ACCESSIBILITY_ANIMATION_CONTROL)(defined 1 && 1)
3758void LocalFrameView::updatePlayStateForAllAnimations(const IntRect& visibleRect)
3759{
3760 if (auto* renderView = m_frame->contentRenderer())
3761 renderView->updatePlayStateForAllAnimations(visibleRect);
3762}
3763
3764void LocalFrameView::updatePlayStateForAllAnimationsIncludingSubframes()
3765{
3766 applyRecursivelyWithVisibleRect([] (LocalFrameView& frameView, const IntRect& visibleRect) {
3767 frameView.updatePlayStateForAllAnimations(visibleRect);
3768 });
3769}
3770#endif // ENABLE(ACCESSIBILITY_ANIMATION_CONTROL)
3771
3772void LocalFrameView::updateLayerPositionsAfterScrolling()
3773{
3774 // If we're scrolling as a result of updating the view size after layout, we'll update widgets and layer positions soon anyway.
3775 if (layoutContext().layoutPhase() == LocalFrameViewLayoutContext::LayoutPhase::InViewSizeAdjust)
3776 return;
3777
3778 if (!layoutContext().isLayoutNested() && hasViewportConstrainedObjects()) {
3779 if (CheckedPtr renderView = this->renderView()) {
3780 updateWidgetPositions();
3781 layoutContext().flushUpdateLayerPositions();
3782 renderView->layer()->updateLayerPositionsAfterDocumentScroll();
3783 }
3784 }
3785}
3786
3787void LocalFrameView::updateLayerPositionsAfterOverflowScroll(RenderLayer& layer)
3788{
3789 layoutContext().flushUpdateLayerPositions();
3790 layer.updateLayerPositionsAfterOverflowScroll();
3791 scheduleUpdateWidgetPositions();
3792}
3793
3794ScrollingCoordinator* LocalFrameView::scrollingCoordinator() const
3795{
3796 return m_frame->page() ? m_frame->page()->scrollingCoordinator() : nullptr;
3797}
3798
3799bool LocalFrameView::shouldUpdateCompositingLayersAfterScrolling() const
3800{
3801#if ENABLE(ASYNC_SCROLLING)(defined 1 && 1)
3802 // If the scrolling thread is updating the fixed elements, then the LocalFrameView should not update them as well.
3803
3804 RefPtr page = m_frame->page();
3805 if (!page)
3806 return true;
3807
3808 if (&page->mainFrame() != m_frame.ptr())
3809 return true;
3810
3811 RefPtr scrollingCoordinator = this->scrollingCoordinator();
3812 if (!scrollingCoordinator)
3813 return true;
3814
3815 if (scrollingCoordinator->shouldUpdateScrollLayerPositionSynchronously(*this))
3816 return true;
3817
3818 if (currentScrollType() == ScrollType::Programmatic)
3819 return true;
3820
3821 // FIXME: Implement anchor positioning in the scrolling tree.
3822 if (hasAnchorPositionedViewportConstrainedObjects())
3823 return true;
3824
3825 return false;
3826#endif
3827 return true;
3828}
3829
3830void LocalFrameView::updateCompositingLayersAfterScrolling()
3831{
3832 ASSERT(layoutContext().layoutPhase() >= LocalFrameViewLayoutContext::LayoutPhase::InPostLayout || layoutContext().layoutPhase() == LocalFrameViewLayoutContext::LayoutPhase::OutsideLayout)((void)0);
3833
3834 if (!shouldUpdateCompositingLayersAfterScrolling())
3835 return;
3836
3837 if (!layoutContext().isLayoutNested() && hasViewportConstrainedObjects()) {
3838 if (CheckedPtr renderView = this->renderView())
3839 renderView->compositor().updateCompositingLayers(CompositingUpdateType::OnScroll);
3840 }
3841}
3842
3843bool LocalFrameView::isUserScrollInProgress() const
3844{
3845 if (RefPtr scrollingCoordinator = this->scrollingCoordinator()) {
3846 if (scrollingCoordinator->isUserScrollInProgress(scrollingNodeID()))
3847 return true;
3848 }
3849
3850 if (auto scrollAnimator = existingScrollAnimator())
3851 return scrollAnimator->isUserScrollInProgress();
3852
3853 return false;
3854}
3855
3856bool LocalFrameView::isRubberBandInProgress() const
3857{
3858 if (scrollbarsSuppressed())
3859 return false;
3860
3861 if (RefPtr scrollingCoordinator = this->scrollingCoordinator())
3862 return scrollingCoordinator->isRubberBandInProgress(scrollingNodeID());
3863
3864 if (auto scrollAnimator = existingScrollAnimator())
3865 return scrollAnimator->isRubberBandInProgress();
3866
3867 return false;
3868}
3869
3870bool LocalFrameView::isInStableState() const
3871{
3872 if (RefPtr page = m_frame->page())
3873 return page->chrome().client().isInStableState();
3874 return FrameView::isInStableState();
3875}
3876
3877bool LocalFrameView::requestStartKeyboardScrollAnimation(const KeyboardScroll& scrollData)
3878{
3879 if (RefPtr scrollingCoordinator = this->scrollingCoordinator())
3880 return scrollingCoordinator->requestStartKeyboardScrollAnimation(*this, scrollData);
3881
3882 return false;
3883}
3884
3885bool LocalFrameView::requestStopKeyboardScrollAnimation(bool immediate)
3886{
3887 if (RefPtr scrollingCoordinator = this->scrollingCoordinator())
3888 return scrollingCoordinator->requestStopKeyboardScrollAnimation(*this, immediate);
3889
3890 return false;
3891}
3892
3893bool LocalFrameView::requestScrollToPosition(const ScrollPosition& position, const ScrollPositionChangeOptions& options)
3894{
3895 LOG_WITH_STREAM(Scrolling, stream << "LocalFrameView::requestScrollToPosition " << position << " options " << options)((void)0);
3896
3897#if ENABLE(ASYNC_SCROLLING)(defined 1 && 1)
3898 if (auto* tiledBacking = this->tiledBacking(); tiledBacking && options.animated == ScrollIsAnimated::No) {
3899#if PLATFORM(IOS_FAMILY)(defined 1 && 1)
3900 auto contentSize = exposedContentRect().size();
3901#else
3902 auto contentSize = visibleContentRect().size();
3903#endif
3904 tiledBacking->prepopulateRect(FloatRect(position, contentSize));
3905 }
3906#endif
3907
3908#if ENABLE(ASYNC_SCROLLING)(defined 1 && 1) || USE(COORDINATED_GRAPHICS)(defined USE_COORDINATED_GRAPHICS && USE_COORDINATED_GRAPHICS
)
3909 if (RefPtr scrollingCoordinator = this->scrollingCoordinator())
3910 return scrollingCoordinator->requestScrollToPosition(*this, position, options);
3911#else
3912 UNUSED_PARAM(position)(void)position;
3913 UNUSED_PARAM(options)(void)options;
3914#endif
3915
3916 return false;
3917}
3918
3919void LocalFrameView::stopAsyncAnimatedScroll()
3920{
3921#if ENABLE(ASYNC_SCROLLING)(defined 1 && 1)
3922 LOG_WITH_STREAM(Scrolling, stream << "LocalFrameView::stopAsyncAnimatedScroll")((void)0);
3923
3924 if (RefPtr scrollingCoordinator = this->scrollingCoordinator())
3925 return scrollingCoordinator->stopAnimatedScroll(*this);
3926#endif
3927}
3928
3929void LocalFrameView::addTrackedRepaintRect(const FloatRect& r)
3930{
3931 if (!m_isTrackingRepaints || r.isEmpty())
3932 return;
3933
3934 FloatRect repaintRect = r;
3935 repaintRect.moveBy(-scrollPosition());
3936 m_trackedRepaintRects.append(repaintRect);
3937}
3938
3939void LocalFrameView::repaintContentRectangle(const IntRect& r)
3940{
3941 ASSERT(!m_frame->ownerElement())((void)0);
3942
3943 if (!shouldUpdate())
3944 return;
3945
3946 ScrollView::repaintContentRectangle(r);
3947}
3948
3949static unsigned countRenderedCharactersInRenderObjectWithThreshold(const RenderElement& renderer, unsigned threshold)
3950{
3951 unsigned count = 0;
3952 for (CheckedPtr<const RenderObject> descendant = &renderer; descendant; descendant = descendant->nextInPreOrder()) {
3953 if (CheckedPtr renderText = dynamicDowncast<RenderText>(descendant.get())) {
3954 count += renderText->text().length();
3955 if (count >= threshold)
3956 break;
3957 }
3958 }
3959 return count;
3960}
3961
3962bool LocalFrameView::renderedCharactersExceed(unsigned threshold)
3963{
3964 if (!m_frame->contentRenderer())
3965 return false;
3966 return countRenderedCharactersInRenderObjectWithThreshold(*m_frame->contentRenderer(), threshold) >= threshold;
3967}
3968
3969void LocalFrameView::availableContentSizeChanged(AvailableSizeChangeReason reason)
3970{
3971 if (RefPtr document = m_frame->document()) {
3972 // FIXME: Merge this logic with m_setNeedsLayoutWasDeferred and find a more appropriate
3973 // way of handling potential recursive layouts when the viewport is resized to accomodate
3974 // the content but the content always overflows the viewport. See webkit.org/b/165781.
3975 if (layoutContext().layoutPhase() != LocalFrameViewLayoutContext::LayoutPhase::InViewSizeAdjust || !useFixedLayout())
3976 document->updateViewportUnitsOnResize();
3977 }
3978
3979 updateLayoutViewport();
3980 setNeedsLayoutAfterViewConfigurationChange();
3981 ScrollView::availableContentSizeChanged(reason);
3982}
3983
3984bool LocalFrameView::shouldLayoutAfterContentsResized() const
3985{
3986 return !useFixedLayout() || useCustomFixedPositionLayoutRect();
3987}
3988
3989void LocalFrameView::updateContentsSize()
3990{
3991 // We check to make sure the view is attached to a frame() as this method can
3992 // be triggered before the view is attached by Frame::createView(...) setting
3993 // various values such as setScrollBarModes(...) for example. An ASSERT is
3994 // triggered when a view is layout before being attached to a m_frame->
3995 if (!m_frame->view())
3996 return;
3997
3998#if PLATFORM(IOS_FAMILY)(defined 1 && 1)
3999 if (CheckedPtr root = m_frame->contentRenderer()) {
4000 if (useCustomFixedPositionLayoutRect() && hasViewportConstrainedObjects()) {
4001 setViewportConstrainedObjectsNeedLayout();
4002 // We must eagerly enter compositing mode because fixed position elements
4003 // will not have been made compositing via a preceding style change before
4004 // m_useCustomFixedPositionLayoutRect was true.
4005 root->compositor().enableCompositingMode();
4006 }
4007 }
4008#endif
4009
4010 if (shouldLayoutAfterContentsResized() && needsLayout())
4011 layoutContext().layout();
4012
4013 if (CheckedPtr renderView = this->renderView()) {
4014 if (renderView->usesCompositing())
4015 renderView->compositor().frameViewDidChangeSize();
4016 }
4017}
4018
4019void LocalFrameView::addedOrRemovedScrollbar()
4020{
4021 if (CheckedPtr renderView = this->renderView()) {
4022 if (renderView->usesCompositing())
4023 renderView->compositor().frameViewDidAddOrRemoveScrollbars();
4024 }
4025
4026 updateTiledBackingAdaptiveSizing();
4027
4028 InspectorInstrumentation::didAddOrRemoveScrollbars(*this);
4029}
4030
4031OptionSet<TiledBacking::Scrollability> LocalFrameView::computeScrollability() const
4032{
4033 RefPtr page = m_frame->page();
4034
4035 // Use smaller square tiles if the Window is not active to facilitate app napping.
4036 if (!page || !page->isWindowActive())
4037 return { TiledBacking::Scrollability::HorizontallyScrollable, TiledBacking::Scrollability::VerticallyScrollable };
4038
4039 bool horizontallyScrollable;
4040 bool verticallyScrollable;
4041 bool clippedByAncestorView = static_cast<bool>(m_viewExposedRect);
4042
4043#if PLATFORM(IOS_FAMILY)(defined 1 && 1)
4044 if (page)
4045 clippedByAncestorView |= page->enclosedInScrollableAncestorView();
4046#endif
4047
4048 if (delegatesScrollingToNativeView()) {
4049 IntSize documentSize = contentsSize();
4050 IntSize visibleSize = this->visibleSize();
4051
4052 horizontallyScrollable = clippedByAncestorView || documentSize.width() > visibleSize.width();
4053 verticallyScrollable = clippedByAncestorView || documentSize.height() > visibleSize.height();
4054 } else {
4055 horizontallyScrollable = clippedByAncestorView || horizontalScrollbar();
4056 verticallyScrollable = clippedByAncestorView || verticalScrollbar();
4057 }
4058
4059 OptionSet<TiledBacking::Scrollability> scrollability = TiledBacking::Scrollability::NotScrollable;
4060 if (horizontallyScrollable)
4061 scrollability.add(TiledBacking::Scrollability::HorizontallyScrollable);
4062
4063 if (verticallyScrollable)
4064 scrollability.add(TiledBacking::Scrollability::VerticallyScrollable);
4065
4066 return scrollability;
4067}
4068
4069void LocalFrameView::updateTiledBackingAdaptiveSizing()
4070{
4071 CheckedPtr tiledBacking = this->tiledBacking();
4072 if (!tiledBacking)
4073 return;
4074
4075 tiledBacking->setScrollability(computeScrollability());
4076}
4077
4078// FIXME: This shouldn't be called from outside; LocalFrameView should call it when the relevant viewports change.
4079void LocalFrameView::layoutOrVisualViewportChanged()
4080{
4081 if (m_frame->settings().visualViewportAPIEnabled()) {
4082 if (RefPtr window = m_frame->window())
4083 window->visualViewport().update();
4084
4085 if (RefPtr scrollingCoordinator = this->scrollingCoordinator())
4086 scrollingCoordinator->frameViewVisualViewportChanged(*this);
4087 }
4088
4089 if (RefPtr page = m_frame->page())
4090 page->chrome().client().frameViewLayoutOrVisualViewportChanged(*this);
4091}
4092
4093void LocalFrameView::unobscuredContentSizeChanged()
4094{
4095#if PLATFORM(IOS_FAMILY)(defined 1 && 1)
4096 updateTiledBackingAdaptiveSizing();
4097#endif
4098}
4099
4100void LocalFrameView::loadProgressingStatusChanged()
4101{
4102 if (m_firstVisuallyNonEmptyLayoutMilestoneIsPending && m_frame->loader().isComplete())
4103 fireLayoutRelatedMilestonesIfNeeded();
4104 adjustTiledBackingCoverage();
4105}
4106
4107void LocalFrameView::adjustTiledBackingCoverage()
4108{
4109 if (!m_speculativeTilingEnabled)
4110 enableSpeculativeTilingIfNeeded();
4111
4112 CheckedPtr renderView = this->renderView();
4113 if (renderView && renderView->layer() && renderView->layer()->backing())
4114 renderView->layer()->backing()->adjustTiledBackingCoverage();
4115#if PLATFORM(IOS_FAMILY)(defined 1 && 1)
4116 if (LegacyTileCache* tileCache = legacyTileCache())
4117 tileCache->setSpeculativeTileCreationEnabled(m_speculativeTilingEnabled);
4118#endif
4119}
4120
4121static bool shouldEnableSpeculativeTilingDuringLoading(const LocalFrameView& view)
4122{
4123 RefPtr page = view.frame().page();
4124 return page && view.isVisuallyNonEmpty() && !page->progress().isMainLoadProgressing();
4125}
4126
4127void LocalFrameView::enableSpeculativeTilingIfNeeded()
4128{
4129 ASSERT(!m_speculativeTilingEnabled)((void)0);
4130 if (wasScrolledByUser()) {
4131 m_speculativeTilingEnabled = true;
4132 return;
4133 }
4134 if (!shouldEnableSpeculativeTilingDuringLoading(*this))
4135 return;
4136
4137 if (m_speculativeTilingDelayDisabledForTesting) {
4138 speculativeTilingEnableTimerFired();
4139 return;
4140 }
4141
4142 if (m_speculativeTilingEnableTimer.isActive())
4143 return;
4144 // Delay enabling a bit as load completion may trigger further loading from scripts.
4145 static const Seconds speculativeTilingEnableDelay { 500_ms };
4146 m_speculativeTilingEnableTimer.startOneShot(speculativeTilingEnableDelay);
4147}
4148
4149void LocalFrameView::speculativeTilingEnableTimerFired()
4150{
4151 if (m_speculativeTilingEnabled)
4152 return;
4153 m_speculativeTilingEnabled = shouldEnableSpeculativeTilingDuringLoading(*this);
4154 adjustTiledBackingCoverage();
4155}
4156
4157void LocalFrameView::show()
4158{
4159 ScrollView::show();
4160
4161 if (m_frame->isMainFrame()) {
4162 // Turn off speculative tiling for a brief moment after a LocalFrameView appears on screen.
4163 // Note that adjustTiledBackingCoverage() kicks the (500ms) timer to re-enable it.
4164 m_speculativeTilingEnabled = false;
4165 m_lastUserScrollType = std::nullopt;
4166 adjustTiledBackingCoverage();
4167 }
4168}
4169
4170void LocalFrameView::hide()
4171{
4172 ScrollView::hide();
4173 adjustTiledBackingCoverage();
4174}
4175
4176bool LocalFrameView::needsLayout() const
4177{
4178 return layoutContext().needsLayout();
4179}
4180
4181void LocalFrameView::setNeedsLayoutAfterViewConfigurationChange()
4182{
4183 layoutContext().setNeedsLayoutAfterViewConfigurationChange();
4184}
4185
4186void LocalFrameView::setNeedsCompositingConfigurationUpdate()
4187{
4188 CheckedPtr renderView = this->renderView();
4189 if (renderView && renderView->usesCompositing()) {
4190 if (CheckedPtr rootLayer = renderView->layer())
4191 rootLayer->setNeedsCompositingConfigurationUpdate();
4192 renderView->compositor().scheduleCompositingLayerUpdate();
4193 }
4194}
4195
4196void LocalFrameView::setNeedsCompositingGeometryUpdate()
4197{
4198 CheckedPtr renderView = this->renderView();
4199 if (renderView->usesCompositing()) {
4200 if (CheckedPtr rootLayer = renderView->layer())
4201 rootLayer->setNeedsCompositingGeometryUpdate();
4202 renderView->compositor().scheduleCompositingLayerUpdate();
4203 }
4204}
4205
4206void LocalFrameView::setDescendantsNeedUpdateBackingAndHierarchyTraversal()
4207{
4208 CheckedPtr renderView = this->renderView();
4209 if (renderView->usesCompositing()) {
4210 if (CheckedPtr rootLayer = renderView->layer())
4211 rootLayer->setDescendantsNeedUpdateBackingAndHierarchyTraversal();
4212 renderView->compositor().scheduleCompositingLayerUpdate();
4213 }
4214}
4215
4216void LocalFrameView::scheduleSelectionUpdate()
4217{
4218 if (needsLayout())
4219 return;
4220 // FIXME: We should not need to go through the layout process since selection update does not change dimension/geometry.
4221 // However we can't tell at this point if the tree is stable yet, so let's just schedule a root only layout for now.
4222 setNeedsLayoutAfterViewConfigurationChange();
4223}
4224
4225bool LocalFrameView::isTransparent() const
4226{
4227 return m_isTransparent;
4228}
4229
4230void LocalFrameView::setTransparent(bool isTransparent)
4231{
4232 if (m_isTransparent == isTransparent)
4233 return;
4234
4235 m_isTransparent = isTransparent;
4236
4237 // setTransparent can be called in the window between LocalFrameView initialization
4238 // and switching in the new Document; this means that the RenderView that we
4239 // retrieve is actually attached to the previous Document, which is going away,
4240 // and must not update compositing layers.
4241 if (!isViewForDocumentInFrame())
4242 return;
4243
4244 setNeedsLayoutAfterViewConfigurationChange();
4245 setNeedsCompositingConfigurationUpdate();
4246}
4247
4248bool LocalFrameView::hasOpaqueBackground() const
4249{
4250 return !m_isTransparent && m_baseBackgroundColor.isOpaque();
4251}
4252
4253Color LocalFrameView::baseBackgroundColor() const
4254{
4255 return m_baseBackgroundColor;
4256}
4257
4258void LocalFrameView::setBaseBackgroundColor(const Color& backgroundColor)
4259{
4260 Color newBaseBackgroundColor = backgroundColor.isValid() ? backgroundColor : Color::white;
4261 if (m_baseBackgroundColor == newBaseBackgroundColor)
4262 return;
4263
4264#if ENABLE(DARK_MODE_CSS)(defined 1 && 1)
4265 m_styleColorOptions = styleColorOptions();
4266#endif
4267 m_baseBackgroundColor = newBaseBackgroundColor;
4268
4269 if (!isViewForDocumentInFrame())
4270 return;
4271
4272 recalculateScrollbarOverlayStyle();
4273 setNeedsLayoutAfterViewConfigurationChange();
4274 setNeedsCompositingConfigurationUpdate();
4275}
4276
4277#if ENABLE(DARK_MODE_CSS)(defined 1 && 1)
4278void LocalFrameView::updateBaseBackgroundColorIfNecessary()
4279{
4280 auto styleColorOptions = this->styleColorOptions();
4281 if (m_styleColorOptions == styleColorOptions)
4282 return;
4283
4284 m_styleColorOptions = styleColorOptions;
4285 std::optional<Color> backgroundColor;
4286 if (m_isTransparent)
4287 backgroundColor = Color(Color::transparentBlack);
4288
4289 updateBackgroundRecursively(backgroundColor);
4290}
4291#endif
4292
4293void LocalFrameView::updateBackgroundRecursively(const std::optional<Color>& backgroundColor)
4294{
4295 auto intrinsicBaseBackgroundColor = [](LocalFrameView& view) -> Color {
4296#if PLATFORM(COCOA)(defined 1 && 1)
4297 static const auto cssValueControlBackground = CSSValueAppleSystemControlBackground;
4298#else
4299 static const auto cssValueControlBackground = CSSValueWindow;
4300#endif
4301 return RenderTheme::singleton().systemColor(cssValueControlBackground, view.styleColorOptions());
4302 };
4303
4304 for (RefPtr<Frame> frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr())) {
4305 RefPtr localFrame = dynamicDowncast<LocalFrame>(frame);
4306 if (!localFrame)
4307 continue;
4308
4309 if (RefPtr view = localFrame->view()) {
4310 auto baseBackgroundColor = backgroundColor.value_or(intrinsicBaseBackgroundColor(*view));
4311 view->setTransparent(!baseBackgroundColor.isVisible());
4312 view->setBaseBackgroundColor(baseBackgroundColor);
4313 if (view->needsLayout())
4314 view->layoutContext().scheduleLayout();
4315 }
4316 }
4317}
4318
4319bool LocalFrameView::hasExtendedBackgroundRectForPainting() const
4320{
4321 CheckedPtr tiledBacking = this->tiledBacking();
4322 if (!tiledBacking)
4323 return false;
4324
4325 return tiledBacking->hasMargins();
4326}
4327
4328void LocalFrameView::updateExtendBackgroundIfNecessary()
4329{
4330 const auto mode = calculateExtendedBackgroundMode();
4331 if (mode.isEmpty())
4332 return;
4333
4334 updateTilesForExtendedBackgroundMode(mode);
4335}
4336
4337LocalFrameView::ExtendedBackgroundMode LocalFrameView::calculateExtendedBackgroundMode() const
4338{
4339#if PLATFORM(IOS_FAMILY)(defined 1 && 1)
4340 // <rdar://problem/16201373>
4341 return { };
4342#else
4343 if (!m_frame->settings().backgroundShouldExtendBeyondPage())
4344 return { };
4345
4346 // Just because Settings::backgroundShouldExtendBeyondPage() is true does not necessarily mean
4347 // that the background rect needs to be extended for painting. Simple backgrounds can be extended
4348 // just with RenderLayerCompositor's rootExtendedBackgroundColor. More complicated backgrounds,
4349 // such as images, require extending the background rect to continue painting into the extended
4350 // region. This function finds out if it is necessary to extend the background rect for painting.
4351
4352 if (!m_frame->isMainFrame())
4353 return { };
4354
4355 RefPtr document = m_frame->document();
4356 if (!document)
4357 return { };
4358
4359 if (!renderView())
4360 return { };
4361
4362 auto* rootBackgroundRenderer = renderView()->rendererForRootBackground();
4363 if (!rootBackgroundRenderer)
4364 return { };
4365
4366 auto& backgroundLayers = rootBackgroundRenderer->style().backgroundLayers();
4367 if (!Style::hasImageInAnyLayer(backgroundLayers))
4368 return { };
4369
4370 BoxSideSet mode;
4371 auto backgroundRepeat = backgroundLayers.usedFirst().repeat();
4372 if (backgroundRepeat.x() == FillRepeat::Repeat) {
4373 mode.add(BoxSide::Left);
4374 mode.add(BoxSide::Right);
4375 }
4376 if (backgroundRepeat.y() == FillRepeat::Repeat) {
4377 mode.add(BoxSide::Top);
4378 mode.add(BoxSide::Bottom);
4379 }
4380
4381#if ENABLE(BANNER_VIEW_OVERLAYS)(defined ENABLE_BANNER_VIEW_OVERLAYS && ENABLE_BANNER_VIEW_OVERLAYS
)
4382 if (mode.contains(BoxSide::Top)) {
4383 if (RefPtr page = m_frame->page(); page && page->hasBannerViewOverlay())
4384 mode.remove(BoxSide::Top);
4385 }
4386#endif
4387
4388 return mode;
4389#endif
4390}
4391
4392void LocalFrameView::updateTilesForExtendedBackgroundMode(ExtendedBackgroundMode mode)
4393{
4394 CheckedPtr renderView = this->renderView();
4395 if (!renderView)
4396 return;
4397
4398 RenderLayerBacking* backing = renderView->layer()->backing();
4399 if (!backing)
4400 return;
4401
4402 TiledBacking* tiledBacking = backing->tiledBacking();
4403 if (!tiledBacking)
4404 return;
4405
4406 BoxSideSet existingMode;
4407 if (tiledBacking->topMarginHeight() > 0)
4408 existingMode.add(BoxSide::Top);
4409 if (tiledBacking->bottomMarginHeight() > 0)
4410 existingMode.add(BoxSide::Bottom);
4411 if (tiledBacking->leftMarginWidth() > 0)
4412 existingMode.add(BoxSide::Left);
4413 if (tiledBacking->rightMarginWidth() > 0)
4414 existingMode.add(BoxSide::Right);
4415
4416 if (existingMode == mode)
4417 return;
4418
4419 backing->setTiledBackingHasMargins(mode);
4420}
4421
4422IntRect LocalFrameView::extendedBackgroundRectForPainting() const
4423{
4424 CheckedPtr tiledBacking = this->tiledBacking();
4425 if (!tiledBacking)
4426 return IntRect();
4427
4428 CheckedPtr renderView = this->renderView();
4429 if (!renderView)
4430 return IntRect();
4431
4432 LayoutRect extendedRect = renderView->unextendedBackgroundRect();
4433 if (!tiledBacking->hasMargins())
4434 return snappedIntRect(extendedRect);
4435
4436 extendedRect.moveBy(LayoutPoint(-tiledBacking->leftMarginWidth(), -tiledBacking->topMarginHeight()));
4437 extendedRect.expand(LayoutSize(tiledBacking->leftMarginWidth() + tiledBacking->rightMarginWidth(), tiledBacking->topMarginHeight() + tiledBacking->bottomMarginHeight()));
4438 return snappedIntRect(extendedRect);
4439}
4440
4441bool LocalFrameView::shouldUpdateWhileOffscreen() const
4442{
4443 return m_shouldUpdateWhileOffscreen;
4444}
4445
4446void LocalFrameView::setShouldUpdateWhileOffscreen(bool shouldUpdateWhileOffscreen)
4447{
4448 m_shouldUpdateWhileOffscreen = shouldUpdateWhileOffscreen;
4449}
4450
4451bool LocalFrameView::shouldUpdate() const
4452{
4453 if (isOffscreen() && !shouldUpdateWhileOffscreen())
4454 return false;
4455 return true;
4456}
4457
4458bool LocalFrameView::safeToPropagateScrollToParent() const
4459{
4460 RefPtr document = m_frame->document();
4461 if (!document)
4462 return false;
4463
4464 RefPtr parentFrame = m_frame->tree().parent();
4465 if (!parentFrame)
4466 return false;
4467
4468 RefPtr localParent = dynamicDowncast<LocalFrame>(parentFrame);
4469 if (!localParent)
4470 return false;
4471
4472 RefPtr parentDocument = localParent->document();
4473 if (!parentDocument)
4474 return false;
4475
4476 return protect(document->securityOrigin())->isSameOriginDomain(protect(parentDocument->securityOrigin()));
4477}
4478
4479void LocalFrameView::scheduleScrollToAnchorAndTextFragment()
4480{
4481 RefPtr<ContainerNode> anchorNode = m_maintainScrollPositionAnchor;
4482 if (!anchorNode && !(m_pendingTextFragmentIndicatorRange && !m_delayedTextFragmentIndicatorTimer.isActive()))
4483 return;
4484
4485 m_scheduledMaintainScrollPositionAnchor = anchorNode;
4486
4487 if (m_scheduledToScrollToAnchor)
4488 return;
4489
4490 RefPtr document = m_frame->document();
4491 ASSERT(document)((void)0);
4492
4493 m_scheduledToScrollToAnchor = true;
4494 document->eventLoop().queueTask(TaskSource::DOMManipulation, [weakThis = WeakPtr { *this }] {
4495 RefPtr protectedThis = weakThis.get();
4496 if (!protectedThis)
4497 return;
4498 if (!protectedThis->m_maintainScrollPositionAnchor) {
4499 SetForScope scrollPositionAnchor(protectedThis->m_maintainScrollPositionAnchor, protectedThis->m_scheduledMaintainScrollPositionAnchor);
4500 protectedThis->scrollToAnchorAndTextFragmentNowIfNeeded();
4501 } else
4502 protectedThis->scrollToAnchorAndTextFragmentNowIfNeeded();
4503 protectedThis->m_scheduledMaintainScrollPositionAnchor = nullptr;
4504 });
4505}
4506
4507void LocalFrameView::scrollToAnchorAndTextFragmentNowIfNeeded()
4508{
4509 if (!m_scheduledToScrollToAnchor)
4510 return;
4511
4512 m_scheduledToScrollToAnchor = false;
4513 scrollToAnchor();
4514 scrollToPendingTextFragmentRange();
4515}
4516
4517void LocalFrameView::scrollToAnchor()
4518{
4519 RefPtr<ContainerNode> anchorNode = m_maintainScrollPositionAnchor;
4520 if (!anchorNode)
4521 return;
4522
4523 LOG_WITH_STREAM(Scrolling, stream << *this << " scrollToAnchor() " << anchorNode.get())((void)0);
4524
4525 if (!anchorNode->renderer())
4526 return;
4527
4528 cancelScheduledScrolls();
4529
4530 LayoutRect rect;
4531 bool insideFixed = false;
4532 if (anchorNode != m_frame->document() && anchorNode->renderer())
4533 rect = anchorNode->renderer()->absoluteAnchorRectWithScrollMargin(&insideFixed).marginRect;
4534
4535 LOG_WITH_STREAM(Scrolling, stream << " anchor node rect " << rect)((void)0);
4536
4537 // Scroll nested layers and frames to reveal the anchor.
4538 // Align to the top and to the closest side (this matches other browsers).
4539 if (anchorNode->renderer()->writingMode().isHorizontal())
4540 scrollRectToVisible(rect, *anchorNode->renderer(), insideFixed, { SelectionRevealMode::Reveal, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignTopAlways, ShouldAllowCrossOriginScrolling::No });
4541 else if (anchorNode->renderer()->writingMode().blockDirection() == FlowDirection::RightToLeft)
4542 scrollRectToVisible(rect, *anchorNode->renderer(), insideFixed, { SelectionRevealMode::Reveal, ScrollAlignment::alignRightAlways, ScrollAlignment::alignToEdgeIfNeeded, ShouldAllowCrossOriginScrolling::No });
4543 else
4544 scrollRectToVisible(rect, *anchorNode->renderer(), insideFixed, { SelectionRevealMode::Reveal, ScrollAlignment::alignLeftAlways, ScrollAlignment::alignToEdgeIfNeeded, ShouldAllowCrossOriginScrolling::No });
4545
4546 if (AXObjectCache* cache = m_frame->document()->existingAXObjectCache())
4547 cache->handleScrolledToAnchor(*anchorNode);
4548
4549 // scrollRectToVisible can call into setScrollPosition(), which resets m_maintainScrollPositionAnchor.
4550 LOG_WITH_STREAM(Scrolling, stream << " restoring anchor node to " << anchorNode.get())((void)0);
4551 m_maintainScrollPositionAnchor = anchorNode;
4552 cancelScheduledScrolls();
4553}
4554
4555void LocalFrameView::scrollToPendingTextFragmentRange()
4556{
4557 if (m_wasEverScrolledExplicitlyByUser)
4558 return;
4559
4560 if (!m_pendingTextFragmentIndicatorRange)
4561 return;
4562
4563 if (needsLayout())
4564 return;
4565
4566 auto range = *m_pendingTextFragmentIndicatorRange;
4567 auto rangeText = plainText(range);
4568 if (m_pendingTextFragmentIndicatorText != plainText(range))
4569 return;
4570
4571 LOG_WITH_STREAM(Scrolling, stream << *this << " scrollToPendingTextFragmentRange() " << range)((void)0);
4572
4573 if (!range.startContainer().renderer() || !range.endContainer().renderer())
4574 return;
4575
4576 ASSERT(m_frame->document())((void)0);
4577 Ref document = *m_frame->document();
4578
4579 SetForScope skipScrollResetOfScrollToTextFragmentRange(m_skipScrollResetOfScrollToTextFragmentRange, true);
4580
4581 // FIXME: <http://webkit.org/b/245262> (Scroll To Text Fragment should use DelegateMainFrameScroll)
4582 auto selectionChange = revealRangeWithTemporarySelection(range, TemporarySelectionOption::OnlyAllowForwardScrolling);
4583
4584 if (m_delayedTextFragmentIndicatorTimer.isActive())
4585 return;
4586
4587 if (!m_frame->settings().scrollToTextFragmentIndicatorEnabled() || m_frame->page()->isControlledByAutomation())
4588 return;
4589
4590 RefPtr textIndicator = TextIndicator::createWithRange(range, { TextIndicatorOption::DoNotClipToVisibleRect }, WebCore::TextIndicatorPresentationTransition::Bounce);
4591
4592 RefPtr page = m_frame->page();
4593
4594 if (!page)
4595 return;
4596
4597 if (textIndicator) {
4598 RefPtr localMainFrame = page->localMainFrame();
4599 if (!localMainFrame)
4600 return;
4601
4602 auto textRects = RenderObject::absoluteTextRects(range);
4603 if (!textRects.size())
4604 return;
4605
4606 static constexpr OptionSet hitType { HitTestRequest::Type::ReadOnly, HitTestRequest::Type::Active, HitTestRequest::Type::AllowVisibleChildFrameContentOnly };
4607 auto result = localMainFrame->eventHandler().hitTestResultAtPoint(LayoutPoint(textRects.first().center()), hitType);
4608 if (!intersects(range, *protect(result.targetNode())))
4609 return;
4610
4611 if (textRects.size() >= 2) {
4612 result = localMainFrame->eventHandler().hitTestResultAtPoint(LayoutPoint(textRects[1].center()), hitType);
4613 if (!intersects(range, *protect(result.targetNode())))
4614 return;
4615 }
4616
4617 if (textRects.size() >= 4) {
4618 result = localMainFrame->eventHandler().hitTestResultAtPoint(LayoutPoint(textRects.last().center()), hitType);
4619 if (!intersects(range, *protect(result.targetNode())))
4620 return;
4621 result = localMainFrame->eventHandler().hitTestResultAtPoint(LayoutPoint(textRects[textRects.size() - 2].center()), hitType);
4622 if (!intersects(range, *protect(result.targetNode())))
4623 return;
4624 }
4625 if (m_haveCreatedTextIndicator)
4626 document->page()->chrome().client().updateTextIndicator(WTF::move(textIndicator));
4627 else {
4628 document->page()->chrome().client().setTextIndicator(WTF::move(textIndicator));
4629 m_haveCreatedTextIndicator = true;
4630 }
4631 }
4632}
4633
4634void LocalFrameView::updateEmbeddedObject(const SingleThreadWeakPtr<RenderEmbeddedObject>& embeddedObject)
4635{
4636 // The to-be-updated renderer is passed in as WeakPtr for convenience, but it will be alive at this point.
4637 ASSERT(embeddedObject)((void)0);
4638
4639 // No need to update if it's already crashed or known to be missing.
4640 if (embeddedObject->isPluginUnavailable())
4641 return;
4642
4643 if (RefPtr embedOrObject = dynamicDowncast<HTMLPlugInElement>(embeddedObject->frameOwnerElement())) {
4644 if (embedOrObject->needsWidgetUpdate())
4645 embedOrObject->updateWidget(CreatePlugins::Yes);
4646 } else
4647 ASSERT_NOT_REACHED()((void)0);
4648
4649 // It's possible the renderer was destroyed below updateWidget() since loading a plugin may execute arbitrary JavaScript.
4650 if (!embeddedObject)
4651 return;
4652
4653 auto ignoreWidgetState = embeddedObject->updateWidgetPosition();
4654 UNUSED_PARAM(ignoreWidgetState)(void)ignoreWidgetState;
4655}
4656
4657bool LocalFrameView::updateEmbeddedObjects()
4658{
4659 SetForScope inUpdateEmbeddedObjects(m_inUpdateEmbeddedObjects, true);
4660 if (layoutContext().isLayoutNested() || !m_embeddedObjectsToUpdate || m_embeddedObjectsToUpdate->isEmpty())
4661 return true;
4662
4663 WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
4664
4665 // Make sure we process at most numberOfObjectsToUpdate objects to avoid
4666 // potential infinite loops if objects get added to m_embeddedObjectsToUpdate
4667 // during the execution of this loop.
4668 unsigned numberOfObjectsToUpdate = m_embeddedObjectsToUpdate->size();
4669 for (unsigned i = 0; i < numberOfObjectsToUpdate; ++i) {
4670 if (m_embeddedObjectsToUpdate->isEmpty())
4671 break;
4672 WeakPtr embeddedObject = m_embeddedObjectsToUpdate->takeFirst().get();
4673 updateEmbeddedObject(embeddedObject);
4674 }
4675
4676 return m_embeddedObjectsToUpdate->isEmpty();
4677}
4678
4679void LocalFrameView::updateEmbeddedObjectsTimerFired()
4680{
4681 m_updateEmbeddedObjectsTimer.stop();
4682 for (unsigned i = 0; i < maxUpdateEmbeddedObjectsIterations; ++i) {
4683 if (updateEmbeddedObjects())
4684 break;
4685 }
4686}
4687
4688void LocalFrameView::flushAnyPendingPostLayoutTasks()
4689{
4690 layoutContext().flushPostLayoutTasks();
4691 if (m_updateEmbeddedObjectsTimer.isActive())
4692 updateEmbeddedObjectsTimerFired();
4693}
4694
4695void LocalFrameView::performPostLayoutTasks()
4696{
4697 ScriptDisallowedScope::InMainThread scriptDisallowedScope;
4698
4699 // FIXME: We should not run any JavaScript code in this function.
4700 LOG(Layout, "LocalFrameView %p performPostLayoutTasks", this)((void)0);
4701 updateHasReachedSignificantRenderedTextThreshold();
4702
4703 if (!layoutContext().isLayoutNested() && m_frame->document()->documentElement())
4704 fireLayoutRelatedMilestonesIfNeeded();
4705
4706#if PLATFORM(IOS_FAMILY)(defined 1 && 1)
4707 // Only send layout-related delegate callbacks synchronously for the main frame to
4708 // avoid re-entering layout for the main frame while delivering a layout-related delegate
4709 // callback for a subframe.
4710 if (m_frame->isMainFrame()) {
4711 if (Page* page = m_frame->page())
4712 page->chrome().client().didLayout();
4713 }
4714#endif
4715
4716 // FIXME: We should consider adding DidLayout as a LayoutMilestone. That would let us merge this
4717 // with didLayout(LayoutMilestones).
4718 m_frame->loader().client().dispatchDidLayout();
4719
4720 updateWidgetPositions();
4721
4722 updateSnapOffsets();
4723
4724 m_updateEmbeddedObjectsTimer.startOneShot(0_s);
4725
4726 if (RefPtr scrollingCoordinator = this->scrollingCoordinator())
4727 scrollingCoordinator->frameViewLayoutUpdated(*this);
4728
4729 if (CheckedPtr renderView = this->renderView()) {
4730 if (renderView->usesCompositing())
4731 renderView->compositor().frameViewDidLayout();
4732 }
4733
4734 scheduleScrollToAnchorAndTextFragment();
4735
4736 scheduleResizeEventIfNeeded();
4737
4738 updateLayoutViewport();
4739 viewportContentsChanged();
4740
4741 if (!layoutContext().isLayoutNested())
4742 adjustScrollAnchoringPositionForScrollableAreas();
4743
4744 resnapAfterLayout();
4745
4746 m_frame->document()->scheduleDeferredAXObjectCacheUpdate();
4747
4748 RefPtr document = m_frame->document();
4749 if (document && !document->quirks().needsSuppressPostLayoutBoundaryEventsQuirk())
4750 m_frame->eventHandler().scheduleMouseEventTargetUpdateAfterLayout();
4751}
4752
4753void LocalFrameView::addScrollableAreaForScrollAnchoring(ScrollableArea& scrollableArea)
4754{
4755 if (!m_anchoringScrollableAreas)
4756 m_anchoringScrollableAreas = makeUnique<ScrollableAreaSet>();
4757
4758 m_anchoringScrollableAreas->add(scrollableArea);
4759}
4760
4761void LocalFrameView::removeScrollableAreaForScrollAnchoring(ScrollableArea& scrollableArea)
4762{
4763 if (m_anchoringScrollableAreas)
4764 m_anchoringScrollableAreas->remove(scrollableArea);
4765}
4766
4767void LocalFrameView::dequeueScrollableAreaForScrollAnchoringUpdate(ScrollableArea& scrollableArea)
4768{
4769 m_scrollableAreasWithScrollAnchoringControllersNeedingUpdate.remove(scrollableArea);
4770}
4771
4772void LocalFrameView::queueScrollableAreaForScrollAnchoringUpdate(ScrollableArea& scrollableArea)
4773{
4774 m_scrollableAreasWithScrollAnchoringControllersNeedingUpdate.add(scrollableArea);
4775}
4776
4777void LocalFrameView::clearScrollAnchorsInScrollableAreas()
4778{
4779 clearScrollAnchor();
4780 if (!m_anchoringScrollableAreas)
4781 return;
4782
4783 for (CheckedRef scrollableArea : *m_anchoringScrollableAreas)
4784 scrollableArea->clearScrollAnchor();
4785}
4786
4787void LocalFrameView::updateScrollAnchoringBeforeLayoutForScrollableAreas()
4788{
4789 if (CheckedPtr controller = scrollAnchoringController())
4790 controller->updateBeforeLayout();
4791
4792 if (!m_anchoringScrollableAreas)
4793 return;
4794
4795 for (CheckedRef scrollableArea : *m_anchoringScrollableAreas) {
4796 if (CheckedPtr controller = scrollableArea->scrollAnchoringController())
4797 controller->updateBeforeLayout();
4798 }
4799}
4800
4801void LocalFrameView::adjustScrollAnchoringPositionForScrollableAreas()
4802{
4803 auto scrollableAreasNeedingUpdate = std::exchange(m_scrollableAreasWithScrollAnchoringControllersNeedingUpdate, { });
4804 for (auto& scrollableArea : scrollableAreasNeedingUpdate)
4805 scrollableArea.adjustScrollAnchoringPosition();
4806}
4807
4808void LocalFrameView::updateAnchorPositionedAfterScroll()
4809{
4810 if (CheckedPtr view = renderView())
4811 Style::AnchorPositionEvaluator::updateScrollAdjustments(*view);
4812}
4813
4814IntSize LocalFrameView::sizeForResizeEvent() const
4815{
4816#if PLATFORM(IOS_FAMILY)(defined 1 && 1)
4817 if (m_customSizeForResizeEvent)
4818 return *m_customSizeForResizeEvent;
4819#endif
4820 if (useFixedLayout() && !fixedLayoutSize().isEmpty() && delegatesScrolling())
4821 return fixedLayoutSize();
4822 return visibleContentRectIncludingScrollbars().size();
4823}
4824
4825void LocalFrameView::scheduleResizeEventIfNeeded()
4826{
4827 if (layoutContext().isInRenderTreeLayout() || needsLayout())
4828 return;
4829
4830 CheckedPtr renderView = this->renderView();
4831 if (!renderView || renderView->printing())
4832 return;
4833
4834 RefPtr page = m_frame->page();
4835 if (page && page->chrome().client().isSVGImageChromeClient())
4836 return;
4837
4838 IntSize currentSize = sizeForResizeEvent();
4839 float currentZoomFactor = renderView->style().usedZoom();
4840
4841 if (currentSize == m_lastViewportSize && currentZoomFactor == m_lastUsedZoomFactor)
4842 return;
4843
4844 m_lastViewportSize = currentSize;
4845 m_lastUsedZoomFactor = currentZoomFactor;
4846
4847 if (!layoutContext().didFirstLayout())
4848 return;
4849
4850#if PLATFORM(IOS_FAMILY)(defined 1 && 1)
4851 // Don't send the resize event if the document is loading. Some pages automatically reload
4852 // when the window is resized; Safari on iOS often resizes the window while setting up its
4853 // viewport. This obviously can cause problems.
4854 if (RefPtr documentLoader = m_frame->loader().documentLoader()) {
4855 if (documentLoader->isLoadingInAPISense())
4856 return;
4857 }
4858#endif
4859
4860 RefPtr document = m_frame->document();
4861 if (document->quirks().shouldSilenceWindowResizeEventsDuringApplicationSnapshotting()) {
4862 document->addConsoleMessage(MessageSource::Other, MessageLevel::Info, "Window resize events silenced due to: http://webkit.org/b/258597"_s);
4863 FRAMEVIEW_RELEASE_LOG(Events, LOCALFRAMEVIEW_FIRING_RESIZE_EVENTS_DISABLED_FOR_PAGE)do { if (auto& client = logClient()) client->LOCALFRAMEVIEW_FIRING_RESIZE_EVENTS_DISABLED_FOR_PAGE
(PAGE_ID, FRAME_ID, m_frame->isMainFrame()); else 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 = (LogEvents.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("[pageID=%" "ll" "u" ", frameID=%"
"ll" "u" ", isMainFrame=%d] LocalFrameView::scheduleResizeEventIfNeeded: Not firing resize events because they are temporarily disabled for this page"
), "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_LOG49"
) = "[pageID=%" "ll" "u" ", frameID=%" "ll" "u" ", isMainFrame=%d] LocalFrameView::scheduleResizeEventIfNeeded: Not firing resize events because they are temporarily disabled for this page"
; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf
[__builtin_os_log_format_buffer_size("[pageID=%" "ll" "u" ", frameID=%"
"ll" "u" ", isMainFrame=%d] LocalFrameView::scheduleResizeEventIfNeeded: Not firing resize events because they are temporarily disabled for this page"
, WTF::safePrintfType(PAGE_ID) , WTF::safePrintfType(FRAME_ID
) , WTF::safePrintfType(m_frame->isMainFrame()))]; _os_log_impl
(&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t
*)__builtin_os_log_format(_os_fmt_buf, "[pageID=%" "ll" "u" ", frameID=%"
"ll" "u" ", isMainFrame=%d] LocalFrameView::scheduleResizeEventIfNeeded: Not firing resize events because they are temporarily disabled for this page"
, WTF::safePrintfType(PAGE_ID) , WTF::safePrintfType(FRAME_ID
) , WTF::safePrintfType(m_frame->isMainFrame())), (uint32_t
)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic
pop ; } while (0)
;
4864 return;
4865 }
4866
4867 // TODO: move this to a method called for all scrollable areas
4868 clearScrollAnchor();
4869
4870 LOG_WITH_STREAM(Events, stream << "LocalFrameView " << this << " scheduleResizeEventIfNeeded scheduling resize event for document" << document << ", size " << currentSize)((void)0);
4871 document->setNeedsDOMWindowResizeEvent();
4872
4873 bool isMainFrame = m_frame->isMainFrame();
4874 if (InspectorInstrumentation::hasFrontends() && isMainFrame) {
4875 if (InspectorBackendClient* inspectorBackendClient = page ? page->inspectorController().inspectorBackendClient() : nullptr)
4876 inspectorBackendClient->didResizeMainFrame(m_frame.ptr());
4877 }
4878}
4879
4880void LocalFrameView::willStartLiveResize()
4881{
4882 ScrollView::willStartLiveResize();
4883 adjustTiledBackingCoverage();
4884}
4885
4886void LocalFrameView::willEndLiveResize()
4887{
4888 ScrollView::willEndLiveResize();
4889 adjustTiledBackingCoverage();
4890}
4891
4892void LocalFrameView::autoSizeIfEnabled()
4893{
4894 if (!m_shouldAutoSize)
4895 return;
4896
4897 if (m_inAutoSize)
4898 return;
4899
4900 RefPtr document = m_frame->document();
4901 if (!document)
4902 return;
4903
4904 CheckedPtr renderView = document->renderView();
4905 if (!renderView)
4906 return;
4907
4908 CheckedPtr firstChild = renderView->firstChild();
4909 if (!firstChild)
4910 return;
4911
4912 SetForScope changeInAutoSize(m_inAutoSize, true);
4913 if (layoutContext().subtreeLayoutRoot())
4914 layoutContext().convertSubtreeLayoutToFullLayout();
4915
4916 switch (m_autoSizeMode) {
4917 case AutoSizeMode::SizeToContent:
4918 performSizeToContentAutoSize();
4919 break;
4920 case AutoSizeMode::FixedWidth:
4921 performFixedWidthAutoSize();
4922 break;
4923 }
4924
4925 if (RefPtr page = m_frame->page(); page && m_frame->isMainFrame())
4926 page->chrome().client().intrinsicContentsSizeChanged(m_autoSizeContentSize);
4927
4928 m_didRunAutosize = true;
4929}
4930
4931void LocalFrameView::performFixedWidthAutoSize()
4932{
4933 LOG(Layout, "LocalFrameView %p performFixedWidthAutoSize", this)((void)0);
4934
4935 RefPtr document = m_frame->document();
4936 CheckedPtr renderView = document->renderView();
4937 CheckedPtr firstChild = renderView->firstChild();
4938
4939 auto horizonalScrollbarMode = ScrollbarMode::AlwaysOff;
4940 auto verticalScrollbarMode = ScrollbarMode::AlwaysOff;
4941 setVerticalScrollbarLock(false);
4942 setHorizontalScrollbarLock(false);
4943 setScrollbarModes(horizonalScrollbarMode, verticalScrollbarMode, true, true);
4944
4945 ASSERT(is<RenderElement>(*firstChild))((void)0);
4946 CheckedRef documentRenderer = downcast<RenderElement>(*firstChild);
4947 documentRenderer->mutableStyle().setMaxWidth(Style::MaximumSize::Fixed { static_cast<float>(m_autoSizeConstraint.width()) });
4948 resize(m_autoSizeConstraint.width(), m_autoSizeConstraint.height());
4949
4950 Ref<LocalFrameView> protectedThis(*this);
4951 document->updateLayoutIgnorePendingStylesheets();
4952 // While the final content size could slightly be different after the next resize/layout (see below), we intentionally save and report
4953 // the current value to avoid unstable layout (e.g. content "height: 100%").
4954 // See also webkit.org/b/173561
4955 m_autoSizeContentSize = contentsSize();
4956
4957 auto finalWidth = std::max(m_autoSizeConstraint.width(), m_autoSizeContentSize.width());
4958 auto finalHeight = m_autoSizeFixedMinimumHeight ? std::max(m_autoSizeFixedMinimumHeight, m_autoSizeContentSize.height()) : m_autoSizeContentSize.height();
4959 resize(finalWidth, finalHeight);
4960 document->updateLayoutIgnorePendingStylesheets();
4961}
4962
4963void LocalFrameView::performSizeToContentAutoSize()
4964{
4965 // Do the resizing twice. The first time is basically a rough calculation using the preferred width
4966 // which may result in a height change during the second iteration.
4967 // Let's ignore renderers with viewport units first and resolve these boxes during the second phase of the autosizing.
4968 LOG(Layout, "LocalFrameView %p performSizeToContentAutoSize", this)((void)0);
4969 ASSERT(m_frame->document() && m_frame->document()->renderView())((void)0);
4970
4971 RefPtr document = m_frame->document();
4972 CheckedRef renderView = *document->renderView();
4973 CheckedPtr documentRenderer = dynamicDowncast<RenderBox>(renderView->firstChild());
4974 if (!documentRenderer) {
4975 ASSERT_NOT_REACHED()((void)0);
4976 return;
4977 }
4978
4979 auto layoutWithAdjustedStyleIfNeeded = [&] {
4980 document->updateStyleIfNeeded();
4981 CheckedRef style = documentRenderer->mutableStyle();
4982 if (style->logicalHeight().isPercent()) {
4983 // Percent height values on the document renderer when we don't really have a proper viewport size can
4984 // result incorrect rendering in certain layout contexts (e.g flex).
4985 style->setLogicalHeight(CSS::Keyword::Auto { });
4986 }
4987 document->updateLayout();
4988 };
4989
4990 resetOverriddenWidthForCSSDefaultViewportUnits();
4991 resetOverriddenWidthForCSSSmallViewportUnits();
4992 resetOverriddenWidthForCSSLargeViewportUnits();
4993
4994 // Start from the minimum size and allow it to grow.
4995 auto minAutoSize = IntSize { 1, 1 };
4996 resize(minAutoSize);
4997 auto size = frameRect().size();
4998 for (int i = 0; i < 2; i++) {
4999 layoutWithAdjustedStyleIfNeeded();
5000 // Update various sizes including contentsSize, scrollHeight, etc.
5001 auto newSize = IntSize { documentRenderer->minPreferredLogicalWidth(), renderView->documentRect().height() };
5002
5003 // Check to see if a scrollbar is needed for a given dimension and
5004 // if so, increase the other dimension to account for the scrollbar.
5005 // Since the dimensions are only for the view rectangle, once a
5006 // dimension exceeds the maximum, there is no need to increase it further.
5007 if (newSize.width() > m_autoSizeConstraint.width()) {
5008 RefPtr<Scrollbar> localHorizontalScrollbar = horizontalScrollbar();
5009 if (!localHorizontalScrollbar)
5010 localHorizontalScrollbar = createScrollbar(ScrollbarOrientation::Horizontal);
5011 newSize.expand(0, localHorizontalScrollbar->occupiedHeight());
5012 // Don't bother checking for a vertical scrollbar because the width is at
5013 // already greater the maximum.
5014 } else if (newSize.height() > m_autoSizeConstraint.height()) {
5015 RefPtr<Scrollbar> localVerticalScrollbar = verticalScrollbar();
5016 if (!localVerticalScrollbar)
5017 localVerticalScrollbar = createScrollbar(ScrollbarOrientation::Vertical);
5018 newSize.expand(localVerticalScrollbar->occupiedWidth(), 0);
5019 // Don't bother checking for a horizontal scrollbar because the height is
5020 // already greater the maximum.
5021 }
5022
5023 // Ensure the size is at least the min bounds.
5024 newSize = newSize.expandedTo(minAutoSize);
5025
5026 // Bound the dimensions by the max bounds and determine what scrollbars to show.
5027 ScrollbarMode horizonalScrollbarMode = ScrollbarMode::AlwaysOff;
5028 if (newSize.width() > m_autoSizeConstraint.width()) {
5029 newSize.setWidth(m_autoSizeConstraint.width());
5030 horizonalScrollbarMode = ScrollbarMode::AlwaysOn;
5031 }
5032 ScrollbarMode verticalScrollbarMode = ScrollbarMode::AlwaysOff;
5033 if (newSize.height() > m_autoSizeConstraint.height()) {
5034 newSize.setHeight(m_autoSizeConstraint.height());
5035 verticalScrollbarMode = ScrollbarMode::AlwaysOn;
5036 }
5037
5038 if (newSize == size)
5039 continue;
5040
5041 // While loading only allow the size to increase (to avoid twitching during intermediate smaller states)
5042 // unless autoresize has just been turned on or the maximum size is smaller than the current size.
5043 if (m_didRunAutosize && size.height() <= m_autoSizeConstraint.height() && size.width() <= m_autoSizeConstraint.width()
5044 && !m_frame->loader().isComplete() && (newSize.height() < size.height() || newSize.width() < size.width()))
5045 break;
5046
5047 // The first time around, resize to the minimum height again; otherwise,
5048 // on pages (e.g. quirks mode) where the body/document resize to the view size,
5049 // we'll end up not shrinking back down after resizing to the computed preferred width.
5050 resize(newSize.width(), i ? newSize.height() : minAutoSize.height());
5051 // Protect the content from evergrowing layout.
5052 auto preferredViewportWidth = std::min(newSize.width(), m_autoSizeConstraint.width());
5053 overrideWidthForCSSDefaultViewportUnits(preferredViewportWidth);
5054 overrideWidthForCSSSmallViewportUnits(preferredViewportWidth);
5055 overrideWidthForCSSLargeViewportUnits(preferredViewportWidth);
5056 // Force the scrollbar state to avoid the scrollbar code adding them and causing them to be needed. For example,
5057 // a vertical scrollbar may cause text to wrap and thus increase the height (which is the only reason the scollbar is needed).
5058 setVerticalScrollbarLock(false);
5059 setHorizontalScrollbarLock(false);
5060 setScrollbarModes(horizonalScrollbarMode, verticalScrollbarMode, true, true);
5061 }
5062 // All the resizing above may have invalidated style (for example if viewport units are being used).
5063 // FIXME: Use the final layout's result as the content size (webkit.org/b/173561).
5064 layoutWithAdjustedStyleIfNeeded();
5065 m_autoSizeContentSize = contentsSize();
5066}
5067
5068void LocalFrameView::setAutoSizeFixedMinimumHeight(int fixedMinimumHeight)
5069{
5070 if (m_autoSizeFixedMinimumHeight == fixedMinimumHeight)
5071 return;
5072
5073 m_autoSizeFixedMinimumHeight = fixedMinimumHeight;
5074
5075 setNeedsLayoutAfterViewConfigurationChange();
5076}
5077
5078RenderElement* LocalFrameView::viewportRenderer() const
5079{
5080 if (m_viewportRendererType == ViewportRendererType::None)
5081 return nullptr;
5082
5083 RefPtr document = m_frame->document();
5084 if (!document)
5085 return nullptr;
5086
5087 if (m_viewportRendererType == ViewportRendererType::Document) {
5088 auto* documentElement = document->documentElement();
5089 if (!documentElement)
5090 return nullptr;
5091 return documentElement->renderer();
5092 }
5093
5094 if (m_viewportRendererType == ViewportRendererType::Body) {
5095 RefPtr body = document->body();
5096 if (!body)
5097 return nullptr;
5098 return body->renderer();
5099 }
5100
5101 ASSERT_NOT_REACHED()((void)0);
5102 return nullptr;
5103}
5104
5105const Pagination& LocalFrameView::pagination() const
5106{
5107 if (m_pagination != Pagination())
5108 return m_pagination;
5109
5110 if (m_frame->isMainFrame()) {
5111 if (Page* page = m_frame->page())
5112 return page->pagination();
5113 }
5114
5115 return m_pagination;
5116}
5117
5118void LocalFrameView::setPagination(const Pagination& pagination)
5119{
5120 if (m_pagination == pagination)
5121 return;
5122
5123 m_pagination = pagination;
5124
5125 m_frame->document()->styleScope().didChangeStyleSheetEnvironment();
5126}
5127
5128IntRect LocalFrameView::windowClipRect() const
5129{
5130 ASSERT(m_frame->view() == this)((void)0);
5131
5132 if (m_cachedWindowClipRect)
5133 return *m_cachedWindowClipRect;
5134
5135 if (paintsEntireContents())
5136 return contentsToWindow(IntRect(IntPoint(), totalContentsSize()));
5137
5138 // Set our clip rect to be our contents.
5139 IntRect clipRect = contentsToWindow(visibleContentRect(LegacyIOSDocumentVisibleRect));
5140
5141 if (!m_frame->ownerElement())
5142 return clipRect;
5143
5144 // Take our owner element and get its clip rect.
5145 RefPtr ownerElement = m_frame->ownerElement();
5146 if (RefPtr parentView = ownerElement->document().view())
5147 clipRect.intersect(parentView->windowClipRectForFrameOwner(ownerElement.get(), true));
5148 return clipRect;
5149}
5150
5151IntRect LocalFrameView::windowClipRectForFrameOwner(const HTMLFrameOwnerElement* ownerElement, bool clipToLayerContents) const
5152{
5153 // The renderer can sometimes be null when style="display:none" interacts
5154 // with external content and plugins.
5155 if (!ownerElement->renderer())
5156 return windowClipRect();
5157
5158 // If we have no layer, just return our window clip rect.
5159 CheckedPtr enclosingLayer = ownerElement->renderer()->enclosingLayer();
5160 if (!enclosingLayer)
5161 return windowClipRect();
5162
5163 // Apply the clip from the layer.
5164 IntRect clipRect;
5165 if (clipToLayerContents)
5166 clipRect = snappedIntRect(enclosingLayer->childrenClipRect());
5167 else
5168 clipRect = snappedIntRect(enclosingLayer->selfClipRect());
5169 clipRect = contentsToWindow(clipRect);
5170 return intersection(clipRect, windowClipRect());
5171}
5172
5173void LocalFrameView::scrollTo(const ScrollPosition& newPosition)
5174{
5175 IntPoint oldPosition = scrollPosition();
5176 ScrollView::scrollTo(newPosition);
5177 if (oldPosition != scrollPosition())
5178 scrollPositionChanged(oldPosition, scrollPosition());
5179
5180 didChangeScrollOffset();
5181}
5182
5183float LocalFrameView::adjustVerticalPageScrollStepForFixedContent(float step)
5184{
5185 TrackedRendererListHashSet* viewPositionedOutOfFlowBoxes = { };
5186 if (CheckedPtr root = m_frame->contentRenderer()) {
5187 if (!root->hasOutOfFlowBoxes())
5188 return step;
5189 viewPositionedOutOfFlowBoxes = root->outOfFlowBoxes();
5190 }
5191
5192 FloatRect unobscuredContentRect = this->unobscuredContentRect();
5193 float topObscuredArea = 0;
5194 float bottomObscuredArea = 0;
5195 for (CheckedRef viewPositionedOutOfFlowBox : *viewPositionedOutOfFlowBoxes) {
5196 CheckedRef style = viewPositionedOutOfFlowBox->style();
5197 if (style->position() != PositionType::Fixed || style->usedVisibility() == Visibility::Hidden || style->opacity().isTransparent())
5198 continue;
5199
5200 FloatQuad contentQuad = viewPositionedOutOfFlowBox->absoluteContentQuad();
5201 if (!contentQuad.isRectilinear())
5202 continue;
5203
5204 FloatRect contentBoundingBox = contentQuad.boundingBox();
5205 FloatRect fixedRectInView = intersection(unobscuredContentRect, contentBoundingBox);
5206
5207 if (fixedRectInView.width() < unobscuredContentRect.width())
5208 continue;
5209
5210 if (fixedRectInView.y() == unobscuredContentRect.y())
5211 topObscuredArea = std::max(topObscuredArea, fixedRectInView.height());
5212 else if (fixedRectInView.maxY() == unobscuredContentRect.maxY())
5213 bottomObscuredArea = std::max(bottomObscuredArea, fixedRectInView.height());
5214 }
5215
5216 return Scrollbar::pageStep(unobscuredContentRect.height(), unobscuredContentRect.height() - topObscuredArea - bottomObscuredArea);
5217}
5218
5219void LocalFrameView::invalidateScrollbarRect(Scrollbar& scrollbar, const IntRect& rect)
5220{
5221 // Add in our offset within the LocalFrameView.
5222 IntRect dirtyRect = rect;
5223 dirtyRect.moveBy(scrollbar.location());
5224 invalidateRect(dirtyRect);
5225}
5226
5227void LocalFrameView::setVisibleScrollerThumbRect(const IntRect& scrollerThumb)
5228{
5229 if (!m_frame->isMainFrame())
5230 return;
5231
5232 if (RefPtr page = m_frame->page())
5233 page->chrome().client().notifyScrollerThumbIsVisibleInRect(scrollerThumb);
5234}
5235
5236bool LocalFrameView::isScrollable(Scrollability definitionOfScrollable)
5237{
5238 // Check for:
5239 // 1) If there an actual overflow.
5240 // 2) display:none or visibility:hidden set to self or inherited.
5241 // 3) overflow{-x,-y}: hidden;
5242 // 4) scrolling: no;
5243 if (!didFirstLayout())
5244 return false;
5245
5246#if HAVE(RUBBER_BANDING)(defined HAVE_RUBBER_BANDING && HAVE_RUBBER_BANDING)
5247 bool requiresActualOverflowToBeConsideredScrollable = !m_frame->isMainFrame() || definitionOfScrollable != Scrollability::ScrollableOrRubberbandable;
5248#else
5249 UNUSED_PARAM(definitionOfScrollable)(void)definitionOfScrollable;
5250 bool requiresActualOverflowToBeConsideredScrollable = true;
5251#endif
5252
5253 // Covers #1
5254 if (requiresActualOverflowToBeConsideredScrollable) {
5255 IntSize totalContentsSize = this->totalContentsSize();
5256 IntSize visibleContentSize = visibleContentRect(LegacyIOSDocumentVisibleRect).size();
5257 if (totalContentsSize.height() <= visibleContentSize.height() && totalContentsSize.width() <= visibleContentSize.width())
5258 return false;
5259 }
5260
5261 // Covers #2.
5262 RefPtr owner = m_frame->ownerElement();
5263 if (owner && (!owner->renderer() || !owner->renderer()->visibleToHitTesting()))
5264 return false;
5265
5266 // Cover #3 and #4.
5267 ScrollbarMode horizontalMode;
5268 ScrollbarMode verticalMode;
5269 calculateScrollbarModesForLayout(horizontalMode, verticalMode, RulesFromWebContentOnly);
5270 if (horizontalMode == ScrollbarMode::AlwaysOff && verticalMode == ScrollbarMode::AlwaysOff)
5271 return false;
5272
5273 return true;
5274}
5275
5276bool LocalFrameView::isScrollableOrRubberbandable()
5277{
5278 return isScrollable(Scrollability::ScrollableOrRubberbandable);
5279}
5280
5281bool LocalFrameView::hasScrollableOrRubberbandableAncestor()
5282{
5283 if (m_frame->isMainFrame())
5284 return isScrollableOrRubberbandable();
5285
5286 for (RefPtr parent = this->parentFrameView(); parent; parent = parent->parentFrameView()) {
5287 Scrollability frameScrollability = parent->m_frame->isMainFrame() ? Scrollability::ScrollableOrRubberbandable : Scrollability::Scrollable;
5288 if (parent->isScrollable(frameScrollability))
5289 return true;
5290 }
5291
5292 return false;
5293}
5294
5295void LocalFrameView::updateScrollableAreaSet()
5296{
5297 // That ensures that only inner frames are cached.
5298 RefPtr parentFrameView = this->parentFrameView();
5299 if (!parentFrameView)
5300 return;
5301
5302 if (!isScrollable()) {
5303 parentFrameView->removeScrollableArea(this);
5304 return;
5305 }
5306
5307 parentFrameView->addScrollableArea(this);
5308}
5309
5310bool LocalFrameView::shouldSuspendScrollAnimations() const
5311{
5312 return m_frame->loader().state() != FrameState::Complete;
5313}
5314
5315void LocalFrameView::notifyAllFramesThatContentAreaWillPaint() const
5316{
5317 notifyScrollableAreasThatContentAreaWillPaint();
5318
5319 for (RefPtr child = m_frame->tree().firstRenderedChild(); child; child = child->tree().traverseNextRendered(m_frame.ptr())) {
5320 RefPtr localChild = dynamicDowncast<LocalFrame>(child);
5321 if (!localChild)
5322 continue;
5323 if (RefPtr frameView = localChild->view())
5324 frameView->notifyScrollableAreasThatContentAreaWillPaint();
5325 }
5326}
5327
5328void LocalFrameView::notifyScrollableAreasThatContentAreaWillPaint() const
5329{
5330 RefPtr page = m_frame->page();
5331 if (!page)
5332 return;
5333
5334 contentAreaWillPaint();
5335
5336 if (!m_scrollableAreas)
5337 return;
5338
5339 for (CheckedRef area : *m_scrollableAreas) {
5340 CheckedPtr<ScrollableArea> scrollableArea(area);
5341 // ScrollView ScrollableAreas will be handled via the Frame tree traversal above.
5342 if (!is<ScrollView>(scrollableArea))
5343 scrollableArea->contentAreaWillPaint();
5344 }
5345}
5346
5347void LocalFrameView::updateScrollCorner()
5348{
5349 CheckedPtr<RenderElement> renderer;
5350 std::unique_ptr<RenderStyle> cornerStyle;
5351 IntRect cornerRect = scrollCornerRect();
5352 RefPtr doc = m_frame->document();
5353
5354 auto usesStandardScrollbarStyle = [](auto& doc) {
5355 if (!doc || !doc->documentElement())
5356 return false;
5357 if (!doc->documentElement()->renderer())
5358 return false;
5359
5360 return doc->documentElement()->renderer()->style().usesStandardScrollbarStyle();
5361 };
5362
5363 if (!(usesStandardScrollbarStyle(doc) || cornerRect.isEmpty())) {
5364 // Try the <body> element first as a scroll corner source.
5365 RefPtr body = doc ? doc->bodyOrFrameset() : nullptr;
5366 if (body && body->renderer()) {
5367 renderer = body->renderer();
5368 cornerStyle = renderer->getUncachedPseudoStyle({ PseudoElementType::WebKitScrollbarCorner }, &renderer->style());
5369 }
5370
5371 if (!cornerStyle) {
5372 // If the <body> didn't have a custom style, then the root element might.
5373 RefPtr docElement = doc ? doc->documentElement() : nullptr;
5374 if (docElement && docElement->renderer()) {
5375 renderer = docElement->renderer();
5376 cornerStyle = renderer->getUncachedPseudoStyle({ PseudoElementType::WebKitScrollbarCorner }, &renderer->style());
5377 }
5378 }
5379
5380 if (!cornerStyle) {
5381 // If we have an owning iframe/frame element, then it can set the custom scrollbar also.
5382 // FIXME: Seems wrong to do this for cross-origin frames.
5383 if (RefPtr renderer = m_frame->ownerRenderer())
5384 cornerStyle = renderer->getUncachedPseudoStyle({ PseudoElementType::WebKitScrollbarCorner }, &renderer->style());
5385 }
5386 }
5387
5388 if (!cornerStyle || !renderer)
5389 m_scrollCorner = nullptr;
5390 else {
5391 if (!m_scrollCorner) {
5392 m_scrollCorner = createRenderer<RenderScrollbarPart>(protect(renderer->document()), WTF::move(*cornerStyle));
5393 m_scrollCorner->initializeStyle();
5394 } else
5395 m_scrollCorner->setStyle(WTF::move(*cornerStyle));
5396 invalidateScrollCorner(cornerRect);
5397 }
5398}
5399
5400void LocalFrameView::paintScrollCorner(GraphicsContext& context, const IntRect& cornerRect)
5401{
5402 if (context.invalidatingControlTints()) {
5403 updateScrollCorner();
5404 return;
5405 }
5406
5407 if (m_scrollCorner) {
5408 if (m_frame->isMainFrame())
5409 context.fillRect(cornerRect, baseBackgroundColor());
5410 m_scrollCorner->paintIntoRect(context, cornerRect.location(), cornerRect);
5411 return;
5412 }
5413
5414 ScrollView::paintScrollCorner(context, cornerRect);
5415}
5416
5417void LocalFrameView::paintScrollbar(GraphicsContext& context, Scrollbar& bar, const IntRect& rect)
5418{
5419 if (bar.isCustomScrollbar() && m_frame->isMainFrame()) {
5420 IntRect toFill = bar.frameRect();
5421 toFill.intersect(rect);
5422 context.fillRect(toFill, baseBackgroundColor());
5423 }
5424
5425 ScrollView::paintScrollbar(context, bar, rect);
5426}
5427
5428Color LocalFrameView::documentBackgroundColor() const
5429{
5430 // <https://bugs.webkit.org/show_bug.cgi?id=59540> We blend the background color of
5431 // the document and the body against the base background color of the frame view.
5432 // Background images are unfortunately impractical to include.
5433
5434 // Return invalid Color objects whenever there is insufficient information.
5435 RefPtr backgroundDocument = [&] {
5436#if ENABLE(FULLSCREEN_API)(defined 1 && 1)
5437 if (RefPtr page = m_frame->page()) {
5438 if (auto* fullscreenDocument = page->outermostFullscreenDocument())
5439 return fullscreenDocument;
5440 }
5441#endif
5442 return m_frame->document();
5443 }();
5444
5445 if (!backgroundDocument)
5446 return Color();
5447
5448 RefPtr htmlElement = backgroundDocument->documentElement();
5449 RefPtr bodyElement = backgroundDocument->bodyOrFrameset();
5450
5451 // Start with invalid colors.
5452 Color htmlBackgroundColor;
5453 Color bodyBackgroundColor;
5454 if (htmlElement && htmlElement->renderer())
5455 htmlBackgroundColor = htmlElement->renderer()->style().visitedDependentBackgroundColorApplyingColorFilter();
5456 if (bodyElement && bodyElement->renderer())
5457 bodyBackgroundColor = bodyElement->renderer()->style().visitedDependentBackgroundColorApplyingColorFilter();
5458
5459#if ENABLE(FULLSCREEN_API)(defined 1 && 1)
5460 Color fullscreenBackgroundColor = [&] () -> Color {
5461 RefPtr documentFullscreen = backgroundDocument->fullscreenIfExists();
5462 if (!documentFullscreen)
5463 return { };
5464
5465 RefPtr fullscreenElement = documentFullscreen->fullscreenElement();
5466 if (!fullscreenElement)
5467 return { };
5468
5469 CheckedPtr fullscreenRenderer = fullscreenElement->renderer();
5470 if (!fullscreenRenderer)
5471 return { };
5472
5473 auto fullscreenElementColor = fullscreenRenderer->style().visitedDependentBackgroundColorApplyingColorFilter();
5474
5475 WeakPtr backdropRenderer = fullscreenRenderer->pseudoElementRenderer(PseudoElementType::Backdrop);
5476 if (!backdropRenderer)
5477 return fullscreenElementColor;
5478
5479 // Do not blend the fullscreenElementColor atop the backdrop color. The backdrop should
5480 // intentionally be visible underneath (and around) the fullscreen element.
5481 return backdropRenderer->style().visitedDependentBackgroundColorApplyingColorFilter();
5482 }();
5483
5484 // Replace or blend the fullscreen background color with the body background color, if present.
5485 if (fullscreenBackgroundColor.isValid()) {
5486 if (!bodyBackgroundColor.isValid())
5487 bodyBackgroundColor = fullscreenBackgroundColor;
5488 else
5489 bodyBackgroundColor = blendSourceOver(bodyBackgroundColor, fullscreenBackgroundColor);
5490 }
5491#endif
5492
5493 if (!bodyBackgroundColor.isValid()) {
5494 if (!htmlBackgroundColor.isValid())
5495 return Color();
5496 return blendSourceOver(baseBackgroundColor(), htmlBackgroundColor);
5497 }
5498
5499 if (!htmlBackgroundColor.isValid())
5500 return blendSourceOver(baseBackgroundColor(), bodyBackgroundColor);
5501
5502 // We take the aggregate of the base background color
5503 // the <html> background color, and the <body>
5504 // background color to find the document color. The
5505 // addition of the base background color is not
5506 // technically part of the document background, but it
5507 // otherwise poses problems when the aggregate is not
5508 // fully opaque.
5509 return blendSourceOver(blendSourceOver(baseBackgroundColor(), htmlBackgroundColor), bodyBackgroundColor);
5510}
5511
5512bool LocalFrameView::hasCustomScrollbars() const
5513{
5514 for (auto& widget : children()) {
5515 if (RefPtr frame = dynamicDowncast<LocalFrameView>(widget)) {
5516 if (frame->hasCustomScrollbars())
5517 return true;
5518 } else if (RefPtr scrollbar = dynamicDowncast<Scrollbar>(widget)) {
5519 if (scrollbar->isCustomScrollbar())
5520 return true;
5521 }
5522 }
5523 return false;
5524}
5525
5526LocalFrameView* LocalFrameView::parentFrameView() const
5527{
5528 if (!parent())
5529 return nullptr;
5530 RefPtr parentFrame = dynamicDowncast<LocalFrame>(m_frame->tree().parent());
5531 if (!parentFrame)
5532 return nullptr;
5533 return parentFrame->view();
5534}
5535
5536void LocalFrameView::updateControlTints()
5537{
5538 // This is called when control tints are changed from aqua/graphite to clear and vice versa.
5539 // We do a "fake" paint, and when the theme gets a paint call, it can then do an invalidate.
5540 // This is only done if the theme supports control tinting. It's up to the theme and platform
5541 // to define when controls get the tint and to call this function when that changes.
5542
5543 // Optimize the common case where we bring a window to the front while it's still empty.
5544 if (m_frame->document()->url().isEmpty())
5545 return;
5546
5547 // As noted above, this is a "fake" paint, so we should pause counting relevant repainted objects.
5548 RefPtr page = m_frame->page();
5549 bool isCurrentlyCountingRelevantRepaintedObject = false;
5550 if (page) {
5551 isCurrentlyCountingRelevantRepaintedObject = page->isCountingRelevantRepaintedObjects();
5552 page->setIsCountingRelevantRepaintedObjects(false);
5553 }
5554
5555 CheckedPtr renderView = this->renderView();
5556 if ((renderView && renderView->theme().supportsControlTints()) || hasCustomScrollbars())
5557 invalidateControlTints();
5558
5559 if (page)
5560 page->setIsCountingRelevantRepaintedObjects(isCurrentlyCountingRelevantRepaintedObject);
5561}
5562
5563void LocalFrameView::invalidateControlTints()
5564{
5565 traverseForPaintInvalidation(NullGraphicsContext::PaintInvalidationReasons::InvalidatingControlTints);
5566}
5567
5568void LocalFrameView::updateAccessibilityObjectRegions()
5569{
5570#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)(defined ENABLE_ACCESSIBILITY_ISOLATED_TREE && ENABLE_ACCESSIBILITY_ISOLATED_TREE
)
5571 if (!AXObjectCache::accessibilityEnabled() || !AXObjectCache::isIsolatedTreeEnabled())
5572 return;
5573
5574 NullGraphicsContext graphicsContext;
5575 AccessibilityRegionContext accessibilityRegionContext;
5576 paint(graphicsContext, frameRect(), SecurityOriginPaintPolicy::AnyOrigin, &accessibilityRegionContext);
5577#endif
5578}
5579
5580void LocalFrameView::traverseForPaintInvalidation(NullGraphicsContext::PaintInvalidationReasons paintInvalidationReasons)
5581{
5582 if (needsLayout())
5583 layoutContext().layout();
5584
5585 NullGraphicsContext context(paintInvalidationReasons);
5586 if (platformWidget()) {
5587 // FIXME: consult paintsEntireContents().
5588 paintContents(context, visibleContentRect(LegacyIOSDocumentVisibleRect));
5589 } else
5590 paint(context, frameRect());
5591}
5592
5593bool LocalFrameView::wasScrolledByUser() const
5594{
5595 return m_lastUserScrollType.has_value();
5596}
5597
5598void LocalFrameView::setLastUserScrollType(std::optional<UserScrollType> userScrollType)
5599{
5600 LOG(Scrolling, "LocalFrameView::setLastUserScrollType at %d", userScrollType ? std::to_underlying(*userScrollType) : -1)((void)0);
5601
5602 cancelScheduledScrolls();
5603 if (currentScrollType() == ScrollType::Programmatic)
5604 return;
5605
5606 RefPtr document = m_frame->document();
5607 if (userScrollType && document)
5608 document->setGotoAnchorNeededAfterStylesheetsLoad(false);
5609
5610 if (userScrollType == UserScrollType::Explicit) {
5611 m_wasEverScrolledExplicitlyByUser = true;
5612 if (scrollOffset().y() > minimumScrollOffset().y())
5613 m_wasEverScrolledExplicitlyByUserBelowTopEdge = true;
5614 }
5615
5616 m_maintainScrollPositionAnchor = nullptr;
5617 if (m_lastUserScrollType == userScrollType)
5618 return;
5619 m_lastUserScrollType = userScrollType;
5620 adjustTiledBackingCoverage();
5621}
5622
5623void LocalFrameView::willPaintContents(GraphicsContext& context, const IntRect&, PaintingState& paintingState, RegionContext* regionContext)
5624{
5625 RefPtr document = m_frame->document();
5626
5627 if (!context.paintingDisabled())
5628 InspectorInstrumentation::willPaint(*renderView());
5629
5630 paintingState.isTopLevelPainter = !sCurrentPaintTimeStamp;
5631
5632 if (paintingState.isTopLevelPainter)
5633 sCurrentPaintTimeStamp = MonotonicTime::now();
5634
5635 paintingState.paintBehavior = m_paintBehavior;
5636
5637 if (RefPtr parentView = parentFrameView()) {
5638 static constexpr OptionSet flagsToCopy {
5639 PaintBehavior::FlattenCompositingLayers,
5640 PaintBehavior::Snapshotting,
5641 PaintBehavior::DefaultAsynchronousImageDecode,
5642 PaintBehavior::ForceSynchronousImageDecode,
5643 PaintBehavior::ExcludeReplacedContentExceptForIFrames,
5644 PaintBehavior::ExcludeText,
5645 PaintBehavior::FixedAndStickyLayersOnly,
5646 };
5647 m_paintBehavior.add(parentView->paintBehavior() & flagsToCopy);
5648 }
5649
5650 if (document->printing()) {
5651 m_paintBehavior.add(PaintBehavior::FlattenCompositingLayers);
5652 m_paintBehavior.add(PaintBehavior::Snapshotting);
5653 }
5654
5655 if (is<AccessibilityRegionContext>(regionContext))
5656 m_paintBehavior.add(PaintBehavior::FlattenCompositingLayers);
5657
5658 paintingState.isFlatteningPaintOfRootFrame = (m_paintBehavior & PaintBehavior::FlattenCompositingLayers) && !m_frame->ownerElement() && !context.detectingContentfulPaint();
5659 if (paintingState.isFlatteningPaintOfRootFrame)
5660 notifyWidgetsInAllFrames(WidgetNotification::WillPaintFlattened);
5661
5662 ASSERT(!m_isPainting)((void)0);
5663 m_isPainting = true;
5664}
5665
5666void LocalFrameView::didPaintContents(GraphicsContext& context, const IntRect& dirtyRect, PaintingState& paintingState)
5667{
5668 m_isPainting = false;
5669
5670 if (paintingState.isFlatteningPaintOfRootFrame)
5671 notifyWidgetsInAllFrames(WidgetNotification::DidPaintFlattened);
5672
5673 m_paintBehavior = paintingState.paintBehavior;
5674 m_lastPaintTime = MonotonicTime::now();
5675
5676 if (paintingState.isTopLevelPainter)
5677 sCurrentPaintTimeStamp = MonotonicTime();
5678
5679 if (!context.paintingDisabled()) {
5680 InspectorInstrumentation::didPaint(*renderView(), dirtyRect);
5681 // FIXME: should probably not fire milestones for snapshot painting. https://bugs.webkit.org/show_bug.cgi?id=117623
5682 firePaintRelatedMilestonesIfNeeded();
5683 }
5684}
5685
5686void LocalFrameView::paintContents(GraphicsContext& context, const IntRect& dirtyRect, SecurityOriginPaintPolicy securityOriginPaintPolicy, RegionContext* regionContext)
5687{
5688#ifndef NDEBUG1
5689 bool fillWithWarningColor = [&] {
5690 if (m_frame->document()->printing())
5691 return false; // Printing, don't fill with warning color (can't remember why).
5692
5693 if (m_frame->ownerElement())
5694 return false; // Subframe, don't fill with warning color.
5695
5696 if (isTransparent())
5697 return false; // Transparent, don't fill with warning color.
5698
5699 if (m_paintBehavior.containsAny({ PaintBehavior::SelectionOnly, PaintBehavior::FixedAndStickyLayersOnly }))
5700 return false; // Don't fill with warning color for selection and fixed-position snapshots.
5701
5702 if (m_nodeToDraw)
5703 return false; // Element images are transparent, don't fill with warning color.
5704
5705 return true;
5706 }();
5707
5708 if (fillWithWarningColor) {
5709 IntRect debugRect = frameRect();
5710 debugRect.intersect(dirtyRect);
5711 context.fillRect(debugRect, SRGBA<uint8_t> { 255, 64, 255 });
5712 }
5713#endif
5714
5715 CheckedPtr renderView = this->renderView();
5716 if (!renderView) {
5717 LOG_ERROR("called LocalFrameView::paint with nil renderer")((void)0);
5718 return;
5719 }
5720
5721 if (!layoutContext().inPaintableState())
5722 return;
5723
5724 ASSERT(!needsLayout())((void)0);
5725 if (needsLayout()) {
5726 FRAMEVIEW_RELEASE_LOG(Layout, LOCALFRAMEVIEW_NOT_PAINTING_LAYOUT_NEEDED)do { if (auto& client = logClient()) client->LOCALFRAMEVIEW_NOT_PAINTING_LAYOUT_NEEDED
(PAGE_ID, FRAME_ID, m_frame->isMainFrame()); else 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 = (LogLayout.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(" [pageID=%" "ll" "u"
", frameID=%" "ll" "u" ", isMainFrame=%d] LocalFrameView::paintContents: Not painting because render tree needs layout"
), "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_LOG50"
) = " [pageID=%" "ll" "u" ", frameID=%" "ll" "u" ", isMainFrame=%d] LocalFrameView::paintContents: Not painting because render tree needs layout"
; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf
[__builtin_os_log_format_buffer_size(" [pageID=%" "ll" "u"
", frameID=%" "ll" "u" ", isMainFrame=%d] LocalFrameView::paintContents: Not painting because render tree needs layout"
, WTF::safePrintfType(PAGE_ID) , WTF::safePrintfType(FRAME_ID
) , WTF::safePrintfType(m_frame->isMainFrame()))]; _os_log_impl
(&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t
*)__builtin_os_log_format(_os_fmt_buf, " [pageID=%" "ll" "u"
", frameID=%" "ll" "u" ", isMainFrame=%d] LocalFrameView::paintContents: Not painting because render tree needs layout"
, WTF::safePrintfType(PAGE_ID) , WTF::safePrintfType(FRAME_ID
) , WTF::safePrintfType(m_frame->isMainFrame())), (uint32_t
)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic
pop ; } while (0)
;
5727 return;
5728 }
5729
5730 PaintingState paintingState;
5731 willPaintContents(context, dirtyRect, paintingState, regionContext);
5732
5733 // m_nodeToDraw is used to draw only one element (and its descendants)
5734 RenderObject* renderer = m_nodeToDraw ? m_nodeToDraw->renderer() : nullptr;
5735 CheckedPtr rootLayer = renderView->layer();
5736
5737 RenderObject::SetLayoutNeededForbiddenScope forbidSetNeedsLayout(rootLayer->renderer());
5738
5739 rootLayer->paint(context, dirtyRect, LayoutSize(), m_paintBehavior, renderer, { }, securityOriginPaintPolicy == SecurityOriginPaintPolicy::AnyOrigin ? RenderLayer::SecurityOriginPaintPolicy::AnyOrigin : RenderLayer::SecurityOriginPaintPolicy::AccessibleOriginOnly, regionContext);
5740 if (auto* scrollableRootLayer = rootLayer->scrollableArea()) {
5741 if (scrollableRootLayer->containsDirtyOverlayScrollbars() && !regionContext)
5742 scrollableRootLayer->paintOverlayScrollbars(context, dirtyRect, m_paintBehavior, renderer);
5743 }
5744
5745 didPaintContents(context, dirtyRect, paintingState);
5746}
5747
5748void LocalFrameView::setPaintBehavior(OptionSet<PaintBehavior> behavior)
5749{
5750 m_paintBehavior = behavior;
5751}
5752
5753OptionSet<PaintBehavior> LocalFrameView::paintBehavior() const
5754{
5755 return m_paintBehavior;
5756}
5757
5758bool LocalFrameView::isPainting() const
5759{
5760 return m_isPainting;
5761}
5762
5763// FIXME: change this to use the subtreePaint terminology.
5764void LocalFrameView::setNodeToDraw(Node* node)
5765{
5766 m_nodeToDraw = node;
5767}
5768
5769void LocalFrameView::paintContentsForSnapshot(GraphicsContext& context, const IntRect& imageRect, SelectionInSnapshot shouldPaintSelection, CoordinateSpaceForSnapshot coordinateSpace)
5770{
5771 updateLayoutAndStyleIfNeededRecursive();
5772
5773 // Cache paint behavior and set a new behavior appropriate for snapshots.
5774 auto oldBehavior = paintBehavior();
5775 setPaintBehavior(oldBehavior | PaintBehavior::FlattenCompositingLayers | PaintBehavior::Snapshotting);
5776
5777 // If the snapshot should exclude selection, then we'll clear the current selection
5778 // in the render tree only. This will allow us to restore the selection from the DOM
5779 // after we paint the snapshot.
5780 if (shouldPaintSelection == ExcludeSelection) {
5781 for (RefPtr<Frame> frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr())) {
5782 RefPtr localFrame = dynamicDowncast<LocalFrame>(frame);
5783 if (!localFrame)
5784 continue;
5785 if (CheckedPtr renderView = localFrame->contentRenderer())
5786 renderView->selection().clear();
5787 }
5788 }
5789
5790 if (coordinateSpace == DocumentCoordinates)
5791 paintContents(context, imageRect);
5792 else {
5793 // A snapshot in ViewCoordinates will include a scrollbar, and the snapshot will contain
5794 // whatever content the document is currently scrolled to.
5795 paint(context, imageRect);
5796 }
5797
5798 // Restore selection.
5799 if (shouldPaintSelection == ExcludeSelection) {
5800 for (RefPtr<Frame> frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr())) {
5801 RefPtr localFrame = dynamicDowncast<LocalFrame>(frame);
5802 if (!localFrame)
5803 continue;
5804 localFrame->selection().updateAppearance();
5805 }
5806 }
5807
5808 // Restore cached paint behavior.
5809 setPaintBehavior(oldBehavior);
5810}
5811
5812void LocalFrameView::paintOverhangAreas(GraphicsContext& context, const IntRect& horizontalOverhangArea, const IntRect& verticalOverhangArea, const IntRect& dirtyRect)
5813{
5814 if (context.paintingDisabled())
5815 return;
5816
5817 if (m_frame->document()->printing())
5818 return;
5819
5820 ScrollView::paintOverhangAreas(context, horizontalOverhangArea, verticalOverhangArea, dirtyRect);
5821}
5822
5823void LocalFrameView::updateLayoutAndStyleIfNeededRecursive(OptionSet<LayoutOptions> layoutOptions)
5824{
5825 // Style updating, render tree creation, and layout needs to be done multiple times
5826 // for more than one reason. But one reason is that when an <object> element determines
5827 // what it needs to load a subframe, a second pass is needed. That requires update
5828 // passes equal to the number of levels of DOM nesting. That is why this number is large.
5829 // There are test cases where we have roughly 10 levels of DOM nesting, so this needs to
5830 // be greater than that. We have a limit to avoid the possibility of an infinite loop.
5831 // Typical calls will run the loop 2 times (once to do work, once to detect no further work
5832 // is needed).
5833 // FIXME: We should find an approach that does not require a loop at all.
5834 const unsigned maxUpdatePasses = 25;
5835
5836 // Style updates can trigger script, which can cause this LocalFrameView to be destroyed.
5837 Ref<LocalFrameView> protectedThis(*this);
5838
5839 using DescendantsDeque = Deque<Ref<LocalFrameView>, 16>;
5840 auto nextRenderedDescendant = [this] (DescendantsDeque& descendantsDeque) -> RefPtr<LocalFrameView> {
5841 if (descendantsDeque.isEmpty())
5842 descendantsDeque.append(*this);
5843 else {
5844 // Append renderered children after processing the parent, in case the processing
5845 // affects the set of rendered children.
5846 auto previousView = descendantsDeque.takeFirst();
5847 for (RefPtr<Frame> frame = previousView->m_frame->tree().firstRenderedChild(); frame; frame = frame->tree().nextRenderedSibling()) {
5848 RefPtr localFrame = dynamicDowncast<LocalFrame>(frame);
5849 if (!localFrame)
5850 continue;
5851 if (RefPtr view = localFrame->view())
5852 descendantsDeque.append(*view);
5853 }
5854 if (descendantsDeque.isEmpty())
5855 return nullptr;
5856 }
5857 return descendantsDeque.first().ptr();
5858 };
5859
5860 for (unsigned i = 0; i < maxUpdatePasses; ++i) {
5861 bool didWork = false;
5862 DescendantsDeque deque;
5863 while (auto view = nextRenderedDescendant(deque)) {
5864 if (view->m_frame->document()->updateLayout(layoutOptions | LayoutOptions::DoNotLayoutAncestorDocuments) == Document::UpdateLayoutResult::ChangesDone)
5865 didWork = true;
5866 }
5867 if (!didWork)
5868 break;
5869 }
5870
5871#if ASSERT_ENABLED0
5872 auto needsStyleRecalc = [&] {
5873 DescendantsDeque deque;
5874 while (auto view = nextRenderedDescendant(deque)) {
5875 RefPtr document = view->m_frame->document();
5876 if (document && document->childNeedsStyleRecalc())
5877 return true;
5878 }
5879 return false;
5880 };
5881
5882 auto needsLayout = [&] {
5883 DescendantsDeque deque;
5884 while (auto view = nextRenderedDescendant(deque)) {
5885 if (view->needsLayout())
5886 return true;
5887 }
5888 return false;
5889 };
5890#endif // ASSERT_ENABLED
5891
5892 ASSERT(!needsStyleRecalc())((void)0);
5893 ASSERT(!needsLayout())((void)0);
5894}
5895
5896#include <span>
5897
5898template<typename CharacterType>
5899static size_t nonWhitespaceLength(std::span<const CharacterType> characters)
5900{
5901 size_t result = characters.size();
5902 for (auto character : characters) {
5903 if (isASCIIWhitespace(character))
5904 --result;
5905 }
5906 return result;
5907}
5908
5909void LocalFrameView::incrementVisuallyNonEmptyCharacterCountSlowCase(const String& inlineText)
5910{
5911 if (inlineText.is8Bit())
5912 m_visuallyNonEmptyCharacterCount += nonWhitespaceLength(inlineText.span8());
5913 else
5914 m_visuallyNonEmptyCharacterCount += nonWhitespaceLength(inlineText.span16());
5915 ++m_textRendererCountForVisuallyNonEmptyCharacters;
5916}
5917
5918void LocalFrameView::updateHasReachedSignificantRenderedTextThreshold()
5919{
5920 if (m_hasReachedSignificantRenderedTextThreshold)
5921 return;
5922
5923 RefPtr page = m_frame->page();
5924 if (!page || !page->requestedLayoutMilestones().contains(LayoutMilestone::DidRenderSignificantAmountOfText))
5925 return;
5926
5927 RefPtr document = m_frame->document();
5928 if (!document)
5929 return;
5930
5931 document->updateMainArticleElementAfterLayout();
5932 auto hasMainArticleElement = document->hasMainArticleElement();
5933 auto characterThreshold = hasMainArticleElement ? mainArticleSignificantRenderedTextCharacterThreshold : defaultSignificantRenderedTextCharacterThreshold;
5934 if (m_visuallyNonEmptyCharacterCount < characterThreshold)
5935 return;
5936
5937 auto meanLength = hasMainArticleElement ? mainArticleSignificantRenderedTextMeanLength : defaultSignificantRenderedTextMeanLength;
5938 if (!m_textRendererCountForVisuallyNonEmptyCharacters || m_visuallyNonEmptyCharacterCount / static_cast<float>(m_textRendererCountForVisuallyNonEmptyCharacters) < meanLength)
5939 return;
5940
5941 m_hasReachedSignificantRenderedTextThreshold = true;
5942}
5943
5944bool LocalFrameView::qualifiesAsSignificantRenderedText() const
5945{
5946 ASSERT(!m_renderedSignificantAmountOfText)((void)0);
5947 RefPtr document = m_frame->document();
5948 if (!document || document->styleScope().hasPendingSheetsBeforeBody())
5949 return false;
5950 return m_hasReachedSignificantRenderedTextThreshold;
5951}
5952
5953void LocalFrameView::checkAndDispatchDidReachVisuallyNonEmptyState()
5954{
5955 auto qualifiesAsVisuallyNonEmpty = [&] {
5956 // No content yet.
5957 Ref document = *m_frame->document();
5958 RefPtr documentElement = document->documentElement();
5959 if (!documentElement || !documentElement->renderer())
5960 return false;
5961
5962 if (document->hasVisuallyNonEmptyCustomContent())
5963 return true;
5964
5965 // FIXME: We should also ignore renderers with non-final style.
5966 if (document->styleScope().hasPendingSheetsBeforeBody())
5967 return false;
5968
5969 auto finishedParsingMainDocument = m_frame->loader().stateMachine().committedFirstRealDocumentLoad()
5970 && (document->readyState() == Document::ReadyState::Interactive || document->readyState() == Document::ReadyState::Complete);
5971 // Ensure that we always fire visually non-empty milestone eventually.
5972 if (finishedParsingMainDocument && m_frame->loader().isComplete())
5973 return true;
5974
5975 auto isVisible = [](const Element* element) {
5976 if (!element || !element->renderer())
5977 return false;
5978 if (!element->renderer()->opacity())
5979 return false;
5980 return element->renderer()->style().usedVisibility() == Visibility::Visible;
5981 };
5982
5983 if (!isVisible(documentElement.get()))
5984 return false;
5985
5986 if (!isVisible(document->body()))
5987 return false;
5988
5989 // The first few hundred characters rarely contain the interesting content of the page.
5990 if (m_visuallyNonEmptyCharacterCount > visualCharacterThreshold)
5991 return true;
5992
5993 // Use a threshold value to prevent very small amounts of visible content from triggering didFirstVisuallyNonEmptyLayout
5994 if (m_visuallyNonEmptyPixelCount > visualPixelThreshold)
5995 return true;
5996
5997 auto isMoreContentExpected = [&]() {
5998 ASSERT(finishedParsingMainDocument)((void)0);
5999 // Pending css/font loading means we should wait a little longer. Classic non-async, non-defer scripts are all processed by now.
6000 RefPtr documentLoader = m_frame->loader().documentLoader();
6001 if (!documentLoader)
6002 return false;
6003
6004 auto& resourceLoader = documentLoader->cachedResourceLoader();
6005 if (!resourceLoader.requestCount())
6006 return false;
6007
6008 auto& resources = resourceLoader.allCachedResources();
6009 for (auto& resource : resources) {
6010 if (resource.value->isLoaded())
6011 continue;
6012 // ResourceLoadPriority::VeryLow is used for resources that are not needed to render.
6013 if (resource.value->loadPriority() == ResourceLoadPriority::VeryLow)
6014 continue;
6015 if (resource.value->type() == CachedResource::Type::CSSStyleSheet || resource.value->type() == CachedResource::Type::FontResource)
6016 return true;
6017 }
6018 return false;
6019 };
6020
6021 // Finished parsing the main document and we still don't yet have enough content. Check if we might be getting some more.
6022 if (finishedParsingMainDocument)
6023 return !isMoreContentExpected();
6024
6025 return false;
6026 };
6027
6028 if (m_contentQualifiesAsVisuallyNonEmpty)
6029 return;
6030
6031 if (!qualifiesAsVisuallyNonEmpty())
6032 return;
6033
6034 m_contentQualifiesAsVisuallyNonEmpty = true;
6035 if (m_frame->isRootFrame())
6036 m_frame->loader().didReachVisuallyNonEmptyState();
6037}
6038
6039bool LocalFrameView::hasContentfulDescendants() const
6040{
6041 return m_visuallyNonEmptyCharacterCount || m_visuallyNonEmptyPixelCount;
6042}
6043
6044bool LocalFrameView::isViewForDocumentInFrame() const
6045{
6046 CheckedPtr renderView = this->renderView();
6047 if (!renderView)
6048 return false;
6049
6050 return &renderView->frameView() == this;
A function 'isViewForDocumentInFrame' has [[clang::annotate_type("webkit.nodelete")]] but it contains code that could destruct an object
6051}
6052
6053void LocalFrameView::enableFixedWidthAutoSizeMode(bool enable, const IntSize& viewSize)
6054{
6055 // FIXME: Figure out if this flavor of the autosizing should run only on the mainframe.
6056 enableAutoSizeMode(enable, viewSize, AutoSizeMode::FixedWidth);
6057}
6058
6059void LocalFrameView::enableSizeToContentAutoSizeMode(bool enable, const IntSize& viewSize)
6060{
6061 ASSERT(m_frame->isMainFrame())((void)0);
6062 enableAutoSizeMode(enable, viewSize, AutoSizeMode::SizeToContent);
6063}
6064
6065void LocalFrameView::enableAutoSizeMode(bool enable, const IntSize& viewSize, AutoSizeMode mode)
6066{
6067 ASSERT(!enable || !viewSize.isEmpty())((void)0);
6068 if (m_shouldAutoSize == enable && m_autoSizeConstraint == viewSize)
6069 return;
6070
6071 m_autoSizeMode = mode;
6072 m_shouldAutoSize = enable;
6073 m_autoSizeConstraint = viewSize;
6074 m_autoSizeContentSize = contentsSize();
6075 m_didRunAutosize = false;
6076
6077 setNeedsLayoutAfterViewConfigurationChange();
6078 layoutContext().scheduleLayout();
6079 if (m_shouldAutoSize) {
6080 overrideWidthForCSSDefaultViewportUnits(m_autoSizeConstraint.width());
6081 overrideWidthForCSSSmallViewportUnits(m_autoSizeConstraint.width());
6082 overrideWidthForCSSLargeViewportUnits(m_autoSizeConstraint.width());
6083 return;
6084 }
6085
6086 clearSizeOverrideForCSSDefaultViewportUnits();
6087 clearSizeOverrideForCSSSmallViewportUnits();
6088 clearSizeOverrideForCSSLargeViewportUnits();
6089 // Since autosize mode forces the scrollbar mode, change them to being auto.
6090 setVerticalScrollbarLock(false);
6091 setHorizontalScrollbarLock(false);
6092 setScrollbarModes(ScrollbarMode::Auto, ScrollbarMode::Auto);
6093}
6094
6095void LocalFrameView::forceLayout(bool allowSubtreeLayout)
6096{
6097 if (!allowSubtreeLayout && layoutContext().subtreeLayoutRoot())
6098 layoutContext().convertSubtreeLayoutToFullLayout();
6099 layoutContext().layout();
6100}
6101
6102void LocalFrameView::forceLayoutForPagination(const FloatSize& pageSize, const FloatSize& originalPageSize, float maximumShrinkFactor, AdjustViewSize shouldAdjustViewSize)
6103{
6104 if (!renderView())
6105 return;
6106
6107 Ref<LocalFrameView> protectedThis(*this);
6108 CheckedRef renderView = *this->renderView();
6109
6110 // Dumping externalRepresentation(m_frame->renderer()).ascii() is a good trick to see
6111 // the state of things before and after the layout
6112 bool isHorizontalWritingMode = renderView->writingMode().isHorizontal();
6113 float pageLogicalWidth = isHorizontalWritingMode ? pageSize.width() : pageSize.height();
6114 float pageLogicalHeight = isHorizontalWritingMode ? pageSize.height() : pageSize.width();
6115
6116 renderView->setPageLogicalSize({ floor(pageLogicalWidth), floor(pageLogicalHeight) });
6117 renderView->setNeedsLayoutAndPreferredWidthsUpdate();
6118 forceLayout();
6119 if (hasOneRef())
6120 return;
6121
6122 // If we don't fit in the given page width, we'll lay out again. If we don't fit in the
6123 // page width when shrunk, we will lay out at maximum shrink and clip extra content.
6124 // FIXME: We are assuming a shrink-to-fit printing implementation. A cropping
6125 // implementation should not do this!
6126 const LayoutRect& documentRect = renderView->documentRect();
6127 LayoutUnit docLogicalWidth = isHorizontalWritingMode ? documentRect.width() : documentRect.height();
6128 if (docLogicalWidth > pageLogicalWidth) {
6129 int expectedPageWidth = std::min<float>(documentRect.width(), pageSize.width() * maximumShrinkFactor);
6130 int expectedPageHeight = std::min<float>(documentRect.height(), pageSize.height() * maximumShrinkFactor);
6131 FloatSize maxPageSize = m_frame->resizePageRectsKeepingRatio(FloatSize(originalPageSize.width(), originalPageSize.height()), FloatSize(expectedPageWidth, expectedPageHeight));
6132 pageLogicalWidth = isHorizontalWritingMode ? maxPageSize.width() : maxPageSize.height();
6133 pageLogicalHeight = isHorizontalWritingMode ? maxPageSize.height() : maxPageSize.width();
6134
6135 renderView->setPageLogicalSize({ floor(pageLogicalWidth), floor(pageLogicalHeight) });
6136 renderView->setNeedsLayoutAndPreferredWidthsUpdate();
6137 forceLayout();
6138 if (hasOneRef())
6139 return;
6140
6141 const LayoutRect& updatedDocumentRect = renderView->documentRect();
6142 LayoutUnit docLogicalHeight = isHorizontalWritingMode ? updatedDocumentRect.height() : updatedDocumentRect.width();
6143 LayoutUnit docLogicalTop = isHorizontalWritingMode ? updatedDocumentRect.y() : updatedDocumentRect.x();
6144 LayoutUnit docLogicalRight = isHorizontalWritingMode ? updatedDocumentRect.maxX() : updatedDocumentRect.maxY();
6145 LayoutUnit clippedLogicalLeft;
6146 if (!renderView->writingMode().isAnyLeftToRight())
6147 clippedLogicalLeft = docLogicalRight - pageLogicalWidth;
6148 LayoutRect overflow { clippedLogicalLeft, docLogicalTop, LayoutUnit(pageLogicalWidth), docLogicalHeight };
6149
6150 if (!isHorizontalWritingMode)
6151 overflow = overflow.transposedRect();
6152 renderView->clearLayoutOverflow();
6153 renderView->addLayoutOverflow(overflow); // This is how we clip in case we overflow again.
6154 }
6155
6156 if (shouldAdjustViewSize == AdjustViewSize::Yes)
6157 adjustViewSize();
6158}
6159
6160void LocalFrameView::adjustPageHeightDeprecated(float *newBottom, float oldTop, float oldBottom, float /*bottomLimit*/)
6161{
6162 CheckedPtr renderView = this->renderView();
6163 if (!renderView) {
6164 *newBottom = oldBottom;
6165 return;
6166
6167 }
6168 // Use a context with painting disabled.
6169 NullGraphicsContext context;
6170 renderView->setTruncatedAt(static_cast<int>(floorf(oldBottom)));
6171 IntRect dirtyRect(0, static_cast<int>(floorf(oldTop)), renderView->layoutOverflowRect().maxX(), static_cast<int>(ceilf(oldBottom - oldTop)));
6172 renderView->setPrintRect(dirtyRect);
6173 renderView->layer()->paint(context, dirtyRect);
6174 *newBottom = renderView->bestTruncatedAt();
6175 if (!*newBottom)
6176 *newBottom = oldBottom;
6177 renderView->setPrintRect(IntRect());
6178}
6179
6180float LocalFrameView::documentToAbsoluteScaleFactor(std::optional<float> usedZoom) const
6181{
6182 // If usedZoom is passed, it already factors in pageZoomFactor().
6183#if CPU(X86_64)(defined WTF_CPU_X86_64 && WTF_CPU_X86_64)
6184 // FIXME(rdar://165780260): Remove when this optimizer issue is resolved.
6185 // Clang's Intel optimizer requires us to help it figure out `value_or` to
6186 // avoid a performance regression.
6187 if (usedZoom.has_value()) [[unlikely]]
6188 return usedZoom.value() * m_frame->frameScaleFactor();
6189 return m_frame->pageZoomFactor() * m_frame->frameScaleFactor();
6190#else
6191 return usedZoom.value_or(m_frame->pageZoomFactor()) * m_frame->frameScaleFactor();
6192#endif
6193}
6194
6195float LocalFrameView::absoluteToDocumentScaleFactor(std::optional<float> usedZoom) const
6196{
6197 // If usedZoom is passed, it already factors in pageZoomFactor().
6198 return 1 / documentToAbsoluteScaleFactor(usedZoom);
6199}
6200
6201FloatRect LocalFrameView::absoluteToDocumentRect(FloatRect rect, std::optional<float> usedZoom) const
6202{
6203 rect.scale(absoluteToDocumentScaleFactor(usedZoom));
6204 return rect;
6205}
6206
6207FloatPoint LocalFrameView::absoluteToDocumentPoint(FloatPoint p, std::optional<float> usedZoom) const
6208{
6209 return p.scaled(absoluteToDocumentScaleFactor(usedZoom));
6210}
6211
6212DoublePoint LocalFrameView::absoluteToDocumentPoint(DoublePoint p, std::optional<float> usedZoom) const
6213{
6214 return p.scaled(absoluteToDocumentScaleFactor(usedZoom));
6215}
6216
6217FloatRect LocalFrameView::absoluteToClientRect(FloatRect rect, std::optional<float> usedZoom) const
6218{
6219 return documentToClientRect(absoluteToDocumentRect(rect, usedZoom));
6220}
6221
6222FloatSize LocalFrameView::documentToClientOffset() const
6223{
6224 FloatSize clientOrigin;
6225 RefPtr document = m_frame->document();
6226 if (document && document->quirks().shouldUseLayoutViewportForClientRects())
6227 clientOrigin = -toFloatSize(layoutViewportRect().location());
6228 else
6229 clientOrigin = -toFloatSize(visibleContentRect().location());
6230
6231 // Layout and visual viewports are affected by page zoom, so we need to factor that out.
6232 return clientOrigin.scaled(1 / (m_frame->pageZoomFactor() * m_frame->frameScaleFactor()));
6233}
6234
6235FloatRect LocalFrameView::documentToClientRect(FloatRect rect) const
6236{
6237 rect.move(documentToClientOffset());
6238 return rect;
6239}
6240
6241FloatPoint LocalFrameView::documentToClientPoint(FloatPoint p) const
6242{
6243 p.move(documentToClientOffset());
6244 return p;
6245}
6246
6247DoublePoint LocalFrameView::documentToClientPoint(DoublePoint p) const
6248{
6249 p.move(documentToClientOffset());
6250 return p;
6251}
6252
6253FloatRect LocalFrameView::clientToDocumentRect(FloatRect rect) const
6254{
6255 rect.move(-documentToClientOffset());
6256 return rect;
6257}
6258
6259FloatPoint LocalFrameView::clientToDocumentPoint(FloatPoint point) const
6260{
6261 point.move(-documentToClientOffset());
6262 return point;
6263}
6264
6265FloatRect LocalFrameView::clientToLayoutViewportRect(FloatRect rect) const
6266{
6267 ASSERT(m_frame->settings().visualViewportEnabled())((void)0);
6268 rect.scale(m_frame->pageZoomFactor());
6269 return rect;
6270}
6271
6272FloatPoint LocalFrameView::clientToLayoutViewportPoint(FloatPoint p) const
6273{
6274 ASSERT(m_frame->settings().visualViewportEnabled())((void)0);
6275 return p.scaled(m_frame->pageZoomFactor());
6276}
6277
6278void LocalFrameView::setTracksRepaints(bool trackRepaints)
6279{
6280 if (trackRepaints == m_isTrackingRepaints)
6281 return;
6282
6283 // Force layout to flush out any pending repaints.
6284 if (trackRepaints) {
6285 if (RefPtr document = m_frame->document())
6286 document->updateLayout(LayoutOptions::UpdateCompositingLayers);
6287 }
6288
6289 for (RefPtr frame = &m_frame->tree().top(); frame; frame = frame->tree().traverseNext()) {
6290 RefPtr localFrame = dynamicDowncast<LocalFrame>(frame);
6291 if (!localFrame)
6292 continue;
6293 if (CheckedPtr renderView = localFrame->contentRenderer())
6294 renderView->compositor().setTracksRepaints(trackRepaints);
6295 }
6296
6297 resetTrackedRepaints();
6298 m_isTrackingRepaints = trackRepaints;
6299}
6300
6301void LocalFrameView::resetTrackedRepaints()
6302{
6303 m_trackedRepaintRects.clear();
6304 if (CheckedPtr renderView = this->renderView())
6305 renderView->compositor().resetTrackedRepaintRects();
6306}
6307
6308String LocalFrameView::trackedRepaintRectsAsText() const
6309{
6310 auto& frame = this->m_frame.get();
6311 Ref protectedFrame { frame };
6312
6313 if (RefPtr document = frame.document())
6314 document->updateLayout();
6315
6316 TextStream ts;
6317 if (!m_trackedRepaintRects.isEmpty()) {
6318 ts << "(repaint rects\n"_s;
6319 for (auto& rect : m_trackedRepaintRects)
6320 ts << " (rect "_s << LayoutUnit(rect.x()) << ' ' << LayoutUnit(rect.y()) << ' ' << LayoutUnit(rect.width()) << ' ' << LayoutUnit(rect.height()) << ")\n"_s;
6321 ts << ")\n"_s;
6322 }
6323 return ts.release();
6324}
6325
6326void LocalFrameView::startTrackingLayoutUpdates()
6327{
6328 layoutContext().startTrackingLayoutUpdates();
6329}
6330
6331unsigned LocalFrameView::layoutUpdateCount()
6332{
6333 return layoutContext().layoutUpdateCount();
6334}
6335
6336void LocalFrameView::startTrackingRenderLayerPositionUpdates()
6337{
6338 layoutContext().startTrackingRenderLayerPositionUpdates();
6339}
6340
6341unsigned LocalFrameView::renderLayerPositionUpdateCount()
6342{
6343 return layoutContext().renderLayerPositionUpdateCount();
6344}
6345
6346void LocalFrameView::addScrollableAreaForAnimatedScroll(ScrollableArea* scrollableArea)
6347{
6348 if (!m_scrollableAreasForAnimatedScroll)
6349 m_scrollableAreasForAnimatedScroll = makeUnique<ScrollableAreaSet>();
6350
6351 m_scrollableAreasForAnimatedScroll->add(*scrollableArea);
6352}
6353
6354void LocalFrameView::removeScrollableAreaForAnimatedScroll(ScrollableArea* scrollableArea)
6355{
6356 if (m_scrollableAreasForAnimatedScroll)
6357 m_scrollableAreasForAnimatedScroll->remove(*scrollableArea);
6358}
6359
6360bool LocalFrameView::addScrollableArea(ScrollableArea* scrollableArea)
6361{
6362 if (!m_scrollableAreas)
6363 m_scrollableAreas = makeUnique<ScrollableAreaSet>();
6364
6365 if (m_scrollableAreas->add(*scrollableArea).isNewEntry) {
6366 scrollableAreaSetChanged();
6367 return true;
6368 }
6369
6370 return false;
6371}
6372
6373bool LocalFrameView::removeScrollableArea(ScrollableArea* scrollableArea)
6374{
6375 if (m_scrollableAreas && m_scrollableAreas->remove(*scrollableArea)) {
6376 scrollableAreaSetChanged();
6377 return true;
6378 }
6379 return false;
6380}
6381
6382bool LocalFrameView::containsScrollableArea(ScrollableArea* scrollableArea) const
6383{
6384 return m_scrollableAreas && m_scrollableAreas->contains(*scrollableArea);
6385}
6386
6387void LocalFrameView::scrollableAreaSetChanged()
6388{
6389 if (RefPtr scrollingCoordinator = this->scrollingCoordinator())
6390 scrollingCoordinator->frameViewEventTrackingRegionsChanged(*this);
6391}
6392
6393void LocalFrameView::scrollDidEnd()
6394{
6395 if (!isAwaitingScrollend())
6396 return;
6397 setIsAwaitingScrollend(false);
6398 if (!m_frame->view())
6399 return;
6400 if (RefPtr document = m_frame->document())
6401 document->addPendingScrollEventTarget(*document, ScrollEventType::Scrollend);
6402}
6403
6404void LocalFrameView::scheduleScrollEvent()
6405{
6406 setIsAwaitingScrollend(true);
6407 m_frame->eventHandler().scheduleScrollEvent();
6408 m_frame->eventHandler().dispatchFakeMouseMoveEventSoon();
6409}
6410
6411void LocalFrameView::addChild(Widget& widget)
6412{
6413 if (auto* childFrameView = dynamicDowncast<LocalFrameView>(widget)) {
6414 if (childFrameView->isScrollable())
6415 addScrollableArea(childFrameView);
6416 }
6417
6418 ScrollView::addChild(widget);
6419}
6420
6421void LocalFrameView::removeChild(Widget& widget)
6422{
6423 if (auto* childFrameView = dynamicDowncast<LocalFrameView>(widget))
6424 removeScrollableArea(childFrameView);
6425
6426 ScrollView::removeChild(widget);
6427}
6428
6429bool LocalFrameView::handleWheelEventForScrolling(const PlatformWheelEvent& wheelEvent, std::optional<WheelScrollGestureState> gestureState)
6430{
6431 // Note that to allow for rubber-band over-scroll behavior, even non-scrollable views
6432 // should handle wheel events.
6433#if !HAVE(RUBBER_BANDING)(defined HAVE_RUBBER_BANDING && HAVE_RUBBER_BANDING)
6434 if (!isScrollable())
6435 return false;
6436#endif
6437
6438 if (delegatesScrollingToNativeView()) {
6439 ScrollPosition oldPosition = scrollPosition();
6440 ScrollPosition newPosition = oldPosition - IntSize(wheelEvent.deltaX(), wheelEvent.deltaY());
6441 if (oldPosition != newPosition) {
6442 ScrollView::scrollTo(newPosition);
6443 scrollPositionChanged(oldPosition, scrollPosition());
6444 didChangeScrollOffset();
6445 }
6446 return true;
6447 }
6448
6449 // We don't allow mouse wheeling to happen in a ScrollView that has had its scrollbars explicitly disabled.
6450 if (!canHaveScrollbars())
6451 return false;
6452
6453 if (platformWidget())
6454 return false;
6455
6456#if ENABLE(ASYNC_SCROLLING)(defined 1 && 1)
6457 if (RefPtr scrollingCoordinator = this->scrollingCoordinator()) {
6458 auto scrollingNodeID = this->scrollingNodeID();
6459 if (scrollingNodeID && scrollingCoordinator->coordinatesScrollingForFrameView(*this)) {
6460 auto result = scrollingCoordinator->handleWheelEventForScrolling(wheelEvent, *scrollingNodeID, gestureState);
6461 if (!result.needsMainThreadProcessing())
6462 return result.wasHandled;
6463 }
6464 }
6465#endif
6466
6467 return ScrollableArea::handleWheelEventForScrolling(wheelEvent, gestureState);
6468}
6469
6470bool LocalFrameView::isVerticalDocument() const
6471{
6472 CheckedPtr renderView = this->renderView();
6473 if (!renderView)
6474 return true;
6475
6476 return renderView->writingMode().isHorizontal();
6477}
6478
6479bool LocalFrameView::isFlippedDocument() const
6480{
6481 CheckedPtr renderView = this->renderView();
6482 if (!renderView)
6483 return false;
6484
6485 return renderView->writingMode().isBlockFlipped();
6486}
6487
6488void LocalFrameView::notifyWidgetsInAllFrames(WidgetNotification notification)
6489{
6490 for (RefPtr<Frame> frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr())) {
6491 RefPtr localFrame = dynamicDowncast<LocalFrame>(frame);
6492 if (!localFrame)
6493 continue;
6494 if (RefPtr view = localFrame->view())
6495 view->notifyWidgets(notification);
6496 }
6497}
6498
6499AXObjectCache* LocalFrameView::axObjectCache() const
6500{
6501 SUPPRESS_UNCHECKED_LOCAL[[clang::suppress]] AXObjectCache* cache = nullptr;
6502 if (m_frame->document())
6503 cache = m_frame->document()->existingAXObjectCache();
6504
6505 // FIXME: We should generally always be using the main-frame cache rather than
6506 // using it as a fallback as we do here.
6507 if (!cache && !m_frame->isMainFrame()) {
6508 RefPtr localMainFrame = dynamicDowncast<LocalFrame>(m_frame->mainFrame());
6509 if (localMainFrame) {
6510 if (RefPtr mainFrameDocument = localMainFrame->document())
6511 cache = mainFrameDocument->existingAXObjectCache();
6512 }
6513 }
6514 return cache;
6515}
6516
6517#if PLATFORM(IOS_FAMILY)(defined 1 && 1)
6518bool LocalFrameView::useCustomFixedPositionLayoutRect() const
6519{
6520 return !m_frame->settings().visualViewportEnabled() && m_useCustomFixedPositionLayoutRect;
6521}
6522
6523void LocalFrameView::setCustomFixedPositionLayoutRect(const IntRect& rect)
6524{
6525 if (m_useCustomFixedPositionLayoutRect && m_customFixedPositionLayoutRect == rect)
6526 return;
6527 m_useCustomFixedPositionLayoutRect = true;
6528 m_customFixedPositionLayoutRect = rect;
6529 updateContentsSize();
6530}
6531
6532bool LocalFrameView::updateFixedPositionLayoutRect()
6533{
6534 if (!m_useCustomFixedPositionLayoutRect)
6535 return false;
6536
6537 IntRect newRect;
6538 Page* page = m_frame->page();
6539 if (!page || !page->chrome().client().fetchCustomFixedPositionLayoutRect(newRect))
6540 return false;
6541
6542 if (newRect != m_customFixedPositionLayoutRect) {
6543 m_customFixedPositionLayoutRect = newRect;
6544 setViewportConstrainedObjectsNeedLayout();
6545 return true;
6546 }
6547 return false;
6548}
6549
6550void LocalFrameView::setCustomSizeForResizeEvent(IntSize customSize)
6551{
6552 m_customSizeForResizeEvent = customSize;
6553 scheduleResizeEventIfNeeded();
6554}
6555
6556void LocalFrameView::setScrollVelocity(const VelocityData& velocityData)
6557{
6558 if (TiledBacking* tiledBacking = this->tiledBacking())
6559 tiledBacking->setVelocity(velocityData);
6560}
6561#endif // PLATFORM(IOS_FAMILY)
6562
6563void LocalFrameView::setScrollingPerformanceTestingEnabled(bool scrollingPerformanceTestingEnabled)
6564{
6565 if (scrollingPerformanceTestingEnabled) {
6566 RefPtr page = m_frame->page();
6567 if (page && page->performanceLoggingClient())
6568 page->performanceLoggingClient()->logScrollingEvent(PerformanceLoggingClient::ScrollingEvent::LoggingEnabled, MonotonicTime::now(), 0);
6569 }
6570
6571 if (TiledBacking* tiledBacking = this->tiledBacking())
6572 tiledBacking->setScrollingPerformanceTestingEnabled(scrollingPerformanceTestingEnabled);
6573}
6574
6575void LocalFrameView::createScrollbarsController()
6576{
6577 RefPtr page = m_frame->page();
6578 if (!page) {
6579 ScrollView::createScrollbarsController();
6580 return;
6581 }
6582
6583 page->chrome().client().ensureScrollbarsController(*page, *this);
6584}
6585
6586void LocalFrameView::didAddScrollbar(Scrollbar* scrollbar, ScrollbarOrientation orientation)
6587{
6588 RefPtr page = m_frame->page();
6589
6590 ScrollableArea::didAddScrollbar(scrollbar, orientation);
6591
6592 if (page && page->isMonitoringWheelEvents())
6593 scrollAnimator().setWheelEventTestMonitor(page->wheelEventTestMonitor());
6594 if (AXObjectCache* cache = axObjectCache())
6595 cache->onScrollbarUpdate(*this);
6596}
6597
6598void LocalFrameView::willRemoveScrollbar(Scrollbar& scrollbar, ScrollbarOrientation orientation)
6599{
6600 ScrollableArea::willRemoveScrollbar(scrollbar, orientation);
6601 if (AXObjectCache* cache = axObjectCache()) {
6602 cache->remove(scrollbar);
6603 cache->onScrollbarUpdate(*this);
6604 }
6605}
6606
6607void LocalFrameView::scrollbarFrameRectChanged(const Scrollbar& scrollbar) const
6608{
6609 if (auto* cache = axObjectCache())
6610 cache->onScrollbarFrameRectChange(scrollbar);
6611}
6612
6613void LocalFrameView::addPaintPendingMilestones(OptionSet<LayoutMilestone> milestones)
6614{
6615 m_milestonesPendingPaint.add(milestones);
6616}
6617
6618void LocalFrameView::fireLayoutRelatedMilestonesIfNeeded()
6619{
6620 if (m_frame->loader().client().shouldSuppressLayoutMilestones())
6621 return;
6622
6623 OptionSet<LayoutMilestone> requestedMilestones;
6624 OptionSet<LayoutMilestone> milestonesAchieved;
6625 RefPtr page = m_frame->page();
6626 if (page)
6627 requestedMilestones = page->requestedLayoutMilestones();
6628
6629 if (m_firstLayoutCallbackPending) {
6630 m_firstLayoutCallbackPending = false;
6631 m_frame->loader().didFirstLayout();
6632 if (requestedMilestones & LayoutMilestone::DidFirstLayout)
6633 milestonesAchieved.add(LayoutMilestone::DidFirstLayout);
6634 if (m_frame->isMainFrame())
6635 page->startCountingRelevantRepaintedObjects();
6636 }
6637
6638 if (m_firstVisuallyNonEmptyLayoutMilestoneIsPending) {
6639 checkAndDispatchDidReachVisuallyNonEmptyState();
6640 if (m_contentQualifiesAsVisuallyNonEmpty) {
6641 m_firstVisuallyNonEmptyLayoutMilestoneIsPending = false;
6642
6643 addPaintPendingMilestones(LayoutMilestone::DidFirstMeaningfulPaint);
6644 if (requestedMilestones & LayoutMilestone::DidFirstVisuallyNonEmptyLayout)
6645 milestonesAchieved.add(LayoutMilestone::DidFirstVisuallyNonEmptyLayout);
6646 }
6647 }
6648
6649 if (!m_renderedSignificantAmountOfText && qualifiesAsSignificantRenderedText()) {
6650 m_renderedSignificantAmountOfText = true;
6651 if (requestedMilestones & LayoutMilestone::DidRenderSignificantAmountOfText)
6652 milestonesAchieved.add(LayoutMilestone::DidRenderSignificantAmountOfText);
6653 }
6654
6655 if (milestonesAchieved && m_frame->isMainFrame()) {
6656 if (milestonesAchieved.contains(LayoutMilestone::DidFirstVisuallyNonEmptyLayout))
6657 FRAMEVIEW_RELEASE_LOG(Layout, LOCALFRAMEVIEW_FIRING_FIRST_VISUALLY_NON_EMPTY_LAYOUT_MILESTONE)do { if (auto& client = logClient()) client->LOCALFRAMEVIEW_FIRING_FIRST_VISUALLY_NON_EMPTY_LAYOUT_MILESTONE
(PAGE_ID, FRAME_ID, m_frame->isMainFrame()); else 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 = (LogLayout.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("[pageID=%" "ll" "u" ", frameID=%"
"ll" "u" ", isMainFrame=%d] LocalFrameView::fireLayoutRelatedMilestonesIfNeeded: Firing first visually non-empty layout milestone on the main frame"
), "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_LOG51"
) = "[pageID=%" "ll" "u" ", frameID=%" "ll" "u" ", isMainFrame=%d] LocalFrameView::fireLayoutRelatedMilestonesIfNeeded: Firing first visually non-empty layout milestone on the main frame"
; uint8_t _Alignas(16) __attribute__((uninitialized)) _os_fmt_buf
[__builtin_os_log_format_buffer_size("[pageID=%" "ll" "u" ", frameID=%"
"ll" "u" ", isMainFrame=%d] LocalFrameView::fireLayoutRelatedMilestonesIfNeeded: Firing first visually non-empty layout milestone on the main frame"
, WTF::safePrintfType(PAGE_ID) , WTF::safePrintfType(FRAME_ID
) , WTF::safePrintfType(m_frame->isMainFrame()))]; _os_log_impl
(&__dso_handle, _log_tmp, _type_tmp, _os_fmt_str, (uint8_t
*)__builtin_os_log_format(_os_fmt_buf, "[pageID=%" "ll" "u" ", frameID=%"
"ll" "u" ", isMainFrame=%d] LocalFrameView::fireLayoutRelatedMilestonesIfNeeded: Firing first visually non-empty layout milestone on the main frame"
, WTF::safePrintfType(PAGE_ID) , WTF::safePrintfType(FRAME_ID
) , WTF::safePrintfType(m_frame->isMainFrame())), (uint32_t
)sizeof(_os_fmt_buf)) clang diagnostic pop ; }); } }) clang diagnostic
pop ; } while (0)
;
6658 m_frame->loader().didReachLayoutMilestone(milestonesAchieved);
6659 }
6660}
6661
6662void LocalFrameView::firePaintRelatedMilestonesIfNeeded()
6663{
6664 RefPtr page = m_frame->page();
6665 if (!page)
6666 return;
6667
6668 OptionSet<LayoutMilestone> milestonesAchieved;
6669
6670 if (m_milestonesPendingPaint & LayoutMilestone::DidFirstPaintAfterSuppressedIncrementalRendering) {
6671 if (page->requestedLayoutMilestones() & LayoutMilestone::DidFirstPaintAfterSuppressedIncrementalRendering)
6672 milestonesAchieved.add(LayoutMilestone::DidFirstPaintAfterSuppressedIncrementalRendering);
6673 }
6674
6675 if (m_milestonesPendingPaint & LayoutMilestone::DidFirstMeaningfulPaint) {
6676 if (page->requestedLayoutMilestones() & LayoutMilestone::DidFirstMeaningfulPaint)
6677 milestonesAchieved.add(LayoutMilestone::DidFirstMeaningfulPaint);
6678 }
6679
6680 m_milestonesPendingPaint = { };
6681
6682 RefPtr localMainFrame = page->localMainFrame();
6683 if (milestonesAchieved && localMainFrame)
6684 localMainFrame->loader().didReachLayoutMilestone(milestonesAchieved);
6685}
6686
6687void LocalFrameView::setVisualUpdatesAllowedByClient(bool visualUpdatesAllowed)
6688{
6689 if (m_visualUpdatesAllowedByClient == visualUpdatesAllowed)
6690 return;
6691
6692 m_visualUpdatesAllowedByClient = visualUpdatesAllowed;
6693
6694 m_frame->document()->setVisualUpdatesAllowedByClient(visualUpdatesAllowed);
6695}
6696
6697void LocalFrameView::setScrollPinningBehavior(ScrollPinningBehavior pinning)
6698{
6699 m_scrollPinningBehavior = pinning;
6700
6701 if (RefPtr scrollingCoordinator = this->scrollingCoordinator())
6702 scrollingCoordinator->setScrollPinningBehavior(pinning);
6703
6704 updateScrollbars(scrollPosition());
6705}
6706
6707ScrollBehaviorForFixedElements LocalFrameView::scrollBehaviorForFixedElements() const
6708{
6709 return m_frame->settings().backgroundShouldExtendBeyondPage() ? ScrollBehaviorForFixedElements::StickToViewportBounds : ScrollBehaviorForFixedElements::StickToDocumentBounds;
6710}
6711
6712LocalFrame& LocalFrameView::frame() const
6713{
6714 return m_frame;
6715}
6716
6717RenderView* LocalFrameView::renderView() const
6718{
6719 return m_frame->contentRenderer();
6720}
6721
6722int LocalFrameView::mapFromLayoutToCSSUnits(LayoutUnit value) const
6723{
6724 Ref frame = m_frame;
6725 return value / (frame->pageZoomFactor() * frame->frameScaleFactor());
6726}
6727
6728LayoutUnit LocalFrameView::mapFromCSSToLayoutUnits(int value) const
6729{
6730 Ref frame = m_frame;
6731 return LayoutUnit(value * frame->pageZoomFactor() * frame->frameScaleFactor());
6732}
6733
6734void LocalFrameView::didAddWidgetToRenderTree(Widget& widget)
6735{
6736 ASSERT(!m_widgetsInRenderTree.contains(widget))((void)0);
6737 m_widgetsInRenderTree.add(widget);
6738}
6739
6740void LocalFrameView::willRemoveWidgetFromRenderTree(Widget& widget)
6741{
6742 ASSERT(m_widgetsInRenderTree.contains(widget))((void)0);
6743 m_widgetsInRenderTree.remove(widget);
6744}
6745
6746static Vector<Ref<Widget>> collectAndProtectWidgets(const HashSet<SingleThreadWeakRef<Widget>>& set)
6747{
6748 return WTF::map(set, [](auto& widget) -> Ref<Widget> {
6749 return widget.get();
6750 });
6751}
6752
6753void LocalFrameView::updateWidgetPositions()
6754{
6755 m_updateWidgetPositionsTimer.stop();
6756 // updateWidgetPosition() can possibly cause layout to be re-entered (via plug-ins running
6757 // scripts in response to NPP_SetWindow, for example), so we need to keep the Widgets
6758 // alive during enumeration.
6759 for (auto& widget : collectAndProtectWidgets(m_widgetsInRenderTree)) {
6760 if (RefPtr renderer = RenderWidget::find(widget)) {
6761 auto ignoreWidgetState = renderer->updateWidgetPosition();
6762 UNUSED_PARAM(ignoreWidgetState)(void)ignoreWidgetState;
6763 }
6764 }
6765}
6766
6767void LocalFrameView::scheduleUpdateWidgetPositions()
6768{
6769 if (!m_updateWidgetPositionsTimer.isActive())
6770 m_updateWidgetPositionsTimer.startOneShot(0_s);
6771}
6772
6773void LocalFrameView::updateWidgetPositionsTimerFired()
6774{
6775 updateWidgetPositions();
6776}
6777
6778void LocalFrameView::notifyWidgets(WidgetNotification notification)
6779{
6780 for (auto& widget : collectAndProtectWidgets(m_widgetsInRenderTree))
6781 widget->notifyWidget(notification);
6782}
6783
6784void LocalFrameView::setViewExposedRect(std::optional<FloatRect> viewExposedRect)
6785{
6786 if (m_viewExposedRect == viewExposedRect)
6787 return;
6788
6789 LOG_WITH_STREAM(Scrolling, stream << "LocalFrameView " << this << " setViewExposedRect " << (viewExposedRect ? viewExposedRect.value() : FloatRect()))((void)0);
6790
6791 bool hasRectExistenceChanged = !m_viewExposedRect == !viewExposedRect;
6792 m_viewExposedRect = viewExposedRect;
6793
6794 // FIXME: We should support clipping to the exposed rect for subframes as well.
6795 if (!m_frame->isMainFrame())
6796 return;
6797
6798 if (TiledBacking* tiledBacking = this->tiledBacking()) {
6799 if (hasRectExistenceChanged)
6800 updateTiledBackingAdaptiveSizing();
6801 adjustTiledBackingCoverage();
6802 tiledBacking->setTiledScrollingIndicatorPosition(m_viewExposedRect ? m_viewExposedRect.value().location() : FloatPoint());
6803 }
6804
6805 if (RefPtr page = m_frame->page()) {
6806 page->scheduleRenderingUpdate(RenderingUpdateStep::LayerFlush);
6807 page->pageOverlayController().didChangeViewExposedRect();
6808 }
6809}
6810
6811void LocalFrameView::clearSizeOverrideForCSSDefaultViewportUnits()
6812{
6813 if (!m_defaultViewportSizeOverride)
6814 return;
6815
6816 m_defaultViewportSizeOverride = std::nullopt;
6817 if (RefPtr document = m_frame->document())
6818 document->styleScope().didChangeStyleSheetEnvironment();
6819}
6820
6821void LocalFrameView::setSizeForCSSDefaultViewportUnits(FloatSize size)
6822{
6823 setOverrideSizeForCSSDefaultViewportUnits({ size.width(), size.height() });
6824}
6825
6826void LocalFrameView::overrideWidthForCSSDefaultViewportUnits(float width)
6827{
6828 setOverrideSizeForCSSDefaultViewportUnits({ width, m_defaultViewportSizeOverride ? m_defaultViewportSizeOverride->height : std::nullopt });
6829}
6830
6831void LocalFrameView::resetOverriddenWidthForCSSDefaultViewportUnits()
6832{
6833 setOverrideSizeForCSSDefaultViewportUnits({ { }, m_defaultViewportSizeOverride ? m_defaultViewportSizeOverride->height : std::nullopt });
6834}
6835
6836void LocalFrameView::setOverrideSizeForCSSDefaultViewportUnits(OverrideViewportSize size)
6837{
6838 if (m_defaultViewportSizeOverride == size)
6839 return;
6840
6841 m_defaultViewportSizeOverride = size;
6842
6843 if (RefPtr document = m_frame->document())
6844 document->styleScope().didChangeStyleSheetEnvironment();
6845}
6846
6847FloatSize LocalFrameView::sizeForCSSDefaultViewportUnits() const
6848{
6849 return calculateSizeForCSSViewportUnitsOverride(m_defaultViewportSizeOverride);
6850}
6851
6852void LocalFrameView::clearSizeOverrideForCSSSmallViewportUnits()
6853{
6854 if (!m_smallViewportSizeOverride)
6855 return;
6856
6857 m_smallViewportSizeOverride = std::nullopt;
6858 if (RefPtr document = m_frame->document())
6859 document->updateViewportUnitsOnResize();
6860}
6861
6862void LocalFrameView::setSizeForCSSSmallViewportUnits(FloatSize size)
6863{
6864 setOverrideSizeForCSSSmallViewportUnits({ size.width(), size.height() });
6865}
6866
6867void LocalFrameView::overrideWidthForCSSSmallViewportUnits(float width)
6868{
6869 setOverrideSizeForCSSSmallViewportUnits({ width, m_smallViewportSizeOverride ? m_smallViewportSizeOverride->height : std::nullopt });
6870}
6871
6872void LocalFrameView::resetOverriddenWidthForCSSSmallViewportUnits()
6873{
6874 setOverrideSizeForCSSSmallViewportUnits({ { }, m_smallViewportSizeOverride ? m_smallViewportSizeOverride->height : std::nullopt });
6875}
6876
6877void LocalFrameView::setOverrideSizeForCSSSmallViewportUnits(OverrideViewportSize size)
6878{
6879 if (m_smallViewportSizeOverride && *m_smallViewportSizeOverride == size)
6880 return;
6881
6882 m_smallViewportSizeOverride = size;
6883
6884 if (RefPtr document = m_frame->document())
6885 document->updateViewportUnitsOnResize();
6886}
6887
6888FloatSize LocalFrameView::sizeForCSSSmallViewportUnits() const
6889{
6890 return calculateSizeForCSSViewportUnitsOverride(m_smallViewportSizeOverride);
6891}
6892
6893void LocalFrameView::clearSizeOverrideForCSSLargeViewportUnits()
6894{
6895 if (!m_largeViewportSizeOverride)
6896 return;
6897
6898 m_largeViewportSizeOverride = std::nullopt;
6899 if (RefPtr document = m_frame->document())
6900 document->updateViewportUnitsOnResize();
6901}
6902
6903void LocalFrameView::setSizeForCSSLargeViewportUnits(FloatSize size)
6904{
6905 setOverrideSizeForCSSLargeViewportUnits({ size.width(), size.height() });
6906}
6907
6908void LocalFrameView::overrideWidthForCSSLargeViewportUnits(float width)
6909{
6910 setOverrideSizeForCSSLargeViewportUnits({ width, m_largeViewportSizeOverride ? m_largeViewportSizeOverride->height : std::nullopt });
6911}
6912
6913void LocalFrameView::resetOverriddenWidthForCSSLargeViewportUnits()
6914{
6915 setOverrideSizeForCSSLargeViewportUnits({ { }, m_largeViewportSizeOverride ? m_largeViewportSizeOverride->height : std::nullopt });
6916}
6917
6918void LocalFrameView::setOverrideSizeForCSSLargeViewportUnits(OverrideViewportSize size)
6919{
6920 if (m_largeViewportSizeOverride && *m_largeViewportSizeOverride == size)
6921 return;
6922
6923 m_largeViewportSizeOverride = size;
6924
6925 if (RefPtr document = m_frame->document())
6926 document->updateViewportUnitsOnResize();
6927}
6928
6929FloatSize LocalFrameView::sizeForCSSLargeViewportUnits() const
6930{
6931 return calculateSizeForCSSViewportUnitsOverride(m_largeViewportSizeOverride);
6932}
6933
6934FloatSize LocalFrameView::calculateSizeForCSSViewportUnitsOverride(std::optional<OverrideViewportSize> override) const
6935{
6936 OverrideViewportSize viewportSize;
6937
6938 if (override) {
6939 viewportSize = *override;
6940
6941 // auto-size overrides the width only, so we can't always bail out early here.
6942 if (viewportSize.width && viewportSize.height)
6943 return { *viewportSize.width, *viewportSize.height };
6944 }
6945
6946 if (useFixedLayout()) {
6947 auto fixedLayoutSize = this->fixedLayoutSize();
6948 viewportSize.width = viewportSize.width.value_or(fixedLayoutSize.width());
6949 viewportSize.height = viewportSize.height.value_or(fixedLayoutSize.height());
6950 return { *viewportSize.width, *viewportSize.height };
6951 }
6952
6953 // FIXME: the value returned should take into account the value of the overflow
6954 // property on the root element.
6955 auto visibleContentSizeIncludingScrollbars = visibleContentRectIncludingScrollbars().size();
6956 viewportSize.width = viewportSize.width.value_or(visibleContentSizeIncludingScrollbars.width());
6957 viewportSize.height = viewportSize.height.value_or(visibleContentSizeIncludingScrollbars.height());
6958 return { *viewportSize.width, *viewportSize.height };
6959}
6960
6961FloatSize LocalFrameView::sizeForCSSDynamicViewportUnits() const
6962{
6963 if (m_layoutViewportOverrideRect)
6964 return m_layoutViewportOverrideRect->size();
6965
6966 if (useFixedLayout())
6967 return fixedLayoutSize();
6968
6969 if (m_frame->settings().visualViewportEnabled())
6970 return unobscuredContentRectIncludingScrollbars().size();
6971
6972 return viewportConstrainedVisibleContentRect().size();
6973}
6974
6975bool LocalFrameView::shouldPlaceVerticalScrollbarOnLeft() const
6976{
6977 return renderView() && renderView()->shouldPlaceVerticalScrollbarOnLeft();
6978}
6979
6980bool LocalFrameView::isHorizontalWritingMode() const
6981{
6982 return renderView() && renderView()->writingMode().isHorizontal();
6983}
6984
6985TextStream& operator<<(TextStream& ts, const LocalFrameView& view)
6986{
6987 ts << view.debugDescription();
6988 return ts;
6989}
6990
6991void LocalFrameView::didFinishProhibitingScrollingWhenChangingContentSize()
6992{
6993 if (RefPtr document = m_frame->document()) {
6994 if (!document->needsStyleRecalc() && !needsLayout() && !layoutContext().isInLayout())
6995 updateScrollbars(scrollPosition());
6996 else
6997 m_needsDeferredScrollbarsUpdate = true;
6998 }
6999}
7000
7001float LocalFrameView::pageScaleFactor() const
7002{
7003 return m_frame->frameScaleFactor();
7004}
7005
7006void LocalFrameView::didStartScrollAnimation()
7007{
7008 if (RefPtr page = m_frame->page())
7009 page->scheduleRenderingUpdate({ RenderingUpdateStep::Scroll });
7010}
7011
7012void LocalFrameView::updateScrollbarSteps()
7013{
7014 RefPtr documentElement = m_frame->document() ? m_frame->document()->documentElement() : nullptr;
7015 CheckedPtr renderer = documentElement ? documentElement->renderBox() : nullptr;
7016 if (!renderer) {
7017 ScrollView::updateScrollbarSteps();
7018 return;
7019 }
7020
7021 LayoutRect paddedViewRect(LayoutPoint(), visibleSize());
7022 paddedViewRect.contract(renderer->scrollPaddingForViewportRect(paddedViewRect));
7023
7024 if (horizontalScrollbar()) {
7025 int pageStep = Scrollbar::pageStep(paddedViewRect.width());
7026 horizontalScrollbar()->setSteps(Scrollbar::pixelsPerLineStep(paddedViewRect.width()), pageStep);
7027
7028 }
7029 if (verticalScrollbar()) {
7030 int pageStep = Scrollbar::pageStep(paddedViewRect.height());
7031 verticalScrollbar()->setSteps(Scrollbar::pixelsPerLineStep(paddedViewRect.height()), pageStep);
7032 }
7033}
7034
7035OverscrollBehavior LocalFrameView::horizontalOverscrollBehavior() const
7036{
7037 auto* document = m_frame->document();
7038 CheckedPtr scrollingObject = document && document->documentElement() ? document->documentElement()->renderer() : nullptr;
7039 if (scrollingObject && renderView())
7040 return scrollingObject->style().overscrollBehaviorX();
7041 return OverscrollBehavior::Auto;
7042}
7043
7044OverscrollBehavior LocalFrameView::verticalOverscrollBehavior() const
7045{
7046 auto* document = m_frame->document();
7047 CheckedPtr scrollingObject = document && document->documentElement() ? document->documentElement()->renderer() : nullptr;
7048 if (scrollingObject && renderView())
7049 return scrollingObject->style().overscrollBehaviorY();
7050 return OverscrollBehavior::Auto;
7051}
7052
7053Color LocalFrameView::scrollbarThumbColorStyle() const
7054{
7055 RefPtr document = m_frame->document();
7056 CheckedPtr scrollingObject = document && document->documentElement() ? document->documentElement()->renderer() : nullptr;
7057 if (scrollingObject)
7058 return scrollingObject->style().usedScrollbarThumbColor();
7059 return { };
7060}
7061
7062Color LocalFrameView::scrollbarTrackColorStyle() const
7063{
7064 RefPtr document = m_frame->document();
7065 CheckedPtr scrollingObject = document && document->documentElement() ? document->documentElement()->renderer() : nullptr;
7066 if (scrollingObject)
7067 return scrollingObject->style().usedScrollbarTrackColor();
7068 return { };
7069}
7070
7071Style::ScrollbarGutter LocalFrameView::scrollbarGutterStyle() const
7072{
7073 auto* document = m_frame->document();
7074 CheckedPtr scrollingObject = document && document->documentElement() ? document->documentElement()->renderer() : nullptr;
7075 if (scrollingObject)
7076 return scrollingObject->style().scrollbarGutter();
7077 return CSS::Keyword::Auto { };
7078}
7079
7080ScrollbarWidth LocalFrameView::scrollbarWidthStyle() const
7081{
7082 auto* document = m_frame->document();
7083 CheckedPtr scrollingObject = document && document->documentElement() ? document->documentElement()->renderer() : nullptr;
7084 if (scrollingObject && renderView())
7085 return Style::toPlatform(scrollingObject->style().scrollbarWidth());
7086 return ScrollbarWidth::Auto;
7087}
7088
7089std::optional<ScrollbarColor> LocalFrameView::scrollbarColorStyle() const
7090{
7091 RefPtr document = m_frame->document();
7092 CheckedPtr scrollingObject = document && document->documentElement() ? document->documentElement()->renderer() : nullptr;
7093 if (scrollingObject && renderView()) {
7094 if (auto value = scrollingObject->style().scrollbarColor().tryValue()) {
7095 Style::ColorResolver colorResolver { scrollingObject->style() };
7096 return ScrollbarColor {
7097 .thumbColor = colorResolver.colorResolvingCurrentColor(value->thumb),
7098 .trackColor = colorResolver.colorResolvingCurrentColor(value->track)
7099 };
7100 }
7101 }
7102 return { };
7103}
7104
7105bool LocalFrameView::isVisibleToHitTesting() const
7106{
7107 bool isVisibleToHitTest = true;
7108 if (RefPtr owner = m_frame->ownerElement())
7109 isVisibleToHitTest = owner->renderer() && owner->renderer()->visibleToHitTesting();
7110 return isVisibleToHitTest;
7111}
7112
7113LayoutRect LocalFrameView::getPossiblyFixedRectToExpose(const LayoutRect& visibleRect, const LayoutRect& exposeRect, EnumSet<BoxAxis> isFixed, const ScrollAlignment& alignX, const ScrollAlignment& alignY) const
7114{
7115 if (!isFixed.containsAny({ BoxAxis::Horizontal, BoxAxis::Vertical }))
7116 return getPossiblyFixedRectToExpose(visibleRect, exposeRect, false, alignX, alignY);
7117 if (isFixed.containsAll({ BoxAxis::Horizontal, BoxAxis::Vertical }))
7118 return getPossiblyFixedRectToExpose(visibleRect, exposeRect, true, alignX, alignY);
7119
7120 auto fixedRect = getPossiblyFixedRectToExpose(visibleRect, exposeRect, true, alignX, alignY);
7121 auto scrollRect = getPossiblyFixedRectToExpose(visibleRect, exposeRect, false, alignX, alignY);
7122 if (isFixed.contains(BoxAxis::Horizontal))
7123 return { fixedRect.x(), scrollRect.y(), fixedRect.width(), scrollRect.height() };
7124 return { scrollRect.x(), fixedRect.y(), scrollRect.width(), fixedRect.height() };
7125}
7126
7127LayoutRect LocalFrameView::getPossiblyFixedRectToExpose(const LayoutRect& visibleRect, const LayoutRect& exposeRect, bool isFixed, const ScrollAlignment& alignX, const ScrollAlignment& alignY) const
7128{
7129 if (!isFixed)
7130 return getRectToExposeForScrollIntoView(visibleRect, exposeRect, alignX, alignY);
7131
7132 // If the element is inside position:fixed and we're not scaled, no amount of scrolling is going to move things around.
7133 if (frameScaleFactor() == 1)
7134 return visibleRect;
7135
7136 // FIXME: Shouldn't this return visibleRect as well?
7137 if (!m_frame->settings().visualViewportEnabled())
7138 return getRectToExposeForScrollIntoView(visibleRect, exposeRect, alignX, alignY);
7139
7140 // exposeRect is in absolute coords, affected by page scale. Unscale it.
7141 auto unscaledExposeRect = exposeRect;
7142 unscaledExposeRect.scale(1 / frameScaleFactor());
7143 unscaledExposeRect.move(0, -headerHeight());
7144
7145 // These are both in unscaled coordinates.
7146 auto layoutViewport = layoutViewportRect();
7147 auto visualViewport = visualViewportRect();
7148
7149 // The rect to expose may be partially offscreen, which we can't do anything about with position:fixed.
7150 unscaledExposeRect.intersect(layoutViewport);
7151
7152 // Make sure it's not larger than the visual viewport; if so, we'll just move to the top left.
7153 unscaledExposeRect.setSize(unscaledExposeRect.size().shrunkTo(visualViewport.size()));
7154
7155 // Compute how much we have to move the visualViewport to reveal the part of the layoutViewport that contains exposeRect.
7156 auto requiredVisualViewport = getRectToExposeForScrollIntoView(visualViewport, unscaledExposeRect, alignX, alignY);
7157
7158 // Scale it back up.
7159 requiredVisualViewport.scale(frameScaleFactor());
7160 requiredVisualViewport.move(0, headerHeight());
7161 return requiredVisualViewport;
7162}
7163
7164float LocalFrameView::deviceScaleFactor() const
7165{
7166 if (auto* page = m_frame->page())
7167 return page->deviceScaleFactor();
7168 return 1;
7169}
7170
7171void LocalFrameView::writeRenderTreeAsText(TextStream& ts, OptionSet<RenderAsTextFlag> behavior)
7172{
7173 externalRepresentationForLocalFrame(ts, frame(), behavior);
7174}
7175
7176void LocalFrameView::scrollbarStyleDidChange()
7177{
7178 if (auto scrollableAreas = this->scrollableAreas()) {
7179 for (CheckedRef scrollableArea : *scrollableAreas)
7180 scrollableArea->scrollbarsController().updateScrollbarStyle();
7181 }
7182 scrollbarsController().updateScrollbarStyle();
7183}
7184
7185std::optional<FrameIdentifier> LocalFrameView::rootFrameID() const
7186{
7187 return m_frame->rootFrame().frameID();
7188}
7189
7190void LocalFrameView::scrollbarWidthChanged(ScrollbarWidth width)
7191{
7192 scrollbarsController().scrollbarWidthChanged(width);
7193 m_needsDeferredScrollbarsUpdate = true;
7194}
7195
7196int LocalFrameView::scrollbarGutterWidth(bool isHorizontalWritingMode) const
7197{
7198 if (verticalScrollbar() && verticalScrollbar()->isOverlayScrollbar())
7199 return 0;
7200
7201 if (!verticalScrollbar() && !(scrollbarGutterStyle().isAuto() || ScrollbarTheme::theme().usesOverlayScrollbars()) && isHorizontalWritingMode)
7202 return ScrollbarTheme::theme().scrollbarThickness(scrollbarWidthStyle());
7203
7204 if (!verticalScrollbar())
7205 return 0;
7206
7207 return verticalScrollbar()->width();
7208}
7209
7210IntSize LocalFrameView::totalScrollbarSpace() const
7211{
7212 IntSize scrollbarGutter = { horizontalScrollbarIntrusion(), verticalScrollbarIntrusion() };
7213
7214 if (isHorizontalWritingMode()) {
7215 if (scrollbarGutterStyle().isStableBothEdges())
7216 scrollbarGutter.setWidth(scrollbarGutterWidth() * 2);
7217 else
7218 scrollbarGutter.setWidth(scrollbarGutterWidth());
7219 }
7220 return scrollbarGutter;
7221}
7222
7223int LocalFrameView::insetForLeftScrollbarSpace() const
7224{
7225 if (scrollbarGutterStyle().isStableBothEdges())
7226 return scrollbarGutterWidth();
7227 if (shouldPlaceVerticalScrollbarOnLeft())
7228 return verticalScrollbar() ? verticalScrollbar()->occupiedWidth() : 0;
7229 return 0;
7230}
7231
7232} // namespace WebCore
7233
7234#undef PAGE_ID
7235#undef FRAME_ID