Bug Summary

File:Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/Source/WebCore/page/LocalFrameView.cpp
Warning:line 3040, column 11
Local variable 'currentOrigin' is uncounted and unsafe

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple arm64-apple-macosx26.2.0 -O2 -Wundef-prefix=TARGET_OS_ -Werror=undef-prefix -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name 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/MacOSX.platform/Developer/SDKs/MacOSX26.2.sdk/SDKSettings.json -fcompatibility-qualified-id-block-type-checking -fvisibility-inlines-hidden-static-local-var -fno-modulemap-allow-subdirectory-search -fdefine-target-os-macros -enable-tlsdesc -target-cpu apple-m1 -target-feature +v8.4a -target-feature +aes -target-feature +altnzcv -target-feature +ccdp -target-feature +ccpp -target-feature +complxnum -target-feature +crc -target-feature +dotprod -target-feature +flagm -target-feature +fp-armv8 -target-feature +fp16fml -target-feature +fptoint -target-feature +fullfp16 -target-feature +jsconv -target-feature +lse -target-feature +neon -target-feature +pauth -target-feature +perfmon -target-feature +predres -target-feature +ras -target-feature +rcpc -target-feature +rdm -target-feature +sb -target-feature +sha2 -target-feature +sha3 -target-feature +specrestrict -target-feature +ssbs -target-abi darwinpcs -dwarf-ext-refs -fmodule-format=obj -debug-info-kind=standalone -dwarf-version=5 -debugger-tuning=lldb -fdebug-compilation-dir=/Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/Source/WebCore -target-linker-version 1230.1 -fcoverage-compilation-dir=/Volumes/Data/worker/macOS-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/macOS-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/MacOSX.platform/Developer/SDKs/MacOSX26.2.sdk -iquote /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release/WebCore.build/WebCore-generated-files.hmap -iquote /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release/WebCore.build/WebCore-project-headers.hmap -isystem /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/Source/WebCore/PAL/ThirdParty/dav1d/include -iframework /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.2.sdk/System/Library/PrivateFrameworks -iframework /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.2.sdk/System/Library/Frameworks -include-pch /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/PrecompiledHeaders/SharedPrecompiledHeaders/2635492409325514188/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/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release/WebCore.build/WebCore-own-target-headers.hmap -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release/WebCore.build/WebCore-all-target-headers.hmap -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/Release/include -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/Source/WebCore/PAL -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.2.sdk/usr/include/libxslt -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.2.sdk/usr/include/libxml2 -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/Release/DerivedSources/WebCore -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/Release/usr/local/include -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/Release/usr/local/include/WebKitAdditions -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.2.sdk/usr/local/include/WebKitAdditions -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/Release/usr/local/include -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitLibraries/SDKs/macosx26.0-additions.sdk/usr/local/include -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.2.sdk/usr/local/include -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/Release/usr/local/include/webrtc -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.2.sdk/usr/local/include/webrtc -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/Release/usr/local/include/webm -I /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.2.sdk/usr/local/include/webm -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/PAL.build/PAL.build/DerivedSources -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/PAL.build/Release/PAL.build/DerivedSources -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/Source/WebCore -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release/WebCore.build/DerivedSources-normal/arm64 -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release/WebCore.build/DerivedSources/arm64 -I /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/WebCore.build/Release/WebCore.build/DerivedSources -F/Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/Release -I/usr/local/include -stdlib=libc++ -internal-isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.2.sdk/usr/include/c++/v1 -internal-isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.2.sdk/usr/local/include -internal-isystem /Users/buildbot/Library/Developer/Toolchains/swift-webkit.xctoolchain/usr/lib/clang/21/include -internal-externc-isystem /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.2.sdk/usr/include -internal-iframework /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.2.sdk/System/Library/Frameworks -internal-iframework /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.2.sdk/System/Library/SubFrameworks -internal-iframework /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX26.2.sdk/Library/Frameworks -Wno-trigraphs -Werror -Wno-missing-field-initializers -Wmissing-prototypes -Wunreachable-code -Wnon-virtual-dtor -Wno-overloaded-virtual -Wno-exit-time-destructors -Wno-missing-braces -Wparentheses -Wswitch -Wunused-function -Wno-unused-label -Wno-unused-parameter -Wunused-variable -Wunused-value -Wempty-body -Wuninitialized -Wno-unknown-pragmas -Wno-shadow -Wno-four-char-constants -Wno-conversion -Wconstant-conversion -Wint-conversion -Wbool-conversion -Wenum-conversion -Wno-float-conversion -Wnon-literal-null-conversion -Wobjc-literal-conversion -Wsign-compare -Wno-shorten-64-to-32 -Wnewline-eof -Wno-c++11-extensions -Wno-implicit-fallthrough -Wdeprecated-declarations -Winvalid-offsetof -Wno-sign-conversion -Winfinite-recursion -Wmove -Wcomma -Wblock-capture-autoreleasing -Wstrict-prototypes -Wrange-loop-analysis -Wno-semicolon-before-method-body -Wall -Wc99-designator -Wconditional-uninitialized -Wextra -Wdeprecated-enum-enum-conversion -Wdeprecated-enum-float-conversion -Wenum-float-conversion -Wfinal-dtor-non-final-class -Wformat=2 -Wmisleading-indentation -Wpointer-to-int-cast -Wreorder-init-list -Wundef -Wunused-but-set-variable -Wvla -Werror=unguarded-availability -Wno-elaborated-enum-base -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/macOS-Safer-CPP-Checks-EWS/build/scan-build-output/StaticAnalyzer/WebCore/WebCore/normal/arm64/UnifiedSource299.plist -x c++ /Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/WebKitBuild/Release/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 WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
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, ...)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_LOG46"
) = 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
RELEASE_LOG_FORWARDABLE(channel, fmt, PAGE_ID, FRAME_ID, m_frame->isMainFrame(), ##__VA_ARGS__)clang diagnostic push clang diagnostic ignored "-Wunknown-warning-option"
clang diagnostic ignored "-Wunsafe-buffer-usage" clang diagnostic
ignored "-Wunsafe-buffer-usage-in-libc-call" clang diagnostic
ignored "-Wunsafe-buffer-usage-in-format-attr-call" [[clang
::suppress]] __extension__({ os_log_t _log_tmp = (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
(##__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
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 1 && 1)
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 WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
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 WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
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 1 && 1)
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 auto* 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 1 && 1)
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 WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
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 auto* 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 auto* 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 1 && 1)
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 WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
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 (auto* 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 (auto* 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 (auto* page = m_frame->page()) {
2087#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
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 WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
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 WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
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
3024static bool checkTextFragmentSecurity(LocalFrame& frame)
3025{
3026 RefPtr openerFrame = frame.opener();
3027 if (!openerFrame)
3028 return true;
3029
3030 RefPtr localOpener = dynamicDowncast<LocalFrame>(openerFrame.get());
3031 if (!localOpener)
3032 return false;
3033
3034 auto* openerDocument = localOpener->document();
3035 auto* currentDocument = frame.document();
3036 if (!openerDocument || !currentDocument)
3037 return true;
3038
3039 auto& openerOrigin = openerDocument->securityOrigin();
3040 auto& currentOrigin = currentDocument->securityOrigin();
Local variable 'currentOrigin' is uncounted and unsafe
3041
3042 // Block if cross-origin popup
3043 if (!openerOrigin.isSameOriginAs(currentOrigin))
3044 return false;
3045
3046 return true;
3047}
3048
3049bool LocalFrameView::scrollToTextFragment(IsRetry isRetry)
3050{
3051 static constexpr auto scrollToTextFragmentRetryInterval = 500_ms;
3052 static constexpr auto scrollToTextFragmentRetryTimeout = 3_s;
3053
3054 ASSERT(m_frame->document())((void)0);
3055 Ref document = *m_frame->document();
3056
3057 if (!document->settings().scrollToTextFragmentEnabled())
3058 return false;
3059
3060 if (!m_frame->isMainFrame())
3061 return false;
3062
3063 // Block text fragments in cross-origin window.open() popups
3064 if (!checkTextFragmentSecurity(m_frame.get()))
3065 return false;
3066
3067 document->fragmentHighlightRegistry().clear();
3068
3069 auto fragmentDirective = document->fragmentDirective();
3070 if (fragmentDirective.isEmpty())
3071 return false;
3072
3073 FragmentDirectiveParser fragmentDirectiveParser(fragmentDirective);
3074 if (!fragmentDirectiveParser.isValid())
3075 return false;
3076
3077 auto parsedTextDirectives = fragmentDirectiveParser.parsedTextDirectives();
3078 auto highlightRanges = FragmentDirectiveRangeFinder::findRangesFromTextDirectives(parsedTextDirectives, document);
3079 if (m_frame->settings().scrollToTextFragmentMarkingEnabled()) {
3080 for (auto range : highlightRanges)
3081 document->fragmentHighlightRegistry().addAnnotationHighlightWithRange(StaticRange::create(range));
3082 }
3083
3084 // If we didn't find the expected range, retry every 500ms, until the first of:
3085 // - we successfully find the range
3086 // - the user scrolls the view
3087 // - the 3s timeout expires
3088 if (highlightRanges.isEmpty()) {
3089 if (isRetry == IsRetry::No) {
3090 m_scrollToTextFragmentRetryTimer.startRepeating(scrollToTextFragmentRetryInterval);
3091 m_scrollToTextFragmentInitialAttemptTime = MonotonicTime::now();
3092 } else if (!m_scrollToTextFragmentInitialAttemptTime || (MonotonicTime::now() - *m_scrollToTextFragmentInitialAttemptTime >= scrollToTextFragmentRetryTimeout) || m_wasEverScrolledExplicitlyByUser) {
3093 m_scrollToTextFragmentRetryTimer.stop();
3094 }
3095
3096 return false;
3097 }
3098
3099 m_scrollToTextFragmentRetryTimer.stop();
3100
3101 auto range = highlightRanges.first();
3102 RefPtr commonAncestor = commonInclusiveAncestor<ComposedTree>(range);
3103 if (commonAncestor && !is<Element>(commonAncestor))
3104 commonAncestor = commonAncestor->parentElement();
3105 if (commonAncestor) {
3106 document->setCSSTarget(downcast<Element>(commonAncestor.get()));
3107 revealClosedDetailsAndHiddenUntilFoundAncestors(*commonAncestor);
3108 }
3109
3110 // FIXME: <http://webkit.org/b/245262> (Scroll To Text Fragment should use DelegateMainFrameScroll)
3111 auto selectionChange = revealRangeWithTemporarySelection(range);
3112 if (m_frame->settings().scrollToTextFragmentIndicatorEnabled() && !m_frame->page()->isControlledByAutomation())
3113 m_delayedTextFragmentIndicatorTimer.startOneShot(100_ms);
3114
3115 maintainScrollPositionAtScrollToTextFragmentRange(range);
3116
3117 return true;
3118}
3119
3120void LocalFrameView::scrollToTextFragmentRetryTimerFired()
3121{
3122 scrollToTextFragment(IsRetry::Yes);
3123}
3124
3125bool LocalFrameView::scrollToAnchorFragment(StringView fragmentIdentifier)
3126{
3127 // If our URL has no ref, then we have no place we need to jump to.
3128 if (fragmentIdentifier.isNull())
3129 return false;
3130
3131 LOG_WITH_STREAM(Scrolling, stream << *this << " scrollToAnchorFragment " << fragmentIdentifier)((void)0);
3132
3133 ASSERT(m_frame->document())((void)0);
3134 Ref document = *m_frame->document();
3135 RELEASE_ASSERT(document->haveStylesheetsLoaded())do { if (__builtin_expect(!!(!(document->haveStylesheetsLoaded
())), 0)) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash
(); WTFCrashWithInfo(3135, "/Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/Source/WebCore/page/LocalFrameView.cpp"
, __PRETTY_FUNCTION__ ); } while (false); } while (0)
;
3136
3137 RefPtr anchorElement = document->findAnchor(fragmentIdentifier);
3138
3139 LOG(Scrolling, " anchorElement is %p", anchorElement.get())((void)0);
3140
3141 // Setting to null will clear the current target.
3142 document->setCSSTarget(anchorElement.get());
3143
3144 if (is<SVGDocument>(document.get())) {
3145 if (fragmentIdentifier.isEmpty())
3146 return false;
3147 if (auto rootElement = DocumentSVG::rootElement(document.get())) {
3148 if (rootElement->scrollToFragment(fragmentIdentifier))
3149 return true;
3150 // If SVG failed to scrollToAnchor() and anchorElement is null, no other scrolling will be possible.
3151 if (!anchorElement)
3152 return false;
3153 }
3154 } else if (!anchorElement && !(fragmentIdentifier.isEmpty() || equalLettersIgnoringASCIICase(fragmentIdentifier, "top"_s))) {
3155 // Implement the rule that "" and "top" both mean top of page as in other browsers.
3156 return false;
3157 }
3158
3159 if (anchorElement)
3160 revealClosedDetailsAndHiddenUntilFoundAncestors(*anchorElement);
3161
3162 RefPtr<ContainerNode> scrollPositionAnchor = anchorElement;
3163 if (!scrollPositionAnchor)
3164 scrollPositionAnchor = m_frame->document();
3165 maintainScrollPositionAtAnchor(scrollPositionAnchor.get());
3166
3167 if (anchorElement) {
3168 // If the anchor accepts keyboard focus, move focus there to aid users relying on keyboard navigation.
3169 if (anchorElement->isFocusable())
3170 document->setFocusedElement(anchorElement.get(), { { }, { }, { }, { }, { }, { }, FocusVisibility::Visible });
3171 else {
3172 document->setFocusedElement(nullptr);
3173 document->setFocusNavigationStartingNode(anchorElement.get());
3174 }
3175 }
3176
3177 return true;
3178}
3179
3180void LocalFrameView::maintainScrollPositionAtAnchor(ContainerNode* anchorNode)
3181{
3182 LOG(Scrolling, "LocalFrameView::maintainScrollPositionAtAnchor at %p", anchorNode)((void)0);
3183
3184 m_maintainScrollPositionAnchor = anchorNode;
3185 if (!m_maintainScrollPositionAnchor)
3186 return;
3187
3188 cancelScheduledScrolls();
3189
3190 if (RefPtr element = dynamicDowncast<Element>(anchorNode))
3191 m_frame->document()->updateContentRelevancyForScrollIfNeeded(*element);
3192
3193 // We need to update the layout before scrolling, otherwise we could
3194 // really mess things up if an anchor scroll comes at a bad moment.
3195 m_frame->document()->updateStyleIfNeeded();
3196 // Only do a layout if changes have occurred that make it necessary.
3197 CheckedPtr renderView = this->renderView();
3198 if (renderView && renderView->needsLayout())
3199 layoutContext().layout();
3200 else
3201 scheduleScrollToAnchorAndTextFragment();
3202
3203 scrollToAnchorAndTextFragmentNowIfNeeded();
3204}
3205
3206void LocalFrameView::maintainScrollPositionAtScrollToTextFragmentRange(SimpleRange& range)
3207{
3208 m_pendingTextFragmentIndicatorRange = range;
3209 m_pendingTextFragmentIndicatorText = plainText(range);
3210 if (!m_pendingTextFragmentIndicatorRange)
3211 return;
3212
3213 scrollToPendingTextFragmentRange();
3214}
3215
3216void LocalFrameView::scrollElementToRect(const Element& element, const IntRect& rect)
3217{
3218 protect(m_frame->document())->updateLayoutIgnorePendingStylesheets();
3219
3220 LayoutRect bounds;
3221 if (RenderElement* renderer = element.renderer())
3222 bounds = renderer->absoluteAnchorRect();
3223 int centeringOffsetX = (rect.width() - bounds.width()) / 2;
3224 int centeringOffsetY = (rect.height() - bounds.height()) / 2;
3225 setScrollPosition(IntPoint(bounds.x() - centeringOffsetX - rect.x(), bounds.y() - centeringOffsetY - rect.y()));
3226}
3227
3228ScrollableArea* LocalFrameView::scrollableAreaForNode(ContainerNode& node)
3229{
3230 if (node.isDocumentNode())
3231 return this;
3232
3233 if (CheckedPtr renderer = node.renderer()) {
3234 if (auto* renderListBox = dynamicDowncast<RenderListBox>(*renderer))
3235 return renderListBox;
3236
3237 if (CheckedPtr layer = renderer->enclosingLayer()) {
3238 if (CheckedPtr scrollableLayer = layer->enclosingScrollableLayer(IncludeSelfOrNot::IncludeSelf, CrossFrameBoundaries::No))
3239 return scrollableLayer->scrollableArea();
3240 }
3241 }
3242 return nullptr;
3243}
3244
3245void LocalFrameView::setScrollOffsetWithOptions(std::optional<int> x, std::optional<int> y, const ScrollPositionChangeOptions& options)
3246{
3247 if (x && y) {
3248 setScrollOffsetWithOptions({ *x, *y }, options);
3249 return;
3250 }
3251
3252 auto offset = scrollOffsetFromPosition(scrollPosition());
3253
3254 if (x)
3255 offset.setX(*x);
3256
3257 if (y)
3258 offset.setY(*y);
3259
3260 setScrollOffsetWithOptions(offset, options);
3261}
3262
3263void LocalFrameView::setScrollOffsetWithOptions(const ScrollOffset& scrollOffset, const ScrollPositionChangeOptions& options)
3264{
3265 LOG_WITH_STREAM(Scrolling, stream << "LocalFrameView::setScrollOffset " << scrollOffset << " animated " << (options.animated == ScrollIsAnimated::Yes) << ", clearing anchor")((void)0);
3266
3267 auto scrollTypeScope = ScrollTypeScope(*this, options.type);
3268
3269 m_maintainScrollPositionAnchor = nullptr;
3270 cancelScheduledScrolls();
3271
3272 RefPtr page = m_frame->page();
3273 if (page && page->isMonitoringWheelEvents())
3274 scrollAnimator().setWheelEventTestMonitor(page->wheelEventTestMonitor());
3275
3276 ScrollOffset snappedOffset = ceiledIntPoint(scrollAnimator().scrollOffsetAdjustedForSnapping(scrollOffset, options.snapPointSelectionMethod));
3277 auto snappedPosition = scrollPositionFromOffset(snappedOffset);
3278
3279 if (options.animated == ScrollIsAnimated::Yes)
3280 scrollToPositionWithAnimation(snappedPosition, options);
3281 else
3282 ScrollView::setScrollPosition(snappedPosition, options);
3283}
3284
3285void LocalFrameView::scrollToEdgeWithOptions(RectEdges<bool> edges, const ScrollPositionChangeOptions& options)
3286{
3287 LOG_WITH_STREAM(Scrolling, stream << "LocalFrameView::scrollToEdgeWithOptions " << edges << " animated " << (options.animated == ScrollIsAnimated::Yes) << ", clearing anchor")((void)0);
3288
3289 ASSERT(std::ranges::count_if(WebCore::allBoxSides, [edges](auto side) { return edges[side]; }) == 1)((void)0);
3290
3291 auto currentOffset = scrollOffsetFromPosition(scrollPosition());
3292 auto minimumScrollOffset = scrollOffsetFromPosition(minimumScrollPosition());
3293 auto maximumScrollOffset = scrollOffsetFromPosition(maximumScrollPosition());
3294
3295 if (edges.top())
3296 currentOffset.setY(minimumScrollOffset.y());
3297 else if (edges.left())
3298 currentOffset.setX(minimumScrollOffset.x());
3299 else if (edges.bottom())
3300 currentOffset.setY(maximumScrollOffset.y());
3301 else if (edges.right())
3302 currentOffset.setX(maximumScrollOffset.x());
3303
3304 setScrollOffsetWithOptions(currentOffset, options);
3305}
3306
3307void LocalFrameView::setScrollPosition(const ScrollPosition& scrollPosition, const ScrollPositionChangeOptions& options)
3308{
3309 LOG_WITH_STREAM(Scrolling, stream << "LocalFrameView::setScrollPosition " << scrollPosition << " animated " << (options.animated == ScrollIsAnimated::Yes) << ", clearing anchor")((void)0);
3310
3311 setScrollOffsetWithOptions(scrollOffsetFromPosition(scrollPosition), options);
3312}
3313
3314void LocalFrameView::resetScrollAnchor()
3315{
3316 ASSERT(m_frame->document())((void)0);
3317 Ref document = *m_frame->document();
3318
3319 // If CSS target was set previously, we want to set it to 0, recalc
3320 // and possibly repaint because :target pseudo class may have been
3321 // set (see bug 11321).
3322 document->setCSSTarget(nullptr);
3323
3324 if (is<SVGDocument>(document.get())) {
3325 if (auto rootElement = DocumentSVG::rootElement(document.get())) {
3326 // We need to update the layout before resetScrollAnchor(), otherwise we
3327 // could really mess things up if resetting the anchor comes at a bad moment.
3328 document->updateStyleIfNeeded();
3329 rootElement->resetScrollAnchor();
3330 }
3331 }
3332}
3333
3334void LocalFrameView::scheduleScrollToFocusedElement(SelectionRevealMode selectionRevealMode)
3335{
3336 if (selectionRevealMode == SelectionRevealMode::DoNotReveal)
3337 return;
3338
3339 m_selectionRevealModeForFocusedElement = selectionRevealMode;
3340 if (m_shouldScrollToFocusedElement)
3341 return;
3342 m_shouldScrollToFocusedElement = true;
3343 m_delayedScrollToFocusedElementTimer.startOneShot(0_s);
3344}
3345
3346void LocalFrameView::cancelScheduledScrolls()
3347{
3348 cancelScheduledScrollToFocusedElement();
3349 cancelScheduledTextFragmentIndicatorTimer();
3350}
3351
3352void LocalFrameView::cancelScheduledScrollToFocusedElement()
3353{
3354 m_shouldScrollToFocusedElement = false;
3355 m_delayedScrollToFocusedElementTimer.stop();
3356}
3357
3358void LocalFrameView::scrollToFocusedElementImmediatelyIfNeeded()
3359{
3360 if (!m_shouldScrollToFocusedElement)
3361 return;
3362
3363 m_delayedScrollToFocusedElementTimer.stop();
3364 scrollToFocusedElementInternal();
3365}
3366
3367void LocalFrameView::scrollToFocusedElementTimerFired()
3368{
3369 scrollToFocusedElementInternal();
3370}
3371
3372void LocalFrameView::scrollToFocusedElementInternal()
3373{
3374 RELEASE_ASSERT(m_shouldScrollToFocusedElement)do { if (__builtin_expect(!!(!(m_shouldScrollToFocusedElement
)), 0)) do { WTF::isIntegralOrPointerType(); compilerFenceForCrash
(); WTFCrashWithInfo(3374, "/Volumes/Data/worker/macOS-Safer-CPP-Checks-EWS/build/Source/WebCore/page/LocalFrameView.cpp"
, __PRETTY_FUNCTION__ ); } while (false); } while (0)
;
3375 RefPtr document = m_frame->document();
3376 if (!document)
3377 return;
3378
3379 document->updateLayoutIgnorePendingStylesheets();
3380 if (!m_shouldScrollToFocusedElement || m_delayedScrollToFocusedElementTimer.isActive())
3381 return; // Updating the layout may have ran scripts.
3382
3383 m_shouldScrollToFocusedElement = false;
3384
3385 RefPtr focusedElement = document->focusedElement();
3386 if (!focusedElement)
3387 return;
3388 auto updateTarget = focusedElement->focusAppearanceUpdateTarget();
3389 if (!updateTarget)
3390 return;
3391
3392 // Get the scroll-margin of the shadow host when we're inside a user agent shadow root.
3393 if (updateTarget->containingShadowRoot() && updateTarget->containingShadowRoot()->mode() == ShadowRootMode::UserAgent)
3394 updateTarget = updateTarget->shadowHost();
3395
3396 CheckedPtr renderer = updateTarget->renderer();
3397 if (!renderer || renderer->isRenderWidget())
3398 return;
3399
3400 bool insideFixed;
3401 auto absoluteBounds = renderer->absoluteAnchorRectWithScrollMargin(&insideFixed);
3402 auto anchorRectWithScrollMargin = absoluteBounds.marginRect;
3403 auto anchorRect = absoluteBounds.anchorRect;
3404 LocalFrameView::scrollRectToVisible(anchorRectWithScrollMargin, *renderer, insideFixed, { m_selectionRevealModeForFocusedElement, ScrollAlignment::alignCenterIfNeeded, ScrollAlignment::alignCenterIfNeeded, ShouldAllowCrossOriginScrolling::No, ScrollBehavior::Auto, OnlyAllowForwardScrolling::No, AllowScrollingOverflowHidden::Yes, anchorRect });
3405}
3406
3407void LocalFrameView::textFragmentIndicatorTimerFired()
3408{
3409 ASSERT(m_frame->document())((void)0);
3410 Ref document = *m_frame->document();
3411
3412 m_delayedTextFragmentIndicatorTimer.stop();
3413
3414 if (!m_pendingTextFragmentIndicatorRange)
3415 return;
3416
3417 if (m_pendingTextFragmentIndicatorText != plainText(m_pendingTextFragmentIndicatorRange.value()))
3418 return;
3419
3420 auto range = m_pendingTextFragmentIndicatorRange.value();
3421
3422 // FIXME: <http://webkit.org/b/245262> (Scroll To Text Fragment should use DelegateMainFrameScroll)
3423 auto selectionChange = revealRangeWithTemporarySelection(range);
3424
3425 maintainScrollPositionAtScrollToTextFragmentRange(range);
3426}
3427
3428void LocalFrameView::cancelScheduledTextFragmentIndicatorTimer()
3429{
3430 if (m_skipScrollResetOfScrollToTextFragmentRange)
3431 return;
3432 m_pendingTextFragmentIndicatorRange.reset();
3433 m_pendingTextFragmentIndicatorText = String();
3434 m_haveCreatedTextIndicator = false;
3435 m_delayedTextFragmentIndicatorTimer.stop();
3436}
3437
3438static void NODELETE[[clang::annotate_type("webkit.nodelete")]] adjustScrollRectToVisibleOptionsForHiddenOverflow(ScrollRectToVisibleOptions& options, const RenderStyle& style)
3439{
3440 if (options.allowScrollingOverflowHidden == AllowScrollingOverflowHidden::Yes)
3441 return;
3442 if (style.overflowX() == Overflow::Hidden)
3443 options.alignX = ScrollAlignment::noScroll;
3444 if (style.overflowY() == Overflow::Hidden)
3445 options.alignY = ScrollAlignment::noScroll;
3446}
3447
3448static void adjustForScrollByAnchor(const RenderLayerModelObject& renderer, EnumSet<BoxAxis>& isFixed)
3449{
3450 if (!renderer.isFixedPositioned())
3451 return;
3452 auto* renderBox = dynamicDowncast<RenderBox>(&renderer);
3453 ASSERT(renderBox)((void)0);
3454
3455 if (!renderBox || !Style::AnchorPositionEvaluator::isAnchorPositioned(renderBox->style()))
3456 return;
3457 auto adjuster = renderBox->layoutContext().anchorScrollAdjusterFor(*renderBox);
3458 if (!adjuster || !adjuster->hasViewportSnapshot())
3459 return;
3460
3461 if (adjuster->mayNeedXAdjustment())
3462 isFixed.remove(BoxAxis::Horizontal);
3463 if (adjuster->mayNeedYAdjustment())
3464 isFixed.remove(BoxAxis::Vertical);
3465}
3466
3467bool LocalFrameView::scrollRectToVisible(const LayoutRect& absoluteRect, const RenderObject& renderer, bool insideFixed, const ScrollRectToVisibleOptions& options)
3468{
3469 if (options.revealMode == SelectionRevealMode::DoNotReveal)
3470 return false;
3471
3472 if (renderer.isSkippedContent())
3473 return false;
3474
3475 CheckedPtr layer = renderer.enclosingLayer();
3476 if (!layer)
3477 return false;
3478
3479 // FIXME: It would be nice to use RenderLayer::enclosingScrollableLayer here, but that seems to skip overflow:hidden layers.
3480 auto adjustedRect = absoluteRect;
3481 ScrollRectToVisibleOptions adjustedOptions = options;
3482 EnumSet<BoxAxis> isFixed(insideFixed ? EnumSet<BoxAxis> { BoxAxis::Horizontal, BoxAxis::Vertical } : EnumSet<BoxAxis> { });
3483
3484 for (; layer; layer = layer->enclosingContainingBlockLayer(CrossFrameBoundaries::No)) {
3485 if (layer->shouldTryToScrollForScrollIntoView(adjustedOptions)) {
3486 adjustScrollRectToVisibleOptionsForHiddenOverflow(adjustedOptions, layer->renderer().style());
3487 adjustedRect = layer->ensureLayerScrollableArea()->scrollRectToVisible(adjustedRect, adjustedOptions);
3488 if (adjustedOptions.visibilityCheckRect)
3489 adjustedOptions.visibilityCheckRect->setLocation(adjustedRect.location());
3490 }
3491 // FIXME: Make scroll adjustments work for more than just fixedpos elements.
3492 if (insideFixed)
3493 adjustForScrollByAnchor(layer->renderer(), isFixed);
3494 }
3495
3496 CheckedRef frameView = renderer.view().frameView();
3497 RefPtr ownerElement = frameView->m_frame->document() ? frameView->m_frame->document()->ownerElement() : nullptr;
3498 if (ownerElement && ownerElement->renderer())
3499 frameView->scrollRectToVisibleInChildView(adjustedRect, isFixed, adjustedOptions, ownerElement.get());
3500 else
3501 frameView->scrollRectToVisibleInTopLevelView(adjustedRect, isFixed, adjustedOptions);
3502 return true;
3503}
3504
3505TemporarySelectionChange LocalFrameView::revealRangeWithTemporarySelection(const SimpleRange& range, OptionSet<TemporarySelectionOption> extraOptions)
3506{
3507 static constexpr OptionSet defaultOptions {
3508 TemporarySelectionOption::RevealSelection,
3509 TemporarySelectionOption::RevealSelectionBounds,
3510 TemporarySelectionOption::UserTriggered,
3511 TemporarySelectionOption::ForceCenterScroll
3512 };
3513 return { protect(range.startContainer().document()), { range }, defaultOptions | extraOptions };
3514}
3515
3516static ScrollPositionChangeOptions scrollPositionChangeOptionsForElement(const LocalFrameView& frameView, Element* element, const ScrollRectToVisibleOptions& options)
3517{
3518 auto scrollPositionOptions = ScrollPositionChangeOptions::createProgrammatic();
3519 if (!frameView.frame().eventHandler().autoscrollInProgress()
3520 && element
3521 && useSmoothScrolling(options.behavior, element))
3522 scrollPositionOptions.animated = ScrollIsAnimated::Yes;
3523 return scrollPositionOptions;
3524};
3525
3526void LocalFrameView::scrollRectToVisibleInChildView(const LayoutRect& absoluteRect, EnumSet<BoxAxis> isFixed, const ScrollRectToVisibleOptions& options, const HTMLFrameOwnerElement* ownerElement)
3527{
3528 // If scrollbars aren't explicitly forbidden, permit scrolling.
3529 if (m_frame->scrollingMode() == ScrollbarMode::AlwaysOff) {
3530 // If scrollbars are forbidden, user initiated scrolls should obviously be ignored.
3531 if (m_lastUserScrollType == UserScrollType::Explicit)
3532 return;
3533 // Forbid autoscrolls when scrollbars are off, but permits other programmatic scrolls, like navigation to an anchor.
3534 if (m_frame->eventHandler().autoscrollInProgress())
3535 return;
3536 }
3537
3538 // If this assertion fires we need to protect the ownerElement from being destroyed.
3539 ScriptDisallowedScope::InMainThread scriptDisallowedScope;
3540 auto viewRect = visibleContentRect(ScrollableArea::LegacyIOSDocumentVisibleRect);
3541
3542 // scroll-padding applies to the scroll container, but expand the rectangle that we want to expose in order
3543 // simulate padding the scroll container. This rectangle is passed up the tree of scrolling elements to
3544 // ensure that the padding on this scroll container is maintained.
3545 auto targetRect = absoluteRect;
3546 RefPtr element = ownerElement->contentDocument() ? ownerElement->contentDocument()->documentElement() : nullptr;
3547 if (auto* renderer = element ? element->renderBox() : nullptr)
3548 targetRect.expand(renderer->scrollPaddingForViewportRect(viewRect));
3549
3550 auto revealRect = getPossiblyFixedRectToExpose(viewRect, targetRect, isFixed, options.alignX, options.alignY);
3551 auto scrollPosition = roundedIntPoint(revealRect.location());
3552 scrollPosition = constrainedScrollPosition(scrollPosition);
3553
3554 // FIXME: Should we use contentDocument()->scrollingElement()?
3555 // See https://bugs.webkit.org/show_bug.cgi?id=205059
3556 setScrollPosition(scrollPosition, scrollPositionChangeOptionsForElement(*this, element.get(), options));
3557
3558 if (options.shouldAllowCrossOriginScrolling == ShouldAllowCrossOriginScrolling::No && !safeToPropagateScrollToParent())
3559 return;
3560
3561 // FIXME: ideally need to determine if this <iframe> is inside position:fixed.
3562 if (auto* ownerRenderer = ownerElement->renderer())
3563 scrollRectToVisible(contentsToContainingViewContents(enclosingIntRect(targetRect)), *ownerRenderer, false /* insideFixed */, options);
3564}
3565
3566void LocalFrameView::scrollRectToVisibleInTopLevelView(const LayoutRect& absoluteRect, EnumSet<BoxAxis> isFixed, const ScrollRectToVisibleOptions& options)
3567{
3568 if (options.revealMode == SelectionRevealMode::RevealUpToMainFrame && m_frame->isMainFrame())
3569 return;
3570
3571 RefPtr page = m_frame->page();
3572 if (!page)
3573 return;
3574
3575 if (options.revealMode == SelectionRevealMode::DelegateMainFrameScroll && m_frame->isMainFrame()) {
3576 page->chrome().scrollMainFrameToRevealRect(snappedIntRect(absoluteRect));
3577 return;
3578 }
3579
3580 auto minScrollPosition = minimumScrollPosition();
3581 auto maxScrollPosition = maximumScrollPosition();
3582
3583#if !PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
3584 auto viewRect = visibleContentRect();
3585#else
3586 // FIXME: ContentInsets should be taken care of in UI process side. webkit.org/b/199682
3587 // To do that, getRectToExposeForScrollIntoView needs to return the additional scrolling to do beyond content rect.
3588 LayoutRect viewRect = viewRectExpandedByContentInsets();
3589
3590 // FIXME: webkit.org/b/199683 LocalFrameView::visibleContentRect is wrong when content insets are present
3591 maxScrollPosition = scrollPositionFromOffset(ScrollPosition(totalContentsSize() - flooredIntSize(viewRect.size())));
3592
3593 auto contentInsets = page->contentInsets();
3594 minScrollPosition.move(-contentInsets.left(), -contentInsets.top());
3595 maxScrollPosition.move(contentInsets.right(), contentInsets.bottom());
3596#endif
3597 // Move the target rect into "scrollView contents" coordinates.
3598 auto targetRect = absoluteRect;
3599 targetRect.move(0, headerHeight());
3600
3601 // scroll-padding applies to the scroll container, but expand the rectangle that we want to expose in order
3602 // simulate padding the scroll container. This rectangle is passed up the tree of scrolling elements to
3603 // ensure that the padding on this scroll container is maintained.
3604 RefPtr element = m_frame->document() ? m_frame->document()->documentElement() : nullptr;
3605 if (auto* renderBox = element ? element->renderBox() : nullptr)
3606 targetRect.expand(renderBox->scrollPaddingForViewportRect(viewRect));
3607
3608 LayoutRect revealRect = getPossiblyFixedRectToExpose(viewRect, targetRect, isFixed, options.alignX, options.alignY);
3609
3610 // Avoid scrolling to the rounded value of revealRect.location() if we don't actually need to scroll
3611 if (revealRect != viewRect) {
3612 if (options.onlyAllowForwardScrolling == OnlyAllowForwardScrolling::No || (revealRect.y() > viewRect.y())) {
3613
3614 // FIXME: Should we use document()->scrollingElement()?
3615 // See https://bugs.webkit.org/show_bug.cgi?id=205059
3616 ScrollOffset clampedScrollPosition = roundedIntPoint(revealRect.location()).constrainedBetween(minScrollPosition, maxScrollPosition);
3617 setScrollPosition(clampedScrollPosition, scrollPositionChangeOptionsForElement(*this, element.get(), options));
3618 }
3619 }
3620
3621 // This is the outermost view of a web page, so after scrolling this view we
3622 // scroll its container by calling Page::scrollMainFrameToRevealRect.
3623 // This only has an effect on the Mac platform in applications
3624 // that put web views into scrolling containers, such as macOS Mail.
3625 // The canAutoscroll function in EventHandler also knows about this.
3626 page->chrome().scrollContainingScrollViewsToRevealRect(snappedIntRect(absoluteRect));
3627}
3628
3629void LocalFrameView::contentsResized()
3630{
3631 // For non-delegated scrolling, updateTiledBackingAdaptiveSizing() is called via addedOrRemovedScrollbar() which occurs less often.
3632 if (delegatesScrollingToNativeView())
3633 updateTiledBackingAdaptiveSizing();
3634}
3635
3636void LocalFrameView::delegatedScrollingModeDidChange()
3637{
3638 CheckedPtr renderView = this->renderView();
3639 if (!renderView)
3640 return;
3641
3642 RenderLayerCompositor& compositor = renderView->compositor();
3643 // When we switch to delegatesScrolling mode, we should destroy the scrolling/clipping layers in RenderLayerCompositor.
3644 // FIXME: Is this right? What turns compositing back on?
3645 if (compositor.usesCompositing()) {
3646 ASSERT(compositor.usesCompositing())((void)0);
3647 compositor.enableCompositingMode(false);
3648 compositor.clearBackingForAllLayers();
3649 }
3650}
3651
3652void LocalFrameView::setViewportConstrainedObjectsNeedLayout()
3653{
3654 if (!hasViewportConstrainedObjects())
3655 return;
3656
3657 for (CheckedRef renderer : *m_viewportConstrainedObjects) {
3658 renderer->setNeedsLayout();
3659 if (renderer->hasLayer()) {
3660 auto* layer = downcast<RenderBoxModelObject>(renderer).layer();
3661 layer->setNeedsCompositingGeometryUpdate();
3662 }
3663 }
3664}
3665
3666void LocalFrameView::didChangeScrollOffset()
3667{
3668 if (RefPtr page = m_frame->page()) {
3669 page->pageOverlayController().didScrollFrame(m_frame.get());
3670 InspectorInstrumentation::didScroll(*page);
3671 }
3672
3673 m_frame->loader().client().didChangeScrollOffset();
3674}
3675
3676void LocalFrameView::scrollOffsetChangedViaPlatformWidgetImpl(const ScrollOffset& oldOffset, const ScrollOffset& newOffset)
3677{
3678#if PLATFORM(COCOA)(defined 1 && 1)
3679 auto deferrer = WheelEventTestMonitorCompletionDeferrer { m_frame->page()->wheelEventTestMonitor().get(), scrollingNodeIDForTesting(), WheelEventTestMonitor::DeferReason::ContentScrollInProgress };
3680#endif
3681
3682 updateLayerPositionsAfterScrolling();
3683 updateCompositingLayersAfterScrolling();
3684 repaintSlowRepaintObjects();
3685 scrollPositionChanged(scrollPositionFromOffset(oldOffset), scrollPositionFromOffset(newOffset));
3686 clearScrollAnchor();
3687
3688 if (auto* renderView = this->renderView()) {
3689 if (renderView->usesCompositing())
3690 renderView->compositor().didChangeVisibleRect();
3691 }
3692}
3693
3694// These scroll positions are affected by zooming.
3695void LocalFrameView::scrollPositionChanged(const ScrollPosition& oldPosition, const ScrollPosition& newPosition)
3696{
3697 UNUSED_PARAM(oldPosition)(void)oldPosition;
3698 UNUSED_PARAM(newPosition)(void)newPosition;
3699
3700 RefPtr page = m_frame->page();
3701 Seconds throttlingDelay = page ? page->chrome().client().eventThrottlingDelay() : 0_s;
3702
3703 if (throttlingDelay == 0_s) {
3704 m_delayedScrollEventTimer.stop();
3705 scheduleScrollEvent();
3706 } else if (!m_delayedScrollEventTimer.isActive())
3707 m_delayedScrollEventTimer.startOneShot(throttlingDelay);
3708
3709 if (CheckedPtr renderView = this->renderView()) {
3710 if (renderView->usesCompositing())
3711 renderView->compositor().frameViewDidScroll();
3712 }
3713
3714 LOG_WITH_STREAM(Scrolling, stream << "LocalFrameView " << this << " scrollPositionChanged from " << oldPosition << " to " << newPosition << " (scale " << frameScaleFactor() << " )")((void)0);
3715
3716 updateLayoutViewport();
3717 viewportContentsChanged();
3718
3719 if (auto* renderView = this->renderView()) {
3720 if (CheckedPtr layer = renderView->layer())
3721 m_frame->editor().renderLayerDidScroll(*layer);
3722 }
3723}
3724
3725void LocalFrameView::applyRecursivelyWithVisibleRect(NOESCAPE[[clang::noescape]] const Function<void(LocalFrameView& frameView, const IntRect& visibleRect)>& apply)
3726{
3727 IntRect windowClipRect = this->windowClipRect();
3728 auto visibleRect = windowToContents(windowClipRect);
3729 apply(*this, visibleRect);
3730
3731 // Recursive call for subframes. We cache the current LocalFrameView's windowClipRect to avoid recomputing it for every subframe.
3732 SetForScope windowClipRectCache(m_cachedWindowClipRect, &windowClipRect);
3733 for (RefPtr childFrame = m_frame->tree().firstChild(); childFrame; childFrame = childFrame->tree().nextSibling()) {
3734 RefPtr localFrame = dynamicDowncast<LocalFrame>(childFrame);
3735 if (!localFrame)
3736 continue;
3737 if (RefPtr childView = localFrame->view())
3738 childView->applyRecursivelyWithVisibleRect(apply);
3739 }
3740}
3741
3742void LocalFrameView::resumeVisibleImageAnimations(const IntRect& visibleRect)
3743{
3744 if (visibleRect.isEmpty())
3745 return;
3746
3747 if (auto* renderView = m_frame->contentRenderer())
3748 renderView->resumePausedImageAnimationsIfNeeded(visibleRect);
3749}
3750
3751void LocalFrameView::updateScriptedAnimationsAndTimersThrottlingState(const IntRect& visibleRect)
3752{
3753 if (m_frame->isMainFrame())
3754 return;
3755
3756 RefPtr document = m_frame->document();
3757 if (!document)
3758 return;
3759
3760 // We don't throttle zero-size or display:none frames because those are usually utility frames.
3761 bool shouldThrottle = visibleRect.isEmpty() && !m_lastUsedSizeForLayout.isEmpty() && m_frame->ownerRenderer();
3762 document->setTimerThrottlingEnabled(shouldThrottle);
3763
3764 RefPtr page = m_frame->page();
3765 if (!page || !page->canUpdateThrottlingReason(ThrottlingReason::OutsideViewport))
3766 return;
3767
3768 RefPtr scriptedAnimationController = document->scriptedAnimationController();
3769 if (!scriptedAnimationController)
3770 return;
3771
3772 if (shouldThrottle)
3773 scriptedAnimationController->addThrottlingReason(ThrottlingReason::OutsideViewport);
3774 else
3775 scriptedAnimationController->removeThrottlingReason(ThrottlingReason::OutsideViewport);
3776}
3777
3778
3779void LocalFrameView::resumeVisibleImageAnimationsIncludingSubframes()
3780{
3781 applyRecursivelyWithVisibleRect([] (LocalFrameView& frameView, const IntRect& visibleRect) {
3782 frameView.resumeVisibleImageAnimations(visibleRect);
3783 });
3784}
3785
3786#if ENABLE(ACCESSIBILITY_ANIMATION_CONTROL)(defined 1 && 1)
3787void LocalFrameView::updatePlayStateForAllAnimations(const IntRect& visibleRect)
3788{
3789 if (auto* renderView = m_frame->contentRenderer())
3790 renderView->updatePlayStateForAllAnimations(visibleRect);
3791}
3792
3793void LocalFrameView::updatePlayStateForAllAnimationsIncludingSubframes()
3794{
3795 applyRecursivelyWithVisibleRect([] (LocalFrameView& frameView, const IntRect& visibleRect) {
3796 frameView.updatePlayStateForAllAnimations(visibleRect);
3797 });
3798}
3799#endif // ENABLE(ACCESSIBILITY_ANIMATION_CONTROL)
3800
3801void LocalFrameView::updateLayerPositionsAfterScrolling()
3802{
3803 // If we're scrolling as a result of updating the view size after layout, we'll update widgets and layer positions soon anyway.
3804 if (layoutContext().layoutPhase() == LocalFrameViewLayoutContext::LayoutPhase::InViewSizeAdjust)
3805 return;
3806
3807 if (!layoutContext().isLayoutNested() && hasViewportConstrainedObjects()) {
3808 if (CheckedPtr renderView = this->renderView()) {
3809 updateWidgetPositions();
3810 layoutContext().flushUpdateLayerPositions();
3811 renderView->layer()->updateLayerPositionsAfterDocumentScroll();
3812 }
3813 }
3814}
3815
3816void LocalFrameView::updateLayerPositionsAfterOverflowScroll(RenderLayer& layer)
3817{
3818 layoutContext().flushUpdateLayerPositions();
3819 layer.updateLayerPositionsAfterOverflowScroll();
3820 scheduleUpdateWidgetPositions();
3821}
3822
3823ScrollingCoordinator* LocalFrameView::scrollingCoordinator() const
3824{
3825 return m_frame->page() ? m_frame->page()->scrollingCoordinator() : nullptr;
3826}
3827
3828bool LocalFrameView::shouldUpdateCompositingLayersAfterScrolling() const
3829{
3830#if ENABLE(ASYNC_SCROLLING)(defined 1 && 1)
3831 // If the scrolling thread is updating the fixed elements, then the LocalFrameView should not update them as well.
3832
3833 RefPtr page = m_frame->page();
3834 if (!page)
3835 return true;
3836
3837 if (&page->mainFrame() != m_frame.ptr())
3838 return true;
3839
3840 RefPtr scrollingCoordinator = this->scrollingCoordinator();
3841 if (!scrollingCoordinator)
3842 return true;
3843
3844 if (scrollingCoordinator->shouldUpdateScrollLayerPositionSynchronously(*this))
3845 return true;
3846
3847 if (currentScrollType() == ScrollType::Programmatic)
3848 return true;
3849
3850 // FIXME: Implement anchor positioning in the scrolling tree.
3851 if (hasAnchorPositionedViewportConstrainedObjects())
3852 return true;
3853
3854 return false;
3855#endif
3856 return true;
3857}
3858
3859void LocalFrameView::updateCompositingLayersAfterScrolling()
3860{
3861 ASSERT(layoutContext().layoutPhase() >= LocalFrameViewLayoutContext::LayoutPhase::InPostLayout || layoutContext().layoutPhase() == LocalFrameViewLayoutContext::LayoutPhase::OutsideLayout)((void)0);
3862
3863 if (!shouldUpdateCompositingLayersAfterScrolling())
3864 return;
3865
3866 if (!layoutContext().isLayoutNested() && hasViewportConstrainedObjects()) {
3867 if (CheckedPtr renderView = this->renderView())
3868 renderView->compositor().updateCompositingLayers(CompositingUpdateType::OnScroll);
3869 }
3870}
3871
3872bool LocalFrameView::isUserScrollInProgress() const
3873{
3874 if (RefPtr scrollingCoordinator = this->scrollingCoordinator()) {
3875 if (scrollingCoordinator->isUserScrollInProgress(scrollingNodeID()))
3876 return true;
3877 }
3878
3879 if (auto scrollAnimator = existingScrollAnimator())
3880 return scrollAnimator->isUserScrollInProgress();
3881
3882 return false;
3883}
3884
3885bool LocalFrameView::isRubberBandInProgress() const
3886{
3887 if (scrollbarsSuppressed())
3888 return false;
3889
3890 if (RefPtr scrollingCoordinator = this->scrollingCoordinator())
3891 return scrollingCoordinator->isRubberBandInProgress(scrollingNodeID());
3892
3893 if (auto scrollAnimator = existingScrollAnimator())
3894 return scrollAnimator->isRubberBandInProgress();
3895
3896 return false;
3897}
3898
3899bool LocalFrameView::isInStableState() const
3900{
3901 if (RefPtr page = m_frame->page())
3902 return page->chrome().client().isInStableState();
3903 return FrameView::isInStableState();
3904}
3905
3906bool LocalFrameView::requestStartKeyboardScrollAnimation(const KeyboardScroll& scrollData)
3907{
3908 if (RefPtr scrollingCoordinator = this->scrollingCoordinator())
3909 return scrollingCoordinator->requestStartKeyboardScrollAnimation(*this, scrollData);
3910
3911 return false;
3912}
3913
3914bool LocalFrameView::requestStopKeyboardScrollAnimation(bool immediate)
3915{
3916 if (RefPtr scrollingCoordinator = this->scrollingCoordinator())
3917 return scrollingCoordinator->requestStopKeyboardScrollAnimation(*this, immediate);
3918
3919 return false;
3920}
3921
3922bool LocalFrameView::requestScrollToPosition(const ScrollPosition& position, const ScrollPositionChangeOptions& options)
3923{
3924 LOG_WITH_STREAM(Scrolling, stream << "LocalFrameView::requestScrollToPosition " << position << " options " << options)((void)0);
3925
3926#if ENABLE(ASYNC_SCROLLING)(defined 1 && 1)
3927 if (auto* tiledBacking = this->tiledBacking(); tiledBacking && options.animated == ScrollIsAnimated::No) {
3928#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
3929 auto contentSize = exposedContentRect().size();
3930#else
3931 auto contentSize = visibleContentRect().size();
3932#endif
3933 tiledBacking->prepopulateRect(FloatRect(position, contentSize));
3934 }
3935#endif
3936
3937#if ENABLE(ASYNC_SCROLLING)(defined 1 && 1) || USE(COORDINATED_GRAPHICS)(defined USE_COORDINATED_GRAPHICS && USE_COORDINATED_GRAPHICS
)
3938 if (RefPtr scrollingCoordinator = this->scrollingCoordinator())
3939 return scrollingCoordinator->requestScrollToPosition(*this, position, options);
3940#else
3941 UNUSED_PARAM(position)(void)position;
3942 UNUSED_PARAM(options)(void)options;
3943#endif
3944
3945 return false;
3946}
3947
3948void LocalFrameView::stopAsyncAnimatedScroll()
3949{
3950#if ENABLE(ASYNC_SCROLLING)(defined 1 && 1)
3951 LOG_WITH_STREAM(Scrolling, stream << "LocalFrameView::stopAsyncAnimatedScroll")((void)0);
3952
3953 if (RefPtr scrollingCoordinator = this->scrollingCoordinator())
3954 return scrollingCoordinator->stopAnimatedScroll(*this);
3955#endif
3956}
3957
3958void LocalFrameView::addTrackedRepaintRect(const FloatRect& r)
3959{
3960 if (!m_isTrackingRepaints || r.isEmpty())
3961 return;
3962
3963 FloatRect repaintRect = r;
3964 repaintRect.moveBy(-scrollPosition());
3965 m_trackedRepaintRects.append(repaintRect);
3966}
3967
3968void LocalFrameView::repaintContentRectangle(const IntRect& r)
3969{
3970 ASSERT(!m_frame->ownerElement())((void)0);
3971
3972 if (!shouldUpdate())
3973 return;
3974
3975 ScrollView::repaintContentRectangle(r);
3976}
3977
3978static unsigned countRenderedCharactersInRenderObjectWithThreshold(const RenderElement& renderer, unsigned threshold)
3979{
3980 unsigned count = 0;
3981 for (CheckedPtr<const RenderObject> descendant = &renderer; descendant; descendant = descendant->nextInPreOrder()) {
3982 if (auto* renderText = dynamicDowncast<RenderText>(descendant.get())) {
3983 count += renderText->text().length();
3984 if (count >= threshold)
3985 break;
3986 }
3987 }
3988 return count;
3989}
3990
3991bool LocalFrameView::renderedCharactersExceed(unsigned threshold)
3992{
3993 if (!m_frame->contentRenderer())
3994 return false;
3995 return countRenderedCharactersInRenderObjectWithThreshold(*m_frame->contentRenderer(), threshold) >= threshold;
3996}
3997
3998void LocalFrameView::availableContentSizeChanged(AvailableSizeChangeReason reason)
3999{
4000 if (RefPtr document = m_frame->document()) {
4001 // FIXME: Merge this logic with m_setNeedsLayoutWasDeferred and find a more appropriate
4002 // way of handling potential recursive layouts when the viewport is resized to accomodate
4003 // the content but the content always overflows the viewport. See webkit.org/b/165781.
4004 if (layoutContext().layoutPhase() != LocalFrameViewLayoutContext::LayoutPhase::InViewSizeAdjust || !useFixedLayout())
4005 document->updateViewportUnitsOnResize();
4006 }
4007
4008 updateLayoutViewport();
4009 setNeedsLayoutAfterViewConfigurationChange();
4010 ScrollView::availableContentSizeChanged(reason);
4011}
4012
4013bool LocalFrameView::shouldLayoutAfterContentsResized() const
4014{
4015 return !useFixedLayout() || useCustomFixedPositionLayoutRect();
4016}
4017
4018void LocalFrameView::updateContentsSize()
4019{
4020 // We check to make sure the view is attached to a frame() as this method can
4021 // be triggered before the view is attached by Frame::createView(...) setting
4022 // various values such as setScrollBarModes(...) for example. An ASSERT is
4023 // triggered when a view is layout before being attached to a m_frame->
4024 if (!m_frame->view())
4025 return;
4026
4027#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
4028 if (CheckedPtr root = m_frame->contentRenderer()) {
4029 if (useCustomFixedPositionLayoutRect() && hasViewportConstrainedObjects()) {
4030 setViewportConstrainedObjectsNeedLayout();
4031 // We must eagerly enter compositing mode because fixed position elements
4032 // will not have been made compositing via a preceding style change before
4033 // m_useCustomFixedPositionLayoutRect was true.
4034 root->compositor().enableCompositingMode();
4035 }
4036 }
4037#endif
4038
4039 if (shouldLayoutAfterContentsResized() && needsLayout())
4040 layoutContext().layout();
4041
4042 if (CheckedPtr renderView = this->renderView()) {
4043 if (renderView->usesCompositing())
4044 renderView->compositor().frameViewDidChangeSize();
4045 }
4046}
4047
4048void LocalFrameView::addedOrRemovedScrollbar()
4049{
4050 if (CheckedPtr renderView = this->renderView()) {
4051 if (renderView->usesCompositing())
4052 renderView->compositor().frameViewDidAddOrRemoveScrollbars();
4053 }
4054
4055 updateTiledBackingAdaptiveSizing();
4056
4057 InspectorInstrumentation::didAddOrRemoveScrollbars(*this);
4058}
4059
4060OptionSet<TiledBacking::Scrollability> LocalFrameView::computeScrollability() const
4061{
4062 RefPtr page = m_frame->page();
4063
4064 // Use smaller square tiles if the Window is not active to facilitate app napping.
4065 if (!page || !page->isWindowActive())
4066 return { TiledBacking::Scrollability::HorizontallyScrollable, TiledBacking::Scrollability::VerticallyScrollable };
4067
4068 bool horizontallyScrollable;
4069 bool verticallyScrollable;
4070 bool clippedByAncestorView = static_cast<bool>(m_viewExposedRect);
4071
4072#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
4073 if (page)
4074 clippedByAncestorView |= page->enclosedInScrollableAncestorView();
4075#endif
4076
4077 if (delegatesScrollingToNativeView()) {
4078 IntSize documentSize = contentsSize();
4079 IntSize visibleSize = this->visibleSize();
4080
4081 horizontallyScrollable = clippedByAncestorView || documentSize.width() > visibleSize.width();
4082 verticallyScrollable = clippedByAncestorView || documentSize.height() > visibleSize.height();
4083 } else {
4084 horizontallyScrollable = clippedByAncestorView || horizontalScrollbar();
4085 verticallyScrollable = clippedByAncestorView || verticalScrollbar();
4086 }
4087
4088 OptionSet<TiledBacking::Scrollability> scrollability = TiledBacking::Scrollability::NotScrollable;
4089 if (horizontallyScrollable)
4090 scrollability.add(TiledBacking::Scrollability::HorizontallyScrollable);
4091
4092 if (verticallyScrollable)
4093 scrollability.add(TiledBacking::Scrollability::VerticallyScrollable);
4094
4095 return scrollability;
4096}
4097
4098void LocalFrameView::updateTiledBackingAdaptiveSizing()
4099{
4100 CheckedPtr tiledBacking = this->tiledBacking();
4101 if (!tiledBacking)
4102 return;
4103
4104 tiledBacking->setScrollability(computeScrollability());
4105}
4106
4107// FIXME: This shouldn't be called from outside; LocalFrameView should call it when the relevant viewports change.
4108void LocalFrameView::layoutOrVisualViewportChanged()
4109{
4110 if (m_frame->settings().visualViewportAPIEnabled()) {
4111 if (RefPtr window = m_frame->window())
4112 window->visualViewport().update();
4113
4114 if (RefPtr scrollingCoordinator = this->scrollingCoordinator())
4115 scrollingCoordinator->frameViewVisualViewportChanged(*this);
4116 }
4117
4118 if (RefPtr page = m_frame->page())
4119 page->chrome().client().frameViewLayoutOrVisualViewportChanged(*this);
4120}
4121
4122void LocalFrameView::unobscuredContentSizeChanged()
4123{
4124#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
4125 updateTiledBackingAdaptiveSizing();
4126#endif
4127}
4128
4129void LocalFrameView::loadProgressingStatusChanged()
4130{
4131 if (m_firstVisuallyNonEmptyLayoutMilestoneIsPending && m_frame->loader().isComplete())
4132 fireLayoutRelatedMilestonesIfNeeded();
4133 adjustTiledBackingCoverage();
4134}
4135
4136void LocalFrameView::adjustTiledBackingCoverage()
4137{
4138 if (!m_speculativeTilingEnabled)
4139 enableSpeculativeTilingIfNeeded();
4140
4141 CheckedPtr renderView = this->renderView();
4142 if (renderView && renderView->layer() && renderView->layer()->backing())
4143 renderView->layer()->backing()->adjustTiledBackingCoverage();
4144#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
4145 if (LegacyTileCache* tileCache = legacyTileCache())
4146 tileCache->setSpeculativeTileCreationEnabled(m_speculativeTilingEnabled);
4147#endif
4148}
4149
4150static bool shouldEnableSpeculativeTilingDuringLoading(const LocalFrameView& view)
4151{
4152 auto* page = view.frame().page();
4153 return page && view.isVisuallyNonEmpty() && !page->progress().isMainLoadProgressing();
4154}
4155
4156void LocalFrameView::enableSpeculativeTilingIfNeeded()
4157{
4158 ASSERT(!m_speculativeTilingEnabled)((void)0);
4159 if (wasScrolledByUser()) {
4160 m_speculativeTilingEnabled = true;
4161 return;
4162 }
4163 if (!shouldEnableSpeculativeTilingDuringLoading(*this))
4164 return;
4165
4166 if (m_speculativeTilingDelayDisabledForTesting) {
4167 speculativeTilingEnableTimerFired();
4168 return;
4169 }
4170
4171 if (m_speculativeTilingEnableTimer.isActive())
4172 return;
4173 // Delay enabling a bit as load completion may trigger further loading from scripts.
4174 static const Seconds speculativeTilingEnableDelay { 500_ms };
4175 m_speculativeTilingEnableTimer.startOneShot(speculativeTilingEnableDelay);
4176}
4177
4178void LocalFrameView::speculativeTilingEnableTimerFired()
4179{
4180 if (m_speculativeTilingEnabled)
4181 return;
4182 m_speculativeTilingEnabled = shouldEnableSpeculativeTilingDuringLoading(*this);
4183 adjustTiledBackingCoverage();
4184}
4185
4186void LocalFrameView::show()
4187{
4188 ScrollView::show();
4189
4190 if (m_frame->isMainFrame()) {
4191 // Turn off speculative tiling for a brief moment after a LocalFrameView appears on screen.
4192 // Note that adjustTiledBackingCoverage() kicks the (500ms) timer to re-enable it.
4193 m_speculativeTilingEnabled = false;
4194 m_lastUserScrollType = std::nullopt;
4195 adjustTiledBackingCoverage();
4196 }
4197}
4198
4199void LocalFrameView::hide()
4200{
4201 ScrollView::hide();
4202 adjustTiledBackingCoverage();
4203}
4204
4205bool LocalFrameView::needsLayout() const
4206{
4207 return layoutContext().needsLayout();
4208}
4209
4210void LocalFrameView::setNeedsLayoutAfterViewConfigurationChange()
4211{
4212 layoutContext().setNeedsLayoutAfterViewConfigurationChange();
4213}
4214
4215void LocalFrameView::setNeedsCompositingConfigurationUpdate()
4216{
4217 CheckedPtr renderView = this->renderView();
4218 if (renderView && renderView->usesCompositing()) {
4219 if (auto* rootLayer = renderView->layer())
4220 rootLayer->setNeedsCompositingConfigurationUpdate();
4221 renderView->compositor().scheduleCompositingLayerUpdate();
4222 }
4223}
4224
4225void LocalFrameView::setNeedsCompositingGeometryUpdate()
4226{
4227 CheckedPtr renderView = this->renderView();
4228 if (renderView->usesCompositing()) {
4229 if (auto* rootLayer = renderView->layer())
4230 rootLayer->setNeedsCompositingGeometryUpdate();
4231 renderView->compositor().scheduleCompositingLayerUpdate();
4232 }
4233}
4234
4235void LocalFrameView::setDescendantsNeedUpdateBackingAndHierarchyTraversal()
4236{
4237 CheckedPtr renderView = this->renderView();
4238 if (renderView->usesCompositing()) {
4239 if (auto* rootLayer = renderView->layer())
4240 rootLayer->setDescendantsNeedUpdateBackingAndHierarchyTraversal();
4241 renderView->compositor().scheduleCompositingLayerUpdate();
4242 }
4243}
4244
4245void LocalFrameView::scheduleSelectionUpdate()
4246{
4247 if (needsLayout())
4248 return;
4249 // FIXME: We should not need to go through the layout process since selection update does not change dimension/geometry.
4250 // 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.
4251 setNeedsLayoutAfterViewConfigurationChange();
4252}
4253
4254bool LocalFrameView::isTransparent() const
4255{
4256 return m_isTransparent;
4257}
4258
4259void LocalFrameView::setTransparent(bool isTransparent)
4260{
4261 if (m_isTransparent == isTransparent)
4262 return;
4263
4264 m_isTransparent = isTransparent;
4265
4266 // setTransparent can be called in the window between LocalFrameView initialization
4267 // and switching in the new Document; this means that the RenderView that we
4268 // retrieve is actually attached to the previous Document, which is going away,
4269 // and must not update compositing layers.
4270 if (!isViewForDocumentInFrame())
4271 return;
4272
4273 setNeedsLayoutAfterViewConfigurationChange();
4274 setNeedsCompositingConfigurationUpdate();
4275}
4276
4277bool LocalFrameView::hasOpaqueBackground() const
4278{
4279 return !m_isTransparent && m_baseBackgroundColor.isOpaque();
4280}
4281
4282Color LocalFrameView::baseBackgroundColor() const
4283{
4284 return m_baseBackgroundColor;
4285}
4286
4287void LocalFrameView::invalidateForBaseBackgroundOrColorSchemeChange()
4288{
4289 recalculateScrollbarOverlayStyle();
4290 setNeedsLayoutAfterViewConfigurationChange();
4291 setNeedsCompositingConfigurationUpdate();
4292}
4293
4294void LocalFrameView::setBaseBackgroundColor(const Color& backgroundColor)
4295{
4296 Color newBaseBackgroundColor = backgroundColor.isValid() ? backgroundColor : Color::white;
4297 if (m_baseBackgroundColor == newBaseBackgroundColor)
4298 return;
4299
4300#if ENABLE(DARK_MODE_CSS)(defined 1 && 1)
4301 m_styleColorOptions = styleColorOptions();
4302#endif
4303 m_baseBackgroundColor = newBaseBackgroundColor;
4304
4305 if (!isViewForDocumentInFrame())
4306 return;
4307
4308 invalidateForBaseBackgroundOrColorSchemeChange();
4309}
4310
4311#if ENABLE(DARK_MODE_CSS)(defined 1 && 1)
4312void LocalFrameView::updateBaseBackgroundColorIfNecessary()
4313{
4314 auto styleColorOptions = this->styleColorOptions();
4315 if (m_styleColorOptions == styleColorOptions)
4316 return;
4317
4318 m_styleColorOptions = styleColorOptions;
4319 std::optional<Color> backgroundColor;
4320 if (m_isTransparent)
4321 backgroundColor = Color(Color::transparentBlack);
4322
4323 updateBackgroundRecursively(backgroundColor);
4324}
4325#endif
4326
4327void LocalFrameView::updateBackgroundRecursively(const std::optional<Color>& backgroundColor)
4328{
4329 auto intrinsicBaseBackgroundColor = [](LocalFrameView& view) -> Color {
4330#if PLATFORM(COCOA)(defined 1 && 1)
4331 static const auto cssValueControlBackground = CSSValueAppleSystemControlBackground;
4332#else
4333 static const auto cssValueControlBackground = CSSValueWindow;
4334#endif
4335 return RenderTheme::singleton().systemColor(cssValueControlBackground, view.styleColorOptions());
4336 };
4337
4338 for (RefPtr<Frame> frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr())) {
4339 RefPtr localFrame = dynamicDowncast<LocalFrame>(frame);
4340 if (!localFrame)
4341 continue;
4342
4343 if (RefPtr view = localFrame->view()) {
4344 auto baseBackgroundColor = backgroundColor.value_or(intrinsicBaseBackgroundColor(*view));
4345 view->setTransparent(!baseBackgroundColor.isVisible());
4346 view->setBaseBackgroundColor(baseBackgroundColor);
4347 if (view->needsLayout())
4348 view->layoutContext().scheduleLayout();
4349 }
4350 }
4351}
4352
4353bool LocalFrameView::hasExtendedBackgroundRectForPainting() const
4354{
4355 CheckedPtr tiledBacking = this->tiledBacking();
4356 if (!tiledBacking)
4357 return false;
4358
4359 return tiledBacking->hasMargins();
4360}
4361
4362void LocalFrameView::updateExtendBackgroundIfNecessary()
4363{
4364 const auto mode = calculateExtendedBackgroundMode();
4365 if (mode.isEmpty())
4366 return;
4367
4368 updateTilesForExtendedBackgroundMode(mode);
4369}
4370
4371LocalFrameView::ExtendedBackgroundMode LocalFrameView::calculateExtendedBackgroundMode() const
4372{
4373#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
4374 // <rdar://problem/16201373>
4375 return { };
4376#else
4377 if (!m_frame->settings().backgroundShouldExtendBeyondPage())
4378 return { };
4379
4380 // Just because Settings::backgroundShouldExtendBeyondPage() is true does not necessarily mean
4381 // that the background rect needs to be extended for painting. Simple backgrounds can be extended
4382 // just with RenderLayerCompositor's rootExtendedBackgroundColor. More complicated backgrounds,
4383 // such as images, require extending the background rect to continue painting into the extended
4384 // region. This function finds out if it is necessary to extend the background rect for painting.
4385
4386 if (!m_frame->isMainFrame())
4387 return { };
4388
4389 RefPtr document = m_frame->document();
4390 if (!document)
4391 return { };
4392
4393 if (!renderView())
4394 return { };
4395
4396 auto* rootBackgroundRenderer = renderView()->rendererForRootBackground();
4397 if (!rootBackgroundRenderer)
4398 return { };
4399
4400 auto& backgroundLayers = rootBackgroundRenderer->style().backgroundLayers();
4401 if (!Style::hasImageInAnyLayer(backgroundLayers))
4402 return { };
4403
4404 BoxSideSet mode;
4405 auto backgroundRepeat = backgroundLayers.usedFirst().repeat();
4406 if (backgroundRepeat.x() == FillRepeat::Repeat) {
4407 mode.add(BoxSide::Left);
4408 mode.add(BoxSide::Right);
4409 }
4410 if (backgroundRepeat.y() == FillRepeat::Repeat) {
4411 mode.add(BoxSide::Top);
4412 mode.add(BoxSide::Bottom);
4413 }
4414
4415#if ENABLE(BANNER_VIEW_OVERLAYS)(defined ENABLE_BANNER_VIEW_OVERLAYS && ENABLE_BANNER_VIEW_OVERLAYS
)
4416 if (mode.contains(BoxSide::Top)) {
4417 if (RefPtr page = m_frame->page(); page && page->hasBannerViewOverlay())
4418 mode.remove(BoxSide::Top);
4419 }
4420#endif
4421
4422 return mode;
4423#endif
4424}
4425
4426void LocalFrameView::updateTilesForExtendedBackgroundMode(ExtendedBackgroundMode mode)
4427{
4428 CheckedPtr renderView = this->renderView();
4429 if (!renderView)
4430 return;
4431
4432 RenderLayerBacking* backing = renderView->layer()->backing();
4433 if (!backing)
4434 return;
4435
4436 TiledBacking* tiledBacking = backing->tiledBacking();
4437 if (!tiledBacking)
4438 return;
4439
4440 BoxSideSet existingMode;
4441 if (tiledBacking->topMarginHeight() > 0)
4442 existingMode.add(BoxSide::Top);
4443 if (tiledBacking->bottomMarginHeight() > 0)
4444 existingMode.add(BoxSide::Bottom);
4445 if (tiledBacking->leftMarginWidth() > 0)
4446 existingMode.add(BoxSide::Left);
4447 if (tiledBacking->rightMarginWidth() > 0)
4448 existingMode.add(BoxSide::Right);
4449
4450 if (existingMode == mode)
4451 return;
4452
4453 backing->setTiledBackingHasMargins(mode);
4454}
4455
4456IntRect LocalFrameView::extendedBackgroundRectForPainting() const
4457{
4458 CheckedPtr tiledBacking = this->tiledBacking();
4459 if (!tiledBacking)
4460 return IntRect();
4461
4462 CheckedPtr renderView = this->renderView();
4463 if (!renderView)
4464 return IntRect();
4465
4466 LayoutRect extendedRect = renderView->unextendedBackgroundRect();
4467 if (!tiledBacking->hasMargins())
4468 return snappedIntRect(extendedRect);
4469
4470 extendedRect.moveBy(LayoutPoint(-tiledBacking->leftMarginWidth(), -tiledBacking->topMarginHeight()));
4471 extendedRect.expand(LayoutSize(tiledBacking->leftMarginWidth() + tiledBacking->rightMarginWidth(), tiledBacking->topMarginHeight() + tiledBacking->bottomMarginHeight()));
4472 return snappedIntRect(extendedRect);
4473}
4474
4475bool LocalFrameView::shouldUpdateWhileOffscreen() const
4476{
4477 return m_shouldUpdateWhileOffscreen;
4478}
4479
4480void LocalFrameView::setShouldUpdateWhileOffscreen(bool shouldUpdateWhileOffscreen)
4481{
4482 m_shouldUpdateWhileOffscreen = shouldUpdateWhileOffscreen;
4483}
4484
4485bool LocalFrameView::shouldUpdate() const
4486{
4487 if (isOffscreen() && !shouldUpdateWhileOffscreen())
4488 return false;
4489 return true;
4490}
4491
4492bool LocalFrameView::safeToPropagateScrollToParent() const
4493{
4494 RefPtr document = m_frame->document();
4495 if (!document)
4496 return false;
4497
4498 RefPtr parentFrame = m_frame->tree().parent();
4499 if (!parentFrame)
4500 return false;
4501
4502 RefPtr localParent = dynamicDowncast<LocalFrame>(parentFrame);
4503 if (!localParent)
4504 return false;
4505
4506 RefPtr parentDocument = localParent->document();
4507 if (!parentDocument)
4508 return false;
4509
4510 return protect(document->securityOrigin())->isSameOriginDomain(protect(parentDocument->securityOrigin()));
4511}
4512
4513void LocalFrameView::scheduleScrollToAnchorAndTextFragment()
4514{
4515 RefPtr<ContainerNode> anchorNode = m_maintainScrollPositionAnchor;
4516 if (!anchorNode && !(m_pendingTextFragmentIndicatorRange && !m_delayedTextFragmentIndicatorTimer.isActive()))
4517 return;
4518
4519 m_scheduledMaintainScrollPositionAnchor = anchorNode;
4520
4521 if (m_scheduledToScrollToAnchor)
4522 return;
4523
4524 RefPtr document = m_frame->document();
4525 ASSERT(document)((void)0);
4526
4527 m_scheduledToScrollToAnchor = true;
4528 document->eventLoop().queueTask(TaskSource::DOMManipulation, [weakThis = WeakPtr { *this }] {
4529 RefPtr protectedThis = weakThis.get();
4530 if (!protectedThis)
4531 return;
4532 if (!protectedThis->m_maintainScrollPositionAnchor) {
4533 SetForScope scrollPositionAnchor(protectedThis->m_maintainScrollPositionAnchor, protectedThis->m_scheduledMaintainScrollPositionAnchor);
4534 protectedThis->scrollToAnchorAndTextFragmentNowIfNeeded();
4535 } else
4536 protectedThis->scrollToAnchorAndTextFragmentNowIfNeeded();
4537 protectedThis->m_scheduledMaintainScrollPositionAnchor = nullptr;
4538 });
4539}
4540
4541void LocalFrameView::scrollToAnchorAndTextFragmentNowIfNeeded()
4542{
4543 if (!m_scheduledToScrollToAnchor)
4544 return;
4545
4546 m_scheduledToScrollToAnchor = false;
4547 scrollToAnchor();
4548 scrollToPendingTextFragmentRange();
4549}
4550
4551void LocalFrameView::scrollToAnchor()
4552{
4553 RefPtr<ContainerNode> anchorNode = m_maintainScrollPositionAnchor;
4554 if (!anchorNode)
4555 return;
4556
4557 LOG_WITH_STREAM(Scrolling, stream << *this << " scrollToAnchor() " << anchorNode.get())((void)0);
4558
4559 if (!anchorNode->renderer())
4560 return;
4561
4562 cancelScheduledScrolls();
4563
4564 LayoutRect rect;
4565 bool insideFixed = false;
4566 if (anchorNode != m_frame->document() && anchorNode->renderer())
4567 rect = anchorNode->renderer()->absoluteAnchorRectWithScrollMargin(&insideFixed).marginRect;
4568
4569 LOG_WITH_STREAM(Scrolling, stream << " anchor node rect " << rect)((void)0);
4570
4571 // Scroll nested layers and frames to reveal the anchor.
4572 // Align to the top and to the closest side (this matches other browsers).
4573 if (anchorNode->renderer()->writingMode().isHorizontal())
4574 scrollRectToVisible(rect, *anchorNode->renderer(), insideFixed, { SelectionRevealMode::Reveal, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignTopAlways, ShouldAllowCrossOriginScrolling::No });
4575 else if (anchorNode->renderer()->writingMode().blockDirection() == FlowDirection::RightToLeft)
4576 scrollRectToVisible(rect, *anchorNode->renderer(), insideFixed, { SelectionRevealMode::Reveal, ScrollAlignment::alignRightAlways, ScrollAlignment::alignToEdgeIfNeeded, ShouldAllowCrossOriginScrolling::No });
4577 else
4578 scrollRectToVisible(rect, *anchorNode->renderer(), insideFixed, { SelectionRevealMode::Reveal, ScrollAlignment::alignLeftAlways, ScrollAlignment::alignToEdgeIfNeeded, ShouldAllowCrossOriginScrolling::No });
4579
4580 if (AXObjectCache* cache = m_frame->document()->existingAXObjectCache())
4581 cache->handleScrolledToAnchor(*anchorNode);
4582
4583 // scrollRectToVisible can call into setScrollPosition(), which resets m_maintainScrollPositionAnchor.
4584 LOG_WITH_STREAM(Scrolling, stream << " restoring anchor node to " << anchorNode.get())((void)0);
4585 m_maintainScrollPositionAnchor = anchorNode;
4586 cancelScheduledScrolls();
4587}
4588
4589void LocalFrameView::scrollToPendingTextFragmentRange()
4590{
4591 if (m_wasEverScrolledExplicitlyByUser)
4592 return;
4593
4594 if (!m_pendingTextFragmentIndicatorRange)
4595 return;
4596
4597 if (needsLayout())
4598 return;
4599
4600 auto range = *m_pendingTextFragmentIndicatorRange;
4601 auto rangeText = plainText(range);
4602 if (m_pendingTextFragmentIndicatorText != plainText(range))
4603 return;
4604
4605 LOG_WITH_STREAM(Scrolling, stream << *this << " scrollToPendingTextFragmentRange() " << range)((void)0);
4606
4607 if (!range.startContainer().renderer() || !range.endContainer().renderer())
4608 return;
4609
4610 ASSERT(m_frame->document())((void)0);
4611 Ref document = *m_frame->document();
4612
4613 SetForScope skipScrollResetOfScrollToTextFragmentRange(m_skipScrollResetOfScrollToTextFragmentRange, true);
4614
4615 // FIXME: <http://webkit.org/b/245262> (Scroll To Text Fragment should use DelegateMainFrameScroll)
4616 auto selectionChange = revealRangeWithTemporarySelection(range, TemporarySelectionOption::OnlyAllowForwardScrolling);
4617
4618 if (m_delayedTextFragmentIndicatorTimer.isActive())
4619 return;
4620
4621 if (!m_frame->settings().scrollToTextFragmentIndicatorEnabled() || m_frame->page()->isControlledByAutomation())
4622 return;
4623
4624 RefPtr textIndicator = TextIndicator::createWithRange(range, { TextIndicatorOption::DoNotClipToVisibleRect }, WebCore::TextIndicatorPresentationTransition::Bounce);
4625
4626 RefPtr page = m_frame->page();
4627
4628 if (!page)
4629 return;
4630
4631 if (textIndicator) {
4632 RefPtr localMainFrame = page->localMainFrame();
4633 if (!localMainFrame)
4634 return;
4635
4636 auto textRects = RenderObject::absoluteTextRects(range);
4637 if (!textRects.size())
4638 return;
4639
4640 static constexpr OptionSet hitType { HitTestRequest::Type::ReadOnly, HitTestRequest::Type::Active, HitTestRequest::Type::AllowVisibleChildFrameContentOnly };
4641 auto result = localMainFrame->eventHandler().hitTestResultAtPoint(LayoutPoint(textRects.first().center()), hitType);
4642 if (!intersects(range, *protect(result.targetNode())))
4643 return;
4644
4645 if (textRects.size() >= 2) {
4646 result = localMainFrame->eventHandler().hitTestResultAtPoint(LayoutPoint(textRects[1].center()), hitType);
4647 if (!intersects(range, *protect(result.targetNode())))
4648 return;
4649 }
4650
4651 if (textRects.size() >= 4) {
4652 result = localMainFrame->eventHandler().hitTestResultAtPoint(LayoutPoint(textRects.last().center()), hitType);
4653 if (!intersects(range, *protect(result.targetNode())))
4654 return;
4655 result = localMainFrame->eventHandler().hitTestResultAtPoint(LayoutPoint(textRects[textRects.size() - 2].center()), hitType);
4656 if (!intersects(range, *protect(result.targetNode())))
4657 return;
4658 }
4659 if (m_haveCreatedTextIndicator)
4660 document->page()->chrome().client().updateTextIndicator(WTF::move(textIndicator));
4661 else {
4662 document->page()->chrome().client().setTextIndicator(WTF::move(textIndicator));
4663 m_haveCreatedTextIndicator = true;
4664 }
4665 }
4666}
4667
4668void LocalFrameView::updateEmbeddedObject(const SingleThreadWeakPtr<RenderEmbeddedObject>& embeddedObject)
4669{
4670 // The to-be-updated renderer is passed in as WeakPtr for convenience, but it will be alive at this point.
4671 ASSERT(embeddedObject)((void)0);
4672
4673 // No need to update if it's already crashed or known to be missing.
4674 if (embeddedObject->isPluginUnavailable())
4675 return;
4676
4677 if (RefPtr embedOrObject = dynamicDowncast<HTMLPlugInElement>(embeddedObject->frameOwnerElement())) {
4678 if (embedOrObject->needsWidgetUpdate())
4679 embedOrObject->updateWidget(CreatePlugins::Yes);
4680 } else
4681 ASSERT_NOT_REACHED()((void)0);
4682
4683 // It's possible the renderer was destroyed below updateWidget() since loading a plugin may execute arbitrary JavaScript.
4684 if (!embeddedObject)
4685 return;
4686
4687 auto ignoreWidgetState = embeddedObject->updateWidgetPosition();
4688 UNUSED_PARAM(ignoreWidgetState)(void)ignoreWidgetState;
4689}
4690
4691bool LocalFrameView::updateEmbeddedObjects()
4692{
4693 SetForScope inUpdateEmbeddedObjects(m_inUpdateEmbeddedObjects, true);
4694 if (layoutContext().isLayoutNested() || !m_embeddedObjectsToUpdate || m_embeddedObjectsToUpdate->isEmpty())
4695 return true;
4696
4697 WidgetHierarchyUpdatesSuspensionScope suspendWidgetHierarchyUpdates;
4698
4699 // Make sure we process at most numberOfObjectsToUpdate objects to avoid
4700 // potential infinite loops if objects get added to m_embeddedObjectsToUpdate
4701 // during the execution of this loop.
4702 unsigned numberOfObjectsToUpdate = m_embeddedObjectsToUpdate->size();
4703 for (unsigned i = 0; i < numberOfObjectsToUpdate; ++i) {
4704 if (m_embeddedObjectsToUpdate->isEmpty())
4705 break;
4706 WeakPtr embeddedObject = m_embeddedObjectsToUpdate->takeFirst().get();
4707 updateEmbeddedObject(embeddedObject);
4708 }
4709
4710 return m_embeddedObjectsToUpdate->isEmpty();
4711}
4712
4713void LocalFrameView::updateEmbeddedObjectsTimerFired()
4714{
4715 m_updateEmbeddedObjectsTimer.stop();
4716 for (unsigned i = 0; i < maxUpdateEmbeddedObjectsIterations; ++i) {
4717 if (updateEmbeddedObjects())
4718 break;
4719 }
4720}
4721
4722void LocalFrameView::flushAnyPendingPostLayoutTasks()
4723{
4724 layoutContext().flushPostLayoutTasks();
4725 if (m_updateEmbeddedObjectsTimer.isActive())
4726 updateEmbeddedObjectsTimerFired();
4727}
4728
4729void LocalFrameView::performPostLayoutTasks()
4730{
4731 ScriptDisallowedScope::InMainThread scriptDisallowedScope;
4732
4733 // FIXME: We should not run any JavaScript code in this function.
4734 LOG(Layout, "LocalFrameView %p performPostLayoutTasks", this)((void)0);
4735 updateHasReachedSignificantRenderedTextThreshold();
4736
4737 if (!layoutContext().isLayoutNested() && m_frame->document()->documentElement())
4738 fireLayoutRelatedMilestonesIfNeeded();
4739
4740#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
4741 // Only send layout-related delegate callbacks synchronously for the main frame to
4742 // avoid re-entering layout for the main frame while delivering a layout-related delegate
4743 // callback for a subframe.
4744 if (m_frame->isMainFrame()) {
4745 if (Page* page = m_frame->page())
4746 page->chrome().client().didLayout();
4747 }
4748#endif
4749
4750 // FIXME: We should consider adding DidLayout as a LayoutMilestone. That would let us merge this
4751 // with didLayout(LayoutMilestones).
4752 m_frame->loader().client().dispatchDidLayout();
4753
4754 updateWidgetPositions();
4755
4756 updateSnapOffsets();
4757
4758 m_updateEmbeddedObjectsTimer.startOneShot(0_s);
4759
4760 if (RefPtr scrollingCoordinator = this->scrollingCoordinator())
4761 scrollingCoordinator->frameViewLayoutUpdated(*this);
4762
4763 if (CheckedPtr renderView = this->renderView()) {
4764 if (renderView->usesCompositing())
4765 renderView->compositor().frameViewDidLayout();
4766 }
4767
4768 scheduleScrollToAnchorAndTextFragment();
4769
4770 scheduleResizeEventIfNeeded();
4771
4772 updateLayoutViewport();
4773 viewportContentsChanged();
4774
4775 if (!layoutContext().isLayoutNested())
4776 adjustScrollAnchoringPositionForScrollableAreas();
4777
4778 resnapAfterLayout();
4779
4780 m_frame->document()->scheduleDeferredAXObjectCacheUpdate();
4781
4782 RefPtr document = m_frame->document();
4783 if (document && !document->quirks().needsSuppressPostLayoutBoundaryEventsQuirk())
4784 m_frame->eventHandler().scheduleMouseEventTargetUpdateAfterLayout();
4785}
4786
4787void LocalFrameView::addScrollableAreaForScrollAnchoring(ScrollableArea& scrollableArea)
4788{
4789 if (!m_anchoringScrollableAreas)
4790 m_anchoringScrollableAreas = makeUnique<ScrollableAreaSet>();
4791
4792 m_anchoringScrollableAreas->add(scrollableArea);
4793}
4794
4795void LocalFrameView::removeScrollableAreaForScrollAnchoring(ScrollableArea& scrollableArea)
4796{
4797 if (m_anchoringScrollableAreas)
4798 m_anchoringScrollableAreas->remove(scrollableArea);
4799}
4800
4801void LocalFrameView::dequeueScrollableAreaForScrollAnchoringUpdate(ScrollableArea& scrollableArea)
4802{
4803 m_scrollableAreasWithScrollAnchoringControllersNeedingUpdate.remove(scrollableArea);
4804}
4805
4806void LocalFrameView::queueScrollableAreaForScrollAnchoringUpdate(ScrollableArea& scrollableArea)
4807{
4808 m_scrollableAreasWithScrollAnchoringControllersNeedingUpdate.add(scrollableArea);
4809}
4810
4811void LocalFrameView::clearScrollAnchorsInScrollableAreas()
4812{
4813 clearScrollAnchor();
4814 if (!m_anchoringScrollableAreas)
4815 return;
4816
4817 for (CheckedRef scrollableArea : *m_anchoringScrollableAreas)
4818 scrollableArea->clearScrollAnchor();
4819}
4820
4821void LocalFrameView::updateScrollAnchoringBeforeLayoutForScrollableAreas()
4822{
4823 if (CheckedPtr controller = scrollAnchoringController())
4824 controller->updateBeforeLayout();
4825
4826 if (!m_anchoringScrollableAreas)
4827 return;
4828
4829 for (CheckedRef scrollableArea : *m_anchoringScrollableAreas) {
4830 if (CheckedPtr controller = scrollableArea->scrollAnchoringController())
4831 controller->updateBeforeLayout();
4832 }
4833}
4834
4835void LocalFrameView::adjustScrollAnchoringPositionForScrollableAreas()
4836{
4837 auto scrollableAreasNeedingUpdate = std::exchange(m_scrollableAreasWithScrollAnchoringControllersNeedingUpdate, { });
4838 for (auto& scrollableArea : scrollableAreasNeedingUpdate)
4839 scrollableArea.adjustScrollAnchoringPosition();
4840}
4841
4842void LocalFrameView::updateAnchorPositionedAfterScroll()
4843{
4844 if (CheckedPtr view = renderView())
4845 Style::AnchorPositionEvaluator::updateScrollAdjustments(*view);
4846}
4847
4848IntSize LocalFrameView::sizeForResizeEvent() const
4849{
4850#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
4851 if (m_customSizeForResizeEvent)
4852 return *m_customSizeForResizeEvent;
4853#endif
4854 if (useFixedLayout() && !fixedLayoutSize().isEmpty() && delegatesScrolling())
4855 return fixedLayoutSize();
4856 return visibleContentRectIncludingScrollbars().size();
4857}
4858
4859void LocalFrameView::scheduleResizeEventIfNeeded()
4860{
4861 if (layoutContext().isInRenderTreeLayout() || needsLayout())
4862 return;
4863
4864 CheckedPtr renderView = this->renderView();
4865 if (!renderView || renderView->printing())
4866 return;
4867
4868 RefPtr page = m_frame->page();
4869 if (page && page->chrome().client().isSVGImageChromeClient())
4870 return;
4871
4872 IntSize currentSize = sizeForResizeEvent();
4873 float currentZoomFactor = renderView->style().usedZoom();
4874
4875 if (currentSize == m_lastViewportSize && currentZoomFactor == m_lastUsedZoomFactor)
4876 return;
4877
4878 m_lastViewportSize = currentSize;
4879 m_lastUsedZoomFactor = currentZoomFactor;
4880
4881 if (!layoutContext().didFirstLayout())
4882 return;
4883
4884#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
4885 // Don't send the resize event if the document is loading. Some pages automatically reload
4886 // when the window is resized; Safari on iOS often resizes the window while setting up its
4887 // viewport. This obviously can cause problems.
4888 if (RefPtr documentLoader = m_frame->loader().documentLoader()) {
4889 if (documentLoader->isLoadingInAPISense())
4890 return;
4891 }
4892#endif
4893
4894 RefPtr document = m_frame->document();
4895 if (document->quirks().shouldSilenceWindowResizeEventsDuringApplicationSnapshotting()) {
4896 document->addConsoleMessage(MessageSource::Other, MessageLevel::Info, "Window resize events silenced due to: http://webkit.org/b/258597"_s);
4897 FRAMEVIEW_RELEASE_LOG(Events, LocalFrameViewFiringResizeEventsDisabledForPage)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_LOG48"
) = "[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
;
4898 return;
4899 }
4900
4901 // TODO: move this to a method called for all scrollable areas
4902 clearScrollAnchor();
4903
4904 LOG_WITH_STREAM(Events, stream << "LocalFrameView " << this << " scheduleResizeEventIfNeeded scheduling resize event for document" << document << ", size " << currentSize)((void)0);
4905 document->setNeedsDOMWindowResizeEvent();
4906
4907 bool isMainFrame = m_frame->isMainFrame();
4908 if (InspectorInstrumentation::hasFrontends() && isMainFrame) {
4909 if (InspectorBackendClient* inspectorBackendClient = page ? page->inspectorController().inspectorBackendClient() : nullptr)
4910 inspectorBackendClient->didResizeMainFrame(m_frame.ptr());
4911 }
4912}
4913
4914void LocalFrameView::willStartLiveResize()
4915{
4916 ScrollView::willStartLiveResize();
4917 adjustTiledBackingCoverage();
4918}
4919
4920void LocalFrameView::willEndLiveResize()
4921{
4922 ScrollView::willEndLiveResize();
4923 adjustTiledBackingCoverage();
4924}
4925
4926void LocalFrameView::autoSizeIfEnabled()
4927{
4928 if (!m_shouldAutoSize)
4929 return;
4930
4931 if (m_inAutoSize)
4932 return;
4933
4934 RefPtr document = m_frame->document();
4935 if (!document)
4936 return;
4937
4938 CheckedPtr renderView = document->renderView();
4939 if (!renderView)
4940 return;
4941
4942 CheckedPtr firstChild = renderView->firstChild();
4943 if (!firstChild)
4944 return;
4945
4946 SetForScope changeInAutoSize(m_inAutoSize, true);
4947 if (layoutContext().subtreeLayoutRoot())
4948 layoutContext().convertSubtreeLayoutToFullLayout();
4949
4950 switch (m_autoSizeMode) {
4951 case AutoSizeMode::SizeToContent:
4952 performSizeToContentAutoSize();
4953 break;
4954 case AutoSizeMode::FixedWidth:
4955 performFixedWidthAutoSize();
4956 break;
4957 }
4958
4959 if (RefPtr page = m_frame->page(); page && m_frame->isMainFrame())
4960 page->chrome().client().intrinsicContentsSizeChanged(m_autoSizeContentSize);
4961
4962 m_didRunAutosize = true;
4963}
4964
4965void LocalFrameView::performFixedWidthAutoSize()
4966{
4967 LOG(Layout, "LocalFrameView %p performFixedWidthAutoSize", this)((void)0);
4968
4969 RefPtr document = m_frame->document();
4970 CheckedPtr renderView = document->renderView();
4971 CheckedPtr firstChild = renderView->firstChild();
4972
4973 auto horizonalScrollbarMode = ScrollbarMode::AlwaysOff;
4974 auto verticalScrollbarMode = ScrollbarMode::AlwaysOff;
4975 setVerticalScrollbarLock(false);
4976 setHorizontalScrollbarLock(false);
4977 setScrollbarModes(horizonalScrollbarMode, verticalScrollbarMode, true, true);
4978
4979 ASSERT(is<RenderElement>(*firstChild))((void)0);
4980 CheckedRef documentRenderer = downcast<RenderElement>(*firstChild);
4981 documentRenderer->mutableStyle().setMaxWidth(Style::MaximumSize::Fixed { static_cast<float>(m_autoSizeConstraint.width()) });
4982 resize(m_autoSizeConstraint.width(), m_autoSizeConstraint.height());
4983
4984 Ref<LocalFrameView> protectedThis(*this);
4985 document->updateLayoutIgnorePendingStylesheets();
4986 // While the final content size could slightly be different after the next resize/layout (see below), we intentionally save and report
4987 // the current value to avoid unstable layout (e.g. content "height: 100%").
4988 // See also webkit.org/b/173561
4989 m_autoSizeContentSize = contentsSize();
4990
4991 auto finalWidth = std::max(m_autoSizeConstraint.width(), m_autoSizeContentSize.width());
4992 auto finalHeight = m_autoSizeFixedMinimumHeight ? std::max(m_autoSizeFixedMinimumHeight, m_autoSizeContentSize.height()) : m_autoSizeContentSize.height();
4993 resize(finalWidth, finalHeight);
4994 document->updateLayoutIgnorePendingStylesheets();
4995}
4996
4997void LocalFrameView::performSizeToContentAutoSize()
4998{
4999 // Do the resizing twice. The first time is basically a rough calculation using the preferred width
5000 // which may result in a height change during the second iteration.
5001 // Let's ignore renderers with viewport units first and resolve these boxes during the second phase of the autosizing.
5002 LOG(Layout, "LocalFrameView %p performSizeToContentAutoSize", this)((void)0);
5003 ASSERT(m_frame->document() && m_frame->document()->renderView())((void)0);
5004
5005 RefPtr document = m_frame->document();
5006 CheckedRef renderView = *document->renderView();
5007 CheckedPtr documentRenderer = dynamicDowncast<RenderBox>(renderView->firstChild());
5008 if (!documentRenderer) {
5009 ASSERT_NOT_REACHED()((void)0);
5010 return;
5011 }
5012
5013 auto layoutWithAdjustedStyleIfNeeded = [&] {
5014 document->updateStyleIfNeeded();
5015 CheckedRef style = documentRenderer->mutableStyle();
5016 if (style->logicalHeight().isPercent()) {
5017 // Percent height values on the document renderer when we don't really have a proper viewport size can
5018 // result incorrect rendering in certain layout contexts (e.g flex).
5019 style->setLogicalHeight(CSS::Keyword::Auto { });
5020 }
5021 document->updateLayout();
5022 };
5023
5024 resetOverriddenWidthForCSSDefaultViewportUnits();
5025 resetOverriddenWidthForCSSSmallViewportUnits();
5026 resetOverriddenWidthForCSSLargeViewportUnits();
5027
5028 // Start from the minimum size and allow it to grow.
5029 auto minAutoSize = IntSize { 1, 1 };
5030 resize(minAutoSize);
5031 auto size = frameRect().size();
5032 for (int i = 0; i < 2; i++) {
5033 layoutWithAdjustedStyleIfNeeded();
5034 // Update various sizes including contentsSize, scrollHeight, etc.
5035 auto newSize = IntSize { documentRenderer->minPreferredLogicalWidth(), renderView->documentRect().height() };
5036
5037 // Check to see if a scrollbar is needed for a given dimension and
5038 // if so, increase the other dimension to account for the scrollbar.
5039 // Since the dimensions are only for the view rectangle, once a
5040 // dimension exceeds the maximum, there is no need to increase it further.
5041 if (newSize.width() > m_autoSizeConstraint.width()) {
5042 RefPtr<Scrollbar> localHorizontalScrollbar = horizontalScrollbar();
5043 if (!localHorizontalScrollbar)
5044 localHorizontalScrollbar = createScrollbar(ScrollbarOrientation::Horizontal);
5045 newSize.expand(0, localHorizontalScrollbar->occupiedHeight());
5046 // Don't bother checking for a vertical scrollbar because the width is at
5047 // already greater the maximum.
5048 } else if (newSize.height() > m_autoSizeConstraint.height()) {
5049 RefPtr<Scrollbar> localVerticalScrollbar = verticalScrollbar();
5050 if (!localVerticalScrollbar)
5051 localVerticalScrollbar = createScrollbar(ScrollbarOrientation::Vertical);
5052 newSize.expand(localVerticalScrollbar->occupiedWidth(), 0);
5053 // Don't bother checking for a horizontal scrollbar because the height is
5054 // already greater the maximum.
5055 }
5056
5057 // Ensure the size is at least the min bounds.
5058 newSize = newSize.expandedTo(minAutoSize);
5059
5060 // Bound the dimensions by the max bounds and determine what scrollbars to show.
5061 ScrollbarMode horizonalScrollbarMode = ScrollbarMode::AlwaysOff;
5062 if (newSize.width() > m_autoSizeConstraint.width()) {
5063 newSize.setWidth(m_autoSizeConstraint.width());
5064 horizonalScrollbarMode = ScrollbarMode::AlwaysOn;
5065 }
5066 ScrollbarMode verticalScrollbarMode = ScrollbarMode::AlwaysOff;
5067 if (newSize.height() > m_autoSizeConstraint.height()) {
5068 newSize.setHeight(m_autoSizeConstraint.height());
5069 verticalScrollbarMode = ScrollbarMode::AlwaysOn;
5070 }
5071
5072 if (newSize == size)
5073 continue;
5074
5075 // While loading only allow the size to increase (to avoid twitching during intermediate smaller states)
5076 // unless autoresize has just been turned on or the maximum size is smaller than the current size.
5077 if (m_didRunAutosize && size.height() <= m_autoSizeConstraint.height() && size.width() <= m_autoSizeConstraint.width()
5078 && !m_frame->loader().isComplete() && (newSize.height() < size.height() || newSize.width() < size.width()))
5079 break;
5080
5081 // The first time around, resize to the minimum height again; otherwise,
5082 // on pages (e.g. quirks mode) where the body/document resize to the view size,
5083 // we'll end up not shrinking back down after resizing to the computed preferred width.
5084 resize(newSize.width(), i ? newSize.height() : minAutoSize.height());
5085 // Protect the content from evergrowing layout.
5086 auto preferredViewportWidth = std::min(newSize.width(), m_autoSizeConstraint.width());
5087 overrideWidthForCSSDefaultViewportUnits(preferredViewportWidth);
5088 overrideWidthForCSSSmallViewportUnits(preferredViewportWidth);
5089 overrideWidthForCSSLargeViewportUnits(preferredViewportWidth);
5090 // Force the scrollbar state to avoid the scrollbar code adding them and causing them to be needed. For example,
5091 // a vertical scrollbar may cause text to wrap and thus increase the height (which is the only reason the scollbar is needed).
5092 setVerticalScrollbarLock(false);
5093 setHorizontalScrollbarLock(false);
5094 setScrollbarModes(horizonalScrollbarMode, verticalScrollbarMode, true, true);
5095 }
5096 // All the resizing above may have invalidated style (for example if viewport units are being used).
5097 // FIXME: Use the final layout's result as the content size (webkit.org/b/173561).
5098 layoutWithAdjustedStyleIfNeeded();
5099 m_autoSizeContentSize = contentsSize();
5100}
5101
5102void LocalFrameView::setAutoSizeFixedMinimumHeight(int fixedMinimumHeight)
5103{
5104 if (m_autoSizeFixedMinimumHeight == fixedMinimumHeight)
5105 return;
5106
5107 m_autoSizeFixedMinimumHeight = fixedMinimumHeight;
5108
5109 setNeedsLayoutAfterViewConfigurationChange();
5110}
5111
5112RenderElement* LocalFrameView::viewportRenderer() const
5113{
5114 if (m_viewportRendererType == ViewportRendererType::None)
5115 return nullptr;
5116
5117 auto* document = m_frame->document();
5118 if (!document)
5119 return nullptr;
5120
5121 if (m_viewportRendererType == ViewportRendererType::Document) {
5122 auto* documentElement = document->documentElement();
5123 if (!documentElement)
5124 return nullptr;
5125 return documentElement->renderer();
5126 }
5127
5128 if (m_viewportRendererType == ViewportRendererType::Body) {
5129 auto* body = document->body();
5130 if (!body)
5131 return nullptr;
5132 return body->renderer();
5133 }
5134
5135 ASSERT_NOT_REACHED()((void)0);
5136 return nullptr;
5137}
5138
5139const Pagination& LocalFrameView::pagination() const
5140{
5141 if (m_pagination != Pagination())
5142 return m_pagination;
5143
5144 if (m_frame->isMainFrame()) {
5145 if (Page* page = m_frame->page())
5146 return page->pagination();
5147 }
5148
5149 return m_pagination;
5150}
5151
5152void LocalFrameView::setPagination(const Pagination& pagination)
5153{
5154 if (m_pagination == pagination)
5155 return;
5156
5157 m_pagination = pagination;
5158
5159 m_frame->document()->styleScope().didChangeStyleSheetEnvironment();
5160}
5161
5162IntRect LocalFrameView::windowClipRect() const
5163{
5164 ASSERT(m_frame->view() == this)((void)0);
5165
5166 if (m_cachedWindowClipRect)
5167 return *m_cachedWindowClipRect;
5168
5169 if (paintsEntireContents())
5170 return contentsToWindow(IntRect(IntPoint(), totalContentsSize()));
5171
5172 // Set our clip rect to be our contents.
5173 IntRect clipRect = contentsToWindow(visibleContentRect(LegacyIOSDocumentVisibleRect));
5174
5175 if (!m_frame->ownerElement())
5176 return clipRect;
5177
5178 // Take our owner element and get its clip rect.
5179 RefPtr ownerElement = m_frame->ownerElement();
5180 if (RefPtr parentView = ownerElement->document().view())
5181 clipRect.intersect(parentView->windowClipRectForFrameOwner(ownerElement.get(), true));
5182 return clipRect;
5183}
5184
5185IntRect LocalFrameView::windowClipRectForFrameOwner(const HTMLFrameOwnerElement* ownerElement, bool clipToLayerContents) const
5186{
5187 // The renderer can sometimes be null when style="display:none" interacts
5188 // with external content and plugins.
5189 if (!ownerElement->renderer())
5190 return windowClipRect();
5191
5192 // If we have no layer, just return our window clip rect.
5193 CheckedPtr enclosingLayer = ownerElement->renderer()->enclosingLayer();
5194 if (!enclosingLayer)
5195 return windowClipRect();
5196
5197 // Apply the clip from the layer.
5198 IntRect clipRect;
5199 if (clipToLayerContents)
5200 clipRect = snappedIntRect(enclosingLayer->childrenClipRect());
5201 else
5202 clipRect = snappedIntRect(enclosingLayer->selfClipRect());
5203 clipRect = contentsToWindow(clipRect);
5204 return intersection(clipRect, windowClipRect());
5205}
5206
5207void LocalFrameView::scrollTo(const ScrollPosition& newPosition)
5208{
5209 IntPoint oldPosition = scrollPosition();
5210 ScrollView::scrollTo(newPosition);
5211 if (oldPosition != scrollPosition())
5212 scrollPositionChanged(oldPosition, scrollPosition());
5213
5214 didChangeScrollOffset();
5215}
5216
5217float LocalFrameView::adjustVerticalPageScrollStepForFixedContent(float step)
5218{
5219 TrackedRendererListHashSet* viewPositionedOutOfFlowBoxes = { };
5220 if (CheckedPtr root = m_frame->contentRenderer()) {
5221 if (!root->hasOutOfFlowBoxes())
5222 return step;
5223 viewPositionedOutOfFlowBoxes = root->outOfFlowBoxes();
5224 }
5225
5226 FloatRect unobscuredContentRect = this->unobscuredContentRect();
5227 float topObscuredArea = 0;
5228 float bottomObscuredArea = 0;
5229 for (CheckedRef viewPositionedOutOfFlowBox : *viewPositionedOutOfFlowBoxes) {
5230 CheckedRef style = viewPositionedOutOfFlowBox->style();
5231 if (style->position() != PositionType::Fixed || style->usedVisibility() == Visibility::Hidden || style->opacity().isTransparent())
5232 continue;
5233
5234 FloatQuad contentQuad = viewPositionedOutOfFlowBox->absoluteContentQuad();
5235 if (!contentQuad.isRectilinear())
5236 continue;
5237
5238 FloatRect contentBoundingBox = contentQuad.boundingBox();
5239 FloatRect fixedRectInView = intersection(unobscuredContentRect, contentBoundingBox);
5240
5241 if (fixedRectInView.width() < unobscuredContentRect.width())
5242 continue;
5243
5244 if (fixedRectInView.y() == unobscuredContentRect.y())
5245 topObscuredArea = std::max(topObscuredArea, fixedRectInView.height());
5246 else if (fixedRectInView.maxY() == unobscuredContentRect.maxY())
5247 bottomObscuredArea = std::max(bottomObscuredArea, fixedRectInView.height());
5248 }
5249
5250 return Scrollbar::pageStep(unobscuredContentRect.height(), unobscuredContentRect.height() - topObscuredArea - bottomObscuredArea);
5251}
5252
5253void LocalFrameView::invalidateScrollbarRect(Scrollbar& scrollbar, const IntRect& rect)
5254{
5255 // Add in our offset within the LocalFrameView.
5256 IntRect dirtyRect = rect;
5257 dirtyRect.moveBy(scrollbar.location());
5258 invalidateRect(dirtyRect);
5259}
5260
5261void LocalFrameView::setVisibleScrollerThumbRect(const IntRect& scrollerThumb)
5262{
5263 if (!m_frame->isMainFrame())
5264 return;
5265
5266 if (RefPtr page = m_frame->page())
5267 page->chrome().client().notifyScrollerThumbIsVisibleInRect(scrollerThumb);
5268}
5269
5270bool LocalFrameView::isScrollable(Scrollability definitionOfScrollable)
5271{
5272 // Check for:
5273 // 1) If there an actual overflow.
5274 // 2) display:none or visibility:hidden set to self or inherited.
5275 // 3) overflow{-x,-y}: hidden;
5276 // 4) scrolling: no;
5277 if (!didFirstLayout())
5278 return false;
5279
5280#if HAVE(RUBBER_BANDING)(defined 1 && 1)
5281 bool requiresActualOverflowToBeConsideredScrollable = !m_frame->isMainFrame() || definitionOfScrollable != Scrollability::ScrollableOrRubberbandable;
5282#else
5283 UNUSED_PARAM(definitionOfScrollable)(void)definitionOfScrollable;
5284 bool requiresActualOverflowToBeConsideredScrollable = true;
5285#endif
5286
5287 // Covers #1
5288 if (requiresActualOverflowToBeConsideredScrollable) {
5289 IntSize totalContentsSize = this->totalContentsSize();
5290 IntSize visibleContentSize = visibleContentRect(LegacyIOSDocumentVisibleRect).size();
5291 if (totalContentsSize.height() <= visibleContentSize.height() && totalContentsSize.width() <= visibleContentSize.width())
5292 return false;
5293 }
5294
5295 // Covers #2.
5296 RefPtr owner = m_frame->ownerElement();
5297 if (owner && (!owner->renderer() || !owner->renderer()->visibleToHitTesting()))
5298 return false;
5299
5300 // Cover #3 and #4.
5301 ScrollbarMode horizontalMode;
5302 ScrollbarMode verticalMode;
5303 calculateScrollbarModesForLayout(horizontalMode, verticalMode, RulesFromWebContentOnly);
5304 if (horizontalMode == ScrollbarMode::AlwaysOff && verticalMode == ScrollbarMode::AlwaysOff)
5305 return false;
5306
5307 return true;
5308}
5309
5310bool LocalFrameView::isScrollableOrRubberbandable()
5311{
5312 return isScrollable(Scrollability::ScrollableOrRubberbandable);
5313}
5314
5315bool LocalFrameView::hasScrollableOrRubberbandableAncestor()
5316{
5317 if (m_frame->isMainFrame())
5318 return isScrollableOrRubberbandable();
5319
5320 for (RefPtr parent = this->parentFrameView(); parent; parent = parent->parentFrameView()) {
5321 Scrollability frameScrollability = parent->m_frame->isMainFrame() ? Scrollability::ScrollableOrRubberbandable : Scrollability::Scrollable;
5322 if (parent->isScrollable(frameScrollability))
5323 return true;
5324 }
5325
5326 return false;
5327}
5328
5329void LocalFrameView::updateScrollableAreaSet()
5330{
5331 // That ensures that only inner frames are cached.
5332 RefPtr parentFrameView = this->parentFrameView();
5333 if (!parentFrameView)
5334 return;
5335
5336 if (!isScrollable()) {
5337 parentFrameView->removeScrollableArea(this);
5338 return;
5339 }
5340
5341 parentFrameView->addScrollableArea(this);
5342}
5343
5344bool LocalFrameView::shouldSuspendScrollAnimations() const
5345{
5346 return m_frame->loader().state() != FrameState::Complete;
5347}
5348
5349void LocalFrameView::notifyAllFramesThatContentAreaWillPaint() const
5350{
5351 notifyScrollableAreasThatContentAreaWillPaint();
5352
5353 for (RefPtr child = m_frame->tree().firstRenderedChild(); child; child = child->tree().traverseNextRendered(m_frame.ptr())) {
5354 RefPtr localChild = dynamicDowncast<LocalFrame>(child);
5355 if (!localChild)
5356 continue;
5357 if (RefPtr frameView = localChild->view())
5358 frameView->notifyScrollableAreasThatContentAreaWillPaint();
5359 }
5360}
5361
5362void LocalFrameView::notifyScrollableAreasThatContentAreaWillPaint() const
5363{
5364 RefPtr page = m_frame->page();
5365 if (!page)
5366 return;
5367
5368 contentAreaWillPaint();
5369
5370 if (!m_scrollableAreas)
5371 return;
5372
5373 for (CheckedRef area : *m_scrollableAreas) {
5374 CheckedPtr<ScrollableArea> scrollableArea(area);
5375 // ScrollView ScrollableAreas will be handled via the Frame tree traversal above.
5376 if (!is<ScrollView>(scrollableArea))
5377 scrollableArea->contentAreaWillPaint();
5378 }
5379}
5380
5381void LocalFrameView::updateScrollCorner()
5382{
5383 CheckedPtr<RenderElement> renderer;
5384 std::unique_ptr<RenderStyle> cornerStyle;
5385 IntRect cornerRect = scrollCornerRect();
5386 RefPtr doc = m_frame->document();
5387
5388 auto usesStandardScrollbarStyle = [](auto& doc) {
5389 if (!doc || !doc->documentElement())
5390 return false;
5391 if (!doc->documentElement()->renderer())
5392 return false;
5393
5394 return doc->documentElement()->renderer()->style().usesStandardScrollbarStyle();
5395 };
5396
5397 if (!(usesStandardScrollbarStyle(doc) || cornerRect.isEmpty())) {
5398 // Try the <body> element first as a scroll corner source.
5399 RefPtr body = doc ? doc->bodyOrFrameset() : nullptr;
5400 if (body && body->renderer()) {
5401 renderer = body->renderer();
5402 cornerStyle = renderer->getUncachedPseudoStyle({ PseudoElementType::WebKitScrollbarCorner }, &renderer->style());
5403 }
5404
5405 if (!cornerStyle) {
5406 // If the <body> didn't have a custom style, then the root element might.
5407 RefPtr docElement = doc ? doc->documentElement() : nullptr;
5408 if (docElement && docElement->renderer()) {
5409 renderer = docElement->renderer();
5410 cornerStyle = renderer->getUncachedPseudoStyle({ PseudoElementType::WebKitScrollbarCorner }, &renderer->style());
5411 }
5412 }
5413
5414 if (!cornerStyle) {
5415 // If we have an owning iframe/frame element, then it can set the custom scrollbar also.
5416 // FIXME: Seems wrong to do this for cross-origin frames.
5417 if (RefPtr renderer = m_frame->ownerRenderer())
5418 cornerStyle = renderer->getUncachedPseudoStyle({ PseudoElementType::WebKitScrollbarCorner }, &renderer->style());
5419 }
5420 }
5421
5422 if (!cornerStyle || !renderer)
5423 m_scrollCorner = nullptr;
5424 else {
5425 if (!m_scrollCorner) {
5426 m_scrollCorner = createRenderer<RenderScrollbarPart>(protect(renderer->document()), WTF::move(*cornerStyle));
5427 m_scrollCorner->initializeStyle();
5428 } else
5429 m_scrollCorner->setStyle(WTF::move(*cornerStyle));
5430 invalidateScrollCorner(cornerRect);
5431 }
5432}
5433
5434void LocalFrameView::paintScrollCorner(GraphicsContext& context, const IntRect& cornerRect)
5435{
5436 if (context.invalidatingControlTints()) {
5437 updateScrollCorner();
5438 return;
5439 }
5440
5441 if (m_scrollCorner) {
5442 if (m_frame->isMainFrame())
5443 context.fillRect(cornerRect, baseBackgroundColor());
5444 m_scrollCorner->paintIntoRect(context, cornerRect.location(), cornerRect);
5445 return;
5446 }
5447
5448 ScrollView::paintScrollCorner(context, cornerRect);
5449}
5450
5451void LocalFrameView::paintScrollbar(GraphicsContext& context, Scrollbar& bar, const IntRect& rect)
5452{
5453 if (bar.isCustomScrollbar() && m_frame->isMainFrame()) {
5454 IntRect toFill = bar.frameRect();
5455 toFill.intersect(rect);
5456 context.fillRect(toFill, baseBackgroundColor());
5457 }
5458
5459 ScrollView::paintScrollbar(context, bar, rect);
5460}
5461
5462Color LocalFrameView::documentBackgroundColor() const
5463{
5464 // <https://bugs.webkit.org/show_bug.cgi?id=59540> We blend the background color of
5465 // the document and the body against the base background color of the frame view.
5466 // Background images are unfortunately impractical to include.
5467
5468 // Return invalid Color objects whenever there is insufficient information.
5469 RefPtr backgroundDocument = [&] {
5470#if ENABLE(FULLSCREEN_API)(defined 1 && 1)
5471 if (RefPtr page = m_frame->page()) {
5472 if (auto* fullscreenDocument = page->outermostFullscreenDocument())
5473 return fullscreenDocument;
5474 }
5475#endif
5476 return m_frame->document();
5477 }();
5478
5479 if (!backgroundDocument)
5480 return Color();
5481
5482 RefPtr htmlElement = backgroundDocument->documentElement();
5483 RefPtr bodyElement = backgroundDocument->bodyOrFrameset();
5484
5485 // Start with invalid colors.
5486 Color htmlBackgroundColor;
5487 Color bodyBackgroundColor;
5488 if (htmlElement && htmlElement->renderer())
5489 htmlBackgroundColor = htmlElement->renderer()->style().visitedDependentBackgroundColorApplyingColorFilter();
5490 if (bodyElement && bodyElement->renderer())
5491 bodyBackgroundColor = bodyElement->renderer()->style().visitedDependentBackgroundColorApplyingColorFilter();
5492
5493#if ENABLE(FULLSCREEN_API)(defined 1 && 1)
5494 Color fullscreenBackgroundColor = [&] () -> Color {
5495 RefPtr documentFullscreen = backgroundDocument->fullscreenIfExists();
5496 if (!documentFullscreen)
5497 return { };
5498
5499 RefPtr fullscreenElement = documentFullscreen->fullscreenElement();
5500 if (!fullscreenElement)
5501 return { };
5502
5503 CheckedPtr fullscreenRenderer = fullscreenElement->renderer();
5504 if (!fullscreenRenderer)
5505 return { };
5506
5507 auto fullscreenElementColor = fullscreenRenderer->style().visitedDependentBackgroundColorApplyingColorFilter();
5508
5509 WeakPtr backdropRenderer = fullscreenRenderer->pseudoElementRenderer(PseudoElementType::Backdrop);
5510 if (!backdropRenderer)
5511 return fullscreenElementColor;
5512
5513 // Do not blend the fullscreenElementColor atop the backdrop color. The backdrop should
5514 // intentionally be visible underneath (and around) the fullscreen element.
5515 return backdropRenderer->style().visitedDependentBackgroundColorApplyingColorFilter();
5516 }();
5517
5518 // Replace or blend the fullscreen background color with the body background color, if present.
5519 if (fullscreenBackgroundColor.isValid()) {
5520 if (!bodyBackgroundColor.isValid())
5521 bodyBackgroundColor = fullscreenBackgroundColor;
5522 else
5523 bodyBackgroundColor = blendSourceOver(bodyBackgroundColor, fullscreenBackgroundColor);
5524 }
5525#endif
5526
5527 if (!bodyBackgroundColor.isValid()) {
5528 if (!htmlBackgroundColor.isValid())
5529 return Color();
5530 return blendSourceOver(baseBackgroundColor(), htmlBackgroundColor);
5531 }
5532
5533 if (!htmlBackgroundColor.isValid())
5534 return blendSourceOver(baseBackgroundColor(), bodyBackgroundColor);
5535
5536 // We take the aggregate of the base background color
5537 // the <html> background color, and the <body>
5538 // background color to find the document color. The
5539 // addition of the base background color is not
5540 // technically part of the document background, but it
5541 // otherwise poses problems when the aggregate is not
5542 // fully opaque.
5543 return blendSourceOver(blendSourceOver(baseBackgroundColor(), htmlBackgroundColor), bodyBackgroundColor);
5544}
5545
5546bool LocalFrameView::hasCustomScrollbars() const
5547{
5548 for (auto& widget : children()) {
5549 if (RefPtr frame = dynamicDowncast<LocalFrameView>(widget)) {
5550 if (frame->hasCustomScrollbars())
5551 return true;
5552 } else if (RefPtr scrollbar = dynamicDowncast<Scrollbar>(widget)) {
5553 if (scrollbar->isCustomScrollbar())
5554 return true;
5555 }
5556 }
5557 return false;
5558}
5559
5560LocalFrameView* LocalFrameView::parentFrameView() const
5561{
5562 if (!parent())
5563 return nullptr;
5564 auto* parentFrame = dynamicDowncast<LocalFrame>(m_frame->tree().parent());
5565 if (!parentFrame)
5566 return nullptr;
5567 return parentFrame->view();
5568}
5569
5570void LocalFrameView::updateControlTints()
5571{
5572 // This is called when control tints are changed from aqua/graphite to clear and vice versa.
5573 // We do a "fake" paint, and when the theme gets a paint call, it can then do an invalidate.
5574 // This is only done if the theme supports control tinting. It's up to the theme and platform
5575 // to define when controls get the tint and to call this function when that changes.
5576
5577 // Optimize the common case where we bring a window to the front while it's still empty.
5578 if (m_frame->document()->url().isEmpty())
5579 return;
5580
5581 // As noted above, this is a "fake" paint, so we should pause counting relevant repainted objects.
5582 RefPtr page = m_frame->page();
5583 bool isCurrentlyCountingRelevantRepaintedObject = false;
5584 if (page) {
5585 isCurrentlyCountingRelevantRepaintedObject = page->isCountingRelevantRepaintedObjects();
5586 page->setIsCountingRelevantRepaintedObjects(false);
5587 }
5588
5589 CheckedPtr renderView = this->renderView();
5590 if ((renderView && renderView->theme().supportsControlTints()) || hasCustomScrollbars())
5591 invalidateControlTints();
5592
5593 if (page)
5594 page->setIsCountingRelevantRepaintedObjects(isCurrentlyCountingRelevantRepaintedObject);
5595}
5596
5597void LocalFrameView::invalidateControlTints()
5598{
5599 traverseForPaintInvalidation(NullGraphicsContext::PaintInvalidationReasons::InvalidatingControlTints);
5600}
5601
5602void LocalFrameView::updateAccessibilityObjectRegions()
5603{
5604#if ENABLE(ACCESSIBILITY_ISOLATED_TREE)(defined 1 && 1)
5605 if (!AXObjectCache::accessibilityEnabled() || !AXObjectCache::isIsolatedTreeEnabled())
5606 return;
5607
5608 NullGraphicsContext graphicsContext;
5609 AccessibilityRegionContext accessibilityRegionContext;
5610 paint(graphicsContext, frameRect(), SecurityOriginPaintPolicy::AnyOrigin, &accessibilityRegionContext);
5611#endif
5612}
5613
5614void LocalFrameView::traverseForPaintInvalidation(NullGraphicsContext::PaintInvalidationReasons paintInvalidationReasons)
5615{
5616 if (needsLayout())
5617 layoutContext().layout();
5618
5619 NullGraphicsContext context(paintInvalidationReasons);
5620 if (platformWidget()) {
5621 // FIXME: consult paintsEntireContents().
5622 paintContents(context, visibleContentRect(LegacyIOSDocumentVisibleRect));
5623 } else
5624 paint(context, frameRect());
5625}
5626
5627bool LocalFrameView::wasScrolledByUser() const
5628{
5629 return m_lastUserScrollType.has_value();
5630}
5631
5632void LocalFrameView::setLastUserScrollType(std::optional<UserScrollType> userScrollType)
5633{
5634 LOG(Scrolling, "LocalFrameView::setLastUserScrollType at %d", userScrollType ? std::to_underlying(*userScrollType) : -1)((void)0);
5635
5636 cancelScheduledScrolls();
5637 if (currentScrollType() == ScrollType::Programmatic)
5638 return;
5639
5640 RefPtr document = m_frame->document();
5641 if (userScrollType && document)
5642 document->setGotoAnchorNeededAfterStylesheetsLoad(false);
5643
5644 if (userScrollType == UserScrollType::Explicit) {
5645 m_wasEverScrolledExplicitlyByUser = true;
5646 if (scrollOffset().y() > minimumScrollOffset().y())
5647 m_wasEverScrolledExplicitlyByUserBelowTopEdge = true;
5648 }
5649
5650 m_maintainScrollPositionAnchor = nullptr;
5651 if (m_lastUserScrollType == userScrollType)
5652 return;
5653 m_lastUserScrollType = userScrollType;
5654 adjustTiledBackingCoverage();
5655}
5656
5657void LocalFrameView::willPaintContents(GraphicsContext& context, const IntRect&, PaintingState& paintingState, RegionContext* regionContext)
5658{
5659 RefPtr document = m_frame->document();
5660
5661 if (!context.paintingDisabled())
5662 InspectorInstrumentation::willPaint(*renderView());
5663
5664 paintingState.isTopLevelPainter = !sCurrentPaintTimeStamp;
5665
5666 if (paintingState.isTopLevelPainter)
5667 sCurrentPaintTimeStamp = MonotonicTime::now();
5668
5669 paintingState.paintBehavior = m_paintBehavior;
5670
5671 if (RefPtr parentView = parentFrameView()) {
5672 static constexpr OptionSet flagsToCopy {
5673 PaintBehavior::FlattenCompositingLayers,
5674 PaintBehavior::Snapshotting,
5675 PaintBehavior::DefaultAsynchronousImageDecode,
5676 PaintBehavior::ForceSynchronousImageDecode,
5677 PaintBehavior::ExcludeReplacedContentExceptForIFrames,
5678 PaintBehavior::ExcludeText,
5679 PaintBehavior::FixedAndStickyLayersOnly,
5680 };
5681 m_paintBehavior.add(parentView->paintBehavior() & flagsToCopy);
5682 }
5683
5684 if (document->printing()) {
5685 m_paintBehavior.add(PaintBehavior::FlattenCompositingLayers);
5686 m_paintBehavior.add(PaintBehavior::Snapshotting);
5687 }
5688
5689 if (is<AccessibilityRegionContext>(regionContext))
5690 m_paintBehavior.add(PaintBehavior::FlattenCompositingLayers);
5691
5692 paintingState.isFlatteningPaintOfRootFrame = (m_paintBehavior & PaintBehavior::FlattenCompositingLayers) && !m_frame->ownerElement() && !context.detectingContentfulPaint();
5693 if (paintingState.isFlatteningPaintOfRootFrame)
5694 notifyWidgetsInAllFrames(WidgetNotification::WillPaintFlattened);
5695
5696 ASSERT(!m_isPainting)((void)0);
5697 m_isPainting = true;
5698}
5699
5700void LocalFrameView::didPaintContents(GraphicsContext& context, const IntRect& dirtyRect, PaintingState& paintingState)
5701{
5702 m_isPainting = false;
5703
5704 if (paintingState.isFlatteningPaintOfRootFrame)
5705 notifyWidgetsInAllFrames(WidgetNotification::DidPaintFlattened);
5706
5707 m_paintBehavior = paintingState.paintBehavior;
5708 m_lastPaintTime = MonotonicTime::now();
5709
5710 if (paintingState.isTopLevelPainter)
5711 sCurrentPaintTimeStamp = MonotonicTime();
5712
5713 if (!context.paintingDisabled()) {
5714 InspectorInstrumentation::didPaint(*renderView(), dirtyRect);
5715 // FIXME: should probably not fire milestones for snapshot painting. https://bugs.webkit.org/show_bug.cgi?id=117623
5716 firePaintRelatedMilestonesIfNeeded();
5717 }
5718}
5719
5720void LocalFrameView::paintContents(GraphicsContext& context, const IntRect& dirtyRect, SecurityOriginPaintPolicy securityOriginPaintPolicy, RegionContext* regionContext)
5721{
5722#ifndef NDEBUG1
5723 bool fillWithWarningColor = [&] {
5724 if (m_frame->document()->printing())
5725 return false; // Printing, don't fill with warning color (can't remember why).
5726
5727 if (m_frame->ownerElement())
5728 return false; // Subframe, don't fill with warning color.
5729
5730 if (isTransparent())
5731 return false; // Transparent, don't fill with warning color.
5732
5733 if (m_paintBehavior.containsAny({ PaintBehavior::SelectionOnly, PaintBehavior::FixedAndStickyLayersOnly }))
5734 return false; // Don't fill with warning color for selection and fixed-position snapshots.
5735
5736 if (m_nodeToDraw)
5737 return false; // Element images are transparent, don't fill with warning color.
5738
5739 return true;
5740 }();
5741
5742 if (fillWithWarningColor) {
5743 IntRect debugRect = frameRect();
5744 debugRect.intersect(dirtyRect);
5745 context.fillRect(debugRect, SRGBA<uint8_t> { 255, 64, 255 });
5746 }
5747#endif
5748
5749 CheckedPtr renderView = this->renderView();
5750 if (!renderView) {
5751 LOG_ERROR("called LocalFrameView::paint with nil renderer")((void)0);
5752 return;
5753 }
5754
5755 if (!layoutContext().inPaintableState())
5756 return;
5757
5758 ASSERT(!needsLayout())((void)0);
5759 if (needsLayout()) {
5760 FRAMEVIEW_RELEASE_LOG(Layout, LocalFrameViewNotPaintingLayoutNeeded)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_LOG49"
) = " [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
;
5761 return;
5762 }
5763
5764 PaintingState paintingState;
5765 willPaintContents(context, dirtyRect, paintingState, regionContext);
5766
5767 // m_nodeToDraw is used to draw only one element (and its descendants)
5768 RenderObject* renderer = m_nodeToDraw ? m_nodeToDraw->renderer() : nullptr;
5769 CheckedPtr rootLayer = renderView->layer();
5770
5771 RenderObject::SetLayoutNeededForbiddenScope forbidSetNeedsLayout(rootLayer->renderer());
5772
5773 rootLayer->paint(context, dirtyRect, LayoutSize(), m_paintBehavior, renderer, { }, securityOriginPaintPolicy == SecurityOriginPaintPolicy::AnyOrigin ? RenderLayer::SecurityOriginPaintPolicy::AnyOrigin : RenderLayer::SecurityOriginPaintPolicy::AccessibleOriginOnly, regionContext);
5774 if (auto* scrollableRootLayer = rootLayer->scrollableArea()) {
5775 if (scrollableRootLayer->containsDirtyOverlayScrollbars() && !regionContext)
5776 scrollableRootLayer->paintOverlayScrollbars(context, dirtyRect, m_paintBehavior, renderer);
5777 }
5778
5779 didPaintContents(context, dirtyRect, paintingState);
5780}
5781
5782void LocalFrameView::setPaintBehavior(OptionSet<PaintBehavior> behavior)
5783{
5784 m_paintBehavior = behavior;
5785}
5786
5787OptionSet<PaintBehavior> LocalFrameView::paintBehavior() const
5788{
5789 return m_paintBehavior;
5790}
5791
5792bool LocalFrameView::isPainting() const
5793{
5794 return m_isPainting;
5795}
5796
5797// FIXME: change this to use the subtreePaint terminology.
5798void LocalFrameView::setNodeToDraw(Node* node)
5799{
5800 m_nodeToDraw = node;
5801}
5802
5803void LocalFrameView::paintContentsForSnapshot(GraphicsContext& context, const IntRect& imageRect, SelectionInSnapshot shouldPaintSelection, CoordinateSpaceForSnapshot coordinateSpace)
5804{
5805 updateLayoutAndStyleIfNeededRecursive();
5806
5807 // Cache paint behavior and set a new behavior appropriate for snapshots.
5808 auto oldBehavior = paintBehavior();
5809 setPaintBehavior(oldBehavior | PaintBehavior::FlattenCompositingLayers | PaintBehavior::Snapshotting);
5810
5811 // If the snapshot should exclude selection, then we'll clear the current selection
5812 // in the render tree only. This will allow us to restore the selection from the DOM
5813 // after we paint the snapshot.
5814 if (shouldPaintSelection == ExcludeSelection) {
5815 for (RefPtr<Frame> frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr())) {
5816 RefPtr localFrame = dynamicDowncast<LocalFrame>(frame);
5817 if (!localFrame)
5818 continue;
5819 if (CheckedPtr renderView = localFrame->contentRenderer())
5820 renderView->selection().clear();
5821 }
5822 }
5823
5824 if (coordinateSpace == DocumentCoordinates)
5825 paintContents(context, imageRect);
5826 else {
5827 // A snapshot in ViewCoordinates will include a scrollbar, and the snapshot will contain
5828 // whatever content the document is currently scrolled to.
5829 paint(context, imageRect);
5830 }
5831
5832 // Restore selection.
5833 if (shouldPaintSelection == ExcludeSelection) {
5834 for (RefPtr<Frame> frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr())) {
5835 RefPtr localFrame = dynamicDowncast<LocalFrame>(frame);
5836 if (!localFrame)
5837 continue;
5838 localFrame->selection().updateAppearance();
5839 }
5840 }
5841
5842 // Restore cached paint behavior.
5843 setPaintBehavior(oldBehavior);
5844}
5845
5846void LocalFrameView::paintOverhangAreas(GraphicsContext& context, const IntRect& horizontalOverhangArea, const IntRect& verticalOverhangArea, const IntRect& dirtyRect)
5847{
5848 if (context.paintingDisabled())
5849 return;
5850
5851 if (m_frame->document()->printing())
5852 return;
5853
5854 ScrollView::paintOverhangAreas(context, horizontalOverhangArea, verticalOverhangArea, dirtyRect);
5855}
5856
5857void LocalFrameView::updateLayoutAndStyleIfNeededRecursive(OptionSet<LayoutOptions> layoutOptions)
5858{
5859 // Style updating, render tree creation, and layout needs to be done multiple times
5860 // for more than one reason. But one reason is that when an <object> element determines
5861 // what it needs to load a subframe, a second pass is needed. That requires update
5862 // passes equal to the number of levels of DOM nesting. That is why this number is large.
5863 // There are test cases where we have roughly 10 levels of DOM nesting, so this needs to
5864 // be greater than that. We have a limit to avoid the possibility of an infinite loop.
5865 // Typical calls will run the loop 2 times (once to do work, once to detect no further work
5866 // is needed).
5867 // FIXME: We should find an approach that does not require a loop at all.
5868 const unsigned maxUpdatePasses = 25;
5869
5870 // Style updates can trigger script, which can cause this LocalFrameView to be destroyed.
5871 Ref<LocalFrameView> protectedThis(*this);
5872
5873 using DescendantsDeque = Deque<Ref<LocalFrameView>, 16>;
5874 auto nextRenderedDescendant = [this] (DescendantsDeque& descendantsDeque) -> RefPtr<LocalFrameView> {
5875 if (descendantsDeque.isEmpty())
5876 descendantsDeque.append(*this);
5877 else {
5878 // Append renderered children after processing the parent, in case the processing
5879 // affects the set of rendered children.
5880 auto previousView = descendantsDeque.takeFirst();
5881 for (RefPtr<Frame> frame = previousView->m_frame->tree().firstRenderedChild(); frame; frame = frame->tree().nextRenderedSibling()) {
5882 RefPtr localFrame = dynamicDowncast<LocalFrame>(frame);
5883 if (!localFrame)
5884 continue;
5885 if (RefPtr view = localFrame->view())
5886 descendantsDeque.append(*view);
5887 }
5888 if (descendantsDeque.isEmpty())
5889 return nullptr;
5890 }
5891 return descendantsDeque.first().ptr();
5892 };
5893
5894 for (unsigned i = 0; i < maxUpdatePasses; ++i) {
5895 bool didWork = false;
5896 DescendantsDeque deque;
5897 while (auto view = nextRenderedDescendant(deque)) {
5898 if (view->m_frame->document()->updateLayout(layoutOptions | LayoutOptions::DoNotLayoutAncestorDocuments) == Document::UpdateLayoutResult::ChangesDone)
5899 didWork = true;
5900 }
5901 if (!didWork)
5902 break;
5903 }
5904
5905#if ASSERT_ENABLED0
5906 auto needsStyleRecalc = [&] {
5907 DescendantsDeque deque;
5908 while (auto view = nextRenderedDescendant(deque)) {
5909 RefPtr document = view->m_frame->document();
5910 if (document && document->childNeedsStyleRecalc())
5911 return true;
5912 }
5913 return false;
5914 };
5915
5916 auto needsLayout = [&] {
5917 DescendantsDeque deque;
5918 while (auto view = nextRenderedDescendant(deque)) {
5919 if (view->needsLayout())
5920 return true;
5921 }
5922 return false;
5923 };
5924#endif // ASSERT_ENABLED
5925
5926 ASSERT(!needsStyleRecalc())((void)0);
5927 ASSERT(!needsLayout())((void)0);
5928}
5929
5930#include <span>
5931
5932template<typename CharacterType>
5933static size_t nonWhitespaceLength(std::span<const CharacterType> characters)
5934{
5935 size_t result = characters.size();
5936 for (auto character : characters) {
5937 if (isASCIIWhitespace(character))
5938 --result;
5939 }
5940 return result;
5941}
5942
5943void LocalFrameView::incrementVisuallyNonEmptyCharacterCountSlowCase(const String& inlineText)
5944{
5945 if (inlineText.is8Bit())
5946 m_visuallyNonEmptyCharacterCount += nonWhitespaceLength(inlineText.span8());
5947 else
5948 m_visuallyNonEmptyCharacterCount += nonWhitespaceLength(inlineText.span16());
5949 ++m_textRendererCountForVisuallyNonEmptyCharacters;
5950}
5951
5952void LocalFrameView::updateHasReachedSignificantRenderedTextThreshold()
5953{
5954 if (m_hasReachedSignificantRenderedTextThreshold)
5955 return;
5956
5957 RefPtr page = m_frame->page();
5958 if (!page || !page->requestedLayoutMilestones().contains(LayoutMilestone::DidRenderSignificantAmountOfText))
5959 return;
5960
5961 RefPtr document = m_frame->document();
5962 if (!document)
5963 return;
5964
5965 document->updateMainArticleElementAfterLayout();
5966 auto hasMainArticleElement = document->hasMainArticleElement();
5967 auto characterThreshold = hasMainArticleElement ? mainArticleSignificantRenderedTextCharacterThreshold : defaultSignificantRenderedTextCharacterThreshold;
5968 if (m_visuallyNonEmptyCharacterCount < characterThreshold)
5969 return;
5970
5971 auto meanLength = hasMainArticleElement ? mainArticleSignificantRenderedTextMeanLength : defaultSignificantRenderedTextMeanLength;
5972 if (!m_textRendererCountForVisuallyNonEmptyCharacters || m_visuallyNonEmptyCharacterCount / static_cast<float>(m_textRendererCountForVisuallyNonEmptyCharacters) < meanLength)
5973 return;
5974
5975 m_hasReachedSignificantRenderedTextThreshold = true;
5976}
5977
5978bool LocalFrameView::qualifiesAsSignificantRenderedText() const
5979{
5980 ASSERT(!m_renderedSignificantAmountOfText)((void)0);
5981 RefPtr document = m_frame->document();
5982 if (!document || document->styleScope().hasPendingSheetsBeforeBody())
5983 return false;
5984 return m_hasReachedSignificantRenderedTextThreshold;
5985}
5986
5987void LocalFrameView::checkAndDispatchDidReachVisuallyNonEmptyState()
5988{
5989 auto qualifiesAsVisuallyNonEmpty = [&] {
5990 // No content yet.
5991 Ref document = *m_frame->document();
5992 RefPtr documentElement = document->documentElement();
5993 if (!documentElement || !documentElement->renderer())
5994 return false;
5995
5996 if (document->hasVisuallyNonEmptyCustomContent())
5997 return true;
5998
5999 // FIXME: We should also ignore renderers with non-final style.
6000 if (document->styleScope().hasPendingSheetsBeforeBody())
6001 return false;
6002
6003 auto finishedParsingMainDocument = m_frame->loader().stateMachine().committedFirstRealDocumentLoad()
6004 && (document->readyState() == Document::ReadyState::Interactive || document->readyState() == Document::ReadyState::Complete);
6005 // Ensure that we always fire visually non-empty milestone eventually.
6006 if (finishedParsingMainDocument && m_frame->loader().isComplete())
6007 return true;
6008
6009 auto isVisible = [](const Element* element) {
6010 if (!element || !element->renderer())
6011 return false;
6012 if (!element->renderer()->opacity())
6013 return false;
6014 return element->renderer()->style().usedVisibility() == Visibility::Visible;
6015 };
6016
6017 if (!isVisible(documentElement.get()))
6018 return false;
6019
6020 if (!isVisible(document->body()))
6021 return false;
6022
6023 // The first few hundred characters rarely contain the interesting content of the page.
6024 if (m_visuallyNonEmptyCharacterCount > visualCharacterThreshold)
6025 return true;
6026
6027 // Use a threshold value to prevent very small amounts of visible content from triggering didFirstVisuallyNonEmptyLayout
6028 if (m_visuallyNonEmptyPixelCount > visualPixelThreshold)
6029 return true;
6030
6031 auto isMoreContentExpected = [&]() {
6032 ASSERT(finishedParsingMainDocument)((void)0);
6033 // Pending css/font loading means we should wait a little longer. Classic non-async, non-defer scripts are all processed by now.
6034 auto* documentLoader = m_frame->loader().documentLoader();
6035 if (!documentLoader)
6036 return false;
6037
6038 auto& resourceLoader = documentLoader->cachedResourceLoader();
6039 if (!resourceLoader.requestCount())
6040 return false;
6041
6042 auto& resources = resourceLoader.allCachedResources();
6043 for (auto& resource : resources) {
6044 if (resource.value->isLoaded())
6045 continue;
6046 // ResourceLoadPriority::VeryLow is used for resources that are not needed to render.
6047 if (resource.value->loadPriority() == ResourceLoadPriority::VeryLow)
6048 continue;
6049 if (resource.value->type() == CachedResource::Type::CSSStyleSheet || resource.value->type() == CachedResource::Type::FontResource)
6050 return true;
6051 }
6052 return false;
6053 };
6054
6055 // Finished parsing the main document and we still don't yet have enough content. Check if we might be getting some more.
6056 if (finishedParsingMainDocument)
6057 return !isMoreContentExpected();
6058
6059 return false;
6060 };
6061
6062 if (m_contentQualifiesAsVisuallyNonEmpty)
6063 return;
6064
6065 if (!qualifiesAsVisuallyNonEmpty())
6066 return;
6067
6068 m_contentQualifiesAsVisuallyNonEmpty = true;
6069 if (m_frame->isRootFrame())
6070 m_frame->loader().didReachVisuallyNonEmptyState();
6071}
6072
6073bool LocalFrameView::hasContentfulDescendants() const
6074{
6075 return m_visuallyNonEmptyCharacterCount || m_visuallyNonEmptyPixelCount;
6076}
6077
6078bool LocalFrameView::isViewForDocumentInFrame() const
6079{
6080 auto* renderView = this->renderView();
6081 if (!renderView)
6082 return false;
6083
6084 return &renderView->frameView() == this;
6085}
6086
6087void LocalFrameView::enableFixedWidthAutoSizeMode(bool enable, const IntSize& viewSize)
6088{
6089 // FIXME: Figure out if this flavor of the autosizing should run only on the mainframe.
6090 enableAutoSizeMode(enable, viewSize, AutoSizeMode::FixedWidth);
6091}
6092
6093void LocalFrameView::enableSizeToContentAutoSizeMode(bool enable, const IntSize& viewSize)
6094{
6095 ASSERT(m_frame->isMainFrame())((void)0);
6096 enableAutoSizeMode(enable, viewSize, AutoSizeMode::SizeToContent);
6097}
6098
6099void LocalFrameView::enableAutoSizeMode(bool enable, const IntSize& viewSize, AutoSizeMode mode)
6100{
6101 ASSERT(!enable || !viewSize.isEmpty())((void)0);
6102 if (m_shouldAutoSize == enable && m_autoSizeConstraint == viewSize)
6103 return;
6104
6105 m_autoSizeMode = mode;
6106 m_shouldAutoSize = enable;
6107 m_autoSizeConstraint = viewSize;
6108 m_autoSizeContentSize = contentsSize();
6109 m_didRunAutosize = false;
6110
6111 setNeedsLayoutAfterViewConfigurationChange();
6112 layoutContext().scheduleLayout();
6113 if (m_shouldAutoSize) {
6114 overrideWidthForCSSDefaultViewportUnits(m_autoSizeConstraint.width());
6115 overrideWidthForCSSSmallViewportUnits(m_autoSizeConstraint.width());
6116 overrideWidthForCSSLargeViewportUnits(m_autoSizeConstraint.width());
6117 return;
6118 }
6119
6120 clearSizeOverrideForCSSDefaultViewportUnits();
6121 clearSizeOverrideForCSSSmallViewportUnits();
6122 clearSizeOverrideForCSSLargeViewportUnits();
6123 // Since autosize mode forces the scrollbar mode, change them to being auto.
6124 setVerticalScrollbarLock(false);
6125 setHorizontalScrollbarLock(false);
6126 setScrollbarModes(ScrollbarMode::Auto, ScrollbarMode::Auto);
6127}
6128
6129void LocalFrameView::forceLayout(bool allowSubtreeLayout)
6130{
6131 if (!allowSubtreeLayout && layoutContext().subtreeLayoutRoot())
6132 layoutContext().convertSubtreeLayoutToFullLayout();
6133 layoutContext().layout();
6134}
6135
6136void LocalFrameView::forceLayoutForPagination(const FloatSize& pageSize, const FloatSize& originalPageSize, float maximumShrinkFactor, AdjustViewSize shouldAdjustViewSize)
6137{
6138 if (!renderView())
6139 return;
6140
6141 Ref<LocalFrameView> protectedThis(*this);
6142 CheckedRef renderView = *this->renderView();
6143
6144 // Dumping externalRepresentation(m_frame->renderer()).ascii() is a good trick to see
6145 // the state of things before and after the layout
6146 bool isHorizontalWritingMode = renderView->writingMode().isHorizontal();
6147 float pageLogicalWidth = isHorizontalWritingMode ? pageSize.width() : pageSize.height();
6148 float pageLogicalHeight = isHorizontalWritingMode ? pageSize.height() : pageSize.width();
6149
6150 renderView->setPageLogicalSize({ floor(pageLogicalWidth), floor(pageLogicalHeight) });
6151 renderView->setNeedsLayoutAndPreferredWidthsUpdate();
6152 forceLayout();
6153 if (hasOneRef())
6154 return;
6155
6156 // If we don't fit in the given page width, we'll lay out again. If we don't fit in the
6157 // page width when shrunk, we will lay out at maximum shrink and clip extra content.
6158 // FIXME: We are assuming a shrink-to-fit printing implementation. A cropping
6159 // implementation should not do this!
6160 const LayoutRect& documentRect = renderView->documentRect();
6161 LayoutUnit docLogicalWidth = isHorizontalWritingMode ? documentRect.width() : documentRect.height();
6162 if (docLogicalWidth > pageLogicalWidth) {
6163 int expectedPageWidth = std::min<float>(documentRect.width(), pageSize.width() * maximumShrinkFactor);
6164 int expectedPageHeight = std::min<float>(documentRect.height(), pageSize.height() * maximumShrinkFactor);
6165 FloatSize maxPageSize = m_frame->resizePageRectsKeepingRatio(FloatSize(originalPageSize.width(), originalPageSize.height()), FloatSize(expectedPageWidth, expectedPageHeight));
6166 pageLogicalWidth = isHorizontalWritingMode ? maxPageSize.width() : maxPageSize.height();
6167 pageLogicalHeight = isHorizontalWritingMode ? maxPageSize.height() : maxPageSize.width();
6168
6169 renderView->setPageLogicalSize({ floor(pageLogicalWidth), floor(pageLogicalHeight) });
6170 renderView->setNeedsLayoutAndPreferredWidthsUpdate();
6171 forceLayout();
6172 if (hasOneRef())
6173 return;
6174
6175 const LayoutRect& updatedDocumentRect = renderView->documentRect();
6176 LayoutUnit docLogicalHeight = isHorizontalWritingMode ? updatedDocumentRect.height() : updatedDocumentRect.width();
6177 LayoutUnit docLogicalTop = isHorizontalWritingMode ? updatedDocumentRect.y() : updatedDocumentRect.x();
6178 LayoutUnit docLogicalRight = isHorizontalWritingMode ? updatedDocumentRect.maxX() : updatedDocumentRect.maxY();
6179 LayoutUnit clippedLogicalLeft;
6180 if (!renderView->writingMode().isAnyLeftToRight())
6181 clippedLogicalLeft = docLogicalRight - pageLogicalWidth;
6182 LayoutRect overflow { clippedLogicalLeft, docLogicalTop, LayoutUnit(pageLogicalWidth), docLogicalHeight };
6183
6184 if (!isHorizontalWritingMode)
6185 overflow = overflow.transposedRect();
6186 renderView->clearLayoutOverflow();
6187 renderView->addLayoutOverflow(overflow); // This is how we clip in case we overflow again.
6188 }
6189
6190 if (shouldAdjustViewSize == AdjustViewSize::Yes)
6191 adjustViewSize();
6192}
6193
6194void LocalFrameView::adjustPageHeightDeprecated(float *newBottom, float oldTop, float oldBottom, float /*bottomLimit*/)
6195{
6196 CheckedPtr renderView = this->renderView();
6197 if (!renderView) {
6198 *newBottom = oldBottom;
6199 return;
6200
6201 }
6202 // Use a context with painting disabled.
6203 NullGraphicsContext context;
6204 renderView->setTruncatedAt(static_cast<int>(floorf(oldBottom)));
6205 IntRect dirtyRect(0, static_cast<int>(floorf(oldTop)), renderView->layoutOverflowRect().maxX(), static_cast<int>(ceilf(oldBottom - oldTop)));
6206 renderView->setPrintRect(dirtyRect);
6207 renderView->layer()->paint(context, dirtyRect);
6208 *newBottom = renderView->bestTruncatedAt();
6209 if (!*newBottom)
6210 *newBottom = oldBottom;
6211 renderView->setPrintRect(IntRect());
6212}
6213
6214float LocalFrameView::documentToAbsoluteScaleFactor(std::optional<float> usedZoom) const
6215{
6216 // If usedZoom is passed, it already factors in pageZoomFactor().
6217#if CPU(X86_64)(defined WTF_CPU_X86_64 && WTF_CPU_X86_64)
6218 // FIXME(rdar://165780260): Remove when this optimizer issue is resolved.
6219 // Clang's Intel optimizer requires us to help it figure out `value_or` to
6220 // avoid a performance regression.
6221 if (usedZoom.has_value()) [[unlikely]]
6222 return usedZoom.value() * m_frame->frameScaleFactor();
6223 return m_frame->pageZoomFactor() * m_frame->frameScaleFactor();
6224#else
6225 return usedZoom.value_or(m_frame->pageZoomFactor()) * m_frame->frameScaleFactor();
6226#endif
6227}
6228
6229float LocalFrameView::absoluteToDocumentScaleFactor(std::optional<float> usedZoom) const
6230{
6231 // If usedZoom is passed, it already factors in pageZoomFactor().
6232 return 1 / documentToAbsoluteScaleFactor(usedZoom);
6233}
6234
6235FloatRect LocalFrameView::absoluteToDocumentRect(FloatRect rect, std::optional<float> usedZoom) const
6236{
6237 rect.scale(absoluteToDocumentScaleFactor(usedZoom));
6238 return rect;
6239}
6240
6241FloatPoint LocalFrameView::absoluteToDocumentPoint(FloatPoint p, std::optional<float> usedZoom) const
6242{
6243 return p.scaled(absoluteToDocumentScaleFactor(usedZoom));
6244}
6245
6246DoublePoint LocalFrameView::absoluteToDocumentPoint(DoublePoint p, std::optional<float> usedZoom) const
6247{
6248 return p.scaled(absoluteToDocumentScaleFactor(usedZoom));
6249}
6250
6251FloatRect LocalFrameView::absoluteToClientRect(FloatRect rect, std::optional<float> usedZoom) const
6252{
6253 return documentToClientRect(absoluteToDocumentRect(rect, usedZoom));
6254}
6255
6256FloatSize LocalFrameView::documentToClientOffset() const
6257{
6258 FloatSize clientOrigin;
6259 RefPtr document = m_frame->document();
6260 if (document && document->quirks().shouldUseLayoutViewportForClientRects())
6261 clientOrigin = -toFloatSize(layoutViewportRect().location());
6262 else
6263 clientOrigin = -toFloatSize(visibleContentRect().location());
6264
6265 // Layout and visual viewports are affected by page zoom, so we need to factor that out.
6266 return clientOrigin.scaled(1 / (m_frame->pageZoomFactor() * m_frame->frameScaleFactor()));
6267}
6268
6269FloatRect LocalFrameView::documentToClientRect(FloatRect rect) const
6270{
6271 rect.move(documentToClientOffset());
6272 return rect;
6273}
6274
6275FloatPoint LocalFrameView::documentToClientPoint(FloatPoint p) const
6276{
6277 p.move(documentToClientOffset());
6278 return p;
6279}
6280
6281DoublePoint LocalFrameView::documentToClientPoint(DoublePoint p) const
6282{
6283 p.move(documentToClientOffset());
6284 return p;
6285}
6286
6287FloatRect LocalFrameView::clientToDocumentRect(FloatRect rect) const
6288{
6289 rect.move(-documentToClientOffset());
6290 return rect;
6291}
6292
6293FloatPoint LocalFrameView::clientToDocumentPoint(FloatPoint point) const
6294{
6295 point.move(-documentToClientOffset());
6296 return point;
6297}
6298
6299FloatRect LocalFrameView::clientToLayoutViewportRect(FloatRect rect) const
6300{
6301 ASSERT(m_frame->settings().visualViewportEnabled())((void)0);
6302 rect.scale(m_frame->pageZoomFactor());
6303 return rect;
6304}
6305
6306FloatPoint LocalFrameView::clientToLayoutViewportPoint(FloatPoint p) const
6307{
6308 ASSERT(m_frame->settings().visualViewportEnabled())((void)0);
6309 return p.scaled(m_frame->pageZoomFactor());
6310}
6311
6312void LocalFrameView::setTracksRepaints(bool trackRepaints)
6313{
6314 if (trackRepaints == m_isTrackingRepaints)
6315 return;
6316
6317 // Force layout to flush out any pending repaints.
6318 if (trackRepaints) {
6319 if (RefPtr document = m_frame->document())
6320 document->updateLayout(LayoutOptions::UpdateCompositingLayers);
6321 }
6322
6323 for (RefPtr frame = &m_frame->tree().top(); frame; frame = frame->tree().traverseNext()) {
6324 RefPtr localFrame = dynamicDowncast<LocalFrame>(frame);
6325 if (!localFrame)
6326 continue;
6327 if (CheckedPtr renderView = localFrame->contentRenderer())
6328 renderView->compositor().setTracksRepaints(trackRepaints);
6329 }
6330
6331 resetTrackedRepaints();
6332 m_isTrackingRepaints = trackRepaints;
6333}
6334
6335void LocalFrameView::resetTrackedRepaints()
6336{
6337 m_trackedRepaintRects.clear();
6338 if (CheckedPtr renderView = this->renderView())
6339 renderView->compositor().resetTrackedRepaintRects();
6340}
6341
6342String LocalFrameView::trackedRepaintRectsAsText() const
6343{
6344 auto& frame = this->m_frame.get();
6345 Ref protectedFrame { frame };
6346
6347 if (RefPtr document = frame.document())
6348 document->updateLayout();
6349
6350 TextStream ts;
6351 if (!m_trackedRepaintRects.isEmpty()) {
6352 ts << "(repaint rects\n"_s;
6353 for (auto& rect : m_trackedRepaintRects)
6354 ts << " (rect "_s << LayoutUnit(rect.x()) << ' ' << LayoutUnit(rect.y()) << ' ' << LayoutUnit(rect.width()) << ' ' << LayoutUnit(rect.height()) << ")\n"_s;
6355 ts << ")\n"_s;
6356 }
6357 return ts.release();
6358}
6359
6360void LocalFrameView::startTrackingLayoutUpdates()
6361{
6362 layoutContext().startTrackingLayoutUpdates();
6363}
6364
6365unsigned LocalFrameView::layoutUpdateCount()
6366{
6367 return layoutContext().layoutUpdateCount();
6368}
6369
6370void LocalFrameView::startTrackingRenderLayerPositionUpdates()
6371{
6372 layoutContext().startTrackingRenderLayerPositionUpdates();
6373}
6374
6375unsigned LocalFrameView::renderLayerPositionUpdateCount()
6376{
6377 return layoutContext().renderLayerPositionUpdateCount();
6378}
6379
6380void LocalFrameView::addScrollableAreaForAnimatedScroll(ScrollableArea* scrollableArea)
6381{
6382 if (!m_scrollableAreasForAnimatedScroll)
6383 m_scrollableAreasForAnimatedScroll = makeUnique<ScrollableAreaSet>();
6384
6385 m_scrollableAreasForAnimatedScroll->add(*scrollableArea);
6386}
6387
6388void LocalFrameView::removeScrollableAreaForAnimatedScroll(ScrollableArea* scrollableArea)
6389{
6390 if (m_scrollableAreasForAnimatedScroll)
6391 m_scrollableAreasForAnimatedScroll->remove(*scrollableArea);
6392}
6393
6394bool LocalFrameView::addScrollableArea(ScrollableArea* scrollableArea)
6395{
6396 if (!m_scrollableAreas)
6397 m_scrollableAreas = makeUnique<ScrollableAreaSet>();
6398
6399 if (m_scrollableAreas->add(*scrollableArea).isNewEntry) {
6400 scrollableAreaSetChanged();
6401 return true;
6402 }
6403
6404 return false;
6405}
6406
6407bool LocalFrameView::removeScrollableArea(ScrollableArea* scrollableArea)
6408{
6409 if (m_scrollableAreas && m_scrollableAreas->remove(*scrollableArea)) {
6410 scrollableAreaSetChanged();
6411 return true;
6412 }
6413 return false;
6414}
6415
6416bool LocalFrameView::containsScrollableArea(ScrollableArea* scrollableArea) const
6417{
6418 return m_scrollableAreas && m_scrollableAreas->contains(*scrollableArea);
6419}
6420
6421void LocalFrameView::scrollableAreaSetChanged()
6422{
6423 if (RefPtr scrollingCoordinator = this->scrollingCoordinator())
6424 scrollingCoordinator->frameViewEventTrackingRegionsChanged(*this);
6425}
6426
6427void LocalFrameView::scrollDidEnd()
6428{
6429 if (!isAwaitingScrollend())
6430 return;
6431 setIsAwaitingScrollend(false);
6432 if (!m_frame->view())
6433 return;
6434 if (RefPtr document = m_frame->document())
6435 document->addPendingScrollEventTarget(*document, ScrollEventType::Scrollend);
6436}
6437
6438void LocalFrameView::scheduleScrollEvent()
6439{
6440 setIsAwaitingScrollend(true);
6441 m_frame->eventHandler().scheduleScrollEvent();
6442 m_frame->eventHandler().dispatchFakeMouseMoveEventSoon();
6443}
6444
6445void LocalFrameView::addChild(Widget& widget)
6446{
6447 if (auto* childFrameView = dynamicDowncast<LocalFrameView>(widget)) {
6448 if (childFrameView->isScrollable())
6449 addScrollableArea(childFrameView);
6450 }
6451
6452 ScrollView::addChild(widget);
6453}
6454
6455void LocalFrameView::removeChild(Widget& widget)
6456{
6457 if (auto* childFrameView = dynamicDowncast<LocalFrameView>(widget))
6458 removeScrollableArea(childFrameView);
6459
6460 ScrollView::removeChild(widget);
6461}
6462
6463bool LocalFrameView::handleWheelEventForScrolling(const PlatformWheelEvent& wheelEvent, std::optional<WheelScrollGestureState> gestureState)
6464{
6465 // Note that to allow for rubber-band over-scroll behavior, even non-scrollable views
6466 // should handle wheel events.
6467#if !HAVE(RUBBER_BANDING)(defined 1 && 1)
6468 if (!isScrollable())
6469 return false;
6470#endif
6471
6472 if (delegatesScrollingToNativeView()) {
6473 ScrollPosition oldPosition = scrollPosition();
6474 ScrollPosition newPosition = oldPosition - IntSize(wheelEvent.deltaX(), wheelEvent.deltaY());
6475 if (oldPosition != newPosition) {
6476 ScrollView::scrollTo(newPosition);
6477 scrollPositionChanged(oldPosition, scrollPosition());
6478 didChangeScrollOffset();
6479 }
6480 return true;
6481 }
6482
6483 // We don't allow mouse wheeling to happen in a ScrollView that has had its scrollbars explicitly disabled.
6484 if (!canHaveScrollbars())
6485 return false;
6486
6487 if (platformWidget())
6488 return false;
6489
6490#if ENABLE(ASYNC_SCROLLING)(defined 1 && 1)
6491 if (RefPtr scrollingCoordinator = this->scrollingCoordinator()) {
6492 auto scrollingNodeID = this->scrollingNodeID();
6493 if (scrollingNodeID && scrollingCoordinator->coordinatesScrollingForFrameView(*this)) {
6494 auto result = scrollingCoordinator->handleWheelEventForScrolling(wheelEvent, *scrollingNodeID, gestureState);
6495 if (!result.needsMainThreadProcessing())
6496 return result.wasHandled;
6497 }
6498 }
6499#endif
6500
6501 return ScrollableArea::handleWheelEventForScrolling(wheelEvent, gestureState);
6502}
6503
6504bool LocalFrameView::isVerticalDocument() const
6505{
6506 auto* renderView = this->renderView();
6507 if (!renderView)
6508 return true;
6509
6510 return renderView->writingMode().isHorizontal();
6511}
6512
6513bool LocalFrameView::isFlippedDocument() const
6514{
6515 auto* renderView = this->renderView();
6516 if (!renderView)
6517 return false;
6518
6519 return renderView->writingMode().isBlockFlipped();
6520}
6521
6522void LocalFrameView::notifyWidgetsInAllFrames(WidgetNotification notification)
6523{
6524 for (RefPtr<Frame> frame = m_frame.ptr(); frame; frame = frame->tree().traverseNext(m_frame.ptr())) {
6525 RefPtr localFrame = dynamicDowncast<LocalFrame>(frame);
6526 if (!localFrame)
6527 continue;
6528 if (RefPtr view = localFrame->view())
6529 view->notifyWidgets(notification);
6530 }
6531}
6532
6533AXObjectCache* LocalFrameView::axObjectCache() const
6534{
6535 SUPPRESS_UNCHECKED_LOCAL[[clang::suppress]] AXObjectCache* cache = nullptr;
6536 if (m_frame->document())
6537 cache = m_frame->document()->existingAXObjectCache();
6538
6539 // FIXME: We should generally always be using the main-frame cache rather than
6540 // using it as a fallback as we do here.
6541 if (!cache && !m_frame->isMainFrame()) {
6542 RefPtr localMainFrame = dynamicDowncast<LocalFrame>(m_frame->mainFrame());
6543 if (localMainFrame) {
6544 if (RefPtr mainFrameDocument = localMainFrame->document())
6545 cache = mainFrameDocument->existingAXObjectCache();
6546 }
6547 }
6548 return cache;
6549}
6550
6551#if PLATFORM(IOS_FAMILY)(defined WTF_PLATFORM_IOS_FAMILY && WTF_PLATFORM_IOS_FAMILY
)
6552bool LocalFrameView::useCustomFixedPositionLayoutRect() const
6553{
6554 return !m_frame->settings().visualViewportEnabled() && m_useCustomFixedPositionLayoutRect;
6555}
6556
6557void LocalFrameView::setCustomFixedPositionLayoutRect(const IntRect& rect)
6558{
6559 if (m_useCustomFixedPositionLayoutRect && m_customFixedPositionLayoutRect == rect)
6560 return;
6561 m_useCustomFixedPositionLayoutRect = true;
6562 m_customFixedPositionLayoutRect = rect;
6563 updateContentsSize();
6564}
6565
6566bool LocalFrameView::updateFixedPositionLayoutRect()
6567{
6568 if (!m_useCustomFixedPositionLayoutRect)
6569 return false;
6570
6571 IntRect newRect;
6572 Page* page = m_frame->page();
6573 if (!page || !page->chrome().client().fetchCustomFixedPositionLayoutRect(newRect))
6574 return false;
6575
6576 if (newRect != m_customFixedPositionLayoutRect) {
6577 m_customFixedPositionLayoutRect = newRect;
6578 setViewportConstrainedObjectsNeedLayout();
6579 return true;
6580 }
6581 return false;
6582}
6583
6584void LocalFrameView::setCustomSizeForResizeEvent(IntSize customSize)
6585{
6586 m_customSizeForResizeEvent = customSize;
6587 scheduleResizeEventIfNeeded();
6588}
6589
6590void LocalFrameView::setScrollVelocity(const VelocityData& velocityData)
6591{
6592 if (TiledBacking* tiledBacking = this->tiledBacking())
6593 tiledBacking->setVelocity(velocityData);
6594}
6595#endif // PLATFORM(IOS_FAMILY)
6596
6597void LocalFrameView::setScrollingPerformanceTestingEnabled(bool scrollingPerformanceTestingEnabled)
6598{
6599 if (scrollingPerformanceTestingEnabled) {
6600 RefPtr page = m_frame->page();
6601 if (page && page->performanceLoggingClient())
6602 page->performanceLoggingClient()->logScrollingEvent(PerformanceLoggingClient::ScrollingEvent::LoggingEnabled, MonotonicTime::now(), 0);
6603 }
6604
6605 if (TiledBacking* tiledBacking = this->tiledBacking())
6606 tiledBacking->setScrollingPerformanceTestingEnabled(scrollingPerformanceTestingEnabled);
6607}
6608
6609void LocalFrameView::createScrollbarsController()
6610{
6611 RefPtr page = m_frame->page();
6612 if (!page) {
6613 ScrollView::createScrollbarsController();
6614 return;
6615 }
6616
6617 page->chrome().client().ensureScrollbarsController(*page, *this);
6618}
6619
6620void LocalFrameView::didAddScrollbar(Scrollbar* scrollbar, ScrollbarOrientation orientation)
6621{
6622 RefPtr page = m_frame->page();
6623
6624 ScrollableArea::didAddScrollbar(scrollbar, orientation);
6625
6626 if (page && page->isMonitoringWheelEvents())
6627 scrollAnimator().setWheelEventTestMonitor(page->wheelEventTestMonitor());
6628 if (AXObjectCache* cache = axObjectCache())
6629 cache->onScrollbarUpdate(*this);
6630}
6631
6632void LocalFrameView::willRemoveScrollbar(Scrollbar& scrollbar, ScrollbarOrientation orientation)
6633{
6634 ScrollableArea::willRemoveScrollbar(scrollbar, orientation);
6635 if (AXObjectCache* cache = axObjectCache()) {
6636 cache->remove(scrollbar);
6637 cache->onScrollbarUpdate(*this);
6638 }
6639}
6640
6641void LocalFrameView::scrollbarFrameRectChanged(const Scrollbar& scrollbar) const
6642{
6643 if (auto* cache = axObjectCache())
6644 cache->onScrollbarFrameRectChange(scrollbar);
6645}
6646
6647void LocalFrameView::addPaintPendingMilestones(OptionSet<LayoutMilestone> milestones)
6648{
6649 m_milestonesPendingPaint.add(milestones);
6650}
6651
6652void LocalFrameView::fireLayoutRelatedMilestonesIfNeeded()
6653{
6654 if (m_frame->loader().client().shouldSuppressLayoutMilestones())
6655 return;
6656
6657 OptionSet<LayoutMilestone> requestedMilestones;
6658 OptionSet<LayoutMilestone> milestonesAchieved;
6659 RefPtr page = m_frame->page();
6660 if (page)
6661 requestedMilestones = page->requestedLayoutMilestones();
6662
6663 if (m_firstLayoutCallbackPending) {
6664 m_firstLayoutCallbackPending = false;
6665 m_frame->loader().didFirstLayout();
6666 if (requestedMilestones & LayoutMilestone::DidFirstLayout)
6667 milestonesAchieved.add(LayoutMilestone::DidFirstLayout);
6668 if (m_frame->isMainFrame())
6669 page->startCountingRelevantRepaintedObjects();
6670 }
6671
6672 if (m_firstVisuallyNonEmptyLayoutMilestoneIsPending) {
6673 checkAndDispatchDidReachVisuallyNonEmptyState();
6674 if (m_contentQualifiesAsVisuallyNonEmpty) {
6675 m_firstVisuallyNonEmptyLayoutMilestoneIsPending = false;
6676
6677 addPaintPendingMilestones(LayoutMilestone::DidFirstMeaningfulPaint);
6678 if (requestedMilestones & LayoutMilestone::DidFirstVisuallyNonEmptyLayout)
6679 milestonesAchieved.add(LayoutMilestone::DidFirstVisuallyNonEmptyLayout);
6680 }
6681 }
6682
6683 if (!m_renderedSignificantAmountOfText && qualifiesAsSignificantRenderedText()) {
6684 m_renderedSignificantAmountOfText = true;
6685 if (requestedMilestones & LayoutMilestone::DidRenderSignificantAmountOfText)
6686 milestonesAchieved.add(LayoutMilestone::DidRenderSignificantAmountOfText);
6687 }
6688
6689 if (milestonesAchieved && m_frame->isMainFrame()) {
6690 if (milestonesAchieved.contains(LayoutMilestone::DidFirstVisuallyNonEmptyLayout))
6691 FRAMEVIEW_RELEASE_LOG(Layout, LocalFrameViewFiringFirstVisuallyNonEmptyLayoutMilestone)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_LOG50"
) = "[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
;
6692 m_frame->loader().didReachLayoutMilestone(milestonesAchieved);
6693 }
6694}
6695
6696void LocalFrameView::firePaintRelatedMilestonesIfNeeded()
6697{
6698 RefPtr page = m_frame->page();
6699 if (!page)
6700 return;
6701
6702 OptionSet<LayoutMilestone> milestonesAchieved;
6703
6704 if (m_milestonesPendingPaint & LayoutMilestone::DidFirstPaintAfterSuppressedIncrementalRendering) {
6705 if (page->requestedLayoutMilestones() & LayoutMilestone::DidFirstPaintAfterSuppressedIncrementalRendering)
6706 milestonesAchieved.add(LayoutMilestone::DidFirstPaintAfterSuppressedIncrementalRendering);
6707 }
6708
6709 if (m_milestonesPendingPaint & LayoutMilestone::DidFirstMeaningfulPaint) {
6710 if (page->requestedLayoutMilestones() & LayoutMilestone::DidFirstMeaningfulPaint)
6711 milestonesAchieved.add(LayoutMilestone::DidFirstMeaningfulPaint);
6712 }
6713
6714 m_milestonesPendingPaint = { };
6715
6716 RefPtr localMainFrame = page->localMainFrame();
6717 if (milestonesAchieved && localMainFrame)
6718 localMainFrame->loader().didReachLayoutMilestone(milestonesAchieved);
6719}
6720
6721void LocalFrameView::setVisualUpdatesAllowedByClient(bool visualUpdatesAllowed)
6722{
6723 if (m_visualUpdatesAllowedByClient == visualUpdatesAllowed)
6724 return;
6725
6726 m_visualUpdatesAllowedByClient = visualUpdatesAllowed;
6727
6728 m_frame->document()->setVisualUpdatesAllowedByClient(visualUpdatesAllowed);
6729}
6730
6731void LocalFrameView::setScrollPinningBehavior(ScrollPinningBehavior pinning)
6732{
6733 m_scrollPinningBehavior = pinning;
6734
6735 if (RefPtr scrollingCoordinator = this->scrollingCoordinator())
6736 scrollingCoordinator->setScrollPinningBehavior(pinning);
6737
6738 updateScrollbars(scrollPosition());
6739}
6740
6741ScrollBehaviorForFixedElements LocalFrameView::scrollBehaviorForFixedElements() const
6742{
6743 return m_frame->settings().backgroundShouldExtendBeyondPage() ? ScrollBehaviorForFixedElements::StickToViewportBounds : ScrollBehaviorForFixedElements::StickToDocumentBounds;
6744}
6745
6746LocalFrame& LocalFrameView::frame() const
6747{
6748 return m_frame;
6749}
6750
6751RenderView* LocalFrameView::renderView() const
6752{
6753 return m_frame->contentRenderer();
6754}
6755
6756int LocalFrameView::mapFromLayoutToCSSUnits(LayoutUnit value) const
6757{
6758 Ref frame = m_frame;
6759 return value / (frame->pageZoomFactor() * frame->frameScaleFactor());
6760}
6761
6762LayoutUnit LocalFrameView::mapFromCSSToLayoutUnits(int value) const
6763{
6764 Ref frame = m_frame;
6765 return LayoutUnit(value * frame->pageZoomFactor() * frame->frameScaleFactor());
6766}
6767
6768void LocalFrameView::didAddWidgetToRenderTree(Widget& widget)
6769{
6770 ASSERT(!m_widgetsInRenderTree.contains(widget))((void)0);
6771 m_widgetsInRenderTree.add(widget);
6772}
6773
6774void LocalFrameView::willRemoveWidgetFromRenderTree(Widget& widget)
6775{
6776 ASSERT(m_widgetsInRenderTree.contains(widget))((void)0);
6777 m_widgetsInRenderTree.remove(widget);
6778}
6779
6780static Vector<Ref<Widget>> collectAndProtectWidgets(const HashSet<SingleThreadWeakRef<Widget>>& set)
6781{
6782 return WTF::map(set, [](auto& widget) -> Ref<Widget> {
6783 return widget.get();
6784 });
6785}
6786
6787void LocalFrameView::updateWidgetPositions()
6788{
6789 m_updateWidgetPositionsTimer.stop();
6790 // updateWidgetPosition() can possibly cause layout to be re-entered (via plug-ins running
6791 // scripts in response to NPP_SetWindow, for example), so we need to keep the Widgets
6792 // alive during enumeration.
6793 for (auto& widget : collectAndProtectWidgets(m_widgetsInRenderTree)) {
6794 if (RefPtr renderer = RenderWidget::find(widget)) {
6795 auto ignoreWidgetState = renderer->updateWidgetPosition();
6796 UNUSED_PARAM(ignoreWidgetState)(void)ignoreWidgetState;
6797 }
6798 }
6799}
6800
6801void LocalFrameView::scheduleUpdateWidgetPositions()
6802{
6803 if (!m_updateWidgetPositionsTimer.isActive())
6804 m_updateWidgetPositionsTimer.startOneShot(0_s);
6805}
6806
6807void LocalFrameView::updateWidgetPositionsTimerFired()
6808{
6809 updateWidgetPositions();
6810}
6811
6812void LocalFrameView::notifyWidgets(WidgetNotification notification)
6813{
6814 for (auto& widget : collectAndProtectWidgets(m_widgetsInRenderTree))
6815 widget->notifyWidget(notification);
6816}
6817
6818void LocalFrameView::setViewExposedRect(std::optional<FloatRect> viewExposedRect)
6819{
6820 if (m_viewExposedRect == viewExposedRect)
6821 return;
6822
6823 LOG_WITH_STREAM(Scrolling, stream << "LocalFrameView " << this << " setViewExposedRect " << (viewExposedRect ? viewExposedRect.value() : FloatRect()))((void)0);
6824
6825 bool hasRectExistenceChanged = !m_viewExposedRect == !viewExposedRect;
6826 m_viewExposedRect = viewExposedRect;
6827
6828 // FIXME: We should support clipping to the exposed rect for subframes as well.
6829 if (!m_frame->isMainFrame())
6830 return;
6831
6832 if (TiledBacking* tiledBacking = this->tiledBacking()) {
6833 if (hasRectExistenceChanged)
6834 updateTiledBackingAdaptiveSizing();
6835 adjustTiledBackingCoverage();
6836 tiledBacking->setTiledScrollingIndicatorPosition(m_viewExposedRect ? m_viewExposedRect.value().location() : FloatPoint());
6837 }
6838
6839 if (RefPtr page = m_frame->page()) {
6840 page->scheduleRenderingUpdate(RenderingUpdateStep::LayerFlush);
6841 page->pageOverlayController().didChangeViewExposedRect();
6842 }
6843}
6844
6845void LocalFrameView::clearSizeOverrideForCSSDefaultViewportUnits()
6846{
6847 if (!m_defaultViewportSizeOverride)
6848 return;
6849
6850 m_defaultViewportSizeOverride = std::nullopt;
6851 if (RefPtr document = m_frame->document())
6852 document->styleScope().didChangeStyleSheetEnvironment();
6853}
6854
6855void LocalFrameView::setSizeForCSSDefaultViewportUnits(FloatSize size)
6856{
6857 setOverrideSizeForCSSDefaultViewportUnits({ size.width(), size.height() });
6858}
6859
6860void LocalFrameView::overrideWidthForCSSDefaultViewportUnits(float width)
6861{
6862 setOverrideSizeForCSSDefaultViewportUnits({ width, m_defaultViewportSizeOverride ? m_defaultViewportSizeOverride->height : std::nullopt });
6863}
6864
6865void LocalFrameView::resetOverriddenWidthForCSSDefaultViewportUnits()
6866{
6867 setOverrideSizeForCSSDefaultViewportUnits({ { }, m_defaultViewportSizeOverride ? m_defaultViewportSizeOverride->height : std::nullopt });
6868}
6869
6870void LocalFrameView::setOverrideSizeForCSSDefaultViewportUnits(OverrideViewportSize size)
6871{
6872 if (m_defaultViewportSizeOverride == size)
6873 return;
6874
6875 m_defaultViewportSizeOverride = size;
6876
6877 if (RefPtr document = m_frame->document())
6878 document->styleScope().didChangeStyleSheetEnvironment();
6879}
6880
6881FloatSize LocalFrameView::sizeForCSSDefaultViewportUnits() const
6882{
6883 return calculateSizeForCSSViewportUnitsOverride(m_defaultViewportSizeOverride);
6884}
6885
6886void LocalFrameView::clearSizeOverrideForCSSSmallViewportUnits()
6887{
6888 if (!m_smallViewportSizeOverride)
6889 return;
6890
6891 m_smallViewportSizeOverride = std::nullopt;
6892 if (RefPtr document = m_frame->document())
6893 document->updateViewportUnitsOnResize();
6894}
6895
6896void LocalFrameView::setSizeForCSSSmallViewportUnits(FloatSize size)
6897{
6898 setOverrideSizeForCSSSmallViewportUnits({ size.width(), size.height() });
6899}
6900
6901void LocalFrameView::overrideWidthForCSSSmallViewportUnits(float width)
6902{
6903 setOverrideSizeForCSSSmallViewportUnits({ width, m_smallViewportSizeOverride ? m_smallViewportSizeOverride->height : std::nullopt });
6904}
6905
6906void LocalFrameView::resetOverriddenWidthForCSSSmallViewportUnits()
6907{
6908 setOverrideSizeForCSSSmallViewportUnits({ { }, m_smallViewportSizeOverride ? m_smallViewportSizeOverride->height : std::nullopt });
6909}
6910
6911void LocalFrameView::setOverrideSizeForCSSSmallViewportUnits(OverrideViewportSize size)
6912{
6913 if (m_smallViewportSizeOverride && *m_smallViewportSizeOverride == size)
6914 return;
6915
6916 m_smallViewportSizeOverride = size;
6917
6918 if (RefPtr document = m_frame->document())
6919 document->updateViewportUnitsOnResize();
6920}
6921
6922FloatSize LocalFrameView::sizeForCSSSmallViewportUnits() const
6923{
6924 return calculateSizeForCSSViewportUnitsOverride(m_smallViewportSizeOverride);
6925}
6926
6927void LocalFrameView::clearSizeOverrideForCSSLargeViewportUnits()
6928{
6929 if (!m_largeViewportSizeOverride)
6930 return;
6931
6932 m_largeViewportSizeOverride = std::nullopt;
6933 if (RefPtr document = m_frame->document())
6934 document->updateViewportUnitsOnResize();
6935}
6936
6937void LocalFrameView::setSizeForCSSLargeViewportUnits(FloatSize size)
6938{
6939 setOverrideSizeForCSSLargeViewportUnits({ size.width(), size.height() });
6940}
6941
6942void LocalFrameView::overrideWidthForCSSLargeViewportUnits(float width)
6943{
6944 setOverrideSizeForCSSLargeViewportUnits({ width, m_largeViewportSizeOverride ? m_largeViewportSizeOverride->height : std::nullopt });
6945}
6946
6947void LocalFrameView::resetOverriddenWidthForCSSLargeViewportUnits()
6948{
6949 setOverrideSizeForCSSLargeViewportUnits({ { }, m_largeViewportSizeOverride ? m_largeViewportSizeOverride->height : std::nullopt });
6950}
6951
6952void LocalFrameView::setOverrideSizeForCSSLargeViewportUnits(OverrideViewportSize size)
6953{
6954 if (m_largeViewportSizeOverride && *m_largeViewportSizeOverride == size)
6955 return;
6956
6957 m_largeViewportSizeOverride = size;
6958
6959 if (RefPtr document = m_frame->document())
6960 document->updateViewportUnitsOnResize();
6961}
6962
6963FloatSize LocalFrameView::sizeForCSSLargeViewportUnits() const
6964{
6965 return calculateSizeForCSSViewportUnitsOverride(m_largeViewportSizeOverride);
6966}
6967
6968FloatSize LocalFrameView::calculateSizeForCSSViewportUnitsOverride(std::optional<OverrideViewportSize> override) const
6969{
6970 OverrideViewportSize viewportSize;
6971
6972 if (override) {
6973 viewportSize = *override;
6974
6975 // auto-size overrides the width only, so we can't always bail out early here.
6976 if (viewportSize.width && viewportSize.height)
6977 return { *viewportSize.width, *viewportSize.height };
6978 }
6979
6980 if (useFixedLayout()) {
6981 auto fixedLayoutSize = this->fixedLayoutSize();
6982 viewportSize.width = viewportSize.width.value_or(fixedLayoutSize.width());
6983 viewportSize.height = viewportSize.height.value_or(fixedLayoutSize.height());
6984 return { *viewportSize.width, *viewportSize.height };
6985 }
6986
6987 // FIXME: the value returned should take into account the value of the overflow
6988 // property on the root element.
6989 auto visibleContentSizeIncludingScrollbars = visibleContentRectIncludingScrollbars().size();
6990 viewportSize.width = viewportSize.width.value_or(visibleContentSizeIncludingScrollbars.width());
6991 viewportSize.height = viewportSize.height.value_or(visibleContentSizeIncludingScrollbars.height());
6992 return { *viewportSize.width, *viewportSize.height };
6993}
6994
6995FloatSize LocalFrameView::sizeForCSSDynamicViewportUnits() const
6996{
6997 if (m_layoutViewportOverrideRect)
6998 return m_layoutViewportOverrideRect->size();
6999
7000 if (useFixedLayout())
7001 return fixedLayoutSize();
7002
7003 if (m_frame->settings().visualViewportEnabled())
7004 return unobscuredContentRectIncludingScrollbars().size();
7005
7006 return viewportConstrainedVisibleContentRect().size();
7007}
7008
7009bool LocalFrameView::shouldPlaceVerticalScrollbarOnLeft() const
7010{
7011 return renderView() && renderView()->shouldPlaceVerticalScrollbarOnLeft();
7012}
7013
7014bool LocalFrameView::isHorizontalWritingMode() const
7015{
7016 return renderView() && renderView()->writingMode().isHorizontal();
7017}
7018
7019TextStream& operator<<(TextStream& ts, const LocalFrameView& view)
7020{
7021 ts << view.debugDescription();
7022 return ts;
7023}
7024
7025void LocalFrameView::didFinishProhibitingScrollingWhenChangingContentSize()
7026{
7027 if (RefPtr document = m_frame->document()) {
7028 if (!document->needsStyleRecalc() && !needsLayout() && !layoutContext().isInLayout())
7029 updateScrollbars(scrollPosition());
7030 else
7031 m_needsDeferredScrollbarsUpdate = true;
7032 }
7033}
7034
7035float LocalFrameView::pageScaleFactor() const
7036{
7037 return m_frame->frameScaleFactor();
7038}
7039
7040void LocalFrameView::didStartScrollAnimation()
7041{
7042 if (RefPtr page = m_frame->page())
7043 page->scheduleRenderingUpdate({ RenderingUpdateStep::Scroll });
7044}
7045
7046void LocalFrameView::updateScrollbarSteps()
7047{
7048 RefPtr documentElement = m_frame->document() ? m_frame->document()->documentElement() : nullptr;
7049 CheckedPtr renderer = documentElement ? documentElement->renderBox() : nullptr;
7050 if (!renderer) {
7051 ScrollView::updateScrollbarSteps();
7052 return;
7053 }
7054
7055 LayoutRect paddedViewRect(LayoutPoint(), visibleSize());
7056 paddedViewRect.contract(renderer->scrollPaddingForViewportRect(paddedViewRect));
7057
7058 if (horizontalScrollbar()) {
7059 int pageStep = Scrollbar::pageStep(paddedViewRect.width());
7060 horizontalScrollbar()->setSteps(Scrollbar::pixelsPerLineStep(paddedViewRect.width()), pageStep);
7061
7062 }
7063 if (verticalScrollbar()) {
7064 int pageStep = Scrollbar::pageStep(paddedViewRect.height());
7065 verticalScrollbar()->setSteps(Scrollbar::pixelsPerLineStep(paddedViewRect.height()), pageStep);
7066 }
7067}
7068
7069OverscrollBehavior LocalFrameView::horizontalOverscrollBehavior() const
7070{
7071 auto* document = m_frame->document();
7072 auto* scrollingObject = document && document->documentElement() ? document->documentElement()->renderer() : nullptr;
7073 if (scrollingObject && renderView())
7074 return scrollingObject->style().overscrollBehaviorX();
7075 return OverscrollBehavior::Auto;
7076}
7077
7078OverscrollBehavior LocalFrameView::verticalOverscrollBehavior() const
7079{
7080 auto* document = m_frame->document();
7081 auto* scrollingObject = document && document->documentElement() ? document->documentElement()->renderer() : nullptr;
7082 if (scrollingObject && renderView())
7083 return scrollingObject->style().overscrollBehaviorY();
7084 return OverscrollBehavior::Auto;
7085}
7086
7087Color LocalFrameView::scrollbarThumbColorStyle() const
7088{
7089 RefPtr document = m_frame->document();
7090 auto* scrollingObject = document && document->documentElement() ? document->documentElement()->renderer() : nullptr;
7091 if (scrollingObject)
7092 return scrollingObject->style().usedScrollbarThumbColor();
7093 return { };
7094}
7095
7096Color LocalFrameView::scrollbarTrackColorStyle() const
7097{
7098 RefPtr document = m_frame->document();
7099 auto* scrollingObject = document && document->documentElement() ? document->documentElement()->renderer() : nullptr;
7100 if (scrollingObject)
7101 return scrollingObject->style().usedScrollbarTrackColor();
7102 return { };
7103}
7104
7105Style::ScrollbarGutter LocalFrameView::scrollbarGutterStyle() const
7106{
7107 auto* document = m_frame->document();
7108 CheckedPtr scrollingObject = document && document->documentElement() ? document->documentElement()->renderer() : nullptr;
7109 if (scrollingObject)
7110 return scrollingObject->style().scrollbarGutter();
7111 return CSS::Keyword::Auto { };
7112}
7113
7114ScrollbarWidth LocalFrameView::scrollbarWidthStyle() const
7115{
7116 auto* document = m_frame->document();
7117 CheckedPtr scrollingObject = document && document->documentElement() ? document->documentElement()->renderer() : nullptr;
7118 if (scrollingObject && renderView())
7119 return Style::toPlatform(scrollingObject->style().scrollbarWidth());
7120 return ScrollbarWidth::Auto;
7121}
7122
7123std::optional<ScrollbarColor> LocalFrameView::scrollbarColorStyle() const
7124{
7125 RefPtr document = m_frame->document();
7126 CheckedPtr scrollingObject = document && document->documentElement() ? document->documentElement()->renderer() : nullptr;
7127 if (scrollingObject && renderView()) {
7128 if (auto value = scrollingObject->style().scrollbarColor().tryValue()) {
7129 Style::ColorResolver colorResolver { scrollingObject->style() };
7130 return ScrollbarColor {
7131 .thumbColor = colorResolver.colorResolvingCurrentColor(value->thumb),
7132 .trackColor = colorResolver.colorResolvingCurrentColor(value->track)
7133 };
7134 }
7135 }
7136 return { };
7137}
7138
7139bool LocalFrameView::isVisibleToHitTesting() const
7140{
7141 bool isVisibleToHitTest = true;
7142 if (RefPtr owner = m_frame->ownerElement())
7143 isVisibleToHitTest = owner->renderer() && owner->renderer()->visibleToHitTesting();
7144 return isVisibleToHitTest;
7145}
7146
7147LayoutRect LocalFrameView::getPossiblyFixedRectToExpose(const LayoutRect& visibleRect, const LayoutRect& exposeRect, EnumSet<BoxAxis> isFixed, const ScrollAlignment& alignX, const ScrollAlignment& alignY) const
7148{
7149 if (!isFixed.containsAny({ BoxAxis::Horizontal, BoxAxis::Vertical }))
7150 return getPossiblyFixedRectToExpose(visibleRect, exposeRect, false, alignX, alignY);
7151 if (isFixed.containsAll({ BoxAxis::Horizontal, BoxAxis::Vertical }))
7152 return getPossiblyFixedRectToExpose(visibleRect, exposeRect, true, alignX, alignY);
7153
7154 auto fixedRect = getPossiblyFixedRectToExpose(visibleRect, exposeRect, true, alignX, alignY);
7155 auto scrollRect = getPossiblyFixedRectToExpose(visibleRect, exposeRect, false, alignX, alignY);
7156 if (isFixed.contains(BoxAxis::Horizontal))
7157 return { fixedRect.x(), scrollRect.y(), fixedRect.width(), scrollRect.height() };
7158 return { scrollRect.x(), fixedRect.y(), scrollRect.width(), fixedRect.height() };
7159}
7160
7161LayoutRect LocalFrameView::getPossiblyFixedRectToExpose(const LayoutRect& visibleRect, const LayoutRect& exposeRect, bool isFixed, const ScrollAlignment& alignX, const ScrollAlignment& alignY) const
7162{
7163 if (!isFixed)
7164 return getRectToExposeForScrollIntoView(visibleRect, exposeRect, alignX, alignY);
7165
7166 // If the element is inside position:fixed and we're not scaled, no amount of scrolling is going to move things around.
7167 if (frameScaleFactor() == 1)
7168 return visibleRect;
7169
7170 // FIXME: Shouldn't this return visibleRect as well?
7171 if (!m_frame->settings().visualViewportEnabled())
7172 return getRectToExposeForScrollIntoView(visibleRect, exposeRect, alignX, alignY);
7173
7174 // exposeRect is in absolute coords, affected by page scale. Unscale it.
7175 auto unscaledExposeRect = exposeRect;
7176 unscaledExposeRect.scale(1 / frameScaleFactor());
7177 unscaledExposeRect.move(0, -headerHeight());
7178
7179 // These are both in unscaled coordinates.
7180 auto layoutViewport = layoutViewportRect();
7181 auto visualViewport = visualViewportRect();
7182
7183 // The rect to expose may be partially offscreen, which we can't do anything about with position:fixed.
7184 unscaledExposeRect.intersect(layoutViewport);
7185
7186 // Make sure it's not larger than the visual viewport; if so, we'll just move to the top left.
7187 unscaledExposeRect.setSize(unscaledExposeRect.size().shrunkTo(visualViewport.size()));
7188
7189 // Compute how much we have to move the visualViewport to reveal the part of the layoutViewport that contains exposeRect.
7190 auto requiredVisualViewport = getRectToExposeForScrollIntoView(visualViewport, unscaledExposeRect, alignX, alignY);
7191
7192 // Scale it back up.
7193 requiredVisualViewport.scale(frameScaleFactor());
7194 requiredVisualViewport.move(0, headerHeight());
7195 return requiredVisualViewport;
7196}
7197
7198float LocalFrameView::deviceScaleFactor() const
7199{
7200 if (auto* page = m_frame->page())
7201 return page->deviceScaleFactor();
7202 return 1;
7203}
7204
7205void LocalFrameView::writeRenderTreeAsText(TextStream& ts, OptionSet<RenderAsTextFlag> behavior)
7206{
7207 externalRepresentationForLocalFrame(ts, frame(), behavior);
7208}
7209
7210void LocalFrameView::scrollbarStyleDidChange()
7211{
7212 if (auto scrollableAreas = this->scrollableAreas()) {
7213 for (CheckedRef scrollableArea : *scrollableAreas)
7214 scrollableArea->scrollbarsController().updateScrollbarStyle();
7215 }
7216 scrollbarsController().updateScrollbarStyle();
7217}
7218
7219std::optional<FrameIdentifier> LocalFrameView::rootFrameID() const
7220{
7221 return m_frame->rootFrame().frameID();
7222}
7223
7224void LocalFrameView::scrollbarWidthChanged(ScrollbarWidth width)
7225{
7226 scrollbarsController().scrollbarWidthChanged(width);
7227 m_needsDeferredScrollbarsUpdate = true;
7228}
7229
7230int LocalFrameView::scrollbarGutterWidth(bool isHorizontalWritingMode) const
7231{
7232 if (verticalScrollbar() && verticalScrollbar()->isOverlayScrollbar())
7233 return 0;
7234
7235 if (!verticalScrollbar() && !(scrollbarGutterStyle().isAuto() || ScrollbarTheme::theme().usesOverlayScrollbars()) && isHorizontalWritingMode)
7236 return ScrollbarTheme::theme().scrollbarThickness(scrollbarWidthStyle());
7237
7238 if (!verticalScrollbar())
7239 return 0;
7240
7241 return verticalScrollbar()->width();
7242}
7243
7244IntSize LocalFrameView::totalScrollbarSpace() const
7245{
7246 IntSize scrollbarGutter = { horizontalScrollbarIntrusion(), verticalScrollbarIntrusion() };
7247
7248 if (isHorizontalWritingMode()) {
7249 if (scrollbarGutterStyle().isStableBothEdges())
7250 scrollbarGutter.setWidth(scrollbarGutterWidth() * 2);
7251 else
7252 scrollbarGutter.setWidth(scrollbarGutterWidth());
7253 }
7254 return scrollbarGutter;
7255}
7256
7257int LocalFrameView::insetForLeftScrollbarSpace() const
7258{
7259 if (scrollbarGutterStyle().isStableBothEdges())
7260 return scrollbarGutterWidth();
7261 if (shouldPlaceVerticalScrollbarOnLeft())
7262 return verticalScrollbar() ? verticalScrollbar()->occupiedWidth() : 0;
7263 return 0;
7264}
7265
7266} // namespace WebCore
7267
7268#undef PAGE_ID
7269#undef FRAME_ID