Bug Summary

File:root/firefox-clang/obj-x86_64-pc-linux-gnu/layout/generic/./../../../layout/generic/nsIFrame.cpp
Warning:line 3821, column 7
Value stored to 'createdContainer' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -O2 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name Unified_cpp_layout_generic2.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -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 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/layout/generic -fcoverage-compilation-dir=/root/firefox-clang/obj-x86_64-pc-linux-gnu/layout/generic -resource-dir /usr/lib/llvm-23/lib/clang/23 -include /root/firefox-clang/config/gcc_hidden.h -include /root/firefox-clang/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -D _GLIBCXX_ASSERTIONS=1 -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D MOZ_HAS_MOZGLUE -D MOZILLA_INTERNAL_API -D IMPL_LIBXUL -D MOZ_SUPPORT_LEAKCHECKING -D STATIC_EXPORTABLE_JS_API -I /root/firefox-clang/layout/generic -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/layout/generic -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/ipc/ipdl/_ipdlheaders -I /root/firefox-clang/ipc/chromium/src -I /root/firefox-clang/third_party/abseil-cpp -I /root/firefox-clang/toolkit/components/telemetry -I /root/firefox-clang/xpcom/base -I /root/firefox-clang/layout/base -I /root/firefox-clang/layout/forms -I /root/firefox-clang/layout/painting -I /root/firefox-clang/layout/style -I /root/firefox-clang/layout/tables -I /root/firefox-clang/layout/xul -I /root/firefox-clang/docshell/base -I /root/firefox-clang/dom/base -I /root/firefox-clang/dom/html -I /root/firefox-clang/dom/xul -I /root/firefox-clang/gfx/cairo/cairo/src -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /root/firefox-clang/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -I /usr/include/gtk-3.0/unix-print -I /usr/include/gtk-3.0 -I /usr/include/pango-1.0 -I /usr/include/cloudproviders -I /usr/include/at-spi2-atk/2.0 -I /usr/include/at-spi-2.0 -I /usr/include/dbus-1.0 -I /usr/lib/x86_64-linux-gnu/dbus-1.0/include -I /usr/include/fribidi -I /usr/include/harfbuzz -I /usr/include/atk-1.0 -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/glycin-2 -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/gio-unix-2.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/sysprof-6 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/x86_64-linux-gnu/c++/16 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../include/c++/16/backward -internal-isystem /usr/lib/llvm-23/lib/clang/23/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/16/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wno-error=pessimizing-move -Wno-error=large-by-value-copy=128 -Wno-error=implicit-int-float-conversion -Wno-error=thread-safety-analysis -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -Wno-character-conversion -std=gnu++20 -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fno-implicit-modules -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -fdiagnostics-absolute-paths -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -mllvm -dwarf-linkage-names=Abstract -faddrsig -fdwarf2-cfi-asm -o /tmp/scan-build-2026-09-01-224014-2642839-1 -x c++ Unified_cpp_layout_generic2.cpp
1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5/* base class of all rendering objects */
6
7#include "nsIFrame.h"
8
9#include <stdarg.h>
10
11#include <algorithm>
12
13#include "AnchorPositioningUtils.h"
14#include "LayoutLogging.h"
15#include "PseudoStyleType.h"
16#include "RubyUtils.h"
17#include "TextOverflow.h"
18#include "gfx2DGlue.h"
19#include "gfxUtils.h"
20#include "mozilla/AbsoluteContainingBlock.h"
21#include "mozilla/Attributes.h"
22#include "mozilla/CaretAssociationHint.h"
23#include "mozilla/ComputedStyle.h"
24#include "mozilla/DebugOnly.h"
25#include "mozilla/DisplayPortUtils.h"
26#include "mozilla/EventForwards.h"
27#include "mozilla/FocusModel.h"
28#include "mozilla/IntegerRange.h"
29#include "mozilla/Logging.h"
30#include "mozilla/Maybe.h"
31#include "mozilla/PresShell.h"
32#include "mozilla/PresShellInlines.h"
33#include "mozilla/ReflowInput.h"
34#include "mozilla/RestyleManager.h"
35#include "mozilla/ResultExtensions.h"
36#include "mozilla/SVGIntegrationUtils.h"
37#include "mozilla/SVGMaskFrame.h"
38#include "mozilla/SVGObserverUtils.h"
39#include "mozilla/SVGTextFrame.h"
40#include "mozilla/SVGUtils.h"
41#include "mozilla/ScrollContainerFrame.h"
42#include "mozilla/SelectionMovementUtils.h"
43#include "mozilla/ServoStyleConsts.h"
44#include "mozilla/Sprintf.h"
45#include "mozilla/StaticAnalysisFunctions.h"
46#include "mozilla/StaticPrefs_dom.h"
47#include "mozilla/StaticPrefs_layout.h"
48#include "mozilla/StaticPrefs_print.h"
49#include "mozilla/StaticPrefs_ui.h"
50#include "mozilla/TextControlElement.h"
51#include "mozilla/ToString.h"
52#include "mozilla/Try.h"
53#include "mozilla/ViewportFrame.h"
54#include "mozilla/ViewportUtils.h"
55#include "mozilla/WritingModes.h"
56#include "mozilla/dom/AncestorIterator.h"
57#include "mozilla/dom/CSSAnimation.h"
58#include "mozilla/dom/CSSTransition.h"
59#include "mozilla/dom/ContentVisibilityAutoStateChangeEvent.h"
60#include "mozilla/dom/DocumentInlines.h"
61#include "mozilla/dom/ElementInlines.h"
62#include "mozilla/dom/HTMLDetailsElement.h"
63#include "mozilla/dom/Selection.h"
64#include "mozilla/dom/Text.h"
65#include "mozilla/gfx/2D.h"
66#include "mozilla/gfx/PathHelpers.h"
67#include "mozilla/intl/BidiEmbeddingLevel.h"
68#include "nsAnimationManager.h"
69#include "nsAtom.h"
70#include "nsBidiPresUtils.h"
71#include "nsCOMPtr.h"
72#include "nsCSSFrameConstructor.h"
73#include "nsCSSProps.h"
74#include "nsCSSRendering.h"
75#include "nsCanvasFrame.h"
76#include "nsContentUtils.h"
77#include "nsFieldSetFrame.h"
78#include "nsFlexContainerFrame.h"
79#include "nsFocusManager.h"
80#include "nsFrameList.h"
81#include "nsFrameSelection.h"
82#include "nsFrameState.h"
83#include "nsFrameTraversal.h"
84#include "nsGkAtoms.h"
85#include "nsGridContainerFrame.h"
86#include "nsIBaseWindow.h"
87#include "nsIContent.h"
88#include "nsIContentInlines.h"
89#include "nsIPercentBSizeObserver.h"
90#include "nsImageFrame.h"
91#include "nsInlineFrame.h"
92#include "nsLayoutUtils.h"
93#include "nsMenuPopupFrame.h"
94#include "nsNameSpaceManager.h"
95#include "nsPlaceholderFrame.h"
96#include "nsPresContext.h"
97#include "nsPresContextInlines.h"
98#include "nsRange.h"
99#include "nsReadableUtils.h"
100#include "nsString.h"
101#include "nsStyleConsts.h"
102#include "nsStyleStructInlines.h"
103#include "nsStyleTransformMatrix.h"
104#include "nsTableWrapperFrame.h"
105#include "nsTextControlFrame.h"
106#include "nsXULElement.h"
107
108// For triple-click pref
109#include "RetainedDisplayListBuilder.h"
110#include "ScrollSnap.h"
111#include "StickyScrollContainer.h"
112#include "gfxContext.h"
113#include "imgIRequest.h"
114#include "nsBlockFrame.h"
115#include "nsChangeHint.h"
116#include "nsContainerFrame.h"
117#include "nsDisplayList.h"
118#include "nsError.h"
119#include "nsFontInflationData.h"
120#include "nsIFrameInlines.h"
121#include "nsRegion.h"
122#include "nsStyleChangeList.h"
123#include "nsSubDocumentFrame.h"
124#include "nsViewportInfo.h"
125#include "nsWindowSizes.h"
126
127#ifdef ACCESSIBILITY1
128# include "nsAccessibilityService.h"
129#endif
130#if defined(ACCESSIBILITY1) && defined(MOZ_ENABLE_SKIA_PDF1)
131# include "mozilla/a11y/PdfStructTreeBuilder.h"
132#endif
133
134#include "ActiveLayerTracker.h"
135#include "mozilla/AsyncEventDispatcher.h"
136#include "mozilla/CSSClipPathInstance.h"
137#include "mozilla/EffectCompositor.h"
138#include "mozilla/EffectSet.h"
139#include "mozilla/EventListenerManager.h"
140#include "mozilla/EventStateManager.h"
141#include "mozilla/LookAndFeel.h"
142#include "mozilla/MouseEvents.h"
143#include "mozilla/Preferences.h"
144#include "mozilla/ServoStyleSet.h"
145#include "mozilla/ServoStyleSetInlines.h"
146#include "mozilla/css/ImageLoader.h"
147#include "mozilla/dom/HTMLBodyElement.h"
148#include "mozilla/dom/SVGPathData.h"
149#include "mozilla/dom/TouchEvent.h"
150#include "mozilla/gfx/Tools.h"
151#include "mozilla/layers/WebRenderUserData.h"
152#include "mozilla/layout/ScrollAnchorContainer.h"
153#include "nsITheme.h"
154#include "nsPrintfCString.h"
155
156using namespace mozilla;
157using namespace mozilla::css;
158using namespace mozilla::dom;
159using namespace mozilla::gfx;
160using namespace mozilla::layers;
161using namespace mozilla::layout;
162using nsStyleTransformMatrix::TransformReferenceBox;
163
164nsIFrame* nsILineIterator::LineInfo::GetLastFrameOnLine() const {
165 if (!mNumFramesOnLine) {
166 return nullptr; // empty line, not illegal
167 }
168 MOZ_ASSERT(mFirstFrameOnLine)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mFirstFrameOnLine)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mFirstFrameOnLine))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("mFirstFrameOnLine"
, "./../../../layout/generic/nsIFrame.cpp", 168); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "mFirstFrameOnLine" ")"); do { MOZ_CrashSequence
(__null, 168); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
169 nsIFrame* maybeLastFrame = mFirstFrameOnLine;
170 for ([[maybe_unused]] int32_t i : IntegerRange(mNumFramesOnLine - 1)) {
171 maybeLastFrame = maybeLastFrame->GetNextSibling();
172 if (NS_WARN_IF(!maybeLastFrame)NS_warn_if_impl(!maybeLastFrame, "!maybeLastFrame", "./../../../layout/generic/nsIFrame.cpp"
, 172)
) {
173 return nullptr;
174 }
175 }
176 return maybeLastFrame;
177}
178
179#ifdef HAVE_64BIT_BUILD1
180static_assert(sizeof(nsIFrame) == 120, "nsIFrame should remain small");
181#else
182static_assert(sizeof(void*) == 4, "Odd build config?");
183// FIXME(emilio): Investigate why win32 and android-arm32 have bigger sizes (80)
184// than Linux32 (76).
185static_assert(sizeof(nsIFrame) <= 80, "nsIFrame should remain small");
186#endif
187
188const mozilla::LayoutFrameType nsIFrame::sLayoutFrameTypes[kFrameClassCount] = {
189#define FRAME_ID(class_, type_, ...) mozilla::LayoutFrameType::type_,
190#define ABSTRACT_FRAME_ID(...)
191#include "mozilla/FrameIdList.h"
192#undef FRAME_ID
193#undef ABSTRACT_FRAME_ID
194};
195
196const nsIFrame::ClassFlags nsIFrame::sLayoutFrameClassFlags[kFrameClassCount] =
197 {
198#define FRAME_ID(class_, type_, flags_, ...) flags_,
199#define ABSTRACT_FRAME_ID(...)
200#include "mozilla/FrameIdList.h"
201#undef FRAME_ID
202#undef ABSTRACT_FRAME_ID
203};
204
205std::string format_as(nsDirection aDirection) {
206 return aDirection == eDirNext ? "eDirNext" : "eDirPrevious";
207}
208
209std::ostream& operator<<(std::ostream& aStream, nsDirection aDirection) {
210 return aStream << format_as(aDirection);
211}
212
213struct nsContentAndOffset {
214 nsIContent* mContent = nullptr;
215 int32_t mOffset = 0;
216};
217
218#include "nsILineIterator.h"
219#include "prenv.h"
220
221FrameDestroyContext::~FrameDestroyContext() {
222 for (auto& content : mozilla::Reversed(mAnonymousContent)) {
223 mPresShell->NativeAnonymousContentWillBeRemoved(content);
224 content->UnbindFromTree();
225 }
226}
227
228// Formerly the nsIFrameDebug interface
229
230std::ostream& operator<<(std::ostream& aStream, const nsReflowStatus& aStatus) {
231 char complete = 'Y';
232 if (aStatus.IsIncomplete()) {
233 complete = 'N';
234 } else if (aStatus.IsOverflowIncomplete()) {
235 complete = 'O';
236 }
237
238 char brk = 'N';
239 if (aStatus.IsInlineBreakBefore()) {
240 brk = 'B';
241 } else if (aStatus.IsInlineBreakAfter()) {
242 brk = 'A';
243 }
244
245 aStream << "["
246 << "Complete=" << complete << ","
247 << "NIF=" << (aStatus.NextInFlowNeedsReflow() ? 'Y' : 'N') << ","
248 << "Break=" << brk << ","
249 << "FirstLetter=" << (aStatus.FirstLetterComplete() ? 'Y' : 'N')
250 << "]";
251 return aStream;
252}
253
254#ifdef DEBUG1
255
256/**
257 * Note: the log module is created during library initialization which
258 * means that you cannot perform logging before then.
259 */
260mozilla::LazyLogModule nsIFrame::sFrameLogModule("frame");
261
262#endif
263
264NS_DECLARE_FRAME_PROPERTY_DELETABLE(AbsoluteContainingBlockProperty,static const mozilla::FramePropertyDescriptor<AbsoluteContainingBlock
>* AbsoluteContainingBlockProperty() { static const auto descriptor
= mozilla::FramePropertyDescriptor<AbsoluteContainingBlock
>::NewWithDestructor<DeleteValue>(); return &descriptor
; }
265 AbsoluteContainingBlock)static const mozilla::FramePropertyDescriptor<AbsoluteContainingBlock
>* AbsoluteContainingBlockProperty() { static const auto descriptor
= mozilla::FramePropertyDescriptor<AbsoluteContainingBlock
>::NewWithDestructor<DeleteValue>(); return &descriptor
; }
266
267bool nsIFrame::HasAbsolutelyPositionedChildren() const {
268 const auto* absCB = GetAbsoluteContainingBlock();
269 return absCB && absCB->HasAbsoluteFrames();
270}
271
272AbsoluteContainingBlock* nsIFrame::GetAbsoluteContainingBlock() const {
273 if (!IsAbsoluteContainer()) {
274 return nullptr;
275 }
276 AbsoluteContainingBlock* absCB =
277 GetProperty(AbsoluteContainingBlockProperty());
278 NS_ASSERTION(absCB,do { if (!(absCB)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "The frame is marked as an abspos container but doesn't have "
"the property", "absCB", "./../../../layout/generic/nsIFrame.cpp"
, 280); MOZ_PretendNoReturn(); } } while (0)
279 "The frame is marked as an abspos container but doesn't have "do { if (!(absCB)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "The frame is marked as an abspos container but doesn't have "
"the property", "absCB", "./../../../layout/generic/nsIFrame.cpp"
, 280); MOZ_PretendNoReturn(); } } while (0)
280 "the property")do { if (!(absCB)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "The frame is marked as an abspos container but doesn't have "
"the property", "absCB", "./../../../layout/generic/nsIFrame.cpp"
, 280); MOZ_PretendNoReturn(); } } while (0)
;
281 return absCB;
282}
283
284void nsIFrame::MarkAsAbsoluteContainingBlock() {
285 MOZ_ASSERT(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN))>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)"
, "./../../../layout/generic/nsIFrame.cpp", 285); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)"
")"); do { MOZ_CrashSequence(__null, 285); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
286 NS_ASSERTION(!GetProperty(AbsoluteContainingBlockProperty()),do { if (!(!GetProperty(AbsoluteContainingBlockProperty()))) {
NS_DebugBreak(NS_DEBUG_ASSERTION, "Already has an abs-pos containing block property?"
, "!GetProperty(AbsoluteContainingBlockProperty())", "./../../../layout/generic/nsIFrame.cpp"
, 287); MOZ_PretendNoReturn(); } } while (0)
287 "Already has an abs-pos containing block property?")do { if (!(!GetProperty(AbsoluteContainingBlockProperty()))) {
NS_DebugBreak(NS_DEBUG_ASSERTION, "Already has an abs-pos containing block property?"
, "!GetProperty(AbsoluteContainingBlockProperty())", "./../../../layout/generic/nsIFrame.cpp"
, 287); MOZ_PretendNoReturn(); } } while (0)
;
288 NS_ASSERTION(!HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN),do { if (!(!HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN))) {
NS_DebugBreak(NS_DEBUG_ASSERTION, "Already has NS_FRAME_HAS_ABSPOS_CHILDREN state bit?"
, "!HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN)", "./../../../layout/generic/nsIFrame.cpp"
, 289); MOZ_PretendNoReturn(); } } while (0)
289 "Already has NS_FRAME_HAS_ABSPOS_CHILDREN state bit?")do { if (!(!HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN))) {
NS_DebugBreak(NS_DEBUG_ASSERTION, "Already has NS_FRAME_HAS_ABSPOS_CHILDREN state bit?"
, "!HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN)", "./../../../layout/generic/nsIFrame.cpp"
, 289); MOZ_PretendNoReturn(); } } while (0)
;
290 AddStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN);
291 SetProperty(AbsoluteContainingBlockProperty(), new AbsoluteContainingBlock());
292}
293
294void nsIFrame::MarkAsNotAbsoluteContainingBlock() {
295 NS_ASSERTION(!HasAbsolutelyPositionedChildren(), "Think of the children!")do { if (!(!HasAbsolutelyPositionedChildren())) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "Think of the children!", "!HasAbsolutelyPositionedChildren()"
, "./../../../layout/generic/nsIFrame.cpp", 295); MOZ_PretendNoReturn
(); } } while (0)
;
296 NS_ASSERTION(GetProperty(AbsoluteContainingBlockProperty()),do { if (!(GetProperty(AbsoluteContainingBlockProperty()))) {
NS_DebugBreak(NS_DEBUG_ASSERTION, "Should have an abs-pos containing block property"
, "GetProperty(AbsoluteContainingBlockProperty())", "./../../../layout/generic/nsIFrame.cpp"
, 297); MOZ_PretendNoReturn(); } } while (0)
297 "Should have an abs-pos containing block property")do { if (!(GetProperty(AbsoluteContainingBlockProperty()))) {
NS_DebugBreak(NS_DEBUG_ASSERTION, "Should have an abs-pos containing block property"
, "GetProperty(AbsoluteContainingBlockProperty())", "./../../../layout/generic/nsIFrame.cpp"
, 297); MOZ_PretendNoReturn(); } } while (0)
;
298 NS_ASSERTION(HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN),do { if (!(HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN))) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "Should have NS_FRAME_HAS_ABSPOS_CHILDREN state bit"
, "HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN)", "./../../../layout/generic/nsIFrame.cpp"
, 299); MOZ_PretendNoReturn(); } } while (0)
299 "Should have NS_FRAME_HAS_ABSPOS_CHILDREN state bit")do { if (!(HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN))) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "Should have NS_FRAME_HAS_ABSPOS_CHILDREN state bit"
, "HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN)", "./../../../layout/generic/nsIFrame.cpp"
, 299); MOZ_PretendNoReturn(); } } while (0)
;
300 MOZ_ASSERT(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN))>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)"
, "./../../../layout/generic/nsIFrame.cpp", 300); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)"
")"); do { MOZ_CrashSequence(__null, 300); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
301 RemoveStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN);
302 RemoveProperty(AbsoluteContainingBlockProperty());
303}
304
305bool nsIFrame::CheckAndClearPaintedState() {
306 bool result = HasAnyStateBits(NS_FRAME_PAINTED_THEBES);
307 RemoveStateBits(NS_FRAME_PAINTED_THEBES);
308
309 for (const auto& childList : ChildLists()) {
310 for (nsIFrame* child : childList.mList) {
311 if (child->CheckAndClearPaintedState()) {
312 result = true;
313 }
314 }
315 }
316 return result;
317}
318
319nsIFrame* nsIFrame::FindLineContainer() const {
320 MOZ_ASSERT(IsLineParticipant())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsLineParticipant())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsLineParticipant()))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("IsLineParticipant()"
, "./../../../layout/generic/nsIFrame.cpp", 320); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsLineParticipant()" ")"); do { MOZ_CrashSequence
(__null, 320); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
321 nsIFrame* parent = GetParent();
322 while (parent &&
323 (parent->IsLineParticipant() || parent->CanContinueTextRun())) {
324 parent = parent->GetParent();
325 }
326 return parent;
327}
328
329bool nsIFrame::CheckAndClearDisplayListState() {
330 bool result = BuiltDisplayList();
331 SetBuiltDisplayList(false);
332
333 for (const auto& childList : ChildLists()) {
334 for (nsIFrame* child : childList.mList) {
335 if (child->CheckAndClearDisplayListState()) {
336 result = true;
337 }
338 }
339 }
340 return result;
341}
342
343bool nsIFrame::IsVisibleConsideringAncestors(uint32_t aFlags) const {
344 if (!StyleVisibility()->IsVisible() ||
345 HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
346 return false;
347 }
348
349 if (PresShell()->IsUnderHiddenEmbedderElement()) {
350 return false;
351 }
352
353 const nsIFrame* frame = this;
354 while (frame) {
355 // Checking mMozSubtreeHiddenOnlyVisually is relatively slow because it
356 // involves loading more memory. It's only allowed in chrome sheets so let's
357 // only support it in the parent process so we can mostly optimize this out
358 // in content processes.
359 if (XRE_IsParentProcess()) {
360 if (const nsMenuPopupFrame* popup = do_QueryFrame(frame);
361 popup && !popup->IsOpen()) {
362 return false;
363 }
364 if (frame->StyleUIReset()->mMozSubtreeHiddenOnlyVisually) {
365 return false;
366 }
367 }
368
369 // This method is used to determine if a frame is focusable, because it's
370 // called by nsIFrame::IsFocusable. `content-visibility: auto` should not
371 // force this frame to be unfocusable, so we only take into account
372 // `content-visibility: hidden` here.
373 if (this != frame &&
374 frame->HidesContent(IncludeContentVisibility::Hidden)) {
375 return false;
376 }
377
378 if (nsIFrame* parent = frame->GetParent()) {
379 frame = parent;
380 } else {
381 parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(frame);
382 if (!parent) {
383 break;
384 }
385
386 if ((aFlags & nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) == 0 &&
387 parent->PresContext()->IsChrome() &&
388 !frame->PresContext()->IsChrome()) {
389 break;
390 }
391
392 frame = parent;
393 }
394 }
395
396 return true;
397}
398
399void nsIFrame::FindCloserFrameForSelection(
400 const nsPoint& aPoint, FrameWithDistance* aCurrentBestFrame) {
401 if (nsLayoutUtils::PointIsCloserToRect(aPoint, mRect,
402 aCurrentBestFrame->mXDistance,
403 aCurrentBestFrame->mYDistance)) {
404 aCurrentBestFrame->mFrame = this;
405 }
406}
407
408void nsIFrame::ElementStateChanged(mozilla::dom::ElementState aStates) {}
409
410void WeakFrame::Clear(mozilla::PresShell* aPresShell) {
411 if (aPresShell) {
412 aPresShell->RemoveWeakFrame(this);
413 }
414 mFrame = nullptr;
415}
416
417AutoWeakFrame::AutoWeakFrame(const WeakFrame& aOther)
418 : mPrev(nullptr), mFrame(nullptr) {
419 Init(aOther.GetFrame());
420}
421
422void AutoWeakFrame::Clear(mozilla::PresShell* aPresShell) {
423 if (aPresShell) {
424 aPresShell->RemoveAutoWeakFrame(this);
425 }
426 mFrame = nullptr;
427 mPrev = nullptr;
428}
429
430AutoWeakFrame::~AutoWeakFrame() {
431 Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
432}
433
434void AutoWeakFrame::Init(nsIFrame* aFrame) {
435 Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
436 mFrame = aFrame;
437 if (mFrame) {
438 mozilla::PresShell* presShell = mFrame->PresContext()->GetPresShell();
439 NS_WARNING_ASSERTION(presShell, "Null PresShell in AutoWeakFrame!")do { if (!(presShell)) { NS_DebugBreak(NS_DEBUG_WARNING, "Null PresShell in AutoWeakFrame!"
, "presShell", "./../../../layout/generic/nsIFrame.cpp", 439)
; } } while (false)
;
440 if (presShell) {
441 presShell->AddAutoWeakFrame(this);
442 } else {
443 mFrame = nullptr;
444 }
445 }
446}
447
448void WeakFrame::Init(nsIFrame* aFrame) {
449 Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
450 mFrame = aFrame;
451 if (mFrame) {
452 mozilla::PresShell* presShell = mFrame->PresContext()->GetPresShell();
453 MOZ_ASSERT(presShell, "Null PresShell in WeakFrame!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(presShell)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(presShell))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("presShell" " (" "Null PresShell in WeakFrame!"
")", "./../../../layout/generic/nsIFrame.cpp", 453); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "presShell" ") (" "Null PresShell in WeakFrame!"
")"); do { MOZ_CrashSequence(__null, 453); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
454 if (presShell) {
455 presShell->AddWeakFrame(this);
456 } else {
457 mFrame = nullptr;
458 }
459 }
460}
461
462nsIFrame* NS_NewEmptyFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
463 return new (aPresShell) nsIFrame(aStyle, aPresShell->GetPresContext());
464}
465
466nsIFrame::~nsIFrame() {
467 MOZ_COUNT_DTOR(nsIFrame)do { static_assert(std::is_class_v<nsIFrame>, "Token '"
"nsIFrame" "' is not a class type."); static_assert(!std::is_base_of_v
<nsISupports, nsIFrame>, "nsISupports classes don't need to call MOZ_COUNT_CTOR or "
"MOZ_COUNT_DTOR");; NS_LogDtor((void*)this, "nsIFrame", sizeof
(*this)); } while (0)
;
468
469 MOZ_ASSERT(GetVisibility() != Visibility::ApproximatelyVisible,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(GetVisibility() != Visibility::ApproximatelyVisible)
>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(GetVisibility() != Visibility::ApproximatelyVisible)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("GetVisibility() != Visibility::ApproximatelyVisible"
" (" "Visible nsIFrame is being destroyed" ")", "./../../../layout/generic/nsIFrame.cpp"
, 470); AnnotateMozCrashReason("MOZ_ASSERT" "(" "GetVisibility() != Visibility::ApproximatelyVisible"
") (" "Visible nsIFrame is being destroyed" ")"); do { MOZ_CrashSequence
(__null, 470); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
470 "Visible nsIFrame is being destroyed")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(GetVisibility() != Visibility::ApproximatelyVisible)
>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(GetVisibility() != Visibility::ApproximatelyVisible)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("GetVisibility() != Visibility::ApproximatelyVisible"
" (" "Visible nsIFrame is being destroyed" ")", "./../../../layout/generic/nsIFrame.cpp"
, 470); AnnotateMozCrashReason("MOZ_ASSERT" "(" "GetVisibility() != Visibility::ApproximatelyVisible"
") (" "Visible nsIFrame is being destroyed" ")"); do { MOZ_CrashSequence
(__null, 470); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
471}
472
473NS_IMPL_FRAMEARENA_HELPERS(nsIFrame)void* nsIFrame ::operator new(size_t sz, mozilla::PresShell *
aShell) { return aShell->AllocateFrame(nsQueryFrame::nsIFrame_id
, sz); }
474
475// Dummy operator delete. Will never be called, but must be defined
476// to satisfy some C++ ABIs.
477void nsIFrame::operator delete(void*, size_t) {
478 MOZ_CRASH("nsIFrame::operator delete should never be called")do { do { } while (false); MOZ_ReportCrash("" "nsIFrame::operator delete should never be called"
, "./../../../layout/generic/nsIFrame.cpp", 478); AnnotateMozCrashReason
("MOZ_CRASH(" "nsIFrame::operator delete should never be called"
")"); do { MOZ_CrashSequence(__null, 478); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
479}
480
481NS_QUERYFRAME_HEAD(nsIFrame)void* nsIFrame ::QueryFrame(FrameIID id) const { switch (id) {
482 NS_QUERYFRAME_ENTRY(nsIFrame)case nsIFrame ::kFrameIID: { static_assert( std::is_same_v<
nsIFrame, nsIFrame ::Has_NS_DECL_QUERYFRAME_TARGET>, "nsIFrame"
" must declare itself as a queryframe target"); return const_cast
<nsIFrame*>(static_cast<const nsIFrame*>(this)); }
483NS_QUERYFRAME_TAIL_INHERITANCE_ROOTdefault: break; } do { static_assert( mozilla::detail::AssertionConditionType
<decltype(id != GetFrameId())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(id != GetFrameId()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("id != GetFrameId()"
" (" "A frame failed to QueryFrame to its *own type*. " "It may be missing NS_DECL_QUERYFRAME, or a "
"NS_QUERYFRAME_ENTRY() line with its own type name" ")", "./../../../layout/generic/nsIFrame.cpp"
, 483); AnnotateMozCrashReason("MOZ_ASSERT" "(" "id != GetFrameId()"
") (" "A frame failed to QueryFrame to its *own type*. " "It may be missing NS_DECL_QUERYFRAME, or a "
"NS_QUERYFRAME_ENTRY() line with its own type name" ")"); do
{ MOZ_CrashSequence(__null, 483); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false); return nullptr; }
484
485/////////////////////////////////////////////////////////////////////////////
486// nsIFrame
487
488static bool IsFontSizeInflationContainer(nsIFrame* aFrame,
489 const nsStyleDisplay* aStyleDisplay) {
490 /*
491 * Font size inflation is built around the idea that we're inflating
492 * the fonts for a pan-and-zoom UI so that when the user scales up a
493 * block or other container to fill the width of the device, the fonts
494 * will be readable. To do this, we need to pick what counts as a
495 * container.
496 *
497 * From a code perspective, the only hard requirement is that frames
498 * that are line participants (nsIFrame::IsLineParticipant) are never
499 * containers, since line layout assumes that the inflation is consistent
500 * within a line.
501 *
502 * This is not an imposition, since we obviously want a bunch of text
503 * (possibly with inline elements) flowing within a block to count the
504 * block (or higher) as its container.
505 *
506 * We also want form controls, including the text in the anonymous
507 * content inside of them, to match each other and the text next to
508 * them, so they and their anonymous content should also not be a
509 * container.
510 *
511 * However, because we can't reliably compute sizes across XUL during
512 * reflow, any XUL frame with a XUL parent is always a container.
513 *
514 * There are contexts where it would be nice if some blocks didn't
515 * count as a container, so that, for example, an indented quotation
516 * didn't end up with a smaller font size. However, it's hard to
517 * distinguish these situations where we really do want the indented
518 * thing to count as a container, so we don't try, and blocks are
519 * always containers.
520 */
521
522 // The root frame should always be an inflation container.
523 if (!aFrame->GetParent()) {
524 return true;
525 }
526
527 nsIContent* content = aFrame->GetContent();
528 if (content && content->IsInNativeAnonymousSubtree()) {
529 // Native anonymous content shouldn't be a font inflation root,
530 // except for the custom content container.
531 return content ==
532 aFrame->PresContext()->Document()->GetCustomContentContainer();
533 }
534
535 LayoutFrameType frameType = aFrame->Type();
536 bool isInline =
537 aFrame->GetDisplay().IsInlineFlow() || RubyUtils::IsRubyBox(frameType) ||
538 (aStyleDisplay->IsFloatingStyle() &&
539 frameType == LayoutFrameType::Letter) ||
540 // Given multiple frames for the same node, only the
541 // outer one should be considered a container.
542 // (Important, e.g., for scrolled frames.)
543 (aFrame->GetParent()->GetContent() == content) ||
544 (content &&
545 // Form controls shouldn't become inflation containers.
546 (content->IsAnyOfHTMLElements(nsGkAtoms::option, nsGkAtoms::optgroup,
547 nsGkAtoms::select, nsGkAtoms::input,
548 nsGkAtoms::button, nsGkAtoms::textarea)));
549 NS_ASSERTION(!aFrame->IsLineParticipant() || isInline ||do { if (!(!aFrame->IsLineParticipant() || isInline || aFrame
->IsBrFrame() || aFrame->IsMathMLFrame())) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "line participants must not be containers"
, "!aFrame->IsLineParticipant() || isInline || aFrame->IsBrFrame() || aFrame->IsMathMLFrame()"
, "./../../../layout/generic/nsIFrame.cpp", 554); MOZ_PretendNoReturn
(); } } while (0)
550 // br frames and mathml frames report being linedo { if (!(!aFrame->IsLineParticipant() || isInline || aFrame
->IsBrFrame() || aFrame->IsMathMLFrame())) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "line participants must not be containers"
, "!aFrame->IsLineParticipant() || isInline || aFrame->IsBrFrame() || aFrame->IsMathMLFrame()"
, "./../../../layout/generic/nsIFrame.cpp", 554); MOZ_PretendNoReturn
(); } } while (0)
551 // participants even when their position or display isdo { if (!(!aFrame->IsLineParticipant() || isInline || aFrame
->IsBrFrame() || aFrame->IsMathMLFrame())) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "line participants must not be containers"
, "!aFrame->IsLineParticipant() || isInline || aFrame->IsBrFrame() || aFrame->IsMathMLFrame()"
, "./../../../layout/generic/nsIFrame.cpp", 554); MOZ_PretendNoReturn
(); } } while (0)
552 // setdo { if (!(!aFrame->IsLineParticipant() || isInline || aFrame
->IsBrFrame() || aFrame->IsMathMLFrame())) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "line participants must not be containers"
, "!aFrame->IsLineParticipant() || isInline || aFrame->IsBrFrame() || aFrame->IsMathMLFrame()"
, "./../../../layout/generic/nsIFrame.cpp", 554); MOZ_PretendNoReturn
(); } } while (0)
553 aFrame->IsBrFrame() || aFrame->IsMathMLFrame(),do { if (!(!aFrame->IsLineParticipant() || isInline || aFrame
->IsBrFrame() || aFrame->IsMathMLFrame())) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "line participants must not be containers"
, "!aFrame->IsLineParticipant() || isInline || aFrame->IsBrFrame() || aFrame->IsMathMLFrame()"
, "./../../../layout/generic/nsIFrame.cpp", 554); MOZ_PretendNoReturn
(); } } while (0)
554 "line participants must not be containers")do { if (!(!aFrame->IsLineParticipant() || isInline || aFrame
->IsBrFrame() || aFrame->IsMathMLFrame())) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "line participants must not be containers"
, "!aFrame->IsLineParticipant() || isInline || aFrame->IsBrFrame() || aFrame->IsMathMLFrame()"
, "./../../../layout/generic/nsIFrame.cpp", 554); MOZ_PretendNoReturn
(); } } while (0)
;
555 return !isInline;
556}
557
558static void MaybeScheduleReflowSVGNonDisplayText(nsIFrame* aFrame) {
559 if (!aFrame->IsInSVGTextSubtree()) {
560 return;
561 }
562
563 // We need to ensure that any non-display SVGTextFrames get reflowed when a
564 // child text frame gets new style. Thus we need to schedule a reflow in
565 // |DidSetComputedStyle|. We also need to call it from |DestroyFrom|,
566 // because otherwise we won't get notified when style changes to
567 // "display:none".
568 SVGTextFrame* svgTextFrame = static_cast<SVGTextFrame*>(
569 nsLayoutUtils::GetClosestFrameOfType(aFrame, LayoutFrameType::SVGText));
570 nsIFrame* anonBlock = svgTextFrame->PrincipalChildList().FirstChild();
571
572 // Note that we must check NS_FRAME_FIRST_REFLOW on our SVGTextFrame's
573 // anonymous block frame rather than our aFrame, since NS_FRAME_FIRST_REFLOW
574 // may be set on us if we're a new frame that has been inserted after the
575 // document's first reflow. (In which case this DidSetComputedStyle call may
576 // be happening under frame construction under a Reflow() call.)
577 if (!anonBlock || anonBlock->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
578 return;
579 }
580
581 if (!svgTextFrame->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY) ||
582 svgTextFrame->HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW)) {
583 return;
584 }
585
586 svgTextFrame->ScheduleReflowSVGNonDisplayText(
587 IntrinsicDirty::FrameAncestorsAndDescendants);
588}
589
590bool nsIFrame::IsReplaced() const {
591 if (HasAnyClassFlag(ClassFlags::Replaced)) {
592 return true;
593 }
594 if (!Style()->IsAnonBox() && mContent->IsHTMLElement(nsGkAtoms::button)) {
595 // Button always behaves as a replaced element.
596 return true;
597 }
598 return false;
599}
600
601bool nsIFrame::IsAtomicInline() const {
602 // See: https://drafts.csswg.org/css-display-4/#atomic-inline
603 return IsInlineOutside() &&
604 (IsReplaced() || !StyleDisplay()->IsInlineInsideStyle());
605}
606
607bool nsIFrame::ShouldPropagateRepaintsToRoot() const {
608 if (!IsPrimaryFrame()) {
609 // special case for table frames because style images are associated to the
610 // table frame, but the table wrapper frame is the primary frame
611 if (IsTableFrame()) {
612 MOZ_ASSERT(GetParent() && GetParent()->IsTableWrapperFrame())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(GetParent() && GetParent()->IsTableWrapperFrame
())>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(GetParent() && GetParent()->IsTableWrapperFrame
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("GetParent() && GetParent()->IsTableWrapperFrame()"
, "./../../../layout/generic/nsIFrame.cpp", 612); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GetParent() && GetParent()->IsTableWrapperFrame()"
")"); do { MOZ_CrashSequence(__null, 612); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
613 return GetParent()->ShouldPropagateRepaintsToRoot();
614 }
615
616 return false;
617 }
618 nsIContent* content = GetContent();
619 Document* document = content->OwnerDoc();
620 return content == document->GetRootElement() ||
621 content == document->GetBodyElement();
622}
623
624bool nsIFrame::IsRenderedLegend() const {
625 if (auto* parent = GetParent(); parent && parent->IsFieldSetFrame()) {
626 return static_cast<nsFieldSetFrame*>(parent)->GetLegend() == this;
627 }
628 return false;
629}
630
631void nsIFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
632 nsIFrame* aPrevInFlow) {
633 MOZ_ASSERT(nsQueryFrame::FrameIID(mClass) == GetFrameId())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(nsQueryFrame::FrameIID(mClass) == GetFrameId())>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(nsQueryFrame::FrameIID(mClass) == GetFrameId()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("nsQueryFrame::FrameIID(mClass) == GetFrameId()"
, "./../../../layout/generic/nsIFrame.cpp", 633); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "nsQueryFrame::FrameIID(mClass) == GetFrameId()"
")"); do { MOZ_CrashSequence(__null, 633); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
634 MOZ_ASSERT(!mContent, "Double-initing a frame?")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mContent)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!mContent))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!mContent" " (" "Double-initing a frame?"
")", "./../../../layout/generic/nsIFrame.cpp", 634); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!mContent" ") (" "Double-initing a frame?"
")"); do { MOZ_CrashSequence(__null, 634); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
635
636 mContent = aContent;
637 mParent = aParent;
638 MOZ_ASSERT(!mParent || PresShell() == mParent->PresShell())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mParent || PresShell() == mParent->PresShell())>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!mParent || PresShell() == mParent->PresShell()))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!mParent || PresShell() == mParent->PresShell()"
, "./../../../layout/generic/nsIFrame.cpp", 638); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!mParent || PresShell() == mParent->PresShell()"
")"); do { MOZ_CrashSequence(__null, 638); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
639
640 if (aPrevInFlow) {
641 mWritingMode = aPrevInFlow->GetWritingMode();
642
643 // Copy some state bits from prev-in-flow (the bits that should apply
644 // throughout a continuation chain). The bits are sorted according to their
645 // order in nsFrameStateBits.h.
646
647 // clang-format off
648 AddStateBits(aPrevInFlow->GetStateBits() &
649 (NS_FRAME_GENERATED_CONTENT |
650 NS_FRAME_OUT_OF_FLOW |
651 NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN |
652 NS_FRAME_PART_OF_IBSPLIT |
653 NS_FRAME_MAY_BE_TRANSFORMED |
654 NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR));
655 // clang-format on
656
657 // Copy other bits in nsIFrame from prev-in-flow.
658 mHasColumnSpanSiblings = aPrevInFlow->HasColumnSpanSiblings();
659
660 // If our prev-in-flow is an absolute containing block, we must be one, too.
661 if (aPrevInFlow->IsAbsoluteContainer()) {
662 MOZ_ASSERT(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN))>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)"
" (" "We should've carried this bit from our prev-in-flow!" ")"
, "./../../../layout/generic/nsIFrame.cpp", 663); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)"
") (" "We should've carried this bit from our prev-in-flow!"
")"); do { MOZ_CrashSequence(__null, 663); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
663 "We should've carried this bit from our prev-in-flow!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN))>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)"
" (" "We should've carried this bit from our prev-in-flow!" ")"
, "./../../../layout/generic/nsIFrame.cpp", 663); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)"
") (" "We should've carried this bit from our prev-in-flow!"
")"); do { MOZ_CrashSequence(__null, 663); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
664 MarkAsAbsoluteContainingBlock();
665 }
666 } else {
667 PresContext()->ConstructedFrame();
668 }
669
670 if (GetParent()) {
671 if (MOZ_UNLIKELY(mContent == PresContext()->Document()->GetRootElement() &&(__builtin_expect(!!(mContent == PresContext()->Document()
->GetRootElement() && mContent == GetParent()->
GetContent()), 0))
672 mContent == GetParent()->GetContent())(__builtin_expect(!!(mContent == PresContext()->Document()
->GetRootElement() && mContent == GetParent()->
GetContent()), 0))
) {
673 // Our content is the root element and we have the same content as our
674 // parent. That is, we are the internal anonymous frame of the root
675 // element. Copy the used mWritingMode from our parent because
676 // mDocElementContainingBlock gets its mWritingMode from <body>.
677 mWritingMode = GetParent()->GetWritingMode();
678 }
679
680 // Copy some state bits from our parent (the bits that should apply
681 // recursively throughout a subtree). The bits are sorted according to their
682 // order in nsFrameStateBits.h.
683
684 // clang-format off
685 AddStateBits(GetParent()->GetStateBits() &
686 (NS_FRAME_GENERATED_CONTENT |
687 NS_FRAME_IS_SVG_TEXT |
688 NS_FRAME_IN_POPUP |
689 NS_FRAME_IS_NONDISPLAY));
690 // clang-format on
691
692 if (HasAnyStateBits(NS_FRAME_IN_POPUP) && TrackingVisibility()) {
693 // Assume all frames in popups are visible.
694 IncApproximateVisibleCount();
695 }
696 }
697 if (aPrevInFlow) {
698 mMayHaveOpacityAnimation = aPrevInFlow->MayHaveOpacityAnimation();
699 mMayHaveTransformAnimation = aPrevInFlow->MayHaveTransformAnimation();
700 } else if (mContent) {
701 // It's fine to fetch the EffectSet for the style frame here because in the
702 // following code we take care of the case where animations may target
703 // a different frame.
704 EffectSet* effectSet = EffectSet::GetForStyleFrame(this);
705 if (effectSet) {
706 mMayHaveOpacityAnimation = effectSet->MayHaveOpacityAnimation();
707
708 if (effectSet->MayHaveTransformAnimation()) {
709 // If we are the inner table frame for display:table content, then
710 // transform animations should go on our parent frame (the table wrapper
711 // frame).
712 //
713 // We do this when initializing the child frame (table inner frame),
714 // because when initializng the table wrapper frame, we don't yet have
715 // access to its children so we can't tell if we have transform
716 // animations or not.
717 if (SupportsCSSTransforms()) {
718 mMayHaveTransformAnimation = true;
719 AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
720 } else if (aParent && nsLayoutUtils::GetStyleFrame(aParent) == this) {
721 MOZ_ASSERT(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aParent->SupportsCSSTransforms())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aParent->SupportsCSSTransforms
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aParent->SupportsCSSTransforms()" " (" "Style frames that don't support transforms should have parents"
" that do" ")", "./../../../layout/generic/nsIFrame.cpp", 724
); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aParent->SupportsCSSTransforms()"
") (" "Style frames that don't support transforms should have parents"
" that do" ")"); do { MOZ_CrashSequence(__null, 724); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
722 aParent->SupportsCSSTransforms(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aParent->SupportsCSSTransforms())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aParent->SupportsCSSTransforms
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aParent->SupportsCSSTransforms()" " (" "Style frames that don't support transforms should have parents"
" that do" ")", "./../../../layout/generic/nsIFrame.cpp", 724
); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aParent->SupportsCSSTransforms()"
") (" "Style frames that don't support transforms should have parents"
" that do" ")"); do { MOZ_CrashSequence(__null, 724); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
723 "Style frames that don't support transforms should have parents"do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aParent->SupportsCSSTransforms())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aParent->SupportsCSSTransforms
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aParent->SupportsCSSTransforms()" " (" "Style frames that don't support transforms should have parents"
" that do" ")", "./../../../layout/generic/nsIFrame.cpp", 724
); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aParent->SupportsCSSTransforms()"
") (" "Style frames that don't support transforms should have parents"
" that do" ")"); do { MOZ_CrashSequence(__null, 724); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
724 " that do")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aParent->SupportsCSSTransforms())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aParent->SupportsCSSTransforms
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aParent->SupportsCSSTransforms()" " (" "Style frames that don't support transforms should have parents"
" that do" ")", "./../../../layout/generic/nsIFrame.cpp", 724
); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aParent->SupportsCSSTransforms()"
") (" "Style frames that don't support transforms should have parents"
" that do" ")"); do { MOZ_CrashSequence(__null, 724); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
;
725 aParent->mMayHaveTransformAnimation = true;
726 aParent->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
727 }
728 }
729 }
730 }
731
732 const nsStyleDisplay* disp = StyleDisplay();
733 if (disp->HasTransform(this)) {
734 // If 'transform' dynamically changes, RestyleManager takes care of
735 // updating this bit.
736 AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
737 }
738
739 if (nsLayoutUtils::FontSizeInflationEnabled(PresContext()) ||
740 !GetParent()
741#ifdef DEBUG1
742 // We have assertions that check inflation invariants even when
743 // font size inflation is not enabled.
744 || true
745#endif
746 ) {
747 if (IsFontSizeInflationContainer(this, disp)) {
748 AddStateBits(NS_FRAME_FONT_INFLATION_CONTAINER);
749 if (!GetParent() ||
750 // I'd use NS_FRAME_OUT_OF_FLOW, but it's not set yet.
751 disp->IsFloating(this) || disp->IsAbsolutelyPositioned(this) ||
752 GetParent()->IsFlexContainerFrame() ||
753 GetParent()->IsGridContainerFrame()) {
754 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
755 }
756 }
757 NS_ASSERTION(do { if (!(GetParent() || HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER
))) { NS_DebugBreak(NS_DEBUG_ASSERTION, "root frame should always be a container"
, "GetParent() || HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER)"
, "./../../../layout/generic/nsIFrame.cpp", 759); MOZ_PretendNoReturn
(); } } while (0)
758 GetParent() || HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER),do { if (!(GetParent() || HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER
))) { NS_DebugBreak(NS_DEBUG_ASSERTION, "root frame should always be a container"
, "GetParent() || HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER)"
, "./../../../layout/generic/nsIFrame.cpp", 759); MOZ_PretendNoReturn
(); } } while (0)
759 "root frame should always be a container")do { if (!(GetParent() || HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER
))) { NS_DebugBreak(NS_DEBUG_ASSERTION, "root frame should always be a container"
, "GetParent() || HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER)"
, "./../../../layout/generic/nsIFrame.cpp", 759); MOZ_PretendNoReturn
(); } } while (0)
;
760 }
761
762 if (TrackingVisibility() && PresShell()->AssumeAllFramesVisible()) {
763 IncApproximateVisibleCount();
764 }
765
766 DidSetComputedStyle(nullptr);
767
768 // For a newly created frame, we need to update this frame's visibility state.
769 // Usually we update the state when the frame is restyled and has a
770 // VisibilityChange change hint but we don't generate any change hints for
771 // newly created frames.
772 // Note: We don't need to do this for placeholders since placeholders have
773 // different styles so that the styles don't have visibility:hidden even if
774 // the parent has visibility:hidden style. We also don't need to update the
775 // state when creating continuations because its visibility is the same as its
776 // prev-in-flow, and the animation code cares only primary frames.
777 if (!IsPlaceholderFrame() && !aPrevInFlow) {
778 UpdateVisibleDescendantsState();
779 }
780
781 if (!aPrevInFlow && HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
782 // We aren't going to get a reflow, so nothing else will call
783 // InvalidateRenderingObservers, we have to do it here.
784 SVGObserverUtils::InvalidateRenderingObservers(this);
785 }
786}
787
788void nsIFrame::InitPrimaryFrame() {
789 MOZ_ASSERT(IsPrimaryFrame())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsPrimaryFrame())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsPrimaryFrame()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("IsPrimaryFrame()"
, "./../../../layout/generic/nsIFrame.cpp", 789); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsPrimaryFrame()" ")"); do { MOZ_CrashSequence
(__null, 789); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
790 HandlePrimaryFrameStyleChange(nullptr);
791}
792
793// If `aFrame` is a generated content container (::before/::after/::marker/...)
794// whose content changed only in the text of its string items, rewrite the
795// existing generated text nodes in place instead of reconstructing the frame.
796// See nsStyleContent::CanUpdateGeneratedContentText and its usage.
797static void UpdateGeneratedContentTextIfNeeded(
798 nsIFrame* aFrame, const ComputedStyle* aOldComputedStyle) {
799 if (!aFrame->IsGeneratedContentFrame()) {
800 return;
801 }
802 // Update once per element, not per continuation.
803 if (aFrame->GetPrevContinuation()) {
804 return;
805 }
806 nsIContent* content = aFrame->GetContent();
807 if (!content || !content->IsRootOfNativeAnonymousSubtree()) {
808 return;
809 }
810
811 const auto* oldStyle = aOldComputedStyle->StyleContent();
812 const auto* newStyle = aFrame->StyleContent();
813 if (oldStyle->mContent == newStyle->mContent ||
814 !nsStyleContent::CanUpdateGeneratedContentText(*oldStyle, *newStyle)) {
815 return;
816 }
817
818 auto oldItems = oldStyle->NonAltContentItems();
819 auto newItems = newStyle->NonAltContentItems();
820 MOZ_ASSERT(oldItems.Length() == newItems.Length(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(oldItems.Length() == newItems.Length())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(oldItems.Length() == newItems.Length()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("oldItems.Length() == newItems.Length()"
" (" "CanUpdateGeneratedContentText should guarantee equal lengths"
")", "./../../../layout/generic/nsIFrame.cpp", 821); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "oldItems.Length() == newItems.Length()" ") ("
"CanUpdateGeneratedContentText should guarantee equal lengths"
")"); do { MOZ_CrashSequence(__null, 821); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
821 "CanUpdateGeneratedContentText should guarantee equal lengths")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(oldItems.Length() == newItems.Length())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(oldItems.Length() == newItems.Length()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("oldItems.Length() == newItems.Length()"
" (" "CanUpdateGeneratedContentText should guarantee equal lengths"
")", "./../../../layout/generic/nsIFrame.cpp", 821); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "oldItems.Length() == newItems.Length()" ") ("
"CanUpdateGeneratedContentText should guarantee equal lengths"
")"); do { MOZ_CrashSequence(__null, 821); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
822 nsIContent* child = content->GetFirstChild();
823 for (size_t i = 0; i < newItems.Length(); i++) {
824 const auto& item = newItems[i];
825 const bool isString = item.IsString();
826 if (isString && item.AsString().AsString().IsEmpty()) {
827 // Empty strings have no associated content node.
828 continue;
829 }
830 MOZ_ASSERT(child,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(child)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(child))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("child" " (" "Mismatch between content items and generated content nodes?"
")", "./../../../layout/generic/nsIFrame.cpp", 831); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "child" ") (" "Mismatch between content items and generated content nodes?"
")"); do { MOZ_CrashSequence(__null, 831); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
831 "Mismatch between content items and generated content nodes?")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(child)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(child))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("child" " (" "Mismatch between content items and generated content nodes?"
")", "./../../../layout/generic/nsIFrame.cpp", 831); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "child" ") (" "Mismatch between content items and generated content nodes?"
")"); do { MOZ_CrashSequence(__null, 831); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
832 if (isString && item != oldItems[i]) {
833 MOZ_ASSERT(child->IsText(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(child->IsText())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(child->IsText()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("child->IsText()"
" (" "A non-empty string item should map to a text node" ")"
, "./../../../layout/generic/nsIFrame.cpp", 834); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "child->IsText()" ") (" "A non-empty string item should map to a text node"
")"); do { MOZ_CrashSequence(__null, 834); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
834 "A non-empty string item should map to a text node")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(child->IsText())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(child->IsText()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("child->IsText()"
" (" "A non-empty string item should map to a text node" ")"
, "./../../../layout/generic/nsIFrame.cpp", 834); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "child->IsText()" ") (" "A non-empty string item should map to a text node"
")"); do { MOZ_CrashSequence(__null, 834); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
835 child->AsText()->SetText(
836 NS_ConvertUTF8toUTF16(item.AsString().AsString()),
837 /* aNotify = */ true);
838 }
839 child = child->GetNextSibling();
840 }
841 MOZ_ASSERT(!child, "More generated content than content items.")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!child)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(!child))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!child" " (" "More generated content than content items."
")", "./../../../layout/generic/nsIFrame.cpp", 841); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!child" ") (" "More generated content than content items."
")"); do { MOZ_CrashSequence(__null, 841); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
842}
843
844void nsIFrame::HandlePrimaryFrameStyleChange(ComputedStyle* aOldStyle) {
845 const nsStyleDisplay* disp = StyleDisplay();
846 const nsStyleDisplay* oldDisp =
847 aOldStyle ? aOldStyle->StyleDisplay() : nullptr;
848
849 const bool wasQueryContainer = oldDisp && oldDisp->IsQueryContainer();
850 const bool isQueryContainer = disp->IsQueryContainer();
851 if (wasQueryContainer != isQueryContainer) {
852 auto* pc = PresContext();
853 if (isQueryContainer) {
854 pc->RegisterContainerQueryFrame(this);
855 } else {
856 pc->UnregisterContainerQueryFrame(this);
857 }
858 }
859
860 const bool wasReferringToAnchor = aOldStyle &&
861 oldDisp->IsAbsolutelyPositionedStyle() &&
862 aOldStyle->HasAnchorPosReference();
863 const bool isReferringToAnchor = HasAnchorPosReference();
864 if (wasReferringToAnchor && !isReferringToAnchor) {
865 PresShell()->RemoveAnchorPosPositioned(this);
866 RemoveProperty(NormalPositionProperty());
867 } else if (!wasReferringToAnchor && isReferringToAnchor) {
868 PresShell()->AddAnchorPosPositioned(this);
869 }
870
871 bool handleAnchorPosAnchorNameChange =
872 oldDisp ? oldDisp->mAnchorName != disp->mAnchorName
873 : disp->HasAnchorName();
874 if (handleAnchorPosAnchorNameChange &&
875 !HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
876 if (oldDisp && oldDisp->HasAnchorName()) {
877 PresShell()->RemoveAnchorPosAnchor(oldDisp->mAnchorName.AsSpan(), this);
878 }
879 PresShell()->AddAnchorPosAnchor(disp->mAnchorName.AsSpan(), this);
880 }
881
882 // According to the Anchor Positioning spec,
883 // https://drafts.csswg.org/css-anchor-position-1/#last-successful-position-option:
884 //
885 // If el has a last successful position option remove its last successful
886 // position option if any of the following are true:
887 // 1. Its computed position value has changed, its containing block has
888 // changed, or it no longer generates a box.
889 // 2. Its computed value for any longhand of position-try has changed.
890 // 3. Its computed value for any @position-try property has changed.
891 // 4. Any of the @position-try rules referenced by it have been added,
892 // removed, or mutated.
893 //
894 // Case 1 will cause a reframe, so does not need to be handled here.
895 // Cases 2 and 3 are handled here.
896 // TODO: case 4, see bug 1987960.
897 if (aOldStyle && HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) &&
898 HasProperty(LastSuccessfulPositionFallback())) {
899 const auto* pos = StylePosition();
900 const auto* oldPos = aOldStyle->StylePosition();
901 if (pos->mPositionTryFallbacks != oldPos->mPositionTryFallbacks ||
902 pos->mPositionTryOrder != oldPos->mPositionTryOrder ||
903 pos->mOffset != oldPos->mOffset ||
904 pos->mAlignSelf != oldPos->mAlignSelf ||
905 pos->mJustifySelf != oldPos->mJustifySelf ||
906 pos->mPositionAnchor != oldPos->mPositionAnchor ||
907 pos->mPositionArea != oldPos->mPositionArea ||
908 pos->mMinWidth != oldPos->mMinWidth ||
909 pos->mMinHeight != oldPos->mMinHeight ||
910 pos->mMaxWidth != oldPos->mMaxWidth ||
911 pos->mMaxHeight != oldPos->mMaxHeight ||
912 pos->mWidth != oldPos->mWidth || pos->mHeight != oldPos->mHeight ||
913 StyleMargin()->mMargin != aOldStyle->StyleMargin()->mMargin) {
914 RemoveProperty(LastSuccessfulPositionFallback());
915 }
916 }
917
918 const auto cv = disp->ContentVisibility(*this);
919 const auto oldCv = oldDisp ? oldDisp->ContentVisibility(*this)
920 : StyleContentVisibility::Visible;
921 if (!oldDisp || cv != oldCv) {
922 // 'content-visibility' has changed (or this is a new frame being
923 // initialized). Inform PresShell so it can update its bookkeeping
924 // about frames with particular content-visibility values:
925 // * Inform it of the new value (for the values that it cares about):
926 if (cv == StyleContentVisibility::Hidden) {
927 PresShell()->IncrementContentVisibilityHiddenCount();
928 } else if (cv == StyleContentVisibility::Auto) {
929 PresShell()->RegisterContentVisibilityAutoFrame(this);
930 }
931
932 // * Inform it of the old value (for the values that it cares about):
933 if (oldCv == StyleContentVisibility::Hidden) {
934 PresShell()->DecrementContentVisibilityHiddenCount();
935 } else if (oldCv == StyleContentVisibility::Auto) {
936 if (auto* element = Element::FromNodeOrNull(GetContent())) {
937 element->ClearContentRelevancy();
938 }
939 PresShell()->UnregisterContentVisibilityAutoFrame(this);
940 }
941 PresContext()->SetNeedsToUpdateHiddenByContentVisibilityForAnimations();
942 }
943
944 HandleLastRememberedSize();
945
946 bool handleStickyChange =
947 oldDisp ? (disp->mPosition != oldDisp->mPosition &&
948 (disp->mPosition == StylePositionProperty::Sticky ||
949 oldDisp->mPosition == StylePositionProperty::Sticky))
950 : disp->mPosition == StylePositionProperty::Sticky;
951 if (handleStickyChange && !HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
952 if (auto* ssc = StickyScrollContainer::GetOrCreateForFrame(this)) {
953 if (disp->mPosition == StylePositionProperty::Sticky) {
954 ssc->AddFrame(this);
955 } else {
956 ssc->RemoveFrame(this);
957 }
958 }
959 }
960}
961
962void nsIFrame::Destroy(DestroyContext& aContext) {
963 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),do { if (!(!nsContentUtils::IsSafeToRunScript())) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "destroy called on frame while scripts not blocked"
, "!nsContentUtils::IsSafeToRunScript()", "./../../../layout/generic/nsIFrame.cpp"
, 964); MOZ_PretendNoReturn(); } } while (0)
964 "destroy called on frame while scripts not blocked")do { if (!(!nsContentUtils::IsSafeToRunScript())) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "destroy called on frame while scripts not blocked"
, "!nsContentUtils::IsSafeToRunScript()", "./../../../layout/generic/nsIFrame.cpp"
, 964); MOZ_PretendNoReturn(); } } while (0)
;
965 NS_ASSERTION(!GetNextSibling() && !GetPrevSibling(),do { if (!(!GetNextSibling() && !GetPrevSibling())) {
NS_DebugBreak(NS_DEBUG_ASSERTION, "Frames should be removed before destruction."
, "!GetNextSibling() && !GetPrevSibling()", "./../../../layout/generic/nsIFrame.cpp"
, 966); MOZ_PretendNoReturn(); } } while (0)
966 "Frames should be removed before destruction.")do { if (!(!GetNextSibling() && !GetPrevSibling())) {
NS_DebugBreak(NS_DEBUG_ASSERTION, "Frames should be removed before destruction."
, "!GetNextSibling() && !GetPrevSibling()", "./../../../layout/generic/nsIFrame.cpp"
, 966); MOZ_PretendNoReturn(); } } while (0)
;
967 MOZ_ASSERT(!HasAbsolutelyPositionedChildren())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!HasAbsolutelyPositionedChildren())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!HasAbsolutelyPositionedChildren
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!HasAbsolutelyPositionedChildren()", "./../../../layout/generic/nsIFrame.cpp"
, 967); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!HasAbsolutelyPositionedChildren()"
")"); do { MOZ_CrashSequence(__null, 967); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
968 MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)"
" (" "NS_FRAME_PART_OF_IBSPLIT set on non-nsContainerFrame?"
")", "./../../../layout/generic/nsIFrame.cpp", 969); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)"
") (" "NS_FRAME_PART_OF_IBSPLIT set on non-nsContainerFrame?"
")"); do { MOZ_CrashSequence(__null, 969); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
969 "NS_FRAME_PART_OF_IBSPLIT set on non-nsContainerFrame?")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)"
" (" "NS_FRAME_PART_OF_IBSPLIT set on non-nsContainerFrame?"
")", "./../../../layout/generic/nsIFrame.cpp", 969); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)"
") (" "NS_FRAME_PART_OF_IBSPLIT set on non-nsContainerFrame?"
")"); do { MOZ_CrashSequence(__null, 969); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
970
971 MaybeScheduleReflowSVGNonDisplayText(this);
972
973 SVGObserverUtils::InvalidateDirectRenderingObservers(
974 this, SVGObserverUtils::InvalidationFlag::FrameBeingDestroyed);
975
976 const auto* disp = StyleDisplay();
977 if (disp->mPosition == StylePositionProperty::Sticky) {
978 if (auto* ssc = StickyScrollContainer::GetOrCreateForFrame(this)) {
979 ssc->RemoveFrame(this);
980 }
981 }
982
983 if (HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
984 if (nsPlaceholderFrame* placeholder = GetPlaceholderFrame()) {
985 placeholder->SetOutOfFlowFrame(nullptr);
986 }
987 }
988
989 nsPresContext* pc = PresContext();
990 mozilla::PresShell* ps = pc->GetPresShell();
991 if (IsPrimaryFrame()) {
992 if (disp->IsQueryContainer()) {
993 pc->UnregisterContainerQueryFrame(this);
994 }
995 auto contentVisibility = disp->ContentVisibility(*this);
996 if (contentVisibility == StyleContentVisibility::Hidden) {
997 ps->DecrementContentVisibilityHiddenCount();
998 } else if (contentVisibility == StyleContentVisibility::Auto) {
999 ps->UnregisterContentVisibilityAutoFrame(this);
1000 }
1001 // This needs to happen before we clear our Properties() table.
1002 ActiveLayerTracker::TransferActivityToContent(this, mContent);
1003 }
1004
1005 ScrollAnchorContainer* anchor = nullptr;
1006 if (IsScrollAnchor(&anchor)) {
1007 anchor->InvalidateAnchor();
1008 }
1009
1010 if (HasCSSAnimations() || HasCSSTransitions() ||
1011 // It's fine to look up the style frame here since if we're destroying the
1012 // frames for display:table content we should be destroying both wrapper
1013 // and inner frame.
1014 EffectSet::GetForStyleFrame(this)) {
1015 // If no new frame for this element is created by the end of the
1016 // restyling process, stop animations and transitions for this frame
1017 RestyleManager::AnimationsWithDestroyedFrame* adf =
1018 pc->RestyleManager()->GetAnimationsWithDestroyedFrame();
1019 // AnimationsWithDestroyedFrame only lives during the restyling process.
1020 if (adf) {
1021 adf->Put(mContent, mComputedStyle);
1022 }
1023 }
1024
1025 if (HasAnchorPosName()) {
1026 PresShell()->RemoveAnchorPosAnchor(disp->mAnchorName.AsSpan(), this);
1027 }
1028
1029 if (HasAnchorPosReference()) {
1030 ps->RemoveAnchorPosPositioned(this);
1031 }
1032
1033 // Disable visibility tracking. Note that we have to do this before we clear
1034 // frame properties and lose track of whether we were previously visible.
1035 // XXX(seth): It'd be ideal to assert that we're already marked nonvisible
1036 // here, but it's unfortunately tricky to guarantee in the face of things like
1037 // frame reconstruction induced by style changes.
1038 DisableVisibilityTracking();
1039
1040 // Ensure that we're not in the approximately visible list anymore.
1041 ps->RemoveFrameFromApproximatelyVisibleList(this);
1042
1043 ps->NotifyDestroyingFrame(this);
1044
1045 if (HasAnyStateBits(NS_FRAME_EXTERNAL_REFERENCE)) {
1046 ps->ClearFrameRefs(this);
1047 }
1048
1049 // Make sure that our deleted frame can't be returned from GetPrimaryFrame()
1050 if (IsPrimaryFrame()) {
1051 mContent->SetPrimaryFrame(nullptr);
1052
1053 // Pass the root of a generated content subtree (e.g. ::after/::before) to
1054 // aPostDestroyData to unbind it after frame destruction is done.
1055 if (HasAnyStateBits(NS_FRAME_GENERATED_CONTENT) &&
1056 mContent->IsRootOfNativeAnonymousSubtree()) {
1057 aContext.AddAnonymousContent(mContent.forget());
1058 }
1059 }
1060
1061 // Remove all properties attached to the frame, to ensure any property
1062 // destructors that need the frame pointer are handled properly.
1063 RemoveAllProperties();
1064
1065 // Must retrieve the object ID before calling destructors, so the
1066 // vtable is still valid.
1067 //
1068 // Note to future tweakers: having the method that returns the
1069 // object size call the destructor will not avoid an indirect call;
1070 // the compiler cannot devirtualize the call to the destructor even
1071 // if it's from a method defined in the same class.
1072
1073 nsQueryFrame::FrameIID id = GetFrameId();
1074 this->~nsIFrame();
1075
1076#ifdef DEBUG1
1077 {
1078 nsIFrame* rootFrame = ps->GetRootFrame();
1079 MOZ_ASSERT(rootFrame)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(rootFrame)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(rootFrame))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("rootFrame", "./../../../layout/generic/nsIFrame.cpp"
, 1079); AnnotateMozCrashReason("MOZ_ASSERT" "(" "rootFrame" ")"
); do { MOZ_CrashSequence(__null, 1079); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1080 if (this != rootFrame) {
1081 auto* builder = nsLayoutUtils::GetRetainedDisplayListBuilder(rootFrame);
1082 auto* data = builder ? builder->Data() : nullptr;
1083
1084 const bool inData =
1085 data && (data->IsModified(this) || data->HasProps(this));
1086
1087 if (inData) {
1088 DL_LOG(LogLevel::Warning, "Frame %p found in retained data", this)do { const ::mozilla::LogModule* moz_real_module = GetLoggerByProcess
(); if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Warning)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Warning, "Frame %p found in retained data", this)
; } } while (0)
;
1089 }
1090
1091 MOZ_ASSERT(!inData, "Deleted frame in retained data!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!inData)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!inData))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!inData" " (" "Deleted frame in retained data!"
")", "./../../../layout/generic/nsIFrame.cpp", 1091); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!inData" ") (" "Deleted frame in retained data!"
")"); do { MOZ_CrashSequence(__null, 1091); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1092 }
1093 }
1094#endif
1095
1096 // Now that we're totally cleaned out, we need to add ourselves to
1097 // the presshell's recycler.
1098 ps->FreeFrame(id, this);
1099}
1100
1101std::pair<int32_t, int32_t> nsIFrame::GetOffsets() const {
1102 return std::make_pair(0, 0);
1103}
1104
1105static void CompareLayers(
1106 const nsStyleImageLayers* aFirstLayers,
1107 const nsStyleImageLayers* aSecondLayers,
1108 const std::function<void(imgRequestProxy* aReq)>& aCallback) {
1109 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, (*aFirstLayers))for (uint32_t i = ((*aFirstLayers)).mImageCount; (i)-- != 0;) {
1110 const auto& image = aFirstLayers->mLayers[i].mImage;
1111 if (!image.IsImageRequestType() || !image.IsResolved()) {
1112 continue;
1113 }
1114
1115 // aCallback is called when the style image in aFirstLayers is thought to
1116 // be different with the corresponded one in aSecondLayers
1117 if (!aSecondLayers || i >= aSecondLayers->mImageCount ||
1118 (!aSecondLayers->mLayers[i].mImage.IsResolved() ||
1119 image.GetImageRequest() !=
1120 aSecondLayers->mLayers[i].mImage.GetImageRequest())) {
1121 if (imgRequestProxy* req = image.GetImageRequest()) {
1122 aCallback(req);
1123 }
1124 }
1125 }
1126}
1127
1128static void AddAndRemoveImageAssociations(
1129 ImageLoader& aImageLoader, nsIFrame* aFrame,
1130 const nsStyleImageLayers* aOldLayers,
1131 const nsStyleImageLayers* aNewLayers) {
1132 // If the old context had a background-image image, or mask-image image,
1133 // and new context does not have the same image, clear the image load
1134 // notifier (which keeps the image loading, if it still is) for the frame.
1135 // We want to do this conservatively because some frames paint their
1136 // backgrounds from some other frame's style data, and we don't want
1137 // to clear those notifiers unless we have to. (They'll be reset
1138 // when we paint, although we could miss a notification in that
1139 // interval.)
1140 if (aOldLayers && aFrame->HasImageRequest()) {
1141 CompareLayers(aOldLayers, aNewLayers, [&](imgRequestProxy* aReq) {
1142 aImageLoader.DisassociateRequestFromFrame(aReq, aFrame);
1143 });
1144 }
1145
1146 CompareLayers(aNewLayers, aOldLayers, [&](imgRequestProxy* aReq) {
1147 aImageLoader.AssociateRequestToFrame(aReq, aFrame);
1148 });
1149}
1150
1151void nsIFrame::AddDisplayItem(nsDisplayItem* aItem) {
1152 MOZ_DIAGNOSTIC_ASSERT(!mDisplayItems.Contains(aItem))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mDisplayItems.Contains(aItem))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!mDisplayItems.Contains(aItem
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!mDisplayItems.Contains(aItem)", "./../../../layout/generic/nsIFrame.cpp"
, 1152); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "!mDisplayItems.Contains(aItem)"
")"); do { MOZ_CrashSequence(__null, 1152); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1153 mDisplayItems.AppendElement(aItem);
1154#ifdef ACCESSIBILITY1
1155 if (nsAccessibilityService* accService = GetAccService()) {
1156 accService->NotifyOfPossibleBoundsChange(PresShell(), mContent);
1157 }
1158#endif
1159}
1160
1161bool nsIFrame::RemoveDisplayItem(nsDisplayItem* aItem) {
1162 return mDisplayItems.RemoveElement(aItem);
1163}
1164
1165bool nsIFrame::HasDisplayItems() { return !mDisplayItems.IsEmpty(); }
1166
1167bool nsIFrame::HasDisplayItem(nsDisplayItem* aItem) {
1168 return mDisplayItems.Contains(aItem);
1169}
1170
1171bool nsIFrame::HasDisplayItem(uint32_t aKey) {
1172 for (nsDisplayItem* i : mDisplayItems) {
1173 if (i->GetPerFrameKey() == aKey) {
1174 return true;
1175 }
1176 }
1177 return false;
1178}
1179
1180template <typename Condition>
1181static void DiscardDisplayItems(nsIFrame* aFrame, Condition aCondition) {
1182 for (nsDisplayItem* i : aFrame->DisplayItems()) {
1183 // Only discard items that are invalidated by this frame, as we're only
1184 // guaranteed to rebuild those items. Table background items are created by
1185 // the relevant table part, but have the cell frame as the primary frame,
1186 // and we don't want to remove them if this is the cell.
1187 if (aCondition(i) && i->FrameForInvalidation() == aFrame) {
1188 i->SetCantBeReused();
1189 }
1190 }
1191}
1192
1193static void DiscardOldItems(nsIFrame* aFrame) {
1194 DiscardDisplayItems(aFrame,
1195 [](nsDisplayItem* aItem) { return aItem->IsOldItem(); });
1196}
1197
1198void nsIFrame::RemoveDisplayItemDataForDeletion() {
1199 // Destroying a WebRenderUserDataTable can cause destruction of other objects
1200 // which can remove frame properties in their destructor. If we delete a frame
1201 // property it runs the destructor of the stored object in the middle of
1202 // updating the frame property table, so if the destruction of that object
1203 // causes another update to the frame property table it would leave the frame
1204 // property table in an inconsistent state. So we remove it from the table and
1205 // then destroy it. (bug 1530657)
1206 WebRenderUserDataTable* userDataTable =
1207 TakeProperty(WebRenderUserDataProperty::Key());
1208 if (userDataTable) {
1209 for (const auto& data : userDataTable->Values()) {
1210 data->RemoveFromTable();
1211 }
1212 delete userDataTable;
1213 }
1214
1215 auto* builder = nsLayoutUtils::GetRetainedDisplayListBuilder(this);
1216 if (!builder) {
1217 MOZ_ASSERT(DisplayItems().IsEmpty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(DisplayItems().IsEmpty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(DisplayItems().IsEmpty()))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("DisplayItems().IsEmpty()"
, "./../../../layout/generic/nsIFrame.cpp", 1217); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "DisplayItems().IsEmpty()" ")"); do { MOZ_CrashSequence
(__null, 1217); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1218 MOZ_ASSERT(!IsFrameModified())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsFrameModified())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsFrameModified()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("!IsFrameModified()"
, "./../../../layout/generic/nsIFrame.cpp", 1218); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsFrameModified()" ")"); do { MOZ_CrashSequence
(__null, 1218); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1219 return;
1220 }
1221
1222 for (nsDisplayItem* i : DisplayItems()) {
1223 if (i->GetDependentFrame() == this && !i->HasDeletedFrame()) {
1224 i->Frame()->MarkNeedsDisplayItemRebuild();
1225 }
1226 i->RemoveFrame(this);
1227 }
1228
1229 DisplayItems().Clear();
1230
1231 nsAutoString name;
1232#ifdef DEBUG_FRAME_DUMP1
1233 if (DL_LOG_TEST(LogLevel::Debug)(__builtin_expect(!!(mozilla::detail::log_test(GetLoggerByProcess
(), LogLevel::Debug)), 0))
) {
1234 GetFrameName(name);
1235 }
1236#endif
1237 DL_LOGV("Removing display item data for frame %p (%s)", this,do { const ::mozilla::LogModule* moz_real_module = GetLoggerByProcess
(); if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Verbose)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Verbose, "Removing display item data for frame %p (%s)"
, this, NS_ConvertUTF16toUTF8(name).get()); } } while (0)
1238 NS_ConvertUTF16toUTF8(name).get())do { const ::mozilla::LogModule* moz_real_module = GetLoggerByProcess
(); if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Verbose)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Verbose, "Removing display item data for frame %p (%s)"
, this, NS_ConvertUTF16toUTF8(name).get()); } } while (0)
;
1239
1240 builder->Data()->Remove(this);
1241}
1242
1243void nsIFrame::MarkNeedsDisplayItemRebuild() {
1244 if (!nsLayoutUtils::AreRetainedDisplayListsEnabled() || IsFrameModified() ||
1245 HasAnyStateBits(NS_FRAME_IN_POPUP)) {
1246 // Skip frames that are already marked modified.
1247 return;
1248 }
1249
1250 if (Type() == LayoutFrameType::Placeholder) {
1251 nsIFrame* oof = static_cast<nsPlaceholderFrame*>(this)->GetOutOfFlowFrame();
1252 if (oof) {
1253 oof->MarkNeedsDisplayItemRebuild();
1254 }
1255 // Do not mark placeholder frames modified.
1256 return;
1257 }
1258
1259#ifdef ACCESSIBILITY1
1260 if (nsAccessibilityService* accService = GetAccService()) {
1261 accService->NotifyOfPossibleBoundsChange(PresShell(), mContent);
1262 }
1263#endif
1264
1265 nsIFrame* rootFrame = PresShell()->GetRootFrame();
1266
1267 if (rootFrame->IsFrameModified()) {
1268 // The whole frame tree is modified.
1269 return;
1270 }
1271
1272 auto* builder = nsLayoutUtils::GetRetainedDisplayListBuilder(this);
1273 if (!builder) {
1274 MOZ_ASSERT(DisplayItems().IsEmpty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(DisplayItems().IsEmpty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(DisplayItems().IsEmpty()))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("DisplayItems().IsEmpty()"
, "./../../../layout/generic/nsIFrame.cpp", 1274); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "DisplayItems().IsEmpty()" ")"); do { MOZ_CrashSequence
(__null, 1274); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1275 return;
1276 }
1277
1278 RetainedDisplayListData* data = builder->Data();
1279 MOZ_ASSERT(data)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(data)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(data))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("data", "./../../../layout/generic/nsIFrame.cpp"
, 1279); AnnotateMozCrashReason("MOZ_ASSERT" "(" "data" ")");
do { MOZ_CrashSequence(__null, 1279); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1280
1281 if (data->AtModifiedFrameLimit()) {
1282 // This marks the whole frame tree modified.
1283 // See |RetainedDisplayListBuilder::ShouldBuildPartial()|.
1284 data->AddModifiedFrame(rootFrame);
1285 return;
1286 }
1287
1288 nsAutoString name;
1289#ifdef DEBUG_FRAME_DUMP1
1290 if (DL_LOG_TEST(LogLevel::Debug)(__builtin_expect(!!(mozilla::detail::log_test(GetLoggerByProcess
(), LogLevel::Debug)), 0))
) {
1291 GetFrameName(name);
1292 }
1293#endif
1294
1295 DL_LOGV("RDL - Rebuilding display items for frame %p (%s)", this,do { const ::mozilla::LogModule* moz_real_module = GetLoggerByProcess
(); if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Verbose)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Verbose, "RDL - Rebuilding display items for frame %p (%s)"
, this, NS_ConvertUTF16toUTF8(name).get()); } } while (0)
1296 NS_ConvertUTF16toUTF8(name).get())do { const ::mozilla::LogModule* moz_real_module = GetLoggerByProcess
(); if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Verbose)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Verbose, "RDL - Rebuilding display items for frame %p (%s)"
, this, NS_ConvertUTF16toUTF8(name).get()); } } while (0)
;
1297
1298 data->AddModifiedFrame(this);
1299
1300 MOZ_ASSERT(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(PresContext()->LayoutPhaseCount(nsLayoutPhase::DisplayListBuilding
) == 0)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(PresContext()->LayoutPhaseCount(nsLayoutPhase::DisplayListBuilding
) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("PresContext()->LayoutPhaseCount(nsLayoutPhase::DisplayListBuilding) == 0"
, "./../../../layout/generic/nsIFrame.cpp", 1301); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "PresContext()->LayoutPhaseCount(nsLayoutPhase::DisplayListBuilding) == 0"
")"); do { MOZ_CrashSequence(__null, 1301); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1301 PresContext()->LayoutPhaseCount(nsLayoutPhase::DisplayListBuilding) == 0)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(PresContext()->LayoutPhaseCount(nsLayoutPhase::DisplayListBuilding
) == 0)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(PresContext()->LayoutPhaseCount(nsLayoutPhase::DisplayListBuilding
) == 0))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("PresContext()->LayoutPhaseCount(nsLayoutPhase::DisplayListBuilding) == 0"
, "./../../../layout/generic/nsIFrame.cpp", 1301); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "PresContext()->LayoutPhaseCount(nsLayoutPhase::DisplayListBuilding) == 0"
")"); do { MOZ_CrashSequence(__null, 1301); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1302
1303 // Hopefully this is cheap, but we could use a frame state bit to note
1304 // the presence of dependencies to speed it up.
1305 for (nsDisplayItem* i : DisplayItems()) {
1306 if (i->HasDeletedFrame() || i->Frame() == this) {
1307 // Ignore the items with deleted frames, and the items with |this| as
1308 // the primary frame.
1309 continue;
1310 }
1311
1312 if (i->GetDependentFrame() == this) {
1313 // For items with |this| as a dependent frame, mark the primary frame
1314 // for rebuild.
1315 i->Frame()->MarkNeedsDisplayItemRebuild();
1316 }
1317 }
1318}
1319
1320// Subclass hook for style post processing
1321/* virtual */
1322void nsIFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
1323 if (aOldComputedStyle) {
1324#ifdef ACCESSIBILITY1
1325 // Don't notify for reconstructed frames here, since the frame is still
1326 // being constructed at this point and so LocalAccessible::GetFrame() will
1327 // return null. Style changes for reconstructed frames are handled in
1328 // DocAccessible::PruneOrInsertSubtree.
1329 if (nsAccessibilityService* accService = GetAccService()) {
1330 accService->NotifyOfComputedStyleChange(PresShell(), mContent);
1331 }
1332#endif
1333 UpdateGeneratedContentTextIfNeeded(this, aOldComputedStyle);
1334 }
1335
1336 MaybeScheduleReflowSVGNonDisplayText(this);
1337
1338 Document* doc = PresContext()->Document();
1339 ImageLoader& loader = doc->EnsureStyleImageLoader();
1340 // Continuing text frame doesn't initialize its continuation pointer before
1341 // reaching here for the first time, so we have to exclude text frames. This
1342 // doesn't affect correctness because text can't match selectors.
1343 //
1344 // FIXME(emilio): We should consider fixing that.
1345 //
1346 // TODO(emilio): Can we avoid doing some / all of the image stuff when
1347 // isNonTextFirstContinuation is false? We should consider doing this just for
1348 // primary frames and pseudos, but the first-line reparenting code makes it
1349 // all bad, should get around to bug 1465474 eventually :(
1350 const bool isNonText = !IsTextFrame();
1351 if (isNonText) {
1352 mComputedStyle->StartImageLoads(*doc, aOldComputedStyle);
1353 }
1354
1355 const bool isRootElementStyle = Style()->IsRootElementStyle();
1356 if (isRootElementStyle) {
1357 PresShell()->SetNeedsWindowPropertiesSync();
1358 }
1359
1360 const nsStyleImageLayers* oldLayers =
1361 aOldComputedStyle ? &aOldComputedStyle->StyleBackground()->mImage
1362 : nullptr;
1363 const nsStyleImageLayers* newLayers = &StyleBackground()->mImage;
1364 AddAndRemoveImageAssociations(loader, this, oldLayers, newLayers);
1365
1366 oldLayers =
1367 aOldComputedStyle ? &aOldComputedStyle->StyleSVGReset()->mMask : nullptr;
1368 newLayers = &StyleSVGReset()->mMask;
1369 AddAndRemoveImageAssociations(loader, this, oldLayers, newLayers);
1370
1371 const nsStyleDisplay* disp = StyleDisplay();
1372 if (aOldComputedStyle) {
1373 // Detect style changes that should trigger a scroll anchor adjustment
1374 // suppression.
1375 // https://drafts.csswg.org/css-scroll-anchoring/#suppression-triggers
1376 bool needScrollAnchorSuppression = false;
1377
1378 const nsStyleMargin* oldMargin = aOldComputedStyle->StyleMargin();
1379 if (!oldMargin->MarginEquals(*StyleMargin())) {
1380 needScrollAnchorSuppression = true;
1381 }
1382
1383 const nsStylePadding* oldPadding = aOldComputedStyle->StylePadding();
1384 if (oldPadding->mPadding != StylePadding()->mPadding) {
1385 SetHasPaddingChange(true);
1386 needScrollAnchorSuppression = true;
1387 }
1388
1389 const nsStyleDisplay* oldDisp = aOldComputedStyle->StyleDisplay();
1390 if (oldDisp->mOverflowAnchor != disp->mOverflowAnchor) {
1391 if (auto* container = ScrollAnchorContainer::FindFor(this)) {
1392 container->InvalidateAnchor();
1393 }
1394 if (ScrollContainerFrame* scrollContainerFrame = do_QueryFrame(this)) {
1395 scrollContainerFrame->Anchor()->InvalidateAnchor();
1396 }
1397 }
1398
1399 if (mInScrollAnchorChain) {
1400 const nsStylePosition* pos = StylePosition();
1401 const nsStylePosition* oldPos = aOldComputedStyle->StylePosition();
1402 if (!needScrollAnchorSuppression &&
1403 (oldPos->mOffset != pos->mOffset || oldPos->mWidth != pos->mWidth ||
1404 oldPos->mMinWidth != pos->mMinWidth ||
1405 oldPos->mMaxWidth != pos->mMaxWidth ||
1406 oldPos->mHeight != pos->mHeight ||
1407 oldPos->mMinHeight != pos->mMinHeight ||
1408 oldPos->mMaxHeight != pos->mMaxHeight ||
1409 oldDisp->mPosition != disp->mPosition ||
1410 oldDisp->mTransform != disp->mTransform)) {
1411 needScrollAnchorSuppression = true;
1412 }
1413
1414 if (needScrollAnchorSuppression &&
1415 StaticPrefs::layout_css_scroll_anchoring_suppressions_enabled()) {
1416 ScrollAnchorContainer::FindFor(this)->SuppressAdjustments();
1417 }
1418 }
1419
1420 if (disp->mPosition != oldDisp->mPosition) {
1421 if (!disp->IsRelativelyOrStickyPositionedStyle() &&
1422 oldDisp->IsRelativelyOrStickyPositionedStyle()) {
1423 RemoveProperty(NormalPositionProperty());
1424 }
1425 }
1426 if (disp->mScrollSnapAlign != oldDisp->mScrollSnapAlign) {
1427 ScrollSnapUtils::PostPendingResnapFor(this);
1428 }
1429 if (isRootElementStyle &&
1430 disp->mScrollSnapType != oldDisp->mScrollSnapType) {
1431 if (ScrollContainerFrame* sf =
1432 PresShell()->GetRootScrollContainerFrame()) {
1433 sf->PostPendingResnap();
1434 }
1435 }
1436 if (StyleUIReset()->mMozSubtreeHiddenOnlyVisually &&
1437 !aOldComputedStyle->StyleUIReset()->mMozSubtreeHiddenOnlyVisually) {
1438 PresShell::ClearMouseCapture(this);
1439 }
1440 }
1441
1442 imgIRequest* oldBorderImage =
1443 aOldComputedStyle
1444 ? aOldComputedStyle->StyleBorder()->GetBorderImageRequest()
1445 : nullptr;
1446 imgIRequest* newBorderImage = StyleBorder()->GetBorderImageRequest();
1447 // FIXME (Bug 759996): The following is no longer true.
1448 // For border-images, we can't be as conservative (we need to set the
1449 // new loaders if there has been any change) since the CalcDifference
1450 // call depended on the result of GetComputedBorder() and that result
1451 // depends on whether the image has loaded, start the image load now
1452 // so that we'll get notified when it completes loading and can do a
1453 // restyle. Otherwise, the image might finish loading from the
1454 // network before we start listening to its notifications, and then
1455 // we'll never know that it's finished loading. Likewise, we want to
1456 // do this for freshly-created frames to prevent a similar race if the
1457 // image loads between reflow (which can depend on whether the image
1458 // is loaded) and paint. We also don't really care about any callers who try
1459 // to paint borders with a different style, because they won't have the
1460 // correct size for the border either.
1461 if (oldBorderImage != newBorderImage) {
1462 // stop and restart the image loading/notification
1463 if (oldBorderImage && HasImageRequest()) {
1464 loader.DisassociateRequestFromFrame(oldBorderImage, this);
1465 }
1466 if (newBorderImage) {
1467 loader.AssociateRequestToFrame(newBorderImage, this);
1468 }
1469 }
1470
1471 auto GetShapeImageRequest = [](const ComputedStyle* aStyle) -> imgIRequest* {
1472 if (!aStyle) {
1473 return nullptr;
1474 }
1475 auto& shape = aStyle->StyleDisplay()->mShapeOutside;
1476 if (!shape.IsImage()) {
1477 return nullptr;
1478 }
1479 return shape.AsImage().GetImageRequest();
1480 };
1481
1482 imgIRequest* oldShapeImage = GetShapeImageRequest(aOldComputedStyle);
1483 imgIRequest* newShapeImage = GetShapeImageRequest(Style());
1484 if (oldShapeImage != newShapeImage) {
1485 if (oldShapeImage && HasImageRequest()) {
1486 loader.DisassociateRequestFromFrame(oldShapeImage, this);
1487 }
1488 if (newShapeImage) {
1489 loader.AssociateRequestToFrame(
1490 newShapeImage, this,
1491 ImageLoader::Flags::
1492 RequiresReflowOnFirstFrameCompleteAndLoadEventBlocking);
1493 }
1494 }
1495
1496 // SVGObserverUtils::GetEffectProperties() asserts that we only invoke it with
1497 // the first continuation so we need to check that in advance.
1498 const bool isNonTextFirstContinuation = isNonText && !GetPrevContinuation();
1499 if (isNonTextFirstContinuation) {
1500 // Kick off loading of external SVG resources referenced from properties if
1501 // any. This currently includes filter, clip-path, and mask.
1502 SVGObserverUtils::InitiateResourceDocLoads(this);
1503 }
1504
1505 // If the page contains markup that overrides text direction, and
1506 // does not contain any characters that would activate the Unicode
1507 // bidi algorithm, we need to call |SetBidiEnabled| on the pres
1508 // context before reflow starts. See bug 115921.
1509 if (StyleVisibility()->mDirection == StyleDirection::Rtl) {
1510 PresContext()->SetBidiEnabled();
1511 }
1512
1513 // The following part is for caching offset-path:path(). We cache the
1514 // flatten gfx path, so we don't have to rebuild and re-flattern it at
1515 // each cycle if we have animations on offset-* with a fixed offset-path.
1516 const StyleOffsetPath* oldPath =
1517 aOldComputedStyle ? &aOldComputedStyle->StyleDisplay()->mOffsetPath
1518 : nullptr;
1519 const StyleOffsetPath& newPath = StyleDisplay()->mOffsetPath;
1520 if (!oldPath || *oldPath != newPath) {
1521 // FIXME: Bug 1837042. Cache all basic shapes.
1522 if (newPath.IsPath()) {
1523 RefPtr<gfx::PathBuilder> builder = MotionPathUtils::GetPathBuilder();
1524 RefPtr<gfx::Path> path =
1525 MotionPathUtils::BuildSVGPath(newPath.AsSVGPathData(), builder);
1526 if (path) {
1527 // The newPath could be path('') (i.e. empty path), so its gfx path
1528 // could be nullptr, and so we only set property for a non-empty path.
1529 SetProperty(nsIFrame::OffsetPathCache(), path.forget().take());
1530 } else {
1531 // May have an old cached path, so we have to delete it.
1532 RemoveProperty(nsIFrame::OffsetPathCache());
1533 }
1534 } else if (oldPath) {
1535 RemoveProperty(nsIFrame::OffsetPathCache());
1536 }
1537 }
1538
1539 if (IsPrimaryFrame()) {
1540 MOZ_ASSERT(aOldComputedStyle)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aOldComputedStyle)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aOldComputedStyle))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("aOldComputedStyle"
, "./../../../layout/generic/nsIFrame.cpp", 1540); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aOldComputedStyle" ")"); do { MOZ_CrashSequence
(__null, 1540); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1541 HandlePrimaryFrameStyleChange(aOldComputedStyle);
1542 }
1543
1544 RemoveStateBits(NS_FRAME_SIMPLE_DISPLAYLIST);
1545
1546 mMayHaveRoundedCorners = true;
1547}
1548
1549void nsIFrame::HandleLastRememberedSize() {
1550 MOZ_ASSERT(IsPrimaryFrame())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsPrimaryFrame())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsPrimaryFrame()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("IsPrimaryFrame()"
, "./../../../layout/generic/nsIFrame.cpp", 1550); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsPrimaryFrame()" ")"); do { MOZ_CrashSequence
(__null, 1550); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1551 auto* element = Element::FromNodeOrNull(mContent);
1552 if (!element) {
1553 return;
1554 }
1555 const WritingMode wm = GetWritingMode();
1556 const nsStylePosition* stylePos = StylePosition();
1557 bool canRememberBSize = stylePos->ContainIntrinsicBSize(wm).HasAuto();
1558 bool canRememberISize = stylePos->ContainIntrinsicISize(wm).HasAuto();
1559 if (!canRememberBSize) {
1560 element->RemoveLastRememberedBSize();
1561 }
1562 if (!canRememberISize) {
1563 element->RemoveLastRememberedISize();
1564 }
1565 if ((canRememberBSize || canRememberISize) && !HidesContent()) {
1566 bool isNonReplacedInline = IsLineParticipant() && !IsReplaced();
1567 if (!isNonReplacedInline) {
1568 PresContext()->Document()->ObserveForLastRememberedSize(*element);
1569 return;
1570 }
1571 }
1572 PresContext()->Document()->UnobserveForLastRememberedSize(*element);
1573}
1574
1575#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED1
1576void nsIFrame::AssertNewStyleIsSane(ComputedStyle& aNewStyle) {
1577 MOZ_DIAGNOSTIC_ASSERT(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType
() || (mComputedStyle->GetPseudoType() == PseudoStyleType::
FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType
::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType
::MozText && aNewStyle.GetPseudoType() == PseudoStyleType
::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle
.GetPseudoType() == PseudoStyleType::MozText))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aNewStyle.GetPseudoType() ==
mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType
() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType
() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozText && aNewStyle.GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle
->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation
&& aNewStyle.GetPseudoType() == PseudoStyleType::MozText
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
, "./../../../layout/generic/nsIFrame.cpp", 1591); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
")"); do { MOZ_CrashSequence(__null, 1591); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1578 aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType
() || (mComputedStyle->GetPseudoType() == PseudoStyleType::
FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType
::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType
::MozText && aNewStyle.GetPseudoType() == PseudoStyleType
::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle
.GetPseudoType() == PseudoStyleType::MozText))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aNewStyle.GetPseudoType() ==
mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType
() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType
() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozText && aNewStyle.GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle
->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation
&& aNewStyle.GetPseudoType() == PseudoStyleType::MozText
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
, "./../../../layout/generic/nsIFrame.cpp", 1591); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
")"); do { MOZ_CrashSequence(__null, 1591); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1579 // ::first-line continuations are weird, this should probably be fixed viado { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType
() || (mComputedStyle->GetPseudoType() == PseudoStyleType::
FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType
::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType
::MozText && aNewStyle.GetPseudoType() == PseudoStyleType
::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle
.GetPseudoType() == PseudoStyleType::MozText))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aNewStyle.GetPseudoType() ==
mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType
() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType
() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozText && aNewStyle.GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle
->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation
&& aNewStyle.GetPseudoType() == PseudoStyleType::MozText
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
, "./../../../layout/generic/nsIFrame.cpp", 1591); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
")"); do { MOZ_CrashSequence(__null, 1591); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1580 // bug 1465474.do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType
() || (mComputedStyle->GetPseudoType() == PseudoStyleType::
FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType
::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType
::MozText && aNewStyle.GetPseudoType() == PseudoStyleType
::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle
.GetPseudoType() == PseudoStyleType::MozText))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aNewStyle.GetPseudoType() ==
mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType
() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType
() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozText && aNewStyle.GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle
->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation
&& aNewStyle.GetPseudoType() == PseudoStyleType::MozText
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
, "./../../../layout/generic/nsIFrame.cpp", 1591); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
")"); do { MOZ_CrashSequence(__null, 1591); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1581 (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine &&do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType
() || (mComputedStyle->GetPseudoType() == PseudoStyleType::
FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType
::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType
::MozText && aNewStyle.GetPseudoType() == PseudoStyleType
::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle
.GetPseudoType() == PseudoStyleType::MozText))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aNewStyle.GetPseudoType() ==
mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType
() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType
() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozText && aNewStyle.GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle
->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation
&& aNewStyle.GetPseudoType() == PseudoStyleType::MozText
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
, "./../../../layout/generic/nsIFrame.cpp", 1591); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
")"); do { MOZ_CrashSequence(__null, 1591); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1582 aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType
() || (mComputedStyle->GetPseudoType() == PseudoStyleType::
FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType
::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType
::MozText && aNewStyle.GetPseudoType() == PseudoStyleType
::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle
.GetPseudoType() == PseudoStyleType::MozText))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aNewStyle.GetPseudoType() ==
mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType
() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType
() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozText && aNewStyle.GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle
->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation
&& aNewStyle.GetPseudoType() == PseudoStyleType::MozText
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
, "./../../../layout/generic/nsIFrame.cpp", 1591); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
")"); do { MOZ_CrashSequence(__null, 1591); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1583 // ::first-letter continuations are broken, in particular floating ones,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType
() || (mComputedStyle->GetPseudoType() == PseudoStyleType::
FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType
::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType
::MozText && aNewStyle.GetPseudoType() == PseudoStyleType
::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle
.GetPseudoType() == PseudoStyleType::MozText))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aNewStyle.GetPseudoType() ==
mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType
() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType
() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozText && aNewStyle.GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle
->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation
&& aNewStyle.GetPseudoType() == PseudoStyleType::MozText
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
, "./../../../layout/generic/nsIFrame.cpp", 1591); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
")"); do { MOZ_CrashSequence(__null, 1591); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1584 // see bug 1490281. The construction code tries to fix this up after thedo { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType
() || (mComputedStyle->GetPseudoType() == PseudoStyleType::
FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType
::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType
::MozText && aNewStyle.GetPseudoType() == PseudoStyleType
::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle
.GetPseudoType() == PseudoStyleType::MozText))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aNewStyle.GetPseudoType() ==
mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType
() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType
() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozText && aNewStyle.GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle
->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation
&& aNewStyle.GetPseudoType() == PseudoStyleType::MozText
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
, "./../../../layout/generic/nsIFrame.cpp", 1591); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
")"); do { MOZ_CrashSequence(__null, 1591); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1585 // fact, then restyling undoes it...do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType
() || (mComputedStyle->GetPseudoType() == PseudoStyleType::
FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType
::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType
::MozText && aNewStyle.GetPseudoType() == PseudoStyleType
::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle
.GetPseudoType() == PseudoStyleType::MozText))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aNewStyle.GetPseudoType() ==
mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType
() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType
() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozText && aNewStyle.GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle
->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation
&& aNewStyle.GetPseudoType() == PseudoStyleType::MozText
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
, "./../../../layout/generic/nsIFrame.cpp", 1591); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
")"); do { MOZ_CrashSequence(__null, 1591); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1586 (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText &&do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType
() || (mComputedStyle->GetPseudoType() == PseudoStyleType::
FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType
::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType
::MozText && aNewStyle.GetPseudoType() == PseudoStyleType
::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle
.GetPseudoType() == PseudoStyleType::MozText))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aNewStyle.GetPseudoType() ==
mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType
() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType
() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozText && aNewStyle.GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle
->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation
&& aNewStyle.GetPseudoType() == PseudoStyleType::MozText
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
, "./../../../layout/generic/nsIFrame.cpp", 1591); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
")"); do { MOZ_CrashSequence(__null, 1591); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1587 aNewStyle.GetPseudoType() ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType
() || (mComputedStyle->GetPseudoType() == PseudoStyleType::
FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType
::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType
::MozText && aNewStyle.GetPseudoType() == PseudoStyleType
::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle
.GetPseudoType() == PseudoStyleType::MozText))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aNewStyle.GetPseudoType() ==
mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType
() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType
() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozText && aNewStyle.GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle
->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation
&& aNewStyle.GetPseudoType() == PseudoStyleType::MozText
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
, "./../../../layout/generic/nsIFrame.cpp", 1591); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
")"); do { MOZ_CrashSequence(__null, 1591); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1588 PseudoStyleType::MozFirstLetterContinuation) ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType
() || (mComputedStyle->GetPseudoType() == PseudoStyleType::
FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType
::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType
::MozText && aNewStyle.GetPseudoType() == PseudoStyleType
::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle
.GetPseudoType() == PseudoStyleType::MozText))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aNewStyle.GetPseudoType() ==
mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType
() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType
() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozText && aNewStyle.GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle
->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation
&& aNewStyle.GetPseudoType() == PseudoStyleType::MozText
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
, "./../../../layout/generic/nsIFrame.cpp", 1591); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
")"); do { MOZ_CrashSequence(__null, 1591); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1589 (mComputedStyle->GetPseudoType() ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType
() || (mComputedStyle->GetPseudoType() == PseudoStyleType::
FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType
::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType
::MozText && aNewStyle.GetPseudoType() == PseudoStyleType
::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle
.GetPseudoType() == PseudoStyleType::MozText))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aNewStyle.GetPseudoType() ==
mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType
() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType
() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozText && aNewStyle.GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle
->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation
&& aNewStyle.GetPseudoType() == PseudoStyleType::MozText
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
, "./../../../layout/generic/nsIFrame.cpp", 1591); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
")"); do { MOZ_CrashSequence(__null, 1591); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1590 PseudoStyleType::MozFirstLetterContinuation &&do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType
() || (mComputedStyle->GetPseudoType() == PseudoStyleType::
FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType
::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType
::MozText && aNewStyle.GetPseudoType() == PseudoStyleType
::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle
.GetPseudoType() == PseudoStyleType::MozText))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aNewStyle.GetPseudoType() ==
mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType
() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType
() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozText && aNewStyle.GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle
->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation
&& aNewStyle.GetPseudoType() == PseudoStyleType::MozText
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
, "./../../../layout/generic/nsIFrame.cpp", 1591); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
")"); do { MOZ_CrashSequence(__null, 1591); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1591 aNewStyle.GetPseudoType() == PseudoStyleType::MozText))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType
() || (mComputedStyle->GetPseudoType() == PseudoStyleType::
FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType
::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType
::MozText && aNewStyle.GetPseudoType() == PseudoStyleType
::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle
.GetPseudoType() == PseudoStyleType::MozText))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aNewStyle.GetPseudoType() ==
mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType
() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType
() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType
() == PseudoStyleType::MozText && aNewStyle.GetPseudoType
() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle
->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation
&& aNewStyle.GetPseudoType() == PseudoStyleType::MozText
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
, "./../../../layout/generic/nsIFrame.cpp", 1591); AnnotateMozCrashReason
("MOZ_DIAGNOSTIC_ASSERT" "(" "aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() || (mComputedStyle->GetPseudoType() == PseudoStyleType::FirstLine && aNewStyle.GetPseudoType() == PseudoStyleType::MozLineFrame) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozText && aNewStyle.GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation) || (mComputedStyle->GetPseudoType() == PseudoStyleType::MozFirstLetterContinuation && aNewStyle.GetPseudoType() == PseudoStyleType::MozText)"
")"); do { MOZ_CrashSequence(__null, 1591); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1592}
1593#endif
1594
1595/* virtual */
1596nsMargin nsIFrame::GetUsedMargin() const {
1597 nsMargin margin;
1598 if (((mState & NS_FRAME_FIRST_REFLOW) && !(mState & NS_FRAME_IN_REFLOW)) ||
1599 IsInSVGTextSubtree()) {
1600 return margin;
1601 }
1602
1603 if (nsMargin* m = GetProperty(UsedMarginProperty())) {
1604 margin = *m;
1605 } else if (!StyleMargin()->GetMargin(margin)) {
1606 // If we get here, our caller probably shouldn't be calling us...
1607 NS_ERROR(do { NS_DebugBreak(NS_DEBUG_ASSERTION, "Returning bogus 0-sized margin, because this margin "
"depends on layout & isn't cached!", "Error", "./../../../layout/generic/nsIFrame.cpp"
, 1609); MOZ_PretendNoReturn(); } while (0)
1608 "Returning bogus 0-sized margin, because this margin "do { NS_DebugBreak(NS_DEBUG_ASSERTION, "Returning bogus 0-sized margin, because this margin "
"depends on layout & isn't cached!", "Error", "./../../../layout/generic/nsIFrame.cpp"
, 1609); MOZ_PretendNoReturn(); } while (0)
1609 "depends on layout & isn't cached!")do { NS_DebugBreak(NS_DEBUG_ASSERTION, "Returning bogus 0-sized margin, because this margin "
"depends on layout & isn't cached!", "Error", "./../../../layout/generic/nsIFrame.cpp"
, 1609); MOZ_PretendNoReturn(); } while (0)
;
1610 }
1611 return margin;
1612}
1613
1614/* virtual */
1615nsMargin nsIFrame::GetUsedBorder() const {
1616 if (((mState & NS_FRAME_FIRST_REFLOW) && !(mState & NS_FRAME_IN_REFLOW)) ||
1617 IsInSVGTextSubtree()) {
1618 return {};
1619 }
1620
1621 const nsStyleDisplay* disp = StyleDisplay();
1622 if (IsThemed(disp)) {
1623 // Theme methods don't use const-ness.
1624 auto* mutable_this = const_cast<nsIFrame*>(this);
1625 nsPresContext* pc = PresContext();
1626 LayoutDeviceIntMargin widgetBorder = pc->Theme()->GetWidgetBorder(
1627 pc->DeviceContext(), mutable_this, disp->EffectiveAppearance());
1628 return LayoutDevicePixel::ToAppUnits(widgetBorder,
1629 pc->AppUnitsPerDevPixel());
1630 }
1631
1632 return StyleBorder()->GetComputedBorder();
1633}
1634
1635/* virtual */
1636nsMargin nsIFrame::GetUsedPadding() const {
1637 nsMargin padding;
1638 if (((mState & NS_FRAME_FIRST_REFLOW) && !(mState & NS_FRAME_IN_REFLOW)) ||
1639 IsInSVGTextSubtree()) {
1640 return padding;
1641 }
1642
1643 const nsStyleDisplay* disp = StyleDisplay();
1644 if (IsThemed(disp)) {
1645 // Theme methods don't use const-ness.
1646 nsIFrame* mutable_this = const_cast<nsIFrame*>(this);
1647 nsPresContext* pc = PresContext();
1648 LayoutDeviceIntMargin widgetPadding;
1649 if (pc->Theme()->GetWidgetPadding(pc->DeviceContext(), mutable_this,
1650 disp->EffectiveAppearance(),
1651 &widgetPadding)) {
1652 return LayoutDevicePixel::ToAppUnits(widgetPadding,
1653 pc->AppUnitsPerDevPixel());
1654 }
1655 }
1656
1657 if (nsMargin* p = GetProperty(UsedPaddingProperty())) {
1658 padding = *p;
1659 } else if (!StylePadding()->GetPadding(padding)) {
1660 // If we get here, our caller probably shouldn't be calling us...
1661 NS_ERROR(do { NS_DebugBreak(NS_DEBUG_ASSERTION, "Returning bogus 0-sized padding, because this padding "
"depends on layout & isn't cached!", "Error", "./../../../layout/generic/nsIFrame.cpp"
, 1663); MOZ_PretendNoReturn(); } while (0)
1662 "Returning bogus 0-sized padding, because this padding "do { NS_DebugBreak(NS_DEBUG_ASSERTION, "Returning bogus 0-sized padding, because this padding "
"depends on layout & isn't cached!", "Error", "./../../../layout/generic/nsIFrame.cpp"
, 1663); MOZ_PretendNoReturn(); } while (0)
1663 "depends on layout & isn't cached!")do { NS_DebugBreak(NS_DEBUG_ASSERTION, "Returning bogus 0-sized padding, because this padding "
"depends on layout & isn't cached!", "Error", "./../../../layout/generic/nsIFrame.cpp"
, 1663); MOZ_PretendNoReturn(); } while (0)
;
1664 }
1665 return padding;
1666}
1667
1668nsIFrame::Sides nsIFrame::GetSkipSides() const {
1669 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==(__builtin_expect(!!(StyleBorder()->mBoxDecorationBreak ==
StyleBoxDecorationBreak::Clone), 0))
1670 StyleBoxDecorationBreak::Clone)(__builtin_expect(!!(StyleBorder()->mBoxDecorationBreak ==
StyleBoxDecorationBreak::Clone), 0))
&&
1671 !HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
1672 return Sides();
1673 }
1674
1675 // Convert the logical skip sides to physical sides using the frame's
1676 // writing mode
1677 WritingMode writingMode = GetWritingMode();
1678 LogicalSides logicalSkip = GetLogicalSkipSides();
1679 Sides skip;
1680
1681 if (logicalSkip.BStart()) {
1682 if (writingMode.IsVertical()) {
1683 skip |= writingMode.IsVerticalLR() ? SideBits::eLeft : SideBits::eRight;
1684 } else {
1685 skip |= SideBits::eTop;
1686 }
1687 }
1688
1689 if (logicalSkip.BEnd()) {
1690 if (writingMode.IsVertical()) {
1691 skip |= writingMode.IsVerticalLR() ? SideBits::eRight : SideBits::eLeft;
1692 } else {
1693 skip |= SideBits::eBottom;
1694 }
1695 }
1696
1697 if (logicalSkip.IStart()) {
1698 if (writingMode.IsVertical()) {
1699 skip |=
1700 writingMode.IsInlineReversed() ? SideBits::eBottom : SideBits::eTop;
1701 } else {
1702 skip |= writingMode.IsBidiLTR() ? SideBits::eLeft : SideBits::eRight;
1703 }
1704 }
1705
1706 if (logicalSkip.IEnd()) {
1707 if (writingMode.IsVertical()) {
1708 skip |=
1709 writingMode.IsInlineReversed() ? SideBits::eTop : SideBits::eBottom;
1710 } else {
1711 skip |= writingMode.IsBidiLTR() ? SideBits::eRight : SideBits::eLeft;
1712 }
1713 }
1714 return skip;
1715}
1716
1717nsRect nsIFrame::GetPaddingRectRelativeToSelf() const {
1718 nsMargin border = GetUsedBorder().ApplySkipSides(GetSkipSides());
1719 nsRect r(0, 0, mRect.width, mRect.height);
1720 r.Deflate(border);
1721 return r;
1722}
1723
1724nsRect nsIFrame::GetPaddingRect() const {
1725 return GetPaddingRectRelativeToSelf() + GetPosition();
1726}
1727
1728WritingMode nsIFrame::WritingModeForLine(WritingMode aSelfWM,
1729 nsIFrame* aSubFrame) const {
1730 MOZ_ASSERT(aSelfWM == GetWritingMode())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aSelfWM == GetWritingMode())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aSelfWM == GetWritingMode())
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("aSelfWM == GetWritingMode()"
, "./../../../layout/generic/nsIFrame.cpp", 1730); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aSelfWM == GetWritingMode()" ")"); do { MOZ_CrashSequence
(__null, 1730); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1731 WritingMode writingMode = aSelfWM;
1732
1733 if (StyleTextReset()->mUnicodeBidi == StyleUnicodeBidi::Plaintext) {
1734 mozilla::intl::BidiEmbeddingLevel frameLevel =
1735 nsBidiPresUtils::GetFrameBaseLevel(aSubFrame);
1736 writingMode.SetDirectionFromBidiLevel(frameLevel);
1737 }
1738
1739 return writingMode;
1740}
1741
1742nsRect nsIFrame::GetMarginRect() const {
1743 return GetMarginRectRelativeToSelf() + GetPosition();
1744}
1745
1746nsRect nsIFrame::GetMarginRectRelativeToSelf() const {
1747 nsMargin m = GetUsedMargin().ApplySkipSides(GetSkipSides());
1748 nsRect r(0, 0, mRect.width, mRect.height);
1749 r.Inflate(m);
1750 return r;
1751}
1752
1753bool nsIFrame::IsTransformed() const {
1754 if (!HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED)) {
1755 MOZ_ASSERT(!IsCSSTransformed())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsCSSTransformed())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsCSSTransformed()))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("!IsCSSTransformed()"
, "./../../../layout/generic/nsIFrame.cpp", 1755); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsCSSTransformed()" ")"); do { MOZ_CrashSequence
(__null, 1755); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1756 MOZ_ASSERT(!GetParentSVGTransforms())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!GetParentSVGTransforms())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!GetParentSVGTransforms())))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("!GetParentSVGTransforms()"
, "./../../../layout/generic/nsIFrame.cpp", 1756); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!GetParentSVGTransforms()" ")"); do { MOZ_CrashSequence
(__null, 1756); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1757 return false;
1758 }
1759 return IsCSSTransformed() || GetParentSVGTransforms();
1760}
1761
1762bool nsIFrame::IsCSSTransformed() const {
1763 return HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED) &&
1764 (StyleDisplay()->HasTransform(this) || HasAnimationOfTransform());
1765}
1766
1767bool nsIFrame::HasAnimationOfTransform() const {
1768 if (!MayHaveTransformAnimation()) {
1769 MOZ_ASSERT(!IsPrimaryFrame() || !SupportsCSSTransforms() ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsPrimaryFrame() || !SupportsCSSTransforms() || !nsLayoutUtils
::HasAnimationOfTransformAndMotionPath(this))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsPrimaryFrame() || !SupportsCSSTransforms
() || !nsLayoutUtils::HasAnimationOfTransformAndMotionPath(this
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!IsPrimaryFrame() || !SupportsCSSTransforms() || !nsLayoutUtils::HasAnimationOfTransformAndMotionPath(this)"
, "./../../../layout/generic/nsIFrame.cpp", 1770); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsPrimaryFrame() || !SupportsCSSTransforms() || !nsLayoutUtils::HasAnimationOfTransformAndMotionPath(this)"
")"); do { MOZ_CrashSequence(__null, 1770); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1770 !nsLayoutUtils::HasAnimationOfTransformAndMotionPath(this))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsPrimaryFrame() || !SupportsCSSTransforms() || !nsLayoutUtils
::HasAnimationOfTransformAndMotionPath(this))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsPrimaryFrame() || !SupportsCSSTransforms
() || !nsLayoutUtils::HasAnimationOfTransformAndMotionPath(this
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!IsPrimaryFrame() || !SupportsCSSTransforms() || !nsLayoutUtils::HasAnimationOfTransformAndMotionPath(this)"
, "./../../../layout/generic/nsIFrame.cpp", 1770); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsPrimaryFrame() || !SupportsCSSTransforms() || !nsLayoutUtils::HasAnimationOfTransformAndMotionPath(this)"
")"); do { MOZ_CrashSequence(__null, 1770); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1771 return false;
1772 }
1773 return IsPrimaryFrame() && SupportsCSSTransforms() &&
1774 nsLayoutUtils::HasAnimationOfTransformAndMotionPath(this);
1775}
1776
1777bool nsIFrame::ChildrenHavePerspective(
1778 const nsStyleDisplay* aStyleDisplay) const {
1779 MOZ_ASSERT(aStyleDisplay == StyleDisplay())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aStyleDisplay == StyleDisplay())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aStyleDisplay == StyleDisplay
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aStyleDisplay == StyleDisplay()", "./../../../layout/generic/nsIFrame.cpp"
, 1779); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aStyleDisplay == StyleDisplay()"
")"); do { MOZ_CrashSequence(__null, 1779); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1780 return aStyleDisplay->HasPerspective(this);
1781}
1782
1783bool nsIFrame::HasAnimationOfOpacity(EffectSet* aEffectSet) const {
1784 return ((nsLayoutUtils::IsPrimaryStyleFrame(this) ||
1785 nsLayoutUtils::FirstContinuationOrIBSplitSibling(this)
1786 ->IsPrimaryFrame()) &&
1787 nsLayoutUtils::HasAnimationOfPropertySet(
1788 this, nsCSSPropertyIDSet::OpacityProperties(), aEffectSet));
1789}
1790
1791bool nsIFrame::HasOpacityInternal(float aThreshold,
1792 const nsStyleDisplay* aStyleDisplay,
1793 const nsStyleEffects* aStyleEffects,
1794 EffectSet* aEffectSet) const {
1795 MOZ_ASSERT(0.0 <= aThreshold && aThreshold <= 1.0, "Invalid argument")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(0.0 <= aThreshold && aThreshold <= 1.0
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(0.0 <= aThreshold && aThreshold <= 1.0
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"0.0 <= aThreshold && aThreshold <= 1.0" " (" "Invalid argument"
")", "./../../../layout/generic/nsIFrame.cpp", 1795); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "0.0 <= aThreshold && aThreshold <= 1.0"
") (" "Invalid argument" ")"); do { MOZ_CrashSequence(__null
, 1795); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
;
1796 if (aStyleEffects->mOpacity < aThreshold ||
1797 aStyleDisplay->mWillChange.bits & StyleWillChangeBits::OPACITY) {
1798 return true;
1799 }
1800
1801 if (!mMayHaveOpacityAnimation) {
1802 return false;
1803 }
1804
1805 return HasAnimationOfOpacity(aEffectSet);
1806}
1807
1808bool nsIFrame::DoGetParentSVGTransforms(gfx::Matrix*) const { return false; }
1809
1810bool nsIFrame::Extend3DContext(const nsStyleDisplay* aStyleDisplay,
1811 const nsStyleEffects* aStyleEffects,
1812 mozilla::EffectSet* aEffectSetForOpacity) const {
1813 if (!HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED)) {
1814 return false;
1815 }
1816 const nsStyleDisplay* disp = StyleDisplayWithOptionalParam(aStyleDisplay);
1817 if (disp->mTransformStyle != StyleTransformStyle::Preserve3d ||
1818 !SupportsCSSTransforms()) {
1819 return false;
1820 }
1821
1822 // If we're all scroll frame, then all descendants will be clipped, so we
1823 // can't preserve 3d.
1824 if (IsScrollContainerFrame()) {
1825 return false;
1826 }
1827
1828 const nsStyleEffects* effects = StyleEffectsWithOptionalParam(aStyleEffects);
1829 if (HasOpacity(disp, effects, aEffectSetForOpacity)) {
1830 return false;
1831 }
1832
1833 return ShouldApplyOverflowClipping(disp).isEmpty() &&
1834 !GetClipPropClipRect(disp, effects, GetSize()) &&
1835 !SVGIntegrationUtils::UsingEffectsForFrame(this) &&
1836 !effects->HasMixBlendMode() &&
1837 !ForcesStackingContextForViewTransition() &&
1838 disp->mIsolation != StyleIsolation::Isolate;
1839}
1840
1841bool nsIFrame::Combines3DTransformWithAncestors() const {
1842 // Check these first as they are faster then both calls below and are we are
1843 // likely to hit the early return (backface hidden is uncommon and
1844 // GetReferenceFrame is a hot caller of this which only calls this if
1845 // IsCSSTransformed is false).
1846 if (!IsCSSTransformed() && !BackfaceIsHidden()) {
1847 return false;
1848 }
1849 nsIFrame* parent = GetClosestFlattenedTreeAncestorPrimaryFrame();
1850 return parent && parent->Extend3DContext();
1851}
1852
1853bool nsIFrame::In3DContextAndBackfaceIsHidden() const {
1854 // While both tests fail most of the time, test BackfaceIsHidden()
1855 // first since it's likely to fail faster.
1856 return BackfaceIsHidden() && Combines3DTransformWithAncestors();
1857}
1858
1859bool nsIFrame::HasPerspective() const {
1860 if (!IsCSSTransformed()) {
1861 return false;
1862 }
1863 nsIFrame* parent = GetClosestFlattenedTreeAncestorPrimaryFrame();
1864 if (!parent) {
1865 return false;
1866 }
1867 return parent->ChildrenHavePerspective();
1868}
1869
1870nsRect nsIFrame::GetContentRectRelativeToSelf() const {
1871 nsMargin bp = GetUsedBorderAndPadding().ApplySkipSides(GetSkipSides());
1872 nsRect r(0, 0, mRect.width, mRect.height);
1873 r.Deflate(bp);
1874 return r;
1875}
1876
1877nsRect nsIFrame::GetContentRect() const {
1878 return GetContentRectRelativeToSelf() + GetPosition();
1879}
1880
1881bool nsIFrame::ComputeBorderRadii(const BorderRadius& aBorderRadius,
1882 const CornerShapeRect& aCornerShape,
1883 const nsSize& aFrameSize,
1884 const nsSize& aBorderArea, Sides aSkipSides,
1885 nsRectCornerRadii& aRadii) {
1886 // Percentages are relative to whichever side they're on.
1887 for (const auto i : mozilla::AllPhysicalHalfCorners()) {
1888 const LengthPercentage& c = aBorderRadius.Get(i);
1889 nscoord axis = HalfCornerIsX(i) ? aFrameSize.width : aFrameSize.height;
1890 aRadii[i] = std::max(0, c.Resolve(axis));
1891 }
1892
1893 aRadii.mShapeK[mozilla::eCornerTopLeft] = aCornerShape.top_left.k;
1894 aRadii.mShapeK[mozilla::eCornerTopRight] = aCornerShape.top_right.k;
1895 aRadii.mShapeK[mozilla::eCornerBottomLeft] = aCornerShape.bottom_left.k;
1896 aRadii.mShapeK[mozilla::eCornerBottomRight] = aCornerShape.bottom_right.k;
1897
1898 bool isTopLeftSquare =
1899 std::isinf(aCornerShape.top_left.k) && (aCornerShape.top_left.k > 0.0f);
1900 bool isTopRightSquare =
1901 std::isinf(aCornerShape.top_right.k) && (aCornerShape.top_right.k > 0.0f);
1902 bool isBottomLeftSquare = std::isinf(aCornerShape.bottom_left.k) &&
1903 (aCornerShape.bottom_left.k > 0.0f);
1904 bool isBottomRightSquare = std::isinf(aCornerShape.bottom_right.k) &&
1905 (aCornerShape.bottom_right.k > 0.0f);
1906
1907 if (aSkipSides.Intersects(SideBits::eTop | SideBits::eLeft) ||
1908 isTopLeftSquare) {
1909 aRadii.TopLeft() = {};
1910 }
1911 if (aSkipSides.Intersects(SideBits::eTop | SideBits::eRight) ||
1912 isTopRightSquare) {
1913 aRadii.TopRight() = {};
1914 }
1915 if (aSkipSides.Intersects(SideBits::eBottom | SideBits::eLeft) ||
1916 isBottomLeftSquare) {
1917 aRadii.BottomLeft() = {};
1918 }
1919 if (aSkipSides.Intersects(SideBits::eBottom | SideBits::eRight) ||
1920 isBottomRightSquare) {
1921 aRadii.BottomRight() = {};
1922 }
1923
1924 // css3-background specifies this algorithm for reducing
1925 // corner radii when they are too big.
1926 bool haveRadius = false;
1927 double ratio = 1.0f;
1928 for (const auto side : mozilla::AllPhysicalSides()) {
1929 auto hc1 = SideToHalfCorner(side, false, true);
1930 auto hc2 = SideToHalfCorner(side, true, true);
1931 nscoord length =
1932 SideIsVertical(side) ? aBorderArea.height : aBorderArea.width;
1933 nscoord sum = aRadii[hc1] + aRadii[hc2];
1934 if (sum) {
1935 haveRadius = true;
1936 // avoid floating point division in the normal case
1937 if (length < sum) {
1938 ratio = std::min(ratio, double(length) / sum);
1939 }
1940 }
1941 }
1942 if (ratio < 1.0) {
1943 for (const auto corner : mozilla::AllPhysicalHalfCorners()) {
1944 aRadii[corner] *= ratio;
1945 }
1946 }
1947
1948 return haveRadius;
1949}
1950
1951static inline bool RadiiAreDefinitelyZero(const BorderRadius& aBorderRadius) {
1952 for (const auto corner : mozilla::AllPhysicalHalfCorners()) {
1953 if (!aBorderRadius.Get(corner).IsDefinitelyZero()) {
1954 return false;
1955 }
1956 }
1957 return true;
1958}
1959
1960/* virtual */
1961bool nsIFrame::GetBorderRadii(const nsSize& aFrameSize,
1962 const nsSize& aBorderArea, Sides aSkipSides,
1963 nsRectCornerRadii& aRadii) const {
1964 if (!mMayHaveRoundedCorners) {
1965 return false;
1966 }
1967
1968 if (IsThemed()) {
1969 // When we're themed, the native theme code draws the border and
1970 // background, and therefore it doesn't make sense to tell other
1971 // code that's interested in border-radius that we have any radii.
1972 //
1973 // In an ideal world, we might have a way for the them to tell us an
1974 // border radius, but since we don't, we're better off assuming
1975 // zero.
1976 return false;
1977 }
1978
1979 const auto& radii = StyleBorder()->mBorderRadius;
1980 const auto& cornerShape = StyleBorder()->mCornerShape;
1981 const bool hasRadii = ComputeBorderRadii(radii, cornerShape, aFrameSize,
1982 aBorderArea, aSkipSides, aRadii);
1983 if (!hasRadii) {
1984 // TODO(emilio): Maybe we can just remove this bit and do the
1985 // IsDefinitelyZero check unconditionally. That should still avoid most of
1986 // the work, though maybe not the cache miss of going through the style and
1987 // the border struct.
1988 const_cast<nsIFrame*>(this)->mMayHaveRoundedCorners =
1989 !RadiiAreDefinitelyZero(radii);
1990 }
1991 return hasRadii;
1992}
1993
1994bool nsIFrame::GetBorderRadii(nsRectCornerRadii& aRadii) const {
1995 nsSize sz = GetSize();
1996 return GetBorderRadii(sz, sz, GetSkipSides(), aRadii);
1997}
1998
1999bool nsIFrame::GetMarginBoxBorderRadii(nsRectCornerRadii& aRadii) const {
2000 if (!GetBorderRadii(aRadii)) {
2001 return false;
2002 }
2003 aRadii.AdjustOutwards(GetUsedMargin());
2004 return true;
2005}
2006
2007bool nsIFrame::GetPaddingBoxBorderRadii(nsRectCornerRadii& aRadii) const {
2008 if (!GetBorderRadii(aRadii)) {
2009 return false;
2010 }
2011 aRadii.AdjustInwards(GetUsedBorder());
2012 return true;
2013}
2014
2015bool nsIFrame::GetContentBoxBorderRadii(nsRectCornerRadii& aRadii) const {
2016 if (!GetBorderRadii(aRadii)) {
2017 return false;
2018 }
2019 aRadii.AdjustInwards(GetUsedBorderAndPadding());
2020 return true;
2021}
2022
2023bool nsIFrame::GetShapeBoxBorderRadii(nsRectCornerRadii& aRadii) const {
2024 using Tag = StyleShapeOutside::Tag;
2025 auto& shapeOutside = StyleDisplay()->mShapeOutside;
2026 auto box = StyleShapeBox::MarginBox;
2027 switch (shapeOutside.tag) {
2028 case Tag::Image:
2029 case Tag::None:
2030 return false;
2031 case Tag::Box:
2032 box = shapeOutside.AsBox();
2033 break;
2034 case Tag::Shape:
2035 box = shapeOutside.AsShape()._1;
2036 break;
2037 }
2038
2039 switch (box) {
2040 case StyleShapeBox::ContentBox:
2041 return GetContentBoxBorderRadii(aRadii);
2042 case StyleShapeBox::PaddingBox:
2043 return GetPaddingBoxBorderRadii(aRadii);
2044 case StyleShapeBox::BorderBox:
2045 return GetBorderRadii(aRadii);
2046 case StyleShapeBox::MarginBox:
2047 return GetMarginBoxBorderRadii(aRadii);
2048 default:
2049 MOZ_ASSERT_UNREACHABLE("Unexpected box value")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"Unexpected box value" ")", "./../../../layout/generic/nsIFrame.cpp"
, 2049); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Unexpected box value" ")"); do {
MOZ_CrashSequence(__null, 2049); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
2050 return false;
2051 }
2052}
2053
2054nscoord nsIFrame::OneEmInAppUnits() const {
2055 return StyleFont()
2056 ->mFont.size.ScaledBy(nsLayoutUtils::FontSizeInflationFor(this))
2057 .ToAppUnits();
2058}
2059
2060RubyMetrics nsIFrame::RubyMetrics(float aRubyMetricsFactor) const {
2061 RefPtr<nsFontMetrics> fm =
2062 nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
2063 return mozilla::RubyMetrics{
2064 nscoord(NS_round(fm->TrimmedAscent() * aRubyMetricsFactor)),
2065 nscoord(NS_round(fm->TrimmedDescent() * aRubyMetricsFactor))};
2066}
2067
2068nscoord nsIFrame::SynthesizeFallbackBaseline(
2069 WritingMode aWM, BaselineSharingGroup aBaselineGroup) const {
2070 NS_ASSERTION(!IsSubtreeDirty(), "frame must not be dirty")do { if (!(!IsSubtreeDirty())) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "frame must not be dirty", "!IsSubtreeDirty()", "./../../../layout/generic/nsIFrame.cpp"
, 2070); MOZ_PretendNoReturn(); } } while (0)
;
2071 return Baseline::SynthesizeBOffsetFromMarginBox(this, aWM, aBaselineGroup);
2072}
2073
2074nscoord nsIFrame::GetLogicalBaseline(WritingMode aWM) const {
2075 return GetLogicalBaseline(aWM, GetDefaultBaselineSharingGroup(),
2076 BaselineExportContext::LineLayout);
2077}
2078
2079nscoord nsIFrame::GetLogicalBaseline(
2080 WritingMode aWM, BaselineSharingGroup aBaselineGroup,
2081 BaselineExportContext aExportContext) const {
2082 const auto result =
2083 GetNaturalBaselineBOffset(aWM, aBaselineGroup, aExportContext)
2084 .valueOrFrom([this, aWM, aBaselineGroup]() {
2085 return SynthesizeFallbackBaseline(aWM, aBaselineGroup);
2086 });
2087 if (aBaselineGroup == BaselineSharingGroup::Last) {
2088 return BSize(aWM) - result;
2089 }
2090 return result;
2091}
2092
2093const nsFrameList& nsIFrame::GetChildList(ChildListID aListID) const {
2094 if (IsAbsoluteContainer()) {
2095 if (aListID == FrameChildListID::Absolute) {
2096 return GetAbsoluteContainingBlock()->GetChildList();
2097 }
2098 if (aListID == FrameChildListID::PushedAbsolute) {
2099 return GetAbsoluteContainingBlock()->GetPushedChildList();
2100 }
2101 }
2102 return nsFrameList::EmptyList();
2103}
2104
2105void nsIFrame::GetChildLists(nsTArray<ChildList>* aLists) const {
2106 if (const auto* absCB = GetAbsoluteContainingBlock()) {
2107 const nsFrameList& absoluteList = absCB->GetChildList();
2108 absoluteList.AppendIfNonempty(aLists, FrameChildListID::Absolute);
2109 const nsFrameList& pushedAbsoluteList = absCB->GetPushedChildList();
2110 pushedAbsoluteList.AppendIfNonempty(aLists,
2111 FrameChildListID::PushedAbsolute);
2112 }
2113}
2114
2115AutoTArray<nsIFrame::ChildList, 4> nsIFrame::CrossDocChildLists() {
2116 AutoTArray<ChildList, 4> childLists;
2117 nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(this);
2118 if (subdocumentFrame) {
2119 // Descend into the subdocument
2120 nsIFrame* root = subdocumentFrame->GetSubdocumentRootFrame();
2121 if (root) {
2122 childLists.EmplaceBack(
2123 nsFrameList(root, nsLayoutUtils::GetLastSibling(root)),
2124 FrameChildListID::Principal);
2125 }
2126 }
2127
2128 GetChildLists(&childLists);
2129 return childLists;
2130}
2131
2132nsIFrame::CaretBlockAxisMetrics nsIFrame::GetCaretBlockAxisMetrics(
2133 mozilla::WritingMode aWM, const nsFontMetrics& aFM) const {
2134 // Note(dshin): Ultimately, this does something highly similar (But still
2135 // different) to `nsLayoutUtils::GetFirstLinePosition`.
2136 const auto baseline = GetCaretBaseline();
2137 nscoord ascent = 0, descent = 0;
2138 ascent = aFM.MaxAscent();
2139 descent = aFM.MaxDescent();
2140 const nscoord height = ascent + descent;
2141 if (aWM.IsVertical() && aWM.IsLineInverted()) {
2142 return CaretBlockAxisMetrics{.mOffset = baseline - descent,
2143 .mExtent = height};
2144 }
2145 return CaretBlockAxisMetrics{.mOffset = baseline - ascent, .mExtent = height};
2146}
2147
2148nscoord nsIFrame::GetFontMetricsDerivedCaretBaseline() const {
2149 float inflation = nsLayoutUtils::FontSizeInflationFor(this);
2150 RefPtr<nsFontMetrics> fm =
2151 nsLayoutUtils::GetFontMetricsForFrame(this, inflation);
2152 const WritingMode wm = GetWritingMode();
2153 const nscoord lineHeight = ReflowInput::CalcLineHeight(
2154 *Style(), PresContext(), GetContent(), inflation);
2155 return nsLayoutUtils::GetCenteredFontBaseline(fm, lineHeight,
2156 wm.IsLineInverted()) +
2157 GetLogicalUsedBorderAndPadding(wm).BStart(wm);
2158}
2159
2160const nsAtom* nsIFrame::ComputePageValue(const nsAtom* aAutoValue) const {
2161 const nsAtom* value = aAutoValue ? aAutoValue : nsGkAtoms::_empty;
2162 const nsIFrame* frame = this;
2163 // Find what CSS page name value this frame's subtree has, if any.
2164 // Starting with this frame, check if a page name other than auto is present,
2165 // and record it if so. Then, if the current frame is a container frame, find
2166 // the first non-placeholder child and repeat.
2167 // This will find the most deeply nested first in-flow child of this frame's
2168 // subtree, and return its page name (with auto resolved if applicable, and
2169 // subtrees with no page-names returning the empty atom rather than null).
2170 do {
2171 if (const nsAtom* maybePageName = frame->GetStylePageName()) {
2172 value = maybePageName;
2173 }
2174 // Get the next frame to read from.
2175 const nsIFrame* firstNonPlaceholderFrame = nullptr;
2176 // If this is a container frame, inspect its in-flow children.
2177 if (const nsContainerFrame* containerFrame = do_QueryFrame(frame)) {
2178 for (const nsIFrame* childFrame : containerFrame->PrincipalChildList()) {
2179 if (!childFrame->IsPlaceholderFrame()) {
2180 firstNonPlaceholderFrame = childFrame;
2181 break;
2182 }
2183 }
2184 }
2185 frame = firstNonPlaceholderFrame;
2186 } while (frame);
2187 return value;
2188}
2189
2190Visibility nsIFrame::GetVisibility() const {
2191 if (!HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED)) {
2192 return Visibility::Untracked;
2193 }
2194
2195 bool isSet = false;
2196 uint32_t visibleCount = GetProperty(VisibilityStateProperty(), &isSet);
2197
2198 MOZ_ASSERT(isSet,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(isSet)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(isSet))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("isSet" " (" "Should have a VisibilityStateProperty value "
"if NS_FRAME_VISIBILITY_IS_TRACKED is set" ")", "./../../../layout/generic/nsIFrame.cpp"
, 2200); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSet" ") ("
"Should have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is set"
")"); do { MOZ_CrashSequence(__null, 2200); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2199 "Should have a VisibilityStateProperty value "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(isSet)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(isSet))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("isSet" " (" "Should have a VisibilityStateProperty value "
"if NS_FRAME_VISIBILITY_IS_TRACKED is set" ")", "./../../../layout/generic/nsIFrame.cpp"
, 2200); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSet" ") ("
"Should have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is set"
")"); do { MOZ_CrashSequence(__null, 2200); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2200 "if NS_FRAME_VISIBILITY_IS_TRACKED is set")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(isSet)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(isSet))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("isSet" " (" "Should have a VisibilityStateProperty value "
"if NS_FRAME_VISIBILITY_IS_TRACKED is set" ")", "./../../../layout/generic/nsIFrame.cpp"
, 2200); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSet" ") ("
"Should have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is set"
")"); do { MOZ_CrashSequence(__null, 2200); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2201
2202 return visibleCount > 0 ? Visibility::ApproximatelyVisible
2203 : Visibility::ApproximatelyNonVisible;
2204}
2205
2206void nsIFrame::UpdateVisibilitySynchronously() {
2207 mozilla::PresShell* presShell = PresShell();
2208 if (!presShell) {
2209 return;
2210 }
2211
2212 if (presShell->AssumeAllFramesVisible()) {
2213 presShell->EnsureFrameInApproximatelyVisibleList(this);
2214 return;
2215 }
2216
2217 bool visible = StyleVisibility()->IsVisible();
2218 nsIFrame* f = GetParent();
2219 nsRect rect = GetRectRelativeToSelf();
2220 nsIFrame* rectFrame = this;
2221 while (f && visible) {
2222 if (ScrollContainerFrame* sf = do_QueryFrame(f)) {
2223 nsRect transformedRect =
2224 nsLayoutUtils::TransformFrameRectToAncestor(rectFrame, rect, f);
2225 if (!sf->IsRectNearlyVisible(transformedRect)) {
2226 visible = false;
2227 break;
2228 }
2229
2230 // In this code we're trying to synchronously update *approximate*
2231 // visibility. (In the future we may update precise visibility here as
2232 // well, which is why the method name does not contain 'approximate'.) The
2233 // IsRectNearlyVisible() check above tells us that the rect we're checking
2234 // is approximately visible within the scrollframe, but we still need to
2235 // ensure that, even if it was scrolled into view, it'd be visible when we
2236 // consider the rest of the document. To do that, we move transformedRect
2237 // to be contained in the scrollport as best we can (it might not fit) to
2238 // pretend that it was scrolled into view.
2239 rect = transformedRect.MoveInsideAndClamp(sf->GetScrollPortRect());
2240 rectFrame = f;
2241 }
2242 nsIFrame* parent = f->GetParent();
2243 if (!parent) {
2244 parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(f);
2245 if (parent && parent->PresContext()->IsChrome()) {
2246 break;
2247 }
2248 }
2249 f = parent;
2250 }
2251
2252 if (visible) {
2253 presShell->EnsureFrameInApproximatelyVisibleList(this);
2254 } else {
2255 presShell->RemoveFrameFromApproximatelyVisibleList(this);
2256 }
2257}
2258
2259void nsIFrame::EnableVisibilityTracking() {
2260 if (HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED)) {
2261 return; // Nothing to do.
2262 }
2263
2264 MOZ_ASSERT(!HasProperty(VisibilityStateProperty()),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!HasProperty(VisibilityStateProperty()))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!HasProperty(VisibilityStateProperty())))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!HasProperty(VisibilityStateProperty())"
" (" "Shouldn't have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is not set"
")", "./../../../layout/generic/nsIFrame.cpp", 2266); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!HasProperty(VisibilityStateProperty())" ") ("
"Shouldn't have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is not set"
")"); do { MOZ_CrashSequence(__null, 2266); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2265 "Shouldn't have a VisibilityStateProperty value "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!HasProperty(VisibilityStateProperty()))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!HasProperty(VisibilityStateProperty())))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!HasProperty(VisibilityStateProperty())"
" (" "Shouldn't have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is not set"
")", "./../../../layout/generic/nsIFrame.cpp", 2266); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!HasProperty(VisibilityStateProperty())" ") ("
"Shouldn't have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is not set"
")"); do { MOZ_CrashSequence(__null, 2266); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2266 "if NS_FRAME_VISIBILITY_IS_TRACKED is not set")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!HasProperty(VisibilityStateProperty()))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!HasProperty(VisibilityStateProperty())))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!HasProperty(VisibilityStateProperty())"
" (" "Shouldn't have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is not set"
")", "./../../../layout/generic/nsIFrame.cpp", 2266); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!HasProperty(VisibilityStateProperty())" ") ("
"Shouldn't have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is not set"
")"); do { MOZ_CrashSequence(__null, 2266); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2267
2268 // Add the state bit so we know to track visibility for this frame, and
2269 // initialize the frame property.
2270 AddStateBits(NS_FRAME_VISIBILITY_IS_TRACKED);
2271 SetProperty(VisibilityStateProperty(), 0);
2272
2273 mozilla::PresShell* presShell = PresShell();
2274 if (!presShell) {
2275 return;
2276 }
2277
2278 // Schedule a visibility update. This method will virtually always be called
2279 // when layout has changed anyway, so it's very unlikely that any additional
2280 // visibility updates will be triggered by this, but this way we guarantee
2281 // that if this frame is currently visible we'll eventually find out.
2282 presShell->ScheduleApproximateFrameVisibilityUpdateSoon();
2283}
2284
2285void nsIFrame::DisableVisibilityTracking() {
2286 if (!HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED)) {
2287 return; // Nothing to do.
2288 }
2289
2290 bool isSet = false;
2291 uint32_t visibleCount = TakeProperty(VisibilityStateProperty(), &isSet);
2292
2293 MOZ_ASSERT(isSet,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(isSet)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(isSet))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("isSet" " (" "Should have a VisibilityStateProperty value "
"if NS_FRAME_VISIBILITY_IS_TRACKED is set" ")", "./../../../layout/generic/nsIFrame.cpp"
, 2295); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSet" ") ("
"Should have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is set"
")"); do { MOZ_CrashSequence(__null, 2295); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2294 "Should have a VisibilityStateProperty value "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(isSet)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(isSet))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("isSet" " (" "Should have a VisibilityStateProperty value "
"if NS_FRAME_VISIBILITY_IS_TRACKED is set" ")", "./../../../layout/generic/nsIFrame.cpp"
, 2295); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSet" ") ("
"Should have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is set"
")"); do { MOZ_CrashSequence(__null, 2295); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2295 "if NS_FRAME_VISIBILITY_IS_TRACKED is set")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(isSet)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(isSet))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("isSet" " (" "Should have a VisibilityStateProperty value "
"if NS_FRAME_VISIBILITY_IS_TRACKED is set" ")", "./../../../layout/generic/nsIFrame.cpp"
, 2295); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSet" ") ("
"Should have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is set"
")"); do { MOZ_CrashSequence(__null, 2295); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2296
2297 RemoveStateBits(NS_FRAME_VISIBILITY_IS_TRACKED);
2298
2299 if (visibleCount == 0) {
2300 return; // We were nonvisible.
2301 }
2302
2303 // We were visible, so send an OnVisibilityChange() notification.
2304 OnVisibilityChange(Visibility::ApproximatelyNonVisible);
2305}
2306
2307void nsIFrame::DecApproximateVisibleCount(
2308 const Maybe<OnNonvisible>& aNonvisibleAction
2309 /* = Nothing() */) {
2310 MOZ_ASSERT(HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED))>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED)"
, "./../../../layout/generic/nsIFrame.cpp", 2310); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED)"
")"); do { MOZ_CrashSequence(__null, 2310); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2311
2312 bool isSet = false;
2313 uint32_t visibleCount = GetProperty(VisibilityStateProperty(), &isSet);
2314
2315 MOZ_ASSERT(isSet,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(isSet)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(isSet))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("isSet" " (" "Should have a VisibilityStateProperty value "
"if NS_FRAME_VISIBILITY_IS_TRACKED is set" ")", "./../../../layout/generic/nsIFrame.cpp"
, 2317); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSet" ") ("
"Should have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is set"
")"); do { MOZ_CrashSequence(__null, 2317); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2316 "Should have a VisibilityStateProperty value "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(isSet)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(isSet))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("isSet" " (" "Should have a VisibilityStateProperty value "
"if NS_FRAME_VISIBILITY_IS_TRACKED is set" ")", "./../../../layout/generic/nsIFrame.cpp"
, 2317); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSet" ") ("
"Should have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is set"
")"); do { MOZ_CrashSequence(__null, 2317); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2317 "if NS_FRAME_VISIBILITY_IS_TRACKED is set")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(isSet)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(isSet))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("isSet" " (" "Should have a VisibilityStateProperty value "
"if NS_FRAME_VISIBILITY_IS_TRACKED is set" ")", "./../../../layout/generic/nsIFrame.cpp"
, 2317); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSet" ") ("
"Should have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is set"
")"); do { MOZ_CrashSequence(__null, 2317); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2318 MOZ_ASSERT(visibleCount > 0,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(visibleCount > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(visibleCount > 0))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("visibleCount > 0"
" (" "Frame is already nonvisible and we're " "decrementing its visible count?"
")", "./../../../layout/generic/nsIFrame.cpp", 2320); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "visibleCount > 0" ") (" "Frame is already nonvisible and we're "
"decrementing its visible count?" ")"); do { MOZ_CrashSequence
(__null, 2320); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
2319 "Frame is already nonvisible and we're "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(visibleCount > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(visibleCount > 0))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("visibleCount > 0"
" (" "Frame is already nonvisible and we're " "decrementing its visible count?"
")", "./../../../layout/generic/nsIFrame.cpp", 2320); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "visibleCount > 0" ") (" "Frame is already nonvisible and we're "
"decrementing its visible count?" ")"); do { MOZ_CrashSequence
(__null, 2320); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
2320 "decrementing its visible count?")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(visibleCount > 0)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(visibleCount > 0))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("visibleCount > 0"
" (" "Frame is already nonvisible and we're " "decrementing its visible count?"
")", "./../../../layout/generic/nsIFrame.cpp", 2320); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "visibleCount > 0" ") (" "Frame is already nonvisible and we're "
"decrementing its visible count?" ")"); do { MOZ_CrashSequence
(__null, 2320); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2321
2322 visibleCount--;
2323 SetProperty(VisibilityStateProperty(), visibleCount);
2324 if (visibleCount > 0) {
2325 return;
2326 }
2327
2328 // We just became nonvisible, so send an OnVisibilityChange() notification.
2329 OnVisibilityChange(Visibility::ApproximatelyNonVisible, aNonvisibleAction);
2330}
2331
2332void nsIFrame::IncApproximateVisibleCount() {
2333 MOZ_ASSERT(HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED))>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED)"
, "./../../../layout/generic/nsIFrame.cpp", 2333); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED)"
")"); do { MOZ_CrashSequence(__null, 2333); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2334
2335 bool isSet = false;
2336 uint32_t visibleCount = GetProperty(VisibilityStateProperty(), &isSet);
2337
2338 MOZ_ASSERT(isSet,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(isSet)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(isSet))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("isSet" " (" "Should have a VisibilityStateProperty value "
"if NS_FRAME_VISIBILITY_IS_TRACKED is set" ")", "./../../../layout/generic/nsIFrame.cpp"
, 2340); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSet" ") ("
"Should have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is set"
")"); do { MOZ_CrashSequence(__null, 2340); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2339 "Should have a VisibilityStateProperty value "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(isSet)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(isSet))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("isSet" " (" "Should have a VisibilityStateProperty value "
"if NS_FRAME_VISIBILITY_IS_TRACKED is set" ")", "./../../../layout/generic/nsIFrame.cpp"
, 2340); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSet" ") ("
"Should have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is set"
")"); do { MOZ_CrashSequence(__null, 2340); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2340 "if NS_FRAME_VISIBILITY_IS_TRACKED is set")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(isSet)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(isSet))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("isSet" " (" "Should have a VisibilityStateProperty value "
"if NS_FRAME_VISIBILITY_IS_TRACKED is set" ")", "./../../../layout/generic/nsIFrame.cpp"
, 2340); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSet" ") ("
"Should have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is set"
")"); do { MOZ_CrashSequence(__null, 2340); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2341
2342 visibleCount++;
2343 SetProperty(VisibilityStateProperty(), visibleCount);
2344 if (visibleCount > 1) {
2345 return;
2346 }
2347
2348 // We just became visible, so send an OnVisibilityChange() notification.
2349 OnVisibilityChange(Visibility::ApproximatelyVisible);
2350}
2351
2352void nsIFrame::OnVisibilityChange(Visibility aNewVisibility,
2353 const Maybe<OnNonvisible>& aNonvisibleAction
2354 /* = Nothing() */) {
2355 // XXX(seth): In bug 1218990 we'll implement visibility tracking for CSS
2356 // images here.
2357}
2358
2359static nsIFrame* GetActiveSelectionFrame(nsPresContext* aPresContext,
2360 nsIFrame* aFrame) {
2361 nsIContent* capturingContent = PresShell::GetCapturingContent();
2362 if (capturingContent) {
2363 nsIFrame* activeFrame = aPresContext->GetPrimaryFrameFor(capturingContent);
2364 return activeFrame ? activeFrame : aFrame;
2365 }
2366
2367 return aFrame;
2368}
2369
2370bool nsIFrame::ShouldHandleSelectionMovementEvents(ForSelectionStart aType) {
2371 if (GetDisplaySelection() == nsISelectionController::SELECTION_OFF) {
2372 return false;
2373 }
2374 if (UsedUserSelect(this, aType) == StyleUserSelect::None) {
2375 // Check whether style allows selection.
2376 return false;
2377 }
2378 if (IsScrollbarFrame() || IsHTMLCanvasFrame()) {
2379 // Scrollbars and canvas don't move selection with the mouse.
2380 return false;
2381 }
2382 return true;
2383}
2384
2385static Element* FindElementAncestorForMozSelection(nsIContent* aContent) {
2386 NS_ENSURE_TRUE(aContent, nullptr)do { if ((__builtin_expect(!!(!(aContent)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "aContent" ") failed", nullptr
, "./../../../layout/generic/nsIFrame.cpp", 2386); return nullptr
; } } while (false)
;
2387 while (aContent && aContent->IsInNativeAnonymousSubtree()) {
2388 aContent = aContent->GetClosestNativeAnonymousSubtreeRootParentOrHost();
2389 }
2390 NS_ASSERTION(aContent, "aContent isn't in non-anonymous tree?")do { if (!(aContent)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "aContent isn't in non-anonymous tree?"
, "aContent", "./../../../layout/generic/nsIFrame.cpp", 2390)
; MOZ_PretendNoReturn(); } } while (0)
;
2391 return aContent ? aContent->GetAsElementOrParentElement() : nullptr;
2392}
2393
2394already_AddRefed<ComputedStyle> nsIFrame::ComputeSelectionStyle(
2395 int16_t aSelectionStatus) const {
2396 // Just bail out if not a selection-status that ::selection applies to.
2397 if (aSelectionStatus != nsISelectionController::SELECTION_ON &&
2398 aSelectionStatus != nsISelectionController::SELECTION_DISABLED) {
2399 return nullptr;
2400 }
2401 Element* element = FindElementAncestorForMozSelection(GetContent());
2402 if (!element) {
2403 return nullptr;
2404 }
2405 nsIFrame* primaryFrame = element->GetPrimaryFrame();
2406 ComputedStyle* parentStyle = primaryFrame ? primaryFrame->Style() : Style();
2407 RefPtr<ComputedStyle> pseudoStyle =
2408 PresContext()->StyleSet()->ProbePseudoElementStyle(
2409 *element, PseudoStyleType::Selection, nullptr, parentStyle);
2410 if (!pseudoStyle) {
2411 return nullptr;
2412 }
2413 // When in high-contrast mode, the style system ends up ignoring the color
2414 // declarations, which means that the ::selection style becomes the inherited
2415 // color, and default background. That's no good.
2416 // When force-color-adjust is set to none allow using the color styles,
2417 // as they will not be replaced.
2418 if (PresContext()->ForcingColors() &&
2419 pseudoStyle->StyleText()->mForcedColorAdjust !=
2420 StyleForcedColorAdjust::None) {
2421 return nullptr;
2422 }
2423 return do_AddRef(pseudoStyle);
2424}
2425
2426already_AddRefed<ComputedStyle> nsIFrame::ComputeHighlightSelectionStyle(
2427 nsAtom* aHighlightName) {
2428 Element* element = FindElementAncestorForMozSelection(GetContent());
2429 if (!element) {
2430 return nullptr;
2431 }
2432 nsIFrame* primaryFrame = element->GetPrimaryFrame();
2433 ComputedStyle* parentStyle = primaryFrame ? primaryFrame->Style() : Style();
2434 return PresContext()->StyleSet()->ProbePseudoElementStyle(
2435 *element, PseudoStyleType::Highlight, aHighlightName, parentStyle);
2436}
2437
2438already_AddRefed<ComputedStyle> nsIFrame::ComputeTargetTextStyle() const {
2439 const Element* element = FindElementAncestorForMozSelection(GetContent());
2440 if (!element) {
2441 return nullptr;
2442 }
2443 nsIFrame* primaryFrame = element->GetPrimaryFrame();
2444 ComputedStyle* parentStyle = primaryFrame ? primaryFrame->Style() : Style();
2445 RefPtr pseudoStyle = PresContext()->StyleSet()->ProbePseudoElementStyle(
2446 *element, PseudoStyleType::TargetText, nullptr, parentStyle);
2447 if (!pseudoStyle) {
2448 return nullptr;
2449 }
2450 if (PresContext()->ForcingColors() &&
2451 pseudoStyle->StyleText()->mForcedColorAdjust !=
2452 StyleForcedColorAdjust::None) {
2453 return nullptr;
2454 }
2455 return pseudoStyle.forget();
2456}
2457
2458nsTextControlFrame* nsIFrame::GetContainingTextControlFrame() const {
2459 for (const nsIFrame* cur = this; cur; cur = cur->GetParent()) {
2460 if (const nsTextControlFrame* tc = do_QueryFrame(cur)) {
2461 return const_cast<nsTextControlFrame*>(tc);
2462 }
2463 if (cur->Style()->IsAnonBox()) {
2464 // Anon boxes could belong to the <input> / <textarea> itself.
2465 continue;
2466 }
2467 auto* content = cur->GetContent();
2468 if (!content || !content->IsInNativeAnonymousSubtree()) {
2469 // All content inside text controls is anonymous.
2470 break;
2471 }
2472 }
2473 return nullptr;
2474}
2475
2476bool nsIFrame::CanBeDynamicReflowRoot() const {
2477 const auto& display = *StyleDisplay();
2478 if (IsLineParticipant() || display.mDisplay.IsRuby() ||
2479 display.IsInnerTableStyle() ||
2480 display.DisplayInside() == StyleDisplayInside::Table) {
2481 // We have a display type where 'width' and 'height' don't actually set the
2482 // width or height (i.e., the size depends on content).
2483 MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!HasAnyStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT))>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!HasAnyStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("!HasAnyStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT)"
" (" "should not have dynamic reflow root bit" ")", "./../../../layout/generic/nsIFrame.cpp"
, 2484); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!HasAnyStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT)"
") (" "should not have dynamic reflow root bit" ")"); do { MOZ_CrashSequence
(__null, 2484); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
2484 "should not have dynamic reflow root bit")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!HasAnyStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT))>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!HasAnyStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("!HasAnyStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT)"
" (" "should not have dynamic reflow root bit" ")", "./../../../layout/generic/nsIFrame.cpp"
, 2484); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!HasAnyStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT)"
") (" "should not have dynamic reflow root bit" ")"); do { MOZ_CrashSequence
(__null, 2484); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2485 return false;
2486 }
2487
2488 // In general, frames that have contain:layout+size can be reflow roots.
2489 // (One exception: table-wrapper frames don't work well as reflow roots,
2490 // because their inner-table ReflowInput init path tries to reuse & deref
2491 // the wrapper's containing block's reflow input, which may be null if we
2492 // initiate reflow from the table-wrapper itself.)
2493 //
2494 // Changes to `contain` force frame reconstructions, so we used to use
2495 // NS_FRAME_REFLOW_ROOT, this bit could be set for the whole lifetime of
2496 // this frame. But after the support of `content-visibility: auto` which
2497 // is with contain layout + size when it's not relevant to user, and only
2498 // with contain layout when it is relevant. The frame does not reconstruct
2499 // when the relevancy changes. So we use NS_FRAME_DYNAMIC_REFLOW_ROOT instead.
2500 //
2501 // We place it above the pref check on purpose, to make sure it works for
2502 // containment even with the pref disabled.
2503 if (display.IsContainLayout() && GetContainSizeAxes().IsBoth()) {
2504 return true;
2505 }
2506
2507 if (!StaticPrefs::layout_dynamic_reflow_roots_enabled()) {
2508 return false;
2509 }
2510
2511 // We can't serve as a dynamic reflow root if our used 'width' and 'height'
2512 // might be influenced by content.
2513 //
2514 // FIXME: For display:block, we should probably optimize inline-size: auto.
2515 // FIXME: Other flex and grid cases?
2516 const auto& pos = *StylePosition();
2517 const auto anchorResolutionParams = AnchorPosResolutionParams::From(this);
2518 const auto width = pos.GetWidth(anchorResolutionParams);
2519 const auto height = pos.GetHeight(anchorResolutionParams);
2520 if (!width->IsLengthPercentage() || width->HasPercent() ||
2521 !height->IsLengthPercentage() || height->HasPercent() ||
2522 IsIntrinsicKeyword(*pos.GetMinWidth(anchorResolutionParams)) ||
2523 IsIntrinsicKeyword(*pos.GetMaxWidth(anchorResolutionParams)) ||
2524 IsIntrinsicKeyword(*pos.GetMinHeight(anchorResolutionParams)) ||
2525 IsIntrinsicKeyword(*pos.GetMaxHeight(anchorResolutionParams)) ||
2526 ((pos.GetMinWidth(anchorResolutionParams)->IsAuto() ||
2527 pos.GetMinHeight(anchorResolutionParams)->IsAuto()) &&
2528 IsFlexOrGridItem())) {
2529 return false;
2530 }
2531
2532 // If our flex-basis is 'auto', it'll defer to 'width' (or 'height') which
2533 // we've already checked. Otherwise, it preempts them, so we need to
2534 // perform the same "could-this-value-be-influenced-by-content" checks that
2535 // we performed for 'width' and 'height' above.
2536 if (IsFlexItem()) {
2537 const auto& flexBasis = pos.mFlexBasis;
2538 if (!flexBasis.IsAuto()) {
2539 if (!flexBasis.IsSize() || !flexBasis.AsSize().IsLengthPercentage() ||
2540 flexBasis.AsSize().HasPercent()) {
2541 return false;
2542 }
2543 }
2544 }
2545
2546 if (!IsFixedPosContainingBlock()) {
2547 // We can't treat this frame as a reflow root, since dynamic changes
2548 // to absolutely-positioned frames inside of it require that we
2549 // reflow the placeholder before we reflow the absolutely positioned
2550 // frame.
2551 // FIXME: Alternatively, we could sort the reflow roots in
2552 // PresShell::ProcessReflowCommands by depth in the tree, from
2553 // deepest to least deep. However, for performance (FIXME) we
2554 // should really be sorting them in the opposite order!
2555 return false;
2556 }
2557
2558 // If we participate in a container's block reflow context, or margins
2559 // can collapse through us, we can't be a dynamic reflow root.
2560 // (NS_BLOCK_BFC is block specific bit, check first as an optimization, it's
2561 // okay because we also check that it is a block frame.)
2562 if (!HasAnyStateBits(NS_BLOCK_BFC) && IsBlockFrameOrSubclass()) {
2563 return false;
2564 }
2565
2566 // Subgrids are never reflow roots, but 'contain:layout/paint' prevents
2567 // creating a subgrid in the first place.
2568 if (pos.mGridTemplateColumns.IsSubgrid() ||
2569 pos.mGridTemplateRows.IsSubgrid()) {
2570 // NOTE: we could check that 'display' of our parent's primary frame is
2571 // '[inline-]grid' here but that's probably not worth it in practice.
2572 if (!display.IsContainLayout() && !display.IsContainPaint()) {
2573 return false;
2574 }
2575 }
2576
2577 // If we are split, we can't be a dynamic reflow root. Our reflow status may
2578 // change after reflow, and our parent is responsible to create or delete our
2579 // next-in-flow.
2580 if (GetPrevContinuation() || GetNextContinuation()) {
2581 return false;
2582 }
2583
2584 return true;
2585}
2586
2587/********************************************************
2588 * Refreshes each content's frame
2589 *********************************************************/
2590
2591void nsIFrame::DisplayOutlineUnconditional(nsDisplayListBuilder* aBuilder,
2592 const nsDisplayListSet& aLists) {
2593 // Per https://drafts.csswg.org/css-tables-3/#global-style-overrides:
2594 // "All css properties of table-column and table-column-group boxes are
2595 // ignored, except when explicitly specified by this specification."
2596 // CSS outlines fall into this category, so we skip them on these boxes.
2597 MOZ_ASSERT(!IsTableColGroupFrame() && !IsTableColFrame())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsTableColGroupFrame() && !IsTableColFrame(
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!IsTableColGroupFrame() && !IsTableColFrame(
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!IsTableColGroupFrame() && !IsTableColFrame()", "./../../../layout/generic/nsIFrame.cpp"
, 2597); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsTableColGroupFrame() && !IsTableColFrame()"
")"); do { MOZ_CrashSequence(__null, 2597); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2598 const auto& outline = *StyleOutline();
2599
2600 if (!outline.ShouldPaintOutline()) {
2601 return;
2602 }
2603
2604 // Outlines are painted by the table wrapper frame.
2605 if (IsTableFrame()) {
2606 return;
2607 }
2608
2609 if (HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT) &&
2610 ScrollableOverflowRect().IsEmpty()) {
2611 // Skip parts of IB-splits with an empty overflow rect, see bug 434301.
2612 // We may still want to fix some of the overflow area calculations over in
2613 // that bug.
2614 return;
2615 }
2616
2617 // We don't display outline-style: auto on themed frames that have their own
2618 // focus indicators.
2619 if (outline.mOutlineStyle.IsAuto()) {
2620 auto* disp = StyleDisplay();
2621 if (IsThemed(disp) && PresContext()->Theme()->ThemeDrawsFocusForWidget(
2622 this, disp->EffectiveAppearance())) {
2623 return;
2624 }
2625 }
2626
2627 aLists.Outlines()->AppendNewToTop<nsDisplayOutline>(aBuilder, this);
2628}
2629
2630void nsIFrame::DisplayOutline(nsDisplayListBuilder* aBuilder,
2631 const nsDisplayListSet& aLists) {
2632 if (!IsVisibleForPainting()) {
2633 return;
2634 }
2635
2636 DisplayOutlineUnconditional(aBuilder, aLists);
2637}
2638
2639void nsIFrame::DisplayInsetBoxShadowUnconditional(
2640 nsDisplayListBuilder* aBuilder, nsDisplayList* aList) {
2641 // XXXbz should box-shadow for rows/rowgroups/columns/colgroups get painted
2642 // just because we're visible? Or should it depend on the cell visibility
2643 // when we're not the whole table?
2644 const auto* effects = StyleEffects();
2645 if (effects->HasBoxShadowWithInset(true)) {
2646 aList->AppendNewToTop<nsDisplayBoxShadowInner>(aBuilder, this);
2647 }
2648}
2649
2650void nsIFrame::DisplayInsetBoxShadow(nsDisplayListBuilder* aBuilder,
2651 nsDisplayList* aList) {
2652 if (!IsVisibleForPainting()) {
2653 return;
2654 }
2655
2656 DisplayInsetBoxShadowUnconditional(aBuilder, aList);
2657}
2658
2659void nsIFrame::DisplayOutsetBoxShadowUnconditional(
2660 nsDisplayListBuilder* aBuilder, nsDisplayList* aList) {
2661 // XXXbz should box-shadow for rows/rowgroups/columns/colgroups get painted
2662 // just because we're visible? Or should it depend on the cell visibility
2663 // when we're not the whole table?
2664 const auto* effects = StyleEffects();
2665 if (effects->HasBoxShadowWithInset(false)) {
2666 aList->AppendNewToTop<nsDisplayBoxShadowOuter>(aBuilder, this);
2667 }
2668}
2669
2670void nsIFrame::DisplayOutsetBoxShadow(nsDisplayListBuilder* aBuilder,
2671 nsDisplayList* aList) {
2672 if (!IsVisibleForPainting()) {
2673 return;
2674 }
2675
2676 DisplayOutsetBoxShadowUnconditional(aBuilder, aList);
2677}
2678
2679void nsIFrame::DisplayCaret(nsDisplayListBuilder* aBuilder,
2680 nsDisplayList* aList) {
2681 if (!IsVisibleForPainting()) {
2682 return;
2683 }
2684
2685 aList->AppendNewToTop<nsDisplayCaret>(aBuilder, this);
2686}
2687
2688nscolor nsIFrame::GetCaretColorAt(int32_t aOffset) {
2689 return nsLayoutUtils::GetTextColor(this, &nsStyleUI::mCaretColor);
2690}
2691
2692auto nsIFrame::ComputeShouldPaintBackground() const -> ShouldPaintBackground {
2693 nsPresContext* pc = PresContext();
2694 ShouldPaintBackground settings{pc->GetBackgroundColorDraw(),
2695 pc->GetBackgroundImageDraw()};
2696 if (settings.mColor && settings.mImage) {
2697 return settings;
2698 }
2699
2700 if (StyleVisibility()->mPrintColorAdjust == StylePrintColorAdjust::Exact) {
2701 return {true, true};
2702 }
2703
2704 return settings;
2705}
2706
2707bool nsIFrame::DisplayBackgroundUnconditional(nsDisplayListBuilder* aBuilder,
2708 const nsDisplayListSet& aLists) {
2709 if (aBuilder->IsForEventDelivery() && !aBuilder->HitTestIsForVisibility()) {
2710 // For hit-testing, we generally just need a light-weight data structure
2711 // like nsDisplayEventReceiver. But if the hit-testing is for visibility,
2712 // then we need to know the opaque region in order to determine whether to
2713 // stop or not.
2714 aLists.BorderBackground()->AppendNewToTop<nsDisplayEventReceiver>(aBuilder,
2715 this);
2716 return false;
2717 }
2718
2719 const AppendedBackgroundType result =
2720 nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
2721 aBuilder, this,
2722 GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this),
2723 aLists.BorderBackground());
2724
2725 if (result == AppendedBackgroundType::None) {
2726 aBuilder->BuildCompositorHitTestInfoIfNeeded(this,
2727 aLists.BorderBackground());
2728 }
2729
2730 return result == AppendedBackgroundType::ThemedBackground;
2731}
2732
2733void nsIFrame::DisplayBorderBackgroundOutline(nsDisplayListBuilder* aBuilder,
2734 const nsDisplayListSet& aLists) {
2735 // The visibility check belongs here since child elements have the
2736 // opportunity to override the visibility property and display even if
2737 // their parent is hidden.
2738 if (!IsVisibleForPainting()) {
2739 return;
2740 }
2741
2742 DisplayOutsetBoxShadowUnconditional(aBuilder, aLists.BorderBackground());
2743
2744 bool bgIsThemed = DisplayBackgroundUnconditional(aBuilder, aLists);
2745 DisplayInsetBoxShadowUnconditional(aBuilder, aLists.BorderBackground());
2746
2747 // If there's a themed background, we should not create a border item.
2748 // It won't be rendered.
2749 // Don't paint borders for tables here, since they paint them in a different
2750 // order.
2751 if (!bgIsThemed && StyleBorder()->HasBorder() && !IsTableFrame()) {
2752 aLists.BorderBackground()->AppendNewToTop<nsDisplayBorder>(aBuilder, this);
2753 }
2754
2755 DisplayOutlineUnconditional(aBuilder, aLists);
2756}
2757
2758inline static bool IsSVGContentWithCSSClip(const nsIFrame* aFrame) {
2759 // The CSS spec says that the 'clip' property only applies to absolutely
2760 // positioned elements, whereas the SVG spec says that it applies to SVG
2761 // elements regardless of the value of the 'position' property. Here we obey
2762 // the CSS spec for outer-<svg> (since that's what we generally do), but
2763 // obey the SVG spec for other SVG elements to which 'clip' applies.
2764 return aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT) &&
2765 aFrame->GetContent()->IsAnyOfSVGElements(nsGkAtoms::svg,
2766 nsGkAtoms::foreignObject);
2767}
2768
2769Maybe<nsRect> nsIFrame::GetClipPropClipRect(const nsStyleDisplay* aDisp,
2770 const nsStyleEffects* aEffects,
2771 const nsSize& aSize) const {
2772 if (aEffects->mClip.IsAuto() ||
2773 !(aDisp->IsAbsolutelyPositioned(this) || IsSVGContentWithCSSClip(this))) {
2774 return Nothing();
2775 }
2776
2777 auto& clipRect = aEffects->mClip.AsRect();
2778 nsRect rect = clipRect.ToLayoutRect();
2779 if (MOZ_LIKELY(StyleBorder()->mBoxDecorationBreak ==(__builtin_expect(!!(StyleBorder()->mBoxDecorationBreak ==
StyleBoxDecorationBreak::Slice), 1))
2780 StyleBoxDecorationBreak::Slice)(__builtin_expect(!!(StyleBorder()->mBoxDecorationBreak ==
StyleBoxDecorationBreak::Slice), 1))
) {
2781 // The clip applies to the joined boxes so it's relative the first
2782 // continuation.
2783 nscoord y = 0;
2784 for (nsIFrame* f = GetPrevContinuation(); f; f = f->GetPrevContinuation()) {
2785 y += f->GetRect().height;
2786 }
2787 rect.MoveBy(nsPoint(0, -y));
2788 }
2789
2790 if (clipRect.right.IsAuto()) {
2791 rect.width = aSize.width - rect.x;
2792 }
2793 if (clipRect.bottom.IsAuto()) {
2794 rect.height = aSize.height - rect.y;
2795 }
2796 return Some(rect);
2797}
2798
2799// https://drafts.csswg.org/css-view-transitions-1/#named-and-transitioning
2800//
2801// Note https://github.com/w3c/csswg-drafts/issues/11772, however, for the root
2802// style check.
2803bool nsIFrame::ForcesStackingContextForViewTransition() const {
2804 auto* style = Style();
2805 return !style->IsRootElementStyle() &&
2806 (style->StyleUIReset()->HasViewTransitionName() ||
2807 HasAnyStateBits(NS_FRAME_CAPTURED_IN_VIEW_TRANSITION) ||
2808 style->StyleDisplay()->mWillChange.bits &
2809 mozilla::StyleWillChangeBits::VIEW_TRANSITION_NAME);
2810}
2811
2812/**
2813 * If the CSS 'overflow' property applies to this frame, and is not
2814 * handled by constructing a dedicated nsHTML/XULScrollFrame, set up clipping
2815 * for that overflow in aBuilder->ClipState() to clip all containing-block
2816 * descendants.
2817 */
2818static void ApplyOverflowClipping(
2819 nsDisplayListBuilder* aBuilder, const nsIFrame* aFrame,
2820 PhysicalAxes aClipAxes,
2821 DisplayListClipState::AutoClipMultiple& aClipState) {
2822 nsRect clipRect;
2823 nsRectCornerRadii radii;
2824 nsMargin inset;
2825 bool haveRadii = aFrame->ComputeOverflowClipRectRelativeToSelf(
2826 aClipAxes, clipRect, radii, inset);
2827 aClipState.ClipContainingBlockDescendantsExtra(
2828 clipRect + aBuilder->ToReferenceFrame(aFrame),
2829 haveRadii ? &radii : nullptr, haveRadii ? &inset : nullptr);
2830}
2831
2832static Sides ToSkipSides(PhysicalAxes aClipAxes) {
2833 SideBits result{};
2834 if (!aClipAxes.contains(PhysicalAxis::Vertical)) {
2835 result |= SideBits::eTop;
2836 result |= SideBits::eBottom;
2837 }
2838 if (!aClipAxes.contains(PhysicalAxis::Horizontal)) {
2839 result |= SideBits::eLeft;
2840 result |= SideBits::eRight;
2841 }
2842 return Sides(result);
2843}
2844
2845bool nsIFrame::ComputeOverflowClipRectRelativeToSelf(
2846 const PhysicalAxes aClipAxes, nsRect& aOutRect,
2847 nsRectCornerRadii& aOutRadii, nsMargin& aOutInset) const {
2848 // Only 'clip' is handled here (and 'hidden' for table frames, and any
2849 // non-'visible' value for blocks in a paginated context).
2850 // We allow 'clip' to apply to any kind of frame. This is required by
2851 // comboboxes which make their display text (an inline frame) have clipping.
2852 MOZ_ASSERT(!aClipAxes.isEmpty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!aClipAxes.isEmpty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!aClipAxes.isEmpty()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("!aClipAxes.isEmpty()"
, "./../../../layout/generic/nsIFrame.cpp", 2852); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!aClipAxes.isEmpty()" ")"); do { MOZ_CrashSequence
(__null, 2852); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2853 MOZ_ASSERT(ShouldApplyOverflowClipping(StyleDisplay()) == aClipAxes)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ShouldApplyOverflowClipping(StyleDisplay()) == aClipAxes
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(ShouldApplyOverflowClipping(StyleDisplay()) == aClipAxes
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"ShouldApplyOverflowClipping(StyleDisplay()) == aClipAxes", "./../../../layout/generic/nsIFrame.cpp"
, 2853); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ShouldApplyOverflowClipping(StyleDisplay()) == aClipAxes"
")"); do { MOZ_CrashSequence(__null, 2853); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2854 auto boxMargin = OverflowClipMargin(aClipAxes, /* aAllowNegative = */ true);
2855 boxMargin.ApplySkipSides(GetSkipSides() | ToSkipSides(aClipAxes));
2856 aOutInset = -boxMargin;
2857
2858 aOutRect = nsRect(nsPoint(), GetSize());
2859 aOutRect.Inflate(boxMargin);
2860 if (MOZ_UNLIKELY(!aClipAxes.contains(PhysicalAxis::Horizontal))(__builtin_expect(!!(!aClipAxes.contains(PhysicalAxis::Horizontal
)), 0))
) {
2861 // NOTE(mats) We shouldn't be clipping at all in this dimension really,
2862 // but clipping in just one axis isn't supported by our GFX APIs so we
2863 // clip to our visual overflow rect instead.
2864 nsRect o = InkOverflowRectRelativeToSelf();
2865 aOutRect.x = o.x;
2866 aOutRect.width = o.width;
2867 }
2868 if (MOZ_UNLIKELY(!aClipAxes.contains(PhysicalAxis::Vertical))(__builtin_expect(!!(!aClipAxes.contains(PhysicalAxis::Vertical
)), 0))
) {
2869 // See the note above.
2870 nsRect o = InkOverflowRectRelativeToSelf();
2871 aOutRect.y = o.y;
2872 aOutRect.height = o.height;
2873 }
2874 if (!GetBorderRadii(aOutRadii)) {
2875 return false;
2876 }
2877 aOutRadii.AdjustOutwards(boxMargin);
2878 return true;
2879}
2880
2881nsMargin nsIFrame::OverflowClipMargin(PhysicalAxes aClipAxes,
2882 bool aAllowNegative) const {
2883 nsMargin result;
2884 if (aClipAxes.isEmpty()) {
2885 return result;
2886 }
2887 const auto& margin = StyleMargin()->mOverflowClipMargin;
2888 if (!aAllowNegative && margin.offset.IsZero()) {
2889 return result;
2890 }
2891 switch (margin.visual_box) {
2892 case StyleOverflowClipMarginBox::BorderBox:
2893 break;
2894 case StyleOverflowClipMarginBox::PaddingBox:
2895 result = -GetUsedBorder();
2896 break;
2897 case StyleOverflowClipMarginBox::ContentBox:
2898 result = -GetUsedBorderAndPadding();
2899 break;
2900 }
2901 if (!margin.offset.IsZero()) {
2902 nscoord marginAu = margin.offset.ToAppUnits();
2903 result += nsMargin(marginAu, marginAu, marginAu, marginAu);
2904 }
2905 if (!aAllowNegative) {
2906 result.EnsureAtLeast(nsMargin());
2907 }
2908 return result;
2909}
2910
2911/**
2912 * Returns whether a display item that gets created with the builder's current
2913 * state will have a scrolled clip, i.e. a clip that is scrolled by a scroll
2914 * frame which does not move the item itself.
2915 */
2916static bool BuilderHasScrolledClip(nsDisplayListBuilder* aBuilder) {
2917 const DisplayItemClipChain* currentClip =
2918 aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder);
2919 if (!currentClip) {
2920 return false;
2921 }
2922
2923 const ActiveScrolledRoot* currentClipASR = currentClip->mASR;
2924 const ActiveScrolledRoot* currentASR = aBuilder->CurrentActiveScrolledRoot();
2925 return ActiveScrolledRoot::PickDescendant(currentClipASR, currentASR) !=
2926 currentASR;
2927}
2928
2929class AutoTrackStackingContextBits {
2930 nsDisplayListBuilder& mBuilder;
2931 StackingContextBits mBitsToSet;
2932
2933 public:
2934 explicit AutoTrackStackingContextBits(nsDisplayListBuilder& aBuilder)
2935 : mBuilder(aBuilder), mBitsToSet(aBuilder.GetStackingContextBits()) {}
2936
2937 ~AutoTrackStackingContextBits() {
2938 mBuilder.SetStackingContextBits(mBitsToSet);
2939 }
2940
2941 void AddToParent(StackingContextBits aBits) { mBitsToSet |= aBits; }
2942};
2943
2944static bool IsFrameOrAncestorApzAware(nsIFrame* aFrame) {
2945 nsIContent* node = aFrame->GetContent();
2946 if (!node) {
2947 return false;
2948 }
2949
2950 do {
2951 if (node->IsNodeApzAware()) {
2952 return true;
2953 }
2954 nsIContent* shadowRoot = node->GetShadowRoot();
2955 if (shadowRoot && shadowRoot->IsNodeApzAware()) {
2956 return true;
2957 }
2958
2959 // Even if the node owning aFrame doesn't have apz-aware event listeners
2960 // itself, its shadow root or display: contents ancestors (which have no
2961 // frames) might, so we need to account for them too.
2962 } while ((node = node->GetFlattenedTreeParent()) && node->IsElement() &&
2963 node->AsElement()->IsDisplayContents());
2964
2965 return false;
2966}
2967
2968static void CheckForApzAwareEventHandlers(nsDisplayListBuilder* aBuilder,
2969 nsIFrame* aFrame) {
2970 if (aBuilder->GetAncestorHasApzAwareEventHandler()) {
2971 return;
2972 }
2973
2974 if (IsFrameOrAncestorApzAware(aFrame)) {
2975 aBuilder->SetAncestorHasApzAwareEventHandler(true);
2976 }
2977}
2978
2979static void UpdateCurrentHitTestInfo(nsDisplayListBuilder* aBuilder,
2980 nsIFrame* aFrame) {
2981 if (!aBuilder->BuildCompositorHitTestInfo()) {
2982 // Compositor hit test info is not used.
2983 return;
2984 }
2985
2986 CheckForApzAwareEventHandlers(aBuilder, aFrame);
2987
2988 const CompositorHitTestInfo info =
2989 aFrame->GetCompositorHitTestInfoWithoutPointerEvents(aBuilder);
2990 aBuilder->SetInheritedCompositorHitTestInfo(info);
2991}
2992
2993/**
2994 * True if aDescendant participates the context aAncestor participating.
2995 */
2996static bool FrameParticipatesIn3DContext(nsIFrame* aAncestor,
2997 nsIFrame* aDescendant) {
2998 MOZ_ASSERT(aAncestor != aDescendant)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aAncestor != aDescendant)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aAncestor != aDescendant))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("aAncestor != aDescendant"
, "./../../../layout/generic/nsIFrame.cpp", 2998); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aAncestor != aDescendant" ")"); do { MOZ_CrashSequence
(__null, 2998); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2999 MOZ_ASSERT(aAncestor->GetContent() != aDescendant->GetContent())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aAncestor->GetContent() != aDescendant->GetContent
())>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aAncestor->GetContent() != aDescendant->GetContent
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aAncestor->GetContent() != aDescendant->GetContent()"
, "./../../../layout/generic/nsIFrame.cpp", 2999); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aAncestor->GetContent() != aDescendant->GetContent()"
")"); do { MOZ_CrashSequence(__null, 2999); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3000 MOZ_ASSERT(aAncestor->Extend3DContext())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aAncestor->Extend3DContext())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aAncestor->Extend3DContext
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aAncestor->Extend3DContext()", "./../../../layout/generic/nsIFrame.cpp"
, 3000); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aAncestor->Extend3DContext()"
")"); do { MOZ_CrashSequence(__null, 3000); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3001
3002 nsIFrame* ancestor = aAncestor->FirstContinuation();
3003 MOZ_ASSERT(ancestor->IsPrimaryFrame())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ancestor->IsPrimaryFrame())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ancestor->IsPrimaryFrame(
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("ancestor->IsPrimaryFrame()", "./../../../layout/generic/nsIFrame.cpp"
, 3003); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ancestor->IsPrimaryFrame()"
")"); do { MOZ_CrashSequence(__null, 3003); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3004
3005 nsIFrame* frame;
3006 for (frame = aDescendant->GetClosestFlattenedTreeAncestorPrimaryFrame();
3007 frame && ancestor != frame;
3008 frame = frame->GetClosestFlattenedTreeAncestorPrimaryFrame()) {
3009 if (!frame->Extend3DContext()) {
3010 return false;
3011 }
3012 }
3013
3014 MOZ_ASSERT(frame == ancestor)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(frame == ancestor)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(frame == ancestor))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("frame == ancestor"
, "./../../../layout/generic/nsIFrame.cpp", 3014); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "frame == ancestor" ")"); do { MOZ_CrashSequence
(__null, 3014); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3015 return true;
3016}
3017
3018static bool ItemParticipatesIn3DContext(nsIFrame* aAncestor,
3019 nsDisplayItem* aItem) {
3020 auto type = aItem->GetType();
3021 const bool isContainer = type == DisplayItemType::TYPE_WRAP_LIST ||
3022 type == DisplayItemType::TYPE_CONTAINER;
3023
3024 if (isContainer && aItem->GetChildren()->Length() == 1) {
3025 // If the wraplist has only one child item, use the type of that item.
3026 type = aItem->GetChildren()->GetBottom()->GetType();
3027 }
3028
3029 if (type != DisplayItemType::TYPE_TRANSFORM &&
3030 type != DisplayItemType::TYPE_PERSPECTIVE) {
3031 return false;
3032 }
3033 nsIFrame* transformFrame = aItem->Frame();
3034 if (aAncestor->GetContent() == transformFrame->GetContent()) {
3035 return true;
3036 }
3037 return FrameParticipatesIn3DContext(aAncestor, transformFrame);
3038}
3039
3040/**
3041 * If the frame of aItem is, or descends from, a frame that participates in the
3042 * 3D rendering context of aAncestor and has a hidden backface, returns that
3043 * participant, else null.
3044 *
3045 * Combines3DTransformWithAncestors() counts a hidden backface as participating
3046 * even without a transform, but such a frame never gets a transform item of its
3047 * own, so ItemParticipatesIn3DContext() cannot see it. Reporting the
3048 * participant lets the caller build the leaf that both the painting and the hit
3049 * testing paths cull on. The frame is reported for descendants too, because
3050 * backface-visibility is not inherited yet the whole subtree has to disappear
3051 * with the participant.
3052 */
3053static nsIFrame* BackfaceHidden3DParticipantFor(nsIFrame* aAncestor,
3054 nsDisplayItem* aItem) {
3055 MOZ_ASSERT(aAncestor->Extend3DContext())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aAncestor->Extend3DContext())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aAncestor->Extend3DContext
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aAncestor->Extend3DContext()", "./../../../layout/generic/nsIFrame.cpp"
, 3055); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aAncestor->Extend3DContext()"
")"); do { MOZ_CrashSequence(__null, 3055); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3056
3057 nsIFrame* ancestor = aAncestor->FirstContinuation();
3058 for (nsIFrame* frame = aItem->Frame(); frame && frame != ancestor;
3059 frame = frame->GetClosestFlattenedTreeAncestorPrimaryFrame()) {
3060 if (frame->In3DContextAndBackfaceIsHidden()) {
3061 return frame;
3062 }
3063 }
3064 return nullptr;
3065}
3066
3067/**
3068 * Flushes the non-participants accumulated so far into a separator transform
3069 * item on aFrame, and moves that item over to aParticipants. It does not touch
3070 * anything already in aParticipants, and it is a no-op when nothing has
3071 * accumulated. This should be called whenever there is a switch between
3072 * the participants and non-participants.
3073 */
3074static void FlushNonParticipantsIntoSeparatorTransform(
3075 nsDisplayListBuilder* aBuilder, nsIFrame* aFrame,
3076 nsDisplayList* aNonParticipants, nsDisplayList* aParticipants, int& aIndex,
3077 nsDisplayItem** aSeparator) {
3078 if (aNonParticipants->IsEmpty()) {
3079 return;
3080 }
3081
3082 nsDisplayTransform* item = MakeDisplayItemWithIndex<nsDisplayTransform>(
3083 aBuilder, aFrame, aIndex++, aNonParticipants, aBuilder->GetVisibleRect());
3084
3085 if (*aSeparator == nullptr && item) {
3086 *aSeparator = item;
3087 }
3088
3089 aParticipants->AppendToTop(item);
3090}
3091
3092// Try to compute a clip rect to bound the contents of the mask item
3093// that will be built for |aMaskedFrame|. If we're not able to compute
3094// one, return an empty Maybe.
3095// The returned clip rect, if there is one, is relative to |aMaskedFrame|.
3096// Bug 1958124: If this is inlined in
3097// nsIFrame::BuildDisplayListForStackingContext on Win32, its variables enlarge
3098// that function's stack frame, which is on a deeply recursive path that is
3099// already up against the stack limit. Only masked frames reach this code, so
3100// never inlining it on Win32 costs us nothing.
3101#if defined(XP_WIN) && !defined(_WIN64)
3102MOZ_NEVER_INLINE__attribute__((noinline))
3103#endif
3104static Maybe<nsRect> ComputeClipForMaskItem(
3105 nsDisplayListBuilder* aBuilder, nsIFrame* aMaskedFrame,
3106 const SVGUtils::MaskUsage& aMaskUsage) {
3107 const nsStyleSVGReset* svgReset = aMaskedFrame->StyleSVGReset();
3108
3109 nsPoint offsetToUserSpace =
3110 nsLayoutUtils::ComputeOffsetToUserSpace(aBuilder, aMaskedFrame);
3111 int32_t devPixelRatio = aMaskedFrame->PresContext()->AppUnitsPerDevPixel();
3112 gfxPoint devPixelOffsetToUserSpace =
3113 nsLayoutUtils::PointToGfxPoint(offsetToUserSpace, devPixelRatio);
3114 CSSToLayoutDeviceScale cssToDevScale =
3115 aMaskedFrame->PresContext()->CSSToDevPixelScale();
3116
3117 nsPoint toReferenceFrame;
3118 aBuilder->FindReferenceFrameFor(aMaskedFrame, &toReferenceFrame);
3119
3120 Maybe<gfxRect> combinedClip;
3121 if (aMaskUsage.ShouldApplyBasicShapeOrPath()) {
3122 Maybe<Rect> result =
3123 CSSClipPathInstance::GetBoundingRectForBasicShapeOrPathClip(
3124 aMaskedFrame, svgReset->mClipPath);
3125 if (result) {
3126 combinedClip = Some(ThebesRect(*result));
3127 }
3128 } else if (aMaskUsage.ShouldApplyClipPath()) {
3129 gfxRect result = SVGUtils::GetBBox(
3130 aMaskedFrame,
3131 {SVGBBoxFlag::IncludeClipped, SVGBBoxFlag::IncludeFillGeometry,
3132 SVGBBoxFlag::IncludeMarkers, SVGBBoxFlag::IncludeStroke,
3133 SVGBBoxFlag::DoNotClipToBBoxOfContentInsideClipPath});
3134 combinedClip = Some(
3135 ThebesRect((CSSRect::FromUnknownRect(ToRect(result)) * cssToDevScale)
3136 .ToUnknownRect()));
3137 } else {
3138 // The code for this case is adapted from ComputeMaskGeometry().
3139
3140 nsRect borderArea(toReferenceFrame, aMaskedFrame->GetSize());
3141 borderArea -= offsetToUserSpace;
3142
3143 // Use an infinite dirty rect to pass into nsCSSRendering::
3144 // GetImageLayerClip() because we don't have an actual dirty rect to
3145 // pass in. This is fine because the only time GetImageLayerClip() will
3146 // not intersect the incoming dirty rect with something is in the "NoClip"
3147 // case, and we handle that specially.
3148 nsRect dirtyRect(nscoord_MIN / 2, nscoord_MIN / 2, nscoord_MAX,
3149 nscoord_MAX);
3150
3151 nsIFrame* firstFrame =
3152 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aMaskedFrame);
3153 nsTArray<SVGMaskFrame*> maskFrames;
3154 // XXX check return value?
3155 SVGObserverUtils::GetAndObserveMasks(firstFrame, &maskFrames);
3156
3157 for (uint32_t i = 0; i < maskFrames.Length(); ++i) {
3158 gfxRect clipArea;
3159 if (maskFrames[i]) {
3160 clipArea = maskFrames[i]->GetMaskArea(aMaskedFrame);
3161 clipArea = ThebesRect(
3162 (CSSRect::FromUnknownRect(ToRect(clipArea)) * cssToDevScale)
3163 .ToUnknownRect());
3164 } else {
3165 const auto& layer = svgReset->mMask.mLayers[i];
3166 if (layer.mClip == StyleBackgroundClip::NoClip) {
3167 return Nothing();
3168 }
3169
3170 nsCSSRendering::ImageLayerClipState clipState;
3171 nsCSSRendering::GetImageLayerClip(
3172 layer, aMaskedFrame, *aMaskedFrame->StyleBorder(), borderArea,
3173 dirtyRect, false /* aWillPaintBorder */, devPixelRatio, &clipState);
3174 clipArea = clipState.mDirtyRectInDevPx;
3175 }
3176 combinedClip = UnionMaybeRects(combinedClip, Some(clipArea));
3177 }
3178 }
3179 if (combinedClip) {
3180 // Convert to user space.
3181 *combinedClip += devPixelOffsetToUserSpace;
3182
3183 // Round the clip out. In FrameLayerBuilder we round clips to nearest
3184 // pixels, and if we have a really thin clip here, that can cause the
3185 // clip to become empty if we didn't round out here.
3186 // The rounding happens in coordinates that are relative to the reference
3187 // frame, which matches what FrameLayerBuilder does.
3188 combinedClip->RoundOut();
3189
3190 // Convert to app units.
3191 nsRect result =
3192 nsLayoutUtils::RoundGfxRectToAppRect(*combinedClip, devPixelRatio);
3193
3194 // The resulting clip is relative to the reference frame, but the caller
3195 // expects it to be relative to the masked frame, so adjust it.
3196 result -= toReferenceFrame;
3197 return Some(result);
3198 }
3199 return Nothing();
3200}
3201
3202struct AutoCheckBuilder {
3203 explicit AutoCheckBuilder(nsDisplayListBuilder* aBuilder)
3204 : mBuilder(aBuilder) {
3205 aBuilder->Check();
3206 }
3207
3208 ~AutoCheckBuilder() { mBuilder->Check(); }
3209
3210 nsDisplayListBuilder* mBuilder;
3211};
3212
3213/**
3214 * Tries to reuse a top-level stacking context item from the previous paint.
3215 * Returns true if an item was reused, otherwise false.
3216 */
3217bool TryToReuseStackingContextItem(nsDisplayListBuilder* aBuilder,
3218 nsDisplayList* aList, nsIFrame* aFrame) {
3219 if (!aBuilder->IsForPainting() || !aBuilder->IsPartialUpdate() ||
3220 aBuilder->InInvalidSubtree()) {
3221 return false;
3222 }
3223
3224 if (aFrame->IsFrameModified() || aFrame->HasModifiedDescendants()) {
3225 return false;
3226 }
3227
3228 auto& items = aFrame->DisplayItems();
3229 auto* res = std::find_if(
3230 items.begin(), items.end(),
3231 [](nsDisplayItem* aItem) { return aItem->IsPreProcessed(); });
3232
3233 if (res == items.end()) {
3234 return false;
3235 }
3236
3237 nsDisplayItem* container = *res;
3238 MOZ_ASSERT(container->Frame() == aFrame)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(container->Frame() == aFrame)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(container->Frame() == aFrame
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"container->Frame() == aFrame", "./../../../layout/generic/nsIFrame.cpp"
, 3238); AnnotateMozCrashReason("MOZ_ASSERT" "(" "container->Frame() == aFrame"
")"); do { MOZ_CrashSequence(__null, 3238); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3239 DL_LOGD("RDL - Found SC item %p (%s) (frame: %p)", container,do { const ::mozilla::LogModule* moz_real_module = GetLoggerByProcess
(); if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Debug)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Debug, "RDL - Found SC item %p (%s) (frame: %p)",
container, container->Name(), container->Frame()); } }
while (0)
3240 container->Name(), container->Frame())do { const ::mozilla::LogModule* moz_real_module = GetLoggerByProcess
(); if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Debug)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Debug, "RDL - Found SC item %p (%s) (frame: %p)",
container, container->Name(), container->Frame()); } }
while (0)
;
3241
3242 aList->AppendToTop(container);
3243 aBuilder->ReuseDisplayItem(container);
3244 return true;
3245}
3246
3247void nsIFrame::BuildDisplayListForStackingContext(
3248 nsDisplayListBuilder* aBuilder, nsDisplayList* aList,
3249 bool* aCreatedContainerItem) {
3250#ifdef DEBUG1
3251 DL_LOGV("BuildDisplayListForStackingContext (%p) <", this)do { const ::mozilla::LogModule* moz_real_module = GetLoggerByProcess
(); if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Verbose)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Verbose, "BuildDisplayListForStackingContext (%p) <"
, this); } } while (0)
;
3252 ScopeExit e(
3253 [this]() { DL_LOGV("> BuildDisplayListForStackingContext (%p)", this)do { const ::mozilla::LogModule* moz_real_module = GetLoggerByProcess
(); if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Verbose)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Verbose, "> BuildDisplayListForStackingContext (%p)"
, this); } } while (0)
; });
3254#endif
3255
3256 AutoCheckBuilder check(aBuilder);
3257
3258 if (aBuilder->IsReusingStackingContextItems() &&
3259 TryToReuseStackingContextItem(aBuilder, aList, this)) {
3260 if (aCreatedContainerItem) {
3261 *aCreatedContainerItem = true;
3262 }
3263 return;
3264 }
3265
3266 if (HasAnyStateBits(NS_FRAME_TOO_DEEP_IN_FRAME_TREE |
3267 NS_FRAME_IS_NONDISPLAY)) {
3268 return;
3269 }
3270
3271 const auto& style = *Style();
3272 const nsStyleDisplay* disp = style.StyleDisplay();
3273 const nsStyleEffects* effects = style.StyleEffects();
3274 EffectSet* effectSetForOpacity =
3275 EffectSet::GetForFrame(this, nsCSSPropertyIDSet::OpacityProperties());
3276 // We can stop right away if this is a zero-opacity stacking context and
3277 // we're painting, and we're not animating opacity.
3278 bool needHitTestInfo = aBuilder->BuildCompositorHitTestInfo() &&
3279 Style()->PointerEvents() != StylePointerEvents::None;
3280 bool opacityItemForEventsOnly = false;
3281 if (effects->IsTransparent() && aBuilder->IsForPainting() &&
3282 !(disp->mWillChange.bits & StyleWillChangeBits::OPACITY) &&
3283 !nsLayoutUtils::HasAnimationOfPropertySet(
3284 this, nsCSSPropertyIDSet::OpacityProperties(), effectSetForOpacity)) {
3285 if (needHitTestInfo) {
3286 opacityItemForEventsOnly = true;
3287 } else {
3288 return;
3289 }
3290 }
3291
3292 // Root gets handled in
3293 // ScrollContainerFrame::MaybeCreateTopLayerAndWrapRootItems.
3294 const bool capturedByViewTransition =
3295 HasAnyStateBits(NS_FRAME_CAPTURED_IN_VIEW_TRANSITION) &&
3296 !style.IsRootElementStyle();
3297
3298 // Do not need to build the display list of the captured frames for event
3299 // delivery (i.e. to determine if this frame is under the mouse position).
3300 //
3301 // Per spec, hit-testing is skipped because the element’s DOM location does
3302 // not correspond to where its contents are rendered, so we could just skip
3303 // the building of the display list for these frames, and then these APIs,
3304 // e.g. elementFromPoint(), will skip these frames as well.
3305 // https://drafts.csswg.org/css-view-transitions-1/#view-transition-stacking-layer
3306 if (capturedByViewTransition && aBuilder->IsForEventDelivery()) {
3307 return;
3308 }
3309
3310 // For preserves3d, use the dirty rect already installed on the
3311 // builder, since aDirtyRect maybe distorted for transforms along
3312 // the chain.
3313 nsRect visibleRect = aBuilder->GetVisibleRect();
3314 nsRect dirtyRect = aBuilder->GetDirtyRect();
3315
3316 // We build an opacity item if it's not going to be drawn by SVG content.
3317 // We could in principle skip creating an nsDisplayOpacity item if
3318 // nsDisplayOpacity::NeedsActiveLayer returns false and usingSVGEffects is
3319 // true (the nsDisplayFilter/nsDisplayMasksAndClipPaths could handle the
3320 // opacity). Since SVG has perf issues where we sometimes spend a lot of
3321 // time creating display list items that might be helpful. We'd need to
3322 // restore our mechanism to do that (changed in bug 1482403), and we'd
3323 // need to invalidate the frame if the value that would be return from
3324 // NeedsActiveLayer was to change, which we don't currently do.
3325 const bool useOpacity =
3326 HasVisualOpacity(disp, effects, effectSetForOpacity) &&
3327 !SVGUtils::CanOptimizeOpacity(this);
3328
3329 const bool isTransformed = IsTransformed();
3330 const bool hasPerspective = isTransformed && HasPerspective();
3331 const bool extend3DContext =
3332 Extend3DContext(disp, effects, effectSetForOpacity);
3333 const bool combines3DTransformWithAncestors =
3334 (extend3DContext || isTransformed) && Combines3DTransformWithAncestors();
3335
3336 UniquePtr<nsDisplayListBuilder::AutoPreserves3DContext>
3337 autoPreserves3DContext;
3338 if (extend3DContext && !combines3DTransformWithAncestors) {
3339 // Start a new preserves3d context to keep informations on
3340 // nsDisplayListBuilder.
3341 autoPreserves3DContext =
3342 MakeUnique<nsDisplayListBuilder::AutoPreserves3DContext>(aBuilder);
3343 // Save dirty rect on the builder to avoid being distorted for
3344 // multiple transforms along the chain.
3345 aBuilder->SavePreserves3DRect();
3346
3347 // We rebuild everything within preserve-3d and don't try
3348 // to retain, so override the dirty rect now.
3349 if (aBuilder->IsRetainingDisplayList()) {
3350 dirtyRect = visibleRect;
3351 aBuilder->SetDisablePartialUpdates(true);
3352 }
3353 }
3354
3355 AutoTrackStackingContextBits stackingContextTracker(*aBuilder);
3356 aBuilder->ClearStackingContextBits();
3357
3358 nsRect visibleRectOutsideTransform = visibleRect;
3359 nsDisplayTransform::PrerenderInfo prerenderInfo;
3360 bool inTransform = aBuilder->IsInTransform();
3361 if (isTransformed) {
3362 prerenderInfo = nsDisplayTransform::ShouldPrerenderTransformedContent(
3363 aBuilder, this, &visibleRect);
3364
3365 switch (prerenderInfo.mDecision) {
3366 case nsDisplayTransform::PrerenderDecision::Full:
3367 case nsDisplayTransform::PrerenderDecision::Partial:
3368 dirtyRect = visibleRect;
3369 break;
3370 case nsDisplayTransform::PrerenderDecision::No: {
3371 // If we didn't prerender an animated frame in a preserve-3d context,
3372 // then we want disable async animations for the rest of the preserve-3d
3373 // (especially ancestors).
3374 if ((extend3DContext || combines3DTransformWithAncestors) &&
3375 prerenderInfo.mHasAnimations) {
3376 aBuilder->SavePreserves3DAllowAsyncAnimation(false);
3377 }
3378
3379 const nsRect overflow = InkOverflowRectRelativeToSelf();
3380 if (overflow.IsEmpty() && !extend3DContext) {
3381 return;
3382 }
3383
3384 // If we're in preserve-3d then grab the dirty rect that was given to
3385 // the root and transform using the combined transform.
3386 if (combines3DTransformWithAncestors) {
3387 visibleRect = dirtyRect = aBuilder->GetPreserves3DRect();
3388 }
3389
3390 const float appPerDev = PresContext()->AppUnitsPerDevPixel();
3391 uint32_t flags = nsDisplayTransform::kTransformRectFlags &
3392 ~nsDisplayTransform::OFFSET_BY_ORIGIN;
3393 if (!hasPerspective) {
3394 flags &= ~nsDisplayTransform::INCLUDE_PERSPECTIVE;
3395 }
3396 if (!combines3DTransformWithAncestors) {
3397 flags &= ~nsDisplayTransform::INCLUDE_PRESERVE3D_ANCESTORS;
3398 }
3399 auto transform = nsDisplayTransform::GetResultingTransformMatrix(
3400 this, nsPoint(), appPerDev, flags);
3401 nsRect untransformedDirtyRect;
3402 if (nsDisplayTransform::UntransformRect(dirtyRect, overflow, transform,
3403 appPerDev,
3404 &untransformedDirtyRect)) {
3405 dirtyRect = untransformedDirtyRect;
3406 nsDisplayTransform::UntransformRect(visibleRect, overflow, transform,
3407 appPerDev, &visibleRect);
3408 } else {
3409 // This should only happen if the transform is singular, in which case
3410 // nothing is visible anyway
3411 dirtyRect.SetEmpty();
3412 visibleRect.SetEmpty();
3413 }
3414 }
3415 }
3416 inTransform = true;
3417 } else if (IsFixedPosContainingBlock()) {
3418 // Restict the building area to the overflow rect for these frames, since
3419 // RetainedDisplayListBuilder uses it to know if the size of the stacking
3420 // context changed.
3421 visibleRect.IntersectRect(visibleRect, InkOverflowRect());
3422 dirtyRect.IntersectRect(dirtyRect, InkOverflowRect());
3423 }
3424
3425 bool hasOverrideDirtyRect = false;
3426 // If we're doing a partial build, we're not invalid and we're capable
3427 // of having an override building rect (stacking context and fixed pos
3428 // containing block), then we should assume we have one.
3429 // Either we have an explicit one, or nothing in our subtree changed and
3430 // we have an implicit empty rect.
3431 //
3432 // These conditions should match |CanStoreDisplayListBuildingRect()| in
3433 // RetainedDisplayListBuilder.cpp
3434 if (!aBuilder->IsReusingStackingContextItems() &&
3435 aBuilder->IsPartialUpdate() && !aBuilder->InInvalidSubtree() &&
3436 !IsFrameModified() && IsFixedPosContainingBlock() &&
3437 !GetPrevContinuation() && !GetNextContinuation()) {
3438 dirtyRect = nsRect();
3439 if (HasOverrideDirtyRegion()) {
3440 nsDisplayListBuilder::DisplayListBuildingData* data =
3441 GetProperty(nsDisplayListBuilder::DisplayListBuildingRect());
3442 if (data) {
3443 dirtyRect = data->mDirtyRect.Intersect(visibleRect);
3444 hasOverrideDirtyRect = true;
3445 }
3446 }
3447 }
3448
3449 const bool usingFilter = effects->HasFilters() && !style.IsRootElementStyle();
3450 const SVGUtils::MaskUsage maskUsage =
3451 SVGUtils::DetermineMaskUsage(this, false);
3452 const bool usingMask = maskUsage.UsingMaskOrClipPath();
3453 const bool usingSVGEffects = usingFilter || usingMask;
3454
3455 const nsRect visibleRectOutsideSVGEffects = visibleRect;
3456 nsDisplayList hoistedScrollInfoItemsStorage(aBuilder);
3457 if (usingSVGEffects) {
3458 dirtyRect =
3459 SVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, dirtyRect);
3460 visibleRect =
3461 SVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, visibleRect);
3462 aBuilder->EnterSVGEffectsContents(this, &hoistedScrollInfoItemsStorage);
3463 }
3464
3465 const bool useStickyPosition =
3466 disp->mPosition == StylePositionProperty::Sticky;
3467 bool shouldFlattenStickyItem = true;
3468
3469 const bool useFixedPosition =
3470 disp->mPosition == StylePositionProperty::Fixed &&
3471 aBuilder->IsPaintingToWindow() && !IsMenuPopupFrame() &&
3472 (DisplayPortUtils::IsFixedPosFrameInDisplayPort(this) ||
3473 BuilderHasScrolledClip(aBuilder));
3474
3475 if (capturedByViewTransition) {
3476 // Captured view transition elements must have their frames built regardless
3477 // of onscreen visibility so they can be snapshotted, since the snapshot can
3478 // itself be in view. We set visibleRect and dirtyRect to ensure the frame
3479 // and its descendants are painted.
3480 visibleRect = InkOverflowRectRelativeToSelf();
3481 dirtyRect = InkOverflowRectRelativeToSelf();
3482 }
3483
3484 nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList(
3485 aBuilder, this, visibleRect, dirtyRect, isTransformed);
3486
3487 UpdateCurrentHitTestInfo(aBuilder, this);
3488
3489 // Depending on the effects that are applied to this frame, we can create
3490 // multiple container display items and wrap them around our contents.
3491 // This enum lists all the potential container display items, in the order
3492 // outside to inside.
3493 enum class ContainerItemType : uint8_t {
3494 None = 0,
3495 FixedPosition,
3496 OwnLayerForTransformWithRoundedClip,
3497 Perspective,
3498 Transform,
3499 Filter,
3500 ViewTransitionCapture,
3501 };
3502
3503 // NOTE(emilio): The order of these RAII objects is quite subtle.
3504 nsDisplayListBuilder::AutoEnterViewTransitionCapture
3505 inViewTransitionCaptureSetter(aBuilder, capturedByViewTransition);
3506 RefPtr<const ActiveScrolledRoot> stickyASR = nullptr;
3507 nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(aBuilder);
3508 if (aBuilder->IsInViewTransitionCapture()) {
3509 // View transition contents shouldn't scroll along our ASR. They get
3510 // "pulled out" of the rendering (or when they don't, you can't scroll
3511 // anyways).
3512 asrSetter.SetCurrentActiveScrolledRoot(nullptr);
3513 }
3514 DisplayListClipState::AutoSaveRestore stickyItemClipState(aBuilder);
3515 if (useStickyPosition) {
3516 StickyScrollContainer* stickyScrollContainer =
3517 StickyScrollContainer::GetOrCreateForFrame(this);
3518 if (stickyScrollContainer && aBuilder->IsPaintingToWindow()) {
3519 if (!aBuilder->IsInViewTransitionCapture() &&
3520 stickyScrollContainer->ScrollContainer()
3521 ->IsMaybeAsynchronouslyScrolled()) {
3522 shouldFlattenStickyItem = false;
3523 }
3524 stickyScrollContainer->SetShouldFlatten(shouldFlattenStickyItem);
3525 }
3526
3527 if (shouldFlattenStickyItem) {
3528 stickyASR = aBuilder->CurrentActiveScrolledRoot();
3529 } else {
3530 stickyASR = aBuilder->GetOrCreateActiveScrolledRoot(
3531 aBuilder->CurrentActiveScrolledRoot(), this,
3532 ActiveScrolledRoot::ASRKind::Sticky);
3533 asrSetter.SetCurrentActiveScrolledRoot(stickyASR);
3534 stickyItemClipState.MaybeRemoveDisplayportClip();
3535 }
3536 }
3537
3538 nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
3539
3540 auto cssClip = GetClipPropClipRect(disp, effects, GetSize());
3541 auto ApplyClipProp = [&](DisplayListClipState::AutoSaveRestore& aClipState) {
3542 if (!cssClip) {
3543 return;
3544 }
3545 nsPoint offset = aBuilder->GetCurrentFrameOffsetToReferenceFrame();
3546 aBuilder->IntersectDirtyRect(*cssClip);
3547 aBuilder->IntersectVisibleRect(*cssClip);
3548 aClipState.ClipContentDescendants(*cssClip + offset);
3549 };
3550
3551 // The CSS clip property is effectively inside the transform, but outside the
3552 // filters. So if we're not transformed we can apply it just here for
3553 // simplicity, instead of on each of the places that handle clipCapturedBy.
3554 DisplayListClipState::AutoSaveRestore untransformedCssClip(aBuilder);
3555 if (!isTransformed) {
3556 ApplyClipProp(untransformedCssClip);
3557 }
3558
3559 // If there is a current clip, then depending on the container items we
3560 // create, different things can happen to it. Some container items simply
3561 // propagate the clip to their children and aren't clipped themselves.
3562 // But other container items, especially those that establish a different
3563 // geometry for their contents (e.g. transforms), capture the clip on
3564 // themselves and unset the clip for their contents. If we create more than
3565 // one of those container items, the clip will be captured on the outermost
3566 // one and the inner container items will be unclipped.
3567 ContainerItemType clipCapturedBy = ContainerItemType::None;
3568 if (capturedByViewTransition) {
3569 clipCapturedBy = isTransformed ? ContainerItemType::Transform
3570 : ContainerItemType::ViewTransitionCapture;
3571 } else if (useFixedPosition) {
3572 clipCapturedBy = ContainerItemType::FixedPosition;
3573 } else if (isTransformed) {
3574 const DisplayItemClipChain* currentClip =
3575 aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder);
3576 if ((hasPerspective || extend3DContext) &&
3577 (currentClip && currentClip->HasRoundedCorners())) {
3578 // If we're creating an nsDisplayTransform item that is going to combine
3579 // its transform with its children (preserve-3d or perspective), then we
3580 // can't have an intermediate surface. Mask layers force an intermediate
3581 // surface, so if we're going to need both then create a separate
3582 // wrapping layer for the mask.
3583 clipCapturedBy = ContainerItemType::OwnLayerForTransformWithRoundedClip;
3584 } else if (hasPerspective) {
3585 clipCapturedBy = ContainerItemType::Perspective;
3586 } else {
3587 clipCapturedBy = ContainerItemType::Transform;
3588 }
3589 } else if (usingFilter) {
3590 clipCapturedBy = ContainerItemType::Filter;
3591 }
3592
3593 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
3594 if (clipCapturedBy != ContainerItemType::None) {
3595 clipState.Clear();
3596 }
3597
3598 DisplayListClipState::AutoSaveRestore transformedCssClip(aBuilder);
3599 if (isTransformed) {
3600 // FIXME(emilio, bug 1525159): In the case we have a both a transform _and_
3601 // filters, this clips the input to the filters as well, which is not
3602 // correct (clipping by the `clip` property is supposed to happen after
3603 // applying the filter effects, per [1].
3604 //
3605 // This is not a regression though, since we used to do that anyway before
3606 // bug 1514384, and even without the transform we get it wrong.
3607 //
3608 // [1]: https://drafts.fxtf.org/css-masking/#placement
3609 ApplyClipProp(transformedCssClip);
3610 }
3611
3612 uint32_t numActiveScrollframesEncounteredBefore =
3613 aBuilder->GetNumActiveScrollframesEncountered();
3614
3615 nsDisplayListCollection set(aBuilder);
3616 Maybe<nsRect> clipForMask;
3617
3618 {
3619 DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
3620 nsDisplayListBuilder::AutoInTransformSetter inTransformSetter(aBuilder,
3621 inTransform);
3622 nsDisplayListBuilder::AutoEnterFilter filterASRSetter(aBuilder,
3623 usingFilter);
3624 nsDisplayListBuilder::AutoInEventsOnly inEventsSetter(
3625 aBuilder, opacityItemForEventsOnly);
3626
3627 DisplayListClipState::AutoSaveRestore stickyItemNestedClipState(aBuilder);
3628 if (useStickyPosition && !shouldFlattenStickyItem) {
3629 stickyItemNestedClipState.MaybeRemoveDisplayportClip();
3630 }
3631
3632 // If we have a mask, compute a clip to bound the masked content.
3633 // This is necessary in case the content moves with an ancestor
3634 // ASR of the mask.
3635 // Don't do this if we also have a filter, because then the clip
3636 // would be applied before the filter, violating
3637 // https://www.w3.org/TR/filter-effects-1/#placement.
3638 // Filters are a containing block for fixed and absolute descendants,
3639 // so the masked content cannot move with an ancestor ASR.
3640 if (usingMask && !usingFilter) {
3641 clipForMask = ComputeClipForMaskItem(aBuilder, this, maskUsage);
3642 if (clipForMask) {
3643 aBuilder->IntersectDirtyRect(*clipForMask);
3644 aBuilder->IntersectVisibleRect(*clipForMask);
3645 nestedClipState.ClipContentDescendants(
3646 *clipForMask + aBuilder->GetCurrentFrameOffsetToReferenceFrame());
3647 }
3648 }
3649
3650 // extend3DContext also guarantees that applyAbsPosClipping and
3651 // usingSVGEffects are false We only modify the preserve-3d rect if we are
3652 // the top of a preserve-3d heirarchy
3653 if (extend3DContext) {
3654 // Mark these first so MarkAbsoluteFramesForDisplayList knows if we are
3655 // going to be forced to descend into frames.
3656 aBuilder->MarkPreserve3DFramesForDisplayList(this);
3657 }
3658
3659 aBuilder->AdjustWindowDraggingRegion(this);
3660
3661 MarkAbsoluteFramesForDisplayList(aBuilder);
3662 aBuilder->Check();
3663 BuildDisplayList(aBuilder, set);
3664 // mBuiltDisplayList is observed only by painting-related tests (reftests
3665 // and paint-regression mochitests via nsIDOMWindowUtils), so record the
3666 // build only for painting display lists, not hit-testing, frame
3667 // visibility, or glyph-mask builds.
3668 if (aBuilder->IsForPainting()) {
3669 SetBuiltDisplayList(true);
3670 }
3671 aBuilder->Check();
3672 aBuilder->DisplayCaret(this, set.Outlines());
3673
3674 // Blend modes are a real pain for retained display lists. We build a blend
3675 // container item if the built list contains any blend mode items within
3676 // the current stacking context. This can change without an invalidation
3677 // to the stacking context frame, or the blend mode frame (e.g. by moving
3678 // an intermediate frame).
3679 // When we gain/remove a blend container item, we need to mark this frame
3680 // as invalid and have the full display list for merging to track
3681 // the change correctly.
3682 // It seems really hard to track this in advance, as the bookkeeping
3683 // required to note which stacking contexts have blend descendants
3684 // is complex and likely to be buggy.
3685 // Instead we're doing the sad thing, detecting it afterwards, and just
3686 // repeating display list building if it changed.
3687 // We have to repeat building for the entire display list (or at least
3688 // the outer stacking context), since we need to mark this frame as invalid
3689 // to remove any existing content that isn't wrapped in the blend container,
3690 // and then we need to build content infront/behind the blend container
3691 // to get correct positioning during merging.
3692 if (aBuilder->ContainsBlendMode() && aBuilder->IsRetainingDisplayList()) {
3693 if (aBuilder->IsPartialUpdate()) {
3694 aBuilder->SetPartialBuildFailed(true);
3695 } else {
3696 aBuilder->SetDisablePartialUpdates(true);
3697 }
3698 }
3699 }
3700
3701 if (aBuilder->IsBackgroundOnly()) {
3702 set.BlockBorderBackgrounds()->DeleteAll(aBuilder);
3703 set.Floats()->DeleteAll(aBuilder);
3704 set.Content()->DeleteAll(aBuilder);
3705 set.PositionedDescendants()->DeleteAll(aBuilder);
3706 set.Outlines()->DeleteAll(aBuilder);
3707 }
3708
3709 if (hasOverrideDirtyRect &&
3710 StaticPrefs::layout_display_list_show_rebuild_area()) {
3711 nsDisplaySolidColor* color = MakeDisplayItem<nsDisplaySolidColor>(
3712 aBuilder, this,
3713 dirtyRect + aBuilder->GetCurrentFrameOffsetToReferenceFrame(),
3714 NS_RGBA(255, 0, 0, 64)((nscolor)(((64) << 24) | ((0) << 16) | ((0) <<
8) | (255)))
);
3715 if (color) {
3716 color->SetOverrideZIndex(INT32_MAX(2147483647));
3717 set.PositionedDescendants()->AppendToTop(color);
3718 }
3719 }
3720
3721 nsIContent* content = GetContent();
3722 if (!content) {
3723 content = PresContext()->Document()->GetRootElement();
3724 }
3725
3726 nsDisplayList resultList(aBuilder);
3727 set.SerializeWithCorrectZOrder(&resultList, content);
3728
3729 // Get the ASR to use for the container items that we create here.
3730 const ActiveScrolledRoot* containerItemASR = contASRTracker.GetContainerASR();
3731
3732 bool createdContainer = false;
3733 const StackingContextBits localIsolationReasons = [&] {
3734 auto reasons = StackingContextBits::None;
3735 if (!GetParent()) {
3736 // We don't need to isolate the root frame.
3737 return reasons;
3738 }
3739 // Elements with a view-transition name also form a backdrop-root. Same for
3740 // masks / clip-path.
3741 // See https://www.w3.org/TR/css-view-transitions-1/#named-and-transitioning
3742 // and https://github.com/w3c/csswg-drafts/issues/11772
3743 const bool hasViewTransitionName =
3744 style.StyleUIReset()->HasViewTransitionName() &&
3745 !style.IsRootElementStyle();
3746 if ((disp->mWillChange.bits & StyleWillChangeBits::BACKDROP_ROOT) ||
3747 hasViewTransitionName || usingMask) {
3748 reasons |= StackingContextBits::ContainsBackdropFilter;
3749 }
3750 if (!combines3DTransformWithAncestors) {
3751 reasons |= StackingContextBits::MayContainNonIsolated3DTransform;
3752 }
3753 return reasons;
3754 }();
3755
3756 StackingContextBits currentIsolationReasons =
3757 localIsolationReasons & aBuilder->GetStackingContextBits();
3758 bool isolated = false;
3759 auto MarkAsIsolated = [&] {
3760 isolated = true;
3761 currentIsolationReasons = StackingContextBits::None;
3762 };
3763 auto ShouldForceIsolation = [&] {
3764 if (localIsolationReasons == StackingContextBits::None) {
3765 return false;
3766 }
3767 bool force = currentIsolationReasons != StackingContextBits::None;
3768 MarkAsIsolated();
3769 return force;
3770 };
3771
3772 // If adding both a nsDisplayBlendContainer and a nsDisplayBlendMode to the
3773 // same list, the nsDisplayBlendContainer should be added first. This only
3774 // happens when the element creating this stacking context has mix-blend-mode
3775 // and also contains a child which has mix-blend-mode.
3776 // The nsDisplayBlendContainer must be added to the list first, so it does not
3777 // isolate the containing element blending as well.
3778 if (aBuilder->ContainsBlendMode()) {
3779 resultList.AppendToTop(nsDisplayBlendContainer::CreateForMixBlendMode(
3780 aBuilder, this, &resultList, containerItemASR,
3781 nsDisplayItem::ContainerASRType::AncestorOfContained));
3782 createdContainer = true;
3783 MarkAsIsolated();
3784 }
3785
3786 // NOTE: When changing this condition make sure to tweak ScrollContainerFrame
3787 // as well.
3788 const bool usingBackdropFilter = effects->HasBackdropFilters() &&
3789 IsVisibleForPainting() &&
3790 !style.IsRootElementStyle();
3791 if (usingBackdropFilter) {
3792 stackingContextTracker.AddToParent(
3793 StackingContextBits::ContainsBackdropFilter);
3794 nsRect backdropRect =
3795 GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this);
3796 resultList.AppendNewToTop<nsDisplayBackdropFilters>(
3797 aBuilder, this, &resultList, backdropRect, this);
3798 createdContainer = true;
3799 MarkAsIsolated();
3800 }
3801
3802 // If there are any SVG effects, wrap the list up in an SVG effects item
3803 // (which also handles CSS group opacity). Note that we create an SVG effects
3804 // item even if resultList is empty, since a filter can produce graphical
3805 // output even if the element being filtered wouldn't otherwise do so.
3806 if (usingSVGEffects) {
3807 MOZ_ASSERT(usingFilter || usingMask,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(usingFilter || usingMask)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(usingFilter || usingMask))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("usingFilter || usingMask"
" (" "Beside filter & mask/clip-path, what else effect do we have?"
")", "./../../../layout/generic/nsIFrame.cpp", 3808); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "usingFilter || usingMask" ") (" "Beside filter & mask/clip-path, what else effect do we have?"
")"); do { MOZ_CrashSequence(__null, 3808); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
3808 "Beside filter & mask/clip-path, what else effect do we have?")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(usingFilter || usingMask)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(usingFilter || usingMask))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("usingFilter || usingMask"
" (" "Beside filter & mask/clip-path, what else effect do we have?"
")", "./../../../layout/generic/nsIFrame.cpp", 3808); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "usingFilter || usingMask" ") (" "Beside filter & mask/clip-path, what else effect do we have?"
")"); do { MOZ_CrashSequence(__null, 3808); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3809
3810 if (clipCapturedBy == ContainerItemType::Filter) {
3811 clipState.Restore();
3812 }
3813 // Revert to the post-filter dirty rect.
3814 aBuilder->SetVisibleRect(visibleRectOutsideSVGEffects);
3815
3816 // Skip all filter effects while generating glyph mask.
3817 if (usingFilter && !aBuilder->IsForGenerateGlyphMask()) {
3818 /* List now emptied, so add the new list to the top. */
3819 resultList.AppendNewToTop<nsDisplayFilters>(aBuilder, this, &resultList,
3820 this, usingBackdropFilter);
3821 createdContainer = true;
Value stored to 'createdContainer' is never read
3822 }
3823
3824 if (usingMask) {
3825 // The mask should move with aBuilder->CurrentActiveScrolledRoot(), so
3826 // that's the ASR we prefer to use for the mask item. However, we can
3827 // only do this if the mask if clipped with respect to that ASR, because
3828 // an item always needs to have finite bounds with respect to its ASR.
3829 // If we weren't able to compute a clip for the mask, we fall back to
3830 // using containerItemASR, which is the lowest common ancestor clip of
3831 // the mask's contents. That's not entirely correct, but it satisfies
3832 // the base requirement of the ASR system (that items have finite bounds
3833 // wrt. their ASR).
3834 const ActiveScrolledRoot* maskASR =
3835 clipForMask.isSome() ? aBuilder->CurrentActiveScrolledRoot()
3836 : containerItemASR;
3837 /* List now emptied, so add the new list to the top. */
3838 resultList.AppendNewToTop<nsDisplayMasksAndClipPaths>(
3839 aBuilder, this, &resultList, maskASR,
3840 clipForMask.isSome()
3841 ? nsDisplayItem::ContainerASRType::Constant
3842 : nsDisplayItem::ContainerASRType::AncestorOfContained,
3843 usingBackdropFilter, ShouldForceIsolation());
3844 createdContainer = true;
3845 }
3846
3847 // TODO(miko): We could probably create a wraplist here and avoid creating
3848 // it later in |BuildDisplayListForChild()|.
3849 createdContainer = false;
3850
3851 // Also add the hoisted scroll info items. We need those for APZ scrolling
3852 // because nsDisplayMasksAndClipPaths items can't build active layers.
3853 aBuilder->ExitSVGEffectsContents();
3854 resultList.AppendToTop(&hoistedScrollInfoItemsStorage);
3855 }
3856
3857 // If the list is non-empty and there is CSS group opacity without SVG
3858 // effects, wrap it up in an opacity item.
3859 if (useOpacity) {
3860 const bool needsActiveOpacityLayer =
3861 nsDisplayOpacity::NeedsActiveLayer(this);
3862 resultList.AppendNewToTop<nsDisplayOpacity>(
3863 aBuilder, this, &resultList, containerItemASR,
3864 nsDisplayItem::ContainerASRType::AncestorOfContained,
3865 opacityItemForEventsOnly, needsActiveOpacityLayer, usingBackdropFilter,
3866 ShouldForceIsolation());
3867 createdContainer = true;
3868 }
3869
3870 // We build nsDisplayViewTransitionCapture here, and then use
3871 // nsDisplayTransform to wrap it, to make sure we create the correct transform
3872 // for the captured element and its descendants. This is necessary to make
3873 // sure our captured element doesn't become blurry when using scale()
3874 // transform.
3875 //
3876 // So the display list looks like this:
3877 // nsDisplayTransform // For the captured element if it is transformed
3878 // VTCapture // For the captured element
3879 // ...
3880 // Other display items // For the descendants of the captured element
3881 // ...
3882 // ...
3883 //
3884 // We intentionally use nsDisplayTransform to wrap the VTCapture (so it's
3885 // different from opacity display item) because nsDisplayTransform may push a
3886 // reference frame which creates a new coordinate system in WR. So it's just
3887 // like a separator between the VTCapture the outside, if it is transformed.
3888 if (capturedByViewTransition) {
3889 resultList.AppendNewToTop<nsDisplayViewTransitionCapture>(
3890 aBuilder, this, &resultList, nullptr, false);
3891 createdContainer = true;
3892 MarkAsIsolated();
3893 // We don't want the capture to be clipped, so we do this _after_ building
3894 // the wrapping item.
3895 if (clipCapturedBy == ContainerItemType::ViewTransitionCapture) {
3896 clipState.Restore();
3897 }
3898 }
3899
3900 // If we're going to apply a transformation and don't have preserve-3d set,
3901 // wrap everything in an nsDisplayTransform. If there's nothing in the list,
3902 // don't add anything.
3903 //
3904 // For the preserve-3d case we want to individually wrap every child in the
3905 // list with a separate nsDisplayTransform instead. When the child is already
3906 // an nsDisplayTransform, we can skip this step, as the computed transform
3907 // will already include our own.
3908 //
3909 // We also traverse into sublists created by nsDisplayWrapList, so that we
3910 // find all the correct children.
3911 //
3912 // We still need to create nsDisplayTransform to wrap the VT capture element
3913 // to make sure WR renders it properly if it is transformed as well.
3914 if (isTransformed) {
3915 if (extend3DContext) {
3916 // Install dummy nsDisplayTransform as a leaf containing
3917 // descendants not participating this 3D rendering context.
3918 nsDisplayList nonparticipants(aBuilder);
3919 nsDisplayList participants(aBuilder);
3920 int index = 1;
3921
3922 nsDisplayItem* separator = nullptr;
3923
3924 // TODO: This can be simplified: |participants| is just |resultList|.
3925 for (nsDisplayItem* item : resultList.TakeItems()) {
3926 if (ItemParticipatesIn3DContext(this, item) &&
3927 !item->GetClip().HasClip()) {
3928 // The frame of this item participates the same 3D context.
3929 FlushNonParticipantsIntoSeparatorTransform(
3930 aBuilder, this, &nonparticipants, &participants, index,
3931 &separator);
3932
3933 participants.AppendToTop(item);
3934 } else if (nsIFrame* backfaceHidden =
3935 BackfaceHidden3DParticipantFor(this, item)) {
3936 // The item belongs to a participant with a hidden backface. Give it a
3937 // leaf keyed on that participant rather than adding it to the shared
3938 // separator below, which is keyed on us and so would be culled only
3939 // when our own backface is turned away.
3940 FlushNonParticipantsIntoSeparatorTransform(
3941 aBuilder, this, &nonparticipants, &participants, index,
3942 &separator);
3943
3944 nsDisplayList itemList(aBuilder);
3945 itemList.AppendToTop(item);
3946 participants.AppendToTop(MakeDisplayItemWithIndex<nsDisplayTransform>(
3947 aBuilder, backfaceHidden, index++, &itemList,
3948 aBuilder->GetVisibleRect()));
3949 } else {
3950 // The frame of the item doesn't participate the current
3951 // context, or has no transform.
3952 //
3953 // For items participating but not transformed, they are add
3954 // to nonparticipants to get a separator layer for handling
3955 // clips, if there is, on an intermediate surface.
3956 // \see ContainerLayer::DefaultComputeEffectiveTransforms().
3957 nonparticipants.AppendToTop(item);
3958 }
3959 }
3960 FlushNonParticipantsIntoSeparatorTransform(
3961 aBuilder, this, &nonparticipants, &participants, index, &separator);
3962
3963 if (separator) {
3964 createdContainer = true;
3965 MarkAsIsolated();
3966 }
3967
3968 resultList.AppendToTop(&participants);
3969 }
3970
3971 transformedCssClip.Restore();
3972 if (clipCapturedBy == ContainerItemType::Transform) {
3973 // Restore clip state now so nsDisplayTransform is clipped properly.
3974 clipState.Restore();
3975 }
3976 // Revert to the dirtyrect coming in from the parent, without our transform
3977 // taken into account.
3978 aBuilder->SetVisibleRect(visibleRectOutsideTransform);
3979
3980 if (this != aBuilder->RootReferenceFrame()) {
3981 // Revert to the outer reference frame and offset because all display
3982 // items we create from now on are outside the transform.
3983 nsPoint toOuterReferenceFrame;
3984 const nsIFrame* outerReferenceFrame =
3985 aBuilder->FindReferenceFrameFor(GetParent(), &toOuterReferenceFrame);
3986 toOuterReferenceFrame += GetPosition();
3987
3988 buildingDisplayList.SetReferenceFrameAndCurrentOffset(
3989 outerReferenceFrame, toOuterReferenceFrame);
3990 }
3991
3992 // We would like to block async animations for ancestors of ones not
3993 // prerendered in the preserve-3d tree. Now that we've finished processing
3994 // all descendants, update allowAsyncAnimation to take their prerender
3995 // state into account
3996 // FIXME: We don't block async animations for previous siblings because
3997 // their prerender decisions have been made. We may have to figure out a
3998 // better way to rollback their prerender decisions.
3999 // Alternatively we could not block animations for later siblings, and only
4000 // block them for ancestors of a blocked one.
4001 if ((extend3DContext || combines3DTransformWithAncestors) &&
4002 prerenderInfo.CanUseAsyncAnimations() &&
4003 !aBuilder->GetPreserves3DAllowAsyncAnimation()) {
4004 // aBuilder->GetPreserves3DAllowAsyncAnimation() means the inner or
4005 // previous silbing frames are allowed/disallowed for async animations.
4006 prerenderInfo.mDecision = nsDisplayTransform::PrerenderDecision::No;
4007 }
4008
4009 nsDisplayTransform* transformItem = MakeDisplayItem<nsDisplayTransform>(
4010 aBuilder, this, &resultList, visibleRect, prerenderInfo.mDecision,
4011 usingBackdropFilter, ShouldForceIsolation());
4012 if (transformItem) {
4013 resultList.AppendToTop(transformItem);
4014 createdContainer = true;
4015
4016 if (numActiveScrollframesEncounteredBefore !=
4017 aBuilder->GetNumActiveScrollframesEncountered()) {
4018 transformItem->SetContainsASRs(true);
4019 }
4020
4021 if (hasPerspective) {
4022 transformItem->MarkWithAssociatedPerspective();
4023
4024 if (clipCapturedBy == ContainerItemType::Perspective) {
4025 clipState.Restore();
4026 }
4027 resultList.AppendNewToTop<nsDisplayPerspective>(aBuilder, this,
4028 &resultList);
4029 createdContainer = true;
4030 }
4031
4032 // TODO(emilio): Ideally should also isolate when the transform is
4033 // potentially animated (prerenderInfo.mHasAnimations), but that causes a
4034 // lot of fuzz on Windows due to text antialiasing.
4035 const bool hasMaybe3dTransform =
4036 hasPerspective || !transformItem->GetTransform().Is2D();
4037 if (hasMaybe3dTransform) {
4038 stackingContextTracker.AddToParent(
4039 StackingContextBits::MayContainNonIsolated3DTransform);
4040 }
4041 }
4042 if (clipCapturedBy ==
4043 ContainerItemType::OwnLayerForTransformWithRoundedClip) {
4044 clipState.Restore();
4045 resultList.AppendNewToTopWithIndex<nsDisplayOwnLayer>(
4046 aBuilder, this,
4047 /* aIndex = */ nsDisplayOwnLayer::OwnLayerForTransformWithRoundedClip,
4048 &resultList, aBuilder->CurrentActiveScrolledRoot(),
4049 nsDisplayItem::ContainerASRType::Constant,
4050 nsDisplayOwnLayerFlags::None, ScrollbarData{},
4051 /* aForceActive = */ false, false);
4052 createdContainer = true;
4053 }
4054 }
4055
4056 // If we have sticky positioning, wrap it in a sticky position item.
4057 if (useFixedPosition && !capturedByViewTransition) {
4058 if (clipCapturedBy == ContainerItemType::FixedPosition) {
4059 clipState.Restore();
4060 }
4061 // The ASR for the fixed item should be the ASR of our containing block,
4062 // which has been set as the builder's current ASR, unless this frame is
4063 // invisible and we hadn't saved display item data for it. In that case,
4064 // we need to take the containerItemASR since we might have fixed children.
4065 // For WebRender, we want to the know what |containerItemASR| is for the
4066 // case where the fixed-pos item is not a "real" fixed-pos item (e.g. it's
4067 // nested inside a scrolling transform), so we stash that on the display
4068 // item as well.
4069 const ActiveScrolledRoot* fixedASR = ActiveScrolledRoot::PickAncestor(
4070 containerItemASR, aBuilder->CurrentActiveScrolledRoot());
4071 const ActiveScrolledRoot* scrollTargetASR =
4072 containerItemASR ? containerItemASR->GetNearestScrollASR() : nullptr;
4073 resultList.AppendNewToTop<nsDisplayFixedPosition>(
4074 aBuilder, this, &resultList, fixedASR,
4075 nsDisplayItem::ContainerASRType::AncestorOfContained, scrollTargetASR,
4076 ShouldForceIsolation());
4077 createdContainer = true;
4078 } else if (useStickyPosition && !capturedByViewTransition) {
4079 // For position:sticky, the clip needs to be applied both to the sticky
4080 // container item and to the contents. The container item needs the clip
4081 // because a scrolled clip needs to move independently from the sticky
4082 // contents, and the contents need the clip so that they have finite
4083 // clipped bounds with respect to the container item's ASR. The latter is
4084 // a little tricky in the case where the sticky item has both fixed and
4085 // non-fixed descendants, because that means that the sticky container
4086 // item's ASR is the ASR of the fixed descendant.
4087 // For WebRender display list building, though, we still want to know the
4088 // the ASR that the sticky container item would normally have, so we stash
4089 // that on the display item as the "container ASR" (i.e. the normal ASR of
4090 // the container item, excluding the special behaviour induced by fixed
4091 // descendants).
4092 const ActiveScrolledRoot* stickyItemASR = ActiveScrolledRoot::PickAncestor(
4093 containerItemASR, aBuilder->CurrentActiveScrolledRoot());
4094
4095 auto* stickyItem = MakeDisplayItem<nsDisplayStickyPosition>(
4096 aBuilder, this, &resultList, stickyItemASR,
4097 nsDisplayItem::ContainerASRType::AncestorOfContained,
4098 aBuilder->CurrentActiveScrolledRoot());
4099
4100 stickyItem->SetShouldFlatten(shouldFlattenStickyItem);
4101
4102 resultList.AppendToTop(stickyItem);
4103 createdContainer = true;
4104
4105 // If the sticky element is inside a filter, annotate the scroll frame that
4106 // scrolls the filter as having out-of-flow content inside a filter (this
4107 // inhibits paint skipping).
4108 if (aBuilder->GetFilterASR() && aBuilder->GetFilterASR() == stickyItemASR) {
4109 aBuilder->GetFilterASR()
4110 ->GetNearestScrollASR()
4111 ->ScrollFrame()
4112 ->SetHasOutOfFlowContentInsideFilter();
4113 }
4114 }
4115
4116 // If there's blending, wrap up the list in a blend-mode item. Note that
4117 // opacity can be applied before blending as the blend color is not affected
4118 // by foreground opacity (only background alpha).
4119 if (effects->mMixBlendMode != StyleBlend::Normal) {
4120 stackingContextTracker.AddToParent(
4121 StackingContextBits::ContainsMixBlendMode);
4122 resultList.AppendNewToTop<nsDisplayBlendMode>(
4123 aBuilder, this, &resultList, effects->mMixBlendMode, containerItemASR,
4124 nsDisplayItem::ContainerASRType::AncestorOfContained, false);
4125 createdContainer = true;
4126 MarkAsIsolated();
4127 }
4128
4129 if (!isolated && localIsolationReasons != StackingContextBits::None) {
4130 resultList.AppendToTop(nsDisplayBlendContainer::CreateForIsolation(
4131 aBuilder, this, &resultList, containerItemASR,
4132 nsDisplayItem::ContainerASRType::AncestorOfContained,
4133 ShouldForceIsolation()));
4134 createdContainer = true;
4135 }
4136
4137 if (!isolated && aBuilder->MayContainNonIsolated3DTransform()) {
4138 stackingContextTracker.AddToParent(
4139 StackingContextBits::MayContainNonIsolated3DTransform);
4140 }
4141
4142 if (aBuilder->IsReusingStackingContextItems()) {
4143 if (resultList.IsEmpty()) {
4144 return;
4145 }
4146
4147 nsDisplayItem* container = resultList.GetBottom();
4148 if (resultList.Length() > 1 || container->Frame() != this) {
4149 container = MakeDisplayItem<nsDisplayContainer>(
4150 aBuilder, this, containerItemASR,
4151 nsDisplayItem::ContainerASRType::AncestorOfContained, &resultList);
4152 } else {
4153 MOZ_ASSERT(resultList.Length() == 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(resultList.Length() == 1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(resultList.Length() == 1))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("resultList.Length() == 1"
, "./../../../layout/generic/nsIFrame.cpp", 4153); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "resultList.Length() == 1" ")"); do { MOZ_CrashSequence
(__null, 4153); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4154 resultList.Clear();
4155 }
4156
4157 // Mark the outermost display item as reusable. These display items and
4158 // their chidren can be reused during the next paint if no ancestor or
4159 // descendant frames have been modified.
4160 if (!container->IsReusedItem()) {
4161 container->SetReusable();
4162 }
4163 aList->AppendToTop(container);
4164 createdContainer = true;
4165 } else {
4166 aList->AppendToTop(&resultList);
4167 }
4168
4169 if (aCreatedContainerItem) {
4170 *aCreatedContainerItem = createdContainer;
4171 }
4172}
4173
4174static nsDisplayItem* WrapInWrapList(nsDisplayListBuilder* aBuilder,
4175 nsIFrame* aFrame, nsDisplayList* aList,
4176 const ActiveScrolledRoot* aContainerASR,
4177 bool aBuiltContainerItem = false) {
4178 nsDisplayItem* item = aList->GetBottom();
4179 if (!item) {
4180 return nullptr;
4181 }
4182
4183 // We need a wrap list if there are multiple items, or if the single
4184 // item has a different frame. This can change in a partial build depending
4185 // on which items we build, so we need to ensure that we don't transition
4186 // to/from a wrap list without invalidating correctly.
4187 bool needsWrapList =
4188 aList->Length() > 1 || item->Frame() != aFrame || item->GetChildren();
4189
4190 // If we have an explicit container item (that can't change without an
4191 // invalidation) or we're doing a full build and don't need a wrap list, then
4192 // we can skip adding one.
4193 if (aBuiltContainerItem || (!aBuilder->IsPartialUpdate() && !needsWrapList)) {
4194 MOZ_ASSERT(aList->Length() == 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aList->Length() == 1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aList->Length() == 1))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("aList->Length() == 1"
, "./../../../layout/generic/nsIFrame.cpp", 4194); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aList->Length() == 1" ")"); do { MOZ_CrashSequence
(__null, 4194); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4195 aList->Clear();
4196 return item;
4197 }
4198
4199 // If we're doing a partial build and we didn't need a wrap list
4200 // previously then we can try to work from there.
4201 if (aBuilder->IsPartialUpdate() &&
4202 !aFrame->HasDisplayItem(uint32_t(DisplayItemType::TYPE_CONTAINER))) {
4203 // If we now need a wrap list, we must previously have had no display items
4204 // or a single one belonging to this frame. Mark the item itself as
4205 // discarded so that RetainedDisplayListBuilder uses the ones we just built.
4206 // We don't want to mark the frame as modified as that would invalidate
4207 // positioned descendants that might be outside of this list, and might not
4208 // have been rebuilt this time.
4209 if (needsWrapList) {
4210 DiscardOldItems(aFrame);
4211 } else {
4212 MOZ_ASSERT(aList->Length() == 1)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aList->Length() == 1)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aList->Length() == 1))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("aList->Length() == 1"
, "./../../../layout/generic/nsIFrame.cpp", 4212); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aList->Length() == 1" ")"); do { MOZ_CrashSequence
(__null, 4212); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4213 aList->Clear();
4214 return item;
4215 }
4216 }
4217
4218 // The last case we could try to handle is when we previously had a wrap list,
4219 // but no longer need it. Unfortunately we can't differentiate this case from
4220 // a partial build where other children exist but we just didn't build them
4221 // this time.
4222 // TODO:RetainedDisplayListBuilder's merge phase has the full list and
4223 // could strip them out.
4224
4225 return MakeDisplayItem<nsDisplayContainer>(
4226 aBuilder, aFrame, aContainerASR,
4227 nsDisplayItem::ContainerASRType::AncestorOfContained, aList);
4228}
4229
4230/**
4231 * Check if a frame should be visited for building display list.
4232 */
4233static bool DescendIntoChild(nsDisplayListBuilder* aBuilder,
4234 const nsIFrame* aChild, const nsRect& aVisible,
4235 const nsRect& aDirty) {
4236 if (aChild->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
4237 return true;
4238 }
4239
4240 // If the child is a scrollframe that we want to ignore, then we need
4241 // to descend into it because its scrolled child may intersect the dirty
4242 // area even if the scrollframe itself doesn't.
4243 if (aChild == aBuilder->GetIgnoreScrollFrame()) {
4244 return true;
4245 }
4246
4247 // There are cases where the "ignore scroll frame" on the builder is not set
4248 // correctly, and so we additionally want to catch cases where the child is
4249 // a root scrollframe and we are ignoring scrolling on the viewport.
4250 if (aChild == aBuilder->GetPresShellIgnoreScrollFrame()) {
4251 return true;
4252 }
4253
4254 nsRect overflow = aChild->InkOverflowRect();
4255
4256 // On mobile, there may be a dynamic toolbar. The root content document's
4257 // root scroll frame's ink overflow rect does not include the toolbar
4258 // height, but if the toolbar is hidden, we still want to be able to target
4259 // content underneath the toolbar, so expand the overflow rect here to
4260 // allow display list building to descend into the scroll frame.
4261 if (aBuilder->IsForEventDelivery() &&
4262 aChild == aChild->PresShell()->GetRootScrollContainerFrame() &&
4263 aChild->PresContext()->IsRootContentDocumentCrossProcess() &&
4264 aChild->PresContext()->HasDynamicToolbar()) {
4265 overflow.SizeTo(nsLayoutUtils::ExpandHeightForDynamicToolbar(
4266 aChild->PresContext(), overflow.Size()));
4267 }
4268
4269 if (aDirty.Intersects(overflow)) {
4270 return true;
4271 }
4272
4273 if (aChild->ForceDescendIntoIfVisible() && aVisible.Intersects(overflow)) {
4274 return true;
4275 }
4276
4277 if (aChild->IsTablePart()) {
4278 // Relative positioning and transforms can cause table parts to move, but we
4279 // will still paint the backgrounds for their ancestor parts under them at
4280 // their 'normal' position. That means that we must consider the overflow
4281 // rects at both positions.
4282
4283 // We convert the overflow rect into the nsTableFrame's coordinate
4284 // space, applying the normal position offset at each step. Then we
4285 // compare that against the builder's cached dirty rect in table
4286 // coordinate space.
4287 const nsIFrame* f = aChild;
4288 nsRect normalPositionOverflowRelativeToTable = overflow;
4289
4290 while (f->IsTablePart()) {
4291 normalPositionOverflowRelativeToTable += f->GetNormalPosition();
4292 f = f->GetParent();
4293 }
4294
4295 nsDisplayTableBackgroundSet* tableBGs = aBuilder->GetTableBackgroundSet();
4296 if (tableBGs && tableBGs->GetDirtyRect().Intersects(
4297 normalPositionOverflowRelativeToTable)) {
4298 return true;
4299 }
4300 }
4301
4302 return false;
4303}
4304
4305void nsIFrame::BuildDisplayListForSimpleChild(nsDisplayListBuilder* aBuilder,
4306 nsIFrame* aChild,
4307 const nsDisplayListSet& aLists) {
4308 // This is the shortcut for frames been handled along the common
4309 // path, the most common one of THE COMMON CASE mentioned later.
4310 MOZ_ASSERT(aChild->Type() != LayoutFrameType::Placeholder)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aChild->Type() != LayoutFrameType::Placeholder)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aChild->Type() != LayoutFrameType::Placeholder)))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("aChild->Type() != LayoutFrameType::Placeholder"
, "./../../../layout/generic/nsIFrame.cpp", 4310); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aChild->Type() != LayoutFrameType::Placeholder"
")"); do { MOZ_CrashSequence(__null, 4310); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4311 MOZ_ASSERT(!aBuilder->GetSelectedFramesOnly() &&do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!aBuilder->GetSelectedFramesOnly() && !aBuilder
->GetIncludeAllOutOfFlows())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!aBuilder->GetSelectedFramesOnly
() && !aBuilder->GetIncludeAllOutOfFlows()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("!aBuilder->GetSelectedFramesOnly() && !aBuilder->GetIncludeAllOutOfFlows()"
" (" "It should be held for painting to window" ")", "./../../../layout/generic/nsIFrame.cpp"
, 4313); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aBuilder->GetSelectedFramesOnly() && !aBuilder->GetIncludeAllOutOfFlows()"
") (" "It should be held for painting to window" ")"); do { MOZ_CrashSequence
(__null, 4313); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
4312 !aBuilder->GetIncludeAllOutOfFlows(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!aBuilder->GetSelectedFramesOnly() && !aBuilder
->GetIncludeAllOutOfFlows())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!aBuilder->GetSelectedFramesOnly
() && !aBuilder->GetIncludeAllOutOfFlows()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("!aBuilder->GetSelectedFramesOnly() && !aBuilder->GetIncludeAllOutOfFlows()"
" (" "It should be held for painting to window" ")", "./../../../layout/generic/nsIFrame.cpp"
, 4313); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aBuilder->GetSelectedFramesOnly() && !aBuilder->GetIncludeAllOutOfFlows()"
") (" "It should be held for painting to window" ")"); do { MOZ_CrashSequence
(__null, 4313); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
4313 "It should be held for painting to window")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!aBuilder->GetSelectedFramesOnly() && !aBuilder
->GetIncludeAllOutOfFlows())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!aBuilder->GetSelectedFramesOnly
() && !aBuilder->GetIncludeAllOutOfFlows()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("!aBuilder->GetSelectedFramesOnly() && !aBuilder->GetIncludeAllOutOfFlows()"
" (" "It should be held for painting to window" ")", "./../../../layout/generic/nsIFrame.cpp"
, 4313); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aBuilder->GetSelectedFramesOnly() && !aBuilder->GetIncludeAllOutOfFlows()"
") (" "It should be held for painting to window" ")"); do { MOZ_CrashSequence
(__null, 4313); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4314 MOZ_ASSERT(aChild->HasAnyStateBits(NS_FRAME_SIMPLE_DISPLAYLIST))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aChild->HasAnyStateBits(NS_FRAME_SIMPLE_DISPLAYLIST
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aChild->HasAnyStateBits(NS_FRAME_SIMPLE_DISPLAYLIST
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aChild->HasAnyStateBits(NS_FRAME_SIMPLE_DISPLAYLIST)", "./../../../layout/generic/nsIFrame.cpp"
, 4314); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aChild->HasAnyStateBits(NS_FRAME_SIMPLE_DISPLAYLIST)"
")"); do { MOZ_CrashSequence(__null, 4314); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4315
4316 const nsPoint offset = aChild->GetOffsetTo(this);
4317 const nsRect visible = aBuilder->GetVisibleRect() - offset;
4318 const nsRect dirty = aBuilder->GetDirtyRect() - offset;
4319
4320 if (!DescendIntoChild(aBuilder, aChild, visible, dirty)) {
4321 DL_LOGV("Skipped frame %p", aChild)do { const ::mozilla::LogModule* moz_real_module = GetLoggerByProcess
(); if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Verbose)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Verbose, "Skipped frame %p", aChild); } } while (
0)
;
4322 return;
4323 }
4324
4325 // Child cannot be transformed since it is not a stacking context.
4326 nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(
4327 aBuilder, aChild, visible, dirty, false);
4328
4329 UpdateCurrentHitTestInfo(aBuilder, aChild);
4330
4331 aChild->MarkAbsoluteFramesForDisplayList(aBuilder);
4332 aBuilder->AdjustWindowDraggingRegion(aChild);
4333 aBuilder->Check();
4334 aChild->BuildDisplayList(aBuilder, aLists);
4335 if (aBuilder->IsForPainting()) {
4336 aChild->SetBuiltDisplayList(true);
4337 }
4338 aBuilder->Check();
4339 aBuilder->DisplayCaret(aChild, aLists.Outlines());
4340}
4341
4342static bool ShouldSkipFrame(nsDisplayListBuilder* aBuilder,
4343 const nsIFrame* aFrame) {
4344 // If painting is restricted to just the background of the top level frame,
4345 // then we have nothing to do here.
4346 if (aBuilder->IsBackgroundOnly()) {
4347 return true;
4348 }
4349 if (aBuilder->IsForGenerateGlyphMask()) {
4350 if ((aFrame->IsLeaf() && !aFrame->IsTextFrame()) ||
4351 aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
4352 // Only in-flow text frames are painted for background-clip: text mask
4353 // generation.
4354 return true;
4355 }
4356 }
4357 if (aBuilder->GetSelectedFramesOnly() && aFrame->IsLeaf() &&
4358 !aFrame->IsSelected()) {
4359 return true;
4360 }
4361 static const nsFrameState skipFlags = NS_FRAME_TOO_DEEP_IN_FRAME_TREE |
4362 NS_FRAME_IS_NONDISPLAY |
4363 NS_FRAME_POSITION_VISIBILITY_HIDDEN;
4364 if (aFrame->HasAnyStateBits(skipFlags)) {
4365 return true;
4366 }
4367 // Checking mMozSubtreeHiddenOnlyVisually is relatively slow because it
4368 // involves loading more memory. It's only allowed in chrome sheets so let's
4369 // only support it in the parent process so we can mostly optimize this out in
4370 // content processes.
4371 return XRE_IsParentProcess() &&
4372 aFrame->StyleUIReset()->mMozSubtreeHiddenOnlyVisually;
4373}
4374
4375#if defined(ACCESSIBILITY1) && defined(MOZ_ENABLE_SKIA_PDF1)
4376// Bug 2025119: If this is inlined in nsIFrame::BuildDisplayListForChild on
4377// Win32, we end up with crashes when there is deep recursion due to the
4378// increased stack size caused by the additional variables here. Work around
4379// this by having this code in its own function and ensuring it is never inlined
4380// on Win32.
4381# if defined(XP_WIN) && !defined(_WIN64)
4382MOZ_NEVER_INLINE__attribute__((noinline))
4383# endif
4384static void MaybeAddAccId(nsIFrame* aChildOrOutOfFlow,
4385 nsDisplayListBuilder* aBuilder,
4386 const nsDisplayListSet& aLists) {
4387 auto [innerWindowId, accId] =
4388 a11y::PdfStructTreeBuilder::GetAccId(aChildOrOutOfFlow);
4389 if (!innerWindowId) {
4390 return;
4391 }
4392 // When generating tagged PDF, associate this content with the correct
4393 // node in the structure tree.
4394 auto* item = MakeDisplayItem<nsDisplayAccessibleId>(
4395 aBuilder, aChildOrOutOfFlow, innerWindowId, accId);
4396 aLists.Content()->AppendToTop(item);
4397}
4398#endif
4399
4400void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
4401 nsIFrame* aChild,
4402 const nsDisplayListSet& aLists,
4403 DisplayChildFlags aFlags) {
4404 AutoCheckBuilder check(aBuilder);
4405 MOZ_ASSERT(!HidesContent(), "Caller should check")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!HidesContent())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!HidesContent()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!HidesContent()"
" (" "Caller should check" ")", "./../../../layout/generic/nsIFrame.cpp"
, 4405); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!HidesContent()"
") (" "Caller should check" ")"); do { MOZ_CrashSequence(__null
, 4405); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
;
4406#ifdef DEBUG1
4407 DL_LOGV("BuildDisplayListForChild (%p) <", aChild)do { const ::mozilla::LogModule* moz_real_module = GetLoggerByProcess
(); if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Verbose)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Verbose, "BuildDisplayListForChild (%p) <", aChild
); } } while (0)
;
4408 ScopeExit e(
4409 [aChild]() { DL_LOGV("> BuildDisplayListForChild (%p)", aChild)do { const ::mozilla::LogModule* moz_real_module = GetLoggerByProcess
(); if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Verbose)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Verbose, "> BuildDisplayListForChild (%p)", aChild
); } } while (0)
; });
4410#endif
4411
4412 nsIFrame* child = aChild;
4413 auto* placeholder = child->IsPlaceholderFrame()
4414 ? static_cast<nsPlaceholderFrame*>(child)
4415 : nullptr;
4416 nsIFrame* childOrOutOfFlow =
4417 placeholder ? placeholder->GetOutOfFlowFrame() : child;
4418 if (!childOrOutOfFlow || ShouldSkipFrame(aBuilder, childOrOutOfFlow)) {
4419 return;
4420 }
4421
4422 // If we're generating a display list for printing, include Link items for
4423 // frames that correspond to HTML link elements so that we can have active
4424 // links in saved PDF output. Note that the state of "within a link" is
4425 // set on the display-list builder, such that all descendants of the link
4426 // element will generate display-list links.
4427 // TODO: we should be able to optimize this so as to avoid creating links
4428 // for the same destination that entirely overlap each other, which adds
4429 // nothing useful to the final PDF.
4430 Maybe<nsDisplayListBuilder::Linkifier> linkifier;
4431 if (aBuilder->IsForPrinting()) {
4432 if (StaticPrefs::print_save_as_pdf_links_enabled()) {
4433 linkifier.emplace(aBuilder, childOrOutOfFlow, aLists.Content());
4434 linkifier->MaybeAppendLink(aBuilder, childOrOutOfFlow);
4435 }
4436#if defined(ACCESSIBILITY1) && defined(MOZ_ENABLE_SKIA_PDF1)
4437 MaybeAddAccId(childOrOutOfFlow, aBuilder, aLists);
4438#endif
4439 }
4440
4441 nsIFrame* parent = childOrOutOfFlow->GetParent();
4442 const auto* parentDisplay = parent->StyleDisplay();
4443 const auto overflowClipAxes =
4444 parent->ShouldApplyOverflowClipping(parentDisplay);
4445
4446 const bool isPaintingToWindow = aBuilder->IsPaintingToWindow();
4447 const bool doingShortcut =
4448 isPaintingToWindow &&
4449 child->HasAnyStateBits(NS_FRAME_SIMPLE_DISPLAYLIST) &&
4450 // Animations may change the stacking context state.
4451 // ShouldApplyOverflowClipping is affected by the parent style, which does
4452 // not invalidate the NS_FRAME_SIMPLE_DISPLAYLIST bit.
4453 !(!overflowClipAxes.isEmpty() || child->MayHaveTransformAnimation() ||
4454 child->MayHaveOpacityAnimation());
4455
4456 if (StaticPrefs::layout_css_scroll_anchoring_highlight()) {
4457 if (child->FirstContinuation()->IsScrollAnchor()) {
4458 nsRect bounds = child->GetContentRectRelativeToSelf() +
4459 aBuilder->ToReferenceFrame(child);
4460 nsDisplaySolidColor* color = MakeDisplayItem<nsDisplaySolidColor>(
4461 aBuilder, child, bounds, NS_RGBA(255, 0, 255, 64)((nscolor)(((64) << 24) | ((255) << 16) | ((0) <<
8) | (255)))
);
4462 if (color) {
4463 color->SetOverrideZIndex(INT32_MAX(2147483647));
4464 aLists.PositionedDescendants()->AppendToTop(color);
4465 }
4466 }
4467 }
4468
4469 if (doingShortcut) {
4470 BuildDisplayListForSimpleChild(aBuilder, child, aLists);
4471 return;
4472 }
4473
4474 // dirty rect in child-relative coordinates
4475 NS_ASSERTION(aBuilder->GetCurrentFrame() == this, "Wrong coord space!")do { if (!(aBuilder->GetCurrentFrame() == this)) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "Wrong coord space!", "aBuilder->GetCurrentFrame() == this"
, "./../../../layout/generic/nsIFrame.cpp", 4475); MOZ_PretendNoReturn
(); } } while (0)
;
4476 const nsPoint offset = child->GetOffsetTo(this);
4477 nsRect visible = aBuilder->GetVisibleRect() - offset;
4478 nsRect dirty = aBuilder->GetDirtyRect() - offset;
4479
4480 nsDisplayListBuilder::OutOfFlowDisplayData* savedOutOfFlowData = nullptr;
4481 if (placeholder) {
4482 if (placeholder->HasAnyStateBits(PLACEHOLDER_FOR_TOPLAYER)) {
4483 // If the out-of-flow frame is in the top layer, the viewport frame
4484 // will paint it. Skip it here. Note that, only out-of-flow frames
4485 // with this property should be skipped, because non-HTML elements
4486 // may stop their children from being out-of-flow. Those frames
4487 // should still be handled in the normal in-flow path.
4488 return;
4489 }
4490
4491 child = childOrOutOfFlow;
4492
4493 // If 'child' is a pushed out-of-flow then it's owned by a block that's not
4494 // an ancestor of the placeholder, and it will be painted by that block and
4495 // should not be painted through the placeholder. Also recheck
4496 // NS_FRAME_TOO_DEEP_IN_FRAME_TREE and NS_FRAME_IS_NONDISPLAY.
4497 static const nsFrameState skipFlags =
4498 (NS_FRAME_IS_PUSHED_OUT_OF_FLOW | NS_FRAME_TOO_DEEP_IN_FRAME_TREE |
4499 NS_FRAME_IS_NONDISPLAY);
4500 if (child->HasAnyStateBits(skipFlags) || nsLayoutUtils::IsPopup(child)) {
4501 return;
4502 }
4503
4504 // For a transformed absolute containing block, if the abspos's placeholder
4505 // is in a different continuation's subtree, the abspos was already built
4506 // directly by DisplayAbsoluteFramesNotBuiltByPlaceholder() on its
4507 // containing block. Exclude inline frames because IB-split (bug 489100)
4508 // can cause the placeholder to live in a later IB-split sibling.
4509 if (parent->IsTransformed() && !parent->IsInlineFrame() &&
4510 child->IsAbsolutelyPositioned() &&
4511 !nsLayoutUtils::IsProperAncestorFrame(parent, placeholder)) {
4512 return;
4513 }
4514
4515 MOZ_ASSERT(child->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(child->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW))>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(child->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("child->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)"
, "./../../../layout/generic/nsIFrame.cpp", 4515); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "child->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)"
")"); do { MOZ_CrashSequence(__null, 4515); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4516 savedOutOfFlowData = nsDisplayListBuilder::GetOutOfFlowData(child);
4517
4518 if (aBuilder->GetIncludeAllOutOfFlows()) {
4519 visible = child->InkOverflowRect();
4520 dirty = child->InkOverflowRect();
4521 } else if (savedOutOfFlowData) {
4522 visible =
4523 savedOutOfFlowData->GetVisibleRectForFrame(aBuilder, child, &dirty);
4524 } else {
4525 // The out-of-flow frame did not intersect the dirty area. We may still
4526 // need to traverse into it, since it may contain placeholders we need
4527 // to enter to reach other out-of-flow frames that are visible.
4528 visible.SetEmpty();
4529 dirty.SetEmpty();
4530 }
4531 }
4532
4533 NS_ASSERTION(!child->IsPlaceholderFrame(),do { if (!(!child->IsPlaceholderFrame())) { NS_DebugBreak(
NS_DEBUG_ASSERTION, "Should have dealt with placeholders already"
, "!child->IsPlaceholderFrame()", "./../../../layout/generic/nsIFrame.cpp"
, 4534); MOZ_PretendNoReturn(); } } while (0)
4534 "Should have dealt with placeholders already")do { if (!(!child->IsPlaceholderFrame())) { NS_DebugBreak(
NS_DEBUG_ASSERTION, "Should have dealt with placeholders already"
, "!child->IsPlaceholderFrame()", "./../../../layout/generic/nsIFrame.cpp"
, 4534); MOZ_PretendNoReturn(); } } while (0)
;
4535
4536 if (!DescendIntoChild(aBuilder, child, visible, dirty)) {
4537 DL_LOGV("Skipped frame %p", child)do { const ::mozilla::LogModule* moz_real_module = GetLoggerByProcess
(); if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, LogLevel::Verbose)), 0))) { mozilla::detail::log_print(moz_real_module
, LogLevel::Verbose, "Skipped frame %p", child); } } while (0
)
;
4538 return;
4539 }
4540
4541 const bool isSVG = child->HasAnyStateBits(NS_FRAME_SVG_LAYOUT);
4542
4543 // This flag is raised if the control flow strays off the common path.
4544 // The common path is the most common one of THE COMMON CASE mentioned later.
4545 bool awayFromCommonPath = !isPaintingToWindow;
4546
4547 // true if this is a real or pseudo stacking context
4548 bool pseudoStackingContext =
4549 aFlags.contains(DisplayChildFlag::ForcePseudoStackingContext);
4550
4551 if (!pseudoStackingContext && !isSVG &&
4552 aFlags.contains(DisplayChildFlag::Inline) &&
4553 !child->IsLineParticipant()) {
4554 // child is a non-inline frame in an inline context, i.e.,
4555 // it acts like inline-block or inline-table. Therefore it is a
4556 // pseudo-stacking-context.
4557 pseudoStackingContext = true;
4558 }
4559
4560 // Since we're now sure that we're adding this frame to the display list
4561 // (which means we're painting it, modulo occlusion), mark it as visible
4562 // within the displayport.
4563 if (isPaintingToWindow && child->TrackingVisibility() &&
4564 child->IsVisibleForPainting()) {
4565 child->PresShell()->EnsureFrameInApproximatelyVisibleList(child);
4566 awayFromCommonPath = true;
4567 }
4568
4569 // Child is composited if it's transformed, partially transparent, or has
4570 // SVG effects or a blend mode..
4571 const nsStyleDisplay* disp = child->StyleDisplay();
4572 const nsStyleEffects* effects = child->StyleEffects();
4573
4574 const bool isPositioned = disp->IsPositionedStyle();
4575 const bool isStackingContext =
4576 aFlags.contains(DisplayChildFlag::ForceStackingContext) ||
4577 child->IsStackingContext(disp, effects);
4578
4579 if (pseudoStackingContext || isStackingContext || isPositioned ||
4580 placeholder || (!isSVG && disp->IsFloating(child)) ||
4581 (isSVG && effects->mClip.IsRect() && IsSVGContentWithCSSClip(child))) {
4582 pseudoStackingContext = true;
4583 awayFromCommonPath = true;
4584 }
4585
4586 NS_ASSERTION(!isStackingContext || pseudoStackingContext,do { if (!(!isStackingContext || pseudoStackingContext)) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "Stacking contexts must also be pseudo-stacking-contexts"
, "!isStackingContext || pseudoStackingContext", "./../../../layout/generic/nsIFrame.cpp"
, 4587); MOZ_PretendNoReturn(); } } while (0)
4587 "Stacking contexts must also be pseudo-stacking-contexts")do { if (!(!isStackingContext || pseudoStackingContext)) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "Stacking contexts must also be pseudo-stacking-contexts"
, "!isStackingContext || pseudoStackingContext", "./../../../layout/generic/nsIFrame.cpp"
, 4587); MOZ_PretendNoReturn(); } } while (0)
;
4588
4589 nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(
4590 aBuilder, child, visible, dirty);
4591
4592 UpdateCurrentHitTestInfo(aBuilder, child);
4593
4594 DisplayListClipState::AutoClipMultiple clipState(aBuilder);
4595 nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(aBuilder);
4596
4597 if (savedOutOfFlowData) {
4598 aBuilder->SetBuildingInvisibleItems(false);
4599
4600 nsIFrame* scrollsWithAnchor = nullptr;
4601 if (aBuilder->IsPaintingToWindow() &&
4602 // If we are in view transition capture we get a null asr no matter
4603 // what, so don't bother checking for async scrolling with a CSS anchor
4604 // pos anchor.
4605 !aBuilder->IsInViewTransitionCapture() &&
4606 child->IsAbsolutelyPositioned(disp) &&
4607 // If there is an active view transition in this document it is tricky
4608 // to determine what will be an active scroll frame outside of that
4609 // frame's BuildDisplayList, so don't bother to async scroll with an
4610 // anchor in that case. Bug 2001861 tracks removing this check.
4611 !PresContext()->Document()->GetActiveViewTransition()) {
4612 scrollsWithAnchor = AnchorPositioningUtils::GetAnchorThatFrameScrollsWith(
4613 child, aBuilder);
4614
4615 if (scrollsWithAnchor && aBuilder->IsRetainingDisplayList()) {
4616 if (aBuilder->IsPartialUpdate()) {
4617 aBuilder->SetPartialBuildFailed(true);
4618 } else {
4619 aBuilder->SetDisablePartialUpdates(true);
4620 }
4621 }
4622 }
4623
4624 const ActiveScrolledRoot* asr =
4625 savedOutOfFlowData->mContainingBlockActiveScrolledRoot;
4626
4627#ifdef DEBUG1
4628 if (aBuilder->IsPaintingToWindow()) {
4629 // Assert that the asr is as expected.
4630 if (savedOutOfFlowData->mContainingBlockInViewTransitionCapture) {
4631 MOZ_ASSERT(asr == nullptr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(asr == nullptr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(asr == nullptr))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("asr == nullptr"
, "./../../../layout/generic/nsIFrame.cpp", 4631); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "asr == nullptr" ")"); do { MOZ_CrashSequence
(__null, 4631); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4632 MOZ_ASSERT(aBuilder->IsInViewTransitionCapture())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aBuilder->IsInViewTransitionCapture())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(aBuilder->IsInViewTransitionCapture()))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("aBuilder->IsInViewTransitionCapture()"
, "./../../../layout/generic/nsIFrame.cpp", 4632); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aBuilder->IsInViewTransitionCapture()" ")"
); do { MOZ_CrashSequence(__null, 4632); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4633 } else if ((asr ? FrameAndASRKind{asr->mFrame, asr->mKind}
4634 : FrameAndASRKind::default_value()) !=
4635 DisplayPortUtils::GetASRAncestorFrame(
4636 {child->GetParent(), ActiveScrolledRoot::ASRKind::Scroll},
4637 aBuilder)) {
4638 // A weird case for native anonymous content in the custom content
4639 // container when the root is captured by a view transition. This
4640 // content is built outside of the view transition capture but the
4641 // containing block (the canvas frame) was built inside the capture, so
4642 // savedOutOfFlowData is saved as if we are inside the capture while we
4643 // are outside it (bug 2002160).
4644 MOZ_ASSERT(asr == nullptr)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(asr == nullptr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(asr == nullptr))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("asr == nullptr"
, "./../../../layout/generic/nsIFrame.cpp", 4644); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "asr == nullptr" ")"); do { MOZ_CrashSequence
(__null, 4644); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4645 MOZ_ASSERT(PresContext()->Document()->GetActiveViewTransition())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(PresContext()->Document()->GetActiveViewTransition
())>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(PresContext()->Document()->GetActiveViewTransition
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("PresContext()->Document()->GetActiveViewTransition()"
, "./../../../layout/generic/nsIFrame.cpp", 4645); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "PresContext()->Document()->GetActiveViewTransition()"
")"); do { MOZ_CrashSequence(__null, 4645); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4646 MOZ_ASSERT(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(child->GetParent()->GetContent()->IsInNativeAnonymousSubtree
())>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(child->GetParent()->GetContent()->IsInNativeAnonymousSubtree
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("child->GetParent()->GetContent()->IsInNativeAnonymousSubtree()"
, "./../../../layout/generic/nsIFrame.cpp", 4647); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "child->GetParent()->GetContent()->IsInNativeAnonymousSubtree()"
")"); do { MOZ_CrashSequence(__null, 4647); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
4647 child->GetParent()->GetContent()->IsInNativeAnonymousSubtree())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(child->GetParent()->GetContent()->IsInNativeAnonymousSubtree
())>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(child->GetParent()->GetContent()->IsInNativeAnonymousSubtree
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("child->GetParent()->GetContent()->IsInNativeAnonymousSubtree()"
, "./../../../layout/generic/nsIFrame.cpp", 4647); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "child->GetParent()->GetContent()->IsInNativeAnonymousSubtree()"
")"); do { MOZ_CrashSequence(__null, 4647); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4648 bool inTopLayer = false;
4649 nsIFrame* curr = child->GetParent();
4650 while (curr) {
4651 if (curr->StyleDisplay()->mTopLayer == StyleTopLayer::Auto) {
4652 inTopLayer = true;
4653 break;
4654 }
4655 curr = curr->GetParent();
4656 }
4657 MOZ_ASSERT(inTopLayer)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(inTopLayer)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(inTopLayer))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("inTopLayer", "./../../../layout/generic/nsIFrame.cpp"
, 4657); AnnotateMozCrashReason("MOZ_ASSERT" "(" "inTopLayer"
")"); do { MOZ_CrashSequence(__null, 4657); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4658 }
4659 }
4660#endif
4661
4662 if (scrollsWithAnchor) {
4663 asr = DisplayPortUtils::ActivateDisplayportOnASRAncestors(
4664 scrollsWithAnchor, child->GetParent(), asr, aBuilder);
4665
4666 // TODO should we set the scroll parent id too?
4667 // https://github.com/w3c/csswg-drafts/issues/12042
4668 }
4669
4670 if (aBuilder->IsInViewTransitionCapture()) {
4671 if (!savedOutOfFlowData->mContainingBlockInViewTransitionCapture) {
4672 clipState.Clear();
4673 } else {
4674 clipState.SetClipChainForContainingBlockDescendants(
4675 savedOutOfFlowData->mContainingBlockClipChain);
4676 }
4677 asrSetter.SetCurrentActiveScrolledRoot(nullptr);
4678 } else {
4679 clipState.SetClipChainForContainingBlockDescendants(
4680 savedOutOfFlowData->mContainingBlockClipChain);
4681 asrSetter.SetCurrentActiveScrolledRoot(asr);
4682 asrSetter.SetCurrentScrollParentId(savedOutOfFlowData->mScrollParentId);
4683 }
4684 MOZ_ASSERT(awayFromCommonPath,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(awayFromCommonPath)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(awayFromCommonPath))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("awayFromCommonPath"
" (" "It is impossible when savedOutOfFlowData is true" ")",
"./../../../layout/generic/nsIFrame.cpp", 4685); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "awayFromCommonPath" ") (" "It is impossible when savedOutOfFlowData is true"
")"); do { MOZ_CrashSequence(__null, 4685); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
4685 "It is impossible when savedOutOfFlowData is true")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(awayFromCommonPath)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(awayFromCommonPath))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("awayFromCommonPath"
" (" "It is impossible when savedOutOfFlowData is true" ")",
"./../../../layout/generic/nsIFrame.cpp", 4685); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "awayFromCommonPath" ") (" "It is impossible when savedOutOfFlowData is true"
")"); do { MOZ_CrashSequence(__null, 4685); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4686 } else if (HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) &&
4687 placeholder) {
4688 NS_ASSERTION(visible.IsEmpty(), "should have empty visible rect")do { if (!(visible.IsEmpty())) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "should have empty visible rect", "visible.IsEmpty()", "./../../../layout/generic/nsIFrame.cpp"
, 4688); MOZ_PretendNoReturn(); } } while (0)
;
4689 // Every item we build from now until we descent into an out of flow that
4690 // does have saved out of flow data should be invisible. This state gets
4691 // restored when AutoBuildingDisplayList gets out of scope.
4692 aBuilder->SetBuildingInvisibleItems(true);
4693
4694 // If we have nested out-of-flow frames and the outer one isn't visible
4695 // then we won't have stored clip data for it. We can just clear the clip
4696 // instead since we know we won't render anything, and the inner out-of-flow
4697 // frame will setup the correct clip for itself.
4698 clipState.SetClipChainForContainingBlockDescendants(nullptr);
4699 }
4700
4701 // Setup clipping for the parent's overflow:clip,
4702 // or overflow:hidden on elements that don't support scrolling (and therefore
4703 // don't create nsHTML/XULScrollFrame). This clipping needs to not clip
4704 // anything directly rendered by the parent, only the rendering of its
4705 // children.
4706 // Don't use overflowClip to restrict the dirty rect, since some of the
4707 // descendants may not be clipped by it. Even if we end up with unnecessary
4708 // display items, they'll be pruned during ComputeVisibility.
4709 //
4710 // FIXME(emilio): Why can't we handle this more similarly to `clip` (on the
4711 // parent, rather than on the children)? Would ClipContentDescendants do what
4712 // we want?
4713 if (!overflowClipAxes.isEmpty()) {
4714 ApplyOverflowClipping(aBuilder, parent, overflowClipAxes, clipState);
4715 awayFromCommonPath = true;
4716 }
4717
4718 nsDisplayList list(aBuilder);
4719 nsDisplayList extraPositionedDescendants(aBuilder);
4720 const ActiveScrolledRoot* wrapListASR;
4721 bool builtContainerItem = false;
4722 if (isStackingContext) {
4723 // True stacking context.
4724 // For stacking contexts, BuildDisplayListForStackingContext handles
4725 // clipping and MarkAbsoluteFramesForDisplayList.
4726 nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
4727 child->BuildDisplayListForStackingContext(aBuilder, &list,
4728 &builtContainerItem);
4729 wrapListASR = contASRTracker.GetContainerASR();
4730 if (!aBuilder->IsReusingStackingContextItems() &&
4731 aBuilder->GetCaretFrame() == child) {
4732 builtContainerItem = false;
4733 }
4734 } else {
4735 Maybe<nsRect> clipPropClip =
4736 child->GetClipPropClipRect(disp, effects, child->GetSize());
4737 if (clipPropClip) {
4738 aBuilder->IntersectVisibleRect(*clipPropClip);
4739 aBuilder->IntersectDirtyRect(*clipPropClip);
4740 clipState.ClipContentDescendants(*clipPropClip +
4741 aBuilder->ToReferenceFrame(child));
4742 awayFromCommonPath = true;
4743 }
4744
4745 child->MarkAbsoluteFramesForDisplayList(aBuilder);
4746 if (aBuilder->IsForPainting()) {
4747 child->SetBuiltDisplayList(true);
4748 }
4749
4750 // Some SVG frames might change opacity without invalidating the frame, so
4751 // exclude them from the fast-path.
4752 if (!awayFromCommonPath && !child->IsSVGFrame()) {
4753 // The shortcut is available for the child for next time.
4754 child->AddStateBits(NS_FRAME_SIMPLE_DISPLAYLIST);
4755 }
4756
4757 if (!pseudoStackingContext) {
4758 // THIS IS THE COMMON CASE.
4759 // Not a pseudo or real stacking context. Do the simple thing and
4760 // return early.
4761 aBuilder->AdjustWindowDraggingRegion(child);
4762 aBuilder->Check();
4763 child->BuildDisplayList(aBuilder, aLists);
4764 aBuilder->Check();
4765 aBuilder->DisplayCaret(child, aLists.Outlines());
4766 return;
4767 }
4768
4769 // A pseudo-stacking context (e.g., a positioned element with z-index auto).
4770 // We allow positioned descendants of the child to escape to our parent
4771 // stacking context's positioned descendant list, because they might be
4772 // z-index:non-auto
4773 nsDisplayListCollection pseudoStack(aBuilder);
4774
4775 aBuilder->AdjustWindowDraggingRegion(child);
4776 nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
4777 aBuilder->Check();
4778 child->BuildDisplayList(aBuilder, pseudoStack);
4779 aBuilder->Check();
4780 if (aBuilder->DisplayCaret(child, pseudoStack.Outlines())) {
4781 builtContainerItem = false;
4782 }
4783 wrapListASR = contASRTracker.GetContainerASR();
4784
4785 list.AppendToTop(pseudoStack.BorderBackground());
4786 list.AppendToTop(pseudoStack.BlockBorderBackgrounds());
4787 list.AppendToTop(pseudoStack.Floats());
4788 list.AppendToTop(pseudoStack.Content());
4789 list.AppendToTop(pseudoStack.Outlines());
4790 extraPositionedDescendants.AppendToTop(pseudoStack.PositionedDescendants());
4791 }
4792
4793 buildingForChild.RestoreBuildingInvisibleItemsValue();
4794
4795 if (!list.IsEmpty()) {
4796 if (isPositioned || isStackingContext) {
4797 // Genuine stacking contexts, and positioned pseudo-stacking-contexts,
4798 // go in this level.
4799 nsDisplayItem* item = WrapInWrapList(aBuilder, child, &list, wrapListASR,
4800 builtContainerItem);
4801 if (isSVG) {
4802 aLists.Content()->AppendToTop(item);
4803 } else {
4804 aLists.PositionedDescendants()->AppendToTop(item);
4805 }
4806 } else if (!isSVG && disp->IsFloating(child)) {
4807 aLists.Floats()->AppendToTop(
4808 WrapInWrapList(aBuilder, child, &list, wrapListASR));
4809 } else {
4810 aLists.Content()->AppendToTop(&list);
4811 }
4812 }
4813 // We delay placing the positioned descendants of positioned frames to here,
4814 // because in the absence of z-index this is the correct order for them.
4815 // This doesn't affect correctness because the positioned descendants list
4816 // is sorted by z-order and content in BuildDisplayListForStackingContext,
4817 // but it means that sort routine needs to do less work.
4818 aLists.PositionedDescendants()->AppendToTop(&extraPositionedDescendants);
4819}
4820
4821void nsIFrame::MarkAbsoluteFramesForDisplayList(
4822 nsDisplayListBuilder* aBuilder) {
4823 if (const auto* absCB = GetAbsoluteContainingBlock()) {
4824 aBuilder->MarkFramesForDisplayList(this, absCB->GetChildList());
4825 }
4826}
4827
4828nsIContent* nsIFrame::GetExplicitEventTargetContent(
4829 const WidgetEvent* aEvent /* = nullptr */) const {
4830 // Return the content as-is if this is not a generated content.
4831 if (!IsGeneratedContentFrame()) {
4832 return GetContent();
4833 }
4834 // If the content is a generated content, it won't handle any events from the
4835 // DOM point of view. Therefore, let's return the parent of the generated
4836 // content.
4837 const nsIFrame* generatedRoot = this;
4838 while (true) {
4839 auto* parent = nsLayoutUtils::GetParentOrPlaceholderFor(generatedRoot);
4840 if (!parent || !parent->IsGeneratedContentFrame()) {
4841 break;
4842 }
4843 generatedRoot = parent;
4844 }
4845 // Return the non-generated ancestor.
4846 return generatedRoot->GetContent()->GetParent();
4847}
4848
4849nsIContent* nsIFrame::GetEventTargetContent(
4850 const mozilla::WidgetEvent* aEvent /* = nullptr */) const {
4851 return nsContentUtils::GetEventTargetContent(
4852 GetExplicitEventTargetContent(aEvent), aEvent);
4853}
4854
4855void nsIFrame::FireDOMEvent(const nsAString& aDOMEventName,
4856 nsIContent* aContent) {
4857 nsIContent* target = aContent ? aContent : GetContent();
4858
4859 if (target) {
4860 auto asyncDispatcher = MakeRefPtr<AsyncEventDispatcher>(
4861 target, aDOMEventName, CanBubble::eYes, ChromeOnlyDispatch::eNo);
4862 DebugOnly<nsresult> rv = asyncDispatcher->PostDOMEvent();
4863 NS_ASSERTION(NS_SUCCEEDED(rv), "AsyncEventDispatcher failed to dispatch")do { if (!(((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1
))))) { NS_DebugBreak(NS_DEBUG_ASSERTION, "AsyncEventDispatcher failed to dispatch"
, "NS_SUCCEEDED(rv)", "./../../../layout/generic/nsIFrame.cpp"
, 4863); MOZ_PretendNoReturn(); } } while (0)
;
4864 }
4865}
4866
4867nsresult nsIFrame::HandleEvent(nsPresContext* aPresContext,
4868 WidgetGUIEvent* aEvent,
4869 nsEventStatus* aEventStatus) {
4870 if (aEvent->mMessage == eMouseMove) {
4871 // XXX If the second argument of HandleDrag() is WidgetMouseEvent,
4872 // the implementation becomes simpler.
4873 return HandleDrag(aPresContext, aEvent, aEventStatus);
4874 }
4875
4876 if ((aEvent->mClass == eMouseEventClass &&
4877 aEvent->AsMouseEvent()->mButton == MouseButton::ePrimary) ||
4878 aEvent->mClass == eTouchEventClass) {
4879 if (aEvent->mMessage == eMouseDown || aEvent->mMessage == eTouchStart) {
4880 HandlePress(aPresContext, aEvent, aEventStatus);
4881 } else if (aEvent->mMessage == eMouseUp || aEvent->mMessage == eTouchEnd) {
4882 HandleRelease(aPresContext, aEvent, aEventStatus);
4883 }
4884 return NS_OK;
4885 }
4886
4887 // When secondary button is down, we need to move selection to make users
4888 // possible to paste something at click point quickly.
4889 // When middle button is down, we need to just move selection and focus at
4890 // the clicked point. Note that even if middle click paste is not enabled,
4891 // Chrome moves selection at middle mouse button down. So, we should follow
4892 // the behavior for the compatibility.
4893 if (aEvent->mMessage == eMouseDown) {
4894 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
4895 if (mouseEvent && (mouseEvent->mButton == MouseButton::eSecondary ||
4896 mouseEvent->mButton == MouseButton::eMiddle)) {
4897 if (*aEventStatus == nsEventStatus_eConsumeNoDefault) {
4898 return NS_OK;
4899 }
4900 return MoveCaretToEventPoint(aPresContext, mouseEvent, aEventStatus);
4901 }
4902 }
4903
4904 return NS_OK;
4905}
4906
4907nsresult nsIFrame::GetDataForTableSelection(
4908 const nsFrameSelection* aFrameSelection, mozilla::PresShell* aPresShell,
4909 WidgetMouseEvent* aMouseEvent, nsIContent** aParentContent,
4910 int32_t* aContentOffset, TableSelectionMode* aTarget) {
4911 if (!aFrameSelection || !aPresShell || !aMouseEvent || !aParentContent ||
4912 !aContentOffset || !aTarget) {
4913 return NS_ERROR_NULL_POINTER;
4914 }
4915
4916 *aParentContent = nullptr;
4917 *aContentOffset = 0;
4918 *aTarget = TableSelectionMode::None;
4919
4920 int16_t displaySelection = aPresShell->GetSelectionFlags();
4921
4922 bool selectingTableCells = aFrameSelection->IsInTableSelectionMode();
4923
4924 // DISPLAY_ALL means we're in an editor.
4925 // If already in cell selection mode,
4926 // continue selecting with mouse drag or end on mouse up,
4927 // or when using shift key to extend block of cells
4928 // (Mouse down does normal selection unless Ctrl/Cmd is pressed)
4929 bool doTableSelection =
4930 displaySelection == nsISelectionDisplay::DISPLAY_ALL &&
4931 selectingTableCells &&
4932 (aMouseEvent->mMessage == eMouseMove ||
4933 (aMouseEvent->mMessage == eMouseUp &&
4934 aMouseEvent->mButton == MouseButton::ePrimary) ||
4935 aMouseEvent->IsShift());
4936
4937 if (!doTableSelection) {
4938 // In Browser, special 'table selection' key must be pressed for table
4939 // selection or when just Shift is pressed and we're already in table/cell
4940 // selection mode
4941#ifdef XP_MACOSX
4942 doTableSelection = aMouseEvent->IsMeta() ||
4943 (aMouseEvent->IsShift() && selectingTableCells);
4944#else
4945 doTableSelection = aMouseEvent->IsControl() ||
4946 (aMouseEvent->IsShift() && selectingTableCells);
4947#endif
4948 }
4949 if (!doTableSelection) {
4950 return NS_OK;
4951 }
4952
4953 // Get the cell frame or table frame (or parent) of the current content node
4954 nsIFrame* frame = this;
4955 bool foundCell = false;
4956 bool foundTable = false;
4957
4958 const Element* const independentSelectionLimiter =
4959 aFrameSelection->GetIndependentSelectionRootElement();
4960
4961 if (independentSelectionLimiter &&
4962 !independentSelectionLimiter->Contains(GetContent())) {
4963 // If our content is not under the limiting node, stop the search right now.
4964 return NS_OK;
4965 }
4966
4967 for (; frame; frame = frame->GetParent()) {
4968 // Check for a table cell by querying to a known CellFrame interface
4969 if (static_cast<nsITableCellLayout*>(do_QueryFrame(frame))) {
4970 foundCell = true;
4971 // TODO: If we want to use proximity to top or left border
4972 // for row and column selection, this is the place to do it
4973 break;
4974 }
4975 if (frame->IsTableWrapperFrame()) {
4976 // If not a cell, check for table. This will happen when the starting
4977 // frame is the table or child of a table, such as a row (we were
4978 // in-between cells or in table border).
4979 // TODO: How can we select row when along left table edge
4980 // or select column when along top edge?
4981 foundTable = true;
4982 break;
4983 }
4984 }
4985
4986 if (!foundCell && !foundTable) {
4987 // We aren't in a cell or table.
4988 return NS_OK;
4989 }
4990
4991 nsIContent* tableOrCellContent = frame->GetContent();
4992 if (!tableOrCellContent) {
4993 return NS_OK;
4994 }
4995
4996 if (independentSelectionLimiter &&
4997 !independentSelectionLimiter->Contains(tableOrCellContent)) {
4998 return NS_OK;
4999 }
5000
5001 nsCOMPtr<nsIContent> parentContent = tableOrCellContent->GetParent();
5002 if (!parentContent) {
5003 return NS_ERROR_FAILURE;
5004 }
5005
5006 const int32_t offset =
5007 parentContent->ComputeIndexOf_Deprecated(tableOrCellContent);
5008 // Not likely?
5009 if (offset < 0) {
5010 return NS_ERROR_FAILURE;
5011 }
5012
5013 // Everything is OK -- set the return values
5014 parentContent.forget(aParentContent);
5015
5016 *aContentOffset = offset;
5017
5018#if 0
5019 if (selectRow)
5020 *aTarget = TableSelectionMode::Row;
5021 else if (selectColumn)
5022 *aTarget = TableSelectionMode::Column;
5023 else
5024#endif
5025 if (foundCell) {
5026 *aTarget = TableSelectionMode::Cell;
5027 } else if (foundTable) {
5028 *aTarget = TableSelectionMode::Table;
5029 }
5030
5031 return NS_OK;
5032}
5033
5034static bool IsEditingHost(const nsIFrame* aFrame) {
5035 if (aFrame->Style()->GetPseudoType() ==
5036 PseudoStyleType::MozTextControlEditingRoot) {
5037 return true;
5038 }
5039 nsIContent* content = aFrame->GetContent();
5040 return content && content->IsEditingHost();
5041}
5042
5043mozilla::StyleUserSelect nsIFrame::UsedUserSelect(
5044 const nsIFrame* aFrame, nsIFrame::ForSelectionStart aType) {
5045 return UsedUserSelectRecurse(aFrame, aType).valueOr(StyleUserSelect::Text);
5046}
5047
5048Maybe<mozilla::StyleUserSelect> nsIFrame::UsedUserSelectRecurse(
5049 const nsIFrame* aFrame, nsIFrame::ForSelectionStart aType) {
5050 if (aFrame->IsGeneratedContentFrame()) {
5051 return Some(StyleUserSelect::None);
5052 }
5053
5054 // Per https://drafts.csswg.org/css-ui-4/#content-selection:
5055 //
5056 // The used value is the same as the computed value, except:
5057 //
5058 // 1 - on editable elements where the used value is always 'contain'
5059 // regardless of the computed value
5060 // 2 - when the computed value is auto, in which case the used value is one
5061 // of the other values...
5062 //
5063 // See https://github.com/w3c/csswg-drafts/issues/3344 to see why we do this
5064 // at used-value time instead of at computed-value time.
5065
5066 // Although the draft does not define that editable elements under an editing
5067 // host should ignore `user-select`, Chrome ignores `user-select:none` and
5068 // `user-select:all` and we've already considered as ignoring the
5069 // `user-select` according to this test:
5070 // https://searchfox.org/firefox-main/rev/6abddcb0a5076c3b888686ede6f4cf7d082460d3/dom/base/test/test_bug1101364.html#73-85
5071 // https://searchfox.org/firefox-main/rev/4fd0fa7e5814c0b51f1dd075821988377bc56cc1/testing/web-platform/tests/css/css-ui/user-select-none-in-editable.html#23-24,32-36
5072 // Therefore, we check aFrame->ContentIsEditable() instead of
5073 // IsEditingHost(aFrame).
5074 if (aFrame->IsTextInputFrame() || aFrame->ContentIsEditable()) {
5075 // We don't implement 'contain' itself, but we make 'text' behave as
5076 // 'contain' for contenteditable and <input> / <textarea> elements anyway so
5077 // this is ok.
5078 return Some(StyleUserSelect::Text);
5079 }
5080
5081 auto style = aFrame->Style()->UserSelect();
5082 if (style != StyleUserSelect::Auto) {
5083 return Some(style);
5084 }
5085
5086 const auto IsButton = [](const nsIFrame* aFrame) {
5087 const auto* content = aFrame->GetContent();
5088 if (!content) {
5089 return false;
5090 }
5091 return content->IsAnyOfHTMLElements(nsGkAtoms::button);
5092 };
5093
5094 auto* parent = nsLayoutUtils::GetParentOrPlaceholderFor(aFrame);
5095 const auto parentResult =
5096 parent ? UsedUserSelectRecurse(parent, aType) : Nothing{};
5097 if (parentResult.valueOr(StyleUserSelect::Auto) != StyleUserSelect::None &&
5098 aType == nsIFrame::ForSelectionStart::Yes && IsButton(aFrame)) {
5099 // If the parent did not force our hands, we want non-`contenteditable`
5100 // buttons to be clickable without accidentally selecting the text within
5101 // it, but still allow selection that originates outside of it.
5102 return Some(StyleUserSelect::None);
5103 }
5104 return parentResult;
5105}
5106
5107bool nsIFrame::IsSelectable(StyleUserSelect* aSelectStyle) const {
5108 auto style = UsedUserSelect(this, ForSelectionStart::No);
5109 if (aSelectStyle) {
5110 *aSelectStyle = style;
5111 }
5112 return style != StyleUserSelect::None;
5113}
5114
5115bool nsIFrame::ShouldPaintNormalSelection() const {
5116 if (IsSelectable()) {
5117 // NOTE: Ideally, we should return false if display selection is "OFF".
5118 // However, here is a hot path at painting. Therefore, it should be checked
5119 // before and we shouldn't need to check it.
5120 return true;
5121 }
5122 // If we're not selectable by user, we should paint selection only while the
5123 // normal selection is styled as "attention" by "Find in Page" or something.
5124 return GetDisplaySelection() == nsISelectionController::SELECTION_ATTENTION;
5125}
5126
5127bool nsIFrame::ShouldHaveLineIfEmpty() const {
5128 switch (Style()->GetPseudoType()) {
5129 case PseudoStyleType::NotPseudo:
5130 break;
5131 case PseudoStyleType::MozScrolledContent:
5132 return GetParent()->ShouldHaveLineIfEmpty();
5133 default:
5134 return false;
5135 }
5136 return IsInputButtonControlFrame() || IsEditingHost(this);
5137}
5138
5139/**
5140 * Handles the Mouse Press Event for the frame
5141 */
5142NS_IMETHODIMPnsresult
5143nsIFrame::HandlePress(nsPresContext* aPresContext, WidgetGUIEvent* aEvent,
5144 nsEventStatus* aEventStatus) {
5145 NS_ENSURE_ARG_POINTER(aEventStatus)do { if ((__builtin_expect(!!(!(aEventStatus)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "aEventStatus" ") failed"
, nullptr, "./../../../layout/generic/nsIFrame.cpp", 5145); return
NS_ERROR_INVALID_POINTER; } } while (false)
;
5146 if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
5147 return NS_OK;
5148 }
5149
5150 NS_ENSURE_ARG_POINTER(aEvent)do { if ((__builtin_expect(!!(!(aEvent)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "aEvent" ") failed", nullptr
, "./../../../layout/generic/nsIFrame.cpp", 5150); return NS_ERROR_INVALID_POINTER
; } } while (false)
;
5151 if (aEvent->mClass == eTouchEventClass) {
5152 return NS_OK;
5153 }
5154
5155 return MoveCaretToEventPoint(aPresContext, aEvent->AsMouseEvent(),
5156 aEventStatus);
5157}
5158
5159nsresult nsIFrame::MoveCaretToEventPoint(nsPresContext* aPresContext,
5160 WidgetMouseEvent* aMouseEvent,
5161 nsEventStatus* aEventStatus) {
5162 MOZ_ASSERT(aPresContext)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aPresContext)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aPresContext))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("aPresContext", "./../../../layout/generic/nsIFrame.cpp"
, 5162); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aPresContext"
")"); do { MOZ_CrashSequence(__null, 5162); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5163 MOZ_ASSERT(aMouseEvent)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aMouseEvent)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aMouseEvent))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("aMouseEvent", "./../../../layout/generic/nsIFrame.cpp"
, 5163); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aMouseEvent"
")"); do { MOZ_CrashSequence(__null, 5163); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5164 MOZ_ASSERT(aMouseEvent->mMessage == eMouseDown)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aMouseEvent->mMessage == eMouseDown)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(aMouseEvent->mMessage == eMouseDown))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aMouseEvent->mMessage == eMouseDown"
, "./../../../layout/generic/nsIFrame.cpp", 5164); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aMouseEvent->mMessage == eMouseDown" ")"
); do { MOZ_CrashSequence(__null, 5164); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5165 MOZ_ASSERT(aEventStatus)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aEventStatus)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aEventStatus))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("aEventStatus", "./../../../layout/generic/nsIFrame.cpp"
, 5165); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aEventStatus"
")"); do { MOZ_CrashSequence(__null, 5165); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5166 MOZ_ASSERT(nsEventStatus_eConsumeNoDefault != *aEventStatus)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(nsEventStatus_eConsumeNoDefault != *aEventStatus)>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(nsEventStatus_eConsumeNoDefault != *aEventStatus))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("nsEventStatus_eConsumeNoDefault != *aEventStatus"
, "./../../../layout/generic/nsIFrame.cpp", 5166); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "nsEventStatus_eConsumeNoDefault != *aEventStatus"
")"); do { MOZ_CrashSequence(__null, 5166); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5167
5168 mozilla::PresShell* presShell = aPresContext->GetPresShell();
5169 if (!presShell) {
5170 return NS_ERROR_FAILURE;
5171 }
5172
5173 // We often get out of sync state issues with mousedown events that
5174 // get interrupted by alerts/dialogs.
5175 // Check with the ESM to see if we should process this one
5176 EventStateManager* const esm = aPresContext->EventStateManager();
5177 if (!esm->EventStatusOK(aMouseEvent)) {
5178 return NS_OK;
5179 }
5180
5181 if (nsIContent* dragGestureContent = esm->GetTrackingDragGestureContent()) {
5182 // When the current tracked dragging gesture is different than this frame,
5183 // it means this frame was being dragged, however it got moved/destroyed. So
5184 // we should consider the drag is still happening, so return early here.
5185 // Additionally, when dragGestureContent is a `Text` and the text frame is
5186 // reframed by a preceding event listener, we're the parent element frame.
5187 // In that case, we need to treat this as a normal mouse button down.
5188 // Therefore, we should compare the inclusive flattened tree ancestor
5189 // element of dragGestureContent and our content.
5190 const bool isDragGestureContent =
5191 mContent == dragGestureContent ||
5192 mContent ==
5193 dragGestureContent->GetInclusiveFlattenedTreeAncestorElement();
5194 if (!isDragGestureContent) {
5195 return NS_OK;
5196 }
5197 }
5198
5199 const nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(
5200 aMouseEvent, RelativeTo{this});
5201
5202 // When not using `alt`, and clicking on a draggable, but non-editable
5203 // element, don't do anything, and let d&d handle the event.
5204 //
5205 // See bug 48876, bug 388659 and bug 55921 for context here.
5206 //
5207 // FIXME(emilio): The .Contains(pt) check looks a bit fishy. When would it be
5208 // false given we're the event target? If it is needed, why not checking the
5209 // actual draggable node rect instead?
5210 if (!aMouseEvent->IsAlt() && GetRectRelativeToSelf().Contains(pt)) {
5211 for (nsIContent* content = mContent; content;
5212 content = content->GetFlattenedTreeParent()) {
5213 if (nsContentUtils::ContentIsDraggable(content) &&
5214 !content->IsEditable()) {
5215 return NS_OK;
5216 }
5217 }
5218 }
5219
5220 // If we are in Navigator and the click is in a draggable node, we don't want
5221 // to start selection because we don't want to interfere with a potential
5222 // drag of said node and steal all its glory.
5223 const bool isEditor =
5224 presShell->GetSelectionFlags() == nsISelectionDisplay::DISPLAY_ALL;
5225
5226 // Don't do something if it's middle button down event.
5227 const bool isPrimaryButtonDown =
5228 aMouseEvent->mButton == MouseButton::ePrimary;
5229
5230 // Check whether this frame should handle selection events. If not, don't
5231 // tell selection the mouse event even occurred.
5232 if (!ShouldHandleSelectionMovementEvents(ForSelectionStart::Yes)) {
5233 return NS_OK;
5234 }
5235
5236 if (isPrimaryButtonDown) {
5237 // If the mouse is dragged outside the nearest enclosing scrollable area
5238 // while making a selection, the area will be scrolled. To do this, capture
5239 // the mouse on the nearest scroll container frame. If there isn't a scroll
5240 // container frame, or something else is already capturing the mouse,
5241 // there's no reason to capture.
5242 if (!PresShell::GetCapturingContent()) {
5243 ScrollContainerFrame* scrollContainerFrame =
5244 nsLayoutUtils::GetNearestScrollContainerFrame(
5245 this, nsLayoutUtils::SCROLLABLE_SAME_DOC |
5246 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
5247 if (scrollContainerFrame) {
5248 nsIFrame* capturingFrame = scrollContainerFrame;
5249 PresShell::SetCapturingContent(capturingFrame->GetContent(),
5250 CaptureFlags::IgnoreAllowedState);
5251 }
5252 }
5253 }
5254
5255 // XXX This is screwy; it really should use the selection frame, not the
5256 // event frame
5257 const nsFrameSelection* frameselection = GetConstFrameSelection();
5258 if (!frameselection || frameselection->GetDisplaySelection() ==
5259 nsISelectionController::SELECTION_OFF) {
5260 return NS_OK; // nothing to do we cannot affect selection from here
5261 }
5262
5263#ifdef XP_MACOSX
5264 // If Control key is pressed on macOS, it should be treated as right click.
5265 // So, don't change selection.
5266 if (aMouseEvent->IsControl()) {
5267 return NS_OK;
5268 }
5269 const bool control = aMouseEvent->IsMeta();
5270#else
5271 const bool control = aMouseEvent->IsControl();
5272#endif
5273
5274 RefPtr<nsFrameSelection> fc = const_cast<nsFrameSelection*>(frameselection);
5275 if (isPrimaryButtonDown && aMouseEvent->mClickCount > 1) {
5276 // These methods aren't const but can't actually delete anything,
5277 // so no need for AutoWeakFrame.
5278 fc->SetDragState(true);
5279 return HandleMultiplePress(aPresContext, aMouseEvent, aEventStatus,
5280 control);
5281 }
5282
5283 ContentOffsets offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN);
5284
5285 if (!offsets.content) {
5286 return NS_ERROR_FAILURE;
5287 }
5288
5289 const bool isSecondaryButton =
5290 aMouseEvent->mButton == MouseButton::eSecondary;
5291 if (isSecondaryButton &&
5292 !MovingCaretToEventPointAllowedIfSecondaryButtonEvent(
5293 *frameselection, *aMouseEvent, *offsets.content,
5294 // When we collapse selection in nsFrameSelection::TakeFocus,
5295 // we always collapse selection to the start offset. Therefore,
5296 // we can ignore the end offset here. E.g., when an <img> is clicked,
5297 // set the primary offset to after it, but the the secondary offset
5298 // may be before it, see OffsetsForSingleFrame for the detail.
5299 offsets.StartOffset())) {
5300 return NS_OK;
5301 }
5302
5303 if (aMouseEvent->mMessage == eMouseDown &&
5304 aMouseEvent->mButton == MouseButton::eMiddle &&
5305 !offsets.content->IsEditable()) {
5306 // However, some users don't like the Chrome compatible behavior of
5307 // middle mouse click. They want to keep selection after starting
5308 // autoscroll. However, the selection change is important for middle
5309 // mouse past. Therefore, we should allow users to take the traditional
5310 // behavior back by themselves unless middle click paste is enabled or
5311 // autoscrolling is disabled.
5312 if (!Preferences::GetBool("middlemouse.paste", false) &&
5313 Preferences::GetBool("general.autoScroll", false) &&
5314 Preferences::GetBool("general.autoscroll.prevent_to_collapse_selection_"
5315 "by_middle_mouse_down",
5316 false)) {
5317 return NS_OK;
5318 }
5319 }
5320
5321 if (isPrimaryButtonDown) {
5322 // Let Ctrl/Cmd + left mouse down do table selection instead of drag
5323 // initiation.
5324 nsCOMPtr<nsIContent> parentContent;
5325 int32_t contentOffset;
5326 TableSelectionMode target;
5327 nsresult rv = GetDataForTableSelection(
5328 frameselection, presShell, aMouseEvent, getter_AddRefs(parentContent),
5329 &contentOffset, &target);
5330 if (NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1))) && parentContent) {
5331 fc->SetDragState(true);
5332 return fc->HandleTableSelection(parentContent, contentOffset, target,
5333 aMouseEvent);
5334 }
5335 }
5336
5337 fc->SetDelayedCaretData(nullptr);
5338
5339 if (isPrimaryButtonDown) {
5340 // Check if any part of this frame is selected, and if the user clicked
5341 // inside the selected region, and if it's the left button. If so, we delay
5342 // starting a new selection since the user may be trying to drag the
5343 // selected region to some other app.
5344
5345 if (GetContent() && GetContent()->IsMaybeSelected()) {
5346 bool inSelection = false;
5347 UniquePtr<SelectionDetails> details = frameselection->LookUpSelection(
5348 offsets.content, 0, offsets.EndOffset(),
5349 nsFrameSelection::IgnoreNormalSelection::No);
5350
5351 //
5352 // If there are any details, check to see if the user clicked
5353 // within any selected region of the frame.
5354 //
5355
5356 for (SelectionDetails* curDetail = details.get(); curDetail;
5357 curDetail = curDetail->mNext.get()) {
5358 //
5359 // If the user clicked inside a selection, then just
5360 // return without doing anything. We will handle placing
5361 // the caret later on when the mouse is released. We ignore
5362 // the spellcheck, find and url formatting selections.
5363 //
5364 if (curDetail->mSelectionType != SelectionType::eSpellCheck &&
5365 curDetail->mSelectionType != SelectionType::eFind &&
5366 curDetail->mSelectionType != SelectionType::eURLSecondary &&
5367 curDetail->mSelectionType != SelectionType::eURLStrikeout &&
5368 curDetail->mSelectionType != SelectionType::eHighlight &&
5369 curDetail->mSelectionType != SelectionType::eTargetText &&
5370 curDetail->mStart <= offsets.StartOffset() &&
5371 offsets.EndOffset() <= curDetail->mEnd) {
5372 inSelection = true;
5373 }
5374 }
5375
5376 if (inSelection) {
5377 fc->SetDragState(false);
5378 fc->SetDelayedCaretData(aMouseEvent);
5379 return NS_OK;
5380 }
5381 }
5382
5383 fc->SetDragState(true);
5384 }
5385
5386 // Do not touch any nsIFrame members after this point without adding
5387 // weakFrame checks.
5388 const nsFrameSelection::FocusMode focusMode = [&]() {
5389 // If "Shift" and "Ctrl" are both pressed, "Shift" is given precedence. This
5390 // mimics the old behaviour.
5391 const bool isShift =
5392 aMouseEvent->IsShift() &&
5393 // If Shift + secondary button press shoud open context menu without a
5394 // contextmenu event, user wants to open context menu like as a
5395 // secondary button press without Shift key.
5396 !(isSecondaryButton &&
5397 StaticPrefs::dom_event_contextmenu_shift_suppresses_event());
5398 if (isShift) {
5399 // If clicked in a link when focused content is editable, we should
5400 // collapse selection in the link for compatibility with Blink.
5401 if (isEditor) {
5402 for (Element* element : mContent->InclusiveAncestorsOfType<Element>()) {
5403 if (element->IsLink()) {
5404 return nsFrameSelection::FocusMode::kCollapseToNewPoint;
5405 }
5406 }
5407 }
5408 return nsFrameSelection::FocusMode::kExtendSelection;
5409 }
5410
5411 if (isPrimaryButtonDown && control) {
5412 return nsFrameSelection::FocusMode::kMultiRangeSelection;
5413 }
5414
5415 return nsFrameSelection::FocusMode::kCollapseToNewPoint;
5416 }();
5417
5418 nsresult rv = fc->HandleClick(
5419 MOZ_KnownLive(offsets.content)(offsets.content) /* bug 1636889 */, offsets.StartOffset(),
5420 offsets.EndOffset(), focusMode, offsets.associate);
5421 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
5422 return rv;
5423 }
5424
5425 // We don't handle mouse button up if it's middle button.
5426 if (isPrimaryButtonDown && offsets.offset != offsets.secondaryOffset) {
5427 fc->MaintainSelection();
5428 }
5429
5430 if (isPrimaryButtonDown && isEditor && !aMouseEvent->IsShift() &&
5431 (offsets.EndOffset() - offsets.StartOffset()) == 1) {
5432 // A single node is selected and we aren't extending an existing selection,
5433 // which means the user clicked directly on an object (either
5434 // `user-select: all` or a non-text node without children). Therefore,
5435 // disable selection extension during mouse moves.
5436 // XXX This is a bit hacky; shouldn't editor be able to deal with this?
5437 fc->SetDragState(false);
5438 }
5439
5440 return NS_OK;
5441}
5442
5443bool nsIFrame::MovingCaretToEventPointAllowedIfSecondaryButtonEvent(
5444 const nsFrameSelection& aFrameSelection,
5445 WidgetMouseEvent& aSecondaryButtonEvent,
5446 const nsIContent& aContentAtEventPoint, int32_t aOffsetAtEventPoint) const {
5447 MOZ_ASSERT(aSecondaryButtonEvent.mButton == MouseButton::eSecondary)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aSecondaryButtonEvent.mButton == MouseButton::eSecondary
)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aSecondaryButtonEvent.mButton == MouseButton::eSecondary
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"aSecondaryButtonEvent.mButton == MouseButton::eSecondary", "./../../../layout/generic/nsIFrame.cpp"
, 5447); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aSecondaryButtonEvent.mButton == MouseButton::eSecondary"
")"); do { MOZ_CrashSequence(__null, 5447); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5448
5449 if (NS_WARN_IF(aOffsetAtEventPoint < 0)NS_warn_if_impl(aOffsetAtEventPoint < 0, "aOffsetAtEventPoint < 0"
, "./../../../layout/generic/nsIFrame.cpp", 5449)
) {
5450 return false;
5451 }
5452
5453 const bool contentIsEditable = aContentAtEventPoint.IsEditable();
5454 const TextControlElement* const contentAsTextControl =
5455 TextControlElement::FromNodeOrNull(
5456 aContentAtEventPoint.IsTextControlElement()
5457 ? &aContentAtEventPoint
5458 : aContentAtEventPoint.GetClosestNativeAnonymousSubtreeRoot());
5459 const Selection& selection = aFrameSelection.NormalSelection();
5460 const bool selectionIsCollapsed =
5461 selection.AreNormalAndCrossShadowBoundaryRangesCollapsed();
5462 // If right click in a selection range, we should not collapse
5463 // selection.
5464 if (!selectionIsCollapsed && nsContentUtils::IsPointInSelection(
5465 selection, aContentAtEventPoint,
5466 static_cast<uint32_t>(aOffsetAtEventPoint),
5467 true /* aAllowCrossShadowBoundary */)) {
5468 return false;
5469 }
5470 const bool wantToPreventMoveCaret =
5471 StaticPrefs::
5472 ui_mouse_right_click_move_caret_stop_if_in_focused_editable_node() &&
5473 selectionIsCollapsed && (contentIsEditable || contentAsTextControl);
5474 const bool wantToPreventCollapseSelection =
5475 StaticPrefs::
5476 ui_mouse_right_click_collapse_selection_stop_if_non_collapsed_selection() &&
5477 !selectionIsCollapsed;
5478 if (wantToPreventMoveCaret || wantToPreventCollapseSelection) {
5479 // If currently selection is limited in an editing host, we should not
5480 // collapse selection nor move caret if the clicked point is in the
5481 // ancestor limiter. Otherwise, this mouse click moves focus from the
5482 // editing host to different one or blur the editing host. In this case,
5483 // we need to update selection because keeping current selection in the
5484 // editing host looks like it's not blurred.
5485 // FIXME: If the active editing host is the document element, editor
5486 // does not set ancestor limiter properly. Fix it in the editor side.
5487 if (nsIContent* ancestorLimiter = selection.GetAncestorLimiter()) {
5488 MOZ_ASSERT(ancestorLimiter->IsEditable())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ancestorLimiter->IsEditable())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(ancestorLimiter->IsEditable
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("ancestorLimiter->IsEditable()", "./../../../layout/generic/nsIFrame.cpp"
, 5488); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ancestorLimiter->IsEditable()"
")"); do { MOZ_CrashSequence(__null, 5488); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5489 return !aContentAtEventPoint.IsInclusiveDescendantOf(ancestorLimiter);
5490 }
5491 }
5492 // If selection is editable and `stop_if_in_focused_editable_node` pref is
5493 // set to true, user does not want to move caret to right click place if
5494 // clicked in the focused text control element.
5495 if (wantToPreventMoveCaret && contentAsTextControl &&
5496 contentAsTextControl == nsFocusManager::GetFocusedElementStatic()) {
5497 return false;
5498 }
5499 // If currently selection is not limited in an editing host, we should
5500 // collapse selection only when this click moves focus to an editing
5501 // host because we need to update selection in this case.
5502 if (wantToPreventCollapseSelection && !contentIsEditable) {
5503 return false;
5504 }
5505
5506 return !StaticPrefs::
5507 ui_mouse_right_click_collapse_selection_stop_if_non_editable_node() ||
5508 // The user does not want to collapse selection into non-editable
5509 // content by a right button click.
5510 contentIsEditable ||
5511 // Treat clicking in a text control as always clicked on editable
5512 // content because we want a hack only for clicking in normal text
5513 // nodes which is outside any editing hosts.
5514 contentAsTextControl;
5515}
5516
5517nsresult nsIFrame::SelectByTypeAtPoint(const nsPoint& aPoint,
5518 nsSelectionAmount aBeginAmountType,
5519 nsSelectionAmount aEndAmountType,
5520 uint32_t aSelectFlags) {
5521 // No point in selecting if selection is turned off
5522 if (!ShouldHandleSelectionMovementEvents()) {
5523 return NS_OK;
5524 }
5525
5526 ContentOffsets offsets = GetContentOffsetsFromPoint(
5527 aPoint, SKIP_HIDDEN | IGNORE_NATIVE_ANONYMOUS_SUBTREE);
5528 if (!offsets.content) {
5529 return NS_ERROR_FAILURE;
5530 }
5531
5532 FrameAndOffset frameAndOffset = SelectionMovementUtils::GetFrameForNodeOffset(
5533 offsets.content, offsets.offset, offsets.associate);
5534 if (!frameAndOffset) {
5535 return NS_ERROR_FAILURE;
5536 }
5537 return frameAndOffset->PeekBackwardAndForwardForSelection(
5538 aBeginAmountType, aEndAmountType,
5539 static_cast<int32_t>(frameAndOffset.mOffsetInFrameContent),
5540 aBeginAmountType != eSelectWord, aSelectFlags);
5541}
5542
5543/**
5544 * Multiple Mouse Press -- line or paragraph selection -- for the frame.
5545 * Wouldn't it be nice if this didn't have to be hardwired into Frame code?
5546 */
5547NS_IMETHODIMPnsresult
5548nsIFrame::HandleMultiplePress(nsPresContext* aPresContext,
5549 WidgetGUIEvent* aEvent,
5550 nsEventStatus* aEventStatus, bool aControlHeld) {
5551 NS_ENSURE_ARG_POINTER(aEvent)do { if ((__builtin_expect(!!(!(aEvent)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "aEvent" ") failed", nullptr
, "./../../../layout/generic/nsIFrame.cpp", 5551); return NS_ERROR_INVALID_POINTER
; } } while (false)
;
5552 NS_ENSURE_ARG_POINTER(aEventStatus)do { if ((__builtin_expect(!!(!(aEventStatus)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "aEventStatus" ") failed"
, nullptr, "./../../../layout/generic/nsIFrame.cpp", 5552); return
NS_ERROR_INVALID_POINTER; } } while (false)
;
5553
5554 if (nsEventStatus_eConsumeNoDefault == *aEventStatus ||
5555 !ShouldHandleSelectionMovementEvents()) {
5556 return NS_OK;
5557 }
5558
5559 // Find out whether we're doing line or paragraph selection.
5560 // If browser.triple_click_selects_paragraph is true, triple-click selects
5561 // paragraph. Otherwise, triple-click selects line, and quadruple-click
5562 // selects paragraph (on platforms that support quadruple-click).
5563 nsSelectionAmount beginAmount, endAmount;
5564 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
5565 if (!mouseEvent) {
5566 return NS_OK;
5567 }
5568
5569 if (mouseEvent->mClickCount == 4) {
5570 beginAmount = endAmount = eSelectParagraph;
5571 } else if (mouseEvent->mClickCount == 3) {
5572 if (Preferences::GetBool("browser.triple_click_selects_paragraph")) {
5573 beginAmount = endAmount = eSelectParagraph;
5574 } else {
5575 beginAmount = eSelectBeginLine;
5576 endAmount = eSelectEndLine;
5577 }
5578 } else if (mouseEvent->mClickCount == 2) {
5579 // We only want inline frames; PeekBackwardAndForward dislikes blocks
5580 beginAmount = endAmount = eSelectWord;
5581 } else {
5582 return NS_OK;
5583 }
5584
5585 nsPoint relPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(
5586 mouseEvent, RelativeTo{this});
5587 return SelectByTypeAtPoint(relPoint, beginAmount, endAmount,
5588 (aControlHeld ? SELECT_ACCUMULATE : 0));
5589}
5590
5591nsresult nsIFrame::PeekBackwardAndForwardForSelection(
5592 nsSelectionAmount aAmountBack, nsSelectionAmount aAmountForward,
5593 int32_t aStartPos, bool aJumpLines, uint32_t aSelectFlags) {
5594 nsIFrame* baseFrame = this;
5595 int32_t baseOffset = aStartPos;
5596 nsresult rv;
5597
5598 PeekOffsetOptions peekOffsetOptions{PeekOffsetOption::StopAtScroller};
5599 if (aJumpLines) {
5600 peekOffsetOptions += PeekOffsetOption::JumpLines;
5601 }
5602
5603 Element* const ancestorLimiter = [&]() -> Element* {
5604 const nsFrameSelection* const frameSelection = GetConstFrameSelection();
5605 return frameSelection
5606 ? frameSelection
5607 ->GetAncestorLimiterOrIndependentSelectionRootElement()
5608 : nullptr;
5609 }();
5610
5611 if (aAmountBack == eSelectWord) {
5612 // To avoid selecting the previous word when at start of word,
5613 // first move one character forward.
5614 PeekOffsetStruct pos(eSelectCharacter, eDirNext, aStartPos, nsPoint(0, 0),
5615 peekOffsetOptions, eDefaultBehavior, ancestorLimiter);
5616 rv = PeekOffset(&pos);
5617 if (NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)))) {
5618 baseFrame = pos.mResultFrame;
5619 baseOffset = pos.mContentOffset;
5620 }
5621 }
5622
5623 // Search backward for a boundary.
5624 PeekOffsetStruct startpos(aAmountBack, eDirPrevious, baseOffset,
5625 nsPoint(0, 0), peekOffsetOptions, eDefaultBehavior,
5626 ancestorLimiter);
5627 rv = baseFrame->PeekOffset(&startpos);
5628 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
5629 return rv;
5630 }
5631
5632 // If the backward search stayed within the same frame, search forward from
5633 // that position for the end boundary; but if it crossed out to a sibling or
5634 // ancestor, start from the original position.
5635 if (startpos.mResultFrame == baseFrame) {
5636 baseOffset = startpos.mContentOffset;
5637 } else {
5638 baseFrame = this;
5639 baseOffset = aStartPos;
5640 }
5641
5642 PeekOffsetStruct endpos(aAmountForward, eDirNext, baseOffset, nsPoint(0, 0),
5643 peekOffsetOptions, eDefaultBehavior, ancestorLimiter);
5644 rv = baseFrame->PeekOffset(&endpos);
5645 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
5646 return rv;
5647 }
5648
5649 // Keep frameSelection alive.
5650 RefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
5651
5652 const nsFrameSelection::FocusMode focusMode =
5653 (aSelectFlags & SELECT_ACCUMULATE)
5654 ? nsFrameSelection::FocusMode::kMultiRangeSelection
5655 : nsFrameSelection::FocusMode::kCollapseToNewPoint;
5656 rv = frameSelection->HandleClick(
5657 MOZ_KnownLive(startpos.mResultContent)(startpos.mResultContent) /* bug 1636889 */,
5658 startpos.mContentOffset, startpos.mContentOffset, focusMode,
5659 CaretAssociationHint::After);
5660 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
5661 return rv;
5662 }
5663
5664 rv = frameSelection->HandleClick(
5665 MOZ_KnownLive(endpos.mResultContent)(endpos.mResultContent) /* bug 1636889 */,
5666 endpos.mContentOffset, endpos.mContentOffset,
5667 nsFrameSelection::FocusMode::kExtendSelection,
5668 CaretAssociationHint::Before);
5669 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
5670 return rv;
5671 }
5672 if (aAmountBack == eSelectWord) {
5673 frameSelection->SetClickSelectionType(ClickSelectionType::Double);
5674 } else if (aAmountBack == eSelectParagraph) {
5675 frameSelection->SetClickSelectionType(ClickSelectionType::Triple);
5676 }
5677
5678 // maintain selection
5679 return frameSelection->MaintainSelection(aAmountBack);
5680}
5681
5682NS_IMETHODIMPnsresult nsIFrame::HandleDrag(nsPresContext* aPresContext,
5683 WidgetGUIEvent* aEvent,
5684 nsEventStatus* aEventStatus) {
5685 MOZ_ASSERT(aEvent->mClass == eMouseEventClass,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aEvent->mClass == eMouseEventClass)>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(aEvent->mClass == eMouseEventClass))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aEvent->mClass == eMouseEventClass"
" (" "HandleDrag can only handle mouse event" ")", "./../../../layout/generic/nsIFrame.cpp"
, 5686); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aEvent->mClass == eMouseEventClass"
") (" "HandleDrag can only handle mouse event" ")"); do { MOZ_CrashSequence
(__null, 5686); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
5686 "HandleDrag can only handle mouse event")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aEvent->mClass == eMouseEventClass)>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(aEvent->mClass == eMouseEventClass))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aEvent->mClass == eMouseEventClass"
" (" "HandleDrag can only handle mouse event" ")", "./../../../layout/generic/nsIFrame.cpp"
, 5686); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aEvent->mClass == eMouseEventClass"
") (" "HandleDrag can only handle mouse event" ")"); do { MOZ_CrashSequence
(__null, 5686); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5687
5688 NS_ENSURE_ARG_POINTER(aEventStatus)do { if ((__builtin_expect(!!(!(aEventStatus)), 0))) { NS_DebugBreak
(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "aEventStatus" ") failed"
, nullptr, "./../../../layout/generic/nsIFrame.cpp", 5688); return
NS_ERROR_INVALID_POINTER; } } while (false)
;
5689
5690 RefPtr<nsFrameSelection> frameselection = GetFrameSelection();
5691 if (!frameselection) {
5692 return NS_OK;
5693 }
5694
5695 bool mouseDown = frameselection->GetDragState();
5696 if (!mouseDown) {
5697 return NS_OK;
5698 }
5699
5700 nsIFrame* scrollbar =
5701 nsLayoutUtils::GetClosestFrameOfType(this, LayoutFrameType::Scrollbar);
5702 if (!scrollbar && !ShouldHandleSelectionMovementEvents()) {
5703 // XXX Do we really need to exclude non-selectable content here?
5704 // GetContentOffsetsFromPoint can handle it just fine, although some
5705 // other stuff might not like it.
5706 return NS_OK;
5707 }
5708
5709 frameselection->StopAutoScrollTimer();
5710
5711 // Check if we are dragging in a table cell
5712 nsCOMPtr<nsIContent> parentContent;
5713 int32_t contentOffset;
5714 TableSelectionMode target;
5715 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
5716 mozilla::PresShell* presShell = aPresContext->PresShell();
5717 nsresult result;
5718 result = GetDataForTableSelection(frameselection, presShell, mouseEvent,
5719 getter_AddRefs(parentContent),
5720 &contentOffset, &target);
5721
5722 AutoWeakFrame weakThis = this;
5723 if (NS_SUCCEEDED(result)((bool)(__builtin_expect(!!(!NS_FAILED_impl(result)), 1))) && parentContent) {
5724 result = frameselection->HandleTableSelection(parentContent, contentOffset,
5725 target, mouseEvent);
5726 if (NS_WARN_IF(NS_FAILED(result))NS_warn_if_impl(((bool)(__builtin_expect(!!(NS_FAILED_impl(result
)), 0))), "NS_FAILED(result)", "./../../../layout/generic/nsIFrame.cpp"
, 5726)
) {
5727 return result;
5728 }
5729 } else {
5730 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent,
5731 RelativeTo{this});
5732 frameselection->HandleDrag(this, pt);
5733 }
5734
5735 // The frameselection object notifies selection listeners synchronously above
5736 // which might have killed us.
5737 if (!weakThis.IsAlive()) {
5738 return NS_OK;
5739 }
5740
5741 // Get the nearest scroll container frame.
5742 ScrollContainerFrame* scrollContainerFrame =
5743 nsLayoutUtils::GetNearestScrollContainerFrame(
5744 this, nsLayoutUtils::SCROLLABLE_SAME_DOC |
5745 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
5746
5747 if (scrollContainerFrame) {
5748 nsIFrame* capturingFrame = scrollContainerFrame->GetScrolledFrame();
5749 if (capturingFrame) {
5750 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(
5751 mouseEvent, RelativeTo{capturingFrame});
5752 frameselection->StartAutoScrollTimer(capturingFrame, pt, 30);
5753 }
5754 }
5755
5756 return NS_OK;
5757}
5758
5759/**
5760 * This static method handles part of the nsIFrame::HandleRelease in a way
5761 * which doesn't rely on the nsIFrame object to stay alive.
5762 */
5763MOZ_CAN_RUN_SCRIPT_BOUNDARY static nsresult HandleFrameSelection(
5764 nsFrameSelection* aFrameSelection, nsIFrame::ContentOffsets& aOffsets,
5765 bool aHandleTableSel, int32_t aContentOffsetForTableSel,
5766 TableSelectionMode aTargetForTableSel,
5767 nsIContent* aParentContentForTableSel, WidgetGUIEvent* aEvent,
5768 const nsEventStatus* aEventStatus) {
5769 if (!aFrameSelection) {
5770 return NS_OK;
5771 }
5772
5773 nsresult rv = NS_OK;
5774
5775 if (nsEventStatus_eConsumeNoDefault != *aEventStatus) {
5776 if (!aHandleTableSel) {
5777 if (!aOffsets.content || !aFrameSelection->HasDelayedCaretData()) {
5778 return NS_ERROR_FAILURE;
5779 }
5780
5781 // We are doing this to simulate what we would have done on HandlePress.
5782 // We didn't do it there to give the user an opportunity to drag
5783 // the text, but since they didn't drag, we want to place the
5784 // caret.
5785 // However, we'll use the mouse position from the release, since:
5786 // * it's easier
5787 // * that's the normal click position to use (although really, in
5788 // the normal case, small movements that don't count as a drag
5789 // can do selection)
5790 aFrameSelection->SetDragState(true);
5791
5792 const nsFrameSelection::FocusMode focusMode =
5793 aFrameSelection->IsShiftDownInDelayedCaretData()
5794 ? nsFrameSelection::FocusMode::kExtendSelection
5795 : nsFrameSelection::FocusMode::kCollapseToNewPoint;
5796 rv = aFrameSelection->HandleClick(
5797 MOZ_KnownLive(aOffsets.content)(aOffsets.content) /* bug 1636889 */,
5798 aOffsets.StartOffset(), aOffsets.EndOffset(), focusMode,
5799 aOffsets.associate);
5800 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
5801 return rv;
5802 }
5803 } else if (aParentContentForTableSel) {
5804 aFrameSelection->SetDragState(false);
5805 rv = aFrameSelection->HandleTableSelection(
5806 aParentContentForTableSel, aContentOffsetForTableSel,
5807 aTargetForTableSel, aEvent->AsMouseEvent());
5808 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
5809 return rv;
5810 }
5811 }
5812 aFrameSelection->SetDelayedCaretData(nullptr);
5813 }
5814
5815 aFrameSelection->SetDragState(false);
5816 aFrameSelection->StopAutoScrollTimer();
5817
5818 return NS_OK;
5819}
5820
5821NS_IMETHODIMPnsresult nsIFrame::HandleRelease(nsPresContext* aPresContext,
5822 WidgetGUIEvent* aEvent,
5823 nsEventStatus* aEventStatus) {
5824 if (aEvent->mClass != eMouseEventClass) {
5825 return NS_OK;
5826 }
5827
5828 nsIFrame* activeFrame = GetActiveSelectionFrame(aPresContext, this);
5829
5830 nsCOMPtr<nsIContent> captureContent = PresShell::GetCapturingContent();
5831
5832 const bool selectionOff = !ShouldHandleSelectionMovementEvents();
5833
5834 RefPtr<nsFrameSelection> frameselection;
5835 ContentOffsets offsets;
5836 nsCOMPtr<nsIContent> parentContent;
5837 int32_t contentOffsetForTableSel = 0;
5838 TableSelectionMode targetForTableSel = TableSelectionMode::None;
5839 bool handleTableSelection = true;
5840
5841 if (!selectionOff) {
5842 frameselection = GetFrameSelection();
5843 if (nsEventStatus_eConsumeNoDefault != *aEventStatus && frameselection) {
5844 // Check if the frameselection recorded the mouse going down.
5845 // If not, the user must have clicked in a part of the selection.
5846 // Place the caret before continuing!
5847
5848 if (frameselection->MouseDownRecorded()) {
5849 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(
5850 aEvent, RelativeTo{this});
5851 offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN);
5852 handleTableSelection = false;
5853 } else {
5854 GetDataForTableSelection(frameselection, PresShell(),
5855 aEvent->AsMouseEvent(),
5856 getter_AddRefs(parentContent),
5857 &contentOffsetForTableSel, &targetForTableSel);
5858 }
5859 }
5860 }
5861
5862 // We might be capturing in some other document and the event just happened to
5863 // trickle down here. Make sure that document's frame selection is notified.
5864 // Note, this may cause the current nsIFrame object to be deleted, bug 336592.
5865 RefPtr<nsFrameSelection> frameSelection;
5866 if (activeFrame != this &&
5867 activeFrame->ShouldHandleSelectionMovementEvents()) {
5868 frameSelection = activeFrame->GetFrameSelection();
5869 }
5870
5871 // Also check the selection of the capturing content which might be in a
5872 // different document.
5873 if (!frameSelection && captureContent) {
5874 if (Document* doc = captureContent->GetComposedDoc()) {
5875 mozilla::PresShell* capturingPresShell = doc->GetPresShell();
5876 if (capturingPresShell &&
5877 capturingPresShell != PresContext()->GetPresShell()) {
5878 frameSelection = capturingPresShell->FrameSelection();
5879 }
5880 }
5881 }
5882
5883 if (frameSelection) {
5884 AutoWeakFrame wf(this);
5885 frameSelection->SetDragState(false);
5886 frameSelection->StopAutoScrollTimer();
5887 if (wf.IsAlive()) {
5888 ScrollContainerFrame* scrollContainerFrame =
5889 nsLayoutUtils::GetNearestScrollContainerFrame(
5890 this, nsLayoutUtils::SCROLLABLE_SAME_DOC |
5891 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
5892 if (scrollContainerFrame) {
5893 // Perform any additional scrolling needed to maintain CSS snap point
5894 // requirements when autoscrolling is over.
5895 scrollContainerFrame->ScrollSnap();
5896 }
5897 }
5898 }
5899
5900 // Do not call any methods of the current object after this point!!!
5901 // The object is perhaps dead!
5902
5903 return selectionOff ? NS_OK
5904 : HandleFrameSelection(
5905 frameselection, offsets, handleTableSelection,
5906 contentOffsetForTableSel, targetForTableSel,
5907 parentContent, aEvent, aEventStatus);
5908}
5909
5910struct MOZ_STACK_CLASS FrameContentRange {
5911 FrameContentRange(nsIContent* aContent, int32_t aStart, int32_t aEnd)
5912 : content(aContent), start(aStart), end(aEnd) {}
5913 nsCOMPtr<nsIContent> content;
5914 int32_t start;
5915 int32_t end;
5916};
5917
5918static bool IsRelevantBlockFrame(const nsIFrame* aFrame) {
5919 if (!aFrame->IsBlockOutside() && !aFrame->IsTableCaption()) {
5920 return false;
5921 }
5922 if (aFrame->GetContent() &&
5923 aFrame->GetContent()->IsInNativeAnonymousSubtree() &&
5924 !aFrame->GetContent()->HasBeenInUAWidget()) {
5925 // This helps skipping things like scrollbar parts.
5926 return false;
5927 }
5928 auto pseudoType = aFrame->Style()->GetPseudoType();
5929 if (PseudoStyle::IsAnonBox(pseudoType)) {
5930 // Table cell contents should be considered block boundaries for this
5931 // purpose.
5932 return pseudoType == PseudoStyleType::MozCellContent;
5933 }
5934 return true;
5935}
5936
5937// Retrieve the content offsets of a frame
5938static FrameContentRange GetRangeForFrame(const nsIFrame* aFrame,
5939 bool includeReplaced = false) {
5940 nsIContent* content = aFrame->GetContent();
5941 if (!content) {
5942 NS_WARNING("Frame has no content")NS_DebugBreak(NS_DEBUG_WARNING, "Frame has no content", nullptr
, "./../../../layout/generic/nsIFrame.cpp", 5942)
;
5943 return FrameContentRange(nullptr, -1, -1);
5944 }
5945
5946 LayoutFrameType type = aFrame->Type();
5947 if (type == LayoutFrameType::Text) {
5948 auto [offset, offsetEnd] = aFrame->GetOffsets();
5949 return FrameContentRange(content, offset, offsetEnd);
5950 }
5951
5952 if (type == LayoutFrameType::Br) {
5953 nsIContent* parent = content->GetParent();
5954 const int32_t beginOffset = parent->ComputeIndexOf_Deprecated(content);
5955 return FrameContentRange(parent, beginOffset, beginOffset);
5956 }
5957
5958 while (content->IsRootOfNativeAnonymousSubtree()) {
5959 content = content->GetParent();
5960 }
5961
5962 if (!includeReplaced && aFrame->IsReplaced()) {
5963 if (auto* parent = content->GetParent()) {
5964 // TODO(emilio): Revise this in presence of Shadow DOM / display:
5965 // contents, it's likely that we don't want to just walk the light tree,
5966 // and we need to change the representation of FrameContentRange.
5967 Maybe<uint32_t> index = parent->ComputeIndexOf(content);
5968 MOZ_ASSERT(index.isSome())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(index.isSome())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(index.isSome()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("index.isSome()"
, "./../../../layout/generic/nsIFrame.cpp", 5968); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "index.isSome()" ")"); do { MOZ_CrashSequence
(__null, 5968); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5969 return FrameContentRange(parent, static_cast<int32_t>(*index),
5970 static_cast<int32_t>(*index + 1));
5971 }
5972 }
5973
5974 return FrameContentRange(content, 0, content->GetChildCount());
5975}
5976
5977// The FrameTarget represents the closest frame to a point that can be selected
5978// The frame is the frame represented, frameEdge says whether one end of the
5979// frame is the result (in which case different handling is needed), and
5980// afterFrame says which end is represented if frameEdge is true
5981struct FrameTarget {
5982 explicit operator bool() const { return !!frame; }
5983
5984 nsIFrame* frame = nullptr;
5985 bool frameEdge = false;
5986 bool afterFrame = false;
5987};
5988
5989// See function implementation for information
5990static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame,
5991 const nsPoint& aPoint,
5992 uint32_t aFlags);
5993
5994static bool SelfIsSelectable(nsIFrame* aFrame, nsIFrame* aParentFrame,
5995 uint32_t aFlags) {
5996 // We should not move selection into a native anonymous subtree when handling
5997 // selection outside it.
5998 if ((aFlags & nsIFrame::IGNORE_NATIVE_ANONYMOUS_SUBTREE) &&
5999 aParentFrame->GetClosestNativeAnonymousSubtreeRoot() !=
6000 aFrame->GetClosestNativeAnonymousSubtreeRoot()) {
6001 return false;
6002 }
6003 if ((aFlags & nsIFrame::SKIP_HIDDEN) &&
6004 (!aFrame->StyleVisibility()->IsVisible() ||
6005 aFrame->IsHiddenByContentVisibilityOnAnyAncestor(
6006 nsIFrame::IncludeContentVisibility::Hidden))) {
6007 return false;
6008 }
6009 if (aFrame->IsGeneratedContentFrame()) {
6010 return false;
6011 }
6012 // XXX Why do we ignore `aFlag & nsIFrame::IGNORE_SELECTION_STYLE`?
6013 if (aFrame->Style()->UserSelect() == StyleUserSelect::None) {
6014 return false;
6015 }
6016 if (aFrame->IsEmpty() &&
6017 (!aFrame->IsTextFrame() || !aFrame->ContentIsEditable())) {
6018 // FIXME(emilio): Historically we haven't treated empty frames as
6019 // selectable, but also we had special-cases so that editable empty text
6020 // frames returned false from IsEmpty(). Sort this out (probably by
6021 // removing the IsEmpty() condition altogether).
6022 return false;
6023 }
6024 return true;
6025}
6026
6027static bool FrameContentCanHaveParentSelectionRange(nsIFrame* aFrame) {
6028 // If we are only near (not directly over) then don't traverse frames with
6029 // independent selection (e.g. text controls, see bug 268497). Note that this
6030 // prevents any of the users of this method from entering form controls.
6031 // XXX We might want some way to allow using the up-arrow to go into a form
6032 // control, but the focus didn't work right anyway; it'd probably be enough
6033 // if the left and right arrows could enter textboxes (which I don't believe
6034 // they can at the moment)
6035 if (aFrame->IsTextInputFrame()) {
6036 return false;
6037 }
6038 return !aFrame->IsGeneratedContentFrame();
6039}
6040
6041static bool SelectionDescendToKids(nsIFrame* aFrame) {
6042 if (!FrameContentCanHaveParentSelectionRange(aFrame)) {
6043 return false;
6044 }
6045 auto style = aFrame->Style()->UserSelect();
6046 return style != StyleUserSelect::All && style != StyleUserSelect::None;
6047}
6048
6049static FrameTarget GetSelectionClosestFrameForChild(nsIFrame* aChild,
6050 const nsPoint& aPoint,
6051 uint32_t aFlags) {
6052 nsIFrame* parent = aChild->GetParent();
6053 if (SelectionDescendToKids(aChild)) {
6054 nsPoint pt = aPoint - aChild->GetOffsetTo(parent);
6055 return GetSelectionClosestFrame(aChild, pt, aFlags);
6056 }
6057 return FrameTarget{aChild, false, false};
6058}
6059
6060// When the cursor needs to be at the beginning of a block, it shouldn't be
6061// before the first child. A click on a block whose first child is a block
6062// should put the cursor in the child. The cursor shouldn't be between the
6063// blocks, because that's not where it's expected.
6064// Note that this method is guaranteed to succeed.
6065static FrameTarget DrillDownToSelectionFrame(nsIFrame* aFrame, bool aEndFrame,
6066 uint32_t aFlags) {
6067 if (SelectionDescendToKids(aFrame)) {
6068 nsIFrame* result = nullptr;
6069 nsIFrame* frame = aFrame->PrincipalChildList().FirstChild();
6070 if (!aEndFrame) {
6071 while (frame && !SelfIsSelectable(frame, aFrame, aFlags)) {
6072 frame = frame->GetNextSibling();
6073 }
6074 if (frame) {
6075 result = frame;
6076 }
6077 } else {
6078 // Because the frame tree is singly linked, to find the last frame,
6079 // we have to iterate through all the frames
6080 // XXX I have a feeling this could be slow for long blocks, although
6081 // I can't find any slowdowns
6082 while (frame) {
6083 if (SelfIsSelectable(frame, aFrame, aFlags)) {
6084 result = frame;
6085 }
6086 frame = frame->GetNextSibling();
6087 }
6088 }
6089 if (result) {
6090 return DrillDownToSelectionFrame(result, aEndFrame, aFlags);
6091 }
6092 }
6093 // If the current frame has no targetable children, target the current frame
6094 return FrameTarget{aFrame, true, aEndFrame};
6095}
6096
6097// This method finds the closest valid FrameTarget on a given line; if there is
6098// no valid FrameTarget on the line, it returns a null FrameTarget
6099static FrameTarget GetSelectionClosestFrameForLine(
6100 nsBlockFrame* aParent, nsBlockFrame::LineIterator aLine,
6101 const nsPoint& aPoint, uint32_t aFlags) {
6102 // Account for end of lines (any iterator from the block is valid)
6103 if (aLine == aParent->LinesEnd()) {
6104 return DrillDownToSelectionFrame(aParent, true, aFlags);
6105 }
6106 nsIFrame* closestFromIStart = nullptr;
6107 nsIFrame* closestFromIEnd = nullptr;
6108 nscoord closestIStart = aLine->IStart(), closestIEnd = aLine->IEnd();
6109 WritingMode wm = aLine->mWritingMode;
6110 LogicalPoint pt(wm, aPoint, aLine->mContainerSize);
6111 bool canSkipBr = false;
6112 bool lastFrameWasEditable = false;
6113 for (nsIFrame* frame : aLine->ChildFrames()) {
6114 // Skip brFrames. Can only skip if the line contains at least
6115 // one selectable and non-empty frame before. Also, avoid skipping brs if
6116 // the previous thing had a different editableness than us, since then we
6117 // may end up not being able to select after it if the br is the last thing
6118 // on the line.
6119 if (!SelfIsSelectable(frame, aParent, aFlags) ||
6120 (canSkipBr && frame->IsBrFrame() &&
6121 lastFrameWasEditable == frame->GetContent()->IsEditable())) {
6122 continue;
6123 }
6124 canSkipBr = true;
6125 lastFrameWasEditable =
6126 frame->GetContent() && frame->GetContent()->IsEditable();
6127 LogicalRect frameRect =
6128 LogicalRect(wm, frame->GetRect(), aLine->mContainerSize);
6129 if (pt.I(wm) >= frameRect.IStart(wm)) {
6130 if (pt.I(wm) < frameRect.IEnd(wm)) {
6131 return GetSelectionClosestFrameForChild(frame, aPoint, aFlags);
6132 }
6133 if (frameRect.IEnd(wm) >= closestIStart) {
6134 closestFromIStart = frame;
6135 closestIStart = frameRect.IEnd(wm);
6136 }
6137 } else {
6138 if (frameRect.IStart(wm) <= closestIEnd) {
6139 closestFromIEnd = frame;
6140 closestIEnd = frameRect.IStart(wm);
6141 }
6142 }
6143 }
6144 if (!closestFromIStart && !closestFromIEnd) {
6145 // We should only get here if there are no selectable frames on a line
6146 // XXX Do we need more elaborate handling here?
6147 return FrameTarget();
6148 }
6149 if (closestFromIStart &&
6150 (!closestFromIEnd ||
6151 (abs(pt.I(wm) - closestIStart) <= abs(pt.I(wm) - closestIEnd)))) {
6152 return GetSelectionClosestFrameForChild(closestFromIStart, aPoint, aFlags);
6153 }
6154 return GetSelectionClosestFrameForChild(closestFromIEnd, aPoint, aFlags);
6155}
6156
6157// This method is for the special handling we do for block frames; they're
6158// special because they represent paragraphs and because they are organized
6159// into lines, which have bounds that are not stored elsewhere in the
6160// frame tree. Returns a null FrameTarget for frames which are not
6161// blocks or blocks with no lines except editable one.
6162static FrameTarget GetSelectionClosestFrameForBlock(nsIFrame* aFrame,
6163 const nsPoint& aPoint,
6164 uint32_t aFlags) {
6165 nsBlockFrame* bf = do_QueryFrame(aFrame);
6166 if (!bf) {
6167 return FrameTarget();
6168 }
6169
6170 // This code searches for the correct line
6171 nsBlockFrame::LineIterator end = bf->LinesEnd();
6172 nsBlockFrame::LineIterator curLine = bf->LinesBegin();
6173 nsBlockFrame::LineIterator closestLine = end;
6174
6175 if (curLine != end) {
6176 // Convert aPoint into a LogicalPoint in the writing-mode of this block
6177 WritingMode wm = curLine->mWritingMode;
6178 LogicalPoint pt(wm, aPoint, curLine->mContainerSize);
6179 do {
6180 // Check to see if our point lies within the line's block-direction bounds
6181 nscoord BCoord = pt.B(wm) - curLine->BStart();
6182 nscoord BSize = curLine->BSize();
6183 if (BCoord >= 0 && BCoord < BSize) {
6184 closestLine = curLine;
6185 break; // We found the line; stop looking
6186 }
6187 if (BCoord < 0) {
6188 break;
6189 }
6190 ++curLine;
6191 } while (curLine != end);
6192
6193 if (closestLine == end) {
6194 nsBlockFrame::LineIterator prevLine = curLine.prev();
6195 nsBlockFrame::LineIterator nextLine = curLine;
6196 // Avoid empty lines
6197 while (nextLine != end && nextLine->IsEmpty()) {
6198 ++nextLine;
6199 }
6200 while (prevLine != end && prevLine->IsEmpty()) {
6201 --prevLine;
6202 }
6203
6204 // This hidden pref dictates whether a point above or below all lines
6205 // comes up with a line or the beginning or end of the frame; 0 on
6206 // Windows, 1 on other platforms by default at the writing of this code
6207 int32_t dragOutOfFrame =
6208 Preferences::GetInt("browser.drag_out_of_frame_style");
6209
6210 if (prevLine == end) {
6211 if (dragOutOfFrame == 1 || nextLine == end) {
6212 return DrillDownToSelectionFrame(aFrame, false, aFlags);
6213 }
6214 closestLine = nextLine;
6215 } else if (nextLine == end) {
6216 if (dragOutOfFrame == 1) {
6217 return DrillDownToSelectionFrame(aFrame, true, aFlags);
6218 }
6219 closestLine = prevLine;
6220 } else { // Figure out which line is closer
6221 if (pt.B(wm) - prevLine->BEnd() < nextLine->BStart() - pt.B(wm)) {
6222 closestLine = prevLine;
6223 } else {
6224 closestLine = nextLine;
6225 }
6226 }
6227 }
6228 }
6229
6230 do {
6231 if (auto target =
6232 GetSelectionClosestFrameForLine(bf, closestLine, aPoint, aFlags)) {
6233 return target;
6234 }
6235 ++closestLine;
6236 } while (closestLine != end);
6237
6238 // Fall back to just targeting the last targetable place
6239 return DrillDownToSelectionFrame(aFrame, true, aFlags);
6240}
6241
6242// Use frame edge for grid, flex, table, and non-editable images. Choose the
6243// edge based on the point position past the frame rect. If past the middle,
6244// caret should be at end, otherwise at start. This behavior matches Blink.
6245//
6246// TODO(emilio): Can we use this code path for all other frames? We only get
6247// there when we didn't find selectable children... Editable images need _not_
6248// to use the frame edge tho, see below.
6249static bool UseFrameEdge(nsIFrame* aFrame) {
6250 if (aFrame->IsFlexOrGridContainer() || aFrame->IsTableFrame()) {
6251 return true;
6252 }
6253 // FIXME(bug 713387): The text frame check here shouldn't be needed.
6254 if (aFrame->IsReplaced() && !aFrame->IsTextFrame() &&
6255 !aFrame->GetContent()->IsEditable()) {
6256 // Editable replaced elements are a special-case because editing relies
6257 // on clicking on an editable image selecting it, for it to show resizers.
6258 return true;
6259 }
6260 return false;
6261}
6262
6263static FrameTarget LastResortFrameTargetForFrame(nsIFrame* aFrame,
6264 const nsPoint& aPoint) {
6265 if (!UseFrameEdge(aFrame)) {
6266 return {aFrame, false, false};
6267 }
6268 const auto& rect = aFrame->GetRectRelativeToSelf();
6269 nscoord reference;
6270 nscoord middle;
6271 if (aFrame->GetWritingMode().IsVertical()) {
6272 reference = aPoint.y;
6273 middle = rect.Height() / 2;
6274 } else {
6275 reference = aPoint.x;
6276 middle = rect.Width() / 2;
6277 }
6278 const bool afterFrame = reference > middle;
6279 return {aFrame, true, afterFrame};
6280}
6281
6282// GetSelectionClosestFrame is the helper function that calculates the closest
6283// frame to the given point.
6284// It doesn't completely account for offset styles, so needs to be used in
6285// restricted environments.
6286// Cannot handle overlapping frames correctly, so it should receive the output
6287// of GetFrameForPoint
6288// Guaranteed to return a valid FrameTarget.
6289// aPoint is relative to aFrame.
6290static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame,
6291 const nsPoint& aPoint,
6292 uint32_t aFlags) {
6293 // Handle blocks; if the frame isn't a block, the method fails
6294 if (auto target = GetSelectionClosestFrameForBlock(aFrame, aPoint, aFlags)) {
6295 return target;
6296 }
6297
6298 if (aFlags & nsIFrame::IGNORE_NATIVE_ANONYMOUS_SUBTREE &&
6299 !FrameContentCanHaveParentSelectionRange(aFrame)) {
6300 return LastResortFrameTargetForFrame(aFrame, aPoint);
6301 }
6302
6303 if (nsIFrame* kid = aFrame->PrincipalChildList().FirstChild()) {
6304 // Go through all the child frames to find the closest one
6305 nsIFrame::FrameWithDistance closest = {nullptr, nscoord_MAX, nscoord_MAX};
6306 for (; kid; kid = kid->GetNextSibling()) {
6307 if (!SelfIsSelectable(kid, aFrame, aFlags)) {
6308 continue;
6309 }
6310
6311 kid->FindCloserFrameForSelection(aPoint, &closest);
6312 }
6313 if (closest.mFrame) {
6314 if (closest.mFrame->IsInSVGTextSubtree()) {
6315 return FrameTarget{closest.mFrame, false, false};
6316 }
6317 return GetSelectionClosestFrameForChild(closest.mFrame, aPoint, aFlags);
6318 }
6319 }
6320
6321 return LastResortFrameTargetForFrame(aFrame, aPoint);
6322}
6323
6324static nsIFrame::ContentOffsets OffsetsForSingleFrame(nsIFrame* aFrame,
6325 const nsPoint& aPoint) {
6326 nsIFrame::ContentOffsets offsets;
6327 FrameContentRange range = GetRangeForFrame(aFrame);
6328 offsets.content = range.content;
6329 // If there are continuations (meaning it's not one rectangle), this is the
6330 // best this function can do
6331 if (aFrame->GetNextContinuation() || aFrame->GetPrevContinuation()) {
6332 offsets.offset = range.start;
6333 offsets.secondaryOffset = range.end;
6334 offsets.associate = CaretAssociationHint::After;
6335 return offsets;
6336 }
6337
6338 // Figure out whether the offsets should be over, after, or before the frame
6339 nsRect rect(nsPoint(0, 0), aFrame->GetSize());
6340
6341 bool isBlock = !aFrame->StyleDisplay()->IsInlineFlow();
6342 bool isRtl = (aFrame->StyleVisibility()->mDirection == StyleDirection::Rtl);
6343 if ((isBlock && rect.y < aPoint.y) ||
6344 (!isBlock && ((isRtl && rect.x + rect.width / 2 > aPoint.x) ||
6345 (!isRtl && rect.x + rect.width / 2 < aPoint.x)))) {
6346 offsets.offset = range.end;
6347 if (rect.Contains(aPoint)) {
6348 offsets.secondaryOffset = range.start;
6349 } else {
6350 offsets.secondaryOffset = range.end;
6351 }
6352 } else {
6353 offsets.offset = range.start;
6354 if (rect.Contains(aPoint)) {
6355 offsets.secondaryOffset = range.end;
6356 } else {
6357 offsets.secondaryOffset = range.start;
6358 }
6359 }
6360 offsets.associate = offsets.offset == range.start
6361 ? CaretAssociationHint::After
6362 : CaretAssociationHint::Before;
6363 return offsets;
6364}
6365
6366static nsIFrame* AdjustFrameForSelectionStyles(nsIFrame* aFrame) {
6367 nsIFrame* adjustedFrame = aFrame;
6368 for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
6369 // These are the conditions that make all children not able to handle
6370 // a cursor.
6371 auto userSelect = frame->Style()->UserSelect();
6372 if (userSelect != StyleUserSelect::Auto &&
6373 userSelect != StyleUserSelect::All) {
6374 break;
6375 }
6376 if (userSelect == StyleUserSelect::All ||
6377 frame->IsGeneratedContentFrame()) {
6378 adjustedFrame = frame;
6379 }
6380 }
6381 return adjustedFrame;
6382}
6383
6384nsIFrame::ContentOffsets nsIFrame::GetContentOffsetsFromPoint(
6385 const nsPoint& aPoint, uint32_t aFlags) {
6386 nsIFrame* adjustedFrame;
6387 if (aFlags & IGNORE_SELECTION_STYLE) {
6388 adjustedFrame = this;
6389 } else {
6390 // This section of code deals with special selection styles. Note that
6391 // -moz-all exists, even though it doesn't need to be explicitly handled.
6392 //
6393 // The offset is forced not to end up in generated content; content offsets
6394 // cannot represent content outside of the document's content tree.
6395
6396 adjustedFrame = AdjustFrameForSelectionStyles(this);
6397
6398 // `user-select: all` needs special handling, because clicking on it should
6399 // lead to the whole frame being selected.
6400 if (adjustedFrame->Style()->UserSelect() == StyleUserSelect::All) {
6401 nsPoint adjustedPoint = aPoint + GetOffsetTo(adjustedFrame);
6402 return OffsetsForSingleFrame(adjustedFrame, adjustedPoint);
6403 }
6404
6405 // For other cases, try to find a closest frame starting from the parent of
6406 // the unselectable frame
6407 if (adjustedFrame != this) {
6408 adjustedFrame = adjustedFrame->GetParent();
6409 }
6410 }
6411
6412 nsPoint adjustedPoint = aPoint + GetOffsetTo(adjustedFrame);
6413
6414 FrameTarget closest =
6415 GetSelectionClosestFrame(adjustedFrame, adjustedPoint, aFlags);
6416
6417 // If the correct offset is at one end of a frame, use offset-based
6418 // calculation method
6419 if (closest.frameEdge) {
6420 ContentOffsets offsets;
6421 bool includeReplaced = (aFlags & INCLUDE_REPLACED) != 0;
6422 FrameContentRange range = GetRangeForFrame(closest.frame, includeReplaced);
6423 offsets.content = range.content;
6424 if (closest.afterFrame) {
6425 offsets.offset = range.end;
6426 } else {
6427 offsets.offset = range.start;
6428 }
6429 offsets.secondaryOffset = offsets.offset;
6430 offsets.associate = offsets.offset == range.start
6431 ? CaretAssociationHint::After
6432 : CaretAssociationHint::Before;
6433 return offsets;
6434 }
6435
6436 nsPoint pt;
6437 if (closest.frame != this) {
6438 if (closest.frame->IsInSVGTextSubtree()) {
6439 pt = nsLayoutUtils::TransformAncestorPointToFrame(
6440 RelativeTo{closest.frame}, aPoint, RelativeTo{this});
6441 } else {
6442 pt = aPoint - closest.frame->GetOffsetTo(this);
6443 }
6444 } else {
6445 pt = aPoint;
6446 }
6447 return closest.frame->CalcContentOffsetsFromFramePoint(pt);
6448
6449 // XXX should I add some kind of offset standardization?
6450 // consider <b>xxxxx</b><i>zzzzz</i>; should any click between the last
6451 // x and first z put the cursor in the same logical position in addition
6452 // to the same visual position?
6453}
6454
6455nsIFrame::ContentOffsets nsIFrame::CalcContentOffsetsFromFramePoint(
6456 const nsPoint& aPoint) {
6457 return OffsetsForSingleFrame(this, aPoint);
6458}
6459
6460bool nsIFrame::AssociateImage(const StyleImage& aImage) {
6461 imgRequestProxy* req = aImage.GetImageRequest();
6462 if (!req) {
6463 return false;
6464 }
6465
6466 PresContext()->Document()->EnsureStyleImageLoader().AssociateRequestToFrame(
6467 req, this);
6468 return true;
6469}
6470
6471void nsIFrame::DisassociateImage(const StyleImage& aImage) {
6472 imgRequestProxy* req = aImage.GetImageRequest();
6473 if (!req) {
6474 return;
6475 }
6476
6477 PresContext()
6478 ->Document()
6479 ->EnsureStyleImageLoader()
6480 .DisassociateRequestFromFrame(req, this);
6481}
6482
6483const ComputedStyle* nsIFrame::UsedStyleForImages() const {
6484 if (IsCanvasFrame()) {
6485 // XXXdholbert Maybe we should use FindCanvasBackground here (instead of
6486 // FindBackground), since we're inside an IsCanvasFrame check? Though then
6487 // we'd also have to copypaste or abstract-away the multi-part root-frame
6488 // lookup that the canvas-flavored API requires.
6489 return nsCSSRendering::FindBackground(this);
6490 }
6491 return Style();
6492}
6493
6494// The touch-action CSS property applies to: all elements except: non-replaced
6495// inline elements, table rows, row groups, table columns, and column groups.
6496StyleTouchAction nsIFrame::UsedTouchAction() const {
6497 if (IsLineParticipant()) {
6498 return StyleTouchAction::AUTO;
6499 }
6500 auto& disp = *StyleDisplay();
6501 if (disp.IsInternalTableStyleExceptCell()) {
6502 return StyleTouchAction::AUTO;
6503 }
6504 return disp.mTouchAction;
6505}
6506
6507nsIFrame::Cursor nsIFrame::GetCursor(const nsPoint&) {
6508 StyleCursorKind kind = StyleUI()->Cursor().keyword;
6509 if (kind == StyleCursorKind::Auto) {
6510 // If this is editable, I-beam cursor is better for most elements.
6511 kind = (mContent && mContent->IsEditable()) ? StyleCursorKind::Text
6512 : StyleCursorKind::Default;
6513 }
6514 if (kind == StyleCursorKind::Text && GetWritingMode().IsVertical()) {
6515 // Per CSS UI spec, UA may treat value 'text' as
6516 // 'vertical-text' for vertical text.
6517 kind = StyleCursorKind::VerticalText;
6518 }
6519
6520 return Cursor{kind, AllowCustomCursorImage::Yes};
6521}
6522
6523// Resize and incremental reflow
6524
6525/* virtual */
6526void nsIFrame::MarkIntrinsicISizesDirty() {
6527 // If we're a flex item, clear our flex-item-specific cached measurements
6528 // (which likely depended on our now-stale intrinsic isize).
6529 if (IsFlexItem()) {
6530 nsFlexContainerFrame::MarkCachedFlexMeasurementsDirty(this);
6531 }
6532
6533 if (IsGridItem()) {
6534 nsGridContainerFrame::MarkCachedGridMeasurementsDirty(this);
6535 }
6536
6537 if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT)) {
6538 nsFontInflationData::MarkFontInflationDataTextDirty(this);
6539 }
6540}
6541
6542void nsIFrame::MarkSubtreeDirty() {
6543 if (HasAnyStateBits(NS_FRAME_IS_DIRTY)) {
6544 return;
6545 }
6546 // Unconditionally mark given frame dirty.
6547 AddStateBits(NS_FRAME_IS_DIRTY);
6548
6549 // Mark all descendants dirty, unless:
6550 // - Already dirty.
6551 // - TableColGroup
6552 AutoTArray<nsIFrame*, 32> stack;
6553 for (const auto& childLists : ChildLists()) {
6554 for (nsIFrame* kid : childLists.mList) {
6555 stack.AppendElement(kid);
6556 }
6557 }
6558 while (!stack.IsEmpty()) {
6559 nsIFrame* f = stack.PopLastElement();
6560 if (f->HasAnyStateBits(NS_FRAME_IS_DIRTY) || f->IsTableColGroupFrame()) {
6561 continue;
6562 }
6563
6564 f->AddStateBits(NS_FRAME_IS_DIRTY);
6565
6566 for (const auto& childLists : f->ChildLists()) {
6567 for (nsIFrame* kid : childLists.mList) {
6568 stack.AppendElement(kid);
6569 }
6570 }
6571 }
6572}
6573
6574void nsIFrame::MarkPrincipalChildrenDirty() {
6575 for (nsIFrame* childFrame : PrincipalChildList()) {
6576 childFrame->MarkSubtreeDirty();
6577 }
6578}
6579
6580/* virtual */
6581void nsIFrame::AddInlineMinISize(const IntrinsicSizeInput& aInput,
6582 InlineMinISizeData* aData) {
6583 // Note: we are one of the children that mPercentageBasisForChildren was
6584 // prepared for (i.e. our parent frame prepares the percentage basis for us,
6585 // not for our own children). Hence it's fine that we're resolving our
6586 // percentages sizes against this basis in IntrinsicForContainer().
6587 nscoord isize = nsLayoutUtils::IntrinsicForContainer(
6588 aInput.mContext, this, IntrinsicISizeType::MinISize,
6589 aInput.mPercentageBasisForChildren);
6590 aData->DefaultAddInlineMinISize(this, isize);
6591}
6592
6593/* virtual */
6594void nsIFrame::AddInlinePrefISize(const IntrinsicSizeInput& aInput,
6595 nsIFrame::InlinePrefISizeData* aData) {
6596 // Note: we are one of the children that mPercentageBasisForChildren was
6597 // prepared for (i.e. our parent frame prepares the percentage basis for us,
6598 // not for our own children). Hence it's fine that we're resolving our
6599 // percentages sizes against this basis in IntrinsicForContainer().
6600 nscoord isize = nsLayoutUtils::IntrinsicForContainer(
6601 aInput.mContext, this, IntrinsicISizeType::PrefISize,
6602 aInput.mPercentageBasisForChildren);
6603 aData->DefaultAddInlinePrefISize(isize);
6604}
6605
6606void nsIFrame::InlineMinISizeData::DefaultAddInlineMinISize(nsIFrame* aFrame,
6607 nscoord aISize,
6608 bool aAllowBreak) {
6609 auto parent = aFrame->GetParent();
6610 MOZ_ASSERT(parent, "Must have a parent if we get here!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(parent)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(parent))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("parent" " (" "Must have a parent if we get here!"
")", "./../../../layout/generic/nsIFrame.cpp", 6610); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "parent" ") (" "Must have a parent if we get here!"
")"); do { MOZ_CrashSequence(__null, 6610); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6611 const bool mayBreak = aAllowBreak && !aFrame->CanContinueTextRun() &&
6612 !parent->Style()->ShouldSuppressLineBreak() &&
6613 parent->StyleText()->WhiteSpaceCanWrap(parent);
6614 if (mayBreak) {
6615 OptionallyBreak();
6616 }
6617 mTrailingWhitespace = 0;
6618 mSkipWhitespace = false;
6619 mCurrentLine += aISize;
6620 mAtStartOfLine = false;
6621 if (mayBreak) {
6622 OptionallyBreak();
6623 }
6624}
6625
6626void nsIFrame::InlinePrefISizeData::DefaultAddInlinePrefISize(nscoord aISize) {
6627 mCurrentLine = NSCoordSaturatingAdd(mCurrentLine, aISize);
6628 mTrailingWhitespace = 0;
6629 mSkipWhitespace = false;
6630 mLineIsEmpty = false;
6631}
6632
6633void nsIFrame::InlineMinISizeData::ForceBreak() {
6634 mCurrentLine -= mTrailingWhitespace;
6635 mPrevLines = std::max(mPrevLines, mCurrentLine);
6636 mCurrentLine = mTrailingWhitespace = 0;
6637
6638 for (const FloatInfo& floatInfo : mFloats) {
6639 mPrevLines = std::max(floatInfo.ISize(), mPrevLines);
6640 }
6641 mFloats.Clear();
6642 mSkipWhitespace = true;
6643}
6644
6645void nsIFrame::InlineMinISizeData::OptionallyBreak(nscoord aHyphenWidth) {
6646 // If we can fit more content into a smaller width by staying on this
6647 // line (because we're still at a negative offset due to negative
6648 // text-indent or negative margin), don't break. Otherwise, do the
6649 // same as ForceBreak. it doesn't really matter when we accumulate
6650 // floats.
6651 if (mCurrentLine + aHyphenWidth < 0 || mAtStartOfLine) {
6652 return;
6653 }
6654 mCurrentLine += aHyphenWidth;
6655 ForceBreak();
6656}
6657
6658void nsIFrame::InlinePrefISizeData::ForceBreak(UsedClear aClearType) {
6659 // If this force break is not clearing any float, we can leave all the
6660 // floats to the next force break.
6661 if (!mFloats.IsEmpty() && aClearType != UsedClear::None) {
6662 // Preferred isize accumulated for floats that have already
6663 // been cleared past
6664 nscoord floatsDone = 0;
6665 // Preferred isize accumulated for floats that have not yet
6666 // been cleared past
6667 nscoord floatsCurLeft = 0, floatsCurRight = 0;
6668
6669 for (const FloatInfo& floatInfo : mFloats) {
6670 const nsStyleDisplay* floatDisp = floatInfo.Frame()->StyleDisplay();
6671 auto cbWM = floatInfo.Frame()->GetParent()->GetWritingMode();
6672 UsedClear clearType = floatDisp->UsedClear(cbWM);
6673 if (clearType == UsedClear::Left || clearType == UsedClear::Right ||
6674 clearType == UsedClear::Both) {
6675 nscoord floatsCur = NSCoordSaturatingAdd(floatsCurLeft, floatsCurRight);
6676 if (floatsCur > floatsDone) {
6677 floatsDone = floatsCur;
6678 }
6679 if (clearType != UsedClear::Right) {
6680 floatsCurLeft = 0;
6681 }
6682 if (clearType != UsedClear::Left) {
6683 floatsCurRight = 0;
6684 }
6685 }
6686
6687 UsedFloat floatStyle = floatDisp->UsedFloat(cbWM);
6688 nscoord& floatsCur =
6689 floatStyle == UsedFloat::Left ? floatsCurLeft : floatsCurRight;
6690 nscoord floatISize = floatInfo.ISize();
6691 // Negative-width floats don't change the available space so they
6692 // shouldn't change our intrinsic line isize either.
6693 floatsCur = NSCoordSaturatingAdd(floatsCur, std::max(0, floatISize));
6694 }
6695
6696 nscoord floatsCur = NSCoordSaturatingAdd(floatsCurLeft, floatsCurRight);
6697 if (floatsCur > floatsDone) {
6698 floatsDone = floatsCur;
6699 }
6700
6701 mCurrentLine = NSCoordSaturatingAdd(mCurrentLine, floatsDone);
6702
6703 if (aClearType == UsedClear::Both) {
6704 mFloats.Clear();
6705 } else {
6706 // If the break type does not clear all floats, it means there may
6707 // be some floats whose isize should contribute to the intrinsic
6708 // isize of the next line. The code here scans the current mFloats
6709 // and keeps floats which are not cleared by this break. Note that
6710 // floats may be cleared directly or indirectly. See below.
6711 nsTArray<FloatInfo> newFloats;
6712 MOZ_ASSERT(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aClearType == UsedClear::Left || aClearType == UsedClear
::Right)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aClearType == UsedClear::Left || aClearType == UsedClear
::Right))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aClearType == UsedClear::Left || aClearType == UsedClear::Right"
" (" "Other values should have been handled in other branches"
")", "./../../../layout/generic/nsIFrame.cpp", 6714); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aClearType == UsedClear::Left || aClearType == UsedClear::Right"
") (" "Other values should have been handled in other branches"
")"); do { MOZ_CrashSequence(__null, 6714); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
6713 aClearType == UsedClear::Left || aClearType == UsedClear::Right,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aClearType == UsedClear::Left || aClearType == UsedClear
::Right)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aClearType == UsedClear::Left || aClearType == UsedClear
::Right))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aClearType == UsedClear::Left || aClearType == UsedClear::Right"
" (" "Other values should have been handled in other branches"
")", "./../../../layout/generic/nsIFrame.cpp", 6714); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aClearType == UsedClear::Left || aClearType == UsedClear::Right"
") (" "Other values should have been handled in other branches"
")"); do { MOZ_CrashSequence(__null, 6714); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
6714 "Other values should have been handled in other branches")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aClearType == UsedClear::Left || aClearType == UsedClear
::Right)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aClearType == UsedClear::Left || aClearType == UsedClear
::Right))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aClearType == UsedClear::Left || aClearType == UsedClear::Right"
" (" "Other values should have been handled in other branches"
")", "./../../../layout/generic/nsIFrame.cpp", 6714); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aClearType == UsedClear::Left || aClearType == UsedClear::Right"
") (" "Other values should have been handled in other branches"
")"); do { MOZ_CrashSequence(__null, 6714); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6715 UsedFloat clearFloatType =
6716 aClearType == UsedClear::Left ? UsedFloat::Left : UsedFloat::Right;
6717 // Iterate the array in reverse so that we can stop when there are
6718 // no longer any floats we need to keep. See below.
6719 for (FloatInfo& floatInfo : Reversed(mFloats)) {
6720 const nsStyleDisplay* floatDisp = floatInfo.Frame()->StyleDisplay();
6721 auto cbWM = floatInfo.Frame()->GetParent()->GetWritingMode();
6722 if (floatDisp->UsedFloat(cbWM) != clearFloatType) {
6723 newFloats.AppendElement(floatInfo);
6724 } else {
6725 // This is a float on the side that this break directly clears
6726 // which means we're not keeping it in mFloats. However, if
6727 // this float clears floats on the opposite side (via a value
6728 // of either 'both' or one of 'left'/'right'), any remaining
6729 // (earlier) floats on that side would be indirectly cleared
6730 // as well. Thus, we should break out of this loop and stop
6731 // considering earlier floats to be kept in mFloats.
6732 UsedClear clearType = floatDisp->UsedClear(cbWM);
6733 if (clearType != aClearType && clearType != UsedClear::None) {
6734 break;
6735 }
6736 }
6737 }
6738 newFloats.Reverse();
6739 mFloats = std::move(newFloats);
6740 }
6741 }
6742
6743 mCurrentLine =
6744 NSCoordSaturatingSubtract(mCurrentLine, mTrailingWhitespace, nscoord_MAX);
6745 mPrevLines = std::max(mPrevLines, mCurrentLine);
6746 mCurrentLine = mTrailingWhitespace = 0;
6747 mSkipWhitespace = true;
6748 mLineIsEmpty = true;
6749}
6750
6751static nscoord ResolvePadding(const LengthPercentage& aStyle,
6752 nscoord aPercentageBasis) {
6753 return nsLayoutUtils::ResolveToLength<true>(aStyle, aPercentageBasis);
6754}
6755
6756static nscoord ResolveMargin(const AnchorResolvedMargin& aStyle,
6757 nscoord aPercentageBasis) {
6758 if (!aStyle->IsLengthPercentage()) {
6759 return nscoord(0);
6760 }
6761 return nsLayoutUtils::ResolveToLength<false>(aStyle->AsLengthPercentage(),
6762 aPercentageBasis);
6763}
6764static nsIFrame::IntrinsicSizeOffsetData IntrinsicSizeOffsets(
6765 nsIFrame* aFrame, nscoord aPercentageBasis, bool aForISize) {
6766 nsIFrame::IntrinsicSizeOffsetData result;
6767 WritingMode wm = aFrame->GetWritingMode();
6768 bool verticalAxis = aForISize == wm.IsVertical();
6769 const auto* styleMargin = aFrame->StyleMargin();
6770 const auto anchorResolutionParams = AnchorPosResolutionParams::From(aFrame);
6771 if (verticalAxis) {
6772 result.margin +=
6773 ResolveMargin(styleMargin->GetMargin(eSideTop, anchorResolutionParams),
6774 aPercentageBasis);
6775 result.margin += ResolveMargin(
6776 styleMargin->GetMargin(eSideBottom, anchorResolutionParams),
6777 aPercentageBasis);
6778 } else {
6779 result.margin +=
6780 ResolveMargin(styleMargin->GetMargin(eSideLeft, anchorResolutionParams),
6781 aPercentageBasis);
6782 result.margin += ResolveMargin(
6783 styleMargin->GetMargin(eSideRight, anchorResolutionParams),
6784 aPercentageBasis);
6785 }
6786
6787 const auto& padding = aFrame->StylePadding()->mPadding;
6788 if (verticalAxis) {
6789 result.padding += ResolvePadding(padding.Get(eSideTop), aPercentageBasis);
6790 result.padding +=
6791 ResolvePadding(padding.Get(eSideBottom), aPercentageBasis);
6792 } else {
6793 result.padding += ResolvePadding(padding.Get(eSideLeft), aPercentageBasis);
6794 result.padding += ResolvePadding(padding.Get(eSideRight), aPercentageBasis);
6795 }
6796
6797 const nsStyleBorder* styleBorder = aFrame->StyleBorder();
6798 if (verticalAxis) {
6799 result.border += styleBorder->GetComputedBorderWidth(eSideTop);
6800 result.border += styleBorder->GetComputedBorderWidth(eSideBottom);
6801 } else {
6802 result.border += styleBorder->GetComputedBorderWidth(eSideLeft);
6803 result.border += styleBorder->GetComputedBorderWidth(eSideRight);
6804 }
6805
6806 const nsStyleDisplay* disp = aFrame->StyleDisplay();
6807 if (aFrame->IsThemed(disp)) {
6808 nsPresContext* presContext = aFrame->PresContext();
6809
6810 LayoutDeviceIntMargin border = presContext->Theme()->GetWidgetBorder(
6811 presContext->DeviceContext(), aFrame, disp->EffectiveAppearance());
6812 result.border = presContext->DevPixelsToAppUnits(
6813 verticalAxis ? border.TopBottom() : border.LeftRight());
6814
6815 LayoutDeviceIntMargin padding;
6816 if (presContext->Theme()->GetWidgetPadding(
6817 presContext->DeviceContext(), aFrame, disp->EffectiveAppearance(),
6818 &padding)) {
6819 result.padding = presContext->DevPixelsToAppUnits(
6820 verticalAxis ? padding.TopBottom() : padding.LeftRight());
6821 }
6822 }
6823 return result;
6824}
6825
6826/* virtual */ nsIFrame::IntrinsicSizeOffsetData nsIFrame::IntrinsicISizeOffsets(
6827 nscoord aPercentageBasis) {
6828 return IntrinsicSizeOffsets(this, aPercentageBasis, true);
6829}
6830
6831nsIFrame::IntrinsicSizeOffsetData nsIFrame::IntrinsicBSizeOffsets(
6832 nscoord aPercentageBasis) {
6833 return IntrinsicSizeOffsets(this, aPercentageBasis, false);
6834}
6835
6836/* virtual */
6837IntrinsicSize nsIFrame::GetIntrinsicSize() {
6838 // Defaults to no intrinsic size.
6839 return IntrinsicSize();
6840}
6841
6842AspectRatio nsIFrame::GetAspectRatio() const {
6843 // Per spec, 'aspect-ratio' property applies to all elements except inline
6844 // boxes and internal ruby or table boxes.
6845 // https://drafts.csswg.org/css-sizing-4/#aspect-ratio
6846 // For those frame types that don't support aspect-ratio, they must not have
6847 // the natural ratio, so this early return is fine.
6848 if (!SupportsAspectRatio()) {
6849 return AspectRatio();
6850 }
6851
6852 const StyleAspectRatio& ar = StylePosition()->mAspectRatio;
6853 const bool hasRatio = ar.HasRatio();
6854 // If aspect-ratio is zero or infinite, it's a degenerate ratio and behaves
6855 // as auto.
6856 // https://drafts.csswg.org/css-sizing-4/#valdef-aspect-ratio-ratio
6857 // Non-auto. Return the preferred aspect ratio from the aspect-ratio style.
6858 if (hasRatio && !ar.auto_) {
6859 if (auto ratio = ar.ratio.AsRatio().ToLayoutRatio(UseBoxSizing::Yes)) {
6860 return ratio;
6861 }
6862 }
6863
6864 if (auto intrinsicRatio = GetIntrinsicRatio()) {
6865 return intrinsicRatio;
6866 }
6867
6868 if (hasRatio) {
6869 // This returns a 0 ratio for degenerate rations, like the auto case below.
6870 return ar.ratio.AsRatio().ToLayoutRatio(UseBoxSizing::No);
6871 }
6872
6873 return AspectRatio();
6874}
6875
6876/* virtual */
6877AspectRatio nsIFrame::GetIntrinsicRatio() const { return AspectRatio(); }
6878
6879static bool ShouldApplyAutomaticMinimumOnInlineAxis(
6880 WritingMode aWM, bool aIsScrollableOverflow,
6881 const AnchorPosResolutionParams& aParams,
6882 const nsStylePosition* aPosition) {
6883 // Apply the automatic minimum size for aspect ratio:
6884 // Note: The replaced elements shouldn't be here, so we only check the scroll
6885 // container.
6886 // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-minimum
6887 return !aIsScrollableOverflow && aPosition->MinISize(aWM, aParams)->IsAuto();
6888}
6889
6890/* virtual */
6891nsIFrame::SizeComputationResult nsIFrame::ComputeSize(
6892 const SizeComputationInput& aSizingInput, WritingMode aWM,
6893 const LogicalSize& aCBSize, nscoord aAvailableISize,
6894 const LogicalSize& aMargin, const LogicalSize& aBorderPadding,
6895 const StyleSizeOverrides& aSizeOverrides, ComputeSizeFlags aFlags) {
6896 MOZ_ASSERT(!GetIntrinsicRatio(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!GetIntrinsicRatio())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!GetIntrinsicRatio()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("!GetIntrinsicRatio()"
" (" "Please override this method and call " "nsContainerFrame::ComputeSizeWithIntrinsicDimensions instead."
")", "./../../../layout/generic/nsIFrame.cpp", 6898); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!GetIntrinsicRatio()" ") (" "Please override this method and call "
"nsContainerFrame::ComputeSizeWithIntrinsicDimensions instead."
")"); do { MOZ_CrashSequence(__null, 6898); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
6897 "Please override this method and call "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!GetIntrinsicRatio())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!GetIntrinsicRatio()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("!GetIntrinsicRatio()"
" (" "Please override this method and call " "nsContainerFrame::ComputeSizeWithIntrinsicDimensions instead."
")", "./../../../layout/generic/nsIFrame.cpp", 6898); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!GetIntrinsicRatio()" ") (" "Please override this method and call "
"nsContainerFrame::ComputeSizeWithIntrinsicDimensions instead."
")"); do { MOZ_CrashSequence(__null, 6898); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
6898 "nsContainerFrame::ComputeSizeWithIntrinsicDimensions instead.")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!GetIntrinsicRatio())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!GetIntrinsicRatio()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("!GetIntrinsicRatio()"
" (" "Please override this method and call " "nsContainerFrame::ComputeSizeWithIntrinsicDimensions instead."
")", "./../../../layout/generic/nsIFrame.cpp", 6898); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!GetIntrinsicRatio()" ") (" "Please override this method and call "
"nsContainerFrame::ComputeSizeWithIntrinsicDimensions instead."
")"); do { MOZ_CrashSequence(__null, 6898); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6899 LogicalSize result =
6900 ComputeAutoSize(aSizingInput, aWM, aCBSize, aAvailableISize, aMargin,
6901 aBorderPadding, aSizeOverrides, aFlags);
6902 const nsStylePosition* stylePos = StylePosition();
6903 const nsStyleDisplay* disp = StyleDisplay();
6904 const auto anchorResolutionParams = AnchorPosResolutionParams::From(
6905 this, aSizingInput.mAnchorPosResolutionCache);
6906 auto aspectRatioUsage = AspectRatioUsage::None;
6907
6908 const auto boxSizingAdjust = stylePos->mBoxSizing == StyleBoxSizing::BorderBox
6909 ? aBorderPadding
6910 : LogicalSize(aWM);
6911 nscoord boxSizingToMarginEdgeISize = aMargin.ISize(aWM) +
6912 aBorderPadding.ISize(aWM) -
6913 boxSizingAdjust.ISize(aWM);
6914
6915 const auto& aspectRatio = aSizeOverrides.mAspectRatio
6916 ? *aSizeOverrides.mAspectRatio
6917 : GetAspectRatio();
6918 const auto styleISize =
6919 aSizeOverrides.mStyleISize
6920 ? AnchorResolvedSizeHelper::Overridden(*aSizeOverrides.mStyleISize)
6921 : stylePos->ISize(aWM, anchorResolutionParams);
6922 // For bsize, we consider overrides *and then* we resolve 'stretch' to a
6923 // nscoord value, for convenience (so that we can assume that either
6924 // isAutoBSize is true, or styleBSize is of type LengthPercentage()).
6925 const auto styleBSize = [&] {
6926 auto styleBSizeConsideringOverrides =
6927 aSizeOverrides.mStyleBSize
6928 ? AnchorResolvedSizeHelper::Overridden(*aSizeOverrides.mStyleBSize)
6929 : stylePos->BSize(aWM, anchorResolutionParams);
6930 if (styleBSizeConsideringOverrides->BehavesLikeStretchOnBlockAxis() &&
6931 aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
6932 // We've got a 'stretch' BSize; resolve it to a length:
6933 nscoord stretchBSize = nsLayoutUtils::ComputeStretchBSize(
6934 aCBSize.BSize(aWM), aMargin.BSize(aWM), aBorderPadding.BSize(aWM),
6935 stylePos->mBoxSizing);
6936 // Note(dshin): This allocates.
6937 return AnchorResolvedSizeHelper::LengthPercentage(
6938 LengthPercentage::FromAppUnits(stretchBSize));
6939 }
6940 return styleBSizeConsideringOverrides;
6941 }();
6942
6943 auto parentFrame = GetParent();
6944 auto alignCB = parentFrame;
6945 bool isGridItem = IsGridItem();
6946 const bool isSubgrid = IsSubgrid();
6947 if (parentFrame && parentFrame->IsTableWrapperFrame() && IsTableFrame()) {
6948 // An inner table frame is sized as a grid item if its table wrapper is,
6949 // because they actually have the same CB (the wrapper's CB).
6950 // @see ReflowInput::InitCBReflowInput
6951 auto tableWrapper = GetParent();
6952 auto grandParent = tableWrapper->GetParent();
6953 isGridItem = grandParent->IsGridContainerFrame() &&
6954 !tableWrapper->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
6955 if (isGridItem) {
6956 // When resolving justify/align-self below, we want to use the grid
6957 // container's justify/align-items value and WritingMode.
6958 alignCB = grandParent;
6959 }
6960 }
6961
6962 // flexItemMainAxis is set if this frame is a flex item in a modern flexbox
6963 // layout. It indicates which logical axis (in this frame's own WM)
6964 // corresponds to its flex container's main axis.
6965 Maybe<LogicalAxis> flexItemMainAxis;
6966 if (IsFlexItem() && !parentFrame->HasAnyStateBits(
6967 NS_STATE_FLEX_IS_EMULATING_LEGACY_WEBKIT_BOX)) {
6968 flexItemMainAxis = Some(nsFlexContainerFrame::IsItemInlineAxisMainAxis(this)
6969 ? LogicalAxis::Inline
6970 : LogicalAxis::Block);
6971 }
6972
6973 const bool isAutoISize = styleISize->IsAuto();
6974 const bool isAutoBSize =
6975 nsLayoutUtils::IsAutoBSize(*styleBSize, aCBSize.BSize(aWM));
6976
6977 MOZ_ASSERT(isAutoBSize || styleBSize->IsLengthPercentage(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(isAutoBSize || styleBSize->IsLengthPercentage())>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(isAutoBSize || styleBSize->IsLengthPercentage()))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("isAutoBSize || styleBSize->IsLengthPercentage()"
" (" "We should have resolved away any non-'auto'-like flavors "
"of styleBSize into a LengthPercentage. (If this fails, we "
"might run afoul of some AsLengthPercentage() call below.)" ")"
, "./../../../layout/generic/nsIFrame.cpp", 6980); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "isAutoBSize || styleBSize->IsLengthPercentage()"
") (" "We should have resolved away any non-'auto'-like flavors "
"of styleBSize into a LengthPercentage. (If this fails, we "
"might run afoul of some AsLengthPercentage() call below.)" ")"
); do { MOZ_CrashSequence(__null, 6980); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
6978 "We should have resolved away any non-'auto'-like flavors "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(isAutoBSize || styleBSize->IsLengthPercentage())>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(isAutoBSize || styleBSize->IsLengthPercentage()))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("isAutoBSize || styleBSize->IsLengthPercentage()"
" (" "We should have resolved away any non-'auto'-like flavors "
"of styleBSize into a LengthPercentage. (If this fails, we "
"might run afoul of some AsLengthPercentage() call below.)" ")"
, "./../../../layout/generic/nsIFrame.cpp", 6980); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "isAutoBSize || styleBSize->IsLengthPercentage()"
") (" "We should have resolved away any non-'auto'-like flavors "
"of styleBSize into a LengthPercentage. (If this fails, we "
"might run afoul of some AsLengthPercentage() call below.)" ")"
); do { MOZ_CrashSequence(__null, 6980); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
6979 "of styleBSize into a LengthPercentage. (If this fails, we "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(isAutoBSize || styleBSize->IsLengthPercentage())>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(isAutoBSize || styleBSize->IsLengthPercentage()))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("isAutoBSize || styleBSize->IsLengthPercentage()"
" (" "We should have resolved away any non-'auto'-like flavors "
"of styleBSize into a LengthPercentage. (If this fails, we "
"might run afoul of some AsLengthPercentage() call below.)" ")"
, "./../../../layout/generic/nsIFrame.cpp", 6980); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "isAutoBSize || styleBSize->IsLengthPercentage()"
") (" "We should have resolved away any non-'auto'-like flavors "
"of styleBSize into a LengthPercentage. (If this fails, we "
"might run afoul of some AsLengthPercentage() call below.)" ")"
); do { MOZ_CrashSequence(__null, 6980); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
6980 "might run afoul of some AsLengthPercentage() call below.)")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(isAutoBSize || styleBSize->IsLengthPercentage())>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(isAutoBSize || styleBSize->IsLengthPercentage()))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("isAutoBSize || styleBSize->IsLengthPercentage()"
" (" "We should have resolved away any non-'auto'-like flavors "
"of styleBSize into a LengthPercentage. (If this fails, we "
"might run afoul of some AsLengthPercentage() call below.)" ")"
, "./../../../layout/generic/nsIFrame.cpp", 6980); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "isAutoBSize || styleBSize->IsLengthPercentage()"
") (" "We should have resolved away any non-'auto'-like flavors "
"of styleBSize into a LengthPercentage. (If this fails, we "
"might run afoul of some AsLengthPercentage() call below.)" ")"
); do { MOZ_CrashSequence(__null, 6980); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6981
6982 // Compute inline-axis size
6983 const bool isSubgriddedInInlineAxis =
6984 isSubgrid && static_cast<nsGridContainerFrame*>(this)->IsColSubgrid();
6985
6986 // Per https://drafts.csswg.org/css-grid/#subgrid-box-alignment, if we are
6987 // subgridded in the inline-axis, ignore our style inline-size, and stretch to
6988 // fill the CB.
6989 const bool shouldComputeISize = !isAutoISize && !isSubgriddedInInlineAxis;
6990 if (shouldComputeISize) {
6991 auto iSizeResult =
6992 ComputeISizeValue(aSizingInput.mRenderingContext, aWM, aCBSize,
6993 boxSizingAdjust, boxSizingToMarginEdgeISize,
6994 *styleISize, *styleBSize, aspectRatio, aFlags);
6995 result.ISize(aWM) = iSizeResult.mISize;
6996 aspectRatioUsage = iSizeResult.mAspectRatioUsage;
6997 } else if (MOZ_UNLIKELY(isGridItem)(__builtin_expect(!!(isGridItem), 0)) && !IsTrueOverflowContainer()) {
6998 // 'auto' inline-size for grid-level box - fill the CB for 'stretch' /
6999 // 'normal' and clamp it to the CB if requested:
7000 bool isStretchAligned = false;
7001 bool mayUseAspectRatio = aspectRatio && !isAutoBSize;
7002 if (!aFlags.contains(ComputeSizeFlag::ShrinkWrap) &&
7003 !StyleMargin()->HasInlineAxisAuto(aWM, anchorResolutionParams) &&
7004 !alignCB->IsMasonry(aWM, LogicalAxis::Inline)) {
7005 auto inlineAxisAlignment = stylePos->UsedSelfAlignment(
7006 aWM, LogicalAxis::Inline, alignCB->GetWritingMode(),
7007 alignCB->Style());
7008 isStretchAligned = inlineAxisAlignment == StyleAlignFlags::STRETCH ||
7009 (inlineAxisAlignment == StyleAlignFlags::NORMAL &&
7010 !mayUseAspectRatio);
7011 }
7012
7013 // Apply the preferred aspect ratio for alignments other than *stretch* and
7014 // *normal without aspect ratio*.
7015 // The spec says all other values should size the items as fit-content, and
7016 // the intrinsic size should respect the preferred aspect ratio, so we also
7017 // apply aspect ratio for all other values.
7018 // https://drafts.csswg.org/css-grid/#grid-item-sizing
7019 if (!isStretchAligned && mayUseAspectRatio) {
7020 result.ISize(aWM) = ComputeISizeValueFromAspectRatio(
7021 aWM, aCBSize, boxSizingAdjust, styleBSize->AsLengthPercentage(),
7022 aspectRatio);
7023 aspectRatioUsage = AspectRatioUsage::ToComputeISize;
7024 }
7025
7026 if (isStretchAligned ||
7027 aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize)) {
7028 auto iSizeToFillCB =
7029 std::max(nscoord(0), aCBSize.ISize(aWM) - aBorderPadding.ISize(aWM) -
7030 aMargin.ISize(aWM));
7031 if (isStretchAligned || result.ISize(aWM) > iSizeToFillCB) {
7032 result.ISize(aWM) = iSizeToFillCB;
7033 }
7034 }
7035 } else if (aspectRatio && !isAutoBSize) {
7036 // Note: if both the inline size and the block size are auto, the block axis
7037 // is the ratio-dependent axis by default. That means we only need to
7038 // transfer the resolved inline size via aspect-ratio to block axis later in
7039 // this method, but not the other way around.
7040 //
7041 // In this branch, we transfer the non-auto block size via aspect-ration to
7042 // inline axis.
7043 result.ISize(aWM) = ComputeISizeValueFromAspectRatio(
7044 aWM, aCBSize, boxSizingAdjust, styleBSize->AsLengthPercentage(),
7045 aspectRatio);
7046 aspectRatioUsage = AspectRatioUsage::ToComputeISize;
7047 }
7048
7049 // Calculate and apply transferred min & max size contraints.
7050 // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-size-transfers
7051 //
7052 // Note: The basic principle is that sizing constraints transfer through the
7053 // aspect-ratio to the other side to preserve the aspect ratio to the extent
7054 // that they can without violating any sizes specified explicitly on that
7055 // affected axis.
7056 //
7057 // FIXME: The spec words may not be correct, so we may have to update this
7058 // tentative solution once this spec issue gets resolved. Here, we clamp the
7059 // flex base size by the transferred min and max sizes, and don't include
7060 // the transferred min & max sizes into its used min & max sizes. So this
7061 // lets us match other browsers' current behaviors.
7062 // https://github.com/w3c/csswg-drafts/issues/6071
7063 //
7064 // Note: This may make more sense if we clamp the flex base size in
7065 // FlexItem::ResolveFlexBaseSizeFromAspectRatio(). However, the result should
7066 // be identical. FlexItem::ResolveFlexBaseSizeFromAspectRatio() only handles
7067 // the case of the definite cross size, and the definite cross size is clamped
7068 // by the min & max cross sizes below in this function. This means its flex
7069 // base size has been clamped by the transferred min & max size already after
7070 // generating the flex items. So here we make the code more general for both
7071 // definite cross size and indefinite cross size.
7072 const bool isDefiniteISize = styleISize->IsLengthPercentage();
7073 const auto minBSizeCoord = stylePos->MinBSize(aWM, anchorResolutionParams);
7074 const auto maxBSizeCoord = stylePos->MaxBSize(aWM, anchorResolutionParams);
7075 const bool isAutoMinBSize =
7076 nsLayoutUtils::IsAutoBSize(*minBSizeCoord, aCBSize.BSize(aWM));
7077 const bool isAutoMaxBSize =
7078 nsLayoutUtils::IsAutoBSize(*maxBSizeCoord, aCBSize.BSize(aWM));
7079 if (aspectRatio && !isDefiniteISize) {
7080 // Note: the spec mentions that
7081 // 1. This transferred minimum is capped by any definite preferred or
7082 // maximum size in the destination axis.
7083 // 2. This transferred maximum is floored by any definite preferred or
7084 // minimum size in the destination axis.
7085 //
7086 // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-size-transfers
7087 //
7088 // The spec requires us to clamp these by the specified size (it calls it
7089 // the preferred size). However, we actually don't need to worry about that,
7090 // because we are here only if the inline size is indefinite.
7091 //
7092 // We do not need to clamp the transferred minimum and maximum as long as we
7093 // always apply the transferred min/max size before the explicit min/max
7094 // size; the result will be identical.
7095 const nscoord transferredMinISize =
7096 isAutoMinBSize ? 0
7097 : ComputeISizeValueFromAspectRatio(
7098 aWM, aCBSize, boxSizingAdjust,
7099 minBSizeCoord->AsLengthPercentage(), aspectRatio);
7100 const nscoord transferredMaxISize =
7101 isAutoMaxBSize ? nscoord_MAX
7102 : ComputeISizeValueFromAspectRatio(
7103 aWM, aCBSize, boxSizingAdjust,
7104 maxBSizeCoord->AsLengthPercentage(), aspectRatio);
7105
7106 result.ISize(aWM) =
7107 CSSMinMax(result.ISize(aWM), transferredMinISize, transferredMaxISize);
7108 }
7109
7110 // Flex items ignore their min & max sizing properties in their flex
7111 // container's main-axis. (Those properties get applied later in the flexbox
7112 // algorithm.)
7113 const bool isFlexItemInlineAxisMainAxis =
7114 flexItemMainAxis && *flexItemMainAxis == LogicalAxis::Inline;
7115 // Grid items that are subgridded in inline-axis also ignore their min & max
7116 // sizing properties in that axis.
7117 const bool shouldIgnoreMinMaxISize =
7118 isFlexItemInlineAxisMainAxis || isSubgriddedInInlineAxis;
7119 const auto maxISizeCoord = stylePos->MaxISize(aWM, anchorResolutionParams);
7120 nscoord maxISize = NS_UNCONSTRAINEDSIZE;
7121 if (!maxISizeCoord->IsNone() && !shouldIgnoreMinMaxISize) {
7122 maxISize =
7123 ComputeISizeValue(aSizingInput.mRenderingContext, aWM, aCBSize,
7124 boxSizingAdjust, boxSizingToMarginEdgeISize,
7125 *maxISizeCoord, *styleBSize, aspectRatio, aFlags)
7126 .mISize;
7127 result.ISize(aWM) = std::min(maxISize, result.ISize(aWM));
7128 }
7129
7130 const nscoord bSizeAsPercentageBasis = ComputeBSizeValueAsPercentageBasis(
7131 *styleBSize, *minBSizeCoord, *maxBSizeCoord, aCBSize.BSize(aWM),
7132 boxSizingAdjust.BSize(aWM));
7133 const IntrinsicSizeInput input(
7134 aSizingInput.mRenderingContext,
7135 Some(aCBSize.ConvertTo(GetWritingMode(), aWM)),
7136 Some(LogicalSize(aWM, NS_UNCONSTRAINEDSIZE, bSizeAsPercentageBasis)
7137 .ConvertTo(GetWritingMode(), aWM)));
7138 const auto minISizeCoord = stylePos->MinISize(aWM, anchorResolutionParams);
7139 nscoord minISize;
7140 if (!minISizeCoord->IsAuto() && !shouldIgnoreMinMaxISize) {
7141 minISize =
7142 ComputeISizeValue(aSizingInput.mRenderingContext, aWM, aCBSize,
7143 boxSizingAdjust, boxSizingToMarginEdgeISize,
7144 *minISizeCoord, *styleBSize, aspectRatio, aFlags)
7145 .mISize;
7146 } else if (MOZ_UNLIKELY((__builtin_expect(!!(aFlags.contains(ComputeSizeFlag::IApplyAutoMinSize
)), 0))
7147 aFlags.contains(ComputeSizeFlag::IApplyAutoMinSize))(__builtin_expect(!!(aFlags.contains(ComputeSizeFlag::IApplyAutoMinSize
)), 0))
) {
7148 // This implements "Implied Minimum Size of Grid Items".
7149 // https://drafts.csswg.org/css-grid/#min-size-auto
7150 minISize = std::min(maxISize, GetMinISize(input));
7151 if (styleISize->IsLengthPercentage()) {
7152 minISize = std::min(minISize, result.ISize(aWM));
7153 } else if (aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize)) {
7154 // "if the grid item spans only grid tracks that have a fixed max track
7155 // sizing function, its automatic minimum size in that dimension is
7156 // further clamped to less than or equal to the size necessary to fit
7157 // its margin box within the resulting grid area (flooring at zero)"
7158 // https://drafts.csswg.org/css-grid/#min-size-auto
7159 auto maxMinISize =
7160 std::max(nscoord(0), aCBSize.ISize(aWM) - aBorderPadding.ISize(aWM) -
7161 aMargin.ISize(aWM));
7162 minISize = std::min(minISize, maxMinISize);
7163 }
7164 } else if (aspectRatioUsage == AspectRatioUsage::ToComputeISize &&
7165 ShouldApplyAutomaticMinimumOnInlineAxis(
7166 aWM, disp->IsScrollableOverflow(), anchorResolutionParams,
7167 stylePos)) {
7168 // This means we successfully applied aspect-ratio and now need to check
7169 // if we need to apply the automatic content-based minimum size:
7170 // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-minimum
7171 MOZ_ASSERT(!HasReplacedSizing(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!HasReplacedSizing())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!HasReplacedSizing()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("!HasReplacedSizing()"
" (" "aspect-ratio minimums should not apply to replaced elements"
")", "./../../../layout/generic/nsIFrame.cpp", 7172); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!HasReplacedSizing()" ") (" "aspect-ratio minimums should not apply to replaced elements"
")"); do { MOZ_CrashSequence(__null, 7172); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
7172 "aspect-ratio minimums should not apply to replaced elements")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!HasReplacedSizing())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!HasReplacedSizing()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("!HasReplacedSizing()"
" (" "aspect-ratio minimums should not apply to replaced elements"
")", "./../../../layout/generic/nsIFrame.cpp", 7172); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!HasReplacedSizing()" ") (" "aspect-ratio minimums should not apply to replaced elements"
")"); do { MOZ_CrashSequence(__null, 7172); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7173 // The inline size computed by aspect-ratio shouldn't less than the
7174 // min-content size, which should be capped by its maximum inline size.
7175 minISize = std::min(GetMinISize(input), maxISize);
7176 } else {
7177 // Treat "min-width: auto" as 0.
7178 // NOTE: Technically, "auto" is supposed to behave like "min-content" on
7179 // flex items. However, we don't need to worry about that here, because
7180 // flex items' min-sizes are intentionally ignored until the flex
7181 // container explicitly considers them during space distribution.
7182 minISize = 0;
7183 }
7184 result.ISize(aWM) = std::max(minISize, result.ISize(aWM));
7185
7186 // Compute block-axis size
7187 // (but not if we have auto bsize -- then, we'll just stick with the bsize
7188 // that we already calculated in the initial ComputeAutoSize() call. However,
7189 // if we have a valid preferred aspect ratio, we still have to compute the
7190 // block size because aspect ratio affects the intrinsic content size.)
7191 const bool isSubgriddedInBlockAxis =
7192 isSubgrid && static_cast<nsGridContainerFrame*>(this)->IsRowSubgrid();
7193
7194 // Per https://drafts.csswg.org/css-grid/#subgrid-box-alignment, if we are
7195 // subgridded in the block-axis, ignore our style block-size, and stretch to
7196 // fill the CB.
7197 const bool shouldComputeBSize = !isAutoBSize && !isSubgriddedInBlockAxis;
7198 if (shouldComputeBSize) {
7199 result.BSize(aWM) = nsLayoutUtils::ComputeBSizeValue(
7200 aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
7201 styleBSize->AsLengthPercentage());
7202 } else if (MOZ_UNLIKELY(isGridItem)(__builtin_expect(!!(isGridItem), 0)) && styleBSize->IsAuto() &&
7203 !aFlags.contains(ComputeSizeFlag::IsGridMeasuringReflow) &&
7204 !IsTrueOverflowContainer() &&
7205 !alignCB->IsMasonry(aWM, LogicalAxis::Block)) {
7206 auto cbSize = aCBSize.BSize(aWM);
7207 if (cbSize != NS_UNCONSTRAINEDSIZE) {
7208 // 'auto' block-size for grid-level box - fill the CB for 'stretch' /
7209 // 'normal' and clamp it to the CB if requested:
7210 bool isStretchAligned = false;
7211 bool mayUseAspectRatio =
7212 aspectRatio && result.ISize(aWM) != NS_UNCONSTRAINEDSIZE;
7213 if (!StyleMargin()->HasBlockAxisAuto(aWM, anchorResolutionParams)) {
7214 auto blockAxisAlignment = stylePos->UsedSelfAlignment(
7215 aWM, LogicalAxis::Block, alignCB->GetWritingMode(),
7216 alignCB->Style());
7217 isStretchAligned = blockAxisAlignment == StyleAlignFlags::STRETCH ||
7218 (blockAxisAlignment == StyleAlignFlags::NORMAL &&
7219 !mayUseAspectRatio);
7220 }
7221
7222 // Apply the preferred aspect ratio for alignments other than *stretch*
7223 // and *normal without aspect ratio*.
7224 // The spec says all other values should size the items as fit-content,
7225 // and the intrinsic size should respect the preferred aspect ratio, so
7226 // we also apply aspect ratio for all other values.
7227 // https://drafts.csswg.org/css-grid/#grid-item-sizing
7228 if (!isStretchAligned && mayUseAspectRatio) {
7229 result.BSize(aWM) = aspectRatio.ComputeRatioDependentSize(
7230 LogicalAxis::Block, aWM, result.ISize(aWM), boxSizingAdjust);
7231 MOZ_ASSERT(aspectRatioUsage == AspectRatioUsage::None)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aspectRatioUsage == AspectRatioUsage::None)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(aspectRatioUsage == AspectRatioUsage::None))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("aspectRatioUsage == AspectRatioUsage::None"
, "./../../../layout/generic/nsIFrame.cpp", 7231); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aspectRatioUsage == AspectRatioUsage::None"
")"); do { MOZ_CrashSequence(__null, 7231); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7232 aspectRatioUsage = AspectRatioUsage::ToComputeBSize;
7233 }
7234
7235 if (isStretchAligned ||
7236 aFlags.contains(ComputeSizeFlag::BClampMarginBoxMinSize)) {
7237 auto bSizeToFillCB = nsLayoutUtils::ComputeStretchContentBoxBSize(
7238 cbSize, aMargin.BSize(aWM), aBorderPadding.BSize(aWM));
7239 if (isStretchAligned || (result.BSize(aWM) != NS_UNCONSTRAINEDSIZE &&
7240 result.BSize(aWM) > bSizeToFillCB)) {
7241 result.BSize(aWM) = bSizeToFillCB;
7242 }
7243 }
7244 }
7245 } else if (aspectRatio) {
7246 // If both inline and block dimensions are auto, the block axis is the
7247 // ratio-dependent axis by default.
7248 // If we have a super large inline size, aspect-ratio should still be
7249 // applied (so aspectRatioUsage flag is set as expected). That's why we
7250 // apply aspect-ratio unconditionally for auto block size here.
7251 result.BSize(aWM) = aspectRatio.ComputeRatioDependentSize(
7252 LogicalAxis::Block, aWM, result.ISize(aWM), boxSizingAdjust);
7253 MOZ_ASSERT(aspectRatioUsage == AspectRatioUsage::None)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aspectRatioUsage == AspectRatioUsage::None)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(aspectRatioUsage == AspectRatioUsage::None))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("aspectRatioUsage == AspectRatioUsage::None"
, "./../../../layout/generic/nsIFrame.cpp", 7253); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aspectRatioUsage == AspectRatioUsage::None"
")"); do { MOZ_CrashSequence(__null, 7253); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7254 aspectRatioUsage = AspectRatioUsage::ToComputeBSize;
7255 }
7256
7257 if (result.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
7258 // Flex items ignore their min & max sizing properties in their flex
7259 // container's main-axis. (Those properties get applied later in the flexbox
7260 // algorithm.)
7261 const bool isFlexItemBlockAxisMainAxis =
7262 flexItemMainAxis && *flexItemMainAxis == LogicalAxis::Block;
7263 // Grid items that are subgridded in block-axis also ignore their min & max
7264 // sizing properties in that axis.
7265 const bool shouldIgnoreMinMaxBSize =
7266 isFlexItemBlockAxisMainAxis || isSubgriddedInBlockAxis;
7267 if (!isAutoMaxBSize && !shouldIgnoreMinMaxBSize) {
7268 nscoord maxBSize = nsLayoutUtils::ComputeBSizeValueHandlingStretch(
7269 aCBSize.BSize(aWM), aMargin.BSize(aWM), aBorderPadding.BSize(aWM),
7270 boxSizingAdjust.BSize(aWM), *maxBSizeCoord);
7271 result.BSize(aWM) = std::min(maxBSize, result.BSize(aWM));
7272 }
7273
7274 if (!isAutoMinBSize && !shouldIgnoreMinMaxBSize) {
7275 nscoord minBSize = nsLayoutUtils::ComputeBSizeValueHandlingStretch(
7276 aCBSize.BSize(aWM), aMargin.BSize(aWM), aBorderPadding.BSize(aWM),
7277 boxSizingAdjust.BSize(aWM), *minBSizeCoord);
7278 result.BSize(aWM) = std::max(minBSize, result.BSize(aWM));
7279 }
7280 }
7281
7282 if (IsThemed(disp)) {
7283 nsPresContext* pc = PresContext();
7284 const LayoutDeviceIntSize widget = pc->Theme()->GetMinimumWidgetSize(
7285 pc, this, disp->EffectiveAppearance());
7286
7287 // Convert themed widget's physical dimensions to logical coords
7288 LogicalSize size(aWM, LayoutDeviceIntSize::ToAppUnits(
7289 widget, pc->AppUnitsPerDevPixel()));
7290
7291 // GetMinimumWidgetSize() returns border-box; we need content-box.
7292 size -= aBorderPadding;
7293
7294 if (size.BSize(aWM) > result.BSize(aWM)) {
7295 result.BSize(aWM) = size.BSize(aWM);
7296 }
7297 if (size.ISize(aWM) > result.ISize(aWM)) {
7298 result.ISize(aWM) = size.ISize(aWM);
7299 }
7300 }
7301
7302 result.ISize(aWM) = std::max(0, result.ISize(aWM));
7303 result.BSize(aWM) = std::max(0, result.BSize(aWM));
7304
7305 return {result, aspectRatioUsage};
7306}
7307
7308nscoord nsIFrame::ComputeBSizeValueAsPercentageBasis(
7309 const StyleSize& aStyleBSize, const StyleSize& aStyleMinBSize,
7310 const StyleMaxSize& aStyleMaxBSize, nscoord aCBBSize,
7311 nscoord aContentEdgeToBoxSizingBSize) {
7312 if (nsLayoutUtils::IsAutoBSize(aStyleBSize, aCBBSize)) {
7313 return NS_UNCONSTRAINEDSIZE;
7314 }
7315
7316 // TODO(dholbert): This is a temporary hack, to be fixed up in bug 1933604.
7317 // We don't know have aMargin or aBorderPadding args available,
7318 // so we use these dummy zero-valued variables as placeholders in
7319 // our call to ComputeBSizeValueHandlingStretch. (This might mean we
7320 // end up resolving 'stretch' to something slighlty-too-large for the
7321 // purposes of this call, if there's actually nonzero margin/border/padding).
7322 const nscoord dummyMargin = 0;
7323 const nscoord dummyBorderPadding = 0;
7324
7325 const nscoord bSize = nsLayoutUtils::ComputeBSizeValueHandlingStretch(
7326 aCBBSize, dummyMargin, dummyBorderPadding, aContentEdgeToBoxSizingBSize,
7327 aStyleBSize);
7328
7329 const nscoord minBSize =
7330 nsLayoutUtils::IsAutoBSize(aStyleMinBSize, aCBBSize)
7331 ? 0
7332 : nsLayoutUtils::ComputeBSizeValueHandlingStretch(
7333 aCBBSize, dummyMargin, dummyBorderPadding,
7334 aContentEdgeToBoxSizingBSize, aStyleMinBSize);
7335
7336 const nscoord maxBSize =
7337 nsLayoutUtils::IsAutoBSize(aStyleMaxBSize, aCBBSize)
7338 ? NS_UNCONSTRAINEDSIZE
7339 : nsLayoutUtils::ComputeBSizeValueHandlingStretch(
7340 aCBBSize, dummyMargin, dummyBorderPadding,
7341 aContentEdgeToBoxSizingBSize, aStyleMaxBSize);
7342
7343 return CSSMinMax(bSize, minBSize, maxBSize);
7344}
7345
7346nsRect nsIFrame::ComputeTightBounds(DrawTarget* aDrawTarget) const {
7347 return InkOverflowRect();
7348}
7349
7350/* virtual */
7351nsresult nsIFrame::GetPrefWidthTightBounds(gfxContext* aContext, nscoord* aX,
7352 nscoord* aXMost) {
7353 return NS_ERROR_NOT_IMPLEMENTED;
7354}
7355
7356/* virtual */
7357LogicalSize nsIFrame::ComputeAutoSize(
7358 const SizeComputationInput& aSizingInput, WritingMode aWM,
7359 const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize,
7360 const mozilla::LogicalSize& aMargin,
7361 const mozilla::LogicalSize& aBorderPadding,
7362 const StyleSizeOverrides& aSizeOverrides, ComputeSizeFlags aFlags) {
7363 if (IsAbsolutelyPositionedWithDefiniteContainingBlock()) {
7364 return ComputeAbsolutePosAutoSize(aSizingInput, aWM, aCBSize,
7365 aAvailableISize, aMargin, aBorderPadding,
7366 aSizeOverrides, aFlags);
7367 }
7368
7369 // Use basic shrink-wrapping as a default implementation.
7370 LogicalSize result(aWM, 0xdeadbeef, NS_UNCONSTRAINEDSIZE);
7371
7372 const auto anchorResolutionParams = AnchorPosResolutionParams::From(this);
7373 // don't bother setting it if the result won't be used
7374 const auto styleISize =
7375 aSizeOverrides.mStyleISize
7376 ? AnchorResolvedSizeHelper::Overridden(*aSizeOverrides.mStyleISize)
7377 : StylePosition()->ISize(aWM, anchorResolutionParams);
7378 if (styleISize->IsAuto()) {
7379 nscoord availBased = nsLayoutUtils::ComputeStretchContentBoxISize(
7380 aAvailableISize, aMargin.ISize(aWM), aBorderPadding.ISize(aWM));
7381 const auto* stylePos = StylePosition();
7382 const auto styleBSize =
7383 aSizeOverrides.mStyleBSize
7384 ? AnchorResolvedSizeHelper::Overridden(*aSizeOverrides.mStyleBSize)
7385 : stylePos->BSize(aWM, anchorResolutionParams);
7386 const LogicalSize contentEdgeToBoxSizing =
7387 stylePos->mBoxSizing == StyleBoxSizing::BorderBox ? aBorderPadding
7388 : LogicalSize(aWM);
7389 const nscoord bSize = ComputeBSizeValueAsPercentageBasis(
7390 *styleBSize, *stylePos->MinBSize(aWM, anchorResolutionParams),
7391 *stylePos->MaxBSize(aWM, anchorResolutionParams), aCBSize.BSize(aWM),
7392 contentEdgeToBoxSizing.BSize(aWM));
7393 const IntrinsicSizeInput input(
7394 aSizingInput.mRenderingContext,
7395 Some(aCBSize.ConvertTo(GetWritingMode(), aWM)),
7396 Some(LogicalSize(aWM, NS_UNCONSTRAINEDSIZE, bSize)
7397 .ConvertTo(GetWritingMode(), aWM)));
7398 result.ISize(aWM) = ShrinkISizeToFit(input, availBased, aFlags);
7399 }
7400 return result;
7401}
7402
7403bool nsIFrame::IsAbsolutelyPositionedWithDefiniteContainingBlock() const {
7404 // TODO(dshin, Bug 1927861): Even if an absolute container should have a
7405 // definite size, in a continuation context, the full extent of the containing
7406 // block is not known.
7407 return MOZ_UNLIKELY(IsAbsolutelyPositioned())(__builtin_expect(!!(IsAbsolutelyPositioned()), 0)) && !GetPrevInFlow();
7408}
7409
7410LogicalSize nsIFrame::ComputeAbsolutePosAutoSize(
7411 const SizeComputationInput& aSizingInput, WritingMode aWM,
7412 const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize,
7413 const mozilla::LogicalSize& aMargin,
7414 const mozilla::LogicalSize& aBorderPadding,
7415 const StyleSizeOverrides& aSizeOverrides, const ComputeSizeFlags& aFlags) {
7416 MOZ_ASSERT(IsAbsolutelyPositionedWithDefiniteContainingBlock(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsAbsolutelyPositionedWithDefiniteContainingBlock())
>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(IsAbsolutelyPositionedWithDefiniteContainingBlock())
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("IsAbsolutelyPositionedWithDefiniteContainingBlock()"
" (" "Asking for absolute auto size when not absolute" ")", "./../../../layout/generic/nsIFrame.cpp"
, 7417); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsAbsolutelyPositionedWithDefiniteContainingBlock()"
") (" "Asking for absolute auto size when not absolute" ")")
; do { MOZ_CrashSequence(__null, 7417); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
7417 "Asking for absolute auto size when not absolute")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsAbsolutelyPositionedWithDefiniteContainingBlock())
>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(IsAbsolutelyPositionedWithDefiniteContainingBlock())
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("IsAbsolutelyPositionedWithDefiniteContainingBlock()"
" (" "Asking for absolute auto size when not absolute" ")", "./../../../layout/generic/nsIFrame.cpp"
, 7417); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsAbsolutelyPositionedWithDefiniteContainingBlock()"
") (" "Asking for absolute auto size when not absolute" ")")
; do { MOZ_CrashSequence(__null, 7417); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7418 // Ideally, this is an assertion, but the containing block could just be
7419 // really big.
7420 NS_WARNING_ASSERTION(aCBSize.ISize(aWM) != NS_UNCONSTRAINEDSIZE &&do { if (!(aCBSize.ISize(aWM) != NS_UNCONSTRAINEDSIZE &&
aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE)) { NS_DebugBreak
(NS_DEBUG_WARNING, "Absolute containing block size not definite?"
, "aCBSize.ISize(aWM) != NS_UNCONSTRAINEDSIZE && aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE"
, "./../../../layout/generic/nsIFrame.cpp", 7422); } } while (
false)
7421 aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE,do { if (!(aCBSize.ISize(aWM) != NS_UNCONSTRAINEDSIZE &&
aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE)) { NS_DebugBreak
(NS_DEBUG_WARNING, "Absolute containing block size not definite?"
, "aCBSize.ISize(aWM) != NS_UNCONSTRAINEDSIZE && aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE"
, "./../../../layout/generic/nsIFrame.cpp", 7422); } } while (
false)
7422 "Absolute containing block size not definite?")do { if (!(aCBSize.ISize(aWM) != NS_UNCONSTRAINEDSIZE &&
aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE)) { NS_DebugBreak
(NS_DEBUG_WARNING, "Absolute containing block size not definite?"
, "aCBSize.ISize(aWM) != NS_UNCONSTRAINEDSIZE && aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE"
, "./../../../layout/generic/nsIFrame.cpp", 7422); } } while (
false)
;
7423 LogicalSize result(aWM, static_cast<nscoord>(0xdeadbeef),
7424 NS_UNCONSTRAINEDSIZE);
7425
7426 const auto* stylePos = StylePosition();
7427 const auto anchorResolutionParams =
7428 AnchorPosOffsetResolutionParams::UseCBFrameSize(
7429 AnchorPosResolutionParams::From(&aSizingInput));
7430 const auto& styleISize =
7431 aSizeOverrides.mStyleISize
7432 ? AnchorResolvedSizeHelper::Overridden(*aSizeOverrides.mStyleISize)
7433 : stylePos->ISize(aWM, anchorResolutionParams.mBaseParams);
7434 const auto& styleBSize =
7435 aSizeOverrides.mStyleBSize
7436 ? AnchorResolvedSizeHelper::Overridden(*aSizeOverrides.mStyleBSize)
7437 : stylePos->BSize(aWM, anchorResolutionParams.mBaseParams);
7438 const auto iStartOffsetIsAuto =
7439 stylePos
7440 ->GetAnchorResolvedInset(LogicalSide::IStart, aWM,
7441 anchorResolutionParams)
7442 ->IsAuto();
7443 const auto iEndOffsetIsAuto =
7444 stylePos
7445 ->GetAnchorResolvedInset(LogicalSide::IEnd, aWM,
7446 anchorResolutionParams)
7447 ->IsAuto();
7448 const auto bStartOffsetIsAuto =
7449 stylePos
7450 ->GetAnchorResolvedInset(LogicalSide::BStart, aWM,
7451 anchorResolutionParams)
7452 ->IsAuto();
7453 const auto bEndOffsetIsAuto =
7454 stylePos
7455 ->GetAnchorResolvedInset(LogicalSide::BEnd, aWM,
7456 anchorResolutionParams)
7457 ->IsAuto();
7458 const auto boxSizingAdjust = stylePos->mBoxSizing == StyleBoxSizing::BorderBox
7459 ? aBorderPadding
7460 : LogicalSize(aWM);
7461 auto shouldStretch = [](StyleAlignFlags aAlignment, const nsIFrame* aFrame,
7462 bool aStartIsAuto, bool aEndIsAuto) {
7463 if (aStartIsAuto || aEndIsAuto) {
7464 // Note(dshin, bug 1930427): This is not part of the current spec [1];
7465 // however, no one implements the new inset behaviour [2], and the old
7466 // behaviour [3] ends up computing the static size if both or one inset is
7467 // auto.
7468 //
7469 // [1]: https://drafts.csswg.org/css-position-3/#abspos-auto-size
7470 // [2]: https://drafts.csswg.org/css-position-3/#resolving-insets
7471 // [3]: https://drafts.csswg.org/css-position-3/#abspos-old
7472 return false;
7473 }
7474 // Don't care about flag bits for auto-sizing.
7475 aAlignment &= ~StyleAlignFlags::FLAG_BITS;
7476
7477 if (aAlignment == StyleAlignFlags::STRETCH) {
7478 return true;
7479 }
7480
7481 if (aAlignment == StyleAlignFlags::NORMAL) {
7482 // Some replaced elements behave as semi-replaced elements - we want them
7483 // to stretch (See bug 1740580).
7484 return !aFrame->HasReplacedSizing() && !aFrame->IsTableWrapperFrame();
7485 }
7486
7487 return false;
7488 };
7489
7490 // i.e. Absolute containing block
7491
7492 // Self alignment properties translate `auto` to normal for this purpose.
7493 // https://drafts.csswg.org/css-align-3/#valdef-justify-self-auto
7494 nsContainerFrame* contFrame = static_cast<nsContainerFrame*>(this);
7495 const StylePositionArea posArea = stylePos->mPositionArea;
7496 const auto containerWM = GetParent()->GetWritingMode();
7497 auto containerAxis = [&](LogicalAxis aSubjectAxis) {
7498 return aWM.ConvertAxisTo(aSubjectAxis, containerWM);
7499 };
7500 const auto inlineSelfAlign =
7501 contFrame->CSSAlignmentForAbsPosChildWithinContainingBlock(
7502 aSizingInput, containerAxis(LogicalAxis::Inline), posArea, aCBSize);
7503 const auto blockSelfAlign =
7504 contFrame->CSSAlignmentForAbsPosChildWithinContainingBlock(
7505 aSizingInput, containerAxis(LogicalAxis::Block), posArea, aCBSize);
7506 const auto iShouldStretch = shouldStretch(
7507 inlineSelfAlign, this, iStartOffsetIsAuto, iEndOffsetIsAuto);
7508 const auto bShouldStretch =
7509 shouldStretch(blockSelfAlign, this, bStartOffsetIsAuto, bEndOffsetIsAuto);
7510 const auto iSizeIsAuto = styleISize->IsAuto();
7511 // Note(dshin, bug 1789477): `auto` in the context of abs-element uses
7512 // stretch-fit sizing, given specific alignment conditions [1]. Effectively,
7513 // `auto` is `stretch`. `nsLayoutUtils::IsAutoBSize` is not the right tool
7514 // here, since the mapping is explicit, and it's incorrect to e.g. map
7515 // `fit-content` to `stretch`.
7516 // `-moz-available` behaves like `auto` in general, so map the same way.
7517 // When Bug 567039 brings `-moz-available` into alignment with `stretch`, this
7518 // special check can be removed. TODO(dshin): we're probably duplicating the
7519 // `stretch` logic here, since `stretch` is `stretch-fit` sizing [2].
7520 //
7521 // [1]: https://drafts.csswg.org/css-position/#abspos-auto-size
7522 // [2]: https://drafts.csswg.org/css-sizing-4/#valdef-width-stretch
7523 const auto bSizeIsAuto = styleBSize->IsAuto() || styleBSize->IsMozAvailable();
7524 if (bSizeIsAuto && bShouldStretch) {
7525 result.BSize(aWM) = nsLayoutUtils::ComputeStretchContentBoxBSize(
7526 aCBSize.BSize(aWM), aMargin.BSize(aWM), aBorderPadding.BSize(aWM));
7527 }
7528 if (iSizeIsAuto) {
7529 if (iShouldStretch) {
7530 // inline-size to make our margin-box fill the containing block:
7531 result.ISize(aWM) = nsLayoutUtils::ComputeStretchContentBoxISize(
7532 aCBSize.ISize(aWM), aMargin.ISize(aWM), aBorderPadding.ISize(aWM));
7533 } else {
7534 // inline-size to make our margin-box fill aAvailableISize:
7535 nscoord availBased = nsLayoutUtils::ComputeStretchContentBoxISize(
7536 aAvailableISize, aMargin.ISize(aWM), aBorderPadding.ISize(aWM));
7537
7538 const nscoord bSize = ComputeBSizeValueAsPercentageBasis(
7539 styleBSize->IsAuto() && result.BSize(aWM) != NS_UNCONSTRAINEDSIZE
7540 ? StyleSize::FromAppUnits(result.BSize(aWM))
7541 : *styleBSize,
7542 *stylePos->MinBSize(aWM, anchorResolutionParams.mBaseParams),
7543 *stylePos->MaxBSize(aWM, anchorResolutionParams.mBaseParams),
7544 aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM));
7545
7546 const IntrinsicSizeInput input(
7547 aSizingInput.mRenderingContext,
7548 Some(aCBSize.ConvertTo(GetWritingMode(), aWM)),
7549 Some(LogicalSize(aWM, NS_UNCONSTRAINEDSIZE, bSize)
7550 .ConvertTo(GetWritingMode(), aWM)));
7551 result.ISize(aWM) = ShrinkISizeToFit(input, availBased, aFlags);
7552 }
7553 }
7554
7555 const auto& aspectRatio = aSizeOverrides.mAspectRatio
7556 ? *aSizeOverrides.mAspectRatio
7557 : GetAspectRatio();
7558 if (aspectRatio) {
7559 auto aspectRatioUsage = AspectRatioUsage::None;
7560 if (iSizeIsAuto != bSizeIsAuto) {
7561 // Auto axis is dependent.
7562 if (iSizeIsAuto) {
7563 aspectRatioUsage = AspectRatioUsage::ToComputeBSize;
7564 } else {
7565 aspectRatioUsage = AspectRatioUsage::ToComputeISize;
7566 }
7567 } else if (iSizeIsAuto) {
7568 // Both axes are `auto`.
7569 if (iShouldStretch != bShouldStretch) {
7570 // If an axis has stretch, that behaves like a definite size.
7571 aspectRatioUsage = iShouldStretch ? AspectRatioUsage::ToComputeBSize
7572 : AspectRatioUsage::ToComputeISize;
7573 } else if (!iShouldStretch) {
7574 // If one axis has `auto` inset, that is the ratio dependent axis,
7575 // otherwise the block axis is.
7576 const bool inlineInsetHasAuto = iStartOffsetIsAuto || iEndOffsetIsAuto;
7577 const bool blockInsetHasAuto = bStartOffsetIsAuto || bEndOffsetIsAuto;
7578 aspectRatioUsage = inlineInsetHasAuto && !blockInsetHasAuto
7579 ? AspectRatioUsage::ToComputeISize
7580 : AspectRatioUsage::ToComputeBSize;
7581 }
7582 }
7583
7584 if (aspectRatioUsage == AspectRatioUsage::ToComputeBSize &&
7585 !bShouldStretch) {
7586 result.BSize(aWM) = aspectRatio.ComputeRatioDependentSize(
7587 LogicalAxis::Block, aWM, result.ISize(aWM), boxSizingAdjust);
7588 } else if (aspectRatioUsage == AspectRatioUsage::ToComputeISize &&
7589 !iShouldStretch && result.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
7590 result.ISize(aWM) = aspectRatio.ComputeRatioDependentSize(
7591 LogicalAxis::Inline, aWM, result.BSize(aWM), boxSizingAdjust);
7592 }
7593 }
7594
7595 return result;
7596}
7597
7598nscoord nsIFrame::ShrinkISizeToFit(const IntrinsicSizeInput& aInput,
7599 nscoord aISizeInCB,
7600 ComputeSizeFlags aFlags) {
7601 // If we're a container for font size inflation, then shrink
7602 // wrapping inside of us should not apply font size inflation.
7603 AutoMaybeDisableFontInflation an(this);
7604
7605 nscoord result;
7606 nscoord minISize = GetMinISize(aInput);
7607 if (minISize > aISizeInCB) {
7608 const bool clamp = aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize);
7609 result = MOZ_UNLIKELY(clamp)(__builtin_expect(!!(clamp), 0)) ? aISizeInCB : minISize;
7610 } else {
7611 nscoord prefISize = GetPrefISize(aInput);
7612 if (prefISize > aISizeInCB) {
7613 result = aISizeInCB;
7614 } else {
7615 result = prefISize;
7616 }
7617 }
7618 return result;
7619}
7620
7621nscoord nsIFrame::IntrinsicISizeFromInline(const IntrinsicSizeInput& aInput,
7622 IntrinsicISizeType aType) {
7623 MOZ_ASSERT(!IsContainerForFontSizeInflation(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsContainerForFontSizeInflation())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsContainerForFontSizeInflation
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!IsContainerForFontSizeInflation()" " (" "Should not be a container for font size inflation!"
")", "./../../../layout/generic/nsIFrame.cpp", 7624); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsContainerForFontSizeInflation()" ") (" "Should not be a container for font size inflation!"
")"); do { MOZ_CrashSequence(__null, 7624); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
7624 "Should not be a container for font size inflation!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!IsContainerForFontSizeInflation())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!IsContainerForFontSizeInflation
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!IsContainerForFontSizeInflation()" " (" "Should not be a container for font size inflation!"
")", "./../../../layout/generic/nsIFrame.cpp", 7624); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsContainerForFontSizeInflation()" ") (" "Should not be a container for font size inflation!"
")"); do { MOZ_CrashSequence(__null, 7624); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7625
7626 if (aType == IntrinsicISizeType::MinISize) {
7627 InlineMinISizeData data;
7628 AddInlineMinISize(aInput, &data);
7629 data.ForceBreak();
7630 return data.mPrevLines;
7631 }
7632
7633 InlinePrefISizeData data;
7634 AddInlinePrefISize(aInput, &data);
7635 data.ForceBreak();
7636 return data.mPrevLines;
7637}
7638
7639nscoord nsIFrame::ComputeISizeValueFromAspectRatio(
7640 WritingMode aWM, const LogicalSize& aCBSize,
7641 const LogicalSize& aContentEdgeToBoxSizing, const LengthPercentage& aBSize,
7642 const AspectRatio& aAspectRatio) const {
7643 MOZ_ASSERT(aAspectRatio, "Must have a valid AspectRatio!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aAspectRatio)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aAspectRatio))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("aAspectRatio" " ("
"Must have a valid AspectRatio!" ")", "./../../../layout/generic/nsIFrame.cpp"
, 7643); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aAspectRatio"
") (" "Must have a valid AspectRatio!" ")"); do { MOZ_CrashSequence
(__null, 7643); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
7644 const nscoord bSize = nsLayoutUtils::ComputeBSizeValue(
7645 aCBSize.BSize(aWM), aContentEdgeToBoxSizing.BSize(aWM), aBSize);
7646 return aAspectRatio.ComputeRatioDependentSize(LogicalAxis::Inline, aWM, bSize,
7647 aContentEdgeToBoxSizing);
7648}
7649
7650nsIFrame::ISizeComputationResult nsIFrame::ComputeISizeValue(
7651 gfxContext* aRenderingContext, const WritingMode aWM,
7652 const LogicalSize& aCBSize, const LogicalSize& aContentEdgeToBoxSizing,
7653 nscoord aBoxSizingToMarginEdge, ExtremumLength aSize,
7654 Maybe<nscoord> aAvailableISizeOverride, const StyleSize& aStyleBSize,
7655 const AspectRatio& aAspectRatio, ComputeSizeFlags aFlags) {
7656 auto GetAvailableISize = [&]() {
7657 return aCBSize.ISize(aWM) - aBoxSizingToMarginEdge -
7658 aContentEdgeToBoxSizing.ISize(aWM);
7659 };
7660
7661 // If 'this' is a container for font size inflation, then shrink
7662 // wrapping inside of it should not apply font size inflation.
7663 AutoMaybeDisableFontInflation an(this);
7664 // If we have an aspect-ratio and a definite block size, we should use them to
7665 // resolve the sizes with intrinsic keywords.
7666 // https://github.com/w3c/csswg-drafts/issues/5032
7667 Maybe<nscoord> iSizeFromAspectRatio = [&]() -> Maybe<nscoord> {
7668 if (aSize == ExtremumLength::MozAvailable ||
7669 aSize == ExtremumLength::Stretch) {
7670 return Nothing();
7671 }
7672 if (!aAspectRatio) {
7673 return Nothing();
7674 }
7675 if (nsLayoutUtils::IsAutoBSize(aStyleBSize, aCBSize.BSize(aWM))) {
7676 return Nothing();
7677 }
7678
7679 // Helper used below to resolve aStyleBSize if it's 'stretch' or an alias.
7680 // XXXdholbert Really we should be resolving 'stretch' and its aliases
7681 // sooner; see bug 2000035.
7682 auto ResolveStretchBSize = [&]() {
7683 MOZ_ASSERT(aStyleBSize.BehavesLikeStretchOnBlockAxis(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aStyleBSize.BehavesLikeStretchOnBlockAxis())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(aStyleBSize.BehavesLikeStretchOnBlockAxis()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("aStyleBSize.BehavesLikeStretchOnBlockAxis()"
" (" "Only call me for 'stretch'-like BSizes" ")", "./../../../layout/generic/nsIFrame.cpp"
, 7684); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aStyleBSize.BehavesLikeStretchOnBlockAxis()"
") (" "Only call me for 'stretch'-like BSizes" ")"); do { MOZ_CrashSequence
(__null, 7684); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
7684 "Only call me for 'stretch'-like BSizes")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aStyleBSize.BehavesLikeStretchOnBlockAxis())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(aStyleBSize.BehavesLikeStretchOnBlockAxis()))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("aStyleBSize.BehavesLikeStretchOnBlockAxis()"
" (" "Only call me for 'stretch'-like BSizes" ")", "./../../../layout/generic/nsIFrame.cpp"
, 7684); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aStyleBSize.BehavesLikeStretchOnBlockAxis()"
") (" "Only call me for 'stretch'-like BSizes" ")"); do { MOZ_CrashSequence
(__null, 7684); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
7685 MOZ_ASSERT(aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE"
" (" "If aStyleBSize is stretch-like, then unconstrained " "aCBSize.BSize should make us return via the IsAutoBSize "
"check above" ")", "./../../../layout/generic/nsIFrame.cpp",
7688); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE"
") (" "If aStyleBSize is stretch-like, then unconstrained " "aCBSize.BSize should make us return via the IsAutoBSize "
"check above" ")"); do { MOZ_CrashSequence(__null, 7688); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
7686 "If aStyleBSize is stretch-like, then unconstrained "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE"
" (" "If aStyleBSize is stretch-like, then unconstrained " "aCBSize.BSize should make us return via the IsAutoBSize "
"check above" ")", "./../../../layout/generic/nsIFrame.cpp",
7688); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE"
") (" "If aStyleBSize is stretch-like, then unconstrained " "aCBSize.BSize should make us return via the IsAutoBSize "
"check above" ")"); do { MOZ_CrashSequence(__null, 7688); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
7687 "aCBSize.BSize should make us return via the IsAutoBSize "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE"
" (" "If aStyleBSize is stretch-like, then unconstrained " "aCBSize.BSize should make us return via the IsAutoBSize "
"check above" ")", "./../../../layout/generic/nsIFrame.cpp",
7688); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE"
") (" "If aStyleBSize is stretch-like, then unconstrained " "aCBSize.BSize should make us return via the IsAutoBSize "
"check above" ")"); do { MOZ_CrashSequence(__null, 7688); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
7688 "check above")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE"
" (" "If aStyleBSize is stretch-like, then unconstrained " "aCBSize.BSize should make us return via the IsAutoBSize "
"check above" ")", "./../../../layout/generic/nsIFrame.cpp",
7688); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE"
") (" "If aStyleBSize is stretch-like, then unconstrained " "aCBSize.BSize should make us return via the IsAutoBSize "
"check above" ")"); do { MOZ_CrashSequence(__null, 7688); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
;
7689
7690 // NOTE: the borderPadding and margin variables might be zero-filled
7691 // instead of having the true values, if those values haven't been
7692 // stashed in our property-table yet (e.g. if we're in the midst of
7693 // setting up a ReflowInput for our first reflow). So ideally, we should
7694 // be resolving 'stretch' **in our callers** rather than here, if those
7695 // callers have more up-to-date resolved margin/border/padding values.
7696 // We'll still make a best-effort attempt to resolve 'stretch' here,
7697 // though, for the benefit of callers that might not have handled it, to
7698 // be sure we don't abort in aStyleBSize.AsLengthPercentage(). Ultimately
7699 // this all can be removed when we fix bug 2000035.
7700 const auto borderPadding = GetLogicalUsedBorderAndPadding(aWM);
7701 const auto margin = GetLogicalUsedMargin(aWM);
7702 nscoord stretchBSize = nsLayoutUtils::ComputeStretchBSize(
7703 aCBSize.BSize(aWM), margin.BStartEnd(aWM),
7704 borderPadding.BStartEnd(aWM), StylePosition()->mBoxSizing);
7705 return LengthPercentage::FromAppUnits(stretchBSize);
7706 };
7707
7708 return Some(ComputeISizeValueFromAspectRatio(
7709 aWM, aCBSize, aContentEdgeToBoxSizing,
7710 aStyleBSize.BehavesLikeStretchOnBlockAxis()
7711 ? ResolveStretchBSize()
7712 : aStyleBSize.AsLengthPercentage(),
7713 aAspectRatio));
7714 }();
7715
7716 const auto* stylePos = StylePosition();
7717 const auto anchorResolutionParams = AnchorPosResolutionParams::From(this);
7718 const nscoord bSize = ComputeBSizeValueAsPercentageBasis(
7719 aStyleBSize, *stylePos->MinBSize(aWM, anchorResolutionParams),
7720 *stylePos->MaxBSize(aWM, anchorResolutionParams), aCBSize.BSize(aWM),
7721 aContentEdgeToBoxSizing.BSize(aWM));
7722 const IntrinsicSizeInput input(
7723 aRenderingContext, Some(aCBSize.ConvertTo(GetWritingMode(), aWM)),
7724 Some(LogicalSize(aWM, NS_UNCONSTRAINEDSIZE, bSize)
7725 .ConvertTo(GetWritingMode(), aWM)));
7726 nscoord result;
7727 switch (aSize) {
7728 case ExtremumLength::MaxContent:
7729 result =
7730 iSizeFromAspectRatio ? *iSizeFromAspectRatio : GetPrefISize(input);
7731 NS_ASSERTION(result >= 0, "inline-size less than zero")do { if (!(result >= 0)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "inline-size less than zero", "result >= 0", "./../../../layout/generic/nsIFrame.cpp"
, 7731); MOZ_PretendNoReturn(); } } while (0)
;
7732 return {result, iSizeFromAspectRatio ? AspectRatioUsage::ToComputeISize
7733 : AspectRatioUsage::None};
7734 case ExtremumLength::MinContent:
7735 result =
7736 iSizeFromAspectRatio ? *iSizeFromAspectRatio : GetMinISize(input);
7737 NS_ASSERTION(result >= 0, "inline-size less than zero")do { if (!(result >= 0)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "inline-size less than zero", "result >= 0", "./../../../layout/generic/nsIFrame.cpp"
, 7737); MOZ_PretendNoReturn(); } } while (0)
;
7738 if (MOZ_UNLIKELY((__builtin_expect(!!(aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize
)), 0))
7739 aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize))(__builtin_expect(!!(aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize
)), 0))
) {
7740 result = std::min(GetAvailableISize(), result);
7741 }
7742 return {result, iSizeFromAspectRatio ? AspectRatioUsage::ToComputeISize
7743 : AspectRatioUsage::None};
7744 case ExtremumLength::FitContentFunction:
7745 case ExtremumLength::FitContent: {
7746 nscoord pref = NS_UNCONSTRAINEDSIZE;
7747 nscoord min = 0;
7748 if (iSizeFromAspectRatio) {
7749 // The min-content and max-content size are identical and equal to the
7750 // size computed from the block size and the aspect ratio.
7751 pref = min = *iSizeFromAspectRatio;
7752 } else {
7753 pref = GetPrefISize(input);
7754 min = GetMinISize(input);
7755 }
7756
7757 const nscoord fill = aAvailableISizeOverride ? *aAvailableISizeOverride
7758 : GetAvailableISize();
7759 if (MOZ_UNLIKELY((__builtin_expect(!!(aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize
)), 0))
7760 aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize))(__builtin_expect(!!(aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize
)), 0))
) {
7761 min = std::min(min, fill);
7762 }
7763 result = std::max(min, std::min(pref, fill));
7764 NS_ASSERTION(result >= 0, "inline-size less than zero")do { if (!(result >= 0)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "inline-size less than zero", "result >= 0", "./../../../layout/generic/nsIFrame.cpp"
, 7764); MOZ_PretendNoReturn(); } } while (0)
;
7765 return {result};
7766 }
7767 case ExtremumLength::MozAvailable:
7768 case ExtremumLength::Stretch:
7769 return {GetAvailableISize()};
7770 }
7771 MOZ_ASSERT_UNREACHABLE("Unknown extremum length?")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"Unknown extremum length?" ")", "./../../../layout/generic/nsIFrame.cpp"
, 7771); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Unknown extremum length?" ")"); do
{ MOZ_CrashSequence(__null, 7771); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
7772 return {};
7773}
7774
7775nscoord nsIFrame::ComputeISizeValue(const WritingMode aWM,
7776 const LogicalSize& aCBSize,
7777 const LogicalSize& aContentEdgeToBoxSizing,
7778 const LengthPercentage& aSize) const {
7779 LAYOUT_WARN_IF_FALSE(do { if ((__builtin_expect(!!(mozilla::detail::log_test(sLayoutLog
, mozilla::LogLevel::Warning)), 0)) && !(aCBSize.ISize
(aWM) != NS_UNCONSTRAINEDSIZE)) { mozilla::detail::LayoutLogWarning
("have unconstrained inline-size; this should only result from "
"very large sizes, not attempts at intrinsic inline-size " "calculation"
, "aCBSize.ISize(aWM) != NS_UNCONSTRAINEDSIZE", "./../../../layout/generic/nsIFrame.cpp"
, 7783); } } while (0)
7780 aCBSize.ISize(aWM) != NS_UNCONSTRAINEDSIZE,do { if ((__builtin_expect(!!(mozilla::detail::log_test(sLayoutLog
, mozilla::LogLevel::Warning)), 0)) && !(aCBSize.ISize
(aWM) != NS_UNCONSTRAINEDSIZE)) { mozilla::detail::LayoutLogWarning
("have unconstrained inline-size; this should only result from "
"very large sizes, not attempts at intrinsic inline-size " "calculation"
, "aCBSize.ISize(aWM) != NS_UNCONSTRAINEDSIZE", "./../../../layout/generic/nsIFrame.cpp"
, 7783); } } while (0)
7781 "have unconstrained inline-size; this should only result from "do { if ((__builtin_expect(!!(mozilla::detail::log_test(sLayoutLog
, mozilla::LogLevel::Warning)), 0)) && !(aCBSize.ISize
(aWM) != NS_UNCONSTRAINEDSIZE)) { mozilla::detail::LayoutLogWarning
("have unconstrained inline-size; this should only result from "
"very large sizes, not attempts at intrinsic inline-size " "calculation"
, "aCBSize.ISize(aWM) != NS_UNCONSTRAINEDSIZE", "./../../../layout/generic/nsIFrame.cpp"
, 7783); } } while (0)
7782 "very large sizes, not attempts at intrinsic inline-size "do { if ((__builtin_expect(!!(mozilla::detail::log_test(sLayoutLog
, mozilla::LogLevel::Warning)), 0)) && !(aCBSize.ISize
(aWM) != NS_UNCONSTRAINEDSIZE)) { mozilla::detail::LayoutLogWarning
("have unconstrained inline-size; this should only result from "
"very large sizes, not attempts at intrinsic inline-size " "calculation"
, "aCBSize.ISize(aWM) != NS_UNCONSTRAINEDSIZE", "./../../../layout/generic/nsIFrame.cpp"
, 7783); } } while (0)
7783 "calculation")do { if ((__builtin_expect(!!(mozilla::detail::log_test(sLayoutLog
, mozilla::LogLevel::Warning)), 0)) && !(aCBSize.ISize
(aWM) != NS_UNCONSTRAINEDSIZE)) { mozilla::detail::LayoutLogWarning
("have unconstrained inline-size; this should only result from "
"very large sizes, not attempts at intrinsic inline-size " "calculation"
, "aCBSize.ISize(aWM) != NS_UNCONSTRAINEDSIZE", "./../../../layout/generic/nsIFrame.cpp"
, 7783); } } while (0)
;
7784 NS_ASSERTION(aCBSize.ISize(aWM) >= 0, "inline-size less than zero")do { if (!(aCBSize.ISize(aWM) >= 0)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "inline-size less than zero", "aCBSize.ISize(aWM) >= 0",
"./../../../layout/generic/nsIFrame.cpp", 7784); MOZ_PretendNoReturn
(); } } while (0)
;
7785
7786 nscoord result = aSize.Resolve(aCBSize.ISize(aWM));
7787 // The result of a calc() expression might be less than 0; we
7788 // should clamp at runtime (below). (Percentages and coords that
7789 // are less than 0 have already been dropped by the parser.)
7790 result -= aContentEdgeToBoxSizing.ISize(aWM);
7791 return std::max(0, result);
7792}
7793
7794void nsIFrame::DidReflow(nsPresContext* aPresContext,
7795 const ReflowInput* aReflowInput) {
7796 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("nsIFrame::DidReflow"))do { if ((int(((mozilla::LogModule*)(nsIFrame::sFrameLogModule
))->Level()) & (0x1))) { TraceMsg ("nsIFrame::DidReflow"
); } } while (0)
;
7797
7798 if (IsHiddenByContentVisibilityOfInFlowParentForLayout()) {
7799 RemoveStateBits(NS_FRAME_IN_REFLOW);
7800 return;
7801 }
7802
7803 SVGObserverUtils::InvalidateDirectRenderingObservers(this);
7804
7805 RemoveStateBits(NS_FRAME_IN_REFLOW | NS_FRAME_FIRST_REFLOW |
7806 NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
7807
7808 // Clear bits that were used in ReflowInput::InitResizeFlags (see
7809 // comment there for why we can't clear it there).
7810 SetHasBSizeChange(false);
7811 SetHasPaddingChange(false);
7812
7813 // Notify the percent bsize observer if there is a percent bsize.
7814 // The observer may be able to initiate another reflow with a computed
7815 // bsize. This happens in the case where a table cell has no computed
7816 // bsize but can fabricate one when the cell bsize is known.
7817 if (aReflowInput && aReflowInput->mPercentBSizeObserver && !GetPrevInFlow()) {
7818 const auto bsize = aReflowInput->mStylePosition->BSize(
7819 aReflowInput->GetWritingMode(),
7820 AnchorPosResolutionParams::From(aReflowInput));
7821 if (bsize->HasPercent()) {
7822 aReflowInput->mPercentBSizeObserver->NotifyPercentBSize(*aReflowInput);
7823 }
7824 }
7825
7826 aPresContext->ReflowedFrame();
7827}
7828
7829/* virtual */
7830bool nsIFrame::CanContinueTextRun() const {
7831 // By default, a frame will *not* allow a text run to be continued
7832 // through it.
7833 return false;
7834}
7835
7836void nsIFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
7837 const ReflowInput& aReflowInput,
7838 nsReflowStatus& aStatus) {
7839 MarkInReflow();
7840 DO_GLOBAL_REFLOW_COUNT("nsFrame")aPresContext->CountReflows(("nsFrame"), (nsIFrame*)this);;
7841 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aStatus.IsEmpty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aStatus.IsEmpty()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("aStatus.IsEmpty()"
" (" "Caller should pass a fresh reflow status!" ")", "./../../../layout/generic/nsIFrame.cpp"
, 7841); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aStatus.IsEmpty()"
") (" "Caller should pass a fresh reflow status!" ")"); do {
MOZ_CrashSequence(__null, 7841); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
7842 aDesiredSize.ClearSize();
7843}
7844
7845bool nsIFrame::IsContentDisabled() const {
7846 auto* element = nsGenericHTMLElement::FromNodeOrNull(GetContent());
7847 return element && element->IsDisabled();
7848}
7849
7850bool nsIFrame::IsContentRelevant() const {
7851 MOZ_ASSERT(StyleDisplay()->ContentVisibility(*this) ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(StyleDisplay()->ContentVisibility(*this) == StyleContentVisibility
::Auto)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(StyleDisplay()->ContentVisibility(*this) == StyleContentVisibility
::Auto))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("StyleDisplay()->ContentVisibility(*this) == StyleContentVisibility::Auto"
, "./../../../layout/generic/nsIFrame.cpp", 7852); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "StyleDisplay()->ContentVisibility(*this) == StyleContentVisibility::Auto"
")"); do { MOZ_CrashSequence(__null, 7852); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
7852 StyleContentVisibility::Auto)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(StyleDisplay()->ContentVisibility(*this) == StyleContentVisibility
::Auto)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(StyleDisplay()->ContentVisibility(*this) == StyleContentVisibility
::Auto))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("StyleDisplay()->ContentVisibility(*this) == StyleContentVisibility::Auto"
, "./../../../layout/generic/nsIFrame.cpp", 7852); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "StyleDisplay()->ContentVisibility(*this) == StyleContentVisibility::Auto"
")"); do { MOZ_CrashSequence(__null, 7852); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7853
7854 auto* element = Element::FromNodeOrNull(GetContent());
7855 MOZ_ASSERT(element)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(element)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(element))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("element", "./../../../layout/generic/nsIFrame.cpp"
, 7855); AnnotateMozCrashReason("MOZ_ASSERT" "(" "element" ")"
); do { MOZ_CrashSequence(__null, 7855); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7856
7857 Maybe<ContentRelevancy> relevancy = element->GetContentRelevancy();
7858 return relevancy.isSome() && !relevancy->isEmpty();
7859}
7860
7861bool nsIFrame::HidesContent(
7862 const EnumSet<IncludeContentVisibility>& aInclude) const {
7863 auto effectiveContentVisibility = StyleDisplay()->ContentVisibility(*this);
7864 if (aInclude.contains(IncludeContentVisibility::Hidden) &&
7865 effectiveContentVisibility == StyleContentVisibility::Hidden) {
7866 return true;
7867 }
7868
7869 if (aInclude.contains(IncludeContentVisibility::Auto) &&
7870 effectiveContentVisibility == StyleContentVisibility::Auto) {
7871 return !IsContentRelevant();
7872 }
7873
7874 return false;
7875}
7876
7877bool nsIFrame::HidesContentForLayout() const {
7878 return HidesContent() && !PresShell()->IsForcingLayoutForHiddenContent(this);
7879}
7880
7881bool nsIFrame::IsHiddenByContentVisibilityOfInFlowParentForLayout() const {
7882 const auto* parent = GetInFlowParent();
7883 // The anonymous children owned by parent are important for properly sizing
7884 // their parents.
7885 return parent && parent->HidesContentForLayout() &&
7886 !(parent->HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES) &&
7887 Style()->IsAnonBox());
7888}
7889
7890nsIFrame* nsIFrame::GetClosestContentVisibilityAncestor(
7891 const EnumSet<IncludeContentVisibility>& aInclude) const {
7892 // Before we walk the frame tree: check PresShell bookkeeping to see if there
7893 // exists *any* frame with a content-visibility value that matches aInclude.
7894 mozilla::PresShell* ps = PresShell();
7895 bool doesAnySuchFrameExist =
7896 (aInclude.contains(IncludeContentVisibility::Hidden) &&
7897 ps->HasContentVisibilityHiddenFrames()) ||
7898 (aInclude.contains(IncludeContentVisibility::Auto) &&
7899 ps->HasContentVisibilityAutoFrames());
7900
7901 if (!doesAnySuchFrameExist) {
7902 return nullptr;
7903 }
7904
7905 auto* parent = GetInFlowParent();
7906 bool isAnonymousBlock = Style()->IsAnonBox() && parent &&
7907 parent->HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES);
7908 for (nsIFrame* cur = parent; cur; cur = cur->GetInFlowParent()) {
7909 if (!isAnonymousBlock && cur->HidesContent(aInclude)) {
7910 return cur;
7911 }
7912
7913 // Anonymous boxes are not hidden by the content-visibility of their first
7914 // non-anonymous ancestor, but can be hidden by ancestors further up the
7915 // tree.
7916 isAnonymousBlock = false;
7917 }
7918
7919 return nullptr;
7920}
7921
7922static bool IsClosedDetailsSlot(const Element* aElement) {
7923 const auto* slot = HTMLSlotElement::FromNodeOrNull(aElement);
7924 if (!slot || slot->HasName()) {
7925 return false;
7926 }
7927 const auto* details =
7928 HTMLDetailsElement::FromNodeOrNull(slot->GetContainingShadowHost());
7929 return details && !details->GetBoolAttr(nsGkAtoms::open);
7930}
7931
7932bool nsIFrame::IsHiddenUntilFoundOrClosedDetails() const {
7933 for (const auto* f = this; f; f = f->GetInFlowParent()) {
7934 if (f->HidesContent(nsIFrame::IncludeContentVisibility::Hidden)) {
7935 if (const auto* element = Element::FromNode(f->GetContent());
7936 element &&
7937 !element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
7938 nsGkAtoms::untilFound, eIgnoreCase) &&
7939 !IsClosedDetailsSlot(element)) {
7940 return false;
7941 }
7942 }
7943 }
7944 return true;
7945}
7946
7947bool nsIFrame::IsHiddenByContentVisibilityOnAnyAncestor(
7948 const EnumSet<IncludeContentVisibility>& aInclude) const {
7949 return !!GetClosestContentVisibilityAncestor(aInclude);
7950}
7951
7952bool nsIFrame::HasSelectionInSubtree() {
7953 if (IsSelected()) {
7954 return true;
7955 }
7956
7957 RefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
7958 if (!frameSelection) {
7959 return false;
7960 }
7961
7962 const Selection& selection = frameSelection->NormalSelection();
7963
7964 for (uint32_t i = 0; i < selection.RangeCount(); i++) {
7965 auto* range = selection.GetRangeAt(i);
7966 MOZ_ASSERT(range)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(range)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(range))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("range", "./../../../layout/generic/nsIFrame.cpp"
, 7966); AnnotateMozCrashReason("MOZ_ASSERT" "(" "range" ")")
; do { MOZ_CrashSequence(__null, 7966); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7967
7968 const auto* commonAncestorNode =
7969 range->GetRegisteredClosestCommonInclusiveAncestor();
7970 if (commonAncestorNode &&
7971 commonAncestorNode->IsInclusiveDescendantOf(GetContent())) {
7972 return true;
7973 }
7974 }
7975
7976 return false;
7977}
7978
7979bool nsIFrame::UpdateIsRelevantContent(
7980 const ContentRelevancy& aRelevancyToUpdate) {
7981 MOZ_ASSERT(StyleDisplay()->ContentVisibility(*this) ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(StyleDisplay()->ContentVisibility(*this) == StyleContentVisibility
::Auto)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(StyleDisplay()->ContentVisibility(*this) == StyleContentVisibility
::Auto))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("StyleDisplay()->ContentVisibility(*this) == StyleContentVisibility::Auto"
, "./../../../layout/generic/nsIFrame.cpp", 7982); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "StyleDisplay()->ContentVisibility(*this) == StyleContentVisibility::Auto"
")"); do { MOZ_CrashSequence(__null, 7982); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
7982 StyleContentVisibility::Auto)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(StyleDisplay()->ContentVisibility(*this) == StyleContentVisibility
::Auto)>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(StyleDisplay()->ContentVisibility(*this) == StyleContentVisibility
::Auto))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("StyleDisplay()->ContentVisibility(*this) == StyleContentVisibility::Auto"
, "./../../../layout/generic/nsIFrame.cpp", 7982); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "StyleDisplay()->ContentVisibility(*this) == StyleContentVisibility::Auto"
")"); do { MOZ_CrashSequence(__null, 7982); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7983
7984 auto* element = Element::FromNodeOrNull(GetContent());
7985 MOZ_ASSERT(element)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(element)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(element))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("element", "./../../../layout/generic/nsIFrame.cpp"
, 7985); AnnotateMozCrashReason("MOZ_ASSERT" "(" "element" ")"
); do { MOZ_CrashSequence(__null, 7985); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7986
7987 ContentRelevancy newRelevancy;
7988 Maybe<ContentRelevancy> oldRelevancy = element->GetContentRelevancy();
7989 if (oldRelevancy.isSome()) {
7990 newRelevancy = *oldRelevancy;
7991 }
7992
7993 auto setRelevancyValue = [&](ContentRelevancyReason reason, bool value) {
7994 if (value) {
7995 newRelevancy += reason;
7996 } else {
7997 newRelevancy -= reason;
7998 }
7999 };
8000
8001 if (!oldRelevancy ||
8002 aRelevancyToUpdate.contains(ContentRelevancyReason::Visible)) {
8003 Maybe<bool> visible = element->GetVisibleForContentVisibility();
8004 if (visible.isSome()) {
8005 setRelevancyValue(ContentRelevancyReason::Visible, *visible);
8006 }
8007 }
8008
8009 if (!oldRelevancy ||
8010 aRelevancyToUpdate.contains(ContentRelevancyReason::FocusInSubtree)) {
8011 setRelevancyValue(ContentRelevancyReason::FocusInSubtree,
8012 element->State().HasAtLeastOneOfStates(
8013 ElementState::FOCUS_WITHIN | ElementState::FOCUS));
8014 }
8015
8016 if (!oldRelevancy ||
8017 aRelevancyToUpdate.contains(ContentRelevancyReason::Selected)) {
8018 setRelevancyValue(ContentRelevancyReason::Selected,
8019 HasSelectionInSubtree());
8020 }
8021
8022 // If the proximity to the viewport has not been determined yet,
8023 // and neither the element nor its contents are focused or selected,
8024 // we should wait for the determination of the proximity. Otherwise,
8025 // there might be a redundant contentvisibilityautostatechange event.
8026 // See https://github.com/w3c/csswg-drafts/issues/9803
8027 bool isProximityToViewportDetermined =
8028 oldRelevancy ? true : element->GetVisibleForContentVisibility().isSome();
8029 if (!isProximityToViewportDetermined && newRelevancy.isEmpty()) {
8030 return false;
8031 }
8032
8033 bool overallRelevancyChanged =
8034 !oldRelevancy || oldRelevancy->isEmpty() != newRelevancy.isEmpty();
8035 if (!oldRelevancy || *oldRelevancy != newRelevancy) {
8036 element->SetContentRelevancy(newRelevancy);
8037 }
8038
8039 if (!overallRelevancyChanged) {
8040 return false;
8041 }
8042
8043 HandleLastRememberedSize();
8044 PresContext()->SetNeedsToUpdateHiddenByContentVisibilityForAnimations();
8045 PresShell()->FrameNeedsReflow(
8046 this, IntrinsicDirty::FrameAncestorsAndDescendants, NS_FRAME_IS_DIRTY);
8047 InvalidateFrame();
8048
8049 ContentVisibilityAutoStateChangeEventInit init;
8050 init.mSkipped = newRelevancy.isEmpty();
8051 RefPtr<ContentVisibilityAutoStateChangeEvent> event =
8052 ContentVisibilityAutoStateChangeEvent::Constructor(
8053 element, u"contentvisibilityautostatechange"_ns, init);
8054
8055 // Per
8056 // https://drafts.csswg.org/css-contain/#content-visibility-auto-state-changed
8057 // "This event is dispatched by posting a task at the time when the state
8058 // change occurs."
8059 auto asyncDispatcher =
8060 MakeRefPtr<AsyncEventDispatcher>(element, event.forget());
8061 DebugOnly<nsresult> rv = asyncDispatcher->PostDOMEvent();
8062 NS_ASSERTION(NS_SUCCEEDED(rv), "AsyncEventDispatcher failed to dispatch")do { if (!(((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1
))))) { NS_DebugBreak(NS_DEBUG_ASSERTION, "AsyncEventDispatcher failed to dispatch"
, "NS_SUCCEEDED(rv)", "./../../../layout/generic/nsIFrame.cpp"
, 8062); MOZ_PretendNoReturn(); } } while (0)
;
8063 return true;
8064}
8065
8066nsresult nsIFrame::CharacterDataChanged(const CharacterDataChangeInfo&) {
8067 MOZ_ASSERT_UNREACHABLE("should only be called for text frames")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"should only be called for text frames" ")", "./../../../layout/generic/nsIFrame.cpp"
, 8067); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "should only be called for text frames"
")"); do { MOZ_CrashSequence(__null, 8067); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8068 return NS_OK;
8069}
8070
8071nsresult nsIFrame::AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute,
8072 AttrModType) {
8073 return NS_OK;
8074}
8075
8076nsIFrame* nsIFrame::GetPrevContinuation() const { return nullptr; }
8077
8078void nsIFrame::SetPrevContinuation(nsIFrame*) {
8079 MOZ_ASSERT_UNREACHABLE("Not splittable!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"Not splittable!" ")", "./../../../layout/generic/nsIFrame.cpp"
, 8079); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Not splittable!" ")"); do { MOZ_CrashSequence
(__null, 8079); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
8080}
8081
8082nsIFrame* nsIFrame::GetNextContinuation() const { return nullptr; }
8083
8084void nsIFrame::SetNextContinuation(nsIFrame*) {
8085 MOZ_ASSERT_UNREACHABLE("Not splittable!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"Not splittable!" ")", "./../../../layout/generic/nsIFrame.cpp"
, 8085); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Not splittable!" ")"); do { MOZ_CrashSequence
(__null, 8085); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
8086}
8087
8088nsIFrame* nsIFrame::GetPrevInFlow() const { return nullptr; }
8089
8090void nsIFrame::SetPrevInFlow(nsIFrame*) {
8091 MOZ_ASSERT_UNREACHABLE("Not splittable!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"Not splittable!" ")", "./../../../layout/generic/nsIFrame.cpp"
, 8091); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Not splittable!" ")"); do { MOZ_CrashSequence
(__null, 8091); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
8092}
8093
8094nsIFrame* nsIFrame::GetNextInFlow() const { return nullptr; }
8095
8096void nsIFrame::SetNextInFlow(nsIFrame*) {
8097 MOZ_ASSERT_UNREACHABLE("Not splittable!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"Not splittable!" ")", "./../../../layout/generic/nsIFrame.cpp"
, 8097); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Not splittable!" ")"); do { MOZ_CrashSequence
(__null, 8097); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
8098}
8099
8100nsIFrame* nsIFrame::GetTailContinuation() {
8101 nsIFrame* frame = this;
8102 while (frame->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
8103 frame = frame->GetPrevContinuation();
8104 NS_ASSERTION(frame, "first continuation can't be overflow container")do { if (!(frame)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "first continuation can't be overflow container"
, "frame", "./../../../layout/generic/nsIFrame.cpp", 8104); MOZ_PretendNoReturn
(); } } while (0)
;
8105 }
8106 for (nsIFrame* next = frame->GetNextContinuation();
8107 next && !next->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
8108 next = frame->GetNextContinuation()) {
8109 frame = next;
8110 }
8111
8112 MOZ_ASSERT(frame, "illegal state in continuation chain.")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(frame)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(frame))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("frame" " (" "illegal state in continuation chain."
")", "./../../../layout/generic/nsIFrame.cpp", 8112); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "frame" ") (" "illegal state in continuation chain."
")"); do { MOZ_CrashSequence(__null, 8112); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8113 return frame;
8114}
8115
8116nsIWidget* nsIFrame::GetOwnWidget() const {
8117 if (IsMenuPopupFrame()) {
8118 return static_cast<const nsMenuPopupFrame*>(this)->GetWidget();
8119 }
8120 if (!GetParent()) {
8121 return PresShell()->GetOwnWidget();
8122 }
8123 return nullptr;
8124}
8125
8126template <nsPoint (nsIFrame::*PositionGetter)() const>
8127static nsPoint OffsetCalculator(const nsIFrame* aThis, const nsIFrame* aOther) {
8128 MOZ_ASSERT(aOther, "Must have frame for destination coordinate system!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aOther)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(aOther))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aOther" " (" "Must have frame for destination coordinate system!"
")", "./../../../layout/generic/nsIFrame.cpp", 8128); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aOther" ") (" "Must have frame for destination coordinate system!"
")"); do { MOZ_CrashSequence(__null, 8128); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8129
8130 NS_ASSERTION(aThis->PresContext() == aOther->PresContext(),do { if (!(aThis->PresContext() == aOther->PresContext(
))) { NS_DebugBreak(NS_DEBUG_ASSERTION, "GetOffsetTo called on frames in different documents"
, "aThis->PresContext() == aOther->PresContext()", "./../../../layout/generic/nsIFrame.cpp"
, 8131); MOZ_PretendNoReturn(); } } while (0)
8131 "GetOffsetTo called on frames in different documents")do { if (!(aThis->PresContext() == aOther->PresContext(
))) { NS_DebugBreak(NS_DEBUG_ASSERTION, "GetOffsetTo called on frames in different documents"
, "aThis->PresContext() == aOther->PresContext()", "./../../../layout/generic/nsIFrame.cpp"
, 8131); MOZ_PretendNoReturn(); } } while (0)
;
8132
8133 nsPoint offset(0, 0);
8134 const nsIFrame* f;
8135 for (f = aThis; f != aOther && f; f = f->GetParent()) {
8136 offset += (f->*PositionGetter)();
8137 }
8138
8139 if (f != aOther) {
8140 // Looks like aOther wasn't an ancestor of |this|. So now we have
8141 // the root-frame-relative position of |this| in |offset|. Convert back
8142 // to the coordinates of aOther
8143 while (aOther) {
8144 offset -= (aOther->*PositionGetter)();
8145 aOther = aOther->GetParent();
8146 }
8147 }
8148
8149 return offset;
8150}
8151
8152nsPoint nsIFrame::GetOffsetTo(const nsIFrame* aOther) const {
8153 return OffsetCalculator<&nsIFrame::GetPosition>(this, aOther);
8154}
8155
8156nsPoint nsIFrame::GetOffsetToRootFrame() const {
8157 return GetOffsetTo(PresShell()->GetRootFrame());
8158}
8159
8160nsPoint nsIFrame::GetOffsetToIgnoringScrolling(const nsIFrame* aOther) const {
8161 return OffsetCalculator<&nsIFrame::GetPositionIgnoringScrolling>(this,
8162 aOther);
8163}
8164
8165nsPoint nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther) const {
8166 return GetOffsetToCrossDoc(aOther, PresContext()->AppUnitsPerDevPixel());
8167}
8168
8169nsPoint nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther,
8170 const int32_t aAPD) const {
8171 MOZ_ASSERT(aOther, "Must have frame for destination coordinate system!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aOther)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(aOther))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aOther" " (" "Must have frame for destination coordinate system!"
")", "./../../../layout/generic/nsIFrame.cpp", 8171); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aOther" ") (" "Must have frame for destination coordinate system!"
")"); do { MOZ_CrashSequence(__null, 8171); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8172 MOZ_DIAGNOSTIC_ASSERT(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(PresContext()->GetRootPresContext() == aOther->
PresContext()->GetRootPresContext())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(PresContext()->GetRootPresContext
() == aOther->PresContext()->GetRootPresContext()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("PresContext()->GetRootPresContext() == aOther->PresContext()->GetRootPresContext()"
" (" "trying to get the offset between frames in different document "
"hierarchies?" ")", "./../../../layout/generic/nsIFrame.cpp"
, 8176); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "PresContext()->GetRootPresContext() == aOther->PresContext()->GetRootPresContext()"
") (" "trying to get the offset between frames in different document "
"hierarchies?" ")"); do { MOZ_CrashSequence(__null, 8176); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
8173 PresContext()->GetRootPresContext() ==do { static_assert( mozilla::detail::AssertionConditionType<
decltype(PresContext()->GetRootPresContext() == aOther->
PresContext()->GetRootPresContext())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(PresContext()->GetRootPresContext
() == aOther->PresContext()->GetRootPresContext()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("PresContext()->GetRootPresContext() == aOther->PresContext()->GetRootPresContext()"
" (" "trying to get the offset between frames in different document "
"hierarchies?" ")", "./../../../layout/generic/nsIFrame.cpp"
, 8176); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "PresContext()->GetRootPresContext() == aOther->PresContext()->GetRootPresContext()"
") (" "trying to get the offset between frames in different document "
"hierarchies?" ")"); do { MOZ_CrashSequence(__null, 8176); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
8174 aOther->PresContext()->GetRootPresContext(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(PresContext()->GetRootPresContext() == aOther->
PresContext()->GetRootPresContext())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(PresContext()->GetRootPresContext
() == aOther->PresContext()->GetRootPresContext()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("PresContext()->GetRootPresContext() == aOther->PresContext()->GetRootPresContext()"
" (" "trying to get the offset between frames in different document "
"hierarchies?" ")", "./../../../layout/generic/nsIFrame.cpp"
, 8176); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "PresContext()->GetRootPresContext() == aOther->PresContext()->GetRootPresContext()"
") (" "trying to get the offset between frames in different document "
"hierarchies?" ")"); do { MOZ_CrashSequence(__null, 8176); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
8175 "trying to get the offset between frames in different document "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(PresContext()->GetRootPresContext() == aOther->
PresContext()->GetRootPresContext())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(PresContext()->GetRootPresContext
() == aOther->PresContext()->GetRootPresContext()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("PresContext()->GetRootPresContext() == aOther->PresContext()->GetRootPresContext()"
" (" "trying to get the offset between frames in different document "
"hierarchies?" ")", "./../../../layout/generic/nsIFrame.cpp"
, 8176); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "PresContext()->GetRootPresContext() == aOther->PresContext()->GetRootPresContext()"
") (" "trying to get the offset between frames in different document "
"hierarchies?" ")"); do { MOZ_CrashSequence(__null, 8176); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
8176 "hierarchies?")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(PresContext()->GetRootPresContext() == aOther->
PresContext()->GetRootPresContext())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(PresContext()->GetRootPresContext
() == aOther->PresContext()->GetRootPresContext()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("PresContext()->GetRootPresContext() == aOther->PresContext()->GetRootPresContext()"
" (" "trying to get the offset between frames in different document "
"hierarchies?" ")", "./../../../layout/generic/nsIFrame.cpp"
, 8176); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "PresContext()->GetRootPresContext() == aOther->PresContext()->GetRootPresContext()"
") (" "trying to get the offset between frames in different document "
"hierarchies?" ")"); do { MOZ_CrashSequence(__null, 8176); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
;
8177
8178 const nsIFrame* root = nullptr;
8179 // offset will hold the final offset
8180 // docOffset holds the currently accumulated offset at the current APD, it
8181 // will be converted and added to offset when the current APD changes.
8182 nsPoint offset(0, 0), docOffset(0, 0);
8183 const nsIFrame* f = this;
8184 int32_t currAPD = PresContext()->AppUnitsPerDevPixel();
8185 while (f && f != aOther) {
8186 docOffset += f->GetPosition();
8187 nsIFrame* parent = f->GetParent();
8188 if (parent) {
8189 f = parent;
8190 } else {
8191 nsPoint newOffset(0, 0);
8192 root = f;
8193 f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f, &newOffset);
8194 int32_t newAPD = f ? f->PresContext()->AppUnitsPerDevPixel() : 0;
8195 if (!f || newAPD != currAPD) {
8196 // Convert docOffset to the right APD and add it to offset.
8197 offset += docOffset.ScaleToOtherAppUnits(currAPD, aAPD);
8198 docOffset.x = docOffset.y = 0;
8199 }
8200 currAPD = newAPD;
8201 docOffset += newOffset;
8202 }
8203 }
8204 if (f == aOther) {
8205 offset += docOffset.ScaleToOtherAppUnits(currAPD, aAPD);
8206 } else {
8207 // Looks like aOther wasn't an ancestor of |this|. So now we have
8208 // the root-document-relative position of |this| in |offset|. Subtract the
8209 // root-document-relative position of |aOther| from |offset|.
8210 // This call won't try to recurse again because root is an ancestor of
8211 // aOther.
8212 nsPoint negOffset = aOther->GetOffsetToCrossDoc(root, aAPD);
8213 offset -= negOffset;
8214 }
8215
8216 return offset;
8217}
8218
8219CSSIntRect nsIFrame::GetScreenRect() const {
8220 return CSSIntRect::FromAppUnitsToNearest(GetScreenRectInAppUnits());
8221}
8222
8223nsRect nsIFrame::GetScreenRectInAppUnits() const {
8224 nsPresContext* presContext = PresContext();
8225 nsIFrame* rootFrame = presContext->PresShell()->GetRootFrame();
8226 nsPoint rootScreenPos(0, 0);
8227 nsPoint rootFrameOffsetInParent(0, 0);
8228 nsIFrame* rootFrameParent = nsLayoutUtils::GetCrossDocParentFrameInProcess(
8229 rootFrame, &rootFrameOffsetInParent);
8230 if (rootFrameParent) {
8231 nsRect parentScreenRectAppUnits =
8232 rootFrameParent->GetScreenRectInAppUnits();
8233 nsPresContext* parentPresContext = rootFrameParent->PresContext();
8234 double parentScale = double(presContext->AppUnitsPerDevPixel()) /
8235 parentPresContext->AppUnitsPerDevPixel();
8236 nsPoint rootPt =
8237 parentScreenRectAppUnits.TopLeft() + rootFrameOffsetInParent;
8238 rootScreenPos.x = NS_round(parentScale * rootPt.x);
8239 rootScreenPos.y = NS_round(parentScale * rootPt.y);
8240 } else if (nsCOMPtr<nsIWidget> rootWidget = presContext->GetRootWidget()) {
8241 LayoutDeviceIntPoint rootDevPx = rootWidget->WidgetToScreenOffset();
8242 rootScreenPos.x = presContext->DevPixelsToAppUnits(rootDevPx.x);
8243 rootScreenPos.y = presContext->DevPixelsToAppUnits(rootDevPx.y);
8244 }
8245
8246 return nsRect(rootScreenPos + GetOffsetTo(rootFrame), GetSize());
8247}
8248
8249nsIWidget* nsIFrame::GetNearestWidget() const {
8250 if (!HasAnyStateBits(NS_FRAME_IN_POPUP)) {
8251 return PresContext()->GetRootWidget();
8252 }
8253 nsPoint unused;
8254 return GetNearestWidget(unused);
8255}
8256
8257nsIWidget* nsIFrame::GetNearestWidget(nsPoint& aOffset) const {
8258 aOffset.MoveTo(0, 0);
8259 nsIFrame* frame = const_cast<nsIFrame*>(this);
8260 const auto targetAPD = PresContext()->AppUnitsPerDevPixel();
8261 auto curAPD = targetAPD;
8262 do {
8263 if (auto* widget = frame->GetOwnWidget()) {
8264 aOffset = aOffset.ScaleToOtherAppUnits(curAPD, targetAPD);
8265 return widget;
8266 }
8267 aOffset += frame->GetPosition();
8268 nsPoint crossDocOffset;
8269 frame =
8270 nsLayoutUtils::GetCrossDocParentFrameInProcess(frame, &crossDocOffset);
8271 if (!frame) {
8272 break;
8273 }
8274 auto newAPD = frame->PresContext()->AppUnitsPerDevPixel();
8275 aOffset = aOffset.ScaleToOtherAppUnits(curAPD, newAPD);
8276 aOffset += crossDocOffset;
8277 curAPD = newAPD;
8278 } while (true);
8279 aOffset = aOffset.ScaleToOtherAppUnits(curAPD, targetAPD);
8280 return PresContext()->GetRootWidget();
8281}
8282
8283Matrix4x4Flagged nsIFrame::GetTransformMatrix(
8284 ViewportType aViewportType, RelativeTo aStopAtAncestor,
8285 nsIFrame** aOutAncestor, TransformMatrixFlags aFlags) const {
8286 MOZ_ASSERT(aOutAncestor, "Need a place to put the ancestor!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aOutAncestor)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aOutAncestor))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("aOutAncestor" " ("
"Need a place to put the ancestor!" ")", "./../../../layout/generic/nsIFrame.cpp"
, 8286); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aOutAncestor"
") (" "Need a place to put the ancestor!" ")"); do { MOZ_CrashSequence
(__null, 8286); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
8287
8288 auto GetPositionMaybeIgnoringScrolling = [aFlags](const nsIFrame* aFrame) {
8289 return aFlags.contains(TransformMatrixFlag::IgnoreScrolling)
8290 ? aFrame->GetPositionIgnoringScrolling()
8291 : aFrame->GetPosition();
8292 };
8293
8294 /* If we're transformed, we want to hand back the combination
8295 * transform/translate matrix that will apply our current transform, then
8296 * shift us to our parent.
8297 */
8298 const bool isTransformed = IsTransformed();
8299 const nsIFrame* zoomedContentRoot = nullptr;
8300 if (aStopAtAncestor.mViewportType == ViewportType::Visual) {
8301 zoomedContentRoot = ViewportUtils::IsZoomedContentRoot(this);
8302 if (zoomedContentRoot) {
8303 MOZ_ASSERT(aViewportType != ViewportType::Visual)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aViewportType != ViewportType::Visual)>::isValid,
"invalid assertion condition"); if ((__builtin_expect(!!(!(!
!(aViewportType != ViewportType::Visual))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aViewportType != ViewportType::Visual"
, "./../../../layout/generic/nsIFrame.cpp", 8303); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aViewportType != ViewportType::Visual" ")"
); do { MOZ_CrashSequence(__null, 8303); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8304 }
8305 }
8306
8307 if (isTransformed || zoomedContentRoot) {
8308 MOZ_ASSERT(GetParent())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(GetParent())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(GetParent()))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("GetParent()", "./../../../layout/generic/nsIFrame.cpp"
, 8308); AnnotateMozCrashReason("MOZ_ASSERT" "(" "GetParent()"
")"); do { MOZ_CrashSequence(__null, 8308); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8309 Matrix4x4Flagged result;
8310 int32_t scaleFactor = aFlags.contains(TransformMatrixFlag::InCSSUnits)
8311 ? AppUnitsPerCSSPixel()
8312 : PresContext()->AppUnitsPerDevPixel();
8313
8314 /* Compute the delta to the parent, which we need because we are converting
8315 * coordinates to our parent.
8316 */
8317 if (isTransformed) {
8318 // Note: this converts from Matrix4x4 to Matrix4x4Flagged.
8319 result = nsDisplayTransform::GetResultingTransformMatrix(
8320 this, nsPoint(), scaleFactor,
8321 nsDisplayTransform::INCLUDE_PERSPECTIVE);
8322 }
8323
8324 // The offset from a zoomed content root to its parent (e.g. from
8325 // a canvas frame to a scroll frame) is in layout coordinates, so
8326 // apply it before applying any layout-to-visual transform.
8327 *aOutAncestor = GetParent();
8328 nsPoint delta = GetPositionMaybeIgnoringScrolling(this);
8329 /* Combine the raw transform with a translation to our parent. */
8330 result.PostTranslate(NSAppUnitsToFloatPixels(delta.x, scaleFactor),
8331 NSAppUnitsToFloatPixels(delta.y, scaleFactor), 0.0f);
8332
8333 if (zoomedContentRoot) {
8334 Matrix4x4Flagged layoutToVisual;
8335 if (aFlags.contains(TransformMatrixFlag::InCSSUnits)) {
8336 layoutToVisual = ViewportUtils::GetVisualToLayoutTransform(
8337 zoomedContentRoot->GetContent())
8338 .Inverse()
8339 .ToUnknownMatrix();
8340 } else {
8341 layoutToVisual =
8342 ViewportUtils::GetVisualToLayoutTransform<LayoutDevicePixel>(
8343 zoomedContentRoot->GetContent())
8344 .Inverse()
8345 .ToUnknownMatrix();
8346 }
8347 result = result * layoutToVisual;
8348 }
8349
8350 return result;
8351 }
8352
8353 // We are not transformed, so the returned transform is just going to be a
8354 // translation up to whatever ancestor we decide to stop at.
8355
8356 nsPoint crossdocOffset;
8357 *aOutAncestor =
8358 nsLayoutUtils::GetCrossDocParentFrameInProcess(this, &crossdocOffset);
8359
8360 /* Otherwise, we're not transformed. In that case, we'll walk up the frame
8361 * tree until we either hit the root frame or something that may be
8362 * transformed. We'll then change coordinates into that frame, since we're
8363 * guaranteed that nothing in-between can be transformed. First, however,
8364 * we have to check to see if we have a parent. If not, we'll set the
8365 * outparam to null (indicating that there's nothing left) and will hand back
8366 * the identity matrix.
8367 */
8368 if (!*aOutAncestor) {
8369 return Matrix4x4Flagged();
8370 }
8371
8372 /* Keep iterating while the frame can't possibly be transformed. */
8373 const nsIFrame* current = this;
8374 auto shouldStopAt = [](const nsIFrame* aCurrent, RelativeTo& aStopAtAncestor,
8375 nsIFrame* aOutAncestor, TransformMatrixFlags aFlags) {
8376 return aOutAncestor->IsTransformed() ||
8377 ((aStopAtAncestor.mViewportType == ViewportType::Visual) &&
8378 ViewportUtils::IsZoomedContentRoot(aOutAncestor)) ||
8379 (aFlags.contains(
8380 TransformMatrixFlag::StopAtStackingContextAndDisplayPort) &&
8381 (aOutAncestor->IsStackingContext() ||
8382 DisplayPortUtils::FrameHasDisplayPort(aOutAncestor, aCurrent)));
8383 };
8384
8385 // We run the GetOffsetToCrossDoc code here as an optimization, instead of
8386 // walking the parent chain here and then asking GetOffsetToCrossDoc to walk
8387 // the same parent chain and compute the offset.
8388 const int32_t finalAPD = PresContext()->AppUnitsPerDevPixel();
8389 // offset accumulates the offset at finalAPD.
8390 nsPoint offset = GetPositionMaybeIgnoringScrolling(this);
8391
8392 int32_t currAPD = (*aOutAncestor)->PresContext()->AppUnitsPerDevPixel();
8393 // docOffset accumulates the current offset at currAPD, and then flushes to
8394 // offset at finalAPD when the APD changes or we finish.
8395 nsPoint docOffset = crossdocOffset;
8396 MOZ_ASSERT(crossdocOffset == nsPoint(0, 0) || !GetParent())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(crossdocOffset == nsPoint(0, 0) || !GetParent())>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(crossdocOffset == nsPoint(0, 0) || !GetParent()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("crossdocOffset == nsPoint(0, 0) || !GetParent()"
, "./../../../layout/generic/nsIFrame.cpp", 8396); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "crossdocOffset == nsPoint(0, 0) || !GetParent()"
")"); do { MOZ_CrashSequence(__null, 8396); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8397
8398 while (*aOutAncestor != aStopAtAncestor.mFrame &&
8399 !shouldStopAt(current, aStopAtAncestor, *aOutAncestor, aFlags)) {
8400 docOffset += GetPositionMaybeIgnoringScrolling(*aOutAncestor);
8401
8402 nsIFrame* parent = (*aOutAncestor)->GetParent();
8403 if (!parent) {
8404 crossdocOffset.x = crossdocOffset.y = 0;
8405 parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(*aOutAncestor,
8406 &crossdocOffset);
8407
8408 int32_t newAPD =
8409 parent ? parent->PresContext()->AppUnitsPerDevPixel() : currAPD;
8410 if (!parent || newAPD != currAPD) {
8411 // Convert docOffset to finalAPD and add it to offset.
8412 offset += docOffset.ScaleToOtherAppUnits(currAPD, finalAPD);
8413 docOffset.x = docOffset.y = 0;
8414 }
8415 currAPD = newAPD;
8416 docOffset += crossdocOffset;
8417
8418 if (!parent) {
8419 break;
8420 }
8421 }
8422
8423 current = *aOutAncestor;
8424 *aOutAncestor = parent;
8425 }
8426 offset += docOffset.ScaleToOtherAppUnits(currAPD, finalAPD);
8427
8428 NS_ASSERTION(*aOutAncestor, "Somehow ended up with a null ancestor...?")do { if (!(*aOutAncestor)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "Somehow ended up with a null ancestor...?", "*aOutAncestor"
, "./../../../layout/generic/nsIFrame.cpp", 8428); MOZ_PretendNoReturn
(); } } while (0)
;
8429
8430 int32_t scaleFactor = aFlags.contains(TransformMatrixFlag::InCSSUnits)
8431 ? AppUnitsPerCSSPixel()
8432 : PresContext()->AppUnitsPerDevPixel();
8433 return Matrix4x4Flagged::Translation2d(
8434 NSAppUnitsToFloatPixels(offset.x, scaleFactor),
8435 NSAppUnitsToFloatPixels(offset.y, scaleFactor));
8436}
8437
8438static void InvalidateRenderingObservers(nsIFrame* aDisplayRoot,
8439 nsIFrame* aFrame,
8440 bool aFrameChanged = true) {
8441 MOZ_ASSERT(aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame)"
, "./../../../layout/generic/nsIFrame.cpp", 8441); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame)"
")"); do { MOZ_CrashSequence(__null, 8441); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8442 SVGObserverUtils::InvalidateDirectRenderingObservers(aFrame);
8443 nsIFrame* parent = aFrame;
8444 while (parent != aDisplayRoot &&
8445 (parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(parent)) &&
8446 !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
8447 SVGObserverUtils::InvalidateDirectRenderingObservers(parent);
8448 }
8449
8450 if (!aFrameChanged) {
8451 return;
8452 }
8453
8454 aFrame->MarkNeedsDisplayItemRebuild();
8455}
8456
8457static void SchedulePaintInternal(
8458 nsIFrame* aDisplayRoot, nsIFrame* aFrame,
8459 nsIFrame::PaintType aType = nsIFrame::PAINT_DEFAULT) {
8460 MOZ_ASSERT(aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame)"
, "./../../../layout/generic/nsIFrame.cpp", 8460); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame)"
")"); do { MOZ_CrashSequence(__null, 8460); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8461 nsPresContext* pres = aDisplayRoot->PresContext()->GetRootPresContext();
8462
8463 // No need to schedule a paint for an external document since they aren't
8464 // painted directly.
8465 if (!pres || (pres->Document() && pres->Document()->IsResourceDoc())) {
8466 return;
8467 }
8468 if (!pres->GetContainerWeak()) {
8469 NS_WARNING("Shouldn't call SchedulePaint in a detached pres context")NS_DebugBreak(NS_DEBUG_WARNING, "Shouldn't call SchedulePaint in a detached pres context"
, nullptr, "./../../../layout/generic/nsIFrame.cpp", 8469)
;
8470 return;
8471 }
8472
8473 pres->PresShell()->SchedulePaint();
8474
8475 if (aType == nsIFrame::PAINT_DEFAULT) {
8476 aDisplayRoot->AddStateBits(NS_FRAME_UPDATE_LAYER_TREE);
8477 }
8478}
8479
8480static void InvalidateFrameInternal(nsIFrame* aFrame, bool aHasDisplayItem,
8481 bool aRebuildDisplayItems) {
8482 if (aHasDisplayItem) {
8483 aFrame->AddStateBits(NS_FRAME_NEEDS_PAINT);
8484 }
8485
8486 if (aRebuildDisplayItems) {
8487 aFrame->MarkNeedsDisplayItemRebuild();
8488 }
8489 SVGObserverUtils::InvalidateDirectRenderingObservers(aFrame);
8490 bool needsSchedulePaint = false;
8491 if (nsLayoutUtils::IsPopup(aFrame)) {
8492 needsSchedulePaint = true;
8493 } else {
8494 nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
8495 while (parent &&
8496 !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
8497 if (aHasDisplayItem && !parent->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
8498 parent->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
8499 }
8500 SVGObserverUtils::InvalidateDirectRenderingObservers(parent);
8501
8502 // If we're inside a popup, then we need to make sure that we
8503 // call schedule paint so that the NS_FRAME_UPDATE_LAYER_TREE
8504 // flag gets added to the popup display root frame.
8505 if (nsLayoutUtils::IsPopup(parent)) {
8506 needsSchedulePaint = true;
8507 break;
8508 }
8509 parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(parent);
8510 }
8511 if (!parent) {
8512 needsSchedulePaint = true;
8513 }
8514 }
8515 if (!aHasDisplayItem) {
8516 return;
8517 }
8518 if (needsSchedulePaint) {
8519 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
8520 SchedulePaintInternal(displayRoot, aFrame);
8521 }
8522 if (aFrame->HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
8523 aFrame->RemoveProperty(nsIFrame::InvalidationRect());
8524 aFrame->RemoveStateBits(NS_FRAME_HAS_INVALID_RECT);
8525 }
8526}
8527
8528void nsIFrame::InvalidateFrameSubtree(bool aRebuildDisplayItems /* = true */) {
8529 InvalidateFrame(0, aRebuildDisplayItems);
8530
8531 if (HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT)) {
8532 return;
8533 }
8534
8535 AddStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT);
8536
8537 for (const auto& childList : CrossDocChildLists()) {
8538 for (nsIFrame* child : childList.mList) {
8539 // Don't explicitly rebuild display items for our descendants,
8540 // since we should be marked and it implicitly includes all
8541 // descendants.
8542 child->InvalidateFrameSubtree(false);
8543 }
8544 }
8545}
8546
8547void nsIFrame::ClearInvalidationStateBits() {
8548 if (HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
8549 for (const auto& childList : CrossDocChildLists()) {
8550 for (nsIFrame* child : childList.mList) {
8551 child->ClearInvalidationStateBits();
8552 }
8553 }
8554 }
8555
8556 RemoveStateBits(NS_FRAME_NEEDS_PAINT | NS_FRAME_DESCENDANT_NEEDS_PAINT |
8557 NS_FRAME_ALL_DESCENDANTS_NEED_PAINT);
8558}
8559
8560bool HasRetainedDataFor(const nsIFrame* aFrame, uint32_t aDisplayItemKey) {
8561 if (RefPtr<WebRenderUserData> data =
8562 GetWebRenderUserData<WebRenderFallbackData>(aFrame,
8563 aDisplayItemKey)) {
8564 return true;
8565 }
8566
8567 return false;
8568}
8569
8570void nsIFrame::InvalidateFrame(uint32_t aDisplayItemKey,
8571 bool aRebuildDisplayItems /* = true */) {
8572 bool hasDisplayItem =
8573 !aDisplayItemKey || HasRetainedDataFor(this, aDisplayItemKey);
8574 InvalidateFrameInternal(this, hasDisplayItem, aRebuildDisplayItems);
8575}
8576
8577void nsIFrame::InvalidateFrameWithRect(const nsRect& aRect,
8578 uint32_t aDisplayItemKey,
8579 bool aRebuildDisplayItems /* = true */) {
8580 if (aRect.IsEmpty()) {
8581 return;
8582 }
8583 bool hasDisplayItem =
8584 !aDisplayItemKey || HasRetainedDataFor(this, aDisplayItemKey);
8585 bool alreadyInvalid = false;
8586 if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) {
8587 InvalidateFrameInternal(this, hasDisplayItem, aRebuildDisplayItems);
8588 } else {
8589 alreadyInvalid = true;
8590 }
8591
8592 if (!hasDisplayItem) {
8593 return;
8594 }
8595
8596 nsRect* rect;
8597 if (HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
8598 rect = GetProperty(InvalidationRect());
8599 MOZ_ASSERT(rect)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(rect)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(rect))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("rect", "./../../../layout/generic/nsIFrame.cpp"
, 8599); AnnotateMozCrashReason("MOZ_ASSERT" "(" "rect" ")");
do { MOZ_CrashSequence(__null, 8599); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8600 } else {
8601 if (alreadyInvalid) {
8602 return;
8603 }
8604 rect = new nsRect();
8605 AddProperty(InvalidationRect(), rect);
8606 AddStateBits(NS_FRAME_HAS_INVALID_RECT);
8607 }
8608
8609 *rect = rect->Union(aRect);
8610}
8611
8612bool nsIFrame::IsInvalid(nsRect& aRect) {
8613 if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) {
8614 return false;
8615 }
8616
8617 if (HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
8618 nsRect* rect = GetProperty(InvalidationRect());
8619 NS_ASSERTION(do { if (!(rect)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "Must have an invalid rect if NS_FRAME_HAS_INVALID_RECT is set!"
, "rect", "./../../../layout/generic/nsIFrame.cpp", 8620); MOZ_PretendNoReturn
(); } } while (0)
8620 rect, "Must have an invalid rect if NS_FRAME_HAS_INVALID_RECT is set!")do { if (!(rect)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "Must have an invalid rect if NS_FRAME_HAS_INVALID_RECT is set!"
, "rect", "./../../../layout/generic/nsIFrame.cpp", 8620); MOZ_PretendNoReturn
(); } } while (0)
;
8621 aRect = *rect;
8622 } else {
8623 aRect.SetEmpty();
8624 }
8625 return true;
8626}
8627
8628void nsIFrame::SchedulePaint(PaintType aType, bool aFrameChanged) {
8629 if (PresShell()->IsPaintingSuppressed()) {
8630 // We can't have any display items yet, and when we unsuppress we will
8631 // invalidate the root frame.
8632 return;
8633 }
8634 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
8635 InvalidateRenderingObservers(displayRoot, this, aFrameChanged);
8636 SchedulePaintInternal(displayRoot, this, aType);
8637}
8638
8639void nsIFrame::SchedulePaintWithoutInvalidatingObservers(PaintType aType) {
8640 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
8641 SchedulePaintInternal(displayRoot, this, aType);
8642}
8643
8644void nsIFrame::InvalidateLayer(DisplayItemType aDisplayItemKey,
8645 const nsIntRect* aDamageRect,
8646 const nsRect* aFrameDamageRect,
8647 uint32_t aFlags /* = 0 */) {
8648 NS_ASSERTION(aDisplayItemKey > DisplayItemType::TYPE_ZERO, "Need a key")do { if (!(aDisplayItemKey > DisplayItemType::TYPE_ZERO)) {
NS_DebugBreak(NS_DEBUG_ASSERTION, "Need a key", "aDisplayItemKey > DisplayItemType::TYPE_ZERO"
, "./../../../layout/generic/nsIFrame.cpp", 8648); MOZ_PretendNoReturn
(); } } while (0)
;
8649
8650 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
8651 InvalidateRenderingObservers(displayRoot, this, false);
8652
8653 // Check if frame supports WebRender's async update
8654 if ((aFlags & UPDATE_IS_ASYNC) &&
8655 WebRenderUserData::SupportsAsyncUpdate(this)) {
8656 // WebRender does not use layer, then return nullptr.
8657 return;
8658 }
8659
8660 if (aFrameDamageRect && aFrameDamageRect->IsEmpty()) {
8661 return;
8662 }
8663
8664 // In the bug 930056, dialer app startup but not shown on the
8665 // screen because sometimes we don't have any retainned data
8666 // for remote type displayitem and thus Repaint event is not
8667 // triggered. So, always invalidate in this case.
8668 DisplayItemType displayItemKey = aDisplayItemKey;
8669 if (aDisplayItemKey == DisplayItemType::TYPE_REMOTE) {
8670 displayItemKey = DisplayItemType::TYPE_ZERO;
8671 }
8672
8673 if (aFrameDamageRect) {
8674 InvalidateFrameWithRect(*aFrameDamageRect,
8675 static_cast<uint32_t>(displayItemKey));
8676 } else {
8677 InvalidateFrame(static_cast<uint32_t>(displayItemKey));
8678 }
8679}
8680
8681static nsRect ComputeEffectsRect(nsIFrame* aFrame, const nsRect& aOverflowRect,
8682 const nsSize& aNewSize) {
8683 nsRect r = aOverflowRect;
8684
8685 if (aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
8686 // For SVG frames, we only need to account for filters.
8687 // TODO: We could also take account of clipPath and mask to reduce the
8688 // ink overflow, but that's not essential.
8689 if (aFrame->StyleEffects()->HasFilters()) {
8690 aFrame->SetOrUpdateDeletableProperty(nsIFrame::PreEffectsBBoxProperty(),
8691 r);
8692 r = SVGUtils::GetPostFilterInkOverflowRect(aFrame, aOverflowRect);
8693 }
8694 return r;
8695 }
8696
8697 // box-shadow
8698 r.UnionRect(r, nsLayoutUtils::GetBoxShadowRectForFrame(aFrame, aNewSize));
8699
8700 // border-image-outset.
8701 // We need to include border-image-outset because it can cause the
8702 // border image to be drawn beyond the border box.
8703
8704 // (1) It's important we not check whether there's a border-image
8705 // since the style hint for a change in border image doesn't cause
8706 // reflow, and that's probably more important than optimizing the
8707 // overflow areas for the silly case of border-image-outset without
8708 // border-image
8709 // (2) It's important that we not check whether the border-image
8710 // is actually loaded, since that would require us to reflow when
8711 // the image loads.
8712 const nsStyleBorder* styleBorder = aFrame->StyleBorder();
8713 nsMargin outsetMargin = styleBorder->GetImageOutset();
8714
8715 if (outsetMargin != nsMargin(0, 0, 0, 0)) {
8716 nsRect outsetRect(nsPoint(0, 0), aNewSize);
8717 outsetRect.Inflate(outsetMargin);
8718 r.UnionRect(r, outsetRect);
8719 }
8720
8721 // Note that we don't remove the outlineInnerRect if a frame loses outline
8722 // style. That would require an extra property lookup for every frame,
8723 // or a new frame state bit to track whether a property had been stored,
8724 // or something like that. It's not worth doing that here. At most it's
8725 // only one heap-allocated rect per frame and it will be cleaned up when
8726 // the frame dies.
8727
8728 if (SVGIntegrationUtils::UsingOverflowAffectingEffects(aFrame)) {
8729 aFrame->SetOrUpdateDeletableProperty(nsIFrame::PreEffectsBBoxProperty(), r);
8730 r = SVGIntegrationUtils::ComputePostEffectsInkOverflowRect(aFrame, r);
8731 }
8732
8733 return r;
8734}
8735
8736void nsIFrame::SetPosition(const nsPoint& aPt) {
8737 if (mRect.TopLeft() == aPt) {
8738 return;
8739 }
8740 mRect.MoveTo(aPt);
8741 MarkNeedsDisplayItemRebuild();
8742}
8743
8744void nsIFrame::MovePositionBy(const nsPoint& aTranslation) {
8745 nsPoint position = GetNormalPosition() + aTranslation;
8746
8747 const nsMargin* computedOffsets = nullptr;
8748 if (IsRelativelyOrStickyPositioned()) {
8749 computedOffsets = GetProperty(nsIFrame::ComputedOffsetProperty());
8750 }
8751 ReflowInput::ApplyRelativePositioning(
8752 this, computedOffsets ? *computedOffsets : nsMargin(), &position);
8753 SetPosition(position);
8754}
8755
8756nsRect nsIFrame::GetNormalRect() const {
8757 bool hasProperty;
8758 nsPoint normalPosition = GetProperty(NormalPositionProperty(), &hasProperty);
8759 if (hasProperty) {
8760 return nsRect(normalPosition, GetSize());
8761 }
8762 return GetRect();
8763}
8764
8765nsRect nsIFrame::GetBoundingClientRect() {
8766 return nsLayoutUtils::GetAllInFlowRectsUnion(
8767 this, nsLayoutUtils::GetContainingBlockForClientRect(this),
8768 nsLayoutUtils::GetAllInFlowRectsFlag::AccountForTransforms);
8769}
8770
8771nsPoint nsIFrame::GetPositionIgnoringScrolling() const {
8772 return GetParent() ? GetParent()->GetPositionOfChildIgnoringScrolling(this)
8773 : GetPosition();
8774}
8775
8776nsRect nsIFrame::GetOverflowRect(OverflowType aType) const {
8777 // Note that in some cases the overflow area might not have been
8778 // updated (yet) to reflect any outline set on the frame or the area
8779 // of child frames. That's OK because any reflow that updates these
8780 // areas will invalidate the appropriate area, so any (mis)uses of
8781 // this method will be fixed up.
8782
8783 if (mOverflow.mType == OverflowStorageType::Large) {
8784 // there is an overflow rect, and it's not stored as deltas but as
8785 // a separately-allocated rect
8786 return GetOverflowAreasProperty()->Overflow(aType);
8787 }
8788
8789 if (aType == OverflowType::Ink &&
8790 mOverflow.mType != OverflowStorageType::None) {
8791 return InkOverflowFromDeltas();
8792 }
8793
8794 return GetRectRelativeToSelf();
8795}
8796
8797OverflowAreas nsIFrame::GetOverflowAreas() const {
8798 if (mOverflow.mType == OverflowStorageType::Large) {
8799 // there is an overflow rect, and it's not stored as deltas but as
8800 // a separately-allocated rect
8801 return *GetOverflowAreasProperty();
8802 }
8803
8804 return OverflowAreas(InkOverflowFromDeltas(),
8805 nsRect(nsPoint(0, 0), GetSize()));
8806}
8807
8808OverflowAreas nsIFrame::GetOverflowAreasRelativeToSelf() const {
8809 if (IsTransformed()) {
8810 if (OverflowAreas* preTransformOverflows =
8811 GetProperty(PreTransformOverflowAreasProperty())) {
8812 return *preTransformOverflows;
8813 }
8814 }
8815 return GetOverflowAreas();
8816}
8817
8818OverflowAreas nsIFrame::GetOverflowAreasRelativeToParent() const {
8819 return GetOverflowAreas() + GetPosition();
8820}
8821
8822OverflowAreas nsIFrame::GetActualAndNormalOverflowAreasRelativeToParent()
8823 const {
8824 if (MOZ_LIKELY(!IsRelativelyOrStickyPositioned())(__builtin_expect(!!(!IsRelativelyOrStickyPositioned()), 1))) {
8825 return GetOverflowAreasRelativeToParent();
8826 }
8827
8828 const OverflowAreas overflows = GetOverflowAreas();
8829 OverflowAreas actualAndNormalOverflows = overflows + GetNormalPosition();
8830 if (IsRelativelyPositioned()) {
8831 actualAndNormalOverflows.UnionWith(overflows + GetPosition());
8832 } else {
8833 // For sticky positioned elements, we only use the normal position for the
8834 // scrollable overflow. This avoids circular dependencies between sticky
8835 // positioned elements and their scroll container. (The scroll position and
8836 // the scroll container's size impact the sticky position, so we don't want
8837 // the sticky position to impact them.)
8838 MOZ_ASSERT(IsStickyPositioned())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsStickyPositioned())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsStickyPositioned()))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("IsStickyPositioned()"
, "./../../../layout/generic/nsIFrame.cpp", 8838); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsStickyPositioned()" ")"); do { MOZ_CrashSequence
(__null, 8838); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
8839 actualAndNormalOverflows.UnionWith(
8840 OverflowAreas(overflows.InkOverflow() + GetPosition(), nsRect()));
8841 }
8842 return actualAndNormalOverflows;
8843}
8844
8845nsRect nsIFrame::ScrollableOverflowRectRelativeToParent() const {
8846 return ScrollableOverflowRect() + GetPosition();
8847}
8848
8849nsRect nsIFrame::InkOverflowRectRelativeToParent() const {
8850 return InkOverflowRect() + GetPosition();
8851}
8852
8853nsRect nsIFrame::ScrollableOverflowRectRelativeToSelf() const {
8854 if (IsTransformed()) {
8855 if (OverflowAreas* preTransformOverflows =
8856 GetProperty(PreTransformOverflowAreasProperty())) {
8857 return preTransformOverflows->ScrollableOverflow();
8858 }
8859 }
8860 return ScrollableOverflowRect();
8861}
8862
8863nsRect nsIFrame::InkOverflowRectRelativeToSelf() const {
8864 if (IsTransformed()) {
8865 if (OverflowAreas* preTransformOverflows =
8866 GetProperty(PreTransformOverflowAreasProperty())) {
8867 return preTransformOverflows->InkOverflow();
8868 }
8869 }
8870 return InkOverflowRect();
8871}
8872
8873nsRect nsIFrame::PreEffectsInkOverflowRect() const {
8874 nsRect* r = GetProperty(nsIFrame::PreEffectsBBoxProperty());
8875 return r ? *r : InkOverflowRectRelativeToSelf();
8876}
8877
8878bool nsIFrame::UpdateOverflow() {
8879 MOZ_ASSERT(FrameMaintainsOverflow(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(FrameMaintainsOverflow())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(FrameMaintainsOverflow()))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("FrameMaintainsOverflow()"
" (" "Non-display SVG do not maintain ink overflow rects" ")"
, "./../../../layout/generic/nsIFrame.cpp", 8880); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "FrameMaintainsOverflow()" ") (" "Non-display SVG do not maintain ink overflow rects"
")"); do { MOZ_CrashSequence(__null, 8880); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
8880 "Non-display SVG do not maintain ink overflow rects")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(FrameMaintainsOverflow())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(FrameMaintainsOverflow()))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("FrameMaintainsOverflow()"
" (" "Non-display SVG do not maintain ink overflow rects" ")"
, "./../../../layout/generic/nsIFrame.cpp", 8880); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "FrameMaintainsOverflow()" ") (" "Non-display SVG do not maintain ink overflow rects"
")"); do { MOZ_CrashSequence(__null, 8880); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8881
8882 nsRect rect(nsPoint(0, 0), GetSize());
8883 OverflowAreas overflowAreas(rect, rect);
8884
8885 if (!ComputeCustomOverflow(overflowAreas)) {
8886 // If updating overflow wasn't supported by this frame, then it should
8887 // have scheduled any necessary reflows. We can return false to say nothing
8888 // changed, and wait for reflow to correct it.
8889 return false;
8890 }
8891
8892 UnionChildOverflow(overflowAreas);
8893
8894 if (FinishAndStoreOverflow(overflowAreas, GetSize())) {
8895 return true;
8896 }
8897
8898 // Frames that combine their 3d transform with their ancestors
8899 // only compute a pre-transform overflow rect, and then contribute
8900 // to the normal overflow rect of the preserve-3d root. Always return
8901 // true here so that we propagate changes up to the root for final
8902 // calculation.
8903 return Combines3DTransformWithAncestors();
8904}
8905
8906/* virtual */
8907bool nsIFrame::ComputeCustomOverflow(OverflowAreas& aOverflowAreas) {
8908 return true;
8909}
8910
8911bool nsIFrame::DoesClipChildrenInBothAxes() const {
8912 if (IsScrollContainerOrSubclass()) {
8913 return true;
8914 }
8915 const nsStyleDisplay* display = StyleDisplay();
8916 if (display->IsContainPaint() && SupportsContainLayoutAndPaint()) {
8917 return true;
8918 }
8919 return display->mOverflowX == StyleOverflow::Clip &&
8920 display->mOverflowY == StyleOverflow::Clip;
8921}
8922
8923/* virtual */
8924void nsIFrame::UnionChildOverflow(OverflowAreas& aOverflowAreas,
8925 bool aAsIfScrolled) {
8926 if (aAsIfScrolled || !DoesClipChildrenInBothAxes()) {
8927 nsLayoutUtils::UnionChildOverflow(this, aOverflowAreas);
8928 }
8929}
8930
8931// Return true if this form control element's preferred size property (but not
8932// percentage max size property) contains a percentage value that should be
8933// resolved against zero when calculating its min-content contribution in the
8934// corresponding axis.
8935//
8936// For proper replaced elements, the percentage value in both their max size
8937// property or preferred size property should be resolved against zero. This is
8938// handled in IsPercentageResolvedAgainstZero().
8939inline static bool FormControlShrinksForPercentSize(const nsIFrame* aFrame) {
8940 if (!aFrame->IsReplaced()) {
8941 // Quick test to reject most frames.
8942 return false;
8943 }
8944
8945 switch (aFrame->Type()) {
8946 case LayoutFrameType::Progress:
8947 case LayoutFrameType::Range:
8948 case LayoutFrameType::TextInput:
8949 case LayoutFrameType::ColorControl:
8950 case LayoutFrameType::ComboboxControl:
8951 case LayoutFrameType::ListControl:
8952 case LayoutFrameType::CheckboxRadio:
8953 case LayoutFrameType::FileControl:
8954 case LayoutFrameType::ImageControl:
8955 return true;
8956 default:
8957 // True buttons (<button>, backed by block/grid/flex frame) and most
8958 // button-flavored <inputs> (those backed by InputButtonControlFrame)
8959 // don't have this shrinking behavior. But color-inputs and comboboxes do;
8960 // and both of those derive from ButtonControlFrame. So: we can't easily
8961 // use do_QueryFrame to differentiate the buttons-that-do vs. the
8962 // buttons-that-don't. So we explicitly list the buttons-that-do by
8963 // LayoutFrameType above, and the others fall into this catch-all.
8964 return false;
8965 }
8966}
8967
8968bool nsIFrame::IsPercentageResolvedAgainstZero(
8969 const StyleSize& aStyleSize, const StyleMaxSize& aStyleMaxSize) const {
8970 const bool sizeHasPercent = aStyleSize.HasPercent();
8971 return ((sizeHasPercent || aStyleMaxSize.HasPercent()) &&
8972 HasReplacedSizing()) ||
8973 (sizeHasPercent && FormControlShrinksForPercentSize(this));
8974}
8975
8976// Summary of the Cyclic-Percentage Intrinsic Size Contribution Rules:
8977//
8978// Element Type | Replaced | Non-replaced
8979// Contribution Type | min-content max-content | min-content max-content
8980// ---------------------------------------------------------------------------
8981// min size | zero zero | zero zero
8982// max & preferred size | zero initial | initial initial
8983//
8984// https://drafts.csswg.org/css-sizing-3/#cyclic-percentage-contribution
8985bool nsIFrame::IsPercentageResolvedAgainstZero(const LengthPercentage& aSize,
8986 SizeProperty aProperty) const {
8987 // Early return to avoid calling the virtual function, IsFrameOfType().
8988 if (aProperty == SizeProperty::MinSize) {
8989 return true;
8990 }
8991
8992 const bool hasPercentOnReplaced = aSize.HasPercent() && HasReplacedSizing();
8993 if (aProperty == SizeProperty::MaxSize) {
8994 return hasPercentOnReplaced;
8995 }
8996
8997 MOZ_ASSERT(aProperty == SizeProperty::Size)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aProperty == SizeProperty::Size)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aProperty == SizeProperty::Size
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"aProperty == SizeProperty::Size", "./../../../layout/generic/nsIFrame.cpp"
, 8997); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aProperty == SizeProperty::Size"
")"); do { MOZ_CrashSequence(__null, 8997); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8998 return hasPercentOnReplaced ||
8999 (aSize.HasPercent() && FormControlShrinksForPercentSize(this));
9000}
9001
9002bool nsIFrame::IsBlockWrapper() const {
9003 auto pseudoType = Style()->GetPseudoType();
9004 return pseudoType == PseudoStyleType::MozBlockInsideInlineWrapper ||
9005 pseudoType == PseudoStyleType::MozCellContent ||
9006 pseudoType == PseudoStyleType::MozColumnSpanWrapper;
9007}
9008
9009bool nsIFrame::IsBlockFrameOrSubclass() const {
9010 const nsBlockFrame* thisAsBlock = do_QueryFrame(this);
9011 return !!thisAsBlock;
9012}
9013
9014bool nsIFrame::IsInlineFrameOrSubclass() const {
9015 const nsInlineFrame* asInline = do_QueryFrame(this);
9016 return !!asInline;
9017}
9018
9019bool nsIFrame::IsImageFrameOrSubclass() const {
9020 const nsImageFrame* asImage = do_QueryFrame(this);
9021 return !!asImage;
9022}
9023
9024bool nsIFrame::IsScrollContainerOrSubclass() const {
9025 const ScrollContainerFrame* asScrollContainer = do_QueryFrame(this);
9026 return !!asScrollContainer;
9027}
9028
9029bool nsIFrame::IsSubgrid() const {
9030 return IsGridContainerFrame() &&
9031 static_cast<const nsGridContainerFrame*>(this)->IsSubgrid();
9032}
9033
9034static nsIFrame* GetNearestBlockContainer(nsIFrame* frame) {
9035 while (!frame->IsBlockContainer()) {
9036 frame = frame->GetParent();
9037 NS_ASSERTION(do { if (!(frame)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "How come we got to the root frame without seeing a containing block?"
, "frame", "./../../../layout/generic/nsIFrame.cpp", 9039); MOZ_PretendNoReturn
(); } } while (0)
9038 frame,do { if (!(frame)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "How come we got to the root frame without seeing a containing block?"
, "frame", "./../../../layout/generic/nsIFrame.cpp", 9039); MOZ_PretendNoReturn
(); } } while (0)
9039 "How come we got to the root frame without seeing a containing block?")do { if (!(frame)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "How come we got to the root frame without seeing a containing block?"
, "frame", "./../../../layout/generic/nsIFrame.cpp", 9039); MOZ_PretendNoReturn
(); } } while (0)
;
9040 }
9041 return frame;
9042}
9043
9044bool nsIFrame::IsBlockContainer() const {
9045 // The block wrappers we use to wrap blocks inside inlines aren't
9046 // described in the CSS spec. We need to make them not be containing
9047 // blocks.
9048 // Since the parent of such a block is either a normal block or
9049 // another such pseudo, this shouldn't cause anything bad to happen.
9050 // Also the anonymous blocks inside table cells are not containing blocks.
9051 //
9052 // If we ever start skipping table row groups from being containing blocks,
9053 // you need to remove the StickyScrollContainer hack referencing bug 1421660.
9054 // Table rows are not containing blocks either
9055 return !IsLineParticipant() && !IsBlockWrapper() && !IsTableRowFrame();
9056}
9057
9058nsIFrame* nsIFrame::GetContainingBlock(
9059 uint32_t aFlags, const nsStyleDisplay* aStyleDisplay) const {
9060 MOZ_ASSERT(aStyleDisplay == StyleDisplay())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aStyleDisplay == StyleDisplay())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aStyleDisplay == StyleDisplay
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aStyleDisplay == StyleDisplay()", "./../../../layout/generic/nsIFrame.cpp"
, 9060); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aStyleDisplay == StyleDisplay()"
")"); do { MOZ_CrashSequence(__null, 9060); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9061
9062 // Keep this in sync with MightBeContainingBlockFor in ReflowInput.cpp.
9063
9064 if (!GetParent()) {
9065 return nullptr;
9066 }
9067 // MathML frames might have absolute positioning style, but they would
9068 // still be in-flow. So we have to check to make sure that the frame
9069 // is really out-of-flow too.
9070 nsIFrame* f;
9071 if (IsAbsolutelyPositioned(aStyleDisplay)) {
9072 f = GetParent(); // the parent is always the containing block
9073 } else {
9074 f = GetNearestBlockContainer(GetParent());
9075 }
9076
9077 if (aFlags & SKIP_SCROLLED_FRAME && f &&
9078 f->Style()->GetPseudoType() == PseudoStyleType::MozScrolledContent) {
9079 f = f->GetParent();
9080 }
9081 return f;
9082}
9083
9084#ifdef DEBUG_FRAME_DUMP1
9085
9086Maybe<uint32_t> nsIFrame::ContentIndexInContainer(const nsIFrame* aFrame) {
9087 if (nsIContent* content = aFrame->GetContent()) {
9088 return content->ComputeIndexInParentContent();
9089 }
9090 return Nothing();
9091}
9092
9093nsAutoCString nsIFrame::ListTag(bool aListOnlyDeterministic) const {
9094 nsAutoString tmp;
9095 GetFrameName(tmp);
9096
9097 nsAutoCString tag;
9098 tag += NS_ConvertUTF16toUTF8(tmp);
9099 ListPtr(tag, aListOnlyDeterministic, this, "@");
9100 return tag;
9101}
9102
9103std::string nsIFrame::ConvertToString(const LogicalRect& aRect,
9104 const WritingMode aWM, ListFlags aFlags) {
9105 if (aFlags.contains(ListFlag::DisplayInCSSPixels)) {
9106 // Abuse CSSRect to store all LogicalRect's dimensions in CSS pixels.
9107 return ToString(mozilla::CSSRect(CSSPixel::FromAppUnits(aRect.IStart(aWM)),
9108 CSSPixel::FromAppUnits(aRect.BStart(aWM)),
9109 CSSPixel::FromAppUnits(aRect.ISize(aWM)),
9110 CSSPixel::FromAppUnits(aRect.BSize(aWM))));
9111 }
9112 return ToString(aRect);
9113}
9114
9115std::string nsIFrame::ConvertToString(const LogicalSize& aSize,
9116 const WritingMode aWM, ListFlags aFlags) {
9117 if (aFlags.contains(ListFlag::DisplayInCSSPixels)) {
9118 // Abuse CSSSize to store all LogicalSize's dimensions in CSS pixels.
9119 return ToString(CSSSize(CSSPixel::FromAppUnits(aSize.ISize(aWM)),
9120 CSSPixel::FromAppUnits(aSize.BSize(aWM))));
9121 }
9122 return ToString(aSize);
9123}
9124
9125// Debugging
9126void nsIFrame::ListGeneric(nsACString& aTo, const char* aPrefix,
9127 ListFlags aFlags) const {
9128 aTo += aPrefix;
9129 const bool onlyDeterministic =
9130 aFlags.contains(ListFlag::OnlyListDeterministicInfo);
9131 aTo += ListTag(onlyDeterministic);
9132 if (!onlyDeterministic) {
9133 if (GetParent()) {
9134 aTo += nsPrintfCString(" parent=%p", static_cast<void*>(GetParent()));
9135 }
9136 if (GetNextSibling()) {
9137 aTo += nsPrintfCString(" next=%p", static_cast<void*>(GetNextSibling()));
9138 }
9139 }
9140 if (GetPrevContinuation()) {
9141 bool fluid = GetPrevInFlow() == GetPrevContinuation();
9142 aTo += nsPrintfCString(" prev-%s", fluid ? "in-flow" : "continuation");
9143 ListPtr(aTo, aFlags, GetPrevContinuation());
9144 }
9145 if (GetNextContinuation()) {
9146 bool fluid = GetNextInFlow() == GetNextContinuation();
9147 aTo += nsPrintfCString(" next-%s", fluid ? "in-flow" : "continuation");
9148 ListPtr(aTo, aFlags, GetNextContinuation());
9149 }
9150 if (const nsAtom* const autoPageValue =
9151 GetProperty(AutoPageValueProperty())) {
9152 aTo += " AutoPage=";
9153 aTo += nsAtomCString(autoPageValue);
9154 }
9155 if (const nsIFrame::PageValues* const pageValues =
9156 GetProperty(PageValuesProperty())) {
9157 aTo += " PageValues={";
9158 if (pageValues->mStartPageValue) {
9159 aTo += nsAtomCString(pageValues->mStartPageValue);
9160 } else {
9161 aTo += "<null>";
9162 }
9163 aTo += ", ";
9164 if (pageValues->mEndPageValue) {
9165 aTo += nsAtomCString(pageValues->mEndPageValue);
9166 } else {
9167 aTo += "<null>";
9168 }
9169 aTo += "}";
9170 }
9171 void* IBsibling = GetProperty(IBSplitSibling());
9172 if (IBsibling) {
9173 aTo += nsPrintfCString(" IBSplitSibling");
9174 ListPtr(aTo, aFlags, IBsibling);
9175 }
9176 void* IBprevsibling = GetProperty(IBSplitPrevSibling());
9177 if (IBprevsibling) {
9178 aTo += nsPrintfCString(" IBSplitPrevSibling");
9179 ListPtr(aTo, aFlags, IBprevsibling);
9180 }
9181 if (nsLayoutUtils::FontSizeInflationEnabled(PresContext())) {
9182 if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT)) {
9183 aTo += nsPrintfCString(" FFR");
9184 if (nsFontInflationData* data =
9185 nsFontInflationData::FindFontInflationDataFor(this)) {
9186 aTo += nsPrintfCString(
9187 ",enabled=%s,UIS=%s", data->InflationEnabled() ? "yes" : "no",
9188 ConvertToString(data->UsableISize(), aFlags).c_str());
9189 }
9190 }
9191 if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER)) {
9192 aTo += nsPrintfCString(" FIC");
9193 }
9194 aTo += nsPrintfCString(" FI=%f", nsLayoutUtils::FontSizeInflationFor(this));
9195 }
9196 aTo += nsPrintfCString(" %s", ConvertToString(mRect, aFlags).c_str());
9197
9198 mozilla::WritingMode wm = GetWritingMode();
9199 if (wm.IsVertical() || wm.IsBidiRTL()) {
9200 aTo +=
9201 nsPrintfCString(" wm=%s logical-size=(%s)", ToString(wm).c_str(),
9202 ConvertToString(GetLogicalSize(), wm, aFlags).c_str());
9203 }
9204
9205 nsIFrame* parent = GetParent();
9206 if (parent) {
9207 WritingMode pWM = parent->GetWritingMode();
9208 if (pWM.IsVertical() || pWM.IsBidiRTL()) {
9209 nsSize containerSize = parent->mRect.Size();
9210 LogicalRect lr(pWM, mRect, containerSize);
9211 aTo += nsPrintfCString(" parent-wm=%s cs=(%s) logical-rect=%s",
9212 ToString(pWM).c_str(),
9213 ConvertToString(containerSize, aFlags).c_str(),
9214 ConvertToString(lr, pWM, aFlags).c_str());
9215 }
9216 }
9217 nsIFrame* f = const_cast<nsIFrame*>(this);
9218 if (f->HasOverflowAreas()) {
9219 nsRect io = f->InkOverflowRect();
9220 if (!io.IsEqualEdges(mRect)) {
9221 aTo += nsPrintfCString(" ink-overflow=%s",
9222 ConvertToString(io, aFlags).c_str());
9223 }
9224 nsRect so = f->ScrollableOverflowRect();
9225 if (!so.IsEqualEdges(mRect)) {
9226 aTo += nsPrintfCString(" scr-overflow=%s",
9227 ConvertToString(so, aFlags).c_str());
9228 }
9229 }
9230 if (OverflowAreas* preTransformOverflows =
9231 f->GetProperty(PreTransformOverflowAreasProperty())) {
9232 nsRect io = preTransformOverflows->InkOverflow();
9233 if (!io.IsEqualEdges(mRect) &&
9234 (!f->HasOverflowAreas() || !io.IsEqualEdges(f->InkOverflowRect()))) {
9235 aTo += nsPrintfCString(" pre-transform-ink-overflow=%s",
9236 ConvertToString(io, aFlags).c_str());
9237 }
9238 nsRect so = preTransformOverflows->ScrollableOverflow();
9239 if (!so.IsEqualEdges(mRect) &&
9240 (!f->HasOverflowAreas() ||
9241 !so.IsEqualEdges(f->ScrollableOverflowRect()))) {
9242 aTo += nsPrintfCString(" pre-transform-scr-overflow=%s",
9243 ConvertToString(so, aFlags).c_str());
9244 }
9245 }
9246 bool hasNormalPosition;
9247 nsPoint normalPosition = GetNormalPosition(&hasNormalPosition);
9248 if (hasNormalPosition) {
9249 aTo += nsPrintfCString(" normal-position=%s",
9250 ConvertToString(normalPosition, aFlags).c_str());
9251 }
9252 if (HasProperty(BidiDataProperty())) {
9253 FrameBidiData bidi = GetBidiData();
9254 aTo += nsPrintfCString(" bidi(%d,%d,%d)", bidi.baseLevel.Value(),
9255 bidi.embeddingLevel.Value(),
9256 bidi.precedingControl.Value());
9257 }
9258 if (IsTransformed()) {
9259 aTo += nsPrintfCString(" transformed");
9260 }
9261 if (ChildrenHavePerspective()) {
9262 aTo += nsPrintfCString(" perspective");
9263 }
9264 if (Extend3DContext()) {
9265 aTo += nsPrintfCString(" extend-3d");
9266 }
9267 if (Combines3DTransformWithAncestors()) {
9268 aTo += nsPrintfCString(" combines-3d-transform-with-ancestors");
9269 }
9270 if (mContent) {
9271 if (!onlyDeterministic) {
9272 aTo += nsPrintfCString(" [content=%p]", static_cast<void*>(mContent));
9273 }
9274 if (IsPrimaryFrame() && DisplayPortUtils::HasDisplayPort(mContent)) {
9275 // Anon boxes and continuations point to the same content - Just print on
9276 // primary frame.
9277 aTo += "[displayport]"_ns;
9278 }
9279 }
9280 if (!onlyDeterministic) {
9281 aTo += nsPrintfCString("[cs=%p]", static_cast<void*>(mComputedStyle));
9282 }
9283 if (mComputedStyle) {
9284 const auto pseudoType = mComputedStyle->GetPseudoType();
9285 const auto pseudoTypeStr = ToString(pseudoType);
9286 if (!pseudoTypeStr.empty()) {
9287 aTo += nsPrintfCString("[%s]", pseudoTypeStr.c_str());
9288 }
9289 }
9290
9291 auto contentVisibility = StyleDisplay()->ContentVisibility(*this);
9292 if (contentVisibility != StyleContentVisibility::Visible) {
9293 aTo += nsPrintfCString(" [content-visibility=");
9294 if (contentVisibility == StyleContentVisibility::Auto) {
9295 aTo += "auto, "_ns;
9296 } else if (contentVisibility == StyleContentVisibility::Hidden) {
9297 aTo += "hidden, "_ns;
9298 }
9299
9300 if (HidesContent()) {
9301 aTo += "HidesContent=hidden"_ns;
9302 } else {
9303 aTo += "HidesContent=visibile"_ns;
9304 }
9305 aTo += "]";
9306 }
9307
9308 if (IsFrameModified()) {
9309 aTo += nsPrintfCString(" modified");
9310 }
9311
9312 if (HasModifiedDescendants()) {
9313 aTo += nsPrintfCString(" has-modified-descendants");
9314 }
9315}
9316
9317void nsIFrame::List(FILE* out, const char* aPrefix, ListFlags aFlags) const {
9318 nsCString str;
9319 ListGeneric(str, aPrefix, aFlags);
9320 fprintf_stderr(out, "%s\n", str.get());
9321}
9322
9323void nsIFrame::ListTextRuns(FILE* out) const {
9324 nsTHashSet<const void*> seen;
9325 ListTextRuns(out, seen);
9326}
9327
9328void nsIFrame::ListTextRuns(FILE* out, nsTHashSet<const void*>& aSeen) const {
9329 for (const auto& childList : ChildLists()) {
9330 for (const nsIFrame* kid : childList.mList) {
9331 kid->ListTextRuns(out, aSeen);
9332 }
9333 }
9334}
9335
9336void nsIFrame::ListMatchedRules(FILE* out, const char* aPrefix) const {
9337 AutoTArray<StyleMatchingDeclarationBlock, 8> decls;
9338 Servo_ComputedValues_GetMatchingDeclarations(
9339 Style(), /* with_starting_style = */ false, &decls);
9340 for (const StyleMatchingDeclarationBlock& block : decls) {
9341 nsAutoCString ruleText;
9342 Servo_DeclarationBlock_GetCssText(block.block, &ruleText);
9343 fprintf_stderr(out, "%s%s\n", aPrefix, ruleText.get());
9344 }
9345}
9346
9347void nsIFrame::ListWithMatchedRules(FILE* out, const char* aPrefix) const {
9348 fprintf_stderr(out, "%s%s\n", aPrefix, ListTag().get());
9349
9350 nsCString rulePrefix;
9351 rulePrefix += aPrefix;
9352 rulePrefix += " ";
9353 ListMatchedRules(out, rulePrefix.get());
9354}
9355
9356nsresult nsIFrame::GetFrameName(nsAString& aResult) const {
9357 return MakeFrameName(u"Frame"_ns, aResult);
9358}
9359
9360nsresult nsIFrame::MakeFrameName(const nsAString& aType,
9361 nsAString& aResult) const {
9362 aResult = aType;
9363 if (mContent && !mContent->IsText()) {
9364 nsAutoString buf;
9365 mContent->NodeInfo()->NameAtom()->ToString(buf);
9366 if (nsAtom* id = mContent->GetID()) {
9367 buf.AppendLiteral(" id=");
9368 buf.Append(nsDependentAtomString(id));
9369 }
9370 if (IsSubDocumentFrame()) {
9371 nsAutoString src;
9372 mContent->AsElement()->GetAttr(nsGkAtoms::src, src);
9373 buf.AppendLiteral(" src=");
9374 buf.Append(src);
9375 }
9376 aResult.Append('(');
9377 aResult.Append(buf);
9378 aResult.Append(')');
9379 }
9380 aResult.Append('(');
9381 Maybe<uint32_t> index = ContentIndexInContainer(this);
9382 if (index.isSome()) {
9383 aResult.AppendInt(*index);
9384 } else {
9385 aResult.AppendInt(-1);
9386 }
9387 aResult.Append(')');
9388 return NS_OK;
9389}
9390
9391void nsIFrame::DumpFrameTree() const { DumpFrameTree(false); }
9392
9393void nsIFrame::DumpFrameTree(bool aListOnlyDeterministic) const {
9394 ListFlags flags;
9395 if (aListOnlyDeterministic) {
9396 flags += ListFlag::OnlyListDeterministicInfo;
9397 }
9398 PresShell()->GetRootFrame()->List(stderrstderr, "", flags);
9399}
9400
9401void nsIFrame::DumpFrameTreeInCSSPixels() const {
9402 DumpFrameTreeInCSSPixels(false);
9403}
9404
9405void nsIFrame::DumpFrameTreeInCSSPixels(bool aListOnlyDeterministic) const {
9406 ListFlags flags{ListFlag::DisplayInCSSPixels};
9407 if (aListOnlyDeterministic) {
9408 flags += ListFlag::OnlyListDeterministicInfo;
9409 }
9410 PresShell()->GetRootFrame()->List(stderrstderr, "", flags);
9411}
9412
9413void nsIFrame::DumpFrameTreeLimited() const { List(stderrstderr); }
9414void nsIFrame::DumpFrameTreeLimitedInCSSPixels() const {
9415 List(stderrstderr, "", ListFlag::DisplayInCSSPixels);
9416}
9417
9418#endif
9419
9420bool nsIFrame::IsVisibleForPainting() const {
9421 return StyleVisibility()->IsVisible();
9422}
9423
9424bool nsIFrame::IsVisibleOrCollapsedForPainting() const {
9425 return StyleVisibility()->IsVisibleOrCollapsed();
9426}
9427
9428/* virtual */
9429bool nsIFrame::IsEmpty() {
9430 return IsHiddenByContentVisibilityOfInFlowParentForLayout();
9431}
9432
9433bool nsIFrame::CachedIsEmpty() {
9434 NS_ASSERTION(!HasAnyStateBits(NS_FRAME_IS_DIRTY) ||do { if (!(!HasAnyStateBits(NS_FRAME_IS_DIRTY) || IsHiddenByContentVisibilityOfInFlowParentForLayout
())) { NS_DebugBreak(NS_DEBUG_ASSERTION, "Must only be called on reflowed lines or those hidden by "
"content-visibility.", "!HasAnyStateBits(NS_FRAME_IS_DIRTY) || IsHiddenByContentVisibilityOfInFlowParentForLayout()"
, "./../../../layout/generic/nsIFrame.cpp", 9437); MOZ_PretendNoReturn
(); } } while (0)
9435 IsHiddenByContentVisibilityOfInFlowParentForLayout(),do { if (!(!HasAnyStateBits(NS_FRAME_IS_DIRTY) || IsHiddenByContentVisibilityOfInFlowParentForLayout
())) { NS_DebugBreak(NS_DEBUG_ASSERTION, "Must only be called on reflowed lines or those hidden by "
"content-visibility.", "!HasAnyStateBits(NS_FRAME_IS_DIRTY) || IsHiddenByContentVisibilityOfInFlowParentForLayout()"
, "./../../../layout/generic/nsIFrame.cpp", 9437); MOZ_PretendNoReturn
(); } } while (0)
9436 "Must only be called on reflowed lines or those hidden by "do { if (!(!HasAnyStateBits(NS_FRAME_IS_DIRTY) || IsHiddenByContentVisibilityOfInFlowParentForLayout
())) { NS_DebugBreak(NS_DEBUG_ASSERTION, "Must only be called on reflowed lines or those hidden by "
"content-visibility.", "!HasAnyStateBits(NS_FRAME_IS_DIRTY) || IsHiddenByContentVisibilityOfInFlowParentForLayout()"
, "./../../../layout/generic/nsIFrame.cpp", 9437); MOZ_PretendNoReturn
(); } } while (0)
9437 "content-visibility.")do { if (!(!HasAnyStateBits(NS_FRAME_IS_DIRTY) || IsHiddenByContentVisibilityOfInFlowParentForLayout
())) { NS_DebugBreak(NS_DEBUG_ASSERTION, "Must only be called on reflowed lines or those hidden by "
"content-visibility.", "!HasAnyStateBits(NS_FRAME_IS_DIRTY) || IsHiddenByContentVisibilityOfInFlowParentForLayout()"
, "./../../../layout/generic/nsIFrame.cpp", 9437); MOZ_PretendNoReturn
(); } } while (0)
;
9438 return IsEmpty();
9439}
9440
9441/* virtual */
9442bool nsIFrame::IsSelfEmpty() {
9443 return IsHiddenByContentVisibilityOfInFlowParentForLayout();
9444}
9445
9446nsISelectionController* nsIFrame::GetSelectionController() const {
9447 if (nsTextControlFrame* const tcf = GetContainingTextControlFrame()) {
9448 return tcf->ControlElement()->GetSelectionController();
9449 }
9450 return static_cast<nsISelectionController*>(PresShell());
9451}
9452
9453int16_t nsIFrame::GetDisplaySelection() const {
9454 nsISelectionController* const selCon = GetSelectionController();
9455 if (MOZ_UNLIKELY(!selCon)(__builtin_expect(!!(!selCon), 0))) {
9456 return nsISelectionController::SELECTION_OFF;
9457 }
9458 int16_t display = nsISelectionController::SELECTION_OFF;
9459 selCon->GetDisplaySelection(&display);
9460 return display;
9461}
9462
9463already_AddRefed<nsFrameSelection> nsIFrame::GetFrameSelection() {
9464 RefPtr<nsFrameSelection> fs =
9465 const_cast<nsFrameSelection*>(GetConstFrameSelection());
9466 return fs.forget();
9467}
9468
9469const nsFrameSelection* nsIFrame::GetConstFrameSelection() const {
9470 if (nsTextControlFrame* tcf = GetContainingTextControlFrame()) {
9471 return tcf->GetOwnedFrameSelection();
9472 }
9473 return PresShell()->ConstFrameSelection();
9474}
9475
9476bool nsIFrame::IsFrameSelected() const {
9477 NS_ASSERTION(!GetContent() || GetContent()->IsMaybeSelected(),do { if (!(!GetContent() || GetContent()->IsMaybeSelected(
))) { NS_DebugBreak(NS_DEBUG_ASSERTION, "use the public IsSelected() instead"
, "!GetContent() || GetContent()->IsMaybeSelected()", "./../../../layout/generic/nsIFrame.cpp"
, 9478); MOZ_PretendNoReturn(); } } while (0)
9478 "use the public IsSelected() instead")do { if (!(!GetContent() || GetContent()->IsMaybeSelected(
))) { NS_DebugBreak(NS_DEBUG_ASSERTION, "use the public IsSelected() instead"
, "!GetContent() || GetContent()->IsMaybeSelected()", "./../../../layout/generic/nsIFrame.cpp"
, 9478); MOZ_PretendNoReturn(); } } while (0)
;
9479 if (const ShadowRoot* shadowRoot =
9480 GetContent()->GetShadowRootForSelection()) {
9481 return shadowRoot->IsSelected(0, shadowRoot->GetChildCount());
9482 }
9483 return GetContent()->IsSelected(0, GetContent()->GetChildCount());
9484}
9485
9486nsresult nsIFrame::GetPointFromOffset(int32_t inOffset, nsPoint* outPoint) {
9487 MOZ_ASSERT(outPoint != nullptr, "Null parameter")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(outPoint != nullptr)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(outPoint != nullptr))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("outPoint != nullptr"
" (" "Null parameter" ")", "./../../../layout/generic/nsIFrame.cpp"
, 9487); AnnotateMozCrashReason("MOZ_ASSERT" "(" "outPoint != nullptr"
") (" "Null parameter" ")"); do { MOZ_CrashSequence(__null, 9487
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
9488 nsRect contentRect = GetContentRectRelativeToSelf();
9489 nsPoint pt = contentRect.TopLeft();
9490 if (mContent) {
9491 nsIContent* newContent = mContent->GetParent();
9492 if (newContent) {
9493 const int32_t newOffset = newContent->ComputeIndexOf_Deprecated(mContent);
9494
9495 // Find the direction of the frame from the EmbeddingLevelProperty,
9496 // which is the resolved bidi level set in
9497 // nsBidiPresUtils::ResolveParagraph (odd levels = right-to-left).
9498 // If the embedding level isn't set, just use the CSS direction
9499 // property.
9500 bool hasBidiData;
9501 FrameBidiData bidiData = GetProperty(BidiDataProperty(), &hasBidiData);
9502 bool isRTL = hasBidiData
9503 ? bidiData.embeddingLevel.IsRTL()
9504 : StyleVisibility()->mDirection == StyleDirection::Rtl;
9505 if ((!isRTL && inOffset > newOffset) ||
9506 (isRTL && inOffset <= newOffset)) {
9507 pt = contentRect.TopRight();
9508 }
9509 }
9510 }
9511 *outPoint = pt;
9512 return NS_OK;
9513}
9514
9515nsresult nsIFrame::GetCharacterRectsInRange(int32_t aInOffset, int32_t aLength,
9516 nsTArray<nsRect>& aOutRect) {
9517 /* no text */
9518 return NS_ERROR_FAILURE;
9519}
9520
9521nsresult nsIFrame::GetChildFrameContainingOffset(int32_t inContentOffset,
9522 bool inHint,
9523 int32_t* outFrameContentOffset,
9524 nsIFrame** outChildFrame) {
9525 MOZ_ASSERT(outChildFrame && outFrameContentOffset, "Null parameter")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(outChildFrame && outFrameContentOffset)>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(outChildFrame && outFrameContentOffset))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("outChildFrame && outFrameContentOffset"
" (" "Null parameter" ")", "./../../../layout/generic/nsIFrame.cpp"
, 9525); AnnotateMozCrashReason("MOZ_ASSERT" "(" "outChildFrame && outFrameContentOffset"
") (" "Null parameter" ")"); do { MOZ_CrashSequence(__null, 9525
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
9526 *outFrameContentOffset = (int32_t)inHint;
9527 // the best frame to reflect any given offset would be a visible frame if
9528 // possible i.e. we are looking for a valid frame to place the blinking caret
9529 nsRect rect = GetRect();
9530 if (!rect.width || !rect.height) {
9531 // if we have a 0 width or height then lets look for another frame that
9532 // possibly has the same content. If we have no frames in flow then just
9533 // let us return 'this' frame
9534 nsIFrame* nextFlow = GetNextInFlow();
9535 if (nextFlow) {
9536 return nextFlow->GetChildFrameContainingOffset(
9537 inContentOffset, inHint, outFrameContentOffset, outChildFrame);
9538 }
9539 }
9540 *outChildFrame = this;
9541 return NS_OK;
9542}
9543
9544//
9545// What I've pieced together about this routine:
9546// Starting with a block frame (from which a line frame can be gotten)
9547// and a line number, drill down and get the first/last selectable
9548// frame on that line, depending on aPos->mDirection.
9549// aOutSideLimit != 0 means ignore aLineStart, instead work from
9550// the end (if > 0) or beginning (if < 0).
9551//
9552static nsresult GetNextPrevLineFromBlockFrame(PeekOffsetStruct* aPos,
9553 nsIFrame* aBlockFrame,
9554 int32_t aLineStart,
9555 int8_t aOutSideLimit) {
9556 MOZ_ASSERT(aPos)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aPos)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(aPos))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("aPos", "./../../../layout/generic/nsIFrame.cpp"
, 9556); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aPos" ")");
do { MOZ_CrashSequence(__null, 9556); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9557 MOZ_ASSERT(aBlockFrame)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aBlockFrame)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aBlockFrame))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("aBlockFrame", "./../../../layout/generic/nsIFrame.cpp"
, 9557); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aBlockFrame"
")"); do { MOZ_CrashSequence(__null, 9557); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9558
9559 nsPresContext* pc = aBlockFrame->PresContext();
9560
9561 // magic numbers: aLineStart will be -1 for end of block, 0 will be start of
9562 // block.
9563
9564 aPos->mResultFrame = nullptr;
9565 aPos->mResultContent = nullptr;
9566 aPos->mAttach = aPos->mDirection == eDirNext ? CaretAssociationHint::After
9567 : CaretAssociationHint::Before;
9568
9569 AutoAssertNoDomMutations guard;
9570 nsILineIterator* it = aBlockFrame->GetLineIterator();
9571 if (!it) {
9572 return NS_ERROR_FAILURE;
9573 }
9574 int32_t searchingLine = aLineStart;
9575 int32_t countLines = it->GetNumLines();
9576 if (aOutSideLimit > 0) { // start at end
9577 searchingLine = countLines;
9578 } else if (aOutSideLimit < 0) { // start at beginning
9579 searchingLine = -1; //"next" will be 0
9580 } else if ((aPos->mDirection == eDirPrevious && searchingLine == 0) ||
9581 (aPos->mDirection == eDirNext &&
9582 searchingLine >= (countLines - 1))) {
9583 // Not found.
9584 return NS_ERROR_FAILURE;
9585 }
9586 nsIFrame* resultFrame = nullptr;
9587 nsIFrame* farStoppingFrame = nullptr; // we keep searching until we find a
9588 // "this" frame then we go to next line
9589 nsIFrame* nearStoppingFrame = nullptr; // if we are backing up from edge,
9590 // stop here
9591 bool isBeforeFirstFrame, isAfterLastFrame;
9592 bool found = false;
9593
9594 const bool forceInEditableRegion =
9595 aPos->mOptions.contains(PeekOffsetOption::ForceEditableRegion);
9596 while (!found) {
9597 if (aPos->mDirection == eDirPrevious) {
9598 searchingLine--;
9599 } else {
9600 searchingLine++;
9601 }
9602 if ((aPos->mDirection == eDirPrevious && searchingLine < 0) ||
9603 (aPos->mDirection == eDirNext && searchingLine >= countLines)) {
9604 // we need to jump to new block frame.
9605 return NS_ERROR_FAILURE;
9606 }
9607 {
9608 auto line = it->GetLine(searchingLine).unwrap();
9609 if (!line.mNumFramesOnLine) {
9610 continue;
9611 }
9612 nsIFrame* firstFrame = nullptr;
9613 nsIFrame* lastFrame = nullptr;
9614 nsIFrame* frame = line.mFirstFrameOnLine;
9615 int32_t i = line.mNumFramesOnLine;
9616 do {
9617 // If the caller wants a frame for a inclusive ancestor of the ancestor
9618 // limiter, ignore frames for outside the limiter.
9619 if (aPos->FrameContentIsInAncestorLimiter(frame)) {
9620 if (!firstFrame) {
9621 firstFrame = frame;
9622 }
9623 lastFrame = frame;
9624 }
9625 if (i == 1) {
9626 break;
9627 }
9628 frame = frame->GetNextSibling();
9629 if (!frame) {
9630 NS_ERROR("GetLine promised more frames than could be found")do { NS_DebugBreak(NS_DEBUG_ASSERTION, "GetLine promised more frames than could be found"
, "Error", "./../../../layout/generic/nsIFrame.cpp", 9630); MOZ_PretendNoReturn
(); } while (0)
;
9631 return NS_ERROR_FAILURE;
9632 }
9633 } while (--i);
9634 if (!lastFrame) {
9635 // If we're looking for an editable content frame, but all frames in the
9636 // line are not in the specified editing host, return error because we
9637 // must reach the editing host boundary.
9638 return NS_ERROR_FAILURE;
9639 }
9640 // Don't enter into native anonymous subtrees.
9641 if (!lastFrame->ContentIsRootOfNativeAnonymousSubtree()) {
9642 nsIFrame::GetLastLeaf(&lastFrame);
9643 }
9644
9645 if (aPos->mDirection == eDirNext) {
9646 nearStoppingFrame = firstFrame;
9647 farStoppingFrame = lastFrame;
9648 } else {
9649 nearStoppingFrame = lastFrame;
9650 farStoppingFrame = firstFrame;
9651 }
9652 }
9653 nsPoint offset = aBlockFrame->GetOffsetToRootFrame();
9654 nsPoint newDesiredPos =
9655 aPos->mDesiredCaretPos -
9656 offset; // get desired position into blockframe coords
9657 // TODO: nsILineIterator::FindFrameAt should take optional editing host
9658 // parameter and if it's set, it should return the nearest editable frame
9659 // for the editing host when the frame at the desired position is not
9660 // editable.
9661 nsresult rv = it->FindFrameAt(searchingLine, newDesiredPos, &resultFrame,
9662 &isBeforeFirstFrame, &isAfterLastFrame);
9663 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
9664 continue;
9665 }
9666
9667 if (resultFrame) {
9668 // If ancestor limiter is specified and we reached outside content of it,
9669 // return error because we reached its element boundary.
9670 if (!aPos->FrameContentIsInAncestorLimiter(resultFrame)) {
9671 return NS_ERROR_FAILURE;
9672 }
9673 // Check to see if this is ANOTHER blockframe inside the other one that
9674 // we should look inside.
9675 if (resultFrame->CanProvideLineIterator() &&
9676 IsRelevantBlockFrame(resultFrame)) {
9677 aPos->mResultFrame = resultFrame;
9678 return NS_OK;
9679 }
9680 // resultFrame is not a block frame
9681 Maybe<nsFrameIterator> frameIterator;
9682 frameIterator.emplace(
9683 pc, resultFrame, nsFrameIterator::Type::PostOrder,
9684 false, // aVisual
9685 aPos->mOptions.contains(PeekOffsetOption::StopAtScroller),
9686 false, // aFollowOOFs
9687 false // aSkipPopupChecks
9688 );
9689
9690 auto FoundValidFrame = [forceInEditableRegion, aPos](
9691 const nsIFrame::ContentOffsets& aOffsets,
9692 const nsIFrame* aFrame) {
9693 if (!aOffsets.content) {
9694 return false;
9695 }
9696 if (!aFrame->IsSelectable()) {
9697 return false;
9698 }
9699 if (aPos->mAncestorLimiter &&
9700 !aOffsets.content->IsInclusiveDescendantOf(
9701 aPos->mAncestorLimiter)) {
9702 return false;
9703 }
9704 if (forceInEditableRegion && !aOffsets.content->IsEditable()) {
9705 return false;
9706 }
9707 return true;
9708 };
9709
9710 nsIFrame* storeOldResultFrame = resultFrame;
9711 while (!found) {
9712 nsPoint point;
9713 nsRect tempRect = resultFrame->GetRect();
9714 nsPoint offset = resultFrame->GetOffsetToRootFrame();
9715 if (resultFrame->GetWritingMode().IsVertical()) {
9716 point.y = aPos->mDesiredCaretPos.y;
9717 point.x = tempRect.width + offset.x;
9718 } else {
9719 point.y = tempRect.height + offset.y;
9720 point.x = aPos->mDesiredCaretPos.x;
9721 }
9722
9723 if (!resultFrame->IsViewportFrame()) {
9724 nsPoint offset = resultFrame->GetOffsetToRootFrame();
9725 nsIFrame::ContentOffsets offsets =
9726 resultFrame->GetContentOffsetsFromPoint(
9727 point - offset, nsIFrame::IGNORE_NATIVE_ANONYMOUS_SUBTREE);
9728 aPos->mResultContent = offsets.content;
9729 aPos->mContentOffset = offsets.offset;
9730 aPos->mAttach = offsets.associate;
9731 if (FoundValidFrame(offsets, resultFrame)) {
9732 found = true;
9733 break;
9734 }
9735 }
9736
9737 if (aPos->mDirection == eDirPrevious &&
9738 resultFrame == farStoppingFrame) {
9739 break;
9740 }
9741 if (aPos->mDirection == eDirNext && resultFrame == nearStoppingFrame) {
9742 break;
9743 }
9744 // always try previous on THAT line if that fails go the other way
9745 resultFrame = frameIterator->Traverse(/* aForward = */ false);
9746 if (!resultFrame) {
9747 return NS_ERROR_FAILURE;
9748 }
9749 }
9750
9751 if (!found) {
9752 resultFrame = storeOldResultFrame;
9753 frameIterator.reset();
9754 frameIterator.emplace(
9755 pc, resultFrame, nsFrameIterator::Type::Leaf,
9756 false, // aVisual
9757 aPos->mOptions.contains(PeekOffsetOption::StopAtScroller),
9758 false, // aFollowOOFs
9759 false // aSkipPopupChecks
9760 );
9761 MOZ_ASSERT(frameIterator)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(frameIterator)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(frameIterator))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("frameIterator",
"./../../../layout/generic/nsIFrame.cpp", 9761); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "frameIterator" ")"); do { MOZ_CrashSequence
(__null, 9761); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
9762 }
9763 while (!found) {
9764 nsPoint point = aPos->mDesiredCaretPos;
9765 nsPoint offset = resultFrame->GetOffsetToRootFrame();
9766 nsIFrame::ContentOffsets offsets =
9767 resultFrame->GetContentOffsetsFromPoint(
9768 point - offset, nsIFrame::IGNORE_NATIVE_ANONYMOUS_SUBTREE);
9769 aPos->mResultContent = offsets.content;
9770 aPos->mContentOffset = offsets.offset;
9771 aPos->mAttach = offsets.associate;
9772 if (FoundValidFrame(offsets, resultFrame)) {
9773 found = true;
9774 aPos->mAttach = resultFrame == farStoppingFrame
9775 ? CaretAssociationHint::Before
9776 : CaretAssociationHint::After;
9777 break;
9778 }
9779 if (aPos->mDirection == eDirPrevious &&
9780 resultFrame == nearStoppingFrame) {
9781 break;
9782 }
9783 if (aPos->mDirection == eDirNext && resultFrame == farStoppingFrame) {
9784 break;
9785 }
9786 // previous didnt work now we try "next"
9787 nsIFrame* tempFrame = frameIterator->Traverse(/* aForward = */ true);
9788 if (!tempFrame) {
9789 break;
9790 }
9791 resultFrame = tempFrame;
9792 }
9793 aPos->mResultFrame = resultFrame;
9794 } else {
9795 // we need to jump to new block frame.
9796 aPos->mAmount = eSelectLine;
9797 aPos->mStartOffset = 0;
9798 aPos->mAttach = aPos->mDirection == eDirNext
9799 ? CaretAssociationHint::Before
9800 : CaretAssociationHint::After;
9801 if (aPos->mDirection == eDirPrevious) {
9802 aPos->mStartOffset = -1; // start from end
9803 }
9804 return aBlockFrame->PeekOffset(aPos);
9805 }
9806 }
9807 return NS_OK;
9808}
9809
9810nsIFrame::CaretPosition nsIFrame::GetExtremeCaretPosition(bool aStart,
9811 uint32_t aFlags) {
9812 CaretPosition result;
9813
9814 FrameTarget targetFrame = DrillDownToSelectionFrame(this, !aStart, aFlags);
9815 FrameContentRange range = GetRangeForFrame(targetFrame.frame);
9816 result.mResultContent = range.content;
9817 result.mContentOffset = aStart ? range.start : range.end;
9818 return result;
9819}
9820
9821// If this is a preformatted text frame, see if it ends with a newline
9822static nsContentAndOffset FindLineBreakInText(nsIFrame* aFrame,
9823 nsDirection aDirection) {
9824 nsContentAndOffset result;
9825
9826 if (aFrame->IsGeneratedContentFrame() ||
9827 !aFrame->HasSignificantTerminalNewline()) {
9828 return result;
9829 }
9830
9831 int32_t endOffset = aFrame->GetOffsets().second;
9832 result.mContent = aFrame->GetContent();
9833 result.mOffset = endOffset - (aDirection == eDirPrevious ? 0 : 1);
9834 return result;
9835}
9836
9837// Find the first (or last) descendant of the given frame
9838// which is either a block-level frame or a BRFrame, or some other kind of break
9839// which stops the line.
9840static nsContentAndOffset FindLineBreakingFrame(nsIFrame* aFrame,
9841 nsDirection aDirection) {
9842 nsContentAndOffset result;
9843
9844 if (aFrame->IsGeneratedContentFrame()) {
9845 return result;
9846 }
9847
9848 // Treat form controls and other replaced inline level elements as inline
9849 // leaves.
9850 if (aFrame->IsReplaced() && aFrame->IsInlineOutside() &&
9851 !aFrame->IsBrFrame() && !aFrame->IsTextFrame()) {
9852 return result;
9853 }
9854
9855 // Check the frame itself
9856 // Fall through block-in-inline split frames because their mContent is
9857 // the content of the inline frames they were created from. The
9858 // first/last child of such frames is the real block frame we're
9859 // looking for.
9860 if ((IsRelevantBlockFrame(aFrame) &&
9861 !aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) ||
9862 aFrame->IsBrFrame()) {
9863 nsIContent* content = aFrame->GetContent();
9864 result.mContent = content->GetParent();
9865 // In some cases (bug 310589, bug 370174) we end up here with a null
9866 // content. This probably shouldn't ever happen, but since it sometimes
9867 // does, we want to avoid crashing here.
9868 NS_ASSERTION(result.mContent, "Unexpected orphan content")do { if (!(result.mContent)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "Unexpected orphan content", "result.mContent", "./../../../layout/generic/nsIFrame.cpp"
, 9868); MOZ_PretendNoReturn(); } } while (0)
;
9869 if (result.mContent) {
9870 result.mOffset = result.mContent->ComputeIndexOf_Deprecated(content) +
9871 (aDirection == eDirPrevious ? 1 : 0);
9872 }
9873 return result;
9874 }
9875
9876 result = FindLineBreakInText(aFrame, aDirection);
9877 if (result.mContent) {
9878 return result;
9879 }
9880
9881 // Iterate over children and call ourselves recursively
9882 if (aDirection == eDirPrevious) {
9883 nsIFrame* child = aFrame->PrincipalChildList().LastChild();
9884 while (child && !result.mContent) {
9885 result = FindLineBreakingFrame(child, aDirection);
9886 child = child->GetPrevSibling();
9887 }
9888 } else { // eDirNext
9889 nsIFrame* child = aFrame->PrincipalChildList().FirstChild();
9890 while (child && !result.mContent) {
9891 result = FindLineBreakingFrame(child, aDirection);
9892 child = child->GetNextSibling();
9893 }
9894 }
9895 return result;
9896}
9897
9898enum class OffsetIsAtLineEdge : bool { No, Yes };
9899
9900static void SetPeekResultFromFrame(PeekOffsetStruct& aPos, nsIFrame* aFrame,
9901 int32_t aOffset,
9902 OffsetIsAtLineEdge aAtLineEdge) {
9903 FrameContentRange range = GetRangeForFrame(aFrame);
9904 aPos.mResultFrame = aFrame;
9905 aPos.mResultContent = range.content;
9906 // Output offset is relative to content, not frame
9907 aPos.mContentOffset =
9908 aOffset < 0 ? range.end + aOffset + 1 : range.start + aOffset;
9909 // Ensure we don't go past the range. This is important if aFrame is empty.
9910 aPos.mContentOffset = std::clamp(aPos.mContentOffset, range.start, range.end);
9911 if (aAtLineEdge == OffsetIsAtLineEdge::Yes) {
9912 aPos.mAttach = aPos.mContentOffset == range.start
9913 ? CaretAssociationHint::After
9914 : CaretAssociationHint::Before;
9915 }
9916}
9917
9918nsresult nsIFrame::PeekOffsetForParagraph(PeekOffsetStruct* aPos) {
9919 nsIFrame* frame = this;
9920 nsContentAndOffset blockFrameOrBR;
9921 blockFrameOrBR.mContent = nullptr;
9922 bool reachedLimit = IsRelevantBlockFrame(frame) || IsEditingHost(frame);
9923
9924 auto traverse = [&aPos](nsIFrame* current) {
9925 return aPos->mDirection == eDirPrevious ? current->GetPrevSibling()
9926 : current->GetNextSibling();
9927 };
9928
9929 // Go through containing frames until reaching a block frame.
9930 // In each step, search the previous (or next) siblings for the closest
9931 // "stop frame" (a block frame or a BRFrame).
9932 // If found, set it to be the selection boundary and abort.
9933 while (!reachedLimit) {
9934 nsIFrame* parent = frame->GetParent();
9935 // Treat a frame associated with the root content as if it were a block
9936 // frame.
9937 if (!frame->mContent || !frame->mContent->GetParent()) {
9938 reachedLimit = true;
9939 break;
9940 }
9941
9942 if (aPos->mDirection == eDirNext) {
9943 // Try to find our own line-break before looking at our siblings.
9944 blockFrameOrBR = FindLineBreakInText(frame, eDirNext);
9945 }
9946
9947 nsIFrame* sibling = traverse(frame);
9948 while (sibling && !blockFrameOrBR.mContent) {
9949 blockFrameOrBR = FindLineBreakingFrame(sibling, aPos->mDirection);
9950 sibling = traverse(sibling);
9951 }
9952 if (blockFrameOrBR.mContent) {
9953 aPos->mResultContent = blockFrameOrBR.mContent;
9954 aPos->mContentOffset = blockFrameOrBR.mOffset;
9955 break;
9956 }
9957 frame = parent;
9958 reachedLimit =
9959 frame && (IsRelevantBlockFrame(frame) || IsEditingHost(frame));
9960 }
9961
9962 if (reachedLimit) { // no "stop frame" found
9963 if (aPos->mOptions.contains(PeekOffsetOption::ForCaretMove)) {
9964 // For a caret move, drill down to a leaf so scroll-into-view geometry,
9965 // which only uses text-frame offsets when the focus node is a Text node,
9966 // gets a usable position. Frame children include flattened-tree
9967 // descendants, so this also covers shadow-DOM content that the element
9968 // branch below reaches via GetShadowRootForSelection.
9969 const bool atEnd = aPos->mDirection == eDirNext;
9970 FrameTarget targetFrame = DrillDownToSelectionFrame(
9971 frame, atEnd, nsIFrame::IGNORE_NATIVE_ANONYMOUS_SUBTREE);
9972 SetPeekResultFromFrame(*aPos, targetFrame.frame, atEnd ? -1 : 0,
9973 OffsetIsAtLineEdge::Yes);
9974 } else {
9975 // For selection (e.g. triple-click paragraph selection), the block
9976 // container itself is the expected boundary.
9977 aPos->mResultContent = frame->GetContent();
9978 if (aPos->mResultContent) {
9979 if (ShadowRoot* shadowRoot =
9980 aPos->mResultContent->GetShadowRootForSelection()) {
9981 // Even if there's no children for this node,
9982 // the elements inside the shadow root is still
9983 // selectable
9984 aPos->mResultContent = shadowRoot;
9985 }
9986 }
9987 if (aPos->mDirection == eDirPrevious) {
9988 aPos->mContentOffset = 0;
9989 } else if (aPos->mResultContent) {
9990 aPos->mContentOffset = aPos->mResultContent->GetChildCount();
9991 }
9992 }
9993 }
9994 return NS_OK;
9995}
9996
9997// Determine movement direction relative to frame
9998static bool IsMovingInFrameDirection(const nsIFrame* frame,
9999 nsDirection aDirection, bool aVisual) {
10000 bool isReverseDirection =
10001 aVisual && nsBidiPresUtils::IsReversedDirectionFrame(frame);
10002 return aDirection == (isReverseDirection ? eDirPrevious : eDirNext);
10003}
10004
10005// Determines "are we looking for a boundary between whitespace and
10006// non-whitespace (in the direction we're moving in)". It is true when moving
10007// forward and looking for a beginning of a word, or when moving backwards and
10008// looking for an end of a word.
10009static bool ShouldWordSelectionEatSpace(const PeekOffsetStruct& aPos) {
10010 if (aPos.mWordMovementType != eDefaultBehavior) {
10011 // aPos->mWordMovementType possible values:
10012 // eEndWord: eat the space if we're moving backwards
10013 // eStartWord: eat the space if we're moving forwards
10014 return (aPos.mWordMovementType == eEndWord) ==
10015 (aPos.mDirection == eDirPrevious);
10016 }
10017 // Use the hidden preference which is based on operating system
10018 // behavior. This pref only affects whether moving forward by word
10019 // should go to the end of this word or start of the next word. When
10020 // going backwards, the start of the word is always used, on every
10021 // operating system.
10022 return aPos.mDirection == eDirNext &&
10023 StaticPrefs::layout_word_select_eat_space_to_next_word();
10024}
10025
10026void nsIFrame::SelectablePeekReport::TransferTo(PeekOffsetStruct& aPos) const {
10027 return SetPeekResultFromFrame(aPos, mFrame, mOffset, OffsetIsAtLineEdge::No);
10028}
10029
10030nsIFrame::SelectablePeekReport::SelectablePeekReport(
10031 const mozilla::GenericErrorResult<nsresult>&& aErr) {
10032 MOZ_ASSERT(NS_FAILED(aErr.operator nsresult()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(((bool)(__builtin_expect(!!(NS_FAILED_impl(aErr.operator
nsresult())), 0))))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(((bool)(__builtin_expect(!!(
NS_FAILED_impl(aErr.operator nsresult())), 0)))))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("((bool)(__builtin_expect(!!(NS_FAILED_impl(aErr.operator nsresult())), 0)))"
, "./../../../layout/generic/nsIFrame.cpp", 10032); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "((bool)(__builtin_expect(!!(NS_FAILED_impl(aErr.operator nsresult())), 0)))"
")"); do { MOZ_CrashSequence(__null, 10032); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10033 // Return an empty report
10034}
10035
10036nsresult nsIFrame::PeekOffsetForCharacter(PeekOffsetStruct* aPos,
10037 int32_t aOffset) {
10038 SelectablePeekReport current{this, aOffset};
10039
10040 nsIFrame::FrameSearchResult peekSearchState = CONTINUE;
10041
10042 const bool forceEditableRegion =
10043 aPos->mOptions.contains(PeekOffsetOption::ForceEditableRegion);
10044
10045 while (peekSearchState != FOUND) {
10046 const bool movingInFrameDirection = IsMovingInFrameDirection(
10047 current.mFrame, aPos->mDirection,
10048 aPos->mOptions.contains(PeekOffsetOption::Visual));
10049
10050 if (current.mJumpedLine) {
10051 // If we jumped lines, it's as if we found a character, but we still need
10052 // to eat non-renderable content on the new line.
10053 peekSearchState = current.PeekOffsetNoAmount(movingInFrameDirection);
10054 } else {
10055 PeekOffsetCharacterOptions options;
10056 options.mRespectClusters = aPos->mAmount == eSelectCluster;
10057 peekSearchState =
10058 current.PeekOffsetCharacter(movingInFrameDirection, options);
10059 if (peekSearchState == FOUND && forceEditableRegion &&
10060 !current.mFrame->ContentIsEditable()) {
10061 // Treat non-editable content as unselectable. Note that we may need to
10062 // set mJumpedLine propery even if it's not editable. Therefore, we
10063 // cannot skip the above call.
10064 peekSearchState = CONTINUE_UNSELECTABLE;
10065 }
10066 }
10067
10068 current.mMovedOverNonSelectableText |=
10069 peekSearchState == CONTINUE_UNSELECTABLE;
10070
10071 if (peekSearchState != FOUND) {
10072 SelectablePeekReport next = current.mFrame->GetFrameFromDirection(*aPos);
10073 if (next.Failed()) {
10074 return NS_ERROR_FAILURE;
10075 }
10076 next.mJumpedLine |= current.mJumpedLine;
10077 next.mMovedOverNonSelectableText |= current.mMovedOverNonSelectableText;
10078 next.mHasSelectableFrame |= current.mHasSelectableFrame;
10079 current = next;
10080 }
10081
10082 // Found frame, but because we moved over non selectable text we want
10083 // the offset to be at the frame edge. Note that if we are extending the
10084 // selection, this doesn't matter.
10085 if (peekSearchState == FOUND && current.mMovedOverNonSelectableText &&
10086 (!aPos->mOptions.contains(PeekOffsetOption::Extend) ||
10087 current.mHasSelectableFrame)) {
10088 auto [start, end] = current.mFrame->GetOffsets();
10089 current.mOffset = aPos->mDirection == eDirNext ? 0 : end - start;
10090 }
10091 }
10092
10093 // Set outputs
10094 current.TransferTo(*aPos);
10095 // If we're dealing with a text frame and moving backward positions us at
10096 // the end of that line, decrease the offset by one to make sure that
10097 // we're placed before the linefeed character on the previous line.
10098 if (current.mOffset < 0 && current.mJumpedLine &&
10099 aPos->mDirection == eDirPrevious &&
10100 current.mFrame->HasSignificantTerminalNewline() &&
10101 !current.mIgnoredBrFrame) {
10102 --aPos->mContentOffset;
10103 }
10104 return NS_OK;
10105}
10106
10107nsresult nsIFrame::PeekOffsetForWord(PeekOffsetStruct* aPos, int32_t aOffset) {
10108 SelectablePeekReport current{this, aOffset};
10109 bool shouldStopAtHardBreak =
10110 aPos->mWordMovementType == eDefaultBehavior &&
10111 StaticPrefs::layout_word_select_eat_space_to_next_word();
10112 bool wordSelectEatSpace = ShouldWordSelectionEatSpace(*aPos);
10113
10114 PeekWordState state;
10115 while (true) {
10116 bool movingInFrameDirection = IsMovingInFrameDirection(
10117 current.mFrame, aPos->mDirection,
10118 aPos->mOptions.contains(PeekOffsetOption::Visual));
10119
10120 FrameSearchResult searchResult = current.mFrame->PeekOffsetWord(
10121 movingInFrameDirection, wordSelectEatSpace,
10122 aPos->mOptions.contains(PeekOffsetOption::IsKeyboardSelect),
10123 &current.mOffset, &state,
10124 !aPos->mOptions.contains(PeekOffsetOption::PreserveSpaces));
10125 if (searchResult == FOUND) {
10126 break;
10127 }
10128
10129 SelectablePeekReport next = [&]() {
10130 PeekOffsetOptions options = aPos->mOptions;
10131 if (state.mSawInlineCharacter) {
10132 // If we've already found a character, we don't want to stop at
10133 // placeholder frame boundary if there is in the word.
10134 options += PeekOffsetOption::StopAtPlaceholder;
10135 }
10136 return current.mFrame->GetFrameFromDirection(aPos->mDirection, options,
10137 aPos->mAncestorLimiter);
10138 }();
10139 if (next.Failed()) {
10140 // If we've crossed the line boundary, check to make sure that we
10141 // have not consumed a trailing newline as whitespace if it's
10142 // significant.
10143 if (next.mJumpedLine && wordSelectEatSpace &&
10144 current.mFrame->HasSignificantTerminalNewline() &&
10145 current.mFrame->StyleText()->mWhiteSpaceCollapse !=
10146 StyleWhiteSpaceCollapse::PreserveBreaks) {
10147 current.mOffset -= 1;
10148 }
10149 break;
10150 }
10151
10152 if ((next.mJumpedLine || next.mFoundPlaceholder) && !wordSelectEatSpace &&
10153 state.mSawBeforeType) {
10154 // We can't jump lines if we're looking for whitespace following
10155 // non-whitespace, and we already encountered non-whitespace.
10156 break;
10157 }
10158
10159 if (shouldStopAtHardBreak && next.mJumpedHardBreak) {
10160 /**
10161 * Prev, always: Jump and stop right there
10162 * Next, saw inline: just stop
10163 * Next, no inline: Jump and consume whitespaces
10164 */
10165 if (aPos->mDirection == eDirPrevious) {
10166 // Try moving to the previous line if exists
10167 current.TransferTo(*aPos);
10168 current.mFrame->PeekOffsetForCharacter(aPos, current.mOffset);
10169 return NS_OK;
10170 }
10171 if (state.mSawInlineCharacter || current.mJumpedHardBreak) {
10172 if (current.mFrame->HasSignificantTerminalNewline()) {
10173 current.mOffset -= 1;
10174 }
10175 current.TransferTo(*aPos);
10176 return NS_OK;
10177 }
10178 // Mark the state as whitespace and continue
10179 state.Update(false, true);
10180 }
10181
10182 if (next.mJumpedLine) {
10183 state.mContext.Truncate();
10184 }
10185 current = next;
10186 // Jumping a line is equivalent to encountering whitespace
10187 // This affects only when it already met an actual character
10188 if (wordSelectEatSpace && next.mJumpedLine) {
10189 state.SetSawBeforeType();
10190 }
10191 }
10192
10193 // Set outputs
10194 current.TransferTo(*aPos);
10195 return NS_OK;
10196}
10197
10198static nsIFrame* GetFirstSelectableDescendantWithLineIterator(
10199 const PeekOffsetStruct& aPeekOffsetStruct, nsIFrame* aParentFrame) {
10200 const bool forceEditableRegion = aPeekOffsetStruct.mOptions.contains(
10201 PeekOffsetOption::ForceEditableRegion);
10202 auto FoundValidFrame = [aPeekOffsetStruct,
10203 forceEditableRegion](const nsIFrame* aFrame) {
10204 if (!aFrame->IsSelectable()) {
10205 return false;
10206 }
10207 if (!aPeekOffsetStruct.FrameContentIsInAncestorLimiter(aFrame)) {
10208 return false;
10209 }
10210 if (forceEditableRegion && !aFrame->ContentIsEditable()) {
10211 return false;
10212 }
10213 return true;
10214 };
10215
10216 for (nsIFrame* child : aParentFrame->PrincipalChildList()) {
10217 // some children may not be selectable, e.g. :before / :after pseudoelements
10218 // content with user-select: none, or contenteditable="false"
10219 // we need to skip them
10220 if (child->CanProvideLineIterator() && FoundValidFrame(child)) {
10221 return child;
10222 }
10223 if (nsIFrame* nested = GetFirstSelectableDescendantWithLineIterator(
10224 aPeekOffsetStruct, child)) {
10225 return nested;
10226 }
10227 }
10228 return nullptr;
10229}
10230
10231nsresult nsIFrame::PeekOffsetForLine(PeekOffsetStruct* aPos) {
10232 nsIFrame* blockFrame = this;
10233 nsresult result = NS_ERROR_FAILURE;
10234
10235 // outer loop
10236 // moving to a next block when no more blocks are available in a subtree
10237 AutoAssertNoDomMutations guard;
10238 while (NS_FAILED(result)((bool)(__builtin_expect(!!(NS_FAILED_impl(result)), 0)))) {
10239 auto [newBlock, lineFrame] = blockFrame->GetContainingBlockForLine(
10240 aPos->mOptions.contains(PeekOffsetOption::StopAtScroller));
10241 if (!newBlock) {
10242 return NS_ERROR_FAILURE;
10243 }
10244 // FYI: If the editing host is an inline element, the block frame content
10245 // may be either not editable or editable but belonging to different editing
10246 // host.
10247 blockFrame = newBlock;
10248 nsILineIterator* iter = blockFrame->GetLineIterator();
10249 int32_t thisLine = iter->FindLineContaining(lineFrame);
10250 if (NS_WARN_IF(thisLine < 0)NS_warn_if_impl(thisLine < 0, "thisLine < 0", "./../../../layout/generic/nsIFrame.cpp"
, 10250)
) {
10251 return NS_ERROR_FAILURE;
10252 }
10253
10254 int8_t edgeCase = 0; // no edge case. This should look at thisLine
10255
10256 // this part will find a frame or a block frame. If it's a block frame
10257 // it will "drill down" to find a viable frame or it will return an
10258 // error.
10259 nsIFrame* lastFrame = this;
10260
10261 // inner loop - crawling the frames within a specific block subtree
10262 while (true) {
10263 result =
10264 GetNextPrevLineFromBlockFrame(aPos, blockFrame, thisLine, edgeCase);
10265 // we came back to same spot! keep going
10266 if (NS_SUCCEEDED(result)((bool)(__builtin_expect(!!(!NS_FAILED_impl(result)), 1))) &&
10267 (!aPos->mResultFrame || aPos->mResultFrame == lastFrame)) {
10268 aPos->mResultFrame = nullptr;
10269 lastFrame = nullptr;
10270 if (aPos->mDirection == eDirPrevious) {
10271 thisLine--;
10272 } else {
10273 thisLine++;
10274 }
10275 continue;
10276 }
10277
10278 if (NS_FAILED(result)((bool)(__builtin_expect(!!(NS_FAILED_impl(result)), 0)))) {
10279 break;
10280 }
10281
10282 lastFrame = aPos->mResultFrame; // set last frame
10283 /* SPECIAL CHECK FOR NAVIGATION INTO TABLES
10284 * when we hit a frame which doesn't have line iterator, we need to
10285 * drill down and find a child with the line iterator to prevent the
10286 * crawling process to prematurely finish. Note that this is only sound if
10287 * we're guaranteed to not have multiple children implementing
10288 * LineIterator.
10289 *
10290 * So far known cases are:
10291 * 1) table wrapper (drill down into table row group)
10292 * 2) table cell (drill down into its only anon child)
10293 */
10294 const bool shouldDrillIntoChildren =
10295 aPos->mResultFrame->IsTableWrapperFrame() ||
10296 aPos->mResultFrame->IsTableCellFrame();
10297
10298 if (shouldDrillIntoChildren) {
10299 nsIFrame* child = GetFirstSelectableDescendantWithLineIterator(
10300 *aPos, aPos->mResultFrame);
10301 if (child) {
10302 aPos->mResultFrame = child;
10303 }
10304 }
10305
10306 if (!aPos->mResultFrame->CanProvideLineIterator()) {
10307 // no more selectable content at this level
10308 break;
10309 }
10310
10311 if (aPos->mResultFrame == blockFrame) {
10312 // Make sure block element is not the same as the one we had before.
10313 break;
10314 }
10315
10316 // we've struck another block element with selectable content!
10317 if (aPos->mDirection == eDirPrevious) {
10318 edgeCase = 1; // far edge, search from end backwards
10319 } else {
10320 edgeCase = -1; // near edge search from beginning onwards
10321 }
10322 thisLine = 0; // this line means nothing now.
10323 // everything else means something so keep looking "inside" the
10324 // block
10325 blockFrame = aPos->mResultFrame;
10326 }
10327 }
10328 return result;
10329}
10330
10331nsresult nsIFrame::PeekOffsetForLineEdge(PeekOffsetStruct* aPos) {
10332 // Adjusted so that the caret can't get confused when content changes
10333 nsIFrame* frame = AdjustFrameForSelectionStyles(this);
10334 // FIXME: Use PeekOffsetStruct::mAncestorLimiter instead.
10335 Element* editingHost = frame->GetContent()->GetEditingHost();
10336
10337 auto [blockFrame, lineFrame] = frame->GetContainingBlockForLine(
10338 aPos->mOptions.contains(PeekOffsetOption::StopAtScroller));
10339 if (!blockFrame) {
10340 return NS_ERROR_FAILURE;
10341 }
10342 AutoAssertNoDomMutations guard;
10343 nsILineIterator* it = blockFrame->GetLineIterator();
10344 int32_t thisLine = it->FindLineContaining(lineFrame);
10345 if (thisLine < 0) {
10346 return NS_ERROR_FAILURE;
10347 }
10348
10349 nsIFrame* baseFrame = nullptr;
10350 bool endOfLine = eSelectEndLine == aPos->mAmount;
10351
10352 if (aPos->mOptions.contains(PeekOffsetOption::Visual) &&
10353 PresContext()->BidiEnabled()) {
10354 nsIFrame* firstFrame;
10355 bool isReordered;
10356 nsIFrame* lastFrame;
10357 MOZ_TRY(__extension__({ auto mozTryVarTempResult = ::mozilla::ToResult
(it->CheckLineOrder(thisLine, &isReordered, &firstFrame
, &lastFrame)); if ((__builtin_expect(!!(mozTryVarTempResult
.isErr()), 0))) { return mozTryVarTempResult.propagateErr(); }
mozTryVarTempResult.unwrap(); })
10358 it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame))__extension__({ auto mozTryVarTempResult = ::mozilla::ToResult
(it->CheckLineOrder(thisLine, &isReordered, &firstFrame
, &lastFrame)); if ((__builtin_expect(!!(mozTryVarTempResult
.isErr()), 0))) { return mozTryVarTempResult.propagateErr(); }
mozTryVarTempResult.unwrap(); })
;
10359 baseFrame = endOfLine ? lastFrame : firstFrame;
10360 } else {
10361 auto line = it->GetLine(thisLine).unwrap();
10362
10363 nsIFrame* frame = line.mFirstFrameOnLine;
10364 bool lastFrameWasEditable = false;
10365 for (int32_t count = line.mNumFramesOnLine; count;
10366 --count, frame = frame->GetNextSibling()) {
10367 if (frame->IsGeneratedContentFrame()) {
10368 continue;
10369 }
10370 // When jumping to the end of the line with the "end" key,
10371 // try to skip over brFrames
10372 if (endOfLine && line.mNumFramesOnLine > 1 && frame->IsBrFrame() &&
10373 lastFrameWasEditable == frame->GetContent()->IsEditable()) {
10374 continue;
10375 }
10376 lastFrameWasEditable =
10377 frame->GetContent() && frame->GetContent()->IsEditable();
10378 baseFrame = frame;
10379 if (!endOfLine) {
10380 break;
10381 }
10382 }
10383 }
10384 if (!baseFrame) {
10385 return NS_ERROR_FAILURE;
10386 }
10387 // Make sure we are not leaving our inline editing host if exists
10388 if (editingHost) {
10389 if (nsIFrame* frame = editingHost->GetPrimaryFrame()) {
10390 if (frame->IsInlineOutside() &&
10391 !editingHost->Contains(baseFrame->GetContent())) {
10392 baseFrame = frame;
10393 if (endOfLine) {
10394 baseFrame = baseFrame->LastContinuation();
10395 }
10396 }
10397 }
10398 }
10399 FrameTarget targetFrame = DrillDownToSelectionFrame(
10400 baseFrame, endOfLine, nsIFrame::IGNORE_NATIVE_ANONYMOUS_SUBTREE);
10401 SetPeekResultFromFrame(*aPos, targetFrame.frame, endOfLine ? -1 : 0,
10402 OffsetIsAtLineEdge::Yes);
10403 if (endOfLine && targetFrame.frame->HasSignificantTerminalNewline()) {
10404 // Do not position the caret after the terminating newline if we're
10405 // trying to move to the end of line (see bug 596506)
10406 --aPos->mContentOffset;
10407 }
10408 if (!aPos->mResultContent) {
10409 return NS_ERROR_FAILURE;
10410 }
10411 return NS_OK;
10412}
10413
10414nsresult nsIFrame::PeekOffset(PeekOffsetStruct* aPos) {
10415 MOZ_ASSERT(aPos)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aPos)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(aPos))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("aPos", "./../../../layout/generic/nsIFrame.cpp"
, 10415); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aPos" ")")
; do { MOZ_CrashSequence(__null, 10415); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
10416
10417 if (NS_WARN_IF(HasAnyStateBits(NS_FRAME_IS_DIRTY))NS_warn_if_impl(HasAnyStateBits(NS_FRAME_IS_DIRTY), "HasAnyStateBits(NS_FRAME_IS_DIRTY)"
, "./../../../layout/generic/nsIFrame.cpp", 10417)
) {
10418 // FIXME(Bug 1654362): <caption> currently can remain dirty.
10419 return NS_ERROR_UNEXPECTED;
10420 }
10421
10422 // Translate content offset to be relative to frame
10423 int32_t offset = aPos->mStartOffset - GetRangeForFrame(this).start;
10424
10425 switch (aPos->mAmount) {
10426 case eSelectCharacter:
10427 case eSelectCluster:
10428 return PeekOffsetForCharacter(aPos, offset);
10429 case eSelectWordNoSpace:
10430 // eSelectWordNoSpace means that we should not be eating any whitespace
10431 // when moving to the adjacent word. This means that we should set aPos->
10432 // mWordMovementType to eEndWord if we're moving forwards, and to
10433 // eStartWord if we're moving backwards.
10434 if (aPos->mDirection == eDirPrevious) {
10435 aPos->mWordMovementType = eStartWord;
10436 } else {
10437 aPos->mWordMovementType = eEndWord;
10438 }
10439 // Intentionally fall through the eSelectWord case.
10440 [[fallthrough]];
10441 case eSelectWord:
10442 return PeekOffsetForWord(aPos, offset);
10443 case eSelectLine:
10444 return PeekOffsetForLine(aPos);
10445 case eSelectBeginLine:
10446 case eSelectEndLine:
10447 return PeekOffsetForLineEdge(aPos);
10448 case eSelectParagraph:
10449 return PeekOffsetForParagraph(aPos);
10450 default: {
10451 NS_ASSERTION(false, "Invalid amount")do { if (!(false)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "Invalid amount"
, "false", "./../../../layout/generic/nsIFrame.cpp", 10451); MOZ_PretendNoReturn
(); } } while (0)
;
10452 return NS_ERROR_FAILURE;
10453 }
10454 }
10455}
10456
10457nsIFrame::FrameSearchResult nsIFrame::PeekOffsetNoAmount(bool aForward,
10458 int32_t* aOffset) {
10459 NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range")do { if (!(aOffset && *aOffset <= 1)) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "aOffset out of range", "aOffset && *aOffset <= 1"
, "./../../../layout/generic/nsIFrame.cpp", 10459); MOZ_PretendNoReturn
(); } } while (0)
;
10460 // Sure, we can stop right here.
10461 return FOUND;
10462}
10463
10464nsIFrame::FrameSearchResult nsIFrame::PeekOffsetCharacter(
10465 bool aForward, int32_t* aOffset, PeekOffsetCharacterOptions aOptions) {
10466 NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range")do { if (!(aOffset && *aOffset <= 1)) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "aOffset out of range", "aOffset && *aOffset <= 1"
, "./../../../layout/generic/nsIFrame.cpp", 10466); MOZ_PretendNoReturn
(); } } while (0)
;
10467 int32_t startOffset = *aOffset;
10468 // A negative offset means "end of frame", which in our case means offset 1.
10469 if (startOffset < 0) {
10470 startOffset = 1;
10471 }
10472 if (aForward == (startOffset == 0)) {
10473 // We're before the frame and moving forward, or after it and moving
10474 // backwards: skip to the other side and we're done.
10475 *aOffset = 1 - startOffset;
10476 return FOUND;
10477 }
10478 return CONTINUE;
10479}
10480
10481nsIFrame::FrameSearchResult nsIFrame::PeekOffsetWord(
10482 bool aForward, bool aWordSelectEatSpace, bool aIsKeyboardSelect,
10483 int32_t* aOffset, PeekWordState* aState, bool /*aTrimSpaces*/) {
10484 NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range")do { if (!(aOffset && *aOffset <= 1)) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "aOffset out of range", "aOffset && *aOffset <= 1"
, "./../../../layout/generic/nsIFrame.cpp", 10484); MOZ_PretendNoReturn
(); } } while (0)
;
10485 int32_t startOffset = *aOffset;
10486 // This isn't text, so truncate the context
10487 aState->mContext.Truncate();
10488 if (startOffset < 0) {
10489 startOffset = 1;
10490 }
10491 if (aForward == (startOffset == 0)) {
10492 // We're before the frame and moving forward, or after it and moving
10493 // backwards. If we're looking for non-whitespace, we found it (without
10494 // skipping this frame).
10495 if (!aState->mAtStart) {
10496 if (aState->mLastCharWasPunctuation) {
10497 // We're not punctuation, so this is a punctuation boundary.
10498 if (BreakWordBetweenPunctuation(aState, aForward, false, false,
10499 aIsKeyboardSelect)) {
10500 return FOUND;
10501 }
10502 } else {
10503 // This is not a punctuation boundary.
10504 if (aWordSelectEatSpace && aState->mSawBeforeType) {
10505 return FOUND;
10506 }
10507 }
10508 }
10509 // Otherwise skip to the other side and note that we encountered
10510 // non-whitespace.
10511 *aOffset = 1 - startOffset;
10512 aState->Update(false, // not punctuation
10513 false // not whitespace
10514 );
10515 if (!aWordSelectEatSpace) {
10516 aState->SetSawBeforeType();
10517 }
10518 }
10519 return CONTINUE;
10520}
10521
10522// static
10523bool nsIFrame::BreakWordBetweenPunctuation(const PeekWordState* aState,
10524 bool aForward, bool aPunctAfter,
10525 bool aWhitespaceAfter,
10526 bool aIsKeyboardSelect) {
10527 NS_ASSERTION(aPunctAfter != aState->mLastCharWasPunctuation,do { if (!(aPunctAfter != aState->mLastCharWasPunctuation)
) { NS_DebugBreak(NS_DEBUG_ASSERTION, "Call this only at punctuation boundaries"
, "aPunctAfter != aState->mLastCharWasPunctuation", "./../../../layout/generic/nsIFrame.cpp"
, 10528); MOZ_PretendNoReturn(); } } while (0)
10528 "Call this only at punctuation boundaries")do { if (!(aPunctAfter != aState->mLastCharWasPunctuation)
) { NS_DebugBreak(NS_DEBUG_ASSERTION, "Call this only at punctuation boundaries"
, "aPunctAfter != aState->mLastCharWasPunctuation", "./../../../layout/generic/nsIFrame.cpp"
, 10528); MOZ_PretendNoReturn(); } } while (0)
;
10529 if (aState->mLastCharWasWhitespace) {
10530 // We always stop between whitespace and punctuation
10531 return true;
10532 }
10533 if (!StaticPrefs::layout_word_select_stop_at_punctuation()) {
10534 // When this pref is false, we never stop at a punctuation boundary unless
10535 // it's followed by whitespace (in the relevant direction).
10536 return aWhitespaceAfter;
10537 }
10538 if (!aIsKeyboardSelect) {
10539 // mouse caret movement (e.g. word selection) always stops at every
10540 // punctuation boundary
10541 return true;
10542 }
10543 bool afterPunct = aForward ? aState->mLastCharWasPunctuation : aPunctAfter;
10544 if (!afterPunct) {
10545 // keyboard caret movement only stops after punctuation (in content order)
10546 return false;
10547 }
10548 // Stop only if we've seen some non-punctuation since the last whitespace;
10549 // don't stop after punctuation that follows whitespace.
10550 return aState->mSeenNonPunctuationSinceWhitespace;
10551}
10552
10553std::pair<nsIFrame*, nsIFrame*> nsIFrame::GetContainingBlockForLine(
10554 bool aLockScroll) const {
10555 const nsIFrame* parentFrame = this;
10556 const nsIFrame* frame;
10557 while (parentFrame) {
10558 frame = parentFrame;
10559 if (frame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
10560 // if we are searching for a frame that is not in flow we will not find
10561 // it. we must instead look for its placeholder
10562 if (frame->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
10563 // abspos continuations don't have placeholders, get the fif
10564 frame = frame->FirstInFlow();
10565 }
10566 frame = frame->GetPlaceholderFrame();
10567 if (!frame) {
10568 return std::pair(nullptr, nullptr);
10569 }
10570 }
10571 parentFrame = frame->GetParent();
10572 if (parentFrame) {
10573 if (aLockScroll && parentFrame->IsScrollContainerFrame()) {
10574 return std::pair(nullptr, nullptr);
10575 }
10576 if (parentFrame->CanProvideLineIterator()) {
10577 return std::pair(const_cast<nsIFrame*>(parentFrame),
10578 const_cast<nsIFrame*>(frame));
10579 }
10580 }
10581 }
10582 return std::pair(nullptr, nullptr);
10583}
10584
10585Result<bool, nsresult> nsIFrame::IsVisuallyAtLineEdge(
10586 nsILineIterator* aLineIterator, int32_t aLine, nsDirection aDirection) {
10587 auto line = aLineIterator->GetLine(aLine).unwrap();
10588
10589 const bool lineIsRTL = aLineIterator->IsLineIteratorFlowRTL();
10590
10591 nsIFrame *firstFrame = nullptr, *lastFrame = nullptr;
10592 bool isReordered = false;
10593 MOZ_TRY(aLineIterator->CheckLineOrder(aLine, &isReordered, &firstFrame,__extension__({ auto mozTryVarTempResult = ::mozilla::ToResult
(aLineIterator->CheckLineOrder(aLine, &isReordered, &
firstFrame, &lastFrame)); if ((__builtin_expect(!!(mozTryVarTempResult
.isErr()), 0))) { return mozTryVarTempResult.propagateErr(); }
mozTryVarTempResult.unwrap(); })
10594 &lastFrame))__extension__({ auto mozTryVarTempResult = ::mozilla::ToResult
(aLineIterator->CheckLineOrder(aLine, &isReordered, &
firstFrame, &lastFrame)); if ((__builtin_expect(!!(mozTryVarTempResult
.isErr()), 0))) { return mozTryVarTempResult.propagateErr(); }
mozTryVarTempResult.unwrap(); })
;
10595 if (!firstFrame || !lastFrame) {
10596 return true; // XXX: Why true? We check whether `this` is at the edge...
10597 }
10598
10599 nsIFrame* leftmostFrame = lineIsRTL ? lastFrame : firstFrame;
10600 nsIFrame* rightmostFrame = lineIsRTL ? firstFrame : lastFrame;
10601 auto FrameIsRTL = [](nsIFrame* aFrame) {
10602 return nsBidiPresUtils::FrameDirection(aFrame) ==
10603 mozilla::intl::BidiDirection::RTL;
10604 };
10605 if (!lineIsRTL == (aDirection == eDirPrevious)) {
10606 nsIFrame* maybeLeftmostFrame = leftmostFrame;
10607 for ([[maybe_unused]] int32_t i : IntegerRange(line.mNumFramesOnLine)) {
10608 if (maybeLeftmostFrame == this) {
10609 return true;
10610 }
10611 // If left edge of the line starts with placeholder frames, we can ignore
10612 // them and should keep checking the following frames.
10613 if (!maybeLeftmostFrame->IsPlaceholderFrame()) {
10614 if ((FrameIsRTL(maybeLeftmostFrame) == lineIsRTL) ==
10615 (aDirection == eDirPrevious)) {
10616 nsIFrame::GetFirstLeaf(&maybeLeftmostFrame);
10617 } else {
10618 nsIFrame::GetLastLeaf(&maybeLeftmostFrame);
10619 }
10620 return maybeLeftmostFrame == this;
10621 }
10622 maybeLeftmostFrame = nsBidiPresUtils::GetFrameToRightOf(
10623 maybeLeftmostFrame, line.mFirstFrameOnLine, line.mNumFramesOnLine);
10624 if (!maybeLeftmostFrame) {
10625 return false;
10626 }
10627 }
10628 return false;
10629 }
10630
10631 nsIFrame* maybeRightmostFrame = rightmostFrame;
10632 for ([[maybe_unused]] int32_t i : IntegerRange(line.mNumFramesOnLine)) {
10633 if (maybeRightmostFrame == this) {
10634 return true;
10635 }
10636 // If the line ends with placehlder frames, we can ignore them and should
10637 // keep checking the preceding frames.
10638 if (!maybeRightmostFrame->IsPlaceholderFrame()) {
10639 if ((FrameIsRTL(maybeRightmostFrame) == lineIsRTL) ==
10640 (aDirection == eDirPrevious)) {
10641 nsIFrame::GetFirstLeaf(&maybeRightmostFrame);
10642 } else {
10643 nsIFrame::GetLastLeaf(&maybeRightmostFrame);
10644 }
10645 return maybeRightmostFrame == this;
10646 }
10647 maybeRightmostFrame = nsBidiPresUtils::GetFrameToLeftOf(
10648 maybeRightmostFrame, line.mFirstFrameOnLine, line.mNumFramesOnLine);
10649 if (!maybeRightmostFrame) {
10650 return false;
10651 }
10652 }
10653 return false;
10654}
10655
10656Result<bool, nsresult> nsIFrame::IsLogicallyAtLineEdge(
10657 nsILineIterator* aLineIterator, int32_t aLine, nsDirection aDirection) {
10658 auto line = aLineIterator->GetLine(aLine).unwrap();
10659 if (!line.mNumFramesOnLine) {
10660 return false;
10661 }
10662 MOZ_ASSERT(line.mFirstFrameOnLine)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(line.mFirstFrameOnLine)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(line.mFirstFrameOnLine))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("line.mFirstFrameOnLine"
, "./../../../layout/generic/nsIFrame.cpp", 10662); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "line.mFirstFrameOnLine" ")"); do { MOZ_CrashSequence
(__null, 10662); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
10663
10664 if (aDirection == eDirPrevious) {
10665 nsIFrame* maybeFirstFrame = line.mFirstFrameOnLine;
10666 for ([[maybe_unused]] int32_t i : IntegerRange(line.mNumFramesOnLine)) {
10667 if (maybeFirstFrame == this) {
10668 return true;
10669 }
10670 // If the line starts with placeholder frames, we can ignore them and
10671 // should keep checking the following frames.
10672 if (!maybeFirstFrame->IsPlaceholderFrame()) {
10673 nsIFrame::GetFirstLeaf(&maybeFirstFrame);
10674 return maybeFirstFrame == this;
10675 }
10676 maybeFirstFrame = maybeFirstFrame->GetNextSibling();
10677 if (!maybeFirstFrame) {
10678 return false;
10679 }
10680 }
10681 return false;
10682 }
10683
10684 // eDirNext
10685 nsIFrame* maybeLastFrame = line.GetLastFrameOnLine();
10686 for ([[maybe_unused]] int32_t i : IntegerRange(line.mNumFramesOnLine)) {
10687 if (maybeLastFrame == this) {
10688 return true;
10689 }
10690 // If the line ends with placehlder frames, we can ignore them and should
10691 // keep checking the preceding frames.
10692 if (!maybeLastFrame->IsPlaceholderFrame()) {
10693 nsIFrame::GetLastLeaf(&maybeLastFrame);
10694 return maybeLastFrame == this;
10695 }
10696 maybeLastFrame = maybeLastFrame->GetPrevSibling();
10697 }
10698 return false;
10699}
10700
10701nsIFrame::SelectablePeekReport nsIFrame::GetFrameFromDirection(
10702 nsDirection aDirection, const PeekOffsetOptions& aOptions,
10703 const Element* aAncestorLimiter) {
10704 SelectablePeekReport result;
10705
10706 nsPresContext* presContext = PresContext();
10707 const bool needsVisualTraversal =
10708 aOptions.contains(PeekOffsetOption::Visual) && presContext->BidiEnabled();
10709 const bool followOofs =
10710 !aOptions.contains(PeekOffsetOption::StopAtPlaceholder);
10711 nsFrameIterator frameIterator(
10712 presContext, this, nsFrameIterator::Type::Leaf, needsVisualTraversal,
10713 aOptions.contains(PeekOffsetOption::StopAtScroller), followOofs,
10714 false, // aSkipPopupChecks
10715 aAncestorLimiter);
10716
10717 // Find the prev/next selectable frame
10718 bool selectable = false;
10719 nsIFrame* traversedFrame = this;
10720 AutoAssertNoDomMutations guard;
10721 const nsFrameSelection* frameSelection =
10722 GetContent() ? GetContent()->GetFrameSelection() : nullptr;
10723 while (!selectable) {
10724 auto [blockFrame, lineFrame] = traversedFrame->GetContainingBlockForLine(
10725 aOptions.contains(PeekOffsetOption::StopAtScroller));
10726 if (!blockFrame) {
10727 return result;
10728 }
10729
10730 nsILineIterator* it = blockFrame->GetLineIterator();
10731 int32_t thisLine = it->FindLineContaining(lineFrame);
10732 if (thisLine < 0) {
10733 return result;
10734 }
10735
10736 bool atLineEdge = MOZ_TRY(__extension__({ auto mozTryVarTempResult = ::mozilla::ToResult
(needsVisualTraversal ? traversedFrame->IsVisuallyAtLineEdge
(it, thisLine, aDirection) : traversedFrame->IsLogicallyAtLineEdge
(it, thisLine, aDirection)); if ((__builtin_expect(!!(mozTryVarTempResult
.isErr()), 0))) { return mozTryVarTempResult.propagateErr(); }
mozTryVarTempResult.unwrap(); })
10737 needsVisualTraversal__extension__({ auto mozTryVarTempResult = ::mozilla::ToResult
(needsVisualTraversal ? traversedFrame->IsVisuallyAtLineEdge
(it, thisLine, aDirection) : traversedFrame->IsLogicallyAtLineEdge
(it, thisLine, aDirection)); if ((__builtin_expect(!!(mozTryVarTempResult
.isErr()), 0))) { return mozTryVarTempResult.propagateErr(); }
mozTryVarTempResult.unwrap(); })
10738 ? traversedFrame->IsVisuallyAtLineEdge(it, thisLine, aDirection)__extension__({ auto mozTryVarTempResult = ::mozilla::ToResult
(needsVisualTraversal ? traversedFrame->IsVisuallyAtLineEdge
(it, thisLine, aDirection) : traversedFrame->IsLogicallyAtLineEdge
(it, thisLine, aDirection)); if ((__builtin_expect(!!(mozTryVarTempResult
.isErr()), 0))) { return mozTryVarTempResult.propagateErr(); }
mozTryVarTempResult.unwrap(); })
10739 : traversedFrame->IsLogicallyAtLineEdge(it, thisLine, aDirection))__extension__({ auto mozTryVarTempResult = ::mozilla::ToResult
(needsVisualTraversal ? traversedFrame->IsVisuallyAtLineEdge
(it, thisLine, aDirection) : traversedFrame->IsLogicallyAtLineEdge
(it, thisLine, aDirection)); if ((__builtin_expect(!!(mozTryVarTempResult
.isErr()), 0))) { return mozTryVarTempResult.propagateErr(); }
mozTryVarTempResult.unwrap(); })
;
10740 if (atLineEdge) {
10741 result.mJumpedLine = true;
10742 if (!aOptions.contains(PeekOffsetOption::JumpLines)) {
10743 return result; // we are done. cannot jump lines
10744 }
10745 int32_t lineToCheckWrap =
10746 aDirection == eDirPrevious ? thisLine - 1 : thisLine;
10747 if (lineToCheckWrap < 0 ||
10748 !it->GetLine(lineToCheckWrap).unwrap().mIsWrapped) {
10749 result.mJumpedHardBreak = true;
10750 }
10751 }
10752
10753 traversedFrame = frameIterator.Traverse(aDirection == eDirNext);
10754 if (!traversedFrame) {
10755 return result;
10756 }
10757
10758 if (aOptions.contains(PeekOffsetOption::StopAtPlaceholder) &&
10759 traversedFrame->IsPlaceholderFrame()) {
10760 // XXX If the placeholder frame does not have meaningful content, the user
10761 // may want to select as a word around the out-of-flow cotent. However,
10762 // non-text frame resets context in nsIFrame::PeekOffsetWord(). Therefore,
10763 // next text frame considers the new word starts from its edge. So, it's
10764 // not enough to implement such behavior with adding a check here whether
10765 // the real frame may change the word with its contents if it were not
10766 // out-of-flow.
10767 result.mFoundPlaceholder = true;
10768 return result;
10769 }
10770
10771 auto IsSelectableFrame = [aAncestorLimiter, aOptions,
10772 frameSelection](const nsIFrame* aFrame) {
10773 if (!aFrame->IsSelectable() || MOZ_UNLIKELY(!aFrame->GetContent())(__builtin_expect(!!(!aFrame->GetContent()), 0))) {
10774 return false;
10775 }
10776 // If the found frame content is managed by different nsFrameSelection, we
10777 // cannot select the frame content with current selection.
10778 if (frameSelection != aFrame->GetContent()->GetFrameSelection()) {
10779 return false;
10780 }
10781 if (MOZ_UNLIKELY(aAncestorLimiter &&(__builtin_expect(!!(aAncestorLimiter && !aAncestorLimiter
->GetPrimaryFrame()), 0))
10782 !aAncestorLimiter->GetPrimaryFrame())(__builtin_expect(!!(aAncestorLimiter && !aAncestorLimiter
->GetPrimaryFrame()), 0))
&&
10783 !aFrame->GetContent()->IsInclusiveFlatTreeDescendantOf(
10784 aAncestorLimiter)) {
10785 return false;
10786 }
10787 return !aOptions.contains(PeekOffsetOption::ForceEditableRegion) ||
10788 aFrame->GetContent()->IsEditable();
10789 };
10790
10791 // Skip br frames, but only if we can select something before hitting the
10792 // end of the line or a non-selectable region.
10793 if (atLineEdge && aDirection == eDirPrevious &&
10794 traversedFrame->IsBrFrame()) {
10795 for (nsIFrame* current = traversedFrame->GetPrevSibling(); current;
10796 current = current->GetPrevSibling()) {
10797 if (!current->IsBlockOutside() && IsSelectableFrame(current)) {
10798 if (!current->IsBrFrame()) {
10799 result.mIgnoredBrFrame = true;
10800 }
10801 break;
10802 }
10803 }
10804 if (result.mIgnoredBrFrame) {
10805 continue;
10806 }
10807 }
10808
10809 selectable = IsSelectableFrame(traversedFrame);
10810 if (MOZ_UNLIKELY(!frameSelection)(__builtin_expect(!!(!frameSelection), 0)) && selectable &&
10811 MOZ_LIKELY(traversedFrame->GetContent())(__builtin_expect(!!(traversedFrame->GetContent()), 1))) {
10812 frameSelection = traversedFrame->GetContent()->GetFrameSelection();
10813 }
10814 if (!selectable) {
10815 if (traversedFrame->IsSelectable()) {
10816 result.mHasSelectableFrame = true;
10817 }
10818 result.mMovedOverNonSelectableText = true;
10819 }
10820 } // while (!selectable)
10821
10822 result.mOffset = (aDirection == eDirNext) ? 0 : -1;
10823
10824 if (aOptions.contains(PeekOffsetOption::Visual) &&
10825 nsBidiPresUtils::IsReversedDirectionFrame(traversedFrame)) {
10826 // The new frame is reverse-direction, go to the other end
10827 result.mOffset = -1 - result.mOffset;
10828 }
10829 result.mFrame = traversedFrame;
10830 return result;
10831}
10832
10833nsIFrame::SelectablePeekReport nsIFrame::GetFrameFromDirection(
10834 const PeekOffsetStruct& aPos) {
10835 return GetFrameFromDirection(aPos.mDirection, aPos.mOptions,
10836 aPos.mAncestorLimiter);
10837}
10838
10839/* virtual */
10840void nsIFrame::ChildIsDirty(nsIFrame* aChild) {
10841 MOZ_ASSERT_UNREACHABLE(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"should never be called on a frame that doesn't " "inherit from nsContainerFrame"
")", "./../../../layout/generic/nsIFrame.cpp", 10843); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "false" ") (" "MOZ_ASSERT_UNREACHABLE: " "should never be called on a frame that doesn't "
"inherit from nsContainerFrame" ")"); do { MOZ_CrashSequence
(__null, 10843); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
10842 "should never be called on a frame that doesn't "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"should never be called on a frame that doesn't " "inherit from nsContainerFrame"
")", "./../../../layout/generic/nsIFrame.cpp", 10843); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "false" ") (" "MOZ_ASSERT_UNREACHABLE: " "should never be called on a frame that doesn't "
"inherit from nsContainerFrame" ")"); do { MOZ_CrashSequence
(__null, 10843); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
10843 "inherit from nsContainerFrame")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"should never be called on a frame that doesn't " "inherit from nsContainerFrame"
")", "./../../../layout/generic/nsIFrame.cpp", 10843); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "false" ") (" "MOZ_ASSERT_UNREACHABLE: " "should never be called on a frame that doesn't "
"inherit from nsContainerFrame" ")"); do { MOZ_CrashSequence
(__null, 10843); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
10844}
10845
10846#ifdef ACCESSIBILITY1
10847a11y::AccType nsIFrame::AccessibleType() {
10848 if (IsTableCaption() && !GetRect().IsEmpty()) {
10849 return a11y::eHTMLCaptionType;
10850 }
10851 return a11y::eNoType;
10852}
10853#endif
10854
10855bool nsIFrame::ClearOverflowRects() {
10856 if (mOverflow.mType == OverflowStorageType::None) {
10857 return false;
10858 }
10859 if (mOverflow.mType == OverflowStorageType::Large) {
10860 RemoveProperty(OverflowAreasProperty());
10861 }
10862 mOverflow.mType = OverflowStorageType::None;
10863 return true;
10864}
10865
10866bool nsIFrame::SetOverflowAreas(const OverflowAreas& aOverflowAreas) {
10867 if (mOverflow.mType == OverflowStorageType::Large) {
10868 OverflowAreas* overflow = GetOverflowAreasProperty();
10869 bool changed = *overflow != aOverflowAreas;
10870 *overflow = aOverflowAreas;
10871
10872 // Don't bother with converting to the deltas form if we already
10873 // have a property.
10874 return changed;
10875 }
10876
10877 const nsRect& vis = aOverflowAreas.InkOverflow();
10878 uint32_t l = -vis.x, // left edge: positive delta is leftwards
10879 t = -vis.y, // top: positive is upwards
10880 r = vis.XMost() - mRect.width, // right: positive is rightwards
10881 b = vis.YMost() - mRect.height; // bottom: positive is downwards
10882 if (aOverflowAreas.ScrollableOverflow().IsEqualEdges(
10883 nsRect(nsPoint(0, 0), GetSize())) &&
10884 l <= InkOverflowDeltas::kMax && t <= InkOverflowDeltas::kMax &&
10885 r <= InkOverflowDeltas::kMax && b <= InkOverflowDeltas::kMax &&
10886 // we have to check these against zero because we *never* want to
10887 // set a frame as having no overflow in this function. This is
10888 // because FinishAndStoreOverflow calls this function prior to
10889 // SetRect based on whether the overflow areas match aNewSize.
10890 // In the case where the overflow areas exactly match mRect but
10891 // do not match aNewSize, we need to store overflow in a property
10892 // so that our eventual SetRect/SetSize will know that it has to
10893 // reset our overflow areas.
10894 (l | t | r | b) != 0) {
10895 InkOverflowDeltas oldDeltas = mOverflow.mInkOverflowDeltas;
10896 // It's a "small" overflow area so we store the deltas for each edge
10897 // directly in the frame, rather than allocating a separate rect.
10898 // If they're all zero, that's fine; we're setting things to
10899 // no-overflow.
10900 mOverflow.mInkOverflowDeltas.mLeft = l;
10901 mOverflow.mInkOverflowDeltas.mTop = t;
10902 mOverflow.mInkOverflowDeltas.mRight = r;
10903 mOverflow.mInkOverflowDeltas.mBottom = b;
10904 // There was no scrollable overflow before, and there isn't now.
10905 return oldDeltas != mOverflow.mInkOverflowDeltas;
10906 } else {
10907 bool changed =
10908 !aOverflowAreas.ScrollableOverflow().IsEqualEdges(
10909 nsRect(nsPoint(0, 0), GetSize())) ||
10910 !aOverflowAreas.InkOverflow().IsEqualEdges(InkOverflowFromDeltas());
10911
10912 // it's a large overflow area that we need to store as a property
10913 mOverflow.mType = OverflowStorageType::Large;
10914 AddProperty(OverflowAreasProperty(), new OverflowAreas(aOverflowAreas));
10915 return changed;
10916 }
10917}
10918
10919enum class ApplyTransform : bool { No, Yes };
10920
10921/**
10922 * Compute the outline inner rect (so without outline-width and outline-offset)
10923 * of aFrame, maybe iterating over its descendants, in aFrame's coordinate space
10924 * or its post-transform coordinate space (depending on aApplyTransform).
10925 */
10926static nsRect ComputeOutlineInnerRect(
10927 nsIFrame* aFrame, ApplyTransform aApplyTransform, bool& aOutValid,
10928 const nsSize* aSizeOverride = nullptr,
10929 const OverflowAreas* aOverflowOverride = nullptr) {
10930 const nsRect bounds(nsPoint(0, 0),
10931 aSizeOverride ? *aSizeOverride : aFrame->GetSize());
10932
10933 // The SVG container frames besides SVGTextFrame do not maintain
10934 // an accurate mRect. It will make the outline be larger than
10935 // we expect, we need to make them narrow to their children's outline.
10936 // aOutValid is set to false if the returned nsRect is not valid
10937 // and should not be included in the outline rectangle.
10938 aOutValid = !aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT) ||
10939 !aFrame->IsSVGContainerFrame() || aFrame->IsSVGTextFrame();
10940
10941 nsRect u;
10942
10943 if (!aFrame->FrameMaintainsOverflow()) {
10944 return u;
10945 }
10946
10947 // Start from our border-box, transformed. See comment below about
10948 // transform of children.
10949 bool doTransform =
10950 aApplyTransform == ApplyTransform::Yes && aFrame->IsTransformed();
10951 TransformReferenceBox boundsRefBox(nullptr, bounds);
10952 if (doTransform) {
10953 u = nsDisplayTransform::TransformRect(bounds, aFrame, boundsRefBox);
10954 } else {
10955 u = bounds;
10956 }
10957
10958 if (aOutValid && !StaticPrefs::layout_outline_include_overflow()) {
10959 return u;
10960 }
10961
10962 // Only iterate through the children if the overflow areas suggest
10963 // that we might need to, and if the frame doesn't clip its overflow
10964 // anyway.
10965 if (aOverflowOverride) {
10966 if (!doTransform && bounds.IsEqualEdges(aOverflowOverride->InkOverflow()) &&
10967 bounds.IsEqualEdges(aOverflowOverride->ScrollableOverflow())) {
10968 return u;
10969 }
10970 } else {
10971 if (!doTransform && bounds.IsEqualEdges(aFrame->InkOverflowRect()) &&
10972 bounds.IsEqualEdges(aFrame->ScrollableOverflowRect())) {
10973 return u;
10974 }
10975 }
10976 const nsStyleDisplay* disp = aFrame->StyleDisplay();
10977 LayoutFrameType fType = aFrame->Type();
10978 if (fType == LayoutFrameType::ScrollContainer ||
10979 fType == LayoutFrameType::ListControl ||
10980 fType == LayoutFrameType::SVGOuterSVG) {
10981 return u;
10982 }
10983
10984 auto overflowClipAxes = aFrame->ShouldApplyOverflowClipping(disp);
10985 auto overflowClipMargin = aFrame->OverflowClipMargin(
10986 overflowClipAxes, /* aAllowNegative = */ false);
10987 if (overflowClipAxes == kPhysicalAxesBoth && overflowClipMargin.IsAllZero()) {
10988 return u;
10989 }
10990
10991 const nsStyleEffects* effects = aFrame->StyleEffects();
10992 Maybe<nsRect> clipPropClipRect =
10993 aFrame->GetClipPropClipRect(disp, effects, bounds.Size());
10994
10995 // Iterate over all children except pop-up, absolutely-positioned,
10996 // float, and overflow ones.
10997 const FrameChildListIDs skip = {FrameChildListID::Absolute,
10998 FrameChildListID::Float,
10999 FrameChildListID::Overflow};
11000 for (const auto& [list, listID] : aFrame->ChildLists()) {
11001 if (skip.contains(listID)) {
11002 continue;
11003 }
11004
11005 for (nsIFrame* child : list) {
11006 if (child->IsPlaceholderFrame()) {
11007 continue;
11008 }
11009
11010 // Note that passing ApplyTransform::Yes when
11011 // child->Combines3DTransformWithAncestors() returns true is incorrect if
11012 // our aApplyTransform is No... but the opposite would be as well.
11013 // This is because elements within a preserve-3d scene are always
11014 // transformed up to the top of the scene. This means we don't have a
11015 // mechanism for getting a transform up to an intermediate point within
11016 // the scene. We choose to over-transform rather than under-transform
11017 // because this is consistent with other overflow areas.
11018 bool validRect = true;
11019 nsRect childRect =
11020 ComputeOutlineInnerRect(child, ApplyTransform::Yes, validRect) +
11021 child->GetPosition();
11022
11023 if (!validRect) {
11024 continue;
11025 }
11026
11027 if (clipPropClipRect) {
11028 // Intersect with the clip before transforming.
11029 childRect.IntersectRect(childRect, *clipPropClipRect);
11030 }
11031
11032 // Note that we transform each child separately according to
11033 // aFrame's transform, and then union, which gives a different
11034 // (smaller) result from unioning and then transforming the
11035 // union. This doesn't match the way we handle overflow areas
11036 // with 2-D transforms, though it does match the way we handle
11037 // overflow areas in preserve-3d 3-D scenes.
11038 if (doTransform && !child->Combines3DTransformWithAncestors()) {
11039 childRect =
11040 nsDisplayTransform::TransformRect(childRect, aFrame, boundsRefBox);
11041 }
11042
11043 // If a SVGContainer has a non-SVGContainer child, we assign
11044 // its child's outline to this SVGContainer directly.
11045 if (!aOutValid && validRect) {
11046 u = childRect;
11047 aOutValid = true;
11048 } else {
11049 u = u.UnionEdges(childRect);
11050 }
11051 }
11052 }
11053
11054 if (!overflowClipAxes.isEmpty()) {
11055 OverflowAreas::ApplyOverflowClippingOnRect(u, bounds, overflowClipAxes,
11056 overflowClipMargin);
11057 }
11058 return u;
11059}
11060
11061static void ComputeAndIncludeOutlineArea(nsIFrame* aFrame,
11062 OverflowAreas& aOverflowAreas,
11063 const nsSize& aNewSize) {
11064 const nsStyleOutline* outline = aFrame->StyleOutline();
11065 if (!outline->ShouldPaintOutline()) {
11066 return;
11067 }
11068
11069 // When the outline property is set on a :-moz-block-inside-inline-wrapper
11070 // pseudo-element, it inherited that outline from the inline that was broken
11071 // because it contained a block. In that case, we don't want a really wide
11072 // outline if the block inside the inline is narrow, so union the actual
11073 // contents of the anonymous blocks.
11074 nsIFrame* frameForArea = aFrame;
11075 do {
11076 PseudoStyleType pseudoType = frameForArea->Style()->GetPseudoType();
11077 if (pseudoType != PseudoStyleType::MozBlockInsideInlineWrapper) {
11078 break;
11079 }
11080 // If we're done, we really want it and all its later siblings.
11081 frameForArea = frameForArea->PrincipalChildList().FirstChild();
11082 NS_ASSERTION(frameForArea, "anonymous block with no children?")do { if (!(frameForArea)) { NS_DebugBreak(NS_DEBUG_ASSERTION,
"anonymous block with no children?", "frameForArea", "./../../../layout/generic/nsIFrame.cpp"
, 11082); MOZ_PretendNoReturn(); } } while (0)
;
11083 } while (frameForArea);
11084
11085 // Find the union of the border boxes of all descendants, or in
11086 // the block-in-inline case, all descendants we care about.
11087 //
11088 // Note that the interesting perspective-related cases are taken
11089 // care of by the code that handles those issues for overflow
11090 // calling FinishAndStoreOverflow again, which in turn calls this
11091 // function again. We still need to deal with preserve-3d a bit.
11092 nsRect innerRect;
11093 bool validRect = false;
11094 if (frameForArea == aFrame) {
11095 innerRect = ComputeOutlineInnerRect(aFrame, ApplyTransform::No, validRect,
11096 &aNewSize, &aOverflowAreas);
11097 } else {
11098 for (; frameForArea; frameForArea = frameForArea->GetNextSibling()) {
11099 nsRect r =
11100 ComputeOutlineInnerRect(frameForArea, ApplyTransform::Yes, validRect);
11101
11102 // Adjust for offsets transforms up to aFrame's pre-transform
11103 // (i.e., normal) coordinate space; see comments in
11104 // UnionBorderBoxes for some of the subtlety here.
11105 for (nsIFrame *f = frameForArea, *parent = f->GetParent();
11106 /* see middle of loop */; f = parent, parent = f->GetParent()) {
11107 r += f->GetPosition();
11108 if (parent == aFrame) {
11109 break;
11110 }
11111 if (parent->IsTransformed() && !f->Combines3DTransformWithAncestors()) {
11112 TransformReferenceBox refBox(parent);
11113 r = nsDisplayTransform::TransformRect(r, parent, refBox);
11114 }
11115 }
11116
11117 innerRect.UnionRect(innerRect, r);
11118 }
11119 }
11120
11121 // Keep this code in sync with nsDisplayOutline::GetInnerRect.
11122 if (innerRect == aFrame->GetRectRelativeToSelf()) {
11123 aFrame->RemoveProperty(nsIFrame::OutlineInnerRectProperty());
11124 } else {
11125 aFrame->SetOrUpdateDeletableProperty(nsIFrame::OutlineInnerRectProperty(),
11126 innerRect);
11127 }
11128
11129 nsRect outerRect(innerRect);
11130 outerRect.Inflate(outline->EffectiveOffsetFor(outerRect));
11131
11132 if (outline->mOutlineStyle.IsAuto()) {
11133 nsPresContext* pc = aFrame->PresContext();
11134
11135 pc->Theme()->GetWidgetOverflow(pc->DeviceContext(), aFrame,
11136 StyleAppearance::FocusOutline, &outerRect);
11137 } else {
11138 outerRect.Inflate(outline->mOutlineWidth);
11139 }
11140
11141 nsRect& vo = aOverflowAreas.InkOverflow();
11142 vo = vo.UnionEdges(innerRect.Union(outerRect));
11143}
11144
11145bool nsIFrame::FinishAndStoreOverflow(OverflowAreas& aOverflowAreas,
11146 nsSize aNewSize,
11147 const nsStyleDisplay* aStyleDisplay) {
11148 MOZ_ASSERT(FrameMaintainsOverflow(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(FrameMaintainsOverflow())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(FrameMaintainsOverflow()))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("FrameMaintainsOverflow()"
" (" "Don't call - overflow rects not maintained on these SVG frames"
")", "./../../../layout/generic/nsIFrame.cpp", 11149); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "FrameMaintainsOverflow()" ") (" "Don't call - overflow rects not maintained on these SVG frames"
")"); do { MOZ_CrashSequence(__null, 11149); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
11149 "Don't call - overflow rects not maintained on these SVG frames")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(FrameMaintainsOverflow())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(FrameMaintainsOverflow()))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("FrameMaintainsOverflow()"
" (" "Don't call - overflow rects not maintained on these SVG frames"
")", "./../../../layout/generic/nsIFrame.cpp", 11149); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "FrameMaintainsOverflow()" ") (" "Don't call - overflow rects not maintained on these SVG frames"
")"); do { MOZ_CrashSequence(__null, 11149); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11150
11151 const nsStyleDisplay* disp = StyleDisplayWithOptionalParam(aStyleDisplay);
11152 bool hasTransform = IsTransformed();
11153
11154 nsRect bounds(nsPoint(), aNewSize);
11155 // Store the passed in overflow area if we are a preserve-3d frame or we have
11156 // a transform, and it's not just the frame bounds.
11157 if (hasTransform || Combines3DTransformWithAncestors()) {
11158 if (!aOverflowAreas.InkOverflow().IsEqualEdges(bounds) ||
11159 !aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds)) {
11160 OverflowAreas* initial = GetProperty(nsIFrame::InitialOverflowProperty());
11161 if (!initial) {
11162 AddProperty(nsIFrame::InitialOverflowProperty(),
11163 new OverflowAreas(aOverflowAreas));
11164 } else if (initial != &aOverflowAreas) {
11165 *initial = aOverflowAreas;
11166 }
11167 } else {
11168 RemoveProperty(nsIFrame::InitialOverflowProperty());
11169 }
11170#ifdef DEBUG1
11171 SetProperty(nsIFrame::DebugInitialOverflowPropertyApplied(), true);
11172#endif
11173 } else {
11174#ifdef DEBUG1
11175 RemoveProperty(nsIFrame::DebugInitialOverflowPropertyApplied());
11176#endif
11177 }
11178
11179 const nsSize oldSize = mRect.Size();
11180 const bool sizeChanged = oldSize != aNewSize;
11181
11182 // Our frame size may not have been computed and set yet, but code under
11183 // functions such as ComputeEffectsRect (which we're about to call) use the
11184 // values that are stored in our frame rect to compute their results. We
11185 // need the results from those functions to be based on the frame size that
11186 // we *will* have, so we temporarily set our frame size here before calling
11187 // those functions.
11188 //
11189 // XXX Someone should document here why we revert the frame size before we
11190 // return rather than just leaving it set.
11191 //
11192 // We pass false here to avoid invalidating display items for this temporary
11193 // change. We sometimes reflow frames multiple times, with the final size
11194 // being the same as the initial. The single call to SetSize after reflow is
11195 // done will take care of invalidating display items if the size has actually
11196 // changed.
11197 SetSize(aNewSize, false);
11198
11199 if (sizeChanged && ChildrenHavePerspective(disp) &&
11200 RecomputePerspectiveChildrenOverflow(this)) {
11201 aOverflowAreas.SetAllTo(bounds);
11202 DebugOnly<bool> ok = ComputeCustomOverflow(aOverflowAreas);
11203 // ComputeCustomOverflow() should not return false, when
11204 // FrameMaintainsOverflow() returns true.
11205 MOZ_ASSERT(ok, "FrameMaintainsOverflow() != ComputeCustomOverflow()")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(ok)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(ok))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("ok" " (" "FrameMaintainsOverflow() != ComputeCustomOverflow()"
")", "./../../../layout/generic/nsIFrame.cpp", 11205); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ok" ") (" "FrameMaintainsOverflow() != ComputeCustomOverflow()"
")"); do { MOZ_CrashSequence(__null, 11205); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11206 UnionChildOverflow(aOverflowAreas);
11207 }
11208
11209 const auto overflowClipAxes = ShouldApplyOverflowClipping(disp);
11210
11211 // This is now called FinishAndStoreOverflow() instead of
11212 // StoreOverflow() because frame-generic ways of adding overflow
11213 // can happen here, e.g. CSS2 outline and native theme.
11214 // If the overflow area width or height is nscoord_MAX, then a saturating
11215 // union may have encountered an overflow, so the overflow may not contain the
11216 // frame border-box. Don't warn in that case.
11217 // Don't warn for SVG either, since SVG doesn't need the overflow area
11218 // to contain the frame bounds.
11219#ifdef DEBUG1
11220 for (const auto otype : AllOverflowTypes()) {
11221 const nsRect& r = aOverflowAreas.Overflow(otype);
11222 NS_ASSERTION(aNewSize.width == 0 || aNewSize.height == 0 ||do { if (!(aNewSize.width == 0 || aNewSize.height == 0 || r.width
== nscoord_MAX || r.height == nscoord_MAX || HasAnyStateBits
(NS_FRAME_SVG_LAYOUT) || r.Contains(nsRect(nsPoint(), aNewSize
)))) { NS_DebugBreak(NS_DEBUG_ASSERTION, "Computed overflow area must contain frame bounds"
, "aNewSize.width == 0 || aNewSize.height == 0 || r.width == nscoord_MAX || r.height == nscoord_MAX || HasAnyStateBits(NS_FRAME_SVG_LAYOUT) || r.Contains(nsRect(nsPoint(), aNewSize))"
, "./../../../layout/generic/nsIFrame.cpp", 11226); MOZ_PretendNoReturn
(); } } while (0)
11223 r.width == nscoord_MAX || r.height == nscoord_MAX ||do { if (!(aNewSize.width == 0 || aNewSize.height == 0 || r.width
== nscoord_MAX || r.height == nscoord_MAX || HasAnyStateBits
(NS_FRAME_SVG_LAYOUT) || r.Contains(nsRect(nsPoint(), aNewSize
)))) { NS_DebugBreak(NS_DEBUG_ASSERTION, "Computed overflow area must contain frame bounds"
, "aNewSize.width == 0 || aNewSize.height == 0 || r.width == nscoord_MAX || r.height == nscoord_MAX || HasAnyStateBits(NS_FRAME_SVG_LAYOUT) || r.Contains(nsRect(nsPoint(), aNewSize))"
, "./../../../layout/generic/nsIFrame.cpp", 11226); MOZ_PretendNoReturn
(); } } while (0)
11224 HasAnyStateBits(NS_FRAME_SVG_LAYOUT) ||do { if (!(aNewSize.width == 0 || aNewSize.height == 0 || r.width
== nscoord_MAX || r.height == nscoord_MAX || HasAnyStateBits
(NS_FRAME_SVG_LAYOUT) || r.Contains(nsRect(nsPoint(), aNewSize
)))) { NS_DebugBreak(NS_DEBUG_ASSERTION, "Computed overflow area must contain frame bounds"
, "aNewSize.width == 0 || aNewSize.height == 0 || r.width == nscoord_MAX || r.height == nscoord_MAX || HasAnyStateBits(NS_FRAME_SVG_LAYOUT) || r.Contains(nsRect(nsPoint(), aNewSize))"
, "./../../../layout/generic/nsIFrame.cpp", 11226); MOZ_PretendNoReturn
(); } } while (0)
11225 r.Contains(nsRect(nsPoint(), aNewSize)),do { if (!(aNewSize.width == 0 || aNewSize.height == 0 || r.width
== nscoord_MAX || r.height == nscoord_MAX || HasAnyStateBits
(NS_FRAME_SVG_LAYOUT) || r.Contains(nsRect(nsPoint(), aNewSize
)))) { NS_DebugBreak(NS_DEBUG_ASSERTION, "Computed overflow area must contain frame bounds"
, "aNewSize.width == 0 || aNewSize.height == 0 || r.width == nscoord_MAX || r.height == nscoord_MAX || HasAnyStateBits(NS_FRAME_SVG_LAYOUT) || r.Contains(nsRect(nsPoint(), aNewSize))"
, "./../../../layout/generic/nsIFrame.cpp", 11226); MOZ_PretendNoReturn
(); } } while (0)
11226 "Computed overflow area must contain frame bounds")do { if (!(aNewSize.width == 0 || aNewSize.height == 0 || r.width
== nscoord_MAX || r.height == nscoord_MAX || HasAnyStateBits
(NS_FRAME_SVG_LAYOUT) || r.Contains(nsRect(nsPoint(), aNewSize
)))) { NS_DebugBreak(NS_DEBUG_ASSERTION, "Computed overflow area must contain frame bounds"
, "aNewSize.width == 0 || aNewSize.height == 0 || r.width == nscoord_MAX || r.height == nscoord_MAX || HasAnyStateBits(NS_FRAME_SVG_LAYOUT) || r.Contains(nsRect(nsPoint(), aNewSize))"
, "./../../../layout/generic/nsIFrame.cpp", 11226); MOZ_PretendNoReturn
(); } } while (0)
;
11227 }
11228#endif
11229
11230 // Overflow area must always include the frame's top-left and bottom-right,
11231 // even if the frame rect is empty (so we can scroll to those positions).
11232 const bool shouldIncludeBounds = [&] {
11233 if (aNewSize.width == 0 && IsInlineFrame()) {
11234 // Pending a real fix for bug 426879, don't do this for inline frames with
11235 // zero width.
11236 return false;
11237 }
11238 if (HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
11239 // Do not do this for SVG either, since it will usually massively increase
11240 // the area unnecessarily (except for SVG that applies clipping, since
11241 // that's the pre-existing behavior, and breaks pre-rendering otherwise).
11242 // FIXME(bug 1770704): This check most likely wants to be removed or check
11243 // for specific frame types at least.
11244 return !overflowClipAxes.isEmpty();
11245 }
11246 return true;
11247 }();
11248
11249 if (shouldIncludeBounds) {
11250 for (const auto otype : AllOverflowTypes()) {
11251 nsRect& o = aOverflowAreas.Overflow(otype);
11252 o = o.UnionEdges(bounds);
11253 }
11254 }
11255
11256 // If we clip our children, clear accumulated overflow area in the affected
11257 // dimension(s). The children are actually clipped to the padding-box, but
11258 // since the overflow area should include the entire border-box, just set it
11259 // to the border-box size here.
11260 if (!overflowClipAxes.isEmpty()) {
11261 aOverflowAreas.ApplyClipping(
11262 bounds, overflowClipAxes,
11263 OverflowClipMargin(overflowClipAxes, /* aAllowNegative = */ false));
11264 }
11265
11266 ComputeAndIncludeOutlineArea(this, aOverflowAreas, aNewSize);
11267
11268 // Nothing in here should affect scrollable overflow.
11269 aOverflowAreas.InkOverflow() =
11270 ComputeEffectsRect(this, aOverflowAreas.InkOverflow(), aNewSize);
11271
11272 // Absolute position clipping
11273 const nsStyleEffects* effects = StyleEffects();
11274 Maybe<nsRect> clipPropClipRect = GetClipPropClipRect(disp, effects, aNewSize);
11275 if (clipPropClipRect) {
11276 for (const auto otype : AllOverflowTypes()) {
11277 nsRect& o = aOverflowAreas.Overflow(otype);
11278 o.IntersectRect(o, *clipPropClipRect);
11279 }
11280 }
11281
11282 /* If we're transformed, transform the overflow rect by the current
11283 * transformation. */
11284 if (hasTransform) {
11285 SetProperty(nsIFrame::PreTransformOverflowAreasProperty(),
11286 new OverflowAreas(aOverflowAreas));
11287
11288 if (Combines3DTransformWithAncestors()) {
11289 /* If we're a preserve-3d leaf frame, then our pre-transform overflow
11290 * should be correct. Our post-transform overflow is empty though, because
11291 * we only contribute to the overflow area of the preserve-3d root frame.
11292 * If we're an intermediate frame then the pre-transform overflow should
11293 * contain all our non-preserve-3d children, which is what we want. Again
11294 * we have no post-transform overflow.
11295 */
11296 aOverflowAreas.SetAllTo(nsRect());
11297 } else {
11298 TransformReferenceBox refBox(this);
11299 for (const auto otype : AllOverflowTypes()) {
11300 nsRect& o = aOverflowAreas.Overflow(otype);
11301 // If the overflow is empty, it can still have a non-zero length in one
11302 // axis. Transforming such axis-bound rect can cause the resulting rect
11303 // to be non-empty, e.g. by rotating the rect.
11304 if (!o.IsEmpty()) {
11305 o = nsDisplayTransform::TransformRect(o, this, refBox);
11306 }
11307 }
11308
11309 /* If we're the root of the 3d context, then we want to include the
11310 * overflow areas of all the participants. This won't have happened yet as
11311 * the code above set their overflow area to empty. Manually collect these
11312 * overflow areas now.
11313 */
11314 if (Extend3DContext(disp, effects)) {
11315 ComputePreserve3DChildrenOverflow(aOverflowAreas);
11316 }
11317 }
11318 } else {
11319 RemoveProperty(nsIFrame::PreTransformOverflowAreasProperty());
11320 }
11321
11322 /* Revert the size change in case some caller is depending on this. */
11323 SetSize(oldSize, false);
11324
11325 bool anyOverflowChanged;
11326 if (aOverflowAreas != OverflowAreas(bounds, bounds)) {
11327 anyOverflowChanged = SetOverflowAreas(aOverflowAreas);
11328 } else {
11329 anyOverflowChanged = ClearOverflowRects();
11330 }
11331
11332 if (anyOverflowChanged) {
11333 SVGObserverUtils::InvalidateDirectRenderingObservers(this);
11334 if (nsBlockFrame* block = do_QueryFrame(this)) {
11335 // NOTE(emilio): we need to use BeforeReflow::Yes, because we want to
11336 // invalidate in cases where we _used_ to have an overflow marker and no
11337 // longer do.
11338 if (TextOverflow::CanHaveOverflowMarkers(
11339 block, TextOverflow::BeforeReflow::Yes)) {
11340 DiscardDisplayItems(this, [](nsDisplayItem* aItem) {
11341 return aItem->GetType() == DisplayItemType::TYPE_TEXT_OVERFLOW;
11342 });
11343 SchedulePaint(PAINT_DEFAULT);
11344 }
11345 }
11346 }
11347 return anyOverflowChanged;
11348}
11349
11350bool nsIFrame::RecomputePerspectiveChildrenOverflow(
11351 const nsIFrame* aStartFrame) {
11352 bool changed = false;
11353 for (const auto& childList : ChildLists()) {
11354 for (nsIFrame* child : childList.mList) {
11355 if (!child->FrameMaintainsOverflow()) {
11356 continue; // frame does not maintain overflow rects
11357 }
11358 if (child->HasPerspective()) {
11359 OverflowAreas* initialOverflow =
11360 child->GetProperty(nsIFrame::InitialOverflowProperty());
11361 const nsRect bounds(nsPoint(), child->GetSize());
11362 OverflowAreas overflow;
11363 if (initialOverflow) {
11364 overflow = *initialOverflow;
11365 } else {
11366 overflow.SetAllTo(bounds);
11367 }
11368 changed |= child->FinishAndStoreOverflow(overflow, bounds.Size());
11369 } else if (child->GetContent() == aStartFrame->GetContent() ||
11370 child->GetClosestFlattenedTreeAncestorPrimaryFrame() ==
11371 aStartFrame) {
11372 // If a frame is using perspective, then the size used to compute
11373 // perspective-origin is the size of the frame belonging to its parent
11374 // style. We must find any descendant frames using our size
11375 // (by recursing into frames that have the same containing block)
11376 // to update their overflow rects too.
11377 if (child->RecomputePerspectiveChildrenOverflow(aStartFrame)) {
11378 changed = true;
11379 // Update the overflow for the child to take any changes from its
11380 // children into account.
11381 child->UpdateOverflow();
11382 }
11383 }
11384 }
11385 }
11386 return changed;
11387}
11388
11389void nsIFrame::ComputePreserve3DChildrenOverflow(
11390 OverflowAreas& aOverflowAreas) {
11391 // Find all descendants that participate in the 3d context, and include their
11392 // overflow. These descendants have an empty overflow, so won't have been
11393 // included in the normal overflow calculation. Any children that don't
11394 // participate have normal overflow, so will have been included already.
11395
11396 nsRect childVisual;
11397 nsRect childScrollable;
11398 for (const auto& childList : ChildLists()) {
11399 for (nsIFrame* child : childList.mList) {
11400 // If this child participates in the 3d context, then take the
11401 // pre-transform region (which contains all descendants that aren't
11402 // participating in the 3d context) and transform it into the 3d context
11403 // root coordinate space.
11404 if (child->Combines3DTransformWithAncestors()) {
11405 OverflowAreas childOverflow = child->GetOverflowAreasRelativeToSelf();
11406 TransformReferenceBox refBox(child);
11407 for (const auto otype : AllOverflowTypes()) {
11408 nsRect& o = childOverflow.Overflow(otype);
11409 o = nsDisplayTransform::TransformRect(o, child, refBox);
11410 }
11411
11412 aOverflowAreas.UnionWith(childOverflow);
11413
11414 // If this child also extends the 3d context, then recurse into it
11415 // looking for more participants.
11416 if (child->Extend3DContext()) {
11417 child->ComputePreserve3DChildrenOverflow(aOverflowAreas);
11418 }
11419 }
11420 }
11421 }
11422}
11423
11424bool nsIFrame::ZIndexApplies() const {
11425 return StyleDisplay()->IsPositionedStyle() || IsFlexOrGridItem() ||
11426 IsMenuPopupFrame();
11427}
11428
11429Maybe<int32_t> nsIFrame::ZIndex() const {
11430 if (!ZIndexApplies()) {
11431 return Nothing();
11432 }
11433 const auto& zIndex = StylePosition()->mZIndex;
11434 if (zIndex.IsAuto()) {
11435 return Nothing();
11436 }
11437 return Some(zIndex.AsInteger());
11438}
11439
11440bool nsIFrame::IsScrollAnchor(ScrollAnchorContainer** aOutContainer) {
11441 if (!mInScrollAnchorChain) {
11442 return false;
11443 }
11444
11445 nsIFrame* f = this;
11446
11447 // FIXME(emilio, bug 1629280): We should find a non-null anchor if we have the
11448 // flag set, but bug 1629280 makes it so that we cannot really assert it /
11449 // make this just a `while (true)`, and uncomment the below assertion.
11450 while (auto* container = ScrollAnchorContainer::FindFor(f)) {
11451 // MOZ_ASSERT(f->IsInScrollAnchorChain());
11452 if (nsIFrame* anchor = container->AnchorNode()) {
11453 if (anchor != this) {
11454 return false;
11455 }
11456 if (aOutContainer) {
11457 *aOutContainer = container;
11458 }
11459 return true;
11460 }
11461
11462 f = container->Frame();
11463 }
11464
11465 return false;
11466}
11467
11468bool nsIFrame::IsInScrollAnchorChain() const { return mInScrollAnchorChain; }
11469
11470void nsIFrame::SetInScrollAnchorChain(bool aInChain) {
11471 mInScrollAnchorChain = aInChain;
11472}
11473
11474uint32_t nsIFrame::GetDepthInFrameTree() const {
11475 uint32_t result = 0;
11476 for (nsContainerFrame* ancestor = GetParent(); ancestor;
11477 ancestor = ancestor->GetParent()) {
11478 result++;
11479 }
11480 return result;
11481}
11482
11483/**
11484 * This function takes a frame that is part of a block-in-inline split,
11485 * and _if_ that frame is an anonymous block created by an ib split it
11486 * returns the block's preceding inline. This is needed because the
11487 * split inline's style is the parent of the anonymous block's style.
11488 *
11489 * If aFrame is not an anonymous block, null is returned.
11490 */
11491static nsIFrame* GetIBSplitSiblingForAnonymousBlock(const nsIFrame* aFrame) {
11492 MOZ_ASSERT(aFrame, "Must have a non-null frame!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aFrame)>::isValid, "invalid assertion condition")
; if ((__builtin_expect(!!(!(!!(aFrame))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aFrame" " (" "Must have a non-null frame!"
")", "./../../../layout/generic/nsIFrame.cpp", 11492); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aFrame" ") (" "Must have a non-null frame!"
")"); do { MOZ_CrashSequence(__null, 11492); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11493 NS_ASSERTION(aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT),do { if (!(aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT
))) { NS_DebugBreak(NS_DEBUG_ASSERTION, "GetIBSplitSibling should only be called on ib-split frames"
, "aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)", "./../../../layout/generic/nsIFrame.cpp"
, 11494); MOZ_PretendNoReturn(); } } while (0)
11494 "GetIBSplitSibling should only be called on ib-split frames")do { if (!(aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT
))) { NS_DebugBreak(NS_DEBUG_ASSERTION, "GetIBSplitSibling should only be called on ib-split frames"
, "aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)", "./../../../layout/generic/nsIFrame.cpp"
, 11494); MOZ_PretendNoReturn(); } } while (0)
;
11495
11496 if (aFrame->Style()->GetPseudoType() !=
11497 PseudoStyleType::MozBlockInsideInlineWrapper) {
11498 // it's not an anonymous block
11499 return nullptr;
11500 }
11501
11502 // Find the first continuation of the frame. (Ugh. This ends up
11503 // being O(N^2) when it is called O(N) times.)
11504 aFrame = aFrame->FirstContinuation();
11505
11506 /*
11507 * Now look up the nsGkAtoms::IBSplitPrevSibling
11508 * property.
11509 */
11510 nsIFrame* ibSplitSibling =
11511 aFrame->GetProperty(nsIFrame::IBSplitPrevSibling());
11512 NS_ASSERTION(ibSplitSibling, "Broken frame tree?")do { if (!(ibSplitSibling)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "Broken frame tree?", "ibSplitSibling", "./../../../layout/generic/nsIFrame.cpp"
, 11512); MOZ_PretendNoReturn(); } } while (0)
;
11513 return ibSplitSibling;
11514}
11515
11516/**
11517 * Get the parent, corrected for the mangled frame tree resulting from
11518 * having a block within an inline. The result only differs from the
11519 * result of |GetParent| when |GetParent| returns an anonymous block
11520 * that was created for an element that was 'display: inline' because
11521 * that element contained a block.
11522 *
11523 * Also skip anonymous scrolled-content parents; inherit directly from the
11524 * outer scroll frame.
11525 *
11526 * Also skip NAC parents if the child frame is NAC.
11527 */
11528static nsIFrame* GetCorrectedParent(const nsIFrame* aFrame) {
11529 nsIFrame* parent = aFrame->GetParent();
11530 if (!parent) {
11531 return nullptr;
11532 }
11533
11534 // For a table caption we want the _inner_ table frame (unless it's anonymous)
11535 // as the style parent.
11536 if (aFrame->IsTableCaption()) {
11537 MOZ_ASSERT(parent->IsTableWrapperFrame())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(parent->IsTableWrapperFrame())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(parent->IsTableWrapperFrame
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("parent->IsTableWrapperFrame()", "./../../../layout/generic/nsIFrame.cpp"
, 11537); AnnotateMozCrashReason("MOZ_ASSERT" "(" "parent->IsTableWrapperFrame()"
")"); do { MOZ_CrashSequence(__null, 11537); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11538 nsTableFrame* innerTable =
11539 static_cast<const nsTableWrapperFrame*>(parent)->InnerTableFrame();
11540 if (!innerTable->Style()->IsAnonBox()) {
11541 return innerTable;
11542 }
11543 }
11544
11545 // Table wrappers are always anon boxes; if we're in here for an outer
11546 // table, that actually means its the _inner_ table that wants to
11547 // know its parent. So get the pseudo of the inner in that case.
11548 auto pseudo = aFrame->Style()->GetPseudoType();
11549 if (pseudo == PseudoStyleType::MozTableWrapper) {
11550 MOZ_ASSERT(aFrame->IsTableWrapperFrame())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aFrame->IsTableWrapperFrame())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aFrame->IsTableWrapperFrame
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aFrame->IsTableWrapperFrame()", "./../../../layout/generic/nsIFrame.cpp"
, 11550); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aFrame->IsTableWrapperFrame()"
")"); do { MOZ_CrashSequence(__null, 11550); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11551 nsTableFrame* innerTable =
11552 static_cast<const nsTableWrapperFrame*>(aFrame)->InnerTableFrame();
11553 pseudo = innerTable->Style()->GetPseudoType();
11554 }
11555
11556 // Prevent a NAC pseudo-element from inheriting from its NAC parent, and
11557 // inherit from the NAC generator element instead. (We exclude element-backed
11558 // pseudos from this check, since they're not NAC.)
11559 if (pseudo != PseudoStyleType::NotPseudo &&
11560 !PseudoStyle::IsElementBackedPseudo(pseudo)) {
11561 MOZ_ASSERT(aFrame->GetContent())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aFrame->GetContent())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aFrame->GetContent()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("aFrame->GetContent()"
, "./../../../layout/generic/nsIFrame.cpp", 11561); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aFrame->GetContent()" ")"); do { MOZ_CrashSequence
(__null, 11561); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
11562 Element* element = Element::FromNode(aFrame->GetContent());
11563 // Make sure to only do the fixup for anonymous content pseudos (i.e. avoid
11564 // fixup for ::first-line and such).
11565 if (element && !element->IsRootOfNativeAnonymousSubtree() &&
11566 element->GetPseudoElementType() == aFrame->Style()->GetPseudoType()) {
11567 while (parent->GetContent() &&
11568 !parent->GetContent()->IsRootOfNativeAnonymousSubtree()) {
11569 parent = parent->GetInFlowParent();
11570 }
11571 parent = parent->GetInFlowParent();
11572 }
11573 }
11574
11575 return nsIFrame::CorrectStyleParentFrame(parent, pseudo);
11576}
11577
11578/* static */
11579nsIFrame* nsIFrame::CorrectStyleParentFrame(nsIFrame* aProspectiveParent,
11580 PseudoStyleType aChildPseudo) {
11581 MOZ_ASSERT(aProspectiveParent, "Must have a prospective parent")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aProspectiveParent)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aProspectiveParent))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("aProspectiveParent"
" (" "Must have a prospective parent" ")", "./../../../layout/generic/nsIFrame.cpp"
, 11581); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aProspectiveParent"
") (" "Must have a prospective parent" ")"); do { MOZ_CrashSequence
(__null, 11581); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
11582
11583 if (aChildPseudo < PseudoStyleType::NotPseudo) {
11584 // Non-inheriting anon boxes have no style parent frame at all.
11585 if (PseudoStyle::IsNonInheritingAnonBox(aChildPseudo)) {
11586 return nullptr;
11587 }
11588
11589 // Other anon boxes are parented to their actual parent already, except
11590 // for non-elements. Those should not be treated as an anon box.
11591 if (PseudoStyle::IsAnonBox(aChildPseudo) &&
11592 !PseudoStyle::IsNonElement(aChildPseudo)) {
11593 NS_ASSERTION(aChildPseudo != PseudoStyleType::MozBlockInsideInlineWrapper,do { if (!(aChildPseudo != PseudoStyleType::MozBlockInsideInlineWrapper
)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "Should have dealt with kids that have "
"NS_FRAME_PART_OF_IBSPLIT elsewhere", "aChildPseudo != PseudoStyleType::MozBlockInsideInlineWrapper"
, "./../../../layout/generic/nsIFrame.cpp", 11595); MOZ_PretendNoReturn
(); } } while (0)
11594 "Should have dealt with kids that have "do { if (!(aChildPseudo != PseudoStyleType::MozBlockInsideInlineWrapper
)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "Should have dealt with kids that have "
"NS_FRAME_PART_OF_IBSPLIT elsewhere", "aChildPseudo != PseudoStyleType::MozBlockInsideInlineWrapper"
, "./../../../layout/generic/nsIFrame.cpp", 11595); MOZ_PretendNoReturn
(); } } while (0)
11595 "NS_FRAME_PART_OF_IBSPLIT elsewhere")do { if (!(aChildPseudo != PseudoStyleType::MozBlockInsideInlineWrapper
)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "Should have dealt with kids that have "
"NS_FRAME_PART_OF_IBSPLIT elsewhere", "aChildPseudo != PseudoStyleType::MozBlockInsideInlineWrapper"
, "./../../../layout/generic/nsIFrame.cpp", 11595); MOZ_PretendNoReturn
(); } } while (0)
;
11596 return aProspectiveParent;
11597 }
11598 }
11599
11600 // Otherwise, walk up out of all anon boxes. For placeholder frames, walk out
11601 // of all pseudo-elements as well. Otherwise ReparentComputedStyle could
11602 // cause style data to be out of sync with the frame tree.
11603 nsIFrame* parent = aProspectiveParent;
11604 do {
11605 if (parent->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
11606 nsIFrame* sibling = GetIBSplitSiblingForAnonymousBlock(parent);
11607
11608 if (sibling) {
11609 // |parent| was a block in an {ib} split; use the inline as
11610 // |the style parent.
11611 parent = sibling;
11612 }
11613 }
11614
11615 if (!parent->Style()->IsPseudoOrAnonBox()) {
11616 return parent;
11617 }
11618
11619 if (!parent->Style()->IsAnonBox() && aChildPseudo != PseudoStyleType::MAX) {
11620 // nsPlaceholderFrame passes in PseudoStyleType::MAX for
11621 // aChildPseudo (even though that's not a valid pseudo-type) just to
11622 // trigger this behavior of walking up to the nearest non-pseudo
11623 // ancestor.
11624 return parent;
11625 }
11626
11627 parent = parent->GetInFlowParent();
11628 } while (parent);
11629
11630 if (aProspectiveParent->Style()->GetPseudoType() ==
11631 PseudoStyleType::MozViewportScroll) {
11632 // aProspectiveParent is the scrollframe for a viewport
11633 // and the kids are the anonymous scrollbars
11634 return aProspectiveParent;
11635 }
11636
11637 // We can get here if the root element is absolutely positioned.
11638 // We can't test for this very accurately, but it can only happen
11639 // when the prospective parent is a canvas frame.
11640 NS_ASSERTION(aProspectiveParent->IsCanvasFrame(),do { if (!(aProspectiveParent->IsCanvasFrame())) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "Should have found a parent before this"
, "aProspectiveParent->IsCanvasFrame()", "./../../../layout/generic/nsIFrame.cpp"
, 11641); MOZ_PretendNoReturn(); } } while (0)
11641 "Should have found a parent before this")do { if (!(aProspectiveParent->IsCanvasFrame())) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "Should have found a parent before this"
, "aProspectiveParent->IsCanvasFrame()", "./../../../layout/generic/nsIFrame.cpp"
, 11641); MOZ_PretendNoReturn(); } } while (0)
;
11642 return nullptr;
11643}
11644
11645ComputedStyle* nsIFrame::DoGetParentComputedStyle(
11646 nsIFrame** aProviderFrame) const {
11647 *aProviderFrame = nullptr;
11648
11649 // Handle display:contents and the root frame, when there's no parent frame
11650 // to inherit from.
11651 if (MOZ_LIKELY(mContent)(__builtin_expect(!!(mContent), 1))) {
11652 Element* parentElement = mContent->GetFlattenedTreeParentElement();
11653 if (MOZ_LIKELY(parentElement)(__builtin_expect(!!(parentElement), 1))) {
11654 auto pseudo = Style()->GetPseudoType();
11655 if (pseudo == PseudoStyleType::NotPseudo || !mContent->IsElement() ||
11656 (!PseudoStyle::IsAnonBox(pseudo) &&
11657 // Ensure that we don't return the display:contents style
11658 // of the parent content for pseudos that have the same content
11659 // as their primary frame (like -moz-list-bullets do):
11660 IsPrimaryFrame()) ||
11661 /* if next is true then it's really a request for the table frame's
11662 parent context, see nsTable[Outer]Frame::GetParentComputedStyle. */
11663 pseudo == PseudoStyleType::MozTableWrapper) {
11664 // In some edge cases involving display: contents, we may end up here
11665 // for something that's pending to be reframed. In this case we return
11666 // the wrong style from here (because we've already lost track of it!),
11667 // but it's not a big deal as we're going to be reframed anyway.
11668 if (MOZ_LIKELY(parentElement->HasServoData())(__builtin_expect(!!(parentElement->HasServoData()), 1)) &&
11669 Servo_Element_IsDisplayContents(parentElement)) {
11670 RefPtr<ComputedStyle> style =
11671 ServoStyleSet::ResolveServoStyle(*parentElement);
11672 // NOTE(emilio): we return a weak reference because the element also
11673 // holds the style context alive. This is a bit silly (we could've
11674 // returned a weak ref directly), but it's probably not worth
11675 // optimizing, given this function has just one caller which is rare,
11676 // and this path is rare itself.
11677 return style;
11678 }
11679 }
11680 } else {
11681 if (Style()->GetPseudoType() == PseudoStyleType::NotPseudo) {
11682 // We're a frame for the root. We have no style parent.
11683 return nullptr;
11684 }
11685 }
11686 }
11687
11688 if (!HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
11689 /*
11690 * If this frame is an anonymous block created when an inline with a block
11691 * inside it got split, then the parent style is on its preceding inline. We
11692 * can get to it using GetIBSplitSiblingForAnonymousBlock.
11693 */
11694 if (HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
11695 nsIFrame* ibSplitSibling = GetIBSplitSiblingForAnonymousBlock(this);
11696 if (ibSplitSibling) {
11697 return (*aProviderFrame = ibSplitSibling)->Style();
11698 }
11699 }
11700
11701 // If this frame is one of the blocks that split an inline, we must
11702 // return the "special" inline parent, i.e., the parent that this
11703 // frame would have if we didn't mangle the frame structure.
11704 *aProviderFrame = GetCorrectedParent(this);
11705 return *aProviderFrame ? (*aProviderFrame)->Style() : nullptr;
11706 }
11707
11708 // We're an out-of-flow frame. For out-of-flow frames, we must
11709 // resolve underneath the placeholder's parent. The placeholder is
11710 // reached from the first-in-flow.
11711 nsPlaceholderFrame* placeholder = FirstInFlow()->GetPlaceholderFrame();
11712 if (!placeholder) {
11713 MOZ_ASSERT_UNREACHABLE("no placeholder frame for out-of-flow frame")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"no placeholder frame for out-of-flow frame" ")", "./../../../layout/generic/nsIFrame.cpp"
, 11713); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "no placeholder frame for out-of-flow frame"
")"); do { MOZ_CrashSequence(__null, 11713); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11714 *aProviderFrame = GetCorrectedParent(this);
11715 return *aProviderFrame ? (*aProviderFrame)->Style() : nullptr;
11716 }
11717 return placeholder->GetParentComputedStyleForOutOfFlow(aProviderFrame);
11718}
11719
11720void nsIFrame::GetLastLeaf(nsIFrame** aFrame) {
11721 if (!aFrame || !*aFrame) {
11722 return;
11723 }
11724 for (nsIFrame* maybeLastLeaf = (*aFrame)->PrincipalChildList().LastChild();
11725 maybeLastLeaf;) {
11726 nsIFrame* lastChildNotInSubTree = nullptr;
11727 for (nsIFrame* child = maybeLastLeaf; child;
11728 child = child->GetPrevSibling()) {
11729 // ignore anonymous elements, e.g. mozTableAdd* mozTableRemove*
11730 // see bug 278197 comment #12 #13 for details
11731 if (!child->ContentIsRootOfNativeAnonymousSubtree()) {
11732 lastChildNotInSubTree = child;
11733 break;
11734 }
11735 }
11736 if (!lastChildNotInSubTree) {
11737 return;
11738 }
11739 *aFrame = lastChildNotInSubTree;
11740 maybeLastLeaf = lastChildNotInSubTree->PrincipalChildList().LastChild();
11741 }
11742}
11743
11744void nsIFrame::GetFirstLeaf(nsIFrame** aFrame) {
11745 if (!aFrame || !*aFrame) {
11746 return;
11747 }
11748 nsIFrame* child = *aFrame;
11749 while (true) {
11750 child = child->PrincipalChildList().FirstChild();
11751 if (!child) {
11752 return; // nothing to do
11753 }
11754 *aFrame = child;
11755 }
11756}
11757
11758bool nsIFrame::IsFocusableDueToScrollFrame() {
11759 if (!IsScrollContainerFrame()) {
11760 if (nsFieldSetFrame* fieldset = do_QueryFrame(this)) {
11761 // TODO: Do we have similar special-cases like this where we can have
11762 // anonymous scrollable boxes hanging off a primary frame?
11763 if (nsIFrame* inner = fieldset->GetInner()) {
11764 return inner->IsFocusableDueToScrollFrame();
11765 }
11766 }
11767 return false;
11768 }
11769 if (!mContent->IsHTMLElement()) {
11770 return false;
11771 }
11772 if (Style()->IsPseudoElement()) {
11773 return false;
11774 }
11775 if (mContent->IsRootOfNativeAnonymousSubtree()) {
11776 return false;
11777 }
11778 if (!mContent->GetParent()) {
11779 return false;
11780 }
11781 if (mContent->AsElement()->HasAttr(nsGkAtoms::tabindex)) {
11782 return false;
11783 }
11784 // Elements with scrollable view are focusable with script & tabbable
11785 // Otherwise you couldn't scroll them with keyboard, which is an accessibility
11786 // issue (e.g. Section 508 rules) However, we don't make them to be focusable
11787 // with the mouse, because the extra focus outlines are considered
11788 // unnecessarily ugly. When clicked on, the selection position within the
11789 // element will be enough to make them keyboard scrollable.
11790 auto* scrollContainer = static_cast<ScrollContainerFrame*>(this);
11791 if (scrollContainer->GetScrollStyles().IsHiddenInBothDirections()) {
11792 return false;
11793 }
11794 if (scrollContainer->GetScrollRangeForUserInputEvents().IsEqualEdges(
11795 nsRect())) {
11796 return false;
11797 }
11798 return true;
11799}
11800
11801Focusable nsIFrame::IsFocusable(IsFocusableFlags aFlags) {
11802 // cannot focus content in print preview mode. Only the root can be focused,
11803 // but that's handled elsewhere.
11804 if (PresContext()->Type() == nsPresContext::eContext_PrintPreview) {
11805 return {};
11806 }
11807
11808 if (!mContent || !mContent->IsElement()) {
11809 return {};
11810 }
11811
11812 if (!(aFlags & IsFocusableFlags::IgnoreVisibility) &&
11813 !IsVisibleConsideringAncestors()) {
11814 return {};
11815 }
11816
11817 const StyleUserFocus uf = StyleUI()->UserFocus();
11818 if (uf == StyleUserFocus::None) {
11819 return {};
11820 }
11821 MOZ_ASSERT(!StyleUI()->IsInert(), "inert implies -moz-user-focus: none")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!StyleUI()->IsInert())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!StyleUI()->IsInert()))),
0))) { do { } while (false); MOZ_ReportAssertionFailure("!StyleUI()->IsInert()"
" (" "inert implies -moz-user-focus: none" ")", "./../../../layout/generic/nsIFrame.cpp"
, 11821); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!StyleUI()->IsInert()"
") (" "inert implies -moz-user-focus: none" ")"); do { MOZ_CrashSequence
(__null, 11821); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
11822
11823 const PseudoStyleType pseudo = Style()->GetPseudoType();
11824 if (pseudo == PseudoStyleType::MozAnonymousItem) {
11825 return {};
11826 }
11827
11828 Focusable focusable;
11829 if (auto* xul = nsXULElement::FromNode(mContent)) {
11830 // As a legacy special-case, -moz-user-focus controls focusability and
11831 // tabability of XUL elements in some circumstances (which default to
11832 // -moz-user-focus: ignore).
11833 auto focusability = xul->GetXULFocusability(aFlags);
11834 focusable.mFocusable =
11835 focusability.mForcedFocusable.valueOr(uf == StyleUserFocus::Normal);
11836 if (focusable) {
11837 focusable.mTabIndex = focusability.mForcedTabIndexIfFocusable.valueOr(0);
11838 }
11839 } else {
11840 focusable = mContent->IsFocusableWithoutStyle(aFlags);
11841 }
11842
11843 if (focusable) {
11844 return focusable;
11845 }
11846
11847 // If we're focusing with the mouse we never focus scroll areas.
11848 if (!(aFlags & IsFocusableFlags::WithMouse) &&
11849 IsFocusableDueToScrollFrame()) {
11850 return {true, 0};
11851 }
11852
11853 // FIXME(emilio): some callers rely on somewhat broken return values
11854 // (focusable = false, but non-negative tab-index) from
11855 // IsFocusableWithoutStyle (for image maps in particular).
11856 return focusable;
11857}
11858
11859/**
11860 * @return true if this text frame ends with a newline character which is
11861 * treated as preformatted. It should return false if this is not a text frame.
11862 */
11863bool nsIFrame::HasSignificantTerminalNewline() const { return false; }
11864
11865static StyleAlignmentBaseline ConvertSVGDominantBaselineToAlignmentBaseline(
11866 StyleDominantBaseline aDominantBaseline) {
11867 // Most of these are approximate mappings.
11868 switch (aDominantBaseline) {
11869 case StyleDominantBaseline::Hanging:
11870 case StyleDominantBaseline::TextTop:
11871 return StyleAlignmentBaseline::TextTop;
11872 case StyleDominantBaseline::TextBottom:
11873 case StyleDominantBaseline::Ideographic:
11874 return StyleAlignmentBaseline::TextBottom;
11875 case StyleDominantBaseline::Central:
11876 case StyleDominantBaseline::Middle:
11877 case StyleDominantBaseline::Mathematical:
11878 return StyleAlignmentBaseline::Middle;
11879 case StyleDominantBaseline::Auto:
11880 case StyleDominantBaseline::Alphabetic:
11881 return StyleAlignmentBaseline::Baseline;
11882 default:
11883 MOZ_ASSERT_UNREACHABLE("unexpected aDominantBaseline value")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"unexpected aDominantBaseline value" ")", "./../../../layout/generic/nsIFrame.cpp"
, 11883); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "unexpected aDominantBaseline value"
")"); do { MOZ_CrashSequence(__null, 11883); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11884 return StyleAlignmentBaseline::Baseline;
11885 }
11886}
11887
11888StyleDominantBaseline nsIFrame::DominantBaseline() const {
11889 auto dominantBaseline = StyleVisibility()->mDominantBaseline;
11890 if (dominantBaseline != StyleDominantBaseline::Auto) {
11891 return dominantBaseline;
11892 }
11893 // https://drafts.csswg.org/css-inline-3/#alignment-baseline-property
11894 // > Equivalent to alphabetic in horizontal writing modes and
11895 // > in vertical writing modes when text-orientation is sideways.
11896 // > Equivalent to central in vertical writing modes when
11897 // > text-orientation is mixed or upright.
11898 return GetWritingMode().IsCentralBaseline()
11899 ? StyleDominantBaseline::Central
11900 : StyleDominantBaseline::Alphabetic;
11901}
11902
11903StyleAlignmentBaseline nsIFrame::AlignmentBaseline() const {
11904 if (IsInSVGTextSubtree()) {
11905 // TODO Bug 2010721 - Implement alignment-baseline directly for SVG
11906 // text elements instead of only approximating from dominant-baseline.
11907 auto dominantBaseline = StyleVisibility()->mDominantBaseline;
11908 return ConvertSVGDominantBaselineToAlignmentBaseline(dominantBaseline);
11909 }
11910
11911 if (IsTableCellFrame()) {
11912 // The alignment-baseline / vertical-align property on a table cell is used
11913 // to align its children (see nsTableCellFrame::GetTableCellAlignment), but
11914 // does not affect the alignment of the cell itself within its row.
11915 return StyleAlignmentBaseline::Baseline;
11916 }
11917
11918 if (StyleDisplay()->mAlignmentBaseline == StyleAlignmentBaseline::Baseline) {
11919 // The CSS Inline Layout specification says `alignment-baseline: baseline`
11920 // uses the dominant baseline choice of the parent.
11921 auto dominantBaseline =
11922 GetParent() ? GetParent()->DominantBaseline() : DominantBaseline();
11923 switch (dominantBaseline) {
11924 case StyleDominantBaseline::TextBottom:
11925 return StyleAlignmentBaseline::TextBottom;
11926 case StyleDominantBaseline::Alphabetic:
11927 return StyleAlignmentBaseline::Alphabetic;
11928 case StyleDominantBaseline::Ideographic:
11929 return StyleAlignmentBaseline::Ideographic;
11930 case StyleDominantBaseline::Middle:
11931 return StyleAlignmentBaseline::Middle;
11932 case StyleDominantBaseline::Central:
11933 return StyleAlignmentBaseline::Central;
11934 case StyleDominantBaseline::Mathematical:
11935 return StyleAlignmentBaseline::Mathematical;
11936 case StyleDominantBaseline::Hanging:
11937 return StyleAlignmentBaseline::Hanging;
11938 case StyleDominantBaseline::TextTop:
11939 return StyleAlignmentBaseline::TextTop;
11940 case StyleDominantBaseline::Auto:
11941 MOZ_ASSERT_UNREACHABLE("Auto is resolved in DominantBaseline()")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"Auto is resolved in DominantBaseline()" ")", "./../../../layout/generic/nsIFrame.cpp"
, 11941); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Auto is resolved in DominantBaseline()"
")"); do { MOZ_CrashSequence(__null, 11941); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11942 break;
11943 }
11944 }
11945
11946 return StyleDisplay()->mAlignmentBaseline;
11947}
11948
11949void nsIFrame::UpdateStyleOfChildAnonBox(nsIFrame* aChildFrame,
11950 ServoRestyleState& aRestyleState) {
11951#ifdef DEBUG1
11952 nsIFrame* parent = aChildFrame->GetInFlowParent();
11953 if (aChildFrame->IsTableFrame()) {
11954 parent = parent->GetParent();
11955 }
11956 if (parent->IsLineFrame()) {
11957 parent = parent->GetParent();
11958 }
11959 MOZ_ASSERT(nsLayoutUtils::FirstContinuationOrIBSplitSibling(parent) == this,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(nsLayoutUtils::FirstContinuationOrIBSplitSibling(parent
) == this)>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!(nsLayoutUtils::FirstContinuationOrIBSplitSibling
(parent) == this))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("nsLayoutUtils::FirstContinuationOrIBSplitSibling(parent) == this"
" (" "This should only be used for children!" ")", "./../../../layout/generic/nsIFrame.cpp"
, 11960); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nsLayoutUtils::FirstContinuationOrIBSplitSibling(parent) == this"
") (" "This should only be used for children!" ")"); do { MOZ_CrashSequence
(__null, 11960); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
11960 "This should only be used for children!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(nsLayoutUtils::FirstContinuationOrIBSplitSibling(parent
) == this)>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!(nsLayoutUtils::FirstContinuationOrIBSplitSibling
(parent) == this))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("nsLayoutUtils::FirstContinuationOrIBSplitSibling(parent) == this"
" (" "This should only be used for children!" ")", "./../../../layout/generic/nsIFrame.cpp"
, 11960); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nsLayoutUtils::FirstContinuationOrIBSplitSibling(parent) == this"
") (" "This should only be used for children!" ")"); do { MOZ_CrashSequence
(__null, 11960); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
11961#endif // DEBUG
11962 MOZ_ASSERT(!GetContent() || !aChildFrame->GetContent() ||do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!GetContent() || !aChildFrame->GetContent() || aChildFrame
->GetContent() == GetContent())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!GetContent() || !aChildFrame
->GetContent() || aChildFrame->GetContent() == GetContent
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!GetContent() || !aChildFrame->GetContent() || aChildFrame->GetContent() == GetContent()"
" (" "What content node is it a frame for?" ")", "./../../../layout/generic/nsIFrame.cpp"
, 11964); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!GetContent() || !aChildFrame->GetContent() || aChildFrame->GetContent() == GetContent()"
") (" "What content node is it a frame for?" ")"); do { MOZ_CrashSequence
(__null, 11964); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
11963 aChildFrame->GetContent() == GetContent(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!GetContent() || !aChildFrame->GetContent() || aChildFrame
->GetContent() == GetContent())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!GetContent() || !aChildFrame
->GetContent() || aChildFrame->GetContent() == GetContent
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!GetContent() || !aChildFrame->GetContent() || aChildFrame->GetContent() == GetContent()"
" (" "What content node is it a frame for?" ")", "./../../../layout/generic/nsIFrame.cpp"
, 11964); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!GetContent() || !aChildFrame->GetContent() || aChildFrame->GetContent() == GetContent()"
") (" "What content node is it a frame for?" ")"); do { MOZ_CrashSequence
(__null, 11964); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
11964 "What content node is it a frame for?")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!GetContent() || !aChildFrame->GetContent() || aChildFrame
->GetContent() == GetContent())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!GetContent() || !aChildFrame
->GetContent() || aChildFrame->GetContent() == GetContent
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!GetContent() || !aChildFrame->GetContent() || aChildFrame->GetContent() == GetContent()"
" (" "What content node is it a frame for?" ")", "./../../../layout/generic/nsIFrame.cpp"
, 11964); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!GetContent() || !aChildFrame->GetContent() || aChildFrame->GetContent() == GetContent()"
") (" "What content node is it a frame for?" ")"); do { MOZ_CrashSequence
(__null, 11964); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
11965 MOZ_ASSERT(!aChildFrame->GetPrevContinuation(),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!aChildFrame->GetPrevContinuation())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!aChildFrame->GetPrevContinuation()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!aChildFrame->GetPrevContinuation()"
" (" "Only first continuations should end up here" ")", "./../../../layout/generic/nsIFrame.cpp"
, 11966); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aChildFrame->GetPrevContinuation()"
") (" "Only first continuations should end up here" ")"); do
{ MOZ_CrashSequence(__null, 11966); __attribute__((nomerge))
::abort(); } while (false); } } while (false)
11966 "Only first continuations should end up here")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!aChildFrame->GetPrevContinuation())>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!aChildFrame->GetPrevContinuation()))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!aChildFrame->GetPrevContinuation()"
" (" "Only first continuations should end up here" ")", "./../../../layout/generic/nsIFrame.cpp"
, 11966); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aChildFrame->GetPrevContinuation()"
") (" "Only first continuations should end up here" ")"); do
{ MOZ_CrashSequence(__null, 11966); __attribute__((nomerge))
::abort(); } while (false); } } while (false)
;
11967
11968 // We could force the caller to pass in the pseudo, since some callers know it
11969 // statically... But this API is a bit nicer.
11970 auto pseudo = aChildFrame->Style()->GetPseudoType();
11971 MOZ_ASSERT(PseudoStyle::IsAnonBox(pseudo), "Child is not an anon box?")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(PseudoStyle::IsAnonBox(pseudo))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(PseudoStyle::IsAnonBox(pseudo
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("PseudoStyle::IsAnonBox(pseudo)" " (" "Child is not an anon box?"
")", "./../../../layout/generic/nsIFrame.cpp", 11971); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "PseudoStyle::IsAnonBox(pseudo)" ") (" "Child is not an anon box?"
")"); do { MOZ_CrashSequence(__null, 11971); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11972 MOZ_ASSERT(!PseudoStyle::IsNonInheritingAnonBox(pseudo),do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!PseudoStyle::IsNonInheritingAnonBox(pseudo))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!PseudoStyle::IsNonInheritingAnonBox(pseudo)))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!PseudoStyle::IsNonInheritingAnonBox(pseudo)"
" (" "Why did the caller bother calling us?" ")", "./../../../layout/generic/nsIFrame.cpp"
, 11973); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!PseudoStyle::IsNonInheritingAnonBox(pseudo)"
") (" "Why did the caller bother calling us?" ")"); do { MOZ_CrashSequence
(__null, 11973); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
11973 "Why did the caller bother calling us?")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!PseudoStyle::IsNonInheritingAnonBox(pseudo))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!PseudoStyle::IsNonInheritingAnonBox(pseudo)))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!PseudoStyle::IsNonInheritingAnonBox(pseudo)"
" (" "Why did the caller bother calling us?" ")", "./../../../layout/generic/nsIFrame.cpp"
, 11973); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!PseudoStyle::IsNonInheritingAnonBox(pseudo)"
") (" "Why did the caller bother calling us?" ")"); do { MOZ_CrashSequence
(__null, 11973); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
11974
11975 // Anon boxes inherit from their parent; that's us.
11976 RefPtr<ComputedStyle> newContext =
11977 aRestyleState.StyleSet().ResolveInheritingAnonymousBoxStyle(pseudo,
11978 Style());
11979
11980 nsChangeHint childHint =
11981 UpdateStyleOfOwnedChildFrame(aChildFrame, newContext, aRestyleState);
11982
11983 // Now that we've updated the style on aChildFrame, check whether it itself
11984 // has anon boxes to deal with.
11985 ServoRestyleState childrenState(*aChildFrame, aRestyleState, childHint,
11986 ServoRestyleState::CanUseHandledHints::Yes);
11987 aChildFrame->UpdateStyleOfOwnedAnonBoxes(childrenState);
11988
11989 // Assuming anon boxes don't have ::backdrop associated with them... if that
11990 // ever changes, we'd need to handle that here, like we do in
11991 // RestyleManager::ProcessPostTraversal
11992
11993 // We do need to handle block pseudo-elements here, though. Especially list
11994 // bullets.
11995 if (nsBlockFrame* block = do_QueryFrame(aChildFrame)) {
11996 block->UpdatePseudoElementStyles(childrenState);
11997 }
11998}
11999
12000/* static */
12001nsChangeHint nsIFrame::UpdateStyleOfOwnedChildFrame(
12002 nsIFrame* aChildFrame, ComputedStyle* aNewComputedStyle,
12003 ServoRestyleState& aRestyleState,
12004 const Maybe<ComputedStyle*>& aContinuationComputedStyle) {
12005 // Figure out whether we have an actual change. It's important that we do
12006 // this, for several reasons:
12007 //
12008 // 1) Even if all the child's changes are due to properties it inherits from
12009 // us, it's possible that no one ever asked us for those style structs and
12010 // hence changes to them aren't reflected in the changes handled at all.
12011 //
12012 // 2) Content can change stylesheets that change the styles of pseudos, and
12013 // extensions can add/remove stylesheets that change the styles of
12014 // anonymous boxes directly.
12015 uint32_t equalStructs; // Not used, actually.
12016 nsChangeHint childHint = aChildFrame->Style()->CalcStyleDifference(
12017 *aNewComputedStyle, &equalStructs);
12018
12019 // If aChildFrame is out of flow, then aRestyleState's "changes handled by the
12020 // parent" doesn't apply to it, because it may have some other parent in the
12021 // frame tree.
12022 if (!aChildFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
12023 childHint = NS_RemoveSubsumedHints(
12024 childHint, aRestyleState.ChangesHandledFor(aChildFrame));
12025 }
12026 if (childHint) {
12027 if (childHint & nsChangeHint_ReconstructFrame) {
12028 // If we generate a reconstruct here, remove any non-reconstruct hints we
12029 // may have already generated for this content.
12030 aRestyleState.ChangeList().PopChangesForContent(
12031 aChildFrame->GetContent());
12032 }
12033 aRestyleState.ChangeList().AppendChange(
12034 aChildFrame, aChildFrame->GetContent(), childHint);
12035 }
12036
12037 aChildFrame->SetComputedStyle(aNewComputedStyle);
12038 ComputedStyle* continuationStyle = aContinuationComputedStyle
12039 ? *aContinuationComputedStyle
12040 : aNewComputedStyle;
12041 for (nsIFrame* kid = aChildFrame->GetNextContinuation(); kid;
12042 kid = kid->GetNextContinuation()) {
12043 kid->SetComputedStyle(continuationStyle);
12044 }
12045
12046 return childHint;
12047}
12048
12049/* static */
12050void nsIFrame::AddInPopupStateBitToDescendants(nsIFrame* aFrame) {
12051 if (!aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP) &&
12052 aFrame->TrackingVisibility()) {
12053 // Assume all frames in popups are visible.
12054 aFrame->IncApproximateVisibleCount();
12055 }
12056
12057 aFrame->AddStateBits(NS_FRAME_IN_POPUP);
12058
12059 for (const auto& childList : aFrame->CrossDocChildLists()) {
12060 for (nsIFrame* child : childList.mList) {
12061 AddInPopupStateBitToDescendants(child);
12062 }
12063 }
12064}
12065
12066/* static */
12067void nsIFrame::RemoveInPopupStateBitFromDescendants(nsIFrame* aFrame) {
12068 if (!aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP) ||
12069 nsLayoutUtils::IsPopup(aFrame)) {
12070 return;
12071 }
12072
12073 aFrame->RemoveStateBits(NS_FRAME_IN_POPUP);
12074
12075 if (aFrame->TrackingVisibility()) {
12076 // We assume all frames in popups are visible, so this decrement balances
12077 // out the increment in AddInPopupStateBitToDescendants above.
12078 aFrame->DecApproximateVisibleCount();
12079 }
12080 for (const auto& childList : aFrame->CrossDocChildLists()) {
12081 for (nsIFrame* child : childList.mList) {
12082 RemoveInPopupStateBitFromDescendants(child);
12083 }
12084 }
12085}
12086
12087void nsIFrame::SetParent(nsContainerFrame* aParent) {
12088 // If our parent is a wrapper anon box, our new parent should be too. We
12089 // _can_ change parent if our parent is a wrapper anon box, because some
12090 // wrapper anon boxes can have continuations.
12091 MOZ_ASSERT_IF(ParentIsWrapperAnonBox(),do { if (ParentIsWrapperAnonBox()) { do { static_assert( mozilla
::detail::AssertionConditionType<decltype(aParent->Style
()->IsInheritingAnonBox())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aParent->Style()->IsInheritingAnonBox
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aParent->Style()->IsInheritingAnonBox()", "./../../../layout/generic/nsIFrame.cpp"
, 12092); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aParent->Style()->IsInheritingAnonBox()"
")"); do { MOZ_CrashSequence(__null, 12092); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
12092 aParent->Style()->IsInheritingAnonBox())do { if (ParentIsWrapperAnonBox()) { do { static_assert( mozilla
::detail::AssertionConditionType<decltype(aParent->Style
()->IsInheritingAnonBox())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aParent->Style()->IsInheritingAnonBox
()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("aParent->Style()->IsInheritingAnonBox()", "./../../../layout/generic/nsIFrame.cpp"
, 12092); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aParent->Style()->IsInheritingAnonBox()"
")"); do { MOZ_CrashSequence(__null, 12092); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
12093
12094 // Note that the current mParent may already be destroyed at this point.
12095 mParent = aParent;
12096 MOZ_ASSERT(!mParent || PresShell() == mParent->PresShell())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mParent || PresShell() == mParent->PresShell())>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!mParent || PresShell() == mParent->PresShell()))
), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!mParent || PresShell() == mParent->PresShell()"
, "./../../../layout/generic/nsIFrame.cpp", 12096); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!mParent || PresShell() == mParent->PresShell()"
")"); do { MOZ_CrashSequence(__null, 12096); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12097
12098 nsFrameState flagsToPropagateSameDoc =
12099 GetStateBits() & (NS_FRAME_CONTAINS_RELATIVE_BSIZE |
12100 NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE);
12101 if (flagsToPropagateSameDoc) {
12102 for (nsIFrame* f = aParent; f; f = f->GetParent()) {
12103 if (f->HasAllStateBits(flagsToPropagateSameDoc)) {
12104 break;
12105 }
12106 f->AddStateBits(flagsToPropagateSameDoc);
12107 }
12108 }
12109
12110 if (HasInvalidFrameInSubtree()) {
12111 for (nsIFrame* f = aParent;
12112 f && !f->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT |
12113 NS_FRAME_IS_NONDISPLAY);
12114 f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
12115 f->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
12116 }
12117 }
12118
12119 if (aParent->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
12120 AddInPopupStateBitToDescendants(this);
12121 } else {
12122 RemoveInPopupStateBitFromDescendants(this);
12123 }
12124
12125 // If our new parent only has invalid children, then we just invalidate
12126 // ourselves too. This is probably faster than clearing the flag all
12127 // the way up the frame tree.
12128 if (aParent->HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT)) {
12129 InvalidateFrame();
12130 } else {
12131 SchedulePaint();
12132 }
12133}
12134
12135bool nsIFrame::IsStackingContext(const nsStyleDisplay* aStyleDisplay,
12136 const nsStyleEffects* aStyleEffects) {
12137 // Properties that influence the output of this function should be handled in
12138 // change_bits_for_longhand as well.
12139 if (HasOpacity(aStyleDisplay, aStyleEffects, nullptr)) {
12140 return true;
12141 }
12142 if (IsTransformed()) {
12143 return true;
12144 }
12145 auto willChange = aStyleDisplay->mWillChange.bits;
12146 if (aStyleDisplay->IsContainPaint() || aStyleDisplay->IsContainLayout() ||
12147 willChange & StyleWillChangeBits::CONTAIN) {
12148 if (SupportsContainLayoutAndPaint()) {
12149 return true;
12150 }
12151 }
12152
12153 if (ForcesStackingContextForViewTransition()) {
12154 return true;
12155 }
12156
12157 // strictly speaking, 'perspective' doesn't require visual atomicity,
12158 // but the spec says it acts like the rest of these
12159 if (aStyleDisplay->HasPerspectiveStyle() ||
12160 willChange & StyleWillChangeBits::PERSPECTIVE) {
12161 if (SupportsCSSTransforms()) {
12162 return true;
12163 }
12164 }
12165 if (!StylePosition()->mZIndex.IsAuto() ||
12166 willChange & StyleWillChangeBits::Z_INDEX) {
12167 if (ZIndexApplies()) {
12168 return true;
12169 }
12170 }
12171 // Elements captured in a view transition during a view transition or whose
12172 // view-transition-name computed value is not none (at any time) form a s
12173 // https://drafts.csswg.org/css-view-transitions-1/#named-and-transitioning
12174 return aStyleEffects->mMixBlendMode != StyleBlend::Normal ||
12175 SVGIntegrationUtils::UsingEffectsForFrame(this) ||
12176 aStyleDisplay->IsPositionForcingStackingContext() ||
12177 aStyleDisplay->mIsolation != StyleIsolation::Auto ||
12178 willChange & StyleWillChangeBits::STACKING_CONTEXT_UNCONDITIONAL;
12179}
12180
12181bool nsIFrame::IsStackingContext() {
12182 return IsStackingContext(StyleDisplay(), StyleEffects());
12183}
12184
12185static bool IsFrameRectScrolledOutOfView(const nsIFrame* aTarget,
12186 const nsRect& aTargetRect,
12187 const nsIFrame* aParent) {
12188 // The ancestor frame we are checking if it clips out aTargetRect relative to
12189 // aTarget.
12190 nsIFrame* clipParent = nullptr;
12191
12192 // find the first scrollable frame or root frame if we are in a fixed pos
12193 // subtree
12194 for (nsIFrame* f = const_cast<nsIFrame*>(aParent); f;
12195 f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
12196 ScrollContainerFrame* scrollContainerFrame = do_QueryFrame(f);
12197 if (scrollContainerFrame) {
12198 clipParent = f;
12199 break;
12200 }
12201 if (f->StyleDisplay()->mPosition == StylePositionProperty::Fixed &&
12202 nsLayoutUtils::IsReallyFixedPos(f)) {
12203 clipParent = f->GetParent();
12204 break;
12205 }
12206 }
12207
12208 if (!clipParent) {
12209 // Even if we couldn't find the nearest scrollable frame, it might mean we
12210 // are in an out-of-process iframe, try to see if |aTarget| frame is
12211 // scrolled out of view in an scrollable frame in a cross-process ancestor
12212 // document.
12213 return nsLayoutUtils::FrameRectIsScrolledOutOfViewInCrossProcess(
12214 aTarget, aTargetRect);
12215 }
12216
12217 nsRect clipRect = clipParent->InkOverflowRectRelativeToSelf();
12218 // We consider that the target is scrolled out if the scrollable (or root)
12219 // frame is empty.
12220 if (clipRect.IsEmpty()) {
12221 return true;
12222 }
12223
12224 nsRect transformedRect = nsLayoutUtils::TransformFrameRectToAncestor(
12225 aTarget, aTargetRect, clipParent);
12226
12227 if (transformedRect.IsEmpty()) {
12228 // If the transformed rect is empty it represents a line or a point that we
12229 // should check is outside the the scrollable rect.
12230 if (transformedRect.x > clipRect.XMost() ||
12231 transformedRect.y > clipRect.YMost() ||
12232 clipRect.x > transformedRect.XMost() ||
12233 clipRect.y > transformedRect.YMost()) {
12234 return true;
12235 }
12236 } else if (!transformedRect.Intersects(clipRect)) {
12237 return true;
12238 }
12239
12240 nsIFrame* parent = clipParent->GetParent();
12241 if (!parent) {
12242 return false;
12243 }
12244
12245 return IsFrameRectScrolledOutOfView(clipParent, transformedRect, parent);
12246}
12247
12248bool nsIFrame::IsScrolledOutOfView() const {
12249 nsRect rect = InkOverflowRectRelativeToSelf();
12250 return IsFrameRectScrolledOutOfView(this, rect, this);
12251}
12252
12253gfx::Matrix nsIFrame::ComputeWidgetTransform() const {
12254 const nsStyleUIReset* uiReset = StyleUIReset();
12255 if (uiReset->mMozWindowTransform.IsNone()) {
12256 return gfx::Matrix();
12257 }
12258
12259 TransformReferenceBox refBox(nullptr, nsRect(nsPoint(), GetSize()));
12260
12261 int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
12262 gfx::Matrix4x4 matrix = nsStyleTransformMatrix::ReadTransforms(
12263 uiReset->mMozWindowTransform, refBox, float(appUnitsPerDevPixel),
12264 mComputedStyle->EffectiveZoom());
12265
12266 gfx::Matrix result2d;
12267 if (!matrix.CanDraw2D(&result2d)) {
12268 // FIXME: It would be preferable to reject non-2D transforms at parse time.
12269 NS_WARNING(NS_DebugBreak(NS_DEBUG_WARNING, "-moz-window-transform does not describe a 2D transform, "
"but only 2d transforms are supported", nullptr, "./../../../layout/generic/nsIFrame.cpp"
, 12271)
12270 "-moz-window-transform does not describe a 2D transform, "NS_DebugBreak(NS_DEBUG_WARNING, "-moz-window-transform does not describe a 2D transform, "
"but only 2d transforms are supported", nullptr, "./../../../layout/generic/nsIFrame.cpp"
, 12271)
12271 "but only 2d transforms are supported")NS_DebugBreak(NS_DEBUG_WARNING, "-moz-window-transform does not describe a 2D transform, "
"but only 2d transforms are supported", nullptr, "./../../../layout/generic/nsIFrame.cpp"
, 12271)
;
12272 return gfx::Matrix();
12273 }
12274
12275 return result2d;
12276}
12277
12278void nsIFrame::DoUpdateStyleOfOwnedAnonBoxes(ServoRestyleState& aRestyleState) {
12279 // As a special case, we check for {ib}-split block frames here, rather
12280 // than have an nsInlineFrame::AppendDirectlyOwnedAnonBoxes implementation
12281 // that returns them.
12282 //
12283 // (If we did handle them in AppendDirectlyOwnedAnonBoxes, we would have to
12284 // return *all* of the in-flow {ib}-split block frames, not just the first
12285 // one. For restyling, we really just need the first in flow, and the other
12286 // user of the AppendOwnedAnonBoxes API, AllChildIterator, doesn't need to
12287 // know about them at all, since these block frames never create NAC. So we
12288 // avoid any unncessary hashtable lookups for the {ib}-split frames by calling
12289 // UpdateStyleOfOwnedAnonBoxesForIBSplit directly here.)
12290 if (IsInlineFrame()) {
12291 if (HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
12292 static_cast<nsInlineFrame*>(this)->UpdateStyleOfOwnedAnonBoxesForIBSplit(
12293 aRestyleState);
12294 }
12295 return;
12296 }
12297
12298 AutoTArray<OwnedAnonBox, 4> frames;
12299 AppendDirectlyOwnedAnonBoxes(frames);
12300 for (OwnedAnonBox& box : frames) {
12301 if (box.mUpdateStyleFn) {
12302 box.mUpdateStyleFn(this, box.mAnonBoxFrame, aRestyleState);
12303 } else {
12304 UpdateStyleOfChildAnonBox(box.mAnonBoxFrame, aRestyleState);
12305 }
12306 }
12307}
12308
12309/* virtual */
12310void nsIFrame::AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) {
12311 MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES))>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(!HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES)))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("!HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES)"
, "./../../../layout/generic/nsIFrame.cpp", 12311); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES)"
")"); do { MOZ_CrashSequence(__null, 12311); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12312 MOZ_ASSERT_UNREACHABLE(do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"Subclasses that have directly owned anonymous boxes should override "
"this method!" ")", "./../../../layout/generic/nsIFrame.cpp"
, 12314); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Subclasses that have directly owned anonymous boxes should override "
"this method!" ")"); do { MOZ_CrashSequence(__null, 12314); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
12313 "Subclasses that have directly owned anonymous boxes should override "do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"Subclasses that have directly owned anonymous boxes should override "
"this method!" ")", "./../../../layout/generic/nsIFrame.cpp"
, 12314); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Subclasses that have directly owned anonymous boxes should override "
"this method!" ")"); do { MOZ_CrashSequence(__null, 12314); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
12314 "this method!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"Subclasses that have directly owned anonymous boxes should override "
"this method!" ")", "./../../../layout/generic/nsIFrame.cpp"
, 12314); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Subclasses that have directly owned anonymous boxes should override "
"this method!" ")"); do { MOZ_CrashSequence(__null, 12314); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
;
12315}
12316
12317void nsIFrame::DoAppendOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) {
12318 size_t i = aResult.Length();
12319 AppendDirectlyOwnedAnonBoxes(aResult);
12320
12321 // After appending the directly owned anonymous boxes of this frame to
12322 // aResult above, we need to check each of them to see if they own
12323 // any anonymous boxes themselves. Note that we keep progressing
12324 // through aResult, looking for additional entries in aResult from these
12325 // subsequent AppendDirectlyOwnedAnonBoxes calls. (Thus we can't
12326 // use a ranged for loop here.)
12327
12328 while (i < aResult.Length()) {
12329 nsIFrame* f = aResult[i].mAnonBoxFrame;
12330 if (f->HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES)) {
12331 f->AppendDirectlyOwnedAnonBoxes(aResult);
12332 }
12333 ++i;
12334 }
12335}
12336
12337nsIFrame::CaretPosition::CaretPosition() : mContentOffset(0) {}
12338
12339nsIFrame::CaretPosition::~CaretPosition() = default;
12340
12341bool nsIFrame::HasCSSAnimations() {
12342 auto* collection = AnimationCollection<CSSAnimation>::Get(this);
12343 return collection && !collection->mAnimations.IsEmpty();
12344}
12345
12346bool nsIFrame::HasCSSTransitions() {
12347 auto* collection = AnimationCollection<CSSTransition>::Get(this);
12348 return collection && !collection->mAnimations.IsEmpty();
12349}
12350
12351void nsIFrame::AddSizeOfExcludingThisForTree(nsWindowSizes& aSizes) const {
12352 aSizes.mLayoutFramePropertiesSize +=
12353 mProperties.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
12354
12355 // We don't do this for Gecko because this stuff is stored in the nsPresArena
12356 // and so measured elsewhere.
12357 if (!aSizes.mState.HaveSeenPtr(mComputedStyle)) {
12358 mComputedStyle->AddSizeOfIncludingThis(aSizes,
12359 &aSizes.mLayoutComputedValuesNonDom);
12360 }
12361
12362 for (const auto& childList : ChildLists()) {
12363 for (const nsIFrame* f : childList.mList) {
12364 f->AddSizeOfExcludingThisForTree(aSizes);
12365 }
12366 }
12367}
12368
12369nsRect nsIFrame::GetCompositorHitTestArea(nsDisplayListBuilder* aBuilder) {
12370 nsRect area;
12371
12372 ScrollContainerFrame* scrollContainerFrame =
12373 nsLayoutUtils::GetScrollContainerFrameFor(this);
12374 if (scrollContainerFrame) {
12375 // If this frame is the scrolled frame of a scroll container frame, then we
12376 // need to pick up the area corresponding to the overflow rect as well.
12377 // Otherwise the parts of the overflow that are not occupied by descendants
12378 // get skipped and the APZ code sends touch events to the content underneath
12379 // instead. See https://bugzilla.mozilla.org/show_bug.cgi?id=1127773#c15.
12380 area = ScrollableOverflowRect();
12381 } else {
12382 area = GetRectRelativeToSelf();
12383 }
12384
12385 if (!area.IsEmpty()) {
12386 return area + aBuilder->ToReferenceFrame(this);
12387 }
12388
12389 return area;
12390}
12391
12392CompositorHitTestInfo nsIFrame::GetCompositorHitTestInfo(
12393 nsDisplayListBuilder* aBuilder) {
12394 CompositorHitTestInfo result = CompositorHitTestInvisibleToHit;
12395 if (Style()->PointerEvents() == StylePointerEvents::None) {
12396 return result;
12397 }
12398 return GetCompositorHitTestInfoWithoutPointerEvents(aBuilder);
12399}
12400
12401CompositorHitTestInfo nsIFrame::GetCompositorHitTestInfoWithoutPointerEvents(
12402 nsDisplayListBuilder* aBuilder) {
12403 CompositorHitTestInfo result = CompositorHitTestInvisibleToHit;
12404
12405 if (aBuilder->IsInsidePointerEventsNoneDoc() ||
12406 aBuilder->IsInViewTransitionCapture()) {
12407 // Somewhere up the parent document chain is a subdocument with pointer-
12408 // events:none set on it, or we're getting captured in a view transition.
12409 return result;
12410 }
12411 if (!GetParent()) {
12412 MOZ_ASSERT(IsViewportFrame())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(IsViewportFrame())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(IsViewportFrame()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("IsViewportFrame()"
, "./../../../layout/generic/nsIFrame.cpp", 12412); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsViewportFrame()" ")"); do { MOZ_CrashSequence
(__null, 12412); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
12413 // Viewport frames are never event targets, other frames, like canvas
12414 // frames, are the event targets for any regions viewport frames may cover.
12415 return result;
12416 }
12417 if (!StyleVisibility()->IsVisible()) {
12418 return result;
12419 }
12420
12421 // Anything that didn't match the above conditions is visible to hit-testing.
12422 result = CompositorHitTestFlags::eVisibleToHitTest;
12423 SVGUtils::MaskUsage maskUsage = SVGUtils::DetermineMaskUsage(this, false);
12424 if (maskUsage.UsingMaskOrClipPath()) {
12425 // If WebRender is enabled, simple clip-paths can be converted into WR
12426 // clips that WR knows how to hit-test against, so we don't need to mark
12427 // it as an irregular area.
12428 if (!maskUsage.IsSimpleClipShape()) {
12429 result += CompositorHitTestFlags::eIrregularArea;
12430 }
12431 }
12432
12433 if (aBuilder->IsBuildingNonLayerizedScrollbar()) {
12434 // Scrollbars may be painted into a layer below the actual layer they will
12435 // scroll, and therefore wheel events may be dispatched to the outer frame
12436 // instead of the intended scrollframe. To address this, we force a d-t-c
12437 // region on scrollbar frames that won't be placed in their own layer. See
12438 // bug 1213324 for details.
12439 result += CompositorHitTestFlags::eInactiveScrollframe;
12440 } else if (aBuilder->GetAncestorHasApzAwareEventHandler()) {
12441 result += CompositorHitTestFlags::eApzAwareListeners;
12442 } else if (IsRangeFrame()) {
12443 // Range frames handle touch events directly without having a touch listener
12444 // so we need to let APZ know that this area cares about events.
12445 result += CompositorHitTestFlags::eApzAwareListeners;
12446 }
12447
12448 if (aBuilder->IsTouchEventPrefEnabledDoc()) {
12449 // Inherit the touch-action flags from the parent, if there is one. We do
12450 // this because of how the touch-action on a frame combines the touch-action
12451 // from ancestor DOM elements. Refer to the documentation in
12452 // TouchActionHelper.cpp for details; this code is meant to be equivalent to
12453 // that code, but woven into the top-down recursive display list building
12454 // process.
12455 CompositorHitTestInfo inheritedTouchAction =
12456 aBuilder->GetInheritedCompositorHitTestInfo() &
12457 CompositorHitTestTouchActionMask;
12458
12459 nsIFrame* touchActionFrame = this;
12460 if (ScrollContainerFrame* scrollContainerFrame =
12461 nsLayoutUtils::GetScrollContainerFrameFor(this)) {
12462 ScrollStyles ss = scrollContainerFrame->GetScrollStyles();
12463 if (ss.mVertical != StyleOverflow::Hidden ||
12464 ss.mHorizontal != StyleOverflow::Hidden) {
12465 touchActionFrame = scrollContainerFrame;
12466 // On scrollframes, stop inheriting the pan-x and pan-y flags; instead,
12467 // reset them back to zero to allow panning on the scrollframe unless we
12468 // encounter an element that disables it that's inside the scrollframe.
12469 // This is equivalent to the |considerPanning| variable in
12470 // TouchActionHelper.cpp, but for a top-down traversal.
12471 CompositorHitTestInfo panMask(
12472 CompositorHitTestFlags::eTouchActionPanXDisabled,
12473 CompositorHitTestFlags::eTouchActionPanYDisabled);
12474 inheritedTouchAction -= panMask;
12475 }
12476 }
12477
12478 result += inheritedTouchAction;
12479
12480 const StyleTouchAction touchAction = touchActionFrame->UsedTouchAction();
12481 // The CSS allows the syntax auto | none | [pan-x || pan-y] | manipulation
12482 // so we can eliminate some combinations of things.
12483 if (touchAction == StyleTouchAction::AUTO) {
12484 // nothing to do
12485 } else if (touchAction & StyleTouchAction::MANIPULATION) {
12486 result += CompositorHitTestFlags::eTouchActionAnimatingZoomDisabled;
12487 } else {
12488 // This path handles the cases none | [pan-x || pan-y || pinch-zoom] so
12489 // double-tap is disabled in here.
12490 if (!(touchAction & StyleTouchAction::PINCH_ZOOM)) {
12491 result += CompositorHitTestFlags::eTouchActionPinchZoomDisabled;
12492 }
12493
12494 result += CompositorHitTestFlags::eTouchActionAnimatingZoomDisabled;
12495
12496 if (!(touchAction & StyleTouchAction::PAN_X)) {
12497 result += CompositorHitTestFlags::eTouchActionPanXDisabled;
12498 }
12499 if (!(touchAction & StyleTouchAction::PAN_Y)) {
12500 result += CompositorHitTestFlags::eTouchActionPanYDisabled;
12501 }
12502 if (touchAction & StyleTouchAction::NONE) {
12503 // all the touch-action disabling flags will already have been set above
12504 MOZ_ASSERT(result.contains(CompositorHitTestTouchActionMask))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(result.contains(CompositorHitTestTouchActionMask))>
::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(result.contains(CompositorHitTestTouchActionMask))))
, 0))) { do { } while (false); MOZ_ReportAssertionFailure("result.contains(CompositorHitTestTouchActionMask)"
, "./../../../layout/generic/nsIFrame.cpp", 12504); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "result.contains(CompositorHitTestTouchActionMask)"
")"); do { MOZ_CrashSequence(__null, 12504); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12505 }
12506 }
12507 }
12508
12509 const Maybe<ScrollDirection> scrollDirection =
12510 aBuilder->GetCurrentScrollbarDirection();
12511 if (scrollDirection.isSome()) {
12512 if (GetContent()->IsXULElement(nsGkAtoms::thumb)) {
12513 const bool thumbGetsLayer = aBuilder->GetCurrentScrollbarTarget() !=
12514 layers::ScrollableLayerGuid::NULL_SCROLL_ID;
12515 if (thumbGetsLayer) {
12516 result += CompositorHitTestFlags::eScrollbarThumb;
12517 } else {
12518 result += CompositorHitTestFlags::eInactiveScrollframe;
12519 }
12520 }
12521
12522 if (*scrollDirection == ScrollDirection::eVertical) {
12523 result += CompositorHitTestFlags::eScrollbarVertical;
12524 }
12525
12526 // includes the ScrollbarFrame, SliderFrame, anything else that
12527 // might be inside the xul:scrollbar
12528 result += CompositorHitTestFlags::eScrollbar;
12529 }
12530
12531 return result;
12532}
12533
12534// Returns true if we can guarantee there is no visible descendants.
12535static bool HasNoVisibleDescendants(const nsIFrame* aFrame) {
12536 for (const auto& childList : aFrame->ChildLists()) {
12537 for (nsIFrame* f : childList.mList) {
12538 if (nsPlaceholderFrame::GetRealFrameFor(f)
12539 ->IsVisibleOrMayHaveVisibleDescendants()) {
12540 return false;
12541 }
12542 }
12543 }
12544 return true;
12545}
12546
12547void nsIFrame::UpdateVisibleDescendantsState() {
12548 if (StyleVisibility()->IsVisible()) {
12549 // Notify invisible ancestors that a visible descendant exists now.
12550 nsIFrame* ancestor;
12551 for (ancestor = GetInFlowParent();
12552 ancestor && !ancestor->StyleVisibility()->IsVisible();
12553 ancestor = ancestor->GetInFlowParent()) {
12554 ancestor->mAllDescendantsAreInvisible = false;
12555 }
12556 } else {
12557 mAllDescendantsAreInvisible = HasNoVisibleDescendants(this);
12558 }
12559}
12560
12561PhysicalAxes nsIFrame::ShouldApplyOverflowClipping(
12562 const nsStyleDisplay* aDisp) const {
12563 MOZ_ASSERT(aDisp == StyleDisplay(), "Wrong display struct")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aDisp == StyleDisplay())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aDisp == StyleDisplay()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("aDisp == StyleDisplay()"
" (" "Wrong display struct" ")", "./../../../layout/generic/nsIFrame.cpp"
, 12563); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aDisp == StyleDisplay()"
") (" "Wrong display struct" ")"); do { MOZ_CrashSequence(__null
, 12563); __attribute__((nomerge)) ::abort(); } while (false)
; } } while (false)
;
12564
12565 if (IsScrollContainerOrSubclass()) {
12566 // Scrollers deal with overflow on their own.
12567 return {};
12568 }
12569
12570 // 'contain:paint', which we handle as 'overflow:clip' here. 'contain:paint'
12571 // should prevent all means of escaping that clipping (e.g. because it forms a
12572 // fixed-pos containing block).
12573 if (aDisp->IsContainPaint() && SupportsContainLayoutAndPaint()) {
12574 return kPhysicalAxesBoth;
12575 }
12576
12577 // and overflow: scrollable that we should interpret as clip
12578 if (aDisp->IsScrollableOverflow()) {
12579 // REVIEW: these are the frame types that set up clipping.
12580 LayoutFrameType type = Type();
12581 switch (type) {
12582 case LayoutFrameType::CheckboxRadio:
12583 case LayoutFrameType::ComboboxControl:
12584 case LayoutFrameType::Progress:
12585 case LayoutFrameType::Range:
12586 case LayoutFrameType::SubDocument:
12587 case LayoutFrameType::SVGForeignObject:
12588 case LayoutFrameType::SVGInnerSVG:
12589 case LayoutFrameType::SVGOuterSVG:
12590 case LayoutFrameType::SVGSymbol:
12591 case LayoutFrameType::Image:
12592 case LayoutFrameType::HTMLVideo:
12593 case LayoutFrameType::TableCell:
12594 return kPhysicalAxesBoth;
12595 case LayoutFrameType::Table:
12596 // Tables, for legacy reasons only clip when hidden in both directions,
12597 // and treat all other scrollable overflow values as `visible`.
12598 // This is (somewhat, since other browsers make this change at
12599 // computed-value time, see bug 1918789) interoperable css2-era
12600 // behavior. We might be able to change this, but not today.
12601 // See layout/reftests/table-overflow/bug785684-x.html
12602 return aDisp->mOverflowX == StyleOverflow::Hidden &&
12603 aDisp->mOverflowY == StyleOverflow::Hidden
12604 ? kPhysicalAxesBoth
12605 : PhysicalAxes();
12606 case LayoutFrameType::TextInput:
12607 // It has an anonymous scroll container frame that handles any overflow.
12608 return PhysicalAxes();
12609 default:
12610 break;
12611 }
12612 if (IsSuppressedScrollableBlockForPrint()) {
12613 return kPhysicalAxesBoth;
12614 }
12615 }
12616
12617 if (aDisp->mOverflowX == StyleOverflow::Clip ||
12618 aDisp->mOverflowY == StyleOverflow::Clip) {
12619 // FIXME: we could use GetViewportScrollStylesOverrideElement() here instead
12620 // if that worked correctly in a print context. (see bug 1654667)
12621 const auto* element = Element::FromNodeOrNull(GetContent());
12622 if (!element ||
12623 !PresContext()->ElementWouldPropagateScrollStyles(*element)) {
12624 PhysicalAxes axes;
12625 if (aDisp->mOverflowX == StyleOverflow::Clip) {
12626 axes += PhysicalAxis::Horizontal;
12627 }
12628 if (aDisp->mOverflowY == StyleOverflow::Clip) {
12629 axes += PhysicalAxis::Vertical;
12630 }
12631 return axes;
12632 }
12633 }
12634
12635 return PhysicalAxes();
12636}
12637
12638bool nsIFrame::IsSuppressedScrollableBlockForPrint() const {
12639 // This condition needs to match the suppressScrollFrame logic in the frame
12640 // constructor.
12641 if (!PresContext()->IsPaginated() || !IsBlockFrame() ||
12642 !StyleDisplay()->IsScrollableOverflow() ||
12643 !StyleDisplay()->IsBlockOutsideStyle() ||
12644 mContent->IsInNativeAnonymousSubtree()) {
12645 return false;
12646 }
12647 if (auto* element = Element::FromNode(mContent);
12648 element && PresContext()->ElementWouldPropagateScrollStyles(*element)) {
12649 return false;
12650 }
12651 return true;
12652}
12653
12654PhysicalAxes nsIFrame::GetAnchorPosCompensatingForScroll() const {
12655 if (!HasAnchorPosReference()) {
12656 return {};
12657 }
12658 const auto* prop = GetProperty(AnchorPosReferences());
12659 if (!prop) {
12660 return {};
12661 }
12662
12663 return prop->CompensatingForScrollAxes();
12664}
12665
12666bool nsIFrame::HasUnreflowedContainerQueryAncestor() const {
12667 // If this frame has done the first reflow, its ancestors are guaranteed to
12668 // have as well.
12669 if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW) ||
12670 !PresContext()->HasContainerQueryFrames()) {
12671 return false;
12672 }
12673 for (nsIFrame* cur = GetInFlowParent(); cur; cur = cur->GetInFlowParent()) {
12674 if (!cur->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
12675 // Done first reflow from this ancestor up, including query containers.
12676 return false;
12677 }
12678 if (cur->StyleDisplay()->IsQueryContainer()) {
12679 return true;
12680 }
12681 }
12682 // No query container from this frame up to root.
12683 return false;
12684}
12685
12686bool nsIFrame::ShouldBreakBefore(const BreakType aBreakType) const {
12687 const auto* display = StyleDisplay();
12688 return ShouldBreakBetween(display, display->mBreakBefore, aBreakType);
12689}
12690
12691bool nsIFrame::ShouldBreakAfter(const BreakType aBreakType) const {
12692 const auto* display = StyleDisplay();
12693 return ShouldBreakBetween(display, display->mBreakAfter, aBreakType);
12694}
12695
12696bool nsIFrame::ShouldBreakBetween(const nsStyleDisplay* aDisplay,
12697 const StyleBreakBetween aBreakBetween,
12698 const BreakType aBreakType) const {
12699 const bool shouldBreakBetween = [&] {
12700 switch (aBreakBetween) {
12701 case StyleBreakBetween::Always:
12702 return true;
12703 case StyleBreakBetween::Auto:
12704 case StyleBreakBetween::Avoid:
12705 return false;
12706 case StyleBreakBetween::Page:
12707 case StyleBreakBetween::Left:
12708 case StyleBreakBetween::Right:
12709 return aBreakType == BreakType::Page;
12710 }
12711 MOZ_ASSERT_UNREACHABLE("Unknown break-between value!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: "
"Unknown break-between value!" ")", "./../../../layout/generic/nsIFrame.cpp"
, 12711); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Unknown break-between value!" ")"
); do { MOZ_CrashSequence(__null, 12711); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
12712 return false;
12713 }();
12714
12715 if (!shouldBreakBetween) {
12716 return false;
12717 }
12718 if (IsAbsolutelyPositioned(aDisplay)) {
12719 // 'break-before' and 'break-after' properties does not apply to
12720 // absolutely-positioned boxes.
12721 return false;
12722 }
12723 return true;
12724}
12725
12726#ifdef DEBUG1
12727static void GetTagName(nsIFrame* aFrame, nsIContent* aContent, int aResultSize,
12728 char* aResult) {
12729 if (aContent) {
12730 snprintf(aResult, aResultSize, "%s@%p",
12731 nsAtomCString(aContent->NodeInfo()->NameAtom()).get(), aFrame);
12732 } else {
12733 snprintf(aResult, aResultSize, "@%p", aFrame);
12734 }
12735}
12736
12737void nsIFrame::Trace(const char* aMethod, bool aEnter) {
12738 if (NS_FRAME_LOG_TEST(sFrameLogModule, NS_FRAME_TRACE_CALLS)(int(((mozilla::LogModule*)(sFrameLogModule))->Level()) &
(0x1))
) {
12739 char tagbuf[40];
12740 GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
12741 printf_stderr("%s: %s %s", tagbuf, aEnter ? "enter" : "exit", aMethod);
12742 }
12743}
12744
12745void nsIFrame::Trace(const char* aMethod, bool aEnter,
12746 const nsReflowStatus& aStatus) {
12747 if (NS_FRAME_LOG_TEST(sFrameLogModule, NS_FRAME_TRACE_CALLS)(int(((mozilla::LogModule*)(sFrameLogModule))->Level()) &
(0x1))
) {
12748 char tagbuf[40];
12749 GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
12750 printf_stderr("%s: %s %s, status=%scomplete%s", tagbuf,
12751 aEnter ? "enter" : "exit", aMethod,
12752 aStatus.IsIncomplete() ? "not" : "",
12753 (aStatus.NextInFlowNeedsReflow()) ? "+reflow" : "");
12754 }
12755}
12756
12757void nsIFrame::TraceMsg(const char* aFormatString, ...) {
12758 if (NS_FRAME_LOG_TEST(sFrameLogModule, NS_FRAME_TRACE_CALLS)(int(((mozilla::LogModule*)(sFrameLogModule))->Level()) &
(0x1))
) {
12759 // Format arguments into a buffer
12760 char argbuf[200];
12761 va_list ap;
12762 va_start(ap, aFormatString)__builtin_va_start(ap, aFormatString);
12763 VsprintfLiteral(argbuf, aFormatString, ap);
12764 va_end(ap)__builtin_va_end(ap);
12765
12766 char tagbuf[40];
12767 GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
12768 printf_stderr("%s: %s", tagbuf, argbuf);
12769 }
12770}
12771
12772void nsIFrame::VerifyDirtyBitSet(const nsFrameList& aFrameList) {
12773 for (nsIFrame* f : aFrameList) {
12774 NS_ASSERTION(f->HasAnyStateBits(NS_FRAME_IS_DIRTY), "dirty bit not set")do { if (!(f->HasAnyStateBits(NS_FRAME_IS_DIRTY))) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "dirty bit not set", "f->HasAnyStateBits(NS_FRAME_IS_DIRTY)"
, "./../../../layout/generic/nsIFrame.cpp", 12774); MOZ_PretendNoReturn
(); } } while (0)
;
12775 }
12776}
12777
12778// Validation of SideIsVertical.
12779# define CASE(side, result) \
12780 static_assert(SideIsVertical(side) == result, "SideIsVertical is wrong")
12781CASE(eSideTop, false);
12782CASE(eSideRight, true);
12783CASE(eSideBottom, false);
12784CASE(eSideLeft, true);
12785# undef CASE
12786
12787// Validation of HalfCornerIsX.
12788# define CASE(corner, result) \
12789 static_assert(HalfCornerIsX(corner) == result, "HalfCornerIsX is wrong")
12790CASE(eCornerTopLeftX, true);
12791CASE(eCornerTopLeftY, false);
12792CASE(eCornerTopRightX, true);
12793CASE(eCornerTopRightY, false);
12794CASE(eCornerBottomRightX, true);
12795CASE(eCornerBottomRightY, false);
12796CASE(eCornerBottomLeftX, true);
12797CASE(eCornerBottomLeftY, false);
12798# undef CASE
12799
12800// Validation of HalfToFullCorner.
12801# define CASE(corner, result) \
12802 static_assert(HalfToFullCorner(corner) == result, \
12803 "HalfToFullCorner is " \
12804 "wrong")
12805CASE(eCornerTopLeftX, eCornerTopLeft);
12806CASE(eCornerTopLeftY, eCornerTopLeft);
12807CASE(eCornerTopRightX, eCornerTopRight);
12808CASE(eCornerTopRightY, eCornerTopRight);
12809CASE(eCornerBottomRightX, eCornerBottomRight);
12810CASE(eCornerBottomRightY, eCornerBottomRight);
12811CASE(eCornerBottomLeftX, eCornerBottomLeft);
12812CASE(eCornerBottomLeftY, eCornerBottomLeft);
12813# undef CASE
12814
12815// Validation of FullToHalfCorner.
12816# define CASE(corner, vert, result) \
12817 static_assert(FullToHalfCorner(corner, vert) == result, \
12818 "FullToHalfCorner is wrong")
12819CASE(eCornerTopLeft, false, eCornerTopLeftX);
12820CASE(eCornerTopLeft, true, eCornerTopLeftY);
12821CASE(eCornerTopRight, false, eCornerTopRightX);
12822CASE(eCornerTopRight, true, eCornerTopRightY);
12823CASE(eCornerBottomRight, false, eCornerBottomRightX);
12824CASE(eCornerBottomRight, true, eCornerBottomRightY);
12825CASE(eCornerBottomLeft, false, eCornerBottomLeftX);
12826CASE(eCornerBottomLeft, true, eCornerBottomLeftY);
12827# undef CASE
12828
12829// Validation of SideToFullCorner.
12830# define CASE(side, second, result) \
12831 static_assert(SideToFullCorner(side, second) == result, \
12832 "SideToFullCorner is wrong")
12833CASE(eSideTop, false, eCornerTopLeft);
12834CASE(eSideTop, true, eCornerTopRight);
12835
12836CASE(eSideRight, false, eCornerTopRight);
12837CASE(eSideRight, true, eCornerBottomRight);
12838
12839CASE(eSideBottom, false, eCornerBottomRight);
12840CASE(eSideBottom, true, eCornerBottomLeft);
12841
12842CASE(eSideLeft, false, eCornerBottomLeft);
12843CASE(eSideLeft, true, eCornerTopLeft);
12844# undef CASE
12845
12846// Validation of SideToHalfCorner.
12847# define CASE(side, second, parallel, result) \
12848 static_assert(SideToHalfCorner(side, second, parallel) == result, \
12849 "SideToHalfCorner is wrong")
12850CASE(eSideTop, false, true, eCornerTopLeftX);
12851CASE(eSideTop, false, false, eCornerTopLeftY);
12852CASE(eSideTop, true, true, eCornerTopRightX);
12853CASE(eSideTop, true, false, eCornerTopRightY);
12854
12855CASE(eSideRight, false, false, eCornerTopRightX);
12856CASE(eSideRight, false, true, eCornerTopRightY);
12857CASE(eSideRight, true, false, eCornerBottomRightX);
12858CASE(eSideRight, true, true, eCornerBottomRightY);
12859
12860CASE(eSideBottom, false, true, eCornerBottomRightX);
12861CASE(eSideBottom, false, false, eCornerBottomRightY);
12862CASE(eSideBottom, true, true, eCornerBottomLeftX);
12863CASE(eSideBottom, true, false, eCornerBottomLeftY);
12864
12865CASE(eSideLeft, false, false, eCornerBottomLeftX);
12866CASE(eSideLeft, false, true, eCornerBottomLeftY);
12867CASE(eSideLeft, true, false, eCornerTopLeftX);
12868CASE(eSideLeft, true, true, eCornerTopLeftY);
12869# undef CASE
12870
12871#endif