Bug Summary

File:root/firefox-clang/obj-x86_64-pc-linux-gnu/layout/generic/./../../../layout/generic/nsIFrame.cpp
Warning:line 3824, 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/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-10-17-231001-351276-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#include <type_traits>
13
14#include "AnchorPositioningUtils.h"
15#include "LayoutLogging.h"
16#include "PseudoStyleType.h"
17#include "RubyUtils.h"
18#include "TextOverflow.h"
19#include "gfx2DGlue.h"
20#include "gfxUtils.h"
21#include "mozilla/AbsoluteContainingBlock.h"
22#include "mozilla/Attributes.h"
23#include "mozilla/CaretAssociationHint.h"
24#include "mozilla/ComputedStyle.h"
25#include "mozilla/DebugOnly.h"
26#include "mozilla/DisplayPortUtils.h"
27#include "mozilla/EventForwards.h"
28#include "mozilla/FocusModel.h"
29#include "mozilla/IntegerRange.h"
30#include "mozilla/Logging.h"
31#include "mozilla/Maybe.h"
32#include "mozilla/PresShell.h"
33#include "mozilla/PresShellInlines.h"
34#include "mozilla/ReflowInput.h"
35#include "mozilla/RestyleManager.h"
36#include "mozilla/ResultExtensions.h"
37#include "mozilla/SVGIntegrationUtils.h"
38#include "mozilla/SVGMaskFrame.h"
39#include "mozilla/SVGObserverUtils.h"
40#include "mozilla/SVGTextFrame.h"
41#include "mozilla/SVGUtils.h"
42#include "mozilla/ScrollContainerFrame.h"
43#include "mozilla/SelectionMovementUtils.h"
44#include "mozilla/ServoStyleConsts.h"
45#include "mozilla/Sprintf.h"
46#include "mozilla/StaticAnalysisFunctions.h"
47#include "mozilla/StaticPrefs_dom.h"
48#include "mozilla/StaticPrefs_layout.h"
49#include "mozilla/StaticPrefs_print.h"
50#include "mozilla/StaticPrefs_ui.h"
51#include "mozilla/TextControlElement.h"
52#include "mozilla/ToString.h"
53#include "mozilla/Try.h"
54#include "mozilla/ViewportFrame.h"
55#include "mozilla/ViewportUtils.h"
56#include "mozilla/WritingModes.h"
57#include "mozilla/dom/AncestorIterator.h"
58#include "mozilla/dom/CSSAnimation.h"
59#include "mozilla/dom/CSSTransition.h"
60#include "mozilla/dom/ContentVisibilityAutoStateChangeEvent.h"
61#include "mozilla/dom/DocumentInlines.h"
62#include "mozilla/dom/ElementInlines.h"
63#include "mozilla/dom/HTMLDetailsElement.h"
64#include "mozilla/dom/Range.h"
65#include "mozilla/dom/Selection.h"
66#include "mozilla/dom/Text.h"
67#include "mozilla/gfx/2D.h"
68#include "mozilla/gfx/PathHelpers.h"
69#include "mozilla/intl/BidiEmbeddingLevel.h"
70#include "nsAnimationManager.h"
71#include "nsAtom.h"
72#include "nsBidiPresUtils.h"
73#include "nsCOMPtr.h"
74#include "nsCSSFrameConstructor.h"
75#include "nsCSSProps.h"
76#include "nsCSSRendering.h"
77#include "nsCanvasFrame.h"
78#include "nsContentUtils.h"
79#include "nsFieldSetFrame.h"
80#include "nsFlexContainerFrame.h"
81#include "nsFocusManager.h"
82#include "nsFrameList.h"
83#include "nsFrameSelection.h"
84#include "nsFrameState.h"
85#include "nsFrameTraversal.h"
86#include "nsGkAtoms.h"
87#include "nsGridContainerFrame.h"
88#include "nsIBaseWindow.h"
89#include "nsIContent.h"
90#include "nsIContentInlines.h"
91#include "nsIPercentBSizeObserver.h"
92#include "nsImageFrame.h"
93#include "nsInlineFrame.h"
94#include "nsLayoutUtils.h"
95#include "nsMenuPopupFrame.h"
96#include "nsNameSpaceManager.h"
97#include "nsPlaceholderFrame.h"
98#include "nsPresContext.h"
99#include "nsPresContextInlines.h"
100#include "nsReadableUtils.h"
101#include "nsString.h"
102#include "nsStyleConsts.h"
103#include "nsStyleStructInlines.h"
104#include "nsStyleTransformMatrix.h"
105#include "nsTableWrapperFrame.h"
106#include "nsTextControlFrame.h"
107#include "nsXULElement.h"
108
109// For triple-click pref
110#include "RetainedDisplayListBuilder.h"
111#include "ScrollSnap.h"
112#include "StickyScrollContainer.h"
113#include "gfxContext.h"
114#include "imgIRequest.h"
115#include "nsBlockFrame.h"
116#include "nsChangeHint.h"
117#include "nsContainerFrame.h"
118#include "nsDisplayList.h"
119#include "nsError.h"
120#include "nsFontInflationData.h"
121#include "nsIFrameInlines.h"
122#include "nsRegion.h"
123#include "nsStyleChangeList.h"
124#include "nsSubDocumentFrame.h"
125#include "nsViewportInfo.h"
126#include "nsWindowSizes.h"
127
128#ifdef ACCESSIBILITY1
129# include "mozilla/a11y/PdfStructTreeBuilder.h"
130# include "nsAccessibilityService.h"
131#endif
132
133#include "ActiveLayerTracker.h"
134#include "mozilla/AsyncEventDispatcher.h"
135#include "mozilla/CSSClipPathInstance.h"
136#include "mozilla/EffectCompositor.h"
137#include "mozilla/EffectSet.h"
138#include "mozilla/EventListenerManager.h"
139#include "mozilla/EventStateManager.h"
140#include "mozilla/LookAndFeel.h"
141#include "mozilla/MouseEvents.h"
142#include "mozilla/Preferences.h"
143#include "mozilla/ServoStyleSet.h"
144#include "mozilla/ServoStyleSetInlines.h"
145#include "mozilla/css/ImageLoader.h"
146#include "mozilla/dom/HTMLBodyElement.h"
147#include "mozilla/dom/SVGPathData.h"
148#include "mozilla/dom/TouchEvent.h"
149#include "mozilla/gfx/Tools.h"
150#include "mozilla/layers/WebRenderUserData.h"
151#include "mozilla/layout/ScrollAnchorContainer.h"
152#include "nsITheme.h"
153#include "nsPrintfCString.h"
154
155using namespace mozilla;
156using namespace mozilla::css;
157using namespace mozilla::dom;
158using namespace mozilla::gfx;
159using namespace mozilla::layers;
160using namespace mozilla::layout;
161using nsStyleTransformMatrix::TransformReferenceBox;
162
163nsIFrame* nsILineIterator::LineInfo::GetLastFrameOnLine() const {
164 if (!mNumFramesOnLine) {
165 return nullptr; // empty line, not illegal
166 }
167 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", 167); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "mFirstFrameOnLine" ")"); do { MOZ_CrashSequence
(__null, 167); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
168 nsIFrame* maybeLastFrame = mFirstFrameOnLine;
169 for ([[maybe_unused]] int32_t i : IntegerRange(mNumFramesOnLine - 1)) {
170 maybeLastFrame = maybeLastFrame->GetNextSibling();
171 if (NS_WARN_IF(!maybeLastFrame)NS_warn_if_impl(!maybeLastFrame, "!maybeLastFrame", "./../../../layout/generic/nsIFrame.cpp"
, 171)
) {
172 return nullptr;
173 }
174 }
175 return maybeLastFrame;
176}
177
178#ifdef HAVE_64BIT_BUILD1
179static_assert(sizeof(nsIFrame) == 120, "nsIFrame should remain small");
180#else
181static_assert(sizeof(void*) == 4, "Odd build config?");
182// FIXME(emilio): Investigate why win32 and android-arm32 have bigger sizes (80)
183// than Linux32 (76).
184static_assert(sizeof(nsIFrame) <= 80, "nsIFrame should remain small");
185#endif
186
187const mozilla::LayoutFrameType nsIFrame::sLayoutFrameTypes[kFrameClassCount] = {
188#define FRAME_ID(class_, type_, ...) mozilla::LayoutFrameType::type_,
189#define ABSTRACT_FRAME_ID(...)
190#include "mozilla/FrameIdList.h"
191#undef FRAME_ID
192#undef ABSTRACT_FRAME_ID
193};
194
195const nsIFrame::ClassFlags nsIFrame::sLayoutFrameClassFlags[kFrameClassCount] =
196 {
197#define FRAME_ID(class_, type_, flags_, ...) flags_,
198#define ABSTRACT_FRAME_ID(...)
199#include "mozilla/FrameIdList.h"
200#undef FRAME_ID
201#undef ABSTRACT_FRAME_ID
202};
203
204std::string format_as(nsDirection aDirection) {
205 return aDirection == eDirNext ? "eDirNext" : "eDirPrevious";
206}
207
208std::ostream& operator<<(std::ostream& aStream, nsDirection aDirection) {
209 return aStream << format_as(aDirection);
210}
211
212struct nsContentAndOffset {
213 nsIContent* mContent = nullptr;
214 int32_t mOffset = 0;
215 // Whether the boundary is a newline inside a text node rather than a <br> or
216 // a block frame.
217 bool mIsTerminalNewlineInText = false;
218};
219
220#include "nsILineIterator.h"
221#include "prenv.h"
222
223FrameDestroyContext::~FrameDestroyContext() {
224 for (auto& content : mozilla::Reversed(mAnonymousContent)) {
225 mPresShell->NativeAnonymousContentWillBeRemoved(content);
226 content->UnbindFromTree();
227 }
228}
229
230// Formerly the nsIFrameDebug interface
231
232std::ostream& operator<<(std::ostream& aStream, const nsReflowStatus& aStatus) {
233 char complete = 'Y';
234 if (aStatus.IsIncomplete()) {
235 complete = 'N';
236 } else if (aStatus.IsOverflowIncomplete()) {
237 complete = 'O';
238 }
239
240 char brk = 'N';
241 if (aStatus.IsInlineBreakBefore()) {
242 brk = 'B';
243 } else if (aStatus.IsInlineBreakAfter()) {
244 brk = 'A';
245 }
246
247 aStream << "["
248 << "Complete=" << complete << ","
249 << "NIF=" << (aStatus.NextInFlowNeedsReflow() ? 'Y' : 'N') << ","
250 << "Break=" << brk << ","
251 << "FirstLetter=" << (aStatus.FirstLetterComplete() ? 'Y' : 'N')
252 << "]";
253 return aStream;
254}
255
256#ifdef DEBUG1
257
258/**
259 * Note: the log module is created during library initialization which
260 * means that you cannot perform logging before then.
261 */
262mozilla::LazyLogModule nsIFrame::sFrameLogModule("frame");
263
264#endif
265
266NS_DECLARE_FRAME_PROPERTY_DELETABLE(AbsoluteContainingBlockProperty,static const mozilla::FramePropertyDescriptor<AbsoluteContainingBlock
>* AbsoluteContainingBlockProperty() { static const auto descriptor
= mozilla::FramePropertyDescriptor<AbsoluteContainingBlock
>::NewWithDestructor<DeleteValue>(); return &descriptor
; }
267 AbsoluteContainingBlock)static const mozilla::FramePropertyDescriptor<AbsoluteContainingBlock
>* AbsoluteContainingBlockProperty() { static const auto descriptor
= mozilla::FramePropertyDescriptor<AbsoluteContainingBlock
>::NewWithDestructor<DeleteValue>(); return &descriptor
; }
268
269bool nsIFrame::HasAbsolutelyPositionedChildren() const {
270 const auto* absCB = GetAbsoluteContainingBlock();
271 return absCB && absCB->HasAbsoluteFrames();
272}
273
274AbsoluteContainingBlock* nsIFrame::GetAbsoluteContainingBlock() const {
275 if (!IsAbsoluteContainer()) {
276 return nullptr;
277 }
278 AbsoluteContainingBlock* absCB =
279 GetProperty(AbsoluteContainingBlockProperty());
280 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"
, 282); MOZ_PretendNoReturn(); } } while (0)
281 "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"
, 282); MOZ_PretendNoReturn(); } } while (0)
282 "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"
, 282); MOZ_PretendNoReturn(); } } while (0)
;
283 return absCB;
284}
285
286void nsIFrame::MarkAsAbsoluteContainingBlock() {
287 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", 287); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)"
")"); do { MOZ_CrashSequence(__null, 287); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
288 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"
, 289); MOZ_PretendNoReturn(); } } while (0)
289 "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"
, 289); MOZ_PretendNoReturn(); } } while (0)
;
290 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"
, 291); MOZ_PretendNoReturn(); } } while (0)
291 "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"
, 291); MOZ_PretendNoReturn(); } } while (0)
;
292 AddStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN);
293 SetProperty(AbsoluteContainingBlockProperty(), new AbsoluteContainingBlock());
294}
295
296void nsIFrame::MarkAsNotAbsoluteContainingBlock() {
297 NS_ASSERTION(!HasAbsolutelyPositionedChildren(), "Think of the children!")do { if (!(!HasAbsolutelyPositionedChildren())) { NS_DebugBreak
(NS_DEBUG_ASSERTION, "Think of the children!", "!HasAbsolutelyPositionedChildren()"
, "./../../../layout/generic/nsIFrame.cpp", 297); MOZ_PretendNoReturn
(); } } while (0)
;
298 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"
, 299); MOZ_PretendNoReturn(); } } while (0)
299 "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"
, 299); MOZ_PretendNoReturn(); } } while (0)
;
300 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"
, 301); MOZ_PretendNoReturn(); } } while (0)
301 "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"
, 301); MOZ_PretendNoReturn(); } } while (0)
;
302 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", 302); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)"
")"); do { MOZ_CrashSequence(__null, 302); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
303 RemoveStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN);
304 RemoveProperty(AbsoluteContainingBlockProperty());
305}
306
307bool nsIFrame::CheckAndClearPaintedState() {
308 bool result = HasAnyStateBits(NS_FRAME_PAINTED_THEBES);
309 RemoveStateBits(NS_FRAME_PAINTED_THEBES);
310
311 for (const auto& childList : ChildLists()) {
312 for (nsIFrame* child : childList.mList) {
313 if (child->CheckAndClearPaintedState()) {
314 result = true;
315 }
316 }
317 }
318 return result;
319}
320
321nsIFrame* nsIFrame::FindLineContainer() const {
322 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", 322); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsLineParticipant()" ")"); do { MOZ_CrashSequence
(__null, 322); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
323 nsIFrame* parent = GetParent();
324 while (parent &&
325 (parent->IsLineParticipant() || parent->CanContinueTextRun())) {
326 parent = parent->GetParent();
327 }
328 return parent;
329}
330
331bool nsIFrame::CheckAndClearDisplayListState() {
332 bool result = BuiltDisplayList();
333 SetBuiltDisplayList(false);
334
335 for (const auto& childList : ChildLists()) {
336 for (nsIFrame* child : childList.mList) {
337 if (child->CheckAndClearDisplayListState()) {
338 result = true;
339 }
340 }
341 }
342 return result;
343}
344
345bool nsIFrame::IsVisibleConsideringAncestors(uint32_t aFlags) const {
346 if (!StyleVisibility()->IsVisible() ||
347 HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
348 return false;
349 }
350
351 if (PresShell()->IsUnderHiddenEmbedderElement()) {
352 return false;
353 }
354
355 const nsIFrame* frame = this;
356 while (frame) {
357 // Checking mMozSubtreeHiddenOnlyVisually is relatively slow because it
358 // involves loading more memory. It's only allowed in chrome sheets so let's
359 // only support it in the parent process so we can mostly optimize this out
360 // in content processes.
361 if (XRE_IsParentProcess()) {
362 if (const nsMenuPopupFrame* popup = do_QueryFrame(frame);
363 popup && !popup->IsOpen()) {
364 return false;
365 }
366 if (frame->StyleUIReset()->mMozSubtreeHiddenOnlyVisually) {
367 return false;
368 }
369 }
370
371 // This method is used to determine if a frame is focusable, because it's
372 // called by nsIFrame::IsFocusable. `content-visibility: auto` should not
373 // force this frame to be unfocusable, so we only take into account
374 // `content-visibility: hidden` here.
375 if (this != frame &&
376 frame->HidesContent(IncludeContentVisibility::Hidden)) {
377 return false;
378 }
379
380 if (nsIFrame* parent = frame->GetParent()) {
381 frame = parent;
382 } else {
383 parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(frame);
384 if (!parent) {
385 break;
386 }
387
388 if ((aFlags & nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) == 0 &&
389 parent->PresContext()->IsChrome() &&
390 !frame->PresContext()->IsChrome()) {
391 break;
392 }
393
394 frame = parent;
395 }
396 }
397
398 return true;
399}
400
401void nsIFrame::FindCloserFrameForSelection(
402 const nsPoint& aPoint, FrameWithDistance* aCurrentBestFrame) {
403 if (nsLayoutUtils::PointIsCloserToRect(aPoint, mRect,
404 aCurrentBestFrame->mXDistance,
405 aCurrentBestFrame->mYDistance)) {
406 aCurrentBestFrame->mFrame = this;
407 }
408}
409
410void nsIFrame::ElementStateChanged(mozilla::dom::ElementState aStates) {}
411
412void WeakFrame::Clear(mozilla::PresShell* aPresShell) {
413 if (aPresShell) {
414 aPresShell->RemoveWeakFrame(this);
415 }
416 mFrame = nullptr;
417}
418
419AutoWeakFrame::AutoWeakFrame(const WeakFrame& aOther)
420 : mPrev(nullptr), mFrame(nullptr) {
421 Init(aOther.GetFrame());
422}
423
424void AutoWeakFrame::Clear(mozilla::PresShell* aPresShell) {
425 if (aPresShell) {
426 aPresShell->RemoveAutoWeakFrame(this);
427 }
428 mFrame = nullptr;
429 mPrev = nullptr;
430}
431
432AutoWeakFrame::~AutoWeakFrame() {
433 Clear(mFrame ? mFrame->PresShell() : nullptr);
434}
435
436void AutoWeakFrame::Init(nsIFrame* aFrame) {
437 Clear(mFrame ? mFrame->PresShell() : nullptr);
438 mFrame = aFrame;
439 if (mFrame) {
440 mozilla::PresShell* presShell = mFrame->PresShell();
441 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", 441)
; } } while (false)
;
442 if (presShell) {
443 presShell->AddAutoWeakFrame(this);
444 } else {
445 mFrame = nullptr;
446 }
447 }
448}
449
450void WeakFrame::Init(nsIFrame* aFrame) {
451 Clear(mFrame ? mFrame->PresShell() : nullptr);
452 mFrame = aFrame;
453 if (mFrame) {
454 mozilla::PresShell* presShell = mFrame->PresShell();
455 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", 455); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "presShell" ") (" "Null PresShell in WeakFrame!"
")"); do { MOZ_CrashSequence(__null, 455); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
456 if (presShell) {
457 presShell->AddWeakFrame(this);
458 } else {
459 mFrame = nullptr;
460 }
461 }
462}
463
464WeakFrame& WeakFrame::operator=(WeakFrame&& aOther) {
465 if (this != &aOther) {
466 Init(aOther.mFrame);
467 aOther.Clear(aOther.mFrame ? aOther.mFrame->PresShell() : nullptr);
468 }
469 return *this;
470}
471
472nsIFrame* NS_NewEmptyFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
473 return new (aPresShell) nsIFrame(aStyle, aPresShell->GetPresContext());
474}
475
476nsIFrame::~nsIFrame() {
477 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)
;
478
479 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"
, 480); AnnotateMozCrashReason("MOZ_ASSERT" "(" "GetVisibility() != Visibility::ApproximatelyVisible"
") (" "Visible nsIFrame is being destroyed" ")"); do { MOZ_CrashSequence
(__null, 480); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
480 "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"
, 480); AnnotateMozCrashReason("MOZ_ASSERT" "(" "GetVisibility() != Visibility::ApproximatelyVisible"
") (" "Visible nsIFrame is being destroyed" ")"); do { MOZ_CrashSequence
(__null, 480); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
481}
482
483NS_IMPL_FRAMEARENA_HELPERS(nsIFrame)void* nsIFrame ::operator new(size_t sz, mozilla::PresShell *
aShell) { return aShell->AllocateFrame(nsQueryFrame::nsIFrame_id
, sz); }
484
485// Dummy operator delete. Will never be called, but must be defined
486// to satisfy some C++ ABIs.
487void nsIFrame::operator delete(void*, size_t) {
488 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", 488); AnnotateMozCrashReason
("MOZ_CRASH(" "nsIFrame::operator delete should never be called"
")"); do { MOZ_CrashSequence(__null, 488); __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
489}
490
491NS_QUERYFRAME_HEAD(nsIFrame)void* nsIFrame ::QueryFrame(FrameIID id) const { switch (id) {
492 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)); }
493NS_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"
, 493); 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, 493); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false); return nullptr; }
494
495/////////////////////////////////////////////////////////////////////////////
496// nsIFrame
497
498static bool IsFontSizeInflationContainer(nsIFrame* aFrame,
499 const nsStyleDisplay* aStyleDisplay) {
500 /*
501 * Font size inflation is built around the idea that we're inflating
502 * the fonts for a pan-and-zoom UI so that when the user scales up a
503 * block or other container to fill the width of the device, the fonts
504 * will be readable. To do this, we need to pick what counts as a
505 * container.
506 *
507 * From a code perspective, the only hard requirement is that frames
508 * that are line participants (nsIFrame::IsLineParticipant) are never
509 * containers, since line layout assumes that the inflation is consistent
510 * within a line.
511 *
512 * This is not an imposition, since we obviously want a bunch of text
513 * (possibly with inline elements) flowing within a block to count the
514 * block (or higher) as its container.
515 *
516 * We also want form controls, including the text in the anonymous
517 * content inside of them, to match each other and the text next to
518 * them, so they and their anonymous content should also not be a
519 * container.
520 *
521 * However, because we can't reliably compute sizes across XUL during
522 * reflow, any XUL frame with a XUL parent is always a container.
523 *
524 * There are contexts where it would be nice if some blocks didn't
525 * count as a container, so that, for example, an indented quotation
526 * didn't end up with a smaller font size. However, it's hard to
527 * distinguish these situations where we really do want the indented
528 * thing to count as a container, so we don't try, and blocks are
529 * always containers.
530 */
531
532 // The root frame should always be an inflation container.
533 if (!aFrame->GetParent()) {
534 return true;
535 }
536
537 nsIContent* content = aFrame->GetContent();
538 if (content && content->IsInNativeAnonymousSubtree()) {
539 // Native anonymous content shouldn't be a font inflation root,
540 // except for the custom content container.
541 return content ==
542 aFrame->PresContext()->Document()->GetCustomContentContainer();
543 }
544
545 LayoutFrameType frameType = aFrame->Type();
546 bool isInline =
547 aFrame->GetDisplay().IsInlineFlow() || RubyUtils::IsRubyBox(frameType) ||
548 (aStyleDisplay->IsFloatingStyle() &&
549 frameType == LayoutFrameType::Letter) ||
550 // Given multiple frames for the same node, only the
551 // outer one should be considered a container.
552 // (Important, e.g., for scrolled frames.)
553 (aFrame->GetParent()->GetContent() == content) ||
554 (content &&
555 // Form controls shouldn't become inflation containers.
556 (content->IsAnyOfHTMLElements(nsGkAtoms::option, nsGkAtoms::optgroup,
557 nsGkAtoms::select, nsGkAtoms::input,
558 nsGkAtoms::button, nsGkAtoms::textarea)));
559 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", 564); MOZ_PretendNoReturn
(); } } while (0)
560 // 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", 564); MOZ_PretendNoReturn
(); } } while (0)
561 // 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", 564); MOZ_PretendNoReturn
(); } } while (0)
562 // 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", 564); MOZ_PretendNoReturn
(); } } while (0)
563 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", 564); MOZ_PretendNoReturn
(); } } while (0)
564 "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", 564); MOZ_PretendNoReturn
(); } } while (0)
;
565 return !isInline;
566}
567
568static void MaybeScheduleReflowSVGNonDisplayText(nsIFrame* aFrame) {
569 if (!aFrame->IsInSVGTextSubtree()) {
570 return;
571 }
572
573 // We need to ensure that any non-display SVGTextFrames get reflowed when a
574 // child text frame gets new style. Thus we need to schedule a reflow in
575 // |DidSetComputedStyle|. We also need to call it from |DestroyFrom|,
576 // because otherwise we won't get notified when style changes to
577 // "display:none".
578 SVGTextFrame* svgTextFrame = static_cast<SVGTextFrame*>(
579 nsLayoutUtils::GetClosestFrameOfType(aFrame, LayoutFrameType::SVGText));
580 nsIFrame* anonBlock = svgTextFrame->PrincipalChildList().FirstChild();
581
582 // Note that we must check NS_FRAME_FIRST_REFLOW on our SVGTextFrame's
583 // anonymous block frame rather than our aFrame, since NS_FRAME_FIRST_REFLOW
584 // may be set on us if we're a new frame that has been inserted after the
585 // document's first reflow. (In which case this DidSetComputedStyle call may
586 // be happening under frame construction under a Reflow() call.)
587 if (!anonBlock || anonBlock->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
588 return;
589 }
590
591 if (!svgTextFrame->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY) ||
592 svgTextFrame->HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW)) {
593 return;
594 }
595 if (SVGUtils::AnyOuterSVGIsCallingReflowSVG(svgTextFrame)) {
596 return;
597 }
598
599 svgTextFrame->ScheduleReflowSVGNonDisplayText(
600 IntrinsicDirty::FrameAncestorsAndDescendants);
601}
602
603bool nsIFrame::IsReplaced() const {
604 if (HasAnyClassFlag(ClassFlags::Replaced)) {
605 return true;
606 }
607 if (!Style()->IsAnonBox() && mContent->IsHTMLElement(nsGkAtoms::button)) {
608 // Button always behaves as a replaced element.
609 return true;
610 }
611 return false;
612}
613
614bool nsIFrame::IsAtomicInline() const {
615 // See: https://drafts.csswg.org/css-display-4/#atomic-inline
616 return IsInlineOutside() &&
617 (IsReplaced() || !StyleDisplay()->IsInlineInsideStyle());
618}
619
620bool nsIFrame::ShouldPropagateRepaintsToRoot() const {
621 if (!IsPrimaryFrame()) {
622 // special case for table frames because style images are associated to the
623 // table frame, but the table wrapper frame is the primary frame
624 if (IsTableFrame()) {
625 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", 625); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "GetParent() && GetParent()->IsTableWrapperFrame()"
")"); do { MOZ_CrashSequence(__null, 625); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
626 return GetParent()->ShouldPropagateRepaintsToRoot();
627 }
628
629 return false;
630 }
631 nsIContent* content = GetContent();
632 Document* document = content->OwnerDoc();
633 return content == document->GetRootElement() ||
634 content == document->GetBodyElement();
635}
636
637bool nsIFrame::IsRenderedLegend() const {
638 if (auto* parent = GetParent(); parent && parent->IsFieldSetFrame()) {
639 return static_cast<nsFieldSetFrame*>(parent)->GetLegend() == this;
640 }
641 return false;
642}
643
644void nsIFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
645 nsIFrame* aPrevInFlow) {
646 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", 646); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "nsQueryFrame::FrameIID(mClass) == GetFrameId()"
")"); do { MOZ_CrashSequence(__null, 646); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
647 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", 647); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!mContent" ") (" "Double-initing a frame?"
")"); do { MOZ_CrashSequence(__null, 647); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
648
649 mContent = aContent;
650 mParent = aParent;
651 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", 651); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!mParent || PresShell() == mParent->PresShell()"
")"); do { MOZ_CrashSequence(__null, 651); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
652
653 if (aPrevInFlow) {
654 mWritingMode = aPrevInFlow->GetWritingMode();
655
656 // Copy some state bits from prev-in-flow (the bits that should apply
657 // throughout a continuation chain). The bits are sorted according to their
658 // order in nsFrameStateBits.h.
659
660 // clang-format off
661 AddStateBits(aPrevInFlow->GetStateBits() &
662 (NS_FRAME_GENERATED_CONTENT |
663 NS_FRAME_OUT_OF_FLOW |
664 NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN |
665 NS_FRAME_PART_OF_IBSPLIT |
666 NS_FRAME_MAY_BE_TRANSFORMED |
667 NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR));
668 // clang-format on
669
670 // Copy other bits in nsIFrame from prev-in-flow.
671 mHasColumnSpanSiblings = aPrevInFlow->HasColumnSpanSiblings();
672
673 // If our prev-in-flow is an absolute containing block, we must be one, too.
674 if (aPrevInFlow->IsAbsoluteContainer()) {
675 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", 676); 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, 676); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
676 "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", 676); 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, 676); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
677 MarkAsAbsoluteContainingBlock();
678 }
679 } else {
680 PresContext()->ConstructedFrame();
681 }
682
683 if (GetParent()) {
684 if (MOZ_UNLIKELY(mContent == PresContext()->Document()->GetRootElement() &&(__builtin_expect(!!(mContent == PresContext()->Document()
->GetRootElement() && mContent == GetParent()->
GetContent()), 0))
685 mContent == GetParent()->GetContent())(__builtin_expect(!!(mContent == PresContext()->Document()
->GetRootElement() && mContent == GetParent()->
GetContent()), 0))
) {
686 // Our content is the root element and we have the same content as our
687 // parent. That is, we are the internal anonymous frame of the root
688 // element. Copy the used mWritingMode from our parent because
689 // mDocElementContainingBlock gets its mWritingMode from <body>.
690 mWritingMode = GetParent()->GetWritingMode();
691 }
692
693 // Copy some state bits from our parent (the bits that should apply
694 // recursively throughout a subtree). The bits are sorted according to their
695 // order in nsFrameStateBits.h.
696
697 // clang-format off
698 AddStateBits(GetParent()->GetStateBits() &
699 (NS_FRAME_GENERATED_CONTENT |
700 NS_FRAME_IS_SVG_TEXT |
701 NS_FRAME_IN_POPUP |
702 NS_FRAME_IS_NONDISPLAY));
703 // clang-format on
704
705 if (HasAnyStateBits(NS_FRAME_IN_POPUP) && TrackingVisibility()) {
706 // Assume all frames in popups are visible.
707 IncApproximateVisibleCount();
708 }
709 }
710 if (aPrevInFlow) {
711 mMayHaveOpacityAnimation = aPrevInFlow->MayHaveOpacityAnimation();
712 mMayHaveTransformAnimation = aPrevInFlow->MayHaveTransformAnimation();
713 } else if (mContent) {
714 // It's fine to fetch the EffectSet for the style frame here because in the
715 // following code we take care of the case where animations may target
716 // a different frame.
717 EffectSet* effectSet = EffectSet::GetForStyleFrame(this);
718 if (effectSet) {
719 mMayHaveOpacityAnimation = effectSet->MayHaveOpacityAnimation();
720
721 if (effectSet->MayHaveTransformAnimation()) {
722 // If we are the inner table frame for display:table content, then
723 // transform animations should go on our parent frame (the table wrapper
724 // frame).
725 //
726 // We do this when initializing the child frame (table inner frame),
727 // because when initializng the table wrapper frame, we don't yet have
728 // access to its children so we can't tell if we have transform
729 // animations or not.
730 if (SupportsCSSTransforms()) {
731 mMayHaveTransformAnimation = true;
732 AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
733 } else if (aParent && nsLayoutUtils::GetStyleFrame(aParent) == this) {
734 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", 737
); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aParent->SupportsCSSTransforms()"
") (" "Style frames that don't support transforms should have parents"
" that do" ")"); do { MOZ_CrashSequence(__null, 737); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
735 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", 737
); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aParent->SupportsCSSTransforms()"
") (" "Style frames that don't support transforms should have parents"
" that do" ")"); do { MOZ_CrashSequence(__null, 737); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
736 "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", 737
); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aParent->SupportsCSSTransforms()"
") (" "Style frames that don't support transforms should have parents"
" that do" ")"); do { MOZ_CrashSequence(__null, 737); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
737 " 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", 737
); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aParent->SupportsCSSTransforms()"
") (" "Style frames that don't support transforms should have parents"
" that do" ")"); do { MOZ_CrashSequence(__null, 737); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
;
738 aParent->mMayHaveTransformAnimation = true;
739 aParent->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
740 }
741 }
742 }
743 }
744
745 const nsStyleDisplay* disp = StyleDisplay();
746 if (disp->HasTransform(this)) {
747 // If 'transform' dynamically changes, RestyleManager takes care of
748 // updating this bit.
749 AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
750 }
751
752 if (nsLayoutUtils::FontSizeInflationEnabled(PresContext()) ||
753 !GetParent()
754#ifdef DEBUG1
755 // We have assertions that check inflation invariants even when
756 // font size inflation is not enabled.
757 || true
758#endif
759 ) {
760 if (IsFontSizeInflationContainer(this, disp)) {
761 AddStateBits(NS_FRAME_FONT_INFLATION_CONTAINER);
762 if (!GetParent() ||
763 // I'd use NS_FRAME_OUT_OF_FLOW, but it's not set yet.
764 disp->IsFloating(this) || disp->IsAbsolutelyPositioned(this) ||
765 GetParent()->IsFlexContainerFrame() ||
766 GetParent()->IsGridContainerFrame()) {
767 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
768 }
769 }
770 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", 772); MOZ_PretendNoReturn
(); } } while (0)
771 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", 772); MOZ_PretendNoReturn
(); } } while (0)
772 "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", 772); MOZ_PretendNoReturn
(); } } while (0)
;
773 }
774
775 if (TrackingVisibility() && PresShell()->AssumeAllFramesVisible()) {
776 IncApproximateVisibleCount();
777 }
778
779 DidSetComputedStyle(nullptr);
780
781 // For a newly created frame, we need to update this frame's visibility state.
782 // Usually we update the state when the frame is restyled and has a
783 // VisibilityChange change hint but we don't generate any change hints for
784 // newly created frames.
785 // Note: We don't need to do this for placeholders since placeholders have
786 // different styles so that the styles don't have visibility:hidden even if
787 // the parent has visibility:hidden style. We also don't need to update the
788 // state when creating continuations because its visibility is the same as its
789 // prev-in-flow, and the animation code cares only primary frames.
790 if (!IsPlaceholderFrame() && !aPrevInFlow) {
791 UpdateVisibleDescendantsState();
792 }
793
794 if (!aPrevInFlow && HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
795 // We aren't going to get a reflow, so nothing else will call
796 // InvalidateRenderingObservers, we have to do it here.
797 SVGObserverUtils::InvalidateRenderingObservers(this);
798 }
799}
800
801void nsIFrame::InitPrimaryFrame() {
802 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", 802); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsPrimaryFrame()" ")"); do { MOZ_CrashSequence
(__null, 802); __attribute__((nomerge)) ::abort(); } while (false
); } } while (false)
;
803 HandlePrimaryFrameStyleChange(nullptr);
804}
805
806// If `aFrame` is a generated content container (::before/::after/::marker/...)
807// whose content changed only in the text of its string items, rewrite the
808// existing generated text nodes in place instead of reconstructing the frame.
809// See nsStyleContent::CanUpdateGeneratedContentText and its usage.
810static void UpdateGeneratedContentTextIfNeeded(
811 nsIFrame* aFrame, const ComputedStyle* aOldComputedStyle) {
812 if (!aFrame->IsGeneratedContentFrame()) {
813 return;
814 }
815 // Update once per element, not per continuation.
816 if (aFrame->GetPrevContinuation()) {
817 return;
818 }
819 nsIContent* content = aFrame->GetContent();
820 if (!content || !content->IsRootOfNativeAnonymousSubtree()) {
821 return;
822 }
823
824 const auto* oldStyle = aOldComputedStyle->StyleContent();
825 const auto* newStyle = aFrame->StyleContent();
826 if (oldStyle->mContent == newStyle->mContent ||
827 !nsStyleContent::CanUpdateGeneratedContentText(*oldStyle, *newStyle)) {
828 return;
829 }
830
831 auto oldItems = oldStyle->NonAltContentItems();
832 auto newItems = newStyle->NonAltContentItems();
833 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", 834); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "oldItems.Length() == newItems.Length()" ") ("
"CanUpdateGeneratedContentText should guarantee equal lengths"
")"); do { MOZ_CrashSequence(__null, 834); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
834 "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", 834); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "oldItems.Length() == newItems.Length()" ") ("
"CanUpdateGeneratedContentText should guarantee equal lengths"
")"); do { MOZ_CrashSequence(__null, 834); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
835 nsIContent* child = content->GetFirstChild();
836 for (size_t i = 0; i < newItems.Length(); i++) {
837 const auto& item = newItems[i];
838 const bool isString = item.IsString();
839 if (isString && item.AsString().AsString().IsEmpty()) {
840 // Empty strings have no associated content node.
841 continue;
842 }
843 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", 844); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "child" ") (" "Mismatch between content items and generated content nodes?"
")"); do { MOZ_CrashSequence(__null, 844); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
844 "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", 844); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "child" ") (" "Mismatch between content items and generated content nodes?"
")"); do { MOZ_CrashSequence(__null, 844); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
845 if (isString && item != oldItems[i]) {
846 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", 847); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "child->IsText()" ") (" "A non-empty string item should map to a text node"
")"); do { MOZ_CrashSequence(__null, 847); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
847 "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", 847); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "child->IsText()" ") (" "A non-empty string item should map to a text node"
")"); do { MOZ_CrashSequence(__null, 847); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
848 child->AsText()->SetText(
849 NS_ConvertUTF8toUTF16(item.AsString().AsString()),
850 /* aNotify = */ true);
851 }
852 child = child->GetNextSibling();
853 }
854 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", 854); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!child" ") (" "More generated content than content items."
")"); do { MOZ_CrashSequence(__null, 854); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
855}
856
857void nsIFrame::HandlePrimaryFrameStyleChange(ComputedStyle* aOldStyle) {
858 const nsStyleDisplay* disp = StyleDisplay();
859 const nsStyleDisplay* oldDisp =
860 aOldStyle ? aOldStyle->StyleDisplay() : nullptr;
861
862 const bool wasQueryContainer = oldDisp && oldDisp->IsQueryContainer();
863 const bool isQueryContainer = disp->IsQueryContainer();
864 if (wasQueryContainer != isQueryContainer) {
865 auto* pc = PresContext();
866 if (isQueryContainer) {
867 pc->RegisterContainerQueryFrame(this);
868 } else {
869 pc->UnregisterContainerQueryFrame(this);
870 }
871 }
872
873 const bool wasReferringToAnchor = aOldStyle &&
874 oldDisp->IsAbsolutelyPositionedStyle() &&
875 aOldStyle->HasAnchorPosReference();
876 const bool isReferringToAnchor = HasAnchorPosReference();
877 if (wasReferringToAnchor && !isReferringToAnchor) {
878 PresShell()->RemoveAnchorPosPositioned(this);
879 RemoveProperty(NormalPositionProperty());
880 } else if (!wasReferringToAnchor && isReferringToAnchor) {
881 PresShell()->AddAnchorPosPositioned(this);
882 }
883
884 bool handleAnchorPosAnchorNameChange =
885 oldDisp ? oldDisp->mAnchorName != disp->mAnchorName
886 : disp->HasAnchorName();
887 if (handleAnchorPosAnchorNameChange &&
888 !HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
889 if (oldDisp && oldDisp->HasAnchorName()) {
890 PresShell()->RemoveAnchorPosAnchor(oldDisp->mAnchorName.AsSpan(), this);
891 }
892 PresShell()->AddAnchorPosAnchor(disp->mAnchorName.AsSpan(), this);
893 }
894
895 // According to the Anchor Positioning spec,
896 // https://drafts.csswg.org/css-anchor-position-1/#last-successful-position-option:
897 //
898 // If el has a last successful position option remove its last successful
899 // position option if any of the following are true:
900 // 1. Its computed position value has changed, its containing block has
901 // changed, or it no longer generates a box.
902 // 2. Its computed value for any longhand of position-try has changed.
903 // 3. Its computed value for any @position-try property has changed.
904 // 4. Any of the @position-try rules referenced by it have been added,
905 // removed, or mutated.
906 //
907 // Case 1 will cause a reframe, so does not need to be handled here.
908 // Cases 2 and 3 are handled here.
909 // TODO: case 4, see bug 1987960.
910 if (aOldStyle && HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) &&
911 HasProperty(LastSuccessfulPositionFallback())) {
912 const auto* pos = StylePosition();
913 const auto* oldPos = aOldStyle->StylePosition();
914 if (pos->mPositionTryFallbacks != oldPos->mPositionTryFallbacks ||
915 pos->mPositionTryOrder != oldPos->mPositionTryOrder ||
916 pos->mOffset != oldPos->mOffset ||
917 pos->mAlignSelf != oldPos->mAlignSelf ||
918 pos->mJustifySelf != oldPos->mJustifySelf ||
919 pos->mPositionAnchor != oldPos->mPositionAnchor ||
920 pos->mPositionArea != oldPos->mPositionArea ||
921 pos->mMinWidth != oldPos->mMinWidth ||
922 pos->mMinHeight != oldPos->mMinHeight ||
923 pos->mMaxWidth != oldPos->mMaxWidth ||
924 pos->mMaxHeight != oldPos->mMaxHeight ||
925 pos->mWidth != oldPos->mWidth || pos->mHeight != oldPos->mHeight ||
926 StyleMargin()->mMargin != aOldStyle->StyleMargin()->mMargin) {
927 RemoveProperty(LastSuccessfulPositionFallback());
928 }
929 }
930
931 const auto cv = disp->ContentVisibility(*this);
932 const auto oldCv = oldDisp ? oldDisp->ContentVisibility(*this)
933 : StyleContentVisibility::Visible;
934 if (!oldDisp || cv != oldCv) {
935 // 'content-visibility' has changed (or this is a new frame being
936 // initialized). Inform PresShell so it can update its bookkeeping
937 // about frames with particular content-visibility values:
938 // * Inform it of the new value (for the values that it cares about):
939 if (cv == StyleContentVisibility::Hidden) {
940 PresShell()->IncrementContentVisibilityHiddenCount();
941 } else if (cv == StyleContentVisibility::Auto) {
942 PresShell()->RegisterContentVisibilityAutoFrame(this);
943 }
944
945 // * Inform it of the old value (for the values that it cares about):
946 if (oldCv == StyleContentVisibility::Hidden) {
947 PresShell()->DecrementContentVisibilityHiddenCount();
948 } else if (oldCv == StyleContentVisibility::Auto) {
949 if (auto* element = Element::FromNodeOrNull(GetContent())) {
950 element->ClearContentRelevancy();
951 }
952 PresShell()->UnregisterContentVisibilityAutoFrame(this);
953 }
954 PresContext()->SetNeedsToUpdateHiddenByContentVisibilityForAnimations();
955 }
956
957 HandleLastRememberedSize();
958
959 bool handleStickyChange =
960 oldDisp ? (disp->mPosition != oldDisp->mPosition &&
961 (disp->mPosition == StylePositionProperty::Sticky ||
962 oldDisp->mPosition == StylePositionProperty::Sticky))
963 : disp->mPosition == StylePositionProperty::Sticky;
964 if (handleStickyChange &&
965 !HasAnyStateBits(NS_FRAME_IS_NONDISPLAY | NS_FRAME_SVG_LAYOUT)) {
966 if (auto* ssc = StickyScrollContainer::GetOrCreateForFrame(this)) {
967 if (IsStickyPositioned()) {
968 ssc->AddFrame(this);
969 } else {
970 ssc->RemoveFrame(this);
971 }
972 }
973 }
974}
975
976void nsIFrame::Destroy(DestroyContext& aContext) {
977 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"
, 978); MOZ_PretendNoReturn(); } } while (0)
978 "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"
, 978); MOZ_PretendNoReturn(); } } while (0)
;
979 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"
, 980); MOZ_PretendNoReturn(); } } while (0)
980 "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"
, 980); MOZ_PretendNoReturn(); } } while (0)
;
981 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"
, 981); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!HasAbsolutelyPositionedChildren()"
")"); do { MOZ_CrashSequence(__null, 981); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
982 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", 983); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)"
") (" "NS_FRAME_PART_OF_IBSPLIT set on non-nsContainerFrame?"
")"); do { MOZ_CrashSequence(__null, 983); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
983 "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", 983); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)"
") (" "NS_FRAME_PART_OF_IBSPLIT set on non-nsContainerFrame?"
")"); do { MOZ_CrashSequence(__null, 983); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
984
985 MaybeScheduleReflowSVGNonDisplayText(this);
986
987 SVGObserverUtils::InvalidateDirectRenderingObservers(
988 this, SVGObserverUtils::InvalidationFlag::FrameBeingDestroyed);
989
990 if (IsStickyPositioned()) {
991 if (auto* ssc = StickyScrollContainer::GetForFrame(this)) {
992 ssc->RemoveFrame(this);
993 }
994 }
995
996 if (HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
997 if (nsPlaceholderFrame* placeholder = GetPlaceholderFrame()) {
998 placeholder->SetOutOfFlowFrame(nullptr);
999 }
1000 }
1001
1002 nsPresContext* pc = PresContext();
1003 mozilla::PresShell* ps = pc->GetPresShell();
1004 const auto* disp = StyleDisplay();
1005 if (IsPrimaryFrame()) {
1006 if (disp->IsQueryContainer()) {
1007 pc->UnregisterContainerQueryFrame(this);
1008 }
1009 auto contentVisibility = disp->ContentVisibility(*this);
1010 if (contentVisibility == StyleContentVisibility::Hidden) {
1011 ps->DecrementContentVisibilityHiddenCount();
1012 } else if (contentVisibility == StyleContentVisibility::Auto) {
1013 ps->UnregisterContentVisibilityAutoFrame(this);
1014 }
1015 // This needs to happen before we clear our Properties() table.
1016 ActiveLayerTracker::TransferActivityToContent(this, mContent);
1017 }
1018
1019 ScrollAnchorContainer* anchor = nullptr;
1020 if (IsScrollAnchor(&anchor)) {
1021 anchor->InvalidateAnchor();
1022 }
1023
1024 if (HasCSSAnimations() || HasCSSTransitions() ||
1025 // It's fine to look up the style frame here since if we're destroying the
1026 // frames for display:table content we should be destroying both wrapper
1027 // and inner frame.
1028 EffectSet::GetForStyleFrame(this)) {
1029 // If no new frame for this element is created by the end of the
1030 // restyling process, stop animations and transitions for this frame
1031 RestyleManager::AnimationsWithDestroyedFrame* adf =
1032 pc->RestyleManager()->GetAnimationsWithDestroyedFrame();
1033 // AnimationsWithDestroyedFrame only lives during the restyling process.
1034 if (adf) {
1035 adf->Put(mContent, mComputedStyle);
1036 }
1037 }
1038
1039 if (HasAnchorPosName()) {
1040 PresShell()->RemoveAnchorPosAnchor(disp->mAnchorName.AsSpan(), this);
1041 }
1042
1043 if (HasAnchorPosReference()) {
1044 ps->RemoveAnchorPosPositioned(this);
1045 }
1046
1047 // Disable visibility tracking. Note that we have to do this before we clear
1048 // frame properties and lose track of whether we were previously visible.
1049 // XXX(seth): It'd be ideal to assert that we're already marked nonvisible
1050 // here, but it's unfortunately tricky to guarantee in the face of things like
1051 // frame reconstruction induced by style changes.
1052 DisableVisibilityTracking();
1053
1054 // Ensure that we're not in the approximately visible list anymore.
1055 ps->RemoveFrameFromApproximatelyVisibleList(this);
1056
1057 ps->NotifyDestroyingFrame(this);
1058
1059 if (HasAnyStateBits(NS_FRAME_EXTERNAL_REFERENCE)) {
1060 ps->ClearFrameRefs(this);
1061 }
1062
1063 // Make sure that our deleted frame can't be returned from GetPrimaryFrame()
1064 if (IsPrimaryFrame()) {
1065 mContent->SetPrimaryFrame(nullptr);
1066
1067 // Pass the root of a generated content subtree (e.g. ::after/::before) to
1068 // aPostDestroyData to unbind it after frame destruction is done.
1069 if (HasAnyStateBits(NS_FRAME_GENERATED_CONTENT) &&
1070 mContent->IsRootOfNativeAnonymousSubtree()) {
1071 aContext.AddAnonymousContent(mContent.forget());
1072 }
1073 }
1074
1075 // Remove all properties attached to the frame, to ensure any property
1076 // destructors that need the frame pointer are handled properly.
1077 RemoveAllProperties();
1078
1079 // Must retrieve the object ID before calling destructors, so the
1080 // vtable is still valid.
1081 //
1082 // Note to future tweakers: having the method that returns the
1083 // object size call the destructor will not avoid an indirect call;
1084 // the compiler cannot devirtualize the call to the destructor even
1085 // if it's from a method defined in the same class.
1086
1087 nsQueryFrame::FrameIID id = GetFrameId();
1088 this->~nsIFrame();
1089
1090#ifdef DEBUG1
1091 {
1092 nsIFrame* rootFrame = ps->GetRootFrame();
1093 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"
, 1093); AnnotateMozCrashReason("MOZ_ASSERT" "(" "rootFrame" ")"
); do { MOZ_CrashSequence(__null, 1093); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1094 if (this != rootFrame) {
1095 auto* builder = nsLayoutUtils::GetRetainedDisplayListBuilder(rootFrame);
1096 auto* data = builder ? builder->Data() : nullptr;
1097
1098 const bool inData =
1099 data && (data->IsModified(this) || data->HasProps(this));
1100
1101 if (inData) {
1102 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)
;
1103 }
1104
1105 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", 1105); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!inData" ") (" "Deleted frame in retained data!"
")"); do { MOZ_CrashSequence(__null, 1105); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1106 }
1107 }
1108#endif
1109
1110 // Now that we're totally cleaned out, we need to add ourselves to
1111 // the presshell's recycler.
1112 ps->FreeFrame(id, this);
1113}
1114
1115std::pair<int32_t, int32_t> nsIFrame::GetOffsets() const {
1116 return std::make_pair(0, 0);
1117}
1118
1119static void CompareLayers(
1120 const nsStyleImageLayers* aFirstLayers,
1121 const nsStyleImageLayers* aSecondLayers,
1122 const std::function<void(imgRequestProxy* aReq)>& aCallback) {
1123 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, (*aFirstLayers))for (uint32_t i = ((*aFirstLayers)).mImageCount; (i)-- != 0;) {
1124 const auto& image = aFirstLayers->mLayers[i].mImage;
1125 if (!image.IsImageRequestType() || !image.IsResolved()) {
1126 continue;
1127 }
1128
1129 // aCallback is called when the style image in aFirstLayers is thought to
1130 // be different with the corresponded one in aSecondLayers
1131 if (!aSecondLayers || i >= aSecondLayers->mImageCount ||
1132 (!aSecondLayers->mLayers[i].mImage.IsResolved() ||
1133 image.GetImageRequest() !=
1134 aSecondLayers->mLayers[i].mImage.GetImageRequest())) {
1135 if (imgRequestProxy* req = image.GetImageRequest()) {
1136 aCallback(req);
1137 }
1138 }
1139 }
1140}
1141
1142static void AddAndRemoveImageAssociations(
1143 ImageLoader& aImageLoader, nsIFrame* aFrame,
1144 const nsStyleImageLayers* aOldLayers,
1145 const nsStyleImageLayers* aNewLayers) {
1146 // If the old context had a background-image image, or mask-image image,
1147 // and new context does not have the same image, clear the image load
1148 // notifier (which keeps the image loading, if it still is) for the frame.
1149 // We want to do this conservatively because some frames paint their
1150 // backgrounds from some other frame's style data, and we don't want
1151 // to clear those notifiers unless we have to. (They'll be reset
1152 // when we paint, although we could miss a notification in that
1153 // interval.)
1154 if (aOldLayers && aFrame->HasImageRequest()) {
1155 CompareLayers(aOldLayers, aNewLayers, [&](imgRequestProxy* aReq) {
1156 aImageLoader.DisassociateRequestFromFrame(aReq, aFrame);
1157 });
1158 }
1159
1160 CompareLayers(aNewLayers, aOldLayers, [&](imgRequestProxy* aReq) {
1161 aImageLoader.AssociateRequestToFrame(aReq, aFrame);
1162 });
1163}
1164
1165void nsIFrame::AddDisplayItem(nsDisplayItem* aItem) {
1166 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"
, 1166); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "!mDisplayItems.Contains(aItem)"
")"); do { MOZ_CrashSequence(__null, 1166); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1167 mDisplayItems.AppendElement(aItem);
1168#ifdef ACCESSIBILITY1
1169 if (nsAccessibilityService* accService = GetAccService()) {
1170 accService->NotifyOfPossibleBoundsChange(PresShell(), mContent);
1171 }
1172#endif
1173}
1174
1175bool nsIFrame::RemoveDisplayItem(nsDisplayItem* aItem) {
1176 return mDisplayItems.RemoveElement(aItem);
1177}
1178
1179bool nsIFrame::HasDisplayItems() { return !mDisplayItems.IsEmpty(); }
1180
1181bool nsIFrame::HasDisplayItem(nsDisplayItem* aItem) {
1182 return mDisplayItems.Contains(aItem);
1183}
1184
1185bool nsIFrame::HasDisplayItem(uint32_t aKey) {
1186 for (nsDisplayItem* i : mDisplayItems) {
1187 if (i->GetPerFrameKey() == aKey) {
1188 return true;
1189 }
1190 }
1191 return false;
1192}
1193
1194template <typename Condition>
1195static void DiscardDisplayItems(nsIFrame* aFrame, Condition aCondition) {
1196 for (nsDisplayItem* i : aFrame->DisplayItems()) {
1197 // Only discard items that are invalidated by this frame, as we're only
1198 // guaranteed to rebuild those items. Table background items are created by
1199 // the relevant table part, but have the cell frame as the primary frame,
1200 // and we don't want to remove them if this is the cell.
1201 if (aCondition(i) && i->FrameForInvalidation() == aFrame) {
1202 i->SetCantBeReused();
1203 }
1204 }
1205}
1206
1207static void DiscardOldItems(nsIFrame* aFrame) {
1208 DiscardDisplayItems(aFrame,
1209 [](nsDisplayItem* aItem) { return aItem->IsOldItem(); });
1210}
1211
1212void nsIFrame::RemoveDisplayItemDataForDeletion() {
1213 // Destroying a WebRenderUserDataTable can cause destruction of other objects
1214 // which can remove frame properties in their destructor. If we delete a frame
1215 // property it runs the destructor of the stored object in the middle of
1216 // updating the frame property table, so if the destruction of that object
1217 // causes another update to the frame property table it would leave the frame
1218 // property table in an inconsistent state. So we remove it from the table and
1219 // then destroy it. (bug 1530657)
1220 WebRenderUserDataTable* userDataTable =
1221 TakeProperty(WebRenderUserDataProperty::Key());
1222 if (userDataTable) {
1223 for (const auto& data : userDataTable->Values()) {
1224 data->RemoveFromTable();
1225 }
1226 delete userDataTable;
1227 }
1228
1229 auto* builder = nsLayoutUtils::GetRetainedDisplayListBuilder(this);
1230 if (!builder) {
1231 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", 1231); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "DisplayItems().IsEmpty()" ")"); do { MOZ_CrashSequence
(__null, 1231); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1232 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", 1232); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsFrameModified()" ")"); do { MOZ_CrashSequence
(__null, 1232); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1233 return;
1234 }
1235
1236 for (nsDisplayItem* i : DisplayItems()) {
1237 if (i->GetDependentFrame() == this && !i->HasDeletedFrame()) {
1238 i->Frame()->MarkNeedsDisplayItemRebuild();
1239 }
1240 i->RemoveFrame(this);
1241 }
1242
1243 DisplayItems().Clear();
1244
1245 nsAutoString name;
1246#ifdef DEBUG_FRAME_DUMP1
1247 if (DL_LOG_TEST(LogLevel::Debug)(__builtin_expect(!!(mozilla::detail::log_test(GetLoggerByProcess
(), LogLevel::Debug)), 0))
) {
1248 GetFrameName(name);
1249 }
1250#endif
1251 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)
1252 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)
;
1253
1254 builder->Data()->Remove(this);
1255}
1256
1257void nsIFrame::MarkNeedsDisplayItemRebuild() {
1258 if (!nsLayoutUtils::AreRetainedDisplayListsEnabled() || IsFrameModified() ||
1259 HasAnyStateBits(NS_FRAME_IN_POPUP)) {
1260 // Skip frames that are already marked modified.
1261 return;
1262 }
1263
1264 if (Type() == LayoutFrameType::Placeholder) {
1265 nsIFrame* oof = static_cast<nsPlaceholderFrame*>(this)->GetOutOfFlowFrame();
1266 if (oof) {
1267 oof->MarkNeedsDisplayItemRebuild();
1268 }
1269 // Do not mark placeholder frames modified.
1270 return;
1271 }
1272
1273#ifdef ACCESSIBILITY1
1274 if (nsAccessibilityService* accService = GetAccService()) {
1275 accService->NotifyOfPossibleBoundsChange(PresShell(), mContent);
1276 }
1277#endif
1278
1279 nsIFrame* rootFrame = PresShell()->GetRootFrame();
1280
1281 if (rootFrame->IsFrameModified()) {
1282 // The whole frame tree is modified.
1283 return;
1284 }
1285
1286 auto* builder = nsLayoutUtils::GetRetainedDisplayListBuilder(this);
1287 if (!builder) {
1288 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", 1288); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "DisplayItems().IsEmpty()" ")"); do { MOZ_CrashSequence
(__null, 1288); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1289 return;
1290 }
1291
1292 RetainedDisplayListData* data = builder->Data();
1293 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"
, 1293); AnnotateMozCrashReason("MOZ_ASSERT" "(" "data" ")");
do { MOZ_CrashSequence(__null, 1293); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1294
1295 if (data->AtModifiedFrameLimit()) {
1296 // This marks the whole frame tree modified.
1297 // See |RetainedDisplayListBuilder::ShouldBuildPartial()|.
1298 data->AddModifiedFrame(rootFrame);
1299 return;
1300 }
1301
1302 nsAutoString name;
1303#ifdef DEBUG_FRAME_DUMP1
1304 if (DL_LOG_TEST(LogLevel::Debug)(__builtin_expect(!!(mozilla::detail::log_test(GetLoggerByProcess
(), LogLevel::Debug)), 0))
) {
1305 GetFrameName(name);
1306 }
1307#endif
1308
1309 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)
1310 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)
;
1311
1312 data->AddModifiedFrame(this);
1313
1314 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", 1315); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "PresContext()->LayoutPhaseCount(nsLayoutPhase::DisplayListBuilding) == 0"
")"); do { MOZ_CrashSequence(__null, 1315); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1315 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", 1315); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "PresContext()->LayoutPhaseCount(nsLayoutPhase::DisplayListBuilding) == 0"
")"); do { MOZ_CrashSequence(__null, 1315); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1316
1317 // Hopefully this is cheap, but we could use a frame state bit to note
1318 // the presence of dependencies to speed it up.
1319 for (nsDisplayItem* i : DisplayItems()) {
1320 if (i->HasDeletedFrame() || i->Frame() == this) {
1321 // Ignore the items with deleted frames, and the items with |this| as
1322 // the primary frame.
1323 continue;
1324 }
1325
1326 if (i->GetDependentFrame() == this) {
1327 // For items with |this| as a dependent frame, mark the primary frame
1328 // for rebuild.
1329 i->Frame()->MarkNeedsDisplayItemRebuild();
1330 }
1331 }
1332}
1333
1334// Subclass hook for style post processing
1335/* virtual */
1336void nsIFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
1337 if (aOldComputedStyle) {
1338#ifdef ACCESSIBILITY1
1339 // Don't notify for reconstructed frames here, since the frame is still
1340 // being constructed at this point and so LocalAccessible::GetFrame() will
1341 // return null. Style changes for reconstructed frames are handled in
1342 // DocAccessible::PruneOrInsertSubtree.
1343 if (nsAccessibilityService* accService = GetAccService()) {
1344 accService->NotifyOfComputedStyleChange(PresShell(), mContent);
1345 }
1346#endif
1347 UpdateGeneratedContentTextIfNeeded(this, aOldComputedStyle);
1348 }
1349
1350 MaybeScheduleReflowSVGNonDisplayText(this);
1351
1352 Document* doc = PresContext()->Document();
1353 ImageLoader& loader = doc->EnsureStyleImageLoader();
1354 // Continuing text frame doesn't initialize its continuation pointer before
1355 // reaching here for the first time, so we have to exclude text frames. This
1356 // doesn't affect correctness because text can't match selectors.
1357 //
1358 // FIXME(emilio): We should consider fixing that.
1359 //
1360 // TODO(emilio): Can we avoid doing some / all of the image stuff when
1361 // isNonTextFirstContinuation is false? We should consider doing this just for
1362 // primary frames and pseudos, but the first-line reparenting code makes it
1363 // all bad, should get around to bug 1465474 eventually :(
1364 const bool isNonText = !IsTextFrame();
1365 if (isNonText) {
1366 mComputedStyle->StartImageLoads(*doc, aOldComputedStyle);
1367 }
1368
1369 const bool isRootElementStyle = Style()->IsRootElementStyle();
1370 if (isRootElementStyle) {
1371 PresShell()->SetNeedsWindowPropertiesSync();
1372 }
1373
1374 const nsStyleImageLayers* oldLayers =
1375 aOldComputedStyle ? &aOldComputedStyle->StyleBackground()->mImage
1376 : nullptr;
1377 const nsStyleImageLayers* newLayers = &StyleBackground()->mImage;
1378 AddAndRemoveImageAssociations(loader, this, oldLayers, newLayers);
1379
1380 oldLayers =
1381 aOldComputedStyle ? &aOldComputedStyle->StyleSVGReset()->mMask : nullptr;
1382 newLayers = &StyleSVGReset()->mMask;
1383 AddAndRemoveImageAssociations(loader, this, oldLayers, newLayers);
1384
1385 const nsStyleDisplay* disp = StyleDisplay();
1386 if (aOldComputedStyle) {
1387 // Detect style changes that should trigger a scroll anchor adjustment
1388 // suppression.
1389 // https://drafts.csswg.org/css-scroll-anchoring/#suppression-triggers
1390 bool needScrollAnchorSuppression = false;
1391
1392 const nsStyleMargin* oldMargin = aOldComputedStyle->StyleMargin();
1393 if (!oldMargin->MarginEquals(*StyleMargin())) {
1394 needScrollAnchorSuppression = true;
1395 }
1396
1397 const nsStylePadding* oldPadding = aOldComputedStyle->StylePadding();
1398 if (oldPadding->mPadding != StylePadding()->mPadding) {
1399 SetHasPaddingChange(true);
1400 needScrollAnchorSuppression = true;
1401 }
1402
1403 const nsStyleDisplay* oldDisp = aOldComputedStyle->StyleDisplay();
1404 if (oldDisp->mOverflowAnchor != disp->mOverflowAnchor) {
1405 if (auto* container = ScrollAnchorContainer::FindFor(this)) {
1406 container->InvalidateAnchor();
1407 }
1408 if (ScrollContainerFrame* scrollContainerFrame = do_QueryFrame(this)) {
1409 scrollContainerFrame->Anchor()->InvalidateAnchor();
1410 }
1411 }
1412
1413 if (mInScrollAnchorChain) {
1414 const nsStylePosition* pos = StylePosition();
1415 const nsStylePosition* oldPos = aOldComputedStyle->StylePosition();
1416 if (!needScrollAnchorSuppression &&
1417 (oldPos->mOffset != pos->mOffset || oldPos->mWidth != pos->mWidth ||
1418 oldPos->mMinWidth != pos->mMinWidth ||
1419 oldPos->mMaxWidth != pos->mMaxWidth ||
1420 oldPos->mHeight != pos->mHeight ||
1421 oldPos->mMinHeight != pos->mMinHeight ||
1422 oldPos->mMaxHeight != pos->mMaxHeight ||
1423 oldDisp->mPosition != disp->mPosition ||
1424 oldDisp->mTransform != disp->mTransform)) {
1425 needScrollAnchorSuppression = true;
1426 }
1427
1428 if (needScrollAnchorSuppression &&
1429 StaticPrefs::layout_css_scroll_anchoring_suppressions_enabled()) {
1430 ScrollAnchorContainer::FindFor(this)->SuppressAdjustments();
1431 }
1432 }
1433
1434 if (disp->mPosition != oldDisp->mPosition) {
1435 if (!disp->IsRelativelyOrStickyPositionedStyle() &&
1436 oldDisp->IsRelativelyOrStickyPositionedStyle()) {
1437 RemoveProperty(NormalPositionProperty());
1438 }
1439 }
1440 if (disp->mScrollSnapAlign != oldDisp->mScrollSnapAlign) {
1441 ScrollSnapUtils::PostPendingResnapFor(this);
1442 }
1443 if (isRootElementStyle &&
1444 disp->mScrollSnapType != oldDisp->mScrollSnapType) {
1445 if (ScrollContainerFrame* sf =
1446 PresShell()->GetRootScrollContainerFrame()) {
1447 sf->PostPendingResnap();
1448 }
1449 }
1450 if (StyleUIReset()->mMozSubtreeHiddenOnlyVisually &&
1451 !aOldComputedStyle->StyleUIReset()->mMozSubtreeHiddenOnlyVisually) {
1452 PresShell::ClearMouseCapture(this);
1453 }
1454 }
1455
1456 imgIRequest* oldBorderImage =
1457 aOldComputedStyle
1458 ? aOldComputedStyle->StyleBorder()->GetBorderImageRequest()
1459 : nullptr;
1460 imgIRequest* newBorderImage = StyleBorder()->GetBorderImageRequest();
1461 // FIXME (Bug 759996): The following is no longer true.
1462 // For border-images, we can't be as conservative (we need to set the
1463 // new loaders if there has been any change) since the CalcDifference
1464 // call depended on the result of GetComputedBorder() and that result
1465 // depends on whether the image has loaded, start the image load now
1466 // so that we'll get notified when it completes loading and can do a
1467 // restyle. Otherwise, the image might finish loading from the
1468 // network before we start listening to its notifications, and then
1469 // we'll never know that it's finished loading. Likewise, we want to
1470 // do this for freshly-created frames to prevent a similar race if the
1471 // image loads between reflow (which can depend on whether the image
1472 // is loaded) and paint. We also don't really care about any callers who try
1473 // to paint borders with a different style, because they won't have the
1474 // correct size for the border either.
1475 if (oldBorderImage != newBorderImage) {
1476 // stop and restart the image loading/notification
1477 if (oldBorderImage && HasImageRequest()) {
1478 loader.DisassociateRequestFromFrame(oldBorderImage, this);
1479 }
1480 if (newBorderImage) {
1481 loader.AssociateRequestToFrame(newBorderImage, this);
1482 }
1483 }
1484
1485 auto GetShapeImageRequest = [](const ComputedStyle* aStyle) -> imgIRequest* {
1486 if (!aStyle) {
1487 return nullptr;
1488 }
1489 auto& shape = aStyle->StyleDisplay()->mShapeOutside;
1490 if (!shape.IsImage()) {
1491 return nullptr;
1492 }
1493 return shape.AsImage().GetImageRequest();
1494 };
1495
1496 imgIRequest* oldShapeImage = GetShapeImageRequest(aOldComputedStyle);
1497 imgIRequest* newShapeImage = GetShapeImageRequest(Style());
1498 if (oldShapeImage != newShapeImage) {
1499 if (oldShapeImage && HasImageRequest()) {
1500 loader.DisassociateRequestFromFrame(oldShapeImage, this);
1501 }
1502 if (newShapeImage) {
1503 loader.AssociateRequestToFrame(
1504 newShapeImage, this,
1505 ImageLoader::Flags::
1506 RequiresReflowOnFirstFrameCompleteAndLoadEventBlocking);
1507 }
1508 }
1509
1510 // SVGObserverUtils::GetEffectProperties() asserts that we only invoke it with
1511 // the first continuation so we need to check that in advance.
1512 const bool isNonTextFirstContinuation = isNonText && !GetPrevContinuation();
1513 if (isNonTextFirstContinuation) {
1514 // Kick off loading of external SVG resources referenced from properties if
1515 // any. This currently includes filter, clip-path, and mask.
1516 SVGObserverUtils::InitiateResourceDocLoads(this);
1517 }
1518
1519 // If the page contains markup that overrides text direction, and
1520 // does not contain any characters that would activate the Unicode
1521 // bidi algorithm, we need to call |SetBidiEnabled| on the pres
1522 // context before reflow starts. See bug 115921.
1523 if (StyleVisibility()->mDirection == StyleDirection::Rtl) {
1524 PresContext()->SetBidiEnabled();
1525 }
1526
1527 // The following part is for caching offset-path:path(). We cache the
1528 // flatten gfx path, so we don't have to rebuild and re-flattern it at
1529 // each cycle if we have animations on offset-* with a fixed offset-path.
1530 const StyleOffsetPath* oldPath =
1531 aOldComputedStyle ? &aOldComputedStyle->StyleDisplay()->mOffsetPath
1532 : nullptr;
1533 const StyleOffsetPath& newPath = StyleDisplay()->mOffsetPath;
1534 if (!oldPath || *oldPath != newPath) {
1535 // FIXME: Bug 1837042. Cache all basic shapes.
1536 if (newPath.IsPath()) {
1537 RefPtr<gfx::PathBuilder> builder = MotionPathUtils::GetPathBuilder();
1538 RefPtr<gfx::Path> path =
1539 MotionPathUtils::BuildSVGPath(newPath.AsSVGPathData(), builder);
1540 if (path) {
1541 // The newPath could be path('') (i.e. empty path), so its gfx path
1542 // could be nullptr, and so we only set property for a non-empty path.
1543 SetProperty(nsIFrame::OffsetPathCache(), path.forget().take());
1544 } else {
1545 // May have an old cached path, so we have to delete it.
1546 RemoveProperty(nsIFrame::OffsetPathCache());
1547 }
1548 } else if (oldPath) {
1549 RemoveProperty(nsIFrame::OffsetPathCache());
1550 }
1551 }
1552
1553 if (IsPrimaryFrame()) {
1554 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", 1554); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aOldComputedStyle" ")"); do { MOZ_CrashSequence
(__null, 1554); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1555 HandlePrimaryFrameStyleChange(aOldComputedStyle);
1556 }
1557
1558 RemoveStateBits(NS_FRAME_SIMPLE_DISPLAYLIST);
1559
1560 mMayHaveRoundedCorners = true;
1561}
1562
1563void nsIFrame::HandleLastRememberedSize() {
1564 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", 1564); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsPrimaryFrame()" ")"); do { MOZ_CrashSequence
(__null, 1564); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1565 auto* element = Element::FromNodeOrNull(mContent);
1566 if (!element) {
1567 return;
1568 }
1569 const WritingMode wm = GetWritingMode();
1570 const nsStylePosition* stylePos = StylePosition();
1571 bool canRememberBSize = stylePos->ContainIntrinsicBSize(wm).HasAuto();
1572 bool canRememberISize = stylePos->ContainIntrinsicISize(wm).HasAuto();
1573 if (!canRememberBSize) {
1574 element->RemoveLastRememberedBSize();
1575 }
1576 if (!canRememberISize) {
1577 element->RemoveLastRememberedISize();
1578 }
1579 if ((canRememberBSize || canRememberISize) && !HidesContent()) {
1580 bool isNonReplacedInline = IsLineParticipant() && !IsReplaced();
1581 if (!isNonReplacedInline) {
1582 PresContext()->Document()->ObserveForLastRememberedSize(*element);
1583 return;
1584 }
1585 }
1586 PresContext()->Document()->UnobserveForLastRememberedSize(*element);
1587}
1588
1589#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED1
1590void nsIFrame::AssertNewStyleIsSane(ComputedStyle& aNewStyle) {
1591 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", 1605); 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, 1605); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1592 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", 1605); 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, 1605); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1593 // ::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", 1605); 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, 1605); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1594 // 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", 1605); 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, 1605); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1595 (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", 1605); 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, 1605); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1596 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", 1605); 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, 1605); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1597 // ::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", 1605); 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, 1605); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1598 // 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", 1605); 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, 1605); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1599 // 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", 1605); 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, 1605); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1600 (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", 1605); 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, 1605); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1601 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", 1605); 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, 1605); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1602 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", 1605); 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, 1605); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1603 (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", 1605); 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, 1605); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1604 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", 1605); 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, 1605); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1605 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", 1605); 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, 1605); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1606}
1607#endif
1608
1609/* virtual */
1610nsMargin nsIFrame::GetUsedMargin() const {
1611 nsMargin margin;
1612 if (((mState & NS_FRAME_FIRST_REFLOW) && !(mState & NS_FRAME_IN_REFLOW)) ||
1613 IsInSVGTextSubtree()) {
1614 return margin;
1615 }
1616
1617 if (nsMargin* m = GetProperty(UsedMarginProperty())) {
1618 margin = *m;
1619 } else if (!StyleMargin()->GetMargin(margin)) {
1620 // If we get here, our caller probably shouldn't be calling us...
1621 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"
, 1623); MOZ_PretendNoReturn(); } while (0)
1622 "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"
, 1623); MOZ_PretendNoReturn(); } while (0)
1623 "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"
, 1623); MOZ_PretendNoReturn(); } while (0)
;
1624 }
1625 return margin;
1626}
1627
1628/* virtual */
1629nsMargin nsIFrame::GetUsedBorder() const {
1630 if (((mState & NS_FRAME_FIRST_REFLOW) && !(mState & NS_FRAME_IN_REFLOW)) ||
1631 IsInSVGTextSubtree()) {
1632 return {};
1633 }
1634
1635 const nsStyleDisplay* disp = StyleDisplay();
1636 if (IsThemed(disp)) {
1637 // Theme methods don't use const-ness.
1638 auto* mutable_this = const_cast<nsIFrame*>(this);
1639 nsPresContext* pc = PresContext();
1640 LayoutDeviceIntMargin widgetBorder = pc->Theme()->GetWidgetBorder(
1641 pc->DeviceContext(), mutable_this, disp->EffectiveAppearance());
1642 return LayoutDevicePixel::ToAppUnits(widgetBorder,
1643 pc->AppUnitsPerDevPixel());
1644 }
1645
1646 return StyleBorder()->GetComputedBorder();
1647}
1648
1649/* virtual */
1650nsMargin nsIFrame::GetUsedPadding() const {
1651 nsMargin padding;
1652 if (((mState & NS_FRAME_FIRST_REFLOW) && !(mState & NS_FRAME_IN_REFLOW)) ||
1653 IsInSVGTextSubtree()) {
1654 return padding;
1655 }
1656
1657 const nsStyleDisplay* disp = StyleDisplay();
1658 if (IsThemed(disp)) {
1659 // Theme methods don't use const-ness.
1660 nsIFrame* mutable_this = const_cast<nsIFrame*>(this);
1661 nsPresContext* pc = PresContext();
1662 LayoutDeviceIntMargin widgetPadding;
1663 if (pc->Theme()->GetWidgetPadding(pc->DeviceContext(), mutable_this,
1664 disp->EffectiveAppearance(),
1665 &widgetPadding)) {
1666 return LayoutDevicePixel::ToAppUnits(widgetPadding,
1667 pc->AppUnitsPerDevPixel());
1668 }
1669 }
1670
1671 if (nsMargin* p = GetProperty(UsedPaddingProperty())) {
1672 padding = *p;
1673 } else if (!StylePadding()->GetPadding(padding)) {
1674 // If we get here, our caller probably shouldn't be calling us...
1675 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"
, 1677); MOZ_PretendNoReturn(); } while (0)
1676 "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"
, 1677); MOZ_PretendNoReturn(); } while (0)
1677 "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"
, 1677); MOZ_PretendNoReturn(); } while (0)
;
1678 }
1679 return padding;
1680}
1681
1682nsIFrame::Sides nsIFrame::GetSkipSides() const {
1683 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==(__builtin_expect(!!(StyleBorder()->mBoxDecorationBreak ==
StyleBoxDecorationBreak::Clone), 0))
1684 StyleBoxDecorationBreak::Clone)(__builtin_expect(!!(StyleBorder()->mBoxDecorationBreak ==
StyleBoxDecorationBreak::Clone), 0))
&&
1685 !HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
1686 return Sides();
1687 }
1688
1689 // Convert the logical skip sides to physical sides using the frame's
1690 // writing mode
1691 WritingMode writingMode = GetWritingMode();
1692 LogicalSides logicalSkip = GetLogicalSkipSides();
1693 Sides skip;
1694
1695 if (logicalSkip.BStart()) {
1696 if (writingMode.IsVertical()) {
1697 skip |= writingMode.IsVerticalLR() ? SideBits::eLeft : SideBits::eRight;
1698 } else {
1699 skip |= SideBits::eTop;
1700 }
1701 }
1702
1703 if (logicalSkip.BEnd()) {
1704 if (writingMode.IsVertical()) {
1705 skip |= writingMode.IsVerticalLR() ? SideBits::eRight : SideBits::eLeft;
1706 } else {
1707 skip |= SideBits::eBottom;
1708 }
1709 }
1710
1711 if (logicalSkip.IStart()) {
1712 if (writingMode.IsVertical()) {
1713 skip |=
1714 writingMode.IsInlineReversed() ? SideBits::eBottom : SideBits::eTop;
1715 } else {
1716 skip |= writingMode.IsBidiLTR() ? SideBits::eLeft : SideBits::eRight;
1717 }
1718 }
1719
1720 if (logicalSkip.IEnd()) {
1721 if (writingMode.IsVertical()) {
1722 skip |=
1723 writingMode.IsInlineReversed() ? SideBits::eTop : SideBits::eBottom;
1724 } else {
1725 skip |= writingMode.IsBidiLTR() ? SideBits::eRight : SideBits::eLeft;
1726 }
1727 }
1728 return skip;
1729}
1730
1731nsRect nsIFrame::GetPaddingRectRelativeToSelf() const {
1732 nsMargin border = GetUsedBorder().ApplySkipSides(GetSkipSides());
1733 nsRect r(0, 0, mRect.width, mRect.height);
1734 r.Deflate(border);
1735 return r;
1736}
1737
1738nsRect nsIFrame::GetPaddingRect() const {
1739 return GetPaddingRectRelativeToSelf() + GetPosition();
1740}
1741
1742WritingMode nsIFrame::WritingModeForLine(WritingMode aSelfWM,
1743 nsIFrame* aSubFrame) const {
1744 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", 1744); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aSelfWM == GetWritingMode()" ")"); do { MOZ_CrashSequence
(__null, 1744); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1745 WritingMode writingMode = aSelfWM;
1746
1747 if (StyleTextReset()->mUnicodeBidi == StyleUnicodeBidi::Plaintext) {
1748 mozilla::intl::BidiEmbeddingLevel frameLevel =
1749 nsBidiPresUtils::GetFrameBaseLevel(aSubFrame);
1750 writingMode.SetDirectionFromBidiLevel(frameLevel);
1751 }
1752
1753 return writingMode;
1754}
1755
1756nsRect nsIFrame::GetMarginRect() const {
1757 return GetMarginRectRelativeToSelf() + GetPosition();
1758}
1759
1760nsRect nsIFrame::GetMarginRectRelativeToSelf() const {
1761 nsMargin m = GetUsedMargin().ApplySkipSides(GetSkipSides());
1762 nsRect r(0, 0, mRect.width, mRect.height);
1763 r.Inflate(m);
1764 return r;
1765}
1766
1767bool nsIFrame::IsTransformed() const {
1768 if (!HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED)) {
1769 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", 1769); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsCSSTransformed()" ")"); do { MOZ_CrashSequence
(__null, 1769); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1770 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", 1770); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!GetParentSVGTransforms()" ")"); do { MOZ_CrashSequence
(__null, 1770); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
1771 return false;
1772 }
1773 return IsCSSTransformed() || GetParentSVGTransforms();
1774}
1775
1776bool nsIFrame::IsCSSTransformed() const {
1777 return HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED) &&
1778 (StyleDisplay()->HasTransform(this) || HasAnimationOfTransform());
1779}
1780
1781bool nsIFrame::HasAnimationOfTransform() const {
1782 if (!MayHaveTransformAnimation()) {
1783 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", 1784); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsPrimaryFrame() || !SupportsCSSTransforms() || !nsLayoutUtils::HasAnimationOfTransformAndMotionPath(this)"
")"); do { MOZ_CrashSequence(__null, 1784); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
1784 !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", 1784); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsPrimaryFrame() || !SupportsCSSTransforms() || !nsLayoutUtils::HasAnimationOfTransformAndMotionPath(this)"
")"); do { MOZ_CrashSequence(__null, 1784); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1785 return false;
1786 }
1787 return IsPrimaryFrame() && SupportsCSSTransforms() &&
1788 nsLayoutUtils::HasAnimationOfTransformAndMotionPath(this);
1789}
1790
1791bool nsIFrame::ChildrenHavePerspective(
1792 const nsStyleDisplay* aStyleDisplay) const {
1793 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"
, 1793); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aStyleDisplay == StyleDisplay()"
")"); do { MOZ_CrashSequence(__null, 1793); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1794 return aStyleDisplay->HasPerspective(this);
1795}
1796
1797bool nsIFrame::HasAnimationOfOpacity(EffectSet* aEffectSet) const {
1798 return ((nsLayoutUtils::IsPrimaryStyleFrame(this) ||
1799 nsLayoutUtils::FirstContinuationOrIBSplitSibling(this)
1800 ->IsPrimaryFrame()) &&
1801 nsLayoutUtils::HasAnimationOfPropertySet(
1802 this, nsCSSPropertyIDSet::OpacityProperties(), aEffectSet));
1803}
1804
1805bool nsIFrame::HasOpacityInternal(float aThreshold,
1806 const nsStyleDisplay* aStyleDisplay,
1807 const nsStyleEffects* aStyleEffects,
1808 EffectSet* aEffectSet) const {
1809 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", 1809); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "0.0 <= aThreshold && aThreshold <= 1.0"
") (" "Invalid argument" ")"); do { MOZ_CrashSequence(__null
, 1809); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
;
1810 if (aStyleEffects->mOpacity < aThreshold ||
1811 aStyleDisplay->mWillChange.bits & StyleWillChangeBits::OPACITY) {
1812 return true;
1813 }
1814
1815 if (!mMayHaveOpacityAnimation) {
1816 return false;
1817 }
1818
1819 return HasAnimationOfOpacity(aEffectSet);
1820}
1821
1822bool nsIFrame::DoGetParentSVGTransforms(gfx::Matrix*) const { return false; }
1823
1824bool nsIFrame::Extend3DContext(const nsStyleDisplay* aStyleDisplay,
1825 const nsStyleEffects* aStyleEffects,
1826 mozilla::EffectSet* aEffectSetForOpacity) const {
1827 if (!HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED)) {
1828 return false;
1829 }
1830 const nsStyleDisplay* disp = StyleDisplayWithOptionalParam(aStyleDisplay);
1831 if (disp->mTransformStyle != StyleTransformStyle::Preserve3d ||
1832 !SupportsCSSTransforms()) {
1833 return false;
1834 }
1835
1836 // If we're all scroll frame, then all descendants will be clipped, so we
1837 // can't preserve 3d.
1838 if (IsScrollContainerFrame()) {
1839 return false;
1840 }
1841
1842 const nsStyleEffects* effects = StyleEffectsWithOptionalParam(aStyleEffects);
1843 if (HasOpacity(disp, effects, aEffectSetForOpacity)) {
1844 return false;
1845 }
1846
1847 return ShouldApplyOverflowClipping(disp).isEmpty() &&
1848 !GetClipPropClipRect(disp, effects, GetSize()) &&
1849 !SVGIntegrationUtils::UsingEffectsForFrame(this) &&
1850 !effects->HasMixBlendMode() &&
1851 !ForcesStackingContextForViewTransition() &&
1852 disp->mIsolation != StyleIsolation::Isolate;
1853}
1854
1855bool nsIFrame::Combines3DTransformWithAncestors() const {
1856 // Check these first as they are faster then both calls below and are we are
1857 // likely to hit the early return (backface hidden is uncommon and
1858 // GetReferenceFrame is a hot caller of this which only calls this if
1859 // IsCSSTransformed is false).
1860 if (!IsCSSTransformed() && !BackfaceIsHidden()) {
1861 return false;
1862 }
1863 nsIFrame* parent = GetClosestFlattenedTreeAncestorPrimaryFrame();
1864 return parent && parent->Extend3DContext();
1865}
1866
1867bool nsIFrame::In3DContextAndBackfaceIsHidden() const {
1868 // While both tests fail most of the time, test BackfaceIsHidden()
1869 // first since it's likely to fail faster.
1870 return BackfaceIsHidden() && Combines3DTransformWithAncestors();
1871}
1872
1873bool nsIFrame::HasPerspective() const {
1874 if (!IsCSSTransformed()) {
1875 return false;
1876 }
1877 nsIFrame* parent = GetClosestFlattenedTreeAncestorPrimaryFrame();
1878 if (!parent) {
1879 return false;
1880 }
1881 return parent->ChildrenHavePerspective();
1882}
1883
1884nsRect nsIFrame::GetContentRectRelativeToSelf() const {
1885 nsMargin bp = GetUsedBorderAndPadding().ApplySkipSides(GetSkipSides());
1886 nsRect r(0, 0, mRect.width, mRect.height);
1887 r.Deflate(bp);
1888 return r;
1889}
1890
1891nsRect nsIFrame::GetContentRect() const {
1892 return GetContentRectRelativeToSelf() + GetPosition();
1893}
1894
1895bool nsIFrame::ComputeBorderRadii(const BorderRadius& aBorderRadius,
1896 const CornerShapeRect& aCornerShape,
1897 const nsSize& aFrameSize,
1898 const nsSize& aBorderArea, Sides aSkipSides,
1899 nsRectCornerRadii& aRadii) {
1900 // Percentages are relative to whichever side they're on.
1901 for (const auto i : mozilla::AllPhysicalHalfCorners()) {
1902 const LengthPercentage& c = aBorderRadius.Get(i);
1903 nscoord axis = HalfCornerIsX(i) ? aFrameSize.width : aFrameSize.height;
1904 aRadii[i] = std::max(0, c.Resolve(axis));
1905 }
1906
1907 aRadii.mShapeK[mozilla::eCornerTopLeft] = aCornerShape.top_left.k;
1908 aRadii.mShapeK[mozilla::eCornerTopRight] = aCornerShape.top_right.k;
1909 aRadii.mShapeK[mozilla::eCornerBottomLeft] = aCornerShape.bottom_left.k;
1910 aRadii.mShapeK[mozilla::eCornerBottomRight] = aCornerShape.bottom_right.k;
1911
1912 bool isTopLeftSquare =
1913 std::isinf(aCornerShape.top_left.k) && (aCornerShape.top_left.k > 0.0f);
1914 bool isTopRightSquare =
1915 std::isinf(aCornerShape.top_right.k) && (aCornerShape.top_right.k > 0.0f);
1916 bool isBottomLeftSquare = std::isinf(aCornerShape.bottom_left.k) &&
1917 (aCornerShape.bottom_left.k > 0.0f);
1918 bool isBottomRightSquare = std::isinf(aCornerShape.bottom_right.k) &&
1919 (aCornerShape.bottom_right.k > 0.0f);
1920
1921 if (aSkipSides.Intersects(SideBits::eTop | SideBits::eLeft) ||
1922 isTopLeftSquare) {
1923 aRadii.TopLeft() = {};
1924 }
1925 if (aSkipSides.Intersects(SideBits::eTop | SideBits::eRight) ||
1926 isTopRightSquare) {
1927 aRadii.TopRight() = {};
1928 }
1929 if (aSkipSides.Intersects(SideBits::eBottom | SideBits::eLeft) ||
1930 isBottomLeftSquare) {
1931 aRadii.BottomLeft() = {};
1932 }
1933 if (aSkipSides.Intersects(SideBits::eBottom | SideBits::eRight) ||
1934 isBottomRightSquare) {
1935 aRadii.BottomRight() = {};
1936 }
1937
1938 // css3-background specifies this algorithm for reducing
1939 // corner radii when they are too big.
1940 bool haveRadius = false;
1941 double ratio = 1.0f;
1942 for (const auto side : mozilla::AllPhysicalSides()) {
1943 auto hc1 = SideToHalfCorner(side, false, true);
1944 auto hc2 = SideToHalfCorner(side, true, true);
1945 nscoord length =
1946 SideIsVertical(side) ? aBorderArea.height : aBorderArea.width;
1947 nscoord sum = aRadii[hc1] + aRadii[hc2];
1948 if (sum) {
1949 haveRadius = true;
1950 // avoid floating point division in the normal case
1951 if (length < sum) {
1952 ratio = std::min(ratio, double(length) / sum);
1953 }
1954 }
1955 }
1956 if (ratio < 1.0) {
1957 for (const auto corner : mozilla::AllPhysicalHalfCorners()) {
1958 aRadii[corner] *= ratio;
1959 }
1960 }
1961
1962 return haveRadius;
1963}
1964
1965static inline bool RadiiAreDefinitelyZero(const BorderRadius& aBorderRadius) {
1966 for (const auto corner : mozilla::AllPhysicalHalfCorners()) {
1967 if (!aBorderRadius.Get(corner).IsDefinitelyZero()) {
1968 return false;
1969 }
1970 }
1971 return true;
1972}
1973
1974/* virtual */
1975bool nsIFrame::GetBorderRadii(const nsSize& aFrameSize,
1976 const nsSize& aBorderArea, Sides aSkipSides,
1977 nsRectCornerRadii& aRadii) const {
1978 if (!mMayHaveRoundedCorners) {
1979 return false;
1980 }
1981
1982 if (IsThemed()) {
1983 // When we're themed, the native theme code draws the border and
1984 // background, and therefore it doesn't make sense to tell other
1985 // code that's interested in border-radius that we have any radii.
1986 //
1987 // In an ideal world, we might have a way for the them to tell us an
1988 // border radius, but since we don't, we're better off assuming
1989 // zero.
1990 return false;
1991 }
1992
1993 const auto& radii = StyleBorder()->mBorderRadius;
1994 const auto& cornerShape = StyleBorder()->mCornerShape;
1995 const bool hasRadii = ComputeBorderRadii(radii, cornerShape, aFrameSize,
1996 aBorderArea, aSkipSides, aRadii);
1997 if (!hasRadii) {
1998 // TODO(emilio): Maybe we can just remove this bit and do the
1999 // IsDefinitelyZero check unconditionally. That should still avoid most of
2000 // the work, though maybe not the cache miss of going through the style and
2001 // the border struct.
2002 const_cast<nsIFrame*>(this)->mMayHaveRoundedCorners =
2003 !RadiiAreDefinitelyZero(radii);
2004 }
2005 return hasRadii;
2006}
2007
2008bool nsIFrame::GetBorderRadii(nsRectCornerRadii& aRadii) const {
2009 nsSize sz = GetSize();
2010 return GetBorderRadii(sz, sz, GetSkipSides(), aRadii);
2011}
2012
2013bool nsIFrame::GetMarginBoxBorderRadii(nsRectCornerRadii& aRadii) const {
2014 if (!GetBorderRadii(aRadii)) {
2015 return false;
2016 }
2017 aRadii.AdjustOutwards(GetUsedMargin());
2018 return true;
2019}
2020
2021bool nsIFrame::GetPaddingBoxBorderRadii(nsRectCornerRadii& aRadii) const {
2022 if (!GetBorderRadii(aRadii)) {
2023 return false;
2024 }
2025 aRadii.AdjustInwards(GetUsedBorder());
2026 return true;
2027}
2028
2029bool nsIFrame::GetContentBoxBorderRadii(nsRectCornerRadii& aRadii) const {
2030 if (!GetBorderRadii(aRadii)) {
2031 return false;
2032 }
2033 aRadii.AdjustInwards(GetUsedBorderAndPadding());
2034 return true;
2035}
2036
2037bool nsIFrame::GetShapeBoxBorderRadii(nsRectCornerRadii& aRadii) const {
2038 using Tag = StyleShapeOutside::Tag;
2039 auto& shapeOutside = StyleDisplay()->mShapeOutside;
2040 auto box = StyleShapeBox::MarginBox;
2041 switch (shapeOutside.tag) {
2042 case Tag::Image:
2043 case Tag::None:
2044 return false;
2045 case Tag::Box:
2046 box = shapeOutside.AsBox();
2047 break;
2048 case Tag::Shape:
2049 box = shapeOutside.AsShape()._1;
2050 break;
2051 }
2052
2053 switch (box) {
2054 case StyleShapeBox::ContentBox:
2055 return GetContentBoxBorderRadii(aRadii);
2056 case StyleShapeBox::PaddingBox:
2057 return GetPaddingBoxBorderRadii(aRadii);
2058 case StyleShapeBox::BorderBox:
2059 return GetBorderRadii(aRadii);
2060 case StyleShapeBox::MarginBox:
2061 return GetMarginBoxBorderRadii(aRadii);
2062 default:
2063 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"
, 2063); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Unexpected box value" ")"); do {
MOZ_CrashSequence(__null, 2063); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
2064 return false;
2065 }
2066}
2067
2068nscoord nsIFrame::OneEmInAppUnits() const {
2069 return StyleFont()
2070 ->mFont.size.ScaledBy(nsLayoutUtils::FontSizeInflationFor(this))
2071 .ToAppUnits();
2072}
2073
2074RubyMetrics nsIFrame::RubyMetrics(float aRubyMetricsFactor) const {
2075 RefPtr<nsFontMetrics> fm =
2076 nsLayoutUtils::GetInflatedFontMetricsForFrame(this);
2077 return mozilla::RubyMetrics{
2078 nscoord(NS_round(fm->TrimmedAscent() * aRubyMetricsFactor)),
2079 nscoord(NS_round(fm->TrimmedDescent() * aRubyMetricsFactor))};
2080}
2081
2082nscoord nsIFrame::SynthesizeFallbackBaseline(
2083 WritingMode aWM, BaselineSharingGroup aBaselineGroup) const {
2084 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"
, 2084); MOZ_PretendNoReturn(); } } while (0)
;
2085 return Baseline::SynthesizeBOffsetFromMarginBox(this, aWM, aBaselineGroup);
2086}
2087
2088nscoord nsIFrame::GetLogicalBaseline(WritingMode aWM) const {
2089 return GetLogicalBaseline(aWM, GetDefaultBaselineSharingGroup(),
2090 BaselineExportContext::LineLayout);
2091}
2092
2093nscoord nsIFrame::GetLogicalBaseline(
2094 WritingMode aWM, BaselineSharingGroup aBaselineGroup,
2095 BaselineExportContext aExportContext) const {
2096 const auto result =
2097 GetNaturalBaselineBOffset(aWM, aBaselineGroup, aExportContext)
2098 .valueOrFrom([this, aWM, aBaselineGroup]() {
2099 return SynthesizeFallbackBaseline(aWM, aBaselineGroup);
2100 });
2101 if (aBaselineGroup == BaselineSharingGroup::Last) {
2102 return BSize(aWM) - result;
2103 }
2104 return result;
2105}
2106
2107const nsFrameList& nsIFrame::GetChildList(ChildListID aListID) const {
2108 if (IsAbsoluteContainer()) {
2109 if (aListID == FrameChildListID::Absolute) {
2110 return GetAbsoluteContainingBlock()->GetChildList();
2111 }
2112 if (aListID == FrameChildListID::PushedAbsolute) {
2113 return GetAbsoluteContainingBlock()->GetPushedChildList();
2114 }
2115 }
2116 return nsFrameList::EmptyList();
2117}
2118
2119void nsIFrame::GetChildLists(nsTArray<ChildList>* aLists) const {
2120 if (const auto* absCB = GetAbsoluteContainingBlock()) {
2121 const nsFrameList& absoluteList = absCB->GetChildList();
2122 absoluteList.AppendIfNonempty(aLists, FrameChildListID::Absolute);
2123 const nsFrameList& pushedAbsoluteList = absCB->GetPushedChildList();
2124 pushedAbsoluteList.AppendIfNonempty(aLists,
2125 FrameChildListID::PushedAbsolute);
2126 }
2127}
2128
2129AutoTArray<nsIFrame::ChildList, 4> nsIFrame::CrossDocChildLists() {
2130 AutoTArray<ChildList, 4> childLists;
2131 nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(this);
2132 if (subdocumentFrame) {
2133 // Descend into the subdocument
2134 nsIFrame* root = subdocumentFrame->GetSubdocumentRootFrame();
2135 if (root) {
2136 childLists.EmplaceBack(
2137 nsFrameList(root, nsLayoutUtils::GetLastSibling(root)),
2138 FrameChildListID::Principal);
2139 }
2140 }
2141
2142 GetChildLists(&childLists);
2143 return childLists;
2144}
2145
2146nsIFrame::CaretBlockAxisMetrics nsIFrame::GetCaretBlockAxisMetrics(
2147 mozilla::WritingMode aWM, const nsFontMetrics& aFM) const {
2148 // Note(dshin): Ultimately, this does something highly similar (But still
2149 // different) to `nsLayoutUtils::GetFirstLinePosition`.
2150 const auto baseline = GetCaretBaseline();
2151 nscoord ascent = 0, descent = 0;
2152 ascent = aFM.MaxAscent();
2153 descent = aFM.MaxDescent();
2154 const nscoord height = ascent + descent;
2155 if (aWM.IsVertical() && aWM.IsLineInverted()) {
2156 return CaretBlockAxisMetrics{.mOffset = baseline - descent,
2157 .mExtent = height};
2158 }
2159 return CaretBlockAxisMetrics{.mOffset = baseline - ascent, .mExtent = height};
2160}
2161
2162nscoord nsIFrame::GetFontMetricsDerivedCaretBaseline() const {
2163 float inflation = nsLayoutUtils::FontSizeInflationFor(this);
2164 RefPtr<nsFontMetrics> fm =
2165 nsLayoutUtils::GetFontMetricsForFrame(this, inflation);
2166 const WritingMode wm = GetWritingMode();
2167 const nscoord lineHeight = ReflowInput::CalcLineHeight(
2168 *Style(), PresContext(), GetContent(), inflation);
2169 return nsLayoutUtils::GetCenteredFontBaseline(fm, lineHeight,
2170 wm.IsLineInverted()) +
2171 GetLogicalUsedBorderAndPadding(wm).BStart(wm);
2172}
2173
2174const nsAtom* nsIFrame::ComputePageValue(const nsAtom* aAutoValue) const {
2175 const nsAtom* value = aAutoValue ? aAutoValue : nsGkAtoms::_empty;
2176 const nsIFrame* frame = this;
2177 // Find what CSS page name value this frame's subtree has, if any.
2178 // Starting with this frame, check if a page name other than auto is present,
2179 // and record it if so. Then, if the current frame is a container frame, find
2180 // the first non-placeholder child and repeat.
2181 // This will find the most deeply nested first in-flow child of this frame's
2182 // subtree, and return its page name (with auto resolved if applicable, and
2183 // subtrees with no page-names returning the empty atom rather than null).
2184 do {
2185 if (const nsAtom* maybePageName = frame->GetStylePageName()) {
2186 value = maybePageName;
2187 }
2188 // Get the next frame to read from.
2189 const nsIFrame* firstNonPlaceholderFrame = nullptr;
2190 // If this is a container frame, inspect its in-flow children.
2191 if (const nsContainerFrame* containerFrame = do_QueryFrame(frame)) {
2192 for (const nsIFrame* childFrame : containerFrame->PrincipalChildList()) {
2193 if (!childFrame->IsPlaceholderFrame()) {
2194 firstNonPlaceholderFrame = childFrame;
2195 break;
2196 }
2197 }
2198 }
2199 frame = firstNonPlaceholderFrame;
2200 } while (frame);
2201 return value;
2202}
2203
2204Visibility nsIFrame::GetVisibility() const {
2205 if (!HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED)) {
2206 return Visibility::Untracked;
2207 }
2208
2209 bool isSet = false;
2210 uint32_t visibleCount = GetProperty(VisibilityStateProperty(), &isSet);
2211
2212 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"
, 2214); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSet" ") ("
"Should have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is set"
")"); do { MOZ_CrashSequence(__null, 2214); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2213 "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"
, 2214); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSet" ") ("
"Should have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is set"
")"); do { MOZ_CrashSequence(__null, 2214); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2214 "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"
, 2214); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSet" ") ("
"Should have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is set"
")"); do { MOZ_CrashSequence(__null, 2214); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2215
2216 return visibleCount > 0 ? Visibility::ApproximatelyVisible
2217 : Visibility::ApproximatelyNonVisible;
2218}
2219
2220void nsIFrame::UpdateVisibilitySynchronously() {
2221 mozilla::PresShell* presShell = PresShell();
2222 if (!presShell) {
2223 return;
2224 }
2225
2226 if (presShell->AssumeAllFramesVisible()) {
2227 presShell->EnsureFrameInApproximatelyVisibleList(this);
2228 return;
2229 }
2230
2231 bool visible = StyleVisibility()->IsVisible();
2232 nsIFrame* f = GetParent();
2233 nsRect rect = GetRectRelativeToSelf();
2234 nsIFrame* rectFrame = this;
2235 while (f && visible) {
2236 if (ScrollContainerFrame* sf = do_QueryFrame(f)) {
2237 nsRect transformedRect =
2238 nsLayoutUtils::TransformFrameRectToAncestor(rectFrame, rect, f);
2239 if (!sf->IsRectNearlyVisible(transformedRect)) {
2240 visible = false;
2241 break;
2242 }
2243
2244 // In this code we're trying to synchronously update *approximate*
2245 // visibility. (In the future we may update precise visibility here as
2246 // well, which is why the method name does not contain 'approximate'.) The
2247 // IsRectNearlyVisible() check above tells us that the rect we're checking
2248 // is approximately visible within the scrollframe, but we still need to
2249 // ensure that, even if it was scrolled into view, it'd be visible when we
2250 // consider the rest of the document. To do that, we move transformedRect
2251 // to be contained in the scrollport as best we can (it might not fit) to
2252 // pretend that it was scrolled into view.
2253 rect = transformedRect.MoveInsideAndClamp(sf->GetScrollPortRect());
2254 rectFrame = f;
2255 }
2256 nsIFrame* parent = f->GetParent();
2257 if (!parent) {
2258 parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(f);
2259 if (parent && parent->PresContext()->IsChrome()) {
2260 break;
2261 }
2262 }
2263 f = parent;
2264 }
2265
2266 if (visible) {
2267 presShell->EnsureFrameInApproximatelyVisibleList(this);
2268 } else {
2269 presShell->RemoveFrameFromApproximatelyVisibleList(this);
2270 }
2271}
2272
2273void nsIFrame::EnableVisibilityTracking() {
2274 if (HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED)) {
2275 return; // Nothing to do.
2276 }
2277
2278 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", 2280); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!HasProperty(VisibilityStateProperty())" ") ("
"Shouldn't have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is not set"
")"); do { MOZ_CrashSequence(__null, 2280); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2279 "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", 2280); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!HasProperty(VisibilityStateProperty())" ") ("
"Shouldn't have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is not set"
")"); do { MOZ_CrashSequence(__null, 2280); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2280 "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", 2280); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!HasProperty(VisibilityStateProperty())" ") ("
"Shouldn't have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is not set"
")"); do { MOZ_CrashSequence(__null, 2280); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2281
2282 // Add the state bit so we know to track visibility for this frame, and
2283 // initialize the frame property.
2284 AddStateBits(NS_FRAME_VISIBILITY_IS_TRACKED);
2285 SetProperty(VisibilityStateProperty(), 0);
2286
2287 mozilla::PresShell* presShell = PresShell();
2288 if (!presShell) {
2289 return;
2290 }
2291
2292 // Schedule a visibility update. This method will virtually always be called
2293 // when layout has changed anyway, so it's very unlikely that any additional
2294 // visibility updates will be triggered by this, but this way we guarantee
2295 // that if this frame is currently visible we'll eventually find out.
2296 presShell->ScheduleApproximateFrameVisibilityUpdateSoon();
2297}
2298
2299void nsIFrame::DisableVisibilityTracking() {
2300 if (!HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED)) {
2301 return; // Nothing to do.
2302 }
2303
2304 bool isSet = false;
2305 uint32_t visibleCount = TakeProperty(VisibilityStateProperty(), &isSet);
2306
2307 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"
, 2309); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSet" ") ("
"Should have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is set"
")"); do { MOZ_CrashSequence(__null, 2309); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2308 "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"
, 2309); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSet" ") ("
"Should have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is set"
")"); do { MOZ_CrashSequence(__null, 2309); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2309 "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"
, 2309); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSet" ") ("
"Should have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is set"
")"); do { MOZ_CrashSequence(__null, 2309); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2310
2311 RemoveStateBits(NS_FRAME_VISIBILITY_IS_TRACKED);
2312
2313 if (visibleCount == 0) {
2314 return; // We were nonvisible.
2315 }
2316
2317 // We were visible, so send an OnVisibilityChange() notification.
2318 OnVisibilityChange(Visibility::ApproximatelyNonVisible);
2319}
2320
2321void nsIFrame::DecApproximateVisibleCount(
2322 const Maybe<OnNonvisible>& aNonvisibleAction
2323 /* = Nothing() */) {
2324 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", 2324); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED)"
")"); do { MOZ_CrashSequence(__null, 2324); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2325
2326 bool isSet = false;
2327 uint32_t visibleCount = GetProperty(VisibilityStateProperty(), &isSet);
2328
2329 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"
, 2331); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSet" ") ("
"Should have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is set"
")"); do { MOZ_CrashSequence(__null, 2331); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2330 "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"
, 2331); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSet" ") ("
"Should have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is set"
")"); do { MOZ_CrashSequence(__null, 2331); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2331 "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"
, 2331); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSet" ") ("
"Should have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is set"
")"); do { MOZ_CrashSequence(__null, 2331); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2332 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", 2334); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "visibleCount > 0" ") (" "Frame is already nonvisible and we're "
"decrementing its visible count?" ")"); do { MOZ_CrashSequence
(__null, 2334); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
2333 "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", 2334); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "visibleCount > 0" ") (" "Frame is already nonvisible and we're "
"decrementing its visible count?" ")"); do { MOZ_CrashSequence
(__null, 2334); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
2334 "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", 2334); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "visibleCount > 0" ") (" "Frame is already nonvisible and we're "
"decrementing its visible count?" ")"); do { MOZ_CrashSequence
(__null, 2334); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2335
2336 visibleCount--;
2337 SetProperty(VisibilityStateProperty(), visibleCount);
2338 if (visibleCount > 0) {
2339 return;
2340 }
2341
2342 // We just became nonvisible, so send an OnVisibilityChange() notification.
2343 OnVisibilityChange(Visibility::ApproximatelyNonVisible, aNonvisibleAction);
2344}
2345
2346void nsIFrame::IncApproximateVisibleCount() {
2347 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", 2347); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED)"
")"); do { MOZ_CrashSequence(__null, 2347); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2348
2349 bool isSet = false;
2350 uint32_t visibleCount = GetProperty(VisibilityStateProperty(), &isSet);
2351
2352 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"
, 2354); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSet" ") ("
"Should have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is set"
")"); do { MOZ_CrashSequence(__null, 2354); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2353 "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"
, 2354); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSet" ") ("
"Should have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is set"
")"); do { MOZ_CrashSequence(__null, 2354); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
2354 "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"
, 2354); AnnotateMozCrashReason("MOZ_ASSERT" "(" "isSet" ") ("
"Should have a VisibilityStateProperty value " "if NS_FRAME_VISIBILITY_IS_TRACKED is set"
")"); do { MOZ_CrashSequence(__null, 2354); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2355
2356 visibleCount++;
2357 SetProperty(VisibilityStateProperty(), visibleCount);
2358 if (visibleCount > 1) {
2359 return;
2360 }
2361
2362 // We just became visible, so send an OnVisibilityChange() notification.
2363 OnVisibilityChange(Visibility::ApproximatelyVisible);
2364}
2365
2366void nsIFrame::OnVisibilityChange(Visibility aNewVisibility,
2367 const Maybe<OnNonvisible>& aNonvisibleAction
2368 /* = Nothing() */) {
2369 // XXX(seth): In bug 1218990 we'll implement visibility tracking for CSS
2370 // images here.
2371}
2372
2373static nsIFrame* GetActiveSelectionFrame(nsPresContext* aPresContext,
2374 nsIFrame* aFrame) {
2375 nsIContent* capturingContent = PresShell::GetCapturingContent();
2376 if (capturingContent) {
2377 nsIFrame* activeFrame = aPresContext->GetPrimaryFrameFor(capturingContent);
2378 return activeFrame ? activeFrame : aFrame;
2379 }
2380
2381 return aFrame;
2382}
2383
2384bool nsIFrame::ShouldHandleSelectionMovementEvents(ForSelectionStart aType) {
2385 if (GetDisplaySelection() == nsISelectionController::SELECTION_OFF) {
2386 return false;
2387 }
2388 if (UsedUserSelect(this, aType) == StyleUserSelect::None) {
2389 // Check whether style allows selection.
2390 return false;
2391 }
2392 if (IsScrollbarFrame() || IsHTMLCanvasFrame()) {
2393 // Scrollbars and canvas don't move selection with the mouse.
2394 return false;
2395 }
2396 return true;
2397}
2398
2399static Element* FindElementAncestorForMozSelection(nsIContent* aContent) {
2400 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", 2400); return nullptr
; } } while (false)
;
2401 while (aContent && aContent->IsInNativeAnonymousSubtree()) {
2402 aContent = aContent->GetClosestNativeAnonymousSubtreeRootParentOrHost();
2403 }
2404 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", 2404)
; MOZ_PretendNoReturn(); } } while (0)
;
2405 return aContent ? aContent->GetAsElementOrParentElement() : nullptr;
2406}
2407
2408already_AddRefed<ComputedStyle> nsIFrame::ComputeSelectionStyle(
2409 int16_t aSelectionStatus) const {
2410 // Just bail out if not a selection-status that ::selection applies to.
2411 if (aSelectionStatus != nsISelectionController::SELECTION_ON &&
2412 aSelectionStatus != nsISelectionController::SELECTION_DISABLED) {
2413 return nullptr;
2414 }
2415 Element* element = FindElementAncestorForMozSelection(GetContent());
2416 if (!element) {
2417 return nullptr;
2418 }
2419 nsIFrame* primaryFrame = element->GetPrimaryFrame();
2420 ComputedStyle* parentStyle = primaryFrame ? primaryFrame->Style() : Style();
2421 RefPtr<ComputedStyle> pseudoStyle =
2422 PresContext()->StyleSet()->ProbePseudoElementStyle(
2423 *element, PseudoStyleType::Selection, nullptr, parentStyle);
2424 if (!pseudoStyle) {
2425 return nullptr;
2426 }
2427 // When in high-contrast mode, the style system ends up ignoring the color
2428 // declarations, which means that the ::selection style becomes the inherited
2429 // color, and default background. That's no good.
2430 // When force-color-adjust is set to none allow using the color styles,
2431 // as they will not be replaced.
2432 if (PresContext()->ForcingColors() &&
2433 pseudoStyle->StyleText()->mForcedColorAdjust !=
2434 StyleForcedColorAdjust::None) {
2435 return nullptr;
2436 }
2437 return do_AddRef(pseudoStyle);
2438}
2439
2440already_AddRefed<ComputedStyle> nsIFrame::ComputeHighlightSelectionStyle(
2441 nsAtom* aHighlightName) {
2442 Element* element = FindElementAncestorForMozSelection(GetContent());
2443 if (!element) {
2444 return nullptr;
2445 }
2446 nsIFrame* primaryFrame = element->GetPrimaryFrame();
2447 ComputedStyle* parentStyle = primaryFrame ? primaryFrame->Style() : Style();
2448 return PresContext()->StyleSet()->ProbePseudoElementStyle(
2449 *element, PseudoStyleType::Highlight, aHighlightName, parentStyle);
2450}
2451
2452already_AddRefed<ComputedStyle> nsIFrame::ComputeTargetTextStyle() const {
2453 const Element* element = FindElementAncestorForMozSelection(GetContent());
2454 if (!element) {
2455 return nullptr;
2456 }
2457 nsIFrame* primaryFrame = element->GetPrimaryFrame();
2458 ComputedStyle* parentStyle = primaryFrame ? primaryFrame->Style() : Style();
2459 RefPtr pseudoStyle = PresContext()->StyleSet()->ProbePseudoElementStyle(
2460 *element, PseudoStyleType::TargetText, nullptr, parentStyle);
2461 if (!pseudoStyle) {
2462 return nullptr;
2463 }
2464 if (PresContext()->ForcingColors() &&
2465 pseudoStyle->StyleText()->mForcedColorAdjust !=
2466 StyleForcedColorAdjust::None) {
2467 return nullptr;
2468 }
2469 return pseudoStyle.forget();
2470}
2471
2472nsTextControlFrame* nsIFrame::GetContainingTextControlFrame() const {
2473 for (const nsIFrame* cur = this; cur; cur = cur->GetParent()) {
2474 if (const nsTextControlFrame* tc = do_QueryFrame(cur)) {
2475 return const_cast<nsTextControlFrame*>(tc);
2476 }
2477 if (cur->Style()->IsAnonBox()) {
2478 // Anon boxes could belong to the <input> / <textarea> itself.
2479 continue;
2480 }
2481 auto* content = cur->GetContent();
2482 if (!content || !content->IsInNativeAnonymousSubtree()) {
2483 // All content inside text controls is anonymous.
2484 break;
2485 }
2486 }
2487 return nullptr;
2488}
2489
2490bool nsIFrame::CanBeDynamicReflowRoot() const {
2491 const auto& display = *StyleDisplay();
2492 if (IsLineParticipant() || display.mDisplay.IsRuby() ||
2493 display.IsInnerTableStyle() ||
2494 display.DisplayInside() == StyleDisplayInside::Table) {
2495 // We have a display type where 'width' and 'height' don't actually set the
2496 // width or height (i.e., the size depends on content).
2497 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"
, 2498); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!HasAnyStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT)"
") (" "should not have dynamic reflow root bit" ")"); do { MOZ_CrashSequence
(__null, 2498); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
2498 "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"
, 2498); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!HasAnyStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT)"
") (" "should not have dynamic reflow root bit" ")"); do { MOZ_CrashSequence
(__null, 2498); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2499 return false;
2500 }
2501
2502 // In general, frames that have contain:layout+size can be reflow roots.
2503 // (One exception: table-wrapper frames don't work well as reflow roots,
2504 // because their inner-table ReflowInput init path tries to reuse & deref
2505 // the wrapper's containing block's reflow input, which may be null if we
2506 // initiate reflow from the table-wrapper itself.)
2507 //
2508 // Changes to `contain` force frame reconstructions, so we used to use
2509 // NS_FRAME_REFLOW_ROOT, this bit could be set for the whole lifetime of
2510 // this frame. But after the support of `content-visibility: auto` which
2511 // is with contain layout + size when it's not relevant to user, and only
2512 // with contain layout when it is relevant. The frame does not reconstruct
2513 // when the relevancy changes. So we use NS_FRAME_DYNAMIC_REFLOW_ROOT instead.
2514 //
2515 // We place it above the pref check on purpose, to make sure it works for
2516 // containment even with the pref disabled.
2517 if (display.IsContainLayout() && GetContainSizeAxes().IsBoth()) {
2518 return true;
2519 }
2520
2521 if (!StaticPrefs::layout_dynamic_reflow_roots_enabled()) {
2522 return false;
2523 }
2524
2525 // We can't serve as a dynamic reflow root if our used 'width' and 'height'
2526 // might be influenced by content.
2527 //
2528 // FIXME: For display:block, we should probably optimize inline-size: auto.
2529 // FIXME: Other flex and grid cases?
2530 const auto& pos = *StylePosition();
2531 const auto anchorResolutionParams = AnchorPosResolutionParams::From(this);
2532 const auto width = pos.GetWidth(anchorResolutionParams);
2533 const auto height = pos.GetHeight(anchorResolutionParams);
2534 if (!width->IsLengthPercentage() || width->HasPercent() ||
2535 !height->IsLengthPercentage() || height->HasPercent() ||
2536 IsIntrinsicKeyword(*pos.GetMinWidth(anchorResolutionParams)) ||
2537 IsIntrinsicKeyword(*pos.GetMaxWidth(anchorResolutionParams)) ||
2538 IsIntrinsicKeyword(*pos.GetMinHeight(anchorResolutionParams)) ||
2539 IsIntrinsicKeyword(*pos.GetMaxHeight(anchorResolutionParams)) ||
2540 ((pos.GetMinWidth(anchorResolutionParams)->IsAuto() ||
2541 pos.GetMinHeight(anchorResolutionParams)->IsAuto()) &&
2542 IsFlexOrGridItem())) {
2543 return false;
2544 }
2545
2546 // If our flex-basis is 'auto', it'll defer to 'width' (or 'height') which
2547 // we've already checked. Otherwise, it preempts them, so we need to
2548 // perform the same "could-this-value-be-influenced-by-content" checks that
2549 // we performed for 'width' and 'height' above.
2550 if (IsFlexItem()) {
2551 const auto& flexBasis = pos.mFlexBasis;
2552 if (!flexBasis.IsAuto()) {
2553 if (!flexBasis.IsSize() || !flexBasis.AsSize().IsLengthPercentage() ||
2554 flexBasis.AsSize().HasPercent()) {
2555 return false;
2556 }
2557 }
2558 }
2559
2560 if (!IsFixedPosContainingBlock()) {
2561 // We can't treat this frame as a reflow root, since dynamic changes
2562 // to absolutely-positioned frames inside of it require that we
2563 // reflow the placeholder before we reflow the absolutely positioned
2564 // frame.
2565 // FIXME: Alternatively, we could sort the reflow roots in
2566 // PresShell::ProcessReflowCommands by depth in the tree, from
2567 // deepest to least deep. However, for performance (FIXME) we
2568 // should really be sorting them in the opposite order!
2569 return false;
2570 }
2571
2572 // If we participate in a container's block reflow context, or margins
2573 // can collapse through us, we can't be a dynamic reflow root.
2574 // (NS_BLOCK_BFC is block specific bit, check first as an optimization, it's
2575 // okay because we also check that it is a block frame.)
2576 if (!HasAnyStateBits(NS_BLOCK_BFC) && IsBlockFrameOrSubclass()) {
2577 return false;
2578 }
2579
2580 // Subgrids are never reflow roots, but 'contain:layout/paint' prevents
2581 // creating a subgrid in the first place.
2582 if (pos.mGridTemplateColumns.IsSubgrid() ||
2583 pos.mGridTemplateRows.IsSubgrid()) {
2584 // NOTE: we could check that 'display' of our parent's primary frame is
2585 // '[inline-]grid' here but that's probably not worth it in practice.
2586 if (!display.IsContainLayout() && !display.IsContainPaint()) {
2587 return false;
2588 }
2589 }
2590
2591 // If we are split, we can't be a dynamic reflow root. Our reflow status may
2592 // change after reflow, and our parent is responsible to create or delete our
2593 // next-in-flow.
2594 if (GetPrevContinuation() || GetNextContinuation()) {
2595 return false;
2596 }
2597
2598 return true;
2599}
2600
2601/********************************************************
2602 * Refreshes each content's frame
2603 *********************************************************/
2604
2605void nsIFrame::DisplayOutlineUnconditional(nsDisplayListBuilder* aBuilder,
2606 const nsDisplayListSet& aLists) {
2607 // Per https://drafts.csswg.org/css-tables-3/#global-style-overrides:
2608 // "All css properties of table-column and table-column-group boxes are
2609 // ignored, except when explicitly specified by this specification."
2610 // CSS outlines fall into this category, so we skip them on these boxes.
2611 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"
, 2611); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!IsTableColGroupFrame() && !IsTableColFrame()"
")"); do { MOZ_CrashSequence(__null, 2611); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2612 const auto& outline = *StyleOutline();
2613
2614 if (!outline.ShouldPaintOutline()) {
2615 return;
2616 }
2617
2618 // Outlines are painted by the table wrapper frame.
2619 if (IsTableFrame()) {
2620 return;
2621 }
2622
2623 if (HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT) &&
2624 ScrollableOverflowRect().IsEmpty()) {
2625 // Skip parts of IB-splits with an empty overflow rect, see bug 434301.
2626 // We may still want to fix some of the overflow area calculations over in
2627 // that bug.
2628 return;
2629 }
2630
2631 // We don't display outline-style: auto on themed frames that have their own
2632 // focus indicators.
2633 if (outline.mOutlineStyle.IsAuto()) {
2634 auto* disp = StyleDisplay();
2635 if (IsThemed(disp) && PresContext()->Theme()->ThemeDrawsFocusForWidget(
2636 this, disp->EffectiveAppearance())) {
2637 return;
2638 }
2639 }
2640
2641 aLists.Outlines()->AppendNewToTop<nsDisplayOutline>(aBuilder, this);
2642}
2643
2644void nsIFrame::DisplayOutline(nsDisplayListBuilder* aBuilder,
2645 const nsDisplayListSet& aLists) {
2646 if (!IsVisibleForPainting()) {
2647 return;
2648 }
2649
2650 DisplayOutlineUnconditional(aBuilder, aLists);
2651}
2652
2653void nsIFrame::DisplayInsetBoxShadowUnconditional(
2654 nsDisplayListBuilder* aBuilder, nsDisplayList* aList) {
2655 // XXXbz should box-shadow for rows/rowgroups/columns/colgroups get painted
2656 // just because we're visible? Or should it depend on the cell visibility
2657 // when we're not the whole table?
2658 const auto* effects = StyleEffects();
2659 if (effects->HasBoxShadowWithInset(true)) {
2660 aList->AppendNewToTop<nsDisplayBoxShadowInner>(aBuilder, this);
2661 }
2662}
2663
2664void nsIFrame::DisplayInsetBoxShadow(nsDisplayListBuilder* aBuilder,
2665 nsDisplayList* aList) {
2666 if (!IsVisibleForPainting()) {
2667 return;
2668 }
2669
2670 DisplayInsetBoxShadowUnconditional(aBuilder, aList);
2671}
2672
2673void nsIFrame::DisplayOutsetBoxShadowUnconditional(
2674 nsDisplayListBuilder* aBuilder, nsDisplayList* aList) {
2675 // XXXbz should box-shadow for rows/rowgroups/columns/colgroups get painted
2676 // just because we're visible? Or should it depend on the cell visibility
2677 // when we're not the whole table?
2678 const auto* effects = StyleEffects();
2679 if (effects->HasBoxShadowWithInset(false)) {
2680 aList->AppendNewToTop<nsDisplayBoxShadowOuter>(aBuilder, this);
2681 }
2682}
2683
2684void nsIFrame::DisplayOutsetBoxShadow(nsDisplayListBuilder* aBuilder,
2685 nsDisplayList* aList) {
2686 if (!IsVisibleForPainting()) {
2687 return;
2688 }
2689
2690 DisplayOutsetBoxShadowUnconditional(aBuilder, aList);
2691}
2692
2693void nsIFrame::DisplayCaret(nsDisplayListBuilder* aBuilder,
2694 nsDisplayList* aList) {
2695 if (!IsVisibleForPainting()) {
2696 return;
2697 }
2698
2699 aList->AppendNewToTop<nsDisplayCaret>(aBuilder, this);
2700}
2701
2702nscolor nsIFrame::GetCaretColorAt(int32_t aOffset) {
2703 return nsLayoutUtils::GetTextColor(this, &nsStyleUI::mCaretColor);
2704}
2705
2706auto nsIFrame::ComputeShouldPaintBackground() const -> ShouldPaintBackground {
2707 nsPresContext* pc = PresContext();
2708 ShouldPaintBackground settings{pc->GetBackgroundColorDraw(),
2709 pc->GetBackgroundImageDraw()};
2710 if (settings.mColor && settings.mImage) {
2711 return settings;
2712 }
2713
2714 if (StyleVisibility()->mPrintColorAdjust == StylePrintColorAdjust::Exact) {
2715 return {true, true};
2716 }
2717
2718 return settings;
2719}
2720
2721bool nsIFrame::DisplayBackgroundUnconditional(nsDisplayListBuilder* aBuilder,
2722 const nsDisplayListSet& aLists) {
2723 if (aBuilder->IsForEventDelivery() && !aBuilder->HitTestIsForVisibility()) {
2724 // For hit-testing, we generally just need a light-weight data structure
2725 // like nsDisplayEventReceiver. But if the hit-testing is for visibility,
2726 // then we need to know the opaque region in order to determine whether to
2727 // stop or not.
2728 aLists.BorderBackground()->AppendNewToTop<nsDisplayEventReceiver>(aBuilder,
2729 this);
2730 return false;
2731 }
2732
2733 const AppendedBackgroundType result =
2734 nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
2735 aBuilder, this,
2736 GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this),
2737 aLists.BorderBackground());
2738
2739 if (result == AppendedBackgroundType::None) {
2740 aBuilder->BuildCompositorHitTestInfoIfNeeded(this,
2741 aLists.BorderBackground());
2742 }
2743
2744 return result == AppendedBackgroundType::ThemedBackground;
2745}
2746
2747void nsIFrame::DisplayBorderBackgroundOutline(nsDisplayListBuilder* aBuilder,
2748 const nsDisplayListSet& aLists) {
2749 // The visibility check belongs here since child elements have the
2750 // opportunity to override the visibility property and display even if
2751 // their parent is hidden.
2752 if (!IsVisibleForPainting()) {
2753 return;
2754 }
2755
2756 DisplayOutsetBoxShadowUnconditional(aBuilder, aLists.BorderBackground());
2757
2758 bool bgIsThemed = DisplayBackgroundUnconditional(aBuilder, aLists);
2759 DisplayInsetBoxShadowUnconditional(aBuilder, aLists.BorderBackground());
2760
2761 // If there's a themed background, we should not create a border item.
2762 // It won't be rendered.
2763 // Don't paint borders for tables here, since they paint them in a different
2764 // order.
2765 if (!bgIsThemed && StyleBorder()->HasBorder() && !IsTableFrame()) {
2766 aLists.BorderBackground()->AppendNewToTop<nsDisplayBorder>(aBuilder, this);
2767 }
2768
2769 DisplayOutlineUnconditional(aBuilder, aLists);
2770}
2771
2772inline static bool IsSVGContentWithCSSClip(const nsIFrame* aFrame) {
2773 // The CSS spec says that the 'clip' property only applies to absolutely
2774 // positioned elements, whereas the SVG spec says that it applies to SVG
2775 // elements regardless of the value of the 'position' property. Here we obey
2776 // the CSS spec for outer-<svg> (since that's what we generally do), but
2777 // obey the SVG spec for other SVG elements to which 'clip' applies.
2778 return aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT) &&
2779 aFrame->GetContent()->IsAnyOfSVGElements(nsGkAtoms::svg,
2780 nsGkAtoms::foreignObject);
2781}
2782
2783Maybe<nsRect> nsIFrame::GetClipPropClipRect(const nsStyleDisplay* aDisp,
2784 const nsStyleEffects* aEffects,
2785 const nsSize& aSize) const {
2786 if (aEffects->mClip.IsAuto() ||
2787 !(aDisp->IsAbsolutelyPositioned(this) || IsSVGContentWithCSSClip(this))) {
2788 return Nothing();
2789 }
2790
2791 auto& clipRect = aEffects->mClip.AsRect();
2792 nsRect rect = clipRect.ToLayoutRect();
2793 if (MOZ_LIKELY(StyleBorder()->mBoxDecorationBreak ==(__builtin_expect(!!(StyleBorder()->mBoxDecorationBreak ==
StyleBoxDecorationBreak::Slice), 1))
2794 StyleBoxDecorationBreak::Slice)(__builtin_expect(!!(StyleBorder()->mBoxDecorationBreak ==
StyleBoxDecorationBreak::Slice), 1))
) {
2795 // The clip applies to the joined boxes so it's relative the first
2796 // continuation.
2797 nscoord y = 0;
2798 for (nsIFrame* f = GetPrevContinuation(); f; f = f->GetPrevContinuation()) {
2799 y += f->GetRect().height;
2800 }
2801 rect.MoveBy(nsPoint(0, -y));
2802 }
2803
2804 if (clipRect.right.IsAuto()) {
2805 rect.width = aSize.width - rect.x;
2806 }
2807 if (clipRect.bottom.IsAuto()) {
2808 rect.height = aSize.height - rect.y;
2809 }
2810 return Some(rect);
2811}
2812
2813// https://drafts.csswg.org/css-view-transitions-1/#named-and-transitioning
2814//
2815// Note https://github.com/w3c/csswg-drafts/issues/11772, however, for the root
2816// style check.
2817bool nsIFrame::ForcesStackingContextForViewTransition() const {
2818 auto* style = Style();
2819 return !style->IsRootElementStyle() &&
2820 (style->StyleUIReset()->HasViewTransitionName() ||
2821 HasAnyStateBits(NS_FRAME_CAPTURED_IN_VIEW_TRANSITION) ||
2822 style->StyleDisplay()->mWillChange.bits &
2823 mozilla::StyleWillChangeBits::VIEW_TRANSITION_NAME);
2824}
2825
2826/**
2827 * If the CSS 'overflow' property applies to this frame, and is not
2828 * handled by constructing a dedicated nsHTML/XULScrollFrame, set up clipping
2829 * for that overflow in aBuilder->ClipState() to clip all containing-block
2830 * descendants.
2831 */
2832static void ApplyOverflowClipping(
2833 nsDisplayListBuilder* aBuilder, const nsIFrame* aFrame,
2834 PhysicalAxes aClipAxes,
2835 DisplayListClipState::AutoClipMultiple& aClipState) {
2836 nsRect clipRect;
2837 nsRectCornerRadii radii;
2838 nsMargin inset;
2839 bool haveRadii = aFrame->ComputeOverflowClipRectRelativeToSelf(
2840 aClipAxes, clipRect, radii, inset);
2841 aClipState.ClipContainingBlockDescendantsExtra(
2842 clipRect + aBuilder->ToReferenceFrame(aFrame),
2843 haveRadii ? &radii : nullptr, haveRadii ? &inset : nullptr);
2844}
2845
2846static Sides ToSkipSides(PhysicalAxes aClipAxes) {
2847 SideBits result{};
2848 if (!aClipAxes.contains(PhysicalAxis::Vertical)) {
2849 result |= SideBits::eTop;
2850 result |= SideBits::eBottom;
2851 }
2852 if (!aClipAxes.contains(PhysicalAxis::Horizontal)) {
2853 result |= SideBits::eLeft;
2854 result |= SideBits::eRight;
2855 }
2856 return Sides(result);
2857}
2858
2859bool nsIFrame::ComputeOverflowClipRectRelativeToSelf(
2860 const PhysicalAxes aClipAxes, nsRect& aOutRect,
2861 nsRectCornerRadii& aOutRadii, nsMargin& aOutInset) const {
2862 // Only 'clip' is handled here (and 'hidden' for table frames, and any
2863 // non-'visible' value for blocks in a paginated context).
2864 // We allow 'clip' to apply to any kind of frame. This is required by
2865 // comboboxes which make their display text (an inline frame) have clipping.
2866 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", 2866); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!aClipAxes.isEmpty()" ")"); do { MOZ_CrashSequence
(__null, 2866); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
2867 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"
, 2867); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ShouldApplyOverflowClipping(StyleDisplay()) == aClipAxes"
")"); do { MOZ_CrashSequence(__null, 2867); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2868 auto boxMargin = OverflowClipMargin(aClipAxes, /* aAllowNegative = */ true);
2869 boxMargin.ApplySkipSides(GetSkipSides() | ToSkipSides(aClipAxes));
2870 aOutInset = -boxMargin;
2871
2872 aOutRect = nsRect(nsPoint(), GetSize());
2873 aOutRect.Inflate(boxMargin);
2874 if (MOZ_UNLIKELY(!aClipAxes.contains(PhysicalAxis::Horizontal))(__builtin_expect(!!(!aClipAxes.contains(PhysicalAxis::Horizontal
)), 0))
) {
2875 // NOTE(mats) We shouldn't be clipping at all in this dimension really,
2876 // but clipping in just one axis isn't supported by our GFX APIs so we
2877 // clip to our visual overflow rect instead.
2878 nsRect o = InkOverflowRectRelativeToSelf();
2879 aOutRect.x = o.x;
2880 aOutRect.width = o.width;
2881 }
2882 if (MOZ_UNLIKELY(!aClipAxes.contains(PhysicalAxis::Vertical))(__builtin_expect(!!(!aClipAxes.contains(PhysicalAxis::Vertical
)), 0))
) {
2883 // See the note above.
2884 nsRect o = InkOverflowRectRelativeToSelf();
2885 aOutRect.y = o.y;
2886 aOutRect.height = o.height;
2887 }
2888 if (!GetBorderRadii(aOutRadii)) {
2889 return false;
2890 }
2891 aOutRadii.AdjustOutwards(boxMargin);
2892 return true;
2893}
2894
2895nsMargin nsIFrame::OverflowClipMargin(PhysicalAxes aClipAxes,
2896 bool aAllowNegative) const {
2897 nsMargin result;
2898 if (aClipAxes.isEmpty()) {
2899 return result;
2900 }
2901 const auto& margin = StyleMargin()->mOverflowClipMargin;
2902 if (!aAllowNegative && margin.offset.IsZero()) {
2903 return result;
2904 }
2905 switch (margin.visual_box) {
2906 case StyleOverflowClipMarginBox::BorderBox:
2907 break;
2908 case StyleOverflowClipMarginBox::PaddingBox:
2909 result = -GetUsedBorder();
2910 break;
2911 case StyleOverflowClipMarginBox::ContentBox:
2912 result = -GetUsedBorderAndPadding();
2913 break;
2914 }
2915 if (!margin.offset.IsZero()) {
2916 nscoord marginAu = margin.offset.ToAppUnits();
2917 result += nsMargin(marginAu, marginAu, marginAu, marginAu);
2918 }
2919 if (!aAllowNegative) {
2920 result.EnsureAtLeast(nsMargin());
2921 }
2922 return result;
2923}
2924
2925/**
2926 * Returns whether a display item that gets created with the builder's current
2927 * state will have a scrolled clip, i.e. a clip that is scrolled by a scroll
2928 * frame which does not move the item itself.
2929 */
2930static bool BuilderHasScrolledClip(nsDisplayListBuilder* aBuilder) {
2931 const DisplayItemClipChain* currentClip =
2932 aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder);
2933 if (!currentClip) {
2934 return false;
2935 }
2936
2937 const ActiveScrolledRoot* currentClipASR = currentClip->mASR;
2938 const ActiveScrolledRoot* currentASR = aBuilder->CurrentActiveScrolledRoot();
2939 return ActiveScrolledRoot::PickDescendant(currentClipASR, currentASR) !=
2940 currentASR;
2941}
2942
2943class AutoTrackStackingContextBits {
2944 nsDisplayListBuilder& mBuilder;
2945 StackingContextBits mBitsToSet;
2946
2947 public:
2948 explicit AutoTrackStackingContextBits(nsDisplayListBuilder& aBuilder)
2949 : mBuilder(aBuilder), mBitsToSet(aBuilder.GetStackingContextBits()) {}
2950
2951 ~AutoTrackStackingContextBits() {
2952 mBuilder.SetStackingContextBits(mBitsToSet);
2953 }
2954
2955 void AddToParent(StackingContextBits aBits) { mBitsToSet |= aBits; }
2956};
2957
2958static bool IsFrameOrAncestorApzAware(nsIFrame* aFrame) {
2959 nsIContent* node = aFrame->GetContent();
2960 if (!node) {
2961 return false;
2962 }
2963
2964 do {
2965 if (node->IsNodeApzAware()) {
2966 return true;
2967 }
2968 nsIContent* shadowRoot = node->GetShadowRoot();
2969 if (shadowRoot && shadowRoot->IsNodeApzAware()) {
2970 return true;
2971 }
2972
2973 // Even if the node owning aFrame doesn't have apz-aware event listeners
2974 // itself, its shadow root or display: contents ancestors (which have no
2975 // frames) might, so we need to account for them too.
2976 } while ((node = node->GetFlattenedTreeParent()) && node->IsElement() &&
2977 node->AsElement()->IsDisplayContents());
2978
2979 return false;
2980}
2981
2982static void CheckForApzAwareEventHandlers(nsDisplayListBuilder* aBuilder,
2983 nsIFrame* aFrame) {
2984 if (aBuilder->GetAncestorHasApzAwareEventHandler()) {
2985 return;
2986 }
2987
2988 if (IsFrameOrAncestorApzAware(aFrame)) {
2989 aBuilder->SetAncestorHasApzAwareEventHandler(true);
2990 }
2991}
2992
2993static void UpdateCurrentHitTestInfo(nsDisplayListBuilder* aBuilder,
2994 nsIFrame* aFrame) {
2995 if (!aBuilder->BuildCompositorHitTestInfo()) {
2996 // Compositor hit test info is not used.
2997 return;
2998 }
2999
3000 CheckForApzAwareEventHandlers(aBuilder, aFrame);
3001
3002 const CompositorHitTestInfo info =
3003 aFrame->GetCompositorHitTestInfoWithoutPointerEvents(aBuilder);
3004 aBuilder->SetInheritedCompositorHitTestInfo(info);
3005}
3006
3007/**
3008 * True if aDescendant participates the context aAncestor participating.
3009 */
3010static bool FrameParticipatesIn3DContext(nsIFrame* aAncestor,
3011 nsIFrame* aDescendant) {
3012 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", 3012); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aAncestor != aDescendant" ")"); do { MOZ_CrashSequence
(__null, 3012); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3013 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", 3013); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aAncestor->GetContent() != aDescendant->GetContent()"
")"); do { MOZ_CrashSequence(__null, 3013); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3014 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"
, 3014); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aAncestor->Extend3DContext()"
")"); do { MOZ_CrashSequence(__null, 3014); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3015
3016 nsIFrame* ancestor = aAncestor->FirstContinuation();
3017 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"
, 3017); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ancestor->IsPrimaryFrame()"
")"); do { MOZ_CrashSequence(__null, 3017); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3018
3019 nsIFrame* frame;
3020 for (frame = aDescendant->GetClosestFlattenedTreeAncestorPrimaryFrame();
3021 frame && ancestor != frame;
3022 frame = frame->GetClosestFlattenedTreeAncestorPrimaryFrame()) {
3023 if (!frame->Extend3DContext()) {
3024 return false;
3025 }
3026 }
3027
3028 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", 3028); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "frame == ancestor" ")"); do { MOZ_CrashSequence
(__null, 3028); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
3029 return true;
3030}
3031
3032static bool ItemParticipatesIn3DContext(nsIFrame* aAncestor,
3033 nsDisplayItem* aItem) {
3034 auto type = aItem->GetType();
3035 const bool isContainer = type == DisplayItemType::TYPE_WRAP_LIST ||
3036 type == DisplayItemType::TYPE_CONTAINER;
3037
3038 if (isContainer && aItem->GetChildren()->Length() == 1) {
3039 // If the wraplist has only one child item, use the type of that item.
3040 type = aItem->GetChildren()->GetBottom()->GetType();
3041 }
3042
3043 if (type != DisplayItemType::TYPE_TRANSFORM &&
3044 type != DisplayItemType::TYPE_PERSPECTIVE) {
3045 return false;
3046 }
3047 nsIFrame* transformFrame = aItem->Frame();
3048 if (aAncestor->GetContent() == transformFrame->GetContent()) {
3049 return true;
3050 }
3051 return FrameParticipatesIn3DContext(aAncestor, transformFrame);
3052}
3053
3054static void WrapSeparatorTransform(nsDisplayListBuilder* aBuilder,
3055 nsIFrame* aFrame,
3056 nsDisplayList* aNonParticipants,
3057 nsDisplayList* aParticipants, int aIndex,
3058 nsDisplayItem** aSeparator) {
3059 if (aNonParticipants->IsEmpty()) {
3060 return;
3061 }
3062
3063 nsDisplayTransform* item = MakeDisplayItemWithIndex<nsDisplayTransform>(
3064 aBuilder, aFrame, aIndex, aNonParticipants, aBuilder->GetVisibleRect());
3065
3066 if (*aSeparator == nullptr && item) {
3067 *aSeparator = item;
3068 }
3069
3070 aParticipants->AppendToTop(item);
3071}
3072
3073static bool HitTestsCapturedElementsAtLivePosition(nsPresContext* aPc) {
3074 switch (StaticPrefs::dom_viewTransitions_hit_testing()) {
3075 case 0:
3076 return false;
3077 case 1:
3078 return true;
3079 default:
3080 return aPc->IsChrome();
3081 }
3082}
3083
3084// Try to compute a clip rect to bound the contents of the mask item
3085// that will be built for |aMaskedFrame|. If we're not able to compute
3086// one, return an empty Maybe.
3087// The returned clip rect, if there is one, is relative to |aMaskedFrame|.
3088// Bug 1958124: If this is inlined in
3089// nsIFrame::BuildDisplayListForStackingContext on Win32, its variables enlarge
3090// that function's stack frame, which is on a deeply recursive path that is
3091// already up against the stack limit. Only masked frames reach this code, so
3092// never inlining it on Win32 costs us nothing.
3093#if defined(XP_WIN) && !defined(_WIN64)
3094MOZ_NEVER_INLINE__attribute__((noinline))
3095#endif
3096static Maybe<nsRect> ComputeClipForMaskItem(
3097 nsDisplayListBuilder* aBuilder, nsIFrame* aMaskedFrame,
3098 const SVGUtils::MaskUsage& aMaskUsage) {
3099 const nsStyleSVGReset* svgReset = aMaskedFrame->StyleSVGReset();
3100
3101 nsPoint offsetToUserSpace =
3102 nsLayoutUtils::ComputeOffsetToUserSpace(aBuilder, aMaskedFrame);
3103 int32_t devPixelRatio = aMaskedFrame->PresContext()->AppUnitsPerDevPixel();
3104 gfxPoint devPixelOffsetToUserSpace =
3105 nsLayoutUtils::PointToGfxPoint(offsetToUserSpace, devPixelRatio);
3106 CSSToLayoutDeviceScale cssToDevScale =
3107 aMaskedFrame->PresContext()->CSSToDevPixelScale();
3108
3109 nsPoint toReferenceFrame;
3110 aBuilder->FindReferenceFrameFor(aMaskedFrame, &toReferenceFrame);
3111
3112 Maybe<gfxRect> combinedClip;
3113 if (aMaskUsage.ShouldApplyBasicShapeOrPath()) {
3114 Maybe<Rect> result =
3115 CSSClipPathInstance::GetBoundingRectForBasicShapeOrPathClip(
3116 aMaskedFrame, svgReset->mClipPath);
3117 if (result) {
3118 combinedClip = Some(ThebesRect(*result));
3119 }
3120 } else if (aMaskUsage.ShouldApplyClipPath()) {
3121 gfxRect result = SVGUtils::GetBBox(
3122 aMaskedFrame,
3123 {SVGBBoxFlag::IncludeClipped, SVGBBoxFlag::IncludeFillGeometry,
3124 SVGBBoxFlag::IncludeMarkers, SVGBBoxFlag::IncludeStroke,
3125 SVGBBoxFlag::DoNotClipToBBoxOfContentInsideClipPath});
3126 combinedClip = Some(
3127 ThebesRect((CSSRect::FromUnknownRect(ToRect(result)) * cssToDevScale)
3128 .ToUnknownRect()));
3129 } else {
3130 // The code for this case is adapted from ComputeMaskGeometry().
3131
3132 nsRect borderArea(toReferenceFrame, aMaskedFrame->GetSize());
3133 borderArea -= offsetToUserSpace;
3134
3135 // Use an infinite dirty rect to pass into nsCSSRendering::
3136 // GetImageLayerClip() because we don't have an actual dirty rect to
3137 // pass in. This is fine because the only time GetImageLayerClip() will
3138 // not intersect the incoming dirty rect with something is in the "NoClip"
3139 // case, and we handle that specially.
3140 nsRect dirtyRect(nscoord_MIN / 2, nscoord_MIN / 2, nscoord_MAX,
3141 nscoord_MAX);
3142
3143 nsIFrame* firstFrame =
3144 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aMaskedFrame);
3145 nsTArray<SVGMaskFrame*> maskFrames;
3146 // XXX check return value?
3147 SVGObserverUtils::GetAndObserveMasks(firstFrame, &maskFrames);
3148
3149 for (uint32_t i = 0; i < maskFrames.Length(); ++i) {
3150 gfxRect clipArea;
3151 if (maskFrames[i]) {
3152 clipArea = maskFrames[i]->GetMaskArea(aMaskedFrame);
3153 clipArea = ThebesRect(
3154 (CSSRect::FromUnknownRect(ToRect(clipArea)) * cssToDevScale)
3155 .ToUnknownRect());
3156 } else {
3157 const auto& layer = svgReset->mMask.mLayers[i];
3158 if (layer.mClip == StyleBackgroundClip::NoClip) {
3159 return Nothing();
3160 }
3161
3162 nsCSSRendering::ImageLayerClipState clipState;
3163 nsCSSRendering::GetImageLayerClip(
3164 layer, aMaskedFrame, *aMaskedFrame->StyleBorder(), borderArea,
3165 dirtyRect, false /* aWillPaintBorder */, devPixelRatio, &clipState);
3166 clipArea = clipState.mDirtyRectInDevPx;
3167 }
3168 combinedClip = UnionMaybeRects(combinedClip, Some(clipArea));
3169 }
3170 }
3171 if (combinedClip) {
3172 // Convert to user space.
3173 *combinedClip += devPixelOffsetToUserSpace;
3174
3175 // Round the clip out. In FrameLayerBuilder we round clips to nearest
3176 // pixels, and if we have a really thin clip here, that can cause the
3177 // clip to become empty if we didn't round out here.
3178 // The rounding happens in coordinates that are relative to the reference
3179 // frame, which matches what FrameLayerBuilder does.
3180 combinedClip->RoundOut();
3181
3182 // Convert to app units.
3183 nsRect result =
3184 nsLayoutUtils::RoundGfxRectToAppRect(*combinedClip, devPixelRatio);
3185
3186 // The resulting clip is relative to the reference frame, but the caller
3187 // expects it to be relative to the masked frame, so adjust it.
3188 result -= toReferenceFrame;
3189 return Some(result);
3190 }
3191 return Nothing();
3192}
3193
3194struct AutoCheckBuilder {
3195 explicit AutoCheckBuilder(nsDisplayListBuilder* aBuilder)
3196 : mBuilder(aBuilder) {
3197 aBuilder->Check();
3198 }
3199
3200 ~AutoCheckBuilder() { mBuilder->Check(); }
3201
3202 nsDisplayListBuilder* mBuilder;
3203};
3204
3205/**
3206 * Tries to reuse a top-level stacking context item from the previous paint.
3207 * Returns true if an item was reused, otherwise false.
3208 */
3209bool TryToReuseStackingContextItem(nsDisplayListBuilder* aBuilder,
3210 nsDisplayList* aList, nsIFrame* aFrame) {
3211 if (!aBuilder->IsForPainting() || !aBuilder->IsPartialUpdate() ||
3212 aBuilder->InInvalidSubtree()) {
3213 return false;
3214 }
3215
3216 if (aFrame->IsFrameModified() || aFrame->HasModifiedDescendants()) {
3217 return false;
3218 }
3219
3220 auto& items = aFrame->DisplayItems();
3221 auto* res = std::find_if(
3222 items.begin(), items.end(),
3223 [](nsDisplayItem* aItem) { return aItem->IsPreProcessed(); });
3224
3225 if (res == items.end()) {
3226 return false;
3227 }
3228
3229 nsDisplayItem* container = *res;
3230 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"
, 3230); AnnotateMozCrashReason("MOZ_ASSERT" "(" "container->Frame() == aFrame"
")"); do { MOZ_CrashSequence(__null, 3230); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3231 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)
3232 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)
;
3233
3234 aList->AppendToTop(container);
3235 aBuilder->ReuseDisplayItem(container);
3236 return true;
3237}
3238
3239void nsIFrame::BuildDisplayListForStackingContext(
3240 nsDisplayListBuilder* aBuilder, nsDisplayList* aList,
3241 bool* aCreatedContainerItem) {
3242#ifdef DEBUG1
3243 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)
;
3244 ScopeExit e(
3245 [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)
; });
3246#endif
3247
3248 AutoCheckBuilder check(aBuilder);
3249
3250 if (aBuilder->IsReusingStackingContextItems() &&
3251 TryToReuseStackingContextItem(aBuilder, aList, this)) {
3252 if (aCreatedContainerItem) {
3253 *aCreatedContainerItem = true;
3254 }
3255 return;
3256 }
3257
3258 if (HasAnyStateBits(NS_FRAME_TOO_DEEP_IN_FRAME_TREE |
3259 NS_FRAME_IS_NONDISPLAY)) {
3260 return;
3261 }
3262
3263 const auto& style = *Style();
3264 const nsStyleDisplay* disp = style.StyleDisplay();
3265 const nsStyleEffects* effects = style.StyleEffects();
3266 EffectSet* effectSetForOpacity =
3267 EffectSet::GetForFrame(this, nsCSSPropertyIDSet::OpacityProperties());
3268 // We can stop right away if this is a zero-opacity stacking context and
3269 // we're painting, and we're not animating opacity.
3270 bool needHitTestInfo = aBuilder->BuildCompositorHitTestInfo() &&
3271 Style()->PointerEvents() != StylePointerEvents::None;
3272 bool opacityItemForEventsOnly = false;
3273 if (effects->IsTransparent() && aBuilder->IsForPainting() &&
3274 !(disp->mWillChange.bits & StyleWillChangeBits::OPACITY) &&
3275 !nsLayoutUtils::HasAnimationOfPropertySet(
3276 this, nsCSSPropertyIDSet::OpacityProperties(), effectSetForOpacity)) {
3277 if (needHitTestInfo) {
3278 opacityItemForEventsOnly = true;
3279 } else {
3280 return;
3281 }
3282 }
3283
3284 // Root gets handled in
3285 // ScrollContainerFrame::MaybeCreateTopLayerAndWrapRootItems.
3286 const bool capturedByViewTransition =
3287 HasAnyStateBits(NS_FRAME_CAPTURED_IN_VIEW_TRANSITION) &&
3288 !style.IsRootElementStyle();
3289
3290 // Do not need to build the display list of the captured frames for event
3291 // delivery (i.e. to determine if this frame is under the mouse position).
3292 //
3293 // Per spec, hit-testing is skipped because the element’s DOM location does
3294 // not correspond to where its contents are rendered, so we could just skip
3295 // the building of the display list for these frames, and then these APIs,
3296 // e.g. elementFromPoint(), will skip these frames as well.
3297 // https://drafts.csswg.org/css-view-transitions-1/#view-transition-stacking-layer
3298 //
3299 // dom.viewTransitions.hit-testing can make us hit-test them at their live
3300 // position instead, which chrome documents do by default so the browser UI
3301 // stays interactive while it animates. See
3302 // https://github.com/w3c/csswg-drafts/issues/10930
3303 if (capturedByViewTransition && aBuilder->IsForEventDelivery() &&
3304 !HitTestsCapturedElementsAtLivePosition(PresContext())) {
3305 return;
3306 }
3307
3308 // For preserves3d, use the dirty rect already installed on the
3309 // builder, since aDirtyRect maybe distorted for transforms along
3310 // the chain.
3311 nsRect visibleRect = aBuilder->GetVisibleRect();
3312 nsRect dirtyRect = aBuilder->GetDirtyRect();
3313
3314 // We build an opacity item if it's not going to be drawn by SVG content.
3315 // We could in principle skip creating an nsDisplayOpacity item if
3316 // nsDisplayOpacity::NeedsActiveLayer returns false and usingSVGEffects is
3317 // true (the nsDisplayFilter/nsDisplayMasksAndClipPaths could handle the
3318 // opacity). Since SVG has perf issues where we sometimes spend a lot of
3319 // time creating display list items that might be helpful. We'd need to
3320 // restore our mechanism to do that (changed in bug 1482403), and we'd
3321 // need to invalidate the frame if the value that would be return from
3322 // NeedsActiveLayer was to change, which we don't currently do.
3323 const bool useOpacity =
3324 HasVisualOpacity(disp, effects, effectSetForOpacity) &&
3325 !SVGUtils::CanOptimizeOpacity(this);
3326
3327 const bool isTransformed = IsTransformed();
3328 const bool hasPerspective = isTransformed && HasPerspective();
3329 const bool extend3DContext =
3330 Extend3DContext(disp, effects, effectSetForOpacity);
3331 const bool combines3DTransformWithAncestors =
3332 (extend3DContext || isTransformed) && Combines3DTransformWithAncestors();
3333
3334 UniquePtr<nsDisplayListBuilder::AutoPreserves3DContext>
3335 autoPreserves3DContext;
3336 if (extend3DContext && !combines3DTransformWithAncestors) {
3337 // Start a new preserves3d context to keep informations on
3338 // nsDisplayListBuilder.
3339 autoPreserves3DContext =
3340 MakeUnique<nsDisplayListBuilder::AutoPreserves3DContext>(aBuilder);
3341 // Save dirty rect on the builder to avoid being distorted for
3342 // multiple transforms along the chain.
3343 aBuilder->SavePreserves3DRect();
3344
3345 // We rebuild everything within preserve-3d and don't try
3346 // to retain, so override the dirty rect now.
3347 if (aBuilder->IsRetainingDisplayList()) {
3348 dirtyRect = visibleRect;
3349 aBuilder->SetDisablePartialUpdates(true);
3350 }
3351 }
3352
3353 AutoTrackStackingContextBits stackingContextTracker(*aBuilder);
3354 aBuilder->ClearStackingContextBits();
3355
3356 nsRect visibleRectOutsideTransform = visibleRect;
3357 nsDisplayTransform::PrerenderInfo prerenderInfo;
3358 bool inTransform = aBuilder->IsInTransform();
3359 if (isTransformed) {
3360 prerenderInfo = nsDisplayTransform::ShouldPrerenderTransformedContent(
3361 aBuilder, this, &visibleRect);
3362
3363 switch (prerenderInfo.mDecision) {
3364 case nsDisplayTransform::PrerenderDecision::Full:
3365 case nsDisplayTransform::PrerenderDecision::Partial:
3366 dirtyRect = visibleRect;
3367 break;
3368 case nsDisplayTransform::PrerenderDecision::No: {
3369 // If we didn't prerender an animated frame in a preserve-3d context,
3370 // then we want disable async animations for the rest of the preserve-3d
3371 // (especially ancestors).
3372 if ((extend3DContext || combines3DTransformWithAncestors) &&
3373 prerenderInfo.mHasAnimations) {
3374 aBuilder->SavePreserves3DAllowAsyncAnimation(false);
3375 }
3376
3377 const nsRect overflow = InkOverflowRectRelativeToSelf();
3378 if (overflow.IsEmpty() && !extend3DContext) {
3379 return;
3380 }
3381
3382 // If we're in preserve-3d then grab the dirty rect that was given to
3383 // the root and transform using the combined transform.
3384 if (combines3DTransformWithAncestors) {
3385 visibleRect = dirtyRect = aBuilder->GetPreserves3DRect();
3386 }
3387
3388 const float appPerDev = PresContext()->AppUnitsPerDevPixel();
3389 uint32_t flags = nsDisplayTransform::kTransformRectFlags &
3390 ~nsDisplayTransform::OFFSET_BY_ORIGIN;
3391 if (!hasPerspective) {
3392 flags &= ~nsDisplayTransform::INCLUDE_PERSPECTIVE;
3393 }
3394 if (!combines3DTransformWithAncestors) {
3395 flags &= ~nsDisplayTransform::INCLUDE_PRESERVE3D_ANCESTORS;
3396 }
3397 auto transform = nsDisplayTransform::GetResultingTransformMatrix(
3398 this, nsPoint(), appPerDev, flags);
3399 nsRect untransformedDirtyRect;
3400 if (nsDisplayTransform::UntransformRect(dirtyRect, overflow, transform,
3401 appPerDev,
3402 &untransformedDirtyRect)) {
3403 dirtyRect = untransformedDirtyRect;
3404 nsDisplayTransform::UntransformRect(visibleRect, overflow, transform,
3405 appPerDev, &visibleRect);
3406 } else {
3407 // This should only happen if the transform is singular, in which case
3408 // nothing is visible anyway
3409 dirtyRect.SetEmpty();
3410 visibleRect.SetEmpty();
3411 }
3412 }
3413 }
3414 inTransform = true;
3415 } else if (IsFixedPosContainingBlock()) {
3416 // Restict the building area to the overflow rect for these frames, since
3417 // RetainedDisplayListBuilder uses it to know if the size of the stacking
3418 // context changed.
3419 visibleRect.IntersectRect(visibleRect, InkOverflowRect());
3420 dirtyRect.IntersectRect(dirtyRect, InkOverflowRect());
3421 }
3422
3423 bool hasOverrideDirtyRect = false;
3424 // If we're doing a partial build, we're not invalid and we're capable
3425 // of having an override building rect (stacking context and fixed pos
3426 // containing block), then we should assume we have one.
3427 // Either we have an explicit one, or nothing in our subtree changed and
3428 // we have an implicit empty rect.
3429 //
3430 // These conditions should match |CanStoreDisplayListBuildingRect()| in
3431 // RetainedDisplayListBuilder.cpp
3432 if (!aBuilder->IsReusingStackingContextItems() &&
3433 aBuilder->IsPartialUpdate() && !aBuilder->InInvalidSubtree() &&
3434 !IsFrameModified() && IsFixedPosContainingBlock() &&
3435 !GetPrevContinuation() && !GetNextContinuation()) {
3436 dirtyRect = nsRect();
3437 if (HasOverrideDirtyRegion()) {
3438 nsDisplayListBuilder::DisplayListBuildingData* data =
3439 GetProperty(nsDisplayListBuilder::DisplayListBuildingRect());
3440 if (data) {
3441 dirtyRect = data->mDirtyRect.Intersect(visibleRect);
3442 hasOverrideDirtyRect = true;
3443 }
3444 }
3445 }
3446
3447 const bool usingFilter = effects->HasFilters() && !style.IsRootElementStyle();
3448 const SVGUtils::MaskUsage maskUsage =
3449 SVGUtils::DetermineMaskUsage(this, false);
3450 const bool usingMask = maskUsage.UsingMaskOrClipPath();
3451 const bool usingSVGEffects = usingFilter || usingMask;
3452
3453 const nsRect visibleRectOutsideSVGEffects = visibleRect;
3454 nsDisplayList hoistedScrollInfoItemsStorage(aBuilder);
3455 if (usingSVGEffects) {
3456 dirtyRect =
3457 SVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, dirtyRect);
3458 visibleRect =
3459 SVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, visibleRect);
3460 aBuilder->EnterSVGEffectsContents(this, &hoistedScrollInfoItemsStorage);
3461 }
3462
3463 const bool useStickyPosition =
3464 disp->mPosition == StylePositionProperty::Sticky;
3465 bool shouldFlattenStickyItem = true;
3466
3467 const bool useFixedPosition =
3468 disp->mPosition == StylePositionProperty::Fixed &&
3469 aBuilder->IsPaintingToWindow() && !IsMenuPopupFrame() &&
3470 (DisplayPortUtils::IsFixedPosFrameInDisplayPort(this) ||
3471 BuilderHasScrolledClip(aBuilder));
3472
3473 if (capturedByViewTransition) {
3474 // Captured view transition elements must have their frames built regardless
3475 // of onscreen visibility so they can be snapshotted, since the snapshot can
3476 // itself be in view. We set visibleRect and dirtyRect to ensure the frame
3477 // and its descendants are painted.
3478 visibleRect = InkOverflowRectRelativeToSelf();
3479 dirtyRect = InkOverflowRectRelativeToSelf();
3480 }
3481
3482 nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList(
3483 aBuilder, this, visibleRect, dirtyRect, isTransformed);
3484
3485 UpdateCurrentHitTestInfo(aBuilder, this);
3486
3487 // Depending on the effects that are applied to this frame, we can create
3488 // multiple container display items and wrap them around our contents.
3489 // This enum lists all the potential container display items, in the order
3490 // outside to inside.
3491 enum class ContainerItemType : uint8_t {
3492 None = 0,
3493 FixedPosition,
3494 OwnLayerForTransformWithRoundedClip,
3495 Perspective,
3496 Transform,
3497 Filter,
3498 ViewTransitionCapture,
3499 };
3500
3501 // NOTE(emilio): The order of these RAII objects is quite subtle.
3502 nsDisplayListBuilder::AutoEnterViewTransitionCapture
3503 inViewTransitionCaptureSetter(aBuilder, capturedByViewTransition);
3504 RefPtr<const ActiveScrolledRoot> stickyASR = nullptr;
3505 nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(aBuilder);
3506 if (aBuilder->IsInViewTransitionCapture()) {
3507 // View transition contents shouldn't scroll along our ASR. They get
3508 // "pulled out" of the rendering (or when they don't, you can't scroll
3509 // anyways).
3510 asrSetter.SetCurrentActiveScrolledRoot(nullptr);
3511 }
3512 DisplayListClipState::AutoSaveRestore stickyItemClipState(aBuilder);
3513 if (useStickyPosition) {
3514 StickyScrollContainer* stickyScrollContainer =
3515 StickyScrollContainer::GetOrCreateForFrame(this);
3516 if (stickyScrollContainer && aBuilder->IsPaintingToWindow()) {
3517 if (!aBuilder->IsInViewTransitionCapture() &&
3518 stickyScrollContainer->ScrollContainer()
3519 ->IsMaybeAsynchronouslyScrolled()) {
3520 shouldFlattenStickyItem = false;
3521 }
3522 // The flattening decision stored on the StickyScrollContainer is shared
3523 // by every sticky frame that it scrolls, so only store decisions which
3524 // apply to all of them. Being inside a view transition capture flattens
3525 // just the captured frame (which gets no display item at all), so
3526 // storing that decision would incorrectly flatten the container's other
3527 // sticky frames as well.
3528 if (!aBuilder->IsInViewTransitionCapture()) {
3529 stickyScrollContainer->SetShouldFlatten(shouldFlattenStickyItem);
3530 }
3531 }
3532
3533 if (shouldFlattenStickyItem) {
3534 stickyASR = aBuilder->CurrentActiveScrolledRoot();
3535 } else {
3536 stickyASR = aBuilder->GetOrCreateActiveScrolledRoot(
3537 aBuilder->CurrentActiveScrolledRoot(), this,
3538 ActiveScrolledRoot::ASRKind::Sticky);
3539 asrSetter.SetCurrentActiveScrolledRoot(stickyASR);
3540 stickyItemClipState.MaybeRemoveDisplayportClip();
3541 }
3542 }
3543
3544 nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
3545
3546 auto cssClip = GetClipPropClipRect(disp, effects, GetSize());
3547 auto ApplyClipProp = [&](DisplayListClipState::AutoSaveRestore& aClipState) {
3548 if (!cssClip) {
3549 return;
3550 }
3551 nsPoint offset = aBuilder->GetCurrentFrameOffsetToReferenceFrame();
3552 aBuilder->IntersectDirtyRect(*cssClip);
3553 aBuilder->IntersectVisibleRect(*cssClip);
3554 aClipState.ClipContentDescendants(*cssClip + offset);
3555 };
3556
3557 // The CSS clip property is effectively inside the transform, but outside the
3558 // filters. So if we're not transformed we can apply it just here for
3559 // simplicity, instead of on each of the places that handle clipCapturedBy.
3560 DisplayListClipState::AutoSaveRestore untransformedCssClip(aBuilder);
3561 if (!isTransformed) {
3562 ApplyClipProp(untransformedCssClip);
3563 }
3564
3565 // If there is a current clip, then depending on the container items we
3566 // create, different things can happen to it. Some container items simply
3567 // propagate the clip to their children and aren't clipped themselves.
3568 // But other container items, especially those that establish a different
3569 // geometry for their contents (e.g. transforms), capture the clip on
3570 // themselves and unset the clip for their contents. If we create more than
3571 // one of those container items, the clip will be captured on the outermost
3572 // one and the inner container items will be unclipped.
3573 ContainerItemType clipCapturedBy = ContainerItemType::None;
3574 if (capturedByViewTransition) {
3575 clipCapturedBy = isTransformed ? ContainerItemType::Transform
3576 : ContainerItemType::ViewTransitionCapture;
3577 } else if (useFixedPosition) {
3578 clipCapturedBy = ContainerItemType::FixedPosition;
3579 } else if (isTransformed) {
3580 const DisplayItemClipChain* currentClip =
3581 aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder);
3582 if ((hasPerspective || extend3DContext) &&
3583 (currentClip && currentClip->HasRoundedCorners())) {
3584 // If we're creating an nsDisplayTransform item that is going to combine
3585 // its transform with its children (preserve-3d or perspective), then we
3586 // can't have an intermediate surface. Mask layers force an intermediate
3587 // surface, so if we're going to need both then create a separate
3588 // wrapping layer for the mask.
3589 clipCapturedBy = ContainerItemType::OwnLayerForTransformWithRoundedClip;
3590 } else if (hasPerspective) {
3591 clipCapturedBy = ContainerItemType::Perspective;
3592 } else {
3593 clipCapturedBy = ContainerItemType::Transform;
3594 }
3595 } else if (usingFilter) {
3596 clipCapturedBy = ContainerItemType::Filter;
3597 }
3598
3599 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
3600 if (clipCapturedBy != ContainerItemType::None) {
3601 clipState.Clear();
3602 }
3603
3604 DisplayListClipState::AutoSaveRestore transformedCssClip(aBuilder);
3605 if (isTransformed) {
3606 // FIXME(emilio, bug 1525159): In the case we have a both a transform _and_
3607 // filters, this clips the input to the filters as well, which is not
3608 // correct (clipping by the `clip` property is supposed to happen after
3609 // applying the filter effects, per [1].
3610 //
3611 // This is not a regression though, since we used to do that anyway before
3612 // bug 1514384, and even without the transform we get it wrong.
3613 //
3614 // [1]: https://drafts.fxtf.org/css-masking/#placement
3615 ApplyClipProp(transformedCssClip);
3616 }
3617
3618 uint32_t numActiveScrollframesEncounteredBefore =
3619 aBuilder->GetNumActiveScrollframesEncountered();
3620
3621 nsDisplayListCollection set(aBuilder);
3622 Maybe<nsRect> clipForMask;
3623
3624 {
3625 DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
3626 nsDisplayListBuilder::AutoInTransformSetter inTransformSetter(aBuilder,
3627 inTransform);
3628 nsDisplayListBuilder::AutoEnterFilter filterASRSetter(aBuilder,
3629 usingFilter);
3630 nsDisplayListBuilder::AutoInEventsOnly inEventsSetter(
3631 aBuilder, opacityItemForEventsOnly);
3632
3633 DisplayListClipState::AutoSaveRestore stickyItemNestedClipState(aBuilder);
3634 if (useStickyPosition && !shouldFlattenStickyItem) {
3635 stickyItemNestedClipState.MaybeRemoveDisplayportClip();
3636 }
3637
3638 // If we have a mask, compute a clip to bound the masked content.
3639 // This is necessary in case the content moves with an ancestor
3640 // ASR of the mask.
3641 // Don't do this if we also have a filter, because then the clip
3642 // would be applied before the filter, violating
3643 // https://www.w3.org/TR/filter-effects-1/#placement.
3644 // Filters are a containing block for fixed and absolute descendants,
3645 // so the masked content cannot move with an ancestor ASR.
3646 if (usingMask && !usingFilter) {
3647 clipForMask = ComputeClipForMaskItem(aBuilder, this, maskUsage);
3648 if (clipForMask) {
3649 aBuilder->IntersectDirtyRect(*clipForMask);
3650 aBuilder->IntersectVisibleRect(*clipForMask);
3651 nestedClipState.ClipContentDescendants(
3652 *clipForMask + aBuilder->GetCurrentFrameOffsetToReferenceFrame());
3653 }
3654 }
3655
3656 // extend3DContext also guarantees that applyAbsPosClipping and
3657 // usingSVGEffects are false We only modify the preserve-3d rect if we are
3658 // the top of a preserve-3d heirarchy
3659 if (extend3DContext) {
3660 // Mark these first so MarkAbsoluteFramesForDisplayList knows if we are
3661 // going to be forced to descend into frames.
3662 aBuilder->MarkPreserve3DFramesForDisplayList(this);
3663 }
3664
3665 aBuilder->AdjustWindowDraggingRegion(this);
3666
3667 MarkAbsoluteFramesForDisplayList(aBuilder);
3668 aBuilder->Check();
3669 BuildDisplayList(aBuilder, set);
3670 // mBuiltDisplayList is observed only by painting-related tests (reftests
3671 // and paint-regression mochitests via nsIDOMWindowUtils), so record the
3672 // build only for painting display lists, not hit-testing, frame
3673 // visibility, or glyph-mask builds.
3674 if (aBuilder->IsForPainting()) {
3675 SetBuiltDisplayList(true);
3676 }
3677 aBuilder->Check();
3678 aBuilder->DisplayCaret(this, set.Outlines());
3679
3680 // Blend modes are a real pain for retained display lists. We build a blend
3681 // container item if the built list contains any blend mode items within
3682 // the current stacking context. This can change without an invalidation
3683 // to the stacking context frame, or the blend mode frame (e.g. by moving
3684 // an intermediate frame).
3685 // When we gain/remove a blend container item, we need to mark this frame
3686 // as invalid and have the full display list for merging to track
3687 // the change correctly.
3688 // It seems really hard to track this in advance, as the bookkeeping
3689 // required to note which stacking contexts have blend descendants
3690 // is complex and likely to be buggy.
3691 // Instead we're doing the sad thing, detecting it afterwards, and just
3692 // repeating display list building if it changed.
3693 // We have to repeat building for the entire display list (or at least
3694 // the outer stacking context), since we need to mark this frame as invalid
3695 // to remove any existing content that isn't wrapped in the blend container,
3696 // and then we need to build content infront/behind the blend container
3697 // to get correct positioning during merging.
3698 if (aBuilder->ContainsBlendMode() && aBuilder->IsRetainingDisplayList()) {
3699 if (aBuilder->IsPartialUpdate()) {
3700 aBuilder->SetPartialBuildFailed(true);
3701 } else {
3702 aBuilder->SetDisablePartialUpdates(true);
3703 }
3704 }
3705 }
3706
3707 if (aBuilder->IsBackgroundOnly()) {
3708 set.BlockBorderBackgrounds()->DeleteAll(aBuilder);
3709 set.Floats()->DeleteAll(aBuilder);
3710 set.Content()->DeleteAll(aBuilder);
3711 set.PositionedDescendants()->DeleteAll(aBuilder);
3712 set.Outlines()->DeleteAll(aBuilder);
3713 }
3714
3715 if (hasOverrideDirtyRect &&
3716 StaticPrefs::layout_display_list_show_rebuild_area()) {
3717 nsDisplaySolidColor* color = MakeDisplayItem<nsDisplaySolidColor>(
3718 aBuilder, this,
3719 dirtyRect + aBuilder->GetCurrentFrameOffsetToReferenceFrame(),
3720 NS_RGBA(255, 0, 0, 64)((nscolor)(((64) << 24) | ((0) << 16) | ((0) <<
8) | (255)))
);
3721 if (color) {
3722 color->SetOverrideZIndex(INT32_MAX(2147483647));
3723 set.PositionedDescendants()->AppendToTop(color);
3724 }
3725 }
3726
3727 nsIContent* content = GetContent();
3728 if (!content) {
3729 content = PresContext()->Document()->GetRootElement();
3730 }
3731
3732 nsDisplayList resultList(aBuilder);
3733 set.SerializeWithCorrectZOrder(&resultList, content);
3734
3735 // Get the ASR to use for the container items that we create here.
3736 const ActiveScrolledRoot* containerItemASR = contASRTracker.GetContainerASR();
3737
3738 bool createdContainer = false;
3739 const StackingContextBits localIsolationReasons = [&] {
3740 auto reasons = StackingContextBits::None;
3741 if (!GetParent()) {
3742 // We don't need to isolate the root frame.
3743 return reasons;
3744 }
3745 // Elements with a view-transition name also form a backdrop-root. Same for
3746 // masks / clip-path.
3747 // See https://www.w3.org/TR/css-view-transitions-1/#named-and-transitioning
3748 // and https://github.com/w3c/csswg-drafts/issues/11772
3749 const bool hasViewTransitionName =
3750 style.StyleUIReset()->HasViewTransitionName() &&
3751 !style.IsRootElementStyle();
3752 if ((disp->mWillChange.bits & StyleWillChangeBits::BACKDROP_ROOT) ||
3753 hasViewTransitionName || usingMask) {
3754 reasons |= StackingContextBits::ContainsBackdropFilter;
3755 }
3756 return reasons;
3757 }();
3758
3759 StackingContextBits currentIsolationReasons =
3760 localIsolationReasons & aBuilder->GetStackingContextBits();
3761 bool isolated = false;
3762 auto MarkAsIsolated = [&] {
3763 isolated = true;
3764 currentIsolationReasons = StackingContextBits::None;
3765 };
3766 auto ShouldForceIsolation = [&] {
3767 if (localIsolationReasons == StackingContextBits::None) {
3768 return false;
3769 }
3770 bool force = currentIsolationReasons != StackingContextBits::None;
3771 MarkAsIsolated();
3772 return force;
3773 };
3774
3775 // If adding both a nsDisplayBlendContainer and a nsDisplayBlendMode to the
3776 // same list, the nsDisplayBlendContainer should be added first. This only
3777 // happens when the element creating this stacking context has mix-blend-mode
3778 // and also contains a child which has mix-blend-mode.
3779 // The nsDisplayBlendContainer must be added to the list first, so it does not
3780 // isolate the containing element blending as well.
3781 if (aBuilder->ContainsBlendMode()) {
3782 resultList.AppendToTop(nsDisplayBlendContainer::CreateForMixBlendMode(
3783 aBuilder, this, &resultList, containerItemASR,
3784 nsDisplayItem::ContainerASRType::AncestorOfContained));
3785 createdContainer = true;
3786 MarkAsIsolated();
3787 }
3788
3789 // NOTE: When changing this condition make sure to tweak ScrollContainerFrame
3790 // as well.
3791 const bool usingBackdropFilter = effects->HasBackdropFilters() &&
3792 IsVisibleForPainting() &&
3793 !style.IsRootElementStyle();
3794 if (usingBackdropFilter) {
3795 stackingContextTracker.AddToParent(
3796 StackingContextBits::ContainsBackdropFilter);
3797 nsRect backdropRect =
3798 GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this);
3799 resultList.AppendNewToTop<nsDisplayBackdropFilters>(
3800 aBuilder, this, &resultList, backdropRect, this);
3801 createdContainer = true;
3802 MarkAsIsolated();
3803 }
3804
3805 // If there are any SVG effects, wrap the list up in an SVG effects item
3806 // (which also handles CSS group opacity). Note that we create an SVG effects
3807 // item even if resultList is empty, since a filter can produce graphical
3808 // output even if the element being filtered wouldn't otherwise do so.
3809 if (usingSVGEffects) {
3810 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", 3811); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "usingFilter || usingMask" ") (" "Beside filter & mask/clip-path, what else effect do we have?"
")"); do { MOZ_CrashSequence(__null, 3811); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
3811 "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", 3811); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "usingFilter || usingMask" ") (" "Beside filter & mask/clip-path, what else effect do we have?"
")"); do { MOZ_CrashSequence(__null, 3811); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3812
3813 if (clipCapturedBy == ContainerItemType::Filter) {
3814 clipState.Restore();
3815 }
3816 // Revert to the post-filter dirty rect.
3817 aBuilder->SetVisibleRect(visibleRectOutsideSVGEffects);
3818
3819 // Skip all filter effects while generating glyph mask.
3820 if (usingFilter && !aBuilder->IsForGenerateGlyphMask()) {
3821 /* List now emptied, so add the new list to the top. */
3822 resultList.AppendNewToTop<nsDisplayFilters>(aBuilder, this, &resultList,
3823 this, usingBackdropFilter);
3824 createdContainer = true;
Value stored to 'createdContainer' is never read
3825 }
3826
3827 if (usingMask) {
3828 // The mask should move with aBuilder->CurrentActiveScrolledRoot(), so
3829 // that's the ASR we prefer to use for the mask item. However, we can
3830 // only do this if the mask if clipped with respect to that ASR, because
3831 // an item always needs to have finite bounds with respect to its ASR.
3832 // If we weren't able to compute a clip for the mask, we fall back to
3833 // using containerItemASR, which is the lowest common ancestor clip of
3834 // the mask's contents. That's not entirely correct, but it satisfies
3835 // the base requirement of the ASR system (that items have finite bounds
3836 // wrt. their ASR).
3837 const ActiveScrolledRoot* maskASR =
3838 clipForMask.isSome() ? aBuilder->CurrentActiveScrolledRoot()
3839 : containerItemASR;
3840 /* List now emptied, so add the new list to the top. */
3841 resultList.AppendNewToTop<nsDisplayMasksAndClipPaths>(
3842 aBuilder, this, &resultList, maskASR,
3843 clipForMask.isSome()
3844 ? nsDisplayItem::ContainerASRType::Constant
3845 : nsDisplayItem::ContainerASRType::AncestorOfContained,
3846 usingBackdropFilter, ShouldForceIsolation());
3847 createdContainer = true;
3848 }
3849
3850 // TODO(miko): We could probably create a wraplist here and avoid creating
3851 // it later in |BuildDisplayListForChild()|.
3852 createdContainer = false;
3853
3854 // Also add the hoisted scroll info items. We need those for APZ scrolling
3855 // because nsDisplayMasksAndClipPaths items can't build active layers.
3856 aBuilder->ExitSVGEffectsContents();
3857 resultList.AppendToTop(&hoistedScrollInfoItemsStorage);
3858 }
3859
3860 // If the list is non-empty and there is CSS group opacity without SVG
3861 // effects, wrap it up in an opacity item.
3862 if (useOpacity) {
3863 const bool needsActiveOpacityLayer =
3864 nsDisplayOpacity::NeedsActiveLayer(this);
3865 resultList.AppendNewToTop<nsDisplayOpacity>(
3866 aBuilder, this, &resultList, containerItemASR,
3867 nsDisplayItem::ContainerASRType::AncestorOfContained,
3868 opacityItemForEventsOnly, needsActiveOpacityLayer, usingBackdropFilter,
3869 ShouldForceIsolation());
3870 createdContainer = true;
3871 }
3872
3873 // We build nsDisplayViewTransitionCapture here, and then use
3874 // nsDisplayTransform to wrap it, to make sure we create the correct transform
3875 // for the captured element and its descendants. This is necessary to make
3876 // sure our captured element doesn't become blurry when using scale()
3877 // transform.
3878 //
3879 // So the display list looks like this:
3880 // nsDisplayTransform // For the captured element if it is transformed
3881 // VTCapture // For the captured element
3882 // ...
3883 // Other display items // For the descendants of the captured element
3884 // ...
3885 // ...
3886 //
3887 // We intentionally use nsDisplayTransform to wrap the VTCapture (so it's
3888 // different from opacity display item) because nsDisplayTransform may push a
3889 // reference frame which creates a new coordinate system in WR. So it's just
3890 // like a separator between the VTCapture the outside, if it is transformed.
3891 if (capturedByViewTransition) {
3892 resultList.AppendNewToTop<nsDisplayViewTransitionCapture>(
3893 aBuilder, this, &resultList, nullptr, false);
3894 createdContainer = true;
3895 MarkAsIsolated();
3896 // We don't want the capture to be clipped, so we do this _after_ building
3897 // the wrapping item.
3898 if (clipCapturedBy == ContainerItemType::ViewTransitionCapture) {
3899 clipState.Restore();
3900 }
3901 }
3902
3903 // If we're going to apply a transformation and don't have preserve-3d set,
3904 // wrap everything in an nsDisplayTransform. If there's nothing in the list,
3905 // don't add anything.
3906 //
3907 // For the preserve-3d case we want to individually wrap every child in the
3908 // list with a separate nsDisplayTransform instead. When the child is already
3909 // an nsDisplayTransform, we can skip this step, as the computed transform
3910 // will already include our own.
3911 //
3912 // We also traverse into sublists created by nsDisplayWrapList, so that we
3913 // find all the correct children.
3914 //
3915 // We still need to create nsDisplayTransform to wrap the VT capture element
3916 // to make sure WR renders it properly if it is transformed as well.
3917 if (isTransformed) {
3918 if (extend3DContext) {
3919 // Install dummy nsDisplayTransform as a leaf containing
3920 // descendants not participating this 3D rendering context.
3921 nsDisplayList nonparticipants(aBuilder);
3922 nsDisplayList participants(aBuilder);
3923 int index = 1;
3924
3925 nsDisplayItem* separator = nullptr;
3926
3927 // TODO: This can be simplified: |participants| is just |resultList|.
3928 for (nsDisplayItem* item : resultList.TakeItems()) {
3929 if (ItemParticipatesIn3DContext(this, item) &&
3930 !item->GetClip().HasClip()) {
3931 // The frame of this item participates the same 3D context.
3932 WrapSeparatorTransform(aBuilder, this, &nonparticipants,
3933 &participants, index++, &separator);
3934
3935 participants.AppendToTop(item);
3936 } else {
3937 // The frame of the item doesn't participate the current
3938 // context, or has no transform.
3939 //
3940 // For items participating but not transformed, they are add
3941 // to nonparticipants to get a separator layer for handling
3942 // clips, if there is, on an intermediate surface.
3943 // \see ContainerLayer::DefaultComputeEffectiveTransforms().
3944 nonparticipants.AppendToTop(item);
3945 }
3946 }
3947 WrapSeparatorTransform(aBuilder, this, &nonparticipants, &participants,
3948 index++, &separator);
3949
3950 if (separator) {
3951 createdContainer = true;
3952 MarkAsIsolated();
3953 }
3954
3955 resultList.AppendToTop(&participants);
3956 }
3957
3958 transformedCssClip.Restore();
3959 if (clipCapturedBy == ContainerItemType::Transform) {
3960 // Restore clip state now so nsDisplayTransform is clipped properly.
3961 clipState.Restore();
3962 }
3963 // Revert to the dirtyrect coming in from the parent, without our transform
3964 // taken into account.
3965 aBuilder->SetVisibleRect(visibleRectOutsideTransform);
3966
3967 if (this != aBuilder->RootReferenceFrame()) {
3968 // Revert to the outer reference frame and offset because all display
3969 // items we create from now on are outside the transform.
3970 nsPoint toOuterReferenceFrame;
3971 const nsIFrame* outerReferenceFrame =
3972 aBuilder->FindReferenceFrameFor(GetParent(), &toOuterReferenceFrame);
3973 toOuterReferenceFrame += GetPosition();
3974
3975 buildingDisplayList.SetReferenceFrameAndCurrentOffset(
3976 outerReferenceFrame, toOuterReferenceFrame);
3977 }
3978
3979 // We would like to block async animations for ancestors of ones not
3980 // prerendered in the preserve-3d tree. Now that we've finished processing
3981 // all descendants, update allowAsyncAnimation to take their prerender
3982 // state into account
3983 // FIXME: We don't block async animations for previous siblings because
3984 // their prerender decisions have been made. We may have to figure out a
3985 // better way to rollback their prerender decisions.
3986 // Alternatively we could not block animations for later siblings, and only
3987 // block them for ancestors of a blocked one.
3988 if ((extend3DContext || combines3DTransformWithAncestors) &&
3989 prerenderInfo.CanUseAsyncAnimations() &&
3990 !aBuilder->GetPreserves3DAllowAsyncAnimation()) {
3991 // aBuilder->GetPreserves3DAllowAsyncAnimation() means the inner or
3992 // previous silbing frames are allowed/disallowed for async animations.
3993 prerenderInfo.mDecision = nsDisplayTransform::PrerenderDecision::No;
3994 }
3995
3996 // A transform does not form a Backdrop Root, so if a descendant has a
3997 // backdrop-filter this stacking context must not be used to resolve it,
3998 // even though WebRender may give it a surface (e.g. to apply a clip it
3999 // inherits). Unless we are forcing isolation, in which case we are the
4000 // backdrop root that the descendant should resolve from.
4001 const bool forceIsolation = ShouldForceIsolation();
4002 const bool wrapsBackdropFilter =
4003 usingBackdropFilter ||
4004 (!forceIsolation && aBuilder->ContainsBackdropFilter());
4005
4006 nsDisplayTransform* transformItem = MakeDisplayItem<nsDisplayTransform>(
4007 aBuilder, this, &resultList, visibleRect, prerenderInfo.mDecision,
4008 wrapsBackdropFilter, forceIsolation);
4009 if (transformItem) {
4010 resultList.AppendToTop(transformItem);
4011 createdContainer = true;
4012
4013 if (numActiveScrollframesEncounteredBefore !=
4014 aBuilder->GetNumActiveScrollframesEncountered()) {
4015 transformItem->SetContainsASRs(true);
4016 }
4017
4018 if (hasPerspective) {
4019 transformItem->MarkWithAssociatedPerspective();
4020
4021 if (clipCapturedBy == ContainerItemType::Perspective) {
4022 clipState.Restore();
4023 }
4024 resultList.AppendNewToTop<nsDisplayPerspective>(aBuilder, this,
4025 &resultList);
4026 createdContainer = true;
4027 }
4028 }
4029 if (clipCapturedBy ==
4030 ContainerItemType::OwnLayerForTransformWithRoundedClip) {
4031 clipState.Restore();
4032 resultList.AppendNewToTopWithIndex<nsDisplayOwnLayer>(
4033 aBuilder, this,
4034 /* aIndex = */ nsDisplayOwnLayer::OwnLayerForTransformWithRoundedClip,
4035 &resultList, aBuilder->CurrentActiveScrolledRoot(),
4036 nsDisplayItem::ContainerASRType::Constant,
4037 nsDisplayOwnLayerFlags::None, ScrollbarData{},
4038 /* aForceActive = */ false, false);
4039 createdContainer = true;
4040 }
4041 }
4042
4043 // If we have sticky positioning, wrap it in a sticky position item.
4044 if (useFixedPosition && !capturedByViewTransition) {
4045 if (clipCapturedBy == ContainerItemType::FixedPosition) {
4046 clipState.Restore();
4047 }
4048 // The ASR for the fixed item should be the ASR of our containing block,
4049 // which has been set as the builder's current ASR, unless this frame is
4050 // invisible and we hadn't saved display item data for it. In that case,
4051 // we need to take the containerItemASR since we might have fixed children.
4052 // For WebRender, we want to the know what |containerItemASR| is for the
4053 // case where the fixed-pos item is not a "real" fixed-pos item (e.g. it's
4054 // nested inside a scrolling transform), so we stash that on the display
4055 // item as well.
4056 const ActiveScrolledRoot* fixedASR = ActiveScrolledRoot::PickAncestor(
4057 containerItemASR, aBuilder->CurrentActiveScrolledRoot());
4058 const ActiveScrolledRoot* scrollTargetASR =
4059 containerItemASR ? containerItemASR->GetNearestScrollASR() : nullptr;
4060 resultList.AppendNewToTop<nsDisplayFixedPosition>(
4061 aBuilder, this, &resultList, fixedASR,
4062 nsDisplayItem::ContainerASRType::AncestorOfContained, scrollTargetASR,
4063 ShouldForceIsolation());
4064 createdContainer = true;
4065 } else if (useStickyPosition && !capturedByViewTransition) {
4066 // For position:sticky, the clip needs to be applied both to the sticky
4067 // container item and to the contents. The container item needs the clip
4068 // because a scrolled clip needs to move independently from the sticky
4069 // contents, and the contents need the clip so that they have finite
4070 // clipped bounds with respect to the container item's ASR. The latter is
4071 // a little tricky in the case where the sticky item has both fixed and
4072 // non-fixed descendants, because that means that the sticky container
4073 // item's ASR is the ASR of the fixed descendant.
4074 // For WebRender display list building, though, we still want to know the
4075 // the ASR that the sticky container item would normally have, so we stash
4076 // that on the display item as the "container ASR" (i.e. the normal ASR of
4077 // the container item, excluding the special behaviour induced by fixed
4078 // descendants).
4079 const ActiveScrolledRoot* stickyItemASR = ActiveScrolledRoot::PickAncestor(
4080 containerItemASR, aBuilder->CurrentActiveScrolledRoot());
4081
4082 auto* stickyItem = MakeDisplayItem<nsDisplayStickyPosition>(
4083 aBuilder, this, &resultList, stickyItemASR,
4084 nsDisplayItem::ContainerASRType::AncestorOfContained,
4085 aBuilder->CurrentActiveScrolledRoot());
4086
4087 stickyItem->SetShouldFlatten(shouldFlattenStickyItem);
4088
4089 resultList.AppendToTop(stickyItem);
4090 createdContainer = true;
4091
4092 // If the sticky element is inside a filter, annotate the scroll frame that
4093 // scrolls the filter as having out-of-flow content inside a filter (this
4094 // inhibits paint skipping).
4095 if (aBuilder->GetFilterASR() && aBuilder->GetFilterASR() == stickyItemASR) {
4096 aBuilder->GetFilterASR()
4097 ->GetNearestScrollASR()
4098 ->ScrollFrame()
4099 ->SetHasOutOfFlowContentInsideFilter();
4100 }
4101 }
4102
4103 // If there's blending, wrap up the list in a blend-mode item. Note that
4104 // opacity can be applied before blending as the blend color is not affected
4105 // by foreground opacity (only background alpha).
4106 if (effects->mMixBlendMode != StyleBlend::Normal) {
4107 stackingContextTracker.AddToParent(
4108 StackingContextBits::ContainsMixBlendMode);
4109 resultList.AppendNewToTop<nsDisplayBlendMode>(
4110 aBuilder, this, &resultList, effects->mMixBlendMode, containerItemASR,
4111 nsDisplayItem::ContainerASRType::AncestorOfContained, false);
4112 createdContainer = true;
4113 MarkAsIsolated();
4114 }
4115
4116 if (!isolated && localIsolationReasons != StackingContextBits::None) {
4117 resultList.AppendToTop(nsDisplayBlendContainer::CreateForIsolation(
4118 aBuilder, this, &resultList, containerItemASR,
4119 nsDisplayItem::ContainerASRType::AncestorOfContained,
4120 ShouldForceIsolation()));
4121 createdContainer = true;
4122 }
4123
4124 if (aBuilder->IsReusingStackingContextItems()) {
4125 if (resultList.IsEmpty()) {
4126 return;
4127 }
4128
4129 nsDisplayItem* container = resultList.GetBottom();
4130 if (resultList.Length() > 1 || container->Frame() != this) {
4131 container = MakeDisplayItem<nsDisplayContainer>(
4132 aBuilder, this, containerItemASR,
4133 nsDisplayItem::ContainerASRType::AncestorOfContained, &resultList);
4134 } else {
4135 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", 4135); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "resultList.Length() == 1" ")"); do { MOZ_CrashSequence
(__null, 4135); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4136 resultList.Clear();
4137 }
4138
4139 // Mark the outermost display item as reusable. These display items and
4140 // their chidren can be reused during the next paint if no ancestor or
4141 // descendant frames have been modified.
4142 if (!container->IsReusedItem()) {
4143 container->SetReusable();
4144 }
4145 aList->AppendToTop(container);
4146 createdContainer = true;
4147 } else {
4148 aList->AppendToTop(&resultList);
4149 }
4150
4151 if (aCreatedContainerItem) {
4152 *aCreatedContainerItem = createdContainer;
4153 }
4154}
4155
4156static nsDisplayItem* WrapInWrapList(nsDisplayListBuilder* aBuilder,
4157 nsIFrame* aFrame, nsDisplayList* aList,
4158 const ActiveScrolledRoot* aContainerASR,
4159 bool aBuiltContainerItem = false) {
4160 nsDisplayItem* item = aList->GetBottom();
4161 if (!item) {
4162 return nullptr;
4163 }
4164
4165 // We need a wrap list if there are multiple items, or if the single
4166 // item has a different frame. This can change in a partial build depending
4167 // on which items we build, so we need to ensure that we don't transition
4168 // to/from a wrap list without invalidating correctly.
4169 bool needsWrapList =
4170 aList->Length() > 1 || item->Frame() != aFrame || item->GetChildren();
4171
4172 // If we have an explicit container item (that can't change without an
4173 // invalidation) or we're doing a full build and don't need a wrap list, then
4174 // we can skip adding one.
4175 if (aBuiltContainerItem || (!aBuilder->IsPartialUpdate() && !needsWrapList)) {
4176 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", 4176); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aList->Length() == 1" ")"); do { MOZ_CrashSequence
(__null, 4176); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4177 aList->Clear();
4178 return item;
4179 }
4180
4181 // If we're doing a partial build and we didn't need a wrap list
4182 // previously then we can try to work from there.
4183 if (aBuilder->IsPartialUpdate() &&
4184 !aFrame->HasDisplayItem(uint32_t(DisplayItemType::TYPE_CONTAINER))) {
4185 // If we now need a wrap list, we must previously have had no display items
4186 // or a single one belonging to this frame. Mark the item itself as
4187 // discarded so that RetainedDisplayListBuilder uses the ones we just built.
4188 // We don't want to mark the frame as modified as that would invalidate
4189 // positioned descendants that might be outside of this list, and might not
4190 // have been rebuilt this time.
4191 if (needsWrapList) {
4192 DiscardOldItems(aFrame);
4193 } else {
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
4200 // The last case we could try to handle is when we previously had a wrap list,
4201 // but no longer need it. Unfortunately we can't differentiate this case from
4202 // a partial build where other children exist but we just didn't build them
4203 // this time.
4204 // TODO:RetainedDisplayListBuilder's merge phase has the full list and
4205 // could strip them out.
4206
4207 return MakeDisplayItem<nsDisplayContainer>(
4208 aBuilder, aFrame, aContainerASR,
4209 nsDisplayItem::ContainerASRType::AncestorOfContained, aList);
4210}
4211
4212/**
4213 * Check if a frame should be visited for building display list.
4214 */
4215static bool DescendIntoChild(nsDisplayListBuilder* aBuilder,
4216 const nsIFrame* aChild, const nsRect& aVisible,
4217 const nsRect& aDirty) {
4218 if (aChild->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
4219 return true;
4220 }
4221
4222 // If the child is a scrollframe that we want to ignore, then we need
4223 // to descend into it because its scrolled child may intersect the dirty
4224 // area even if the scrollframe itself doesn't.
4225 if (aChild == aBuilder->GetIgnoreScrollFrame()) {
4226 return true;
4227 }
4228
4229 // There are cases where the "ignore scroll frame" on the builder is not set
4230 // correctly, and so we additionally want to catch cases where the child is
4231 // a root scrollframe and we are ignoring scrolling on the viewport.
4232 if (aChild == aBuilder->GetPresShellIgnoreScrollFrame()) {
4233 return true;
4234 }
4235
4236 nsRect overflow = aChild->InkOverflowRect();
4237
4238 // On mobile, there may be a dynamic toolbar. The root content document's
4239 // root scroll frame's ink overflow rect does not include the toolbar
4240 // height, but if the toolbar is hidden, we still want to be able to target
4241 // content underneath the toolbar, so expand the overflow rect here to
4242 // allow display list building to descend into the scroll frame.
4243 if (aBuilder->IsForEventDelivery() &&
4244 aChild == aChild->PresShell()->GetRootScrollContainerFrame() &&
4245 aChild->PresContext()->IsRootContentDocumentCrossProcess() &&
4246 aChild->PresContext()->HasDynamicToolbar()) {
4247 overflow.SizeTo(nsLayoutUtils::ExpandHeightForDynamicToolbar(
4248 aChild->PresContext(), overflow.Size()));
4249 }
4250
4251 if (aDirty.Intersects(overflow)) {
4252 return true;
4253 }
4254
4255 if (aChild->ForceDescendIntoIfVisible() && aVisible.Intersects(overflow)) {
4256 return true;
4257 }
4258
4259 if (aChild->IsTablePart()) {
4260 // Relative positioning and transforms can cause table parts to move, but we
4261 // will still paint the backgrounds for their ancestor parts under them at
4262 // their 'normal' position. That means that we must consider the overflow
4263 // rects at both positions.
4264
4265 // We convert the overflow rect into the nsTableFrame's coordinate
4266 // space, applying the normal position offset at each step. Then we
4267 // compare that against the builder's cached dirty rect in table
4268 // coordinate space.
4269 const nsIFrame* f = aChild;
4270 nsRect normalPositionOverflowRelativeToTable = overflow;
4271
4272 while (f->IsTablePart()) {
4273 normalPositionOverflowRelativeToTable += f->GetNormalPosition();
4274 f = f->GetParent();
4275 }
4276
4277 nsDisplayTableBackgroundSet* tableBGs = aBuilder->GetTableBackgroundSet();
4278 if (tableBGs && tableBGs->GetDirtyRect().Intersects(
4279 normalPositionOverflowRelativeToTable)) {
4280 return true;
4281 }
4282 }
4283
4284 return false;
4285}
4286
4287void nsIFrame::BuildDisplayListForSimpleChild(nsDisplayListBuilder* aBuilder,
4288 nsIFrame* aChild,
4289 const nsDisplayListSet& aLists) {
4290 // This is the shortcut for frames been handled along the common
4291 // path, the most common one of THE COMMON CASE mentioned later.
4292 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", 4292); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aChild->Type() != LayoutFrameType::Placeholder"
")"); do { MOZ_CrashSequence(__null, 4292); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4293 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"
, 4295); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aBuilder->GetSelectedFramesOnly() && !aBuilder->GetIncludeAllOutOfFlows()"
") (" "It should be held for painting to window" ")"); do { MOZ_CrashSequence
(__null, 4295); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
4294 !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"
, 4295); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aBuilder->GetSelectedFramesOnly() && !aBuilder->GetIncludeAllOutOfFlows()"
") (" "It should be held for painting to window" ")"); do { MOZ_CrashSequence
(__null, 4295); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
4295 "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"
, 4295); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aBuilder->GetSelectedFramesOnly() && !aBuilder->GetIncludeAllOutOfFlows()"
") (" "It should be held for painting to window" ")"); do { MOZ_CrashSequence
(__null, 4295); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
4296 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"
, 4296); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aChild->HasAnyStateBits(NS_FRAME_SIMPLE_DISPLAYLIST)"
")"); do { MOZ_CrashSequence(__null, 4296); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4297
4298 const nsPoint offset = aChild->GetOffsetTo(this);
4299 const nsRect visible = aBuilder->GetVisibleRect() - offset;
4300 const nsRect dirty = aBuilder->GetDirtyRect() - offset;
4301
4302 if (!DescendIntoChild(aBuilder, aChild, visible, dirty)) {
4303 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)
;
4304 return;
4305 }
4306
4307 // Child cannot be transformed since it is not a stacking context.
4308 nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(
4309 aBuilder, aChild, visible, dirty, false);
4310
4311 UpdateCurrentHitTestInfo(aBuilder, aChild);
4312
4313 aChild->MarkAbsoluteFramesForDisplayList(aBuilder);
4314 aBuilder->AdjustWindowDraggingRegion(aChild);
4315 aBuilder->Check();
4316 aChild->BuildDisplayList(aBuilder, aLists);
4317 if (aBuilder->IsForPainting()) {
4318 aChild->SetBuiltDisplayList(true);
4319 }
4320 aBuilder->Check();
4321 aBuilder->DisplayCaret(aChild, aLists.Outlines());
4322}
4323
4324static bool ShouldSkipFrame(nsDisplayListBuilder* aBuilder,
4325 const nsIFrame* aFrame) {
4326 // If painting is restricted to just the background of the top level frame,
4327 // then we have nothing to do here.
4328 if (aBuilder->IsBackgroundOnly()) {
4329 return true;
4330 }
4331 if (aBuilder->IsForGenerateGlyphMask()) {
4332 if ((aFrame->IsLeaf() && !aFrame->IsTextFrame()) ||
4333 aFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
4334 // Only in-flow text frames are painted for background-clip: text mask
4335 // generation.
4336 return true;
4337 }
4338 }
4339 if (aBuilder->GetSelectedFramesOnly() && aFrame->IsLeaf() &&
4340 !aFrame->IsSelected()) {
4341 return true;
4342 }
4343 static const nsFrameState skipFlags = NS_FRAME_TOO_DEEP_IN_FRAME_TREE |
4344 NS_FRAME_IS_NONDISPLAY |
4345 NS_FRAME_POSITION_VISIBILITY_HIDDEN;
4346 if (aFrame->HasAnyStateBits(skipFlags)) {
4347 return true;
4348 }
4349 // Checking mMozSubtreeHiddenOnlyVisually is relatively slow because it
4350 // involves loading more memory. It's only allowed in chrome sheets so let's
4351 // only support it in the parent process so we can mostly optimize this out in
4352 // content processes.
4353 return XRE_IsParentProcess() &&
4354 aFrame->StyleUIReset()->mMozSubtreeHiddenOnlyVisually;
4355}
4356
4357#ifdef ACCESSIBILITY1
4358// Bug 2025119: If this is inlined in nsIFrame::BuildDisplayListForChild on
4359// Win32, we end up with crashes when there is deep recursion due to the
4360// increased stack size caused by the additional variables here. Work around
4361// this by having this code in its own function and ensuring it is never inlined
4362// on Win32.
4363# if defined(XP_WIN) && !defined(_WIN64)
4364MOZ_NEVER_INLINE__attribute__((noinline))
4365# endif
4366static void MaybeAddAccId(nsIFrame* aChildOrOutOfFlow,
4367 nsDisplayListBuilder* aBuilder,
4368 const nsDisplayListSet& aLists) {
4369 auto [innerWindowId, accId] =
4370 a11y::PdfStructTreeBuilder::GetAccId(aChildOrOutOfFlow);
4371 if (!innerWindowId) {
4372 return;
4373 }
4374 // When generating tagged PDF, associate this content with the correct
4375 // node in the structure tree.
4376 auto* item = MakeDisplayItem<nsDisplayAccessibleId>(
4377 aBuilder, aChildOrOutOfFlow, innerWindowId, accId);
4378 aLists.Content()->AppendToTop(item);
4379}
4380#endif
4381
4382void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
4383 nsIFrame* aChild,
4384 const nsDisplayListSet& aLists,
4385 DisplayChildFlags aFlags) {
4386 AutoCheckBuilder check(aBuilder);
4387 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"
, 4387); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!HidesContent()"
") (" "Caller should check" ")"); do { MOZ_CrashSequence(__null
, 4387); __attribute__((nomerge)) ::abort(); } while (false);
} } while (false)
;
4388#ifdef DEBUG1
4389 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)
;
4390 ScopeExit e(
4391 [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)
; });
4392#endif
4393
4394 nsIFrame* child = aChild;
4395 auto* placeholder = child->IsPlaceholderFrame()
4396 ? static_cast<nsPlaceholderFrame*>(child)
4397 : nullptr;
4398 nsIFrame* childOrOutOfFlow =
4399 placeholder ? placeholder->GetOutOfFlowFrame() : child;
4400 if (!childOrOutOfFlow || ShouldSkipFrame(aBuilder, childOrOutOfFlow)) {
4401 return;
4402 }
4403
4404 if (aBuilder->IsInLineClampAbsPosTraversal()) {
4405 const bool isOnForcedDescendPath = childOrOutOfFlow->HasAnyStateBits(
4406 NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO);
4407
4408 if (!isOnForcedDescendPath) {
4409 return;
4410 }
4411 }
4412
4413 // If we're generating a display list for printing, include Link items for
4414 // frames that correspond to HTML link elements so that we can have active
4415 // links in saved PDF output. Note that the state of "within a link" is
4416 // set on the display-list builder, such that all descendants of the link
4417 // element will generate display-list links.
4418 // TODO: we should be able to optimize this so as to avoid creating links
4419 // for the same destination that entirely overlap each other, which adds
4420 // nothing useful to the final PDF.
4421 Maybe<nsDisplayListBuilder::Linkifier> linkifier;
4422 if (aBuilder->IsForPrinting()) {
4423 if (StaticPrefs::print_save_as_pdf_links_enabled()) {
4424 linkifier.emplace(aBuilder, childOrOutOfFlow, aLists.Content());
4425 linkifier->MaybeAppendLink(aBuilder, childOrOutOfFlow);
4426 }
4427#ifdef ACCESSIBILITY1
4428 MaybeAddAccId(childOrOutOfFlow, aBuilder, aLists);
4429#endif
4430 }
4431
4432 nsIFrame* parent = childOrOutOfFlow->GetParent();
4433 const auto* parentDisplay = parent->StyleDisplay();
4434 const auto overflowClipAxes =
4435 parent->ShouldApplyOverflowClipping(parentDisplay);
4436
4437 const bool isPaintingToWindow = aBuilder->IsPaintingToWindow();
4438 const bool doingShortcut =
4439 isPaintingToWindow &&
4440 child->HasAnyStateBits(NS_FRAME_SIMPLE_DISPLAYLIST) &&
4441 // Animations may change the stacking context state.
4442 // ShouldApplyOverflowClipping is affected by the parent style, which does
4443 // not invalidate the NS_FRAME_SIMPLE_DISPLAYLIST bit.
4444 !(!overflowClipAxes.isEmpty() || child->MayHaveTransformAnimation() ||
4445 child->MayHaveOpacityAnimation());
4446
4447 if (StaticPrefs::layout_css_scroll_anchoring_highlight()) {
4448 if (child->FirstContinuation()->IsScrollAnchor()) {
4449 nsRect bounds = child->GetContentRectRelativeToSelf() +
4450 aBuilder->ToReferenceFrame(child);
4451 nsDisplaySolidColor* color = MakeDisplayItem<nsDisplaySolidColor>(
4452 aBuilder, child, bounds, NS_RGBA(255, 0, 255, 64)((nscolor)(((64) << 24) | ((255) << 16) | ((0) <<
8) | (255)))
);
4453 if (color) {
4454 color->SetOverrideZIndex(INT32_MAX(2147483647));
4455 aLists.PositionedDescendants()->AppendToTop(color);
4456 }
4457 }
4458 }
4459
4460 if (doingShortcut) {
4461 BuildDisplayListForSimpleChild(aBuilder, child, aLists);
4462 return;
4463 }
4464
4465 // dirty rect in child-relative coordinates
4466 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", 4466); MOZ_PretendNoReturn
(); } } while (0)
;
4467 const nsPoint offset = child->GetOffsetTo(this);
4468 nsRect visible = aBuilder->GetVisibleRect() - offset;
4469 nsRect dirty = aBuilder->GetDirtyRect() - offset;
4470
4471 nsDisplayListBuilder::OutOfFlowDisplayData* savedOutOfFlowData = nullptr;
4472 if (placeholder) {
4473 if (placeholder->HasAnyStateBits(PLACEHOLDER_FOR_TOPLAYER)) {
4474 // If the out-of-flow frame is in the top layer, the viewport frame
4475 // will paint it. Skip it here. Note that, only out-of-flow frames
4476 // with this property should be skipped, because non-HTML elements
4477 // may stop their children from being out-of-flow. Those frames
4478 // should still be handled in the normal in-flow path.
4479 return;
4480 }
4481
4482 child = childOrOutOfFlow;
4483
4484 // If 'child' is a pushed out-of-flow then it's owned by a block that's not
4485 // an ancestor of the placeholder, and it will be painted by that block and
4486 // should not be painted through the placeholder. Also recheck
4487 // NS_FRAME_TOO_DEEP_IN_FRAME_TREE and NS_FRAME_IS_NONDISPLAY.
4488 static const nsFrameState skipFlags =
4489 (NS_FRAME_IS_PUSHED_OUT_OF_FLOW | NS_FRAME_TOO_DEEP_IN_FRAME_TREE |
4490 NS_FRAME_IS_NONDISPLAY);
4491 if (child->HasAnyStateBits(skipFlags) || nsLayoutUtils::IsPopup(child)) {
4492 return;
4493 }
4494
4495 // For a transformed absolute containing block, if the abspos's placeholder
4496 // is in a different continuation's subtree, the abspos was already built
4497 // directly by DisplayAbsoluteFramesNotBuiltByPlaceholder() on its
4498 // containing block. Exclude inline frames because IB-split (bug 489100)
4499 // can cause the placeholder to live in a later IB-split sibling.
4500 if (parent->IsTransformed() && !parent->IsInlineFrame() &&
4501 child->IsAbsolutelyPositioned() &&
4502 !nsLayoutUtils::IsProperAncestorFrame(parent, placeholder)) {
4503 return;
4504 }
4505
4506 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", 4506); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "child->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)"
")"); do { MOZ_CrashSequence(__null, 4506); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4507 savedOutOfFlowData = nsDisplayListBuilder::GetOutOfFlowData(child);
4508
4509 if (aBuilder->GetIncludeAllOutOfFlows()) {
4510 visible = child->InkOverflowRect();
4511 dirty = child->InkOverflowRect();
4512 } else if (savedOutOfFlowData) {
4513 visible =
4514 savedOutOfFlowData->GetVisibleRectForFrame(aBuilder, child, &dirty);
4515 } else {
4516 // The out-of-flow frame did not intersect the dirty area. We may still
4517 // need to traverse into it, since it may contain placeholders we need
4518 // to enter to reach other out-of-flow frames that are visible.
4519 visible.SetEmpty();
4520 dirty.SetEmpty();
4521 }
4522 }
4523
4524 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"
, 4525); MOZ_PretendNoReturn(); } } while (0)
4525 "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"
, 4525); MOZ_PretendNoReturn(); } } while (0)
;
4526
4527 if (!DescendIntoChild(aBuilder, child, visible, dirty)) {
4528 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
)
;
4529 return;
4530 }
4531
4532 const bool isSVG = child->HasAnyStateBits(NS_FRAME_SVG_LAYOUT);
4533
4534 // This flag is raised if the control flow strays off the common path.
4535 // The common path is the most common one of THE COMMON CASE mentioned later.
4536 bool awayFromCommonPath = !isPaintingToWindow;
4537
4538 // true if this is a real or pseudo stacking context
4539 bool pseudoStackingContext =
4540 aFlags.contains(DisplayChildFlag::ForcePseudoStackingContext);
4541
4542 if (!pseudoStackingContext && !isSVG &&
4543 aFlags.contains(DisplayChildFlag::Inline) &&
4544 !child->IsLineParticipant()) {
4545 // child is a non-inline frame in an inline context, i.e.,
4546 // it acts like inline-block or inline-table. Therefore it is a
4547 // pseudo-stacking-context.
4548 pseudoStackingContext = true;
4549 }
4550
4551 // Since we're now sure that we're adding this frame to the display list
4552 // (which means we're painting it, modulo occlusion), mark it as visible
4553 // within the displayport.
4554 if (isPaintingToWindow && child->TrackingVisibility() &&
4555 child->IsVisibleForPainting()) {
4556 child->PresShell()->EnsureFrameInApproximatelyVisibleList(child);
4557 awayFromCommonPath = true;
4558 }
4559
4560 // Child is composited if it's transformed, partially transparent, or has
4561 // SVG effects or a blend mode..
4562 const nsStyleDisplay* disp = child->StyleDisplay();
4563 const nsStyleEffects* effects = child->StyleEffects();
4564
4565 const bool isPositioned = disp->IsPositionedStyle();
4566 const bool isStackingContext =
4567 aFlags.contains(DisplayChildFlag::ForceStackingContext) ||
4568 child->IsStackingContext(disp, effects);
4569
4570 if (pseudoStackingContext || isStackingContext || isPositioned ||
4571 placeholder || (!isSVG && disp->IsFloating(child)) ||
4572 (isSVG && effects->mClip.IsRect() && IsSVGContentWithCSSClip(child))) {
4573 pseudoStackingContext = true;
4574 awayFromCommonPath = true;
4575 }
4576
4577 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"
, 4578); MOZ_PretendNoReturn(); } } while (0)
4578 "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"
, 4578); MOZ_PretendNoReturn(); } } while (0)
;
4579
4580 Maybe<nsDisplayListBuilder::AutoInLineClampAbsPosTraversal>
4581 buildAbsPosNormally;
4582 if (aBuilder->IsInLineClampAbsPosTraversal() &&
4583 child->IsAbsolutelyPositioned() && savedOutOfFlowData) {
4584 buildAbsPosNormally.emplace(aBuilder, false);
4585 }
4586
4587 nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(
4588 aBuilder, child, visible, dirty);
4589
4590 UpdateCurrentHitTestInfo(aBuilder, child);
4591
4592 DisplayListClipState::AutoClipMultiple clipState(aBuilder);
4593 nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(aBuilder);
4594
4595 if (savedOutOfFlowData) {
4596 aBuilder->SetBuildingInvisibleItems(false);
4597#ifdef DEBUG1
4598 savedOutOfFlowData->CheckASR(aBuilder, child);
4599#endif
4600 const ActiveScrolledRoot* asr =
4601 savedOutOfFlowData->mContainingBlockActiveScrolledRoot;
4602 if (child->IsAbsolutelyPositioned(disp)) {
4603 asr = DisplayPortUtils::GetASRForAbsPosFrame(child, asr, aBuilder);
4604 }
4605
4606 if (aBuilder->IsInViewTransitionCapture()) {
4607 if (!savedOutOfFlowData->mContainingBlockInViewTransitionCapture) {
4608 clipState.Clear();
4609 } else {
4610 clipState.SetClipChainForContainingBlockDescendants(
4611 savedOutOfFlowData->mContainingBlockClipChain);
4612 }
4613 asrSetter.SetCurrentActiveScrolledRoot(nullptr);
4614 } else {
4615 clipState.SetClipChainForContainingBlockDescendants(
4616 savedOutOfFlowData->mContainingBlockClipChain);
4617 asrSetter.SetCurrentActiveScrolledRoot(asr);
4618 asrSetter.SetCurrentScrollParentId(savedOutOfFlowData->mScrollParentId);
4619 }
4620 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", 4621); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "awayFromCommonPath" ") (" "It is impossible when savedOutOfFlowData is true"
")"); do { MOZ_CrashSequence(__null, 4621); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
4621 "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", 4621); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "awayFromCommonPath" ") (" "It is impossible when savedOutOfFlowData is true"
")"); do { MOZ_CrashSequence(__null, 4621); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
4622 } else if (HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) &&
4623 placeholder) {
4624 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"
, 4624); MOZ_PretendNoReturn(); } } while (0)
;
4625 // Every item we build from now until we descent into an out of flow that
4626 // does have saved out of flow data should be invisible. This state gets
4627 // restored when AutoBuildingDisplayList gets out of scope.
4628 aBuilder->SetBuildingInvisibleItems(true);
4629
4630 // If we have nested out-of-flow frames and the outer one isn't visible
4631 // then we won't have stored clip data for it. We can just clear the clip
4632 // instead since we know we won't render anything, and the inner out-of-flow
4633 // frame will setup the correct clip for itself.
4634 clipState.SetClipChainForContainingBlockDescendants(nullptr);
4635 }
4636
4637 // Setup clipping for the parent's overflow:clip,
4638 // or overflow:hidden on elements that don't support scrolling (and therefore
4639 // don't create nsHTML/XULScrollFrame). This clipping needs to not clip
4640 // anything directly rendered by the parent, only the rendering of its
4641 // children.
4642 // Don't use overflowClip to restrict the dirty rect, since some of the
4643 // descendants may not be clipped by it. Even if we end up with unnecessary
4644 // display items, they'll be pruned during ComputeVisibility.
4645 //
4646 // FIXME(emilio): Why can't we handle this more similarly to `clip` (on the
4647 // parent, rather than on the children)? Would ClipContentDescendants do what
4648 // we want?
4649 if (!overflowClipAxes.isEmpty()) {
4650 ApplyOverflowClipping(aBuilder, parent, overflowClipAxes, clipState);
4651 awayFromCommonPath = true;
4652 }
4653
4654 nsDisplayList list(aBuilder);
4655 nsDisplayList extraPositionedDescendants(aBuilder);
4656 const ActiveScrolledRoot* wrapListASR;
4657 bool builtContainerItem = false;
4658 if (isStackingContext) {
4659 // True stacking context.
4660 // For stacking contexts, BuildDisplayListForStackingContext handles
4661 // clipping and MarkAbsoluteFramesForDisplayList.
4662 nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
4663 child->BuildDisplayListForStackingContext(aBuilder, &list,
4664 &builtContainerItem);
4665 wrapListASR = contASRTracker.GetContainerASR();
4666 if (!aBuilder->IsReusingStackingContextItems() &&
4667 aBuilder->GetCaretFrame() == child) {
4668 builtContainerItem = false;
4669 }
4670 } else {
4671 Maybe<nsRect> clipPropClip =
4672 child->GetClipPropClipRect(disp, effects, child->GetSize());
4673 if (clipPropClip) {
4674 aBuilder->IntersectVisibleRect(*clipPropClip);
4675 aBuilder->IntersectDirtyRect(*clipPropClip);
4676 clipState.ClipContentDescendants(*clipPropClip +
4677 aBuilder->ToReferenceFrame(child));
4678 awayFromCommonPath = true;
4679 }
4680
4681 child->MarkAbsoluteFramesForDisplayList(aBuilder);
4682 if (aBuilder->IsForPainting()) {
4683 child->SetBuiltDisplayList(true);
4684 }
4685
4686 // Some SVG frames might change opacity without invalidating the frame, so
4687 // exclude them from the fast-path.
4688 if (!awayFromCommonPath && !child->IsSVGFrame()) {
4689 // The shortcut is available for the child for next time.
4690 child->AddStateBits(NS_FRAME_SIMPLE_DISPLAYLIST);
4691 }
4692
4693 if (!pseudoStackingContext) {
4694 // THIS IS THE COMMON CASE.
4695 // Not a pseudo or real stacking context. Do the simple thing and
4696 // return early.
4697 aBuilder->AdjustWindowDraggingRegion(child);
4698 aBuilder->Check();
4699 child->BuildDisplayList(aBuilder, aLists);
4700 aBuilder->Check();
4701 aBuilder->DisplayCaret(child, aLists.Outlines());
4702 return;
4703 }
4704
4705 // A pseudo-stacking context (e.g., a positioned element with z-index auto).
4706 // We allow positioned descendants of the child to escape to our parent
4707 // stacking context's positioned descendant list, because they might be
4708 // z-index:non-auto
4709 nsDisplayListCollection pseudoStack(aBuilder);
4710
4711 aBuilder->AdjustWindowDraggingRegion(child);
4712 nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
4713 aBuilder->Check();
4714 child->BuildDisplayList(aBuilder, pseudoStack);
4715 aBuilder->Check();
4716 if (aBuilder->DisplayCaret(child, pseudoStack.Outlines())) {
4717 builtContainerItem = false;
4718 }
4719 wrapListASR = contASRTracker.GetContainerASR();
4720
4721 list.AppendToTop(pseudoStack.BorderBackground());
4722 list.AppendToTop(pseudoStack.BlockBorderBackgrounds());
4723 list.AppendToTop(pseudoStack.Floats());
4724 list.AppendToTop(pseudoStack.Content());
4725 list.AppendToTop(pseudoStack.Outlines());
4726 extraPositionedDescendants.AppendToTop(pseudoStack.PositionedDescendants());
4727 }
4728
4729 buildingForChild.RestoreBuildingInvisibleItemsValue();
4730
4731 if (!list.IsEmpty()) {
4732 if (isPositioned || isStackingContext) {
4733 // Genuine stacking contexts, and positioned pseudo-stacking-contexts,
4734 // go in this level.
4735 nsDisplayItem* item = WrapInWrapList(aBuilder, child, &list, wrapListASR,
4736 builtContainerItem);
4737 if (isSVG) {
4738 aLists.Content()->AppendToTop(item);
4739 } else {
4740 aLists.PositionedDescendants()->AppendToTop(item);
4741 }
4742 } else if (!isSVG && disp->IsFloating(child)) {
4743 aLists.Floats()->AppendToTop(
4744 WrapInWrapList(aBuilder, child, &list, wrapListASR));
4745 } else {
4746 aLists.Content()->AppendToTop(&list);
4747 }
4748 }
4749 // We delay placing the positioned descendants of positioned frames to here,
4750 // because in the absence of z-index this is the correct order for them.
4751 // This doesn't affect correctness because the positioned descendants list
4752 // is sorted by z-order and content in BuildDisplayListForStackingContext,
4753 // but it means that sort routine needs to do less work.
4754 aLists.PositionedDescendants()->AppendToTop(&extraPositionedDescendants);
4755}
4756
4757void nsIFrame::MarkAbsoluteFramesForDisplayList(
4758 nsDisplayListBuilder* aBuilder) {
4759 if (const auto* absCB = GetAbsoluteContainingBlock()) {
4760 aBuilder->MarkFramesForDisplayList(this, absCB->GetChildList());
4761 }
4762}
4763
4764nsIContent* nsIFrame::GetExplicitEventTargetContent(
4765 const WidgetEvent* aEvent /* = nullptr */) const {
4766 // Return the content as-is if this is not a generated content.
4767 if (!IsGeneratedContentFrame()) {
4768 return GetContent();
4769 }
4770 // If the content is a generated content, it won't handle any events from the
4771 // DOM point of view. Therefore, let's return the parent of the generated
4772 // content.
4773 const nsIFrame* generatedRoot = this;
4774 while (true) {
4775 auto* parent = nsLayoutUtils::GetParentOrPlaceholderFor(generatedRoot);
4776 if (!parent || !parent->IsGeneratedContentFrame()) {
4777 break;
4778 }
4779 generatedRoot = parent;
4780 }
4781 // Return the non-generated ancestor.
4782 return generatedRoot->GetContent()->GetParent();
4783}
4784
4785nsIContent* nsIFrame::GetEventTargetContent(
4786 const mozilla::WidgetEvent* aEvent /* = nullptr */) const {
4787 return nsContentUtils::GetEventTargetContent(
4788 GetExplicitEventTargetContent(aEvent), aEvent);
4789}
4790
4791void nsIFrame::FireDOMEvent(const nsAString& aDOMEventName,
4792 nsIContent* aContent) {
4793 nsIContent* target = aContent ? aContent : GetContent();
4794
4795 if (target) {
4796 auto asyncDispatcher = MakeRefPtr<AsyncEventDispatcher>(
4797 target, aDOMEventName, CanBubble::eYes, ChromeOnlyDispatch::eNo);
4798 DebugOnly<nsresult> rv = asyncDispatcher->PostDOMEvent();
4799 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"
, 4799); MOZ_PretendNoReturn(); } } while (0)
;
4800 }
4801}
4802
4803nsresult nsIFrame::HandleEvent(nsPresContext* aPresContext,
4804 WidgetGUIEvent* aEvent,
4805 nsEventStatus* aEventStatus) {
4806 if (aEvent->mMessage == eMouseMove) {
4807 // XXX If the second argument of HandleDrag() is WidgetMouseEvent,
4808 // the implementation becomes simpler.
4809 return HandleDrag(aPresContext, aEvent, aEventStatus);
4810 }
4811
4812 if (aEvent->mMessage == eContextMenu) {
4813 // A web page cannot cancel the context menu when the user has turned off
4814 // dom.event.contextmenu.enabled, and the menu opens anyway.
4815 const bool isMenuCanceled =
4816 *aEventStatus == nsEventStatus_eConsumeNoDefault &&
4817 (Preferences::GetBool("dom.event.contextmenu.enabled", true) ||
4818 (mContent && mContent->NodePrincipal()->IsSystemPrincipal()));
4819 // An untrusted event must not move the selection, since that is the
4820 // user's.
4821 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
4822 if (!isMenuCanceled && mouseEvent && mouseEvent->IsReal() &&
4823 aEvent->IsTrusted()) {
4824 HandleContextMenuEventToSelectWordOrLink(*mouseEvent);
4825 // Be aware, this frame might have already been destroyed here.
4826 }
4827 return NS_OK;
4828 }
4829
4830 if ((aEvent->mClass == eMouseEventClass &&
4831 aEvent->AsMouseEvent()->mButton == MouseButton::ePrimary) ||
4832 aEvent->mClass == eTouchEventClass) {
4833 if (aEvent->mMessage == eMouseDown || aEvent->mMessage == eTouchStart) {
4834 HandlePress(aPresContext, aEvent, aEventStatus);
4835 } else if (aEvent->mMessage == eMouseUp || aEvent->mMessage == eTouchEnd) {
4836 HandleRelease(aPresContext, aEvent, aEventStatus);
4837 }
4838 return NS_OK;
4839 }
4840
4841 // When secondary button is down, we need to move selection to make users
4842 // possible to paste something at click point quickly.
4843 // When middle button is down, we need to just move selection and focus at
4844 // the clicked point. Note that even if middle click paste is not enabled,
4845 // Chrome moves selection at middle mouse button down. So, we should follow
4846 // the behavior for the compatibility.
4847 if (aEvent->mMessage == eMouseDown) {
4848 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
4849 if (mouseEvent && (mouseEvent->mButton == MouseButton::eSecondary ||
4850 mouseEvent->mButton == MouseButton::eMiddle)) {
4851 if (*aEventStatus == nsEventStatus_eConsumeNoDefault) {
4852 return NS_OK;
4853 }
4854 return MoveCaretToEventPoint(aPresContext, mouseEvent, aEventStatus);
4855 }
4856 }
4857
4858 return NS_OK;
4859}
4860
4861nsresult nsIFrame::GetDataForTableSelection(
4862 const nsFrameSelection* aFrameSelection, mozilla::PresShell* aPresShell,
4863 WidgetMouseEvent* aMouseEvent, nsIContent** aParentContent,
4864 int32_t* aContentOffset, TableSelectionMode* aTarget) {
4865 if (!aFrameSelection || !aPresShell || !aMouseEvent || !aParentContent ||
4866 !aContentOffset || !aTarget) {
4867 return NS_ERROR_NULL_POINTER;
4868 }
4869
4870 *aParentContent = nullptr;
4871 *aContentOffset = 0;
4872 *aTarget = TableSelectionMode::None;
4873
4874 int16_t displaySelection = aPresShell->GetSelectionFlags();
4875
4876 bool selectingTableCells = aFrameSelection->IsInTableSelectionMode();
4877
4878 // DISPLAY_ALL means we're in an editor.
4879 // If already in cell selection mode,
4880 // continue selecting with mouse drag or end on mouse up,
4881 // or when using shift key to extend block of cells
4882 // (Mouse down does normal selection unless Ctrl/Cmd is pressed)
4883 bool doTableSelection =
4884 displaySelection == nsISelectionDisplay::DISPLAY_ALL &&
4885 selectingTableCells &&
4886 (aMouseEvent->mMessage == eMouseMove ||
4887 (aMouseEvent->mMessage == eMouseUp &&
4888 aMouseEvent->mButton == MouseButton::ePrimary) ||
4889 aMouseEvent->IsShift());
4890
4891 if (!doTableSelection) {
4892 // In Browser, special 'table selection' key must be pressed for table
4893 // selection or when just Shift is pressed and we're already in table/cell
4894 // selection mode
4895#ifdef XP_MACOSX
4896 doTableSelection = aMouseEvent->IsMeta() ||
4897 (aMouseEvent->IsShift() && selectingTableCells);
4898#else
4899 doTableSelection = aMouseEvent->IsControl() ||
4900 (aMouseEvent->IsShift() && selectingTableCells);
4901#endif
4902 }
4903 if (!doTableSelection) {
4904 return NS_OK;
4905 }
4906
4907 // Get the cell frame or table frame (or parent) of the current content node
4908 nsIFrame* frame = this;
4909 bool foundCell = false;
4910 bool foundTable = false;
4911
4912 const Element* const independentSelectionLimiter =
4913 aFrameSelection->GetIndependentSelectionRootElement();
4914
4915 if (independentSelectionLimiter &&
4916 !independentSelectionLimiter->Contains(GetContent())) {
4917 // If our content is not under the limiting node, stop the search right now.
4918 return NS_OK;
4919 }
4920
4921 for (; frame; frame = frame->GetParent()) {
4922 // Check for a table cell by querying to a known CellFrame interface
4923 if (static_cast<nsITableCellLayout*>(do_QueryFrame(frame))) {
4924 foundCell = true;
4925 // TODO: If we want to use proximity to top or left border
4926 // for row and column selection, this is the place to do it
4927 break;
4928 }
4929 if (frame->IsTableWrapperFrame()) {
4930 // If not a cell, check for table. This will happen when the starting
4931 // frame is the table or child of a table, such as a row (we were
4932 // in-between cells or in table border).
4933 // TODO: How can we select row when along left table edge
4934 // or select column when along top edge?
4935 foundTable = true;
4936 break;
4937 }
4938 }
4939
4940 if (!foundCell && !foundTable) {
4941 // We aren't in a cell or table.
4942 return NS_OK;
4943 }
4944
4945 nsIContent* tableOrCellContent = frame->GetContent();
4946 if (!tableOrCellContent) {
4947 return NS_OK;
4948 }
4949
4950 if (independentSelectionLimiter &&
4951 !independentSelectionLimiter->Contains(tableOrCellContent)) {
4952 return NS_OK;
4953 }
4954
4955 nsCOMPtr<nsIContent> parentContent = tableOrCellContent->GetParent();
4956 if (!parentContent) {
4957 return NS_ERROR_FAILURE;
4958 }
4959
4960 const int32_t offset =
4961 parentContent->ComputeIndexOf_Deprecated(tableOrCellContent);
4962 // Not likely?
4963 if (offset < 0) {
4964 return NS_ERROR_FAILURE;
4965 }
4966
4967 // Everything is OK -- set the return values
4968 parentContent.forget(aParentContent);
4969
4970 *aContentOffset = offset;
4971
4972#if 0
4973 if (selectRow)
4974 *aTarget = TableSelectionMode::Row;
4975 else if (selectColumn)
4976 *aTarget = TableSelectionMode::Column;
4977 else
4978#endif
4979 if (foundCell) {
4980 *aTarget = TableSelectionMode::Cell;
4981 } else if (foundTable) {
4982 *aTarget = TableSelectionMode::Table;
4983 }
4984
4985 return NS_OK;
4986}
4987
4988static bool IsEditingHost(const nsIFrame* aFrame) {
4989 if (aFrame->Style()->GetPseudoType() ==
4990 PseudoStyleType::MozTextControlEditingRoot) {
4991 return true;
4992 }
4993 nsIContent* content = aFrame->GetContent();
4994 return content && content->IsEditingHost();
4995}
4996
4997mozilla::StyleUserSelect nsIFrame::UsedUserSelect(
4998 const nsIFrame* aFrame, nsIFrame::ForSelectionStart aType) {
4999 return UsedUserSelectRecurse(aFrame, aType).valueOr(StyleUserSelect::Text);
5000}
5001
5002Maybe<mozilla::StyleUserSelect> nsIFrame::UsedUserSelectRecurse(
5003 const nsIFrame* aFrame, nsIFrame::ForSelectionStart aType) {
5004 if (aFrame->IsGeneratedContentFrame()) {
5005 return Some(StyleUserSelect::None);
5006 }
5007
5008 // Per https://drafts.csswg.org/css-ui-4/#content-selection:
5009 //
5010 // The used value is the same as the computed value, except:
5011 //
5012 // 1 - on editable elements where the used value is always 'contain'
5013 // regardless of the computed value
5014 // 2 - when the computed value is auto, in which case the used value is one
5015 // of the other values...
5016 //
5017 // See https://github.com/w3c/csswg-drafts/issues/3344 to see why we do this
5018 // at used-value time instead of at computed-value time.
5019
5020 // Although the draft does not define that editable elements under an editing
5021 // host should ignore `user-select`, Chrome ignores `user-select:none` and
5022 // `user-select:all` and we've already considered as ignoring the
5023 // `user-select` according to this test:
5024 // https://searchfox.org/firefox-main/rev/6abddcb0a5076c3b888686ede6f4cf7d082460d3/dom/base/test/test_bug1101364.html#73-85
5025 // https://searchfox.org/firefox-main/rev/4fd0fa7e5814c0b51f1dd075821988377bc56cc1/testing/web-platform/tests/css/css-ui/user-select-none-in-editable.html#23-24,32-36
5026 // Therefore, we check aFrame->ContentIsEditable() instead of
5027 // IsEditingHost(aFrame).
5028 if (aFrame->IsTextInputFrame() || aFrame->ContentIsEditable()) {
5029 // We don't implement 'contain' itself, but we make 'text' behave as
5030 // 'contain' for contenteditable and <input> / <textarea> elements anyway so
5031 // this is ok.
5032 return Some(StyleUserSelect::Text);
5033 }
5034
5035 auto style = aFrame->Style()->UserSelect();
5036 if (style != StyleUserSelect::Auto) {
5037 return Some(style);
5038 }
5039
5040 const auto IsButton = [](const nsIFrame* aFrame) {
5041 const auto* content = aFrame->GetContent();
5042 if (!content) {
5043 return false;
5044 }
5045 return content->IsAnyOfHTMLElements(nsGkAtoms::button);
5046 };
5047
5048 auto* parent = nsLayoutUtils::GetParentOrPlaceholderFor(aFrame);
5049 const auto parentResult =
5050 parent ? UsedUserSelectRecurse(parent, aType) : Nothing{};
5051 if (parentResult.valueOr(StyleUserSelect::Auto) != StyleUserSelect::None &&
5052 aType == nsIFrame::ForSelectionStart::Yes && IsButton(aFrame)) {
5053 // If the parent did not force our hands, we want non-`contenteditable`
5054 // buttons to be clickable without accidentally selecting the text within
5055 // it, but still allow selection that originates outside of it.
5056 return Some(StyleUserSelect::None);
5057 }
5058 return parentResult;
5059}
5060
5061bool nsIFrame::IsSelectable(StyleUserSelect* aSelectStyle) const {
5062 auto style = UsedUserSelect(this, ForSelectionStart::No);
5063 if (aSelectStyle) {
5064 *aSelectStyle = style;
5065 }
5066 return style != StyleUserSelect::None;
5067}
5068
5069bool nsIFrame::ShouldPaintNormalSelection() const {
5070 if (IsSelectable()) {
5071 // NOTE: Ideally, we should return false if display selection is "OFF".
5072 // However, here is a hot path at painting. Therefore, it should be checked
5073 // before and we shouldn't need to check it.
5074 return true;
5075 }
5076 // If we're not selectable by user, we should paint selection only while the
5077 // normal selection is styled as "attention" by "Find in Page" or something.
5078 return GetDisplaySelection() == nsISelectionController::SELECTION_ATTENTION;
5079}
5080
5081bool nsIFrame::ShouldHaveLineIfEmpty() const {
5082 switch (Style()->GetPseudoType()) {
5083 case PseudoStyleType::NotPseudo:
5084 break;
5085 case PseudoStyleType::MozScrolledContent:
5086 return GetParent()->ShouldHaveLineIfEmpty();
5087 default:
5088 return false;
5089 }
5090 return IsInputButtonControlFrame() || IsEditingHost(this);
5091}
5092
5093/**
5094 * Handles the Mouse Press Event for the frame
5095 */
5096NS_IMETHODIMPnsresult
5097nsIFrame::HandlePress(nsPresContext* aPresContext, WidgetGUIEvent* aEvent,
5098 nsEventStatus* aEventStatus) {
5099 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", 5099); return
NS_ERROR_INVALID_POINTER; } } while (false)
;
5100 if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
5101 return NS_OK;
5102 }
5103
5104 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", 5104); return NS_ERROR_INVALID_POINTER
; } } while (false)
;
5105 if (aEvent->mClass == eTouchEventClass) {
5106 return NS_OK;
5107 }
5108
5109 return MoveCaretToEventPoint(aPresContext, aEvent->AsMouseEvent(),
5110 aEventStatus);
5111}
5112
5113nsresult nsIFrame::MoveCaretToEventPoint(nsPresContext* aPresContext,
5114 WidgetMouseEvent* aMouseEvent,
5115 nsEventStatus* aEventStatus) {
5116 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"
, 5116); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aPresContext"
")"); do { MOZ_CrashSequence(__null, 5116); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5117 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"
, 5117); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aMouseEvent"
")"); do { MOZ_CrashSequence(__null, 5117); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5118 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", 5118); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aMouseEvent->mMessage == eMouseDown" ")"
); do { MOZ_CrashSequence(__null, 5118); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5119 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"
, 5119); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aEventStatus"
")"); do { MOZ_CrashSequence(__null, 5119); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5120 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", 5120); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "nsEventStatus_eConsumeNoDefault != *aEventStatus"
")"); do { MOZ_CrashSequence(__null, 5120); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5121
5122 mozilla::PresShell* presShell = aPresContext->GetPresShell();
5123 if (!presShell) {
5124 return NS_ERROR_FAILURE;
5125 }
5126
5127 // We often get out of sync state issues with mousedown events that
5128 // get interrupted by alerts/dialogs.
5129 // Check with the ESM to see if we should process this one
5130 EventStateManager* const esm = aPresContext->EventStateManager();
5131 if (!esm->EventStatusOK(aMouseEvent)) {
5132 return NS_OK;
5133 }
5134
5135 if (nsIContent* dragGestureContent = esm->GetTrackingDragGestureContent()) {
5136 // When the current tracked dragging gesture is different than this frame,
5137 // it means this frame was being dragged, however it got moved/destroyed. So
5138 // we should consider the drag is still happening, so return early here.
5139 // Additionally, when dragGestureContent is a `Text` and the text frame is
5140 // reframed by a preceding event listener, we're the parent element frame.
5141 // In that case, we need to treat this as a normal mouse button down.
5142 // Therefore, we should compare the inclusive flattened tree ancestor
5143 // element of dragGestureContent and our content.
5144 const bool isDragGestureContent =
5145 mContent == dragGestureContent ||
5146 mContent ==
5147 dragGestureContent->GetInclusiveFlattenedTreeAncestorElement();
5148 if (!isDragGestureContent) {
5149 return NS_OK;
5150 }
5151 }
5152
5153 const nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(
5154 aMouseEvent, RelativeTo{this});
5155
5156 // When not using `alt`, and clicking on a draggable, but non-editable
5157 // element, don't do anything, and let d&d handle the event.
5158 //
5159 // See bug 48876, bug 388659 and bug 55921 for context here.
5160 //
5161 // FIXME(emilio): The .Contains(pt) check looks a bit fishy. When would it be
5162 // false given we're the event target? If it is needed, why not checking the
5163 // actual draggable node rect instead?
5164 if (!aMouseEvent->IsAlt() && GetRectRelativeToSelf().Contains(pt)) {
5165 for (nsIContent* content = mContent; content;
5166 content = content->GetFlattenedTreeParent()) {
5167 if (nsContentUtils::ContentIsDraggable(content) &&
5168 !content->IsEditable()) {
5169 return NS_OK;
5170 }
5171 }
5172 }
5173
5174 // If we are in Navigator and the click is in a draggable node, we don't want
5175 // to start selection because we don't want to interfere with a potential
5176 // drag of said node and steal all its glory.
5177 const bool isEditor =
5178 presShell->GetSelectionFlags() == nsISelectionDisplay::DISPLAY_ALL;
5179
5180 // Don't do something if it's middle button down event.
5181 const bool isPrimaryButtonDown =
5182 aMouseEvent->mButton == MouseButton::ePrimary;
5183
5184 // Check whether this frame should handle selection events. If not, don't
5185 // tell selection the mouse event even occurred.
5186 if (!ShouldHandleSelectionMovementEvents(ForSelectionStart::Yes)) {
5187 return NS_OK;
5188 }
5189
5190 if (isPrimaryButtonDown) {
5191 // If the mouse is dragged outside the nearest enclosing scrollable area
5192 // while making a selection, the area will be scrolled. To do this, capture
5193 // the mouse on the nearest scroll container frame. If there isn't a scroll
5194 // container frame, or something else is already capturing the mouse,
5195 // there's no reason to capture.
5196 if (!PresShell::GetCapturingContent()) {
5197 ScrollContainerFrame* scrollContainerFrame =
5198 nsLayoutUtils::GetNearestScrollContainerFrame(
5199 this, nsLayoutUtils::SCROLLABLE_SAME_DOC |
5200 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
5201 if (scrollContainerFrame) {
5202 nsIFrame* capturingFrame = scrollContainerFrame;
5203 PresShell::SetCapturingContent(capturingFrame->GetContent(),
5204 CaptureFlags::IgnoreAllowedState);
5205 }
5206 }
5207 }
5208
5209 // XXX This is screwy; it really should use the selection frame, not the
5210 // event frame
5211 const nsFrameSelection* frameselection = GetConstFrameSelection();
5212 if (!frameselection || frameselection->GetDisplaySelection() ==
5213 nsISelectionController::SELECTION_OFF) {
5214 return NS_OK; // nothing to do we cannot affect selection from here
5215 }
5216
5217#ifdef XP_MACOSX
5218 // If Control key is pressed on macOS, it should be treated as right click.
5219 // So, don't change selection.
5220 if (aMouseEvent->IsControl()) {
5221 return NS_OK;
5222 }
5223 const bool control = aMouseEvent->IsMeta();
5224#else
5225 const bool control = aMouseEvent->IsControl();
5226#endif
5227
5228 RefPtr<nsFrameSelection> fc = const_cast<nsFrameSelection*>(frameselection);
5229 if (isPrimaryButtonDown && aMouseEvent->mClickCount > 1) {
5230 // These methods aren't const but can't actually delete anything,
5231 // so no need for AutoWeakFrame.
5232 fc->SetDragState(true);
5233 return HandleMultiplePress(aPresContext, aMouseEvent, aEventStatus,
5234 control);
5235 }
5236
5237 ContentOffsets offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN);
5238
5239 if (!offsets.content) {
5240 return NS_ERROR_FAILURE;
5241 }
5242
5243 const bool isSecondaryButton =
5244 aMouseEvent->mButton == MouseButton::eSecondary;
5245 if (isSecondaryButton) {
5246 if (!MovingCaretToEventPointAllowedIfSecondaryButtonEvent(
5247 *frameselection, *aMouseEvent, *offsets.content,
5248 // When we collapse selection in nsFrameSelection::TakeFocus,
5249 // we always collapse selection to the start offset. Therefore,
5250 // we can ignore the end offset here. E.g., when an <img> is
5251 // clicked, set the primary offset to after it, but the the
5252 // secondary offset may be before it, see OffsetsForSingleFrame for
5253 // the detail.
5254 offsets.StartOffset())) {
5255 return NS_OK;
5256 }
5257 }
5258
5259 if (aMouseEvent->mMessage == eMouseDown &&
5260 aMouseEvent->mButton == MouseButton::eMiddle &&
5261 !offsets.content->IsEditable()) {
5262 // However, some users don't like the Chrome compatible behavior of
5263 // middle mouse click. They want to keep selection after starting
5264 // autoscroll. However, the selection change is important for middle
5265 // mouse past. Therefore, we should allow users to take the traditional
5266 // behavior back by themselves unless middle click paste is enabled or
5267 // autoscrolling is disabled.
5268 if (!Preferences::GetBool("middlemouse.paste", false) &&
5269 Preferences::GetBool("general.autoScroll", false) &&
5270 Preferences::GetBool("general.autoscroll.prevent_to_collapse_selection_"
5271 "by_middle_mouse_down",
5272 false)) {
5273 return NS_OK;
5274 }
5275 }
5276
5277 if (isPrimaryButtonDown) {
5278 // Let Ctrl/Cmd + left mouse down do table selection instead of drag
5279 // initiation.
5280 nsCOMPtr<nsIContent> parentContent;
5281 int32_t contentOffset;
5282 TableSelectionMode target;
5283 nsresult rv = GetDataForTableSelection(
5284 frameselection, presShell, aMouseEvent, getter_AddRefs(parentContent),
5285 &contentOffset, &target);
5286 if (NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1))) && parentContent) {
5287 fc->SetDragState(true);
5288 return fc->HandleTableSelection(parentContent, contentOffset, target,
5289 aMouseEvent);
5290 }
5291 }
5292
5293 fc->SetDelayedCaretData(nullptr);
5294
5295 if (isPrimaryButtonDown) {
5296 // Check if any part of this frame is selected, and if the user clicked
5297 // inside the selected region, and if it's the left button. If so, we delay
5298 // starting a new selection since the user may be trying to drag the
5299 // selected region to some other app.
5300
5301 if (GetContent() && GetContent()->IsMaybeSelected()) {
5302 bool inSelection = false;
5303 UniquePtr<SelectionDetails> details = frameselection->LookUpSelection(
5304 offsets.content, 0, offsets.EndOffset(),
5305 nsFrameSelection::IgnoreNormalSelection::No);
5306
5307 //
5308 // If there are any details, check to see if the user clicked
5309 // within any selected region of the frame.
5310 //
5311
5312 for (SelectionDetails* curDetail = details.get(); curDetail;
5313 curDetail = curDetail->mNext.get()) {
5314 //
5315 // If the user clicked inside a selection, then just
5316 // return without doing anything. We will handle placing
5317 // the caret later on when the mouse is released. We ignore
5318 // the spellcheck, find and url formatting selections.
5319 //
5320 if (curDetail->mSelectionType != SelectionType::eSpellCheck &&
5321 curDetail->mSelectionType != SelectionType::eFind &&
5322 curDetail->mSelectionType != SelectionType::eURLSecondary &&
5323 curDetail->mSelectionType != SelectionType::eURLStrikeout &&
5324 curDetail->mSelectionType != SelectionType::eHighlight &&
5325 curDetail->mSelectionType != SelectionType::eTargetText &&
5326 curDetail->mStart <= offsets.StartOffset() &&
5327 offsets.EndOffset() <= curDetail->mEnd) {
5328 inSelection = true;
5329 }
5330 }
5331
5332 if (inSelection) {
5333 fc->SetDragState(false);
5334 fc->SetDelayedCaretData(aMouseEvent);
5335 return NS_OK;
5336 }
5337 }
5338
5339 fc->SetDragState(true);
5340 }
5341
5342 // Do not touch any nsIFrame members after this point without adding
5343 // weakFrame checks.
5344 const nsFrameSelection::FocusMode focusMode = [&]() {
5345 // If "Shift" and "Ctrl" are both pressed, "Shift" is given precedence. This
5346 // mimics the old behaviour.
5347 const bool isShift =
5348 aMouseEvent->IsShift() &&
5349 // If Shift + secondary button press shoud open context menu without a
5350 // contextmenu event, user wants to open context menu like as a
5351 // secondary button press without Shift key.
5352 !(isSecondaryButton &&
5353 StaticPrefs::dom_event_contextmenu_shift_suppresses_event());
5354 if (isShift) {
5355 // If clicked in a link when focused content is editable, we should
5356 // collapse selection in the link for compatibility with Blink.
5357 if (isEditor) {
5358 for (Element* element : mContent->InclusiveAncestorsOfType<Element>()) {
5359 if (element->IsLink()) {
5360 return nsFrameSelection::FocusMode::kCollapseToNewPoint;
5361 }
5362 }
5363 }
5364 return nsFrameSelection::FocusMode::kExtendSelection;
5365 }
5366
5367 if (isPrimaryButtonDown && control) {
5368 return nsFrameSelection::FocusMode::kMultiRangeSelection;
5369 }
5370
5371 return nsFrameSelection::FocusMode::kCollapseToNewPoint;
5372 }();
5373
5374 nsresult rv = fc->HandleClick(
5375 MOZ_KnownLive(offsets.content)(offsets.content) /* bug 1636889 */, offsets.StartOffset(),
5376 offsets.EndOffset(), focusMode, offsets.associate);
5377 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
5378 return rv;
5379 }
5380
5381 // We don't handle mouse button up if it's middle button.
5382 if (isPrimaryButtonDown && offsets.offset != offsets.secondaryOffset) {
5383 fc->MaintainSelection();
5384 }
5385
5386 if (isPrimaryButtonDown && isEditor && !aMouseEvent->IsShift() &&
5387 (offsets.EndOffset() - offsets.StartOffset()) == 1) {
5388 // A single node is selected and we aren't extending an existing selection,
5389 // which means the user clicked directly on an object (either
5390 // `user-select: all` or a non-text node without children). Therefore,
5391 // disable selection extension during mouse moves.
5392 // XXX This is a bit hacky; shouldn't editor be able to deal with this?
5393 fc->SetDragState(false);
5394 }
5395
5396 return NS_OK;
5397}
5398
5399bool nsIFrame::MovingCaretToEventPointAllowedIfSecondaryButtonEvent(
5400 const nsFrameSelection& aFrameSelection,
5401 WidgetMouseEvent& aSecondaryButtonEvent,
5402 const nsIContent& aContentAtEventPoint, int32_t aOffsetAtEventPoint) const {
5403 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"
, 5403); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aSecondaryButtonEvent.mButton == MouseButton::eSecondary"
")"); do { MOZ_CrashSequence(__null, 5403); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5404
5405 if (NS_WARN_IF(aOffsetAtEventPoint < 0)NS_warn_if_impl(aOffsetAtEventPoint < 0, "aOffsetAtEventPoint < 0"
, "./../../../layout/generic/nsIFrame.cpp", 5405)
) {
5406 return false;
5407 }
5408
5409 const bool contentIsEditable = aContentAtEventPoint.IsEditable();
5410 const TextControlElement* const contentAsTextControl =
5411 TextControlElement::FromNodeOrNull(
5412 aContentAtEventPoint.IsTextControlElement()
5413 ? &aContentAtEventPoint
5414 : aContentAtEventPoint.GetClosestNativeAnonymousSubtreeRoot());
5415 const Selection& selection = aFrameSelection.NormalSelection();
5416 const bool selectionIsCollapsed =
5417 selection.AreNormalAndCrossShadowBoundaryRangesCollapsed();
5418 // If right click in a selection range, we should not collapse
5419 // selection.
5420 if (!selectionIsCollapsed && nsContentUtils::IsPointInSelection(
5421 selection, aContentAtEventPoint,
5422 static_cast<uint32_t>(aOffsetAtEventPoint),
5423 true /* aAllowCrossShadowBoundary */)) {
5424 return false;
5425 }
5426 const bool wantToPreventMoveCaret =
5427 StaticPrefs::
5428 ui_mouse_right_click_move_caret_stop_if_in_focused_editable_node() &&
5429 selectionIsCollapsed && (contentIsEditable || contentAsTextControl);
5430 const bool wantToPreventCollapseSelection =
5431 StaticPrefs::
5432 ui_mouse_right_click_collapse_selection_stop_if_non_collapsed_selection() &&
5433 !selectionIsCollapsed;
5434 if (wantToPreventMoveCaret || wantToPreventCollapseSelection) {
5435 // If currently selection is limited in an editing host, we should not
5436 // collapse selection nor move caret if the clicked point is in the
5437 // ancestor limiter. Otherwise, this mouse click moves focus from the
5438 // editing host to different one or blur the editing host. In this case,
5439 // we need to update selection because keeping current selection in the
5440 // editing host looks like it's not blurred.
5441 // FIXME: If the active editing host is the document element, editor
5442 // does not set ancestor limiter properly. Fix it in the editor side.
5443 if (nsIContent* ancestorLimiter = selection.GetAncestorLimiter()) {
5444 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"
, 5444); AnnotateMozCrashReason("MOZ_ASSERT" "(" "ancestorLimiter->IsEditable()"
")"); do { MOZ_CrashSequence(__null, 5444); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5445 return !aContentAtEventPoint.IsInclusiveDescendantOf(ancestorLimiter);
5446 }
5447 }
5448 // If selection is editable and `stop_if_in_focused_editable_node` pref is
5449 // set to true, user does not want to move caret to right click place if
5450 // clicked in the focused text control element.
5451 if (wantToPreventMoveCaret && contentAsTextControl &&
5452 contentAsTextControl == nsFocusManager::GetFocusedElementStatic()) {
5453 return false;
5454 }
5455 // If currently selection is not limited in an editing host, we should
5456 // collapse selection only when this click moves focus to an editing
5457 // host because we need to update selection in this case.
5458 if (wantToPreventCollapseSelection && !contentIsEditable) {
5459 return false;
5460 }
5461
5462 return !StaticPrefs::
5463 ui_mouse_right_click_collapse_selection_stop_if_non_editable_node() ||
5464 // The user does not want to collapse selection into non-editable
5465 // content by a right button click.
5466 contentIsEditable ||
5467 // Treat clicking in a text control as always clicked on editable
5468 // content because we want a hack only for clicking in normal text
5469 // nodes which is outside any editing hosts.
5470 contentAsTextControl;
5471}
5472
5473void nsIFrame::HandleContextMenuEventToSelectWordOrLink(
5474 const WidgetMouseEvent& aContextMenuEvent) {
5475 MOZ_ASSERT(aContextMenuEvent.mMessage == eContextMenu)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aContextMenuEvent.mMessage == eContextMenu)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(aContextMenuEvent.mMessage == eContextMenu))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("aContextMenuEvent.mMessage == eContextMenu"
, "./../../../layout/generic/nsIFrame.cpp", 5475); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aContextMenuEvent.mMessage == eContextMenu"
")"); do { MOZ_CrashSequence(__null, 5475); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5476
5477 if (!StaticPrefs::ui_mouse_right_click_select_under_cursor()) {
5478 return;
5479 }
5480
5481 // A menu opened from the keyboard acts on the selection the user already
5482 // made, and has no click point to select at.
5483 if (aContextMenuEvent.IsContextMenuKeyEvent()) {
5484 return;
5485 }
5486
5487 if (!ShouldHandleSelectionMovementEvents(ForSelectionStart::Yes)) {
5488 return;
5489 }
5490
5491 const nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(
5492 &aContextMenuEvent, RelativeTo{this});
5493 ContentOffsets offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN);
5494 if (!offsets.content) {
5495 return;
5496 }
5497
5498 const RefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
5499 if (!frameSelection) {
5500 return;
5501 }
5502
5503 if (!SelectingWordOrLinkAtEventPointAllowed(
5504 *frameSelection, aContextMenuEvent, pt, *offsets.content,
5505 offsets.StartOffset())) {
5506 return;
5507 }
5508
5509 SelectWordOrLinkAtPoint(pt,
5510 MOZ_KnownLive(*offsets.content)(*offsets.content) /* bug 1636889 */);
5511}
5512
5513bool nsIFrame::SelectingWordOrLinkAtEventPointAllowed(
5514 const nsFrameSelection& aFrameSelection,
5515 const WidgetMouseEvent& aContextMenuEvent, const nsPoint& aPoint,
5516 const nsIContent& aContentAtEventPoint, int32_t aOffsetAtEventPoint) const {
5517 MOZ_ASSERT(aContextMenuEvent.mMessage == eContextMenu)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aContextMenuEvent.mMessage == eContextMenu)>::isValid
, "invalid assertion condition"); if ((__builtin_expect(!!(!(
!!(aContextMenuEvent.mMessage == eContextMenu))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("aContextMenuEvent.mMessage == eContextMenu"
, "./../../../layout/generic/nsIFrame.cpp", 5517); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aContextMenuEvent.mMessage == eContextMenu"
")"); do { MOZ_CrashSequence(__null, 5517); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
5518
5519 const bool contentIsEditable = aContentAtEventPoint.IsEditable();
5520 if (contentIsEditable &&
5521 !StaticPrefs::ui_mouse_right_click_select_in_editable()) {
5522 return false;
5523 }
5524
5525 // Shift + secondary button press has already extended or collapsed the
5526 // selection on mouse down, so the user is pointing at a boundary rather than
5527 // at a word.
5528 if (aContextMenuEvent.IsShift()) {
5529 return false;
5530 }
5531
5532 if (NS_WARN_IF(aOffsetAtEventPoint < 0)NS_warn_if_impl(aOffsetAtEventPoint < 0, "aOffsetAtEventPoint < 0"
, "./../../../layout/generic/nsIFrame.cpp", 5532)
) {
5533 return false;
5534 }
5535
5536 const Selection& selection = aFrameSelection.NormalSelection();
5537 // A click inside the selection keeps it, so that the context menu acts on
5538 // what the user selected rather than on what they happened to click.
5539 if (nsContentUtils::IsPointInSelection(
5540 selection, aContentAtEventPoint,
5541 static_cast<uint32_t>(aOffsetAtEventPoint),
5542 true /* aAllowCrossShadowBoundary */)) {
5543 return false;
5544 }
5545
5546 // GetContentOffsetsFromPoint() snaps to the closest text, so the click may
5547 // have landed next to the text rather than on it, e.g. in the padding of a
5548 // block. Only a click on the text itself selects.
5549 const nsIFrame* const frameAtPoint =
5550 nsLayoutUtils::GetFrameForPoint(RelativeTo{this}, aPoint);
5551 return frameAtPoint && frameAtPoint->IsTextFrame();
5552}
5553
5554nsresult nsIFrame::SelectWordOrLinkAtPoint(
5555 const nsPoint& aPoint, const nsIContent& aContentAtEventPoint) {
5556 Element* linkToSelect = nullptr;
5557 for (Element* element :
5558 aContentAtEventPoint.InclusiveFlatTreeAncestorsOfType<Element>()) {
5559 if (element->IsLink()) {
5560 linkToSelect = element;
5561 break;
5562 }
5563 }
5564 if (!linkToSelect) {
5565 return SelectByTypeAtPoint(aPoint, eSelectWord, eSelectWord, 0);
5566 }
5567
5568 RefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
5569 if (!frameSelection) {
5570 return NS_OK;
5571 }
5572 // Passing different start and end offsets is how HandleClick() selects a
5573 // range rather than collapsing, so this covers every child of the link.
5574 const uint32_t endOffset = linkToSelect->GetChildCount();
5575 nsCOMPtr<nsIContent> link = linkToSelect;
5576 return frameSelection->HandleClick(
5577 link, 0u, endOffset, nsFrameSelection::FocusMode::kCollapseToNewPoint,
5578 CaretAssociationHint::After);
5579}
5580
5581nsresult nsIFrame::SelectByTypeAtPoint(const nsPoint& aPoint,
5582 nsSelectionAmount aBeginAmountType,
5583 nsSelectionAmount aEndAmountType,
5584 uint32_t aSelectFlags) {
5585 // No point in selecting if selection is turned off
5586 if (!ShouldHandleSelectionMovementEvents()) {
5587 return NS_OK;
5588 }
5589
5590 ContentOffsets offsets = GetContentOffsetsFromPoint(
5591 aPoint, SKIP_HIDDEN | IGNORE_NATIVE_ANONYMOUS_SUBTREE);
5592 if (!offsets.content) {
5593 return NS_ERROR_FAILURE;
5594 }
5595
5596 FrameAndOffset frameAndOffset = SelectionMovementUtils::GetFrameForNodeOffset(
5597 offsets.content, offsets.offset, offsets.associate);
5598 if (!frameAndOffset) {
5599 return NS_ERROR_FAILURE;
5600 }
5601 return frameAndOffset->PeekBackwardAndForwardForSelection(
5602 aBeginAmountType, aEndAmountType,
5603 static_cast<int32_t>(frameAndOffset.mOffsetInFrameContent),
5604 aBeginAmountType != eSelectWord, aSelectFlags);
5605}
5606
5607/**
5608 * Multiple Mouse Press -- line or paragraph selection -- for the frame.
5609 * Wouldn't it be nice if this didn't have to be hardwired into Frame code?
5610 */
5611NS_IMETHODIMPnsresult
5612nsIFrame::HandleMultiplePress(nsPresContext* aPresContext,
5613 WidgetGUIEvent* aEvent,
5614 nsEventStatus* aEventStatus, bool aControlHeld) {
5615 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", 5615); return NS_ERROR_INVALID_POINTER
; } } while (false)
;
5616 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", 5616); return
NS_ERROR_INVALID_POINTER; } } while (false)
;
5617
5618 if (nsEventStatus_eConsumeNoDefault == *aEventStatus ||
5619 !ShouldHandleSelectionMovementEvents()) {
5620 return NS_OK;
5621 }
5622
5623 // Find out whether we're doing line or paragraph selection.
5624 // If browser.triple_click_selects_paragraph is true, triple-click selects
5625 // paragraph. Otherwise, triple-click selects line, and quadruple-click
5626 // selects paragraph (on platforms that support quadruple-click).
5627 nsSelectionAmount beginAmount, endAmount;
5628 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
5629 if (!mouseEvent) {
5630 return NS_OK;
5631 }
5632
5633 if (mouseEvent->mClickCount == 4) {
5634 beginAmount = endAmount = eSelectParagraph;
5635 } else if (mouseEvent->mClickCount == 3) {
5636 if (Preferences::GetBool("browser.triple_click_selects_paragraph")) {
5637 beginAmount = endAmount = eSelectParagraph;
5638 } else {
5639 beginAmount = eSelectBeginLine;
5640 endAmount = eSelectEndLine;
5641 }
5642 } else if (mouseEvent->mClickCount == 2) {
5643 // We only want inline frames; PeekBackwardAndForward dislikes blocks
5644 beginAmount = endAmount = eSelectWord;
5645 } else {
5646 return NS_OK;
5647 }
5648
5649 nsPoint relPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(
5650 mouseEvent, RelativeTo{this});
5651 return SelectByTypeAtPoint(relPoint, beginAmount, endAmount,
5652 (aControlHeld ? SELECT_ACCUMULATE : 0));
5653}
5654
5655nsresult nsIFrame::PeekBackwardAndForwardForSelection(
5656 nsSelectionAmount aAmountBack, nsSelectionAmount aAmountForward,
5657 int32_t aStartPos, bool aJumpLines, uint32_t aSelectFlags) {
5658 nsIFrame* baseFrame = this;
5659 int32_t baseOffset = aStartPos;
5660 nsresult rv;
5661
5662 PeekOffsetOptions peekOffsetOptions{PeekOffsetOption::StopAtScroller};
5663 if (aJumpLines) {
5664 peekOffsetOptions += PeekOffsetOption::JumpLines;
5665 }
5666
5667 Element* const ancestorLimiter = [&]() -> Element* {
5668 const nsFrameSelection* const frameSelection = GetConstFrameSelection();
5669 return frameSelection
5670 ? frameSelection
5671 ->GetAncestorLimiterOrIndependentSelectionRootElement()
5672 : nullptr;
5673 }();
5674
5675 if (aAmountBack == eSelectWord) {
5676 // To avoid selecting the previous word when at start of word,
5677 // first move one character forward.
5678 PeekOffsetStruct pos(eSelectCharacter, eDirNext, aStartPos, nsPoint(0, 0),
5679 peekOffsetOptions, eDefaultBehavior, ancestorLimiter);
5680 rv = PeekOffset(&pos);
5681 if (NS_SUCCEEDED(rv)((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)))) {
5682 baseFrame = pos.mResultFrame;
5683 baseOffset = pos.mContentOffset;
5684 }
5685 }
5686
5687 // Search backward for a boundary.
5688 PeekOffsetStruct startpos(aAmountBack, eDirPrevious, baseOffset,
5689 nsPoint(0, 0), peekOffsetOptions, eDefaultBehavior,
5690 ancestorLimiter);
5691 rv = baseFrame->PeekOffset(&startpos);
5692 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
5693 return rv;
5694 }
5695
5696 // If the backward search stayed within the same frame, search forward from
5697 // that position for the end boundary; but if it crossed out to a sibling or
5698 // ancestor, start from the original position.
5699 if (startpos.mResultFrame == baseFrame) {
5700 baseOffset = startpos.mContentOffset;
5701 } else {
5702 baseFrame = this;
5703 baseOffset = aStartPos;
5704 }
5705
5706 PeekOffsetStruct endpos(aAmountForward, eDirNext, baseOffset, nsPoint(0, 0),
5707 peekOffsetOptions, eDefaultBehavior, ancestorLimiter);
5708 rv = baseFrame->PeekOffset(&endpos);
5709 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
5710 return rv;
5711 }
5712
5713 // Keep frameSelection alive.
5714 RefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
5715
5716 const nsFrameSelection::FocusMode focusMode =
5717 (aSelectFlags & SELECT_ACCUMULATE)
5718 ? nsFrameSelection::FocusMode::kMultiRangeSelection
5719 : nsFrameSelection::FocusMode::kCollapseToNewPoint;
5720 rv = frameSelection->HandleClick(
5721 MOZ_KnownLive(startpos.mResultContent)(startpos.mResultContent) /* bug 1636889 */,
5722 startpos.mContentOffset, startpos.mContentOffset, focusMode,
5723 CaretAssociationHint::After);
5724 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
5725 return rv;
5726 }
5727
5728 rv = frameSelection->HandleClick(
5729 MOZ_KnownLive(endpos.mResultContent)(endpos.mResultContent) /* bug 1636889 */,
5730 endpos.mContentOffset, endpos.mContentOffset,
5731 nsFrameSelection::FocusMode::kExtendSelection,
5732 CaretAssociationHint::Before);
5733 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
5734 return rv;
5735 }
5736 if (aAmountBack == eSelectWord) {
5737 frameSelection->SetClickSelectionType(ClickSelectionType::Double);
5738 } else if (aAmountBack == eSelectParagraph) {
5739 frameSelection->SetClickSelectionType(ClickSelectionType::Triple);
5740 }
5741
5742 // maintain selection
5743 return frameSelection->MaintainSelection(aAmountBack);
5744}
5745
5746NS_IMETHODIMPnsresult nsIFrame::HandleDrag(nsPresContext* aPresContext,
5747 WidgetGUIEvent* aEvent,
5748 nsEventStatus* aEventStatus) {
5749 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"
, 5750); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aEvent->mClass == eMouseEventClass"
") (" "HandleDrag can only handle mouse event" ")"); do { MOZ_CrashSequence
(__null, 5750); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
5750 "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"
, 5750); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aEvent->mClass == eMouseEventClass"
") (" "HandleDrag can only handle mouse event" ")"); do { MOZ_CrashSequence
(__null, 5750); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
5751
5752 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", 5752); return
NS_ERROR_INVALID_POINTER; } } while (false)
;
5753
5754 RefPtr<nsFrameSelection> frameselection = GetFrameSelection();
5755 if (!frameselection) {
5756 return NS_OK;
5757 }
5758
5759 bool mouseDown = frameselection->GetDragState();
5760 if (!mouseDown) {
5761 return NS_OK;
5762 }
5763
5764 nsIFrame* scrollbar =
5765 nsLayoutUtils::GetClosestFrameOfType(this, LayoutFrameType::Scrollbar);
5766 if (!scrollbar && !ShouldHandleSelectionMovementEvents()) {
5767 // XXX Do we really need to exclude non-selectable content here?
5768 // GetContentOffsetsFromPoint can handle it just fine, although some
5769 // other stuff might not like it.
5770 return NS_OK;
5771 }
5772
5773 frameselection->StopAutoScrollTimer();
5774
5775 // Check if we are dragging in a table cell
5776 nsCOMPtr<nsIContent> parentContent;
5777 int32_t contentOffset;
5778 TableSelectionMode target;
5779 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
5780 mozilla::PresShell* presShell = aPresContext->PresShell();
5781 nsresult result;
5782 result = GetDataForTableSelection(frameselection, presShell, mouseEvent,
5783 getter_AddRefs(parentContent),
5784 &contentOffset, &target);
5785
5786 AutoWeakFrame weakThis = this;
5787 if (NS_SUCCEEDED(result)((bool)(__builtin_expect(!!(!NS_FAILED_impl(result)), 1))) && parentContent) {
5788 result = frameselection->HandleTableSelection(parentContent, contentOffset,
5789 target, mouseEvent);
5790 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"
, 5790)
) {
5791 return result;
5792 }
5793 } else {
5794 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent,
5795 RelativeTo{this});
5796 frameselection->HandleDrag(this, pt);
5797 }
5798
5799 // The frameselection object notifies selection listeners synchronously above
5800 // which might have killed us.
5801 if (!weakThis.IsAlive()) {
5802 return NS_OK;
5803 }
5804
5805 // Get the nearest scroll container frame.
5806 ScrollContainerFrame* scrollContainerFrame =
5807 nsLayoutUtils::GetNearestScrollContainerFrame(
5808 this, nsLayoutUtils::SCROLLABLE_SAME_DOC |
5809 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
5810
5811 if (scrollContainerFrame) {
5812 nsIFrame* capturingFrame = scrollContainerFrame->GetScrolledFrame();
5813 if (capturingFrame) {
5814 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(
5815 mouseEvent, RelativeTo{capturingFrame});
5816 frameselection->StartAutoScrollTimer(capturingFrame, pt, 30);
5817 }
5818 }
5819
5820 return NS_OK;
5821}
5822
5823/**
5824 * This static method handles part of the nsIFrame::HandleRelease in a way
5825 * which doesn't rely on the nsIFrame object to stay alive.
5826 */
5827MOZ_CAN_RUN_SCRIPT_BOUNDARY static nsresult HandleFrameSelection(
5828 nsFrameSelection* aFrameSelection, nsIFrame::ContentOffsets& aOffsets,
5829 bool aHandleTableSel, int32_t aContentOffsetForTableSel,
5830 TableSelectionMode aTargetForTableSel,
5831 nsIContent* aParentContentForTableSel, WidgetGUIEvent* aEvent,
5832 const nsEventStatus* aEventStatus) {
5833 if (!aFrameSelection) {
5834 return NS_OK;
5835 }
5836
5837 nsresult rv = NS_OK;
5838
5839 if (nsEventStatus_eConsumeNoDefault != *aEventStatus) {
5840 if (!aHandleTableSel) {
5841 if (!aOffsets.content || !aFrameSelection->HasDelayedCaretData()) {
5842 return NS_ERROR_FAILURE;
5843 }
5844
5845 // We are doing this to simulate what we would have done on HandlePress.
5846 // We didn't do it there to give the user an opportunity to drag
5847 // the text, but since they didn't drag, we want to place the
5848 // caret.
5849 // However, we'll use the mouse position from the release, since:
5850 // * it's easier
5851 // * that's the normal click position to use (although really, in
5852 // the normal case, small movements that don't count as a drag
5853 // can do selection)
5854 aFrameSelection->SetDragState(true);
5855
5856 const nsFrameSelection::FocusMode focusMode =
5857 aFrameSelection->IsShiftDownInDelayedCaretData()
5858 ? nsFrameSelection::FocusMode::kExtendSelection
5859 : nsFrameSelection::FocusMode::kCollapseToNewPoint;
5860 rv = aFrameSelection->HandleClick(
5861 MOZ_KnownLive(aOffsets.content)(aOffsets.content) /* bug 1636889 */,
5862 aOffsets.StartOffset(), aOffsets.EndOffset(), focusMode,
5863 aOffsets.associate);
5864 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
5865 return rv;
5866 }
5867 } else if (aParentContentForTableSel) {
5868 aFrameSelection->SetDragState(false);
5869 rv = aFrameSelection->HandleTableSelection(
5870 aParentContentForTableSel, aContentOffsetForTableSel,
5871 aTargetForTableSel, aEvent->AsMouseEvent());
5872 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
5873 return rv;
5874 }
5875 }
5876 aFrameSelection->SetDelayedCaretData(nullptr);
5877 }
5878
5879 aFrameSelection->SetDragState(false);
5880 aFrameSelection->StopAutoScrollTimer();
5881
5882 return NS_OK;
5883}
5884
5885NS_IMETHODIMPnsresult nsIFrame::HandleRelease(nsPresContext* aPresContext,
5886 WidgetGUIEvent* aEvent,
5887 nsEventStatus* aEventStatus) {
5888 if (aEvent->mClass != eMouseEventClass) {
5889 return NS_OK;
5890 }
5891
5892 nsIFrame* activeFrame = GetActiveSelectionFrame(aPresContext, this);
5893
5894 nsCOMPtr<nsIContent> captureContent = PresShell::GetCapturingContent();
5895
5896 const bool selectionOff = !ShouldHandleSelectionMovementEvents();
5897
5898 RefPtr<nsFrameSelection> frameselection;
5899 ContentOffsets offsets;
5900 nsCOMPtr<nsIContent> parentContent;
5901 int32_t contentOffsetForTableSel = 0;
5902 TableSelectionMode targetForTableSel = TableSelectionMode::None;
5903 bool handleTableSelection = true;
5904
5905 if (!selectionOff) {
5906 frameselection = GetFrameSelection();
5907 if (nsEventStatus_eConsumeNoDefault != *aEventStatus && frameselection) {
5908 // Check if the frameselection recorded the mouse going down.
5909 // If not, the user must have clicked in a part of the selection.
5910 // Place the caret before continuing!
5911
5912 if (frameselection->MouseDownRecorded()) {
5913 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(
5914 aEvent, RelativeTo{this});
5915 offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN);
5916 handleTableSelection = false;
5917 } else {
5918 GetDataForTableSelection(frameselection, PresShell(),
5919 aEvent->AsMouseEvent(),
5920 getter_AddRefs(parentContent),
5921 &contentOffsetForTableSel, &targetForTableSel);
5922 }
5923 }
5924 }
5925
5926 // We might be capturing in some other document and the event just happened to
5927 // trickle down here. Make sure that document's frame selection is notified.
5928 // Note, this may cause the current nsIFrame object to be deleted, bug 336592.
5929 RefPtr<nsFrameSelection> frameSelection;
5930 if (activeFrame != this &&
5931 activeFrame->ShouldHandleSelectionMovementEvents()) {
5932 frameSelection = activeFrame->GetFrameSelection();
5933 }
5934
5935 // Also check the selection of the capturing content which might be in a
5936 // different document.
5937 if (!frameSelection && captureContent) {
5938 if (Document* doc = captureContent->GetComposedDoc()) {
5939 mozilla::PresShell* capturingPresShell = doc->GetPresShell();
5940 if (capturingPresShell &&
5941 capturingPresShell != PresContext()->GetPresShell()) {
5942 frameSelection = capturingPresShell->FrameSelection();
5943 }
5944 }
5945 }
5946
5947 if (frameSelection) {
5948 AutoWeakFrame wf(this);
5949 frameSelection->SetDragState(false);
5950 frameSelection->StopAutoScrollTimer();
5951 if (wf.IsAlive()) {
5952 ScrollContainerFrame* scrollContainerFrame =
5953 nsLayoutUtils::GetNearestScrollContainerFrame(
5954 this, nsLayoutUtils::SCROLLABLE_SAME_DOC |
5955 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
5956 if (scrollContainerFrame) {
5957 // Perform any additional scrolling needed to maintain CSS snap point
5958 // requirements when autoscrolling is over.
5959 scrollContainerFrame->ScrollSnap();
5960 }
5961 }
5962 }
5963
5964 // Do not call any methods of the current object after this point!!!
5965 // The object is perhaps dead!
5966
5967 return selectionOff ? NS_OK
5968 : HandleFrameSelection(
5969 frameselection, offsets, handleTableSelection,
5970 contentOffsetForTableSel, targetForTableSel,
5971 parentContent, aEvent, aEventStatus);
5972}
5973
5974struct MOZ_STACK_CLASS FrameContentRange {
5975 FrameContentRange(nsIContent* aContent, int32_t aStart, int32_t aEnd)
5976 : content(aContent), start(aStart), end(aEnd) {}
5977 nsCOMPtr<nsIContent> content;
5978 int32_t start;
5979 int32_t end;
5980};
5981
5982static bool IsRelevantBlockFrame(const nsIFrame* aFrame) {
5983 if (!aFrame->IsBlockOutside() && !aFrame->IsTableCaption()) {
5984 return false;
5985 }
5986 if (aFrame->GetContent() &&
5987 aFrame->GetContent()->IsInNativeAnonymousSubtree() &&
5988 !aFrame->GetContent()->HasBeenInUAWidget()) {
5989 // This helps skipping things like scrollbar parts.
5990 return false;
5991 }
5992 auto pseudoType = aFrame->Style()->GetPseudoType();
5993 if (PseudoStyle::IsAnonBox(pseudoType)) {
5994 // Table cell contents should be considered block boundaries for this
5995 // purpose.
5996 return pseudoType == PseudoStyleType::MozCellContent;
5997 }
5998 return true;
5999}
6000
6001// Retrieve the content offsets of a frame
6002static FrameContentRange GetRangeForFrame(const nsIFrame* aFrame,
6003 bool includeReplaced = false) {
6004 nsIContent* content = aFrame->GetContent();
6005 if (!content) {
6006 NS_WARNING("Frame has no content")NS_DebugBreak(NS_DEBUG_WARNING, "Frame has no content", nullptr
, "./../../../layout/generic/nsIFrame.cpp", 6006)
;
6007 return FrameContentRange(nullptr, -1, -1);
6008 }
6009
6010 LayoutFrameType type = aFrame->Type();
6011 if (type == LayoutFrameType::Text) {
6012 auto [offset, offsetEnd] = aFrame->GetOffsets();
6013 return FrameContentRange(content, offset, offsetEnd);
6014 }
6015
6016 if (type == LayoutFrameType::Br) {
6017 nsIContent* parent = content->GetParent();
6018 const int32_t beginOffset = parent->ComputeIndexOf_Deprecated(content);
6019 return FrameContentRange(parent, beginOffset, beginOffset);
6020 }
6021
6022 while (content->IsRootOfNativeAnonymousSubtree()) {
6023 content = content->GetParent();
6024 }
6025
6026 if (!includeReplaced && aFrame->IsReplaced()) {
6027 if (auto* parent = content->GetParent()) {
6028 // TODO(emilio): Revise this in presence of Shadow DOM / display:
6029 // contents, it's likely that we don't want to just walk the light tree,
6030 // and we need to change the representation of FrameContentRange.
6031 Maybe<uint32_t> index = parent->ComputeIndexOf(content);
6032 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", 6032); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "index.isSome()" ")"); do { MOZ_CrashSequence
(__null, 6032); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
6033 return FrameContentRange(parent, static_cast<int32_t>(*index),
6034 static_cast<int32_t>(*index + 1));
6035 }
6036 }
6037
6038 return FrameContentRange(content, 0, content->GetChildCount());
6039}
6040
6041// The FrameTarget represents the closest frame to a point that can be selected
6042// The frame is the frame represented, frameEdge says whether one end of the
6043// frame is the result (in which case different handling is needed), and
6044// afterFrame says which end is represented if frameEdge is true
6045struct FrameTarget {
6046 explicit operator bool() const { return !!frame; }
6047
6048 nsIFrame* frame = nullptr;
6049 bool frameEdge = false;
6050 bool afterFrame = false;
6051};
6052
6053// See function implementation for information
6054static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame,
6055 const nsPoint& aPoint,
6056 uint32_t aFlags);
6057
6058static bool SelfIsSelectable(nsIFrame* aFrame, nsIFrame* aParentFrame,
6059 uint32_t aFlags) {
6060 // We should not move selection into a native anonymous subtree when handling
6061 // selection outside it.
6062 if ((aFlags & nsIFrame::IGNORE_NATIVE_ANONYMOUS_SUBTREE) &&
6063 aParentFrame->GetClosestNativeAnonymousSubtreeRoot() !=
6064 aFrame->GetClosestNativeAnonymousSubtreeRoot()) {
6065 return false;
6066 }
6067 if ((aFlags & nsIFrame::SKIP_HIDDEN) &&
6068 (!aFrame->StyleVisibility()->IsVisible() ||
6069 aFrame->IsHiddenByContentVisibilityOnAnyAncestor(
6070 nsIFrame::IncludeContentVisibility::Hidden))) {
6071 return false;
6072 }
6073 if (aFrame->IsGeneratedContentFrame()) {
6074 return false;
6075 }
6076 // XXX Why do we ignore `aFlag & nsIFrame::IGNORE_SELECTION_STYLE`?
6077 if (aFrame->Style()->UserSelect() == StyleUserSelect::None) {
6078 return false;
6079 }
6080 if (aFrame->IsEmpty() &&
6081 (!aFrame->IsTextFrame() || !aFrame->ContentIsEditable())) {
6082 // FIXME(emilio): Historically we haven't treated empty frames as
6083 // selectable, but also we had special-cases so that editable empty text
6084 // frames returned false from IsEmpty(). Sort this out (probably by
6085 // removing the IsEmpty() condition altogether).
6086 return false;
6087 }
6088 return true;
6089}
6090
6091static bool FrameContentCanHaveParentSelectionRange(nsIFrame* aFrame) {
6092 // If we are only near (not directly over) then don't traverse frames with
6093 // independent selection (e.g. text controls, see bug 268497). Note that this
6094 // prevents any of the users of this method from entering form controls.
6095 // XXX We might want some way to allow using the up-arrow to go into a form
6096 // control, but the focus didn't work right anyway; it'd probably be enough
6097 // if the left and right arrows could enter textboxes (which I don't believe
6098 // they can at the moment)
6099 if (aFrame->IsTextInputFrame()) {
6100 return false;
6101 }
6102 return !aFrame->IsGeneratedContentFrame();
6103}
6104
6105static bool SelectionDescendToKids(nsIFrame* aFrame) {
6106 if (!FrameContentCanHaveParentSelectionRange(aFrame)) {
6107 return false;
6108 }
6109 auto style = aFrame->Style()->UserSelect();
6110 return style != StyleUserSelect::All && style != StyleUserSelect::None;
6111}
6112
6113static FrameTarget GetSelectionClosestFrameForChild(nsIFrame* aChild,
6114 const nsPoint& aPoint,
6115 uint32_t aFlags) {
6116 nsIFrame* parent = aChild->GetParent();
6117 if (SelectionDescendToKids(aChild)) {
6118 nsPoint pt = aPoint - aChild->GetOffsetTo(parent);
6119 return GetSelectionClosestFrame(aChild, pt, aFlags);
6120 }
6121 return FrameTarget{aChild, false, false};
6122}
6123
6124// When the cursor needs to be at the beginning of a block, it shouldn't be
6125// before the first child. A click on a block whose first child is a block
6126// should put the cursor in the child. The cursor shouldn't be between the
6127// blocks, because that's not where it's expected.
6128// Note that this method is guaranteed to succeed.
6129static FrameTarget DrillDownToSelectionFrame(nsIFrame* aFrame, bool aEndFrame,
6130 uint32_t aFlags) {
6131 if (SelectionDescendToKids(aFrame)) {
6132 nsIFrame* result = nullptr;
6133 nsIFrame* frame = aFrame->PrincipalChildList().FirstChild();
6134 if (!aEndFrame) {
6135 while (frame && !SelfIsSelectable(frame, aFrame, aFlags)) {
6136 frame = frame->GetNextSibling();
6137 }
6138 if (frame) {
6139 result = frame;
6140 }
6141 } else {
6142 // Because the frame tree is singly linked, to find the last frame,
6143 // we have to iterate through all the frames
6144 // XXX I have a feeling this could be slow for long blocks, although
6145 // I can't find any slowdowns
6146 while (frame) {
6147 if (SelfIsSelectable(frame, aFrame, aFlags)) {
6148 result = frame;
6149 }
6150 frame = frame->GetNextSibling();
6151 }
6152 }
6153 if (result) {
6154 return DrillDownToSelectionFrame(result, aEndFrame, aFlags);
6155 }
6156 }
6157 // If the current frame has no targetable children, target the current frame
6158 return FrameTarget{aFrame, true, aEndFrame};
6159}
6160
6161// This method finds the closest valid FrameTarget on a given line; if there is
6162// no valid FrameTarget on the line, it returns a null FrameTarget
6163static FrameTarget GetSelectionClosestFrameForLine(
6164 nsBlockFrame* aParent, nsBlockFrame::LineIterator aLine,
6165 const nsPoint& aPoint, uint32_t aFlags) {
6166 // Account for end of lines (any iterator from the block is valid)
6167 if (aLine == aParent->LinesEnd()) {
6168 return DrillDownToSelectionFrame(aParent, true, aFlags);
6169 }
6170 nsIFrame* closestFromIStart = nullptr;
6171 nsIFrame* closestFromIEnd = nullptr;
6172 nscoord closestIStart = aLine->IStart(), closestIEnd = aLine->IEnd();
6173 WritingMode wm = aLine->mWritingMode;
6174 LogicalPoint pt(wm, aPoint, aLine->mContainerSize);
6175 bool canSkipBr = false;
6176 bool lastFrameWasEditable = false;
6177 for (nsIFrame* frame : aLine->ChildFrames()) {
6178 // Skip brFrames. Can only skip if the line contains at least
6179 // one selectable and non-empty frame before. Also, avoid skipping brs if
6180 // the previous thing had a different editableness than us, since then we
6181 // may end up not being able to select after it if the br is the last thing
6182 // on the line.
6183 if (!SelfIsSelectable(frame, aParent, aFlags) ||
6184 (canSkipBr && frame->IsBrFrame() &&
6185 lastFrameWasEditable == frame->GetContent()->IsEditable())) {
6186 continue;
6187 }
6188 canSkipBr = true;
6189 lastFrameWasEditable =
6190 frame->GetContent() && frame->GetContent()->IsEditable();
6191 LogicalRect frameRect =
6192 LogicalRect(wm, frame->GetRect(), aLine->mContainerSize);
6193 if (pt.I(wm) >= frameRect.IStart(wm)) {
6194 if (pt.I(wm) < frameRect.IEnd(wm)) {
6195 return GetSelectionClosestFrameForChild(frame, aPoint, aFlags);
6196 }
6197 if (frameRect.IEnd(wm) >= closestIStart) {
6198 closestFromIStart = frame;
6199 closestIStart = frameRect.IEnd(wm);
6200 }
6201 } else {
6202 if (frameRect.IStart(wm) <= closestIEnd) {
6203 closestFromIEnd = frame;
6204 closestIEnd = frameRect.IStart(wm);
6205 }
6206 }
6207 }
6208 if (!closestFromIStart && !closestFromIEnd) {
6209 // We should only get here if there are no selectable frames on a line
6210 // XXX Do we need more elaborate handling here?
6211 return FrameTarget();
6212 }
6213 if (closestFromIStart &&
6214 (!closestFromIEnd ||
6215 (abs(pt.I(wm) - closestIStart) <= abs(pt.I(wm) - closestIEnd)))) {
6216 return GetSelectionClosestFrameForChild(closestFromIStart, aPoint, aFlags);
6217 }
6218 return GetSelectionClosestFrameForChild(closestFromIEnd, aPoint, aFlags);
6219}
6220
6221// This method is for the special handling we do for block frames; they're
6222// special because they represent paragraphs and because they are organized
6223// into lines, which have bounds that are not stored elsewhere in the
6224// frame tree. Returns a null FrameTarget for frames which are not
6225// blocks or blocks with no lines except editable one.
6226static FrameTarget GetSelectionClosestFrameForBlock(nsIFrame* aFrame,
6227 const nsPoint& aPoint,
6228 uint32_t aFlags) {
6229 nsBlockFrame* bf = do_QueryFrame(aFrame);
6230 if (!bf) {
6231 return FrameTarget();
6232 }
6233
6234 // This code searches for the correct line
6235 nsBlockFrame::LineIterator end = bf->LinesEnd();
6236 nsBlockFrame::LineIterator curLine = bf->LinesBegin();
6237 nsBlockFrame::LineIterator closestLine = end;
6238
6239 if (curLine != end) {
6240 // Convert aPoint into a LogicalPoint in the writing-mode of this block
6241 WritingMode wm = curLine->mWritingMode;
6242 LogicalPoint pt(wm, aPoint, curLine->mContainerSize);
6243 do {
6244 // Check to see if our point lies within the line's block-direction bounds
6245 nscoord BCoord = pt.B(wm) - curLine->BStart();
6246 nscoord BSize = curLine->BSize();
6247 if (BCoord >= 0 && BCoord < BSize) {
6248 closestLine = curLine;
6249 break; // We found the line; stop looking
6250 }
6251 if (BCoord < 0) {
6252 break;
6253 }
6254 ++curLine;
6255 } while (curLine != end);
6256
6257 if (closestLine == end) {
6258 nsBlockFrame::LineIterator prevLine = curLine.prev();
6259 nsBlockFrame::LineIterator nextLine = curLine;
6260 // Avoid empty lines
6261 while (nextLine != end && nextLine->IsEmpty()) {
6262 ++nextLine;
6263 }
6264 while (prevLine != end && prevLine->IsEmpty()) {
6265 --prevLine;
6266 }
6267
6268 // This hidden pref dictates whether a point above or below all lines
6269 // comes up with a line or the beginning or end of the frame; 0 on
6270 // Windows, 1 on other platforms by default at the writing of this code
6271 int32_t dragOutOfFrame =
6272 Preferences::GetInt("browser.drag_out_of_frame_style");
6273
6274 if (prevLine == end) {
6275 if (dragOutOfFrame == 1 || nextLine == end) {
6276 return DrillDownToSelectionFrame(aFrame, false, aFlags);
6277 }
6278 closestLine = nextLine;
6279 } else if (nextLine == end) {
6280 if (dragOutOfFrame == 1) {
6281 return DrillDownToSelectionFrame(aFrame, true, aFlags);
6282 }
6283 closestLine = prevLine;
6284 } else { // Figure out which line is closer
6285 if (pt.B(wm) - prevLine->BEnd() < nextLine->BStart() - pt.B(wm)) {
6286 closestLine = prevLine;
6287 } else {
6288 closestLine = nextLine;
6289 }
6290 }
6291 }
6292 }
6293
6294 do {
6295 if (auto target =
6296 GetSelectionClosestFrameForLine(bf, closestLine, aPoint, aFlags)) {
6297 return target;
6298 }
6299 ++closestLine;
6300 } while (closestLine != end);
6301
6302 // Fall back to just targeting the last targetable place
6303 return DrillDownToSelectionFrame(aFrame, true, aFlags);
6304}
6305
6306// Use frame edge for grid, flex, table, and non-editable images. Choose the
6307// edge based on the point position past the frame rect. If past the middle,
6308// caret should be at end, otherwise at start. This behavior matches Blink.
6309//
6310// TODO(emilio): Can we use this code path for all other frames? We only get
6311// there when we didn't find selectable children... Editable images need _not_
6312// to use the frame edge tho, see below.
6313static bool UseFrameEdge(nsIFrame* aFrame) {
6314 if (aFrame->IsFlexOrGridContainer() || aFrame->IsTableFrame()) {
6315 return true;
6316 }
6317 // FIXME(bug 713387): The text frame check here shouldn't be needed.
6318 if (aFrame->IsReplaced() && !aFrame->IsTextFrame() &&
6319 !aFrame->GetContent()->IsEditable()) {
6320 // Editable replaced elements are a special-case because editing relies
6321 // on clicking on an editable image selecting it, for it to show resizers.
6322 return true;
6323 }
6324 return false;
6325}
6326
6327static FrameTarget LastResortFrameTargetForFrame(nsIFrame* aFrame,
6328 const nsPoint& aPoint) {
6329 if (!UseFrameEdge(aFrame)) {
6330 return {aFrame, false, false};
6331 }
6332 const auto& rect = aFrame->GetRectRelativeToSelf();
6333 nscoord reference;
6334 nscoord middle;
6335 if (aFrame->GetWritingMode().IsVertical()) {
6336 reference = aPoint.y;
6337 middle = rect.Height() / 2;
6338 } else {
6339 reference = aPoint.x;
6340 middle = rect.Width() / 2;
6341 }
6342 const bool afterFrame = reference > middle;
6343 return {aFrame, true, afterFrame};
6344}
6345
6346// GetSelectionClosestFrame is the helper function that calculates the closest
6347// frame to the given point.
6348// It doesn't completely account for offset styles, so needs to be used in
6349// restricted environments.
6350// Cannot handle overlapping frames correctly, so it should receive the output
6351// of GetFrameForPoint
6352// Guaranteed to return a valid FrameTarget.
6353// aPoint is relative to aFrame.
6354static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame,
6355 const nsPoint& aPoint,
6356 uint32_t aFlags) {
6357 // Handle blocks; if the frame isn't a block, the method fails
6358 if (auto target = GetSelectionClosestFrameForBlock(aFrame, aPoint, aFlags)) {
6359 return target;
6360 }
6361
6362 if (aFlags & nsIFrame::IGNORE_NATIVE_ANONYMOUS_SUBTREE &&
6363 !FrameContentCanHaveParentSelectionRange(aFrame)) {
6364 return LastResortFrameTargetForFrame(aFrame, aPoint);
6365 }
6366
6367 if (nsIFrame* kid = aFrame->PrincipalChildList().FirstChild()) {
6368 // Go through all the child frames to find the closest one
6369 nsIFrame::FrameWithDistance closest = {nullptr, nscoord_MAX, nscoord_MAX};
6370 for (; kid; kid = kid->GetNextSibling()) {
6371 if (!SelfIsSelectable(kid, aFrame, aFlags)) {
6372 continue;
6373 }
6374
6375 kid->FindCloserFrameForSelection(aPoint, &closest);
6376 }
6377 if (closest.mFrame) {
6378 if (closest.mFrame->IsInSVGTextSubtree()) {
6379 return FrameTarget{closest.mFrame, false, false};
6380 }
6381 return GetSelectionClosestFrameForChild(closest.mFrame, aPoint, aFlags);
6382 }
6383 }
6384
6385 return LastResortFrameTargetForFrame(aFrame, aPoint);
6386}
6387
6388static nsIFrame::ContentOffsets OffsetsForSingleFrame(nsIFrame* aFrame,
6389 const nsPoint& aPoint) {
6390 nsIFrame::ContentOffsets offsets;
6391 FrameContentRange range = GetRangeForFrame(aFrame);
6392 offsets.content = range.content;
6393 // If there are continuations (meaning it's not one rectangle), this is the
6394 // best this function can do
6395 if (aFrame->GetNextContinuation() || aFrame->GetPrevContinuation()) {
6396 offsets.offset = range.start;
6397 offsets.secondaryOffset = range.end;
6398 offsets.associate = CaretAssociationHint::After;
6399 return offsets;
6400 }
6401
6402 // Figure out whether the offsets should be over, after, or before the frame
6403 nsRect rect(nsPoint(0, 0), aFrame->GetSize());
6404
6405 bool isBlock = !aFrame->StyleDisplay()->IsInlineFlow();
6406 bool isRtl = (aFrame->StyleVisibility()->mDirection == StyleDirection::Rtl);
6407 if ((isBlock && rect.y < aPoint.y) ||
6408 (!isBlock && ((isRtl && rect.x + rect.width / 2 > aPoint.x) ||
6409 (!isRtl && rect.x + rect.width / 2 < aPoint.x)))) {
6410 offsets.offset = range.end;
6411 if (rect.Contains(aPoint)) {
6412 offsets.secondaryOffset = range.start;
6413 } else {
6414 offsets.secondaryOffset = range.end;
6415 }
6416 } else {
6417 offsets.offset = range.start;
6418 if (rect.Contains(aPoint)) {
6419 offsets.secondaryOffset = range.end;
6420 } else {
6421 offsets.secondaryOffset = range.start;
6422 }
6423 }
6424 offsets.associate = offsets.offset == range.start
6425 ? CaretAssociationHint::After
6426 : CaretAssociationHint::Before;
6427 return offsets;
6428}
6429
6430static nsIFrame* AdjustFrameForSelectionStyles(nsIFrame* aFrame) {
6431 nsIFrame* adjustedFrame = aFrame;
6432 for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
6433 // These are the conditions that make all children not able to handle
6434 // a cursor.
6435 auto userSelect = frame->Style()->UserSelect();
6436 if (userSelect != StyleUserSelect::Auto &&
6437 userSelect != StyleUserSelect::All) {
6438 break;
6439 }
6440 if (userSelect == StyleUserSelect::All ||
6441 frame->IsGeneratedContentFrame()) {
6442 adjustedFrame = frame;
6443 }
6444 }
6445 return adjustedFrame;
6446}
6447
6448nsIFrame::ContentOffsets nsIFrame::GetContentOffsetsFromPoint(
6449 const nsPoint& aPoint, uint32_t aFlags) {
6450 nsIFrame* adjustedFrame;
6451 if (aFlags & IGNORE_SELECTION_STYLE) {
6452 adjustedFrame = this;
6453 } else {
6454 // This section of code deals with special selection styles. Note that
6455 // -moz-all exists, even though it doesn't need to be explicitly handled.
6456 //
6457 // The offset is forced not to end up in generated content; content offsets
6458 // cannot represent content outside of the document's content tree.
6459
6460 adjustedFrame = AdjustFrameForSelectionStyles(this);
6461
6462 // `user-select: all` needs special handling, because clicking on it should
6463 // lead to the whole frame being selected.
6464 if (adjustedFrame->Style()->UserSelect() == StyleUserSelect::All) {
6465 nsPoint adjustedPoint = aPoint + GetOffsetTo(adjustedFrame);
6466 return OffsetsForSingleFrame(adjustedFrame, adjustedPoint);
6467 }
6468
6469 // For other cases, try to find a closest frame starting from the parent of
6470 // the unselectable frame
6471 if (adjustedFrame != this) {
6472 adjustedFrame = adjustedFrame->GetParent();
6473 }
6474 }
6475
6476 nsPoint adjustedPoint = aPoint + GetOffsetTo(adjustedFrame);
6477
6478 FrameTarget closest =
6479 GetSelectionClosestFrame(adjustedFrame, adjustedPoint, aFlags);
6480
6481 // If the correct offset is at one end of a frame, use offset-based
6482 // calculation method
6483 if (closest.frameEdge) {
6484 ContentOffsets offsets;
6485 bool includeReplaced = (aFlags & INCLUDE_REPLACED) != 0;
6486 FrameContentRange range = GetRangeForFrame(closest.frame, includeReplaced);
6487 offsets.content = range.content;
6488 if (closest.afterFrame) {
6489 offsets.offset = range.end;
6490 } else {
6491 offsets.offset = range.start;
6492 }
6493 offsets.secondaryOffset = offsets.offset;
6494 offsets.associate = offsets.offset == range.start
6495 ? CaretAssociationHint::After
6496 : CaretAssociationHint::Before;
6497 return offsets;
6498 }
6499
6500 nsPoint pt;
6501 if (closest.frame != this) {
6502 if (closest.frame->IsInSVGTextSubtree()) {
6503 pt = nsLayoutUtils::TransformAncestorPointToFrame(
6504 RelativeTo{closest.frame}, aPoint, RelativeTo{this});
6505 } else {
6506 pt = aPoint - closest.frame->GetOffsetTo(this);
6507 }
6508 } else {
6509 pt = aPoint;
6510 }
6511 return closest.frame->CalcContentOffsetsFromFramePoint(pt);
6512
6513 // XXX should I add some kind of offset standardization?
6514 // consider <b>xxxxx</b><i>zzzzz</i>; should any click between the last
6515 // x and first z put the cursor in the same logical position in addition
6516 // to the same visual position?
6517}
6518
6519nsIFrame::ContentOffsets nsIFrame::CalcContentOffsetsFromFramePoint(
6520 const nsPoint& aPoint) {
6521 return OffsetsForSingleFrame(this, aPoint);
6522}
6523
6524bool nsIFrame::AssociateImage(const StyleImage& aImage) {
6525 imgRequestProxy* req = aImage.GetImageRequest();
6526 if (!req) {
6527 return false;
6528 }
6529
6530 PresContext()->Document()->EnsureStyleImageLoader().AssociateRequestToFrame(
6531 req, this);
6532 return true;
6533}
6534
6535void nsIFrame::DisassociateImage(const StyleImage& aImage) {
6536 imgRequestProxy* req = aImage.GetImageRequest();
6537 if (!req) {
6538 return;
6539 }
6540
6541 PresContext()
6542 ->Document()
6543 ->EnsureStyleImageLoader()
6544 .DisassociateRequestFromFrame(req, this);
6545}
6546
6547const ComputedStyle* nsIFrame::UsedStyleForImages() const {
6548 if (IsCanvasFrame()) {
6549 // XXXdholbert Maybe we should use FindCanvasBackground here (instead of
6550 // FindBackground), since we're inside an IsCanvasFrame check? Though then
6551 // we'd also have to copypaste or abstract-away the multi-part root-frame
6552 // lookup that the canvas-flavored API requires.
6553 return nsCSSRendering::FindBackground(this);
6554 }
6555 return Style();
6556}
6557
6558// The touch-action CSS property applies to: all elements except: non-replaced
6559// inline elements, table rows, row groups, table columns, and column groups.
6560StyleTouchAction nsIFrame::UsedTouchAction() const {
6561 if (IsLineParticipant()) {
6562 return StyleTouchAction::AUTO;
6563 }
6564 auto& disp = *StyleDisplay();
6565 if (disp.IsInternalTableStyleExceptCell()) {
6566 return StyleTouchAction::AUTO;
6567 }
6568 return disp.mTouchAction;
6569}
6570
6571nsIFrame::Cursor nsIFrame::GetCursor(const nsPoint&) {
6572 StyleCursorKind kind = StyleUI()->Cursor().keyword;
6573 if (kind == StyleCursorKind::Auto) {
6574 // If this is editable, I-beam cursor is better for most elements.
6575 kind = (mContent && mContent->IsEditable()) ? StyleCursorKind::Text
6576 : StyleCursorKind::Default;
6577 }
6578 if (kind == StyleCursorKind::Text && GetWritingMode().IsVertical()) {
6579 // Per CSS UI spec, UA may treat value 'text' as
6580 // 'vertical-text' for vertical text.
6581 kind = StyleCursorKind::VerticalText;
6582 }
6583
6584 return Cursor{kind, AllowCustomCursorImage::Yes};
6585}
6586
6587// Resize and incremental reflow
6588
6589/* virtual */
6590void nsIFrame::MarkIntrinsicISizesDirty() {
6591 // If we're a flex item, clear our flex-item-specific cached measurements
6592 // (which likely depended on our now-stale intrinsic isize).
6593 if (IsFlexItem()) {
6594 nsFlexContainerFrame::MarkCachedFlexMeasurementsDirty(this);
6595 }
6596
6597 if (IsGridItem()) {
6598 nsGridContainerFrame::MarkCachedGridMeasurementsDirty(this);
6599 }
6600
6601 if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT)) {
6602 nsFontInflationData::MarkFontInflationDataTextDirty(this);
6603 }
6604}
6605
6606void nsIFrame::MarkSubtreeDirty() {
6607 if (HasAnyStateBits(NS_FRAME_IS_DIRTY)) {
6608 return;
6609 }
6610 // Unconditionally mark given frame dirty.
6611 AddStateBits(NS_FRAME_IS_DIRTY);
6612
6613 // Mark all descendants dirty, unless:
6614 // - Already dirty.
6615 // - TableColGroup
6616 AutoTArray<nsIFrame*, 32> stack;
6617 for (const auto& childLists : ChildLists()) {
6618 for (nsIFrame* kid : childLists.mList) {
6619 stack.AppendElement(kid);
6620 }
6621 }
6622 while (!stack.IsEmpty()) {
6623 nsIFrame* f = stack.PopLastElement();
6624 if (f->HasAnyStateBits(NS_FRAME_IS_DIRTY) || f->IsTableColGroupFrame()) {
6625 continue;
6626 }
6627
6628 f->AddStateBits(NS_FRAME_IS_DIRTY);
6629
6630 for (const auto& childLists : f->ChildLists()) {
6631 for (nsIFrame* kid : childLists.mList) {
6632 stack.AppendElement(kid);
6633 }
6634 }
6635 }
6636}
6637
6638void nsIFrame::MarkPrincipalChildrenDirty() {
6639 for (nsIFrame* childFrame : PrincipalChildList()) {
6640 childFrame->MarkSubtreeDirty();
6641 }
6642}
6643
6644/* virtual */
6645void nsIFrame::AddInlineMinISize(const IntrinsicSizeInput& aInput,
6646 InlineMinISizeData* aData) {
6647 // Note: we are one of the children that mPercentageBasisForChildren was
6648 // prepared for (i.e. our parent frame prepares the percentage basis for us,
6649 // not for our own children). Hence it's fine that we're resolving our
6650 // percentages sizes against this basis in IntrinsicForContainer().
6651 nscoord isize = nsLayoutUtils::IntrinsicForContainer(
6652 aInput.mContext, this, IntrinsicISizeType::MinISize,
6653 aInput.mPercentageBasisForChildren);
6654 aData->DefaultAddInlineMinISize(this, isize);
6655}
6656
6657/* virtual */
6658void nsIFrame::AddInlinePrefISize(const IntrinsicSizeInput& aInput,
6659 nsIFrame::InlinePrefISizeData* aData) {
6660 // Note: we are one of the children that mPercentageBasisForChildren was
6661 // prepared for (i.e. our parent frame prepares the percentage basis for us,
6662 // not for our own children). Hence it's fine that we're resolving our
6663 // percentages sizes against this basis in IntrinsicForContainer().
6664 nscoord isize = nsLayoutUtils::IntrinsicForContainer(
6665 aInput.mContext, this, IntrinsicISizeType::PrefISize,
6666 aInput.mPercentageBasisForChildren);
6667 aData->DefaultAddInlinePrefISize(isize);
6668}
6669
6670void nsIFrame::InlineMinISizeData::DefaultAddInlineMinISize(nsIFrame* aFrame,
6671 nscoord aISize,
6672 bool aAllowBreak) {
6673 auto parent = aFrame->GetParent();
6674 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", 6674); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "parent" ") (" "Must have a parent if we get here!"
")"); do { MOZ_CrashSequence(__null, 6674); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6675 const bool mayBreak = aAllowBreak && !aFrame->CanContinueTextRun() &&
6676 !parent->Style()->ShouldSuppressLineBreak() &&
6677 parent->StyleText()->WhiteSpaceCanWrap(parent);
6678 if (mayBreak) {
6679 OptionallyBreak();
6680 }
6681 mTrailingWhitespace = 0;
6682 mSkipWhitespace = false;
6683 mCurrentLine += aISize;
6684 mAtStartOfLine = false;
6685 if (mayBreak) {
6686 OptionallyBreak();
6687 }
6688}
6689
6690void nsIFrame::InlinePrefISizeData::DefaultAddInlinePrefISize(nscoord aISize) {
6691 mCurrentLine = NSCoordSaturatingAdd(mCurrentLine, aISize);
6692 mTrailingWhitespace = 0;
6693 mSkipWhitespace = false;
6694 mLineIsEmpty = false;
6695}
6696
6697void nsIFrame::InlineMinISizeData::ForceBreak() {
6698 mCurrentLine -= mTrailingWhitespace;
6699 mPrevLines = std::max(mPrevLines, mCurrentLine);
6700 mCurrentLine = mTrailingWhitespace = 0;
6701
6702 for (const FloatInfo& floatInfo : mFloats) {
6703 mPrevLines = std::max(floatInfo.ISize(), mPrevLines);
6704 }
6705 mFloats.Clear();
6706 mSkipWhitespace = true;
6707}
6708
6709void nsIFrame::InlineMinISizeData::OptionallyBreak(nscoord aHyphenWidth) {
6710 // If we can fit more content into a smaller width by staying on this
6711 // line (because we're still at a negative offset due to negative
6712 // text-indent or negative margin), don't break. Otherwise, do the
6713 // same as ForceBreak. it doesn't really matter when we accumulate
6714 // floats.
6715 if (mCurrentLine + aHyphenWidth < 0 || mAtStartOfLine) {
6716 return;
6717 }
6718 mCurrentLine += aHyphenWidth;
6719 ForceBreak();
6720}
6721
6722void nsIFrame::InlinePrefISizeData::ForceBreak(UsedClear aClearType) {
6723 // If this force break is not clearing any float, we can leave all the
6724 // floats to the next force break.
6725 if (!mFloats.IsEmpty() && aClearType != UsedClear::None) {
6726 // Preferred isize accumulated for floats that have already
6727 // been cleared past
6728 nscoord floatsDone = 0;
6729 // Preferred isize accumulated for floats that have not yet
6730 // been cleared past
6731 nscoord floatsCurLeft = 0, floatsCurRight = 0;
6732
6733 for (const FloatInfo& floatInfo : mFloats) {
6734 const nsStyleDisplay* floatDisp = floatInfo.Frame()->StyleDisplay();
6735 auto cbWM = floatInfo.Frame()->GetParent()->GetWritingMode();
6736 UsedClear clearType = floatDisp->UsedClear(cbWM);
6737 if (clearType == UsedClear::Left || clearType == UsedClear::Right ||
6738 clearType == UsedClear::Both) {
6739 nscoord floatsCur = NSCoordSaturatingAdd(floatsCurLeft, floatsCurRight);
6740 if (floatsCur > floatsDone) {
6741 floatsDone = floatsCur;
6742 }
6743 if (clearType != UsedClear::Right) {
6744 floatsCurLeft = 0;
6745 }
6746 if (clearType != UsedClear::Left) {
6747 floatsCurRight = 0;
6748 }
6749 }
6750
6751 UsedFloat floatStyle = floatDisp->UsedFloat(cbWM);
6752 nscoord& floatsCur =
6753 floatStyle == UsedFloat::Left ? floatsCurLeft : floatsCurRight;
6754 nscoord floatISize = floatInfo.ISize();
6755 // Negative-width floats don't change the available space so they
6756 // shouldn't change our intrinsic line isize either.
6757 floatsCur = NSCoordSaturatingAdd(floatsCur, std::max(0, floatISize));
6758 }
6759
6760 nscoord floatsCur = NSCoordSaturatingAdd(floatsCurLeft, floatsCurRight);
6761 if (floatsCur > floatsDone) {
6762 floatsDone = floatsCur;
6763 }
6764
6765 mCurrentLine = NSCoordSaturatingAdd(mCurrentLine, floatsDone);
6766
6767 if (aClearType == UsedClear::Both) {
6768 mFloats.Clear();
6769 } else {
6770 // If the break type does not clear all floats, it means there may
6771 // be some floats whose isize should contribute to the intrinsic
6772 // isize of the next line. The code here scans the current mFloats
6773 // and keeps floats which are not cleared by this break. Note that
6774 // floats may be cleared directly or indirectly. See below.
6775 nsTArray<FloatInfo> newFloats;
6776 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", 6778); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aClearType == UsedClear::Left || aClearType == UsedClear::Right"
") (" "Other values should have been handled in other branches"
")"); do { MOZ_CrashSequence(__null, 6778); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
6777 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", 6778); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aClearType == UsedClear::Left || aClearType == UsedClear::Right"
") (" "Other values should have been handled in other branches"
")"); do { MOZ_CrashSequence(__null, 6778); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
6778 "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", 6778); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aClearType == UsedClear::Left || aClearType == UsedClear::Right"
") (" "Other values should have been handled in other branches"
")"); do { MOZ_CrashSequence(__null, 6778); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6779 UsedFloat clearFloatType =
6780 aClearType == UsedClear::Left ? UsedFloat::Left : UsedFloat::Right;
6781 // Iterate the array in reverse so that we can stop when there are
6782 // no longer any floats we need to keep. See below.
6783 for (FloatInfo& floatInfo : Reversed(mFloats)) {
6784 const nsStyleDisplay* floatDisp = floatInfo.Frame()->StyleDisplay();
6785 auto cbWM = floatInfo.Frame()->GetParent()->GetWritingMode();
6786 if (floatDisp->UsedFloat(cbWM) != clearFloatType) {
6787 newFloats.AppendElement(floatInfo);
6788 } else {
6789 // This is a float on the side that this break directly clears
6790 // which means we're not keeping it in mFloats. However, if
6791 // this float clears floats on the opposite side (via a value
6792 // of either 'both' or one of 'left'/'right'), any remaining
6793 // (earlier) floats on that side would be indirectly cleared
6794 // as well. Thus, we should break out of this loop and stop
6795 // considering earlier floats to be kept in mFloats.
6796 UsedClear clearType = floatDisp->UsedClear(cbWM);
6797 if (clearType != aClearType && clearType != UsedClear::None) {
6798 break;
6799 }
6800 }
6801 }
6802 newFloats.Reverse();
6803 mFloats = std::move(newFloats);
6804 }
6805 }
6806
6807 mCurrentLine =
6808 NSCoordSaturatingSubtract(mCurrentLine, mTrailingWhitespace, nscoord_MAX);
6809 mPrevLines = std::max(mPrevLines, mCurrentLine);
6810 mCurrentLine = mTrailingWhitespace = 0;
6811 mSkipWhitespace = true;
6812 mLineIsEmpty = true;
6813}
6814
6815static nscoord ResolvePadding(const LengthPercentage& aStyle,
6816 nscoord aPercentageBasis) {
6817 return nsLayoutUtils::ResolveToLength<true>(aStyle, aPercentageBasis);
6818}
6819
6820static nscoord ResolveMargin(const AnchorResolvedMargin& aStyle,
6821 nscoord aPercentageBasis) {
6822 if (!aStyle->IsLengthPercentage()) {
6823 return nscoord(0);
6824 }
6825 return nsLayoutUtils::ResolveToLength<false>(aStyle->AsLengthPercentage(),
6826 aPercentageBasis);
6827}
6828static nsIFrame::IntrinsicSizeOffsetData IntrinsicSizeOffsets(
6829 nsIFrame* aFrame, nscoord aPercentageBasis, bool aForISize) {
6830 nsIFrame::IntrinsicSizeOffsetData result;
6831 WritingMode wm = aFrame->GetWritingMode();
6832 bool verticalAxis = aForISize == wm.IsVertical();
6833 const auto* styleMargin = aFrame->StyleMargin();
6834 const auto anchorResolutionParams = AnchorPosResolutionParams::From(aFrame);
6835 if (verticalAxis) {
6836 result.margin +=
6837 ResolveMargin(styleMargin->GetMargin(eSideTop, anchorResolutionParams),
6838 aPercentageBasis);
6839 result.margin += ResolveMargin(
6840 styleMargin->GetMargin(eSideBottom, anchorResolutionParams),
6841 aPercentageBasis);
6842 } else {
6843 result.margin +=
6844 ResolveMargin(styleMargin->GetMargin(eSideLeft, anchorResolutionParams),
6845 aPercentageBasis);
6846 result.margin += ResolveMargin(
6847 styleMargin->GetMargin(eSideRight, anchorResolutionParams),
6848 aPercentageBasis);
6849 }
6850
6851 const auto& padding = aFrame->StylePadding()->mPadding;
6852 if (verticalAxis) {
6853 result.padding += ResolvePadding(padding.Get(eSideTop), aPercentageBasis);
6854 result.padding +=
6855 ResolvePadding(padding.Get(eSideBottom), aPercentageBasis);
6856 } else {
6857 result.padding += ResolvePadding(padding.Get(eSideLeft), aPercentageBasis);
6858 result.padding += ResolvePadding(padding.Get(eSideRight), aPercentageBasis);
6859 }
6860
6861 const nsStyleBorder* styleBorder = aFrame->StyleBorder();
6862 if (verticalAxis) {
6863 result.border += styleBorder->GetComputedBorderWidth(eSideTop);
6864 result.border += styleBorder->GetComputedBorderWidth(eSideBottom);
6865 } else {
6866 result.border += styleBorder->GetComputedBorderWidth(eSideLeft);
6867 result.border += styleBorder->GetComputedBorderWidth(eSideRight);
6868 }
6869
6870 const nsStyleDisplay* disp = aFrame->StyleDisplay();
6871 if (aFrame->IsThemed(disp)) {
6872 nsPresContext* presContext = aFrame->PresContext();
6873
6874 LayoutDeviceIntMargin border = presContext->Theme()->GetWidgetBorder(
6875 presContext->DeviceContext(), aFrame, disp->EffectiveAppearance());
6876 result.border = presContext->DevPixelsToAppUnits(
6877 verticalAxis ? border.TopBottom() : border.LeftRight());
6878
6879 LayoutDeviceIntMargin padding;
6880 if (presContext->Theme()->GetWidgetPadding(
6881 presContext->DeviceContext(), aFrame, disp->EffectiveAppearance(),
6882 &padding)) {
6883 result.padding = presContext->DevPixelsToAppUnits(
6884 verticalAxis ? padding.TopBottom() : padding.LeftRight());
6885 }
6886 }
6887 return result;
6888}
6889
6890/* virtual */ nsIFrame::IntrinsicSizeOffsetData nsIFrame::IntrinsicISizeOffsets(
6891 nscoord aPercentageBasis) {
6892 return IntrinsicSizeOffsets(this, aPercentageBasis, true);
6893}
6894
6895nsIFrame::IntrinsicSizeOffsetData nsIFrame::IntrinsicBSizeOffsets(
6896 nscoord aPercentageBasis) {
6897 return IntrinsicSizeOffsets(this, aPercentageBasis, false);
6898}
6899
6900/* virtual */
6901IntrinsicSize nsIFrame::GetIntrinsicSize() {
6902 // Defaults to no intrinsic size.
6903 return IntrinsicSize();
6904}
6905
6906AspectRatio nsIFrame::GetAspectRatio() const {
6907 // Per spec, 'aspect-ratio' property applies to all elements except inline
6908 // boxes and internal ruby or table boxes.
6909 // https://drafts.csswg.org/css-sizing-4/#aspect-ratio
6910 // For those frame types that don't support aspect-ratio, they must not have
6911 // the natural ratio, so this early return is fine.
6912 if (!SupportsAspectRatio()) {
6913 return AspectRatio();
6914 }
6915
6916 const StyleAspectRatio& ar = StylePosition()->mAspectRatio;
6917 const bool hasRatio = ar.HasRatio();
6918 // If aspect-ratio is zero or infinite, it's a degenerate ratio and behaves
6919 // as auto.
6920 // https://drafts.csswg.org/css-sizing-4/#valdef-aspect-ratio-ratio
6921 // Non-auto. Return the preferred aspect ratio from the aspect-ratio style.
6922 if (hasRatio && !ar.auto_) {
6923 if (auto ratio = ar.ratio.AsRatio().ToLayoutRatio(UseBoxSizing::Yes)) {
6924 return ratio;
6925 }
6926 }
6927
6928 if (auto intrinsicRatio = GetIntrinsicRatio()) {
6929 return intrinsicRatio;
6930 }
6931
6932 if (hasRatio) {
6933 // This returns a 0 ratio for degenerate rations, like the auto case below.
6934 return ar.ratio.AsRatio().ToLayoutRatio(UseBoxSizing::No);
6935 }
6936
6937 return AspectRatio();
6938}
6939
6940/* virtual */
6941AspectRatio nsIFrame::GetIntrinsicRatio() const { return AspectRatio(); }
6942
6943static bool ShouldApplyAutomaticMinimumOnInlineAxis(
6944 WritingMode aWM, bool aIsScrollableOverflow,
6945 const AnchorPosResolutionParams& aParams,
6946 const nsStylePosition* aPosition) {
6947 // Apply the automatic minimum size for aspect ratio:
6948 // Note: The replaced elements shouldn't be here, so we only check the scroll
6949 // container.
6950 // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-minimum
6951 return !aIsScrollableOverflow && aPosition->MinISize(aWM, aParams)->IsAuto();
6952}
6953
6954/* virtual */
6955nsIFrame::SizeComputationResult nsIFrame::ComputeSize(
6956 const SizeComputationInput& aSizingInput, WritingMode aWM,
6957 const LogicalSize& aCBSize, nscoord aAvailableISize,
6958 const LogicalSize& aMargin, const LogicalSize& aBorderPadding,
6959 const StyleSizeOverrides& aSizeOverrides, ComputeSizeFlags aFlags) {
6960 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", 6962); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!GetIntrinsicRatio()" ") (" "Please override this method and call "
"nsContainerFrame::ComputeSizeWithIntrinsicDimensions instead."
")"); do { MOZ_CrashSequence(__null, 6962); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
6961 "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", 6962); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!GetIntrinsicRatio()" ") (" "Please override this method and call "
"nsContainerFrame::ComputeSizeWithIntrinsicDimensions instead."
")"); do { MOZ_CrashSequence(__null, 6962); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
6962 "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", 6962); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!GetIntrinsicRatio()" ") (" "Please override this method and call "
"nsContainerFrame::ComputeSizeWithIntrinsicDimensions instead."
")"); do { MOZ_CrashSequence(__null, 6962); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
6963 LogicalSize result =
6964 ComputeAutoSize(aSizingInput, aWM, aCBSize, aAvailableISize, aMargin,
6965 aBorderPadding, aSizeOverrides, aFlags);
6966 const nsStylePosition* stylePos = StylePosition();
6967 const nsStyleDisplay* disp = StyleDisplay();
6968 const auto anchorResolutionParams = AnchorPosResolutionParams::From(
6969 this, aSizingInput.mAnchorPosResolutionCache);
6970 auto aspectRatioUsage = AspectRatioUsage::None;
6971
6972 const auto boxSizingAdjust = stylePos->mBoxSizing == StyleBoxSizing::BorderBox
6973 ? aBorderPadding
6974 : LogicalSize(aWM);
6975 const auto& aspectRatio = aSizeOverrides.mAspectRatio
6976 ? *aSizeOverrides.mAspectRatio
6977 : GetAspectRatio();
6978 const auto styleISize =
6979 aSizeOverrides.mStyleISize
6980 ? AnchorResolvedSizeHelper::Overridden(*aSizeOverrides.mStyleISize)
6981 : stylePos->ISize(aWM, anchorResolutionParams);
6982 // For bsize, we consider overrides *and then* we resolve 'stretch' to a
6983 // nscoord value, for convenience (so that we can assume that either
6984 // isAutoBSize is true, or styleBSize is of type LengthPercentage()).
6985 const auto styleBSize = [&] {
6986 auto styleBSizeConsideringOverrides =
6987 aSizeOverrides.mStyleBSize
6988 ? AnchorResolvedSizeHelper::Overridden(*aSizeOverrides.mStyleBSize)
6989 : stylePos->BSize(aWM, anchorResolutionParams);
6990 if (styleBSizeConsideringOverrides->BehavesLikeStretchOnBlockAxis() &&
6991 aCBSize.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
6992 // We've got a 'stretch' BSize; resolve it to a length:
6993 nscoord stretchBSize = nsLayoutUtils::ComputeStretchSize(
6994 aCBSize.BSize(aWM), aMargin.BSize(aWM), aBorderPadding.BSize(aWM),
6995 stylePos->mBoxSizing);
6996 // Note(dshin): This allocates.
6997 return AnchorResolvedSizeHelper::LengthPercentage(
6998 LengthPercentage::FromAppUnits(stretchBSize));
6999 }
7000 return styleBSizeConsideringOverrides;
7001 }();
7002
7003 auto parentFrame = GetParent();
7004 auto alignCB = parentFrame;
7005 bool isGridItem = IsGridItem();
7006 const bool isSubgrid = IsSubgrid();
7007 if (parentFrame && parentFrame->IsTableWrapperFrame() && IsTableFrame()) {
7008 // An inner table frame is sized as a grid item if its table wrapper is,
7009 // because they actually have the same CB (the wrapper's CB).
7010 // @see ReflowInput::InitCBReflowInput
7011 auto tableWrapper = GetParent();
7012 auto grandParent = tableWrapper->GetParent();
7013 isGridItem = grandParent->IsGridContainerFrame() &&
7014 !tableWrapper->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
7015 if (isGridItem) {
7016 // When resolving justify/align-self below, we want to use the grid
7017 // container's justify/align-items value and WritingMode.
7018 alignCB = grandParent;
7019 }
7020 }
7021
7022 // flexItemMainAxis is set if this frame is a flex item in a modern flexbox
7023 // layout. It indicates which logical axis (in this frame's own WM)
7024 // corresponds to its flex container's main axis.
7025 Maybe<LogicalAxis> flexItemMainAxis;
7026 if (IsFlexItem() && !parentFrame->HasAnyStateBits(
7027 NS_STATE_FLEX_IS_EMULATING_LEGACY_WEBKIT_BOX)) {
7028 flexItemMainAxis = Some(nsFlexContainerFrame::IsItemInlineAxisMainAxis(this)
7029 ? LogicalAxis::Inline
7030 : LogicalAxis::Block);
7031 }
7032
7033 const bool isAutoISize = styleISize->IsAuto();
7034 const bool isAutoBSize =
7035 nsLayoutUtils::IsAutoBSize(*styleBSize, aCBSize.BSize(aWM));
7036
7037 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", 7040); 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, 7040); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
7038 "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", 7040); 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, 7040); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
7039 "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", 7040); 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, 7040); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
7040 "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", 7040); 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, 7040); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7041
7042 // Compute inline-axis size
7043 const bool isSubgriddedInInlineAxis =
7044 isSubgrid && static_cast<nsGridContainerFrame*>(this)->IsColSubgrid();
7045
7046 // Per https://drafts.csswg.org/css-grid/#subgrid-box-alignment, if we are
7047 // subgridded in the inline-axis, ignore our style inline-size, and stretch to
7048 // fill the CB.
7049 const bool shouldComputeISize = !isAutoISize && !isSubgriddedInInlineAxis;
7050 if (shouldComputeISize) {
7051 auto iSizeResult = ComputeISizeValue(
7052 aSizingInput.mRenderingContext, aWM, aCBSize, aMargin, aBorderPadding,
7053 *styleISize, *styleBSize, aspectRatio, aFlags);
7054 result.ISize(aWM) = iSizeResult.mISize;
7055 aspectRatioUsage = iSizeResult.mAspectRatioUsage;
7056 } else if (MOZ_UNLIKELY(isGridItem)(__builtin_expect(!!(isGridItem), 0)) && !IsTrueOverflowContainer()) {
7057 // 'auto' inline-size for grid-level box - fill the CB for 'stretch' /
7058 // 'normal' and clamp it to the CB if requested:
7059 bool isStretchAligned = false;
7060 bool mayUseAspectRatio = aspectRatio && !isAutoBSize;
7061 if (!aFlags.contains(ComputeSizeFlag::ShrinkWrap) &&
7062 !StyleMargin()->HasInlineAxisAuto(aWM, anchorResolutionParams) &&
7063 !alignCB->IsMasonry(aWM, LogicalAxis::Inline)) {
7064 auto inlineAxisAlignment = stylePos->UsedSelfAlignment(
7065 aWM, LogicalAxis::Inline, alignCB->GetWritingMode(),
7066 alignCB->Style());
7067 isStretchAligned = inlineAxisAlignment == StyleAlignFlags::STRETCH ||
7068 (inlineAxisAlignment == StyleAlignFlags::NORMAL &&
7069 !mayUseAspectRatio);
7070 }
7071
7072 // Apply the preferred aspect ratio for alignments other than *stretch* and
7073 // *normal without aspect ratio*.
7074 // The spec says all other values should size the items as fit-content, and
7075 // the intrinsic size should respect the preferred aspect ratio, so we also
7076 // apply aspect ratio for all other values.
7077 // https://drafts.csswg.org/css-grid/#grid-item-sizing
7078 if (!isStretchAligned && mayUseAspectRatio) {
7079 result.ISize(aWM) = ComputeISizeValueFromAspectRatio(
7080 aWM, aCBSize, boxSizingAdjust, styleBSize->AsLengthPercentage(),
7081 aspectRatio);
7082 aspectRatioUsage = AspectRatioUsage::ToComputeISize;
7083 }
7084
7085 if (isStretchAligned ||
7086 aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize)) {
7087 auto iSizeToFillCB =
7088 std::max(nscoord(0), aCBSize.ISize(aWM) - aBorderPadding.ISize(aWM) -
7089 aMargin.ISize(aWM));
7090 if (isStretchAligned || result.ISize(aWM) > iSizeToFillCB) {
7091 result.ISize(aWM) = iSizeToFillCB;
7092 }
7093 }
7094 } else if (aspectRatio && !isAutoBSize) {
7095 // Note: if both the inline size and the block size are auto, the block axis
7096 // is the ratio-dependent axis by default. That means we only need to
7097 // transfer the resolved inline size via aspect-ratio to block axis later in
7098 // this method, but not the other way around.
7099 //
7100 // In this branch, we transfer the non-auto block size via aspect-ration to
7101 // inline axis.
7102 result.ISize(aWM) = ComputeISizeValueFromAspectRatio(
7103 aWM, aCBSize, boxSizingAdjust, styleBSize->AsLengthPercentage(),
7104 aspectRatio);
7105 aspectRatioUsage = AspectRatioUsage::ToComputeISize;
7106 }
7107
7108 // Calculate and apply transferred min & max size contraints.
7109 // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-size-transfers
7110 //
7111 // Note: The basic principle is that sizing constraints transfer through the
7112 // aspect-ratio to the other side to preserve the aspect ratio to the extent
7113 // that they can without violating any sizes specified explicitly on that
7114 // affected axis.
7115 //
7116 // FIXME: The spec words may not be correct, so we may have to update this
7117 // tentative solution once this spec issue gets resolved. Here, we clamp the
7118 // flex base size by the transferred min and max sizes, and don't include
7119 // the transferred min & max sizes into its used min & max sizes. So this
7120 // lets us match other browsers' current behaviors.
7121 // https://github.com/w3c/csswg-drafts/issues/6071
7122 //
7123 // Note: This may make more sense if we clamp the flex base size in
7124 // FlexItem::ResolveFlexBaseSizeFromAspectRatio(). However, the result should
7125 // be identical. FlexItem::ResolveFlexBaseSizeFromAspectRatio() only handles
7126 // the case of the definite cross size, and the definite cross size is clamped
7127 // by the min & max cross sizes below in this function. This means its flex
7128 // base size has been clamped by the transferred min & max size already after
7129 // generating the flex items. So here we make the code more general for both
7130 // definite cross size and indefinite cross size.
7131 const bool isDefiniteISize = styleISize->IsLengthPercentage();
7132 const auto minBSizeCoord = stylePos->MinBSize(aWM, anchorResolutionParams);
7133 const auto maxBSizeCoord = stylePos->MaxBSize(aWM, anchorResolutionParams);
7134 const bool isAutoMinBSize =
7135 nsLayoutUtils::IsAutoBSize(*minBSizeCoord, aCBSize.BSize(aWM));
7136 const bool isAutoMaxBSize =
7137 nsLayoutUtils::IsAutoBSize(*maxBSizeCoord, aCBSize.BSize(aWM));
7138 if (aspectRatio && !isDefiniteISize) {
7139 // Note: the spec mentions that
7140 // 1. This transferred minimum is capped by any definite preferred or
7141 // maximum size in the destination axis.
7142 // 2. This transferred maximum is floored by any definite preferred or
7143 // minimum size in the destination axis.
7144 //
7145 // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-size-transfers
7146 //
7147 // The spec requires us to clamp these by the specified size (it calls it
7148 // the preferred size). However, we actually don't need to worry about that,
7149 // because we are here only if the inline size is indefinite.
7150 //
7151 // We do not need to clamp the transferred minimum and maximum as long as we
7152 // always apply the transferred min/max size before the explicit min/max
7153 // size; the result will be identical.
7154 const nscoord transferredMinISize =
7155 isAutoMinBSize ? 0
7156 : ComputeISizeValueFromAspectRatio(
7157 aWM, aCBSize, boxSizingAdjust,
7158 minBSizeCoord->AsLengthPercentage(), aspectRatio);
7159 const nscoord transferredMaxISize =
7160 isAutoMaxBSize ? nscoord_MAX
7161 : ComputeISizeValueFromAspectRatio(
7162 aWM, aCBSize, boxSizingAdjust,
7163 maxBSizeCoord->AsLengthPercentage(), aspectRatio);
7164
7165 result.ISize(aWM) =
7166 CSSMinMax(result.ISize(aWM), transferredMinISize, transferredMaxISize);
7167 }
7168
7169 // Flex items ignore their min & max sizing properties in their flex
7170 // container's main-axis. (Those properties get applied later in the flexbox
7171 // algorithm.)
7172 const bool isFlexItemInlineAxisMainAxis =
7173 flexItemMainAxis && *flexItemMainAxis == LogicalAxis::Inline;
7174 // Grid items that are subgridded in inline-axis also ignore their min & max
7175 // sizing properties in that axis.
7176 const bool shouldIgnoreMinMaxISize =
7177 isFlexItemInlineAxisMainAxis || isSubgriddedInInlineAxis;
7178 const auto maxISizeCoord = stylePos->MaxISize(aWM, anchorResolutionParams);
7179 nscoord maxISize = NS_UNCONSTRAINEDSIZE;
7180 if (!maxISizeCoord->IsNone() && !shouldIgnoreMinMaxISize) {
7181 maxISize = ComputeISizeValue(aSizingInput.mRenderingContext, aWM, aCBSize,
7182 aMargin, aBorderPadding, *maxISizeCoord,
7183 *styleBSize, aspectRatio, aFlags)
7184 .mISize;
7185 result.ISize(aWM) = std::min(maxISize, result.ISize(aWM));
7186 }
7187
7188 const nscoord bSizeAsPercentageBasis = ComputeBSizeValueAsPercentageBasis(
7189 *styleBSize, *minBSizeCoord, *maxBSizeCoord, aCBSize.BSize(aWM),
7190 boxSizingAdjust.BSize(aWM), aMargin.BSize(aWM),
7191 aBorderPadding.BSize(aWM));
7192 const IntrinsicSizeInput input(
7193 aSizingInput.mRenderingContext,
7194 Some(aCBSize.ConvertTo(GetWritingMode(), aWM)),
7195 Some(LogicalSize(aWM, NS_UNCONSTRAINEDSIZE, bSizeAsPercentageBasis)
7196 .ConvertTo(GetWritingMode(), aWM)));
7197 const auto minISizeCoord = stylePos->MinISize(aWM, anchorResolutionParams);
7198 nscoord minISize;
7199 if (!minISizeCoord->IsAuto() && !shouldIgnoreMinMaxISize) {
7200 minISize = ComputeISizeValue(aSizingInput.mRenderingContext, aWM, aCBSize,
7201 aMargin, aBorderPadding, *minISizeCoord,
7202 *styleBSize, aspectRatio, aFlags)
7203 .mISize;
7204 } else if (MOZ_UNLIKELY((__builtin_expect(!!(aFlags.contains(ComputeSizeFlag::IApplyAutoMinSize
)), 0))
7205 aFlags.contains(ComputeSizeFlag::IApplyAutoMinSize))(__builtin_expect(!!(aFlags.contains(ComputeSizeFlag::IApplyAutoMinSize
)), 0))
) {
7206 // This implements "Implied Minimum Size of Grid Items".
7207 // https://drafts.csswg.org/css-grid/#min-size-auto
7208 minISize = std::min(maxISize, GetMinISize(input));
7209 if (styleISize->IsLengthPercentage()) {
7210 minISize = std::min(minISize, result.ISize(aWM));
7211 } else if (aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize)) {
7212 // "if the grid item spans only grid tracks that have a fixed max track
7213 // sizing function, its automatic minimum size in that dimension is
7214 // further clamped to less than or equal to the size necessary to fit
7215 // its margin box within the resulting grid area (flooring at zero)"
7216 // https://drafts.csswg.org/css-grid/#min-size-auto
7217 auto maxMinISize =
7218 std::max(nscoord(0), aCBSize.ISize(aWM) - aBorderPadding.ISize(aWM) -
7219 aMargin.ISize(aWM));
7220 minISize = std::min(minISize, maxMinISize);
7221 }
7222 } else if (aspectRatioUsage == AspectRatioUsage::ToComputeISize &&
7223 ShouldApplyAutomaticMinimumOnInlineAxis(
7224 aWM, disp->IsScrollableOverflow(), anchorResolutionParams,
7225 stylePos)) {
7226 // This means we successfully applied aspect-ratio and now need to check
7227 // if we need to apply the automatic content-based minimum size:
7228 // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-minimum
7229 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", 7230); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!HasReplacedSizing()" ") (" "aspect-ratio minimums should not apply to replaced elements"
")"); do { MOZ_CrashSequence(__null, 7230); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
7230 "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", 7230); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!HasReplacedSizing()" ") (" "aspect-ratio minimums should not apply to replaced elements"
")"); do { MOZ_CrashSequence(__null, 7230); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7231 // The inline size computed by aspect-ratio shouldn't less than the
7232 // min-content size, which should be capped by its maximum inline size.
7233 minISize = std::min(GetMinISize(input), maxISize);
7234 } else {
7235 // Treat "min-width: auto" as 0.
7236 // NOTE: Technically, "auto" is supposed to behave like "min-content" on
7237 // flex items. However, we don't need to worry about that here, because
7238 // flex items' min-sizes are intentionally ignored until the flex
7239 // container explicitly considers them during space distribution.
7240 minISize = 0;
7241 }
7242 result.ISize(aWM) = std::max(minISize, result.ISize(aWM));
7243
7244 // Compute block-axis size
7245 // (but not if we have auto bsize -- then, we'll just stick with the bsize
7246 // that we already calculated in the initial ComputeAutoSize() call. However,
7247 // if we have a valid preferred aspect ratio, we still have to compute the
7248 // block size because aspect ratio affects the intrinsic content size.)
7249 const bool isSubgriddedInBlockAxis =
7250 isSubgrid && static_cast<nsGridContainerFrame*>(this)->IsRowSubgrid();
7251
7252 // Per https://drafts.csswg.org/css-grid/#subgrid-box-alignment, if we are
7253 // subgridded in the block-axis, ignore our style block-size, and stretch to
7254 // fill the CB.
7255 const bool shouldComputeBSize = !isAutoBSize && !isSubgriddedInBlockAxis;
7256 if (shouldComputeBSize) {
7257 result.BSize(aWM) = nsLayoutUtils::ComputeBSizeValue(
7258 aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
7259 styleBSize->AsLengthPercentage());
7260 } else if (MOZ_UNLIKELY(isGridItem)(__builtin_expect(!!(isGridItem), 0)) && styleBSize->IsAuto() &&
7261 !aFlags.contains(ComputeSizeFlag::IsGridMeasuringReflow) &&
7262 !IsTrueOverflowContainer() &&
7263 !alignCB->IsMasonry(aWM, LogicalAxis::Block)) {
7264 auto cbSize = aCBSize.BSize(aWM);
7265 if (cbSize != NS_UNCONSTRAINEDSIZE) {
7266 // 'auto' block-size for grid-level box - fill the CB for 'stretch' /
7267 // 'normal' and clamp it to the CB if requested:
7268 bool isStretchAligned = false;
7269 bool mayUseAspectRatio =
7270 aspectRatio && result.ISize(aWM) != NS_UNCONSTRAINEDSIZE;
7271 if (!StyleMargin()->HasBlockAxisAuto(aWM, anchorResolutionParams)) {
7272 auto blockAxisAlignment = stylePos->UsedSelfAlignment(
7273 aWM, LogicalAxis::Block, alignCB->GetWritingMode(),
7274 alignCB->Style());
7275 isStretchAligned = blockAxisAlignment == StyleAlignFlags::STRETCH ||
7276 (blockAxisAlignment == StyleAlignFlags::NORMAL &&
7277 !mayUseAspectRatio);
7278 }
7279
7280 // Apply the preferred aspect ratio for alignments other than *stretch*
7281 // and *normal without aspect ratio*.
7282 // The spec says all other values should size the items as fit-content,
7283 // and the intrinsic size should respect the preferred aspect ratio, so
7284 // we also apply aspect ratio for all other values.
7285 // https://drafts.csswg.org/css-grid/#grid-item-sizing
7286 if (!isStretchAligned && mayUseAspectRatio) {
7287 result.BSize(aWM) = aspectRatio.ComputeRatioDependentSize(
7288 LogicalAxis::Block, aWM, result.ISize(aWM), boxSizingAdjust);
7289 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", 7289); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aspectRatioUsage == AspectRatioUsage::None"
")"); do { MOZ_CrashSequence(__null, 7289); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7290 aspectRatioUsage = AspectRatioUsage::ToComputeBSize;
7291 }
7292
7293 if (isStretchAligned ||
7294 aFlags.contains(ComputeSizeFlag::BClampMarginBoxMinSize)) {
7295 auto bSizeToFillCB = nsLayoutUtils::ComputeStretchContentBoxBSize(
7296 cbSize, aMargin.BSize(aWM), aBorderPadding.BSize(aWM));
7297 if (isStretchAligned || (result.BSize(aWM) != NS_UNCONSTRAINEDSIZE &&
7298 result.BSize(aWM) > bSizeToFillCB)) {
7299 result.BSize(aWM) = bSizeToFillCB;
7300 }
7301 }
7302 }
7303 } else if (aspectRatio) {
7304 // If both inline and block dimensions are auto, the block axis is the
7305 // ratio-dependent axis by default.
7306 // If we have a super large inline size, aspect-ratio should still be
7307 // applied (so aspectRatioUsage flag is set as expected). That's why we
7308 // apply aspect-ratio unconditionally for auto block size here.
7309 result.BSize(aWM) = aspectRatio.ComputeRatioDependentSize(
7310 LogicalAxis::Block, aWM, result.ISize(aWM), boxSizingAdjust);
7311 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", 7311); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aspectRatioUsage == AspectRatioUsage::None"
")"); do { MOZ_CrashSequence(__null, 7311); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7312 aspectRatioUsage = AspectRatioUsage::ToComputeBSize;
7313 }
7314
7315 if (result.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
7316 // Flex items ignore their min & max sizing properties in their flex
7317 // container's main-axis. (Those properties get applied later in the flexbox
7318 // algorithm.)
7319 const bool isFlexItemBlockAxisMainAxis =
7320 flexItemMainAxis && *flexItemMainAxis == LogicalAxis::Block;
7321 // Grid items that are subgridded in block-axis also ignore their min & max
7322 // sizing properties in that axis.
7323 const bool shouldIgnoreMinMaxBSize =
7324 isFlexItemBlockAxisMainAxis || isSubgriddedInBlockAxis;
7325 if (!isAutoMaxBSize && !shouldIgnoreMinMaxBSize) {
7326 nscoord maxBSize = nsLayoutUtils::ComputeBSizeValueHandlingStretch(
7327 aCBSize.BSize(aWM), aMargin.BSize(aWM), aBorderPadding.BSize(aWM),
7328 boxSizingAdjust.BSize(aWM), *maxBSizeCoord);
7329 result.BSize(aWM) = std::min(maxBSize, result.BSize(aWM));
7330 }
7331
7332 if (!isAutoMinBSize && !shouldIgnoreMinMaxBSize) {
7333 nscoord minBSize = nsLayoutUtils::ComputeBSizeValueHandlingStretch(
7334 aCBSize.BSize(aWM), aMargin.BSize(aWM), aBorderPadding.BSize(aWM),
7335 boxSizingAdjust.BSize(aWM), *minBSizeCoord);
7336 result.BSize(aWM) = std::max(minBSize, result.BSize(aWM));
7337 }
7338 }
7339
7340 if (IsThemed(disp)) {
7341 nsPresContext* pc = PresContext();
7342 const LayoutDeviceIntSize widget = pc->Theme()->GetMinimumWidgetSize(
7343 pc, this, disp->EffectiveAppearance());
7344
7345 // Convert themed widget's physical dimensions to logical coords
7346 LogicalSize size(aWM, LayoutDeviceIntSize::ToAppUnits(
7347 widget, pc->AppUnitsPerDevPixel()));
7348
7349 // GetMinimumWidgetSize() returns border-box; we need content-box.
7350 size -= aBorderPadding;
7351
7352 if (size.BSize(aWM) > result.BSize(aWM)) {
7353 result.BSize(aWM) = size.BSize(aWM);
7354 }
7355 if (size.ISize(aWM) > result.ISize(aWM)) {
7356 result.ISize(aWM) = size.ISize(aWM);
7357 }
7358 }
7359
7360 result.ISize(aWM) = std::max(0, result.ISize(aWM));
7361 result.BSize(aWM) = std::max(0, result.BSize(aWM));
7362
7363 return {result, aspectRatioUsage};
7364}
7365
7366nscoord nsIFrame::ComputeBSizeValueAsPercentageBasis(
7367 const StyleSize& aStyleBSize, const StyleSize& aStyleMinBSize,
7368 const StyleMaxSize& aStyleMaxBSize, nscoord aCBBSize,
7369 nscoord aContentEdgeToBoxSizingBSize, nscoord aMargin,
7370 nscoord aBorderPadding) {
7371 if (nsLayoutUtils::IsAutoBSize(aStyleBSize, aCBBSize)) {
7372 return NS_UNCONSTRAINEDSIZE;
7373 }
7374
7375 const nscoord bSize = nsLayoutUtils::ComputeBSizeValueHandlingStretch(
7376 aCBBSize, aMargin, aBorderPadding, aContentEdgeToBoxSizingBSize,
7377 aStyleBSize);
7378
7379 const nscoord minBSize =
7380 nsLayoutUtils::IsAutoBSize(aStyleMinBSize, aCBBSize)
7381 ? 0
7382 : nsLayoutUtils::ComputeBSizeValueHandlingStretch(
7383 aCBBSize, aMargin, aBorderPadding, aContentEdgeToBoxSizingBSize,
7384 aStyleMinBSize);
7385
7386 const nscoord maxBSize =
7387 nsLayoutUtils::IsAutoBSize(aStyleMaxBSize, aCBBSize)
7388 ? NS_UNCONSTRAINEDSIZE
7389 : nsLayoutUtils::ComputeBSizeValueHandlingStretch(
7390 aCBBSize, aMargin, aBorderPadding, aContentEdgeToBoxSizingBSize,
7391 aStyleMaxBSize);
7392
7393 return CSSMinMax(bSize, minBSize, maxBSize);
7394}
7395
7396nsRect nsIFrame::ComputeTightBounds(DrawTarget* aDrawTarget) const {
7397 return InkOverflowRect();
7398}
7399
7400/* virtual */
7401nsresult nsIFrame::GetPrefWidthTightBounds(gfxContext* aContext, nscoord* aX,
7402 nscoord* aXMost) {
7403 return NS_ERROR_NOT_IMPLEMENTED;
7404}
7405
7406/* virtual */
7407LogicalSize nsIFrame::ComputeAutoSize(
7408 const SizeComputationInput& aSizingInput, WritingMode aWM,
7409 const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize,
7410 const mozilla::LogicalSize& aMargin,
7411 const mozilla::LogicalSize& aBorderPadding,
7412 const StyleSizeOverrides& aSizeOverrides, ComputeSizeFlags aFlags) {
7413 if (IsAbsolutelyPositionedWithDefiniteContainingBlock()) {
7414 return ComputeAbsolutePosAutoSize(aSizingInput, aWM, aCBSize,
7415 aAvailableISize, aMargin, aBorderPadding,
7416 aSizeOverrides, aFlags);
7417 }
7418
7419 // Use basic shrink-wrapping as a default implementation.
7420 LogicalSize result(aWM, 0xdeadbeef, NS_UNCONSTRAINEDSIZE);
7421
7422 const auto anchorResolutionParams = AnchorPosResolutionParams::From(this);
7423 // don't bother setting it if the result won't be used
7424 const auto styleISize =
7425 aSizeOverrides.mStyleISize
7426 ? AnchorResolvedSizeHelper::Overridden(*aSizeOverrides.mStyleISize)
7427 : StylePosition()->ISize(aWM, anchorResolutionParams);
7428 if (styleISize->IsAuto()) {
7429 nscoord availBased = nsLayoutUtils::ComputeStretchContentBoxISize(
7430 aAvailableISize, aMargin.ISize(aWM), aBorderPadding.ISize(aWM));
7431 const auto* stylePos = StylePosition();
7432 const auto styleBSize =
7433 aSizeOverrides.mStyleBSize
7434 ? AnchorResolvedSizeHelper::Overridden(*aSizeOverrides.mStyleBSize)
7435 : stylePos->BSize(aWM, anchorResolutionParams);
7436 const LogicalSize contentEdgeToBoxSizing =
7437 stylePos->mBoxSizing == StyleBoxSizing::BorderBox ? aBorderPadding
7438 : LogicalSize(aWM);
7439 const nscoord bSize = ComputeBSizeValueAsPercentageBasis(
7440 *styleBSize, *stylePos->MinBSize(aWM, anchorResolutionParams),
7441 *stylePos->MaxBSize(aWM, anchorResolutionParams), aCBSize.BSize(aWM),
7442 contentEdgeToBoxSizing.BSize(aWM), aMargin.BSize(aWM),
7443 aBorderPadding.BSize(aWM));
7444 const IntrinsicSizeInput input(
7445 aSizingInput.mRenderingContext,
7446 Some(aCBSize.ConvertTo(GetWritingMode(), aWM)),
7447 Some(LogicalSize(aWM, NS_UNCONSTRAINEDSIZE, bSize)
7448 .ConvertTo(GetWritingMode(), aWM)));
7449 result.ISize(aWM) = ShrinkISizeToFit(input, availBased, aFlags);
7450 }
7451 return result;
7452}
7453
7454bool nsIFrame::IsAbsolutelyPositionedWithDefiniteContainingBlock() const {
7455 // TODO(dshin, Bug 1927861): Even if an absolute container should have a
7456 // definite size, in a continuation context, the full extent of the containing
7457 // block is not known.
7458 return MOZ_UNLIKELY(IsAbsolutelyPositioned())(__builtin_expect(!!(IsAbsolutelyPositioned()), 0)) && !GetPrevInFlow();
7459}
7460
7461LogicalSize nsIFrame::ComputeAbsolutePosAutoSize(
7462 const SizeComputationInput& aSizingInput, WritingMode aWM,
7463 const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize,
7464 const mozilla::LogicalSize& aMargin,
7465 const mozilla::LogicalSize& aBorderPadding,
7466 const StyleSizeOverrides& aSizeOverrides, const ComputeSizeFlags& aFlags) {
7467 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"
, 7468); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsAbsolutelyPositionedWithDefiniteContainingBlock()"
") (" "Asking for absolute auto size when not absolute" ")")
; do { MOZ_CrashSequence(__null, 7468); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
7468 "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"
, 7468); AnnotateMozCrashReason("MOZ_ASSERT" "(" "IsAbsolutelyPositionedWithDefiniteContainingBlock()"
") (" "Asking for absolute auto size when not absolute" ")")
; do { MOZ_CrashSequence(__null, 7468); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7469 // Ideally, this is an assertion, but the containing block could just be
7470 // really big.
7471 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", 7473); } } while (
false)
7472 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", 7473); } } while (
false)
7473 "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", 7473); } } while (
false)
;
7474 LogicalSize result(aWM, static_cast<nscoord>(0xdeadbeef),
7475 NS_UNCONSTRAINEDSIZE);
7476
7477 const auto* stylePos = StylePosition();
7478 const auto anchorResolutionParams =
7479 AnchorPosOffsetResolutionParams::UseCBFrameSize(
7480 AnchorPosResolutionParams::From(&aSizingInput));
7481 const auto& styleISize =
7482 aSizeOverrides.mStyleISize
7483 ? AnchorResolvedSizeHelper::Overridden(*aSizeOverrides.mStyleISize)
7484 : stylePos->ISize(aWM, anchorResolutionParams.mBaseParams);
7485 const auto& styleBSize =
7486 aSizeOverrides.mStyleBSize
7487 ? AnchorResolvedSizeHelper::Overridden(*aSizeOverrides.mStyleBSize)
7488 : stylePos->BSize(aWM, anchorResolutionParams.mBaseParams);
7489 const auto iStartOffsetIsAuto =
7490 stylePos
7491 ->GetAnchorResolvedInset(LogicalSide::IStart, aWM,
7492 anchorResolutionParams)
7493 ->IsAuto();
7494 const auto iEndOffsetIsAuto =
7495 stylePos
7496 ->GetAnchorResolvedInset(LogicalSide::IEnd, aWM,
7497 anchorResolutionParams)
7498 ->IsAuto();
7499 const auto bStartOffsetIsAuto =
7500 stylePos
7501 ->GetAnchorResolvedInset(LogicalSide::BStart, aWM,
7502 anchorResolutionParams)
7503 ->IsAuto();
7504 const auto bEndOffsetIsAuto =
7505 stylePos
7506 ->GetAnchorResolvedInset(LogicalSide::BEnd, aWM,
7507 anchorResolutionParams)
7508 ->IsAuto();
7509 const auto boxSizingAdjust = stylePos->mBoxSizing == StyleBoxSizing::BorderBox
7510 ? aBorderPadding
7511 : LogicalSize(aWM);
7512 auto shouldStretch = [](StyleAlignFlags aAlignment, const nsIFrame* aFrame,
7513 bool aStartIsAuto, bool aEndIsAuto) {
7514 if (aStartIsAuto || aEndIsAuto) {
7515 // Note(dshin, bug 1930427): This is not part of the current spec [1];
7516 // however, no one implements the new inset behaviour [2], and the old
7517 // behaviour [3] ends up computing the static size if both or one inset is
7518 // auto.
7519 //
7520 // [1]: https://drafts.csswg.org/css-position-3/#abspos-auto-size
7521 // [2]: https://drafts.csswg.org/css-position-3/#resolving-insets
7522 // [3]: https://drafts.csswg.org/css-position-3/#abspos-old
7523 return false;
7524 }
7525 // Don't care about flag bits for auto-sizing.
7526 aAlignment &= ~StyleAlignFlags::FLAG_BITS;
7527
7528 if (aAlignment == StyleAlignFlags::STRETCH) {
7529 return true;
7530 }
7531
7532 if (aAlignment == StyleAlignFlags::NORMAL) {
7533 // Some replaced elements behave as semi-replaced elements - we want them
7534 // to stretch (See bug 1740580).
7535 return !aFrame->HasReplacedSizing() && !aFrame->IsTableWrapperFrame();
7536 }
7537
7538 return false;
7539 };
7540
7541 // i.e. Absolute containing block
7542
7543 // Self alignment properties translate `auto` to normal for this purpose.
7544 // https://drafts.csswg.org/css-align-3/#valdef-justify-self-auto
7545 nsContainerFrame* contFrame = static_cast<nsContainerFrame*>(this);
7546 const StylePositionArea posArea = stylePos->mPositionArea;
7547 const auto containerWM = GetParent()->GetWritingMode();
7548 auto containerAxis = [&](LogicalAxis aSubjectAxis) {
7549 return aWM.ConvertAxisTo(aSubjectAxis, containerWM);
7550 };
7551 const auto inlineSelfAlign =
7552 contFrame->CSSAlignmentForAbsPosChildWithinContainingBlock(
7553 aSizingInput, containerAxis(LogicalAxis::Inline), posArea, aCBSize);
7554 const auto blockSelfAlign =
7555 contFrame->CSSAlignmentForAbsPosChildWithinContainingBlock(
7556 aSizingInput, containerAxis(LogicalAxis::Block), posArea, aCBSize);
7557 const auto iShouldStretch = shouldStretch(
7558 inlineSelfAlign, this, iStartOffsetIsAuto, iEndOffsetIsAuto);
7559 const auto bShouldStretch =
7560 shouldStretch(blockSelfAlign, this, bStartOffsetIsAuto, bEndOffsetIsAuto);
7561 const auto iSizeIsAuto = styleISize->IsAuto();
7562 // Note(dshin, bug 1789477): `auto` in the context of abs-element uses
7563 // stretch-fit sizing, given specific alignment conditions [1]. Effectively,
7564 // `auto` is `stretch`. `nsLayoutUtils::IsAutoBSize` is not the right tool
7565 // here, since the mapping is explicit, and it's incorrect to e.g. map
7566 // `fit-content` to `stretch`.
7567 // `-moz-available` behaves like `auto` in general, so map the same way.
7568 // When Bug 567039 brings `-moz-available` into alignment with `stretch`, this
7569 // special check can be removed. TODO(dshin): we're probably duplicating the
7570 // `stretch` logic here, since `stretch` is `stretch-fit` sizing [2].
7571 //
7572 // [1]: https://drafts.csswg.org/css-position/#abspos-auto-size
7573 // [2]: https://drafts.csswg.org/css-sizing-4/#valdef-width-stretch
7574 const auto bSizeIsAuto = styleBSize->IsAuto() || styleBSize->IsMozAvailable();
7575 if (bSizeIsAuto && bShouldStretch) {
7576 result.BSize(aWM) = nsLayoutUtils::ComputeStretchContentBoxBSize(
7577 aCBSize.BSize(aWM), aMargin.BSize(aWM), aBorderPadding.BSize(aWM));
7578 }
7579 if (iSizeIsAuto) {
7580 if (iShouldStretch) {
7581 // inline-size to make our margin-box fill the containing block:
7582 result.ISize(aWM) = nsLayoutUtils::ComputeStretchContentBoxISize(
7583 aCBSize.ISize(aWM), aMargin.ISize(aWM), aBorderPadding.ISize(aWM));
7584 } else {
7585 // inline-size to make our margin-box fill aAvailableISize:
7586 nscoord availBased = nsLayoutUtils::ComputeStretchContentBoxISize(
7587 aAvailableISize, aMargin.ISize(aWM), aBorderPadding.ISize(aWM));
7588
7589 const nscoord bSize = ComputeBSizeValueAsPercentageBasis(
7590 styleBSize->IsAuto() && result.BSize(aWM) != NS_UNCONSTRAINEDSIZE
7591 ? StyleSize::FromAppUnits(result.BSize(aWM))
7592 : *styleBSize,
7593 *stylePos->MinBSize(aWM, anchorResolutionParams.mBaseParams),
7594 *stylePos->MaxBSize(aWM, anchorResolutionParams.mBaseParams),
7595 aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM), aMargin.BSize(aWM),
7596 aBorderPadding.BSize(aWM));
7597
7598 const IntrinsicSizeInput input(
7599 aSizingInput.mRenderingContext,
7600 Some(aCBSize.ConvertTo(GetWritingMode(), aWM)),
7601 Some(LogicalSize(aWM, NS_UNCONSTRAINEDSIZE, bSize)
7602 .ConvertTo(GetWritingMode(), aWM)));
7603 result.ISize(aWM) = ShrinkISizeToFit(input, availBased, aFlags);
7604 }
7605 }
7606
7607 const auto& aspectRatio = aSizeOverrides.mAspectRatio
7608 ? *aSizeOverrides.mAspectRatio
7609 : GetAspectRatio();
7610 if (aspectRatio) {
7611 auto aspectRatioUsage = AspectRatioUsage::None;
7612 if (iSizeIsAuto != bSizeIsAuto) {
7613 // Auto axis is dependent.
7614 if (iSizeIsAuto) {
7615 aspectRatioUsage = AspectRatioUsage::ToComputeBSize;
7616 } else {
7617 aspectRatioUsage = AspectRatioUsage::ToComputeISize;
7618 }
7619 } else if (iSizeIsAuto) {
7620 // Both axes are `auto`.
7621 if (iShouldStretch != bShouldStretch) {
7622 // If an axis has stretch, that behaves like a definite size.
7623 aspectRatioUsage = iShouldStretch ? AspectRatioUsage::ToComputeBSize
7624 : AspectRatioUsage::ToComputeISize;
7625 } else if (!iShouldStretch) {
7626 // If one axis has `auto` inset, that is the ratio dependent axis,
7627 // otherwise the block axis is.
7628 const bool inlineInsetHasAuto = iStartOffsetIsAuto || iEndOffsetIsAuto;
7629 const bool blockInsetHasAuto = bStartOffsetIsAuto || bEndOffsetIsAuto;
7630 aspectRatioUsage = inlineInsetHasAuto && !blockInsetHasAuto
7631 ? AspectRatioUsage::ToComputeISize
7632 : AspectRatioUsage::ToComputeBSize;
7633 }
7634 }
7635
7636 if (aspectRatioUsage == AspectRatioUsage::ToComputeBSize &&
7637 !bShouldStretch) {
7638 result.BSize(aWM) = aspectRatio.ComputeRatioDependentSize(
7639 LogicalAxis::Block, aWM, result.ISize(aWM), boxSizingAdjust);
7640 } else if (aspectRatioUsage == AspectRatioUsage::ToComputeISize &&
7641 !iShouldStretch && result.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
7642 result.ISize(aWM) = aspectRatio.ComputeRatioDependentSize(
7643 LogicalAxis::Inline, aWM, result.BSize(aWM), boxSizingAdjust);
7644 }
7645 }
7646
7647 return result;
7648}
7649
7650nscoord nsIFrame::ShrinkISizeToFit(const IntrinsicSizeInput& aInput,
7651 nscoord aISizeInCB,
7652 ComputeSizeFlags aFlags) {
7653 // If we're a container for font size inflation, then shrink
7654 // wrapping inside of us should not apply font size inflation.
7655 AutoMaybeDisableFontInflation an(this);
7656
7657 nscoord result;
7658 nscoord minISize = GetMinISize(aInput);
7659 if (minISize > aISizeInCB) {
7660 const bool clamp = aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize);
7661 result = MOZ_UNLIKELY(clamp)(__builtin_expect(!!(clamp), 0)) ? aISizeInCB : minISize;
7662 } else {
7663 nscoord prefISize = GetPrefISize(aInput);
7664 if (prefISize > aISizeInCB) {
7665 result = aISizeInCB;
7666 } else {
7667 result = prefISize;
7668 }
7669 }
7670 return result;
7671}
7672
7673nscoord nsIFrame::IntrinsicISizeFromInline(const IntrinsicSizeInput& aInput,
7674 IntrinsicISizeType aType) {
7675 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", 7676); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsContainerForFontSizeInflation()" ") (" "Should not be a container for font size inflation!"
")"); do { MOZ_CrashSequence(__null, 7676); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
7676 "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", 7676); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!IsContainerForFontSizeInflation()" ") (" "Should not be a container for font size inflation!"
")"); do { MOZ_CrashSequence(__null, 7676); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7677
7678 if (aType == IntrinsicISizeType::MinISize) {
7679 InlineMinISizeData data;
7680 AddInlineMinISize(aInput, &data);
7681 data.ForceBreak();
7682 return data.mPrevLines;
7683 }
7684
7685 InlinePrefISizeData data;
7686 AddInlinePrefISize(aInput, &data);
7687 data.ForceBreak();
7688 return data.mPrevLines;
7689}
7690
7691nscoord nsIFrame::ComputeISizeValueFromAspectRatio(
7692 WritingMode aWM, const LogicalSize& aCBSize,
7693 const LogicalSize& aContentEdgeToBoxSizing, const LengthPercentage& aBSize,
7694 const AspectRatio& aAspectRatio) const {
7695 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"
, 7695); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aAspectRatio"
") (" "Must have a valid AspectRatio!" ")"); do { MOZ_CrashSequence
(__null, 7695); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
7696 const nscoord bSize = nsLayoutUtils::ComputeBSizeValue(
7697 aCBSize.BSize(aWM), aContentEdgeToBoxSizing.BSize(aWM), aBSize);
7698 return aAspectRatio.ComputeRatioDependentSize(LogicalAxis::Inline, aWM, bSize,
7699 aContentEdgeToBoxSizing);
7700}
7701
7702nsIFrame::ISizeComputationResult nsIFrame::ComputeISizeValue(
7703 gfxContext* aRenderingContext, const WritingMode aWM,
7704 const LogicalSize& aCBSize, const LogicalSize& aMargin,
7705 const LogicalSize& aBorderPadding, ExtremumLength aSize,
7706 Maybe<nscoord> aAvailableISizeOverride, const StyleSize& aStyleBSize,
7707 const AspectRatio& aAspectRatio, ComputeSizeFlags aFlags) {
7708 const auto* stylePos = StylePosition();
7709 const LogicalSize contentEdgeToBoxSizing =
7710 stylePos->mBoxSizing == StyleBoxSizing::BorderBox ? aBorderPadding
7711 : LogicalSize(aWM);
7712 auto GetAvailableISize = [&]() {
7713 return aCBSize.ISize(aWM) - aMargin.ISize(aWM) - aBorderPadding.ISize(aWM);
7714 };
7715
7716 // If 'this' is a container for font size inflation, then shrink
7717 // wrapping inside of it should not apply font size inflation.
7718 AutoMaybeDisableFontInflation an(this);
7719 // If we have an aspect-ratio and a definite block size, we should use them to
7720 // resolve the sizes with intrinsic keywords.
7721 // https://github.com/w3c/csswg-drafts/issues/5032
7722 Maybe<nscoord> iSizeFromAspectRatio = [&]() -> Maybe<nscoord> {
7723 if (aSize == ExtremumLength::MozAvailable ||
7724 aSize == ExtremumLength::Stretch) {
7725 return Nothing();
7726 }
7727 if (!aAspectRatio) {
7728 return Nothing();
7729 }
7730 if (nsLayoutUtils::IsAutoBSize(aStyleBSize, aCBSize.BSize(aWM))) {
7731 return Nothing();
7732 }
7733 const nscoord bSize = nsLayoutUtils::ComputeBSizeValueHandlingStretch(
7734 aCBSize.BSize(aWM), aMargin.BSize(aWM), aBorderPadding.BSize(aWM),
7735 contentEdgeToBoxSizing.BSize(aWM), aStyleBSize);
7736 return Some(aAspectRatio.ComputeRatioDependentSize(
7737 LogicalAxis::Inline, aWM, bSize, contentEdgeToBoxSizing));
7738 }();
7739
7740 const auto anchorResolutionParams = AnchorPosResolutionParams::From(this);
7741 const nscoord bSize = ComputeBSizeValueAsPercentageBasis(
7742 aStyleBSize, *stylePos->MinBSize(aWM, anchorResolutionParams),
7743 *stylePos->MaxBSize(aWM, anchorResolutionParams), aCBSize.BSize(aWM),
7744 contentEdgeToBoxSizing.BSize(aWM), aMargin.BSize(aWM),
7745 aBorderPadding.BSize(aWM));
7746 const IntrinsicSizeInput input(
7747 aRenderingContext, Some(aCBSize.ConvertTo(GetWritingMode(), aWM)),
7748 Some(LogicalSize(aWM, NS_UNCONSTRAINEDSIZE, bSize)
7749 .ConvertTo(GetWritingMode(), aWM)));
7750 nscoord result;
7751 switch (aSize) {
7752 case ExtremumLength::MaxContent:
7753 result =
7754 iSizeFromAspectRatio ? *iSizeFromAspectRatio : GetPrefISize(input);
7755 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"
, 7755); MOZ_PretendNoReturn(); } } while (0)
;
7756 return {result, iSizeFromAspectRatio ? AspectRatioUsage::ToComputeISize
7757 : AspectRatioUsage::None};
7758 case ExtremumLength::MinContent:
7759 result =
7760 iSizeFromAspectRatio ? *iSizeFromAspectRatio : GetMinISize(input);
7761 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"
, 7761); MOZ_PretendNoReturn(); } } while (0)
;
7762 if (MOZ_UNLIKELY((__builtin_expect(!!(aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize
)), 0))
7763 aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize))(__builtin_expect(!!(aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize
)), 0))
) {
7764 result = std::min(GetAvailableISize(), result);
7765 }
7766 return {result, iSizeFromAspectRatio ? AspectRatioUsage::ToComputeISize
7767 : AspectRatioUsage::None};
7768 case ExtremumLength::FitContentFunction:
7769 case ExtremumLength::FitContent: {
7770 nscoord pref = NS_UNCONSTRAINEDSIZE;
7771 nscoord min = 0;
7772 if (iSizeFromAspectRatio) {
7773 // The min-content and max-content size are identical and equal to the
7774 // size computed from the block size and the aspect ratio.
7775 pref = min = *iSizeFromAspectRatio;
7776 } else {
7777 pref = GetPrefISize(input);
7778 min = GetMinISize(input);
7779 }
7780
7781 const nscoord fill = aAvailableISizeOverride ? *aAvailableISizeOverride
7782 : GetAvailableISize();
7783 if (MOZ_UNLIKELY((__builtin_expect(!!(aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize
)), 0))
7784 aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize))(__builtin_expect(!!(aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize
)), 0))
) {
7785 min = std::min(min, fill);
7786 }
7787 result = std::max(min, std::min(pref, fill));
7788 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"
, 7788); MOZ_PretendNoReturn(); } } while (0)
;
7789 return {result};
7790 }
7791 case ExtremumLength::MozAvailable:
7792 case ExtremumLength::Stretch:
7793 return {GetAvailableISize()};
7794 }
7795 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"
, 7795); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Unknown extremum length?" ")"); do
{ MOZ_CrashSequence(__null, 7795); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
7796 return {};
7797}
7798
7799nscoord nsIFrame::ComputeISizeValue(const WritingMode aWM,
7800 const LogicalSize& aCBSize,
7801 const LogicalSize& aContentEdgeToBoxSizing,
7802 const LengthPercentage& aSize) const {
7803 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"
, 7807); } } while (0)
7804 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"
, 7807); } } while (0)
7805 "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"
, 7807); } } while (0)
7806 "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"
, 7807); } } while (0)
7807 "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"
, 7807); } } while (0)
;
7808 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", 7808); MOZ_PretendNoReturn
(); } } while (0)
;
7809
7810 nscoord result = aSize.Resolve(aCBSize.ISize(aWM));
7811 // The result of a calc() expression might be less than 0; we
7812 // should clamp at runtime (below). (Percentages and coords that
7813 // are less than 0 have already been dropped by the parser.)
7814 result -= aContentEdgeToBoxSizing.ISize(aWM);
7815 return std::max(0, result);
7816}
7817
7818void nsIFrame::DidReflow(nsPresContext* aPresContext,
7819 const ReflowInput* aReflowInput) {
7820 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("nsIFrame::DidReflow"))do { if ((int(((mozilla::LogModule*)(nsIFrame::sFrameLogModule
))->Level()) & (0x1))) { TraceMsg ("nsIFrame::DidReflow"
); } } while (0)
;
7821
7822 if (IsHiddenByContentVisibilityOfInFlowParentForLayout()) {
7823 RemoveStateBits(NS_FRAME_IN_REFLOW);
7824 return;
7825 }
7826
7827 SVGObserverUtils::InvalidateDirectRenderingObservers(this);
7828
7829 RemoveStateBits(NS_FRAME_IN_REFLOW | NS_FRAME_FIRST_REFLOW |
7830 NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
7831
7832 // Clear bits that were used in ReflowInput::InitResizeFlags (see
7833 // comment there for why we can't clear it there).
7834 SetHasBSizeChange(false);
7835 SetHasPaddingChange(false);
7836
7837 // Notify the percent bsize observer if there is a percent bsize.
7838 // The observer may be able to initiate another reflow with a computed
7839 // bsize. This happens in the case where a table cell has no computed
7840 // bsize but can fabricate one when the cell bsize is known.
7841 if (aReflowInput && aReflowInput->mPercentBSizeObserver && !GetPrevInFlow()) {
7842 const auto bsize = aReflowInput->mStylePosition->BSize(
7843 aReflowInput->GetWritingMode(),
7844 AnchorPosResolutionParams::From(aReflowInput));
7845 if (bsize->HasPercent()) {
7846 aReflowInput->mPercentBSizeObserver->NotifyPercentBSize(*aReflowInput);
7847 }
7848 }
7849
7850 aPresContext->ReflowedFrame();
7851}
7852
7853/* virtual */
7854bool nsIFrame::CanContinueTextRun() const {
7855 // By default, a frame will *not* allow a text run to be continued
7856 // through it.
7857 return false;
7858}
7859
7860void nsIFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
7861 const ReflowInput& aReflowInput,
7862 nsReflowStatus& aStatus) {
7863 MarkInReflow();
7864 DO_GLOBAL_REFLOW_COUNT("nsFrame")aPresContext->CountReflows(("nsFrame"), (nsIFrame*)this);;
7865 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"
, 7865); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aStatus.IsEmpty()"
") (" "Caller should pass a fresh reflow status!" ")"); do {
MOZ_CrashSequence(__null, 7865); __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
;
7866 aDesiredSize.ClearSize();
7867}
7868
7869bool nsIFrame::IsContentDisabled() const {
7870 auto* element = nsGenericHTMLElement::FromNodeOrNull(GetContent());
7871 return element && element->IsDisabled();
7872}
7873
7874bool nsIFrame::IsContentRelevant() const {
7875 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", 7876); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "StyleDisplay()->ContentVisibility(*this) == StyleContentVisibility::Auto"
")"); do { MOZ_CrashSequence(__null, 7876); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
7876 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", 7876); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "StyleDisplay()->ContentVisibility(*this) == StyleContentVisibility::Auto"
")"); do { MOZ_CrashSequence(__null, 7876); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7877
7878 auto* element = Element::FromNodeOrNull(GetContent());
7879 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"
, 7879); AnnotateMozCrashReason("MOZ_ASSERT" "(" "element" ")"
); do { MOZ_CrashSequence(__null, 7879); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7880
7881 Maybe<ContentRelevancy> relevancy = element->GetContentRelevancy();
7882 return relevancy.isSome() && !relevancy->isEmpty();
7883}
7884
7885bool nsIFrame::HidesContent(
7886 const EnumSet<IncludeContentVisibility>& aInclude) const {
7887 auto effectiveContentVisibility = StyleDisplay()->ContentVisibility(*this);
7888 if (aInclude.contains(IncludeContentVisibility::Hidden) &&
7889 effectiveContentVisibility == StyleContentVisibility::Hidden) {
7890 return true;
7891 }
7892
7893 if (aInclude.contains(IncludeContentVisibility::Auto) &&
7894 effectiveContentVisibility == StyleContentVisibility::Auto) {
7895 return !IsContentRelevant();
7896 }
7897
7898 return false;
7899}
7900
7901bool nsIFrame::HidesContentForLayout() const {
7902 return HidesContent() && !PresShell()->IsForcingLayoutForHiddenContent(this);
7903}
7904
7905bool nsIFrame::IsHiddenByContentVisibilityOfInFlowParentForLayout() const {
7906 const auto* parent = GetInFlowParent();
7907 // The anonymous children owned by parent are important for properly sizing
7908 // their parents.
7909 return parent && parent->HidesContentForLayout() &&
7910 !(parent->HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES) &&
7911 Style()->IsAnonBox());
7912}
7913
7914nsIFrame* nsIFrame::GetClosestContentVisibilityAncestor(
7915 const EnumSet<IncludeContentVisibility>& aInclude) const {
7916 // Before we walk the frame tree: check PresShell bookkeeping to see if there
7917 // exists *any* frame with a content-visibility value that matches aInclude.
7918 mozilla::PresShell* ps = PresShell();
7919 bool doesAnySuchFrameExist =
7920 (aInclude.contains(IncludeContentVisibility::Hidden) &&
7921 ps->HasContentVisibilityHiddenFrames()) ||
7922 (aInclude.contains(IncludeContentVisibility::Auto) &&
7923 ps->HasContentVisibilityAutoFrames());
7924
7925 if (!doesAnySuchFrameExist) {
7926 return nullptr;
7927 }
7928
7929 auto* parent = GetInFlowParent();
7930 bool isAnonymousBlock = Style()->IsAnonBox() && parent &&
7931 parent->HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES);
7932 for (nsIFrame* cur = parent; cur; cur = cur->GetInFlowParent()) {
7933 if (!isAnonymousBlock && cur->HidesContent(aInclude)) {
7934 return cur;
7935 }
7936
7937 // Anonymous boxes are not hidden by the content-visibility of their first
7938 // non-anonymous ancestor, but can be hidden by ancestors further up the
7939 // tree.
7940 isAnonymousBlock = false;
7941 }
7942
7943 return nullptr;
7944}
7945
7946static bool IsClosedDetailsSlot(const Element* aElement) {
7947 const auto* slot = HTMLSlotElement::FromNodeOrNull(aElement);
7948 if (!slot || slot->HasName()) {
7949 return false;
7950 }
7951 const auto* details =
7952 HTMLDetailsElement::FromNodeOrNull(slot->GetContainingShadowHost());
7953 return details && !details->GetBoolAttr(nsGkAtoms::open);
7954}
7955
7956bool nsIFrame::IsHiddenUntilFoundOrClosedDetails() const {
7957 for (const auto* f = this; f; f = f->GetInFlowParent()) {
7958 if (f->HidesContent(nsIFrame::IncludeContentVisibility::Hidden)) {
7959 if (const auto* element = Element::FromNode(f->GetContent());
7960 element &&
7961 !element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::hidden,
7962 nsGkAtoms::untilFound, eIgnoreCase) &&
7963 !IsClosedDetailsSlot(element)) {
7964 return false;
7965 }
7966 }
7967 }
7968 return true;
7969}
7970
7971bool nsIFrame::IsHiddenByContentVisibilityOnAnyAncestor(
7972 const EnumSet<IncludeContentVisibility>& aInclude) const {
7973 return !!GetClosestContentVisibilityAncestor(aInclude);
7974}
7975
7976bool nsIFrame::HasSelectionInSubtree() {
7977 if (IsSelected()) {
7978 return true;
7979 }
7980
7981 RefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
7982 if (!frameSelection) {
7983 return false;
7984 }
7985
7986 const Selection& selection = frameSelection->NormalSelection();
7987
7988 for (uint32_t i = 0; i < selection.RangeCount(); i++) {
7989 auto* range = selection.GetRangeAt(i);
7990 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"
, 7990); AnnotateMozCrashReason("MOZ_ASSERT" "(" "range" ")")
; do { MOZ_CrashSequence(__null, 7990); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
7991
7992 const auto* commonAncestorNode =
7993 range->GetRegisteredClosestCommonInclusiveAncestor();
7994 if (commonAncestorNode &&
7995 commonAncestorNode->IsInclusiveDescendantOf(GetContent())) {
7996 return true;
7997 }
7998 }
7999
8000 return false;
8001}
8002
8003bool nsIFrame::UpdateIsRelevantContent(
8004 const ContentRelevancy& aRelevancyToUpdate) {
8005 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", 8006); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "StyleDisplay()->ContentVisibility(*this) == StyleContentVisibility::Auto"
")"); do { MOZ_CrashSequence(__null, 8006); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
8006 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", 8006); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "StyleDisplay()->ContentVisibility(*this) == StyleContentVisibility::Auto"
")"); do { MOZ_CrashSequence(__null, 8006); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8007
8008 auto* element = Element::FromNodeOrNull(GetContent());
8009 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"
, 8009); AnnotateMozCrashReason("MOZ_ASSERT" "(" "element" ")"
); do { MOZ_CrashSequence(__null, 8009); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8010
8011 ContentRelevancy newRelevancy;
8012 Maybe<ContentRelevancy> oldRelevancy = element->GetContentRelevancy();
8013 if (oldRelevancy.isSome()) {
8014 newRelevancy = *oldRelevancy;
8015 }
8016
8017 auto setRelevancyValue = [&](ContentRelevancyReason reason, bool value) {
8018 if (value) {
8019 newRelevancy += reason;
8020 } else {
8021 newRelevancy -= reason;
8022 }
8023 };
8024
8025 if (!oldRelevancy ||
8026 aRelevancyToUpdate.contains(ContentRelevancyReason::Visible)) {
8027 Maybe<bool> visible = element->GetVisibleForContentVisibility();
8028 if (visible.isSome()) {
8029 setRelevancyValue(ContentRelevancyReason::Visible, *visible);
8030 }
8031 }
8032
8033 if (!oldRelevancy ||
8034 aRelevancyToUpdate.contains(ContentRelevancyReason::FocusInSubtree)) {
8035 setRelevancyValue(ContentRelevancyReason::FocusInSubtree,
8036 element->State().HasAtLeastOneOfStates(
8037 ElementState::FOCUS_WITHIN | ElementState::FOCUS));
8038 }
8039
8040 if (!oldRelevancy ||
8041 aRelevancyToUpdate.contains(ContentRelevancyReason::Selected)) {
8042 setRelevancyValue(ContentRelevancyReason::Selected,
8043 HasSelectionInSubtree());
8044 }
8045
8046 // If the proximity to the viewport has not been determined yet,
8047 // and neither the element nor its contents are focused or selected,
8048 // we should wait for the determination of the proximity. Otherwise,
8049 // there might be a redundant contentvisibilityautostatechange event.
8050 // See https://github.com/w3c/csswg-drafts/issues/9803
8051 bool isProximityToViewportDetermined =
8052 oldRelevancy ? true : element->GetVisibleForContentVisibility().isSome();
8053 if (!isProximityToViewportDetermined && newRelevancy.isEmpty()) {
8054 return false;
8055 }
8056
8057 bool overallRelevancyChanged =
8058 !oldRelevancy || oldRelevancy->isEmpty() != newRelevancy.isEmpty();
8059 if (!oldRelevancy || *oldRelevancy != newRelevancy) {
8060 element->SetContentRelevancy(newRelevancy);
8061 }
8062
8063 if (!overallRelevancyChanged) {
8064 return false;
8065 }
8066
8067 HandleLastRememberedSize();
8068 PresContext()->SetNeedsToUpdateHiddenByContentVisibilityForAnimations();
8069 PresShell()->FrameNeedsReflow(
8070 this, IntrinsicDirty::FrameAncestorsAndDescendants, NS_FRAME_IS_DIRTY);
8071 InvalidateFrame();
8072
8073 ContentVisibilityAutoStateChangeEventInit init;
8074 init.mSkipped = newRelevancy.isEmpty();
8075 RefPtr<ContentVisibilityAutoStateChangeEvent> event =
8076 ContentVisibilityAutoStateChangeEvent::Constructor(
8077 element, u"contentvisibilityautostatechange"_ns, init);
8078
8079 // Per
8080 // https://drafts.csswg.org/css-contain/#content-visibility-auto-state-changed
8081 // "This event is dispatched by posting a task at the time when the state
8082 // change occurs."
8083 auto asyncDispatcher =
8084 MakeRefPtr<AsyncEventDispatcher>(element, event.forget());
8085 DebugOnly<nsresult> rv = asyncDispatcher->PostDOMEvent();
8086 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"
, 8086); MOZ_PretendNoReturn(); } } while (0)
;
8087 return true;
8088}
8089
8090nsresult nsIFrame::CharacterDataChanged(const CharacterDataChangeInfo&) {
8091 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"
, 8091); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "should only be called for text frames"
")"); do { MOZ_CrashSequence(__null, 8091); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8092 return NS_OK;
8093}
8094
8095nsresult nsIFrame::AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute,
8096 AttrModType) {
8097 return NS_OK;
8098}
8099
8100nsIFrame* nsIFrame::GetPrevContinuation() const { return nullptr; }
8101
8102void nsIFrame::SetPrevContinuation(nsIFrame*) {
8103 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"
, 8103); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Not splittable!" ")"); do { MOZ_CrashSequence
(__null, 8103); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
8104}
8105
8106nsIFrame* nsIFrame::GetNextContinuation() const { return nullptr; }
8107
8108void nsIFrame::SetNextContinuation(nsIFrame*) {
8109 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"
, 8109); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Not splittable!" ")"); do { MOZ_CrashSequence
(__null, 8109); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
8110}
8111
8112nsIFrame* nsIFrame::GetPrevInFlow() const { return nullptr; }
8113
8114void nsIFrame::SetPrevInFlow(nsIFrame*) {
8115 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"
, 8115); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Not splittable!" ")"); do { MOZ_CrashSequence
(__null, 8115); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
8116}
8117
8118nsIFrame* nsIFrame::GetNextInFlow() const { return nullptr; }
8119
8120void nsIFrame::SetNextInFlow(nsIFrame*) {
8121 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"
, 8121); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Not splittable!" ")"); do { MOZ_CrashSequence
(__null, 8121); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
8122}
8123
8124nsIFrame* nsIFrame::GetTailContinuation() {
8125 nsIFrame* frame = this;
8126 while (frame->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
8127 frame = frame->GetPrevContinuation();
8128 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", 8128); MOZ_PretendNoReturn
(); } } while (0)
;
8129 }
8130 for (nsIFrame* next = frame->GetNextContinuation();
8131 next && !next->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
8132 next = frame->GetNextContinuation()) {
8133 frame = next;
8134 }
8135
8136 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", 8136); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "frame" ") (" "illegal state in continuation chain."
")"); do { MOZ_CrashSequence(__null, 8136); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8137 return frame;
8138}
8139
8140nsIWidget* nsIFrame::GetOwnWidget() const {
8141 if (IsMenuPopupFrame()) {
8142 return static_cast<const nsMenuPopupFrame*>(this)->GetWidget();
8143 }
8144 if (!GetParent()) {
8145 return PresShell()->GetOwnWidget();
8146 }
8147 return nullptr;
8148}
8149
8150// aPosition should be a function that returns the position of a frame relative
8151// to its parent.
8152template <typename PositionGetter>
8153static nsPoint OffsetCalculator(const nsIFrame* aThis, const nsIFrame* aOther,
8154 PositionGetter&& aPosition) {
8155 static_assert(std::is_invocable_r_v<nsPoint, PositionGetter, const nsIFrame*>,
8156 "aPosition must be callable as nsPoint(const nsIFrame*)");
8157
8158 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", 8158); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aOther" ") (" "Must have frame for destination coordinate system!"
")"); do { MOZ_CrashSequence(__null, 8158); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8159
8160 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"
, 8161); MOZ_PretendNoReturn(); } } while (0)
8161 "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"
, 8161); MOZ_PretendNoReturn(); } } while (0)
;
8162
8163 nsPoint offset(0, 0);
8164 const nsIFrame* f;
8165 for (f = aThis; f != aOther && f; f = f->GetParent()) {
8166 offset += aPosition(f);
8167 }
8168
8169 if (f != aOther) {
8170 // Looks like aOther wasn't an ancestor of |this|. So now we have
8171 // the root-frame-relative position of |this| in |offset|. Convert back
8172 // to the coordinates of aOther
8173 while (aOther) {
8174 offset -= aPosition(aOther);
8175 aOther = aOther->GetParent();
8176 }
8177 }
8178
8179 return offset;
8180}
8181
8182nsPoint nsIFrame::GetOffsetTo(const nsIFrame* aOther) const {
8183 return OffsetCalculator(this, aOther, [](const nsIFrame* aFrame) {
8184 return aFrame->GetPosition();
8185 });
8186}
8187
8188nsPoint nsIFrame::GetOffsetToRootFrame() const {
8189 return GetOffsetTo(PresShell()->GetRootFrame());
8190}
8191
8192nsPoint nsIFrame::GetOffsetToIgnoringScrolling(const nsIFrame* aOther) const {
8193 return OffsetCalculator(this, aOther, [](const nsIFrame* aFrame) {
8194 return aFrame->GetPositionIgnoringScrolling();
8195 });
8196}
8197
8198nsPoint nsIFrame::GetOffsetToIgnoringScrollingAndSticky(
8199 const nsIFrame* aOther) const {
8200 return OffsetCalculator(this, aOther, [](const nsIFrame* aFrame) {
8201 return aFrame->GetPositionIgnoringScrollingAndSticky();
8202 });
8203}
8204
8205nsPoint nsIFrame::GetScrollOffsetTo(const nsIFrame* aOther) const {
8206 return OffsetCalculator(this, aOther, [](const nsIFrame* aFrame) {
8207 return aFrame->GetPositionIgnoringScrollingAndSticky() -
8208 aFrame->GetPosition();
8209 });
8210}
8211
8212nsPoint nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther) const {
8213 return GetOffsetToCrossDoc(aOther, PresContext()->AppUnitsPerDevPixel());
8214}
8215
8216nsPoint nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther,
8217 const int32_t aAPD) const {
8218 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", 8218); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aOther" ") (" "Must have frame for destination coordinate system!"
")"); do { MOZ_CrashSequence(__null, 8218); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8219 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"
, 8223); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "PresContext()->GetRootPresContext() == aOther->PresContext()->GetRootPresContext()"
") (" "trying to get the offset between frames in different document "
"hierarchies?" ")"); do { MOZ_CrashSequence(__null, 8223); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
8220 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"
, 8223); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "PresContext()->GetRootPresContext() == aOther->PresContext()->GetRootPresContext()"
") (" "trying to get the offset between frames in different document "
"hierarchies?" ")"); do { MOZ_CrashSequence(__null, 8223); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
8221 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"
, 8223); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "PresContext()->GetRootPresContext() == aOther->PresContext()->GetRootPresContext()"
") (" "trying to get the offset between frames in different document "
"hierarchies?" ")"); do { MOZ_CrashSequence(__null, 8223); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
8222 "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"
, 8223); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "PresContext()->GetRootPresContext() == aOther->PresContext()->GetRootPresContext()"
") (" "trying to get the offset between frames in different document "
"hierarchies?" ")"); do { MOZ_CrashSequence(__null, 8223); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
8223 "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"
, 8223); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "PresContext()->GetRootPresContext() == aOther->PresContext()->GetRootPresContext()"
") (" "trying to get the offset between frames in different document "
"hierarchies?" ")"); do { MOZ_CrashSequence(__null, 8223); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
;
8224
8225 const nsIFrame* root = nullptr;
8226 // offset will hold the final offset
8227 // docOffset holds the currently accumulated offset at the current APD, it
8228 // will be converted and added to offset when the current APD changes.
8229 nsPoint offset(0, 0), docOffset(0, 0);
8230 const nsIFrame* f = this;
8231 int32_t currAPD = PresContext()->AppUnitsPerDevPixel();
8232 while (f && f != aOther) {
8233 docOffset += f->GetPosition();
8234 nsIFrame* parent = f->GetParent();
8235 if (parent) {
8236 f = parent;
8237 } else {
8238 nsPoint newOffset(0, 0);
8239 root = f;
8240 f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f, &newOffset);
8241 int32_t newAPD = f ? f->PresContext()->AppUnitsPerDevPixel() : 0;
8242 if (!f || newAPD != currAPD) {
8243 // Convert docOffset to the right APD and add it to offset.
8244 offset += docOffset.ScaleToOtherAppUnits(currAPD, aAPD);
8245 docOffset.x = docOffset.y = 0;
8246 }
8247 currAPD = newAPD;
8248 docOffset += newOffset;
8249 }
8250 }
8251 if (f == aOther) {
8252 offset += docOffset.ScaleToOtherAppUnits(currAPD, aAPD);
8253 } else {
8254 // Looks like aOther wasn't an ancestor of |this|. So now we have
8255 // the root-document-relative position of |this| in |offset|. Subtract the
8256 // root-document-relative position of |aOther| from |offset|.
8257 // This call won't try to recurse again because root is an ancestor of
8258 // aOther.
8259 nsPoint negOffset = aOther->GetOffsetToCrossDoc(root, aAPD);
8260 offset -= negOffset;
8261 }
8262
8263 return offset;
8264}
8265
8266CSSIntRect nsIFrame::GetScreenRect() const {
8267 return CSSIntRect::FromAppUnitsToNearest(GetScreenRectInAppUnits());
8268}
8269
8270nsRect nsIFrame::GetScreenRectInAppUnits() const {
8271 nsPresContext* presContext = PresContext();
8272 nsIFrame* rootFrame = presContext->PresShell()->GetRootFrame();
8273 nsPoint rootScreenPos(0, 0);
8274 nsPoint rootFrameOffsetInParent(0, 0);
8275 nsIFrame* rootFrameParent = nsLayoutUtils::GetCrossDocParentFrameInProcess(
8276 rootFrame, &rootFrameOffsetInParent);
8277 if (rootFrameParent) {
8278 nsRect parentScreenRectAppUnits =
8279 rootFrameParent->GetScreenRectInAppUnits();
8280 nsPresContext* parentPresContext = rootFrameParent->PresContext();
8281 double parentScale = double(presContext->AppUnitsPerDevPixel()) /
8282 parentPresContext->AppUnitsPerDevPixel();
8283 nsPoint rootPt =
8284 parentScreenRectAppUnits.TopLeft() + rootFrameOffsetInParent;
8285 rootScreenPos.x = NS_round(parentScale * rootPt.x);
8286 rootScreenPos.y = NS_round(parentScale * rootPt.y);
8287 } else if (nsCOMPtr<nsIWidget> rootWidget = presContext->GetRootWidget()) {
8288 LayoutDeviceIntPoint rootDevPx = rootWidget->WidgetToScreenOffset();
8289 rootScreenPos.x = presContext->DevPixelsToAppUnits(rootDevPx.x);
8290 rootScreenPos.y = presContext->DevPixelsToAppUnits(rootDevPx.y);
8291 }
8292
8293 return nsRect(rootScreenPos + GetOffsetTo(rootFrame), GetSize());
8294}
8295
8296nsIWidget* nsIFrame::GetNearestWidget() const {
8297 if (!HasAnyStateBits(NS_FRAME_IN_POPUP)) {
8298 return PresContext()->GetRootWidget();
8299 }
8300 nsPoint unused;
8301 return GetNearestWidget(unused);
8302}
8303
8304nsIWidget* nsIFrame::GetNearestWidget(nsPoint& aOffset) const {
8305 aOffset.MoveTo(0, 0);
8306 nsIFrame* frame = const_cast<nsIFrame*>(this);
8307 const auto targetAPD = PresContext()->AppUnitsPerDevPixel();
8308 auto curAPD = targetAPD;
8309 do {
8310 if (auto* widget = frame->GetOwnWidget()) {
8311 aOffset = aOffset.ScaleToOtherAppUnits(curAPD, targetAPD);
8312 return widget;
8313 }
8314 aOffset += frame->GetPosition();
8315 nsPoint crossDocOffset;
8316 frame =
8317 nsLayoutUtils::GetCrossDocParentFrameInProcess(frame, &crossDocOffset);
8318 if (!frame) {
8319 break;
8320 }
8321 auto newAPD = frame->PresContext()->AppUnitsPerDevPixel();
8322 aOffset = aOffset.ScaleToOtherAppUnits(curAPD, newAPD);
8323 aOffset += crossDocOffset;
8324 curAPD = newAPD;
8325 } while (true);
8326 aOffset = aOffset.ScaleToOtherAppUnits(curAPD, targetAPD);
8327 return PresContext()->GetRootWidget();
8328}
8329
8330Matrix4x4Flagged nsIFrame::GetTransformMatrix(
8331 ViewportType aViewportType, RelativeTo aStopAtAncestor,
8332 nsIFrame** aOutAncestor, TransformMatrixFlags aFlags) const {
8333 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"
, 8333); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aOutAncestor"
") (" "Need a place to put the ancestor!" ")"); do { MOZ_CrashSequence
(__null, 8333); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
8334
8335 auto GetPositionMaybeIgnoringScrolling = [aFlags](const nsIFrame* aFrame) {
8336 return aFlags.contains(TransformMatrixFlag::IgnoreScrolling)
8337 ? aFrame->GetPositionIgnoringScrollingAndSticky()
8338 : aFrame->GetPosition();
8339 };
8340
8341 /* If we're transformed, we want to hand back the combination
8342 * transform/translate matrix that will apply our current transform, then
8343 * shift us to our parent.
8344 */
8345 const bool isTransformed = IsTransformed();
8346 const nsIFrame* zoomedContentRoot = nullptr;
8347 if (aStopAtAncestor.mViewportType == ViewportType::Visual) {
8348 zoomedContentRoot = ViewportUtils::IsZoomedContentRoot(this);
8349 if (zoomedContentRoot) {
8350 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", 8350); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aViewportType != ViewportType::Visual" ")"
); do { MOZ_CrashSequence(__null, 8350); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8351 }
8352 }
8353
8354 if (isTransformed || zoomedContentRoot) {
8355 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"
, 8355); AnnotateMozCrashReason("MOZ_ASSERT" "(" "GetParent()"
")"); do { MOZ_CrashSequence(__null, 8355); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8356 Matrix4x4Flagged result;
8357 int32_t scaleFactor = aFlags.contains(TransformMatrixFlag::InCSSUnits)
8358 ? AppUnitsPerCSSPixel()
8359 : PresContext()->AppUnitsPerDevPixel();
8360
8361 /* Compute the delta to the parent, which we need because we are converting
8362 * coordinates to our parent.
8363 */
8364 if (isTransformed) {
8365 // Note: this converts from Matrix4x4 to Matrix4x4Flagged.
8366 result = nsDisplayTransform::GetResultingTransformMatrix(
8367 this, nsPoint(), scaleFactor,
8368 nsDisplayTransform::INCLUDE_PERSPECTIVE);
8369 }
8370
8371 // The offset from a zoomed content root to its parent (e.g. from
8372 // a canvas frame to a scroll frame) is in layout coordinates, so
8373 // apply it before applying any layout-to-visual transform.
8374 *aOutAncestor = GetParent();
8375 nsPoint delta = GetPositionMaybeIgnoringScrolling(this);
8376 /* Combine the raw transform with a translation to our parent. */
8377 result.PostTranslate(NSAppUnitsToFloatPixels(delta.x, scaleFactor),
8378 NSAppUnitsToFloatPixels(delta.y, scaleFactor), 0.0f);
8379
8380 if (zoomedContentRoot) {
8381 Matrix4x4Flagged layoutToVisual;
8382 if (aFlags.contains(TransformMatrixFlag::InCSSUnits)) {
8383 layoutToVisual = ViewportUtils::GetVisualToLayoutTransform(
8384 zoomedContentRoot->GetContent())
8385 .Inverse()
8386 .ToUnknownMatrix();
8387 } else {
8388 layoutToVisual =
8389 ViewportUtils::GetVisualToLayoutTransform<LayoutDevicePixel>(
8390 zoomedContentRoot->GetContent())
8391 .Inverse()
8392 .ToUnknownMatrix();
8393 }
8394 result = result * layoutToVisual;
8395 }
8396
8397 return result;
8398 }
8399
8400 // We are not transformed, so the returned transform is just going to be a
8401 // translation up to whatever ancestor we decide to stop at.
8402
8403 nsPoint crossdocOffset;
8404 *aOutAncestor =
8405 nsLayoutUtils::GetCrossDocParentFrameInProcess(this, &crossdocOffset);
8406
8407 /* Otherwise, we're not transformed. In that case, we'll walk up the frame
8408 * tree until we either hit the root frame or something that may be
8409 * transformed. We'll then change coordinates into that frame, since we're
8410 * guaranteed that nothing in-between can be transformed. First, however,
8411 * we have to check to see if we have a parent. If not, we'll set the
8412 * outparam to null (indicating that there's nothing left) and will hand back
8413 * the identity matrix.
8414 */
8415 if (!*aOutAncestor) {
8416 return Matrix4x4Flagged();
8417 }
8418
8419 /* Keep iterating while the frame can't possibly be transformed. */
8420 const nsIFrame* current = this;
8421 auto shouldStopAt = [](const nsIFrame* aCurrent, RelativeTo& aStopAtAncestor,
8422 nsIFrame* aOutAncestor, TransformMatrixFlags aFlags) {
8423 return aOutAncestor->IsTransformed() ||
8424 ((aStopAtAncestor.mViewportType == ViewportType::Visual) &&
8425 ViewportUtils::IsZoomedContentRoot(aOutAncestor)) ||
8426 (aFlags.contains(
8427 TransformMatrixFlag::StopAtStackingContextAndDisplayPort) &&
8428 (aOutAncestor->IsStackingContext() ||
8429 DisplayPortUtils::FrameHasDisplayPort(aOutAncestor, aCurrent)));
8430 };
8431
8432 // We run the GetOffsetToCrossDoc code here as an optimization, instead of
8433 // walking the parent chain here and then asking GetOffsetToCrossDoc to walk
8434 // the same parent chain and compute the offset.
8435 const int32_t finalAPD = PresContext()->AppUnitsPerDevPixel();
8436 // offset accumulates the offset at finalAPD.
8437 nsPoint offset = GetPositionMaybeIgnoringScrolling(this);
8438
8439 int32_t currAPD = (*aOutAncestor)->PresContext()->AppUnitsPerDevPixel();
8440 // docOffset accumulates the current offset at currAPD, and then flushes to
8441 // offset at finalAPD when the APD changes or we finish.
8442 nsPoint docOffset = crossdocOffset;
8443 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", 8443); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "crossdocOffset == nsPoint(0, 0) || !GetParent()"
")"); do { MOZ_CrashSequence(__null, 8443); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8444
8445 while (*aOutAncestor != aStopAtAncestor.mFrame &&
8446 !shouldStopAt(current, aStopAtAncestor, *aOutAncestor, aFlags)) {
8447 docOffset += GetPositionMaybeIgnoringScrolling(*aOutAncestor);
8448
8449 nsIFrame* parent = (*aOutAncestor)->GetParent();
8450 if (!parent) {
8451 crossdocOffset.x = crossdocOffset.y = 0;
8452 parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(*aOutAncestor,
8453 &crossdocOffset);
8454
8455 int32_t newAPD =
8456 parent ? parent->PresContext()->AppUnitsPerDevPixel() : currAPD;
8457 if (!parent || newAPD != currAPD) {
8458 // Convert docOffset to finalAPD and add it to offset.
8459 offset += docOffset.ScaleToOtherAppUnits(currAPD, finalAPD);
8460 docOffset.x = docOffset.y = 0;
8461 }
8462 currAPD = newAPD;
8463 docOffset += crossdocOffset;
8464
8465 if (!parent) {
8466 break;
8467 }
8468 }
8469
8470 current = *aOutAncestor;
8471 *aOutAncestor = parent;
8472 }
8473 offset += docOffset.ScaleToOtherAppUnits(currAPD, finalAPD);
8474
8475 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", 8475); MOZ_PretendNoReturn
(); } } while (0)
;
8476
8477 int32_t scaleFactor = aFlags.contains(TransformMatrixFlag::InCSSUnits)
8478 ? AppUnitsPerCSSPixel()
8479 : PresContext()->AppUnitsPerDevPixel();
8480 return Matrix4x4Flagged::Translation2d(
8481 NSAppUnitsToFloatPixels(offset.x, scaleFactor),
8482 NSAppUnitsToFloatPixels(offset.y, scaleFactor));
8483}
8484
8485static void InvalidateRenderingObservers(nsIFrame* aDisplayRoot,
8486 nsIFrame* aFrame,
8487 bool aFrameChanged = true) {
8488 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", 8488); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame)"
")"); do { MOZ_CrashSequence(__null, 8488); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8489 SVGObserverUtils::InvalidateDirectRenderingObservers(aFrame);
8490 nsIFrame* parent = aFrame;
8491 while (parent != aDisplayRoot &&
8492 (parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(parent)) &&
8493 !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
8494 SVGObserverUtils::InvalidateDirectRenderingObservers(parent);
8495 }
8496
8497 if (!aFrameChanged) {
8498 return;
8499 }
8500
8501 aFrame->MarkNeedsDisplayItemRebuild();
8502}
8503
8504static void SchedulePaintInternal(
8505 nsIFrame* aDisplayRoot, nsIFrame* aFrame,
8506 nsIFrame::PaintType aType = nsIFrame::PAINT_DEFAULT) {
8507 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", 8507); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame)"
")"); do { MOZ_CrashSequence(__null, 8507); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8508 nsPresContext* pres = aDisplayRoot->PresContext()->GetRootPresContext();
8509
8510 // No need to schedule a paint for an external document since they aren't
8511 // painted directly.
8512 if (!pres || (pres->Document() && pres->Document()->IsResourceDoc())) {
8513 return;
8514 }
8515 if (!pres->GetContainerWeak()) {
8516 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", 8516)
;
8517 return;
8518 }
8519
8520 pres->PresShell()->SchedulePaint();
8521
8522 if (aType == nsIFrame::PAINT_DEFAULT) {
8523 aDisplayRoot->AddStateBits(NS_FRAME_UPDATE_LAYER_TREE);
8524 }
8525}
8526
8527static void InvalidateFrameInternal(nsIFrame* aFrame, bool aHasDisplayItem,
8528 bool aRebuildDisplayItems) {
8529 if (aHasDisplayItem) {
8530 aFrame->AddStateBits(NS_FRAME_NEEDS_PAINT);
8531 }
8532
8533 if (aRebuildDisplayItems) {
8534 aFrame->MarkNeedsDisplayItemRebuild();
8535 }
8536 SVGObserverUtils::InvalidateDirectRenderingObservers(aFrame);
8537 bool needsSchedulePaint = false;
8538 if (nsLayoutUtils::IsPopup(aFrame)) {
8539 needsSchedulePaint = true;
8540 } else {
8541 nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
8542 while (parent &&
8543 !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
8544 if (aHasDisplayItem && !parent->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
8545 parent->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
8546 }
8547 SVGObserverUtils::InvalidateDirectRenderingObservers(parent);
8548
8549 // If we're inside a popup, then we need to make sure that we
8550 // call schedule paint so that the NS_FRAME_UPDATE_LAYER_TREE
8551 // flag gets added to the popup display root frame.
8552 if (nsLayoutUtils::IsPopup(parent)) {
8553 needsSchedulePaint = true;
8554 break;
8555 }
8556 parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(parent);
8557 }
8558 if (!parent) {
8559 needsSchedulePaint = true;
8560 }
8561 }
8562 if (!aHasDisplayItem) {
8563 return;
8564 }
8565 if (needsSchedulePaint) {
8566 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
8567 SchedulePaintInternal(displayRoot, aFrame);
8568 }
8569 if (aFrame->HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
8570 aFrame->RemoveProperty(nsIFrame::InvalidationRect());
8571 aFrame->RemoveStateBits(NS_FRAME_HAS_INVALID_RECT);
8572 }
8573}
8574
8575void nsIFrame::InvalidateFrameSubtree(bool aRebuildDisplayItems /* = true */) {
8576 InvalidateFrame(0, aRebuildDisplayItems);
8577
8578 if (HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT)) {
8579 return;
8580 }
8581
8582 AddStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT);
8583
8584 for (const auto& childList : CrossDocChildLists()) {
8585 for (nsIFrame* child : childList.mList) {
8586 // Don't explicitly rebuild display items for our descendants,
8587 // since we should be marked and it implicitly includes all
8588 // descendants.
8589 child->InvalidateFrameSubtree(false);
8590 }
8591 }
8592}
8593
8594void nsIFrame::ClearInvalidationStateBits() {
8595 if (HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
8596 for (const auto& childList : CrossDocChildLists()) {
8597 for (nsIFrame* child : childList.mList) {
8598 child->ClearInvalidationStateBits();
8599 }
8600 }
8601 }
8602
8603 RemoveStateBits(NS_FRAME_NEEDS_PAINT | NS_FRAME_DESCENDANT_NEEDS_PAINT |
8604 NS_FRAME_ALL_DESCENDANTS_NEED_PAINT);
8605}
8606
8607bool HasRetainedDataFor(const nsIFrame* aFrame, uint32_t aDisplayItemKey) {
8608 if (RefPtr<WebRenderUserData> data =
8609 GetWebRenderUserData<WebRenderFallbackData>(aFrame,
8610 aDisplayItemKey)) {
8611 return true;
8612 }
8613
8614 return false;
8615}
8616
8617void nsIFrame::InvalidateFrame(uint32_t aDisplayItemKey,
8618 bool aRebuildDisplayItems /* = true */) {
8619 bool hasDisplayItem =
8620 !aDisplayItemKey || HasRetainedDataFor(this, aDisplayItemKey);
8621 InvalidateFrameInternal(this, hasDisplayItem, aRebuildDisplayItems);
8622}
8623
8624void nsIFrame::InvalidateFrameWithRect(const nsRect& aRect,
8625 uint32_t aDisplayItemKey,
8626 bool aRebuildDisplayItems /* = true */) {
8627 if (aRect.IsEmpty()) {
8628 return;
8629 }
8630 bool hasDisplayItem =
8631 !aDisplayItemKey || HasRetainedDataFor(this, aDisplayItemKey);
8632 bool alreadyInvalid = false;
8633 if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) {
8634 InvalidateFrameInternal(this, hasDisplayItem, aRebuildDisplayItems);
8635 } else {
8636 alreadyInvalid = true;
8637 }
8638
8639 if (!hasDisplayItem) {
8640 return;
8641 }
8642
8643 nsRect* rect;
8644 if (HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
8645 rect = GetProperty(InvalidationRect());
8646 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"
, 8646); AnnotateMozCrashReason("MOZ_ASSERT" "(" "rect" ")");
do { MOZ_CrashSequence(__null, 8646); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8647 } else {
8648 if (alreadyInvalid) {
8649 return;
8650 }
8651 rect = new nsRect();
8652 AddProperty(InvalidationRect(), rect);
8653 AddStateBits(NS_FRAME_HAS_INVALID_RECT);
8654 }
8655
8656 *rect = rect->Union(aRect);
8657}
8658
8659bool nsIFrame::IsInvalid(nsRect& aRect) {
8660 if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) {
8661 return false;
8662 }
8663
8664 if (HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
8665 nsRect* rect = GetProperty(InvalidationRect());
8666 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", 8667); MOZ_PretendNoReturn
(); } } while (0)
8667 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", 8667); MOZ_PretendNoReturn
(); } } while (0)
;
8668 aRect = *rect;
8669 } else {
8670 aRect.SetEmpty();
8671 }
8672 return true;
8673}
8674
8675void nsIFrame::SchedulePaint(PaintType aType, bool aFrameChanged) {
8676 if (PresShell()->IsPaintingSuppressed()) {
8677 // We can't have any display items yet, and when we unsuppress we will
8678 // invalidate the root frame.
8679 return;
8680 }
8681 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
8682 InvalidateRenderingObservers(displayRoot, this, aFrameChanged);
8683 SchedulePaintInternal(displayRoot, this, aType);
8684}
8685
8686void nsIFrame::SchedulePaintWithoutInvalidatingObservers(PaintType aType) {
8687 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
8688 SchedulePaintInternal(displayRoot, this, aType);
8689}
8690
8691void nsIFrame::InvalidateLayer(DisplayItemType aDisplayItemKey,
8692 const nsIntRect* aDamageRect,
8693 const nsRect* aFrameDamageRect,
8694 uint32_t aFlags /* = 0 */) {
8695 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", 8695); MOZ_PretendNoReturn
(); } } while (0)
;
8696
8697 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
8698 InvalidateRenderingObservers(displayRoot, this, false);
8699
8700 // Check if frame supports WebRender's async update
8701 if ((aFlags & UPDATE_IS_ASYNC) &&
8702 WebRenderUserData::SupportsAsyncUpdate(this)) {
8703 // WebRender does not use layer, then return nullptr.
8704 return;
8705 }
8706
8707 if (aFrameDamageRect && aFrameDamageRect->IsEmpty()) {
8708 return;
8709 }
8710
8711 // In the bug 930056, dialer app startup but not shown on the
8712 // screen because sometimes we don't have any retainned data
8713 // for remote type displayitem and thus Repaint event is not
8714 // triggered. So, always invalidate in this case.
8715 DisplayItemType displayItemKey = aDisplayItemKey;
8716 if (aDisplayItemKey == DisplayItemType::TYPE_REMOTE) {
8717 displayItemKey = DisplayItemType::TYPE_ZERO;
8718 }
8719
8720 if (aFrameDamageRect) {
8721 InvalidateFrameWithRect(*aFrameDamageRect,
8722 static_cast<uint32_t>(displayItemKey));
8723 } else {
8724 InvalidateFrame(static_cast<uint32_t>(displayItemKey));
8725 }
8726}
8727
8728static nsRect ComputeEffectsRect(nsIFrame* aFrame, const nsRect& aOverflowRect,
8729 const nsSize& aNewSize) {
8730 nsRect r = aOverflowRect;
8731
8732 if (aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
8733 // For SVG frames, we only need to account for filters.
8734 // TODO: We could also take account of clipPath and mask to reduce the
8735 // ink overflow, but that's not essential.
8736 if (aFrame->StyleEffects()->HasFilters()) {
8737 aFrame->SetOrUpdateDeletableProperty(nsIFrame::PreEffectsBBoxProperty(),
8738 r);
8739 r = SVGUtils::GetPostFilterInkOverflowRect(aFrame, aOverflowRect);
8740 }
8741 return r;
8742 }
8743
8744 // box-shadow
8745 r.UnionRect(r, nsLayoutUtils::GetBoxShadowRectForFrame(aFrame, aNewSize));
8746
8747 // border-image-outset.
8748 // We need to include border-image-outset because it can cause the
8749 // border image to be drawn beyond the border box.
8750
8751 // (1) It's important we not check whether there's a border-image
8752 // since the style hint for a change in border image doesn't cause
8753 // reflow, and that's probably more important than optimizing the
8754 // overflow areas for the silly case of border-image-outset without
8755 // border-image
8756 // (2) It's important that we not check whether the border-image
8757 // is actually loaded, since that would require us to reflow when
8758 // the image loads.
8759 const nsStyleBorder* styleBorder = aFrame->StyleBorder();
8760 nsMargin outsetMargin = styleBorder->GetImageOutset();
8761
8762 if (outsetMargin != nsMargin(0, 0, 0, 0)) {
8763 nsRect outsetRect(nsPoint(0, 0), aNewSize);
8764 outsetRect.Inflate(outsetMargin);
8765 r.UnionRect(r, outsetRect);
8766 }
8767
8768 // Note that we don't remove the outlineInnerRect if a frame loses outline
8769 // style. That would require an extra property lookup for every frame,
8770 // or a new frame state bit to track whether a property had been stored,
8771 // or something like that. It's not worth doing that here. At most it's
8772 // only one heap-allocated rect per frame and it will be cleaned up when
8773 // the frame dies.
8774
8775 if (SVGIntegrationUtils::UsingOverflowAffectingEffects(aFrame)) {
8776 aFrame->SetOrUpdateDeletableProperty(nsIFrame::PreEffectsBBoxProperty(), r);
8777 r = SVGIntegrationUtils::ComputePostEffectsInkOverflowRect(aFrame, r);
8778 }
8779
8780 return r;
8781}
8782
8783void nsIFrame::SetPosition(const nsPoint& aPt) {
8784 if (mRect.TopLeft() == aPt) {
8785 return;
8786 }
8787 mRect.MoveTo(aPt);
8788 MarkNeedsDisplayItemRebuild();
8789}
8790
8791void nsIFrame::MovePositionBy(const nsPoint& aTranslation) {
8792 nsPoint position = GetNormalPosition() + aTranslation;
8793
8794 const nsMargin* computedOffsets = nullptr;
8795 if (IsRelativelyOrStickyPositioned()) {
8796 computedOffsets = GetProperty(nsIFrame::ComputedOffsetProperty());
8797 }
8798 ReflowInput::ApplyRelativePositioning(
8799 this, computedOffsets ? *computedOffsets : nsMargin(), &position);
8800 SetPosition(position);
8801}
8802
8803nsRect nsIFrame::GetNormalRect() const {
8804 return nsRect(GetNormalPosition(), GetSize());
8805}
8806
8807nsRect nsIFrame::GetBoundingClientRect() {
8808 return nsLayoutUtils::GetAllInFlowRectsUnion(
8809 this, nsLayoutUtils::GetContainingBlockForClientRect(this),
8810 nsLayoutUtils::GetAllInFlowRectsFlag::AccountForTransforms);
8811}
8812
8813nsPoint nsIFrame::GetPositionIgnoringScrolling() const {
8814 return GetParent() ? GetParent()->GetPositionOfChildIgnoringScrolling(this)
8815 : GetPosition();
8816}
8817
8818nsPoint nsIFrame::GetPositionIgnoringScrollingAndSticky() const {
8819 if (IsStickyPositioned()) {
8820 if (const auto* ssc = StickyScrollContainer::GetForFrame(this)) {
8821 return GetNormalPosition() +
8822 ssc->ComputeTranslationIgnoringScrolling(this);
8823 }
8824 }
8825 return GetPositionIgnoringScrolling();
8826}
8827
8828nsRect nsIFrame::GetOverflowRect(OverflowType aType) const {
8829 // Note that in some cases the overflow area might not have been
8830 // updated (yet) to reflect any outline set on the frame or the area
8831 // of child frames. That's OK because any reflow that updates these
8832 // areas will invalidate the appropriate area, so any (mis)uses of
8833 // this method will be fixed up.
8834
8835 if (mOverflow.mType == OverflowStorageType::Large) {
8836 // there is an overflow rect, and it's not stored as deltas but as
8837 // a separately-allocated rect
8838 return GetOverflowAreasProperty()->Overflow(aType);
8839 }
8840
8841 if (aType == OverflowType::Ink &&
8842 mOverflow.mType != OverflowStorageType::None) {
8843 return InkOverflowFromDeltas();
8844 }
8845
8846 return GetRectRelativeToSelf();
8847}
8848
8849OverflowAreas nsIFrame::GetOverflowAreas() const {
8850 if (mOverflow.mType == OverflowStorageType::Large) {
8851 // there is an overflow rect, and it's not stored as deltas but as
8852 // a separately-allocated rect
8853 return *GetOverflowAreasProperty();
8854 }
8855
8856 return OverflowAreas(InkOverflowFromDeltas(),
8857 nsRect(nsPoint(0, 0), GetSize()));
8858}
8859
8860OverflowAreas nsIFrame::GetOverflowAreasRelativeToSelf() const {
8861 if (IsTransformed()) {
8862 if (OverflowAreas* preTransformOverflows =
8863 GetProperty(PreTransformOverflowAreasProperty())) {
8864 return *preTransformOverflows;
8865 }
8866 }
8867 return GetOverflowAreas();
8868}
8869
8870OverflowAreas nsIFrame::GetOverflowAreasRelativeToParent() const {
8871 return GetOverflowAreas() + GetPosition();
8872}
8873
8874OverflowAreas nsIFrame::GetActualAndNormalOverflowAreasRelativeToParent()
8875 const {
8876 if (MOZ_UNLIKELY(IsAbsolutelyPositioned())(__builtin_expect(!!(IsAbsolutelyPositioned()), 0))) {
8877 const auto* referenceData = GetProperty(AnchorPosReferences());
8878 if (!referenceData || !referenceData->mRememberedScrollOffset ||
8879 referenceData->mDefaultScrollShift == nsPoint()) {
8880 return GetOverflowAreasRelativeToParent();
8881 }
8882 // Including the default scroll shift in the scrollable overflow would
8883 // change the scroll range that produced it. So, like sticky positioned
8884 // frames below, use the position without the default scroll shift for both
8885 // overflow areas, and add the current position only to the ink overflow,
8886 // which is where we paint.
8887 //
8888 // Per spec, "after layout has been performed for abspos, it is additionally
8889 // shifted by the default scroll shift, as if affected by a transform". We
8890 // store the shifts the other way around. UpdateScrollShift() moves the
8891 // frame by subtracting the scroll shift from the frame's current position,
8892 // and stores it back, so GetPosition() is the final position with the
8893 // default scroll shift applied. To get the unshifted position, we undo the
8894 // default scroll shift by adding it back.
8895 // https://drafts.csswg.org/css-anchor-position-1/#default-scroll-shift
8896 const nsPoint unshiftedPosition =
8897 GetPosition() + referenceData->mDefaultScrollShift;
8898 const OverflowAreas overflows = GetOverflowAreas();
8899 OverflowAreas result(overflows.InkOverflow() + GetPosition(), nsRect());
8900 result.UnionWith(overflows + unshiftedPosition);
8901 return result;
8902 }
8903
8904 if (MOZ_LIKELY(!IsRelativelyOrStickyPositioned())(__builtin_expect(!!(!IsRelativelyOrStickyPositioned()), 1))) {
8905 return GetOverflowAreasRelativeToParent();
8906 }
8907
8908 const OverflowAreas overflows = GetOverflowAreas();
8909 OverflowAreas actualAndNormalOverflows = overflows + GetNormalPosition();
8910 if (IsRelativelyPositioned()) {
8911 actualAndNormalOverflows.UnionWith(overflows + GetPosition());
8912 } else {
8913 // For sticky positioned elements, we only use the normal position for the
8914 // scrollable overflow. This avoids circular dependencies between sticky
8915 // positioned elements and their scroll container. (The scroll position and
8916 // the scroll container's size impact the sticky position, so we don't want
8917 // the sticky position to impact them.)
8918 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", 8918); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsStickyPositioned()" ")"); do { MOZ_CrashSequence
(__null, 8918); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
8919 actualAndNormalOverflows.UnionWith(
8920 OverflowAreas(overflows.InkOverflow() + GetPosition(), nsRect()));
8921 }
8922 return actualAndNormalOverflows;
8923}
8924
8925nsRect nsIFrame::ScrollableOverflowRectRelativeToParent() const {
8926 return ScrollableOverflowRect() + GetPosition();
8927}
8928
8929nsRect nsIFrame::InkOverflowRectRelativeToParent() const {
8930 return InkOverflowRect() + GetPosition();
8931}
8932
8933nsRect nsIFrame::ScrollableOverflowRectRelativeToSelf() const {
8934 if (IsTransformed()) {
8935 if (OverflowAreas* preTransformOverflows =
8936 GetProperty(PreTransformOverflowAreasProperty())) {
8937 return preTransformOverflows->ScrollableOverflow();
8938 }
8939 }
8940 return ScrollableOverflowRect();
8941}
8942
8943nsRect nsIFrame::InkOverflowRectRelativeToSelf() const {
8944 if (IsTransformed()) {
8945 if (OverflowAreas* preTransformOverflows =
8946 GetProperty(PreTransformOverflowAreasProperty())) {
8947 return preTransformOverflows->InkOverflow();
8948 }
8949 }
8950 return InkOverflowRect();
8951}
8952
8953nsRect nsIFrame::PreEffectsInkOverflowRect() const {
8954 nsRect* r = GetProperty(nsIFrame::PreEffectsBBoxProperty());
8955 return r ? *r : InkOverflowRectRelativeToSelf();
8956}
8957
8958bool nsIFrame::UpdateOverflow() {
8959 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", 8960); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "FrameMaintainsOverflow()" ") (" "Non-display SVG do not maintain ink overflow rects"
")"); do { MOZ_CrashSequence(__null, 8960); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
8960 "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", 8960); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "FrameMaintainsOverflow()" ") (" "Non-display SVG do not maintain ink overflow rects"
")"); do { MOZ_CrashSequence(__null, 8960); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
8961
8962 nsRect rect(nsPoint(0, 0), GetSize());
8963 OverflowAreas overflowAreas(rect, rect);
8964
8965 if (!ComputeCustomOverflow(overflowAreas)) {
8966 // If updating overflow wasn't supported by this frame, then it should
8967 // have scheduled any necessary reflows. We can return false to say nothing
8968 // changed, and wait for reflow to correct it.
8969 return false;
8970 }
8971
8972 UnionChildOverflow(overflowAreas);
8973
8974 if (FinishAndStoreOverflow(overflowAreas, GetSize())) {
8975 return true;
8976 }
8977
8978 // Frames that combine their 3d transform with their ancestors
8979 // only compute a pre-transform overflow rect, and then contribute
8980 // to the normal overflow rect of the preserve-3d root. Always return
8981 // true here so that we propagate changes up to the root for final
8982 // calculation.
8983 return Combines3DTransformWithAncestors();
8984}
8985
8986/* virtual */
8987bool nsIFrame::ComputeCustomOverflow(OverflowAreas& aOverflowAreas) {
8988 return true;
8989}
8990
8991bool nsIFrame::DoesClipChildrenInBothAxes() const {
8992 if (IsScrollContainerOrSubclass()) {
8993 return true;
8994 }
8995 const nsStyleDisplay* display = StyleDisplay();
8996 if (display->IsContainPaint() && SupportsContainLayoutAndPaint()) {
8997 return true;
8998 }
8999 return display->mOverflowX == StyleOverflow::Clip &&
9000 display->mOverflowY == StyleOverflow::Clip;
9001}
9002
9003/* virtual */
9004void nsIFrame::UnionChildOverflow(OverflowAreas& aOverflowAreas,
9005 bool aAsIfScrolled) {
9006 if (aAsIfScrolled || !DoesClipChildrenInBothAxes()) {
9007 nsLayoutUtils::UnionChildOverflow(this, aOverflowAreas);
9008 }
9009}
9010
9011// Return true if this form control element's preferred size property (but not
9012// percentage max size property) contains a percentage value that should be
9013// resolved against zero when calculating its min-content contribution in the
9014// corresponding axis.
9015//
9016// For proper replaced elements, the percentage value in both their max size
9017// property or preferred size property should be resolved against zero. This is
9018// handled in IsPercentageResolvedAgainstZero().
9019inline static bool FormControlShrinksForPercentSize(const nsIFrame* aFrame) {
9020 if (!aFrame->IsReplaced()) {
9021 // Quick test to reject most frames.
9022 return false;
9023 }
9024
9025 switch (aFrame->Type()) {
9026 case LayoutFrameType::Progress:
9027 case LayoutFrameType::Range:
9028 case LayoutFrameType::TextInput:
9029 case LayoutFrameType::ColorControl:
9030 case LayoutFrameType::ComboboxControl:
9031 case LayoutFrameType::ListControl:
9032 case LayoutFrameType::CheckboxRadio:
9033 case LayoutFrameType::FileControl:
9034 case LayoutFrameType::ImageControl:
9035 return true;
9036 default:
9037 // True buttons (<button>, backed by block/grid/flex frame) and most
9038 // button-flavored <inputs> (those backed by InputButtonControlFrame)
9039 // don't have this shrinking behavior. But color-inputs and comboboxes do;
9040 // and both of those derive from ButtonControlFrame. So: we can't easily
9041 // use do_QueryFrame to differentiate the buttons-that-do vs. the
9042 // buttons-that-don't. So we explicitly list the buttons-that-do by
9043 // LayoutFrameType above, and the others fall into this catch-all.
9044 return false;
9045 }
9046}
9047
9048bool nsIFrame::IsPercentageResolvedAgainstZero(
9049 const StyleSize& aStyleSize, const StyleMaxSize& aStyleMaxSize) const {
9050 const bool sizeHasPercent = aStyleSize.HasPercent();
9051 return ((sizeHasPercent || aStyleMaxSize.HasPercent()) &&
9052 HasReplacedSizing()) ||
9053 (sizeHasPercent && FormControlShrinksForPercentSize(this));
9054}
9055
9056// Summary of the Cyclic-Percentage Intrinsic Size Contribution Rules:
9057//
9058// Element Type | Replaced | Non-replaced
9059// Contribution Type | min-content max-content | min-content max-content
9060// ---------------------------------------------------------------------------
9061// min size | zero zero | zero zero
9062// max & preferred size | zero initial | initial initial
9063//
9064// https://drafts.csswg.org/css-sizing-3/#cyclic-percentage-contribution
9065bool nsIFrame::IsPercentageResolvedAgainstZero(const LengthPercentage& aSize,
9066 SizeProperty aProperty) const {
9067 // Early return to avoid calling the virtual function, IsFrameOfType().
9068 if (aProperty == SizeProperty::MinSize) {
9069 return true;
9070 }
9071
9072 const bool hasPercentOnReplaced = aSize.HasPercent() && HasReplacedSizing();
9073 if (aProperty == SizeProperty::MaxSize) {
9074 return hasPercentOnReplaced;
9075 }
9076
9077 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"
, 9077); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aProperty == SizeProperty::Size"
")"); do { MOZ_CrashSequence(__null, 9077); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9078 return hasPercentOnReplaced ||
9079 (aSize.HasPercent() && FormControlShrinksForPercentSize(this));
9080}
9081
9082bool nsIFrame::IsBlockWrapper() const {
9083 auto pseudoType = Style()->GetPseudoType();
9084 return pseudoType == PseudoStyleType::MozBlockInsideInlineWrapper ||
9085 pseudoType == PseudoStyleType::MozCellContent ||
9086 pseudoType == PseudoStyleType::MozColumnSpanWrapper;
9087}
9088
9089bool nsIFrame::IsBlockFrameOrSubclass() const {
9090 const nsBlockFrame* thisAsBlock = do_QueryFrame(this);
9091 return !!thisAsBlock;
9092}
9093
9094bool nsIFrame::IsInlineFrameOrSubclass() const {
9095 const nsInlineFrame* asInline = do_QueryFrame(this);
9096 return !!asInline;
9097}
9098
9099bool nsIFrame::IsImageFrameOrSubclass() const {
9100 const nsImageFrame* asImage = do_QueryFrame(this);
9101 return !!asImage;
9102}
9103
9104// Unlike its neighbours this must not use do_QueryFrame(), because
9105// IMPL_FAST_QUERYFRAME routes do_QueryFrame<ScrollContainerFrame> through here.
9106bool nsIFrame::IsScrollContainerOrSubclass() const {
9107 const bool result =
9108 IsScrollContainerFrame() || IsListControlFrame() || IsTextInputFrame();
9109 MOZ_ASSERT(result == !!QueryFrame(ScrollContainerFrame::kFrameIID))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(result == !!QueryFrame(ScrollContainerFrame::kFrameIID
))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(result == !!QueryFrame(ScrollContainerFrame::kFrameIID
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("result == !!QueryFrame(ScrollContainerFrame::kFrameIID)", "./../../../layout/generic/nsIFrame.cpp"
, 9109); AnnotateMozCrashReason("MOZ_ASSERT" "(" "result == !!QueryFrame(ScrollContainerFrame::kFrameIID)"
")"); do { MOZ_CrashSequence(__null, 9109); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9110 return result;
9111}
9112
9113bool nsIFrame::IsSubgrid() const {
9114 return IsGridContainerFrame() &&
9115 static_cast<const nsGridContainerFrame*>(this)->IsSubgrid();
9116}
9117
9118static nsIFrame* GetNearestBlockContainer(nsIFrame* frame) {
9119 while (!frame->IsBlockContainer()) {
9120 frame = frame->GetParent();
9121 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", 9123); MOZ_PretendNoReturn
(); } } while (0)
9122 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", 9123); MOZ_PretendNoReturn
(); } } while (0)
9123 "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", 9123); MOZ_PretendNoReturn
(); } } while (0)
;
9124 }
9125 return frame;
9126}
9127
9128bool nsIFrame::IsBlockContainer() const {
9129 // The block wrappers we use to wrap blocks inside inlines aren't
9130 // described in the CSS spec. We need to make them not be containing
9131 // blocks.
9132 // Since the parent of such a block is either a normal block or
9133 // another such pseudo, this shouldn't cause anything bad to happen.
9134 // Also the anonymous blocks inside table cells are not containing blocks.
9135 //
9136 // If we ever start skipping table row groups from being containing blocks,
9137 // you need to remove the StickyScrollContainer hack referencing bug 1421660.
9138 // Table rows are not containing blocks either
9139 return !IsLineParticipant() && !IsBlockWrapper() && !IsTableRowFrame();
9140}
9141
9142nsIFrame* nsIFrame::GetContainingBlock(
9143 uint32_t aFlags, const nsStyleDisplay* aStyleDisplay) const {
9144 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"
, 9144); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aStyleDisplay == StyleDisplay()"
")"); do { MOZ_CrashSequence(__null, 9144); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9145
9146 // Keep this in sync with MightBeContainingBlockFor in ReflowInput.cpp.
9147
9148 if (!GetParent()) {
9149 return nullptr;
9150 }
9151 // MathML frames might have absolute positioning style, but they would
9152 // still be in-flow. So we have to check to make sure that the frame
9153 // is really out-of-flow too.
9154 nsIFrame* f;
9155 if (IsAbsolutelyPositioned(aStyleDisplay)) {
9156 f = GetParent(); // the parent is always the containing block
9157 } else {
9158 f = GetNearestBlockContainer(GetParent());
9159 }
9160
9161 if (aFlags & SKIP_SCROLLED_FRAME && f &&
9162 f->Style()->GetPseudoType() == PseudoStyleType::MozScrolledContent) {
9163 f = f->GetParent();
9164 }
9165 return f;
9166}
9167
9168#ifdef DEBUG_FRAME_DUMP1
9169
9170Maybe<uint32_t> nsIFrame::ContentIndexInContainer(const nsIFrame* aFrame) {
9171 if (nsIContent* content = aFrame->GetContent()) {
9172 return content->ComputeIndexInParentContent();
9173 }
9174 return Nothing();
9175}
9176
9177nsAutoCString nsIFrame::ListTag(bool aListOnlyDeterministic) const {
9178 nsAutoString tmp;
9179 GetFrameName(tmp);
9180
9181 nsAutoCString tag;
9182 tag += NS_ConvertUTF16toUTF8(tmp);
9183 ListPtr(tag, aListOnlyDeterministic, this, "@");
9184 return tag;
9185}
9186
9187std::string nsIFrame::ConvertToString(const LogicalRect& aRect,
9188 const WritingMode aWM, ListFlags aFlags) {
9189 if (aFlags.contains(ListFlag::DisplayInCSSPixels)) {
9190 // Abuse CSSRect to store all LogicalRect's dimensions in CSS pixels.
9191 return ToString(mozilla::CSSRect(CSSPixel::FromAppUnits(aRect.IStart(aWM)),
9192 CSSPixel::FromAppUnits(aRect.BStart(aWM)),
9193 CSSPixel::FromAppUnits(aRect.ISize(aWM)),
9194 CSSPixel::FromAppUnits(aRect.BSize(aWM))));
9195 }
9196 return ToString(aRect);
9197}
9198
9199std::string nsIFrame::ConvertToString(const LogicalSize& aSize,
9200 const WritingMode aWM, ListFlags aFlags) {
9201 if (aFlags.contains(ListFlag::DisplayInCSSPixels)) {
9202 // Abuse CSSSize to store all LogicalSize's dimensions in CSS pixels.
9203 return ToString(CSSSize(CSSPixel::FromAppUnits(aSize.ISize(aWM)),
9204 CSSPixel::FromAppUnits(aSize.BSize(aWM))));
9205 }
9206 return ToString(aSize);
9207}
9208
9209// Debugging
9210void nsIFrame::ListGeneric(nsACString& aTo, const char* aPrefix,
9211 ListFlags aFlags) const {
9212 aTo += aPrefix;
9213 const bool onlyDeterministic =
9214 aFlags.contains(ListFlag::OnlyListDeterministicInfo);
9215 aTo += ListTag(onlyDeterministic);
9216 if (!onlyDeterministic) {
9217 if (GetParent()) {
9218 aTo += nsPrintfCString(" parent=%p", static_cast<void*>(GetParent()));
9219 }
9220 if (GetNextSibling()) {
9221 aTo += nsPrintfCString(" next=%p", static_cast<void*>(GetNextSibling()));
9222 }
9223 }
9224 if (GetPrevContinuation()) {
9225 bool fluid = GetPrevInFlow() == GetPrevContinuation();
9226 aTo += nsPrintfCString(" prev-%s", fluid ? "in-flow" : "continuation");
9227 ListPtr(aTo, aFlags, GetPrevContinuation());
9228 }
9229 if (GetNextContinuation()) {
9230 bool fluid = GetNextInFlow() == GetNextContinuation();
9231 aTo += nsPrintfCString(" next-%s", fluid ? "in-flow" : "continuation");
9232 ListPtr(aTo, aFlags, GetNextContinuation());
9233 }
9234 if (const nsAtom* const autoPageValue =
9235 GetProperty(AutoPageValueProperty())) {
9236 aTo += " AutoPage=";
9237 aTo += nsAtomCString(autoPageValue);
9238 }
9239 if (const nsIFrame::PageValues* const pageValues =
9240 GetProperty(PageValuesProperty())) {
9241 aTo += " PageValues={";
9242 if (pageValues->mStartPageValue) {
9243 aTo += nsAtomCString(pageValues->mStartPageValue);
9244 } else {
9245 aTo += "<null>";
9246 }
9247 aTo += ", ";
9248 if (pageValues->mEndPageValue) {
9249 aTo += nsAtomCString(pageValues->mEndPageValue);
9250 } else {
9251 aTo += "<null>";
9252 }
9253 aTo += "}";
9254 }
9255 void* IBsibling = GetProperty(IBSplitSibling());
9256 if (IBsibling) {
9257 aTo += nsPrintfCString(" IBSplitSibling");
9258 ListPtr(aTo, aFlags, IBsibling);
9259 }
9260 void* IBprevsibling = GetProperty(IBSplitPrevSibling());
9261 if (IBprevsibling) {
9262 aTo += nsPrintfCString(" IBSplitPrevSibling");
9263 ListPtr(aTo, aFlags, IBprevsibling);
9264 }
9265 if (nsLayoutUtils::FontSizeInflationEnabled(PresContext())) {
9266 if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT)) {
9267 aTo += nsPrintfCString(" FFR");
9268 if (nsFontInflationData* data =
9269 nsFontInflationData::FindFontInflationDataFor(this)) {
9270 aTo += nsPrintfCString(
9271 ",enabled=%s,UIS=%s", data->InflationEnabled() ? "yes" : "no",
9272 ConvertToString(data->UsableISize(), aFlags).c_str());
9273 }
9274 }
9275 if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER)) {
9276 aTo += nsPrintfCString(" FIC");
9277 }
9278 aTo += nsPrintfCString(" FI=%f", nsLayoutUtils::FontSizeInflationFor(this));
9279 }
9280 aTo += nsPrintfCString(" %s", ConvertToString(mRect, aFlags).c_str());
9281
9282 mozilla::WritingMode wm = GetWritingMode();
9283 if (wm.IsVertical() || wm.IsBidiRTL()) {
9284 aTo +=
9285 nsPrintfCString(" wm=%s logical-size=(%s)", ToString(wm).c_str(),
9286 ConvertToString(GetLogicalSize(), wm, aFlags).c_str());
9287 }
9288
9289 nsIFrame* parent = GetParent();
9290 if (parent) {
9291 WritingMode pWM = parent->GetWritingMode();
9292 if (pWM.IsVertical() || pWM.IsBidiRTL()) {
9293 nsSize containerSize = parent->mRect.Size();
9294 LogicalRect lr(pWM, mRect, containerSize);
9295 aTo += nsPrintfCString(" parent-wm=%s cs=(%s) logical-rect=%s",
9296 ToString(pWM).c_str(),
9297 ConvertToString(containerSize, aFlags).c_str(),
9298 ConvertToString(lr, pWM, aFlags).c_str());
9299 }
9300 }
9301 nsIFrame* f = const_cast<nsIFrame*>(this);
9302 if (f->HasOverflowAreas()) {
9303 nsRect io = f->InkOverflowRect();
9304 if (!io.IsEqualEdges(mRect)) {
9305 aTo += nsPrintfCString(" ink-overflow=%s",
9306 ConvertToString(io, aFlags).c_str());
9307 }
9308 nsRect so = f->ScrollableOverflowRect();
9309 if (!so.IsEqualEdges(mRect)) {
9310 aTo += nsPrintfCString(" scr-overflow=%s",
9311 ConvertToString(so, aFlags).c_str());
9312 }
9313 }
9314 if (OverflowAreas* preTransformOverflows =
9315 f->GetProperty(PreTransformOverflowAreasProperty())) {
9316 nsRect io = preTransformOverflows->InkOverflow();
9317 if (!io.IsEqualEdges(mRect) &&
9318 (!f->HasOverflowAreas() || !io.IsEqualEdges(f->InkOverflowRect()))) {
9319 aTo += nsPrintfCString(" pre-transform-ink-overflow=%s",
9320 ConvertToString(io, aFlags).c_str());
9321 }
9322 nsRect so = preTransformOverflows->ScrollableOverflow();
9323 if (!so.IsEqualEdges(mRect) &&
9324 (!f->HasOverflowAreas() ||
9325 !so.IsEqualEdges(f->ScrollableOverflowRect()))) {
9326 aTo += nsPrintfCString(" pre-transform-scr-overflow=%s",
9327 ConvertToString(so, aFlags).c_str());
9328 }
9329 }
9330 bool hasNormalPosition;
9331 nsPoint normalPosition = GetNormalPosition(&hasNormalPosition);
9332 if (hasNormalPosition) {
9333 aTo += nsPrintfCString(" normal-position=%s",
9334 ConvertToString(normalPosition, aFlags).c_str());
9335 }
9336 if (HasProperty(BidiDataProperty())) {
9337 FrameBidiData bidi = GetBidiData();
9338 aTo += nsPrintfCString(" bidi(%d,%d,%d)", bidi.baseLevel.Value(),
9339 bidi.embeddingLevel.Value(),
9340 bidi.precedingControl.Value());
9341 }
9342 if (IsTransformed()) {
9343 aTo += nsPrintfCString(" transformed");
9344 }
9345 if (ChildrenHavePerspective()) {
9346 aTo += nsPrintfCString(" perspective");
9347 }
9348 if (Extend3DContext()) {
9349 aTo += nsPrintfCString(" extend-3d");
9350 }
9351 if (Combines3DTransformWithAncestors()) {
9352 aTo += nsPrintfCString(" combines-3d-transform-with-ancestors");
9353 }
9354 if (mContent) {
9355 if (!onlyDeterministic) {
9356 aTo += nsPrintfCString(" [content=%p]", static_cast<void*>(mContent));
9357 }
9358 if (IsPrimaryFrame() && DisplayPortUtils::HasDisplayPort(mContent)) {
9359 // Anon boxes and continuations point to the same content - Just print on
9360 // primary frame.
9361 aTo += "[displayport]"_ns;
9362 }
9363 }
9364 if (!onlyDeterministic) {
9365 aTo += nsPrintfCString("[cs=%p]", static_cast<void*>(mComputedStyle));
9366 }
9367 if (mComputedStyle) {
9368 const auto pseudoType = mComputedStyle->GetPseudoType();
9369 const auto pseudoTypeStr = ToString(pseudoType);
9370 if (!pseudoTypeStr.empty()) {
9371 aTo += nsPrintfCString("[%s]", pseudoTypeStr.c_str());
9372 }
9373 }
9374
9375 auto contentVisibility = StyleDisplay()->ContentVisibility(*this);
9376 if (contentVisibility != StyleContentVisibility::Visible) {
9377 aTo += nsPrintfCString(" [content-visibility=");
9378 if (contentVisibility == StyleContentVisibility::Auto) {
9379 aTo += "auto, "_ns;
9380 } else if (contentVisibility == StyleContentVisibility::Hidden) {
9381 aTo += "hidden, "_ns;
9382 }
9383
9384 if (HidesContent()) {
9385 aTo += "HidesContent=hidden"_ns;
9386 } else {
9387 aTo += "HidesContent=visibile"_ns;
9388 }
9389 aTo += "]";
9390 }
9391
9392 if (IsFrameModified()) {
9393 aTo += nsPrintfCString(" modified");
9394 }
9395
9396 if (HasModifiedDescendants()) {
9397 aTo += nsPrintfCString(" has-modified-descendants");
9398 }
9399}
9400
9401void nsIFrame::List(FILE* out, const char* aPrefix, ListFlags aFlags) const {
9402 nsCString str;
9403 ListGeneric(str, aPrefix, aFlags);
9404 fprintf_stderr(out, "%s\n", str.get());
9405}
9406
9407void nsIFrame::ListTextRuns(FILE* out) const {
9408 nsTHashSet<const void*> seen;
9409 ListTextRuns(out, seen);
9410}
9411
9412void nsIFrame::ListTextRuns(FILE* out, nsTHashSet<const void*>& aSeen) const {
9413 for (const auto& childList : ChildLists()) {
9414 for (const nsIFrame* kid : childList.mList) {
9415 kid->ListTextRuns(out, aSeen);
9416 }
9417 }
9418}
9419
9420void nsIFrame::ListMatchedRules(FILE* out, const char* aPrefix) const {
9421 AutoTArray<StyleMatchingDeclarationBlock, 8> decls;
9422 Servo_ComputedValues_GetMatchingDeclarations(
9423 Style(), /* with_starting_style = */ false, &decls);
9424 for (const StyleMatchingDeclarationBlock& block : decls) {
9425 nsAutoCString ruleText;
9426 Servo_DeclarationBlock_GetCssText(block.block, &ruleText);
9427 fprintf_stderr(out, "%s%s\n", aPrefix, ruleText.get());
9428 }
9429}
9430
9431void nsIFrame::ListWithMatchedRules(FILE* out, const char* aPrefix) const {
9432 fprintf_stderr(out, "%s%s\n", aPrefix, ListTag().get());
9433
9434 nsCString rulePrefix;
9435 rulePrefix += aPrefix;
9436 rulePrefix += " ";
9437 ListMatchedRules(out, rulePrefix.get());
9438}
9439
9440nsresult nsIFrame::GetFrameName(nsAString& aResult) const {
9441 return MakeFrameName(u"Frame"_ns, aResult);
9442}
9443
9444nsresult nsIFrame::MakeFrameName(const nsAString& aType,
9445 nsAString& aResult) const {
9446 aResult = aType;
9447 if (mContent && !mContent->IsText()) {
9448 nsAutoString buf;
9449 mContent->NodeInfo()->NameAtom()->ToString(buf);
9450 if (nsAtom* id = mContent->GetID()) {
9451 buf.AppendLiteral(" id=");
9452 buf.Append(nsDependentAtomString(id));
9453 }
9454 if (IsSubDocumentFrame()) {
9455 nsAutoString src;
9456 mContent->AsElement()->GetAttr(nsGkAtoms::src, src);
9457 buf.AppendLiteral(" src=");
9458 buf.Append(src);
9459 }
9460 aResult.Append('(');
9461 aResult.Append(buf);
9462 aResult.Append(')');
9463 }
9464 aResult.Append('(');
9465 Maybe<uint32_t> index = ContentIndexInContainer(this);
9466 if (index.isSome()) {
9467 aResult.AppendInt(*index);
9468 } else {
9469 aResult.AppendInt(-1);
9470 }
9471 aResult.Append(')');
9472 return NS_OK;
9473}
9474
9475void nsIFrame::DumpFrameTree() const { DumpFrameTree(false); }
9476
9477void nsIFrame::DumpFrameTree(bool aListOnlyDeterministic) const {
9478 ListFlags flags;
9479 if (aListOnlyDeterministic) {
9480 flags += ListFlag::OnlyListDeterministicInfo;
9481 }
9482 PresShell()->GetRootFrame()->List(stderrstderr, "", flags);
9483}
9484
9485void nsIFrame::DumpFrameTreeInCSSPixels() const {
9486 DumpFrameTreeInCSSPixels(false);
9487}
9488
9489void nsIFrame::DumpFrameTreeInCSSPixels(bool aListOnlyDeterministic) const {
9490 ListFlags flags{ListFlag::DisplayInCSSPixels};
9491 if (aListOnlyDeterministic) {
9492 flags += ListFlag::OnlyListDeterministicInfo;
9493 }
9494 PresShell()->GetRootFrame()->List(stderrstderr, "", flags);
9495}
9496
9497void nsIFrame::DumpFrameTreeLimited() const { List(stderrstderr); }
9498void nsIFrame::DumpFrameTreeLimitedInCSSPixels() const {
9499 List(stderrstderr, "", ListFlag::DisplayInCSSPixels);
9500}
9501
9502#endif
9503
9504// FIXME(emilio, bug 1633685): We should ideally figure out how to properly
9505// restyle replicated fixed pos frames... We seem to assume everywhere that they
9506// can't get restyled at the moment...
9507bool nsIFrame::IsInReplicatedFixedPosTree() const {
9508 if (!PresContext()->IsPaginated()) {
9509 return false;
9510 }
9511 for (const nsIFrame* f = this; f; f = f->GetParent()) {
9512 if (f->StyleDisplay()->mPosition == StylePositionProperty::Fixed &&
9513 nsLayoutUtils::IsReallyFixedPos(f)) {
9514 const nsIFrame* pageContent = f->GetParent();
9515 return pageContent->GetPrevInFlow() || pageContent->GetNextInFlow();
9516 }
9517 }
9518 return false;
9519}
9520
9521bool nsIFrame::IsVisibleForPainting() const {
9522 return StyleVisibility()->IsVisible();
9523}
9524
9525bool nsIFrame::IsVisibleOrCollapsedForPainting() const {
9526 return StyleVisibility()->IsVisibleOrCollapsed();
9527}
9528
9529/* virtual */
9530bool nsIFrame::IsEmpty() {
9531 return IsHiddenByContentVisibilityOfInFlowParentForLayout();
9532}
9533
9534bool nsIFrame::CachedIsEmpty() {
9535 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", 9538); MOZ_PretendNoReturn
(); } } while (0)
9536 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", 9538); MOZ_PretendNoReturn
(); } } while (0)
9537 "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", 9538); MOZ_PretendNoReturn
(); } } while (0)
9538 "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", 9538); MOZ_PretendNoReturn
(); } } while (0)
;
9539 return IsEmpty();
9540}
9541
9542/* virtual */
9543bool nsIFrame::IsSelfEmpty() {
9544 return IsHiddenByContentVisibilityOfInFlowParentForLayout();
9545}
9546
9547nsISelectionController* nsIFrame::GetSelectionController() const {
9548 if (nsTextControlFrame* const tcf = GetContainingTextControlFrame()) {
9549 return tcf->ControlElement()->GetSelectionController();
9550 }
9551 return static_cast<nsISelectionController*>(PresShell());
9552}
9553
9554int16_t nsIFrame::GetDisplaySelection() const {
9555 nsISelectionController* const selCon = GetSelectionController();
9556 if (MOZ_UNLIKELY(!selCon)(__builtin_expect(!!(!selCon), 0))) {
9557 return nsISelectionController::SELECTION_OFF;
9558 }
9559 int16_t display = nsISelectionController::SELECTION_OFF;
9560 selCon->GetDisplaySelection(&display);
9561 return display;
9562}
9563
9564already_AddRefed<nsFrameSelection> nsIFrame::GetFrameSelection() {
9565 RefPtr<nsFrameSelection> fs =
9566 const_cast<nsFrameSelection*>(GetConstFrameSelection());
9567 return fs.forget();
9568}
9569
9570const nsFrameSelection* nsIFrame::GetConstFrameSelection() const {
9571 if (nsTextControlFrame* tcf = GetContainingTextControlFrame()) {
9572 return tcf->GetOwnedFrameSelection();
9573 }
9574 return PresShell()->ConstFrameSelection();
9575}
9576
9577bool nsIFrame::IsFrameSelected() const {
9578 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"
, 9579); MOZ_PretendNoReturn(); } } while (0)
9579 "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"
, 9579); MOZ_PretendNoReturn(); } } while (0)
;
9580 if (const ShadowRoot* shadowRoot =
9581 GetContent()->GetShadowRootForSelection()) {
9582 return shadowRoot->IsSelected(0, shadowRoot->GetChildCount());
9583 }
9584 return GetContent()->IsSelected(0, GetContent()->GetChildCount());
9585}
9586
9587nsresult nsIFrame::GetPointFromOffset(int32_t inOffset, nsPoint* outPoint) {
9588 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"
, 9588); AnnotateMozCrashReason("MOZ_ASSERT" "(" "outPoint != nullptr"
") (" "Null parameter" ")"); do { MOZ_CrashSequence(__null, 9588
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
9589 nsRect contentRect = GetContentRectRelativeToSelf();
9590 nsPoint pt = contentRect.TopLeft();
9591 if (mContent) {
9592 nsIContent* newContent = mContent->GetParent();
9593 if (newContent) {
9594 const int32_t newOffset = newContent->ComputeIndexOf_Deprecated(mContent);
9595
9596 // Find the direction of the frame from the EmbeddingLevelProperty,
9597 // which is the resolved bidi level set in
9598 // nsBidiPresUtils::ResolveParagraph (odd levels = right-to-left).
9599 // If the embedding level isn't set, just use the CSS direction
9600 // property.
9601 bool hasBidiData;
9602 FrameBidiData bidiData = GetProperty(BidiDataProperty(), &hasBidiData);
9603 bool isRTL = hasBidiData
9604 ? bidiData.embeddingLevel.IsRTL()
9605 : StyleVisibility()->mDirection == StyleDirection::Rtl;
9606 if ((!isRTL && inOffset > newOffset) ||
9607 (isRTL && inOffset <= newOffset)) {
9608 pt = contentRect.TopRight();
9609 }
9610 }
9611 }
9612 *outPoint = pt;
9613 return NS_OK;
9614}
9615
9616nsresult nsIFrame::GetCharacterRectsInRange(int32_t aInOffset, int32_t aLength,
9617 nsTArray<nsRect>& aOutRect) {
9618 /* no text */
9619 return NS_ERROR_FAILURE;
9620}
9621
9622nsresult nsIFrame::GetChildFrameContainingOffset(int32_t inContentOffset,
9623 bool inHint,
9624 int32_t* outFrameContentOffset,
9625 nsIFrame** outChildFrame) {
9626 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"
, 9626); AnnotateMozCrashReason("MOZ_ASSERT" "(" "outChildFrame && outFrameContentOffset"
") (" "Null parameter" ")"); do { MOZ_CrashSequence(__null, 9626
); __attribute__((nomerge)) ::abort(); } while (false); } } while
(false)
;
9627 *outFrameContentOffset = (int32_t)inHint;
9628 // the best frame to reflect any given offset would be a visible frame if
9629 // possible i.e. we are looking for a valid frame to place the blinking caret
9630 nsRect rect = GetRect();
9631 if (!rect.width || !rect.height) {
9632 // if we have a 0 width or height then lets look for another frame that
9633 // possibly has the same content. If we have no frames in flow then just
9634 // let us return 'this' frame
9635 nsIFrame* nextFlow = GetNextInFlow();
9636 if (nextFlow) {
9637 return nextFlow->GetChildFrameContainingOffset(
9638 inContentOffset, inHint, outFrameContentOffset, outChildFrame);
9639 }
9640 }
9641 *outChildFrame = this;
9642 return NS_OK;
9643}
9644
9645//
9646// What I've pieced together about this routine:
9647// Starting with a block frame (from which a line frame can be gotten)
9648// and a line number, drill down and get the first/last selectable
9649// frame on that line, depending on aPos->mDirection.
9650// aOutSideLimit != 0 means ignore aLineStart, instead work from
9651// the end (if > 0) or beginning (if < 0).
9652//
9653static nsresult GetNextPrevLineFromBlockFrame(PeekOffsetStruct* aPos,
9654 nsIFrame* aBlockFrame,
9655 int32_t aLineStart,
9656 int8_t aOutSideLimit) {
9657 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"
, 9657); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aPos" ")");
do { MOZ_CrashSequence(__null, 9657); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9658 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"
, 9658); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aBlockFrame"
")"); do { MOZ_CrashSequence(__null, 9658); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
9659
9660 nsPresContext* pc = aBlockFrame->PresContext();
9661
9662 // magic numbers: aLineStart will be -1 for end of block, 0 will be start of
9663 // block.
9664
9665 aPos->mResultFrame = nullptr;
9666 aPos->mResultContent = nullptr;
9667 aPos->mAttach = aPos->mDirection == eDirNext ? CaretAssociationHint::After
9668 : CaretAssociationHint::Before;
9669
9670 AutoAssertNoDomMutations guard;
9671 nsILineIterator* it = aBlockFrame->GetLineIterator();
9672 if (!it) {
9673 return NS_ERROR_FAILURE;
9674 }
9675 int32_t searchingLine = aLineStart;
9676 int32_t countLines = it->GetNumLines();
9677 if (aOutSideLimit > 0) { // start at end
9678 searchingLine = countLines;
9679 } else if (aOutSideLimit < 0) { // start at beginning
9680 searchingLine = -1; //"next" will be 0
9681 } else if ((aPos->mDirection == eDirPrevious && searchingLine == 0) ||
9682 (aPos->mDirection == eDirNext &&
9683 searchingLine >= (countLines - 1))) {
9684 // Not found.
9685 return NS_ERROR_FAILURE;
9686 }
9687 nsIFrame* resultFrame = nullptr;
9688 nsIFrame* farStoppingFrame = nullptr; // we keep searching until we find a
9689 // "this" frame then we go to next line
9690 nsIFrame* nearStoppingFrame = nullptr; // if we are backing up from edge,
9691 // stop here
9692 bool isBeforeFirstFrame, isAfterLastFrame;
9693 bool found = false;
9694
9695 const bool forceInEditableRegion =
9696 aPos->mOptions.contains(PeekOffsetOption::ForceEditableRegion);
9697 while (!found) {
9698 if (aPos->mDirection == eDirPrevious) {
9699 searchingLine--;
9700 } else {
9701 searchingLine++;
9702 }
9703 if ((aPos->mDirection == eDirPrevious && searchingLine < 0) ||
9704 (aPos->mDirection == eDirNext && searchingLine >= countLines)) {
9705 // we need to jump to new block frame.
9706 return NS_ERROR_FAILURE;
9707 }
9708 {
9709 auto line = it->GetLine(searchingLine).unwrap();
9710 if (!line.mNumFramesOnLine) {
9711 continue;
9712 }
9713 nsIFrame* firstFrame = nullptr;
9714 nsIFrame* lastFrame = nullptr;
9715 nsIFrame* frame = line.mFirstFrameOnLine;
9716 int32_t i = line.mNumFramesOnLine;
9717 do {
9718 // If the caller wants a frame for a inclusive ancestor of the ancestor
9719 // limiter, ignore frames for outside the limiter.
9720 if (aPos->FrameContentIsInAncestorLimiter(frame)) {
9721 if (!firstFrame) {
9722 firstFrame = frame;
9723 }
9724 lastFrame = frame;
9725 }
9726 if (i == 1) {
9727 break;
9728 }
9729 frame = frame->GetNextSibling();
9730 if (!frame) {
9731 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", 9731); MOZ_PretendNoReturn
(); } while (0)
;
9732 return NS_ERROR_FAILURE;
9733 }
9734 } while (--i);
9735 if (!lastFrame) {
9736 // If we're looking for an editable content frame, but all frames in the
9737 // line are not in the specified editing host, return error because we
9738 // must reach the editing host boundary.
9739 return NS_ERROR_FAILURE;
9740 }
9741 // Don't enter into native anonymous subtrees.
9742 if (!lastFrame->ContentIsRootOfNativeAnonymousSubtree()) {
9743 nsIFrame::GetLastLeaf(&lastFrame);
9744 }
9745
9746 if (aPos->mDirection == eDirNext) {
9747 nearStoppingFrame = firstFrame;
9748 farStoppingFrame = lastFrame;
9749 } else {
9750 nearStoppingFrame = lastFrame;
9751 farStoppingFrame = firstFrame;
9752 }
9753 }
9754 nsPoint offset = aBlockFrame->GetOffsetToRootFrame();
9755 nsPoint newDesiredPos =
9756 aPos->mDesiredCaretPos -
9757 offset; // get desired position into blockframe coords
9758 // TODO: nsILineIterator::FindFrameAt should take optional editing host
9759 // parameter and if it's set, it should return the nearest editable frame
9760 // for the editing host when the frame at the desired position is not
9761 // editable.
9762 nsresult rv = it->FindFrameAt(searchingLine, newDesiredPos, &resultFrame,
9763 &isBeforeFirstFrame, &isAfterLastFrame);
9764 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
9765 continue;
9766 }
9767
9768 if (resultFrame) {
9769 // If ancestor limiter is specified and we reached outside content of it,
9770 // return error because we reached its element boundary.
9771 if (!aPos->FrameContentIsInAncestorLimiter(resultFrame)) {
9772 return NS_ERROR_FAILURE;
9773 }
9774 // Check to see if this is ANOTHER blockframe inside the other one that
9775 // we should look inside.
9776 if (resultFrame->CanProvideLineIterator() &&
9777 IsRelevantBlockFrame(resultFrame)) {
9778 aPos->mResultFrame = resultFrame;
9779 return NS_OK;
9780 }
9781 // resultFrame is not a block frame
9782 Maybe<nsFrameIterator> frameIterator;
9783 frameIterator.emplace(
9784 pc, resultFrame, nsFrameIterator::Type::PostOrder,
9785 false, // aVisual
9786 aPos->mOptions.contains(PeekOffsetOption::StopAtScroller),
9787 false, // aFollowOOFs
9788 false // aSkipPopupChecks
9789 );
9790
9791 auto FoundValidFrame = [forceInEditableRegion, aPos](
9792 const nsIFrame::ContentOffsets& aOffsets,
9793 const nsIFrame* aFrame) {
9794 if (!aOffsets.content) {
9795 return false;
9796 }
9797 if (!aFrame->IsSelectable()) {
9798 return false;
9799 }
9800 if (aPos->mAncestorLimiter &&
9801 !aOffsets.content->IsInclusiveDescendantOf(
9802 aPos->mAncestorLimiter)) {
9803 return false;
9804 }
9805 if (forceInEditableRegion && !aOffsets.content->IsEditable()) {
9806 return false;
9807 }
9808 return true;
9809 };
9810
9811 nsIFrame* storeOldResultFrame = resultFrame;
9812 while (!found) {
9813 nsPoint point;
9814 nsRect tempRect = resultFrame->GetRect();
9815 nsPoint offset = resultFrame->GetOffsetToRootFrame();
9816 if (resultFrame->GetWritingMode().IsVertical()) {
9817 point.y = aPos->mDesiredCaretPos.y;
9818 point.x = tempRect.width + offset.x;
9819 } else {
9820 point.y = tempRect.height + offset.y;
9821 point.x = aPos->mDesiredCaretPos.x;
9822 }
9823
9824 if (!resultFrame->IsViewportFrame()) {
9825 nsPoint offset = resultFrame->GetOffsetToRootFrame();
9826 nsIFrame::ContentOffsets offsets =
9827 resultFrame->GetContentOffsetsFromPoint(
9828 point - offset, nsIFrame::IGNORE_NATIVE_ANONYMOUS_SUBTREE);
9829 aPos->mResultContent = offsets.content;
9830 aPos->mContentOffset = offsets.offset;
9831 aPos->mAttach = offsets.associate;
9832 if (FoundValidFrame(offsets, resultFrame)) {
9833 found = true;
9834 break;
9835 }
9836 }
9837
9838 if (aPos->mDirection == eDirPrevious &&
9839 resultFrame == farStoppingFrame) {
9840 break;
9841 }
9842 if (aPos->mDirection == eDirNext && resultFrame == nearStoppingFrame) {
9843 break;
9844 }
9845 // always try previous on THAT line if that fails go the other way
9846 resultFrame = frameIterator->Traverse(/* aForward = */ false);
9847 if (!resultFrame) {
9848 return NS_ERROR_FAILURE;
9849 }
9850 }
9851
9852 if (!found) {
9853 resultFrame = storeOldResultFrame;
9854 frameIterator.reset();
9855 frameIterator.emplace(
9856 pc, resultFrame, nsFrameIterator::Type::Leaf,
9857 false, // aVisual
9858 aPos->mOptions.contains(PeekOffsetOption::StopAtScroller),
9859 false, // aFollowOOFs
9860 false // aSkipPopupChecks
9861 );
9862 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", 9862); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "frameIterator" ")"); do { MOZ_CrashSequence
(__null, 9862); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
9863 }
9864 while (!found) {
9865 nsPoint point = aPos->mDesiredCaretPos;
9866 nsPoint offset = resultFrame->GetOffsetToRootFrame();
9867 nsIFrame::ContentOffsets offsets =
9868 resultFrame->GetContentOffsetsFromPoint(
9869 point - offset, nsIFrame::IGNORE_NATIVE_ANONYMOUS_SUBTREE);
9870 aPos->mResultContent = offsets.content;
9871 aPos->mContentOffset = offsets.offset;
9872 aPos->mAttach = offsets.associate;
9873 if (FoundValidFrame(offsets, resultFrame)) {
9874 found = true;
9875 aPos->mAttach = resultFrame == farStoppingFrame
9876 ? CaretAssociationHint::Before
9877 : CaretAssociationHint::After;
9878 break;
9879 }
9880 if (aPos->mDirection == eDirPrevious &&
9881 resultFrame == nearStoppingFrame) {
9882 break;
9883 }
9884 if (aPos->mDirection == eDirNext && resultFrame == farStoppingFrame) {
9885 break;
9886 }
9887 // previous didnt work now we try "next"
9888 nsIFrame* tempFrame = frameIterator->Traverse(/* aForward = */ true);
9889 if (!tempFrame) {
9890 break;
9891 }
9892 resultFrame = tempFrame;
9893 }
9894 aPos->mResultFrame = resultFrame;
9895 } else {
9896 // we need to jump to new block frame.
9897 aPos->mAmount = eSelectLine;
9898 aPos->mStartOffset = 0;
9899 aPos->mAttach = aPos->mDirection == eDirNext
9900 ? CaretAssociationHint::Before
9901 : CaretAssociationHint::After;
9902 if (aPos->mDirection == eDirPrevious) {
9903 aPos->mStartOffset = -1; // start from end
9904 }
9905 return aBlockFrame->PeekOffset(aPos);
9906 }
9907 }
9908 return NS_OK;
9909}
9910
9911nsIFrame::CaretPosition nsIFrame::GetExtremeCaretPosition(bool aStart,
9912 uint32_t aFlags) {
9913 CaretPosition result;
9914
9915 FrameTarget targetFrame = DrillDownToSelectionFrame(this, !aStart, aFlags);
9916 FrameContentRange range = GetRangeForFrame(targetFrame.frame);
9917 result.mResultContent = range.content;
9918 result.mContentOffset = aStart ? range.start : range.end;
9919 return result;
9920}
9921
9922// If this is a preformatted text frame, see if it ends with a newline
9923static nsContentAndOffset FindLineBreakInText(nsIFrame* aFrame,
9924 nsDirection aDirection) {
9925 nsContentAndOffset result;
9926
9927 if (aFrame->IsGeneratedContentFrame() ||
9928 !aFrame->HasSignificantTerminalNewline()) {
9929 return result;
9930 }
9931
9932 int32_t endOffset = aFrame->GetOffsets().second;
9933 result.mContent = aFrame->GetContent();
9934 result.mOffset = endOffset - (aDirection == eDirPrevious ? 0 : 1);
9935 result.mIsTerminalNewlineInText = true;
9936 return result;
9937}
9938
9939// Find the first (or last) descendant of the given frame
9940// which is either a block-level frame or a BRFrame, or some other kind of break
9941// which stops the line.
9942static nsContentAndOffset FindLineBreakingFrame(nsIFrame* aFrame,
9943 nsDirection aDirection) {
9944 nsContentAndOffset result;
9945
9946 if (aFrame->IsGeneratedContentFrame()) {
9947 return result;
9948 }
9949
9950 // Treat form controls and other replaced inline level elements as inline
9951 // leaves.
9952 if (aFrame->IsReplaced() && aFrame->IsInlineOutside() &&
9953 !aFrame->IsBrFrame() && !aFrame->IsTextFrame()) {
9954 return result;
9955 }
9956
9957 // Check the frame itself
9958 // Fall through block-in-inline split frames because their mContent is
9959 // the content of the inline frames they were created from. The
9960 // first/last child of such frames is the real block frame we're
9961 // looking for.
9962 if ((IsRelevantBlockFrame(aFrame) &&
9963 !aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) ||
9964 aFrame->IsBrFrame()) {
9965 nsIContent* content = aFrame->GetContent();
9966 result.mContent = content->GetParent();
9967 // In some cases (bug 310589, bug 370174) we end up here with a null
9968 // content. This probably shouldn't ever happen, but since it sometimes
9969 // does, we want to avoid crashing here.
9970 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"
, 9970); MOZ_PretendNoReturn(); } } while (0)
;
9971 if (result.mContent) {
9972 result.mOffset = result.mContent->ComputeIndexOf_Deprecated(content) +
9973 (aDirection == eDirPrevious ? 1 : 0);
9974 }
9975 return result;
9976 }
9977
9978 result = FindLineBreakInText(aFrame, aDirection);
9979 if (result.mContent) {
9980 return result;
9981 }
9982
9983 // Iterate over children and call ourselves recursively
9984 if (aDirection == eDirPrevious) {
9985 nsIFrame* child = aFrame->PrincipalChildList().LastChild();
9986 while (child && !result.mContent) {
9987 result = FindLineBreakingFrame(child, aDirection);
9988 child = child->GetPrevSibling();
9989 }
9990 } else { // eDirNext
9991 nsIFrame* child = aFrame->PrincipalChildList().FirstChild();
9992 while (child && !result.mContent) {
9993 result = FindLineBreakingFrame(child, aDirection);
9994 child = child->GetNextSibling();
9995 }
9996 }
9997 return result;
9998}
9999
10000enum class OffsetIsAtLineEdge : bool { No, Yes };
10001
10002static void SetPeekResultFromFrame(PeekOffsetStruct& aPos, nsIFrame* aFrame,
10003 int32_t aOffset,
10004 OffsetIsAtLineEdge aAtLineEdge) {
10005 FrameContentRange range = GetRangeForFrame(aFrame);
10006 aPos.mResultFrame = aFrame;
10007 aPos.mResultContent = range.content;
10008 // Output offset is relative to content, not frame
10009 aPos.mContentOffset =
10010 aOffset < 0 ? range.end + aOffset + 1 : range.start + aOffset;
10011 // Ensure we don't go past the range. This is important if aFrame is empty.
10012 aPos.mContentOffset = std::clamp(aPos.mContentOffset, range.start, range.end);
10013 if (aAtLineEdge == OffsetIsAtLineEdge::Yes) {
10014 aPos.mAttach = aPos.mContentOffset == range.start
10015 ? CaretAssociationHint::After
10016 : CaretAssociationHint::Before;
10017 }
10018}
10019
10020nsresult nsIFrame::PeekOffsetForParagraph(PeekOffsetStruct* aPos) {
10021 nsIFrame* frame = this;
10022 nsContentAndOffset blockFrameOrBR;
10023 blockFrameOrBR.mContent = nullptr;
10024 bool reachedLimit = IsRelevantBlockFrame(frame) || IsEditingHost(frame);
10025
10026 auto traverse = [&aPos](nsIFrame* current) {
10027 return aPos->mDirection == eDirPrevious ? current->GetPrevSibling()
10028 : current->GetNextSibling();
10029 };
10030
10031 // Go through containing frames until reaching a block frame.
10032 // In each step, search the previous (or next) siblings for the closest
10033 // "stop frame" (a block frame or a BRFrame).
10034 // If found, set it to be the selection boundary and abort.
10035 while (!reachedLimit) {
10036 nsIFrame* parent = frame->GetParent();
10037 // Treat a frame associated with the root content as if it were a block
10038 // frame.
10039 if (!frame->mContent || !frame->mContent->GetParent()) {
10040 reachedLimit = true;
10041 break;
10042 }
10043
10044 if (aPos->mDirection == eDirNext) {
10045 // Try to find our own line-break before looking at our siblings.
10046 blockFrameOrBR = FindLineBreakInText(frame, eDirNext);
10047 }
10048
10049 nsIFrame* sibling = traverse(frame);
10050 while (sibling && !blockFrameOrBR.mContent) {
10051 blockFrameOrBR = FindLineBreakingFrame(sibling, aPos->mDirection);
10052 sibling = traverse(sibling);
10053 }
10054 if (blockFrameOrBR.mContent) {
10055 aPos->mResultContent = blockFrameOrBR.mContent;
10056 aPos->mContentOffset = blockFrameOrBR.mOffset;
10057 if (blockFrameOrBR.mIsTerminalNewlineInText) {
10058 // The boundary sits on the edge between the text frame ending with the
10059 // newline and the one starting the next line, and it belongs to the
10060 // latter. Associating the caret with the end of the preceding line
10061 // instead leaves a later logical character move with nothing to do: it
10062 // only re-associates the caret without advancing the offset.
10063 aPos->mAttach = CaretAssociationHint::After;
10064 }
10065 break;
10066 }
10067 frame = parent;
10068 reachedLimit =
10069 frame && (IsRelevantBlockFrame(frame) || IsEditingHost(frame));
10070 }
10071
10072 if (reachedLimit) { // no "stop frame" found
10073 if (aPos->mOptions.contains(PeekOffsetOption::ForCaretMove)) {
10074 // For a caret move, drill down to a leaf so scroll-into-view geometry,
10075 // which only uses text-frame offsets when the focus node is a Text node,
10076 // gets a usable position. Frame children include flattened-tree
10077 // descendants, so this also covers shadow-DOM content that the element
10078 // branch below reaches via GetShadowRootForSelection.
10079 const bool atEnd = aPos->mDirection == eDirNext;
10080 FrameTarget targetFrame = DrillDownToSelectionFrame(
10081 frame, atEnd, nsIFrame::IGNORE_NATIVE_ANONYMOUS_SUBTREE);
10082 SetPeekResultFromFrame(*aPos, targetFrame.frame, atEnd ? -1 : 0,
10083 OffsetIsAtLineEdge::Yes);
10084 } else {
10085 // For selection (e.g. triple-click paragraph selection), the block
10086 // container itself is the expected boundary.
10087 aPos->mResultContent = frame->GetContent();
10088 if (aPos->mResultContent) {
10089 if (ShadowRoot* shadowRoot =
10090 aPos->mResultContent->GetShadowRootForSelection()) {
10091 // Even if there's no children for this node,
10092 // the elements inside the shadow root is still
10093 // selectable
10094 aPos->mResultContent = shadowRoot;
10095 }
10096 }
10097 if (aPos->mDirection == eDirPrevious) {
10098 aPos->mContentOffset = 0;
10099 } else if (aPos->mResultContent) {
10100 aPos->mContentOffset = aPos->mResultContent->GetChildCount();
10101 }
10102 }
10103 }
10104 return NS_OK;
10105}
10106
10107// Determine movement direction relative to frame
10108static bool IsMovingInFrameDirection(const nsIFrame* frame,
10109 nsDirection aDirection, bool aVisual) {
10110 bool isReverseDirection =
10111 aVisual && nsBidiPresUtils::IsReversedDirectionFrame(frame);
10112 return aDirection == (isReverseDirection ? eDirPrevious : eDirNext);
10113}
10114
10115// Determines "are we looking for a boundary between whitespace and
10116// non-whitespace (in the direction we're moving in)". It is true when moving
10117// forward and looking for a beginning of a word, or when moving backwards and
10118// looking for an end of a word.
10119static bool ShouldWordSelectionEatSpace(const PeekOffsetStruct& aPos) {
10120 if (aPos.mWordMovementType != eDefaultBehavior) {
10121 // aPos->mWordMovementType possible values:
10122 // eEndWord: eat the space if we're moving backwards
10123 // eStartWord: eat the space if we're moving forwards
10124 return (aPos.mWordMovementType == eEndWord) ==
10125 (aPos.mDirection == eDirPrevious);
10126 }
10127 // Use the hidden preference which is based on operating system
10128 // behavior. This pref only affects whether moving forward by word
10129 // should go to the end of this word or start of the next word. When
10130 // going backwards, the start of the word is always used, on every
10131 // operating system.
10132 return aPos.mDirection == eDirNext &&
10133 StaticPrefs::layout_word_select_eat_space_to_next_word();
10134}
10135
10136void nsIFrame::SelectablePeekReport::TransferTo(PeekOffsetStruct& aPos) const {
10137 return SetPeekResultFromFrame(aPos, mFrame, mOffset, OffsetIsAtLineEdge::No);
10138}
10139
10140nsIFrame::SelectablePeekReport::SelectablePeekReport(
10141 const mozilla::GenericErrorResult<nsresult>&& aErr) {
10142 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", 10142); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "((bool)(__builtin_expect(!!(NS_FAILED_impl(aErr.operator nsresult())), 0)))"
")"); do { MOZ_CrashSequence(__null, 10142); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
10143 // Return an empty report
10144}
10145
10146nsresult nsIFrame::PeekOffsetForCharacter(PeekOffsetStruct* aPos,
10147 int32_t aOffset) {
10148 SelectablePeekReport current{this, aOffset};
10149
10150 nsIFrame::FrameSearchResult peekSearchState = CONTINUE;
10151
10152 const bool forceEditableRegion =
10153 aPos->mOptions.contains(PeekOffsetOption::ForceEditableRegion);
10154
10155 while (peekSearchState != FOUND) {
10156 const bool movingInFrameDirection = IsMovingInFrameDirection(
10157 current.mFrame, aPos->mDirection,
10158 aPos->mOptions.contains(PeekOffsetOption::Visual));
10159
10160 if (current.mJumpedLine) {
10161 // If we jumped lines, it's as if we found a character, but we still need
10162 // to eat non-renderable content on the new line.
10163 peekSearchState = current.PeekOffsetNoAmount(movingInFrameDirection);
10164 } else {
10165 PeekOffsetCharacterOptions options;
10166 options.mRespectClusters = aPos->mAmount == eSelectCluster;
10167 peekSearchState =
10168 current.PeekOffsetCharacter(movingInFrameDirection, options);
10169 if (peekSearchState == FOUND && forceEditableRegion &&
10170 !current.mFrame->ContentIsEditable()) {
10171 // Treat non-editable content as unselectable. Note that we may need to
10172 // set mJumpedLine propery even if it's not editable. Therefore, we
10173 // cannot skip the above call.
10174 peekSearchState = CONTINUE_UNSELECTABLE;
10175 }
10176 }
10177
10178 current.mMovedOverNonSelectableText |=
10179 peekSearchState == CONTINUE_UNSELECTABLE;
10180
10181 if (peekSearchState != FOUND) {
10182 SelectablePeekReport next = current.mFrame->GetFrameFromDirection(*aPos);
10183 if (next.Failed()) {
10184 return NS_ERROR_FAILURE;
10185 }
10186 next.mJumpedLine |= current.mJumpedLine;
10187 next.mMovedOverNonSelectableText |= current.mMovedOverNonSelectableText;
10188 next.mHasSelectableFrame |= current.mHasSelectableFrame;
10189 current = next;
10190 }
10191
10192 // Found frame, but because we moved over non selectable text we want
10193 // the offset to be at the frame edge. Note that if we are extending the
10194 // selection, this doesn't matter.
10195 if (peekSearchState == FOUND && current.mMovedOverNonSelectableText &&
10196 (!aPos->mOptions.contains(PeekOffsetOption::Extend) ||
10197 current.mHasSelectableFrame)) {
10198 auto [start, end] = current.mFrame->GetOffsets();
10199 current.mOffset = aPos->mDirection == eDirNext ? 0 : end - start;
10200 }
10201 }
10202
10203 // Set outputs
10204 current.TransferTo(*aPos);
10205 // If we're dealing with a text frame and moving backward positions us at
10206 // the end of that line, decrease the offset by one to make sure that
10207 // we're placed before the linefeed character on the previous line.
10208 if (current.mOffset < 0 && current.mJumpedLine &&
10209 aPos->mDirection == eDirPrevious &&
10210 current.mFrame->HasSignificantTerminalNewline() &&
10211 !current.mIgnoredBrFrame) {
10212 --aPos->mContentOffset;
10213 }
10214 return NS_OK;
10215}
10216
10217nsresult nsIFrame::PeekOffsetForWord(PeekOffsetStruct* aPos, int32_t aOffset) {
10218 SelectablePeekReport current{this, aOffset};
10219 bool shouldStopAtHardBreak =
10220 aPos->mWordMovementType == eDefaultBehavior &&
10221 StaticPrefs::layout_word_select_eat_space_to_next_word();
10222 bool wordSelectEatSpace = ShouldWordSelectionEatSpace(*aPos);
10223
10224 PeekWordState state;
10225 while (true) {
10226 bool movingInFrameDirection = IsMovingInFrameDirection(
10227 current.mFrame, aPos->mDirection,
10228 aPos->mOptions.contains(PeekOffsetOption::Visual));
10229
10230 FrameSearchResult searchResult = current.mFrame->PeekOffsetWord(
10231 movingInFrameDirection, wordSelectEatSpace,
10232 aPos->mOptions.contains(PeekOffsetOption::IsKeyboardSelect),
10233 &current.mOffset, &state,
10234 !aPos->mOptions.contains(PeekOffsetOption::PreserveSpaces));
10235 if (searchResult == FOUND) {
10236 break;
10237 }
10238
10239 SelectablePeekReport next = [&]() {
10240 PeekOffsetOptions options = aPos->mOptions;
10241 if (state.mSawInlineCharacter) {
10242 // If we've already found a character, we don't want to stop at
10243 // placeholder frame boundary if there is in the word.
10244 options += PeekOffsetOption::StopAtPlaceholder;
10245 }
10246 return current.mFrame->GetFrameFromDirection(aPos->mDirection, options,
10247 aPos->mAncestorLimiter);
10248 }();
10249 if (next.Failed()) {
10250 // If we've crossed the line boundary, check to make sure that we
10251 // have not consumed a trailing newline as whitespace if it's
10252 // significant.
10253 if (next.mJumpedLine && wordSelectEatSpace &&
10254 current.mFrame->HasSignificantTerminalNewline() &&
10255 current.mFrame->StyleText()->mWhiteSpaceCollapse !=
10256 StyleWhiteSpaceCollapse::PreserveBreaks) {
10257 current.mOffset -= 1;
10258 }
10259 break;
10260 }
10261
10262 if ((next.mJumpedLine || next.mFoundPlaceholder) && !wordSelectEatSpace &&
10263 state.mSawBeforeType) {
10264 // We can't jump lines if we're looking for whitespace following
10265 // non-whitespace, and we already encountered non-whitespace.
10266 break;
10267 }
10268
10269 if (shouldStopAtHardBreak && next.mJumpedHardBreak) {
10270 /**
10271 * Prev, always: Jump and stop right there
10272 * Next, saw inline: just stop
10273 * Next, no inline: Jump and consume whitespaces
10274 */
10275 if (aPos->mDirection == eDirPrevious) {
10276 // Try moving to the previous line if exists
10277 current.TransferTo(*aPos);
10278 current.mFrame->PeekOffsetForCharacter(aPos, current.mOffset);
10279 return NS_OK;
10280 }
10281 if (state.mSawInlineCharacter || current.mJumpedHardBreak) {
10282 if (current.mFrame->HasSignificantTerminalNewline()) {
10283 current.mOffset -= 1;
10284 }
10285 current.TransferTo(*aPos);
10286 return NS_OK;
10287 }
10288 // Mark the state as whitespace and continue
10289 state.Update(false, true);
10290 }
10291
10292 if (next.mJumpedLine) {
10293 state.mContext.Truncate();
10294 }
10295 current = next;
10296 // Jumping a line is equivalent to encountering whitespace
10297 // This affects only when it already met an actual character
10298 if (wordSelectEatSpace && next.mJumpedLine) {
10299 state.SetSawBeforeType();
10300 }
10301 }
10302
10303 // Set outputs
10304 current.TransferTo(*aPos);
10305 return NS_OK;
10306}
10307
10308static nsIFrame* GetFirstSelectableDescendantWithLineIterator(
10309 const PeekOffsetStruct& aPeekOffsetStruct, nsIFrame* aParentFrame) {
10310 const bool forceEditableRegion = aPeekOffsetStruct.mOptions.contains(
10311 PeekOffsetOption::ForceEditableRegion);
10312 auto FoundValidFrame = [aPeekOffsetStruct,
10313 forceEditableRegion](const nsIFrame* aFrame) {
10314 if (!aFrame->IsSelectable()) {
10315 return false;
10316 }
10317 if (!aPeekOffsetStruct.FrameContentIsInAncestorLimiter(aFrame)) {
10318 return false;
10319 }
10320 if (forceEditableRegion && !aFrame->ContentIsEditable()) {
10321 return false;
10322 }
10323 return true;
10324 };
10325
10326 for (nsIFrame* child : aParentFrame->PrincipalChildList()) {
10327 // some children may not be selectable, e.g. :before / :after pseudoelements
10328 // content with user-select: none, or contenteditable="false"
10329 // we need to skip them
10330 if (child->CanProvideLineIterator() && FoundValidFrame(child)) {
10331 return child;
10332 }
10333 if (nsIFrame* nested = GetFirstSelectableDescendantWithLineIterator(
10334 aPeekOffsetStruct, child)) {
10335 return nested;
10336 }
10337 }
10338 return nullptr;
10339}
10340
10341nsresult nsIFrame::PeekOffsetForLine(PeekOffsetStruct* aPos) {
10342 nsIFrame* blockFrame = this;
10343 nsresult result = NS_ERROR_FAILURE;
10344
10345 // outer loop
10346 // moving to a next block when no more blocks are available in a subtree
10347 AutoAssertNoDomMutations guard;
10348 while (NS_FAILED(result)((bool)(__builtin_expect(!!(NS_FAILED_impl(result)), 0)))) {
10349 auto [newBlock, lineFrame] = blockFrame->GetContainingBlockForLine(
10350 aPos->mOptions.contains(PeekOffsetOption::StopAtScroller));
10351 if (!newBlock) {
10352 return NS_ERROR_FAILURE;
10353 }
10354 // FYI: If the editing host is an inline element, the block frame content
10355 // may be either not editable or editable but belonging to different editing
10356 // host.
10357 blockFrame = newBlock;
10358 nsILineIterator* iter = blockFrame->GetLineIterator();
10359 int32_t thisLine = iter->FindLineContaining(lineFrame);
10360 if (NS_WARN_IF(thisLine < 0)NS_warn_if_impl(thisLine < 0, "thisLine < 0", "./../../../layout/generic/nsIFrame.cpp"
, 10360)
) {
10361 return NS_ERROR_FAILURE;
10362 }
10363
10364 int8_t edgeCase = 0; // no edge case. This should look at thisLine
10365
10366 // this part will find a frame or a block frame. If it's a block frame
10367 // it will "drill down" to find a viable frame or it will return an
10368 // error.
10369 nsIFrame* lastFrame = this;
10370
10371 // inner loop - crawling the frames within a specific block subtree
10372 while (true) {
10373 result =
10374 GetNextPrevLineFromBlockFrame(aPos, blockFrame, thisLine, edgeCase);
10375 // we came back to same spot! keep going
10376 if (NS_SUCCEEDED(result)((bool)(__builtin_expect(!!(!NS_FAILED_impl(result)), 1))) &&
10377 (!aPos->mResultFrame || aPos->mResultFrame == lastFrame)) {
10378 aPos->mResultFrame = nullptr;
10379 lastFrame = nullptr;
10380 if (aPos->mDirection == eDirPrevious) {
10381 thisLine--;
10382 } else {
10383 thisLine++;
10384 }
10385 continue;
10386 }
10387
10388 if (NS_FAILED(result)((bool)(__builtin_expect(!!(NS_FAILED_impl(result)), 0)))) {
10389 break;
10390 }
10391
10392 lastFrame = aPos->mResultFrame; // set last frame
10393 /* SPECIAL CHECK FOR NAVIGATION INTO TABLES
10394 * when we hit a frame which doesn't have line iterator, we need to
10395 * drill down and find a child with the line iterator to prevent the
10396 * crawling process to prematurely finish. Note that this is only sound if
10397 * we're guaranteed to not have multiple children implementing
10398 * LineIterator.
10399 *
10400 * So far known cases are:
10401 * 1) table wrapper (drill down into table row group)
10402 * 2) table cell (drill down into its only anon child)
10403 */
10404 const bool shouldDrillIntoChildren =
10405 aPos->mResultFrame->IsTableWrapperFrame() ||
10406 aPos->mResultFrame->IsTableCellFrame();
10407
10408 if (shouldDrillIntoChildren) {
10409 nsIFrame* child = GetFirstSelectableDescendantWithLineIterator(
10410 *aPos, aPos->mResultFrame);
10411 if (child) {
10412 aPos->mResultFrame = child;
10413 }
10414 }
10415
10416 if (!aPos->mResultFrame->CanProvideLineIterator()) {
10417 // no more selectable content at this level
10418 break;
10419 }
10420
10421 if (aPos->mResultFrame == blockFrame) {
10422 // Make sure block element is not the same as the one we had before.
10423 break;
10424 }
10425
10426 // we've struck another block element with selectable content!
10427 if (aPos->mDirection == eDirPrevious) {
10428 edgeCase = 1; // far edge, search from end backwards
10429 } else {
10430 edgeCase = -1; // near edge search from beginning onwards
10431 }
10432 thisLine = 0; // this line means nothing now.
10433 // everything else means something so keep looking "inside" the
10434 // block
10435 blockFrame = aPos->mResultFrame;
10436 }
10437 }
10438 return result;
10439}
10440
10441nsresult nsIFrame::PeekOffsetForLineEdge(PeekOffsetStruct* aPos) {
10442 // Adjusted so that the caret can't get confused when content changes
10443 nsIFrame* frame = AdjustFrameForSelectionStyles(this);
10444 // FIXME: Use PeekOffsetStruct::mAncestorLimiter instead.
10445 Element* editingHost = frame->GetContent()->GetEditingHost();
10446
10447 auto [blockFrame, lineFrame] = frame->GetContainingBlockForLine(
10448 aPos->mOptions.contains(PeekOffsetOption::StopAtScroller));
10449 if (!blockFrame) {
10450 return NS_ERROR_FAILURE;
10451 }
10452 AutoAssertNoDomMutations guard;
10453 nsILineIterator* it = blockFrame->GetLineIterator();
10454 int32_t thisLine = it->FindLineContaining(lineFrame);
10455 if (thisLine < 0) {
10456 return NS_ERROR_FAILURE;
10457 }
10458
10459 nsIFrame* baseFrame = nullptr;
10460 bool endOfLine = eSelectEndLine == aPos->mAmount;
10461
10462 if (aPos->mOptions.contains(PeekOffsetOption::Visual) &&
10463 PresContext()->BidiEnabled()) {
10464 nsIFrame* firstFrame;
10465 bool isReordered;
10466 nsIFrame* lastFrame;
10467 MOZ_TRY(__extension__({ auto mozTryVarTempResult = ::mozilla::ToResult
(it->CheckLineOrder(thisLine, &isReordered, &firstFrame
, &lastFrame)); if ((__builtin_expect(!!(mozTryVarTempResult
.isErr()), 0))) { return mozTryVarTempResult.propagateErr(); }
mozTryVarTempResult.unwrap(); })
10468 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(); })
;
10469 baseFrame = endOfLine ? lastFrame : firstFrame;
10470 } else {
10471 auto line = it->GetLine(thisLine).unwrap();
10472
10473 nsIFrame* frame = line.mFirstFrameOnLine;
10474 bool lastFrameWasEditable = false;
10475 for (int32_t count = line.mNumFramesOnLine; count;
10476 --count, frame = frame->GetNextSibling()) {
10477 if (frame->IsGeneratedContentFrame()) {
10478 continue;
10479 }
10480 // When jumping to the end of the line with the "end" key,
10481 // try to skip over brFrames
10482 if (endOfLine && line.mNumFramesOnLine > 1 && frame->IsBrFrame() &&
10483 lastFrameWasEditable == frame->GetContent()->IsEditable()) {
10484 continue;
10485 }
10486 lastFrameWasEditable =
10487 frame->GetContent() && frame->GetContent()->IsEditable();
10488 baseFrame = frame;
10489 if (!endOfLine) {
10490 break;
10491 }
10492 }
10493 }
10494 if (!baseFrame) {
10495 return NS_ERROR_FAILURE;
10496 }
10497 // Make sure we are not leaving our inline editing host if exists
10498 if (editingHost) {
10499 if (nsIFrame* frame = editingHost->GetPrimaryFrame()) {
10500 if (frame->IsInlineOutside() &&
10501 !editingHost->Contains(baseFrame->GetContent())) {
10502 baseFrame = frame;
10503 if (endOfLine) {
10504 baseFrame = baseFrame->LastContinuation();
10505 }
10506 }
10507 }
10508 }
10509 FrameTarget targetFrame = DrillDownToSelectionFrame(
10510 baseFrame, endOfLine, nsIFrame::IGNORE_NATIVE_ANONYMOUS_SUBTREE);
10511 SetPeekResultFromFrame(*aPos, targetFrame.frame, endOfLine ? -1 : 0,
10512 OffsetIsAtLineEdge::Yes);
10513 if (endOfLine && targetFrame.frame->HasSignificantTerminalNewline()) {
10514 // Do not position the caret after the terminating newline if we're
10515 // trying to move to the end of line (see bug 596506)
10516 --aPos->mContentOffset;
10517 }
10518 if (!aPos->mResultContent) {
10519 return NS_ERROR_FAILURE;
10520 }
10521 return NS_OK;
10522}
10523
10524nsresult nsIFrame::PeekOffset(PeekOffsetStruct* aPos) {
10525 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"
, 10525); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aPos" ")")
; do { MOZ_CrashSequence(__null, 10525); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
10526
10527 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", 10527)
) {
10528 // FIXME(Bug 1654362): <caption> currently can remain dirty.
10529 return NS_ERROR_UNEXPECTED;
10530 }
10531
10532 // Translate content offset to be relative to frame
10533 int32_t offset = aPos->mStartOffset - GetRangeForFrame(this).start;
10534
10535 switch (aPos->mAmount) {
10536 case eSelectCharacter:
10537 case eSelectCluster:
10538 return PeekOffsetForCharacter(aPos, offset);
10539 case eSelectWordNoSpace:
10540 // eSelectWordNoSpace means that we should not be eating any whitespace
10541 // when moving to the adjacent word. This means that we should set aPos->
10542 // mWordMovementType to eEndWord if we're moving forwards, and to
10543 // eStartWord if we're moving backwards.
10544 if (aPos->mDirection == eDirPrevious) {
10545 aPos->mWordMovementType = eStartWord;
10546 } else {
10547 aPos->mWordMovementType = eEndWord;
10548 }
10549 // Intentionally fall through the eSelectWord case.
10550 [[fallthrough]];
10551 case eSelectWord:
10552 return PeekOffsetForWord(aPos, offset);
10553 case eSelectLine:
10554 return PeekOffsetForLine(aPos);
10555 case eSelectBeginLine:
10556 case eSelectEndLine:
10557 return PeekOffsetForLineEdge(aPos);
10558 case eSelectParagraph:
10559 return PeekOffsetForParagraph(aPos);
10560 default: {
10561 NS_ASSERTION(false, "Invalid amount")do { if (!(false)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "Invalid amount"
, "false", "./../../../layout/generic/nsIFrame.cpp", 10561); MOZ_PretendNoReturn
(); } } while (0)
;
10562 return NS_ERROR_FAILURE;
10563 }
10564 }
10565}
10566
10567nsIFrame::FrameSearchResult nsIFrame::PeekOffsetNoAmount(bool aForward,
10568 int32_t* aOffset) {
10569 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", 10569); MOZ_PretendNoReturn
(); } } while (0)
;
10570 // Sure, we can stop right here.
10571 return FOUND;
10572}
10573
10574nsIFrame::FrameSearchResult nsIFrame::PeekOffsetCharacter(
10575 bool aForward, int32_t* aOffset, PeekOffsetCharacterOptions aOptions) {
10576 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", 10576); MOZ_PretendNoReturn
(); } } while (0)
;
10577 int32_t startOffset = *aOffset;
10578 // A negative offset means "end of frame", which in our case means offset 1.
10579 if (startOffset < 0) {
10580 startOffset = 1;
10581 }
10582 if (aForward == (startOffset == 0)) {
10583 // We're before the frame and moving forward, or after it and moving
10584 // backwards: skip to the other side and we're done.
10585 *aOffset = 1 - startOffset;
10586 return FOUND;
10587 }
10588 return CONTINUE;
10589}
10590
10591nsIFrame::FrameSearchResult nsIFrame::PeekOffsetWord(
10592 bool aForward, bool aWordSelectEatSpace, bool aIsKeyboardSelect,
10593 int32_t* aOffset, PeekWordState* aState, bool /*aTrimSpaces*/) {
10594 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", 10594); MOZ_PretendNoReturn
(); } } while (0)
;
10595 int32_t startOffset = *aOffset;
10596 // This isn't text, so truncate the context
10597 aState->mContext.Truncate();
10598 if (startOffset < 0) {
10599 startOffset = 1;
10600 }
10601 if (aForward == (startOffset == 0)) {
10602 // We're before the frame and moving forward, or after it and moving
10603 // backwards. If we're looking for non-whitespace, we found it (without
10604 // skipping this frame).
10605 if (!aState->mAtStart) {
10606 if (aState->mLastCharWasPunctuation) {
10607 // We're not punctuation, so this is a punctuation boundary.
10608 if (BreakWordBetweenPunctuation(aState, aForward, false, false,
10609 aIsKeyboardSelect)) {
10610 return FOUND;
10611 }
10612 } else {
10613 // This is not a punctuation boundary.
10614 if (aWordSelectEatSpace && aState->mSawBeforeType) {
10615 return FOUND;
10616 }
10617 }
10618 }
10619 // Otherwise skip to the other side and note that we encountered
10620 // non-whitespace.
10621 *aOffset = 1 - startOffset;
10622 aState->Update(false, // not punctuation
10623 false // not whitespace
10624 );
10625 if (!aWordSelectEatSpace) {
10626 aState->SetSawBeforeType();
10627 }
10628 }
10629 return CONTINUE;
10630}
10631
10632// static
10633bool nsIFrame::BreakWordBetweenPunctuation(const PeekWordState* aState,
10634 bool aForward, bool aPunctAfter,
10635 bool aWhitespaceAfter,
10636 bool aIsKeyboardSelect) {
10637 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"
, 10638); MOZ_PretendNoReturn(); } } while (0)
10638 "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"
, 10638); MOZ_PretendNoReturn(); } } while (0)
;
10639 if (aState->mLastCharWasWhitespace) {
10640 // We always stop between whitespace and punctuation
10641 return true;
10642 }
10643 if (!StaticPrefs::layout_word_select_stop_at_punctuation()) {
10644 // When this pref is false, we never stop at a punctuation boundary unless
10645 // it's followed by whitespace (in the relevant direction).
10646 return aWhitespaceAfter;
10647 }
10648 if (!aIsKeyboardSelect) {
10649 // mouse caret movement (e.g. word selection) always stops at every
10650 // punctuation boundary
10651 return true;
10652 }
10653 bool afterPunct = aForward ? aState->mLastCharWasPunctuation : aPunctAfter;
10654 if (!afterPunct) {
10655 // keyboard caret movement only stops after punctuation (in content order)
10656 return false;
10657 }
10658 // Stop only if we've seen some non-punctuation since the last whitespace;
10659 // don't stop after punctuation that follows whitespace.
10660 return aState->mSeenNonPunctuationSinceWhitespace;
10661}
10662
10663std::pair<nsIFrame*, nsIFrame*> nsIFrame::GetContainingBlockForLine(
10664 bool aLockScroll) const {
10665 const nsIFrame* parentFrame = this;
10666 const nsIFrame* frame;
10667 while (parentFrame) {
10668 frame = parentFrame;
10669 if (frame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
10670 // if we are searching for a frame that is not in flow we will not find
10671 // it. we must instead look for its placeholder
10672 if (frame->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
10673 // abspos continuations don't have placeholders, get the fif
10674 frame = frame->FirstInFlow();
10675 }
10676 frame = frame->GetPlaceholderFrame();
10677 if (!frame) {
10678 return std::pair(nullptr, nullptr);
10679 }
10680 }
10681 parentFrame = frame->GetParent();
10682 if (parentFrame) {
10683 if (aLockScroll && parentFrame->IsScrollContainerFrame()) {
10684 return std::pair(nullptr, nullptr);
10685 }
10686 if (parentFrame->CanProvideLineIterator()) {
10687 return std::pair(const_cast<nsIFrame*>(parentFrame),
10688 const_cast<nsIFrame*>(frame));
10689 }
10690 }
10691 }
10692 return std::pair(nullptr, nullptr);
10693}
10694
10695Result<bool, nsresult> nsIFrame::IsVisuallyAtLineEdge(
10696 nsILineIterator* aLineIterator, int32_t aLine, nsDirection aDirection) {
10697 auto line = aLineIterator->GetLine(aLine).unwrap();
10698
10699 const bool lineIsRTL = aLineIterator->IsLineIteratorFlowRTL();
10700
10701 nsIFrame *firstFrame = nullptr, *lastFrame = nullptr;
10702 bool isReordered = false;
10703 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(); })
10704 &lastFrame))__extension__({ auto mozTryVarTempResult = ::mozilla::ToResult
(aLineIterator->CheckLineOrder(aLine, &isReordered, &
firstFrame, &lastFrame)); if ((__builtin_expect(!!(mozTryVarTempResult
.isErr()), 0))) { return mozTryVarTempResult.propagateErr(); }
mozTryVarTempResult.unwrap(); })
;
10705 if (!firstFrame || !lastFrame) {
10706 return true; // XXX: Why true? We check whether `this` is at the edge...
10707 }
10708
10709 nsIFrame* leftmostFrame = lineIsRTL ? lastFrame : firstFrame;
10710 nsIFrame* rightmostFrame = lineIsRTL ? firstFrame : lastFrame;
10711 auto FrameIsRTL = [](nsIFrame* aFrame) {
10712 return nsBidiPresUtils::FrameDirection(aFrame) ==
10713 mozilla::intl::BidiDirection::RTL;
10714 };
10715 if (!lineIsRTL == (aDirection == eDirPrevious)) {
10716 nsIFrame* maybeLeftmostFrame = leftmostFrame;
10717 for ([[maybe_unused]] int32_t i : IntegerRange(line.mNumFramesOnLine)) {
10718 if (maybeLeftmostFrame == this) {
10719 return true;
10720 }
10721 // If left edge of the line starts with placeholder frames, we can ignore
10722 // them and should keep checking the following frames.
10723 if (!maybeLeftmostFrame->IsPlaceholderFrame()) {
10724 if ((FrameIsRTL(maybeLeftmostFrame) == lineIsRTL) ==
10725 (aDirection == eDirPrevious)) {
10726 nsIFrame::GetFirstLeaf(&maybeLeftmostFrame);
10727 } else {
10728 nsIFrame::GetLastLeaf(&maybeLeftmostFrame);
10729 }
10730 return maybeLeftmostFrame == this;
10731 }
10732 maybeLeftmostFrame = nsBidiPresUtils::GetFrameToRightOf(
10733 maybeLeftmostFrame, line.mFirstFrameOnLine, line.mNumFramesOnLine);
10734 if (!maybeLeftmostFrame) {
10735 return false;
10736 }
10737 }
10738 return false;
10739 }
10740
10741 nsIFrame* maybeRightmostFrame = rightmostFrame;
10742 for ([[maybe_unused]] int32_t i : IntegerRange(line.mNumFramesOnLine)) {
10743 if (maybeRightmostFrame == this) {
10744 return true;
10745 }
10746 // If the line ends with placehlder frames, we can ignore them and should
10747 // keep checking the preceding frames.
10748 if (!maybeRightmostFrame->IsPlaceholderFrame()) {
10749 if ((FrameIsRTL(maybeRightmostFrame) == lineIsRTL) ==
10750 (aDirection == eDirPrevious)) {
10751 nsIFrame::GetFirstLeaf(&maybeRightmostFrame);
10752 } else {
10753 nsIFrame::GetLastLeaf(&maybeRightmostFrame);
10754 }
10755 return maybeRightmostFrame == this;
10756 }
10757 maybeRightmostFrame = nsBidiPresUtils::GetFrameToLeftOf(
10758 maybeRightmostFrame, line.mFirstFrameOnLine, line.mNumFramesOnLine);
10759 if (!maybeRightmostFrame) {
10760 return false;
10761 }
10762 }
10763 return false;
10764}
10765
10766Result<bool, nsresult> nsIFrame::IsLogicallyAtLineEdge(
10767 nsILineIterator* aLineIterator, int32_t aLine, nsDirection aDirection) {
10768 auto line = aLineIterator->GetLine(aLine).unwrap();
10769 if (!line.mNumFramesOnLine) {
10770 return false;
10771 }
10772 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", 10772); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "line.mFirstFrameOnLine" ")"); do { MOZ_CrashSequence
(__null, 10772); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
10773
10774 if (aDirection == eDirPrevious) {
10775 nsIFrame* maybeFirstFrame = line.mFirstFrameOnLine;
10776 for ([[maybe_unused]] int32_t i : IntegerRange(line.mNumFramesOnLine)) {
10777 if (maybeFirstFrame == this) {
10778 return true;
10779 }
10780 // If the line starts with placeholder frames, we can ignore them and
10781 // should keep checking the following frames.
10782 if (!maybeFirstFrame->IsPlaceholderFrame()) {
10783 nsIFrame::GetFirstLeaf(&maybeFirstFrame);
10784 return maybeFirstFrame == this;
10785 }
10786 maybeFirstFrame = maybeFirstFrame->GetNextSibling();
10787 if (!maybeFirstFrame) {
10788 return false;
10789 }
10790 }
10791 return false;
10792 }
10793
10794 // eDirNext
10795 nsIFrame* maybeLastFrame = line.GetLastFrameOnLine();
10796 for ([[maybe_unused]] int32_t i : IntegerRange(line.mNumFramesOnLine)) {
10797 if (maybeLastFrame == this) {
10798 return true;
10799 }
10800 // If the line ends with placehlder frames, we can ignore them and should
10801 // keep checking the preceding frames.
10802 if (!maybeLastFrame->IsPlaceholderFrame()) {
10803 nsIFrame::GetLastLeaf(&maybeLastFrame);
10804 return maybeLastFrame == this;
10805 }
10806 maybeLastFrame = maybeLastFrame->GetPrevSibling();
10807 }
10808 return false;
10809}
10810
10811nsIFrame::SelectablePeekReport nsIFrame::GetFrameFromDirection(
10812 nsDirection aDirection, const PeekOffsetOptions& aOptions,
10813 const Element* aAncestorLimiter) {
10814 SelectablePeekReport result;
10815
10816 nsPresContext* presContext = PresContext();
10817 const bool needsVisualTraversal =
10818 aOptions.contains(PeekOffsetOption::Visual) && presContext->BidiEnabled();
10819 const bool followOofs =
10820 !aOptions.contains(PeekOffsetOption::StopAtPlaceholder);
10821 nsFrameIterator frameIterator(
10822 presContext, this, nsFrameIterator::Type::Leaf, needsVisualTraversal,
10823 aOptions.contains(PeekOffsetOption::StopAtScroller), followOofs,
10824 false, // aSkipPopupChecks
10825 aAncestorLimiter);
10826
10827 // Find the prev/next selectable frame
10828 bool selectable = false;
10829 nsIFrame* traversedFrame = this;
10830 AutoAssertNoDomMutations guard;
10831 const nsFrameSelection* frameSelection =
10832 GetContent() ? GetContent()->GetFrameSelection() : nullptr;
10833 while (!selectable) {
10834 auto [blockFrame, lineFrame] = traversedFrame->GetContainingBlockForLine(
10835 aOptions.contains(PeekOffsetOption::StopAtScroller));
10836 if (!blockFrame) {
10837 return result;
10838 }
10839
10840 nsILineIterator* it = blockFrame->GetLineIterator();
10841 int32_t thisLine = it->FindLineContaining(lineFrame);
10842 if (thisLine < 0) {
10843 return result;
10844 }
10845
10846 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(); })
10847 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(); })
10848 ? 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(); })
10849 : 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(); })
;
10850 if (atLineEdge) {
10851 result.mJumpedLine = true;
10852 if (!aOptions.contains(PeekOffsetOption::JumpLines)) {
10853 return result; // we are done. cannot jump lines
10854 }
10855 int32_t lineToCheckWrap =
10856 aDirection == eDirPrevious ? thisLine - 1 : thisLine;
10857 if (lineToCheckWrap < 0 ||
10858 !it->GetLine(lineToCheckWrap).unwrap().mIsWrapped) {
10859 result.mJumpedHardBreak = true;
10860 }
10861 }
10862
10863 traversedFrame = frameIterator.Traverse(aDirection == eDirNext);
10864 if (!traversedFrame) {
10865 return result;
10866 }
10867
10868 if (aOptions.contains(PeekOffsetOption::StopAtPlaceholder) &&
10869 traversedFrame->IsPlaceholderFrame()) {
10870 // XXX If the placeholder frame does not have meaningful content, the user
10871 // may want to select as a word around the out-of-flow cotent. However,
10872 // non-text frame resets context in nsIFrame::PeekOffsetWord(). Therefore,
10873 // next text frame considers the new word starts from its edge. So, it's
10874 // not enough to implement such behavior with adding a check here whether
10875 // the real frame may change the word with its contents if it were not
10876 // out-of-flow.
10877 result.mFoundPlaceholder = true;
10878 return result;
10879 }
10880
10881 auto IsSelectableFrame = [aAncestorLimiter, aOptions,
10882 frameSelection](const nsIFrame* aFrame) {
10883 if (!aFrame->IsSelectable() || MOZ_UNLIKELY(!aFrame->GetContent())(__builtin_expect(!!(!aFrame->GetContent()), 0))) {
10884 return false;
10885 }
10886 // If the found frame content is managed by different nsFrameSelection, we
10887 // cannot select the frame content with current selection.
10888 if (frameSelection != aFrame->GetContent()->GetFrameSelection()) {
10889 return false;
10890 }
10891 if (MOZ_UNLIKELY(aAncestorLimiter &&(__builtin_expect(!!(aAncestorLimiter && !aAncestorLimiter
->GetPrimaryFrame()), 0))
10892 !aAncestorLimiter->GetPrimaryFrame())(__builtin_expect(!!(aAncestorLimiter && !aAncestorLimiter
->GetPrimaryFrame()), 0))
&&
10893 !aFrame->GetContent()->IsInclusiveFlatTreeDescendantOf(
10894 aAncestorLimiter)) {
10895 return false;
10896 }
10897 return !aOptions.contains(PeekOffsetOption::ForceEditableRegion) ||
10898 aFrame->GetContent()->IsEditable();
10899 };
10900
10901 // Skip br frames, but only if we can select something before hitting the
10902 // end of the line or a non-selectable region.
10903 if (atLineEdge && aDirection == eDirPrevious &&
10904 traversedFrame->IsBrFrame()) {
10905 for (nsIFrame* current = traversedFrame->GetPrevSibling(); current;
10906 current = current->GetPrevSibling()) {
10907 if (!current->IsBlockOutside() && IsSelectableFrame(current)) {
10908 if (!current->IsBrFrame()) {
10909 result.mIgnoredBrFrame = true;
10910 }
10911 break;
10912 }
10913 }
10914 if (result.mIgnoredBrFrame) {
10915 continue;
10916 }
10917 }
10918
10919 selectable = IsSelectableFrame(traversedFrame);
10920 if (MOZ_UNLIKELY(!frameSelection)(__builtin_expect(!!(!frameSelection), 0)) && selectable &&
10921 MOZ_LIKELY(traversedFrame->GetContent())(__builtin_expect(!!(traversedFrame->GetContent()), 1))) {
10922 frameSelection = traversedFrame->GetContent()->GetFrameSelection();
10923 }
10924 if (!selectable) {
10925 if (traversedFrame->IsSelectable()) {
10926 result.mHasSelectableFrame = true;
10927 }
10928 result.mMovedOverNonSelectableText = true;
10929 }
10930 } // while (!selectable)
10931
10932 result.mOffset = (aDirection == eDirNext) ? 0 : -1;
10933
10934 if (aOptions.contains(PeekOffsetOption::Visual) &&
10935 nsBidiPresUtils::IsReversedDirectionFrame(traversedFrame)) {
10936 // The new frame is reverse-direction, go to the other end
10937 result.mOffset = -1 - result.mOffset;
10938 }
10939 result.mFrame = traversedFrame;
10940 return result;
10941}
10942
10943nsIFrame::SelectablePeekReport nsIFrame::GetFrameFromDirection(
10944 const PeekOffsetStruct& aPos) {
10945 return GetFrameFromDirection(aPos.mDirection, aPos.mOptions,
10946 aPos.mAncestorLimiter);
10947}
10948
10949/* virtual */
10950void nsIFrame::ChildIsDirty(nsIFrame* aChild) {
10951 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", 10953); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "false" ") (" "MOZ_ASSERT_UNREACHABLE: " "should never be called on a frame that doesn't "
"inherit from nsContainerFrame" ")"); do { MOZ_CrashSequence
(__null, 10953); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
10952 "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", 10953); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "false" ") (" "MOZ_ASSERT_UNREACHABLE: " "should never be called on a frame that doesn't "
"inherit from nsContainerFrame" ")"); do { MOZ_CrashSequence
(__null, 10953); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
10953 "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", 10953); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "false" ") (" "MOZ_ASSERT_UNREACHABLE: " "should never be called on a frame that doesn't "
"inherit from nsContainerFrame" ")"); do { MOZ_CrashSequence
(__null, 10953); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
10954}
10955
10956#ifdef ACCESSIBILITY1
10957a11y::AccType nsIFrame::AccessibleType() {
10958 if (IsTableCaption() && !GetRect().IsEmpty()) {
10959 return a11y::eHTMLCaptionType;
10960 }
10961 return a11y::eNoType;
10962}
10963#endif
10964
10965bool nsIFrame::ClearOverflowRects() {
10966 if (mOverflow.mType == OverflowStorageType::None) {
10967 return false;
10968 }
10969 if (mOverflow.mType == OverflowStorageType::Large) {
10970 RemoveProperty(OverflowAreasProperty());
10971 }
10972 mOverflow.mType = OverflowStorageType::None;
10973 return true;
10974}
10975
10976bool nsIFrame::SetOverflowAreas(const OverflowAreas& aOverflowAreas) {
10977 if (mOverflow.mType == OverflowStorageType::Large) {
10978 OverflowAreas* overflow = GetOverflowAreasProperty();
10979 bool changed = *overflow != aOverflowAreas;
10980 *overflow = aOverflowAreas;
10981
10982 // Don't bother with converting to the deltas form if we already
10983 // have a property.
10984 return changed;
10985 }
10986
10987 const nsRect& vis = aOverflowAreas.InkOverflow();
10988 uint32_t l = -vis.x, // left edge: positive delta is leftwards
10989 t = -vis.y, // top: positive is upwards
10990 r = vis.XMost() - mRect.width, // right: positive is rightwards
10991 b = vis.YMost() - mRect.height; // bottom: positive is downwards
10992 if (aOverflowAreas.ScrollableOverflow().IsEqualEdges(
10993 nsRect(nsPoint(0, 0), GetSize())) &&
10994 l <= InkOverflowDeltas::kMax && t <= InkOverflowDeltas::kMax &&
10995 r <= InkOverflowDeltas::kMax && b <= InkOverflowDeltas::kMax &&
10996 // we have to check these against zero because we *never* want to
10997 // set a frame as having no overflow in this function. This is
10998 // because FinishAndStoreOverflow calls this function prior to
10999 // SetRect based on whether the overflow areas match aNewSize.
11000 // In the case where the overflow areas exactly match mRect but
11001 // do not match aNewSize, we need to store overflow in a property
11002 // so that our eventual SetRect/SetSize will know that it has to
11003 // reset our overflow areas.
11004 (l | t | r | b) != 0) {
11005 InkOverflowDeltas oldDeltas = mOverflow.mInkOverflowDeltas;
11006 // It's a "small" overflow area so we store the deltas for each edge
11007 // directly in the frame, rather than allocating a separate rect.
11008 // If they're all zero, that's fine; we're setting things to
11009 // no-overflow.
11010 mOverflow.mInkOverflowDeltas.mLeft = l;
11011 mOverflow.mInkOverflowDeltas.mTop = t;
11012 mOverflow.mInkOverflowDeltas.mRight = r;
11013 mOverflow.mInkOverflowDeltas.mBottom = b;
11014 // There was no scrollable overflow before, and there isn't now.
11015 return oldDeltas != mOverflow.mInkOverflowDeltas;
11016 } else {
11017 bool changed =
11018 !aOverflowAreas.ScrollableOverflow().IsEqualEdges(
11019 nsRect(nsPoint(0, 0), GetSize())) ||
11020 !aOverflowAreas.InkOverflow().IsEqualEdges(InkOverflowFromDeltas());
11021
11022 // it's a large overflow area that we need to store as a property
11023 mOverflow.mType = OverflowStorageType::Large;
11024 AddProperty(OverflowAreasProperty(), new OverflowAreas(aOverflowAreas));
11025 return changed;
11026 }
11027}
11028
11029enum class ApplyTransform : bool { No, Yes };
11030
11031/**
11032 * Compute the outline inner rect (so without outline-width and outline-offset)
11033 * of aFrame, maybe iterating over its descendants, in aFrame's coordinate space
11034 * or its post-transform coordinate space (depending on aApplyTransform).
11035 */
11036static nsRect ComputeOutlineInnerRect(
11037 nsIFrame* aFrame, ApplyTransform aApplyTransform, bool& aOutValid,
11038 const nsSize* aSizeOverride = nullptr,
11039 const OverflowAreas* aOverflowOverride = nullptr) {
11040 const nsRect bounds(nsPoint(0, 0),
11041 aSizeOverride ? *aSizeOverride : aFrame->GetSize());
11042
11043 // The SVG container frames besides SVGTextFrame do not maintain
11044 // an accurate mRect. It will make the outline be larger than
11045 // we expect, we need to make them narrow to their children's outline.
11046 // aOutValid is set to false if the returned nsRect is not valid
11047 // and should not be included in the outline rectangle.
11048 aOutValid = !aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT) ||
11049 !aFrame->IsSVGContainerFrame() || aFrame->IsSVGTextFrame();
11050
11051 nsRect u;
11052
11053 if (!aFrame->FrameMaintainsOverflow()) {
11054 return u;
11055 }
11056
11057 // Start from our border-box, transformed. See comment below about
11058 // transform of children.
11059 bool doTransform =
11060 aApplyTransform == ApplyTransform::Yes && aFrame->IsTransformed();
11061 TransformReferenceBox boundsRefBox(nullptr, bounds);
11062 if (doTransform) {
11063 u = nsDisplayTransform::TransformRect(bounds, aFrame, boundsRefBox);
11064 } else {
11065 u = bounds;
11066 }
11067
11068 if (aOutValid && !StaticPrefs::layout_outline_include_overflow()) {
11069 return u;
11070 }
11071
11072 // Only iterate through the children if the overflow areas suggest
11073 // that we might need to, and if the frame doesn't clip its overflow
11074 // anyway.
11075 if (aOverflowOverride) {
11076 if (!doTransform && bounds.IsEqualEdges(aOverflowOverride->InkOverflow()) &&
11077 bounds.IsEqualEdges(aOverflowOverride->ScrollableOverflow())) {
11078 return u;
11079 }
11080 } else {
11081 if (!doTransform && bounds.IsEqualEdges(aFrame->InkOverflowRect()) &&
11082 bounds.IsEqualEdges(aFrame->ScrollableOverflowRect())) {
11083 return u;
11084 }
11085 }
11086 const nsStyleDisplay* disp = aFrame->StyleDisplay();
11087 LayoutFrameType fType = aFrame->Type();
11088 if (fType == LayoutFrameType::ScrollContainer ||
11089 fType == LayoutFrameType::ListControl ||
11090 fType == LayoutFrameType::SVGOuterSVG) {
11091 return u;
11092 }
11093
11094 auto overflowClipAxes = aFrame->ShouldApplyOverflowClipping(disp);
11095 auto overflowClipMargin = aFrame->OverflowClipMargin(
11096 overflowClipAxes, /* aAllowNegative = */ false);
11097 if (overflowClipAxes == kPhysicalAxesBoth && overflowClipMargin.IsAllZero()) {
11098 return u;
11099 }
11100
11101 const nsStyleEffects* effects = aFrame->StyleEffects();
11102 Maybe<nsRect> clipPropClipRect =
11103 aFrame->GetClipPropClipRect(disp, effects, bounds.Size());
11104
11105 // Iterate over all children except pop-up, absolutely-positioned,
11106 // float, and overflow ones.
11107 const FrameChildListIDs skip = {FrameChildListID::Absolute,
11108 FrameChildListID::Float,
11109 FrameChildListID::Overflow};
11110 for (const auto& [list, listID] : aFrame->ChildLists()) {
11111 if (skip.contains(listID)) {
11112 continue;
11113 }
11114
11115 for (nsIFrame* child : list) {
11116 if (child->IsPlaceholderFrame()) {
11117 continue;
11118 }
11119
11120 // Note that passing ApplyTransform::Yes when
11121 // child->Combines3DTransformWithAncestors() returns true is incorrect if
11122 // our aApplyTransform is No... but the opposite would be as well.
11123 // This is because elements within a preserve-3d scene are always
11124 // transformed up to the top of the scene. This means we don't have a
11125 // mechanism for getting a transform up to an intermediate point within
11126 // the scene. We choose to over-transform rather than under-transform
11127 // because this is consistent with other overflow areas.
11128 bool validRect = true;
11129 nsRect childRect =
11130 ComputeOutlineInnerRect(child, ApplyTransform::Yes, validRect) +
11131 child->GetPosition();
11132
11133 if (!validRect) {
11134 continue;
11135 }
11136
11137 if (clipPropClipRect) {
11138 // Intersect with the clip before transforming.
11139 childRect.IntersectRect(childRect, *clipPropClipRect);
11140 }
11141
11142 // Note that we transform each child separately according to
11143 // aFrame's transform, and then union, which gives a different
11144 // (smaller) result from unioning and then transforming the
11145 // union. This doesn't match the way we handle overflow areas
11146 // with 2-D transforms, though it does match the way we handle
11147 // overflow areas in preserve-3d 3-D scenes.
11148 if (doTransform && !child->Combines3DTransformWithAncestors()) {
11149 childRect =
11150 nsDisplayTransform::TransformRect(childRect, aFrame, boundsRefBox);
11151 }
11152
11153 // If a SVGContainer has a non-SVGContainer child, we assign
11154 // its child's outline to this SVGContainer directly.
11155 if (!aOutValid && validRect) {
11156 u = childRect;
11157 aOutValid = true;
11158 } else {
11159 u = u.UnionEdges(childRect);
11160 }
11161 }
11162 }
11163
11164 if (!overflowClipAxes.isEmpty()) {
11165 OverflowAreas::ApplyOverflowClippingOnRect(u, bounds, overflowClipAxes,
11166 overflowClipMargin);
11167 }
11168 return u;
11169}
11170
11171static void ComputeAndIncludeOutlineArea(nsIFrame* aFrame,
11172 OverflowAreas& aOverflowAreas,
11173 const nsSize& aNewSize) {
11174 const nsStyleOutline* outline = aFrame->StyleOutline();
11175 if (!outline->ShouldPaintOutline()) {
11176 return;
11177 }
11178
11179 // When the outline property is set on a :-moz-block-inside-inline-wrapper
11180 // pseudo-element, it inherited that outline from the inline that was broken
11181 // because it contained a block. In that case, we don't want a really wide
11182 // outline if the block inside the inline is narrow, so union the actual
11183 // contents of the anonymous blocks.
11184 nsIFrame* frameForArea = aFrame;
11185 do {
11186 PseudoStyleType pseudoType = frameForArea->Style()->GetPseudoType();
11187 if (pseudoType != PseudoStyleType::MozBlockInsideInlineWrapper) {
11188 break;
11189 }
11190 // If we're done, we really want it and all its later siblings.
11191 frameForArea = frameForArea->PrincipalChildList().FirstChild();
11192 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"
, 11192); MOZ_PretendNoReturn(); } } while (0)
;
11193 } while (frameForArea);
11194
11195 // Find the union of the border boxes of all descendants, or in
11196 // the block-in-inline case, all descendants we care about.
11197 //
11198 // Note that the interesting perspective-related cases are taken
11199 // care of by the code that handles those issues for overflow
11200 // calling FinishAndStoreOverflow again, which in turn calls this
11201 // function again. We still need to deal with preserve-3d a bit.
11202 nsRect innerRect;
11203 bool validRect = false;
11204 if (frameForArea == aFrame) {
11205 innerRect = ComputeOutlineInnerRect(aFrame, ApplyTransform::No, validRect,
11206 &aNewSize, &aOverflowAreas);
11207 } else {
11208 for (; frameForArea; frameForArea = frameForArea->GetNextSibling()) {
11209 nsRect r =
11210 ComputeOutlineInnerRect(frameForArea, ApplyTransform::Yes, validRect);
11211
11212 // Adjust for offsets transforms up to aFrame's pre-transform
11213 // (i.e., normal) coordinate space; see comments in
11214 // UnionBorderBoxes for some of the subtlety here.
11215 for (nsIFrame *f = frameForArea, *parent = f->GetParent();
11216 /* see middle of loop */; f = parent, parent = f->GetParent()) {
11217 r += f->GetPosition();
11218 if (parent == aFrame) {
11219 break;
11220 }
11221 if (parent->IsTransformed() && !f->Combines3DTransformWithAncestors()) {
11222 TransformReferenceBox refBox(parent);
11223 r = nsDisplayTransform::TransformRect(r, parent, refBox);
11224 }
11225 }
11226
11227 innerRect.UnionRect(innerRect, r);
11228 }
11229 }
11230
11231 // Keep this code in sync with nsDisplayOutline::GetInnerRect.
11232 if (innerRect == aFrame->GetRectRelativeToSelf()) {
11233 aFrame->RemoveProperty(nsIFrame::OutlineInnerRectProperty());
11234 } else {
11235 aFrame->SetOrUpdateDeletableProperty(nsIFrame::OutlineInnerRectProperty(),
11236 innerRect);
11237 }
11238
11239 nsRect outerRect(innerRect);
11240 outerRect.Inflate(outline->EffectiveOffsetFor(outerRect));
11241
11242 if (outline->mOutlineStyle.IsAuto()) {
11243 nsPresContext* pc = aFrame->PresContext();
11244
11245 pc->Theme()->GetWidgetOverflow(pc->DeviceContext(), aFrame,
11246 StyleAppearance::FocusOutline, &outerRect);
11247 } else {
11248 outerRect.Inflate(outline->mOutlineWidth);
11249 }
11250
11251 nsRect& vo = aOverflowAreas.InkOverflow();
11252 vo = vo.UnionEdges(innerRect.Union(outerRect));
11253}
11254
11255bool nsIFrame::FinishAndStoreOverflow(OverflowAreas& aOverflowAreas,
11256 nsSize aNewSize,
11257 const nsStyleDisplay* aStyleDisplay) {
11258 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", 11259); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "FrameMaintainsOverflow()" ") (" "Don't call - overflow rects not maintained on these SVG frames"
")"); do { MOZ_CrashSequence(__null, 11259); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
11259 "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", 11259); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "FrameMaintainsOverflow()" ") (" "Don't call - overflow rects not maintained on these SVG frames"
")"); do { MOZ_CrashSequence(__null, 11259); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11260
11261 const nsStyleDisplay* disp = StyleDisplayWithOptionalParam(aStyleDisplay);
11262 bool hasTransform = IsTransformed();
11263
11264 nsRect bounds(nsPoint(), aNewSize);
11265 // Store the passed in overflow area if we are a preserve-3d frame or we have
11266 // a transform, and it's not just the frame bounds.
11267 if (hasTransform || Combines3DTransformWithAncestors()) {
11268 if (!aOverflowAreas.InkOverflow().IsEqualEdges(bounds) ||
11269 !aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds)) {
11270 SetOrUpdateDeletableProperty(nsIFrame::InitialOverflowProperty(),
11271 aOverflowAreas);
11272 } else {
11273 RemoveProperty(nsIFrame::InitialOverflowProperty());
11274 }
11275#ifdef DEBUG1
11276 SetProperty(nsIFrame::DebugInitialOverflowPropertyApplied(), true);
11277#endif
11278 } else {
11279#ifdef DEBUG1
11280 RemoveProperty(nsIFrame::DebugInitialOverflowPropertyApplied());
11281#endif
11282 }
11283
11284 const nsSize oldSize = mRect.Size();
11285 const bool sizeChanged = oldSize != aNewSize;
11286
11287 // Our frame size may not have been computed and set yet, but code under
11288 // functions such as ComputeEffectsRect (which we're about to call) use the
11289 // values that are stored in our frame rect to compute their results. We
11290 // need the results from those functions to be based on the frame size that
11291 // we *will* have, so we temporarily set our frame size here before calling
11292 // those functions.
11293 //
11294 // XXX Someone should document here why we revert the frame size before we
11295 // return rather than just leaving it set.
11296 //
11297 // We pass false here to avoid invalidating display items for this temporary
11298 // change. We sometimes reflow frames multiple times, with the final size
11299 // being the same as the initial. The single call to SetSize after reflow is
11300 // done will take care of invalidating display items if the size has actually
11301 // changed.
11302 SetSize(aNewSize, false);
11303
11304 if (sizeChanged && ChildrenHavePerspective(disp) &&
11305 RecomputePerspectiveChildrenOverflow(this)) {
11306 aOverflowAreas.SetAllTo(bounds);
11307 DebugOnly<bool> ok = ComputeCustomOverflow(aOverflowAreas);
11308 // ComputeCustomOverflow() should not return false, when
11309 // FrameMaintainsOverflow() returns true.
11310 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", 11310); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "ok" ") (" "FrameMaintainsOverflow() != ComputeCustomOverflow()"
")"); do { MOZ_CrashSequence(__null, 11310); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11311 UnionChildOverflow(aOverflowAreas);
11312 }
11313
11314 const auto overflowClipAxes = ShouldApplyOverflowClipping(disp);
11315
11316 // This is now called FinishAndStoreOverflow() instead of
11317 // StoreOverflow() because frame-generic ways of adding overflow
11318 // can happen here, e.g. CSS2 outline and native theme.
11319 // If the overflow area width or height is nscoord_MAX, then a saturating
11320 // union may have encountered an overflow, so the overflow may not contain the
11321 // frame border-box. Don't warn in that case.
11322 // Don't warn for SVG either, since SVG doesn't need the overflow area
11323 // to contain the frame bounds.
11324#ifdef DEBUG1
11325 for (const auto otype : AllOverflowTypes()) {
11326 const nsRect& r = aOverflowAreas.Overflow(otype);
11327 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", 11331); MOZ_PretendNoReturn
(); } } while (0)
11328 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", 11331); MOZ_PretendNoReturn
(); } } while (0)
11329 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", 11331); MOZ_PretendNoReturn
(); } } while (0)
11330 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", 11331); MOZ_PretendNoReturn
(); } } while (0)
11331 "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", 11331); MOZ_PretendNoReturn
(); } } while (0)
;
11332 }
11333#endif
11334
11335 // Overflow area must always include the frame's top-left and bottom-right,
11336 // even if the frame rect is empty (so we can scroll to those positions).
11337 const bool shouldIncludeBounds = [&] {
11338 if (aNewSize.width == 0 && IsInlineFrame()) {
11339 // Pending a real fix for bug 426879, don't do this for inline frames with
11340 // zero width.
11341 return false;
11342 }
11343 if (HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
11344 // Do not do this for SVG either, since it will usually massively increase
11345 // the area unnecessarily (except for SVG that applies clipping, since
11346 // that's the pre-existing behavior, and breaks pre-rendering otherwise).
11347 // FIXME(bug 1770704): This check most likely wants to be removed or check
11348 // for specific frame types at least.
11349 return !overflowClipAxes.isEmpty();
11350 }
11351 return true;
11352 }();
11353
11354 if (shouldIncludeBounds) {
11355 for (const auto otype : AllOverflowTypes()) {
11356 nsRect& o = aOverflowAreas.Overflow(otype);
11357 o = o.UnionEdges(bounds);
11358 }
11359 }
11360
11361 // If we clip our children, clear accumulated overflow area in the affected
11362 // dimension(s). The children are actually clipped to the padding-box, but
11363 // since the overflow area should include the entire border-box, just set it
11364 // to the border-box size here.
11365 if (!overflowClipAxes.isEmpty()) {
11366 aOverflowAreas.ApplyClipping(
11367 bounds, overflowClipAxes,
11368 OverflowClipMargin(overflowClipAxes, /* aAllowNegative = */ false));
11369 }
11370
11371 ComputeAndIncludeOutlineArea(this, aOverflowAreas, aNewSize);
11372
11373 // Nothing in here should affect scrollable overflow.
11374 aOverflowAreas.InkOverflow() =
11375 ComputeEffectsRect(this, aOverflowAreas.InkOverflow(), aNewSize);
11376
11377 // Absolute position clipping
11378 const nsStyleEffects* effects = StyleEffects();
11379 Maybe<nsRect> clipPropClipRect = GetClipPropClipRect(disp, effects, aNewSize);
11380 if (clipPropClipRect) {
11381 for (const auto otype : AllOverflowTypes()) {
11382 nsRect& o = aOverflowAreas.Overflow(otype);
11383 o.IntersectRect(o, *clipPropClipRect);
11384 }
11385 }
11386
11387 /* If we're transformed, transform the overflow rect by the current
11388 * transformation. */
11389 if (hasTransform) {
11390 SetProperty(nsIFrame::PreTransformOverflowAreasProperty(),
11391 new OverflowAreas(aOverflowAreas));
11392
11393 if (Combines3DTransformWithAncestors()) {
11394 /* If we're a preserve-3d leaf frame, then our pre-transform overflow
11395 * should be correct. Our post-transform overflow is empty though, because
11396 * we only contribute to the overflow area of the preserve-3d root frame.
11397 * If we're an intermediate frame then the pre-transform overflow should
11398 * contain all our non-preserve-3d children, which is what we want. Again
11399 * we have no post-transform overflow.
11400 */
11401 aOverflowAreas.SetAllTo(nsRect());
11402 } else {
11403 TransformReferenceBox refBox(this);
11404 for (const auto otype : AllOverflowTypes()) {
11405 nsRect& o = aOverflowAreas.Overflow(otype);
11406 // If the overflow is empty, it can still have a non-zero length in one
11407 // axis. Transforming such axis-bound rect can cause the resulting rect
11408 // to be non-empty, e.g. by rotating the rect.
11409 if (!o.IsEmpty()) {
11410 o = nsDisplayTransform::TransformRect(o, this, refBox);
11411 }
11412 }
11413
11414 /* If we're the root of the 3d context, then we want to include the
11415 * overflow areas of all the participants. This won't have happened yet as
11416 * the code above set their overflow area to empty. Manually collect these
11417 * overflow areas now.
11418 */
11419 if (Extend3DContext(disp, effects)) {
11420 ComputePreserve3DChildrenOverflow(aOverflowAreas);
11421 }
11422 }
11423 } else {
11424 RemoveProperty(nsIFrame::PreTransformOverflowAreasProperty());
11425 }
11426
11427 /* Revert the size change in case some caller is depending on this. */
11428 SetSize(oldSize, false);
11429
11430 bool anyOverflowChanged;
11431 if (aOverflowAreas != OverflowAreas(bounds, bounds)) {
11432 anyOverflowChanged = SetOverflowAreas(aOverflowAreas);
11433 } else {
11434 anyOverflowChanged = ClearOverflowRects();
11435 }
11436
11437 if (anyOverflowChanged) {
11438 SVGObserverUtils::InvalidateDirectRenderingObservers(this);
11439 if (nsBlockFrame* block = do_QueryFrame(this)) {
11440 // NOTE(emilio): we need to use BeforeReflow::Yes, because we want to
11441 // invalidate in cases where we _used_ to have an overflow marker and no
11442 // longer do.
11443 if (TextOverflow::CanHaveOverflowMarkers(
11444 block, TextOverflow::BeforeReflow::Yes)) {
11445 DiscardDisplayItems(this, [](nsDisplayItem* aItem) {
11446 return aItem->GetType() == DisplayItemType::TYPE_TEXT_OVERFLOW;
11447 });
11448 SchedulePaint(PAINT_DEFAULT);
11449 }
11450 }
11451 }
11452 return anyOverflowChanged;
11453}
11454
11455bool nsIFrame::RecomputePerspectiveChildrenOverflow(
11456 const nsIFrame* aStartFrame) {
11457 bool changed = false;
11458 for (const auto& childList : ChildLists()) {
11459 for (nsIFrame* child : childList.mList) {
11460 if (!child->FrameMaintainsOverflow()) {
11461 continue; // frame does not maintain overflow rects
11462 }
11463 if (child->HasPerspective()) {
11464 OverflowAreas* initialOverflow =
11465 child->GetProperty(nsIFrame::InitialOverflowProperty());
11466 const nsRect bounds(nsPoint(), child->GetSize());
11467 OverflowAreas overflow;
11468 if (initialOverflow) {
11469 overflow = *initialOverflow;
11470 } else {
11471 overflow.SetAllTo(bounds);
11472 }
11473 changed |= child->FinishAndStoreOverflow(overflow, bounds.Size());
11474 } else if (child->GetContent() == aStartFrame->GetContent() ||
11475 child->GetClosestFlattenedTreeAncestorPrimaryFrame() ==
11476 aStartFrame) {
11477 // If a frame is using perspective, then the size used to compute
11478 // perspective-origin is the size of the frame belonging to its parent
11479 // style. We must find any descendant frames using our size
11480 // (by recursing into frames that have the same containing block)
11481 // to update their overflow rects too.
11482 if (child->RecomputePerspectiveChildrenOverflow(aStartFrame)) {
11483 changed = true;
11484 // Update the overflow for the child to take any changes from its
11485 // children into account.
11486 child->UpdateOverflow();
11487 }
11488 }
11489 }
11490 }
11491 return changed;
11492}
11493
11494void nsIFrame::ComputePreserve3DChildrenOverflow(
11495 OverflowAreas& aOverflowAreas) {
11496 // Find all descendants that participate in the 3d context, and include their
11497 // overflow. These descendants have an empty overflow, so won't have been
11498 // included in the normal overflow calculation. Any children that don't
11499 // participate have normal overflow, so will have been included already.
11500
11501 for (const auto& childList : ChildLists()) {
11502 for (nsIFrame* child : childList.mList) {
11503 // If this child participates in the 3d context, then take the
11504 // pre-transform region (which contains all descendants that aren't
11505 // participating in the 3d context) and transform it into the 3d context
11506 // root coordinate space.
11507 if (child->Combines3DTransformWithAncestors()) {
11508 OverflowAreas childOverflow = child->GetOverflowAreasRelativeToSelf();
11509 TransformReferenceBox refBox(child);
11510 for (const auto otype : AllOverflowTypes()) {
11511 nsRect& o = childOverflow.Overflow(otype);
11512 o = nsDisplayTransform::TransformRect(o, child, refBox);
11513 }
11514
11515 aOverflowAreas.UnionWith(childOverflow);
11516
11517 // If this child also extends the 3d context, then recurse into it
11518 // looking for more participants.
11519 if (child->Extend3DContext()) {
11520 child->ComputePreserve3DChildrenOverflow(aOverflowAreas);
11521 }
11522 }
11523 }
11524 }
11525}
11526
11527bool nsIFrame::ZIndexApplies() const {
11528 return StyleDisplay()->IsPositionedStyle() || IsFlexOrGridItem() ||
11529 IsMenuPopupFrame();
11530}
11531
11532Maybe<int32_t> nsIFrame::ZIndex() const {
11533 if (!ZIndexApplies()) {
11534 return Nothing();
11535 }
11536 const auto& zIndex = StylePosition()->mZIndex;
11537 if (zIndex.IsAuto()) {
11538 return Nothing();
11539 }
11540 return Some(zIndex.AsInteger());
11541}
11542
11543bool nsIFrame::IsScrollAnchor(ScrollAnchorContainer** aOutContainer) {
11544 if (!mInScrollAnchorChain) {
11545 return false;
11546 }
11547
11548 nsIFrame* f = this;
11549
11550 // FIXME(emilio, bug 1629280): We should find a non-null anchor if we have the
11551 // flag set, but bug 1629280 makes it so that we cannot really assert it /
11552 // make this just a `while (true)`, and uncomment the below assertion.
11553 while (auto* container = ScrollAnchorContainer::FindFor(f)) {
11554 // MOZ_ASSERT(f->IsInScrollAnchorChain());
11555 if (nsIFrame* anchor = container->AnchorNode()) {
11556 if (anchor != this) {
11557 return false;
11558 }
11559 if (aOutContainer) {
11560 *aOutContainer = container;
11561 }
11562 return true;
11563 }
11564
11565 f = container->Frame();
11566 }
11567
11568 return false;
11569}
11570
11571bool nsIFrame::IsInScrollAnchorChain() const { return mInScrollAnchorChain; }
11572
11573void nsIFrame::SetInScrollAnchorChain(bool aInChain) {
11574 mInScrollAnchorChain = aInChain;
11575}
11576
11577uint32_t nsIFrame::GetDepthInFrameTree() const {
11578 uint32_t result = 0;
11579 for (nsContainerFrame* ancestor = GetParent(); ancestor;
11580 ancestor = ancestor->GetParent()) {
11581 result++;
11582 }
11583 return result;
11584}
11585
11586/**
11587 * This function takes a frame that is part of a block-in-inline split,
11588 * and _if_ that frame is an anonymous block created by an ib split it
11589 * returns the block's preceding inline. This is needed because the
11590 * split inline's style is the parent of the anonymous block's style.
11591 *
11592 * If aFrame is not an anonymous block, null is returned.
11593 */
11594static nsIFrame* GetIBSplitSiblingForAnonymousBlock(const nsIFrame* aFrame) {
11595 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", 11595); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aFrame" ") (" "Must have a non-null frame!"
")"); do { MOZ_CrashSequence(__null, 11595); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11596 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"
, 11597); MOZ_PretendNoReturn(); } } while (0)
11597 "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"
, 11597); MOZ_PretendNoReturn(); } } while (0)
;
11598
11599 if (aFrame->Style()->GetPseudoType() !=
11600 PseudoStyleType::MozBlockInsideInlineWrapper) {
11601 // it's not an anonymous block
11602 return nullptr;
11603 }
11604
11605 // Find the first continuation of the frame. (Ugh. This ends up
11606 // being O(N^2) when it is called O(N) times.)
11607 aFrame = aFrame->FirstContinuation();
11608
11609 /*
11610 * Now look up the nsGkAtoms::IBSplitPrevSibling
11611 * property.
11612 */
11613 nsIFrame* ibSplitSibling =
11614 aFrame->GetProperty(nsIFrame::IBSplitPrevSibling());
11615 NS_ASSERTION(ibSplitSibling, "Broken frame tree?")do { if (!(ibSplitSibling)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "Broken frame tree?", "ibSplitSibling", "./../../../layout/generic/nsIFrame.cpp"
, 11615); MOZ_PretendNoReturn(); } } while (0)
;
11616 return ibSplitSibling;
11617}
11618
11619/**
11620 * Get the parent, corrected for the mangled frame tree resulting from
11621 * having a block within an inline. The result only differs from the
11622 * result of |GetParent| when |GetParent| returns an anonymous block
11623 * that was created for an element that was 'display: inline' because
11624 * that element contained a block.
11625 *
11626 * Also skip anonymous scrolled-content parents; inherit directly from the
11627 * outer scroll frame.
11628 *
11629 * Also skip NAC parents if the child frame is NAC.
11630 */
11631static nsIFrame* GetCorrectedParent(const nsIFrame* aFrame) {
11632 nsIFrame* parent = aFrame->GetParent();
11633 if (!parent) {
11634 return nullptr;
11635 }
11636
11637 // For a table caption we want the _inner_ table frame (unless it's anonymous)
11638 // as the style parent.
11639 if (aFrame->IsTableCaption()) {
11640 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"
, 11640); AnnotateMozCrashReason("MOZ_ASSERT" "(" "parent->IsTableWrapperFrame()"
")"); do { MOZ_CrashSequence(__null, 11640); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11641 nsTableFrame* innerTable =
11642 static_cast<const nsTableWrapperFrame*>(parent)->InnerTableFrame();
11643 if (!innerTable->Style()->IsAnonBox()) {
11644 return innerTable;
11645 }
11646 }
11647
11648 // Table wrappers are always anon boxes; if we're in here for an outer
11649 // table, that actually means its the _inner_ table that wants to
11650 // know its parent. So get the pseudo of the inner in that case.
11651 auto pseudo = aFrame->Style()->GetPseudoType();
11652 if (pseudo == PseudoStyleType::MozTableWrapper) {
11653 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"
, 11653); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aFrame->IsTableWrapperFrame()"
")"); do { MOZ_CrashSequence(__null, 11653); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11654 nsTableFrame* innerTable =
11655 static_cast<const nsTableWrapperFrame*>(aFrame)->InnerTableFrame();
11656 pseudo = innerTable->Style()->GetPseudoType();
11657 }
11658
11659 // Prevent a NAC pseudo-element from inheriting from its NAC parent, and
11660 // inherit from the NAC generator element instead. (We exclude element-backed
11661 // pseudos from this check, since they're not NAC.)
11662 if (pseudo != PseudoStyleType::NotPseudo &&
11663 !PseudoStyle::IsElementBackedPseudo(pseudo)) {
11664 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", 11664); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "aFrame->GetContent()" ")"); do { MOZ_CrashSequence
(__null, 11664); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
11665 Element* element = Element::FromNode(aFrame->GetContent());
11666 // Make sure to only do the fixup for anonymous content pseudos (i.e. avoid
11667 // fixup for ::first-line and such).
11668 if (element && !element->IsRootOfNativeAnonymousSubtree() &&
11669 element->GetPseudoElementType() == aFrame->Style()->GetPseudoType()) {
11670 while (parent->GetContent() &&
11671 !parent->GetContent()->IsRootOfNativeAnonymousSubtree()) {
11672 parent = parent->GetInFlowParent();
11673 }
11674 parent = parent->GetInFlowParent();
11675 }
11676 }
11677
11678 return nsIFrame::CorrectStyleParentFrame(parent, pseudo);
11679}
11680
11681/* static */
11682nsIFrame* nsIFrame::CorrectStyleParentFrame(nsIFrame* aProspectiveParent,
11683 PseudoStyleType aChildPseudo) {
11684 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"
, 11684); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aProspectiveParent"
") (" "Must have a prospective parent" ")"); do { MOZ_CrashSequence
(__null, 11684); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
11685
11686 if (aChildPseudo < PseudoStyleType::NotPseudo) {
11687 // Non-inheriting anon boxes have no style parent frame at all.
11688 if (PseudoStyle::IsNonInheritingAnonBox(aChildPseudo)) {
11689 return nullptr;
11690 }
11691
11692 // Other anon boxes are parented to their actual parent already, except
11693 // for non-elements. Those should not be treated as an anon box.
11694 if (PseudoStyle::IsAnonBox(aChildPseudo) &&
11695 !PseudoStyle::IsNonElement(aChildPseudo)) {
11696 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", 11698); MOZ_PretendNoReturn
(); } } while (0)
11697 "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", 11698); MOZ_PretendNoReturn
(); } } while (0)
11698 "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", 11698); MOZ_PretendNoReturn
(); } } while (0)
;
11699 return aProspectiveParent;
11700 }
11701 }
11702
11703 // Otherwise, walk up out of all anon boxes. For placeholder frames, walk out
11704 // of all pseudo-elements as well. Otherwise ReparentComputedStyle could
11705 // cause style data to be out of sync with the frame tree.
11706 nsIFrame* parent = aProspectiveParent;
11707 do {
11708 if (parent->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
11709 nsIFrame* sibling = GetIBSplitSiblingForAnonymousBlock(parent);
11710
11711 if (sibling) {
11712 // |parent| was a block in an {ib} split; use the inline as
11713 // |the style parent.
11714 parent = sibling;
11715 }
11716 }
11717
11718 if (!parent->Style()->IsPseudoOrAnonBox()) {
11719 return parent;
11720 }
11721
11722 if (!parent->Style()->IsAnonBox() && aChildPseudo != PseudoStyleType::MAX) {
11723 // nsPlaceholderFrame passes in PseudoStyleType::MAX for
11724 // aChildPseudo (even though that's not a valid pseudo-type) just to
11725 // trigger this behavior of walking up to the nearest non-pseudo
11726 // ancestor.
11727 return parent;
11728 }
11729
11730 parent = parent->GetInFlowParent();
11731 } while (parent);
11732
11733 if (aProspectiveParent->Style()->GetPseudoType() ==
11734 PseudoStyleType::MozViewportScroll) {
11735 // aProspectiveParent is the scrollframe for a viewport
11736 // and the kids are the anonymous scrollbars
11737 return aProspectiveParent;
11738 }
11739
11740 // We can get here if the root element is absolutely positioned.
11741 // We can't test for this very accurately, but it can only happen
11742 // when the prospective parent is a canvas frame.
11743 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"
, 11744); MOZ_PretendNoReturn(); } } while (0)
11744 "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"
, 11744); MOZ_PretendNoReturn(); } } while (0)
;
11745 return nullptr;
11746}
11747
11748ComputedStyle* nsIFrame::DoGetParentComputedStyle(
11749 nsIFrame** aProviderFrame) const {
11750 *aProviderFrame = nullptr;
11751
11752 // Handle display:contents and the root frame, when there's no parent frame
11753 // to inherit from.
11754 if (MOZ_LIKELY(mContent)(__builtin_expect(!!(mContent), 1))) {
11755 Element* parentElement = mContent->GetFlattenedTreeParentElement();
11756 if (MOZ_LIKELY(parentElement)(__builtin_expect(!!(parentElement), 1))) {
11757 auto pseudo = Style()->GetPseudoType();
11758 if (pseudo == PseudoStyleType::NotPseudo || !mContent->IsElement() ||
11759 (!PseudoStyle::IsAnonBox(pseudo) &&
11760 // Ensure that we don't return the display:contents style
11761 // of the parent content for pseudos that have the same content
11762 // as their primary frame (like -moz-list-bullets do):
11763 IsPrimaryFrame()) ||
11764 /* if next is true then it's really a request for the table frame's
11765 parent context, see nsTable[Outer]Frame::GetParentComputedStyle. */
11766 pseudo == PseudoStyleType::MozTableWrapper) {
11767 // In some edge cases involving display: contents, we may end up here
11768 // for something that's pending to be reframed. In this case we return
11769 // the wrong style from here (because we've already lost track of it!),
11770 // but it's not a big deal as we're going to be reframed anyway.
11771 if (MOZ_LIKELY(parentElement->HasServoData())(__builtin_expect(!!(parentElement->HasServoData()), 1)) &&
11772 Servo_Element_IsDisplayContents(parentElement)) {
11773 RefPtr<ComputedStyle> style =
11774 ServoStyleSet::ResolveServoStyle(*parentElement);
11775 // NOTE(emilio): we return a weak reference because the element also
11776 // holds the style context alive. This is a bit silly (we could've
11777 // returned a weak ref directly), but it's probably not worth
11778 // optimizing, given this function has just one caller which is rare,
11779 // and this path is rare itself.
11780 return style;
11781 }
11782 }
11783 } else {
11784 if (Style()->GetPseudoType() == PseudoStyleType::NotPseudo) {
11785 // We're a frame for the root. We have no style parent.
11786 return nullptr;
11787 }
11788 }
11789 }
11790
11791 if (!HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
11792 /*
11793 * If this frame is an anonymous block created when an inline with a block
11794 * inside it got split, then the parent style is on its preceding inline. We
11795 * can get to it using GetIBSplitSiblingForAnonymousBlock.
11796 */
11797 if (HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
11798 nsIFrame* ibSplitSibling = GetIBSplitSiblingForAnonymousBlock(this);
11799 if (ibSplitSibling) {
11800 return (*aProviderFrame = ibSplitSibling)->Style();
11801 }
11802 }
11803
11804 // If this frame is one of the blocks that split an inline, we must
11805 // return the "special" inline parent, i.e., the parent that this
11806 // frame would have if we didn't mangle the frame structure.
11807 *aProviderFrame = GetCorrectedParent(this);
11808 return *aProviderFrame ? (*aProviderFrame)->Style() : nullptr;
11809 }
11810
11811 // We're an out-of-flow frame. For out-of-flow frames, we must
11812 // resolve underneath the placeholder's parent. The placeholder is
11813 // reached from the first-in-flow.
11814 nsPlaceholderFrame* placeholder = FirstInFlow()->GetPlaceholderFrame();
11815 if (!placeholder) {
11816 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"
, 11816); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "no placeholder frame for out-of-flow frame"
")"); do { MOZ_CrashSequence(__null, 11816); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11817 *aProviderFrame = GetCorrectedParent(this);
11818 return *aProviderFrame ? (*aProviderFrame)->Style() : nullptr;
11819 }
11820 return placeholder->GetParentComputedStyleForOutOfFlow(aProviderFrame);
11821}
11822
11823void nsIFrame::GetLastLeaf(nsIFrame** aFrame) {
11824 if (!aFrame || !*aFrame) {
11825 return;
11826 }
11827 for (nsIFrame* maybeLastLeaf = (*aFrame)->PrincipalChildList().LastChild();
11828 maybeLastLeaf;) {
11829 nsIFrame* lastChildNotInSubTree = nullptr;
11830 for (nsIFrame* child = maybeLastLeaf; child;
11831 child = child->GetPrevSibling()) {
11832 // ignore anonymous elements, e.g. mozTableAdd* mozTableRemove*
11833 // see bug 278197 comment #12 #13 for details
11834 if (!child->ContentIsRootOfNativeAnonymousSubtree()) {
11835 lastChildNotInSubTree = child;
11836 break;
11837 }
11838 }
11839 if (!lastChildNotInSubTree) {
11840 return;
11841 }
11842 *aFrame = lastChildNotInSubTree;
11843 maybeLastLeaf = lastChildNotInSubTree->PrincipalChildList().LastChild();
11844 }
11845}
11846
11847void nsIFrame::GetFirstLeaf(nsIFrame** aFrame) {
11848 if (!aFrame || !*aFrame) {
11849 return;
11850 }
11851 nsIFrame* child = *aFrame;
11852 while (true) {
11853 child = child->PrincipalChildList().FirstChild();
11854 if (!child) {
11855 return; // nothing to do
11856 }
11857 *aFrame = child;
11858 }
11859}
11860
11861bool nsIFrame::IsFocusableDueToScrollFrame() {
11862 if (!IsScrollContainerFrame()) {
11863 if (nsFieldSetFrame* fieldset = do_QueryFrame(this)) {
11864 // TODO: Do we have similar special-cases like this where we can have
11865 // anonymous scrollable boxes hanging off a primary frame?
11866 if (nsIFrame* inner = fieldset->GetInner()) {
11867 return inner->IsFocusableDueToScrollFrame();
11868 }
11869 }
11870 return false;
11871 }
11872 if (!mContent->IsHTMLElement()) {
11873 return false;
11874 }
11875 if (Style()->IsPseudoElement()) {
11876 return false;
11877 }
11878 if (mContent->IsRootOfNativeAnonymousSubtree()) {
11879 return false;
11880 }
11881 if (!mContent->GetParent()) {
11882 return false;
11883 }
11884 if (mContent->AsElement()->HasAttr(nsGkAtoms::tabindex)) {
11885 return false;
11886 }
11887 // Elements with scrollable view are focusable with script & tabbable
11888 // Otherwise you couldn't scroll them with keyboard, which is an accessibility
11889 // issue (e.g. Section 508 rules) However, we don't make them to be focusable
11890 // with the mouse, because the extra focus outlines are considered
11891 // unnecessarily ugly. When clicked on, the selection position within the
11892 // element will be enough to make them keyboard scrollable.
11893 auto* scrollContainer = static_cast<ScrollContainerFrame*>(this);
11894 if (scrollContainer->GetScrollStyles().IsHiddenInBothDirections()) {
11895 return false;
11896 }
11897 if (scrollContainer->GetScrollRangeForUserInputEvents().IsEqualEdges(
11898 nsRect())) {
11899 return false;
11900 }
11901 return true;
11902}
11903
11904Focusable nsIFrame::IsFocusable(IsFocusableFlags aFlags) {
11905 // cannot focus content in print preview mode. Only the root can be focused,
11906 // but that's handled elsewhere.
11907 if (PresContext()->Type() == nsPresContext::eContext_PrintPreview) {
11908 return {};
11909 }
11910
11911 if (!mContent || !mContent->IsElement()) {
11912 return {};
11913 }
11914
11915 if (!(aFlags & IsFocusableFlags::IgnoreVisibility) &&
11916 !IsVisibleConsideringAncestors()) {
11917 return {};
11918 }
11919
11920 const StyleUserFocus uf = StyleUI()->UserFocus();
11921 if (uf == StyleUserFocus::None) {
11922 return {};
11923 }
11924 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"
, 11924); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!StyleUI()->IsInert()"
") (" "inert implies -moz-user-focus: none" ")"); do { MOZ_CrashSequence
(__null, 11924); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
11925
11926 const PseudoStyleType pseudo = Style()->GetPseudoType();
11927 if (pseudo == PseudoStyleType::MozAnonymousItem) {
11928 return {};
11929 }
11930
11931 Focusable focusable;
11932 if (auto* xul = nsXULElement::FromNode(mContent)) {
11933 // As a legacy special-case, -moz-user-focus controls focusability and
11934 // tabability of XUL elements in some circumstances (which default to
11935 // -moz-user-focus: ignore).
11936 auto focusability = xul->GetXULFocusability(aFlags);
11937 focusable.mFocusable =
11938 focusability.mForcedFocusable.valueOr(uf == StyleUserFocus::Normal);
11939 if (focusable) {
11940 focusable.mTabIndex = focusability.mForcedTabIndexIfFocusable.valueOr(0);
11941 }
11942 } else {
11943 focusable = mContent->IsFocusableWithoutStyle(aFlags);
11944 }
11945
11946 if (focusable) {
11947 return focusable;
11948 }
11949
11950 // If we're focusing with the mouse we never focus scroll areas.
11951 if (!(aFlags & IsFocusableFlags::WithMouse) &&
11952 IsFocusableDueToScrollFrame()) {
11953 return {true, 0};
11954 }
11955
11956 // FIXME(emilio): some callers rely on somewhat broken return values
11957 // (focusable = false, but non-negative tab-index) from
11958 // IsFocusableWithoutStyle (for image maps in particular).
11959 return focusable;
11960}
11961
11962/**
11963 * @return true if this text frame ends with a newline character which is
11964 * treated as preformatted. It should return false if this is not a text frame.
11965 */
11966bool nsIFrame::HasSignificantTerminalNewline() const { return false; }
11967
11968static StyleAlignmentBaseline ConvertSVGDominantBaselineToAlignmentBaseline(
11969 StyleDominantBaseline aDominantBaseline) {
11970 // Most of these are approximate mappings.
11971 switch (aDominantBaseline) {
11972 case StyleDominantBaseline::Hanging:
11973 case StyleDominantBaseline::TextTop:
11974 return StyleAlignmentBaseline::TextTop;
11975 case StyleDominantBaseline::TextBottom:
11976 case StyleDominantBaseline::Ideographic:
11977 return StyleAlignmentBaseline::TextBottom;
11978 case StyleDominantBaseline::Central:
11979 case StyleDominantBaseline::Middle:
11980 case StyleDominantBaseline::Mathematical:
11981 return StyleAlignmentBaseline::Middle;
11982 case StyleDominantBaseline::Auto:
11983 case StyleDominantBaseline::Alphabetic:
11984 return StyleAlignmentBaseline::Baseline;
11985 default:
11986 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"
, 11986); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "unexpected aDominantBaseline value"
")"); do { MOZ_CrashSequence(__null, 11986); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
11987 return StyleAlignmentBaseline::Baseline;
11988 }
11989}
11990
11991StyleDominantBaseline nsIFrame::DominantBaseline() const {
11992 auto dominantBaseline = StyleVisibility()->mDominantBaseline;
11993 if (dominantBaseline != StyleDominantBaseline::Auto) {
11994 return dominantBaseline;
11995 }
11996 // https://drafts.csswg.org/css-inline-3/#alignment-baseline-property
11997 // > Equivalent to alphabetic in horizontal writing modes and
11998 // > in vertical writing modes when text-orientation is sideways.
11999 // > Equivalent to central in vertical writing modes when
12000 // > text-orientation is mixed or upright.
12001 return GetWritingMode().IsCentralBaseline()
12002 ? StyleDominantBaseline::Central
12003 : StyleDominantBaseline::Alphabetic;
12004}
12005
12006StyleAlignmentBaseline nsIFrame::AlignmentBaseline() const {
12007 if (IsInSVGTextSubtree()) {
12008 // TODO Bug 2010721 - Implement alignment-baseline directly for SVG
12009 // text elements instead of only approximating from dominant-baseline.
12010 auto dominantBaseline = StyleVisibility()->mDominantBaseline;
12011 return ConvertSVGDominantBaselineToAlignmentBaseline(dominantBaseline);
12012 }
12013
12014 if (IsTableCellFrame()) {
12015 // The alignment-baseline / vertical-align property on a table cell is used
12016 // to align its children (see nsTableCellFrame::GetTableCellAlignment), but
12017 // does not affect the alignment of the cell itself within its row.
12018 return StyleAlignmentBaseline::Baseline;
12019 }
12020
12021 if (StyleDisplay()->mAlignmentBaseline == StyleAlignmentBaseline::Baseline) {
12022 // The CSS Inline Layout specification says `alignment-baseline: baseline`
12023 // uses the dominant baseline choice of the parent.
12024 auto dominantBaseline =
12025 GetParent() ? GetParent()->DominantBaseline() : DominantBaseline();
12026 switch (dominantBaseline) {
12027 case StyleDominantBaseline::TextBottom:
12028 return StyleAlignmentBaseline::TextBottom;
12029 case StyleDominantBaseline::Alphabetic:
12030 return StyleAlignmentBaseline::Alphabetic;
12031 case StyleDominantBaseline::Ideographic:
12032 return StyleAlignmentBaseline::Ideographic;
12033 case StyleDominantBaseline::Middle:
12034 return StyleAlignmentBaseline::Middle;
12035 case StyleDominantBaseline::Central:
12036 return StyleAlignmentBaseline::Central;
12037 case StyleDominantBaseline::Mathematical:
12038 return StyleAlignmentBaseline::Mathematical;
12039 case StyleDominantBaseline::Hanging:
12040 return StyleAlignmentBaseline::Hanging;
12041 case StyleDominantBaseline::TextTop:
12042 return StyleAlignmentBaseline::TextTop;
12043 case StyleDominantBaseline::Auto:
12044 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"
, 12044); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Auto is resolved in DominantBaseline()"
")"); do { MOZ_CrashSequence(__null, 12044); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12045 break;
12046 }
12047 }
12048
12049 return StyleDisplay()->mAlignmentBaseline;
12050}
12051
12052void nsIFrame::UpdateStyleOfChildAnonBox(nsIFrame* aChildFrame,
12053 ServoRestyleState& aRestyleState) {
12054#ifdef DEBUG1
12055 nsIFrame* parent = aChildFrame->GetInFlowParent();
12056 if (aChildFrame->IsTableFrame()) {
12057 parent = parent->GetParent();
12058 }
12059 if (parent->IsLineFrame()) {
12060 parent = parent->GetParent();
12061 }
12062 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"
, 12063); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nsLayoutUtils::FirstContinuationOrIBSplitSibling(parent) == this"
") (" "This should only be used for children!" ")"); do { MOZ_CrashSequence
(__null, 12063); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
12063 "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"
, 12063); AnnotateMozCrashReason("MOZ_ASSERT" "(" "nsLayoutUtils::FirstContinuationOrIBSplitSibling(parent) == this"
") (" "This should only be used for children!" ")"); do { MOZ_CrashSequence
(__null, 12063); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
12064#endif // DEBUG
12065 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"
, 12067); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!GetContent() || !aChildFrame->GetContent() || aChildFrame->GetContent() == GetContent()"
") (" "What content node is it a frame for?" ")"); do { MOZ_CrashSequence
(__null, 12067); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
12066 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"
, 12067); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!GetContent() || !aChildFrame->GetContent() || aChildFrame->GetContent() == GetContent()"
") (" "What content node is it a frame for?" ")"); do { MOZ_CrashSequence
(__null, 12067); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
12067 "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"
, 12067); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!GetContent() || !aChildFrame->GetContent() || aChildFrame->GetContent() == GetContent()"
") (" "What content node is it a frame for?" ")"); do { MOZ_CrashSequence
(__null, 12067); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
12068 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"
, 12069); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aChildFrame->GetPrevContinuation()"
") (" "Only first continuations should end up here" ")"); do
{ MOZ_CrashSequence(__null, 12069); __attribute__((nomerge))
::abort(); } while (false); } } while (false)
12069 "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"
, 12069); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!aChildFrame->GetPrevContinuation()"
") (" "Only first continuations should end up here" ")"); do
{ MOZ_CrashSequence(__null, 12069); __attribute__((nomerge))
::abort(); } while (false); } } while (false)
;
12070
12071 // We could force the caller to pass in the pseudo, since some callers know it
12072 // statically... But this API is a bit nicer.
12073 auto pseudo = aChildFrame->Style()->GetPseudoType();
12074 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", 12074); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "PseudoStyle::IsAnonBox(pseudo)" ") (" "Child is not an anon box?"
")"); do { MOZ_CrashSequence(__null, 12074); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12075 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"
, 12076); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!PseudoStyle::IsNonInheritingAnonBox(pseudo)"
") (" "Why did the caller bother calling us?" ")"); do { MOZ_CrashSequence
(__null, 12076); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
12076 "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"
, 12076); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!PseudoStyle::IsNonInheritingAnonBox(pseudo)"
") (" "Why did the caller bother calling us?" ")"); do { MOZ_CrashSequence
(__null, 12076); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
12077
12078 // Anon boxes inherit from their parent; that's us.
12079 RefPtr<ComputedStyle> newContext =
12080 aRestyleState.StyleSet().ResolveInheritingAnonymousBoxStyle(pseudo,
12081 Style());
12082
12083 nsChangeHint childHint =
12084 UpdateStyleOfOwnedChildFrame(aChildFrame, newContext, aRestyleState);
12085
12086 // Now that we've updated the style on aChildFrame, check whether it itself
12087 // has anon boxes to deal with.
12088 ServoRestyleState childrenState(*aChildFrame, aRestyleState, childHint,
12089 ServoRestyleState::CanUseHandledHints::Yes);
12090 aChildFrame->UpdateStyleOfOwnedAnonBoxes(childrenState);
12091
12092 // Assuming anon boxes don't have ::backdrop associated with them... if that
12093 // ever changes, we'd need to handle that here, like we do in
12094 // RestyleManager::ProcessPostTraversal
12095
12096 // We do need to handle block pseudo-elements here, though. Especially list
12097 // bullets.
12098 if (nsBlockFrame* block = do_QueryFrame(aChildFrame)) {
12099 block->UpdatePseudoElementStyles(childrenState);
12100 }
12101}
12102
12103/* static */
12104nsChangeHint nsIFrame::UpdateStyleOfOwnedChildFrame(
12105 nsIFrame* aChildFrame, ComputedStyle* aNewComputedStyle,
12106 ServoRestyleState& aRestyleState,
12107 const Maybe<ComputedStyle*>& aContinuationComputedStyle) {
12108 // Figure out whether we have an actual change. It's important that we do
12109 // this, for several reasons:
12110 //
12111 // 1) Even if all the child's changes are due to properties it inherits from
12112 // us, it's possible that no one ever asked us for those style structs and
12113 // hence changes to them aren't reflected in the changes handled at all.
12114 //
12115 // 2) Content can change stylesheets that change the styles of pseudos, and
12116 // extensions can add/remove stylesheets that change the styles of
12117 // anonymous boxes directly.
12118 uint32_t equalStructs; // Not used, actually.
12119 nsChangeHint childHint = aChildFrame->Style()->CalcStyleDifference(
12120 *aNewComputedStyle, &equalStructs);
12121
12122 // If aChildFrame is out of flow, then aRestyleState's "changes handled by the
12123 // parent" doesn't apply to it, because it may have some other parent in the
12124 // frame tree.
12125 if (!aChildFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
12126 childHint = NS_RemoveSubsumedHints(
12127 childHint, aRestyleState.ChangesHandledFor(aChildFrame));
12128 }
12129 if (childHint) {
12130 if (childHint & nsChangeHint_ReconstructFrame) {
12131 // If we generate a reconstruct here, remove any non-reconstruct hints we
12132 // may have already generated for this content.
12133 aRestyleState.ChangeList().PopChangesForContent(
12134 aChildFrame->GetContent());
12135 }
12136 aRestyleState.ChangeList().AppendChange(
12137 aChildFrame, aChildFrame->GetContent(), childHint);
12138 }
12139
12140 aChildFrame->SetComputedStyle(aNewComputedStyle);
12141 ComputedStyle* continuationStyle = aContinuationComputedStyle
12142 ? *aContinuationComputedStyle
12143 : aNewComputedStyle;
12144 for (nsIFrame* kid = aChildFrame->GetNextContinuation(); kid;
12145 kid = kid->GetNextContinuation()) {
12146 kid->SetComputedStyle(continuationStyle);
12147 }
12148
12149 return childHint;
12150}
12151
12152/* static */
12153void nsIFrame::AddInPopupStateBitToDescendants(nsIFrame* aFrame) {
12154 if (!aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP) &&
12155 aFrame->TrackingVisibility()) {
12156 // Assume all frames in popups are visible.
12157 aFrame->IncApproximateVisibleCount();
12158 }
12159
12160 aFrame->AddStateBits(NS_FRAME_IN_POPUP);
12161
12162 for (const auto& childList : aFrame->CrossDocChildLists()) {
12163 for (nsIFrame* child : childList.mList) {
12164 AddInPopupStateBitToDescendants(child);
12165 }
12166 }
12167}
12168
12169/* static */
12170void nsIFrame::RemoveInPopupStateBitFromDescendants(nsIFrame* aFrame) {
12171 if (!aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP) ||
12172 nsLayoutUtils::IsPopup(aFrame)) {
12173 return;
12174 }
12175
12176 aFrame->RemoveStateBits(NS_FRAME_IN_POPUP);
12177
12178 if (aFrame->TrackingVisibility()) {
12179 // We assume all frames in popups are visible, so this decrement balances
12180 // out the increment in AddInPopupStateBitToDescendants above.
12181 aFrame->DecApproximateVisibleCount();
12182 }
12183 for (const auto& childList : aFrame->CrossDocChildLists()) {
12184 for (nsIFrame* child : childList.mList) {
12185 RemoveInPopupStateBitFromDescendants(child);
12186 }
12187 }
12188}
12189
12190void nsIFrame::SetParent(nsContainerFrame* aParent) {
12191 // If our parent is a wrapper anon box, our new parent should be too. We
12192 // _can_ change parent if our parent is a wrapper anon box, because some
12193 // wrapper anon boxes can have continuations.
12194 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"
, 12195); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aParent->Style()->IsInheritingAnonBox()"
")"); do { MOZ_CrashSequence(__null, 12195); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
12195 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"
, 12195); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aParent->Style()->IsInheritingAnonBox()"
")"); do { MOZ_CrashSequence(__null, 12195); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false); } } while
(false)
;
12196
12197 // Note that the current mParent may already be destroyed at this point.
12198 mParent = aParent;
12199 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", 12199); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!mParent || PresShell() == mParent->PresShell()"
")"); do { MOZ_CrashSequence(__null, 12199); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12200
12201 nsFrameState flagsToPropagateSameDoc =
12202 GetStateBits() & (NS_FRAME_CONTAINS_RELATIVE_BSIZE |
12203 NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE);
12204 if (flagsToPropagateSameDoc) {
12205 for (nsIFrame* f = aParent; f; f = f->GetParent()) {
12206 if (f->HasAllStateBits(flagsToPropagateSameDoc)) {
12207 break;
12208 }
12209 f->AddStateBits(flagsToPropagateSameDoc);
12210 }
12211 }
12212
12213 if (HasInvalidFrameInSubtree()) {
12214 for (nsIFrame* f = aParent;
12215 f && !f->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT |
12216 NS_FRAME_IS_NONDISPLAY);
12217 f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
12218 f->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
12219 }
12220 }
12221
12222 if (aParent->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
12223 AddInPopupStateBitToDescendants(this);
12224 } else {
12225 RemoveInPopupStateBitFromDescendants(this);
12226 }
12227
12228 // If our new parent only has invalid children, then we just invalidate
12229 // ourselves too. This is probably faster than clearing the flag all
12230 // the way up the frame tree.
12231 if (aParent->HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT)) {
12232 InvalidateFrame();
12233 } else {
12234 SchedulePaint();
12235 }
12236}
12237
12238bool nsIFrame::IsStackingContext(const nsStyleDisplay* aStyleDisplay,
12239 const nsStyleEffects* aStyleEffects) {
12240 // Properties that influence the output of this function should be handled in
12241 // change_bits_for_longhand as well.
12242 if (HasOpacity(aStyleDisplay, aStyleEffects, nullptr)) {
12243 return true;
12244 }
12245 if (IsTransformed()) {
12246 return true;
12247 }
12248 auto willChange = aStyleDisplay->mWillChange.bits;
12249 if (aStyleDisplay->IsContainPaint() || aStyleDisplay->IsContainLayout() ||
12250 willChange & StyleWillChangeBits::CONTAIN) {
12251 if (SupportsContainLayoutAndPaint()) {
12252 return true;
12253 }
12254 }
12255
12256 if (ForcesStackingContextForViewTransition()) {
12257 return true;
12258 }
12259
12260 // strictly speaking, 'perspective' doesn't require visual atomicity,
12261 // but the spec says it acts like the rest of these
12262 if (aStyleDisplay->HasPerspectiveStyle() ||
12263 willChange & StyleWillChangeBits::PERSPECTIVE) {
12264 if (SupportsCSSTransforms()) {
12265 return true;
12266 }
12267 }
12268 if (!StylePosition()->mZIndex.IsAuto() ||
12269 willChange & StyleWillChangeBits::Z_INDEX) {
12270 if (ZIndexApplies()) {
12271 return true;
12272 }
12273 }
12274 // Elements captured in a view transition during a view transition or whose
12275 // view-transition-name computed value is not none (at any time) form a s
12276 // https://drafts.csswg.org/css-view-transitions-1/#named-and-transitioning
12277 return aStyleEffects->mMixBlendMode != StyleBlend::Normal ||
12278 SVGIntegrationUtils::UsingEffectsForFrame(this) ||
12279 aStyleDisplay->IsPositionForcingStackingContext() ||
12280 aStyleDisplay->mIsolation != StyleIsolation::Auto ||
12281 willChange & StyleWillChangeBits::STACKING_CONTEXT_UNCONDITIONAL;
12282}
12283
12284bool nsIFrame::IsStackingContext() {
12285 return IsStackingContext(StyleDisplay(), StyleEffects());
12286}
12287
12288static bool IsFrameRectScrolledOutOfView(const nsIFrame* aTarget,
12289 const nsRect& aTargetRect,
12290 const nsIFrame* aParent) {
12291 // The ancestor frame we are checking if it clips out aTargetRect relative to
12292 // aTarget.
12293 nsIFrame* clipParent = nullptr;
12294
12295 // find the first scrollable frame or root frame if we are in a fixed pos
12296 // subtree
12297 for (nsIFrame* f = const_cast<nsIFrame*>(aParent); f;
12298 f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
12299 ScrollContainerFrame* scrollContainerFrame = do_QueryFrame(f);
12300 if (scrollContainerFrame) {
12301 clipParent = f;
12302 break;
12303 }
12304 if (f->StyleDisplay()->mPosition == StylePositionProperty::Fixed &&
12305 nsLayoutUtils::IsReallyFixedPos(f)) {
12306 clipParent = f->GetParent();
12307 break;
12308 }
12309 }
12310
12311 if (!clipParent) {
12312 // Even if we couldn't find the nearest scrollable frame, it might mean we
12313 // are in an out-of-process iframe, try to see if |aTarget| frame is
12314 // scrolled out of view in an scrollable frame in a cross-process ancestor
12315 // document.
12316 return nsLayoutUtils::FrameRectIsScrolledOutOfViewInCrossProcess(
12317 aTarget, aTargetRect);
12318 }
12319
12320 nsRect clipRect = clipParent->InkOverflowRectRelativeToSelf();
12321 // We consider that the target is scrolled out if the scrollable (or root)
12322 // frame is empty.
12323 if (clipRect.IsEmpty()) {
12324 return true;
12325 }
12326
12327 nsRect transformedRect = nsLayoutUtils::TransformFrameRectToAncestor(
12328 aTarget, aTargetRect, clipParent);
12329
12330 if (transformedRect.IsEmpty()) {
12331 // If the transformed rect is empty it represents a line or a point that we
12332 // should check is outside the the scrollable rect.
12333 if (transformedRect.x > clipRect.XMost() ||
12334 transformedRect.y > clipRect.YMost() ||
12335 clipRect.x > transformedRect.XMost() ||
12336 clipRect.y > transformedRect.YMost()) {
12337 return true;
12338 }
12339 } else if (!transformedRect.Intersects(clipRect)) {
12340 return true;
12341 }
12342
12343 nsIFrame* parent = clipParent->GetParent();
12344 if (!parent) {
12345 return false;
12346 }
12347
12348 return IsFrameRectScrolledOutOfView(clipParent, transformedRect, parent);
12349}
12350
12351bool nsIFrame::IsScrolledOutOfView() const {
12352 nsRect rect = InkOverflowRectRelativeToSelf();
12353 return IsFrameRectScrolledOutOfView(this, rect, this);
12354}
12355
12356gfx::Matrix nsIFrame::ComputeWidgetTransform() const {
12357 const nsStyleUIReset* uiReset = StyleUIReset();
12358 if (uiReset->mMozWindowTransform.IsNone()) {
12359 return gfx::Matrix();
12360 }
12361
12362 TransformReferenceBox refBox(this);
12363
12364 int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
12365 gfx::Matrix4x4 matrix = nsStyleTransformMatrix::ReadTransforms(
12366 uiReset->mMozWindowTransform, refBox, float(appUnitsPerDevPixel),
12367 nsStyleTransformMatrix::Zoomed::Yes);
12368
12369 // Apply the transform-origin translation to the matrix.
12370 const StyleTransformOrigin& origin = StyleDisplay()->mTransformOrigin;
12371 Point transformOrigin = nsStyleTransformMatrix::Convert2DPosition(
12372 origin.horizontal, origin.vertical, refBox, appUnitsPerDevPixel);
12373 matrix.ChangeBasis(Point3D(transformOrigin.x, transformOrigin.y, 0));
12374
12375 gfx::Matrix result2d;
12376 if (!matrix.CanDraw2D(&result2d)) {
12377 // FIXME: It would be preferable to reject non-2D transforms at parse time.
12378 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"
, 12380)
12379 "-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"
, 12380)
12380 "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"
, 12380)
;
12381 return gfx::Matrix();
12382 }
12383
12384 return result2d;
12385}
12386
12387void nsIFrame::DoUpdateStyleOfOwnedAnonBoxes(ServoRestyleState& aRestyleState) {
12388 // As a special case, we check for {ib}-split block frames here, rather
12389 // than have an nsInlineFrame::AppendDirectlyOwnedAnonBoxes implementation
12390 // that returns them.
12391 //
12392 // (If we did handle them in AppendDirectlyOwnedAnonBoxes, we would have to
12393 // return *all* of the in-flow {ib}-split block frames, not just the first
12394 // one. For restyling, we really just need the first in flow, and the other
12395 // user of the AppendOwnedAnonBoxes API, AllChildIterator, doesn't need to
12396 // know about them at all, since these block frames never create NAC. So we
12397 // avoid any unncessary hashtable lookups for the {ib}-split frames by calling
12398 // UpdateStyleOfOwnedAnonBoxesForIBSplit directly here.)
12399 if (IsInlineFrame()) {
12400 if (HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
12401 static_cast<nsInlineFrame*>(this)->UpdateStyleOfOwnedAnonBoxesForIBSplit(
12402 aRestyleState);
12403 }
12404 return;
12405 }
12406
12407 AutoTArray<OwnedAnonBox, 4> frames;
12408 AppendDirectlyOwnedAnonBoxes(frames);
12409 for (OwnedAnonBox& box : frames) {
12410 if (box.mUpdateStyleFn) {
12411 box.mUpdateStyleFn(this, box.mAnonBoxFrame, aRestyleState);
12412 } else {
12413 UpdateStyleOfChildAnonBox(box.mAnonBoxFrame, aRestyleState);
12414 }
12415 }
12416}
12417
12418/* virtual */
12419void nsIFrame::AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) {
12420 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", 12420); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "!HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES)"
")"); do { MOZ_CrashSequence(__null, 12420); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12421 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"
, 12423); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Subclasses that have directly owned anonymous boxes should override "
"this method!" ")"); do { MOZ_CrashSequence(__null, 12423); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
12422 "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"
, 12423); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Subclasses that have directly owned anonymous boxes should override "
"this method!" ")"); do { MOZ_CrashSequence(__null, 12423); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
12423 "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"
, 12423); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Subclasses that have directly owned anonymous boxes should override "
"this method!" ")"); do { MOZ_CrashSequence(__null, 12423); __attribute__
((nomerge)) ::abort(); } while (false); } } while (false)
;
12424}
12425
12426void nsIFrame::DoAppendOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) {
12427 size_t i = aResult.Length();
12428 AppendDirectlyOwnedAnonBoxes(aResult);
12429
12430 // After appending the directly owned anonymous boxes of this frame to
12431 // aResult above, we need to check each of them to see if they own
12432 // any anonymous boxes themselves. Note that we keep progressing
12433 // through aResult, looking for additional entries in aResult from these
12434 // subsequent AppendDirectlyOwnedAnonBoxes calls. (Thus we can't
12435 // use a ranged for loop here.)
12436
12437 while (i < aResult.Length()) {
12438 nsIFrame* f = aResult[i].mAnonBoxFrame;
12439 if (f->HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES)) {
12440 f->AppendDirectlyOwnedAnonBoxes(aResult);
12441 }
12442 ++i;
12443 }
12444}
12445
12446nsIFrame::CaretPosition::CaretPosition() : mContentOffset(0) {}
12447
12448nsIFrame::CaretPosition::~CaretPosition() = default;
12449
12450bool nsIFrame::HasCSSAnimations() {
12451 auto* collection = AnimationCollection<CSSAnimation>::Get(this);
12452 return collection && !collection->mAnimations.IsEmpty();
12453}
12454
12455bool nsIFrame::HasCSSTransitions() {
12456 auto* collection = AnimationCollection<CSSTransition>::Get(this);
12457 return collection && !collection->mAnimations.IsEmpty();
12458}
12459
12460void nsIFrame::AddSizeOfExcludingThisForTree(nsWindowSizes& aSizes) const {
12461 aSizes.mLayoutFramePropertiesSize +=
12462 mProperties.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
12463
12464 // We don't do this for Gecko because this stuff is stored in the nsPresArena
12465 // and so measured elsewhere.
12466 if (!aSizes.mState.HaveSeenPtr(mComputedStyle)) {
12467 mComputedStyle->AddSizeOfIncludingThis(aSizes,
12468 &aSizes.mLayoutComputedValuesNonDom);
12469 }
12470
12471 for (const auto& childList : ChildLists()) {
12472 for (const nsIFrame* f : childList.mList) {
12473 f->AddSizeOfExcludingThisForTree(aSizes);
12474 }
12475 }
12476}
12477
12478nsRect nsIFrame::GetCompositorHitTestArea(nsDisplayListBuilder* aBuilder) {
12479 nsRect area;
12480
12481 ScrollContainerFrame* scrollContainerFrame =
12482 nsLayoutUtils::GetScrollContainerFrameFor(this);
12483 if (scrollContainerFrame) {
12484 // If this frame is the scrolled frame of a scroll container frame, then we
12485 // need to pick up the area corresponding to the overflow rect as well.
12486 // Otherwise the parts of the overflow that are not occupied by descendants
12487 // get skipped and the APZ code sends touch events to the content underneath
12488 // instead. See https://bugzilla.mozilla.org/show_bug.cgi?id=1127773#c15.
12489 area = ScrollableOverflowRect();
12490 } else {
12491 area = GetRectRelativeToSelf();
12492 }
12493
12494 if (!area.IsEmpty()) {
12495 return area + aBuilder->ToReferenceFrame(this);
12496 }
12497
12498 return area;
12499}
12500
12501CompositorHitTestInfo nsIFrame::GetCompositorHitTestInfo(
12502 nsDisplayListBuilder* aBuilder) {
12503 CompositorHitTestInfo result = CompositorHitTestInvisibleToHit;
12504 if (Style()->PointerEvents() == StylePointerEvents::None) {
12505 return result;
12506 }
12507 return GetCompositorHitTestInfoWithoutPointerEvents(aBuilder);
12508}
12509
12510CompositorHitTestInfo nsIFrame::GetCompositorHitTestInfoWithoutPointerEvents(
12511 nsDisplayListBuilder* aBuilder) {
12512 CompositorHitTestInfo result = CompositorHitTestInvisibleToHit;
12513
12514 if (aBuilder->IsInsidePointerEventsNoneDoc() ||
12515 aBuilder->IsInViewTransitionCapture()) {
12516 // Somewhere up the parent document chain is a subdocument with pointer-
12517 // events:none set on it, or we're getting captured in a view transition.
12518 return result;
12519 }
12520 if (!GetParent()) {
12521 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", 12521); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "IsViewportFrame()" ")"); do { MOZ_CrashSequence
(__null, 12521); __attribute__((nomerge)) ::abort(); } while (
false); } } while (false)
;
12522 // Viewport frames are never event targets, other frames, like canvas
12523 // frames, are the event targets for any regions viewport frames may cover.
12524 return result;
12525 }
12526 if (!StyleVisibility()->IsVisible()) {
12527 return result;
12528 }
12529
12530 // Anything that didn't match the above conditions is visible to hit-testing.
12531 result = CompositorHitTestFlags::eVisibleToHitTest;
12532 SVGUtils::MaskUsage maskUsage = SVGUtils::DetermineMaskUsage(this, false);
12533 if (maskUsage.UsingMaskOrClipPath()) {
12534 // If WebRender is enabled, simple clip-paths can be converted into WR
12535 // clips that WR knows how to hit-test against, so we don't need to mark
12536 // it as an irregular area.
12537 if (!maskUsage.IsSimpleClipShape()) {
12538 result += CompositorHitTestFlags::eIrregularArea;
12539 }
12540 }
12541
12542 if (aBuilder->IsBuildingNonLayerizedScrollbar()) {
12543 // Scrollbars may be painted into a layer below the actual layer they will
12544 // scroll, and therefore wheel events may be dispatched to the outer frame
12545 // instead of the intended scrollframe. To address this, we force a d-t-c
12546 // region on scrollbar frames that won't be placed in their own layer. See
12547 // bug 1213324 for details.
12548 result += CompositorHitTestFlags::eInactiveScrollframe;
12549 } else if (aBuilder->GetAncestorHasApzAwareEventHandler()) {
12550 result += CompositorHitTestFlags::eApzAwareListeners;
12551 } else if (IsRangeFrame()) {
12552 // Range frames handle touch events directly without having a touch listener
12553 // so we need to let APZ know that this area cares about events.
12554 result += CompositorHitTestFlags::eApzAwareListeners;
12555 }
12556
12557 if (aBuilder->IsTouchEventPrefEnabledDoc()) {
12558 // Inherit the touch-action flags from the parent, if there is one. We do
12559 // this because of how the touch-action on a frame combines the touch-action
12560 // from ancestor DOM elements. Refer to the documentation in
12561 // TouchActionHelper.cpp for details; this code is meant to be equivalent to
12562 // that code, but woven into the top-down recursive display list building
12563 // process.
12564 CompositorHitTestInfo inheritedTouchAction =
12565 aBuilder->GetInheritedCompositorHitTestInfo() &
12566 CompositorHitTestTouchActionMask;
12567
12568 nsIFrame* touchActionFrame = this;
12569 if (ScrollContainerFrame* scrollContainerFrame =
12570 nsLayoutUtils::GetScrollContainerFrameFor(this)) {
12571 ScrollStyles ss = scrollContainerFrame->GetScrollStyles();
12572 if (ss.mVertical != StyleOverflow::Hidden ||
12573 ss.mHorizontal != StyleOverflow::Hidden) {
12574 touchActionFrame = scrollContainerFrame;
12575 // On scrollframes, stop inheriting the pan-x and pan-y flags; instead,
12576 // reset them back to zero to allow panning on the scrollframe unless we
12577 // encounter an element that disables it that's inside the scrollframe.
12578 // This is equivalent to the |considerPanning| variable in
12579 // TouchActionHelper.cpp, but for a top-down traversal.
12580 CompositorHitTestInfo panMask(
12581 CompositorHitTestFlags::eTouchActionPanXDisabled,
12582 CompositorHitTestFlags::eTouchActionPanYDisabled);
12583 inheritedTouchAction -= panMask;
12584 }
12585 }
12586
12587 result += inheritedTouchAction;
12588
12589 const StyleTouchAction touchAction = touchActionFrame->UsedTouchAction();
12590 // The CSS allows the syntax auto | none | [pan-x || pan-y] | manipulation
12591 // so we can eliminate some combinations of things.
12592 if (touchAction == StyleTouchAction::AUTO) {
12593 // nothing to do
12594 } else if (touchAction & StyleTouchAction::MANIPULATION) {
12595 result += CompositorHitTestFlags::eTouchActionAnimatingZoomDisabled;
12596 } else {
12597 // This path handles the cases none | [pan-x || pan-y || pinch-zoom] so
12598 // double-tap is disabled in here.
12599 if (!(touchAction & StyleTouchAction::PINCH_ZOOM)) {
12600 result += CompositorHitTestFlags::eTouchActionPinchZoomDisabled;
12601 }
12602
12603 result += CompositorHitTestFlags::eTouchActionAnimatingZoomDisabled;
12604
12605 if (!(touchAction & StyleTouchAction::PAN_X)) {
12606 result += CompositorHitTestFlags::eTouchActionPanXDisabled;
12607 }
12608 if (!(touchAction & StyleTouchAction::PAN_Y)) {
12609 result += CompositorHitTestFlags::eTouchActionPanYDisabled;
12610 }
12611 if (touchAction & StyleTouchAction::NONE) {
12612 // all the touch-action disabling flags will already have been set above
12613 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", 12613); AnnotateMozCrashReason
("MOZ_ASSERT" "(" "result.contains(CompositorHitTestTouchActionMask)"
")"); do { MOZ_CrashSequence(__null, 12613); __attribute__((
nomerge)) ::abort(); } while (false); } } while (false)
;
12614 }
12615 }
12616 }
12617
12618 const Maybe<ScrollDirection> scrollDirection =
12619 aBuilder->GetCurrentScrollbarDirection();
12620 if (scrollDirection.isSome()) {
12621 if (GetContent()->IsXULElement(nsGkAtoms::thumb)) {
12622 const bool thumbGetsLayer = aBuilder->GetCurrentScrollbarTarget() !=
12623 layers::ScrollableLayerGuid::NULL_SCROLL_ID;
12624 if (thumbGetsLayer) {
12625 result += CompositorHitTestFlags::eScrollbarThumb;
12626 } else {
12627 result += CompositorHitTestFlags::eInactiveScrollframe;
12628 }
12629 }
12630
12631 if (*scrollDirection == ScrollDirection::eVertical) {
12632 result += CompositorHitTestFlags::eScrollbarVertical;
12633 }
12634
12635 // includes the ScrollbarFrame, SliderFrame, anything else that
12636 // might be inside the xul:scrollbar
12637 result += CompositorHitTestFlags::eScrollbar;
12638 }
12639
12640 return result;
12641}
12642
12643// Returns true if we can guarantee there is no visible descendants.
12644static bool HasNoVisibleDescendants(const nsIFrame* aFrame) {
12645 for (const auto& childList : aFrame->ChildLists()) {
12646 for (nsIFrame* f : childList.mList) {
12647 if (nsPlaceholderFrame::GetRealFrameFor(f)
12648 ->IsVisibleOrMayHaveVisibleDescendants()) {
12649 return false;
12650 }
12651 }
12652 }
12653 return true;
12654}
12655
12656void nsIFrame::UpdateVisibleDescendantsState() {
12657 if (StyleVisibility()->IsVisible()) {
12658 // Notify invisible ancestors that a visible descendant exists now.
12659 nsIFrame* ancestor;
12660 for (ancestor = GetInFlowParent();
12661 ancestor && !ancestor->StyleVisibility()->IsVisible();
12662 ancestor = ancestor->GetInFlowParent()) {
12663 ancestor->mAllDescendantsAreInvisible = false;
12664 }
12665 } else {
12666 mAllDescendantsAreInvisible = HasNoVisibleDescendants(this);
12667 }
12668}
12669
12670PhysicalAxes nsIFrame::ShouldApplyOverflowClipping(
12671 const nsStyleDisplay* aDisp) const {
12672 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"
, 12672); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aDisp == StyleDisplay()"
") (" "Wrong display struct" ")"); do { MOZ_CrashSequence(__null
, 12672); __attribute__((nomerge)) ::abort(); } while (false)
; } } while (false)
;
12673
12674 if (IsScrollContainerOrSubclass()) {
12675 // Scrollers deal with overflow on their own.
12676 return {};
12677 }
12678
12679 // 'contain:paint', which we handle as 'overflow:clip' here. 'contain:paint'
12680 // should prevent all means of escaping that clipping (e.g. because it forms a
12681 // fixed-pos containing block).
12682 if (aDisp->IsContainPaint() && SupportsContainLayoutAndPaint()) {
12683 return kPhysicalAxesBoth;
12684 }
12685
12686 // and overflow: scrollable that we should interpret as clip
12687 if (aDisp->IsScrollableOverflow()) {
12688 // REVIEW: these are the frame types that set up clipping.
12689 LayoutFrameType type = Type();
12690 switch (type) {
12691 case LayoutFrameType::CheckboxRadio:
12692 case LayoutFrameType::ComboboxControl:
12693 case LayoutFrameType::Progress:
12694 case LayoutFrameType::Range:
12695 case LayoutFrameType::SubDocument:
12696 case LayoutFrameType::SVGForeignObject:
12697 case LayoutFrameType::SVGInnerSVG:
12698 case LayoutFrameType::SVGOuterSVG:
12699 case LayoutFrameType::SVGSymbol:
12700 case LayoutFrameType::Image:
12701 case LayoutFrameType::HTMLVideo:
12702 case LayoutFrameType::TableCell:
12703 return kPhysicalAxesBoth;
12704 case LayoutFrameType::Table:
12705 // Tables, for legacy reasons only clip when hidden in both directions,
12706 // and treat all other scrollable overflow values as `visible`.
12707 // This is (somewhat, since other browsers make this change at
12708 // computed-value time, see bug 1918789) interoperable css2-era
12709 // behavior. We might be able to change this, but not today.
12710 // See layout/reftests/table-overflow/bug785684-x.html
12711 return aDisp->mOverflowX == StyleOverflow::Hidden &&
12712 aDisp->mOverflowY == StyleOverflow::Hidden
12713 ? kPhysicalAxesBoth
12714 : PhysicalAxes();
12715 case LayoutFrameType::TextInput:
12716 // It has an anonymous scroll container frame that handles any overflow.
12717 return PhysicalAxes();
12718 default:
12719 break;
12720 }
12721 if (IsSuppressedScrollableBlockForPrint()) {
12722 return kPhysicalAxesBoth;
12723 }
12724 }
12725
12726 if (aDisp->mOverflowX == StyleOverflow::Clip ||
12727 aDisp->mOverflowY == StyleOverflow::Clip) {
12728 // FIXME: we could use GetViewportScrollStylesOverrideElement() here instead
12729 // if that worked correctly in a print context. (see bug 1654667)
12730 const auto* element = Element::FromNodeOrNull(GetContent());
12731 if (!element ||
12732 !PresContext()->ElementWouldPropagateScrollStyles(*element)) {
12733 PhysicalAxes axes;
12734 if (aDisp->mOverflowX == StyleOverflow::Clip) {
12735 axes += PhysicalAxis::Horizontal;
12736 }
12737 if (aDisp->mOverflowY == StyleOverflow::Clip) {
12738 axes += PhysicalAxis::Vertical;
12739 }
12740 return axes;
12741 }
12742 }
12743
12744 return PhysicalAxes();
12745}
12746
12747bool nsIFrame::IsSuppressedScrollableBlockForPrint() const {
12748 // This condition needs to match the suppressScrollFrame logic in the frame
12749 // constructor.
12750 if (!PresContext()->IsPaginated() || !IsBlockFrame() ||
12751 !StyleDisplay()->IsScrollableOverflow() ||
12752 !StyleDisplay()->IsBlockOutsideStyle() ||
12753 mContent->IsInNativeAnonymousSubtree()) {
12754 return false;
12755 }
12756 if (auto* element = Element::FromNode(mContent);
12757 element && PresContext()->ElementWouldPropagateScrollStyles(*element)) {
12758 return false;
12759 }
12760 return true;
12761}
12762
12763PhysicalAxes nsIFrame::GetAnchorPosCompensatingForScroll() const {
12764 if (!HasAnchorPosReference()) {
12765 return {};
12766 }
12767 const auto* prop = GetProperty(AnchorPosReferences());
12768 if (!prop) {
12769 return {};
12770 }
12771
12772 return prop->CompensatingForScrollAxes();
12773}
12774
12775bool nsIFrame::HasUnreflowedContainerQueryAncestor() const {
12776 // If this frame has done the first reflow, its ancestors are guaranteed to
12777 // have as well.
12778 if (!HasAnyStateBits(NS_FRAME_FIRST_REFLOW) ||
12779 !PresContext()->HasContainerQueryFrames()) {
12780 return false;
12781 }
12782 for (nsIFrame* cur = GetInFlowParent(); cur; cur = cur->GetInFlowParent()) {
12783 if (!cur->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
12784 // Done first reflow from this ancestor up, including query containers.
12785 return false;
12786 }
12787 if (cur->StyleDisplay()->IsQueryContainer()) {
12788 return true;
12789 }
12790 }
12791 // No query container from this frame up to root.
12792 return false;
12793}
12794
12795bool nsIFrame::ShouldBreakBefore(const BreakType aBreakType) const {
12796 const auto* display = StyleDisplay();
12797 return ShouldBreakBetween(display, display->mBreakBefore, aBreakType);
12798}
12799
12800bool nsIFrame::ShouldBreakAfter(const BreakType aBreakType) const {
12801 const auto* display = StyleDisplay();
12802 return ShouldBreakBetween(display, display->mBreakAfter, aBreakType);
12803}
12804
12805bool nsIFrame::ShouldBreakBetween(const nsStyleDisplay* aDisplay,
12806 const StyleBreakBetween aBreakBetween,
12807 const BreakType aBreakType) const {
12808 const bool shouldBreakBetween = [&] {
12809 switch (aBreakBetween) {
12810 case StyleBreakBetween::Always:
12811 return true;
12812 case StyleBreakBetween::Auto:
12813 case StyleBreakBetween::Avoid:
12814 return false;
12815 case StyleBreakBetween::Page:
12816 case StyleBreakBetween::Left:
12817 case StyleBreakBetween::Right:
12818 return aBreakType == BreakType::Page;
12819 }
12820 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"
, 12820); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") ("
"MOZ_ASSERT_UNREACHABLE: " "Unknown break-between value!" ")"
); do { MOZ_CrashSequence(__null, 12820); __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
12821 return false;
12822 }();
12823
12824 if (!shouldBreakBetween) {
12825 return false;
12826 }
12827 if (IsAbsolutelyPositioned(aDisplay)) {
12828 // 'break-before' and 'break-after' properties does not apply to
12829 // absolutely-positioned boxes.
12830 return false;
12831 }
12832 return true;
12833}
12834
12835#ifdef DEBUG1
12836static void GetTagName(nsIFrame* aFrame, nsIContent* aContent, int aResultSize,
12837 char* aResult) {
12838 if (aContent) {
12839 snprintf(aResult, aResultSize, "%s@%p",
12840 nsAtomCString(aContent->NodeInfo()->NameAtom()).get(), aFrame);
12841 } else {
12842 snprintf(aResult, aResultSize, "@%p", aFrame);
12843 }
12844}
12845
12846void nsIFrame::Trace(const char* aMethod, bool aEnter) {
12847 if (NS_FRAME_LOG_TEST(sFrameLogModule, NS_FRAME_TRACE_CALLS)(int(((mozilla::LogModule*)(sFrameLogModule))->Level()) &
(0x1))
) {
12848 char tagbuf[40];
12849 GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
12850 printf_stderr("%s: %s %s", tagbuf, aEnter ? "enter" : "exit", aMethod);
12851 }
12852}
12853
12854void nsIFrame::Trace(const char* aMethod, bool aEnter,
12855 const nsReflowStatus& aStatus) {
12856 if (NS_FRAME_LOG_TEST(sFrameLogModule, NS_FRAME_TRACE_CALLS)(int(((mozilla::LogModule*)(sFrameLogModule))->Level()) &
(0x1))
) {
12857 char tagbuf[40];
12858 GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
12859 printf_stderr("%s: %s %s, status=%scomplete%s", tagbuf,
12860 aEnter ? "enter" : "exit", aMethod,
12861 aStatus.IsIncomplete() ? "not" : "",
12862 (aStatus.NextInFlowNeedsReflow()) ? "+reflow" : "");
12863 }
12864}
12865
12866void nsIFrame::TraceMsg(const char* aFormatString, ...) {
12867 if (NS_FRAME_LOG_TEST(sFrameLogModule, NS_FRAME_TRACE_CALLS)(int(((mozilla::LogModule*)(sFrameLogModule))->Level()) &
(0x1))
) {
12868 // Format arguments into a buffer
12869 char argbuf[200];
12870 va_list ap;
12871 va_start(ap, aFormatString)__builtin_va_start(ap, aFormatString);
12872 VsprintfLiteral(argbuf, aFormatString, ap);
12873 va_end(ap)__builtin_va_end(ap);
12874
12875 char tagbuf[40];
12876 GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
12877 printf_stderr("%s: %s", tagbuf, argbuf);
12878 }
12879}
12880
12881void nsIFrame::VerifyDirtyBitSet(const nsFrameList& aFrameList) {
12882 for (nsIFrame* f : aFrameList) {
12883 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", 12883); MOZ_PretendNoReturn
(); } } while (0)
;
12884 }
12885}
12886
12887// Validation of SideIsVertical.
12888# define CASE(side, result) \
12889 static_assert(SideIsVertical(side) == result, "SideIsVertical is wrong")
12890CASE(eSideTop, false);
12891CASE(eSideRight, true);
12892CASE(eSideBottom, false);
12893CASE(eSideLeft, true);
12894# undef CASE
12895
12896// Validation of HalfCornerIsX.
12897# define CASE(corner, result) \
12898 static_assert(HalfCornerIsX(corner) == result, "HalfCornerIsX is wrong")
12899CASE(eCornerTopLeftX, true);
12900CASE(eCornerTopLeftY, false);
12901CASE(eCornerTopRightX, true);
12902CASE(eCornerTopRightY, false);
12903CASE(eCornerBottomRightX, true);
12904CASE(eCornerBottomRightY, false);
12905CASE(eCornerBottomLeftX, true);
12906CASE(eCornerBottomLeftY, false);
12907# undef CASE
12908
12909// Validation of HalfToFullCorner.
12910# define CASE(corner, result) \
12911 static_assert(HalfToFullCorner(corner) == result, \
12912 "HalfToFullCorner is " \
12913 "wrong")
12914CASE(eCornerTopLeftX, eCornerTopLeft);
12915CASE(eCornerTopLeftY, eCornerTopLeft);
12916CASE(eCornerTopRightX, eCornerTopRight);
12917CASE(eCornerTopRightY, eCornerTopRight);
12918CASE(eCornerBottomRightX, eCornerBottomRight);
12919CASE(eCornerBottomRightY, eCornerBottomRight);
12920CASE(eCornerBottomLeftX, eCornerBottomLeft);
12921CASE(eCornerBottomLeftY, eCornerBottomLeft);
12922# undef CASE
12923
12924// Validation of FullToHalfCorner.
12925# define CASE(corner, vert, result) \
12926 static_assert(FullToHalfCorner(corner, vert) == result, \
12927 "FullToHalfCorner is wrong")
12928CASE(eCornerTopLeft, false, eCornerTopLeftX);
12929CASE(eCornerTopLeft, true, eCornerTopLeftY);
12930CASE(eCornerTopRight, false, eCornerTopRightX);
12931CASE(eCornerTopRight, true, eCornerTopRightY);
12932CASE(eCornerBottomRight, false, eCornerBottomRightX);
12933CASE(eCornerBottomRight, true, eCornerBottomRightY);
12934CASE(eCornerBottomLeft, false, eCornerBottomLeftX);
12935CASE(eCornerBottomLeft, true, eCornerBottomLeftY);
12936# undef CASE
12937
12938// Validation of SideToFullCorner.
12939# define CASE(side, second, result) \
12940 static_assert(SideToFullCorner(side, second) == result, \
12941 "SideToFullCorner is wrong")
12942CASE(eSideTop, false, eCornerTopLeft);
12943CASE(eSideTop, true, eCornerTopRight);
12944
12945CASE(eSideRight, false, eCornerTopRight);
12946CASE(eSideRight, true, eCornerBottomRight);
12947
12948CASE(eSideBottom, false, eCornerBottomRight);
12949CASE(eSideBottom, true, eCornerBottomLeft);
12950
12951CASE(eSideLeft, false, eCornerBottomLeft);
12952CASE(eSideLeft, true, eCornerTopLeft);
12953# undef CASE
12954
12955// Validation of SideToHalfCorner.
12956# define CASE(side, second, parallel, result) \
12957 static_assert(SideToHalfCorner(side, second, parallel) == result, \
12958 "SideToHalfCorner is wrong")
12959CASE(eSideTop, false, true, eCornerTopLeftX);
12960CASE(eSideTop, false, false, eCornerTopLeftY);
12961CASE(eSideTop, true, true, eCornerTopRightX);
12962CASE(eSideTop, true, false, eCornerTopRightY);
12963
12964CASE(eSideRight, false, false, eCornerTopRightX);
12965CASE(eSideRight, false, true, eCornerTopRightY);
12966CASE(eSideRight, true, false, eCornerBottomRightX);
12967CASE(eSideRight, true, true, eCornerBottomRightY);
12968
12969CASE(eSideBottom, false, true, eCornerBottomRightX);
12970CASE(eSideBottom, false, false, eCornerBottomRightY);
12971CASE(eSideBottom, true, true, eCornerBottomLeftX);
12972CASE(eSideBottom, true, false, eCornerBottomLeftY);
12973
12974CASE(eSideLeft, false, false, eCornerBottomLeftX);
12975CASE(eSideLeft, false, true, eCornerBottomLeftY);
12976CASE(eSideLeft, true, false, eCornerTopLeftX);
12977CASE(eSideLeft, true, true, eCornerTopLeftY);
12978# undef CASE
12979
12980#endif