1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 /* base class of all rendering objects */
8 
9 #include "nsIFrame.h"
10 
11 #include <stdarg.h>
12 #include <algorithm>
13 
14 #include "gfx2DGlue.h"
15 #include "gfxUtils.h"
16 #include "mozilla/Attributes.h"
17 #include "mozilla/ComputedStyle.h"
18 #include "mozilla/DebugOnly.h"
19 #include "mozilla/DisplayPortUtils.h"
20 #include "mozilla/dom/DocumentInlines.h"
21 #include "mozilla/dom/AncestorIterator.h"
22 #include "mozilla/dom/ElementInlines.h"
23 #include "mozilla/dom/ImageTracker.h"
24 #include "mozilla/dom/Selection.h"
25 #include "mozilla/gfx/2D.h"
26 #include "mozilla/gfx/gfxVars.h"
27 #include "mozilla/gfx/PathHelpers.h"
28 #include "mozilla/intl/BidiEmbeddingLevel.h"
29 #include "mozilla/Maybe.h"
30 #include "mozilla/PresShell.h"
31 #include "mozilla/PresShellInlines.h"
32 #include "mozilla/ResultExtensions.h"
33 #include "mozilla/Sprintf.h"
34 #include "mozilla/StaticAnalysisFunctions.h"
35 #include "mozilla/StaticPrefs_layout.h"
36 #include "mozilla/StaticPrefs_print.h"
37 #include "mozilla/SVGMaskFrame.h"
38 #include "mozilla/SVGObserverUtils.h"
39 #include "mozilla/SVGTextFrame.h"
40 #include "mozilla/SVGIntegrationUtils.h"
41 #include "mozilla/SVGUtils.h"
42 #include "mozilla/ToString.h"
43 #include "mozilla/ViewportUtils.h"
44 
45 #include "nsCOMPtr.h"
46 #include "nsFieldSetFrame.h"
47 #include "nsFlexContainerFrame.h"
48 #include "nsFrameList.h"
49 #include "nsPlaceholderFrame.h"
50 #include "nsIBaseWindow.h"
51 #include "nsIContent.h"
52 #include "nsIContentInlines.h"
53 #include "nsContentUtils.h"
54 #include "nsCSSFrameConstructor.h"
55 #include "nsCSSProps.h"
56 #include "nsCSSPseudoElements.h"
57 #include "nsCSSRendering.h"
58 #include "nsAtom.h"
59 #include "nsString.h"
60 #include "nsReadableUtils.h"
61 #include "nsTableWrapperFrame.h"
62 #include "nsView.h"
63 #include "nsViewManager.h"
64 #include "nsIScrollableFrame.h"
65 #include "nsPresContext.h"
66 #include "nsPresContextInlines.h"
67 #include "nsStyleConsts.h"
68 #include "mozilla/Logging.h"
69 #include "nsLayoutUtils.h"
70 #include "LayoutLogging.h"
71 #include "mozilla/RestyleManager.h"
72 #include "nsImageFrame.h"
73 #include "nsInlineFrame.h"
74 #include "nsFrameSelection.h"
75 #include "nsGkAtoms.h"
76 #include "nsGridContainerFrame.h"
77 #include "nsCSSAnonBoxes.h"
78 #include "nsCanvasFrame.h"
79 
80 #include "nsFieldSetFrame.h"
81 #include "nsFrameTraversal.h"
82 #include "nsRange.h"
83 #include "nsITextControlFrame.h"
84 #include "nsNameSpaceManager.h"
85 #include "nsIPercentBSizeObserver.h"
86 #include "nsStyleStructInlines.h"
87 
88 #include "nsBidiPresUtils.h"
89 #include "RubyUtils.h"
90 #include "TextOverflow.h"
91 #include "nsAnimationManager.h"
92 
93 // For triple-click pref
94 #include "imgIRequest.h"
95 #include "nsError.h"
96 #include "nsContainerFrame.h"
97 #include "nsBoxLayoutState.h"
98 #include "nsBlockFrame.h"
99 #include "nsDisplayList.h"
100 #include "nsChangeHint.h"
101 #include "nsDeckFrame.h"
102 #include "nsSubDocumentFrame.h"
103 #include "RetainedDisplayListBuilder.h"
104 
105 #include "gfxContext.h"
106 #include "nsAbsoluteContainingBlock.h"
107 #include "StickyScrollContainer.h"
108 #include "nsFontInflationData.h"
109 #include "nsRegion.h"
110 #include "nsIFrameInlines.h"
111 #include "nsStyleChangeList.h"
112 #include "nsWindowSizes.h"
113 
114 #ifdef ACCESSIBILITY
115 #  include "nsAccessibilityService.h"
116 #endif
117 
118 #include "mozilla/AsyncEventDispatcher.h"
119 #include "mozilla/CSSClipPathInstance.h"
120 #include "mozilla/EffectCompositor.h"
121 #include "mozilla/EffectSet.h"
122 #include "mozilla/EventListenerManager.h"
123 #include "mozilla/EventStateManager.h"
124 #include "mozilla/EventStates.h"
125 #include "mozilla/Preferences.h"
126 #include "mozilla/LookAndFeel.h"
127 #include "mozilla/MouseEvents.h"
128 #include "mozilla/ServoStyleSet.h"
129 #include "mozilla/ServoStyleSetInlines.h"
130 #include "mozilla/css/ImageLoader.h"
131 #include "mozilla/dom/HTMLBodyElement.h"
132 #include "mozilla/dom/SVGPathData.h"
133 #include "mozilla/dom/TouchEvent.h"
134 #include "mozilla/gfx/Tools.h"
135 #include "mozilla/layers/WebRenderUserData.h"
136 #include "mozilla/layout/ScrollAnchorContainer.h"
137 #include "nsPrintfCString.h"
138 #include "ActiveLayerTracker.h"
139 
140 #include "nsITheme.h"
141 
142 using namespace mozilla;
143 using namespace mozilla::css;
144 using namespace mozilla::dom;
145 using namespace mozilla::gfx;
146 using namespace mozilla::layers;
147 using namespace mozilla::layout;
148 typedef nsAbsoluteContainingBlock::AbsPosReflowFlags AbsPosReflowFlags;
149 using nsStyleTransformMatrix::TransformReferenceBox;
150 
151 const mozilla::LayoutFrameType nsIFrame::sLayoutFrameTypes[
152 #define FRAME_ID(...) 1 +
153 #define ABSTRACT_FRAME_ID(...)
154 #include "mozilla/FrameIdList.h"
155 #undef FRAME_ID
156 #undef ABSTRACT_FRAME_ID
157     0] = {
158 #define FRAME_ID(class_, type_, ...) mozilla::LayoutFrameType::type_,
159 #define ABSTRACT_FRAME_ID(...)
160 #include "mozilla/FrameIdList.h"
161 #undef FRAME_ID
162 #undef ABSTRACT_FRAME_ID
163 };
164 
165 const nsIFrame::FrameClassBits nsIFrame::sFrameClassBits[
166 #define FRAME_ID(...) 1 +
167 #define ABSTRACT_FRAME_ID(...)
168 #include "mozilla/FrameIdList.h"
169 #undef FRAME_ID
170 #undef ABSTRACT_FRAME_ID
171     0] = {
172 #define Leaf eFrameClassBitsLeaf
173 #define NotLeaf eFrameClassBitsNone
174 #define FRAME_ID(class_, type_, leaf_, ...) leaf_,
175 #define ABSTRACT_FRAME_ID(...)
176 #include "mozilla/FrameIdList.h"
177 #undef Leaf
178 #undef NotLeaf
179 #undef FRAME_ID
180 #undef ABSTRACT_FRAME_ID
181 };
182 
183 // Struct containing cached metrics for box-wrapped frames.
184 struct nsBoxLayoutMetrics {
185   nsSize mPrefSize;
186   nsSize mMinSize;
187   nsSize mMaxSize;
188 
189   nsSize mBlockMinSize;
190   nsSize mBlockPrefSize;
191   nscoord mBlockAscent;
192 
193   nscoord mFlex;
194   nscoord mAscent;
195 
196   nsSize mLastSize;
197 };
198 
199 struct nsContentAndOffset {
200   nsIContent* mContent = nullptr;
201   int32_t mOffset = 0;
202 };
203 
204 // Some Misc #defines
205 #define SELECTION_DEBUG 0
206 #define FORCE_SELECTION_UPDATE 1
207 #define CALC_DEBUG 0
208 
209 #include "nsILineIterator.h"
210 #include "prenv.h"
211 
NS_DECLARE_FRAME_PROPERTY_DELETABLE(BoxMetricsProperty,nsBoxLayoutMetrics)212 NS_DECLARE_FRAME_PROPERTY_DELETABLE(BoxMetricsProperty, nsBoxLayoutMetrics)
213 
214 static void InitBoxMetrics(nsIFrame* aFrame, bool aClear) {
215   if (aClear) {
216     aFrame->RemoveProperty(BoxMetricsProperty());
217   }
218 
219   nsBoxLayoutMetrics* metrics = new nsBoxLayoutMetrics();
220   aFrame->SetProperty(BoxMetricsProperty(), metrics);
221 
222   aFrame->nsIFrame::MarkIntrinsicISizesDirty();
223   metrics->mBlockAscent = 0;
224   metrics->mLastSize.SizeTo(0, 0);
225 }
226 
227 // Utility function to set a nsRect-valued property table entry on aFrame,
228 // reusing the existing storage if the property happens to be already set.
229 template <typename T>
SetOrUpdateRectValuedProperty(nsIFrame * aFrame,FrameProperties::Descriptor<T> aProperty,const nsRect & aNewValue)230 static void SetOrUpdateRectValuedProperty(
231     nsIFrame* aFrame, FrameProperties::Descriptor<T> aProperty,
232     const nsRect& aNewValue) {
233   bool found;
234   nsRect* rectStorage = aFrame->GetProperty(aProperty, &found);
235   if (!found) {
236     rectStorage = new nsRect(aNewValue);
237     aFrame->AddProperty(aProperty, rectStorage);
238   } else {
239     *rectStorage = aNewValue;
240   }
241 }
242 
IsXULBoxWrapped(const nsIFrame * aFrame)243 static bool IsXULBoxWrapped(const nsIFrame* aFrame) {
244   return aFrame->GetParent() && aFrame->GetParent()->IsXULBoxFrame() &&
245          !aFrame->IsXULBoxFrame();
246 }
247 
UpdateTruncated(const ReflowInput & aReflowInput,const ReflowOutput & aMetrics)248 void nsReflowStatus::UpdateTruncated(const ReflowInput& aReflowInput,
249                                      const ReflowOutput& aMetrics) {
250   const WritingMode containerWM = aMetrics.GetWritingMode();
251   if (aReflowInput.GetWritingMode().IsOrthogonalTo(containerWM)) {
252     // Orthogonal flows are always reflowed with an unconstrained dimension,
253     // so should never end up truncated (see ReflowInput::Init()).
254     mTruncated = false;
255   } else if (aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE &&
256              aReflowInput.AvailableBSize() < aMetrics.BSize(containerWM) &&
257              !aReflowInput.mFlags.mIsTopOfPage) {
258     mTruncated = true;
259   } else {
260     mTruncated = false;
261   }
262 }
263 
264 /* static */
DestroyAnonymousContent(nsPresContext * aPresContext,already_AddRefed<nsIContent> && aContent)265 void nsIFrame::DestroyAnonymousContent(
266     nsPresContext* aPresContext, already_AddRefed<nsIContent>&& aContent) {
267   if (nsCOMPtr<nsIContent> content = aContent) {
268     aPresContext->EventStateManager()->NativeAnonymousContentRemoved(content);
269     aPresContext->PresShell()->NativeAnonymousContentRemoved(content);
270     content->UnbindFromTree();
271   }
272 }
273 
274 // Formerly the nsIFrameDebug interface
275 
operator <<(std::ostream & aStream,const nsReflowStatus & aStatus)276 std::ostream& operator<<(std::ostream& aStream, const nsReflowStatus& aStatus) {
277   char complete = 'Y';
278   if (aStatus.IsIncomplete()) {
279     complete = 'N';
280   } else if (aStatus.IsOverflowIncomplete()) {
281     complete = 'O';
282   }
283 
284   char brk = 'N';
285   if (aStatus.IsInlineBreakBefore()) {
286     brk = 'B';
287   } else if (aStatus.IsInlineBreakAfter()) {
288     brk = 'A';
289   }
290 
291   aStream << "["
292           << "Complete=" << complete << ","
293           << "NIF=" << (aStatus.NextInFlowNeedsReflow() ? 'Y' : 'N') << ","
294           << "Truncated=" << (aStatus.IsTruncated() ? 'Y' : 'N') << ","
295           << "Break=" << brk << ","
296           << "FirstLetter=" << (aStatus.FirstLetterComplete() ? 'Y' : 'N')
297           << "]";
298   return aStream;
299 }
300 
301 #ifdef DEBUG
302 static bool gShowFrameBorders = false;
303 
ShowFrameBorders(bool aEnable)304 void nsIFrame::ShowFrameBorders(bool aEnable) { gShowFrameBorders = aEnable; }
305 
GetShowFrameBorders()306 bool nsIFrame::GetShowFrameBorders() { return gShowFrameBorders; }
307 
308 static bool gShowEventTargetFrameBorder = false;
309 
ShowEventTargetFrameBorder(bool aEnable)310 void nsIFrame::ShowEventTargetFrameBorder(bool aEnable) {
311   gShowEventTargetFrameBorder = aEnable;
312 }
313 
GetShowEventTargetFrameBorder()314 bool nsIFrame::GetShowEventTargetFrameBorder() {
315   return gShowEventTargetFrameBorder;
316 }
317 
318 /**
319  * Note: the log module is created during library initialization which
320  * means that you cannot perform logging before then.
321  */
322 mozilla::LazyLogModule nsIFrame::sFrameLogModule("frame");
323 
324 #endif
325 
NS_DECLARE_FRAME_PROPERTY_DELETABLE(AbsoluteContainingBlockProperty,nsAbsoluteContainingBlock)326 NS_DECLARE_FRAME_PROPERTY_DELETABLE(AbsoluteContainingBlockProperty,
327                                     nsAbsoluteContainingBlock)
328 
329 bool nsIFrame::HasAbsolutelyPositionedChildren() const {
330   return IsAbsoluteContainer() &&
331          GetAbsoluteContainingBlock()->HasAbsoluteFrames();
332 }
333 
GetAbsoluteContainingBlock() const334 nsAbsoluteContainingBlock* nsIFrame::GetAbsoluteContainingBlock() const {
335   NS_ASSERTION(IsAbsoluteContainer(),
336                "The frame is not marked as an abspos container correctly");
337   nsAbsoluteContainingBlock* absCB =
338       GetProperty(AbsoluteContainingBlockProperty());
339   NS_ASSERTION(absCB,
340                "The frame is marked as an abspos container but doesn't have "
341                "the property");
342   return absCB;
343 }
344 
MarkAsAbsoluteContainingBlock()345 void nsIFrame::MarkAsAbsoluteContainingBlock() {
346   MOZ_ASSERT(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN));
347   NS_ASSERTION(!GetProperty(AbsoluteContainingBlockProperty()),
348                "Already has an abs-pos containing block property?");
349   NS_ASSERTION(!HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN),
350                "Already has NS_FRAME_HAS_ABSPOS_CHILDREN state bit?");
351   AddStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN);
352   SetProperty(AbsoluteContainingBlockProperty(),
353               new nsAbsoluteContainingBlock(GetAbsoluteListID()));
354 }
355 
MarkAsNotAbsoluteContainingBlock()356 void nsIFrame::MarkAsNotAbsoluteContainingBlock() {
357   NS_ASSERTION(!HasAbsolutelyPositionedChildren(), "Think of the children!");
358   NS_ASSERTION(GetProperty(AbsoluteContainingBlockProperty()),
359                "Should have an abs-pos containing block property");
360   NS_ASSERTION(HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN),
361                "Should have NS_FRAME_HAS_ABSPOS_CHILDREN state bit");
362   MOZ_ASSERT(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN));
363   RemoveStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN);
364   RemoveProperty(AbsoluteContainingBlockProperty());
365 }
366 
CheckAndClearPaintedState()367 bool nsIFrame::CheckAndClearPaintedState() {
368   bool result = HasAnyStateBits(NS_FRAME_PAINTED_THEBES);
369   RemoveStateBits(NS_FRAME_PAINTED_THEBES);
370 
371   for (const auto& childList : ChildLists()) {
372     for (nsIFrame* child : childList.mList) {
373       if (child->CheckAndClearPaintedState()) {
374         result = true;
375       }
376     }
377   }
378   return result;
379 }
380 
CheckAndClearDisplayListState()381 bool nsIFrame::CheckAndClearDisplayListState() {
382   bool result = BuiltDisplayList();
383   SetBuiltDisplayList(false);
384 
385   for (const auto& childList : ChildLists()) {
386     for (nsIFrame* child : childList.mList) {
387       if (child->CheckAndClearDisplayListState()) {
388         result = true;
389       }
390     }
391   }
392   return result;
393 }
394 
IsVisibleConsideringAncestors(uint32_t aFlags) const395 bool nsIFrame::IsVisibleConsideringAncestors(uint32_t aFlags) const {
396   if (!StyleVisibility()->IsVisible()) {
397     return false;
398   }
399 
400   if (PresShell()->IsUnderHiddenEmbedderElement()) {
401     return false;
402   }
403 
404   const nsIFrame* frame = this;
405   while (frame) {
406     nsView* view = frame->GetView();
407     if (view && view->GetVisibility() == nsViewVisibility_kHide) return false;
408 
409     nsIFrame* parent = frame->GetParent();
410     nsDeckFrame* deck = do_QueryFrame(parent);
411     if (deck) {
412       if (deck->GetSelectedBox() != frame) return false;
413     }
414 
415     if (parent) {
416       frame = parent;
417     } else {
418       parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(frame);
419       if (!parent) break;
420 
421       if ((aFlags & nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) == 0 &&
422           parent->PresContext()->IsChrome() &&
423           !frame->PresContext()->IsChrome()) {
424         break;
425       }
426 
427       frame = parent;
428     }
429   }
430 
431   return true;
432 }
433 
FindCloserFrameForSelection(const nsPoint & aPoint,FrameWithDistance * aCurrentBestFrame)434 void nsIFrame::FindCloserFrameForSelection(
435     const nsPoint& aPoint, FrameWithDistance* aCurrentBestFrame) {
436   if (nsLayoutUtils::PointIsCloserToRect(aPoint, mRect,
437                                          aCurrentBestFrame->mXDistance,
438                                          aCurrentBestFrame->mYDistance)) {
439     aCurrentBestFrame->mFrame = this;
440   }
441 }
442 
ContentStatesChanged(mozilla::EventStates aStates)443 void nsIFrame::ContentStatesChanged(mozilla::EventStates aStates) {}
444 
Clear(mozilla::PresShell * aPresShell)445 void WeakFrame::Clear(mozilla::PresShell* aPresShell) {
446   if (aPresShell) {
447     aPresShell->RemoveWeakFrame(this);
448   }
449   mFrame = nullptr;
450 }
451 
AutoWeakFrame(const WeakFrame & aOther)452 AutoWeakFrame::AutoWeakFrame(const WeakFrame& aOther)
453     : mPrev(nullptr), mFrame(nullptr) {
454   Init(aOther.GetFrame());
455 }
456 
Clear(mozilla::PresShell * aPresShell)457 void AutoWeakFrame::Clear(mozilla::PresShell* aPresShell) {
458   if (aPresShell) {
459     aPresShell->RemoveAutoWeakFrame(this);
460   }
461   mFrame = nullptr;
462   mPrev = nullptr;
463 }
464 
~AutoWeakFrame()465 AutoWeakFrame::~AutoWeakFrame() {
466   Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
467 }
468 
Init(nsIFrame * aFrame)469 void AutoWeakFrame::Init(nsIFrame* aFrame) {
470   Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
471   mFrame = aFrame;
472   if (mFrame) {
473     mozilla::PresShell* presShell = mFrame->PresContext()->GetPresShell();
474     NS_WARNING_ASSERTION(presShell, "Null PresShell in AutoWeakFrame!");
475     if (presShell) {
476       presShell->AddAutoWeakFrame(this);
477     } else {
478       mFrame = nullptr;
479     }
480   }
481 }
482 
Init(nsIFrame * aFrame)483 void WeakFrame::Init(nsIFrame* aFrame) {
484   Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
485   mFrame = aFrame;
486   if (mFrame) {
487     mozilla::PresShell* presShell = mFrame->PresContext()->GetPresShell();
488     MOZ_ASSERT(presShell, "Null PresShell in WeakFrame!");
489     if (presShell) {
490       presShell->AddWeakFrame(this);
491     } else {
492       mFrame = nullptr;
493     }
494   }
495 }
496 
NS_NewEmptyFrame(PresShell * aPresShell,ComputedStyle * aStyle)497 nsIFrame* NS_NewEmptyFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
498   return new (aPresShell) nsIFrame(aStyle, aPresShell->GetPresContext());
499 }
500 
~nsIFrame()501 nsIFrame::~nsIFrame() {
502   MOZ_COUNT_DTOR(nsIFrame);
503 
504   MOZ_ASSERT(GetVisibility() != Visibility::ApproximatelyVisible,
505              "Visible nsFrame is being destroyed");
506 }
507 
NS_IMPL_FRAMEARENA_HELPERS(nsIFrame)508 NS_IMPL_FRAMEARENA_HELPERS(nsIFrame)
509 
510 // Dummy operator delete.  Will never be called, but must be defined
511 // to satisfy some C++ ABIs.
512 void nsIFrame::operator delete(void*, size_t) {
513   MOZ_CRASH("nsIFrame::operator delete should never be called");
514 }
515 
516 NS_QUERYFRAME_HEAD(nsIFrame)
NS_QUERYFRAME_ENTRY(nsIFrame)517   NS_QUERYFRAME_ENTRY(nsIFrame)
518 NS_QUERYFRAME_TAIL_INHERITANCE_ROOT
519 
520 /////////////////////////////////////////////////////////////////////////////
521 // nsIFrame
522 
523 static bool IsFontSizeInflationContainer(nsIFrame* aFrame,
524                                          const nsStyleDisplay* aStyleDisplay) {
525   /*
526    * Font size inflation is built around the idea that we're inflating
527    * the fonts for a pan-and-zoom UI so that when the user scales up a
528    * block or other container to fill the width of the device, the fonts
529    * will be readable.  To do this, we need to pick what counts as a
530    * container.
531    *
532    * From a code perspective, the only hard requirement is that frames
533    * that are line participants
534    * (nsIFrame::IsFrameOfType(nsIFrame::eLineParticipant)) are never
535    * containers, since line layout assumes that the inflation is
536    * consistent within a line.
537    *
538    * This is not an imposition, since we obviously want a bunch of text
539    * (possibly with inline elements) flowing within a block to count the
540    * block (or higher) as its container.
541    *
542    * We also want form controls, including the text in the anonymous
543    * content inside of them, to match each other and the text next to
544    * them, so they and their anonymous content should also not be a
545    * container.
546    *
547    * However, because we can't reliably compute sizes across XUL during
548    * reflow, any XUL frame with a XUL parent is always a container.
549    *
550    * There are contexts where it would be nice if some blocks didn't
551    * count as a container, so that, for example, an indented quotation
552    * didn't end up with a smaller font size.  However, it's hard to
553    * distinguish these situations where we really do want the indented
554    * thing to count as a container, so we don't try, and blocks are
555    * always containers.
556    */
557 
558   // The root frame should always be an inflation container.
559   if (!aFrame->GetParent()) {
560     return true;
561   }
562 
563   nsIContent* content = aFrame->GetContent();
564   if (content && content->IsInNativeAnonymousSubtree()) {
565     // Native anonymous content shouldn't be a font inflation root,
566     // except for the canvas custom content container.
567     nsCanvasFrame* canvas = aFrame->PresShell()->GetCanvasFrame();
568     return canvas && canvas->GetCustomContentContainer() == content;
569   }
570 
571   LayoutFrameType frameType = aFrame->Type();
572   bool isInline =
573       (nsStyleDisplay::IsInlineFlow(aFrame->GetDisplay()) ||
574        RubyUtils::IsRubyBox(frameType) ||
575        (aStyleDisplay->IsFloatingStyle() &&
576         frameType == LayoutFrameType::Letter) ||
577        // Given multiple frames for the same node, only the
578        // outer one should be considered a container.
579        // (Important, e.g., for nsSelectsAreaFrame.)
580        (aFrame->GetParent()->GetContent() == content) ||
581        (content &&
582         // Form controls shouldn't become inflation containers.
583         (content->IsAnyOfHTMLElements(
584             nsGkAtoms::option, nsGkAtoms::optgroup, nsGkAtoms::select,
585             nsGkAtoms::input, nsGkAtoms::button, nsGkAtoms::textarea)))) &&
586       !(aFrame->IsXULBoxFrame() && aFrame->GetParent()->IsXULBoxFrame());
587   NS_ASSERTION(!aFrame->IsFrameOfType(nsIFrame::eLineParticipant) || isInline ||
588                    // br frames and mathml frames report being line
589                    // participants even when their position or display is
590                    // set
591                    aFrame->IsBrFrame() ||
592                    aFrame->IsFrameOfType(nsIFrame::eMathML),
593                "line participants must not be containers");
594   return !isInline;
595 }
596 
MaybeScheduleReflowSVGNonDisplayText(nsIFrame * aFrame)597 static void MaybeScheduleReflowSVGNonDisplayText(nsIFrame* aFrame) {
598   if (!SVGUtils::IsInSVGTextSubtree(aFrame)) {
599     return;
600   }
601 
602   // We need to ensure that any non-display SVGTextFrames get reflowed when a
603   // child text frame gets new style. Thus we need to schedule a reflow in
604   // |DidSetComputedStyle|. We also need to call it from |DestroyFrom|,
605   // because otherwise we won't get notified when style changes to
606   // "display:none".
607   SVGTextFrame* svgTextFrame = static_cast<SVGTextFrame*>(
608       nsLayoutUtils::GetClosestFrameOfType(aFrame, LayoutFrameType::SVGText));
609   nsIFrame* anonBlock = svgTextFrame->PrincipalChildList().FirstChild();
610 
611   // Note that we must check NS_FRAME_FIRST_REFLOW on our SVGTextFrame's
612   // anonymous block frame rather than our aFrame, since NS_FRAME_FIRST_REFLOW
613   // may be set on us if we're a new frame that has been inserted after the
614   // document's first reflow. (In which case this DidSetComputedStyle call may
615   // be happening under frame construction under a Reflow() call.)
616   if (!anonBlock || anonBlock->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
617     return;
618   }
619 
620   if (!svgTextFrame->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY) ||
621       svgTextFrame->HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW)) {
622     return;
623   }
624 
625   svgTextFrame->ScheduleReflowSVGNonDisplayText(IntrinsicDirty::StyleChange);
626 }
627 
IsPrimaryFrameOfRootOrBodyElement() const628 bool nsIFrame::IsPrimaryFrameOfRootOrBodyElement() const {
629   if (!IsPrimaryFrame()) {
630     return false;
631   }
632   nsIContent* content = GetContent();
633   Document* document = content->OwnerDoc();
634   return content == document->GetRootElement() ||
635          content == document->GetBodyElement();
636 }
637 
IsRenderedLegend() const638 bool nsIFrame::IsRenderedLegend() const {
639   if (auto* parent = GetParent(); parent && parent->IsFieldSetFrame()) {
640     return static_cast<nsFieldSetFrame*>(parent)->GetLegend() == this;
641   }
642   return false;
643 }
644 
Init(nsIContent * aContent,nsContainerFrame * aParent,nsIFrame * aPrevInFlow)645 void nsIFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
646                     nsIFrame* aPrevInFlow) {
647   MOZ_ASSERT(nsQueryFrame::FrameIID(mClass) == GetFrameId());
648   MOZ_ASSERT(!mContent, "Double-initing a frame?");
649   NS_ASSERTION(IsFrameOfType(eDEBUGAllFrames) && !IsFrameOfType(eDEBUGNoFrames),
650                "IsFrameOfType implementation that doesn't call base class");
651 
652   mContent = aContent;
653   mParent = aParent;
654   MOZ_DIAGNOSTIC_ASSERT(!mParent || PresShell() == mParent->PresShell());
655 
656   if (aPrevInFlow) {
657     mWritingMode = aPrevInFlow->GetWritingMode();
658 
659     // Copy some state bits from prev-in-flow (the bits that should apply
660     // throughout a continuation chain). The bits are sorted according to their
661     // order in nsFrameStateBits.h.
662 
663     // clang-format off
664     AddStateBits(aPrevInFlow->GetStateBits() &
665                  (NS_FRAME_GENERATED_CONTENT |
666                   NS_FRAME_OUT_OF_FLOW |
667                   NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN |
668                   NS_FRAME_INDEPENDENT_SELECTION |
669                   NS_FRAME_PART_OF_IBSPLIT |
670                   NS_FRAME_MAY_BE_TRANSFORMED |
671                   NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR));
672     // clang-format on
673 
674     // Copy other bits in nsIFrame from prev-in-flow.
675     mHasColumnSpanSiblings = aPrevInFlow->HasColumnSpanSiblings();
676   } else {
677     PresContext()->ConstructedFrame();
678   }
679 
680   if (GetParent()) {
681     if (MOZ_UNLIKELY(mContent == PresContext()->Document()->GetRootElement() &&
682                      mContent == GetParent()->GetContent())) {
683       // Our content is the root element and we have the same content as our
684       // parent. That is, we are the internal anonymous frame of the root
685       // element. Copy the used mWritingMode from our parent because
686       // mDocElementContainingBlock gets its mWritingMode from <body>.
687       mWritingMode = GetParent()->GetWritingMode();
688     }
689 
690     // Copy some state bits from our parent (the bits that should apply
691     // recursively throughout a subtree). The bits are sorted according to their
692     // order in nsFrameStateBits.h.
693 
694     // clang-format off
695     AddStateBits(GetParent()->GetStateBits() &
696                  (NS_FRAME_GENERATED_CONTENT |
697                   NS_FRAME_INDEPENDENT_SELECTION |
698                   NS_FRAME_IS_SVG_TEXT |
699                   NS_FRAME_IN_POPUP |
700                   NS_FRAME_IS_NONDISPLAY));
701     // clang-format on
702 
703     if (HasAnyStateBits(NS_FRAME_IN_POPUP) && TrackingVisibility()) {
704       // Assume all frames in popups are visible.
705       IncApproximateVisibleCount();
706     }
707   }
708   if (aPrevInFlow) {
709     mMayHaveOpacityAnimation = aPrevInFlow->MayHaveOpacityAnimation();
710     mMayHaveTransformAnimation = aPrevInFlow->MayHaveTransformAnimation();
711   } else if (mContent) {
712     // It's fine to fetch the EffectSet for the style frame here because in the
713     // following code we take care of the case where animations may target
714     // a different frame.
715     EffectSet* effectSet = EffectSet::GetEffectSetForStyleFrame(this);
716     if (effectSet) {
717       mMayHaveOpacityAnimation = effectSet->MayHaveOpacityAnimation();
718 
719       if (effectSet->MayHaveTransformAnimation()) {
720         // If we are the inner table frame for display:table content, then
721         // transform animations should go on our parent frame (the table wrapper
722         // frame).
723         //
724         // We do this when initializing the child frame (table inner frame),
725         // because when initializng the table wrapper frame, we don't yet have
726         // access to its children so we can't tell if we have transform
727         // animations or not.
728         if (IsFrameOfType(eSupportsCSSTransforms)) {
729           mMayHaveTransformAnimation = true;
730           AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
731         } else if (aParent && nsLayoutUtils::GetStyleFrame(aParent) == this) {
732           MOZ_ASSERT(
733               aParent->IsFrameOfType(eSupportsCSSTransforms),
734               "Style frames that don't support transforms should have parents"
735               " that do");
736           aParent->mMayHaveTransformAnimation = true;
737           aParent->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
738         }
739       }
740     }
741   }
742 
743   const nsStyleDisplay* disp = StyleDisplay();
744   if (disp->HasTransform(this)) {
745     // If 'transform' dynamically changes, RestyleManager takes care of
746     // updating this bit.
747     AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
748   }
749 
750   if (disp->IsContainLayout() && disp->IsContainSize() &&
751       // All frames that support contain:layout also support contain:size.
752       IsFrameOfType(eSupportsContainLayoutAndPaint) && !IsTableWrapperFrame()) {
753     // In general, frames that have contain:layout+size can be reflow roots.
754     // (One exception: table-wrapper frames don't work well as reflow roots,
755     // because their inner-table ReflowInput init path tries to reuse & deref
756     // the wrapper's containing block's reflow input, which may be null if we
757     // initiate reflow from the table-wrapper itself.)
758     //
759     // Changes to `contain` force frame reconstructions, so this bit can be set
760     // for the whole lifetime of this frame.
761     AddStateBits(NS_FRAME_REFLOW_ROOT);
762   }
763 
764   if (nsLayoutUtils::FontSizeInflationEnabled(PresContext()) ||
765       !GetParent()
766 #ifdef DEBUG
767       // We have assertions that check inflation invariants even when
768       // font size inflation is not enabled.
769       || true
770 #endif
771   ) {
772     if (IsFontSizeInflationContainer(this, disp)) {
773       AddStateBits(NS_FRAME_FONT_INFLATION_CONTAINER);
774       if (!GetParent() ||
775           // I'd use NS_FRAME_OUT_OF_FLOW, but it's not set yet.
776           disp->IsFloating(this) || disp->IsAbsolutelyPositioned(this) ||
777           GetParent()->IsFlexContainerFrame() ||
778           GetParent()->IsGridContainerFrame()) {
779         AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
780       }
781     }
782     NS_ASSERTION(
783         GetParent() || HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER),
784         "root frame should always be a container");
785   }
786 
787   if (PresShell()->AssumeAllFramesVisible() && TrackingVisibility()) {
788     IncApproximateVisibleCount();
789   }
790 
791   DidSetComputedStyle(nullptr);
792 
793   if (::IsXULBoxWrapped(this)) ::InitBoxMetrics(this, false);
794 
795   // For a newly created frame, we need to update this frame's visibility state.
796   // Usually we update the state when the frame is restyled and has a
797   // VisibilityChange change hint but we don't generate any change hints for
798   // newly created frames.
799   // Note: We don't need to do this for placeholders since placeholders have
800   // different styles so that the styles don't have visibility:hidden even if
801   // the parent has visibility:hidden style. We also don't need to update the
802   // state when creating continuations because its visibility is the same as its
803   // prev-in-flow, and the animation code cares only primary frames.
804   if (!IsPlaceholderFrame() && !aPrevInFlow) {
805     UpdateVisibleDescendantsState();
806   }
807 }
808 
DestroyFrom(nsIFrame * aDestructRoot,PostDestroyData & aPostDestroyData)809 void nsIFrame::DestroyFrom(nsIFrame* aDestructRoot,
810                            PostDestroyData& aPostDestroyData) {
811   NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
812                "destroy called on frame while scripts not blocked");
813   NS_ASSERTION(!GetNextSibling() && !GetPrevSibling(),
814                "Frames should be removed before destruction.");
815   NS_ASSERTION(aDestructRoot, "Must specify destruct root");
816   MOZ_ASSERT(!HasAbsolutelyPositionedChildren());
817   MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT),
818              "NS_FRAME_PART_OF_IBSPLIT set on non-nsContainerFrame?");
819 
820   MaybeScheduleReflowSVGNonDisplayText(this);
821 
822   SVGObserverUtils::InvalidateDirectRenderingObservers(this);
823 
824   if (StyleDisplay()->mPosition == StylePositionProperty::Sticky) {
825     StickyScrollContainer* ssc =
826         StickyScrollContainer::GetStickyScrollContainerForFrame(this);
827     if (ssc) {
828       ssc->RemoveFrame(this);
829     }
830   }
831 
832   nsPresContext* presContext = PresContext();
833   mozilla::PresShell* presShell = presContext->GetPresShell();
834   if (mState & NS_FRAME_OUT_OF_FLOW) {
835     nsPlaceholderFrame* placeholder = GetPlaceholderFrame();
836     NS_ASSERTION(
837         !placeholder || (aDestructRoot != this),
838         "Don't call Destroy() on OOFs, call Destroy() on the placeholder.");
839     NS_ASSERTION(!placeholder || nsLayoutUtils::IsProperAncestorFrame(
840                                      aDestructRoot, placeholder),
841                  "Placeholder relationship should have been torn down already; "
842                  "this might mean we have a stray placeholder in the tree.");
843     if (placeholder) {
844       placeholder->SetOutOfFlowFrame(nullptr);
845     }
846   }
847 
848   if (IsPrimaryFrame()) {
849     // This needs to happen before we clear our Properties() table.
850     ActiveLayerTracker::TransferActivityToContent(this, mContent);
851   }
852 
853   ScrollAnchorContainer* anchor = nullptr;
854   if (IsScrollAnchor(&anchor)) {
855     anchor->InvalidateAnchor();
856   }
857 
858   if (HasCSSAnimations() || HasCSSTransitions() ||
859       // It's fine to look up the style frame here since if we're destroying the
860       // frames for display:table content we should be destroying both wrapper
861       // and inner frame.
862       EffectSet::GetEffectSetForStyleFrame(this)) {
863     // If no new frame for this element is created by the end of the
864     // restyling process, stop animations and transitions for this frame
865     RestyleManager::AnimationsWithDestroyedFrame* adf =
866         presContext->RestyleManager()->GetAnimationsWithDestroyedFrame();
867     // AnimationsWithDestroyedFrame only lives during the restyling process.
868     if (adf) {
869       adf->Put(mContent, mComputedStyle);
870     }
871   }
872 
873   // Disable visibility tracking. Note that we have to do this before we clear
874   // frame properties and lose track of whether we were previously visible.
875   // XXX(seth): It'd be ideal to assert that we're already marked nonvisible
876   // here, but it's unfortunately tricky to guarantee in the face of things like
877   // frame reconstruction induced by style changes.
878   DisableVisibilityTracking();
879 
880   // Ensure that we're not in the approximately visible list anymore.
881   PresContext()->GetPresShell()->RemoveFrameFromApproximatelyVisibleList(this);
882 
883   presShell->NotifyDestroyingFrame(this);
884 
885   if (mState & NS_FRAME_EXTERNAL_REFERENCE) {
886     presShell->ClearFrameRefs(this);
887   }
888 
889   nsView* view = GetView();
890   if (view) {
891     view->SetFrame(nullptr);
892     view->Destroy();
893   }
894 
895   // Make sure that our deleted frame can't be returned from GetPrimaryFrame()
896   if (IsPrimaryFrame()) {
897     mContent->SetPrimaryFrame(nullptr);
898 
899     // Pass the root of a generated content subtree (e.g. ::after/::before) to
900     // aPostDestroyData to unbind it after frame destruction is done.
901     if (HasAnyStateBits(NS_FRAME_GENERATED_CONTENT) &&
902         mContent->IsRootOfNativeAnonymousSubtree()) {
903       aPostDestroyData.AddAnonymousContent(mContent.forget());
904     }
905   }
906 
907   // Remove all properties attached to the frame, to ensure any property
908   // destructors that need the frame pointer are handled properly.
909   RemoveAllProperties();
910 
911   // Must retrieve the object ID before calling destructors, so the
912   // vtable is still valid.
913   //
914   // Note to future tweakers: having the method that returns the
915   // object size call the destructor will not avoid an indirect call;
916   // the compiler cannot devirtualize the call to the destructor even
917   // if it's from a method defined in the same class.
918 
919   nsQueryFrame::FrameIID id = GetFrameId();
920   this->~nsIFrame();
921 
922 #ifdef DEBUG
923   {
924     nsIFrame* rootFrame = presShell->GetRootFrame();
925     MOZ_ASSERT(rootFrame);
926     if (this != rootFrame) {
927       const RetainedDisplayListData* data =
928           GetRetainedDisplayListData(rootFrame);
929 
930       const bool inModifiedList = data && data->IsModified(this);
931 
932       if (inModifiedList) {
933         DL_LOG(LogLevel::Warning, "Frame %p found in modified list", this);
934       }
935 
936       MOZ_ASSERT(!inModifiedList,
937                  "A dtor added this frame to modified frames list!");
938     }
939   }
940 #endif
941 
942   // Now that we're totally cleaned out, we need to add ourselves to
943   // the presshell's recycler.
944   presShell->FreeFrame(id, this);
945 }
946 
GetOffsets() const947 std::pair<int32_t, int32_t> nsIFrame::GetOffsets() const {
948   return std::make_pair(0, 0);
949 }
950 
CompareLayers(const nsStyleImageLayers * aFirstLayers,const nsStyleImageLayers * aSecondLayers,const std::function<void (imgRequestProxy * aReq)> & aCallback)951 static void CompareLayers(
952     const nsStyleImageLayers* aFirstLayers,
953     const nsStyleImageLayers* aSecondLayers,
954     const std::function<void(imgRequestProxy* aReq)>& aCallback) {
955   NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, (*aFirstLayers)) {
956     const auto& image = aFirstLayers->mLayers[i].mImage;
957     if (!image.IsImageRequestType() || !image.IsResolved()) {
958       continue;
959     }
960 
961     // aCallback is called when the style image in aFirstLayers is thought to
962     // be different with the corresponded one in aSecondLayers
963     if (!aSecondLayers || i >= aSecondLayers->mImageCount ||
964         (!aSecondLayers->mLayers[i].mImage.IsResolved() ||
965          image.GetImageRequest() !=
966              aSecondLayers->mLayers[i].mImage.GetImageRequest())) {
967       if (imgRequestProxy* req = image.GetImageRequest()) {
968         aCallback(req);
969       }
970     }
971   }
972 }
973 
AddAndRemoveImageAssociations(ImageLoader & aImageLoader,nsIFrame * aFrame,const nsStyleImageLayers * aOldLayers,const nsStyleImageLayers * aNewLayers)974 static void AddAndRemoveImageAssociations(
975     ImageLoader& aImageLoader, nsIFrame* aFrame,
976     const nsStyleImageLayers* aOldLayers,
977     const nsStyleImageLayers* aNewLayers) {
978   // If the old context had a background-image image, or mask-image image,
979   // and new context does not have the same image, clear the image load
980   // notifier (which keeps the image loading, if it still is) for the frame.
981   // We want to do this conservatively because some frames paint their
982   // backgrounds from some other frame's style data, and we don't want
983   // to clear those notifiers unless we have to.  (They'll be reset
984   // when we paint, although we could miss a notification in that
985   // interval.)
986   if (aOldLayers && aFrame->HasImageRequest()) {
987     CompareLayers(aOldLayers, aNewLayers, [&](imgRequestProxy* aReq) {
988       aImageLoader.DisassociateRequestFromFrame(aReq, aFrame);
989     });
990   }
991 
992   CompareLayers(aNewLayers, aOldLayers, [&](imgRequestProxy* aReq) {
993     aImageLoader.AssociateRequestToFrame(aReq, aFrame);
994   });
995 }
996 
AddDisplayItem(nsDisplayItem * aItem)997 void nsIFrame::AddDisplayItem(nsDisplayItem* aItem) {
998   MOZ_DIAGNOSTIC_ASSERT(!mDisplayItems.Contains(aItem));
999   mDisplayItems.AppendElement(aItem);
1000 }
1001 
RemoveDisplayItem(nsDisplayItem * aItem)1002 bool nsIFrame::RemoveDisplayItem(nsDisplayItem* aItem) {
1003   return mDisplayItems.RemoveElement(aItem);
1004 }
1005 
HasDisplayItems()1006 bool nsIFrame::HasDisplayItems() { return !mDisplayItems.IsEmpty(); }
1007 
HasDisplayItem(nsDisplayItem * aItem)1008 bool nsIFrame::HasDisplayItem(nsDisplayItem* aItem) {
1009   return mDisplayItems.Contains(aItem);
1010 }
1011 
HasDisplayItem(uint32_t aKey)1012 bool nsIFrame::HasDisplayItem(uint32_t aKey) {
1013   for (nsDisplayItem* i : mDisplayItems) {
1014     if (i->GetPerFrameKey() == aKey) {
1015       return true;
1016     }
1017   }
1018   return false;
1019 }
1020 
1021 template <typename Condition>
DiscardDisplayItems(nsIFrame * aFrame,Condition aCondition)1022 static void DiscardDisplayItems(nsIFrame* aFrame, Condition aCondition) {
1023   for (nsDisplayItem* i : aFrame->DisplayItems()) {
1024     // Only discard items that are invalidated by this frame, as we're only
1025     // guaranteed to rebuild those items. Table background items are created by
1026     // the relevant table part, but have the cell frame as the primary frame,
1027     // and we don't want to remove them if this is the cell.
1028     if (aCondition(i) && i->FrameForInvalidation() == aFrame) {
1029       i->SetCantBeReused();
1030     }
1031   }
1032 }
1033 
DiscardOldItems(nsIFrame * aFrame)1034 static void DiscardOldItems(nsIFrame* aFrame) {
1035   DiscardDisplayItems(aFrame,
1036                       [](nsDisplayItem* aItem) { return aItem->IsOldItem(); });
1037 }
1038 
RemoveDisplayItemDataForDeletion()1039 void nsIFrame::RemoveDisplayItemDataForDeletion() {
1040   nsAutoString name;
1041 #ifdef DEBUG_FRAME_DUMP
1042   if (DL_LOG_TEST(LogLevel::Debug)) {
1043     GetFrameName(name);
1044   }
1045 #endif
1046   DL_LOGV("Removing display item data for frame %p (%s)", this,
1047           NS_ConvertUTF16toUTF8(name).get());
1048 
1049   // Destroying a WebRenderUserDataTable can cause destruction of other objects
1050   // which can remove frame properties in their destructor. If we delete a frame
1051   // property it runs the destructor of the stored object in the middle of
1052   // updating the frame property table, so if the destruction of that object
1053   // causes another update to the frame property table it would leave the frame
1054   // property table in an inconsistent state. So we remove it from the table and
1055   // then destroy it. (bug 1530657)
1056   WebRenderUserDataTable* userDataTable =
1057       TakeProperty(WebRenderUserDataProperty::Key());
1058   if (userDataTable) {
1059     for (const auto& data : userDataTable->Values()) {
1060       data->RemoveFromTable();
1061     }
1062     delete userDataTable;
1063   }
1064 
1065   for (nsDisplayItem* i : DisplayItems()) {
1066     if (i->GetDependentFrame() == this && !i->HasDeletedFrame()) {
1067       i->Frame()->MarkNeedsDisplayItemRebuild();
1068     }
1069     i->RemoveFrame(this);
1070   }
1071 
1072   DisplayItems().Clear();
1073 
1074   if (!nsLayoutUtils::AreRetainedDisplayListsEnabled()) {
1075     // Retained display lists are disabled, no need to update
1076     // RetainedDisplayListData.
1077     return;
1078   }
1079 
1080   nsIFrame* rootFrame = PresShell()->GetRootFrame();
1081   MOZ_ASSERT(rootFrame);
1082 
1083   RetainedDisplayListData* data = GetOrSetRetainedDisplayListData(rootFrame);
1084 
1085   const bool updateData = IsFrameModified() || HasOverrideDirtyRegion() ||
1086                           MayHaveWillChangeBudget();
1087 
1088   if (!updateData) {
1089     // No RetainedDisplayListData to update.
1090     MOZ_DIAGNOSTIC_ASSERT(!data->IsModified(this),
1091                           "Deleted frame is in modified frame list");
1092     return;
1093   }
1094 
1095   if (MayHaveWillChangeBudget()) {
1096     // Keep the frame in list, so it can be removed from the will-change budget.
1097     data->Flags(this) = RetainedDisplayListData::FrameFlag::HadWillChange;
1098     return;
1099   }
1100 
1101   if (IsFrameModified() || HasOverrideDirtyRegion()) {
1102     // Remove deleted frames from RetainedDisplayListData.
1103     DebugOnly<bool> removed = data->Remove(this);
1104     MOZ_ASSERT(removed,
1105                "Frame had flags set, but it was not found in DisplayListData!");
1106   }
1107 }
1108 
MarkNeedsDisplayItemRebuild()1109 void nsIFrame::MarkNeedsDisplayItemRebuild() {
1110   if (!nsLayoutUtils::AreRetainedDisplayListsEnabled() || IsFrameModified() ||
1111       HasAnyStateBits(NS_FRAME_IN_POPUP)) {
1112     // Skip frames that are already marked modified.
1113     return;
1114   }
1115 
1116   if (Type() == LayoutFrameType::Placeholder) {
1117     nsIFrame* oof = static_cast<nsPlaceholderFrame*>(this)->GetOutOfFlowFrame();
1118     if (oof) {
1119       oof->MarkNeedsDisplayItemRebuild();
1120     }
1121     // Do not mark placeholder frames modified.
1122     return;
1123   }
1124 
1125   if (!nsLayoutUtils::DisplayRootHasRetainedDisplayListBuilder(this)) {
1126     return;
1127   }
1128 
1129   nsIFrame* rootFrame = PresShell()->GetRootFrame();
1130   MOZ_ASSERT(rootFrame);
1131 
1132   if (rootFrame->IsFrameModified()) {
1133     return;
1134   }
1135 
1136   nsAutoString name;
1137 #ifdef DEBUG_FRAME_DUMP
1138   if (DL_LOG_TEST(LogLevel::Debug)) {
1139     GetFrameName(name);
1140   }
1141 #endif
1142 
1143   DL_LOGV("RDL - Rebuilding display items for frame %p (%s)", this,
1144           NS_ConvertUTF16toUTF8(name).get());
1145 
1146   RetainedDisplayListData* data = GetOrSetRetainedDisplayListData(rootFrame);
1147   if (data->ModifiedFramesCount() >
1148       StaticPrefs::layout_display_list_rebuild_frame_limit()) {
1149     // If the modified frames count is above the rebuild limit, mark the root
1150     // frame modified, and stop marking additional frames modified.
1151     data->AddModifiedFrame(rootFrame);
1152     rootFrame->SetFrameIsModified(true);
1153     return;
1154   }
1155 
1156   data->AddModifiedFrame(this);
1157   SetFrameIsModified(true);
1158 
1159   MOZ_ASSERT(
1160       PresContext()->LayoutPhaseCount(nsLayoutPhase::DisplayListBuilding) == 0);
1161 
1162   // Hopefully this is cheap, but we could use a frame state bit to note
1163   // the presence of dependencies to speed it up.
1164   for (nsDisplayItem* i : DisplayItems()) {
1165     if (i->HasDeletedFrame() || i->Frame() == this) {
1166       // Ignore the items with deleted frames, and the items with |this| as
1167       // the primary frame.
1168       continue;
1169     }
1170 
1171     if (i->GetDependentFrame() == this) {
1172       // For items with |this| as a dependent frame, mark the primary frame
1173       // for rebuild.
1174       i->Frame()->MarkNeedsDisplayItemRebuild();
1175     }
1176   }
1177 }
1178 
1179 // Subclass hook for style post processing
1180 /* virtual */
DidSetComputedStyle(ComputedStyle * aOldComputedStyle)1181 void nsIFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
1182 #ifdef ACCESSIBILITY
1183   // Don't notify for reconstructed frames here, since the frame is still being
1184   // constructed at this point and so LocalAccessible::GetFrame() will return
1185   // null. Style changes for reconstructed frames are handled in
1186   // DocAccessible::PruneOrInsertSubtree.
1187   if (aOldComputedStyle) {
1188     if (nsAccessibilityService* accService = GetAccService()) {
1189       accService->NotifyOfComputedStyleChange(PresShell(), mContent);
1190     }
1191   }
1192 #endif
1193 
1194   MaybeScheduleReflowSVGNonDisplayText(this);
1195 
1196   Document* doc = PresContext()->Document();
1197   ImageLoader* loader = doc->StyleImageLoader();
1198   // Continuing text frame doesn't initialize its continuation pointer before
1199   // reaching here for the first time, so we have to exclude text frames. This
1200   // doesn't affect correctness because text can't match selectors.
1201   //
1202   // FIXME(emilio): We should consider fixing that.
1203   //
1204   // TODO(emilio): Can we avoid doing some / all of the image stuff when
1205   // isNonTextFirstContinuation is false? We should consider doing this just for
1206   // primary frames and pseudos, but the first-line reparenting code makes it
1207   // all bad, should get around to bug 1465474 eventually :(
1208   const bool isNonText = !IsTextFrame();
1209   if (isNonText) {
1210     mComputedStyle->StartImageLoads(*doc, aOldComputedStyle);
1211   }
1212 
1213   const nsStyleImageLayers* oldLayers =
1214       aOldComputedStyle ? &aOldComputedStyle->StyleBackground()->mImage
1215                         : nullptr;
1216   const nsStyleImageLayers* newLayers = &StyleBackground()->mImage;
1217   AddAndRemoveImageAssociations(*loader, this, oldLayers, newLayers);
1218 
1219   oldLayers =
1220       aOldComputedStyle ? &aOldComputedStyle->StyleSVGReset()->mMask : nullptr;
1221   newLayers = &StyleSVGReset()->mMask;
1222   AddAndRemoveImageAssociations(*loader, this, oldLayers, newLayers);
1223 
1224   const nsStyleDisplay* disp = StyleDisplay();
1225   bool handleStickyChange = false;
1226   if (aOldComputedStyle) {
1227     // Detect style changes that should trigger a scroll anchor adjustment
1228     // suppression.
1229     // https://drafts.csswg.org/css-scroll-anchoring/#suppression-triggers
1230     bool needAnchorSuppression = false;
1231 
1232     // If we detect a change on margin, padding or border, we store the old
1233     // values on the frame itself between now and reflow, so if someone
1234     // calls GetUsed(Margin|Border|Padding)() before the next reflow, we
1235     // can give an accurate answer.
1236     // We don't want to set the property if one already exists.
1237     nsMargin oldValue(0, 0, 0, 0);
1238     nsMargin newValue(0, 0, 0, 0);
1239     const nsStyleMargin* oldMargin = aOldComputedStyle->StyleMargin();
1240     if (oldMargin->GetMargin(oldValue)) {
1241       if (!StyleMargin()->GetMargin(newValue) || oldValue != newValue) {
1242         if (!HasProperty(UsedMarginProperty())) {
1243           AddProperty(UsedMarginProperty(), new nsMargin(oldValue));
1244         }
1245         needAnchorSuppression = true;
1246       }
1247     }
1248 
1249     const nsStylePadding* oldPadding = aOldComputedStyle->StylePadding();
1250     if (oldPadding->GetPadding(oldValue)) {
1251       if (!StylePadding()->GetPadding(newValue) || oldValue != newValue) {
1252         if (!HasProperty(UsedPaddingProperty())) {
1253           AddProperty(UsedPaddingProperty(), new nsMargin(oldValue));
1254         }
1255         needAnchorSuppression = true;
1256       }
1257     }
1258 
1259     const nsStyleBorder* oldBorder = aOldComputedStyle->StyleBorder();
1260     oldValue = oldBorder->GetComputedBorder();
1261     newValue = StyleBorder()->GetComputedBorder();
1262     if (oldValue != newValue && !HasProperty(UsedBorderProperty())) {
1263       AddProperty(UsedBorderProperty(), new nsMargin(oldValue));
1264     }
1265 
1266     const nsStyleDisplay* oldDisp = aOldComputedStyle->StyleDisplay();
1267     if (oldDisp->mOverflowAnchor != disp->mOverflowAnchor) {
1268       if (auto* container = ScrollAnchorContainer::FindFor(this)) {
1269         container->InvalidateAnchor();
1270       }
1271       if (nsIScrollableFrame* scrollableFrame = do_QueryFrame(this)) {
1272         scrollableFrame->Anchor()->InvalidateAnchor();
1273       }
1274     }
1275 
1276     if (mInScrollAnchorChain) {
1277       const nsStylePosition* pos = StylePosition();
1278       const nsStylePosition* oldPos = aOldComputedStyle->StylePosition();
1279       if (!needAnchorSuppression &&
1280           (oldPos->mOffset != pos->mOffset || oldPos->mWidth != pos->mWidth ||
1281            oldPos->mMinWidth != pos->mMinWidth ||
1282            oldPos->mMaxWidth != pos->mMaxWidth ||
1283            oldPos->mHeight != pos->mHeight ||
1284            oldPos->mMinHeight != pos->mMinHeight ||
1285            oldPos->mMaxHeight != pos->mMaxHeight ||
1286            oldDisp->mPosition != disp->mPosition ||
1287            oldDisp->mTransform != disp->mTransform)) {
1288         needAnchorSuppression = true;
1289       }
1290 
1291       if (needAnchorSuppression &&
1292           StaticPrefs::layout_css_scroll_anchoring_suppressions_enabled()) {
1293         ScrollAnchorContainer::FindFor(this)->SuppressAdjustments();
1294       }
1295     }
1296 
1297     if (disp->mPosition != oldDisp->mPosition) {
1298       if (!disp->IsRelativelyOrStickyPositionedStyle() &&
1299           oldDisp->IsRelativelyOrStickyPositionedStyle()) {
1300         RemoveProperty(NormalPositionProperty());
1301       }
1302 
1303       handleStickyChange = disp->mPosition == StylePositionProperty::Sticky ||
1304                            oldDisp->mPosition == StylePositionProperty::Sticky;
1305     }
1306   } else {  // !aOldComputedStyle
1307     handleStickyChange = disp->mPosition == StylePositionProperty::Sticky;
1308   }
1309 
1310   if (handleStickyChange && !HasAnyStateBits(NS_FRAME_IS_NONDISPLAY) &&
1311       !GetPrevInFlow()) {
1312     // Note that we only add first continuations, but we really only
1313     // want to add first continuation-or-ib-split-siblings. But since we don't
1314     // yet know if we're a later part of a block-in-inline split, we'll just
1315     // add later members of a block-in-inline split here, and then
1316     // StickyScrollContainer will remove them later.
1317     if (auto* ssc =
1318             StickyScrollContainer::GetStickyScrollContainerForFrame(this)) {
1319       if (disp->mPosition == StylePositionProperty::Sticky) {
1320         ssc->AddFrame(this);
1321       } else {
1322         ssc->RemoveFrame(this);
1323       }
1324     }
1325   }
1326 
1327   imgIRequest* oldBorderImage =
1328       aOldComputedStyle
1329           ? aOldComputedStyle->StyleBorder()->GetBorderImageRequest()
1330           : nullptr;
1331   imgIRequest* newBorderImage = StyleBorder()->GetBorderImageRequest();
1332   // FIXME (Bug 759996): The following is no longer true.
1333   // For border-images, we can't be as conservative (we need to set the
1334   // new loaders if there has been any change) since the CalcDifference
1335   // call depended on the result of GetComputedBorder() and that result
1336   // depends on whether the image has loaded, start the image load now
1337   // so that we'll get notified when it completes loading and can do a
1338   // restyle.  Otherwise, the image might finish loading from the
1339   // network before we start listening to its notifications, and then
1340   // we'll never know that it's finished loading.  Likewise, we want to
1341   // do this for freshly-created frames to prevent a similar race if the
1342   // image loads between reflow (which can depend on whether the image
1343   // is loaded) and paint.  We also don't really care about any callers who try
1344   // to paint borders with a different style, because they won't have the
1345   // correct size for the border either.
1346   if (oldBorderImage != newBorderImage) {
1347     // stop and restart the image loading/notification
1348     if (oldBorderImage && HasImageRequest()) {
1349       RemoveProperty(CachedBorderImageDataProperty());
1350       loader->DisassociateRequestFromFrame(oldBorderImage, this);
1351     }
1352     if (newBorderImage) {
1353       loader->AssociateRequestToFrame(newBorderImage, this);
1354     }
1355   }
1356 
1357   auto GetShapeImageRequest = [](const ComputedStyle* aStyle) -> imgIRequest* {
1358     if (!aStyle) {
1359       return nullptr;
1360     }
1361     auto& shape = aStyle->StyleDisplay()->mShapeOutside;
1362     if (!shape.IsImage()) {
1363       return nullptr;
1364     }
1365     return shape.AsImage().GetImageRequest();
1366   };
1367 
1368   imgIRequest* oldShapeImage = GetShapeImageRequest(aOldComputedStyle);
1369   imgIRequest* newShapeImage = GetShapeImageRequest(Style());
1370   if (oldShapeImage != newShapeImage) {
1371     if (oldShapeImage && HasImageRequest()) {
1372       loader->DisassociateRequestFromFrame(oldShapeImage, this);
1373     }
1374     if (newShapeImage) {
1375       loader->AssociateRequestToFrame(
1376           newShapeImage, this,
1377           ImageLoader::Flags::
1378               RequiresReflowOnFirstFrameCompleteAndLoadEventBlocking);
1379     }
1380   }
1381 
1382   // SVGObserverUtils::GetEffectProperties() asserts that we only invoke it with
1383   // the first continuation so we need to check that in advance.
1384   const bool isNonTextFirstContinuation = isNonText && !GetPrevContinuation();
1385   if (isNonTextFirstContinuation) {
1386     // Kick off loading of external SVG resources referenced from properties if
1387     // any. This currently includes filter, clip-path, and mask.
1388     SVGObserverUtils::InitiateResourceDocLoads(this);
1389   }
1390 
1391   // If the page contains markup that overrides text direction, and
1392   // does not contain any characters that would activate the Unicode
1393   // bidi algorithm, we need to call |SetBidiEnabled| on the pres
1394   // context before reflow starts.  See bug 115921.
1395   if (StyleVisibility()->mDirection == StyleDirection::Rtl) {
1396     PresContext()->SetBidiEnabled();
1397   }
1398 
1399   // The following part is for caching offset-path:path(). We cache the
1400   // flatten gfx path, so we don't have to rebuild and re-flattern it at
1401   // each cycle if we have animations on offset-* with a fixed offset-path.
1402   const StyleOffsetPath* oldPath =
1403       aOldComputedStyle ? &aOldComputedStyle->StyleDisplay()->mOffsetPath
1404                         : nullptr;
1405   const StyleOffsetPath& newPath = StyleDisplay()->mOffsetPath;
1406   if (!oldPath || *oldPath != newPath) {
1407     if (newPath.IsPath()) {
1408       // Here we only need to build a valid path for motion path, so
1409       // using the default values of stroke-width, stoke-linecap, and fill-rule
1410       // is fine for now because what we want is to get the point and its normal
1411       // vector along the path, instead of rendering it.
1412       RefPtr<gfx::PathBuilder> builder =
1413           gfxPlatform::GetPlatform()
1414               ->ScreenReferenceDrawTarget()
1415               ->CreatePathBuilder(gfx::FillRule::FILL_WINDING);
1416       RefPtr<gfx::Path> path =
1417           MotionPathUtils::BuildPath(newPath.AsPath(), builder);
1418       if (path) {
1419         // The newPath could be path('') (i.e. empty path), so its gfx path
1420         // could be nullptr, and so we only set property for a non-empty path.
1421         SetProperty(nsIFrame::OffsetPathCache(), path.forget().take());
1422       } else {
1423         // May have an old cached path, so we have to delete it.
1424         RemoveProperty(nsIFrame::OffsetPathCache());
1425       }
1426     } else if (oldPath) {
1427       RemoveProperty(nsIFrame::OffsetPathCache());
1428     }
1429   }
1430 
1431   RemoveStateBits(NS_FRAME_SIMPLE_EVENT_REGIONS | NS_FRAME_SIMPLE_DISPLAYLIST);
1432 
1433   mMayHaveRoundedCorners = true;
1434 }
1435 
1436 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
AssertNewStyleIsSane(ComputedStyle & aNewStyle)1437 void nsIFrame::AssertNewStyleIsSane(ComputedStyle& aNewStyle) {
1438   MOZ_DIAGNOSTIC_ASSERT(
1439       aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() ||
1440       // ::first-line continuations are weird, this should probably be fixed via
1441       // bug 1465474.
1442       (mComputedStyle->GetPseudoType() == PseudoStyleType::firstLine &&
1443        aNewStyle.GetPseudoType() == PseudoStyleType::mozLineFrame) ||
1444       // ::first-letter continuations are broken, in particular floating ones,
1445       // see bug 1490281. The construction code tries to fix this up after the
1446       // fact, then restyling undoes it...
1447       (mComputedStyle->GetPseudoType() == PseudoStyleType::mozText &&
1448        aNewStyle.GetPseudoType() == PseudoStyleType::firstLetterContinuation) ||
1449       (mComputedStyle->GetPseudoType() ==
1450            PseudoStyleType::firstLetterContinuation &&
1451        aNewStyle.GetPseudoType() == PseudoStyleType::mozText));
1452 }
1453 #endif
1454 
ReparentFrameViewTo(nsViewManager * aViewManager,nsView * aNewParentView,nsView * aOldParentView)1455 void nsIFrame::ReparentFrameViewTo(nsViewManager* aViewManager,
1456                                    nsView* aNewParentView,
1457                                    nsView* aOldParentView) {
1458   if (HasView()) {
1459     if (IsMenuPopupFrame()) {
1460       // This view must be parented by the root view, don't reparent it.
1461       return;
1462     }
1463     nsView* view = GetView();
1464     // Verify that the current parent view is what we think it is
1465     // nsView*  parentView;
1466     // NS_ASSERTION(parentView == aOldParentView, "unexpected parent view");
1467 
1468     aViewManager->RemoveChild(view);
1469 
1470     // The view will remember the Z-order and other attributes that have been
1471     // set on it.
1472     nsView* insertBefore =
1473         nsLayoutUtils::FindSiblingViewFor(aNewParentView, this);
1474     aViewManager->InsertChild(aNewParentView, view, insertBefore,
1475                               insertBefore != nullptr);
1476   } else if (HasAnyStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW)) {
1477     for (const auto& childList : ChildLists()) {
1478       // Iterate the child frames, and check each child frame to see if it has
1479       // a view
1480       for (nsIFrame* child : childList.mList) {
1481         child->ReparentFrameViewTo(aViewManager, aNewParentView,
1482                                    aOldParentView);
1483       }
1484     }
1485   }
1486 }
1487 
SyncFrameViewProperties(nsView * aView)1488 void nsIFrame::SyncFrameViewProperties(nsView* aView) {
1489   if (!aView) {
1490     aView = GetView();
1491     if (!aView) {
1492       return;
1493     }
1494   }
1495 
1496   nsViewManager* vm = aView->GetViewManager();
1497 
1498   // Make sure visibility is correct. This only affects nsSubDocumentFrame.
1499   if (!SupportsVisibilityHidden()) {
1500     // See if the view should be hidden or visible
1501     ComputedStyle* sc = Style();
1502     vm->SetViewVisibility(aView, sc->StyleVisibility()->IsVisible()
1503                                      ? nsViewVisibility_kShow
1504                                      : nsViewVisibility_kHide);
1505   }
1506 
1507   const auto zIndex = ZIndex();
1508   const bool autoZIndex = !zIndex;
1509   vm->SetViewZIndex(aView, autoZIndex, zIndex.valueOr(0));
1510 }
1511 
CreateView()1512 void nsIFrame::CreateView() {
1513   MOZ_ASSERT(!HasView());
1514 
1515   nsView* parentView = GetParent()->GetClosestView();
1516   MOZ_ASSERT(parentView, "no parent with view");
1517 
1518   nsViewManager* viewManager = parentView->GetViewManager();
1519   MOZ_ASSERT(viewManager, "null view manager");
1520 
1521   nsView* view = viewManager->CreateView(GetRect(), parentView);
1522   SyncFrameViewProperties(view);
1523 
1524   nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(parentView, this);
1525   // we insert this view 'above' the insertBefore view, unless insertBefore is
1526   // null, in which case we want to call with aAbove == false to insert at the
1527   // beginning in document order
1528   viewManager->InsertChild(parentView, view, insertBefore,
1529                            insertBefore != nullptr);
1530 
1531   // REVIEW: Don't create a widget for fixed-pos elements anymore.
1532   // ComputeRepaintRegionForCopy will calculate the right area to repaint
1533   // when we scroll.
1534   // Reparent views on any child frames (or their descendants) to this
1535   // view. We can just call ReparentFrameViewTo on this frame because
1536   // we know this frame has no view, so it will crawl the children. Also,
1537   // we know that any descendants with views must have 'parentView' as their
1538   // parent view.
1539   ReparentFrameViewTo(viewManager, view, parentView);
1540 
1541   // Remember our view
1542   SetView(view);
1543 
1544   NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
1545                ("nsIFrame::CreateView: frame=%p view=%p", this, view));
1546 }
1547 
1548 // MSVC fails with link error "one or more multiply defined symbols found",
1549 // gcc fails with "hidden symbol `nsIFrame::kPrincipalList' isn't defined"
1550 // etc if they are not defined.
1551 #ifndef _MSC_VER
1552 // static nsIFrame constants; initialized in the header file.
1553 const nsIFrame::ChildListID nsIFrame::kPrincipalList;
1554 const nsIFrame::ChildListID nsIFrame::kAbsoluteList;
1555 const nsIFrame::ChildListID nsIFrame::kBulletList;
1556 const nsIFrame::ChildListID nsIFrame::kCaptionList;
1557 const nsIFrame::ChildListID nsIFrame::kColGroupList;
1558 const nsIFrame::ChildListID nsIFrame::kExcessOverflowContainersList;
1559 const nsIFrame::ChildListID nsIFrame::kFixedList;
1560 const nsIFrame::ChildListID nsIFrame::kFloatList;
1561 const nsIFrame::ChildListID nsIFrame::kOverflowContainersList;
1562 const nsIFrame::ChildListID nsIFrame::kOverflowList;
1563 const nsIFrame::ChildListID nsIFrame::kOverflowOutOfFlowList;
1564 const nsIFrame::ChildListID nsIFrame::kPopupList;
1565 const nsIFrame::ChildListID nsIFrame::kPushedFloatsList;
1566 const nsIFrame::ChildListID nsIFrame::kSelectPopupList;
1567 const nsIFrame::ChildListID nsIFrame::kNoReflowPrincipalList;
1568 #endif
1569 
1570 /* virtual */
GetUsedMargin() const1571 nsMargin nsIFrame::GetUsedMargin() const {
1572   nsMargin margin(0, 0, 0, 0);
1573   if (((mState & NS_FRAME_FIRST_REFLOW) && !(mState & NS_FRAME_IN_REFLOW)) ||
1574       SVGUtils::IsInSVGTextSubtree(this))
1575     return margin;
1576 
1577   nsMargin* m = GetProperty(UsedMarginProperty());
1578   if (m) {
1579     margin = *m;
1580   } else {
1581     if (!StyleMargin()->GetMargin(margin)) {
1582       // If we get here, our caller probably shouldn't be calling us...
1583       NS_ERROR(
1584           "Returning bogus 0-sized margin, because this margin "
1585           "depends on layout & isn't cached!");
1586     }
1587   }
1588   return margin;
1589 }
1590 
1591 /* virtual */
GetUsedBorder() const1592 nsMargin nsIFrame::GetUsedBorder() const {
1593   nsMargin border(0, 0, 0, 0);
1594   if (((mState & NS_FRAME_FIRST_REFLOW) && !(mState & NS_FRAME_IN_REFLOW)) ||
1595       SVGUtils::IsInSVGTextSubtree(this))
1596     return border;
1597 
1598   // Theme methods don't use const-ness.
1599   nsIFrame* mutable_this = const_cast<nsIFrame*>(this);
1600 
1601   const nsStyleDisplay* disp = StyleDisplay();
1602   if (mutable_this->IsThemed(disp)) {
1603     nsPresContext* pc = PresContext();
1604     LayoutDeviceIntMargin widgetBorder = pc->Theme()->GetWidgetBorder(
1605         pc->DeviceContext(), mutable_this, disp->EffectiveAppearance());
1606     border =
1607         LayoutDevicePixel::ToAppUnits(widgetBorder, pc->AppUnitsPerDevPixel());
1608     return border;
1609   }
1610 
1611   nsMargin* b = GetProperty(UsedBorderProperty());
1612   if (b) {
1613     border = *b;
1614   } else {
1615     border = StyleBorder()->GetComputedBorder();
1616   }
1617   return border;
1618 }
1619 
1620 /* virtual */
GetUsedPadding() const1621 nsMargin nsIFrame::GetUsedPadding() const {
1622   nsMargin padding(0, 0, 0, 0);
1623   if (((mState & NS_FRAME_FIRST_REFLOW) && !(mState & NS_FRAME_IN_REFLOW)) ||
1624       SVGUtils::IsInSVGTextSubtree(this))
1625     return padding;
1626 
1627   // Theme methods don't use const-ness.
1628   nsIFrame* mutable_this = const_cast<nsIFrame*>(this);
1629 
1630   const nsStyleDisplay* disp = StyleDisplay();
1631   if (mutable_this->IsThemed(disp)) {
1632     nsPresContext* pc = PresContext();
1633     LayoutDeviceIntMargin widgetPadding;
1634     if (pc->Theme()->GetWidgetPadding(pc->DeviceContext(), mutable_this,
1635                                       disp->EffectiveAppearance(),
1636                                       &widgetPadding)) {
1637       return LayoutDevicePixel::ToAppUnits(widgetPadding,
1638                                            pc->AppUnitsPerDevPixel());
1639     }
1640   }
1641 
1642   nsMargin* p = GetProperty(UsedPaddingProperty());
1643   if (p) {
1644     padding = *p;
1645   } else {
1646     if (!StylePadding()->GetPadding(padding)) {
1647       // If we get here, our caller probably shouldn't be calling us...
1648       NS_ERROR(
1649           "Returning bogus 0-sized padding, because this padding "
1650           "depends on layout & isn't cached!");
1651     }
1652   }
1653   return padding;
1654 }
1655 
GetSkipSides() const1656 nsIFrame::Sides nsIFrame::GetSkipSides() const {
1657   if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
1658                    StyleBoxDecorationBreak::Clone) &&
1659       !HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
1660     return Sides();
1661   }
1662 
1663   // Convert the logical skip sides to physical sides using the frame's
1664   // writing mode
1665   WritingMode writingMode = GetWritingMode();
1666   LogicalSides logicalSkip = GetLogicalSkipSides();
1667   Sides skip;
1668 
1669   if (logicalSkip.BStart()) {
1670     if (writingMode.IsVertical()) {
1671       skip |= writingMode.IsVerticalLR() ? SideBits::eLeft : SideBits::eRight;
1672     } else {
1673       skip |= SideBits::eTop;
1674     }
1675   }
1676 
1677   if (logicalSkip.BEnd()) {
1678     if (writingMode.IsVertical()) {
1679       skip |= writingMode.IsVerticalLR() ? SideBits::eRight : SideBits::eLeft;
1680     } else {
1681       skip |= SideBits::eBottom;
1682     }
1683   }
1684 
1685   if (logicalSkip.IStart()) {
1686     if (writingMode.IsVertical()) {
1687       skip |= SideBits::eTop;
1688     } else {
1689       skip |= writingMode.IsBidiLTR() ? SideBits::eLeft : SideBits::eRight;
1690     }
1691   }
1692 
1693   if (logicalSkip.IEnd()) {
1694     if (writingMode.IsVertical()) {
1695       skip |= SideBits::eBottom;
1696     } else {
1697       skip |= writingMode.IsBidiLTR() ? SideBits::eRight : SideBits::eLeft;
1698     }
1699   }
1700   return skip;
1701 }
1702 
GetPaddingRectRelativeToSelf() const1703 nsRect nsIFrame::GetPaddingRectRelativeToSelf() const {
1704   nsMargin border = GetUsedBorder().ApplySkipSides(GetSkipSides());
1705   nsRect r(0, 0, mRect.width, mRect.height);
1706   r.Deflate(border);
1707   return r;
1708 }
1709 
GetPaddingRect() const1710 nsRect nsIFrame::GetPaddingRect() const {
1711   return GetPaddingRectRelativeToSelf() + GetPosition();
1712 }
1713 
WritingModeForLine(WritingMode aSelfWM,nsIFrame * aSubFrame) const1714 WritingMode nsIFrame::WritingModeForLine(WritingMode aSelfWM,
1715                                          nsIFrame* aSubFrame) const {
1716   MOZ_ASSERT(aSelfWM == GetWritingMode());
1717   WritingMode writingMode = aSelfWM;
1718 
1719   if (StyleTextReset()->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_PLAINTEXT) {
1720     mozilla::intl::BidiEmbeddingLevel frameLevel =
1721         nsBidiPresUtils::GetFrameBaseLevel(aSubFrame);
1722     writingMode.SetDirectionFromBidiLevel(frameLevel);
1723   }
1724 
1725   return writingMode;
1726 }
1727 
GetMarginRect() const1728 nsRect nsIFrame::GetMarginRect() const {
1729   return GetMarginRectRelativeToSelf() + GetPosition();
1730 }
1731 
GetMarginRectRelativeToSelf() const1732 nsRect nsIFrame::GetMarginRectRelativeToSelf() const {
1733   nsMargin m = GetUsedMargin().ApplySkipSides(GetSkipSides());
1734   nsRect r(0, 0, mRect.width, mRect.height);
1735   r.Inflate(m);
1736   return r;
1737 }
1738 
IsTransformed() const1739 bool nsIFrame::IsTransformed() const {
1740   if (!HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED)) {
1741     MOZ_ASSERT(!IsCSSTransformed());
1742     MOZ_ASSERT(!IsSVGTransformed());
1743     return false;
1744   }
1745   return IsCSSTransformed() || IsSVGTransformed();
1746 }
1747 
IsCSSTransformed() const1748 bool nsIFrame::IsCSSTransformed() const {
1749   return HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED) &&
1750          (StyleDisplay()->HasTransform(this) || HasAnimationOfTransform());
1751 }
1752 
HasAnimationOfTransform() const1753 bool nsIFrame::HasAnimationOfTransform() const {
1754   return IsPrimaryFrame() &&
1755          nsLayoutUtils::HasAnimationOfTransformAndMotionPath(this) &&
1756          IsFrameOfType(eSupportsCSSTransforms);
1757 }
1758 
ChildrenHavePerspective(const nsStyleDisplay * aStyleDisplay) const1759 bool nsIFrame::ChildrenHavePerspective(
1760     const nsStyleDisplay* aStyleDisplay) const {
1761   MOZ_ASSERT(aStyleDisplay == StyleDisplay());
1762   return aStyleDisplay->HasPerspective(this);
1763 }
1764 
HasAnimationOfOpacity(EffectSet * aEffectSet) const1765 bool nsIFrame::HasAnimationOfOpacity(EffectSet* aEffectSet) const {
1766   return ((nsLayoutUtils::IsPrimaryStyleFrame(this) ||
1767            nsLayoutUtils::FirstContinuationOrIBSplitSibling(this)
1768                ->IsPrimaryFrame()) &&
1769           nsLayoutUtils::HasAnimationOfPropertySet(
1770               this, nsCSSPropertyIDSet::OpacityProperties(), aEffectSet));
1771 }
1772 
HasOpacityInternal(float aThreshold,const nsStyleDisplay * aStyleDisplay,const nsStyleEffects * aStyleEffects,EffectSet * aEffectSet) const1773 bool nsIFrame::HasOpacityInternal(float aThreshold,
1774                                   const nsStyleDisplay* aStyleDisplay,
1775                                   const nsStyleEffects* aStyleEffects,
1776                                   EffectSet* aEffectSet) const {
1777   MOZ_ASSERT(0.0 <= aThreshold && aThreshold <= 1.0, "Invalid argument");
1778   if (aStyleEffects->mOpacity < aThreshold ||
1779       (aStyleDisplay->mWillChange.bits & StyleWillChangeBits::OPACITY)) {
1780     return true;
1781   }
1782 
1783   if (!mMayHaveOpacityAnimation) {
1784     return false;
1785   }
1786 
1787   return HasAnimationOfOpacity(aEffectSet);
1788 }
1789 
IsSVGTransformed(gfx::Matrix * aOwnTransforms,gfx::Matrix * aFromParentTransforms) const1790 bool nsIFrame::IsSVGTransformed(gfx::Matrix* aOwnTransforms,
1791                                 gfx::Matrix* aFromParentTransforms) const {
1792   return false;
1793 }
1794 
Extend3DContext(const nsStyleDisplay * aStyleDisplay,const nsStyleEffects * aStyleEffects,mozilla::EffectSet * aEffectSetForOpacity) const1795 bool nsIFrame::Extend3DContext(const nsStyleDisplay* aStyleDisplay,
1796                                const nsStyleEffects* aStyleEffects,
1797                                mozilla::EffectSet* aEffectSetForOpacity) const {
1798   if (!(mState & NS_FRAME_MAY_BE_TRANSFORMED)) {
1799     return false;
1800   }
1801   const nsStyleDisplay* disp = StyleDisplayWithOptionalParam(aStyleDisplay);
1802   if (disp->mTransformStyle != StyleTransformStyle::Preserve3d ||
1803       !IsFrameOfType(nsIFrame::eSupportsCSSTransforms)) {
1804     return false;
1805   }
1806 
1807   // If we're all scroll frame, then all descendants will be clipped, so we
1808   // can't preserve 3d.
1809   if (IsScrollFrame()) {
1810     return false;
1811   }
1812 
1813   const nsStyleEffects* effects = StyleEffectsWithOptionalParam(aStyleEffects);
1814   if (HasOpacity(disp, effects, aEffectSetForOpacity)) {
1815     return false;
1816   }
1817 
1818   return ShouldApplyOverflowClipping(disp) == PhysicalAxes::None &&
1819          !GetClipPropClipRect(disp, effects, GetSize()) &&
1820          !SVGIntegrationUtils::UsingEffectsForFrame(this) &&
1821          !effects->HasMixBlendMode() &&
1822          disp->mIsolation != StyleIsolation::Isolate;
1823 }
1824 
Combines3DTransformWithAncestors() const1825 bool nsIFrame::Combines3DTransformWithAncestors() const {
1826   nsIFrame* parent = GetClosestFlattenedTreeAncestorPrimaryFrame();
1827   if (!parent || !parent->Extend3DContext()) {
1828     return false;
1829   }
1830   return IsCSSTransformed() || BackfaceIsHidden();
1831 }
1832 
In3DContextAndBackfaceIsHidden() const1833 bool nsIFrame::In3DContextAndBackfaceIsHidden() const {
1834   // While both tests fail most of the time, test BackfaceIsHidden()
1835   // first since it's likely to fail faster.
1836   return BackfaceIsHidden() && Combines3DTransformWithAncestors();
1837 }
1838 
HasPerspective() const1839 bool nsIFrame::HasPerspective() const {
1840   if (!IsCSSTransformed()) {
1841     return false;
1842   }
1843   nsIFrame* parent = GetClosestFlattenedTreeAncestorPrimaryFrame();
1844   if (!parent) {
1845     return false;
1846   }
1847   return parent->ChildrenHavePerspective();
1848 }
1849 
GetContentRectRelativeToSelf() const1850 nsRect nsIFrame::GetContentRectRelativeToSelf() const {
1851   nsMargin bp = GetUsedBorderAndPadding().ApplySkipSides(GetSkipSides());
1852   nsRect r(0, 0, mRect.width, mRect.height);
1853   r.Deflate(bp);
1854   return r;
1855 }
1856 
GetContentRect() const1857 nsRect nsIFrame::GetContentRect() const {
1858   return GetContentRectRelativeToSelf() + GetPosition();
1859 }
1860 
ComputeBorderRadii(const BorderRadius & aBorderRadius,const nsSize & aFrameSize,const nsSize & aBorderArea,Sides aSkipSides,nscoord aRadii[8])1861 bool nsIFrame::ComputeBorderRadii(const BorderRadius& aBorderRadius,
1862                                   const nsSize& aFrameSize,
1863                                   const nsSize& aBorderArea, Sides aSkipSides,
1864                                   nscoord aRadii[8]) {
1865   // Percentages are relative to whichever side they're on.
1866   for (const auto i : mozilla::AllPhysicalHalfCorners()) {
1867     const LengthPercentage& c = aBorderRadius.Get(i);
1868     nscoord axis = HalfCornerIsX(i) ? aFrameSize.width : aFrameSize.height;
1869     aRadii[i] = std::max(0, c.Resolve(axis));
1870   }
1871 
1872   if (aSkipSides.Top()) {
1873     aRadii[eCornerTopLeftX] = 0;
1874     aRadii[eCornerTopLeftY] = 0;
1875     aRadii[eCornerTopRightX] = 0;
1876     aRadii[eCornerTopRightY] = 0;
1877   }
1878 
1879   if (aSkipSides.Right()) {
1880     aRadii[eCornerTopRightX] = 0;
1881     aRadii[eCornerTopRightY] = 0;
1882     aRadii[eCornerBottomRightX] = 0;
1883     aRadii[eCornerBottomRightY] = 0;
1884   }
1885 
1886   if (aSkipSides.Bottom()) {
1887     aRadii[eCornerBottomRightX] = 0;
1888     aRadii[eCornerBottomRightY] = 0;
1889     aRadii[eCornerBottomLeftX] = 0;
1890     aRadii[eCornerBottomLeftY] = 0;
1891   }
1892 
1893   if (aSkipSides.Left()) {
1894     aRadii[eCornerBottomLeftX] = 0;
1895     aRadii[eCornerBottomLeftY] = 0;
1896     aRadii[eCornerTopLeftX] = 0;
1897     aRadii[eCornerTopLeftY] = 0;
1898   }
1899 
1900   // css3-background specifies this algorithm for reducing
1901   // corner radii when they are too big.
1902   bool haveRadius = false;
1903   double ratio = 1.0f;
1904   for (const auto side : mozilla::AllPhysicalSides()) {
1905     uint32_t hc1 = SideToHalfCorner(side, false, true);
1906     uint32_t hc2 = SideToHalfCorner(side, true, true);
1907     nscoord length =
1908         SideIsVertical(side) ? aBorderArea.height : aBorderArea.width;
1909     nscoord sum = aRadii[hc1] + aRadii[hc2];
1910     if (sum) {
1911       haveRadius = true;
1912       // avoid floating point division in the normal case
1913       if (length < sum) {
1914         ratio = std::min(ratio, double(length) / sum);
1915       }
1916     }
1917   }
1918   if (ratio < 1.0) {
1919     for (const auto corner : mozilla::AllPhysicalHalfCorners()) {
1920       aRadii[corner] *= ratio;
1921     }
1922   }
1923 
1924   return haveRadius;
1925 }
1926 
1927 /* static */
InsetBorderRadii(nscoord aRadii[8],const nsMargin & aOffsets)1928 void nsIFrame::InsetBorderRadii(nscoord aRadii[8], const nsMargin& aOffsets) {
1929   for (const auto side : mozilla::AllPhysicalSides()) {
1930     nscoord offset = aOffsets.Side(side);
1931     uint32_t hc1 = SideToHalfCorner(side, false, false);
1932     uint32_t hc2 = SideToHalfCorner(side, true, false);
1933     aRadii[hc1] = std::max(0, aRadii[hc1] - offset);
1934     aRadii[hc2] = std::max(0, aRadii[hc2] - offset);
1935   }
1936 }
1937 
1938 /* static */
OutsetBorderRadii(nscoord aRadii[8],const nsMargin & aOffsets)1939 void nsIFrame::OutsetBorderRadii(nscoord aRadii[8], const nsMargin& aOffsets) {
1940   auto AdjustOffset = [](const uint32_t aRadius, const nscoord aOffset) {
1941     // Implement the cubic formula to adjust offset when aOffset > 0 and
1942     // aRadius / aOffset < 1.
1943     // https://drafts.csswg.org/css-shapes/#valdef-shape-box-margin-box
1944     if (aOffset > 0) {
1945       const double ratio = aRadius / double(aOffset);
1946       if (ratio < 1.0) {
1947         return nscoord(aOffset * (1.0 + std::pow(ratio - 1, 3)));
1948       }
1949     }
1950     return aOffset;
1951   };
1952 
1953   for (const auto side : mozilla::AllPhysicalSides()) {
1954     const nscoord offset = aOffsets.Side(side);
1955     const uint32_t hc1 = SideToHalfCorner(side, false, false);
1956     const uint32_t hc2 = SideToHalfCorner(side, true, false);
1957     if (aRadii[hc1] > 0) {
1958       const nscoord offset1 = AdjustOffset(aRadii[hc1], offset);
1959       aRadii[hc1] = std::max(0, aRadii[hc1] + offset1);
1960     }
1961     if (aRadii[hc2] > 0) {
1962       const nscoord offset2 = AdjustOffset(aRadii[hc2], offset);
1963       aRadii[hc2] = std::max(0, aRadii[hc2] + offset2);
1964     }
1965   }
1966 }
1967 
RadiiAreDefinitelyZero(const BorderRadius & aBorderRadius)1968 static inline bool RadiiAreDefinitelyZero(const BorderRadius& aBorderRadius) {
1969   for (const auto corner : mozilla::AllPhysicalHalfCorners()) {
1970     if (!aBorderRadius.Get(corner).IsDefinitelyZero()) {
1971       return false;
1972     }
1973   }
1974   return true;
1975 }
1976 
1977 /* virtual */
GetBorderRadii(const nsSize & aFrameSize,const nsSize & aBorderArea,Sides aSkipSides,nscoord aRadii[8]) const1978 bool nsIFrame::GetBorderRadii(const nsSize& aFrameSize,
1979                               const nsSize& aBorderArea, Sides aSkipSides,
1980                               nscoord aRadii[8]) const {
1981   if (!mMayHaveRoundedCorners) {
1982     memset(aRadii, 0, sizeof(nscoord) * 8);
1983     return false;
1984   }
1985 
1986   if (IsThemed()) {
1987     // When we're themed, the native theme code draws the border and
1988     // background, and therefore it doesn't make sense to tell other
1989     // code that's interested in border-radius that we have any radii.
1990     //
1991     // In an ideal world, we might have a way for the them to tell us an
1992     // border radius, but since we don't, we're better off assuming
1993     // zero.
1994     for (const auto corner : mozilla::AllPhysicalHalfCorners()) {
1995       aRadii[corner] = 0;
1996     }
1997     return false;
1998   }
1999 
2000   const auto& radii = StyleBorder()->mBorderRadius;
2001   const bool hasRadii =
2002       ComputeBorderRadii(radii, aFrameSize, aBorderArea, aSkipSides, aRadii);
2003   if (!hasRadii) {
2004     // TODO(emilio): Maybe we can just remove this bit and do the
2005     // IsDefinitelyZero check unconditionally. That should still avoid most of
2006     // the work, though maybe not the cache miss of going through the style and
2007     // the border struct.
2008     const_cast<nsIFrame*>(this)->mMayHaveRoundedCorners =
2009         !RadiiAreDefinitelyZero(radii);
2010   }
2011   return hasRadii;
2012 }
2013 
GetBorderRadii(nscoord aRadii[8]) const2014 bool nsIFrame::GetBorderRadii(nscoord aRadii[8]) const {
2015   nsSize sz = GetSize();
2016   return GetBorderRadii(sz, sz, GetSkipSides(), aRadii);
2017 }
2018 
GetMarginBoxBorderRadii(nscoord aRadii[8]) const2019 bool nsIFrame::GetMarginBoxBorderRadii(nscoord aRadii[8]) const {
2020   return GetBoxBorderRadii(aRadii, GetUsedMargin(), true);
2021 }
2022 
GetPaddingBoxBorderRadii(nscoord aRadii[8]) const2023 bool nsIFrame::GetPaddingBoxBorderRadii(nscoord aRadii[8]) const {
2024   return GetBoxBorderRadii(aRadii, GetUsedBorder(), false);
2025 }
2026 
GetContentBoxBorderRadii(nscoord aRadii[8]) const2027 bool nsIFrame::GetContentBoxBorderRadii(nscoord aRadii[8]) const {
2028   return GetBoxBorderRadii(aRadii, GetUsedBorderAndPadding(), false);
2029 }
2030 
GetBoxBorderRadii(nscoord aRadii[8],nsMargin aOffset,bool aIsOutset) const2031 bool nsIFrame::GetBoxBorderRadii(nscoord aRadii[8], nsMargin aOffset,
2032                                  bool aIsOutset) const {
2033   if (!GetBorderRadii(aRadii)) return false;
2034   if (aIsOutset) {
2035     OutsetBorderRadii(aRadii, aOffset);
2036   } else {
2037     InsetBorderRadii(aRadii, aOffset);
2038   }
2039   for (const auto corner : mozilla::AllPhysicalHalfCorners()) {
2040     if (aRadii[corner]) return true;
2041   }
2042   return false;
2043 }
2044 
GetShapeBoxBorderRadii(nscoord aRadii[8]) const2045 bool nsIFrame::GetShapeBoxBorderRadii(nscoord aRadii[8]) const {
2046   using Tag = StyleShapeOutside::Tag;
2047   auto& shapeOutside = StyleDisplay()->mShapeOutside;
2048   auto box = StyleShapeBox::MarginBox;
2049   switch (shapeOutside.tag) {
2050     case Tag::Image:
2051     case Tag::None:
2052       return false;
2053     case Tag::Box:
2054       box = shapeOutside.AsBox();
2055       break;
2056     case Tag::Shape:
2057       box = shapeOutside.AsShape()._1;
2058       break;
2059   }
2060 
2061   switch (box) {
2062     case StyleShapeBox::ContentBox:
2063       return GetContentBoxBorderRadii(aRadii);
2064     case StyleShapeBox::PaddingBox:
2065       return GetPaddingBoxBorderRadii(aRadii);
2066     case StyleShapeBox::BorderBox:
2067       return GetBorderRadii(aRadii);
2068     case StyleShapeBox::MarginBox:
2069       return GetMarginBoxBorderRadii(aRadii);
2070     default:
2071       MOZ_ASSERT_UNREACHABLE("Unexpected box value");
2072       return false;
2073   }
2074 }
2075 
GetAdditionalComputedStyle(int32_t aIndex) const2076 ComputedStyle* nsIFrame::GetAdditionalComputedStyle(int32_t aIndex) const {
2077   MOZ_ASSERT(aIndex >= 0, "invalid index number");
2078   return nullptr;
2079 }
2080 
SetAdditionalComputedStyle(int32_t aIndex,ComputedStyle * aComputedStyle)2081 void nsIFrame::SetAdditionalComputedStyle(int32_t aIndex,
2082                                           ComputedStyle* aComputedStyle) {
2083   MOZ_ASSERT(aIndex >= 0, "invalid index number");
2084 }
2085 
GetLogicalBaseline(WritingMode aWritingMode) const2086 nscoord nsIFrame::GetLogicalBaseline(WritingMode aWritingMode) const {
2087   NS_ASSERTION(!IsSubtreeDirty(), "frame must not be dirty");
2088   // Baseline for inverted line content is the top (block-start) margin edge,
2089   // as the frame is in effect "flipped" for alignment purposes.
2090   if (aWritingMode.IsLineInverted()) {
2091     return -GetLogicalUsedMargin(aWritingMode).BStart(aWritingMode);
2092   }
2093   // Otherwise, the bottom margin edge, per CSS2.1's definition of the
2094   // 'baseline' value of 'vertical-align'.
2095   return BSize(aWritingMode) +
2096          GetLogicalUsedMargin(aWritingMode).BEnd(aWritingMode);
2097 }
2098 
GetChildList(ChildListID aListID) const2099 const nsFrameList& nsIFrame::GetChildList(ChildListID aListID) const {
2100   if (IsAbsoluteContainer() && aListID == GetAbsoluteListID()) {
2101     return GetAbsoluteContainingBlock()->GetChildList();
2102   } else {
2103     return nsFrameList::EmptyList();
2104   }
2105 }
2106 
GetChildLists(nsTArray<ChildList> * aLists) const2107 void nsIFrame::GetChildLists(nsTArray<ChildList>* aLists) const {
2108   if (IsAbsoluteContainer()) {
2109     nsFrameList absoluteList = GetAbsoluteContainingBlock()->GetChildList();
2110     absoluteList.AppendIfNonempty(aLists, GetAbsoluteListID());
2111   }
2112 }
2113 
CrossDocChildLists()2114 AutoTArray<nsIFrame::ChildList, 4> nsIFrame::CrossDocChildLists() {
2115   AutoTArray<ChildList, 4> childLists;
2116   nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(this);
2117   if (subdocumentFrame) {
2118     // Descend into the subdocument
2119     nsIFrame* root = subdocumentFrame->GetSubdocumentRootFrame();
2120     if (root) {
2121       childLists.EmplaceBack(
2122           nsFrameList(root, nsLayoutUtils::GetLastSibling(root)),
2123           nsIFrame::kPrincipalList);
2124     }
2125   }
2126 
2127   GetChildLists(&childLists);
2128   return childLists;
2129 }
2130 
GetVisibility() const2131 Visibility nsIFrame::GetVisibility() const {
2132   if (!HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED)) {
2133     return Visibility::Untracked;
2134   }
2135 
2136   bool isSet = false;
2137   uint32_t visibleCount = GetProperty(VisibilityStateProperty(), &isSet);
2138 
2139   MOZ_ASSERT(isSet,
2140              "Should have a VisibilityStateProperty value "
2141              "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
2142 
2143   return visibleCount > 0 ? Visibility::ApproximatelyVisible
2144                           : Visibility::ApproximatelyNonVisible;
2145 }
2146 
UpdateVisibilitySynchronously()2147 void nsIFrame::UpdateVisibilitySynchronously() {
2148   mozilla::PresShell* presShell = PresShell();
2149   if (!presShell) {
2150     return;
2151   }
2152 
2153   if (presShell->AssumeAllFramesVisible()) {
2154     presShell->EnsureFrameInApproximatelyVisibleList(this);
2155     return;
2156   }
2157 
2158   bool visible = StyleVisibility()->IsVisible();
2159   nsIFrame* f = GetParent();
2160   nsRect rect = GetRectRelativeToSelf();
2161   nsIFrame* rectFrame = this;
2162   while (f && visible) {
2163     nsIScrollableFrame* sf = do_QueryFrame(f);
2164     if (sf) {
2165       nsRect transformedRect =
2166           nsLayoutUtils::TransformFrameRectToAncestor(rectFrame, rect, f);
2167       if (!sf->IsRectNearlyVisible(transformedRect)) {
2168         visible = false;
2169         break;
2170       }
2171 
2172       // In this code we're trying to synchronously update *approximate*
2173       // visibility. (In the future we may update precise visibility here as
2174       // well, which is why the method name does not contain 'approximate'.) The
2175       // IsRectNearlyVisible() check above tells us that the rect we're checking
2176       // is approximately visible within the scrollframe, but we still need to
2177       // ensure that, even if it was scrolled into view, it'd be visible when we
2178       // consider the rest of the document. To do that, we move transformedRect
2179       // to be contained in the scrollport as best we can (it might not fit) to
2180       // pretend that it was scrolled into view.
2181       rect = transformedRect.MoveInsideAndClamp(sf->GetScrollPortRect());
2182       rectFrame = f;
2183     }
2184     nsIFrame* parent = f->GetParent();
2185     if (!parent) {
2186       parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(f);
2187       if (parent && parent->PresContext()->IsChrome()) {
2188         break;
2189       }
2190     }
2191     f = parent;
2192   }
2193 
2194   if (visible) {
2195     presShell->EnsureFrameInApproximatelyVisibleList(this);
2196   } else {
2197     presShell->RemoveFrameFromApproximatelyVisibleList(this);
2198   }
2199 }
2200 
EnableVisibilityTracking()2201 void nsIFrame::EnableVisibilityTracking() {
2202   if (HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED)) {
2203     return;  // Nothing to do.
2204   }
2205 
2206   MOZ_ASSERT(!HasProperty(VisibilityStateProperty()),
2207              "Shouldn't have a VisibilityStateProperty value "
2208              "if NS_FRAME_VISIBILITY_IS_TRACKED is not set");
2209 
2210   // Add the state bit so we know to track visibility for this frame, and
2211   // initialize the frame property.
2212   AddStateBits(NS_FRAME_VISIBILITY_IS_TRACKED);
2213   SetProperty(VisibilityStateProperty(), 0);
2214 
2215   mozilla::PresShell* presShell = PresShell();
2216   if (!presShell) {
2217     return;
2218   }
2219 
2220   // Schedule a visibility update. This method will virtually always be called
2221   // when layout has changed anyway, so it's very unlikely that any additional
2222   // visibility updates will be triggered by this, but this way we guarantee
2223   // that if this frame is currently visible we'll eventually find out.
2224   presShell->ScheduleApproximateFrameVisibilityUpdateSoon();
2225 }
2226 
DisableVisibilityTracking()2227 void nsIFrame::DisableVisibilityTracking() {
2228   if (!HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED)) {
2229     return;  // Nothing to do.
2230   }
2231 
2232   bool isSet = false;
2233   uint32_t visibleCount = TakeProperty(VisibilityStateProperty(), &isSet);
2234 
2235   MOZ_ASSERT(isSet,
2236              "Should have a VisibilityStateProperty value "
2237              "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
2238 
2239   RemoveStateBits(NS_FRAME_VISIBILITY_IS_TRACKED);
2240 
2241   if (visibleCount == 0) {
2242     return;  // We were nonvisible.
2243   }
2244 
2245   // We were visible, so send an OnVisibilityChange() notification.
2246   OnVisibilityChange(Visibility::ApproximatelyNonVisible);
2247 }
2248 
DecApproximateVisibleCount(const Maybe<OnNonvisible> & aNonvisibleAction)2249 void nsIFrame::DecApproximateVisibleCount(
2250     const Maybe<OnNonvisible>& aNonvisibleAction
2251     /* = Nothing() */) {
2252   MOZ_ASSERT(HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED));
2253 
2254   bool isSet = false;
2255   uint32_t visibleCount = GetProperty(VisibilityStateProperty(), &isSet);
2256 
2257   MOZ_ASSERT(isSet,
2258              "Should have a VisibilityStateProperty value "
2259              "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
2260   MOZ_ASSERT(visibleCount > 0,
2261              "Frame is already nonvisible and we're "
2262              "decrementing its visible count?");
2263 
2264   visibleCount--;
2265   SetProperty(VisibilityStateProperty(), visibleCount);
2266   if (visibleCount > 0) {
2267     return;
2268   }
2269 
2270   // We just became nonvisible, so send an OnVisibilityChange() notification.
2271   OnVisibilityChange(Visibility::ApproximatelyNonVisible, aNonvisibleAction);
2272 }
2273 
IncApproximateVisibleCount()2274 void nsIFrame::IncApproximateVisibleCount() {
2275   MOZ_ASSERT(HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED));
2276 
2277   bool isSet = false;
2278   uint32_t visibleCount = GetProperty(VisibilityStateProperty(), &isSet);
2279 
2280   MOZ_ASSERT(isSet,
2281              "Should have a VisibilityStateProperty value "
2282              "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
2283 
2284   visibleCount++;
2285   SetProperty(VisibilityStateProperty(), visibleCount);
2286   if (visibleCount > 1) {
2287     return;
2288   }
2289 
2290   // We just became visible, so send an OnVisibilityChange() notification.
2291   OnVisibilityChange(Visibility::ApproximatelyVisible);
2292 }
2293 
OnVisibilityChange(Visibility aNewVisibility,const Maybe<OnNonvisible> & aNonvisibleAction)2294 void nsIFrame::OnVisibilityChange(Visibility aNewVisibility,
2295                                   const Maybe<OnNonvisible>& aNonvisibleAction
2296                                   /* = Nothing() */) {
2297   // XXX(seth): In bug 1218990 we'll implement visibility tracking for CSS
2298   // images here.
2299 }
2300 
GetActiveSelectionFrame(nsPresContext * aPresContext,nsIFrame * aFrame)2301 static nsIFrame* GetActiveSelectionFrame(nsPresContext* aPresContext,
2302                                          nsIFrame* aFrame) {
2303   nsIContent* capturingContent = PresShell::GetCapturingContent();
2304   if (capturingContent) {
2305     nsIFrame* activeFrame = aPresContext->GetPrimaryFrameFor(capturingContent);
2306     return activeFrame ? activeFrame : aFrame;
2307   }
2308 
2309   return aFrame;
2310 }
2311 
DetermineDisplaySelection()2312 int16_t nsIFrame::DetermineDisplaySelection() {
2313   int16_t selType = nsISelectionController::SELECTION_OFF;
2314 
2315   nsCOMPtr<nsISelectionController> selCon;
2316   nsresult result =
2317       GetSelectionController(PresContext(), getter_AddRefs(selCon));
2318   if (NS_SUCCEEDED(result) && selCon) {
2319     result = selCon->GetDisplaySelection(&selType);
2320     if (NS_SUCCEEDED(result) &&
2321         (selType != nsISelectionController::SELECTION_OFF)) {
2322       // Check whether style allows selection.
2323       if (!IsSelectable(nullptr)) {
2324         selType = nsISelectionController::SELECTION_OFF;
2325       }
2326     }
2327   }
2328   return selType;
2329 }
2330 
FindElementAncestorForMozSelection(nsIContent * aContent)2331 static Element* FindElementAncestorForMozSelection(nsIContent* aContent) {
2332   NS_ENSURE_TRUE(aContent, nullptr);
2333   while (aContent && aContent->IsInNativeAnonymousSubtree()) {
2334     aContent = aContent->GetClosestNativeAnonymousSubtreeRootParent();
2335   }
2336   NS_ASSERTION(aContent, "aContent isn't in non-anonymous tree?");
2337   return aContent ? aContent->GetAsElementOrParentElement() : nullptr;
2338 }
2339 
ComputeSelectionStyle(int16_t aSelectionStatus) const2340 already_AddRefed<ComputedStyle> nsIFrame::ComputeSelectionStyle(
2341     int16_t aSelectionStatus) const {
2342   // Just bail out if not a selection-status that ::selection applies to.
2343   if (aSelectionStatus != nsISelectionController::SELECTION_ON &&
2344       aSelectionStatus != nsISelectionController::SELECTION_DISABLED) {
2345     return nullptr;
2346   }
2347   // When in high-contrast mode, the style system ends up ignoring the color
2348   // declarations, which means that the ::selection style becomes the inherited
2349   // color, and default background. That's no good.
2350   if (PresContext()->ForcingColors()) {
2351     return nullptr;
2352   }
2353   Element* element = FindElementAncestorForMozSelection(GetContent());
2354   if (!element) {
2355     return nullptr;
2356   }
2357   return PresContext()->StyleSet()->ProbePseudoElementStyle(
2358       *element, PseudoStyleType::selection, Style());
2359 }
2360 
2361 template <typename SizeOrMaxSize>
IsIntrinsicKeyword(const SizeOrMaxSize & aSize)2362 static inline bool IsIntrinsicKeyword(const SizeOrMaxSize& aSize) {
2363   // All keywords other than auto/none/-moz-available depend on intrinsic sizes.
2364   return aSize.IsMaxContent() || aSize.IsMinContent() || aSize.IsFitContent() ||
2365          aSize.IsFitContentFunction();
2366 }
2367 
CanBeDynamicReflowRoot() const2368 bool nsIFrame::CanBeDynamicReflowRoot() const {
2369   if (!StaticPrefs::layout_dynamic_reflow_roots_enabled()) {
2370     return false;
2371   }
2372 
2373   auto& display = *StyleDisplay();
2374   if (IsFrameOfType(nsIFrame::eLineParticipant) ||
2375       nsStyleDisplay::IsRubyDisplayType(display.mDisplay) ||
2376       display.DisplayOutside() == StyleDisplayOutside::InternalTable ||
2377       display.DisplayInside() == StyleDisplayInside::Table ||
2378       (GetParent() && GetParent()->IsXULBoxFrame())) {
2379     // We have a display type where 'width' and 'height' don't actually set the
2380     // width or height (i.e., the size depends on content).
2381     MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT),
2382                "should not have dynamic reflow root bit");
2383     return false;
2384   }
2385 
2386   // We can't serve as a dynamic reflow root if our used 'width' and 'height'
2387   // might be influenced by content.
2388   //
2389   // FIXME: For display:block, we should probably optimize inline-size: auto.
2390   // FIXME: Other flex and grid cases?
2391   auto& pos = *StylePosition();
2392   const auto& width = pos.mWidth;
2393   const auto& height = pos.mHeight;
2394   if (!width.IsLengthPercentage() || width.HasPercent() ||
2395       !height.IsLengthPercentage() || height.HasPercent() ||
2396       IsIntrinsicKeyword(pos.mMinWidth) || IsIntrinsicKeyword(pos.mMaxWidth) ||
2397       IsIntrinsicKeyword(pos.mMinHeight) ||
2398       IsIntrinsicKeyword(pos.mMaxHeight) ||
2399       ((pos.mMinWidth.IsAuto() || pos.mMinHeight.IsAuto()) &&
2400        IsFlexOrGridItem())) {
2401     return false;
2402   }
2403 
2404   // If our flex-basis is 'auto', it'll defer to 'width' (or 'height') which
2405   // we've already checked. Otherwise, it preempts them, so we need to
2406   // perform the same "could-this-value-be-influenced-by-content" checks that
2407   // we performed for 'width' and 'height' above.
2408   if (IsFlexItem()) {
2409     const auto& flexBasis = pos.mFlexBasis;
2410     if (!flexBasis.IsAuto()) {
2411       if (!flexBasis.IsSize() || !flexBasis.AsSize().IsLengthPercentage() ||
2412           flexBasis.AsSize().HasPercent()) {
2413         return false;
2414       }
2415     }
2416   }
2417 
2418   if (!IsFixedPosContainingBlock()) {
2419     // We can't treat this frame as a reflow root, since dynamic changes
2420     // to absolutely-positioned frames inside of it require that we
2421     // reflow the placeholder before we reflow the absolutely positioned
2422     // frame.
2423     // FIXME:  Alternatively, we could sort the reflow roots in
2424     // PresShell::ProcessReflowCommands by depth in the tree, from
2425     // deepest to least deep.  However, for performance (FIXME) we
2426     // should really be sorting them in the opposite order!
2427     return false;
2428   }
2429 
2430   // If we participate in a container's block reflow context, or margins
2431   // can collapse through us, we can't be a dynamic reflow root.
2432   if (IsBlockFrameOrSubclass() &&
2433       !HasAllStateBits(NS_BLOCK_FLOAT_MGR | NS_BLOCK_MARGIN_ROOT)) {
2434     return false;
2435   }
2436 
2437   // Subgrids are never reflow roots, but 'contain:layout/paint' prevents
2438   // creating a subgrid in the first place.
2439   if (pos.mGridTemplateColumns.IsSubgrid() ||
2440       pos.mGridTemplateRows.IsSubgrid()) {
2441     // NOTE: we could check that 'display' of our parent's primary frame is
2442     // '[inline-]grid' here but that's probably not worth it in practice.
2443     if (!display.IsContainLayout() && !display.IsContainPaint()) {
2444       return false;
2445     }
2446   }
2447 
2448   // If we are split, we can't be a dynamic reflow root. Our reflow status may
2449   // change after reflow, and our parent is responsible to create or delete our
2450   // next-in-flow.
2451   if (GetPrevContinuation() || GetNextContinuation()) {
2452     return false;
2453   }
2454 
2455   return true;
2456 }
2457 
2458 /********************************************************
2459  * Refreshes each content's frame
2460  *********************************************************/
2461 
DisplayOutlineUnconditional(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)2462 void nsIFrame::DisplayOutlineUnconditional(nsDisplayListBuilder* aBuilder,
2463                                            const nsDisplayListSet& aLists) {
2464   // Per https://drafts.csswg.org/css-tables-3/#global-style-overrides:
2465   // "All css properties of table-column and table-column-group boxes are
2466   // ignored, except when explicitly specified by this specification."
2467   // CSS outlines fall into this category, so we skip them on these boxes.
2468   MOZ_ASSERT(!IsTableColGroupFrame() && !IsTableColFrame());
2469   const auto& outline = *StyleOutline();
2470 
2471   if (!outline.ShouldPaintOutline()) {
2472     return;
2473   }
2474 
2475   // Outlines are painted by the table wrapper frame.
2476   if (IsTableFrame()) {
2477     return;
2478   }
2479 
2480   if (HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT) &&
2481       ScrollableOverflowRect().IsEmpty()) {
2482     // Skip parts of IB-splits with an empty overflow rect, see bug 434301.
2483     // We may still want to fix some of the overflow area calculations over in
2484     // that bug.
2485     return;
2486   }
2487 
2488   // We don't display outline-style: auto on themed frames that have their own
2489   // focus indicators.
2490   if (outline.mOutlineStyle.IsAuto()) {
2491     auto* disp = StyleDisplay();
2492     if (IsThemed(disp) && PresContext()->Theme()->ThemeDrawsFocusForWidget(
2493                               this, disp->EffectiveAppearance())) {
2494       return;
2495     }
2496   }
2497 
2498   aLists.Outlines()->AppendNewToTop<nsDisplayOutline>(aBuilder, this);
2499 }
2500 
DisplayOutline(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)2501 void nsIFrame::DisplayOutline(nsDisplayListBuilder* aBuilder,
2502                               const nsDisplayListSet& aLists) {
2503   if (!IsVisibleForPainting()) return;
2504 
2505   DisplayOutlineUnconditional(aBuilder, aLists);
2506 }
2507 
DisplayInsetBoxShadowUnconditional(nsDisplayListBuilder * aBuilder,nsDisplayList * aList)2508 void nsIFrame::DisplayInsetBoxShadowUnconditional(
2509     nsDisplayListBuilder* aBuilder, nsDisplayList* aList) {
2510   // XXXbz should box-shadow for rows/rowgroups/columns/colgroups get painted
2511   // just because we're visible?  Or should it depend on the cell visibility
2512   // when we're not the whole table?
2513   const auto* effects = StyleEffects();
2514   if (effects->HasBoxShadowWithInset(true)) {
2515     aList->AppendNewToTop<nsDisplayBoxShadowInner>(aBuilder, this);
2516   }
2517 }
2518 
DisplayInsetBoxShadow(nsDisplayListBuilder * aBuilder,nsDisplayList * aList)2519 void nsIFrame::DisplayInsetBoxShadow(nsDisplayListBuilder* aBuilder,
2520                                      nsDisplayList* aList) {
2521   if (!IsVisibleForPainting()) return;
2522 
2523   DisplayInsetBoxShadowUnconditional(aBuilder, aList);
2524 }
2525 
DisplayOutsetBoxShadowUnconditional(nsDisplayListBuilder * aBuilder,nsDisplayList * aList)2526 void nsIFrame::DisplayOutsetBoxShadowUnconditional(
2527     nsDisplayListBuilder* aBuilder, nsDisplayList* aList) {
2528   // XXXbz should box-shadow for rows/rowgroups/columns/colgroups get painted
2529   // just because we're visible?  Or should it depend on the cell visibility
2530   // when we're not the whole table?
2531   const auto* effects = StyleEffects();
2532   if (effects->HasBoxShadowWithInset(false)) {
2533     aList->AppendNewToTop<nsDisplayBoxShadowOuter>(aBuilder, this);
2534   }
2535 }
2536 
DisplayOutsetBoxShadow(nsDisplayListBuilder * aBuilder,nsDisplayList * aList)2537 void nsIFrame::DisplayOutsetBoxShadow(nsDisplayListBuilder* aBuilder,
2538                                       nsDisplayList* aList) {
2539   if (!IsVisibleForPainting()) return;
2540 
2541   DisplayOutsetBoxShadowUnconditional(aBuilder, aList);
2542 }
2543 
DisplayCaret(nsDisplayListBuilder * aBuilder,nsDisplayList * aList)2544 void nsIFrame::DisplayCaret(nsDisplayListBuilder* aBuilder,
2545                             nsDisplayList* aList) {
2546   if (!IsVisibleForPainting()) return;
2547 
2548   aList->AppendNewToTop<nsDisplayCaret>(aBuilder, this);
2549 }
2550 
GetCaretColorAt(int32_t aOffset)2551 nscolor nsIFrame::GetCaretColorAt(int32_t aOffset) {
2552   return nsLayoutUtils::GetColor(this, &nsStyleUI::mCaretColor);
2553 }
2554 
ComputeShouldPaintBackground() const2555 auto nsIFrame::ComputeShouldPaintBackground() const -> ShouldPaintBackground {
2556   nsPresContext* pc = PresContext();
2557   ShouldPaintBackground settings{pc->GetBackgroundColorDraw(),
2558                                  pc->GetBackgroundImageDraw()};
2559   if (settings.mColor && settings.mImage) {
2560     return settings;
2561   }
2562 
2563   if (!HonorPrintBackgroundSettings() ||
2564       StyleVisibility()->mPrintColorAdjust == StylePrintColorAdjust::Exact) {
2565     return {true, true};
2566   }
2567 
2568   return settings;
2569 }
2570 
DisplayBackgroundUnconditional(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists,bool aForceBackground)2571 bool nsIFrame::DisplayBackgroundUnconditional(nsDisplayListBuilder* aBuilder,
2572                                               const nsDisplayListSet& aLists,
2573                                               bool aForceBackground) {
2574   const bool hitTesting = aBuilder->IsForEventDelivery();
2575   if (hitTesting && !aBuilder->HitTestIsForVisibility()) {
2576     // For hit-testing, we generally just need a light-weight data structure
2577     // like nsDisplayEventReceiver. But if the hit-testing is for visibility,
2578     // then we need to know the opaque region in order to determine whether to
2579     // stop or not.
2580     aLists.BorderBackground()->AppendNewToTop<nsDisplayEventReceiver>(aBuilder,
2581                                                                       this);
2582     return false;
2583   }
2584 
2585   AppendedBackgroundType result = AppendedBackgroundType::None;
2586 
2587   // Here we don't try to detect background propagation. Frames that might
2588   // receive a propagated background should just set aForceBackground to
2589   // true.
2590   if (hitTesting || aForceBackground ||
2591       !StyleBackground()->IsTransparent(this) ||
2592       StyleDisplay()->HasAppearance() ||
2593       // We do forcibly create a display item for background color animations
2594       // even if the current background-color is transparent so that we can
2595       // run the animations on the compositor.
2596       EffectCompositor::HasAnimationsForCompositor(
2597           this, DisplayItemType::TYPE_BACKGROUND_COLOR)) {
2598     result = nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
2599         aBuilder, this,
2600         GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this),
2601         aLists.BorderBackground());
2602   }
2603 
2604   if (result == AppendedBackgroundType::None) {
2605     aBuilder->BuildCompositorHitTestInfoIfNeeded(this,
2606                                                  aLists.BorderBackground());
2607   }
2608 
2609   return result == AppendedBackgroundType::ThemedBackground;
2610 }
2611 
DisplayBorderBackgroundOutline(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists,bool aForceBackground)2612 void nsIFrame::DisplayBorderBackgroundOutline(nsDisplayListBuilder* aBuilder,
2613                                               const nsDisplayListSet& aLists,
2614                                               bool aForceBackground) {
2615   // The visibility check belongs here since child elements have the
2616   // opportunity to override the visibility property and display even if
2617   // their parent is hidden.
2618   if (!IsVisibleForPainting()) {
2619     return;
2620   }
2621 
2622   DisplayOutsetBoxShadowUnconditional(aBuilder, aLists.BorderBackground());
2623 
2624   bool bgIsThemed =
2625       DisplayBackgroundUnconditional(aBuilder, aLists, aForceBackground);
2626 
2627   DisplayInsetBoxShadowUnconditional(aBuilder, aLists.BorderBackground());
2628 
2629   // If there's a themed background, we should not create a border item.
2630   // It won't be rendered.
2631   // Don't paint borders for tables here, since they paint them in a different
2632   // order.
2633   if (!bgIsThemed && StyleBorder()->HasBorder() && !IsTableFrame()) {
2634     aLists.BorderBackground()->AppendNewToTop<nsDisplayBorder>(aBuilder, this);
2635   }
2636 
2637   DisplayOutlineUnconditional(aBuilder, aLists);
2638 }
2639 
IsSVGContentWithCSSClip(const nsIFrame * aFrame)2640 inline static bool IsSVGContentWithCSSClip(const nsIFrame* aFrame) {
2641   // The CSS spec says that the 'clip' property only applies to absolutely
2642   // positioned elements, whereas the SVG spec says that it applies to SVG
2643   // elements regardless of the value of the 'position' property. Here we obey
2644   // the CSS spec for outer-<svg> (since that's what we generally do), but
2645   // obey the SVG spec for other SVG elements to which 'clip' applies.
2646   return aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT) &&
2647          aFrame->GetContent()->IsAnyOfSVGElements(nsGkAtoms::svg,
2648                                                   nsGkAtoms::foreignObject);
2649 }
2650 
FormsBackdropRoot(const nsStyleDisplay * aStyleDisplay,const nsStyleEffects * aStyleEffects,const nsStyleSVGReset * aStyleSVGReset)2651 bool nsIFrame::FormsBackdropRoot(const nsStyleDisplay* aStyleDisplay,
2652                                  const nsStyleEffects* aStyleEffects,
2653                                  const nsStyleSVGReset* aStyleSVGReset) {
2654   // Check if this is a root frame.
2655   if (!GetParent()) {
2656     return true;
2657   }
2658 
2659   // Check for filter effects.
2660   if (aStyleEffects->HasFilters() || aStyleEffects->HasBackdropFilters() ||
2661       aStyleEffects->HasMixBlendMode()) {
2662     return true;
2663   }
2664 
2665   // Check for opacity.
2666   if (HasOpacity(aStyleDisplay, aStyleEffects)) {
2667     return true;
2668   }
2669 
2670   // Check for mask or clip path.
2671   if (aStyleSVGReset->HasMask() || aStyleSVGReset->HasClipPath()) {
2672     return true;
2673   }
2674 
2675   // TODO(cbrewster): Check will-change attributes
2676 
2677   return false;
2678 }
2679 
GetClipPropClipRect(const nsStyleDisplay * aDisp,const nsStyleEffects * aEffects,const nsSize & aSize) const2680 Maybe<nsRect> nsIFrame::GetClipPropClipRect(const nsStyleDisplay* aDisp,
2681                                             const nsStyleEffects* aEffects,
2682                                             const nsSize& aSize) const {
2683   if (aEffects->mClip.IsAuto() ||
2684       !(aDisp->IsAbsolutelyPositioned(this) || IsSVGContentWithCSSClip(this))) {
2685     return Nothing();
2686   }
2687 
2688   auto& clipRect = aEffects->mClip.AsRect();
2689   nsRect rect = clipRect.ToLayoutRect();
2690   if (MOZ_LIKELY(StyleBorder()->mBoxDecorationBreak ==
2691                  StyleBoxDecorationBreak::Slice)) {
2692     // The clip applies to the joined boxes so it's relative the first
2693     // continuation.
2694     nscoord y = 0;
2695     for (nsIFrame* f = GetPrevContinuation(); f; f = f->GetPrevContinuation()) {
2696       y += f->GetRect().height;
2697     }
2698     rect.MoveBy(nsPoint(0, -y));
2699   }
2700 
2701   if (clipRect.right.IsAuto()) {
2702     rect.width = aSize.width - rect.x;
2703   }
2704   if (clipRect.bottom.IsAuto()) {
2705     rect.height = aSize.height - rect.y;
2706   }
2707   return Some(rect);
2708 }
2709 
2710 /**
2711  * If the CSS 'overflow' property applies to this frame, and is not
2712  * handled by constructing a dedicated nsHTML/XULScrollFrame, set up clipping
2713  * for that overflow in aBuilder->ClipState() to clip all containing-block
2714  * descendants.
2715  */
ApplyOverflowClipping(nsDisplayListBuilder * aBuilder,const nsIFrame * aFrame,nsIFrame::PhysicalAxes aClipAxes,DisplayListClipState::AutoClipMultiple & aClipState)2716 static void ApplyOverflowClipping(
2717     nsDisplayListBuilder* aBuilder, const nsIFrame* aFrame,
2718     nsIFrame::PhysicalAxes aClipAxes,
2719     DisplayListClipState::AutoClipMultiple& aClipState) {
2720   // Only 'clip' is handled here (and 'hidden' for table frames, and any
2721   // non-'visible' value for blocks in a paginated context).
2722   // We allow 'clip' to apply to any kind of frame. This is required by
2723   // comboboxes which make their display text (an inline frame) have clipping.
2724   MOZ_ASSERT(aClipAxes != nsIFrame::PhysicalAxes::None);
2725   MOZ_ASSERT(aFrame->ShouldApplyOverflowClipping(aFrame->StyleDisplay()) ==
2726              aClipAxes);
2727 
2728   nsRect clipRect;
2729   bool haveRadii = false;
2730   nscoord radii[8];
2731   auto* disp = aFrame->StyleDisplay();
2732   // Only deflate the padding if we clip to the content-box in that axis.
2733   auto wm = aFrame->GetWritingMode();
2734   bool cbH = (wm.IsVertical() ? disp->mOverflowClipBoxBlock
2735                               : disp->mOverflowClipBoxInline) ==
2736              StyleOverflowClipBox::ContentBox;
2737   bool cbV = (wm.IsVertical() ? disp->mOverflowClipBoxInline
2738                               : disp->mOverflowClipBoxBlock) ==
2739              StyleOverflowClipBox::ContentBox;
2740   nsMargin bp = aFrame->GetUsedPadding();
2741   if (!cbH) {
2742     bp.left = bp.right = nscoord(0);
2743   }
2744   if (!cbV) {
2745     bp.top = bp.bottom = nscoord(0);
2746   }
2747 
2748   bp += aFrame->GetUsedBorder();
2749   bp.ApplySkipSides(aFrame->GetSkipSides());
2750   nsRect rect(nsPoint(0, 0), aFrame->GetSize());
2751   rect.Deflate(bp);
2752   if (MOZ_UNLIKELY(!(aClipAxes & nsIFrame::PhysicalAxes::Horizontal))) {
2753     // NOTE(mats) We shouldn't be clipping at all in this dimension really,
2754     // but clipping in just one axis isn't supported by our GFX APIs so we
2755     // clip to our visual overflow rect instead.
2756     nsRect o = aFrame->InkOverflowRect();
2757     rect.x = o.x;
2758     rect.width = o.width;
2759   }
2760   if (MOZ_UNLIKELY(!(aClipAxes & nsIFrame::PhysicalAxes::Vertical))) {
2761     // See the note above.
2762     nsRect o = aFrame->InkOverflowRect();
2763     rect.y = o.y;
2764     rect.height = o.height;
2765   }
2766   clipRect = rect + aBuilder->ToReferenceFrame(aFrame);
2767   haveRadii = aFrame->GetBoxBorderRadii(radii, bp, false);
2768   aClipState.ClipContainingBlockDescendantsExtra(clipRect,
2769                                                  haveRadii ? radii : nullptr);
2770 }
2771 
2772 #ifdef DEBUG
PaintDebugBorder(nsIFrame * aFrame,DrawTarget * aDrawTarget,const nsRect & aDirtyRect,nsPoint aPt)2773 static void PaintDebugBorder(nsIFrame* aFrame, DrawTarget* aDrawTarget,
2774                              const nsRect& aDirtyRect, nsPoint aPt) {
2775   nsRect r(aPt, aFrame->GetSize());
2776   int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
2777   sRGBColor blueOrRed(aFrame->HasView() ? sRGBColor(0.f, 0.f, 1.f, 1.f)
2778                                         : sRGBColor(1.f, 0.f, 0.f, 1.f));
2779   aDrawTarget->StrokeRect(NSRectToRect(r, appUnitsPerDevPixel),
2780                           ColorPattern(ToDeviceColor(blueOrRed)));
2781 }
2782 
PaintEventTargetBorder(nsIFrame * aFrame,DrawTarget * aDrawTarget,const nsRect & aDirtyRect,nsPoint aPt)2783 static void PaintEventTargetBorder(nsIFrame* aFrame, DrawTarget* aDrawTarget,
2784                                    const nsRect& aDirtyRect, nsPoint aPt) {
2785   nsRect r(aPt, aFrame->GetSize());
2786   int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
2787   ColorPattern purple(ToDeviceColor(sRGBColor(.5f, 0.f, .5f, 1.f)));
2788   aDrawTarget->StrokeRect(NSRectToRect(r, appUnitsPerDevPixel), purple);
2789 }
2790 
DisplayDebugBorders(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,const nsDisplayListSet & aLists)2791 static void DisplayDebugBorders(nsDisplayListBuilder* aBuilder,
2792                                 nsIFrame* aFrame,
2793                                 const nsDisplayListSet& aLists) {
2794   // Draw a border around the child
2795   // REVIEW: From nsContainerFrame::PaintChild
2796   if (nsIFrame::GetShowFrameBorders() && !aFrame->GetRect().IsEmpty()) {
2797     aLists.Outlines()->AppendNewToTop<nsDisplayGeneric>(
2798         aBuilder, aFrame, PaintDebugBorder, "DebugBorder",
2799         DisplayItemType::TYPE_DEBUG_BORDER);
2800   }
2801   // Draw a border around the current event target
2802   if (nsIFrame::GetShowEventTargetFrameBorder() &&
2803       aFrame->PresShell()->GetDrawEventTargetFrame() == aFrame) {
2804     aLists.Outlines()->AppendNewToTop<nsDisplayGeneric>(
2805         aBuilder, aFrame, PaintEventTargetBorder, "EventTargetBorder",
2806         DisplayItemType::TYPE_EVENT_TARGET_BORDER);
2807   }
2808 }
2809 #endif
2810 
2811 /**
2812  * Returns whether a display item that gets created with the builder's current
2813  * state will have a scrolled clip, i.e. a clip that is scrolled by a scroll
2814  * frame which does not move the item itself.
2815  */
BuilderHasScrolledClip(nsDisplayListBuilder * aBuilder)2816 static bool BuilderHasScrolledClip(nsDisplayListBuilder* aBuilder) {
2817   const DisplayItemClipChain* currentClip =
2818       aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder);
2819   if (!currentClip) {
2820     return false;
2821   }
2822 
2823   const ActiveScrolledRoot* currentClipASR = currentClip->mASR;
2824   const ActiveScrolledRoot* currentASR = aBuilder->CurrentActiveScrolledRoot();
2825   return ActiveScrolledRoot::PickDescendant(currentClipASR, currentASR) !=
2826          currentASR;
2827 }
2828 
2829 class AutoSaveRestoreContainsBlendMode {
2830   nsDisplayListBuilder& mBuilder;
2831   bool mSavedContainsBlendMode;
2832 
2833  public:
AutoSaveRestoreContainsBlendMode(nsDisplayListBuilder & aBuilder)2834   explicit AutoSaveRestoreContainsBlendMode(nsDisplayListBuilder& aBuilder)
2835       : mBuilder(aBuilder),
2836         mSavedContainsBlendMode(aBuilder.ContainsBlendMode()) {}
2837 
~AutoSaveRestoreContainsBlendMode()2838   ~AutoSaveRestoreContainsBlendMode() {
2839     mBuilder.SetContainsBlendMode(mSavedContainsBlendMode);
2840   }
2841 };
2842 
2843 class AutoSaveRestoreContainsBackdropFilter {
2844   nsDisplayListBuilder& mBuilder;
2845   bool mSavedContainsBackdropFilter;
2846 
2847  public:
AutoSaveRestoreContainsBackdropFilter(nsDisplayListBuilder & aBuilder)2848   explicit AutoSaveRestoreContainsBackdropFilter(nsDisplayListBuilder& aBuilder)
2849       : mBuilder(aBuilder),
2850         mSavedContainsBackdropFilter(aBuilder.ContainsBackdropFilter()) {}
2851 
2852   /**
2853    * This is called if a stacking context which does not form a backdrop root
2854    * contains a descendent with a backdrop filter. In this case we need to
2855    * delegate backdrop root creation to the next parent in the tree until we hit
2856    * the nearest backdrop root ancestor.
2857    */
DelegateUp(bool aContainsBackdropFilter)2858   void DelegateUp(bool aContainsBackdropFilter) {
2859     mSavedContainsBackdropFilter = aContainsBackdropFilter;
2860   }
2861 
~AutoSaveRestoreContainsBackdropFilter()2862   ~AutoSaveRestoreContainsBackdropFilter() {
2863     mBuilder.SetContainsBackdropFilter(mSavedContainsBackdropFilter);
2864   }
2865 };
2866 
CheckForApzAwareEventHandlers(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame)2867 static void CheckForApzAwareEventHandlers(nsDisplayListBuilder* aBuilder,
2868                                           nsIFrame* aFrame) {
2869   if (aBuilder->GetAncestorHasApzAwareEventHandler()) {
2870     return;
2871   }
2872 
2873   nsIContent* content = aFrame->GetContent();
2874   if (!content) {
2875     return;
2876   }
2877 
2878   if (content->IsNodeApzAware()) {
2879     aBuilder->SetAncestorHasApzAwareEventHandler(true);
2880   }
2881 }
2882 
UpdateCurrentHitTestInfo(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame)2883 static void UpdateCurrentHitTestInfo(nsDisplayListBuilder* aBuilder,
2884                                      nsIFrame* aFrame) {
2885   if (!aBuilder->BuildCompositorHitTestInfo()) {
2886     // Compositor hit test info is not used.
2887     return;
2888   }
2889 
2890   CheckForApzAwareEventHandlers(aBuilder, aFrame);
2891 
2892   const CompositorHitTestInfo info = aFrame->GetCompositorHitTestInfo(aBuilder);
2893   aBuilder->SetCompositorHitTestInfo(info);
2894 }
2895 
2896 /**
2897  * True if aDescendant participates the context aAncestor participating.
2898  */
FrameParticipatesIn3DContext(nsIFrame * aAncestor,nsIFrame * aDescendant)2899 static bool FrameParticipatesIn3DContext(nsIFrame* aAncestor,
2900                                          nsIFrame* aDescendant) {
2901   MOZ_ASSERT(aAncestor != aDescendant);
2902   MOZ_ASSERT(aAncestor->GetContent() != aDescendant->GetContent());
2903   MOZ_ASSERT(aAncestor->Extend3DContext());
2904 
2905   nsIFrame* ancestor = aAncestor->FirstContinuation();
2906   MOZ_ASSERT(ancestor->IsPrimaryFrame());
2907 
2908   nsIFrame* frame;
2909   for (frame = aDescendant->GetClosestFlattenedTreeAncestorPrimaryFrame();
2910        frame && ancestor != frame;
2911        frame = frame->GetClosestFlattenedTreeAncestorPrimaryFrame()) {
2912     if (!frame->Extend3DContext()) {
2913       return false;
2914     }
2915   }
2916 
2917   MOZ_ASSERT(frame == ancestor);
2918   return true;
2919 }
2920 
ItemParticipatesIn3DContext(nsIFrame * aAncestor,nsDisplayItem * aItem)2921 static bool ItemParticipatesIn3DContext(nsIFrame* aAncestor,
2922                                         nsDisplayItem* aItem) {
2923   auto type = aItem->GetType();
2924   const bool isContainer = type == DisplayItemType::TYPE_WRAP_LIST ||
2925                            type == DisplayItemType::TYPE_CONTAINER;
2926 
2927   if (isContainer && aItem->GetChildren()->Length() == 1) {
2928     // If the wraplist has only one child item, use the type of that item.
2929     type = aItem->GetChildren()->GetBottom()->GetType();
2930   }
2931 
2932   if (type != DisplayItemType::TYPE_TRANSFORM &&
2933       type != DisplayItemType::TYPE_PERSPECTIVE) {
2934     return false;
2935   }
2936   nsIFrame* transformFrame = aItem->Frame();
2937   if (aAncestor->GetContent() == transformFrame->GetContent()) {
2938     return true;
2939   }
2940   return FrameParticipatesIn3DContext(aAncestor, transformFrame);
2941 }
2942 
WrapSeparatorTransform(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aNonParticipants,nsDisplayList * aParticipants,int aIndex,nsDisplayItem ** aSeparator)2943 static void WrapSeparatorTransform(nsDisplayListBuilder* aBuilder,
2944                                    nsIFrame* aFrame,
2945                                    nsDisplayList* aNonParticipants,
2946                                    nsDisplayList* aParticipants, int aIndex,
2947                                    nsDisplayItem** aSeparator) {
2948   if (aNonParticipants->IsEmpty()) {
2949     return;
2950   }
2951 
2952   nsDisplayTransform* item = MakeDisplayItemWithIndex<nsDisplayTransform>(
2953       aBuilder, aFrame, aIndex, aNonParticipants, aBuilder->GetVisibleRect());
2954 
2955   if (*aSeparator == nullptr && item) {
2956     *aSeparator = item;
2957   }
2958 
2959   aParticipants->AppendToTop(item);
2960 }
2961 
2962 // Try to compute a clip rect to bound the contents of the mask item
2963 // that will be built for |aMaskedFrame|. If we're not able to compute
2964 // one, return an empty Maybe.
2965 // The returned clip rect, if there is one, is relative to |aMaskedFrame|.
ComputeClipForMaskItem(nsDisplayListBuilder * aBuilder,nsIFrame * aMaskedFrame)2966 static Maybe<nsRect> ComputeClipForMaskItem(nsDisplayListBuilder* aBuilder,
2967                                             nsIFrame* aMaskedFrame) {
2968   const nsStyleSVGReset* svgReset = aMaskedFrame->StyleSVGReset();
2969 
2970   SVGUtils::MaskUsage maskUsage;
2971   SVGUtils::DetermineMaskUsage(aMaskedFrame, false, maskUsage);
2972 
2973   nsPoint offsetToUserSpace =
2974       nsLayoutUtils::ComputeOffsetToUserSpace(aBuilder, aMaskedFrame);
2975   int32_t devPixelRatio = aMaskedFrame->PresContext()->AppUnitsPerDevPixel();
2976   gfxPoint devPixelOffsetToUserSpace =
2977       nsLayoutUtils::PointToGfxPoint(offsetToUserSpace, devPixelRatio);
2978   CSSToLayoutDeviceScale cssToDevScale =
2979       aMaskedFrame->PresContext()->CSSToDevPixelScale();
2980 
2981   nsPoint toReferenceFrame;
2982   aBuilder->FindReferenceFrameFor(aMaskedFrame, &toReferenceFrame);
2983 
2984   Maybe<gfxRect> combinedClip;
2985   if (maskUsage.shouldApplyBasicShapeOrPath) {
2986     Maybe<Rect> result =
2987         CSSClipPathInstance::GetBoundingRectForBasicShapeOrPathClip(
2988             aMaskedFrame, svgReset->mClipPath);
2989     if (result) {
2990       combinedClip = Some(ThebesRect(*result));
2991     }
2992   } else if (maskUsage.shouldApplyClipPath) {
2993     gfxRect result = SVGUtils::GetBBox(
2994         aMaskedFrame,
2995         SVGUtils::eBBoxIncludeClipped | SVGUtils::eBBoxIncludeFill |
2996             SVGUtils::eBBoxIncludeMarkers | SVGUtils::eBBoxIncludeStroke |
2997             SVGUtils::eDoNotClipToBBoxOfContentInsideClipPath);
2998     combinedClip = Some(
2999         ThebesRect((CSSRect::FromUnknownRect(ToRect(result)) * cssToDevScale)
3000                        .ToUnknownRect()));
3001   } else {
3002     // The code for this case is adapted from ComputeMaskGeometry().
3003 
3004     nsRect borderArea(toReferenceFrame, aMaskedFrame->GetSize());
3005     borderArea -= offsetToUserSpace;
3006 
3007     // Use an infinite dirty rect to pass into nsCSSRendering::
3008     // GetImageLayerClip() because we don't have an actual dirty rect to
3009     // pass in. This is fine because the only time GetImageLayerClip() will
3010     // not intersect the incoming dirty rect with something is in the "NoClip"
3011     // case, and we handle that specially.
3012     nsRect dirtyRect(nscoord_MIN / 2, nscoord_MIN / 2, nscoord_MAX,
3013                      nscoord_MAX);
3014 
3015     nsIFrame* firstFrame =
3016         nsLayoutUtils::FirstContinuationOrIBSplitSibling(aMaskedFrame);
3017     nsTArray<SVGMaskFrame*> maskFrames;
3018     // XXX check return value?
3019     SVGObserverUtils::GetAndObserveMasks(firstFrame, &maskFrames);
3020 
3021     for (uint32_t i = 0; i < maskFrames.Length(); ++i) {
3022       gfxRect clipArea;
3023       if (maskFrames[i]) {
3024         clipArea = maskFrames[i]->GetMaskArea(aMaskedFrame);
3025         clipArea = ThebesRect(
3026             (CSSRect::FromUnknownRect(ToRect(clipArea)) * cssToDevScale)
3027                 .ToUnknownRect());
3028       } else {
3029         const auto& layer = svgReset->mMask.mLayers[i];
3030         if (layer.mClip == StyleGeometryBox::NoClip) {
3031           return Nothing();
3032         }
3033 
3034         nsCSSRendering::ImageLayerClipState clipState;
3035         nsCSSRendering::GetImageLayerClip(
3036             layer, aMaskedFrame, *aMaskedFrame->StyleBorder(), borderArea,
3037             dirtyRect, false /* aWillPaintBorder */, devPixelRatio, &clipState);
3038         clipArea = clipState.mDirtyRectInDevPx;
3039       }
3040       combinedClip = UnionMaybeRects(combinedClip, Some(clipArea));
3041     }
3042   }
3043   if (combinedClip) {
3044     if (combinedClip->IsEmpty()) {
3045       // *clipForMask might be empty if all mask references are not resolvable
3046       // or the size of them are empty. We still need to create a transparent
3047       // mask before bug 1276834 fixed, so don't clip ctx by an empty rectangle
3048       // for for now.
3049       return Nothing();
3050     }
3051 
3052     // Convert to user space.
3053     *combinedClip += devPixelOffsetToUserSpace;
3054 
3055     // Round the clip out. In FrameLayerBuilder we round clips to nearest
3056     // pixels, and if we have a really thin clip here, that can cause the
3057     // clip to become empty if we didn't round out here.
3058     // The rounding happens in coordinates that are relative to the reference
3059     // frame, which matches what FrameLayerBuilder does.
3060     combinedClip->RoundOut();
3061 
3062     // Convert to app units.
3063     nsRect result =
3064         nsLayoutUtils::RoundGfxRectToAppRect(*combinedClip, devPixelRatio);
3065 
3066     // The resulting clip is relative to the reference frame, but the caller
3067     // expects it to be relative to the masked frame, so adjust it.
3068     result -= toReferenceFrame;
3069     return Some(result);
3070   }
3071   return Nothing();
3072 }
3073 
3074 struct AutoCheckBuilder {
AutoCheckBuilderAutoCheckBuilder3075   explicit AutoCheckBuilder(nsDisplayListBuilder* aBuilder)
3076       : mBuilder(aBuilder) {
3077     aBuilder->Check();
3078   }
3079 
~AutoCheckBuilderAutoCheckBuilder3080   ~AutoCheckBuilder() { mBuilder->Check(); }
3081 
3082   nsDisplayListBuilder* mBuilder;
3083 };
3084 
3085 /**
3086  * Helper class to track container creation. Stores the first tracked container.
3087  * Used to find the innermost container for hit test information, and to notify
3088  * callers whether a container item was created or not.
3089  */
3090 struct ContainerTracker {
TrackContainerContainerTracker3091   void TrackContainer(nsDisplayItem* aContainer) {
3092     if (!aContainer) {
3093       return;
3094     }
3095 
3096     if (!mContainer) {
3097       mContainer = aContainer;
3098     }
3099 
3100     mCreatedContainer = true;
3101   }
3102 
ResetCreatedContainerContainerTracker3103   void ResetCreatedContainer() { mCreatedContainer = false; }
3104 
3105   nsDisplayItem* mContainer = nullptr;
3106   bool mCreatedContainer = false;
3107 };
3108 
3109 /**
3110  * Tries to reuse a top-level stacking context item from the previous paint.
3111  * Returns true if an item was reused, otherwise false.
3112  */
TryToReuseStackingContextItem(nsDisplayListBuilder * aBuilder,nsDisplayList * aList,nsIFrame * aFrame)3113 bool TryToReuseStackingContextItem(nsDisplayListBuilder* aBuilder,
3114                                    nsDisplayList* aList, nsIFrame* aFrame) {
3115   if (!aBuilder->IsForPainting() || !aBuilder->IsPartialUpdate() ||
3116       aBuilder->InInvalidSubtree()) {
3117     return false;
3118   }
3119 
3120   if (aFrame->IsFrameModified() || aFrame->HasModifiedDescendants()) {
3121     return false;
3122   }
3123 
3124   auto& items = aFrame->DisplayItems();
3125   auto* res = std::find_if(
3126       items.begin(), items.end(),
3127       [](nsDisplayItem* aItem) { return aItem->IsPreProcessed(); });
3128 
3129   if (res == items.end()) {
3130     return false;
3131   }
3132 
3133   nsDisplayItem* container = *res;
3134   MOZ_ASSERT(container->Frame() == aFrame);
3135   DL_LOGD("RDL - Found SC item %p (%s) (frame: %p)", container,
3136           container->Name(), container->Frame());
3137 
3138   aList->AppendToTop(container);
3139   aBuilder->ReuseDisplayItem(container);
3140   return true;
3141 }
3142 
BuildDisplayListForStackingContext(nsDisplayListBuilder * aBuilder,nsDisplayList * aList,bool * aCreatedContainerItem)3143 void nsIFrame::BuildDisplayListForStackingContext(
3144     nsDisplayListBuilder* aBuilder, nsDisplayList* aList,
3145     bool* aCreatedContainerItem) {
3146   if (aBuilder->IsForContent()) {
3147     DL_LOGV("BuildDisplayListForStackingContext (%p) <", this);
3148   }
3149 
3150   ScopeExit e([this, aBuilder]() {
3151     if (aBuilder->IsForContent()) {
3152       DL_LOGV("> BuildDisplayListForStackingContext (%p)", this);
3153     }
3154   });
3155 
3156   AutoCheckBuilder check(aBuilder);
3157 
3158   if (aBuilder->IsReusingStackingContextItems() &&
3159       TryToReuseStackingContextItem(aBuilder, aList, this)) {
3160     if (aCreatedContainerItem) {
3161       *aCreatedContainerItem = true;
3162     }
3163     return;
3164   }
3165 
3166   if (HasAnyStateBits(NS_FRAME_TOO_DEEP_IN_FRAME_TREE)) {
3167     return;
3168   }
3169 
3170   const nsStyleDisplay* disp = StyleDisplay();
3171   const nsStyleEffects* effects = StyleEffects();
3172   EffectSet* effectSetForOpacity = EffectSet::GetEffectSetForFrame(
3173       this, nsCSSPropertyIDSet::OpacityProperties());
3174   // We can stop right away if this is a zero-opacity stacking context and
3175   // we're painting, and we're not animating opacity.
3176   bool needHitTestInfo = aBuilder->BuildCompositorHitTestInfo() &&
3177                          Style()->PointerEvents() != StylePointerEvents::None;
3178   bool opacityItemForEventsOnly = false;
3179   if (effects->mOpacity == 0.0 && aBuilder->IsForPainting() &&
3180       !(disp->mWillChange.bits & StyleWillChangeBits::OPACITY) &&
3181       !nsLayoutUtils::HasAnimationOfPropertySet(
3182           this, nsCSSPropertyIDSet::OpacityProperties(), effectSetForOpacity)) {
3183     if (needHitTestInfo) {
3184       opacityItemForEventsOnly = true;
3185     } else {
3186       return;
3187     }
3188   }
3189 
3190   if (aBuilder->IsForPainting() && disp->mWillChange.bits) {
3191     aBuilder->AddToWillChangeBudget(this, GetSize());
3192   }
3193 
3194   // For preserves3d, use the dirty rect already installed on the
3195   // builder, since aDirtyRect maybe distorted for transforms along
3196   // the chain.
3197   nsRect visibleRect = aBuilder->GetVisibleRect();
3198   nsRect dirtyRect = aBuilder->GetDirtyRect();
3199 
3200   // We build an opacity item if it's not going to be drawn by SVG content.
3201   // We could in principle skip creating an nsDisplayOpacity item if
3202   // nsDisplayOpacity::NeedsActiveLayer returns false and usingSVGEffects is
3203   // true (the nsDisplayFilter/nsDisplayMasksAndClipPaths could handle the
3204   // opacity). Since SVG has perf issues where we sometimes spend a lot of
3205   // time creating display list items that might be helpful.  We'd need to
3206   // restore our mechanism to do that (changed in bug 1482403), and we'd
3207   // need to invalidate the frame if the value that would be return from
3208   // NeedsActiveLayer was to change, which we don't currently do.
3209   const bool useOpacity =
3210       HasVisualOpacity(disp, effects, effectSetForOpacity) &&
3211       !SVGUtils::CanOptimizeOpacity(this);
3212 
3213   const bool isTransformed = IsTransformed();
3214   const bool hasPerspective = isTransformed && HasPerspective();
3215   const bool extend3DContext =
3216       Extend3DContext(disp, effects, effectSetForOpacity);
3217   const bool combines3DTransformWithAncestors =
3218       (extend3DContext || isTransformed) && Combines3DTransformWithAncestors();
3219 
3220   Maybe<nsDisplayListBuilder::AutoPreserves3DContext> autoPreserves3DContext;
3221   if (extend3DContext && !combines3DTransformWithAncestors) {
3222     // Start a new preserves3d context to keep informations on
3223     // nsDisplayListBuilder.
3224     autoPreserves3DContext.emplace(aBuilder);
3225     // Save dirty rect on the builder to avoid being distorted for
3226     // multiple transforms along the chain.
3227     aBuilder->SavePreserves3DRect();
3228 
3229     // We rebuild everything within preserve-3d and don't try
3230     // to retain, so override the dirty rect now.
3231     if (aBuilder->IsRetainingDisplayList()) {
3232       dirtyRect = visibleRect;
3233       aBuilder->SetDisablePartialUpdates(true);
3234     }
3235   }
3236 
3237   const bool useBlendMode = effects->mMixBlendMode != StyleBlend::Normal;
3238   if (useBlendMode) {
3239     aBuilder->SetContainsBlendMode(true);
3240   }
3241 
3242   // reset blend mode so we can keep track if this stacking context needs have
3243   // a nsDisplayBlendContainer. Set the blend mode back when the routine exits
3244   // so we keep track if the parent stacking context needs a container too.
3245   AutoSaveRestoreContainsBlendMode autoRestoreBlendMode(*aBuilder);
3246   aBuilder->SetContainsBlendMode(false);
3247 
3248   bool usingBackdropFilter =
3249       effects->HasBackdropFilters() &&
3250       nsDisplayBackdropFilters::CanCreateWebRenderCommands(aBuilder, this);
3251 
3252   if (usingBackdropFilter) {
3253     aBuilder->SetContainsBackdropFilter(true);
3254   }
3255 
3256   AutoSaveRestoreContainsBackdropFilter autoRestoreBackdropFilter(*aBuilder);
3257   aBuilder->SetContainsBackdropFilter(false);
3258 
3259   nsRect visibleRectOutsideTransform = visibleRect;
3260   nsDisplayTransform::PrerenderInfo prerenderInfo;
3261   bool inTransform = aBuilder->IsInTransform();
3262   if (isTransformed) {
3263     prerenderInfo = nsDisplayTransform::ShouldPrerenderTransformedContent(
3264         aBuilder, this, &visibleRect);
3265 
3266     switch (prerenderInfo.mDecision) {
3267       case nsDisplayTransform::PrerenderDecision::Full:
3268       case nsDisplayTransform::PrerenderDecision::Partial:
3269         dirtyRect = visibleRect;
3270         break;
3271       case nsDisplayTransform::PrerenderDecision::No: {
3272         // If we didn't prerender an animated frame in a preserve-3d context,
3273         // then we want disable async animations for the rest of the preserve-3d
3274         // (especially ancestors).
3275         if ((extend3DContext || combines3DTransformWithAncestors) &&
3276             prerenderInfo.mHasAnimations) {
3277           aBuilder->SavePreserves3DAllowAsyncAnimation(false);
3278         }
3279 
3280         const nsRect overflow = InkOverflowRectRelativeToSelf();
3281         if (overflow.IsEmpty() && !extend3DContext) {
3282           return;
3283         }
3284 
3285         // If we're in preserve-3d then grab the dirty rect that was given to
3286         // the root and transform using the combined transform.
3287         if (combines3DTransformWithAncestors) {
3288           visibleRect = dirtyRect = aBuilder->GetPreserves3DRect();
3289         }
3290 
3291         nsRect untransformedDirtyRect;
3292         if (nsDisplayTransform::UntransformRect(dirtyRect, overflow, this,
3293                                                 &untransformedDirtyRect)) {
3294           dirtyRect = untransformedDirtyRect;
3295           nsDisplayTransform::UntransformRect(visibleRect, overflow, this,
3296                                               &visibleRect);
3297         } else {
3298           // This should only happen if the transform is singular, in which case
3299           // nothing is visible anyway
3300           dirtyRect.SetEmpty();
3301           visibleRect.SetEmpty();
3302         }
3303       }
3304     }
3305     inTransform = true;
3306   } else if (IsFixedPosContainingBlock()) {
3307     // Restict the building area to the overflow rect for these frames, since
3308     // RetainedDisplayListBuilder uses it to know if the size of the stacking
3309     // context changed.
3310     visibleRect.IntersectRect(visibleRect, InkOverflowRect());
3311     dirtyRect.IntersectRect(dirtyRect, InkOverflowRect());
3312   }
3313 
3314   bool hasOverrideDirtyRect = false;
3315   // If we're doing a partial build, we're not invalid and we're capable
3316   // of having an override building rect (stacking context and fixed pos
3317   // containing block), then we should assume we have one.
3318   // Either we have an explicit one, or nothing in our subtree changed and
3319   // we have an implicit empty rect.
3320   //
3321   // These conditions should match |CanStoreDisplayListBuildingRect()| in
3322   // RetainedDisplayListBuilder.cpp
3323   if (!aBuilder->IsReusingStackingContextItems() &&
3324       aBuilder->IsPartialUpdate() && !aBuilder->InInvalidSubtree() &&
3325       !IsFrameModified() && IsFixedPosContainingBlock() &&
3326       !GetPrevContinuation() && !GetNextContinuation()) {
3327     dirtyRect = nsRect();
3328     if (HasOverrideDirtyRegion()) {
3329       nsDisplayListBuilder::DisplayListBuildingData* data =
3330           GetProperty(nsDisplayListBuilder::DisplayListBuildingRect());
3331       if (data) {
3332         dirtyRect = data->mDirtyRect.Intersect(visibleRect);
3333         hasOverrideDirtyRect = true;
3334       }
3335     }
3336   }
3337 
3338   bool usingFilter = effects->HasFilters();
3339   bool usingMask = SVGIntegrationUtils::UsingMaskOrClipPathForFrame(this);
3340   bool usingSVGEffects = usingFilter || usingMask;
3341 
3342   nsRect visibleRectOutsideSVGEffects = visibleRect;
3343   nsDisplayList hoistedScrollInfoItemsStorage(aBuilder);
3344   if (usingSVGEffects) {
3345     dirtyRect =
3346         SVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, dirtyRect);
3347     visibleRect =
3348         SVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, visibleRect);
3349     aBuilder->EnterSVGEffectsContents(this, &hoistedScrollInfoItemsStorage);
3350   }
3351 
3352   bool useStickyPosition = disp->mPosition == StylePositionProperty::Sticky;
3353 
3354   bool useFixedPosition =
3355       disp->mPosition == StylePositionProperty::Fixed &&
3356       (DisplayPortUtils::IsFixedPosFrameInDisplayPort(this) ||
3357        BuilderHasScrolledClip(aBuilder));
3358 
3359   nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList(
3360       aBuilder, this, visibleRect, dirtyRect, isTransformed);
3361 
3362   UpdateCurrentHitTestInfo(aBuilder, this);
3363 
3364   // Depending on the effects that are applied to this frame, we can create
3365   // multiple container display items and wrap them around our contents.
3366   // This enum lists all the potential container display items, in the order
3367   // outside to inside.
3368   enum class ContainerItemType : uint8_t {
3369     None = 0,
3370     OwnLayerIfNeeded,
3371     BlendMode,
3372     FixedPosition,
3373     OwnLayerForTransformWithRoundedClip,
3374     Perspective,
3375     Transform,
3376     SeparatorTransforms,
3377     Opacity,
3378     Filter,
3379     BlendContainer
3380   };
3381 
3382   nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
3383 
3384   auto cssClip = GetClipPropClipRect(disp, effects, GetSize());
3385   auto ApplyClipProp = [&](DisplayListClipState::AutoSaveRestore& aClipState) {
3386     if (!cssClip) {
3387       return;
3388     }
3389     nsPoint offset = aBuilder->GetCurrentFrameOffsetToReferenceFrame();
3390     aBuilder->IntersectDirtyRect(*cssClip);
3391     aBuilder->IntersectVisibleRect(*cssClip);
3392     aClipState.ClipContentDescendants(*cssClip + offset);
3393   };
3394 
3395   // The CSS clip property is effectively inside the transform, but outside the
3396   // filters. So if we're not transformed we can apply it just here for
3397   // simplicity, instead of on each of the places that handle clipCapturedBy.
3398   DisplayListClipState::AutoSaveRestore untransformedCssClip(aBuilder);
3399   if (!isTransformed) {
3400     ApplyClipProp(untransformedCssClip);
3401   }
3402 
3403   // If there is a current clip, then depending on the container items we
3404   // create, different things can happen to it. Some container items simply
3405   // propagate the clip to their children and aren't clipped themselves.
3406   // But other container items, especially those that establish a different
3407   // geometry for their contents (e.g. transforms), capture the clip on
3408   // themselves and unset the clip for their contents. If we create more than
3409   // one of those container items, the clip will be captured on the outermost
3410   // one and the inner container items will be unclipped.
3411   ContainerItemType clipCapturedBy = ContainerItemType::None;
3412   if (useFixedPosition) {
3413     clipCapturedBy = ContainerItemType::FixedPosition;
3414   } else if (isTransformed) {
3415     const DisplayItemClipChain* currentClip =
3416         aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder);
3417     if ((hasPerspective || extend3DContext) &&
3418         (currentClip && currentClip->HasRoundedCorners())) {
3419       // If we're creating an nsDisplayTransform item that is going to combine
3420       // its transform with its children (preserve-3d or perspective), then we
3421       // can't have an intermediate surface. Mask layers force an intermediate
3422       // surface, so if we're going to need both then create a separate
3423       // wrapping layer for the mask.
3424       clipCapturedBy = ContainerItemType::OwnLayerForTransformWithRoundedClip;
3425     } else if (hasPerspective) {
3426       clipCapturedBy = ContainerItemType::Perspective;
3427     } else {
3428       clipCapturedBy = ContainerItemType::Transform;
3429     }
3430   } else if (usingFilter) {
3431     clipCapturedBy = ContainerItemType::Filter;
3432   }
3433 
3434   DisplayListClipState::AutoSaveRestore clipState(aBuilder);
3435   if (clipCapturedBy != ContainerItemType::None) {
3436     clipState.Clear();
3437   }
3438 
3439   DisplayListClipState::AutoSaveRestore transformedCssClip(aBuilder);
3440   if (isTransformed) {
3441     // FIXME(emilio, bug 1525159): In the case we have a both a transform _and_
3442     // filters, this clips the input to the filters as well, which is not
3443     // correct (clipping by the `clip` property is supposed to happen after
3444     // applying the filter effects, per [1].
3445     //
3446     // This is not a regression though, since we used to do that anyway before
3447     // bug 1514384, and even without the transform we get it wrong.
3448     //
3449     // [1]: https://drafts.fxtf.org/css-masking/#placement
3450     ApplyClipProp(transformedCssClip);
3451   }
3452 
3453   nsDisplayListCollection set(aBuilder);
3454   Maybe<nsRect> clipForMask;
3455   bool insertBackdropRoot;
3456   {
3457     DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
3458     nsDisplayListBuilder::AutoInTransformSetter inTransformSetter(aBuilder,
3459                                                                   inTransform);
3460     nsDisplayListBuilder::AutoEnterFilter filterASRSetter(aBuilder,
3461                                                           usingFilter);
3462     nsDisplayListBuilder::AutoInEventsOnly inEventsSetter(
3463         aBuilder, opacityItemForEventsOnly);
3464 
3465     // If we have a mask, compute a clip to bound the masked content.
3466     // This is necessary in case the content moves with an ancestor
3467     // ASR of the mask.
3468     // Don't do this if we also have a filter, because then the clip
3469     // would be applied before the filter, violating
3470     // https://www.w3.org/TR/filter-effects-1/#placement.
3471     // Filters are a containing block for fixed and absolute descendants,
3472     // so the masked content cannot move with an ancestor ASR.
3473     if (usingMask && !usingFilter) {
3474       clipForMask = ComputeClipForMaskItem(aBuilder, this);
3475       if (clipForMask) {
3476         aBuilder->IntersectDirtyRect(*clipForMask);
3477         aBuilder->IntersectVisibleRect(*clipForMask);
3478         nestedClipState.ClipContentDescendants(
3479             *clipForMask + aBuilder->GetCurrentFrameOffsetToReferenceFrame());
3480       }
3481     }
3482 
3483     // extend3DContext also guarantees that applyAbsPosClipping and
3484     // usingSVGEffects are false We only modify the preserve-3d rect if we are
3485     // the top of a preserve-3d heirarchy
3486     if (extend3DContext) {
3487       // Mark these first so MarkAbsoluteFramesForDisplayList knows if we are
3488       // going to be forced to descend into frames.
3489       aBuilder->MarkPreserve3DFramesForDisplayList(this);
3490     }
3491 
3492     aBuilder->AdjustWindowDraggingRegion(this);
3493 
3494     MarkAbsoluteFramesForDisplayList(aBuilder);
3495     aBuilder->Check();
3496     BuildDisplayList(aBuilder, set);
3497     SetBuiltDisplayList(true);
3498     aBuilder->Check();
3499     aBuilder->DisplayCaret(this, set.Outlines());
3500 
3501     insertBackdropRoot = aBuilder->ContainsBackdropFilter() &&
3502                          FormsBackdropRoot(disp, effects, StyleSVGReset());
3503 
3504     // Blend modes are a real pain for retained display lists. We build a blend
3505     // container item if the built list contains any blend mode items within
3506     // the current stacking context. This can change without an invalidation
3507     // to the stacking context frame, or the blend mode frame (e.g. by moving
3508     // an intermediate frame).
3509     // When we gain/remove a blend container item, we need to mark this frame
3510     // as invalid and have the full display list for merging to track
3511     // the change correctly.
3512     // It seems really hard to track this in advance, as the bookkeeping
3513     // required to note which stacking contexts have blend descendants
3514     // is complex and likely to be buggy.
3515     // Instead we're doing the sad thing, detecting it afterwards, and just
3516     // repeating display list building if it changed.
3517     // We have to repeat building for the entire display list (or at least
3518     // the outer stacking context), since we need to mark this frame as invalid
3519     // to remove any existing content that isn't wrapped in the blend container,
3520     // and then we need to build content infront/behind the blend container
3521     // to get correct positioning during merging.
3522     if ((insertBackdropRoot || aBuilder->ContainsBlendMode()) &&
3523         aBuilder->IsRetainingDisplayList()) {
3524       if (aBuilder->IsPartialUpdate()) {
3525         aBuilder->SetPartialBuildFailed(true);
3526       } else {
3527         aBuilder->SetDisablePartialUpdates(true);
3528       }
3529     }
3530   }
3531 
3532   // If a child contains a backdrop filter, but this stacking context does not
3533   // form a backdrop root, we need to propogate up the tree until we find an
3534   // ancestor that does form a backdrop root.
3535   if (!insertBackdropRoot && aBuilder->ContainsBackdropFilter()) {
3536     autoRestoreBackdropFilter.DelegateUp(true);
3537   }
3538 
3539   if (aBuilder->IsBackgroundOnly()) {
3540     set.BlockBorderBackgrounds()->DeleteAll(aBuilder);
3541     set.Floats()->DeleteAll(aBuilder);
3542     set.Content()->DeleteAll(aBuilder);
3543     set.PositionedDescendants()->DeleteAll(aBuilder);
3544     set.Outlines()->DeleteAll(aBuilder);
3545   }
3546 
3547   if (hasOverrideDirtyRect &&
3548       StaticPrefs::layout_display_list_show_rebuild_area()) {
3549     nsDisplaySolidColor* color = MakeDisplayItem<nsDisplaySolidColor>(
3550         aBuilder, this,
3551         dirtyRect + aBuilder->GetCurrentFrameOffsetToReferenceFrame(),
3552         NS_RGBA(255, 0, 0, 64), false);
3553     if (color) {
3554       color->SetOverrideZIndex(INT32_MAX);
3555       set.PositionedDescendants()->AppendToTop(color);
3556     }
3557   }
3558 
3559   nsIContent* content = GetContent();
3560   if (!content) {
3561     content = PresContext()->Document()->GetRootElement();
3562   }
3563 
3564   nsDisplayList resultList(aBuilder);
3565   set.SerializeWithCorrectZOrder(&resultList, content);
3566 
3567 #ifdef DEBUG
3568   DisplayDebugBorders(aBuilder, this, set);
3569 #endif
3570 
3571   // Get the ASR to use for the container items that we create here.
3572   const ActiveScrolledRoot* containerItemASR = contASRTracker.GetContainerASR();
3573 
3574   ContainerTracker ct;
3575 
3576   /* If adding both a nsDisplayBlendContainer and a nsDisplayBlendMode to the
3577    * same list, the nsDisplayBlendContainer should be added first. This only
3578    * happens when the element creating this stacking context has mix-blend-mode
3579    * and also contains a child which has mix-blend-mode.
3580    * The nsDisplayBlendContainer must be added to the list first, so it does not
3581    * isolate the containing element blending as well.
3582    */
3583   if (aBuilder->ContainsBlendMode()) {
3584     DisplayListClipState::AutoSaveRestore blendContainerClipState(aBuilder);
3585     resultList.AppendToTop(nsDisplayBlendContainer::CreateForMixBlendMode(
3586         aBuilder, this, &resultList, containerItemASR));
3587     ct.TrackContainer(resultList.GetTop());
3588   }
3589 
3590   if (insertBackdropRoot) {
3591     DisplayListClipState::AutoSaveRestore backdropRootContainerClipState(
3592         aBuilder);
3593     resultList.AppendNewToTop<nsDisplayBackdropRootContainer>(
3594         aBuilder, this, &resultList, containerItemASR);
3595     ct.TrackContainer(resultList.GetTop());
3596   }
3597 
3598   if (usingBackdropFilter) {
3599     DisplayListClipState::AutoSaveRestore clipState(aBuilder);
3600     nsRect backdropRect =
3601         GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this);
3602     resultList.AppendNewToTop<nsDisplayBackdropFilters>(
3603         aBuilder, this, &resultList, backdropRect);
3604     ct.TrackContainer(resultList.GetTop());
3605   }
3606 
3607   /* If there are any SVG effects, wrap the list up in an SVG effects item
3608    * (which also handles CSS group opacity). Note that we create an SVG effects
3609    * item even if resultList is empty, since a filter can produce graphical
3610    * output even if the element being filtered wouldn't otherwise do so.
3611    */
3612   if (usingSVGEffects) {
3613     MOZ_ASSERT(usingFilter || usingMask,
3614                "Beside filter & mask/clip-path, what else effect do we have?");
3615 
3616     if (clipCapturedBy == ContainerItemType::Filter) {
3617       clipState.Restore();
3618     }
3619     // Revert to the post-filter dirty rect.
3620     aBuilder->SetVisibleRect(visibleRectOutsideSVGEffects);
3621 
3622     // Skip all filter effects while generating glyph mask.
3623     if (usingFilter && !aBuilder->IsForGenerateGlyphMask()) {
3624       /* List now emptied, so add the new list to the top. */
3625       resultList.AppendNewToTop<nsDisplayFilters>(aBuilder, this, &resultList);
3626       ct.TrackContainer(resultList.GetTop());
3627     }
3628 
3629     if (usingMask) {
3630       DisplayListClipState::AutoSaveRestore maskClipState(aBuilder);
3631       // The mask should move with aBuilder->CurrentActiveScrolledRoot(), so
3632       // that's the ASR we prefer to use for the mask item. However, we can
3633       // only do this if the mask if clipped with respect to that ASR, because
3634       // an item always needs to have finite bounds with respect to its ASR.
3635       // If we weren't able to compute a clip for the mask, we fall back to
3636       // using containerItemASR, which is the lowest common ancestor clip of
3637       // the mask's contents. That's not entirely correct, but it satisfies
3638       // the base requirement of the ASR system (that items have finite bounds
3639       // wrt. their ASR).
3640       const ActiveScrolledRoot* maskASR =
3641           clipForMask.isSome() ? aBuilder->CurrentActiveScrolledRoot()
3642                                : containerItemASR;
3643       /* List now emptied, so add the new list to the top. */
3644       resultList.AppendNewToTop<nsDisplayMasksAndClipPaths>(
3645           aBuilder, this, &resultList, maskASR);
3646       ct.TrackContainer(resultList.GetTop());
3647     }
3648 
3649     // TODO(miko): We could probably create a wraplist here and avoid creating
3650     // it later in |BuildDisplayListForChild()|.
3651     ct.ResetCreatedContainer();
3652 
3653     // Also add the hoisted scroll info items. We need those for APZ scrolling
3654     // because nsDisplayMasksAndClipPaths items can't build active layers.
3655     aBuilder->ExitSVGEffectsContents();
3656     resultList.AppendToTop(&hoistedScrollInfoItemsStorage);
3657   }
3658 
3659   /* If the list is non-empty and there is CSS group opacity without SVG
3660    * effects, wrap it up in an opacity item.
3661    */
3662   if (useOpacity) {
3663     // Don't clip nsDisplayOpacity items. We clip their descendants instead.
3664     // The clip we would set on an element with opacity would clip
3665     // all descendant content, but some should not be clipped.
3666     DisplayListClipState::AutoSaveRestore opacityClipState(aBuilder);
3667     const bool needsActiveOpacityLayer =
3668         nsDisplayOpacity::NeedsActiveLayer(aBuilder, this);
3669 
3670     resultList.AppendNewToTop<nsDisplayOpacity>(
3671         aBuilder, this, &resultList, containerItemASR, opacityItemForEventsOnly,
3672         needsActiveOpacityLayer);
3673     ct.TrackContainer(resultList.GetTop());
3674   }
3675 
3676   /* If we're going to apply a transformation and don't have preserve-3d set,
3677    * wrap everything in an nsDisplayTransform. If there's nothing in the list,
3678    * don't add anything.
3679    *
3680    * For the preserve-3d case we want to individually wrap every child in the
3681    * list with a separate nsDisplayTransform instead. When the child is already
3682    * an nsDisplayTransform, we can skip this step, as the computed transform
3683    * will already include our own.
3684    *
3685    * We also traverse into sublists created by nsDisplayWrapList, so that we
3686    * find all the correct children.
3687    */
3688   if (isTransformed && extend3DContext) {
3689     // Install dummy nsDisplayTransform as a leaf containing
3690     // descendants not participating this 3D rendering context.
3691     nsDisplayList nonparticipants(aBuilder);
3692     nsDisplayList participants(aBuilder);
3693     int index = 1;
3694 
3695     nsDisplayItem* separator = nullptr;
3696 
3697     // TODO: This can be simplified: |participants| is just |resultList|.
3698     for (nsDisplayItem* item : resultList.TakeItems()) {
3699       if (ItemParticipatesIn3DContext(this, item) &&
3700           !item->GetClip().HasClip()) {
3701         // The frame of this item participates the same 3D context.
3702         WrapSeparatorTransform(aBuilder, this, &nonparticipants, &participants,
3703                                index++, &separator);
3704 
3705         participants.AppendToTop(item);
3706       } else {
3707         // The frame of the item doesn't participate the current
3708         // context, or has no transform.
3709         //
3710         // For items participating but not transformed, they are add
3711         // to nonparticipants to get a separator layer for handling
3712         // clips, if there is, on an intermediate surface.
3713         // \see ContainerLayer::DefaultComputeEffectiveTransforms().
3714         nonparticipants.AppendToTop(item);
3715       }
3716     }
3717     WrapSeparatorTransform(aBuilder, this, &nonparticipants, &participants,
3718                            index++, &separator);
3719 
3720     if (separator) {
3721       ct.TrackContainer(separator);
3722     }
3723 
3724     resultList.AppendToTop(&participants);
3725   }
3726 
3727   if (isTransformed) {
3728     transformedCssClip.Restore();
3729     if (clipCapturedBy == ContainerItemType::Transform) {
3730       // Restore clip state now so nsDisplayTransform is clipped properly.
3731       clipState.Restore();
3732     }
3733     // Revert to the dirtyrect coming in from the parent, without our transform
3734     // taken into account.
3735     aBuilder->SetVisibleRect(visibleRectOutsideTransform);
3736 
3737     if (this != aBuilder->RootReferenceFrame()) {
3738       // Revert to the outer reference frame and offset because all display
3739       // items we create from now on are outside the transform.
3740       nsPoint toOuterReferenceFrame;
3741       const nsIFrame* outerReferenceFrame =
3742           aBuilder->FindReferenceFrameFor(GetParent(), &toOuterReferenceFrame);
3743       toOuterReferenceFrame += GetPosition();
3744 
3745       buildingDisplayList.SetReferenceFrameAndCurrentOffset(
3746           outerReferenceFrame, toOuterReferenceFrame);
3747     }
3748 
3749     // We would like to block async animations for ancestors of ones not
3750     // prerendered in the preserve-3d tree. Now that we've finished processing
3751     // all descendants, update allowAsyncAnimation to take their prerender
3752     // state into account
3753     // FIXME: We don't block async animations for previous siblings because
3754     // their prerender decisions have been made. We may have to figure out a
3755     // better way to rollback their prerender decisions.
3756     // Alternatively we could not block animations for later siblings, and only
3757     // block them for ancestors of a blocked one.
3758     if ((extend3DContext || combines3DTransformWithAncestors) &&
3759         prerenderInfo.CanUseAsyncAnimations() &&
3760         !aBuilder->GetPreserves3DAllowAsyncAnimation()) {
3761       // aBuilder->GetPreserves3DAllowAsyncAnimation() means the inner or
3762       // previous silbing frames are allowed/disallowed for async animations.
3763       prerenderInfo.mDecision = nsDisplayTransform::PrerenderDecision::No;
3764     }
3765 
3766     nsDisplayTransform* transformItem = MakeDisplayItem<nsDisplayTransform>(
3767         aBuilder, this, &resultList, visibleRect, prerenderInfo.mDecision);
3768     if (transformItem) {
3769       resultList.AppendToTop(transformItem);
3770       ct.TrackContainer(transformItem);
3771     }
3772 
3773     if (hasPerspective) {
3774       transformItem->MarkWithAssociatedPerspective();
3775 
3776       if (clipCapturedBy == ContainerItemType::Perspective) {
3777         clipState.Restore();
3778       }
3779       resultList.AppendNewToTop<nsDisplayPerspective>(aBuilder, this,
3780                                                       &resultList);
3781       ct.TrackContainer(resultList.GetTop());
3782     }
3783   }
3784 
3785   if (clipCapturedBy ==
3786       ContainerItemType::OwnLayerForTransformWithRoundedClip) {
3787     clipState.Restore();
3788     resultList.AppendNewToTopWithIndex<nsDisplayOwnLayer>(
3789         aBuilder, this,
3790         /* aIndex = */ nsDisplayOwnLayer::OwnLayerForTransformWithRoundedClip,
3791         &resultList, aBuilder->CurrentActiveScrolledRoot(),
3792         nsDisplayOwnLayerFlags::None, ScrollbarData{},
3793         /* aForceActive = */ false, false);
3794     ct.TrackContainer(resultList.GetTop());
3795   }
3796 
3797   /* If we have sticky positioning, wrap it in a sticky position item.
3798    */
3799   if (useFixedPosition) {
3800     if (clipCapturedBy == ContainerItemType::FixedPosition) {
3801       clipState.Restore();
3802     }
3803     // The ASR for the fixed item should be the ASR of our containing block,
3804     // which has been set as the builder's current ASR, unless this frame is
3805     // invisible and we hadn't saved display item data for it. In that case,
3806     // we need to take the containerItemASR since we might have fixed children.
3807     // For WebRender, we want to the know what |containerItemASR| is for the
3808     // case where the fixed-pos item is not a "real" fixed-pos item (e.g. it's
3809     // nested inside a scrolling transform), so we stash that on the display
3810     // item as well.
3811     const ActiveScrolledRoot* fixedASR = ActiveScrolledRoot::PickAncestor(
3812         containerItemASR, aBuilder->CurrentActiveScrolledRoot());
3813     resultList.AppendNewToTop<nsDisplayFixedPosition>(
3814         aBuilder, this, &resultList, fixedASR, containerItemASR);
3815     ct.TrackContainer(resultList.GetTop());
3816   } else if (useStickyPosition) {
3817     // For position:sticky, the clip needs to be applied both to the sticky
3818     // container item and to the contents. The container item needs the clip
3819     // because a scrolled clip needs to move independently from the sticky
3820     // contents, and the contents need the clip so that they have finite
3821     // clipped bounds with respect to the container item's ASR. The latter is
3822     // a little tricky in the case where the sticky item has both fixed and
3823     // non-fixed descendants, because that means that the sticky container
3824     // item's ASR is the ASR of the fixed descendant.
3825     // For WebRender display list building, though, we still want to know the
3826     // the ASR that the sticky container item would normally have, so we stash
3827     // that on the display item as the "container ASR" (i.e. the normal ASR of
3828     // the container item, excluding the special behaviour induced by fixed
3829     // descendants).
3830     const ActiveScrolledRoot* stickyASR = ActiveScrolledRoot::PickAncestor(
3831         containerItemASR, aBuilder->CurrentActiveScrolledRoot());
3832 
3833     auto* stickyItem = MakeDisplayItem<nsDisplayStickyPosition>(
3834         aBuilder, this, &resultList, stickyASR,
3835         aBuilder->CurrentActiveScrolledRoot(),
3836         clipState.IsClippedToDisplayPort());
3837 
3838     bool shouldFlatten = true;
3839 
3840     StickyScrollContainer* stickyScrollContainer =
3841         StickyScrollContainer::GetStickyScrollContainerForFrame(this);
3842     if (stickyScrollContainer &&
3843         stickyScrollContainer->ScrollFrame()->IsMaybeAsynchronouslyScrolled()) {
3844       shouldFlatten = false;
3845     }
3846 
3847     stickyItem->SetShouldFlatten(shouldFlatten);
3848 
3849     resultList.AppendToTop(stickyItem);
3850     ct.TrackContainer(stickyItem);
3851 
3852     // If the sticky element is inside a filter, annotate the scroll frame that
3853     // scrolls the filter as having out-of-flow content inside a filter (this
3854     // inhibits paint skipping).
3855     if (aBuilder->GetFilterASR() && aBuilder->GetFilterASR() == stickyASR) {
3856       aBuilder->GetFilterASR()
3857           ->mScrollableFrame->SetHasOutOfFlowContentInsideFilter();
3858     }
3859   }
3860 
3861   /* If there's blending, wrap up the list in a blend-mode item. Note
3862    * that opacity can be applied before blending as the blend color is
3863    * not affected by foreground opacity (only background alpha).
3864    */
3865 
3866   if (useBlendMode) {
3867     DisplayListClipState::AutoSaveRestore blendModeClipState(aBuilder);
3868     resultList.AppendNewToTop<nsDisplayBlendMode>(aBuilder, this, &resultList,
3869                                                   effects->mMixBlendMode,
3870                                                   containerItemASR, false);
3871     ct.TrackContainer(resultList.GetTop());
3872   }
3873 
3874   bool createdOwnLayer = false;
3875   CreateOwnLayerIfNeeded(aBuilder, &resultList,
3876                          nsDisplayOwnLayer::OwnLayerForStackingContext,
3877                          &createdOwnLayer);
3878 
3879   if (createdOwnLayer) {
3880     ct.TrackContainer(resultList.GetTop());
3881   }
3882 
3883   if (aBuilder->IsReusingStackingContextItems()) {
3884     if (resultList.IsEmpty()) {
3885       return;
3886     }
3887 
3888     nsDisplayItem* container = resultList.GetBottom();
3889     if (resultList.Length() > 1 || container->Frame() != this) {
3890       container = MakeDisplayItem<nsDisplayContainer>(
3891           aBuilder, this, containerItemASR, &resultList);
3892     } else {
3893       MOZ_ASSERT(resultList.Length() == 1);
3894       resultList.Clear();
3895     }
3896 
3897     // Mark the outermost display item as reusable. These display items and
3898     // their chidren can be reused during the next paint if no ancestor or
3899     // descendant frames have been modified.
3900     if (!container->IsReusedItem()) {
3901       container->SetReusable();
3902     }
3903     aList->AppendToTop(container);
3904     ct.TrackContainer(container);
3905   } else {
3906     aList->AppendToTop(&resultList);
3907   }
3908 
3909   if (aCreatedContainerItem) {
3910     *aCreatedContainerItem = ct.mCreatedContainer;
3911   }
3912 }
3913 
WrapInWrapList(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const ActiveScrolledRoot * aContainerASR,bool aBuiltContainerItem=false)3914 static nsDisplayItem* WrapInWrapList(nsDisplayListBuilder* aBuilder,
3915                                      nsIFrame* aFrame, nsDisplayList* aList,
3916                                      const ActiveScrolledRoot* aContainerASR,
3917                                      bool aBuiltContainerItem = false) {
3918   nsDisplayItem* item = aList->GetBottom();
3919   if (!item) {
3920     return nullptr;
3921   }
3922 
3923   // We need a wrap list if there are multiple items, or if the single
3924   // item has a different frame. This can change in a partial build depending
3925   // on which items we build, so we need to ensure that we don't transition
3926   // to/from a wrap list without invalidating correctly.
3927   bool needsWrapList =
3928       aList->Length() > 1 || item->Frame() != aFrame || item->GetChildren();
3929 
3930   // If we have an explicit container item (that can't change without an
3931   // invalidation) or we're doing a full build and don't need a wrap list, then
3932   // we can skip adding one.
3933   if (aBuiltContainerItem || (!aBuilder->IsPartialUpdate() && !needsWrapList)) {
3934     MOZ_ASSERT(aList->Length() == 1);
3935     aList->Clear();
3936     return item;
3937   }
3938 
3939   // If we're doing a partial build and we didn't need a wrap list
3940   // previously then we can try to work from there.
3941   if (aBuilder->IsPartialUpdate() &&
3942       !aFrame->HasDisplayItem(uint32_t(DisplayItemType::TYPE_CONTAINER))) {
3943     // If we now need a wrap list, we must previously have had no display items
3944     // or a single one belonging to this frame. Mark the item itself as
3945     // discarded so that RetainedDisplayListBuilder uses the ones we just built.
3946     // We don't want to mark the frame as modified as that would invalidate
3947     // positioned descendants that might be outside of this list, and might not
3948     // have been rebuilt this time.
3949     if (needsWrapList) {
3950       DiscardOldItems(aFrame);
3951     } else {
3952       MOZ_ASSERT(aList->Length() == 1);
3953       aList->Clear();
3954       return item;
3955     }
3956   }
3957 
3958   // The last case we could try to handle is when we previously had a wrap list,
3959   // but no longer need it. Unfortunately we can't differentiate this case from
3960   // a partial build where other children exist but we just didn't build them
3961   // this time.
3962   // TODO:RetainedDisplayListBuilder's merge phase has the full list and
3963   // could strip them out.
3964 
3965   return MakeDisplayItem<nsDisplayContainer>(aBuilder, aFrame, aContainerASR,
3966                                              aList);
3967 }
3968 
3969 /**
3970  * Check if a frame should be visited for building display list.
3971  */
DescendIntoChild(nsDisplayListBuilder * aBuilder,const nsIFrame * aChild,const nsRect & aVisible,const nsRect & aDirty)3972 static bool DescendIntoChild(nsDisplayListBuilder* aBuilder,
3973                              const nsIFrame* aChild, const nsRect& aVisible,
3974                              const nsRect& aDirty) {
3975   if (aChild->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
3976     return true;
3977   }
3978 
3979   // If the child is a scrollframe that we want to ignore, then we need
3980   // to descend into it because its scrolled child may intersect the dirty
3981   // area even if the scrollframe itself doesn't.
3982   if (aChild == aBuilder->GetIgnoreScrollFrame()) {
3983     return true;
3984   }
3985 
3986   // There are cases where the "ignore scroll frame" on the builder is not set
3987   // correctly, and so we additionally want to catch cases where the child is
3988   // a root scrollframe and we are ignoring scrolling on the viewport.
3989   if (aChild == aBuilder->GetPresShellIgnoreScrollFrame()) {
3990     return true;
3991   }
3992 
3993   nsRect overflow = aChild->InkOverflowRect();
3994 
3995   // On mobile, there may be a dynamic toolbar. The root content document's
3996   // root scroll frame's ink overflow rect does not include the toolbar
3997   // height, but if the toolbar is hidden, we still want to be able to target
3998   // content underneath the toolbar, so expand the overflow rect here to
3999   // allow display list building to descend into the scroll frame.
4000   if (aBuilder->IsForEventDelivery() &&
4001       aChild == aChild->PresShell()->GetRootScrollFrame() &&
4002       aChild->PresContext()->IsRootContentDocumentCrossProcess() &&
4003       aChild->PresContext()->HasDynamicToolbar()) {
4004     overflow.SizeTo(nsLayoutUtils::ExpandHeightForDynamicToolbar(
4005         aChild->PresContext(), overflow.Size()));
4006   }
4007 
4008   if (aDirty.Intersects(overflow)) {
4009     return true;
4010   }
4011 
4012   if (aChild->ForceDescendIntoIfVisible() && aVisible.Intersects(overflow)) {
4013     return true;
4014   }
4015 
4016   if (aChild->IsFrameOfType(nsIFrame::eTablePart)) {
4017     // Relative positioning and transforms can cause table parts to move, but we
4018     // will still paint the backgrounds for their ancestor parts under them at
4019     // their 'normal' position. That means that we must consider the overflow
4020     // rects at both positions.
4021 
4022     // We convert the overflow rect into the nsTableFrame's coordinate
4023     // space, applying the normal position offset at each step. Then we
4024     // compare that against the builder's cached dirty rect in table
4025     // coordinate space.
4026     const nsIFrame* f = aChild;
4027     nsRect normalPositionOverflowRelativeToTable = overflow;
4028 
4029     while (f->IsFrameOfType(nsIFrame::eTablePart)) {
4030       normalPositionOverflowRelativeToTable += f->GetNormalPosition();
4031       f = f->GetParent();
4032     }
4033 
4034     nsDisplayTableBackgroundSet* tableBGs = aBuilder->GetTableBackgroundSet();
4035     if (tableBGs && tableBGs->GetDirtyRect().Intersects(
4036                         normalPositionOverflowRelativeToTable)) {
4037       return true;
4038     }
4039   }
4040 
4041   return false;
4042 }
4043 
BuildDisplayListForSimpleChild(nsDisplayListBuilder * aBuilder,nsIFrame * aChild,const nsDisplayListSet & aLists)4044 void nsIFrame::BuildDisplayListForSimpleChild(nsDisplayListBuilder* aBuilder,
4045                                               nsIFrame* aChild,
4046                                               const nsDisplayListSet& aLists) {
4047   // This is the shortcut for frames been handled along the common
4048   // path, the most common one of THE COMMON CASE mentioned later.
4049   MOZ_ASSERT(aChild->Type() != LayoutFrameType::Placeholder);
4050   MOZ_ASSERT(!aBuilder->GetSelectedFramesOnly() &&
4051                  !aBuilder->GetIncludeAllOutOfFlows(),
4052              "It should be held for painting to window");
4053   MOZ_ASSERT(aChild->HasAnyStateBits(NS_FRAME_SIMPLE_DISPLAYLIST));
4054 
4055   const nsPoint offset = aChild->GetOffsetTo(this);
4056   const nsRect visible = aBuilder->GetVisibleRect() - offset;
4057   const nsRect dirty = aBuilder->GetDirtyRect() - offset;
4058 
4059   if (!DescendIntoChild(aBuilder, aChild, visible, dirty)) {
4060     DL_LOGV("Skipped frame %p", aChild);
4061     return;
4062   }
4063 
4064   // Child cannot be transformed since it is not a stacking context.
4065   nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(
4066       aBuilder, aChild, visible, dirty, false);
4067 
4068   UpdateCurrentHitTestInfo(aBuilder, aChild);
4069 
4070   aChild->MarkAbsoluteFramesForDisplayList(aBuilder);
4071   aBuilder->AdjustWindowDraggingRegion(aChild);
4072   aBuilder->Check();
4073   aChild->BuildDisplayList(aBuilder, aLists);
4074   aChild->SetBuiltDisplayList(true);
4075   aBuilder->Check();
4076   aBuilder->DisplayCaret(aChild, aLists.Outlines());
4077 #ifdef DEBUG
4078   DisplayDebugBorders(aBuilder, aChild, aLists);
4079 #endif
4080 }
4081 
DisplayFlagForFlexOrGridItem() const4082 nsIFrame::DisplayChildFlag nsIFrame::DisplayFlagForFlexOrGridItem() const {
4083   MOZ_ASSERT(IsFlexOrGridItem(),
4084              "Should only be called on flex or grid items!");
4085   return DisplayChildFlag::ForcePseudoStackingContext;
4086 }
4087 
ShouldSkipFrame(nsDisplayListBuilder * aBuilder,const nsIFrame * aFrame)4088 static bool ShouldSkipFrame(nsDisplayListBuilder* aBuilder,
4089                             const nsIFrame* aFrame) {
4090   // If painting is restricted to just the background of the top level frame,
4091   // then we have nothing to do here.
4092   if (aBuilder->IsBackgroundOnly()) {
4093     return true;
4094   }
4095 
4096   if (aBuilder->IsForGenerateGlyphMask() &&
4097       (!aFrame->IsTextFrame() && aFrame->IsLeaf())) {
4098     return true;
4099   }
4100 
4101   // The placeholder frame should have the same content as the OOF frame.
4102   if (aBuilder->GetSelectedFramesOnly() &&
4103       (aFrame->IsLeaf() && !aFrame->IsSelected())) {
4104     return true;
4105   }
4106 
4107   static const nsFrameState skipFlags =
4108       (NS_FRAME_TOO_DEEP_IN_FRAME_TREE | NS_FRAME_IS_NONDISPLAY);
4109 
4110   return aFrame->HasAnyStateBits(skipFlags);
4111 }
4112 
BuildDisplayListForChild(nsDisplayListBuilder * aBuilder,nsIFrame * aChild,const nsDisplayListSet & aLists,DisplayChildFlags aFlags)4113 void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
4114                                         nsIFrame* aChild,
4115                                         const nsDisplayListSet& aLists,
4116                                         DisplayChildFlags aFlags) {
4117   AutoCheckBuilder check(aBuilder);
4118   if (aBuilder->IsForContent()) {
4119     DL_LOGV("BuildDisplayListForChild (%p) <", aChild);
4120   }
4121   ScopeExit e([aChild, aBuilder]() {
4122     if (aBuilder->IsForContent()) {
4123       DL_LOGV("> BuildDisplayListForChild (%p)", aChild);
4124     }
4125   });
4126 
4127   if (ShouldSkipFrame(aBuilder, aChild)) {
4128     return;
4129   }
4130 
4131   // If we're generating a display list for printing, include Link items for
4132   // frames that correspond to HTML link elements so that we can have active
4133   // links in saved PDF output. Note that the state of "within a link" is
4134   // set on the display-list builder, such that all descendants of the link
4135   // element will generate display-list links.
4136   // TODO: we should be able to optimize this so as to avoid creating links
4137   // for the same destination that entirely overlap each other, which adds
4138   // nothing useful to the final PDF.
4139   Maybe<nsDisplayListBuilder::Linkifier> linkifier;
4140   if (StaticPrefs::print_save_as_pdf_links_enabled() &&
4141       aBuilder->IsForPrinting()) {
4142     linkifier.emplace(aBuilder, aChild, aLists.Content());
4143     linkifier->MaybeAppendLink(aBuilder, aChild);
4144   }
4145 
4146   nsIFrame* child = aChild;
4147   auto* placeholder = child->IsPlaceholderFrame()
4148                           ? static_cast<nsPlaceholderFrame*>(child)
4149                           : nullptr;
4150   nsIFrame* childOrOutOfFlow =
4151       placeholder ? placeholder->GetOutOfFlowFrame() : child;
4152 
4153   nsIFrame* parent = childOrOutOfFlow->GetParent();
4154   const auto* parentDisplay = parent->StyleDisplay();
4155   const auto overflowClipAxes =
4156       parent->ShouldApplyOverflowClipping(parentDisplay);
4157 
4158   const bool isPaintingToWindow = aBuilder->IsPaintingToWindow();
4159   const bool doingShortcut =
4160       isPaintingToWindow &&
4161       child->HasAnyStateBits(NS_FRAME_SIMPLE_DISPLAYLIST) &&
4162       // Animations may change the stacking context state.
4163       // ShouldApplyOverflowClipping is affected by the parent style, which does
4164       // not invalidate the NS_FRAME_SIMPLE_DISPLAYLIST bit.
4165       !(overflowClipAxes != PhysicalAxes::None ||
4166         child->MayHaveTransformAnimation() || child->MayHaveOpacityAnimation());
4167 
4168   if (aBuilder->IsForPainting()) {
4169     aBuilder->ClearWillChangeBudgetStatus(child);
4170   }
4171 
4172   if (StaticPrefs::layout_css_scroll_anchoring_highlight()) {
4173     if (child->FirstContinuation()->IsScrollAnchor()) {
4174       nsRect bounds = child->GetContentRectRelativeToSelf() +
4175                       aBuilder->ToReferenceFrame(child);
4176       nsDisplaySolidColor* color = MakeDisplayItem<nsDisplaySolidColor>(
4177           aBuilder, child, bounds, NS_RGBA(255, 0, 255, 64));
4178       if (color) {
4179         color->SetOverrideZIndex(INT32_MAX);
4180         aLists.PositionedDescendants()->AppendToTop(color);
4181       }
4182     }
4183   }
4184 
4185   if (doingShortcut) {
4186     BuildDisplayListForSimpleChild(aBuilder, child, aLists);
4187     return;
4188   }
4189 
4190   // dirty rect in child-relative coordinates
4191   NS_ASSERTION(aBuilder->GetCurrentFrame() == this, "Wrong coord space!");
4192   const nsPoint offset = child->GetOffsetTo(this);
4193   nsRect visible = aBuilder->GetVisibleRect() - offset;
4194   nsRect dirty = aBuilder->GetDirtyRect() - offset;
4195 
4196   nsDisplayListBuilder::OutOfFlowDisplayData* savedOutOfFlowData = nullptr;
4197   if (placeholder) {
4198     if (placeholder->HasAnyStateBits(PLACEHOLDER_FOR_TOPLAYER)) {
4199       // If the out-of-flow frame is in the top layer, the viewport frame
4200       // will paint it. Skip it here. Note that, only out-of-flow frames
4201       // with this property should be skipped, because non-HTML elements
4202       // may stop their children from being out-of-flow. Those frames
4203       // should still be handled in the normal in-flow path.
4204       return;
4205     }
4206 
4207     child = childOrOutOfFlow;
4208     if (aBuilder->IsForPainting()) {
4209       aBuilder->ClearWillChangeBudgetStatus(child);
4210     }
4211 
4212     // If 'child' is a pushed float then it's owned by a block that's not an
4213     // ancestor of the placeholder, and it will be painted by that block and
4214     // should not be painted through the placeholder. Also recheck
4215     // NS_FRAME_TOO_DEEP_IN_FRAME_TREE and NS_FRAME_IS_NONDISPLAY.
4216     static const nsFrameState skipFlags =
4217         (NS_FRAME_IS_PUSHED_FLOAT | NS_FRAME_TOO_DEEP_IN_FRAME_TREE |
4218          NS_FRAME_IS_NONDISPLAY);
4219     if (child->HasAnyStateBits(skipFlags) || nsLayoutUtils::IsPopup(child)) {
4220       return;
4221     }
4222 
4223     MOZ_ASSERT(child->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW));
4224     savedOutOfFlowData = nsDisplayListBuilder::GetOutOfFlowData(child);
4225 
4226     if (aBuilder->GetIncludeAllOutOfFlows()) {
4227       visible = child->InkOverflowRect();
4228       dirty = child->InkOverflowRect();
4229     } else if (savedOutOfFlowData) {
4230       visible =
4231           savedOutOfFlowData->GetVisibleRectForFrame(aBuilder, child, &dirty);
4232     } else {
4233       // The out-of-flow frame did not intersect the dirty area. We may still
4234       // need to traverse into it, since it may contain placeholders we need
4235       // to enter to reach other out-of-flow frames that are visible.
4236       visible.SetEmpty();
4237       dirty.SetEmpty();
4238     }
4239   }
4240 
4241   NS_ASSERTION(!child->IsPlaceholderFrame(),
4242                "Should have dealt with placeholders already");
4243 
4244   if (!DescendIntoChild(aBuilder, child, visible, dirty)) {
4245     DL_LOGV("Skipped frame %p", child);
4246     return;
4247   }
4248 
4249   const bool isSVG = child->HasAnyStateBits(NS_FRAME_SVG_LAYOUT);
4250 
4251   // This flag is raised if the control flow strays off the common path.
4252   // The common path is the most common one of THE COMMON CASE mentioned later.
4253   bool awayFromCommonPath = !isPaintingToWindow;
4254 
4255   // true if this is a real or pseudo stacking context
4256   bool pseudoStackingContext =
4257       aFlags.contains(DisplayChildFlag::ForcePseudoStackingContext);
4258 
4259   if (!pseudoStackingContext && !isSVG &&
4260       aFlags.contains(DisplayChildFlag::Inline) &&
4261       !child->IsFrameOfType(eLineParticipant)) {
4262     // child is a non-inline frame in an inline context, i.e.,
4263     // it acts like inline-block or inline-table. Therefore it is a
4264     // pseudo-stacking-context.
4265     pseudoStackingContext = true;
4266   }
4267 
4268   const nsStyleDisplay* ourDisp = StyleDisplay();
4269   // REVIEW: Taken from nsBoxFrame::Paint
4270   // Don't paint our children if the theme object is a leaf.
4271   if (IsThemed(ourDisp) && !PresContext()->Theme()->WidgetIsContainer(
4272                                ourDisp->EffectiveAppearance()))
4273     return;
4274 
4275   // Since we're now sure that we're adding this frame to the display list
4276   // (which means we're painting it, modulo occlusion), mark it as visible
4277   // within the displayport.
4278   if (isPaintingToWindow && child->TrackingVisibility()) {
4279     child->PresShell()->EnsureFrameInApproximatelyVisibleList(child);
4280     awayFromCommonPath = true;
4281   }
4282 
4283   // Child is composited if it's transformed, partially transparent, or has
4284   // SVG effects or a blend mode..
4285   const nsStyleDisplay* disp = child->StyleDisplay();
4286   const nsStyleEffects* effects = child->StyleEffects();
4287 
4288   const bool isPositioned = disp->IsPositionedStyle();
4289   const bool isStackingContext =
4290       aFlags.contains(DisplayChildFlag::ForceStackingContext) ||
4291       child->IsStackingContext(disp, effects);
4292 
4293   if (pseudoStackingContext || isStackingContext || isPositioned ||
4294       placeholder || (!isSVG && disp->IsFloating(child)) ||
4295       (isSVG && effects->mClip.IsRect() && IsSVGContentWithCSSClip(child))) {
4296     pseudoStackingContext = true;
4297     awayFromCommonPath = true;
4298   }
4299 
4300   NS_ASSERTION(!isStackingContext || pseudoStackingContext,
4301                "Stacking contexts must also be pseudo-stacking-contexts");
4302 
4303   nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(
4304       aBuilder, child, visible, dirty);
4305 
4306   UpdateCurrentHitTestInfo(aBuilder, child);
4307 
4308   DisplayListClipState::AutoClipMultiple clipState(aBuilder);
4309   nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(aBuilder);
4310 
4311   if (savedOutOfFlowData) {
4312     aBuilder->SetBuildingInvisibleItems(false);
4313 
4314     clipState.SetClipChainForContainingBlockDescendants(
4315         savedOutOfFlowData->mContainingBlockClipChain);
4316     asrSetter.SetCurrentActiveScrolledRoot(
4317         savedOutOfFlowData->mContainingBlockActiveScrolledRoot);
4318     MOZ_ASSERT(awayFromCommonPath,
4319                "It is impossible when savedOutOfFlowData is true");
4320   } else if (HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) &&
4321              placeholder) {
4322     NS_ASSERTION(visible.IsEmpty(), "should have empty visible rect");
4323     // Every item we build from now until we descent into an out of flow that
4324     // does have saved out of flow data should be invisible. This state gets
4325     // restored when AutoBuildingDisplayList gets out of scope.
4326     aBuilder->SetBuildingInvisibleItems(true);
4327 
4328     // If we have nested out-of-flow frames and the outer one isn't visible
4329     // then we won't have stored clip data for it. We can just clear the clip
4330     // instead since we know we won't render anything, and the inner out-of-flow
4331     // frame will setup the correct clip for itself.
4332     clipState.SetClipChainForContainingBlockDescendants(nullptr);
4333   }
4334 
4335   // Setup clipping for the parent's overflow:clip,
4336   // or overflow:hidden on elements that don't support scrolling (and therefore
4337   // don't create nsHTML/XULScrollFrame). This clipping needs to not clip
4338   // anything directly rendered by the parent, only the rendering of its
4339   // children.
4340   // Don't use overflowClip to restrict the dirty rect, since some of the
4341   // descendants may not be clipped by it. Even if we end up with unnecessary
4342   // display items, they'll be pruned during ComputeVisibility.
4343   //
4344   // FIXME(emilio): Why can't we handle this more similarly to `clip` (on the
4345   // parent, rather than on the children)? Would ClipContentDescendants do what
4346   // we want?
4347   if (overflowClipAxes != PhysicalAxes::None) {
4348     ApplyOverflowClipping(aBuilder, parent, overflowClipAxes, clipState);
4349     awayFromCommonPath = true;
4350   }
4351 
4352   nsDisplayList list(aBuilder);
4353   nsDisplayList extraPositionedDescendants(aBuilder);
4354   const ActiveScrolledRoot* wrapListASR;
4355   bool builtContainerItem = false;
4356   if (isStackingContext) {
4357     // True stacking context.
4358     // For stacking contexts, BuildDisplayListForStackingContext handles
4359     // clipping and MarkAbsoluteFramesForDisplayList.
4360     nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
4361     child->BuildDisplayListForStackingContext(aBuilder, &list,
4362                                               &builtContainerItem);
4363     wrapListASR = contASRTracker.GetContainerASR();
4364     if (!aBuilder->IsReusingStackingContextItems() &&
4365         aBuilder->GetCaretFrame() == child) {
4366       builtContainerItem = false;
4367     }
4368   } else {
4369     Maybe<nsRect> clipPropClip =
4370         child->GetClipPropClipRect(disp, effects, child->GetSize());
4371     if (clipPropClip) {
4372       aBuilder->IntersectVisibleRect(*clipPropClip);
4373       aBuilder->IntersectDirtyRect(*clipPropClip);
4374       clipState.ClipContentDescendants(*clipPropClip +
4375                                        aBuilder->ToReferenceFrame(child));
4376       awayFromCommonPath = true;
4377     }
4378 
4379     child->MarkAbsoluteFramesForDisplayList(aBuilder);
4380     child->SetBuiltDisplayList(true);
4381 
4382     if (!awayFromCommonPath &&
4383         // Some SVG frames might change opacity without invalidating the frame,
4384         // so exclude them from the fast-path.
4385         !child->IsFrameOfType(nsIFrame::eSVG)) {
4386       // The shortcut is available for the child for next time.
4387       child->AddStateBits(NS_FRAME_SIMPLE_DISPLAYLIST);
4388     }
4389 
4390     if (!pseudoStackingContext) {
4391       // THIS IS THE COMMON CASE.
4392       // Not a pseudo or real stacking context. Do the simple thing and
4393       // return early.
4394       aBuilder->AdjustWindowDraggingRegion(child);
4395       aBuilder->Check();
4396       child->BuildDisplayList(aBuilder, aLists);
4397       aBuilder->Check();
4398       aBuilder->DisplayCaret(child, aLists.Outlines());
4399 #ifdef DEBUG
4400       DisplayDebugBorders(aBuilder, child, aLists);
4401 #endif
4402       return;
4403     }
4404 
4405     // A pseudo-stacking context (e.g., a positioned element with z-index auto).
4406     // We allow positioned descendants of the child to escape to our parent
4407     // stacking context's positioned descendant list, because they might be
4408     // z-index:non-auto
4409     nsDisplayListCollection pseudoStack(aBuilder);
4410 
4411     aBuilder->AdjustWindowDraggingRegion(child);
4412     nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
4413     aBuilder->Check();
4414     child->BuildDisplayList(aBuilder, pseudoStack);
4415     aBuilder->Check();
4416     if (aBuilder->DisplayCaret(child, pseudoStack.Outlines())) {
4417       builtContainerItem = false;
4418     }
4419     wrapListASR = contASRTracker.GetContainerASR();
4420 
4421     list.AppendToTop(pseudoStack.BorderBackground());
4422     list.AppendToTop(pseudoStack.BlockBorderBackgrounds());
4423     list.AppendToTop(pseudoStack.Floats());
4424     list.AppendToTop(pseudoStack.Content());
4425     list.AppendToTop(pseudoStack.Outlines());
4426     extraPositionedDescendants.AppendToTop(pseudoStack.PositionedDescendants());
4427 #ifdef DEBUG
4428     DisplayDebugBorders(aBuilder, child, aLists);
4429 #endif
4430   }
4431 
4432   buildingForChild.RestoreBuildingInvisibleItemsValue();
4433 
4434   if (!list.IsEmpty()) {
4435     if (isPositioned || isStackingContext) {
4436       // Genuine stacking contexts, and positioned pseudo-stacking-contexts,
4437       // go in this level.
4438       nsDisplayItem* item = WrapInWrapList(aBuilder, child, &list, wrapListASR,
4439                                            builtContainerItem);
4440       if (isSVG) {
4441         aLists.Content()->AppendToTop(item);
4442       } else {
4443         aLists.PositionedDescendants()->AppendToTop(item);
4444       }
4445     } else if (!isSVG && disp->IsFloating(child)) {
4446       aLists.Floats()->AppendToTop(
4447           WrapInWrapList(aBuilder, child, &list, wrapListASR));
4448     } else {
4449       aLists.Content()->AppendToTop(&list);
4450     }
4451   }
4452   // We delay placing the positioned descendants of positioned frames to here,
4453   // because in the absence of z-index this is the correct order for them.
4454   // This doesn't affect correctness because the positioned descendants list
4455   // is sorted by z-order and content in BuildDisplayListForStackingContext,
4456   // but it means that sort routine needs to do less work.
4457   aLists.PositionedDescendants()->AppendToTop(&extraPositionedDescendants);
4458 }
4459 
MarkAbsoluteFramesForDisplayList(nsDisplayListBuilder * aBuilder)4460 void nsIFrame::MarkAbsoluteFramesForDisplayList(
4461     nsDisplayListBuilder* aBuilder) {
4462   if (IsAbsoluteContainer()) {
4463     aBuilder->MarkFramesForDisplayList(
4464         this, GetAbsoluteContainingBlock()->GetChildList());
4465   }
4466 }
4467 
GetContentForEvent(WidgetEvent * aEvent,nsIContent ** aContent)4468 nsresult nsIFrame::GetContentForEvent(WidgetEvent* aEvent,
4469                                       nsIContent** aContent) {
4470   nsIFrame* f = nsLayoutUtils::GetNonGeneratedAncestor(this);
4471   *aContent = f->GetContent();
4472   NS_IF_ADDREF(*aContent);
4473   return NS_OK;
4474 }
4475 
FireDOMEvent(const nsAString & aDOMEventName,nsIContent * aContent)4476 void nsIFrame::FireDOMEvent(const nsAString& aDOMEventName,
4477                             nsIContent* aContent) {
4478   nsIContent* target = aContent ? aContent : GetContent();
4479 
4480   if (target) {
4481     RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(
4482         target, aDOMEventName, CanBubble::eYes, ChromeOnlyDispatch::eNo);
4483     DebugOnly<nsresult> rv = asyncDispatcher->PostDOMEvent();
4484     NS_ASSERTION(NS_SUCCEEDED(rv), "AsyncEventDispatcher failed to dispatch");
4485   }
4486 }
4487 
HandleEvent(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus)4488 nsresult nsIFrame::HandleEvent(nsPresContext* aPresContext,
4489                                WidgetGUIEvent* aEvent,
4490                                nsEventStatus* aEventStatus) {
4491   if (aEvent->mMessage == eMouseMove) {
4492     // XXX If the second argument of HandleDrag() is WidgetMouseEvent,
4493     //     the implementation becomes simpler.
4494     return HandleDrag(aPresContext, aEvent, aEventStatus);
4495   }
4496 
4497   if ((aEvent->mClass == eMouseEventClass &&
4498        aEvent->AsMouseEvent()->mButton == MouseButton::ePrimary) ||
4499       aEvent->mClass == eTouchEventClass) {
4500     if (aEvent->mMessage == eMouseDown || aEvent->mMessage == eTouchStart) {
4501       HandlePress(aPresContext, aEvent, aEventStatus);
4502     } else if (aEvent->mMessage == eMouseUp || aEvent->mMessage == eTouchEnd) {
4503       HandleRelease(aPresContext, aEvent, aEventStatus);
4504     }
4505     return NS_OK;
4506   }
4507 
4508   // When middle button is down, we need to just move selection and focus at
4509   // the clicked point.  Note that even if middle click paste is not enabled,
4510   // Chrome moves selection at middle mouse button down.  So, we should follow
4511   // the behavior for the compatibility.
4512   if (aEvent->mMessage == eMouseDown) {
4513     WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
4514     if (mouseEvent && mouseEvent->mButton == MouseButton::eMiddle) {
4515       if (*aEventStatus == nsEventStatus_eConsumeNoDefault) {
4516         return NS_OK;
4517       }
4518       return MoveCaretToEventPoint(aPresContext, mouseEvent, aEventStatus);
4519     }
4520   }
4521 
4522   return NS_OK;
4523 }
4524 
GetDataForTableSelection(const nsFrameSelection * aFrameSelection,mozilla::PresShell * aPresShell,WidgetMouseEvent * aMouseEvent,nsIContent ** aParentContent,int32_t * aContentOffset,TableSelectionMode * aTarget)4525 nsresult nsIFrame::GetDataForTableSelection(
4526     const nsFrameSelection* aFrameSelection, mozilla::PresShell* aPresShell,
4527     WidgetMouseEvent* aMouseEvent, nsIContent** aParentContent,
4528     int32_t* aContentOffset, TableSelectionMode* aTarget) {
4529   if (!aFrameSelection || !aPresShell || !aMouseEvent || !aParentContent ||
4530       !aContentOffset || !aTarget)
4531     return NS_ERROR_NULL_POINTER;
4532 
4533   *aParentContent = nullptr;
4534   *aContentOffset = 0;
4535   *aTarget = TableSelectionMode::None;
4536 
4537   int16_t displaySelection = aPresShell->GetSelectionFlags();
4538 
4539   bool selectingTableCells = aFrameSelection->IsInTableSelectionMode();
4540 
4541   // DISPLAY_ALL means we're in an editor.
4542   // If already in cell selection mode,
4543   //  continue selecting with mouse drag or end on mouse up,
4544   //  or when using shift key to extend block of cells
4545   //  (Mouse down does normal selection unless Ctrl/Cmd is pressed)
4546   bool doTableSelection =
4547       displaySelection == nsISelectionDisplay::DISPLAY_ALL &&
4548       selectingTableCells &&
4549       (aMouseEvent->mMessage == eMouseMove ||
4550        (aMouseEvent->mMessage == eMouseUp &&
4551         aMouseEvent->mButton == MouseButton::ePrimary) ||
4552        aMouseEvent->IsShift());
4553 
4554   if (!doTableSelection) {
4555     // In Browser, special 'table selection' key must be pressed for table
4556     // selection or when just Shift is pressed and we're already in table/cell
4557     // selection mode
4558 #ifdef XP_MACOSX
4559     doTableSelection = aMouseEvent->IsMeta() ||
4560                        (aMouseEvent->IsShift() && selectingTableCells);
4561 #else
4562     doTableSelection = aMouseEvent->IsControl() ||
4563                        (aMouseEvent->IsShift() && selectingTableCells);
4564 #endif
4565   }
4566   if (!doTableSelection) return NS_OK;
4567 
4568   // Get the cell frame or table frame (or parent) of the current content node
4569   nsIFrame* frame = this;
4570   bool foundCell = false;
4571   bool foundTable = false;
4572 
4573   // Get the limiting node to stop parent frame search
4574   nsIContent* limiter = aFrameSelection->GetLimiter();
4575 
4576   // If our content node is an ancestor of the limiting node,
4577   // we should stop the search right now.
4578   if (limiter && limiter->IsInclusiveDescendantOf(GetContent())) return NS_OK;
4579 
4580   // We don't initiate row/col selection from here now,
4581   //  but we may in future
4582   // bool selectColumn = false;
4583   // bool selectRow = false;
4584 
4585   while (frame) {
4586     // Check for a table cell by querying to a known CellFrame interface
4587     nsITableCellLayout* cellElement = do_QueryFrame(frame);
4588     if (cellElement) {
4589       foundCell = true;
4590       // TODO: If we want to use proximity to top or left border
4591       //      for row and column selection, this is the place to do it
4592       break;
4593     } else {
4594       // If not a cell, check for table
4595       // This will happen when starting frame is the table or child of a table,
4596       //  such as a row (we were inbetween cells or in table border)
4597       nsTableWrapperFrame* tableFrame = do_QueryFrame(frame);
4598       if (tableFrame) {
4599         foundTable = true;
4600         // TODO: How can we select row when along left table edge
4601         //  or select column when along top edge?
4602         break;
4603       } else {
4604         frame = frame->GetParent();
4605         // Stop if we have hit the selection's limiting content node
4606         if (frame && frame->GetContent() == limiter) break;
4607       }
4608     }
4609   }
4610   // We aren't in a cell or table
4611   if (!foundCell && !foundTable) return NS_OK;
4612 
4613   nsIContent* tableOrCellContent = frame->GetContent();
4614   if (!tableOrCellContent) return NS_ERROR_FAILURE;
4615 
4616   nsCOMPtr<nsIContent> parentContent = tableOrCellContent->GetParent();
4617   if (!parentContent) return NS_ERROR_FAILURE;
4618 
4619   const int32_t offset =
4620       parentContent->ComputeIndexOf_Deprecated(tableOrCellContent);
4621   // Not likely?
4622   if (offset < 0) {
4623     return NS_ERROR_FAILURE;
4624   }
4625 
4626   // Everything is OK -- set the return values
4627   parentContent.forget(aParentContent);
4628 
4629   *aContentOffset = offset;
4630 
4631 #if 0
4632   if (selectRow)
4633     *aTarget = TableSelectionMode::Row;
4634   else if (selectColumn)
4635     *aTarget = TableSelectionMode::Column;
4636   else
4637 #endif
4638   if (foundCell) {
4639     *aTarget = TableSelectionMode::Cell;
4640   } else if (foundTable) {
4641     *aTarget = TableSelectionMode::Table;
4642   }
4643 
4644   return NS_OK;
4645 }
4646 
IsEditingHost(const nsIFrame * aFrame)4647 static bool IsEditingHost(const nsIFrame* aFrame) {
4648   auto* element = nsGenericHTMLElement::FromNodeOrNull(aFrame->GetContent());
4649   return element && element->IsEditableRoot();
4650 }
4651 
IsTopmostModalDialog(const nsIFrame * aFrame)4652 static bool IsTopmostModalDialog(const nsIFrame* aFrame) {
4653   auto* element = Element::FromNodeOrNull(aFrame->GetContent());
4654   return element &&
4655          element->State().HasState(NS_EVENT_STATE_TOPMOST_MODAL_DIALOG);
4656 }
4657 
UsedUserSelect(const nsIFrame * aFrame)4658 static StyleUserSelect UsedUserSelect(const nsIFrame* aFrame) {
4659   if (aFrame->IsGeneratedContentFrame()) {
4660     return StyleUserSelect::None;
4661   }
4662 
4663   // Per https://drafts.csswg.org/css-ui-4/#content-selection:
4664   //
4665   // The computed value is the specified value, except:
4666   //
4667   //   1 - on editable elements where the computed value is always 'contain'
4668   //       regardless of the specified value.
4669   //   2 - when the specified value is auto, which computes to one of the other
4670   //       values [...]
4671   //
4672   // See https://github.com/w3c/csswg-drafts/issues/3344 to see why we do this
4673   // at used-value time instead of at computed-value time.
4674   //
4675   // Also, we check for auto first to allow explicitly overriding the value for
4676   // the editing host.
4677   auto style = aFrame->Style()->UserSelect();
4678   if (style != StyleUserSelect::Auto) {
4679     return style;
4680   }
4681 
4682   if (aFrame->IsTextInputFrame() || IsEditingHost(aFrame) ||
4683       IsTopmostModalDialog(aFrame)) {
4684     // We don't implement 'contain' itself, but we make 'text' behave as
4685     // 'contain' for contenteditable and <input> / <textarea> elements anyway so
4686     // this is ok.
4687     //
4688     // Topmost modal dialogs need to behave like `text` too, because they're
4689     // supposed to be selectable even if their ancestors are inert.
4690     return StyleUserSelect::Text;
4691   }
4692 
4693   auto* parent = nsLayoutUtils::GetParentOrPlaceholderFor(aFrame);
4694   return parent ? UsedUserSelect(parent) : StyleUserSelect::Text;
4695 }
4696 
IsSelectable(StyleUserSelect * aSelectStyle) const4697 bool nsIFrame::IsSelectable(StyleUserSelect* aSelectStyle) const {
4698   auto style = UsedUserSelect(this);
4699   if (aSelectStyle) {
4700     *aSelectStyle = style;
4701   }
4702   return style != StyleUserSelect::None;
4703 }
4704 
ShouldHaveLineIfEmpty() const4705 bool nsIFrame::ShouldHaveLineIfEmpty() const {
4706   if (Style()->IsPseudoOrAnonBox() &&
4707       Style()->GetPseudoType() != PseudoStyleType::scrolledContent) {
4708     return false;
4709   }
4710   return IsEditingHost(this);
4711 }
4712 
4713 /**
4714  * Handles the Mouse Press Event for the frame
4715  */
4716 NS_IMETHODIMP
HandlePress(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus)4717 nsIFrame::HandlePress(nsPresContext* aPresContext, WidgetGUIEvent* aEvent,
4718                       nsEventStatus* aEventStatus) {
4719   NS_ENSURE_ARG_POINTER(aEventStatus);
4720   if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
4721     return NS_OK;
4722   }
4723 
4724   NS_ENSURE_ARG_POINTER(aEvent);
4725   if (aEvent->mClass == eTouchEventClass) {
4726     return NS_OK;
4727   }
4728 
4729   return MoveCaretToEventPoint(aPresContext, aEvent->AsMouseEvent(),
4730                                aEventStatus);
4731 }
4732 
MoveCaretToEventPoint(nsPresContext * aPresContext,WidgetMouseEvent * aMouseEvent,nsEventStatus * aEventStatus)4733 nsresult nsIFrame::MoveCaretToEventPoint(nsPresContext* aPresContext,
4734                                          WidgetMouseEvent* aMouseEvent,
4735                                          nsEventStatus* aEventStatus) {
4736   MOZ_ASSERT(aPresContext);
4737   MOZ_ASSERT(aMouseEvent);
4738   MOZ_ASSERT(aMouseEvent->mMessage == eMouseDown);
4739   MOZ_ASSERT(aMouseEvent->mButton == MouseButton::ePrimary ||
4740              aMouseEvent->mButton == MouseButton::eMiddle);
4741   MOZ_ASSERT(aEventStatus);
4742   MOZ_ASSERT(nsEventStatus_eConsumeNoDefault != *aEventStatus);
4743 
4744   mozilla::PresShell* presShell = aPresContext->GetPresShell();
4745   if (!presShell) {
4746     return NS_ERROR_FAILURE;
4747   }
4748 
4749   // We often get out of sync state issues with mousedown events that
4750   // get interrupted by alerts/dialogs.
4751   // Check with the ESM to see if we should process this one
4752   if (!aPresContext->EventStateManager()->EventStatusOK(aMouseEvent)) {
4753     return NS_OK;
4754   }
4755 
4756   const nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(
4757       aMouseEvent, RelativeTo{this});
4758 
4759   // When not using `alt`, and clicking on a draggable, but non-editable
4760   // element, don't do anything, and let d&d handle the event.
4761   //
4762   // See bug 48876, bug 388659 and bug 55921 for context here.
4763   //
4764   // FIXME(emilio): The .Contains(pt) check looks a bit fishy. When would it be
4765   // false given we're the event target? If it is needed, why not checking the
4766   // actual draggable node rect instead?
4767   if (!aMouseEvent->IsAlt() && GetRectRelativeToSelf().Contains(pt)) {
4768     for (nsIContent* content = mContent; content;
4769          content = content->GetFlattenedTreeParent()) {
4770       if (nsContentUtils::ContentIsDraggable(content) &&
4771           !content->IsEditable()) {
4772         return NS_OK;
4773       }
4774     }
4775   }
4776 
4777   // If we are in Navigator and the click is in a draggable node, we don't want
4778   // to start selection because we don't want to interfere with a potential
4779   // drag of said node and steal all its glory.
4780   const bool isEditor =
4781       presShell->GetSelectionFlags() == nsISelectionDisplay::DISPLAY_ALL;
4782 
4783   // Don't do something if it's middle button down event.
4784   const bool isPrimaryButtonDown =
4785       aMouseEvent->mButton == MouseButton::ePrimary;
4786 
4787   // check whether style allows selection
4788   // if not, don't tell selection the mouse event even occurred.
4789   StyleUserSelect selectStyle;
4790   // check for select: none
4791   if (!IsSelectable(&selectStyle)) {
4792     return NS_OK;
4793   }
4794 
4795   if (isPrimaryButtonDown) {
4796     // If the mouse is dragged outside the nearest enclosing scrollable area
4797     // while making a selection, the area will be scrolled. To do this, capture
4798     // the mouse on the nearest scrollable frame. If there isn't a scrollable
4799     // frame, or something else is already capturing the mouse, there's no
4800     // reason to capture.
4801     if (!PresShell::GetCapturingContent()) {
4802       nsIScrollableFrame* scrollFrame =
4803           nsLayoutUtils::GetNearestScrollableFrame(
4804               this, nsLayoutUtils::SCROLLABLE_SAME_DOC |
4805                         nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
4806       if (scrollFrame) {
4807         nsIFrame* capturingFrame = do_QueryFrame(scrollFrame);
4808         PresShell::SetCapturingContent(capturingFrame->GetContent(),
4809                                        CaptureFlags::IgnoreAllowedState);
4810       }
4811     }
4812   }
4813 
4814   // XXX This is screwy; it really should use the selection frame, not the
4815   // event frame
4816   const nsFrameSelection* frameselection =
4817       selectStyle == StyleUserSelect::Text ? GetConstFrameSelection()
4818                                            : presShell->ConstFrameSelection();
4819 
4820   if (!frameselection || frameselection->GetDisplaySelection() ==
4821                              nsISelectionController::SELECTION_OFF) {
4822     return NS_OK;  // nothing to do we cannot affect selection from here
4823   }
4824 
4825 #ifdef XP_MACOSX
4826   // If Control key is pressed on macOS, it should be treated as right click.
4827   // So, don't change selection.
4828   if (aMouseEvent->IsControl()) {
4829     return NS_OK;
4830   }
4831   const bool control = aMouseEvent->IsMeta();
4832 #else
4833   const bool control = aMouseEvent->IsControl();
4834 #endif
4835 
4836   RefPtr<nsFrameSelection> fc = const_cast<nsFrameSelection*>(frameselection);
4837   if (isPrimaryButtonDown && aMouseEvent->mClickCount > 1) {
4838     // These methods aren't const but can't actually delete anything,
4839     // so no need for AutoWeakFrame.
4840     fc->SetDragState(true);
4841     return HandleMultiplePress(aPresContext, aMouseEvent, aEventStatus,
4842                                control);
4843   }
4844 
4845   ContentOffsets offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN);
4846 
4847   if (!offsets.content) {
4848     return NS_ERROR_FAILURE;
4849   }
4850 
4851   if (aMouseEvent->mMessage == eMouseDown &&
4852       aMouseEvent->mButton == MouseButton::eMiddle &&
4853       !offsets.content->IsEditable()) {
4854     // However, some users don't like the Chrome compatible behavior of
4855     // middle mouse click.  They want to keep selection after starting
4856     // autoscroll.  However, the selection change is important for middle
4857     // mouse past.  Therefore, we should allow users to take the traditional
4858     // behavior back by themselves unless middle click paste is enabled or
4859     // autoscrolling is disabled.
4860     if (!Preferences::GetBool("middlemouse.paste", false) &&
4861         Preferences::GetBool("general.autoScroll", false) &&
4862         Preferences::GetBool("general.autoscroll.prevent_to_collapse_selection_"
4863                              "by_middle_mouse_down",
4864                              false)) {
4865       return NS_OK;
4866     }
4867   }
4868 
4869   if (isPrimaryButtonDown) {
4870     // Let Ctrl/Cmd + left mouse down do table selection instead of drag
4871     // initiation.
4872     nsCOMPtr<nsIContent> parentContent;
4873     int32_t contentOffset;
4874     TableSelectionMode target;
4875     nsresult rv = GetDataForTableSelection(
4876         frameselection, presShell, aMouseEvent, getter_AddRefs(parentContent),
4877         &contentOffset, &target);
4878     if (NS_SUCCEEDED(rv) && parentContent) {
4879       fc->SetDragState(true);
4880       return fc->HandleTableSelection(parentContent, contentOffset, target,
4881                                       aMouseEvent);
4882     }
4883   }
4884 
4885   fc->SetDelayedCaretData(0);
4886 
4887   if (isPrimaryButtonDown) {
4888     // Check if any part of this frame is selected, and if the user clicked
4889     // inside the selected region, and if it's the left button. If so, we delay
4890     // starting a new selection since the user may be trying to drag the
4891     // selected region to some other app.
4892 
4893     if (GetContent() && GetContent()->IsMaybeSelected()) {
4894       bool inSelection = false;
4895       UniquePtr<SelectionDetails> details = frameselection->LookUpSelection(
4896           offsets.content, 0, offsets.EndOffset(), false);
4897 
4898       //
4899       // If there are any details, check to see if the user clicked
4900       // within any selected region of the frame.
4901       //
4902 
4903       for (SelectionDetails* curDetail = details.get(); curDetail;
4904            curDetail = curDetail->mNext.get()) {
4905         //
4906         // If the user clicked inside a selection, then just
4907         // return without doing anything. We will handle placing
4908         // the caret later on when the mouse is released. We ignore
4909         // the spellcheck, find and url formatting selections.
4910         //
4911         if (curDetail->mSelectionType != SelectionType::eSpellCheck &&
4912             curDetail->mSelectionType != SelectionType::eFind &&
4913             curDetail->mSelectionType != SelectionType::eURLSecondary &&
4914             curDetail->mSelectionType != SelectionType::eURLStrikeout &&
4915             curDetail->mStart <= offsets.StartOffset() &&
4916             offsets.EndOffset() <= curDetail->mEnd) {
4917           inSelection = true;
4918         }
4919       }
4920 
4921       if (inSelection) {
4922         fc->SetDragState(false);
4923         fc->SetDelayedCaretData(aMouseEvent);
4924         return NS_OK;
4925       }
4926     }
4927 
4928     fc->SetDragState(true);
4929   }
4930 
4931   // Do not touch any nsFrame members after this point without adding
4932   // weakFrame checks.
4933   const nsFrameSelection::FocusMode focusMode = [&]() {
4934     // If "Shift" and "Ctrl" are both pressed, "Shift" is given precedence. This
4935     // mimics the old behaviour.
4936     if (aMouseEvent->IsShift()) {
4937       // If clicked in a link when focused content is editable, we should
4938       // collapse selection in the link for compatibility with Blink.
4939       if (isEditor) {
4940         nsCOMPtr<nsIURI> uri;
4941         for (Element* element : mContent->InclusiveAncestorsOfType<Element>()) {
4942           if (element->IsLink(getter_AddRefs(uri))) {
4943             return nsFrameSelection::FocusMode::kCollapseToNewPoint;
4944           }
4945         }
4946       }
4947       return nsFrameSelection::FocusMode::kExtendSelection;
4948     }
4949 
4950     if (isPrimaryButtonDown && control) {
4951       return nsFrameSelection::FocusMode::kMultiRangeSelection;
4952     }
4953 
4954     return nsFrameSelection::FocusMode::kCollapseToNewPoint;
4955   }();
4956 
4957   nsresult rv = fc->HandleClick(
4958       MOZ_KnownLive(offsets.content) /* bug 1636889 */, offsets.StartOffset(),
4959       offsets.EndOffset(), focusMode, offsets.associate);
4960   if (NS_FAILED(rv)) {
4961     return rv;
4962   }
4963 
4964   // We don't handle mouse button up if it's middle button.
4965   if (isPrimaryButtonDown && offsets.offset != offsets.secondaryOffset) {
4966     fc->MaintainSelection();
4967   }
4968 
4969   if (isPrimaryButtonDown && isEditor && !aMouseEvent->IsShift() &&
4970       (offsets.EndOffset() - offsets.StartOffset()) == 1) {
4971     // A single node is selected and we aren't extending an existing selection,
4972     // which means the user clicked directly on an object (either
4973     // `user-select: all` or a non-text node without children). Therefore,
4974     // disable selection extension during mouse moves.
4975     // XXX This is a bit hacky; shouldn't editor be able to deal with this?
4976     fc->SetDragState(false);
4977   }
4978 
4979   return NS_OK;
4980 }
4981 
SelectByTypeAtPoint(nsPresContext * aPresContext,const nsPoint & aPoint,nsSelectionAmount aBeginAmountType,nsSelectionAmount aEndAmountType,uint32_t aSelectFlags)4982 nsresult nsIFrame::SelectByTypeAtPoint(nsPresContext* aPresContext,
4983                                        const nsPoint& aPoint,
4984                                        nsSelectionAmount aBeginAmountType,
4985                                        nsSelectionAmount aEndAmountType,
4986                                        uint32_t aSelectFlags) {
4987   NS_ENSURE_ARG_POINTER(aPresContext);
4988 
4989   // No point in selecting if selection is turned off
4990   if (DetermineDisplaySelection() == nsISelectionController::SELECTION_OFF) {
4991     return NS_OK;
4992   }
4993 
4994   ContentOffsets offsets = GetContentOffsetsFromPoint(aPoint, SKIP_HIDDEN);
4995   if (!offsets.content) {
4996     return NS_ERROR_FAILURE;
4997   }
4998 
4999   int32_t offset;
5000   nsIFrame* frame = nsFrameSelection::GetFrameForNodeOffset(
5001       offsets.content, offsets.offset, offsets.associate, &offset);
5002   if (!frame) {
5003     return NS_ERROR_FAILURE;
5004   }
5005   return frame->PeekBackwardAndForward(aBeginAmountType, aEndAmountType, offset,
5006                                        aBeginAmountType != eSelectWord,
5007                                        aSelectFlags);
5008 }
5009 
5010 /**
5011  * Multiple Mouse Press -- line or paragraph selection -- for the frame.
5012  * Wouldn't it be nice if this didn't have to be hardwired into Frame code?
5013  */
5014 NS_IMETHODIMP
HandleMultiplePress(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus,bool aControlHeld)5015 nsIFrame::HandleMultiplePress(nsPresContext* aPresContext,
5016                               WidgetGUIEvent* aEvent,
5017                               nsEventStatus* aEventStatus, bool aControlHeld) {
5018   NS_ENSURE_ARG_POINTER(aEvent);
5019   NS_ENSURE_ARG_POINTER(aEventStatus);
5020 
5021   if (nsEventStatus_eConsumeNoDefault == *aEventStatus ||
5022       DetermineDisplaySelection() == nsISelectionController::SELECTION_OFF) {
5023     return NS_OK;
5024   }
5025 
5026   // Find out whether we're doing line or paragraph selection.
5027   // If browser.triple_click_selects_paragraph is true, triple-click selects
5028   // paragraph. Otherwise, triple-click selects line, and quadruple-click
5029   // selects paragraph (on platforms that support quadruple-click).
5030   nsSelectionAmount beginAmount, endAmount;
5031   WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
5032   if (!mouseEvent) {
5033     return NS_OK;
5034   }
5035 
5036   if (mouseEvent->mClickCount == 4) {
5037     beginAmount = endAmount = eSelectParagraph;
5038   } else if (mouseEvent->mClickCount == 3) {
5039     if (Preferences::GetBool("browser.triple_click_selects_paragraph")) {
5040       beginAmount = endAmount = eSelectParagraph;
5041     } else {
5042       beginAmount = eSelectBeginLine;
5043       endAmount = eSelectEndLine;
5044     }
5045   } else if (mouseEvent->mClickCount == 2) {
5046     // We only want inline frames; PeekBackwardAndForward dislikes blocks
5047     beginAmount = endAmount = eSelectWord;
5048   } else {
5049     return NS_OK;
5050   }
5051 
5052   nsPoint relPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(
5053       mouseEvent, RelativeTo{this});
5054   return SelectByTypeAtPoint(aPresContext, relPoint, beginAmount, endAmount,
5055                              (aControlHeld ? SELECT_ACCUMULATE : 0));
5056 }
5057 
PeekBackwardAndForward(nsSelectionAmount aAmountBack,nsSelectionAmount aAmountForward,int32_t aStartPos,bool aJumpLines,uint32_t aSelectFlags)5058 nsresult nsIFrame::PeekBackwardAndForward(nsSelectionAmount aAmountBack,
5059                                           nsSelectionAmount aAmountForward,
5060                                           int32_t aStartPos, bool aJumpLines,
5061                                           uint32_t aSelectFlags) {
5062   nsIFrame* baseFrame = this;
5063   int32_t baseOffset = aStartPos;
5064   nsresult rv;
5065 
5066   if (aAmountBack == eSelectWord) {
5067     // To avoid selecting the previous word when at start of word,
5068     // first move one character forward.
5069     nsPeekOffsetStruct pos(eSelectCharacter, eDirNext, aStartPos, nsPoint(0, 0),
5070                            aJumpLines,
5071                            true,  // limit on scrolled views
5072                            false, false, false);
5073     rv = PeekOffset(&pos);
5074     if (NS_SUCCEEDED(rv)) {
5075       baseFrame = pos.mResultFrame;
5076       baseOffset = pos.mContentOffset;
5077     }
5078   }
5079 
5080   // Search backward for a boundary.
5081   nsPeekOffsetStruct startpos(aAmountBack, eDirPrevious, baseOffset,
5082                               nsPoint(0, 0), aJumpLines,
5083                               true,  // limit on scrolled views
5084                               false, false, false);
5085   rv = baseFrame->PeekOffset(&startpos);
5086   if (NS_FAILED(rv)) {
5087     return rv;
5088   }
5089 
5090   // If the backward search stayed within the same frame, search forward from
5091   // that position for the end boundary; but if it crossed out to a sibling or
5092   // ancestor, start from the original position.
5093   if (startpos.mResultFrame == baseFrame) {
5094     baseOffset = startpos.mContentOffset;
5095   } else {
5096     baseFrame = this;
5097     baseOffset = aStartPos;
5098   }
5099 
5100   nsPeekOffsetStruct endpos(aAmountForward, eDirNext, baseOffset, nsPoint(0, 0),
5101                             aJumpLines,
5102                             true,  // limit on scrolled views
5103                             false, false, false);
5104   rv = baseFrame->PeekOffset(&endpos);
5105   if (NS_FAILED(rv)) {
5106     return rv;
5107   }
5108 
5109   // Keep frameSelection alive.
5110   RefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
5111 
5112   const nsFrameSelection::FocusMode focusMode =
5113       (aSelectFlags & SELECT_ACCUMULATE)
5114           ? nsFrameSelection::FocusMode::kMultiRangeSelection
5115           : nsFrameSelection::FocusMode::kCollapseToNewPoint;
5116   rv = frameSelection->HandleClick(
5117       MOZ_KnownLive(startpos.mResultContent) /* bug 1636889 */,
5118       startpos.mContentOffset, startpos.mContentOffset, focusMode,
5119       CARET_ASSOCIATE_AFTER);
5120   if (NS_FAILED(rv)) {
5121     return rv;
5122   }
5123 
5124   rv = frameSelection->HandleClick(
5125       MOZ_KnownLive(endpos.mResultContent) /* bug 1636889 */,
5126       endpos.mContentOffset, endpos.mContentOffset,
5127       nsFrameSelection::FocusMode::kExtendSelection, CARET_ASSOCIATE_BEFORE);
5128   if (NS_FAILED(rv)) {
5129     return rv;
5130   }
5131 
5132   // maintain selection
5133   return frameSelection->MaintainSelection(aAmountBack);
5134 }
5135 
HandleDrag(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus)5136 NS_IMETHODIMP nsIFrame::HandleDrag(nsPresContext* aPresContext,
5137                                    WidgetGUIEvent* aEvent,
5138                                    nsEventStatus* aEventStatus) {
5139   MOZ_ASSERT(aEvent->mClass == eMouseEventClass,
5140              "HandleDrag can only handle mouse event");
5141 
5142   NS_ENSURE_ARG_POINTER(aEventStatus);
5143 
5144   RefPtr<nsFrameSelection> frameselection = GetFrameSelection();
5145   if (!frameselection) {
5146     return NS_OK;
5147   }
5148 
5149   bool mouseDown = frameselection->GetDragState();
5150   if (!mouseDown) {
5151     return NS_OK;
5152   }
5153 
5154   nsIFrame* scrollbar =
5155       nsLayoutUtils::GetClosestFrameOfType(this, LayoutFrameType::Scrollbar);
5156   if (!scrollbar) {
5157     // XXX Do we really need to exclude non-selectable content here?
5158     // GetContentOffsetsFromPoint can handle it just fine, although some
5159     // other stuff might not like it.
5160     // NOTE: DetermineDisplaySelection() returns SELECTION_OFF for
5161     // non-selectable frames.
5162     if (DetermineDisplaySelection() == nsISelectionController::SELECTION_OFF) {
5163       return NS_OK;
5164     }
5165   }
5166 
5167   frameselection->StopAutoScrollTimer();
5168 
5169   // Check if we are dragging in a table cell
5170   nsCOMPtr<nsIContent> parentContent;
5171   int32_t contentOffset;
5172   TableSelectionMode target;
5173   WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
5174   mozilla::PresShell* presShell = aPresContext->PresShell();
5175   nsresult result;
5176   result = GetDataForTableSelection(frameselection, presShell, mouseEvent,
5177                                     getter_AddRefs(parentContent),
5178                                     &contentOffset, &target);
5179 
5180   AutoWeakFrame weakThis = this;
5181   if (NS_SUCCEEDED(result) && parentContent) {
5182     result = frameselection->HandleTableSelection(parentContent, contentOffset,
5183                                                   target, mouseEvent);
5184     if (NS_WARN_IF(NS_FAILED(result))) {
5185       return result;
5186     }
5187   } else {
5188     nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent,
5189                                                               RelativeTo{this});
5190     frameselection->HandleDrag(this, pt);
5191   }
5192 
5193   // The frameselection object notifies selection listeners synchronously above
5194   // which might have killed us.
5195   if (!weakThis.IsAlive()) {
5196     return NS_OK;
5197   }
5198 
5199   // get the nearest scrollframe
5200   nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetNearestScrollableFrame(
5201       this, nsLayoutUtils::SCROLLABLE_SAME_DOC |
5202                 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
5203 
5204   if (scrollFrame) {
5205     nsIFrame* capturingFrame = scrollFrame->GetScrolledFrame();
5206     if (capturingFrame) {
5207       nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(
5208           mouseEvent, RelativeTo{capturingFrame});
5209       frameselection->StartAutoScrollTimer(capturingFrame, pt, 30);
5210     }
5211   }
5212 
5213   return NS_OK;
5214 }
5215 
5216 /**
5217  * This static method handles part of the nsIFrame::HandleRelease in a way
5218  * which doesn't rely on the nsFrame object to stay alive.
5219  */
HandleFrameSelection(nsFrameSelection * aFrameSelection,nsIFrame::ContentOffsets & aOffsets,bool aHandleTableSel,int32_t aContentOffsetForTableSel,TableSelectionMode aTargetForTableSel,nsIContent * aParentContentForTableSel,WidgetGUIEvent * aEvent,const nsEventStatus * aEventStatus)5220 MOZ_CAN_RUN_SCRIPT_BOUNDARY static nsresult HandleFrameSelection(
5221     nsFrameSelection* aFrameSelection, nsIFrame::ContentOffsets& aOffsets,
5222     bool aHandleTableSel, int32_t aContentOffsetForTableSel,
5223     TableSelectionMode aTargetForTableSel,
5224     nsIContent* aParentContentForTableSel, WidgetGUIEvent* aEvent,
5225     const nsEventStatus* aEventStatus) {
5226   if (!aFrameSelection) {
5227     return NS_OK;
5228   }
5229 
5230   nsresult rv = NS_OK;
5231 
5232   if (nsEventStatus_eConsumeNoDefault != *aEventStatus) {
5233     if (!aHandleTableSel) {
5234       if (!aOffsets.content || !aFrameSelection->HasDelayedCaretData()) {
5235         return NS_ERROR_FAILURE;
5236       }
5237 
5238       // We are doing this to simulate what we would have done on HandlePress.
5239       // We didn't do it there to give the user an opportunity to drag
5240       // the text, but since they didn't drag, we want to place the
5241       // caret.
5242       // However, we'll use the mouse position from the release, since:
5243       //  * it's easier
5244       //  * that's the normal click position to use (although really, in
5245       //    the normal case, small movements that don't count as a drag
5246       //    can do selection)
5247       aFrameSelection->SetDragState(true);
5248 
5249       const nsFrameSelection::FocusMode focusMode =
5250           aFrameSelection->IsShiftDownInDelayedCaretData()
5251               ? nsFrameSelection::FocusMode::kExtendSelection
5252               : nsFrameSelection::FocusMode::kCollapseToNewPoint;
5253       rv = aFrameSelection->HandleClick(
5254           MOZ_KnownLive(aOffsets.content) /* bug 1636889 */,
5255           aOffsets.StartOffset(), aOffsets.EndOffset(), focusMode,
5256           aOffsets.associate);
5257       if (NS_FAILED(rv)) {
5258         return rv;
5259       }
5260     } else if (aParentContentForTableSel) {
5261       aFrameSelection->SetDragState(false);
5262       rv = aFrameSelection->HandleTableSelection(
5263           aParentContentForTableSel, aContentOffsetForTableSel,
5264           aTargetForTableSel, aEvent->AsMouseEvent());
5265       if (NS_FAILED(rv)) {
5266         return rv;
5267       }
5268     }
5269     aFrameSelection->SetDelayedCaretData(0);
5270   }
5271 
5272   aFrameSelection->SetDragState(false);
5273   aFrameSelection->StopAutoScrollTimer();
5274 
5275   return NS_OK;
5276 }
5277 
HandleRelease(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus)5278 NS_IMETHODIMP nsIFrame::HandleRelease(nsPresContext* aPresContext,
5279                                       WidgetGUIEvent* aEvent,
5280                                       nsEventStatus* aEventStatus) {
5281   if (aEvent->mClass != eMouseEventClass) {
5282     return NS_OK;
5283   }
5284 
5285   nsIFrame* activeFrame = GetActiveSelectionFrame(aPresContext, this);
5286 
5287   nsCOMPtr<nsIContent> captureContent = PresShell::GetCapturingContent();
5288 
5289   bool selectionOff =
5290       (DetermineDisplaySelection() == nsISelectionController::SELECTION_OFF);
5291 
5292   RefPtr<nsFrameSelection> frameselection;
5293   ContentOffsets offsets;
5294   nsCOMPtr<nsIContent> parentContent;
5295   int32_t contentOffsetForTableSel = 0;
5296   TableSelectionMode targetForTableSel = TableSelectionMode::None;
5297   bool handleTableSelection = true;
5298 
5299   if (!selectionOff) {
5300     frameselection = GetFrameSelection();
5301     if (nsEventStatus_eConsumeNoDefault != *aEventStatus && frameselection) {
5302       // Check if the frameselection recorded the mouse going down.
5303       // If not, the user must have clicked in a part of the selection.
5304       // Place the caret before continuing!
5305 
5306       if (frameselection->MouseDownRecorded()) {
5307         nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(
5308             aEvent, RelativeTo{this});
5309         offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN);
5310         handleTableSelection = false;
5311       } else {
5312         GetDataForTableSelection(frameselection, PresShell(),
5313                                  aEvent->AsMouseEvent(),
5314                                  getter_AddRefs(parentContent),
5315                                  &contentOffsetForTableSel, &targetForTableSel);
5316       }
5317     }
5318   }
5319 
5320   // We might be capturing in some other document and the event just happened to
5321   // trickle down here. Make sure that document's frame selection is notified.
5322   // Note, this may cause the current nsFrame object to be deleted, bug 336592.
5323   RefPtr<nsFrameSelection> frameSelection;
5324   if (activeFrame != this && activeFrame->DetermineDisplaySelection() !=
5325                                  nsISelectionController::SELECTION_OFF) {
5326     frameSelection = activeFrame->GetFrameSelection();
5327   }
5328 
5329   // Also check the selection of the capturing content which might be in a
5330   // different document.
5331   if (!frameSelection && captureContent) {
5332     if (Document* doc = captureContent->GetComposedDoc()) {
5333       mozilla::PresShell* capturingPresShell = doc->GetPresShell();
5334       if (capturingPresShell &&
5335           capturingPresShell != PresContext()->GetPresShell()) {
5336         frameSelection = capturingPresShell->FrameSelection();
5337       }
5338     }
5339   }
5340 
5341   if (frameSelection) {
5342     AutoWeakFrame wf(this);
5343     frameSelection->SetDragState(false);
5344     frameSelection->StopAutoScrollTimer();
5345     if (wf.IsAlive()) {
5346       nsIScrollableFrame* scrollFrame =
5347           nsLayoutUtils::GetNearestScrollableFrame(
5348               this, nsLayoutUtils::SCROLLABLE_SAME_DOC |
5349                         nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
5350       if (scrollFrame) {
5351         // Perform any additional scrolling needed to maintain CSS snap point
5352         // requirements when autoscrolling is over.
5353         scrollFrame->ScrollSnap();
5354       }
5355     }
5356   }
5357 
5358   // Do not call any methods of the current object after this point!!!
5359   // The object is perhaps dead!
5360 
5361   return selectionOff ? NS_OK
5362                       : HandleFrameSelection(
5363                             frameselection, offsets, handleTableSelection,
5364                             contentOffsetForTableSel, targetForTableSel,
5365                             parentContent, aEvent, aEventStatus);
5366 }
5367 
5368 struct MOZ_STACK_CLASS FrameContentRange {
FrameContentRangeFrameContentRange5369   FrameContentRange(nsIContent* aContent, int32_t aStart, int32_t aEnd)
5370       : content(aContent), start(aStart), end(aEnd) {}
5371   nsCOMPtr<nsIContent> content;
5372   int32_t start;
5373   int32_t end;
5374 };
5375 
5376 // Retrieve the content offsets of a frame
GetRangeForFrame(const nsIFrame * aFrame)5377 static FrameContentRange GetRangeForFrame(const nsIFrame* aFrame) {
5378   nsIContent* content = aFrame->GetContent();
5379   if (!content) {
5380     NS_WARNING("Frame has no content");
5381     return FrameContentRange(nullptr, -1, -1);
5382   }
5383 
5384   LayoutFrameType type = aFrame->Type();
5385   if (type == LayoutFrameType::Text) {
5386     auto [offset, offsetEnd] = aFrame->GetOffsets();
5387     return FrameContentRange(content, offset, offsetEnd);
5388   }
5389 
5390   if (type == LayoutFrameType::Br) {
5391     nsIContent* parent = content->GetParent();
5392     const int32_t beginOffset = parent->ComputeIndexOf_Deprecated(content);
5393     return FrameContentRange(parent, beginOffset, beginOffset);
5394   }
5395 
5396   while (content->IsRootOfNativeAnonymousSubtree()) {
5397     content = content->GetParent();
5398   }
5399 
5400   nsIContent* parent = content->GetParent();
5401   if (aFrame->IsBlockOutside() || !parent) {
5402     return FrameContentRange(content, 0, content->GetChildCount());
5403   }
5404 
5405   // TODO(emilio): Revise this in presence of Shadow DOM / display: contents,
5406   // it's likely that we don't want to just walk the light tree, and we need to
5407   // change the representation of FrameContentRange.
5408   const int32_t index = parent->ComputeIndexOf_Deprecated(content);
5409   MOZ_ASSERT(index >= 0);
5410   return FrameContentRange(parent, index, index + 1);
5411 }
5412 
5413 // The FrameTarget represents the closest frame to a point that can be selected
5414 // The frame is the frame represented, frameEdge says whether one end of the
5415 // frame is the result (in which case different handling is needed), and
5416 // afterFrame says which end is represented if frameEdge is true
5417 struct FrameTarget {
operator boolFrameTarget5418   explicit operator bool() const { return !!frame; }
5419 
5420   nsIFrame* frame = nullptr;
5421   bool frameEdge = false;
5422   bool afterFrame = false;
5423 };
5424 
5425 // See function implementation for information
5426 static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame,
5427                                             const nsPoint& aPoint,
5428                                             uint32_t aFlags);
5429 
SelfIsSelectable(nsIFrame * aFrame,uint32_t aFlags)5430 static bool SelfIsSelectable(nsIFrame* aFrame, uint32_t aFlags) {
5431   if ((aFlags & nsIFrame::SKIP_HIDDEN) &&
5432       !aFrame->StyleVisibility()->IsVisible()) {
5433     return false;
5434   }
5435   return !aFrame->IsGeneratedContentFrame() &&
5436          aFrame->Style()->UserSelect() != StyleUserSelect::None;
5437 }
5438 
SelectionDescendToKids(nsIFrame * aFrame)5439 static bool SelectionDescendToKids(nsIFrame* aFrame) {
5440   // If we are only near (not directly over) then don't traverse
5441   // frames with independent selection (e.g. text and list controls, see bug
5442   // 268497).  Note that this prevents any of the users of this method from
5443   // entering form controls.
5444   // XXX We might want some way to allow using the up-arrow to go into a form
5445   // control, but the focus didn't work right anyway; it'd probably be enough
5446   // if the left and right arrows could enter textboxes (which I don't believe
5447   // they can at the moment)
5448   if (aFrame->IsTextInputFrame() || aFrame->IsListControlFrame()) {
5449     MOZ_ASSERT(aFrame->HasAnyStateBits(NS_FRAME_INDEPENDENT_SELECTION));
5450     return false;
5451   }
5452 
5453   // Failure in this assertion means a new type of frame forms the root of an
5454   // NS_FRAME_INDEPENDENT_SELECTION subtree. In such case, the condition above
5455   // should be changed to handle it.
5456   MOZ_ASSERT_IF(
5457       aFrame->HasAnyStateBits(NS_FRAME_INDEPENDENT_SELECTION),
5458       aFrame->GetParent()->HasAnyStateBits(NS_FRAME_INDEPENDENT_SELECTION));
5459 
5460   if (aFrame->IsGeneratedContentFrame()) {
5461     return false;
5462   }
5463 
5464   auto style = aFrame->Style()->UserSelect();
5465   return style != StyleUserSelect::All && style != StyleUserSelect::None;
5466 }
5467 
GetSelectionClosestFrameForChild(nsIFrame * aChild,const nsPoint & aPoint,uint32_t aFlags)5468 static FrameTarget GetSelectionClosestFrameForChild(nsIFrame* aChild,
5469                                                     const nsPoint& aPoint,
5470                                                     uint32_t aFlags) {
5471   nsIFrame* parent = aChild->GetParent();
5472   if (SelectionDescendToKids(aChild)) {
5473     nsPoint pt = aPoint - aChild->GetOffsetTo(parent);
5474     return GetSelectionClosestFrame(aChild, pt, aFlags);
5475   }
5476   return FrameTarget{aChild, false, false};
5477 }
5478 
5479 // When the cursor needs to be at the beginning of a block, it shouldn't be
5480 // before the first child.  A click on a block whose first child is a block
5481 // should put the cursor in the child.  The cursor shouldn't be between the
5482 // blocks, because that's not where it's expected.
5483 // Note that this method is guaranteed to succeed.
DrillDownToSelectionFrame(nsIFrame * aFrame,bool aEndFrame,uint32_t aFlags)5484 static FrameTarget DrillDownToSelectionFrame(nsIFrame* aFrame, bool aEndFrame,
5485                                              uint32_t aFlags) {
5486   if (SelectionDescendToKids(aFrame)) {
5487     nsIFrame* result = nullptr;
5488     nsIFrame* frame = aFrame->PrincipalChildList().FirstChild();
5489     if (!aEndFrame) {
5490       while (frame && (!SelfIsSelectable(frame, aFlags) || frame->IsEmpty()))
5491         frame = frame->GetNextSibling();
5492       if (frame) result = frame;
5493     } else {
5494       // Because the frame tree is singly linked, to find the last frame,
5495       // we have to iterate through all the frames
5496       // XXX I have a feeling this could be slow for long blocks, although
5497       //     I can't find any slowdowns
5498       while (frame) {
5499         if (!frame->IsEmpty() && SelfIsSelectable(frame, aFlags))
5500           result = frame;
5501         frame = frame->GetNextSibling();
5502       }
5503     }
5504     if (result) return DrillDownToSelectionFrame(result, aEndFrame, aFlags);
5505   }
5506   // If the current frame has no targetable children, target the current frame
5507   return FrameTarget{aFrame, true, aEndFrame};
5508 }
5509 
5510 // This method finds the closest valid FrameTarget on a given line; if there is
5511 // no valid FrameTarget on the line, it returns a null FrameTarget
GetSelectionClosestFrameForLine(nsBlockFrame * aParent,nsBlockFrame::LineIterator aLine,const nsPoint & aPoint,uint32_t aFlags)5512 static FrameTarget GetSelectionClosestFrameForLine(
5513     nsBlockFrame* aParent, nsBlockFrame::LineIterator aLine,
5514     const nsPoint& aPoint, uint32_t aFlags) {
5515   // Account for end of lines (any iterator from the block is valid)
5516   if (aLine == aParent->LinesEnd())
5517     return DrillDownToSelectionFrame(aParent, true, aFlags);
5518   nsIFrame* frame = aLine->mFirstChild;
5519   nsIFrame* closestFromIStart = nullptr;
5520   nsIFrame* closestFromIEnd = nullptr;
5521   nscoord closestIStart = aLine->IStart(), closestIEnd = aLine->IEnd();
5522   WritingMode wm = aLine->mWritingMode;
5523   LogicalPoint pt(wm, aPoint, aLine->mContainerSize);
5524   bool canSkipBr = false;
5525   bool lastFrameWasEditable = false;
5526   for (int32_t n = aLine->GetChildCount(); n;
5527        --n, frame = frame->GetNextSibling()) {
5528     // Skip brFrames. Can only skip if the line contains at least
5529     // one selectable and non-empty frame before. Also, avoid skipping brs if
5530     // the previous thing had a different editableness than us, since then we
5531     // may end up not being able to select after it if the br is the last thing
5532     // on the line.
5533     if (!SelfIsSelectable(frame, aFlags) || frame->IsEmpty() ||
5534         (canSkipBr && frame->IsBrFrame() &&
5535          lastFrameWasEditable == frame->GetContent()->IsEditable())) {
5536       continue;
5537     }
5538     canSkipBr = true;
5539     lastFrameWasEditable =
5540         frame->GetContent() && frame->GetContent()->IsEditable();
5541     LogicalRect frameRect =
5542         LogicalRect(wm, frame->GetRect(), aLine->mContainerSize);
5543     if (pt.I(wm) >= frameRect.IStart(wm)) {
5544       if (pt.I(wm) < frameRect.IEnd(wm)) {
5545         return GetSelectionClosestFrameForChild(frame, aPoint, aFlags);
5546       }
5547       if (frameRect.IEnd(wm) >= closestIStart) {
5548         closestFromIStart = frame;
5549         closestIStart = frameRect.IEnd(wm);
5550       }
5551     } else {
5552       if (frameRect.IStart(wm) <= closestIEnd) {
5553         closestFromIEnd = frame;
5554         closestIEnd = frameRect.IStart(wm);
5555       }
5556     }
5557   }
5558   if (!closestFromIStart && !closestFromIEnd) {
5559     // We should only get here if there are no selectable frames on a line
5560     // XXX Do we need more elaborate handling here?
5561     return FrameTarget();
5562   }
5563   if (closestFromIStart &&
5564       (!closestFromIEnd ||
5565        (abs(pt.I(wm) - closestIStart) <= abs(pt.I(wm) - closestIEnd)))) {
5566     return GetSelectionClosestFrameForChild(closestFromIStart, aPoint, aFlags);
5567   }
5568   return GetSelectionClosestFrameForChild(closestFromIEnd, aPoint, aFlags);
5569 }
5570 
5571 // This method is for the special handling we do for block frames; they're
5572 // special because they represent paragraphs and because they are organized
5573 // into lines, which have bounds that are not stored elsewhere in the
5574 // frame tree.  Returns a null FrameTarget for frames which are not
5575 // blocks or blocks with no lines except editable one.
GetSelectionClosestFrameForBlock(nsIFrame * aFrame,const nsPoint & aPoint,uint32_t aFlags)5576 static FrameTarget GetSelectionClosestFrameForBlock(nsIFrame* aFrame,
5577                                                     const nsPoint& aPoint,
5578                                                     uint32_t aFlags) {
5579   nsBlockFrame* bf = do_QueryFrame(aFrame);
5580   if (!bf) {
5581     return FrameTarget();
5582   }
5583 
5584   // This code searches for the correct line
5585   nsBlockFrame::LineIterator end = bf->LinesEnd();
5586   nsBlockFrame::LineIterator curLine = bf->LinesBegin();
5587   nsBlockFrame::LineIterator closestLine = end;
5588 
5589   if (curLine != end) {
5590     // Convert aPoint into a LogicalPoint in the writing-mode of this block
5591     WritingMode wm = curLine->mWritingMode;
5592     LogicalPoint pt(wm, aPoint, curLine->mContainerSize);
5593     do {
5594       // Check to see if our point lies within the line's block-direction bounds
5595       nscoord BCoord = pt.B(wm) - curLine->BStart();
5596       nscoord BSize = curLine->BSize();
5597       if (BCoord >= 0 && BCoord < BSize) {
5598         closestLine = curLine;
5599         break;  // We found the line; stop looking
5600       }
5601       if (BCoord < 0) break;
5602       ++curLine;
5603     } while (curLine != end);
5604 
5605     if (closestLine == end) {
5606       nsBlockFrame::LineIterator prevLine = curLine.prev();
5607       nsBlockFrame::LineIterator nextLine = curLine;
5608       // Avoid empty lines
5609       while (nextLine != end && nextLine->IsEmpty()) ++nextLine;
5610       while (prevLine != end && prevLine->IsEmpty()) --prevLine;
5611 
5612       // This hidden pref dictates whether a point above or below all lines
5613       // comes up with a line or the beginning or end of the frame; 0 on
5614       // Windows, 1 on other platforms by default at the writing of this code
5615       int32_t dragOutOfFrame =
5616           Preferences::GetInt("browser.drag_out_of_frame_style");
5617 
5618       if (prevLine == end) {
5619         if (dragOutOfFrame == 1 || nextLine == end)
5620           return DrillDownToSelectionFrame(aFrame, false, aFlags);
5621         closestLine = nextLine;
5622       } else if (nextLine == end) {
5623         if (dragOutOfFrame == 1)
5624           return DrillDownToSelectionFrame(aFrame, true, aFlags);
5625         closestLine = prevLine;
5626       } else {  // Figure out which line is closer
5627         if (pt.B(wm) - prevLine->BEnd() < nextLine->BStart() - pt.B(wm))
5628           closestLine = prevLine;
5629         else
5630           closestLine = nextLine;
5631       }
5632     }
5633   }
5634 
5635   do {
5636     if (auto target =
5637             GetSelectionClosestFrameForLine(bf, closestLine, aPoint, aFlags)) {
5638       return target;
5639     }
5640     ++closestLine;
5641   } while (closestLine != end);
5642 
5643   // Fall back to just targeting the last targetable place
5644   return DrillDownToSelectionFrame(aFrame, true, aFlags);
5645 }
5646 
5647 // Use frame edge for grid, flex, table, and non-editable images. Choose the
5648 // edge based on the point position past the frame rect. If past the middle,
5649 // caret should be at end, otherwise at start. This behavior matches Blink.
5650 //
5651 // TODO(emilio): Can we use this code path for other replaced elements other
5652 // than images? Or even all other frames? We only get there when we didn't find
5653 // selectable children... At least one XUL test fails if we make this apply to
5654 // XUL labels. Also, editable images need _not_ to use the frame edge, see
5655 // below.
UseFrameEdge(nsIFrame * aFrame)5656 static bool UseFrameEdge(nsIFrame* aFrame) {
5657   if (aFrame->IsFlexOrGridContainer() || aFrame->IsTableFrame()) {
5658     return true;
5659   }
5660   const nsImageFrame* image = do_QueryFrame(aFrame);
5661   if (image && !aFrame->GetContent()->IsEditable()) {
5662     // Editable images are a special-case because editing relies on clicking on
5663     // an editable image selecting it, for it to show resizers.
5664     return true;
5665   }
5666   return false;
5667 }
5668 
LastResortFrameTargetForFrame(nsIFrame * aFrame,const nsPoint & aPoint)5669 static FrameTarget LastResortFrameTargetForFrame(nsIFrame* aFrame,
5670                                                  const nsPoint& aPoint) {
5671   if (!UseFrameEdge(aFrame)) {
5672     return {aFrame, false, false};
5673   }
5674   const auto& rect = aFrame->GetRectRelativeToSelf();
5675   nscoord reference;
5676   nscoord middle;
5677   if (aFrame->GetWritingMode().IsVertical()) {
5678     reference = aPoint.y;
5679     middle = rect.Height() / 2;
5680   } else {
5681     reference = aPoint.x;
5682     middle = rect.Width() / 2;
5683   }
5684   const bool afterFrame = reference > middle;
5685   return {aFrame, true, afterFrame};
5686 }
5687 
5688 // GetSelectionClosestFrame is the helper function that calculates the closest
5689 // frame to the given point.
5690 // It doesn't completely account for offset styles, so needs to be used in
5691 // restricted environments.
5692 // Cannot handle overlapping frames correctly, so it should receive the output
5693 // of GetFrameForPoint
5694 // Guaranteed to return a valid FrameTarget.
5695 // aPoint is relative to aFrame.
GetSelectionClosestFrame(nsIFrame * aFrame,const nsPoint & aPoint,uint32_t aFlags)5696 static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame,
5697                                             const nsPoint& aPoint,
5698                                             uint32_t aFlags) {
5699   // Handle blocks; if the frame isn't a block, the method fails
5700   if (auto target = GetSelectionClosestFrameForBlock(aFrame, aPoint, aFlags)) {
5701     return target;
5702   }
5703 
5704   if (nsIFrame* kid = aFrame->PrincipalChildList().FirstChild()) {
5705     // Go through all the child frames to find the closest one
5706     nsIFrame::FrameWithDistance closest = {nullptr, nscoord_MAX, nscoord_MAX};
5707     for (; kid; kid = kid->GetNextSibling()) {
5708       if (!SelfIsSelectable(kid, aFlags) || kid->IsEmpty()) continue;
5709 
5710       kid->FindCloserFrameForSelection(aPoint, &closest);
5711     }
5712     if (closest.mFrame) {
5713       if (SVGUtils::IsInSVGTextSubtree(closest.mFrame))
5714         return FrameTarget{closest.mFrame, false, false};
5715       return GetSelectionClosestFrameForChild(closest.mFrame, aPoint, aFlags);
5716     }
5717   }
5718 
5719   return LastResortFrameTargetForFrame(aFrame, aPoint);
5720 }
5721 
OffsetsForSingleFrame(nsIFrame * aFrame,const nsPoint & aPoint)5722 static nsIFrame::ContentOffsets OffsetsForSingleFrame(nsIFrame* aFrame,
5723                                                       const nsPoint& aPoint) {
5724   nsIFrame::ContentOffsets offsets;
5725   FrameContentRange range = GetRangeForFrame(aFrame);
5726   offsets.content = range.content;
5727   // If there are continuations (meaning it's not one rectangle), this is the
5728   // best this function can do
5729   if (aFrame->GetNextContinuation() || aFrame->GetPrevContinuation()) {
5730     offsets.offset = range.start;
5731     offsets.secondaryOffset = range.end;
5732     offsets.associate = CARET_ASSOCIATE_AFTER;
5733     return offsets;
5734   }
5735 
5736   // Figure out whether the offsets should be over, after, or before the frame
5737   nsRect rect(nsPoint(0, 0), aFrame->GetSize());
5738 
5739   bool isBlock = !aFrame->StyleDisplay()->IsInlineFlow();
5740   bool isRtl = (aFrame->StyleVisibility()->mDirection == StyleDirection::Rtl);
5741   if ((isBlock && rect.y < aPoint.y) ||
5742       (!isBlock && ((isRtl && rect.x + rect.width / 2 > aPoint.x) ||
5743                     (!isRtl && rect.x + rect.width / 2 < aPoint.x)))) {
5744     offsets.offset = range.end;
5745     if (rect.Contains(aPoint))
5746       offsets.secondaryOffset = range.start;
5747     else
5748       offsets.secondaryOffset = range.end;
5749   } else {
5750     offsets.offset = range.start;
5751     if (rect.Contains(aPoint))
5752       offsets.secondaryOffset = range.end;
5753     else
5754       offsets.secondaryOffset = range.start;
5755   }
5756   offsets.associate = offsets.offset == range.start ? CARET_ASSOCIATE_AFTER
5757                                                     : CARET_ASSOCIATE_BEFORE;
5758   return offsets;
5759 }
5760 
AdjustFrameForSelectionStyles(nsIFrame * aFrame)5761 static nsIFrame* AdjustFrameForSelectionStyles(nsIFrame* aFrame) {
5762   nsIFrame* adjustedFrame = aFrame;
5763   for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
5764     // These are the conditions that make all children not able to handle
5765     // a cursor.
5766     auto userSelect = frame->Style()->UserSelect();
5767     if (userSelect != StyleUserSelect::Auto &&
5768         userSelect != StyleUserSelect::All) {
5769       break;
5770     }
5771     if (userSelect == StyleUserSelect::All ||
5772         frame->IsGeneratedContentFrame()) {
5773       adjustedFrame = frame;
5774     }
5775   }
5776   return adjustedFrame;
5777 }
5778 
GetContentOffsetsFromPoint(const nsPoint & aPoint,uint32_t aFlags)5779 nsIFrame::ContentOffsets nsIFrame::GetContentOffsetsFromPoint(
5780     const nsPoint& aPoint, uint32_t aFlags) {
5781   nsIFrame* adjustedFrame;
5782   if (aFlags & IGNORE_SELECTION_STYLE) {
5783     adjustedFrame = this;
5784   } else {
5785     // This section of code deals with special selection styles.  Note that
5786     // -moz-all exists, even though it doesn't need to be explicitly handled.
5787     //
5788     // The offset is forced not to end up in generated content; content offsets
5789     // cannot represent content outside of the document's content tree.
5790 
5791     adjustedFrame = AdjustFrameForSelectionStyles(this);
5792 
5793     // `user-select: all` needs special handling, because clicking on it should
5794     // lead to the whole frame being selected.
5795     if (adjustedFrame->Style()->UserSelect() == StyleUserSelect::All) {
5796       nsPoint adjustedPoint = aPoint + GetOffsetTo(adjustedFrame);
5797       return OffsetsForSingleFrame(adjustedFrame, adjustedPoint);
5798     }
5799 
5800     // For other cases, try to find a closest frame starting from the parent of
5801     // the unselectable frame
5802     if (adjustedFrame != this) {
5803       adjustedFrame = adjustedFrame->GetParent();
5804     }
5805   }
5806 
5807   nsPoint adjustedPoint = aPoint + GetOffsetTo(adjustedFrame);
5808 
5809   FrameTarget closest =
5810       GetSelectionClosestFrame(adjustedFrame, adjustedPoint, aFlags);
5811 
5812   // If the correct offset is at one end of a frame, use offset-based
5813   // calculation method
5814   if (closest.frameEdge) {
5815     ContentOffsets offsets;
5816     FrameContentRange range = GetRangeForFrame(closest.frame);
5817     offsets.content = range.content;
5818     if (closest.afterFrame)
5819       offsets.offset = range.end;
5820     else
5821       offsets.offset = range.start;
5822     offsets.secondaryOffset = offsets.offset;
5823     offsets.associate = offsets.offset == range.start ? CARET_ASSOCIATE_AFTER
5824                                                       : CARET_ASSOCIATE_BEFORE;
5825     return offsets;
5826   }
5827 
5828   nsPoint pt;
5829   if (closest.frame != this) {
5830     if (SVGUtils::IsInSVGTextSubtree(closest.frame)) {
5831       pt = nsLayoutUtils::TransformAncestorPointToFrame(
5832           RelativeTo{closest.frame}, aPoint, RelativeTo{this});
5833     } else {
5834       pt = aPoint - closest.frame->GetOffsetTo(this);
5835     }
5836   } else {
5837     pt = aPoint;
5838   }
5839   return closest.frame->CalcContentOffsetsFromFramePoint(pt);
5840 
5841   // XXX should I add some kind of offset standardization?
5842   // consider <b>xxxxx</b><i>zzzzz</i>; should any click between the last
5843   // x and first z put the cursor in the same logical position in addition
5844   // to the same visual position?
5845 }
5846 
CalcContentOffsetsFromFramePoint(const nsPoint & aPoint)5847 nsIFrame::ContentOffsets nsIFrame::CalcContentOffsetsFromFramePoint(
5848     const nsPoint& aPoint) {
5849   return OffsetsForSingleFrame(this, aPoint);
5850 }
5851 
AssociateImage(const StyleImage & aImage)5852 bool nsIFrame::AssociateImage(const StyleImage& aImage) {
5853   imgRequestProxy* req = aImage.GetImageRequest();
5854   if (!req) {
5855     return false;
5856   }
5857 
5858   mozilla::css::ImageLoader* loader =
5859       PresContext()->Document()->StyleImageLoader();
5860 
5861   loader->AssociateRequestToFrame(req, this);
5862   return true;
5863 }
5864 
DisassociateImage(const StyleImage & aImage)5865 void nsIFrame::DisassociateImage(const StyleImage& aImage) {
5866   imgRequestProxy* req = aImage.GetImageRequest();
5867   if (!req) {
5868     return;
5869   }
5870 
5871   mozilla::css::ImageLoader* loader =
5872       PresContext()->Document()->StyleImageLoader();
5873 
5874   loader->DisassociateRequestFromFrame(req, this);
5875 }
5876 
UsedImageRendering() const5877 StyleImageRendering nsIFrame::UsedImageRendering() const {
5878   ComputedStyle* style;
5879   if (nsCSSRendering::IsCanvasFrame(this)) {
5880     nsCSSRendering::FindBackground(this, &style);
5881   } else {
5882     style = Style();
5883   }
5884   return style->StyleVisibility()->mImageRendering;
5885 }
5886 
5887 // The touch-action CSS property applies to: all elements except: non-replaced
5888 // inline elements, table rows, row groups, table columns, and column groups.
UsedTouchAction() const5889 StyleTouchAction nsIFrame::UsedTouchAction() const {
5890   if (IsFrameOfType(eLineParticipant)) {
5891     return StyleTouchAction::AUTO;
5892   }
5893   auto& disp = *StyleDisplay();
5894   if (disp.IsInternalTableStyleExceptCell()) {
5895     return StyleTouchAction::AUTO;
5896   }
5897   return disp.mTouchAction;
5898 }
5899 
GetCursor(const nsPoint &)5900 Maybe<nsIFrame::Cursor> nsIFrame::GetCursor(const nsPoint&) {
5901   StyleCursorKind kind = StyleUI()->Cursor().keyword;
5902   if (kind == StyleCursorKind::Auto) {
5903     // If this is editable, I-beam cursor is better for most elements.
5904     kind = (mContent && mContent->IsEditable()) ? StyleCursorKind::Text
5905                                                 : StyleCursorKind::Default;
5906   }
5907   if (kind == StyleCursorKind::Text && GetWritingMode().IsVertical()) {
5908     // Per CSS UI spec, UA may treat value 'text' as
5909     // 'vertical-text' for vertical text.
5910     kind = StyleCursorKind::VerticalText;
5911   }
5912 
5913   return Some(Cursor{kind, AllowCustomCursorImage::Yes});
5914 }
5915 
5916 // Resize and incremental reflow
5917 
5918 /* virtual */
MarkIntrinsicISizesDirty()5919 void nsIFrame::MarkIntrinsicISizesDirty() {
5920   // This version is meant only for what used to be box-to-block adaptors.
5921   // It should not be called by other derived classes.
5922   if (::IsXULBoxWrapped(this)) {
5923     nsBoxLayoutMetrics* metrics = BoxMetrics();
5924 
5925     XULSizeNeedsRecalc(metrics->mPrefSize);
5926     XULSizeNeedsRecalc(metrics->mMinSize);
5927     XULSizeNeedsRecalc(metrics->mMaxSize);
5928     XULSizeNeedsRecalc(metrics->mBlockPrefSize);
5929     XULSizeNeedsRecalc(metrics->mBlockMinSize);
5930     XULCoordNeedsRecalc(metrics->mFlex);
5931     XULCoordNeedsRecalc(metrics->mAscent);
5932   }
5933 
5934   // If we're a flex item, clear our flex-item-specific cached measurements
5935   // (which likely depended on our now-stale intrinsic isize).
5936   if (IsFlexItem()) {
5937     nsFlexContainerFrame::MarkCachedFlexMeasurementsDirty(this);
5938   }
5939 
5940   if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT)) {
5941     nsFontInflationData::MarkFontInflationDataTextDirty(this);
5942   }
5943 
5944   if (StaticPrefs::layout_css_grid_item_baxis_measurement_enabled()) {
5945     RemoveProperty(nsGridContainerFrame::CachedBAxisMeasurement::Prop());
5946   }
5947 }
5948 
MarkSubtreeDirty()5949 void nsIFrame::MarkSubtreeDirty() {
5950   if (HasAnyStateBits(NS_FRAME_IS_DIRTY)) {
5951     return;
5952   }
5953   // Unconditionally mark given frame dirty.
5954   AddStateBits(NS_FRAME_IS_DIRTY);
5955 
5956   // Mark all descendants dirty, unless:
5957   // - Already dirty.
5958   // - TableColGroup
5959   // - XULBox
5960   AutoTArray<nsIFrame*, 32> stack;
5961   for (const auto& childLists : ChildLists()) {
5962     for (nsIFrame* kid : childLists.mList) {
5963       stack.AppendElement(kid);
5964     }
5965   }
5966   while (!stack.IsEmpty()) {
5967     nsIFrame* f = stack.PopLastElement();
5968     if (f->HasAnyStateBits(NS_FRAME_IS_DIRTY) || f->IsTableColGroupFrame() ||
5969         f->IsXULBoxFrame()) {
5970       continue;
5971     }
5972 
5973     f->AddStateBits(NS_FRAME_IS_DIRTY);
5974 
5975     for (const auto& childLists : f->ChildLists()) {
5976       for (nsIFrame* kid : childLists.mList) {
5977         stack.AppendElement(kid);
5978       }
5979     }
5980   }
5981 }
5982 
5983 /* virtual */
GetMinISize(gfxContext * aRenderingContext)5984 nscoord nsIFrame::GetMinISize(gfxContext* aRenderingContext) {
5985   nscoord result = 0;
5986   DISPLAY_MIN_INLINE_SIZE(this, result);
5987   return result;
5988 }
5989 
5990 /* virtual */
GetPrefISize(gfxContext * aRenderingContext)5991 nscoord nsIFrame::GetPrefISize(gfxContext* aRenderingContext) {
5992   nscoord result = 0;
5993   DISPLAY_PREF_INLINE_SIZE(this, result);
5994   return result;
5995 }
5996 
5997 /* virtual */
AddInlineMinISize(gfxContext * aRenderingContext,nsIFrame::InlineMinISizeData * aData)5998 void nsIFrame::AddInlineMinISize(gfxContext* aRenderingContext,
5999                                  nsIFrame::InlineMinISizeData* aData) {
6000   nscoord isize = nsLayoutUtils::IntrinsicForContainer(
6001       aRenderingContext, this, IntrinsicISizeType::MinISize);
6002   aData->DefaultAddInlineMinISize(this, isize);
6003 }
6004 
6005 /* virtual */
AddInlinePrefISize(gfxContext * aRenderingContext,nsIFrame::InlinePrefISizeData * aData)6006 void nsIFrame::AddInlinePrefISize(gfxContext* aRenderingContext,
6007                                   nsIFrame::InlinePrefISizeData* aData) {
6008   nscoord isize = nsLayoutUtils::IntrinsicForContainer(
6009       aRenderingContext, this, IntrinsicISizeType::PrefISize);
6010   aData->DefaultAddInlinePrefISize(isize);
6011 }
6012 
DefaultAddInlineMinISize(nsIFrame * aFrame,nscoord aISize,bool aAllowBreak)6013 void nsIFrame::InlineMinISizeData::DefaultAddInlineMinISize(nsIFrame* aFrame,
6014                                                             nscoord aISize,
6015                                                             bool aAllowBreak) {
6016   auto parent = aFrame->GetParent();
6017   MOZ_ASSERT(parent, "Must have a parent if we get here!");
6018   const bool mayBreak = aAllowBreak && !aFrame->CanContinueTextRun() &&
6019                         !parent->Style()->ShouldSuppressLineBreak() &&
6020                         parent->StyleText()->WhiteSpaceCanWrap(parent);
6021   if (mayBreak) {
6022     OptionallyBreak();
6023   }
6024   mTrailingWhitespace = 0;
6025   mSkipWhitespace = false;
6026   mCurrentLine += aISize;
6027   mAtStartOfLine = false;
6028   if (mayBreak) {
6029     OptionallyBreak();
6030   }
6031 }
6032 
DefaultAddInlinePrefISize(nscoord aISize)6033 void nsIFrame::InlinePrefISizeData::DefaultAddInlinePrefISize(nscoord aISize) {
6034   mCurrentLine = NSCoordSaturatingAdd(mCurrentLine, aISize);
6035   mTrailingWhitespace = 0;
6036   mSkipWhitespace = false;
6037   mLineIsEmpty = false;
6038 }
6039 
ForceBreak()6040 void nsIFrame::InlineMinISizeData::ForceBreak() {
6041   mCurrentLine -= mTrailingWhitespace;
6042   mPrevLines = std::max(mPrevLines, mCurrentLine);
6043   mCurrentLine = mTrailingWhitespace = 0;
6044 
6045   for (uint32_t i = 0, i_end = mFloats.Length(); i != i_end; ++i) {
6046     nscoord float_min = mFloats[i].Width();
6047     if (float_min > mPrevLines) mPrevLines = float_min;
6048   }
6049   mFloats.Clear();
6050   mSkipWhitespace = true;
6051 }
6052 
OptionallyBreak(nscoord aHyphenWidth)6053 void nsIFrame::InlineMinISizeData::OptionallyBreak(nscoord aHyphenWidth) {
6054   // If we can fit more content into a smaller width by staying on this
6055   // line (because we're still at a negative offset due to negative
6056   // text-indent or negative margin), don't break.  Otherwise, do the
6057   // same as ForceBreak.  it doesn't really matter when we accumulate
6058   // floats.
6059   if (mCurrentLine + aHyphenWidth < 0 || mAtStartOfLine) return;
6060   mCurrentLine += aHyphenWidth;
6061   ForceBreak();
6062 }
6063 
ForceBreak(StyleClear aBreakType)6064 void nsIFrame::InlinePrefISizeData::ForceBreak(StyleClear aBreakType) {
6065   MOZ_ASSERT(aBreakType == StyleClear::None || aBreakType == StyleClear::Both ||
6066                  aBreakType == StyleClear::Left ||
6067                  aBreakType == StyleClear::Right,
6068              "Must be a physical break type");
6069 
6070   // If this force break is not clearing any float, we can leave all the
6071   // floats to the next force break.
6072   if (mFloats.Length() != 0 && aBreakType != StyleClear::None) {
6073     // preferred widths accumulated for floats that have already
6074     // been cleared past
6075     nscoord floats_done = 0,
6076             // preferred widths accumulated for floats that have not yet
6077             // been cleared past
6078         floats_cur_left = 0, floats_cur_right = 0;
6079 
6080     for (uint32_t i = 0, i_end = mFloats.Length(); i != i_end; ++i) {
6081       const FloatInfo& floatInfo = mFloats[i];
6082       const nsStyleDisplay* floatDisp = floatInfo.Frame()->StyleDisplay();
6083       StyleClear breakType = floatDisp->mBreakType;
6084       if (breakType == StyleClear::Left || breakType == StyleClear::Right ||
6085           breakType == StyleClear::Both) {
6086         nscoord floats_cur =
6087             NSCoordSaturatingAdd(floats_cur_left, floats_cur_right);
6088         if (floats_cur > floats_done) {
6089           floats_done = floats_cur;
6090         }
6091         if (breakType != StyleClear::Right) {
6092           floats_cur_left = 0;
6093         }
6094         if (breakType != StyleClear::Left) {
6095           floats_cur_right = 0;
6096         }
6097       }
6098 
6099       StyleFloat floatStyle = floatDisp->mFloat;
6100       nscoord& floats_cur =
6101           floatStyle == StyleFloat::Left ? floats_cur_left : floats_cur_right;
6102       nscoord floatWidth = floatInfo.Width();
6103       // Negative-width floats don't change the available space so they
6104       // shouldn't change our intrinsic line width either.
6105       floats_cur = NSCoordSaturatingAdd(floats_cur, std::max(0, floatWidth));
6106     }
6107 
6108     nscoord floats_cur =
6109         NSCoordSaturatingAdd(floats_cur_left, floats_cur_right);
6110     if (floats_cur > floats_done) floats_done = floats_cur;
6111 
6112     mCurrentLine = NSCoordSaturatingAdd(mCurrentLine, floats_done);
6113 
6114     if (aBreakType == StyleClear::Both) {
6115       mFloats.Clear();
6116     } else {
6117       // If the break type does not clear all floats, it means there may
6118       // be some floats whose isize should contribute to the intrinsic
6119       // isize of the next line. The code here scans the current mFloats
6120       // and keeps floats which are not cleared by this break. Note that
6121       // floats may be cleared directly or indirectly. See below.
6122       nsTArray<FloatInfo> newFloats;
6123       MOZ_ASSERT(
6124           aBreakType == StyleClear::Left || aBreakType == StyleClear::Right,
6125           "Other values should have been handled in other branches");
6126       StyleFloat clearFloatType =
6127           aBreakType == StyleClear::Left ? StyleFloat::Left : StyleFloat::Right;
6128       // Iterate the array in reverse so that we can stop when there are
6129       // no longer any floats we need to keep. See below.
6130       for (FloatInfo& floatInfo : Reversed(mFloats)) {
6131         const nsStyleDisplay* floatDisp = floatInfo.Frame()->StyleDisplay();
6132         if (floatDisp->mFloat != clearFloatType) {
6133           newFloats.AppendElement(floatInfo);
6134         } else {
6135           // This is a float on the side that this break directly clears
6136           // which means we're not keeping it in mFloats. However, if
6137           // this float clears floats on the opposite side (via a value
6138           // of either 'both' or one of 'left'/'right'), any remaining
6139           // (earlier) floats on that side would be indirectly cleared
6140           // as well. Thus, we should break out of this loop and stop
6141           // considering earlier floats to be kept in mFloats.
6142           StyleClear floatBreakType = floatDisp->mBreakType;
6143           if (floatBreakType != aBreakType &&
6144               floatBreakType != StyleClear::None) {
6145             break;
6146           }
6147         }
6148       }
6149       newFloats.Reverse();
6150       mFloats = std::move(newFloats);
6151     }
6152   }
6153 
6154   mCurrentLine =
6155       NSCoordSaturatingSubtract(mCurrentLine, mTrailingWhitespace, nscoord_MAX);
6156   mPrevLines = std::max(mPrevLines, mCurrentLine);
6157   mCurrentLine = mTrailingWhitespace = 0;
6158   mSkipWhitespace = true;
6159   mLineIsEmpty = true;
6160 }
6161 
ResolveMargin(const LengthPercentageOrAuto & aStyle,nscoord aPercentageBasis)6162 static nscoord ResolveMargin(const LengthPercentageOrAuto& aStyle,
6163                              nscoord aPercentageBasis) {
6164   if (aStyle.IsAuto()) {
6165     return nscoord(0);
6166   }
6167   return nsLayoutUtils::ResolveToLength<false>(aStyle.AsLengthPercentage(),
6168                                                aPercentageBasis);
6169 }
6170 
ResolvePadding(const LengthPercentage & aStyle,nscoord aPercentageBasis)6171 static nscoord ResolvePadding(const LengthPercentage& aStyle,
6172                               nscoord aPercentageBasis) {
6173   return nsLayoutUtils::ResolveToLength<true>(aStyle, aPercentageBasis);
6174 }
6175 
IntrinsicSizeOffsets(nsIFrame * aFrame,nscoord aPercentageBasis,bool aForISize)6176 static nsIFrame::IntrinsicSizeOffsetData IntrinsicSizeOffsets(
6177     nsIFrame* aFrame, nscoord aPercentageBasis, bool aForISize) {
6178   nsIFrame::IntrinsicSizeOffsetData result;
6179   WritingMode wm = aFrame->GetWritingMode();
6180   const auto& margin = aFrame->StyleMargin()->mMargin;
6181   bool verticalAxis = aForISize == wm.IsVertical();
6182   if (verticalAxis) {
6183     result.margin += ResolveMargin(margin.Get(eSideTop), aPercentageBasis);
6184     result.margin += ResolveMargin(margin.Get(eSideBottom), aPercentageBasis);
6185   } else {
6186     result.margin += ResolveMargin(margin.Get(eSideLeft), aPercentageBasis);
6187     result.margin += ResolveMargin(margin.Get(eSideRight), aPercentageBasis);
6188   }
6189 
6190   const auto& padding = aFrame->StylePadding()->mPadding;
6191   if (verticalAxis) {
6192     result.padding += ResolvePadding(padding.Get(eSideTop), aPercentageBasis);
6193     result.padding +=
6194         ResolvePadding(padding.Get(eSideBottom), aPercentageBasis);
6195   } else {
6196     result.padding += ResolvePadding(padding.Get(eSideLeft), aPercentageBasis);
6197     result.padding += ResolvePadding(padding.Get(eSideRight), aPercentageBasis);
6198   }
6199 
6200   const nsStyleBorder* styleBorder = aFrame->StyleBorder();
6201   if (verticalAxis) {
6202     result.border += styleBorder->GetComputedBorderWidth(eSideTop);
6203     result.border += styleBorder->GetComputedBorderWidth(eSideBottom);
6204   } else {
6205     result.border += styleBorder->GetComputedBorderWidth(eSideLeft);
6206     result.border += styleBorder->GetComputedBorderWidth(eSideRight);
6207   }
6208 
6209   const nsStyleDisplay* disp = aFrame->StyleDisplay();
6210   if (aFrame->IsThemed(disp)) {
6211     nsPresContext* presContext = aFrame->PresContext();
6212 
6213     LayoutDeviceIntMargin border = presContext->Theme()->GetWidgetBorder(
6214         presContext->DeviceContext(), aFrame, disp->EffectiveAppearance());
6215     result.border = presContext->DevPixelsToAppUnits(
6216         verticalAxis ? border.TopBottom() : border.LeftRight());
6217 
6218     LayoutDeviceIntMargin padding;
6219     if (presContext->Theme()->GetWidgetPadding(
6220             presContext->DeviceContext(), aFrame, disp->EffectiveAppearance(),
6221             &padding)) {
6222       result.padding = presContext->DevPixelsToAppUnits(
6223           verticalAxis ? padding.TopBottom() : padding.LeftRight());
6224     }
6225   }
6226   return result;
6227 }
6228 
IntrinsicISizeOffsets(nscoord aPercentageBasis)6229 /* virtual */ nsIFrame::IntrinsicSizeOffsetData nsIFrame::IntrinsicISizeOffsets(
6230     nscoord aPercentageBasis) {
6231   return IntrinsicSizeOffsets(this, aPercentageBasis, true);
6232 }
6233 
IntrinsicBSizeOffsets(nscoord aPercentageBasis)6234 nsIFrame::IntrinsicSizeOffsetData nsIFrame::IntrinsicBSizeOffsets(
6235     nscoord aPercentageBasis) {
6236   return IntrinsicSizeOffsets(this, aPercentageBasis, false);
6237 }
6238 
6239 /* virtual */
GetIntrinsicSize()6240 IntrinsicSize nsIFrame::GetIntrinsicSize() {
6241   return IntrinsicSize();  // default is width/height set to eStyleUnit_None
6242 }
6243 
GetAspectRatio() const6244 AspectRatio nsIFrame::GetAspectRatio() const {
6245   // Per spec, 'aspect-ratio' property applies to all elements except inline
6246   // boxes and internal ruby or table boxes.
6247   // https://drafts.csswg.org/css-sizing-4/#aspect-ratio
6248   // For those frame types that don't support aspect-ratio, they must not have
6249   // the natural ratio, so this early return is fine.
6250   if (!IsFrameOfType(eSupportsAspectRatio)) {
6251     return AspectRatio();
6252   }
6253 
6254   const StyleAspectRatio& aspectRatio = StylePosition()->mAspectRatio;
6255   // If aspect-ratio is zero or infinite, it's a degenerate ratio and behaves
6256   // as auto.
6257   // https://drafts.csswg.org/css-sizing-4/#valdef-aspect-ratio-ratio
6258   if (!aspectRatio.BehavesAsAuto()) {
6259     // Non-auto. Return the preferred aspect ratio from the aspect-ratio style.
6260     return aspectRatio.ratio.AsRatio().ToLayoutRatio(UseBoxSizing::Yes);
6261   }
6262 
6263   // The rest of the cases are when aspect-ratio has 'auto'.
6264   if (auto intrinsicRatio = GetIntrinsicRatio()) {
6265     return intrinsicRatio;
6266   }
6267 
6268   if (aspectRatio.HasRatio()) {
6269     // If it's a degenerate ratio, this returns 0. Just the same as the auto
6270     // case.
6271     return aspectRatio.ratio.AsRatio().ToLayoutRatio(UseBoxSizing::No);
6272   }
6273 
6274   return AspectRatio();
6275 }
6276 
6277 /* virtual */
GetIntrinsicRatio() const6278 AspectRatio nsIFrame::GetIntrinsicRatio() const { return AspectRatio(); }
6279 
ShouldApplyAutomaticMinimumOnInlineAxis(WritingMode aWM,const nsStyleDisplay * aDisplay,const nsStylePosition * aPosition)6280 static bool ShouldApplyAutomaticMinimumOnInlineAxis(
6281     WritingMode aWM, const nsStyleDisplay* aDisplay,
6282     const nsStylePosition* aPosition) {
6283   // Apply the automatic minimum size for aspect ratio:
6284   // Note: The replaced elements shouldn't be here, so we only check the scroll
6285   // container.
6286   // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-minimum
6287   return !aDisplay->IsScrollableOverflow() && aPosition->MinISize(aWM).IsAuto();
6288 }
6289 
6290 struct MinMaxSize {
6291   nscoord mMinSize = 0;
6292   nscoord mMaxSize = NS_UNCONSTRAINEDSIZE;
6293 
ClampSizeToMinAndMaxMinMaxSize6294   nscoord ClampSizeToMinAndMax(nscoord aSize) const {
6295     return NS_CSS_MINMAX(aSize, mMinSize, mMaxSize);
6296   }
6297 };
ComputeTransferredMinMaxInlineSize(const WritingMode aWM,const AspectRatio & aAspectRatio,const MinMaxSize & aMinMaxBSize,const LogicalSize & aBoxSizingAdjustment)6298 static MinMaxSize ComputeTransferredMinMaxInlineSize(
6299     const WritingMode aWM, const AspectRatio& aAspectRatio,
6300     const MinMaxSize& aMinMaxBSize, const LogicalSize& aBoxSizingAdjustment) {
6301   // Note: the spec mentions that
6302   // 1. This transferred minimum is capped by any definite preferred or maximum
6303   //    size in the destination axis.
6304   // 2. This transferred maximum is floored by any definite preferred or minimum
6305   //    size in the destination axis
6306   //
6307   // https://drafts.csswg.org/css-sizing-4/#aspect-ratio
6308   //
6309   // The spec requires us to clamp these by the specified size (it calls it the
6310   // preferred size). However, we actually don't need to worry about that,
6311   // because we only use this if the inline size is indefinite.
6312   //
6313   // We do not need to clamp the transferred minimum and maximum as long as we
6314   // always apply the transferred min/max size before the explicit min/max size,
6315   // the result will be identical.
6316 
6317   MinMaxSize transferredISize;
6318 
6319   if (aMinMaxBSize.mMinSize > 0) {
6320     transferredISize.mMinSize = aAspectRatio.ComputeRatioDependentSize(
6321         LogicalAxis::eLogicalAxisInline, aWM, aMinMaxBSize.mMinSize,
6322         aBoxSizingAdjustment);
6323   }
6324 
6325   if (aMinMaxBSize.mMaxSize != NS_UNCONSTRAINEDSIZE) {
6326     transferredISize.mMaxSize = aAspectRatio.ComputeRatioDependentSize(
6327         LogicalAxis::eLogicalAxisInline, aWM, aMinMaxBSize.mMaxSize,
6328         aBoxSizingAdjustment);
6329   }
6330 
6331   // Minimum size wins over maximum size.
6332   transferredISize.mMaxSize =
6333       std::max(transferredISize.mMinSize, transferredISize.mMaxSize);
6334   return transferredISize;
6335 }
6336 
6337 /* virtual */
ComputeSize(gfxContext * aRenderingContext,WritingMode aWM,const LogicalSize & aCBSize,nscoord aAvailableISize,const LogicalSize & aMargin,const LogicalSize & aBorderPadding,const StyleSizeOverrides & aSizeOverrides,ComputeSizeFlags aFlags)6338 nsIFrame::SizeComputationResult nsIFrame::ComputeSize(
6339     gfxContext* aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize,
6340     nscoord aAvailableISize, const LogicalSize& aMargin,
6341     const LogicalSize& aBorderPadding, const StyleSizeOverrides& aSizeOverrides,
6342     ComputeSizeFlags aFlags) {
6343   MOZ_ASSERT(!GetIntrinsicRatio(),
6344              "Please override this method and call "
6345              "nsContainerFrame::ComputeSizeWithIntrinsicDimensions instead.");
6346   LogicalSize result =
6347       ComputeAutoSize(aRenderingContext, aWM, aCBSize, aAvailableISize, aMargin,
6348                       aBorderPadding, aSizeOverrides, aFlags);
6349   const nsStylePosition* stylePos = StylePosition();
6350   const nsStyleDisplay* disp = StyleDisplay();
6351   auto aspectRatioUsage = AspectRatioUsage::None;
6352 
6353   const auto boxSizingAdjust = stylePos->mBoxSizing == StyleBoxSizing::Border
6354                                    ? aBorderPadding
6355                                    : LogicalSize(aWM);
6356   nscoord boxSizingToMarginEdgeISize = aMargin.ISize(aWM) +
6357                                        aBorderPadding.ISize(aWM) -
6358                                        boxSizingAdjust.ISize(aWM);
6359 
6360   const auto& styleISize = aSizeOverrides.mStyleISize
6361                                ? *aSizeOverrides.mStyleISize
6362                                : stylePos->ISize(aWM);
6363   const auto& styleBSize = aSizeOverrides.mStyleBSize
6364                                ? *aSizeOverrides.mStyleBSize
6365                                : stylePos->BSize(aWM);
6366   const auto& aspectRatio = aSizeOverrides.mAspectRatio
6367                                 ? *aSizeOverrides.mAspectRatio
6368                                 : GetAspectRatio();
6369 
6370   auto parentFrame = GetParent();
6371   auto alignCB = parentFrame;
6372   bool isGridItem = IsGridItem();
6373   if (parentFrame && parentFrame->IsTableWrapperFrame() && IsTableFrame()) {
6374     // An inner table frame is sized as a grid item if its table wrapper is,
6375     // because they actually have the same CB (the wrapper's CB).
6376     // @see ReflowInput::InitCBReflowInput
6377     auto tableWrapper = GetParent();
6378     auto grandParent = tableWrapper->GetParent();
6379     isGridItem = grandParent->IsGridContainerFrame() &&
6380                  !tableWrapper->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
6381     if (isGridItem) {
6382       // When resolving justify/align-self below, we want to use the grid
6383       // container's justify/align-items value and WritingMode.
6384       alignCB = grandParent;
6385     }
6386   }
6387   const bool isFlexItem =
6388       IsFlexItem() &&
6389       !parentFrame->HasAnyStateBits(NS_STATE_FLEX_IS_EMULATING_LEGACY_BOX);
6390   // This variable only gets set (and used) if isFlexItem is true.  It
6391   // indicates which axis (in this frame's own WM) corresponds to its
6392   // flex container's main axis.
6393   LogicalAxis flexMainAxis =
6394       eLogicalAxisInline;  // (init to make valgrind happy)
6395   if (isFlexItem) {
6396     flexMainAxis = nsFlexContainerFrame::IsItemInlineAxisMainAxis(this)
6397                        ? eLogicalAxisInline
6398                        : eLogicalAxisBlock;
6399   }
6400 
6401   const bool isOrthogonal = aWM.IsOrthogonalTo(alignCB->GetWritingMode());
6402   const bool isAutoISize = styleISize.IsAuto();
6403   const bool isAutoBSize =
6404       nsLayoutUtils::IsAutoBSize(styleBSize, aCBSize.BSize(aWM)) ||
6405       aFlags.contains(ComputeSizeFlag::UseAutoBSize);
6406   // Compute inline-axis size
6407   if (!isAutoISize) {
6408     auto iSizeResult = ComputeISizeValue(
6409         aRenderingContext, aWM, aCBSize, boxSizingAdjust,
6410         boxSizingToMarginEdgeISize, styleISize, aSizeOverrides, aFlags);
6411     result.ISize(aWM) = iSizeResult.mISize;
6412     aspectRatioUsage = iSizeResult.mAspectRatioUsage;
6413   } else if (MOZ_UNLIKELY(isGridItem) && !IsTrueOverflowContainer()) {
6414     // 'auto' inline-size for grid-level box - fill the CB for 'stretch' /
6415     // 'normal' and clamp it to the CB if requested:
6416     bool stretch = false;
6417     bool mayUseAspectRatio = aspectRatio && !isAutoBSize;
6418     if (!aFlags.contains(ComputeSizeFlag::ShrinkWrap) &&
6419         !StyleMargin()->HasInlineAxisAuto(aWM) &&
6420         !alignCB->IsMasonry(isOrthogonal ? eLogicalAxisBlock
6421                                          : eLogicalAxisInline)) {
6422       auto inlineAxisAlignment =
6423           isOrthogonal ? StylePosition()->UsedAlignSelf(alignCB->Style())._0
6424                        : StylePosition()->UsedJustifySelf(alignCB->Style())._0;
6425       stretch = inlineAxisAlignment == StyleAlignFlags::STRETCH ||
6426                 (inlineAxisAlignment == StyleAlignFlags::NORMAL &&
6427                  !mayUseAspectRatio);
6428     }
6429 
6430     // Apply the preferred aspect ratio for alignments other than *stretch* and
6431     // *normal without aspect ratio*.
6432     // The spec says all other values should size the items as fit-content, and
6433     // the intrinsic size should respect the preferred aspect ratio, so we also
6434     // apply aspect ratio for all other values.
6435     // https://drafts.csswg.org/css-grid/#grid-item-sizing
6436     if (!stretch && mayUseAspectRatio) {
6437       // Note: we don't need to handle aspect ratio for inline axis if both
6438       // width/height are auto. The default ratio-dependent axis is block axis
6439       // in this case, so we can simply get the block size from the non-auto
6440       // |styleBSize|.
6441       auto bSize = nsLayoutUtils::ComputeBSizeValue(
6442           aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
6443           styleBSize.AsLengthPercentage());
6444       result.ISize(aWM) = aspectRatio.ComputeRatioDependentSize(
6445           LogicalAxis::eLogicalAxisInline, aWM, bSize, boxSizingAdjust);
6446       aspectRatioUsage = AspectRatioUsage::ToComputeISize;
6447     }
6448 
6449     if (stretch || aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize)) {
6450       auto iSizeToFillCB =
6451           std::max(nscoord(0), aCBSize.ISize(aWM) - aBorderPadding.ISize(aWM) -
6452                                    aMargin.ISize(aWM));
6453       if (stretch || result.ISize(aWM) > iSizeToFillCB) {
6454         result.ISize(aWM) = iSizeToFillCB;
6455       }
6456     }
6457   } else if (aspectRatio && !isAutoBSize) {
6458     auto bSize = nsLayoutUtils::ComputeBSizeValue(
6459         aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
6460         styleBSize.AsLengthPercentage());
6461     result.ISize(aWM) = aspectRatio.ComputeRatioDependentSize(
6462         LogicalAxis::eLogicalAxisInline, aWM, bSize, boxSizingAdjust);
6463     aspectRatioUsage = AspectRatioUsage::ToComputeISize;
6464   }
6465 
6466   // Calculate and apply transferred min & max size contraints.
6467   // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-size-transfers
6468   //
6469   // Note: The basic principle is that sizing constraints transfer through the
6470   // aspect-ratio to the other side to preserve the aspect ratio to the extent
6471   // that they can without violating any sizes specified explicitly on that
6472   // affected axis.
6473   const bool isDefiniteISize = styleISize.IsLengthPercentage();
6474   const bool isFlexItemInlineAxisMainAxis =
6475       isFlexItem && flexMainAxis == eLogicalAxisInline;
6476   const auto& minBSizeCoord = stylePos->MinBSize(aWM);
6477   const auto& maxBSizeCoord = stylePos->MaxBSize(aWM);
6478   const bool isAutoMinBSize =
6479       nsLayoutUtils::IsAutoBSize(minBSizeCoord, aCBSize.BSize(aWM));
6480   const bool isAutoMaxBSize =
6481       nsLayoutUtils::IsAutoBSize(maxBSizeCoord, aCBSize.BSize(aWM));
6482   if (aspectRatio && !isDefiniteISize && !isFlexItemInlineAxisMainAxis) {
6483     const MinMaxSize minMaxBSize{
6484         isAutoMinBSize ? 0
6485                        : nsLayoutUtils::ComputeBSizeValue(
6486                              aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
6487                              minBSizeCoord.AsLengthPercentage()),
6488         isAutoMaxBSize ? NS_UNCONSTRAINEDSIZE
6489                        : nsLayoutUtils::ComputeBSizeValue(
6490                              aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
6491                              maxBSizeCoord.AsLengthPercentage())};
6492     MinMaxSize transferredMinMaxISize = ComputeTransferredMinMaxInlineSize(
6493         aWM, aspectRatio, minMaxBSize, boxSizingAdjust);
6494 
6495     result.ISize(aWM) =
6496         transferredMinMaxISize.ClampSizeToMinAndMax(result.ISize(aWM));
6497   }
6498 
6499   // Flex items ignore their min & max sizing properties in their
6500   // flex container's main-axis.  (Those properties get applied later in
6501   // the flexbox algorithm.)
6502   const auto& maxISizeCoord = stylePos->MaxISize(aWM);
6503   nscoord maxISize = NS_UNCONSTRAINEDSIZE;
6504   if (!maxISizeCoord.IsNone() && !isFlexItemInlineAxisMainAxis) {
6505     maxISize = ComputeISizeValue(aRenderingContext, aWM, aCBSize,
6506                                  boxSizingAdjust, boxSizingToMarginEdgeISize,
6507                                  maxISizeCoord, aSizeOverrides, aFlags)
6508                    .mISize;
6509     result.ISize(aWM) = std::min(maxISize, result.ISize(aWM));
6510   }
6511 
6512   const auto& minISizeCoord = stylePos->MinISize(aWM);
6513   nscoord minISize;
6514   if (!minISizeCoord.IsAuto() && !isFlexItemInlineAxisMainAxis) {
6515     minISize = ComputeISizeValue(aRenderingContext, aWM, aCBSize,
6516                                  boxSizingAdjust, boxSizingToMarginEdgeISize,
6517                                  minISizeCoord, aSizeOverrides, aFlags)
6518                    .mISize;
6519   } else if (MOZ_UNLIKELY(
6520                  aFlags.contains(ComputeSizeFlag::IApplyAutoMinSize))) {
6521     // This implements "Implied Minimum Size of Grid Items".
6522     // https://drafts.csswg.org/css-grid/#min-size-auto
6523     minISize = std::min(maxISize, GetMinISize(aRenderingContext));
6524     if (styleISize.IsLengthPercentage()) {
6525       minISize = std::min(minISize, result.ISize(aWM));
6526     } else if (aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize)) {
6527       // "if the grid item spans only grid tracks that have a fixed max track
6528       // sizing function, its automatic minimum size in that dimension is
6529       // further clamped to less than or equal to the size necessary to fit
6530       // its margin box within the resulting grid area (flooring at zero)"
6531       // https://drafts.csswg.org/css-grid/#min-size-auto
6532       auto maxMinISize =
6533           std::max(nscoord(0), aCBSize.ISize(aWM) - aBorderPadding.ISize(aWM) -
6534                                    aMargin.ISize(aWM));
6535       minISize = std::min(minISize, maxMinISize);
6536     }
6537   } else if (aspectRatioUsage == AspectRatioUsage::ToComputeISize &&
6538              ShouldApplyAutomaticMinimumOnInlineAxis(aWM, disp, stylePos)) {
6539     // This means we successfully applied aspect-ratio and now need to check
6540     // if we need to apply the implied minimum size:
6541     // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-minimum
6542     MOZ_ASSERT(!IsFrameOfType(eReplacedSizing),
6543                "aspect-ratio minimums should not apply to replaced elements");
6544     // The inline size computed by aspect-ratio shouldn't less than the content
6545     // size.
6546     minISize = GetMinISize(aRenderingContext);
6547   } else {
6548     // Treat "min-width: auto" as 0.
6549     // NOTE: Technically, "auto" is supposed to behave like "min-content" on
6550     // flex items. However, we don't need to worry about that here, because
6551     // flex items' min-sizes are intentionally ignored until the flex
6552     // container explicitly considers them during space distribution.
6553     minISize = 0;
6554   }
6555   result.ISize(aWM) = std::max(minISize, result.ISize(aWM));
6556 
6557   // Compute block-axis size
6558   // (but not if we have auto bsize or if we received the "UseAutoBSize"
6559   // flag -- then, we'll just stick with the bsize that we already calculated
6560   // in the initial ComputeAutoSize() call. However, if we have a valid
6561   // preferred aspect ratio, we still have to compute the block size because
6562   // aspect ratio affects the intrinsic content size.)
6563   if (!isAutoBSize) {
6564     result.BSize(aWM) = nsLayoutUtils::ComputeBSizeValue(
6565         aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
6566         styleBSize.AsLengthPercentage());
6567   } else if (MOZ_UNLIKELY(isGridItem) &&
6568              // FIXME: Any better way to refine the auto check here?
6569              styleBSize.IsAuto() &&
6570              !aFlags.contains(ComputeSizeFlag::UseAutoBSize) &&
6571              !IsTrueOverflowContainer() &&
6572              !alignCB->IsMasonry(isOrthogonal ? eLogicalAxisInline
6573                                               : eLogicalAxisBlock)) {
6574     auto cbSize = aCBSize.BSize(aWM);
6575     if (cbSize != NS_UNCONSTRAINEDSIZE) {
6576       // 'auto' block-size for grid-level box - fill the CB for 'stretch' /
6577       // 'normal' and clamp it to the CB if requested:
6578       bool stretch = false;
6579       bool mayUseAspectRatio =
6580           aspectRatio && result.ISize(aWM) != NS_UNCONSTRAINEDSIZE;
6581       if (!StyleMargin()->HasBlockAxisAuto(aWM)) {
6582         auto blockAxisAlignment =
6583             isOrthogonal ? StylePosition()->UsedJustifySelf(alignCB->Style())._0
6584                          : StylePosition()->UsedAlignSelf(alignCB->Style())._0;
6585         stretch = blockAxisAlignment == StyleAlignFlags::STRETCH ||
6586                   (blockAxisAlignment == StyleAlignFlags::NORMAL &&
6587                    !mayUseAspectRatio);
6588       }
6589 
6590       // Apply the preferred aspect ratio for alignments other than *stretch*
6591       // and *normal without aspect ratio*.
6592       // The spec says all other values should size the items as fit-content,
6593       // and the intrinsic size should respect the preferred aspect ratio, so
6594       // we also apply aspect ratio for all other values.
6595       // https://drafts.csswg.org/css-grid/#grid-item-sizing
6596       if (!stretch && mayUseAspectRatio) {
6597         result.BSize(aWM) = aspectRatio.ComputeRatioDependentSize(
6598             LogicalAxis::eLogicalAxisBlock, aWM, result.ISize(aWM),
6599             boxSizingAdjust);
6600         MOZ_ASSERT(aspectRatioUsage == AspectRatioUsage::None);
6601         aspectRatioUsage = AspectRatioUsage::ToComputeBSize;
6602       }
6603 
6604       if (stretch || aFlags.contains(ComputeSizeFlag::BClampMarginBoxMinSize)) {
6605         auto bSizeToFillCB =
6606             std::max(nscoord(0),
6607                      cbSize - aBorderPadding.BSize(aWM) - aMargin.BSize(aWM));
6608         if (stretch || (result.BSize(aWM) != NS_UNCONSTRAINEDSIZE &&
6609                         result.BSize(aWM) > bSizeToFillCB)) {
6610           result.BSize(aWM) = bSizeToFillCB;
6611         }
6612       }
6613     }
6614   } else if (aspectRatio) {
6615     // If both inline and block dimensions are auto, the block axis is the
6616     // ratio-dependent axis by default.
6617     // If we have a super large inline size, aspect-ratio should still be
6618     // applied (so aspectRatioUsage flag is set as expected). That's why we
6619     // apply aspect-ratio unconditionally for auto block size here.
6620     result.BSize(aWM) = aspectRatio.ComputeRatioDependentSize(
6621         LogicalAxis::eLogicalAxisBlock, aWM, result.ISize(aWM),
6622         boxSizingAdjust);
6623     MOZ_ASSERT(aspectRatioUsage == AspectRatioUsage::None);
6624     aspectRatioUsage = AspectRatioUsage::ToComputeBSize;
6625   }
6626 
6627   if (result.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
6628     const bool isFlexItemBlockAxisMainAxis =
6629         isFlexItem && flexMainAxis == eLogicalAxisBlock;
6630     if (!isAutoMaxBSize && !isFlexItemBlockAxisMainAxis) {
6631       nscoord maxBSize = nsLayoutUtils::ComputeBSizeValue(
6632           aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
6633           maxBSizeCoord.AsLengthPercentage());
6634       result.BSize(aWM) = std::min(maxBSize, result.BSize(aWM));
6635     }
6636 
6637     if (!isAutoMinBSize && !isFlexItemBlockAxisMainAxis) {
6638       nscoord minBSize = nsLayoutUtils::ComputeBSizeValue(
6639           aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
6640           minBSizeCoord.AsLengthPercentage());
6641       result.BSize(aWM) = std::max(minBSize, result.BSize(aWM));
6642     }
6643   }
6644 
6645   if (IsThemed(disp)) {
6646     LayoutDeviceIntSize widget;
6647     bool canOverride = true;
6648     nsPresContext* presContext = PresContext();
6649     presContext->Theme()->GetMinimumWidgetSize(
6650         presContext, this, disp->EffectiveAppearance(), &widget, &canOverride);
6651 
6652     // Convert themed widget's physical dimensions to logical coords
6653     LogicalSize size(aWM,
6654                      nsSize(presContext->DevPixelsToAppUnits(widget.width),
6655                             presContext->DevPixelsToAppUnits(widget.height)));
6656 
6657     // GetMinimumWidgetSize() returns border-box; we need content-box.
6658     size -= aBorderPadding;
6659 
6660     if (size.BSize(aWM) > result.BSize(aWM) || !canOverride) {
6661       result.BSize(aWM) = size.BSize(aWM);
6662     }
6663     if (size.ISize(aWM) > result.ISize(aWM) || !canOverride) {
6664       result.ISize(aWM) = size.ISize(aWM);
6665     }
6666   }
6667 
6668   result.ISize(aWM) = std::max(0, result.ISize(aWM));
6669   result.BSize(aWM) = std::max(0, result.BSize(aWM));
6670 
6671   return {result, aspectRatioUsage};
6672 }
6673 
ComputeTightBounds(DrawTarget * aDrawTarget) const6674 nsRect nsIFrame::ComputeTightBounds(DrawTarget* aDrawTarget) const {
6675   return InkOverflowRect();
6676 }
6677 
6678 /* virtual */
GetPrefWidthTightBounds(gfxContext * aContext,nscoord * aX,nscoord * aXMost)6679 nsresult nsIFrame::GetPrefWidthTightBounds(gfxContext* aContext, nscoord* aX,
6680                                            nscoord* aXMost) {
6681   return NS_ERROR_NOT_IMPLEMENTED;
6682 }
6683 
6684 /* virtual */
ComputeAutoSize(gfxContext * aRenderingContext,WritingMode aWM,const mozilla::LogicalSize & aCBSize,nscoord aAvailableISize,const mozilla::LogicalSize & aMargin,const mozilla::LogicalSize & aBorderPadding,const StyleSizeOverrides & aSizeOverrides,ComputeSizeFlags aFlags)6685 LogicalSize nsIFrame::ComputeAutoSize(
6686     gfxContext* aRenderingContext, WritingMode aWM,
6687     const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize,
6688     const mozilla::LogicalSize& aMargin,
6689     const mozilla::LogicalSize& aBorderPadding,
6690     const StyleSizeOverrides& aSizeOverrides, ComputeSizeFlags aFlags) {
6691   // Use basic shrink-wrapping as a default implementation.
6692   LogicalSize result(aWM, 0xdeadbeef, NS_UNCONSTRAINEDSIZE);
6693 
6694   // don't bother setting it if the result won't be used
6695   const auto& styleISize = aSizeOverrides.mStyleISize
6696                                ? *aSizeOverrides.mStyleISize
6697                                : StylePosition()->ISize(aWM);
6698   if (styleISize.IsAuto()) {
6699     nscoord availBased =
6700         aAvailableISize - aMargin.ISize(aWM) - aBorderPadding.ISize(aWM);
6701     result.ISize(aWM) = ShrinkWidthToFit(aRenderingContext, availBased, aFlags);
6702   }
6703   return result;
6704 }
6705 
ShrinkWidthToFit(gfxContext * aRenderingContext,nscoord aISizeInCB,ComputeSizeFlags aFlags)6706 nscoord nsIFrame::ShrinkWidthToFit(gfxContext* aRenderingContext,
6707                                    nscoord aISizeInCB,
6708                                    ComputeSizeFlags aFlags) {
6709   // If we're a container for font size inflation, then shrink
6710   // wrapping inside of us should not apply font size inflation.
6711   AutoMaybeDisableFontInflation an(this);
6712 
6713   nscoord result;
6714   nscoord minISize = GetMinISize(aRenderingContext);
6715   if (minISize > aISizeInCB) {
6716     const bool clamp = aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize);
6717     result = MOZ_UNLIKELY(clamp) ? aISizeInCB : minISize;
6718   } else {
6719     nscoord prefISize = GetPrefISize(aRenderingContext);
6720     if (prefISize > aISizeInCB) {
6721       result = aISizeInCB;
6722     } else {
6723       result = prefISize;
6724     }
6725   }
6726   return result;
6727 }
6728 
ComputeInlineSizeFromAspectRatio(WritingMode aWM,const LogicalSize & aCBSize,const LogicalSize & aContentEdgeToBoxSizing,const StyleSizeOverrides & aSizeOverrides,ComputeSizeFlags aFlags) const6729 Maybe<nscoord> nsIFrame::ComputeInlineSizeFromAspectRatio(
6730     WritingMode aWM, const LogicalSize& aCBSize,
6731     const LogicalSize& aContentEdgeToBoxSizing,
6732     const StyleSizeOverrides& aSizeOverrides, ComputeSizeFlags aFlags) const {
6733   // FIXME: Bug 1670151: Use GetAspectRatio() to cover replaced elements (and
6734   // then we can drop the check of eSupportsAspectRatio).
6735   const AspectRatio aspectRatio =
6736       aSizeOverrides.mAspectRatio
6737           ? *aSizeOverrides.mAspectRatio
6738           : StylePosition()->mAspectRatio.ToLayoutRatio();
6739   if (!IsFrameOfType(eSupportsAspectRatio) || !aspectRatio) {
6740     return Nothing();
6741   }
6742 
6743   const StyleSize& styleBSize = aSizeOverrides.mStyleBSize
6744                                     ? *aSizeOverrides.mStyleBSize
6745                                     : StylePosition()->BSize(aWM);
6746   if (aFlags.contains(ComputeSizeFlag::UseAutoBSize) ||
6747       nsLayoutUtils::IsAutoBSize(styleBSize, aCBSize.BSize(aWM))) {
6748     return Nothing();
6749   }
6750 
6751   MOZ_ASSERT(styleBSize.IsLengthPercentage());
6752   nscoord bSize = nsLayoutUtils::ComputeBSizeValue(
6753       aCBSize.BSize(aWM), aContentEdgeToBoxSizing.BSize(aWM),
6754       styleBSize.AsLengthPercentage());
6755   return Some(aspectRatio.ComputeRatioDependentSize(
6756       LogicalAxis::eLogicalAxisInline, aWM, bSize, aContentEdgeToBoxSizing));
6757 }
6758 
ComputeISizeValue(gfxContext * aRenderingContext,const WritingMode aWM,const LogicalSize & aContainingBlockSize,const LogicalSize & aContentEdgeToBoxSizing,nscoord aBoxSizingToMarginEdge,ExtremumLength aSize,Maybe<nscoord> aAvailableISizeOverride,const StyleSizeOverrides & aSizeOverrides,ComputeSizeFlags aFlags)6759 nsIFrame::ISizeComputationResult nsIFrame::ComputeISizeValue(
6760     gfxContext* aRenderingContext, const WritingMode aWM,
6761     const LogicalSize& aContainingBlockSize,
6762     const LogicalSize& aContentEdgeToBoxSizing, nscoord aBoxSizingToMarginEdge,
6763     ExtremumLength aSize, Maybe<nscoord> aAvailableISizeOverride,
6764     const StyleSizeOverrides& aSizeOverrides, ComputeSizeFlags aFlags) {
6765   // If 'this' is a container for font size inflation, then shrink
6766   // wrapping inside of it should not apply font size inflation.
6767   AutoMaybeDisableFontInflation an(this);
6768   // If we have an aspect-ratio and a definite block size, we resolve the
6769   // min-content and max-content size by the aspect-ratio and the block size.
6770   // https://github.com/w3c/csswg-drafts/issues/5032
6771   Maybe<nscoord> intrinsicSizeFromAspectRatio =
6772       aSize == ExtremumLength::MozAvailable
6773           ? Nothing()
6774           : ComputeInlineSizeFromAspectRatio(aWM, aContainingBlockSize,
6775                                              aContentEdgeToBoxSizing,
6776                                              aSizeOverrides, aFlags);
6777   nscoord result;
6778   switch (aSize) {
6779     case ExtremumLength::MaxContent:
6780       result = intrinsicSizeFromAspectRatio ? *intrinsicSizeFromAspectRatio
6781                                             : GetPrefISize(aRenderingContext);
6782       NS_ASSERTION(result >= 0, "inline-size less than zero");
6783       return {result, intrinsicSizeFromAspectRatio
6784                           ? AspectRatioUsage::ToComputeISize
6785                           : AspectRatioUsage::None};
6786     case ExtremumLength::MinContent:
6787       result = intrinsicSizeFromAspectRatio ? *intrinsicSizeFromAspectRatio
6788                                             : GetMinISize(aRenderingContext);
6789       NS_ASSERTION(result >= 0, "inline-size less than zero");
6790       if (MOZ_UNLIKELY(
6791               aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize))) {
6792         auto available =
6793             aContainingBlockSize.ISize(aWM) -
6794             (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing.ISize(aWM));
6795         result = std::min(available, result);
6796       }
6797       return {result, intrinsicSizeFromAspectRatio
6798                           ? AspectRatioUsage::ToComputeISize
6799                           : AspectRatioUsage::None};
6800     case ExtremumLength::FitContentFunction:
6801     case ExtremumLength::FitContent: {
6802       nscoord pref = NS_UNCONSTRAINEDSIZE;
6803       nscoord min = 0;
6804       if (intrinsicSizeFromAspectRatio) {
6805         // The min-content and max-content size are identical and equal to the
6806         // size computed from the block size and the aspect ratio.
6807         pref = min = *intrinsicSizeFromAspectRatio;
6808       } else {
6809         pref = GetPrefISize(aRenderingContext);
6810         min = GetMinISize(aRenderingContext);
6811       }
6812 
6813       nscoord fill = aAvailableISizeOverride
6814                          ? *aAvailableISizeOverride
6815                          : aContainingBlockSize.ISize(aWM) -
6816                                (aBoxSizingToMarginEdge +
6817                                 aContentEdgeToBoxSizing.ISize(aWM));
6818 
6819       if (MOZ_UNLIKELY(
6820               aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize))) {
6821         min = std::min(min, fill);
6822       }
6823       result = std::max(min, std::min(pref, fill));
6824       NS_ASSERTION(result >= 0, "inline-size less than zero");
6825       return {result};
6826     }
6827     case ExtremumLength::MozAvailable:
6828       return {aContainingBlockSize.ISize(aWM) -
6829               (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing.ISize(aWM))};
6830   }
6831   MOZ_ASSERT_UNREACHABLE("Unknown extremum length?");
6832   return {};
6833 }
6834 
ComputeISizeValue(const WritingMode aWM,const LogicalSize & aContainingBlockSize,const LogicalSize & aContentEdgeToBoxSizing,const LengthPercentage & aSize)6835 nscoord nsIFrame::ComputeISizeValue(const WritingMode aWM,
6836                                     const LogicalSize& aContainingBlockSize,
6837                                     const LogicalSize& aContentEdgeToBoxSizing,
6838                                     const LengthPercentage& aSize) {
6839   LAYOUT_WARN_IF_FALSE(
6840       aContainingBlockSize.ISize(aWM) != NS_UNCONSTRAINEDSIZE,
6841       "have unconstrained inline-size; this should only result from "
6842       "very large sizes, not attempts at intrinsic inline-size "
6843       "calculation");
6844   NS_ASSERTION(aContainingBlockSize.ISize(aWM) >= 0,
6845                "inline-size less than zero");
6846 
6847   nscoord result = aSize.Resolve(aContainingBlockSize.ISize(aWM));
6848   // The result of a calc() expression might be less than 0; we
6849   // should clamp at runtime (below).  (Percentages and coords that
6850   // are less than 0 have already been dropped by the parser.)
6851   result -= aContentEdgeToBoxSizing.ISize(aWM);
6852   return std::max(0, result);
6853 }
6854 
DidReflow(nsPresContext * aPresContext,const ReflowInput * aReflowInput)6855 void nsIFrame::DidReflow(nsPresContext* aPresContext,
6856                          const ReflowInput* aReflowInput) {
6857   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("nsIFrame::DidReflow"));
6858 
6859   SVGObserverUtils::InvalidateDirectRenderingObservers(
6860       this, SVGObserverUtils::INVALIDATE_REFLOW);
6861 
6862   RemoveStateBits(NS_FRAME_IN_REFLOW | NS_FRAME_FIRST_REFLOW |
6863                   NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
6864 
6865   // Clear bits that were used in ReflowInput::InitResizeFlags (see
6866   // comment there for why we can't clear it there).
6867   SetHasBSizeChange(false);
6868   SetHasPaddingChange(false);
6869 
6870   // Notify the percent bsize observer if there is a percent bsize.
6871   // The observer may be able to initiate another reflow with a computed
6872   // bsize. This happens in the case where a table cell has no computed
6873   // bsize but can fabricate one when the cell bsize is known.
6874   if (aReflowInput && aReflowInput->mPercentBSizeObserver && !GetPrevInFlow()) {
6875     const auto& bsize =
6876         aReflowInput->mStylePosition->BSize(aReflowInput->GetWritingMode());
6877     if (bsize.HasPercent()) {
6878       aReflowInput->mPercentBSizeObserver->NotifyPercentBSize(*aReflowInput);
6879     }
6880   }
6881 
6882   aPresContext->ReflowedFrame();
6883 
6884 #ifdef ACCESSIBILITY
6885   if (nsAccessibilityService* accService =
6886           PresShell::GetAccessibilityService()) {
6887     accService->NotifyOfPossibleBoundsChange(PresShell(), mContent);
6888   }
6889 #endif
6890 }
6891 
FinishReflowWithAbsoluteFrames(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus,bool aConstrainBSize)6892 void nsIFrame::FinishReflowWithAbsoluteFrames(nsPresContext* aPresContext,
6893                                               ReflowOutput& aDesiredSize,
6894                                               const ReflowInput& aReflowInput,
6895                                               nsReflowStatus& aStatus,
6896                                               bool aConstrainBSize) {
6897   ReflowAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput, aStatus,
6898                        aConstrainBSize);
6899 
6900   FinishAndStoreOverflow(&aDesiredSize, aReflowInput.mStyleDisplay);
6901 }
6902 
ReflowAbsoluteFrames(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus,bool aConstrainBSize)6903 void nsIFrame::ReflowAbsoluteFrames(nsPresContext* aPresContext,
6904                                     ReflowOutput& aDesiredSize,
6905                                     const ReflowInput& aReflowInput,
6906                                     nsReflowStatus& aStatus,
6907                                     bool aConstrainBSize) {
6908   if (HasAbsolutelyPositionedChildren()) {
6909     nsAbsoluteContainingBlock* absoluteContainer = GetAbsoluteContainingBlock();
6910 
6911     // Let the absolutely positioned container reflow any absolutely positioned
6912     // child frames that need to be reflowed
6913 
6914     // The containing block for the abs pos kids is formed by our padding edge.
6915     nsMargin usedBorder = GetUsedBorder();
6916     nscoord containingBlockWidth =
6917         std::max(0, aDesiredSize.Width() - usedBorder.LeftRight());
6918     nscoord containingBlockHeight =
6919         std::max(0, aDesiredSize.Height() - usedBorder.TopBottom());
6920     nsContainerFrame* container = do_QueryFrame(this);
6921     NS_ASSERTION(container,
6922                  "Abs-pos children only supported on container frames for now");
6923 
6924     nsRect containingBlock(0, 0, containingBlockWidth, containingBlockHeight);
6925     AbsPosReflowFlags flags =
6926         AbsPosReflowFlags::CBWidthAndHeightChanged;  // XXX could be optimized
6927     if (aConstrainBSize) {
6928       flags |= AbsPosReflowFlags::ConstrainHeight;
6929     }
6930     absoluteContainer->Reflow(container, aPresContext, aReflowInput, aStatus,
6931                               containingBlock, flags,
6932                               &aDesiredSize.mOverflowAreas);
6933   }
6934 }
6935 
6936 /* virtual */
CanContinueTextRun() const6937 bool nsIFrame::CanContinueTextRun() const {
6938   // By default, a frame will *not* allow a text run to be continued
6939   // through it.
6940   return false;
6941 }
6942 
Reflow(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)6943 void nsIFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
6944                       const ReflowInput& aReflowInput,
6945                       nsReflowStatus& aStatus) {
6946   MarkInReflow();
6947   DO_GLOBAL_REFLOW_COUNT("nsFrame");
6948   MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
6949   aDesiredSize.ClearSize();
6950   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
6951 }
6952 
IsContentDisabled() const6953 bool nsIFrame::IsContentDisabled() const {
6954   // FIXME(emilio): Doing this via CSS means callers must ensure the style is up
6955   // to date, and they don't!
6956   if (StyleUI()->UserInput() == StyleUserInput::None) {
6957     return true;
6958   }
6959 
6960   auto* element = nsGenericHTMLElement::FromNodeOrNull(GetContent());
6961   return element && element->IsDisabled();
6962 }
6963 
CharacterDataChanged(const CharacterDataChangeInfo &)6964 nsresult nsIFrame::CharacterDataChanged(const CharacterDataChangeInfo&) {
6965   MOZ_ASSERT_UNREACHABLE("should only be called for text frames");
6966   return NS_OK;
6967 }
6968 
AttributeChanged(int32_t aNameSpaceID,nsAtom * aAttribute,int32_t aModType)6969 nsresult nsIFrame::AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute,
6970                                     int32_t aModType) {
6971   return NS_OK;
6972 }
6973 
6974 // Flow member functions
6975 
GetPrevContinuation() const6976 nsIFrame* nsIFrame::GetPrevContinuation() const { return nullptr; }
6977 
SetPrevContinuation(nsIFrame * aPrevContinuation)6978 void nsIFrame::SetPrevContinuation(nsIFrame* aPrevContinuation) {
6979   MOZ_ASSERT(false, "not splittable");
6980 }
6981 
GetNextContinuation() const6982 nsIFrame* nsIFrame::GetNextContinuation() const { return nullptr; }
6983 
SetNextContinuation(nsIFrame *)6984 void nsIFrame::SetNextContinuation(nsIFrame*) {
6985   MOZ_ASSERT(false, "not splittable");
6986 }
6987 
GetPrevInFlow() const6988 nsIFrame* nsIFrame::GetPrevInFlow() const { return nullptr; }
6989 
SetPrevInFlow(nsIFrame * aPrevInFlow)6990 void nsIFrame::SetPrevInFlow(nsIFrame* aPrevInFlow) {
6991   MOZ_ASSERT(false, "not splittable");
6992 }
6993 
GetNextInFlow() const6994 nsIFrame* nsIFrame::GetNextInFlow() const { return nullptr; }
6995 
SetNextInFlow(nsIFrame *)6996 void nsIFrame::SetNextInFlow(nsIFrame*) { MOZ_ASSERT(false, "not splittable"); }
6997 
GetTailContinuation()6998 nsIFrame* nsIFrame::GetTailContinuation() {
6999   nsIFrame* frame = this;
7000   while (frame->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
7001     frame = frame->GetPrevContinuation();
7002     NS_ASSERTION(frame, "first continuation can't be overflow container");
7003   }
7004   for (nsIFrame* next = frame->GetNextContinuation();
7005        next && !next->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
7006        next = frame->GetNextContinuation()) {
7007     frame = next;
7008   }
7009 
7010   MOZ_ASSERT(frame, "illegal state in continuation chain.");
7011   return frame;
7012 }
7013 
7014 // Associated view object
SetView(nsView * aView)7015 void nsIFrame::SetView(nsView* aView) {
7016   if (aView) {
7017     aView->SetFrame(this);
7018 
7019 #ifdef DEBUG
7020     LayoutFrameType frameType = Type();
7021     NS_ASSERTION(frameType == LayoutFrameType::SubDocument ||
7022                      frameType == LayoutFrameType::ListControl ||
7023                      frameType == LayoutFrameType::Viewport ||
7024                      frameType == LayoutFrameType::MenuPopup,
7025                  "Only specific frame types can have an nsView");
7026 #endif
7027 
7028     // Store the view on the frame.
7029     SetViewInternal(aView);
7030 
7031     // Set the frame state bit that says the frame has a view
7032     AddStateBits(NS_FRAME_HAS_VIEW);
7033 
7034     // Let all of the ancestors know they have a descendant with a view.
7035     for (nsIFrame* f = GetParent();
7036          f && !f->HasAnyStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
7037          f = f->GetParent())
7038       f->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
7039   } else {
7040     MOZ_ASSERT_UNREACHABLE("Destroying a view while the frame is alive?");
7041     RemoveStateBits(NS_FRAME_HAS_VIEW);
7042     SetViewInternal(nullptr);
7043   }
7044 }
7045 
7046 // Find the first geometric parent that has a view
GetAncestorWithView() const7047 nsIFrame* nsIFrame::GetAncestorWithView() const {
7048   for (nsIFrame* f = GetParent(); nullptr != f; f = f->GetParent()) {
7049     if (f->HasView()) {
7050       return f;
7051     }
7052   }
7053   return nullptr;
7054 }
7055 
7056 template <nsPoint (nsIFrame::*PositionGetter)() const>
OffsetCalculator(const nsIFrame * aThis,const nsIFrame * aOther)7057 static nsPoint OffsetCalculator(const nsIFrame* aThis, const nsIFrame* aOther) {
7058   MOZ_ASSERT(aOther, "Must have frame for destination coordinate system!");
7059 
7060   NS_ASSERTION(aThis->PresContext() == aOther->PresContext(),
7061                "GetOffsetTo called on frames in different documents");
7062 
7063   nsPoint offset(0, 0);
7064   const nsIFrame* f;
7065   for (f = aThis; f != aOther && f; f = f->GetParent()) {
7066     offset += (f->*PositionGetter)();
7067   }
7068 
7069   if (f != aOther) {
7070     // Looks like aOther wasn't an ancestor of |this|.  So now we have
7071     // the root-frame-relative position of |this| in |offset|.  Convert back
7072     // to the coordinates of aOther
7073     while (aOther) {
7074       offset -= (aOther->*PositionGetter)();
7075       aOther = aOther->GetParent();
7076     }
7077   }
7078 
7079   return offset;
7080 }
7081 
GetOffsetTo(const nsIFrame * aOther) const7082 nsPoint nsIFrame::GetOffsetTo(const nsIFrame* aOther) const {
7083   return OffsetCalculator<&nsIFrame::GetPosition>(this, aOther);
7084 }
7085 
GetOffsetToIgnoringScrolling(const nsIFrame * aOther) const7086 nsPoint nsIFrame::GetOffsetToIgnoringScrolling(const nsIFrame* aOther) const {
7087   return OffsetCalculator<&nsIFrame::GetPositionIgnoringScrolling>(this,
7088                                                                    aOther);
7089 }
7090 
GetOffsetToCrossDoc(const nsIFrame * aOther) const7091 nsPoint nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther) const {
7092   return GetOffsetToCrossDoc(aOther, PresContext()->AppUnitsPerDevPixel());
7093 }
7094 
GetOffsetToCrossDoc(const nsIFrame * aOther,const int32_t aAPD) const7095 nsPoint nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther,
7096                                       const int32_t aAPD) const {
7097   MOZ_ASSERT(aOther, "Must have frame for destination coordinate system!");
7098   NS_ASSERTION(PresContext()->GetRootPresContext() ==
7099                    aOther->PresContext()->GetRootPresContext(),
7100                "trying to get the offset between frames in different document "
7101                "hierarchies?");
7102   if (PresContext()->GetRootPresContext() !=
7103       aOther->PresContext()->GetRootPresContext()) {
7104     // crash right away, we are almost certainly going to crash anyway.
7105     MOZ_CRASH(
7106         "trying to get the offset between frames in different "
7107         "document hierarchies?");
7108   }
7109 
7110   const nsIFrame* root = nullptr;
7111   // offset will hold the final offset
7112   // docOffset holds the currently accumulated offset at the current APD, it
7113   // will be converted and added to offset when the current APD changes.
7114   nsPoint offset(0, 0), docOffset(0, 0);
7115   const nsIFrame* f = this;
7116   int32_t currAPD = PresContext()->AppUnitsPerDevPixel();
7117   while (f && f != aOther) {
7118     docOffset += f->GetPosition();
7119     nsIFrame* parent = f->GetParent();
7120     if (parent) {
7121       f = parent;
7122     } else {
7123       nsPoint newOffset(0, 0);
7124       root = f;
7125       f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f, &newOffset);
7126       int32_t newAPD = f ? f->PresContext()->AppUnitsPerDevPixel() : 0;
7127       if (!f || newAPD != currAPD) {
7128         // Convert docOffset to the right APD and add it to offset.
7129         offset += docOffset.ScaleToOtherAppUnits(currAPD, aAPD);
7130         docOffset.x = docOffset.y = 0;
7131       }
7132       currAPD = newAPD;
7133       docOffset += newOffset;
7134     }
7135   }
7136   if (f == aOther) {
7137     offset += docOffset.ScaleToOtherAppUnits(currAPD, aAPD);
7138   } else {
7139     // Looks like aOther wasn't an ancestor of |this|.  So now we have
7140     // the root-document-relative position of |this| in |offset|. Subtract the
7141     // root-document-relative position of |aOther| from |offset|.
7142     // This call won't try to recurse again because root is an ancestor of
7143     // aOther.
7144     nsPoint negOffset = aOther->GetOffsetToCrossDoc(root, aAPD);
7145     offset -= negOffset;
7146   }
7147 
7148   return offset;
7149 }
7150 
GetScreenRect() const7151 CSSIntRect nsIFrame::GetScreenRect() const {
7152   return CSSIntRect::FromAppUnitsToNearest(GetScreenRectInAppUnits());
7153 }
7154 
GetScreenRectInAppUnits() const7155 nsRect nsIFrame::GetScreenRectInAppUnits() const {
7156   nsPresContext* presContext = PresContext();
7157   nsIFrame* rootFrame = presContext->PresShell()->GetRootFrame();
7158   nsPoint rootScreenPos(0, 0);
7159   nsPoint rootFrameOffsetInParent(0, 0);
7160   nsIFrame* rootFrameParent = nsLayoutUtils::GetCrossDocParentFrameInProcess(
7161       rootFrame, &rootFrameOffsetInParent);
7162   if (rootFrameParent) {
7163     nsRect parentScreenRectAppUnits =
7164         rootFrameParent->GetScreenRectInAppUnits();
7165     nsPresContext* parentPresContext = rootFrameParent->PresContext();
7166     double parentScale = double(presContext->AppUnitsPerDevPixel()) /
7167                          parentPresContext->AppUnitsPerDevPixel();
7168     nsPoint rootPt =
7169         parentScreenRectAppUnits.TopLeft() + rootFrameOffsetInParent;
7170     rootScreenPos.x = NS_round(parentScale * rootPt.x);
7171     rootScreenPos.y = NS_round(parentScale * rootPt.y);
7172   } else {
7173     nsCOMPtr<nsIWidget> rootWidget =
7174         presContext->PresShell()->GetViewManager()->GetRootWidget();
7175     if (rootWidget) {
7176       LayoutDeviceIntPoint rootDevPx = rootWidget->WidgetToScreenOffset();
7177       rootScreenPos.x = presContext->DevPixelsToAppUnits(rootDevPx.x);
7178       rootScreenPos.y = presContext->DevPixelsToAppUnits(rootDevPx.y);
7179     }
7180   }
7181 
7182   return nsRect(rootScreenPos + GetOffsetTo(rootFrame), GetSize());
7183 }
7184 
7185 // Returns the offset from this frame to the closest geometric parent that
7186 // has a view. Also returns the containing view or null in case of error
GetOffsetFromView(nsPoint & aOffset,nsView ** aView) const7187 void nsIFrame::GetOffsetFromView(nsPoint& aOffset, nsView** aView) const {
7188   MOZ_ASSERT(nullptr != aView, "null OUT parameter pointer");
7189   nsIFrame* frame = const_cast<nsIFrame*>(this);
7190 
7191   *aView = nullptr;
7192   aOffset.MoveTo(0, 0);
7193   do {
7194     aOffset += frame->GetPosition();
7195     frame = frame->GetParent();
7196   } while (frame && !frame->HasView());
7197 
7198   if (frame) {
7199     *aView = frame->GetView();
7200   }
7201 }
7202 
GetNearestWidget() const7203 nsIWidget* nsIFrame::GetNearestWidget() const {
7204   return GetClosestView()->GetNearestWidget(nullptr);
7205 }
7206 
GetNearestWidget(nsPoint & aOffset) const7207 nsIWidget* nsIFrame::GetNearestWidget(nsPoint& aOffset) const {
7208   nsPoint offsetToView;
7209   nsPoint offsetToWidget;
7210   nsIWidget* widget =
7211       GetClosestView(&offsetToView)->GetNearestWidget(&offsetToWidget);
7212   aOffset = offsetToView + offsetToWidget;
7213   return widget;
7214 }
7215 
GetTransformMatrix(ViewportType aViewportType,RelativeTo aStopAtAncestor,nsIFrame ** aOutAncestor,uint32_t aFlags) const7216 Matrix4x4Flagged nsIFrame::GetTransformMatrix(ViewportType aViewportType,
7217                                               RelativeTo aStopAtAncestor,
7218                                               nsIFrame** aOutAncestor,
7219                                               uint32_t aFlags) const {
7220   MOZ_ASSERT(aOutAncestor, "Need a place to put the ancestor!");
7221 
7222   /* If we're transformed, we want to hand back the combination
7223    * transform/translate matrix that will apply our current transform, then
7224    * shift us to our parent.
7225    */
7226   const bool isTransformed = IsTransformed();
7227   const nsIFrame* zoomedContentRoot = nullptr;
7228   if (aStopAtAncestor.mViewportType == ViewportType::Visual) {
7229     zoomedContentRoot = ViewportUtils::IsZoomedContentRoot(this);
7230     if (zoomedContentRoot) {
7231       MOZ_ASSERT(aViewportType != ViewportType::Visual);
7232     }
7233   }
7234 
7235   if (isTransformed || zoomedContentRoot) {
7236     Matrix4x4 result;
7237     int32_t scaleFactor =
7238         ((aFlags & IN_CSS_UNITS) ? AppUnitsPerCSSPixel()
7239                                  : PresContext()->AppUnitsPerDevPixel());
7240 
7241     /* Compute the delta to the parent, which we need because we are converting
7242      * coordinates to our parent.
7243      */
7244     if (isTransformed) {
7245       NS_ASSERTION(nsLayoutUtils::GetCrossDocParentFrameInProcess(this),
7246                    "Cannot transform the viewport frame!");
7247 
7248       result = result * nsDisplayTransform::GetResultingTransformMatrix(
7249                             this, nsPoint(0, 0), scaleFactor,
7250                             nsDisplayTransform::INCLUDE_PERSPECTIVE |
7251                                 nsDisplayTransform::OFFSET_BY_ORIGIN);
7252     }
7253 
7254     // The offset from a zoomed content root to its parent (e.g. from
7255     // a canvas frame to a scroll frame) is in layout coordinates, so
7256     // apply it before applying any layout-to-visual transform.
7257     *aOutAncestor = nsLayoutUtils::GetCrossDocParentFrameInProcess(this);
7258     nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
7259     /* Combine the raw transform with a translation to our parent. */
7260     result.PostTranslate(NSAppUnitsToFloatPixels(delta.x, scaleFactor),
7261                          NSAppUnitsToFloatPixels(delta.y, scaleFactor), 0.0f);
7262 
7263     if (zoomedContentRoot) {
7264       Matrix4x4 layoutToVisual;
7265       ScrollableLayerGuid::ViewID targetScrollId =
7266           nsLayoutUtils::FindOrCreateIDFor(zoomedContentRoot->GetContent());
7267       if (aFlags & nsIFrame::IN_CSS_UNITS) {
7268         layoutToVisual =
7269             ViewportUtils::GetVisualToLayoutTransform(targetScrollId)
7270                 .Inverse()
7271                 .ToUnknownMatrix();
7272       } else {
7273         layoutToVisual =
7274             ViewportUtils::GetVisualToLayoutTransform<LayoutDevicePixel>(
7275                 targetScrollId)
7276                 .Inverse()
7277                 .ToUnknownMatrix();
7278       }
7279       result = result * layoutToVisual;
7280     }
7281 
7282     return result;
7283   }
7284 
7285   if (nsLayoutUtils::IsPopup(this) && IsListControlFrame()) {
7286     nsPresContext* presContext = PresContext();
7287     nsIFrame* docRootFrame = presContext->PresShell()->GetRootFrame();
7288 
7289     // Compute a matrix that transforms from the popup widget to the toplevel
7290     // widget. We use the widgets because they're the simplest and most
7291     // accurate approach --- this should work no matter how the widget position
7292     // was chosen.
7293     nsIWidget* widget = GetView()->GetWidget();
7294     nsPresContext* rootPresContext = PresContext()->GetRootPresContext();
7295     // Maybe the widget hasn't been created yet? Popups without widgets are
7296     // treated as regular frames. That should work since they'll be rendered
7297     // as part of the page if they're rendered at all.
7298     if (widget && rootPresContext) {
7299       nsIWidget* toplevel = rootPresContext->GetNearestWidget();
7300       if (toplevel) {
7301         LayoutDeviceIntRect screenBounds = widget->GetClientBounds();
7302         LayoutDeviceIntRect toplevelScreenBounds = toplevel->GetClientBounds();
7303         LayoutDeviceIntPoint translation =
7304             screenBounds.TopLeft() - toplevelScreenBounds.TopLeft();
7305 
7306         Matrix4x4 transformToTop;
7307         transformToTop._41 = translation.x;
7308         transformToTop._42 = translation.y;
7309 
7310         *aOutAncestor = docRootFrame;
7311         Matrix4x4 docRootTransformToTop =
7312             nsLayoutUtils::GetTransformToAncestor(RelativeTo{docRootFrame},
7313                                                   RelativeTo{nullptr})
7314                 .GetMatrix();
7315         if (docRootTransformToTop.IsSingular()) {
7316           NS_WARNING(
7317               "Containing document is invisible, we can't compute a valid "
7318               "transform");
7319         } else {
7320           docRootTransformToTop.Invert();
7321           return transformToTop * docRootTransformToTop;
7322         }
7323       }
7324     }
7325   }
7326 
7327   *aOutAncestor = nsLayoutUtils::GetCrossDocParentFrameInProcess(this);
7328 
7329   /* Otherwise, we're not transformed.  In that case, we'll walk up the frame
7330    * tree until we either hit the root frame or something that may be
7331    * transformed.  We'll then change coordinates into that frame, since we're
7332    * guaranteed that nothing in-between can be transformed.  First, however,
7333    * we have to check to see if we have a parent.  If not, we'll set the
7334    * outparam to null (indicating that there's nothing left) and will hand back
7335    * the identity matrix.
7336    */
7337   if (!*aOutAncestor) return Matrix4x4();
7338 
7339   /* Keep iterating while the frame can't possibly be transformed. */
7340   const nsIFrame* current = this;
7341   auto shouldStopAt = [](const nsIFrame* aCurrent, nsIFrame* aAncestor,
7342                          uint32_t aFlags) {
7343     return aAncestor->IsTransformed() || nsLayoutUtils::IsPopup(aAncestor) ||
7344            ViewportUtils::IsZoomedContentRoot(aAncestor) ||
7345            ((aFlags & STOP_AT_STACKING_CONTEXT_AND_DISPLAY_PORT) &&
7346             (aAncestor->IsStackingContext() ||
7347              DisplayPortUtils::FrameHasDisplayPort(aAncestor, aCurrent)));
7348   };
7349   while (*aOutAncestor != aStopAtAncestor.mFrame &&
7350          !shouldStopAt(current, *aOutAncestor, aFlags)) {
7351     /* If no parent, stop iterating.  Otherwise, update the ancestor. */
7352     nsIFrame* parent =
7353         nsLayoutUtils::GetCrossDocParentFrameInProcess(*aOutAncestor);
7354     if (!parent) break;
7355 
7356     current = *aOutAncestor;
7357     *aOutAncestor = parent;
7358   }
7359 
7360   NS_ASSERTION(*aOutAncestor, "Somehow ended up with a null ancestor...?");
7361 
7362   /* Translate from this frame to our ancestor, if it exists.  That's the
7363    * entire transform, so we're done.
7364    */
7365   nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
7366   int32_t scaleFactor =
7367       ((aFlags & IN_CSS_UNITS) ? AppUnitsPerCSSPixel()
7368                                : PresContext()->AppUnitsPerDevPixel());
7369   return Matrix4x4::Translation(NSAppUnitsToFloatPixels(delta.x, scaleFactor),
7370                                 NSAppUnitsToFloatPixels(delta.y, scaleFactor),
7371                                 0.0f);
7372 }
7373 
InvalidateRenderingObservers(nsIFrame * aDisplayRoot,nsIFrame * aFrame,bool aFrameChanged=true)7374 static void InvalidateRenderingObservers(nsIFrame* aDisplayRoot,
7375                                          nsIFrame* aFrame,
7376                                          bool aFrameChanged = true) {
7377   MOZ_ASSERT(aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame));
7378   SVGObserverUtils::InvalidateDirectRenderingObservers(aFrame);
7379   nsIFrame* parent = aFrame;
7380   while (parent != aDisplayRoot &&
7381          (parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(parent)) &&
7382          !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
7383     SVGObserverUtils::InvalidateDirectRenderingObservers(parent);
7384   }
7385 
7386   if (!aFrameChanged) {
7387     return;
7388   }
7389 
7390   aFrame->MarkNeedsDisplayItemRebuild();
7391 }
7392 
SchedulePaintInternal(nsIFrame * aDisplayRoot,nsIFrame * aFrame,nsIFrame::PaintType aType=nsIFrame::PAINT_DEFAULT)7393 static void SchedulePaintInternal(
7394     nsIFrame* aDisplayRoot, nsIFrame* aFrame,
7395     nsIFrame::PaintType aType = nsIFrame::PAINT_DEFAULT) {
7396   MOZ_ASSERT(aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame));
7397   nsPresContext* pres = aDisplayRoot->PresContext()->GetRootPresContext();
7398 
7399   // No need to schedule a paint for an external document since they aren't
7400   // painted directly.
7401   if (!pres || (pres->Document() && pres->Document()->IsResourceDoc())) {
7402     return;
7403   }
7404   if (!pres->GetContainerWeak()) {
7405     NS_WARNING("Shouldn't call SchedulePaint in a detached pres context");
7406     return;
7407   }
7408 
7409   pres->PresShell()->ScheduleViewManagerFlush();
7410 
7411   if (aType == nsIFrame::PAINT_DEFAULT) {
7412     aDisplayRoot->AddStateBits(NS_FRAME_UPDATE_LAYER_TREE);
7413   }
7414 }
7415 
InvalidateFrameInternal(nsIFrame * aFrame,bool aHasDisplayItem,bool aRebuildDisplayItems)7416 static void InvalidateFrameInternal(nsIFrame* aFrame, bool aHasDisplayItem,
7417                                     bool aRebuildDisplayItems) {
7418   if (aHasDisplayItem) {
7419     aFrame->AddStateBits(NS_FRAME_NEEDS_PAINT);
7420   }
7421 
7422   if (aRebuildDisplayItems) {
7423     aFrame->MarkNeedsDisplayItemRebuild();
7424   }
7425   SVGObserverUtils::InvalidateDirectRenderingObservers(aFrame);
7426   bool needsSchedulePaint = false;
7427   if (nsLayoutUtils::IsPopup(aFrame)) {
7428     needsSchedulePaint = true;
7429   } else {
7430     nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
7431     while (parent &&
7432            !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
7433       if (aHasDisplayItem && !parent->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
7434         parent->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
7435       }
7436       SVGObserverUtils::InvalidateDirectRenderingObservers(parent);
7437 
7438       // If we're inside a popup, then we need to make sure that we
7439       // call schedule paint so that the NS_FRAME_UPDATE_LAYER_TREE
7440       // flag gets added to the popup display root frame.
7441       if (nsLayoutUtils::IsPopup(parent)) {
7442         needsSchedulePaint = true;
7443         break;
7444       }
7445       parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(parent);
7446     }
7447     if (!parent) {
7448       needsSchedulePaint = true;
7449     }
7450   }
7451   if (!aHasDisplayItem) {
7452     return;
7453   }
7454   if (needsSchedulePaint) {
7455     nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
7456     SchedulePaintInternal(displayRoot, aFrame);
7457   }
7458   if (aFrame->HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
7459     aFrame->RemoveProperty(nsIFrame::InvalidationRect());
7460     aFrame->RemoveStateBits(NS_FRAME_HAS_INVALID_RECT);
7461   }
7462 }
7463 
InvalidateFrameSubtree(bool aRebuildDisplayItems)7464 void nsIFrame::InvalidateFrameSubtree(bool aRebuildDisplayItems /* = true */) {
7465   InvalidateFrame(0, aRebuildDisplayItems);
7466 
7467   if (HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT)) {
7468     return;
7469   }
7470 
7471   AddStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT);
7472 
7473   for (const auto& childList : CrossDocChildLists()) {
7474     for (nsIFrame* child : childList.mList) {
7475       // Don't explicitly rebuild display items for our descendants,
7476       // since we should be marked and it implicitly includes all
7477       // descendants.
7478       child->InvalidateFrameSubtree(false);
7479     }
7480   }
7481 }
7482 
ClearInvalidationStateBits()7483 void nsIFrame::ClearInvalidationStateBits() {
7484   if (HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
7485     for (const auto& childList : CrossDocChildLists()) {
7486       for (nsIFrame* child : childList.mList) {
7487         child->ClearInvalidationStateBits();
7488       }
7489     }
7490   }
7491 
7492   RemoveStateBits(NS_FRAME_NEEDS_PAINT | NS_FRAME_DESCENDANT_NEEDS_PAINT |
7493                   NS_FRAME_ALL_DESCENDANTS_NEED_PAINT);
7494 }
7495 
HasRetainedDataFor(const nsIFrame * aFrame,uint32_t aDisplayItemKey)7496 bool HasRetainedDataFor(const nsIFrame* aFrame, uint32_t aDisplayItemKey) {
7497   if (RefPtr<WebRenderUserData> data =
7498           GetWebRenderUserData<WebRenderFallbackData>(aFrame,
7499                                                       aDisplayItemKey)) {
7500     return true;
7501   }
7502 
7503   return false;
7504 }
7505 
InvalidateFrame(uint32_t aDisplayItemKey,bool aRebuildDisplayItems)7506 void nsIFrame::InvalidateFrame(uint32_t aDisplayItemKey,
7507                                bool aRebuildDisplayItems /* = true */) {
7508   bool hasDisplayItem =
7509       !aDisplayItemKey || HasRetainedDataFor(this, aDisplayItemKey);
7510   InvalidateFrameInternal(this, hasDisplayItem, aRebuildDisplayItems);
7511 }
7512 
InvalidateFrameWithRect(const nsRect & aRect,uint32_t aDisplayItemKey,bool aRebuildDisplayItems)7513 void nsIFrame::InvalidateFrameWithRect(const nsRect& aRect,
7514                                        uint32_t aDisplayItemKey,
7515                                        bool aRebuildDisplayItems /* = true */) {
7516   if (aRect.IsEmpty()) {
7517     return;
7518   }
7519   bool hasDisplayItem =
7520       !aDisplayItemKey || HasRetainedDataFor(this, aDisplayItemKey);
7521   bool alreadyInvalid = false;
7522   if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) {
7523     InvalidateFrameInternal(this, hasDisplayItem, aRebuildDisplayItems);
7524   } else {
7525     alreadyInvalid = true;
7526   }
7527 
7528   if (!hasDisplayItem) {
7529     return;
7530   }
7531 
7532   nsRect* rect;
7533   if (HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
7534     rect = GetProperty(InvalidationRect());
7535     MOZ_ASSERT(rect);
7536   } else {
7537     if (alreadyInvalid) {
7538       return;
7539     }
7540     rect = new nsRect();
7541     AddProperty(InvalidationRect(), rect);
7542     AddStateBits(NS_FRAME_HAS_INVALID_RECT);
7543   }
7544 
7545   *rect = rect->Union(aRect);
7546 }
7547 
7548 /*static*/
7549 uint8_t nsIFrame::sLayerIsPrerenderedDataKey;
7550 
IsInvalid(nsRect & aRect)7551 bool nsIFrame::IsInvalid(nsRect& aRect) {
7552   if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) {
7553     return false;
7554   }
7555 
7556   if (HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
7557     nsRect* rect = GetProperty(InvalidationRect());
7558     NS_ASSERTION(
7559         rect, "Must have an invalid rect if NS_FRAME_HAS_INVALID_RECT is set!");
7560     aRect = *rect;
7561   } else {
7562     aRect.SetEmpty();
7563   }
7564   return true;
7565 }
7566 
SchedulePaint(PaintType aType,bool aFrameChanged)7567 void nsIFrame::SchedulePaint(PaintType aType, bool aFrameChanged) {
7568   nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
7569   InvalidateRenderingObservers(displayRoot, this, aFrameChanged);
7570   SchedulePaintInternal(displayRoot, this, aType);
7571 }
7572 
SchedulePaintWithoutInvalidatingObservers(PaintType aType)7573 void nsIFrame::SchedulePaintWithoutInvalidatingObservers(PaintType aType) {
7574   nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
7575   SchedulePaintInternal(displayRoot, this, aType);
7576 }
7577 
InvalidateLayer(DisplayItemType aDisplayItemKey,const nsIntRect * aDamageRect,const nsRect * aFrameDamageRect,uint32_t aFlags)7578 void nsIFrame::InvalidateLayer(DisplayItemType aDisplayItemKey,
7579                                const nsIntRect* aDamageRect,
7580                                const nsRect* aFrameDamageRect,
7581                                uint32_t aFlags /* = 0 */) {
7582   NS_ASSERTION(aDisplayItemKey > DisplayItemType::TYPE_ZERO, "Need a key");
7583 
7584   nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
7585   InvalidateRenderingObservers(displayRoot, this, false);
7586 
7587   // Check if frame supports WebRender's async update
7588   if ((aFlags & UPDATE_IS_ASYNC) &&
7589       WebRenderUserData::SupportsAsyncUpdate(this)) {
7590     // WebRender does not use layer, then return nullptr.
7591     return;
7592   }
7593 
7594   if (aFrameDamageRect && aFrameDamageRect->IsEmpty()) {
7595     return;
7596   }
7597 
7598   // In the bug 930056, dialer app startup but not shown on the
7599   // screen because sometimes we don't have any retainned data
7600   // for remote type displayitem and thus Repaint event is not
7601   // triggered. So, always invalidate in this case.
7602   DisplayItemType displayItemKey = aDisplayItemKey;
7603   if (aDisplayItemKey == DisplayItemType::TYPE_REMOTE) {
7604     displayItemKey = DisplayItemType::TYPE_ZERO;
7605   }
7606 
7607   if (aFrameDamageRect) {
7608     InvalidateFrameWithRect(*aFrameDamageRect,
7609                             static_cast<uint32_t>(displayItemKey));
7610   } else {
7611     InvalidateFrame(static_cast<uint32_t>(displayItemKey));
7612   }
7613 }
7614 
ComputeEffectsRect(nsIFrame * aFrame,const nsRect & aOverflowRect,const nsSize & aNewSize)7615 static nsRect ComputeEffectsRect(nsIFrame* aFrame, const nsRect& aOverflowRect,
7616                                  const nsSize& aNewSize) {
7617   nsRect r = aOverflowRect;
7618 
7619   if (aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
7620     // For SVG frames, we only need to account for filters.
7621     // TODO: We could also take account of clipPath and mask to reduce the
7622     // ink overflow, but that's not essential.
7623     if (aFrame->StyleEffects()->HasFilters()) {
7624       SetOrUpdateRectValuedProperty(aFrame, nsIFrame::PreEffectsBBoxProperty(),
7625                                     r);
7626       r = SVGUtils::GetPostFilterInkOverflowRect(aFrame, aOverflowRect);
7627     }
7628     return r;
7629   }
7630 
7631   // box-shadow
7632   r.UnionRect(r, nsLayoutUtils::GetBoxShadowRectForFrame(aFrame, aNewSize));
7633 
7634   // border-image-outset.
7635   // We need to include border-image-outset because it can cause the
7636   // border image to be drawn beyond the border box.
7637 
7638   // (1) It's important we not check whether there's a border-image
7639   //     since the style hint for a change in border image doesn't cause
7640   //     reflow, and that's probably more important than optimizing the
7641   //     overflow areas for the silly case of border-image-outset without
7642   //     border-image
7643   // (2) It's important that we not check whether the border-image
7644   //     is actually loaded, since that would require us to reflow when
7645   //     the image loads.
7646   const nsStyleBorder* styleBorder = aFrame->StyleBorder();
7647   nsMargin outsetMargin = styleBorder->GetImageOutset();
7648 
7649   if (outsetMargin != nsMargin(0, 0, 0, 0)) {
7650     nsRect outsetRect(nsPoint(0, 0), aNewSize);
7651     outsetRect.Inflate(outsetMargin);
7652     r.UnionRect(r, outsetRect);
7653   }
7654 
7655   // Note that we don't remove the outlineInnerRect if a frame loses outline
7656   // style. That would require an extra property lookup for every frame,
7657   // or a new frame state bit to track whether a property had been stored,
7658   // or something like that. It's not worth doing that here. At most it's
7659   // only one heap-allocated rect per frame and it will be cleaned up when
7660   // the frame dies.
7661 
7662   if (SVGIntegrationUtils::UsingOverflowAffectingEffects(aFrame)) {
7663     SetOrUpdateRectValuedProperty(aFrame, nsIFrame::PreEffectsBBoxProperty(),
7664                                   r);
7665     r = SVGIntegrationUtils::ComputePostEffectsInkOverflowRect(aFrame, r);
7666   }
7667 
7668   return r;
7669 }
7670 
MovePositionBy(const nsPoint & aTranslation)7671 void nsIFrame::MovePositionBy(const nsPoint& aTranslation) {
7672   nsPoint position = GetNormalPosition() + aTranslation;
7673 
7674   const nsMargin* computedOffsets = nullptr;
7675   if (IsRelativelyOrStickyPositioned()) {
7676     computedOffsets = GetProperty(nsIFrame::ComputedOffsetProperty());
7677   }
7678   ReflowInput::ApplyRelativePositioning(
7679       this, computedOffsets ? *computedOffsets : nsMargin(), &position);
7680   SetPosition(position);
7681 }
7682 
GetNormalRect() const7683 nsRect nsIFrame::GetNormalRect() const {
7684   // It might be faster to first check
7685   // StyleDisplay()->IsRelativelyPositionedStyle().
7686   bool hasProperty;
7687   nsPoint normalPosition = GetProperty(NormalPositionProperty(), &hasProperty);
7688   if (hasProperty) {
7689     return nsRect(normalPosition, GetSize());
7690   }
7691   return GetRect();
7692 }
7693 
GetBoundingClientRect()7694 nsRect nsIFrame::GetBoundingClientRect() {
7695   return nsLayoutUtils::GetAllInFlowRectsUnion(
7696       this, nsLayoutUtils::GetContainingBlockForClientRect(this),
7697       nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
7698 }
7699 
GetPositionIgnoringScrolling() const7700 nsPoint nsIFrame::GetPositionIgnoringScrolling() const {
7701   return GetParent() ? GetParent()->GetPositionOfChildIgnoringScrolling(this)
7702                      : GetPosition();
7703 }
7704 
GetOverflowRect(OverflowType aType) const7705 nsRect nsIFrame::GetOverflowRect(OverflowType aType) const {
7706   // Note that in some cases the overflow area might not have been
7707   // updated (yet) to reflect any outline set on the frame or the area
7708   // of child frames. That's OK because any reflow that updates these
7709   // areas will invalidate the appropriate area, so any (mis)uses of
7710   // this method will be fixed up.
7711 
7712   if (mOverflow.mType == OverflowStorageType::Large) {
7713     // there is an overflow rect, and it's not stored as deltas but as
7714     // a separately-allocated rect
7715     return GetOverflowAreasProperty()->Overflow(aType);
7716   }
7717 
7718   if (aType == OverflowType::Ink &&
7719       mOverflow.mType != OverflowStorageType::None) {
7720     return InkOverflowFromDeltas();
7721   }
7722 
7723   return GetRectRelativeToSelf();
7724 }
7725 
GetOverflowAreas() const7726 OverflowAreas nsIFrame::GetOverflowAreas() const {
7727   if (mOverflow.mType == OverflowStorageType::Large) {
7728     // there is an overflow rect, and it's not stored as deltas but as
7729     // a separately-allocated rect
7730     return *GetOverflowAreasProperty();
7731   }
7732 
7733   return OverflowAreas(InkOverflowFromDeltas(),
7734                        nsRect(nsPoint(0, 0), GetSize()));
7735 }
7736 
GetOverflowAreasRelativeToSelf() const7737 OverflowAreas nsIFrame::GetOverflowAreasRelativeToSelf() const {
7738   if (IsTransformed()) {
7739     if (OverflowAreas* preTransformOverflows =
7740             GetProperty(PreTransformOverflowAreasProperty())) {
7741       return *preTransformOverflows;
7742     }
7743   }
7744   return GetOverflowAreas();
7745 }
7746 
GetOverflowAreasRelativeToParent() const7747 OverflowAreas nsIFrame::GetOverflowAreasRelativeToParent() const {
7748   return GetOverflowAreas() + GetPosition();
7749 }
7750 
GetActualAndNormalOverflowAreasRelativeToParent() const7751 OverflowAreas nsIFrame::GetActualAndNormalOverflowAreasRelativeToParent()
7752     const {
7753   if (MOZ_LIKELY(!IsRelativelyOrStickyPositioned())) {
7754     return GetOverflowAreasRelativeToParent();
7755   }
7756 
7757   const OverflowAreas overflows = GetOverflowAreas();
7758   OverflowAreas actualAndNormalOverflows = overflows + GetPosition();
7759   actualAndNormalOverflows.UnionWith(overflows + GetNormalPosition());
7760   return actualAndNormalOverflows;
7761 }
7762 
ScrollableOverflowRectRelativeToParent() const7763 nsRect nsIFrame::ScrollableOverflowRectRelativeToParent() const {
7764   return ScrollableOverflowRect() + GetPosition();
7765 }
7766 
InkOverflowRectRelativeToParent() const7767 nsRect nsIFrame::InkOverflowRectRelativeToParent() const {
7768   return InkOverflowRect() + GetPosition();
7769 }
7770 
ScrollableOverflowRectRelativeToSelf() const7771 nsRect nsIFrame::ScrollableOverflowRectRelativeToSelf() const {
7772   if (IsTransformed()) {
7773     if (OverflowAreas* preTransformOverflows =
7774             GetProperty(PreTransformOverflowAreasProperty())) {
7775       return preTransformOverflows->ScrollableOverflow();
7776     }
7777   }
7778   return ScrollableOverflowRect();
7779 }
7780 
InkOverflowRectRelativeToSelf() const7781 nsRect nsIFrame::InkOverflowRectRelativeToSelf() const {
7782   if (IsTransformed()) {
7783     if (OverflowAreas* preTransformOverflows =
7784             GetProperty(PreTransformOverflowAreasProperty())) {
7785       return preTransformOverflows->InkOverflow();
7786     }
7787   }
7788   return InkOverflowRect();
7789 }
7790 
PreEffectsInkOverflowRect() const7791 nsRect nsIFrame::PreEffectsInkOverflowRect() const {
7792   nsRect* r = GetProperty(nsIFrame::PreEffectsBBoxProperty());
7793   return r ? *r : InkOverflowRectRelativeToSelf();
7794 }
7795 
UpdateOverflow()7796 bool nsIFrame::UpdateOverflow() {
7797   MOZ_ASSERT(FrameMaintainsOverflow(),
7798              "Non-display SVG do not maintain ink overflow rects");
7799 
7800   nsRect rect(nsPoint(0, 0), GetSize());
7801   OverflowAreas overflowAreas(rect, rect);
7802 
7803   if (!ComputeCustomOverflow(overflowAreas)) {
7804     // If updating overflow wasn't supported by this frame, then it should
7805     // have scheduled any necessary reflows. We can return false to say nothing
7806     // changed, and wait for reflow to correct it.
7807     return false;
7808   }
7809 
7810   UnionChildOverflow(overflowAreas);
7811 
7812   if (FinishAndStoreOverflow(overflowAreas, GetSize())) {
7813     nsView* view = GetView();
7814     if (view) {
7815       ReflowChildFlags flags = GetXULLayoutFlags();
7816       if (!(flags & ReflowChildFlags::NoSizeView)) {
7817         // Make sure the frame's view is properly sized.
7818         nsViewManager* vm = view->GetViewManager();
7819         vm->ResizeView(view, overflowAreas.InkOverflow(), true);
7820       }
7821     }
7822 
7823     return true;
7824   }
7825 
7826   // Frames that combine their 3d transform with their ancestors
7827   // only compute a pre-transform overflow rect, and then contribute
7828   // to the normal overflow rect of the preserve-3d root. Always return
7829   // true here so that we propagate changes up to the root for final
7830   // calculation.
7831   return Combines3DTransformWithAncestors();
7832 }
7833 
7834 /* virtual */
ComputeCustomOverflow(OverflowAreas & aOverflowAreas)7835 bool nsIFrame::ComputeCustomOverflow(OverflowAreas& aOverflowAreas) {
7836   return true;
7837 }
7838 
7839 /* virtual */
UnionChildOverflow(OverflowAreas & aOverflowAreas)7840 void nsIFrame::UnionChildOverflow(OverflowAreas& aOverflowAreas) {
7841   if (!DoesClipChildrenInBothAxes() &&
7842       !(IsXULCollapsed() && (IsXULBoxFrame() || ::IsXULBoxWrapped(this)))) {
7843     nsLayoutUtils::UnionChildOverflow(this, aOverflowAreas);
7844   }
7845 }
7846 
7847 // Return true if this form control element's preferred size property (but not
7848 // percentage max size property) contains a percentage value that should be
7849 // resolved against zero when calculating its min-content contribution in the
7850 // corresponding axis.
7851 //
7852 // For proper replaced elements, the percentage value in both their max size
7853 // property or preferred size property should be resolved against zero. This is
7854 // handled in IsPercentageResolvedAgainstZero().
FormControlShrinksForPercentSize(const nsIFrame * aFrame)7855 inline static bool FormControlShrinksForPercentSize(const nsIFrame* aFrame) {
7856   if (!aFrame->IsFrameOfType(nsIFrame::eReplaced)) {
7857     // Quick test to reject most frames.
7858     return false;
7859   }
7860 
7861   LayoutFrameType fType = aFrame->Type();
7862   if (fType == LayoutFrameType::Meter || fType == LayoutFrameType::Progress ||
7863       fType == LayoutFrameType::Range) {
7864     // progress, meter and range do have this shrinking behavior
7865     // FIXME: Maybe these should be nsIFormControlFrame?
7866     return true;
7867   }
7868 
7869   if (!static_cast<nsIFormControlFrame*>(do_QueryFrame(aFrame))) {
7870     // Not a form control.  This includes fieldsets, which do not
7871     // shrink.
7872     return false;
7873   }
7874 
7875   if (fType == LayoutFrameType::GfxButtonControl ||
7876       fType == LayoutFrameType::HTMLButtonControl) {
7877     // Buttons don't have this shrinking behavior.  (Note that color
7878     // inputs do, even though they inherit from button, so we can't use
7879     // do_QueryFrame here.)
7880     return false;
7881   }
7882 
7883   return true;
7884 }
7885 
IsPercentageResolvedAgainstZero(const StyleSize & aStyleSize,const StyleMaxSize & aStyleMaxSize) const7886 bool nsIFrame::IsPercentageResolvedAgainstZero(
7887     const StyleSize& aStyleSize, const StyleMaxSize& aStyleMaxSize) const {
7888   const bool sizeHasPercent = aStyleSize.HasPercent();
7889   return ((sizeHasPercent || aStyleMaxSize.HasPercent()) &&
7890           IsFrameOfType(nsIFrame::eReplacedSizing)) ||
7891          (sizeHasPercent && FormControlShrinksForPercentSize(this));
7892 }
7893 
7894 // Summary of the Cyclic-Percentage Intrinsic Size Contribution Rules:
7895 //
7896 // Element Type         |       Replaced           |        Non-replaced
7897 // Contribution Type    | min-content  max-content | min-content  max-content
7898 // ---------------------------------------------------------------------------
7899 // min size             | zero         zero        | zero         zero
7900 // max & preferred size | zero         initial     | initial      initial
7901 //
7902 // https://drafts.csswg.org/css-sizing-3/#cyclic-percentage-contribution
IsPercentageResolvedAgainstZero(const LengthPercentage & aSize,SizeProperty aProperty) const7903 bool nsIFrame::IsPercentageResolvedAgainstZero(const LengthPercentage& aSize,
7904                                                SizeProperty aProperty) const {
7905   // Early return to avoid calling the virtual function, IsFrameOfType().
7906   if (aProperty == SizeProperty::MinSize) {
7907     return true;
7908   }
7909 
7910   const bool hasPercentOnReplaced =
7911       aSize.HasPercent() && IsFrameOfType(nsIFrame::eReplacedSizing);
7912   if (aProperty == SizeProperty::MaxSize) {
7913     return hasPercentOnReplaced;
7914   }
7915 
7916   MOZ_ASSERT(aProperty == SizeProperty::Size);
7917   return hasPercentOnReplaced ||
7918          (aSize.HasPercent() && FormControlShrinksForPercentSize(this));
7919 }
7920 
IsBlockWrapper() const7921 bool nsIFrame::IsBlockWrapper() const {
7922   auto pseudoType = Style()->GetPseudoType();
7923   return pseudoType == PseudoStyleType::mozBlockInsideInlineWrapper ||
7924          pseudoType == PseudoStyleType::buttonContent ||
7925          pseudoType == PseudoStyleType::cellContent ||
7926          pseudoType == PseudoStyleType::columnSpanWrapper;
7927 }
7928 
IsBlockFrameOrSubclass() const7929 bool nsIFrame::IsBlockFrameOrSubclass() const {
7930   const nsBlockFrame* thisAsBlock = do_QueryFrame(this);
7931   return !!thisAsBlock;
7932 }
7933 
GetNearestBlockContainer(nsIFrame * frame)7934 static nsIFrame* GetNearestBlockContainer(nsIFrame* frame) {
7935   // The block wrappers we use to wrap blocks inside inlines aren't
7936   // described in the CSS spec.  We need to make them not be containing
7937   // blocks.
7938   // Since the parent of such a block is either a normal block or
7939   // another such pseudo, this shouldn't cause anything bad to happen.
7940   // Also the anonymous blocks inside table cells are not containing blocks.
7941   //
7942   // If we ever start skipping table row groups from being containing blocks,
7943   // you need to remove the StickyScrollContainer hack referencing bug 1421660.
7944   while (frame->IsFrameOfType(nsIFrame::eLineParticipant) ||
7945          frame->IsBlockWrapper() ||
7946          // Table rows are not containing blocks either
7947          frame->IsTableRowFrame()) {
7948     frame = frame->GetParent();
7949     NS_ASSERTION(
7950         frame,
7951         "How come we got to the root frame without seeing a containing block?");
7952   }
7953   return frame;
7954 }
7955 
GetContainingBlock(uint32_t aFlags,const nsStyleDisplay * aStyleDisplay) const7956 nsIFrame* nsIFrame::GetContainingBlock(
7957     uint32_t aFlags, const nsStyleDisplay* aStyleDisplay) const {
7958   MOZ_ASSERT(aStyleDisplay == StyleDisplay());
7959   if (!GetParent()) {
7960     return nullptr;
7961   }
7962   // MathML frames might have absolute positioning style, but they would
7963   // still be in-flow.  So we have to check to make sure that the frame
7964   // is really out-of-flow too.
7965   nsIFrame* f;
7966   if (IsAbsolutelyPositioned(aStyleDisplay)) {
7967     f = GetParent();  // the parent is always the containing block
7968   } else {
7969     f = GetNearestBlockContainer(GetParent());
7970   }
7971 
7972   if (aFlags & SKIP_SCROLLED_FRAME && f &&
7973       f->Style()->GetPseudoType() == PseudoStyleType::scrolledContent) {
7974     f = f->GetParent();
7975   }
7976   return f;
7977 }
7978 
7979 #ifdef DEBUG_FRAME_DUMP
7980 
ContentIndexInContainer(const nsIFrame * aFrame)7981 Maybe<uint32_t> nsIFrame::ContentIndexInContainer(const nsIFrame* aFrame) {
7982   if (nsIContent* content = aFrame->GetContent()) {
7983     return content->ComputeIndexInParentContent();
7984   }
7985   return Nothing();
7986 }
7987 
ListTag() const7988 nsAutoCString nsIFrame::ListTag() const {
7989   nsAutoString tmp;
7990   GetFrameName(tmp);
7991 
7992   nsAutoCString tag;
7993   tag += NS_ConvertUTF16toUTF8(tmp);
7994   tag += nsPrintfCString("@%p", static_cast<const void*>(this));
7995   return tag;
7996 }
7997 
ConvertToString(const LogicalRect & aRect,const WritingMode aWM,ListFlags aFlags)7998 std::string nsIFrame::ConvertToString(const LogicalRect& aRect,
7999                                       const WritingMode aWM, ListFlags aFlags) {
8000   if (aFlags.contains(ListFlag::DisplayInCSSPixels)) {
8001     // Abuse CSSRect to store all LogicalRect's dimensions in CSS pixels.
8002     return ToString(mozilla::CSSRect(CSSPixel::FromAppUnits(aRect.IStart(aWM)),
8003                                      CSSPixel::FromAppUnits(aRect.BStart(aWM)),
8004                                      CSSPixel::FromAppUnits(aRect.ISize(aWM)),
8005                                      CSSPixel::FromAppUnits(aRect.BSize(aWM))));
8006   }
8007   return ToString(aRect);
8008 }
8009 
ConvertToString(const LogicalSize & aSize,const WritingMode aWM,ListFlags aFlags)8010 std::string nsIFrame::ConvertToString(const LogicalSize& aSize,
8011                                       const WritingMode aWM, ListFlags aFlags) {
8012   if (aFlags.contains(ListFlag::DisplayInCSSPixels)) {
8013     // Abuse CSSSize to store all LogicalSize's dimensions in CSS pixels.
8014     return ToString(CSSSize(CSSPixel::FromAppUnits(aSize.ISize(aWM)),
8015                             CSSPixel::FromAppUnits(aSize.BSize(aWM))));
8016   }
8017   return ToString(aSize);
8018 }
8019 
8020 // Debugging
ListGeneric(nsACString & aTo,const char * aPrefix,ListFlags aFlags) const8021 void nsIFrame::ListGeneric(nsACString& aTo, const char* aPrefix,
8022                            ListFlags aFlags) const {
8023   aTo += aPrefix;
8024   aTo += ListTag();
8025   if (HasView()) {
8026     aTo += nsPrintfCString(" [view=%p]", static_cast<void*>(GetView()));
8027   }
8028   if (GetParent()) {
8029     aTo += nsPrintfCString(" parent=%p", static_cast<void*>(GetParent()));
8030   }
8031   if (GetNextSibling()) {
8032     aTo += nsPrintfCString(" next=%p", static_cast<void*>(GetNextSibling()));
8033   }
8034   if (GetPrevContinuation()) {
8035     bool fluid = GetPrevInFlow() == GetPrevContinuation();
8036     aTo += nsPrintfCString(" prev-%s=%p", fluid ? "in-flow" : "continuation",
8037                            static_cast<void*>(GetPrevContinuation()));
8038   }
8039   if (GetNextContinuation()) {
8040     bool fluid = GetNextInFlow() == GetNextContinuation();
8041     aTo += nsPrintfCString(" next-%s=%p", fluid ? "in-flow" : "continuation",
8042                            static_cast<void*>(GetNextContinuation()));
8043   }
8044   void* IBsibling = GetProperty(IBSplitSibling());
8045   if (IBsibling) {
8046     aTo += nsPrintfCString(" IBSplitSibling=%p", IBsibling);
8047   }
8048   void* IBprevsibling = GetProperty(IBSplitPrevSibling());
8049   if (IBprevsibling) {
8050     aTo += nsPrintfCString(" IBSplitPrevSibling=%p", IBprevsibling);
8051   }
8052   if (nsLayoutUtils::FontSizeInflationEnabled(PresContext())) {
8053     if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT)) {
8054       aTo += nsPrintfCString(" FFR");
8055       if (nsFontInflationData* data =
8056               nsFontInflationData::FindFontInflationDataFor(this)) {
8057         aTo += nsPrintfCString(
8058             ",enabled=%s,UIS=%s", data->InflationEnabled() ? "yes" : "no",
8059             ConvertToString(data->UsableISize(), aFlags).c_str());
8060       }
8061     }
8062     if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER)) {
8063       aTo += nsPrintfCString(" FIC");
8064     }
8065     aTo += nsPrintfCString(" FI=%f", nsLayoutUtils::FontSizeInflationFor(this));
8066   }
8067   aTo += nsPrintfCString(" %s", ConvertToString(mRect, aFlags).c_str());
8068 
8069   mozilla::WritingMode wm = GetWritingMode();
8070   if (wm.IsVertical() || wm.IsBidiRTL()) {
8071     aTo +=
8072         nsPrintfCString(" wm=%s logical-size=(%s)", ToString(wm).c_str(),
8073                         ConvertToString(GetLogicalSize(), wm, aFlags).c_str());
8074   }
8075 
8076   nsIFrame* parent = GetParent();
8077   if (parent) {
8078     WritingMode pWM = parent->GetWritingMode();
8079     if (pWM.IsVertical() || pWM.IsBidiRTL()) {
8080       nsSize containerSize = parent->mRect.Size();
8081       LogicalRect lr(pWM, mRect, containerSize);
8082       aTo += nsPrintfCString(" parent-wm=%s cs=(%s) logical-rect=%s",
8083                              ToString(pWM).c_str(),
8084                              ConvertToString(containerSize, aFlags).c_str(),
8085                              ConvertToString(lr, pWM, aFlags).c_str());
8086     }
8087   }
8088   nsIFrame* f = const_cast<nsIFrame*>(this);
8089   if (f->HasOverflowAreas()) {
8090     nsRect vo = f->InkOverflowRect();
8091     if (!vo.IsEqualEdges(mRect)) {
8092       aTo += nsPrintfCString(" ink-overflow=%s",
8093                              ConvertToString(vo, aFlags).c_str());
8094     }
8095     nsRect so = f->ScrollableOverflowRect();
8096     if (!so.IsEqualEdges(mRect)) {
8097       aTo += nsPrintfCString(" scr-overflow=%s",
8098                              ConvertToString(so, aFlags).c_str());
8099     }
8100   }
8101   bool hasNormalPosition;
8102   nsPoint normalPosition = GetNormalPosition(&hasNormalPosition);
8103   if (hasNormalPosition) {
8104     aTo += nsPrintfCString(" normal-position=%s",
8105                            ConvertToString(normalPosition, aFlags).c_str());
8106   }
8107   if (HasProperty(BidiDataProperty())) {
8108     FrameBidiData bidi = GetBidiData();
8109     aTo += nsPrintfCString(" bidi(%d,%d,%d)", bidi.baseLevel.Value(),
8110                            bidi.embeddingLevel.Value(),
8111                            bidi.precedingControl.Value());
8112   }
8113   if (IsTransformed()) {
8114     aTo += nsPrintfCString(" transformed");
8115   }
8116   if (ChildrenHavePerspective()) {
8117     aTo += nsPrintfCString(" perspective");
8118   }
8119   if (Extend3DContext()) {
8120     aTo += nsPrintfCString(" extend-3d");
8121   }
8122   if (Combines3DTransformWithAncestors()) {
8123     aTo += nsPrintfCString(" combines-3d-transform-with-ancestors");
8124   }
8125   if (mContent) {
8126     aTo += nsPrintfCString(" [content=%p]", static_cast<void*>(mContent));
8127   }
8128   aTo += nsPrintfCString(" [cs=%p", static_cast<void*>(mComputedStyle));
8129   if (mComputedStyle) {
8130     auto pseudoType = mComputedStyle->GetPseudoType();
8131     aTo += ToString(pseudoType).c_str();
8132   }
8133   aTo += "]";
8134 
8135   if (IsFrameModified()) {
8136     aTo += nsPrintfCString(" modified");
8137   }
8138 
8139   if (HasModifiedDescendants()) {
8140     aTo += nsPrintfCString(" has-modified-descendants");
8141   }
8142 }
8143 
List(FILE * out,const char * aPrefix,ListFlags aFlags) const8144 void nsIFrame::List(FILE* out, const char* aPrefix, ListFlags aFlags) const {
8145   nsCString str;
8146   ListGeneric(str, aPrefix, aFlags);
8147   fprintf_stderr(out, "%s\n", str.get());
8148 }
8149 
ListTextRuns(FILE * out) const8150 void nsIFrame::ListTextRuns(FILE* out) const {
8151   nsTHashSet<const void*> seen;
8152   ListTextRuns(out, seen);
8153 }
8154 
ListTextRuns(FILE * out,nsTHashSet<const void * > & aSeen) const8155 void nsIFrame::ListTextRuns(FILE* out, nsTHashSet<const void*>& aSeen) const {
8156   for (const auto& childList : ChildLists()) {
8157     for (const nsIFrame* kid : childList.mList) {
8158       kid->ListTextRuns(out, aSeen);
8159     }
8160   }
8161 }
8162 
ListMatchedRules(FILE * out,const char * aPrefix) const8163 void nsIFrame::ListMatchedRules(FILE* out, const char* aPrefix) const {
8164   nsTArray<const RawServoStyleRule*> rawRuleList;
8165   Servo_ComputedValues_GetStyleRuleList(mComputedStyle, &rawRuleList);
8166   for (const RawServoStyleRule* rawRule : rawRuleList) {
8167     nsAutoCString ruleText;
8168     Servo_StyleRule_GetCssText(rawRule, &ruleText);
8169     fprintf_stderr(out, "%s%s\n", aPrefix, ruleText.get());
8170   }
8171 }
8172 
ListWithMatchedRules(FILE * out,const char * aPrefix) const8173 void nsIFrame::ListWithMatchedRules(FILE* out, const char* aPrefix) const {
8174   fprintf_stderr(out, "%s%s\n", aPrefix, ListTag().get());
8175 
8176   nsCString rulePrefix;
8177   rulePrefix += aPrefix;
8178   rulePrefix += "    ";
8179   ListMatchedRules(out, rulePrefix.get());
8180 }
8181 
GetFrameName(nsAString & aResult) const8182 nsresult nsIFrame::GetFrameName(nsAString& aResult) const {
8183   return MakeFrameName(u"Frame"_ns, aResult);
8184 }
8185 
MakeFrameName(const nsAString & aType,nsAString & aResult) const8186 nsresult nsIFrame::MakeFrameName(const nsAString& aType,
8187                                  nsAString& aResult) const {
8188   aResult = aType;
8189   if (mContent && !mContent->IsText()) {
8190     nsAutoString buf;
8191     mContent->NodeInfo()->NameAtom()->ToString(buf);
8192     if (nsAtom* id = mContent->GetID()) {
8193       buf.AppendLiteral(" id=");
8194       buf.Append(nsDependentAtomString(id));
8195     }
8196     if (IsSubDocumentFrame()) {
8197       nsAutoString src;
8198       mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src);
8199       buf.AppendLiteral(" src=");
8200       buf.Append(src);
8201     }
8202     aResult.Append('(');
8203     aResult.Append(buf);
8204     aResult.Append(')');
8205   }
8206   aResult.Append('(');
8207   Maybe<uint32_t> index = ContentIndexInContainer(this);
8208   if (index.isSome()) {
8209     aResult.AppendInt(*index);
8210   } else {
8211     aResult.AppendInt(-1);
8212   }
8213   aResult.Append(')');
8214   return NS_OK;
8215 }
8216 
DumpFrameTree() const8217 void nsIFrame::DumpFrameTree() const {
8218   PresShell()->GetRootFrame()->List(stderr);
8219 }
8220 
DumpFrameTreeInCSSPixels() const8221 void nsIFrame::DumpFrameTreeInCSSPixels() const {
8222   PresShell()->GetRootFrame()->List(stderr, "", ListFlag::DisplayInCSSPixels);
8223 }
8224 
DumpFrameTreeLimited() const8225 void nsIFrame::DumpFrameTreeLimited() const { List(stderr); }
DumpFrameTreeLimitedInCSSPixels() const8226 void nsIFrame::DumpFrameTreeLimitedInCSSPixels() const {
8227   List(stderr, "", ListFlag::DisplayInCSSPixels);
8228 }
8229 
8230 #endif
8231 
IsVisibleForPainting()8232 bool nsIFrame::IsVisibleForPainting() { return StyleVisibility()->IsVisible(); }
8233 
IsVisibleOrCollapsedForPainting()8234 bool nsIFrame::IsVisibleOrCollapsedForPainting() {
8235   return StyleVisibility()->IsVisibleOrCollapsed();
8236 }
8237 
8238 /* virtual */
IsEmpty()8239 bool nsIFrame::IsEmpty() { return false; }
8240 
CachedIsEmpty()8241 bool nsIFrame::CachedIsEmpty() {
8242   MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_IS_DIRTY),
8243              "Must only be called on reflowed lines");
8244   return IsEmpty();
8245 }
8246 
8247 /* virtual */
IsSelfEmpty()8248 bool nsIFrame::IsSelfEmpty() { return false; }
8249 
GetSelectionController(nsPresContext * aPresContext,nsISelectionController ** aSelCon)8250 nsresult nsIFrame::GetSelectionController(nsPresContext* aPresContext,
8251                                           nsISelectionController** aSelCon) {
8252   if (!aPresContext || !aSelCon) return NS_ERROR_INVALID_ARG;
8253 
8254   nsIFrame* frame = this;
8255   while (frame && frame->HasAnyStateBits(NS_FRAME_INDEPENDENT_SELECTION)) {
8256     nsITextControlFrame* tcf = do_QueryFrame(frame);
8257     if (tcf) {
8258       return tcf->GetOwnedSelectionController(aSelCon);
8259     }
8260     frame = frame->GetParent();
8261   }
8262 
8263   *aSelCon = do_AddRef(aPresContext->PresShell()).take();
8264   return NS_OK;
8265 }
8266 
GetFrameSelection()8267 already_AddRefed<nsFrameSelection> nsIFrame::GetFrameSelection() {
8268   RefPtr<nsFrameSelection> fs =
8269       const_cast<nsFrameSelection*>(GetConstFrameSelection());
8270   return fs.forget();
8271 }
8272 
GetConstFrameSelection() const8273 const nsFrameSelection* nsIFrame::GetConstFrameSelection() const {
8274   nsIFrame* frame = const_cast<nsIFrame*>(this);
8275   while (frame && frame->HasAnyStateBits(NS_FRAME_INDEPENDENT_SELECTION)) {
8276     nsITextControlFrame* tcf = do_QueryFrame(frame);
8277     if (tcf) {
8278       return tcf->GetOwnedFrameSelection();
8279     }
8280     frame = frame->GetParent();
8281   }
8282 
8283   return PresShell()->ConstFrameSelection();
8284 }
8285 
IsFrameSelected() const8286 bool nsIFrame::IsFrameSelected() const {
8287   NS_ASSERTION(!GetContent() || GetContent()->IsMaybeSelected(),
8288                "use the public IsSelected() instead");
8289   return GetContent()->IsSelected(0, GetContent()->GetChildCount());
8290 }
8291 
GetPointFromOffset(int32_t inOffset,nsPoint * outPoint)8292 nsresult nsIFrame::GetPointFromOffset(int32_t inOffset, nsPoint* outPoint) {
8293   MOZ_ASSERT(outPoint != nullptr, "Null parameter");
8294   nsRect contentRect = GetContentRectRelativeToSelf();
8295   nsPoint pt = contentRect.TopLeft();
8296   if (mContent) {
8297     nsIContent* newContent = mContent->GetParent();
8298     if (newContent) {
8299       const int32_t newOffset = newContent->ComputeIndexOf_Deprecated(mContent);
8300 
8301       // Find the direction of the frame from the EmbeddingLevelProperty,
8302       // which is the resolved bidi level set in
8303       // nsBidiPresUtils::ResolveParagraph (odd levels = right-to-left).
8304       // If the embedding level isn't set, just use the CSS direction
8305       // property.
8306       bool hasBidiData;
8307       FrameBidiData bidiData = GetProperty(BidiDataProperty(), &hasBidiData);
8308       bool isRTL = hasBidiData
8309                        ? bidiData.embeddingLevel.IsRTL()
8310                        : StyleVisibility()->mDirection == StyleDirection::Rtl;
8311       if ((!isRTL && inOffset > newOffset) ||
8312           (isRTL && inOffset <= newOffset)) {
8313         pt = contentRect.TopRight();
8314       }
8315     }
8316   }
8317   *outPoint = pt;
8318   return NS_OK;
8319 }
8320 
GetCharacterRectsInRange(int32_t aInOffset,int32_t aLength,nsTArray<nsRect> & aOutRect)8321 nsresult nsIFrame::GetCharacterRectsInRange(int32_t aInOffset, int32_t aLength,
8322                                             nsTArray<nsRect>& aOutRect) {
8323   /* no text */
8324   return NS_ERROR_FAILURE;
8325 }
8326 
GetChildFrameContainingOffset(int32_t inContentOffset,bool inHint,int32_t * outFrameContentOffset,nsIFrame ** outChildFrame)8327 nsresult nsIFrame::GetChildFrameContainingOffset(int32_t inContentOffset,
8328                                                  bool inHint,
8329                                                  int32_t* outFrameContentOffset,
8330                                                  nsIFrame** outChildFrame) {
8331   MOZ_ASSERT(outChildFrame && outFrameContentOffset, "Null parameter");
8332   *outFrameContentOffset = (int32_t)inHint;
8333   // the best frame to reflect any given offset would be a visible frame if
8334   // possible i.e. we are looking for a valid frame to place the blinking caret
8335   nsRect rect = GetRect();
8336   if (!rect.width || !rect.height) {
8337     // if we have a 0 width or height then lets look for another frame that
8338     // possibly has the same content.  If we have no frames in flow then just
8339     // let us return 'this' frame
8340     nsIFrame* nextFlow = GetNextInFlow();
8341     if (nextFlow)
8342       return nextFlow->GetChildFrameContainingOffset(
8343           inContentOffset, inHint, outFrameContentOffset, outChildFrame);
8344   }
8345   *outChildFrame = this;
8346   return NS_OK;
8347 }
8348 
8349 //
8350 // What I've pieced together about this routine:
8351 // Starting with a block frame (from which a line frame can be gotten)
8352 // and a line number, drill down and get the first/last selectable
8353 // frame on that line, depending on aPos->mDirection.
8354 // aOutSideLimit != 0 means ignore aLineStart, instead work from
8355 // the end (if > 0) or beginning (if < 0).
8356 //
GetNextPrevLineFromeBlockFrame(nsPresContext * aPresContext,nsPeekOffsetStruct * aPos,nsIFrame * aBlockFrame,int32_t aLineStart,int8_t aOutSideLimit)8357 nsresult nsIFrame::GetNextPrevLineFromeBlockFrame(nsPresContext* aPresContext,
8358                                                   nsPeekOffsetStruct* aPos,
8359                                                   nsIFrame* aBlockFrame,
8360                                                   int32_t aLineStart,
8361                                                   int8_t aOutSideLimit) {
8362   // magic numbers aLineStart will be -1 for end of block 0 will be start of
8363   // block
8364   if (!aBlockFrame || !aPos) return NS_ERROR_NULL_POINTER;
8365 
8366   aPos->mResultFrame = nullptr;
8367   aPos->mResultContent = nullptr;
8368   aPos->mAttach = aPos->mDirection == eDirNext ? CARET_ASSOCIATE_AFTER
8369                                                : CARET_ASSOCIATE_BEFORE;
8370 
8371   nsAutoLineIterator it = aBlockFrame->GetLineIterator();
8372   if (!it) {
8373     return NS_ERROR_FAILURE;
8374   }
8375   int32_t searchingLine = aLineStart;
8376   int32_t countLines = it->GetNumLines();
8377   if (aOutSideLimit > 0)  // start at end
8378     searchingLine = countLines;
8379   else if (aOutSideLimit < 0)  // start at beginning
8380     searchingLine = -1;        //"next" will be 0
8381   else if ((aPos->mDirection == eDirPrevious && searchingLine == 0) ||
8382            (aPos->mDirection == eDirNext &&
8383             searchingLine >= (countLines - 1))) {
8384     // we need to jump to new block frame.
8385     return NS_ERROR_FAILURE;
8386   }
8387   nsIFrame* resultFrame = nullptr;
8388   nsIFrame* farStoppingFrame = nullptr;  // we keep searching until we find a
8389                                          // "this" frame then we go to next line
8390   nsIFrame* nearStoppingFrame = nullptr;  // if we are backing up from edge,
8391                                           // stop here
8392   nsIFrame* firstFrame;
8393   nsIFrame* lastFrame;
8394   bool isBeforeFirstFrame, isAfterLastFrame;
8395   bool found = false;
8396 
8397   nsresult result = NS_OK;
8398   while (!found) {
8399     if (aPos->mDirection == eDirPrevious)
8400       searchingLine--;
8401     else
8402       searchingLine++;
8403     if ((aPos->mDirection == eDirPrevious && searchingLine < 0) ||
8404         (aPos->mDirection == eDirNext && searchingLine >= countLines)) {
8405       // we need to jump to new block frame.
8406       return NS_ERROR_FAILURE;
8407     }
8408     auto line = it->GetLine(searchingLine).unwrap();
8409     if (!line.mNumFramesOnLine) {
8410       continue;
8411     }
8412     lastFrame = firstFrame = line.mFirstFrameOnLine;
8413     for (int32_t lineFrameCount = line.mNumFramesOnLine; lineFrameCount > 1;
8414          lineFrameCount--) {
8415       lastFrame = lastFrame->GetNextSibling();
8416       if (!lastFrame) {
8417         NS_ERROR("GetLine promised more frames than could be found");
8418         return NS_ERROR_FAILURE;
8419       }
8420     }
8421     GetLastLeaf(&lastFrame);
8422 
8423     if (aPos->mDirection == eDirNext) {
8424       nearStoppingFrame = firstFrame;
8425       farStoppingFrame = lastFrame;
8426     } else {
8427       nearStoppingFrame = lastFrame;
8428       farStoppingFrame = firstFrame;
8429     }
8430     nsPoint offset;
8431     nsView* view;  // used for call of get offset from view
8432     aBlockFrame->GetOffsetFromView(offset, &view);
8433     nsPoint newDesiredPos =
8434         aPos->mDesiredCaretPos -
8435         offset;  // get desired position into blockframe coords
8436     result = it->FindFrameAt(searchingLine, newDesiredPos, &resultFrame,
8437                              &isBeforeFirstFrame, &isAfterLastFrame);
8438     if (NS_FAILED(result)) {
8439       continue;
8440     }
8441 
8442     if (resultFrame) {
8443       // check to see if this is ANOTHER blockframe inside the other one if so
8444       // then call into its lines
8445       if (resultFrame->CanProvideLineIterator()) {
8446         aPos->mResultFrame = resultFrame;
8447         return NS_OK;
8448       }
8449       // resultFrame is not a block frame
8450       result = NS_ERROR_FAILURE;
8451 
8452       nsCOMPtr<nsIFrameEnumerator> frameTraversal;
8453       result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
8454                                     aPresContext, resultFrame, ePostOrder,
8455                                     false,  // aVisual
8456                                     aPos->mScrollViewStop,
8457                                     false,  // aFollowOOFs
8458                                     false   // aSkipPopupChecks
8459       );
8460       if (NS_FAILED(result)) return result;
8461 
8462       auto FoundValidFrame = [aPos](const ContentOffsets& aOffsets,
8463                                     const nsIFrame* aFrame) {
8464         if (!aOffsets.content) {
8465           return false;
8466         }
8467         if (!aFrame->IsSelectable(nullptr)) {
8468           return false;
8469         }
8470         if (aPos->mForceEditableRegion && !aOffsets.content->IsEditable()) {
8471           return false;
8472         }
8473         return true;
8474       };
8475 
8476       nsIFrame* storeOldResultFrame = resultFrame;
8477       while (!found) {
8478         nsPoint point;
8479         nsRect tempRect = resultFrame->GetRect();
8480         nsPoint offset;
8481         nsView* view;  // used for call of get offset from view
8482         resultFrame->GetOffsetFromView(offset, &view);
8483         if (!view) {
8484           return NS_ERROR_FAILURE;
8485         }
8486         if (resultFrame->GetWritingMode().IsVertical()) {
8487           point.y = aPos->mDesiredCaretPos.y;
8488           point.x = tempRect.width + offset.x;
8489         } else {
8490           point.y = tempRect.height + offset.y;
8491           point.x = aPos->mDesiredCaretPos.x;
8492         }
8493 
8494         // special check. if we allow non-text selection then we can allow a hit
8495         // location to fall before a table. otherwise there is no way to get and
8496         // click signal to fall before a table (it being a line iterator itself)
8497         mozilla::PresShell* presShell = aPresContext->GetPresShell();
8498         if (!presShell) {
8499           return NS_ERROR_FAILURE;
8500         }
8501         int16_t isEditor = presShell->GetSelectionFlags();
8502         isEditor = isEditor == nsISelectionDisplay::DISPLAY_ALL;
8503         if (isEditor) {
8504           if (resultFrame->IsTableWrapperFrame()) {
8505             if (((point.x - offset.x + tempRect.x) < 0) ||
8506                 ((point.x - offset.x + tempRect.x) >
8507                  tempRect.width))  // off left/right side
8508             {
8509               nsIContent* content = resultFrame->GetContent();
8510               if (content) {
8511                 nsIContent* parent = content->GetParent();
8512                 if (parent) {
8513                   aPos->mResultContent = parent;
8514                   aPos->mContentOffset =
8515                       parent->ComputeIndexOf_Deprecated(content);
8516                   aPos->mAttach = CARET_ASSOCIATE_BEFORE;
8517                   if ((point.x - offset.x + tempRect.x) > tempRect.width) {
8518                     aPos->mContentOffset++;  // go to end of this frame
8519                     aPos->mAttach = CARET_ASSOCIATE_AFTER;
8520                   }
8521                   // result frame is the result frames parent.
8522                   aPos->mResultFrame = resultFrame->GetParent();
8523                   return NS_POSITION_BEFORE_TABLE;
8524                 }
8525               }
8526             }
8527           }
8528         }
8529 
8530         if (!resultFrame->HasView()) {
8531           nsView* view;
8532           nsPoint offset;
8533           resultFrame->GetOffsetFromView(offset, &view);
8534           ContentOffsets offsets =
8535               resultFrame->GetContentOffsetsFromPoint(point - offset);
8536           aPos->mResultContent = offsets.content;
8537           aPos->mContentOffset = offsets.offset;
8538           aPos->mAttach = offsets.associate;
8539           if (FoundValidFrame(offsets, resultFrame)) {
8540             found = true;
8541             break;
8542           }
8543         }
8544 
8545         if (aPos->mDirection == eDirPrevious &&
8546             (resultFrame == farStoppingFrame))
8547           break;
8548         if (aPos->mDirection == eDirNext && (resultFrame == nearStoppingFrame))
8549           break;
8550         // always try previous on THAT line if that fails go the other way
8551         resultFrame = frameTraversal->Traverse(/* aForward = */ false);
8552         if (!resultFrame) return NS_ERROR_FAILURE;
8553       }
8554 
8555       if (!found) {
8556         resultFrame = storeOldResultFrame;
8557 
8558         result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
8559                                       aPresContext, resultFrame, eLeaf,
8560                                       false,  // aVisual
8561                                       aPos->mScrollViewStop,
8562                                       false,  // aFollowOOFs
8563                                       false   // aSkipPopupChecks
8564         );
8565       }
8566       while (!found) {
8567         nsPoint point = aPos->mDesiredCaretPos;
8568         nsView* view;
8569         nsPoint offset;
8570         resultFrame->GetOffsetFromView(offset, &view);
8571         ContentOffsets offsets =
8572             resultFrame->GetContentOffsetsFromPoint(point - offset);
8573         aPos->mResultContent = offsets.content;
8574         aPos->mContentOffset = offsets.offset;
8575         aPos->mAttach = offsets.associate;
8576         if (FoundValidFrame(offsets, resultFrame)) {
8577           found = true;
8578           if (resultFrame == farStoppingFrame)
8579             aPos->mAttach = CARET_ASSOCIATE_BEFORE;
8580           else
8581             aPos->mAttach = CARET_ASSOCIATE_AFTER;
8582           break;
8583         }
8584         if (aPos->mDirection == eDirPrevious &&
8585             (resultFrame == nearStoppingFrame))
8586           break;
8587         if (aPos->mDirection == eDirNext && (resultFrame == farStoppingFrame))
8588           break;
8589         // previous didnt work now we try "next"
8590         nsIFrame* tempFrame = frameTraversal->Traverse(/* aForward = */ true);
8591         if (!tempFrame) break;
8592         resultFrame = tempFrame;
8593       }
8594       aPos->mResultFrame = resultFrame;
8595     } else {
8596       // we need to jump to new block frame.
8597       aPos->mAmount = eSelectLine;
8598       aPos->mStartOffset = 0;
8599       aPos->mAttach = aPos->mDirection == eDirNext ? CARET_ASSOCIATE_BEFORE
8600                                                    : CARET_ASSOCIATE_AFTER;
8601       if (aPos->mDirection == eDirPrevious)
8602         aPos->mStartOffset = -1;  // start from end
8603       return aBlockFrame->PeekOffset(aPos);
8604     }
8605   }
8606   return NS_OK;
8607 }
8608 
GetExtremeCaretPosition(bool aStart)8609 nsIFrame::CaretPosition nsIFrame::GetExtremeCaretPosition(bool aStart) {
8610   CaretPosition result;
8611 
8612   FrameTarget targetFrame = DrillDownToSelectionFrame(this, !aStart, 0);
8613   FrameContentRange range = GetRangeForFrame(targetFrame.frame);
8614   result.mResultContent = range.content;
8615   result.mContentOffset = aStart ? range.start : range.end;
8616   return result;
8617 }
8618 
8619 // If this is a preformatted text frame, see if it ends with a newline
FindLineBreakInText(nsIFrame * aFrame,nsDirection aDirection)8620 static nsContentAndOffset FindLineBreakInText(nsIFrame* aFrame,
8621                                               nsDirection aDirection) {
8622   nsContentAndOffset result;
8623 
8624   if (aFrame->IsGeneratedContentFrame() ||
8625       !aFrame->HasSignificantTerminalNewline()) {
8626     return result;
8627   }
8628 
8629   int32_t endOffset = aFrame->GetOffsets().second;
8630   result.mContent = aFrame->GetContent();
8631   result.mOffset = endOffset - (aDirection == eDirPrevious ? 0 : 1);
8632   return result;
8633 }
8634 
8635 // Find the first (or last) descendant of the given frame
8636 // which is either a block-level frame or a BRFrame, or some other kind of break
8637 // which stops the line.
FindLineBreakingFrame(nsIFrame * aFrame,nsDirection aDirection)8638 static nsContentAndOffset FindLineBreakingFrame(nsIFrame* aFrame,
8639                                                 nsDirection aDirection) {
8640   nsContentAndOffset result;
8641 
8642   if (aFrame->IsGeneratedContentFrame()) {
8643     return result;
8644   }
8645 
8646   // Treat form controls as inline leaves
8647   // XXX we really need a way to determine whether a frame is inline-level
8648   if (static_cast<nsIFormControlFrame*>(do_QueryFrame(aFrame))) {
8649     return result;
8650   }
8651 
8652   // Check the frame itself
8653   // Fall through block-in-inline split frames because their mContent is
8654   // the content of the inline frames they were created from. The
8655   // first/last child of such frames is the real block frame we're
8656   // looking for.
8657   if ((aFrame->IsBlockOutside() &&
8658        !aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) ||
8659       aFrame->IsBrFrame()) {
8660     nsIContent* content = aFrame->GetContent();
8661     result.mContent = content->GetParent();
8662     // In some cases (bug 310589, bug 370174) we end up here with a null
8663     // content. This probably shouldn't ever happen, but since it sometimes
8664     // does, we want to avoid crashing here.
8665     NS_ASSERTION(result.mContent, "Unexpected orphan content");
8666     if (result.mContent) {
8667       result.mOffset = result.mContent->ComputeIndexOf_Deprecated(content) +
8668                        (aDirection == eDirPrevious ? 1 : 0);
8669     }
8670     return result;
8671   }
8672 
8673   result = FindLineBreakInText(aFrame, aDirection);
8674   if (result.mContent) {
8675     return result;
8676   }
8677 
8678   // Iterate over children and call ourselves recursively
8679   if (aDirection == eDirPrevious) {
8680     nsIFrame* child =
8681         aFrame->GetChildList(nsIFrame::kPrincipalList).LastChild();
8682     while (child && !result.mContent) {
8683       result = FindLineBreakingFrame(child, aDirection);
8684       child = child->GetPrevSibling();
8685     }
8686   } else {  // eDirNext
8687     nsIFrame* child = aFrame->PrincipalChildList().FirstChild();
8688     while (child && !result.mContent) {
8689       result = FindLineBreakingFrame(child, aDirection);
8690       child = child->GetNextSibling();
8691     }
8692   }
8693   return result;
8694 }
8695 
PeekOffsetForParagraph(nsPeekOffsetStruct * aPos)8696 nsresult nsIFrame::PeekOffsetForParagraph(nsPeekOffsetStruct* aPos) {
8697   nsIFrame* frame = this;
8698   nsContentAndOffset blockFrameOrBR;
8699   blockFrameOrBR.mContent = nullptr;
8700   bool reachedLimit = frame->IsBlockOutside() || IsEditingHost(frame);
8701 
8702   auto traverse = [&aPos](nsIFrame* current) {
8703     return aPos->mDirection == eDirPrevious ? current->GetPrevSibling()
8704                                             : current->GetNextSibling();
8705   };
8706 
8707   // Go through containing frames until reaching a block frame.
8708   // In each step, search the previous (or next) siblings for the closest
8709   // "stop frame" (a block frame or a BRFrame).
8710   // If found, set it to be the selection boundary and abort.
8711   while (!reachedLimit) {
8712     nsIFrame* parent = frame->GetParent();
8713     // Treat a frame associated with the root content as if it were a block
8714     // frame.
8715     if (!frame->mContent || !frame->mContent->GetParent()) {
8716       reachedLimit = true;
8717       break;
8718     }
8719 
8720     if (aPos->mDirection == eDirNext) {
8721       // Try to find our own line-break before looking at our siblings.
8722       blockFrameOrBR = FindLineBreakInText(frame, eDirNext);
8723     }
8724 
8725     nsIFrame* sibling = traverse(frame);
8726     while (sibling && !blockFrameOrBR.mContent) {
8727       blockFrameOrBR = FindLineBreakingFrame(sibling, aPos->mDirection);
8728       sibling = traverse(sibling);
8729     }
8730     if (blockFrameOrBR.mContent) {
8731       aPos->mResultContent = blockFrameOrBR.mContent;
8732       aPos->mContentOffset = blockFrameOrBR.mOffset;
8733       break;
8734     }
8735     frame = parent;
8736     reachedLimit = frame && (frame->IsBlockOutside() || IsEditingHost(frame));
8737   }
8738 
8739   if (reachedLimit) {  // no "stop frame" found
8740     aPos->mResultContent = frame->GetContent();
8741     if (aPos->mDirection == eDirPrevious) {
8742       aPos->mContentOffset = 0;
8743     } else if (aPos->mResultContent) {
8744       aPos->mContentOffset = aPos->mResultContent->GetChildCount();
8745     }
8746   }
8747   return NS_OK;
8748 }
8749 
8750 // Determine movement direction relative to frame
IsMovingInFrameDirection(const nsIFrame * frame,nsDirection aDirection,bool aVisual)8751 static bool IsMovingInFrameDirection(const nsIFrame* frame,
8752                                      nsDirection aDirection, bool aVisual) {
8753   bool isReverseDirection =
8754       aVisual && nsBidiPresUtils::IsReversedDirectionFrame(frame);
8755   return aDirection == (isReverseDirection ? eDirPrevious : eDirNext);
8756 }
8757 
8758 // Determines "are we looking for a boundary between whitespace and
8759 // non-whitespace (in the direction we're moving in)". It is true when moving
8760 // forward and looking for a beginning of a word, or when moving backwards and
8761 // looking for an end of a word.
ShouldWordSelectionEatSpace(const nsPeekOffsetStruct & aPos)8762 static bool ShouldWordSelectionEatSpace(const nsPeekOffsetStruct& aPos) {
8763   if (aPos.mWordMovementType != eDefaultBehavior) {
8764     // aPos->mWordMovementType possible values:
8765     //       eEndWord: eat the space if we're moving backwards
8766     //       eStartWord: eat the space if we're moving forwards
8767     return (aPos.mWordMovementType == eEndWord) ==
8768            (aPos.mDirection == eDirPrevious);
8769   }
8770   // Use the hidden preference which is based on operating system
8771   // behavior. This pref only affects whether moving forward by word
8772   // should go to the end of this word or start of the next word. When
8773   // going backwards, the start of the word is always used, on every
8774   // operating system.
8775   return aPos.mDirection == eDirNext &&
8776          StaticPrefs::layout_word_select_eat_space_to_next_word();
8777 }
8778 
8779 enum class OffsetIsAtLineEdge : bool { No, Yes };
8780 
SetPeekResultFromFrame(nsPeekOffsetStruct & aPos,nsIFrame * aFrame,int32_t aOffset,OffsetIsAtLineEdge aAtLineEdge)8781 static void SetPeekResultFromFrame(nsPeekOffsetStruct& aPos, nsIFrame* aFrame,
8782                                    int32_t aOffset,
8783                                    OffsetIsAtLineEdge aAtLineEdge) {
8784   FrameContentRange range = GetRangeForFrame(aFrame);
8785   aPos.mResultFrame = aFrame;
8786   aPos.mResultContent = range.content;
8787   // Output offset is relative to content, not frame
8788   aPos.mContentOffset =
8789       aOffset < 0 ? range.end + aOffset + 1 : range.start + aOffset;
8790   if (aAtLineEdge == OffsetIsAtLineEdge::Yes) {
8791     aPos.mAttach = aPos.mContentOffset == range.start ? CARET_ASSOCIATE_AFTER
8792                                                       : CARET_ASSOCIATE_BEFORE;
8793   }
8794 }
8795 
TransferTo(nsPeekOffsetStruct & aPos) const8796 void nsIFrame::SelectablePeekReport::TransferTo(
8797     nsPeekOffsetStruct& aPos) const {
8798   return SetPeekResultFromFrame(aPos, mFrame, mOffset, OffsetIsAtLineEdge::No);
8799 }
8800 
SelectablePeekReport(const mozilla::GenericErrorResult<nsresult> && aErr)8801 nsIFrame::SelectablePeekReport::SelectablePeekReport(
8802     const mozilla::GenericErrorResult<nsresult>&& aErr) {
8803   MOZ_ASSERT(NS_FAILED(aErr.operator nsresult()));
8804   // Return an empty report
8805 }
8806 
PeekOffsetForCharacter(nsPeekOffsetStruct * aPos,int32_t aOffset)8807 nsresult nsIFrame::PeekOffsetForCharacter(nsPeekOffsetStruct* aPos,
8808                                           int32_t aOffset) {
8809   SelectablePeekReport current{this, aOffset};
8810 
8811   nsIFrame::FrameSearchResult peekSearchState = CONTINUE;
8812 
8813   while (peekSearchState != FOUND) {
8814     bool movingInFrameDirection = IsMovingInFrameDirection(
8815         current.mFrame, aPos->mDirection, aPos->mVisual);
8816 
8817     if (current.mJumpedLine) {
8818       // If we jumped lines, it's as if we found a character, but we still need
8819       // to eat non-renderable content on the new line.
8820       peekSearchState = current.PeekOffsetNoAmount(movingInFrameDirection);
8821     } else {
8822       PeekOffsetCharacterOptions options;
8823       options.mRespectClusters = aPos->mAmount == eSelectCluster;
8824       peekSearchState =
8825           current.PeekOffsetCharacter(movingInFrameDirection, options);
8826     }
8827 
8828     current.mMovedOverNonSelectableText |=
8829         peekSearchState == CONTINUE_UNSELECTABLE;
8830 
8831     if (peekSearchState != FOUND) {
8832       SelectablePeekReport next = current.mFrame->GetFrameFromDirection(*aPos);
8833       if (next.Failed()) {
8834         return NS_ERROR_FAILURE;
8835       }
8836       next.mJumpedLine |= current.mJumpedLine;
8837       next.mMovedOverNonSelectableText |= current.mMovedOverNonSelectableText;
8838       next.mHasSelectableFrame |= current.mHasSelectableFrame;
8839       current = next;
8840     }
8841 
8842     // Found frame, but because we moved over non selectable text we want
8843     // the offset to be at the frame edge. Note that if we are extending the
8844     // selection, this doesn't matter.
8845     if (peekSearchState == FOUND && current.mMovedOverNonSelectableText &&
8846         (!aPos->mExtend || current.mHasSelectableFrame)) {
8847       auto [start, end] = current.mFrame->GetOffsets();
8848       current.mOffset = aPos->mDirection == eDirNext ? 0 : end - start;
8849     }
8850   }
8851 
8852   // Set outputs
8853   current.TransferTo(*aPos);
8854   // If we're dealing with a text frame and moving backward positions us at
8855   // the end of that line, decrease the offset by one to make sure that
8856   // we're placed before the linefeed character on the previous line.
8857   if (current.mOffset < 0 && current.mJumpedLine &&
8858       aPos->mDirection == eDirPrevious &&
8859       current.mFrame->HasSignificantTerminalNewline() &&
8860       !current.mIgnoredBrFrame) {
8861     --aPos->mContentOffset;
8862   }
8863   return NS_OK;
8864 }
8865 
PeekOffsetForWord(nsPeekOffsetStruct * aPos,int32_t aOffset)8866 nsresult nsIFrame::PeekOffsetForWord(nsPeekOffsetStruct* aPos,
8867                                      int32_t aOffset) {
8868   SelectablePeekReport current{this, aOffset};
8869   bool shouldStopAtHardBreak =
8870       aPos->mWordMovementType == eDefaultBehavior &&
8871       StaticPrefs::layout_word_select_eat_space_to_next_word();
8872   bool wordSelectEatSpace = ShouldWordSelectionEatSpace(*aPos);
8873 
8874   PeekWordState state;
8875   while (true) {
8876     bool movingInFrameDirection = IsMovingInFrameDirection(
8877         current.mFrame, aPos->mDirection, aPos->mVisual);
8878 
8879     FrameSearchResult searchResult = current.mFrame->PeekOffsetWord(
8880         movingInFrameDirection, wordSelectEatSpace, aPos->mIsKeyboardSelect,
8881         &current.mOffset, &state, aPos->mTrimSpaces);
8882     if (searchResult == FOUND) {
8883       break;
8884     }
8885 
8886     SelectablePeekReport next = current.mFrame->GetFrameFromDirection(*aPos);
8887     if (next.Failed()) {
8888       // If we've crossed the line boundary, check to make sure that we
8889       // have not consumed a trailing newline as whitespace if it's
8890       // significant.
8891       if (next.mJumpedLine && wordSelectEatSpace &&
8892           current.mFrame->HasSignificantTerminalNewline() &&
8893           current.mFrame->StyleText()->mWhiteSpace !=
8894               StyleWhiteSpace::PreLine) {
8895         current.mOffset -= 1;
8896       }
8897       break;
8898     }
8899 
8900     if (next.mJumpedLine && !wordSelectEatSpace && state.mSawBeforeType) {
8901       // We can't jump lines if we're looking for whitespace following
8902       // non-whitespace, and we already encountered non-whitespace.
8903       break;
8904     }
8905 
8906     if (shouldStopAtHardBreak && next.mJumpedHardBreak) {
8907       /**
8908        * Prev, always: Jump and stop right there
8909        * Next, saw inline: just stop
8910        * Next, no inline: Jump and consume whitespaces
8911        */
8912       if (aPos->mDirection == eDirPrevious) {
8913         // Try moving to the previous line if exists
8914         current.TransferTo(*aPos);
8915         current.mFrame->PeekOffsetForCharacter(aPos, current.mOffset);
8916         return NS_OK;
8917       }
8918       if (state.mSawInlineCharacter || current.mJumpedHardBreak) {
8919         if (current.mFrame->HasSignificantTerminalNewline()) {
8920           current.mOffset -= 1;
8921         }
8922         current.TransferTo(*aPos);
8923         return NS_OK;
8924       }
8925       // Mark the state as whitespace and continue
8926       state.Update(false, true);
8927     }
8928 
8929     if (next.mJumpedLine) {
8930       state.mContext.Truncate();
8931     }
8932     current = next;
8933     // Jumping a line is equivalent to encountering whitespace
8934     // This affects only when it already met an actual character
8935     if (wordSelectEatSpace && next.mJumpedLine) {
8936       state.SetSawBeforeType();
8937     }
8938   }
8939 
8940   // Set outputs
8941   current.TransferTo(*aPos);
8942   return NS_OK;
8943 }
8944 
PeekOffsetForLine(nsPeekOffsetStruct * aPos)8945 nsresult nsIFrame::PeekOffsetForLine(nsPeekOffsetStruct* aPos) {
8946   nsIFrame* blockFrame = this;
8947   nsresult result = NS_ERROR_FAILURE;
8948 
8949   while (NS_FAILED(result)) {
8950     auto [newBlock, lineFrame] =
8951         blockFrame->GetContainingBlockForLine(aPos->mScrollViewStop);
8952     if (!newBlock) {
8953       return NS_ERROR_FAILURE;
8954     }
8955     blockFrame = newBlock;
8956     nsAutoLineIterator iter = blockFrame->GetLineIterator();
8957     int32_t thisLine = iter->FindLineContaining(lineFrame);
8958     if (NS_WARN_IF(thisLine < 0)) {
8959       return NS_ERROR_FAILURE;
8960     }
8961 
8962     int edgeCase = 0;  // no edge case. this should look at thisLine
8963 
8964     bool doneLooping = false;  // tells us when no more block frames hit.
8965     // this part will find a frame or a block frame. if it's a block frame
8966     // it will "drill down" to find a viable frame or it will return an
8967     // error.
8968     nsIFrame* lastFrame = this;
8969     do {
8970       result = nsIFrame::GetNextPrevLineFromeBlockFrame(
8971           PresContext(), aPos, blockFrame, thisLine,
8972           edgeCase);  // start from thisLine
8973 
8974       // we came back to same spot! keep going
8975       if (NS_SUCCEEDED(result) &&
8976           (!aPos->mResultFrame || aPos->mResultFrame == lastFrame)) {
8977         aPos->mResultFrame = nullptr;
8978         if (aPos->mDirection == eDirPrevious) {
8979           thisLine--;
8980         } else {
8981           thisLine++;
8982         }
8983       } else {               // if failure or success with different frame.
8984         doneLooping = true;  // do not continue with while loop
8985       }
8986 
8987       lastFrame = aPos->mResultFrame;  // set last frame
8988 
8989       // make sure block element is not the same as the one we had before
8990       if (NS_SUCCEEDED(result) && aPos->mResultFrame &&
8991           blockFrame != aPos->mResultFrame) {
8992         /* SPECIAL CHECK FOR TABLE NAVIGATION
8993             tables need to navigate also and the frame that supports it is
8994             nsTableRowGroupFrame which is INSIDE nsTableWrapperFrame.
8995             If we have stumbled onto an nsTableWrapperFrame we need to drill
8996             into nsTableRowGroup if we hit a header or footer that's ok just
8997             go into them.
8998           */
8999         bool searchTableBool = false;
9000         if (aPos->mResultFrame->IsTableWrapperFrame() ||
9001             aPos->mResultFrame->IsTableCellFrame()) {
9002           nsIFrame* frame =
9003               aPos->mResultFrame->PrincipalChildList().FirstChild();
9004           // got the table frame now
9005           // ok time to drill down to find iterator
9006           while (frame) {
9007             if (frame->CanProvideLineIterator()) {
9008               aPos->mResultFrame = frame;
9009               searchTableBool = true;
9010               result = NS_OK;
9011               break;  // while(frame)
9012             }
9013             result = NS_ERROR_FAILURE;
9014             frame = frame->PrincipalChildList().FirstChild();
9015           }
9016         }
9017 
9018         if (!searchTableBool) {
9019           result = aPos->mResultFrame->CanProvideLineIterator()
9020                        ? NS_OK
9021                        : NS_ERROR_FAILURE;
9022         }
9023 
9024         // we've struck another block element!
9025         if (NS_SUCCEEDED(result)) {
9026           doneLooping = false;
9027           if (aPos->mDirection == eDirPrevious) {
9028             edgeCase = 1;  // far edge, search from end backwards
9029           } else {
9030             edgeCase = -1;  // near edge search from beginning onwards
9031           }
9032           thisLine = 0;  // this line means nothing now.
9033           // everything else means something so keep looking "inside" the
9034           // block
9035           blockFrame = aPos->mResultFrame;
9036         } else {
9037           // THIS is to mean that everything is ok to the containing while
9038           // loop
9039           result = NS_OK;
9040           break;
9041         }
9042       }
9043     } while (!doneLooping);
9044   }
9045   return result;
9046 }
9047 
PeekOffsetForLineEdge(nsPeekOffsetStruct * aPos)9048 nsresult nsIFrame::PeekOffsetForLineEdge(nsPeekOffsetStruct* aPos) {
9049   // Adjusted so that the caret can't get confused when content changes
9050   nsIFrame* frame = AdjustFrameForSelectionStyles(this);
9051   Element* editingHost = frame->GetContent()->GetEditingHost();
9052 
9053   auto [blockFrame, lineFrame] =
9054       frame->GetContainingBlockForLine(aPos->mScrollViewStop);
9055   if (!blockFrame) {
9056     return NS_ERROR_FAILURE;
9057   }
9058   nsAutoLineIterator it = blockFrame->GetLineIterator();
9059   int32_t thisLine = it->FindLineContaining(lineFrame);
9060   if (thisLine < 0) {
9061     return NS_ERROR_FAILURE;
9062   }
9063 
9064   nsIFrame* baseFrame = nullptr;
9065   bool endOfLine = (eSelectEndLine == aPos->mAmount);
9066 
9067   if (aPos->mVisual && PresContext()->BidiEnabled()) {
9068     nsIFrame* firstFrame;
9069     bool isReordered;
9070     nsIFrame* lastFrame;
9071     MOZ_TRY(
9072         it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame));
9073     baseFrame = endOfLine ? lastFrame : firstFrame;
9074   } else {
9075     auto line = it->GetLine(thisLine).unwrap();
9076 
9077     nsIFrame* frame = line.mFirstFrameOnLine;
9078     bool lastFrameWasEditable = false;
9079     for (int32_t count = line.mNumFramesOnLine; count;
9080          --count, frame = frame->GetNextSibling()) {
9081       if (frame->IsGeneratedContentFrame()) {
9082         continue;
9083       }
9084       // When jumping to the end of the line with the "end" key,
9085       // try to skip over brFrames
9086       if (endOfLine && line.mNumFramesOnLine > 1 && frame->IsBrFrame() &&
9087           lastFrameWasEditable == frame->GetContent()->IsEditable()) {
9088         continue;
9089       }
9090       lastFrameWasEditable =
9091           frame->GetContent() && frame->GetContent()->IsEditable();
9092       baseFrame = frame;
9093       if (!endOfLine) {
9094         break;
9095       }
9096     }
9097   }
9098   if (!baseFrame) {
9099     return NS_ERROR_FAILURE;
9100   }
9101   // Make sure we are not leaving our inline editing host if exists
9102   if (editingHost) {
9103     if (nsIFrame* frame = editingHost->GetPrimaryFrame()) {
9104       if (frame->IsInlineOutside() &&
9105           !editingHost->Contains(baseFrame->GetContent())) {
9106         baseFrame = frame;
9107         if (endOfLine) {
9108           baseFrame = baseFrame->LastContinuation();
9109         }
9110       }
9111     }
9112   }
9113   FrameTarget targetFrame = DrillDownToSelectionFrame(baseFrame, endOfLine, 0);
9114   SetPeekResultFromFrame(*aPos, targetFrame.frame, endOfLine ? -1 : 0,
9115                          OffsetIsAtLineEdge::Yes);
9116   if (endOfLine && targetFrame.frame->HasSignificantTerminalNewline()) {
9117     // Do not position the caret after the terminating newline if we're
9118     // trying to move to the end of line (see bug 596506)
9119     --aPos->mContentOffset;
9120   }
9121   if (!aPos->mResultContent) {
9122     return NS_ERROR_FAILURE;
9123   }
9124   return NS_OK;
9125 }
9126 
PeekOffset(nsPeekOffsetStruct * aPos)9127 nsresult nsIFrame::PeekOffset(nsPeekOffsetStruct* aPos) {
9128   MOZ_ASSERT(aPos);
9129 
9130   if (NS_WARN_IF(HasAnyStateBits(NS_FRAME_IS_DIRTY))) {
9131     // FIXME(Bug 1654362): <caption> currently can remain dirty.
9132     return NS_ERROR_UNEXPECTED;
9133   }
9134 
9135   // Translate content offset to be relative to frame
9136   int32_t offset = aPos->mStartOffset - GetRangeForFrame(this).start;
9137 
9138   switch (aPos->mAmount) {
9139     case eSelectCharacter:
9140     case eSelectCluster:
9141       return PeekOffsetForCharacter(aPos, offset);
9142     case eSelectWordNoSpace:
9143       // eSelectWordNoSpace means that we should not be eating any whitespace
9144       // when moving to the adjacent word.  This means that we should set aPos->
9145       // mWordMovementType to eEndWord if we're moving forwards, and to
9146       // eStartWord if we're moving backwards.
9147       if (aPos->mDirection == eDirPrevious) {
9148         aPos->mWordMovementType = eStartWord;
9149       } else {
9150         aPos->mWordMovementType = eEndWord;
9151       }
9152       // Intentionally fall through the eSelectWord case.
9153       [[fallthrough]];
9154     case eSelectWord:
9155       return PeekOffsetForWord(aPos, offset);
9156     case eSelectLine:
9157       return PeekOffsetForLine(aPos);
9158     case eSelectBeginLine:
9159     case eSelectEndLine:
9160       return PeekOffsetForLineEdge(aPos);
9161     case eSelectParagraph:
9162       return PeekOffsetForParagraph(aPos);
9163     default: {
9164       NS_ASSERTION(false, "Invalid amount");
9165       return NS_ERROR_FAILURE;
9166     }
9167   }
9168 }
9169 
PeekOffsetNoAmount(bool aForward,int32_t * aOffset)9170 nsIFrame::FrameSearchResult nsIFrame::PeekOffsetNoAmount(bool aForward,
9171                                                          int32_t* aOffset) {
9172   NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range");
9173   // Sure, we can stop right here.
9174   return FOUND;
9175 }
9176 
PeekOffsetCharacter(bool aForward,int32_t * aOffset,PeekOffsetCharacterOptions aOptions)9177 nsIFrame::FrameSearchResult nsIFrame::PeekOffsetCharacter(
9178     bool aForward, int32_t* aOffset, PeekOffsetCharacterOptions aOptions) {
9179   NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range");
9180   int32_t startOffset = *aOffset;
9181   // A negative offset means "end of frame", which in our case means offset 1.
9182   if (startOffset < 0) startOffset = 1;
9183   if (aForward == (startOffset == 0)) {
9184     // We're before the frame and moving forward, or after it and moving
9185     // backwards: skip to the other side and we're done.
9186     *aOffset = 1 - startOffset;
9187     return FOUND;
9188   }
9189   return CONTINUE;
9190 }
9191 
PeekOffsetWord(bool aForward,bool aWordSelectEatSpace,bool aIsKeyboardSelect,int32_t * aOffset,PeekWordState * aState,bool)9192 nsIFrame::FrameSearchResult nsIFrame::PeekOffsetWord(
9193     bool aForward, bool aWordSelectEatSpace, bool aIsKeyboardSelect,
9194     int32_t* aOffset, PeekWordState* aState, bool /*aTrimSpaces*/) {
9195   NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range");
9196   int32_t startOffset = *aOffset;
9197   // This isn't text, so truncate the context
9198   aState->mContext.Truncate();
9199   if (startOffset < 0) startOffset = 1;
9200   if (aForward == (startOffset == 0)) {
9201     // We're before the frame and moving forward, or after it and moving
9202     // backwards. If we're looking for non-whitespace, we found it (without
9203     // skipping this frame).
9204     if (!aState->mAtStart) {
9205       if (aState->mLastCharWasPunctuation) {
9206         // We're not punctuation, so this is a punctuation boundary.
9207         if (BreakWordBetweenPunctuation(aState, aForward, false, false,
9208                                         aIsKeyboardSelect))
9209           return FOUND;
9210       } else {
9211         // This is not a punctuation boundary.
9212         if (aWordSelectEatSpace && aState->mSawBeforeType) return FOUND;
9213       }
9214     }
9215     // Otherwise skip to the other side and note that we encountered
9216     // non-whitespace.
9217     *aOffset = 1 - startOffset;
9218     aState->Update(false,  // not punctuation
9219                    false   // not whitespace
9220     );
9221     if (!aWordSelectEatSpace) aState->SetSawBeforeType();
9222   }
9223   return CONTINUE;
9224 }
9225 
9226 // static
BreakWordBetweenPunctuation(const PeekWordState * aState,bool aForward,bool aPunctAfter,bool aWhitespaceAfter,bool aIsKeyboardSelect)9227 bool nsIFrame::BreakWordBetweenPunctuation(const PeekWordState* aState,
9228                                            bool aForward, bool aPunctAfter,
9229                                            bool aWhitespaceAfter,
9230                                            bool aIsKeyboardSelect) {
9231   NS_ASSERTION(aPunctAfter != aState->mLastCharWasPunctuation,
9232                "Call this only at punctuation boundaries");
9233   if (aState->mLastCharWasWhitespace) {
9234     // We always stop between whitespace and punctuation
9235     return true;
9236   }
9237   if (!StaticPrefs::layout_word_select_stop_at_punctuation()) {
9238     // When this pref is false, we never stop at a punctuation boundary unless
9239     // it's followed by whitespace (in the relevant direction).
9240     return aWhitespaceAfter;
9241   }
9242   if (!aIsKeyboardSelect) {
9243     // mouse caret movement (e.g. word selection) always stops at every
9244     // punctuation boundary
9245     return true;
9246   }
9247   bool afterPunct = aForward ? aState->mLastCharWasPunctuation : aPunctAfter;
9248   if (!afterPunct) {
9249     // keyboard caret movement only stops after punctuation (in content order)
9250     return false;
9251   }
9252   // Stop only if we've seen some non-punctuation since the last whitespace;
9253   // don't stop after punctuation that follows whitespace.
9254   return aState->mSeenNonPunctuationSinceWhitespace;
9255 }
9256 
CheckVisibility(nsPresContext *,int32_t,int32_t,bool,bool *,bool *)9257 nsresult nsIFrame::CheckVisibility(nsPresContext*, int32_t, int32_t, bool,
9258                                    bool*, bool*) {
9259   return NS_ERROR_NOT_IMPLEMENTED;
9260 }
9261 
GetContainingBlockForLine(bool aLockScroll) const9262 std::pair<nsIFrame*, nsIFrame*> nsIFrame::GetContainingBlockForLine(
9263     bool aLockScroll) const {
9264   const nsIFrame* parentFrame = this;
9265   const nsIFrame* frame;
9266   while (parentFrame) {
9267     frame = parentFrame;
9268     if (frame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
9269       // if we are searching for a frame that is not in flow we will not find
9270       // it. we must instead look for its placeholder
9271       if (frame->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
9272         // abspos continuations don't have placeholders, get the fif
9273         frame = frame->FirstInFlow();
9274       }
9275       frame = frame->GetPlaceholderFrame();
9276       if (!frame) {
9277         return std::pair(nullptr, nullptr);
9278       }
9279     }
9280     parentFrame = frame->GetParent();
9281     if (parentFrame) {
9282       if (aLockScroll && parentFrame->IsScrollFrame()) {
9283         return std::pair(nullptr, nullptr);
9284       }
9285       if (parentFrame->CanProvideLineIterator()) {
9286         return std::pair(const_cast<nsIFrame*>(parentFrame),
9287                          const_cast<nsIFrame*>(frame));
9288       }
9289     }
9290   }
9291   return std::pair(nullptr, nullptr);
9292 }
9293 
IsVisuallyAtLineEdge(nsILineIterator * aLineIterator,int32_t aLine,nsDirection aDirection)9294 Result<bool, nsresult> nsIFrame::IsVisuallyAtLineEdge(
9295     nsILineIterator* aLineIterator, int32_t aLine, nsDirection aDirection) {
9296   nsIFrame* firstFrame;
9297   nsIFrame* lastFrame;
9298 
9299   bool lineIsRTL = aLineIterator->GetDirection();
9300   bool isReordered;
9301 
9302   MOZ_TRY(aLineIterator->CheckLineOrder(aLine, &isReordered, &firstFrame,
9303                                         &lastFrame));
9304 
9305   nsIFrame** framePtr = aDirection == eDirPrevious ? &firstFrame : &lastFrame;
9306   if (!*framePtr) {
9307     return true;
9308   }
9309 
9310   bool frameIsRTL = (nsBidiPresUtils::FrameDirection(*framePtr) ==
9311                      mozilla::intl::BidiDirection::RTL);
9312   if ((frameIsRTL == lineIsRTL) == (aDirection == eDirPrevious)) {
9313     nsIFrame::GetFirstLeaf(framePtr);
9314   } else {
9315     nsIFrame::GetLastLeaf(framePtr);
9316   }
9317   return *framePtr == this;
9318 }
9319 
IsLogicallyAtLineEdge(nsILineIterator * aLineIterator,int32_t aLine,nsDirection aDirection)9320 Result<bool, nsresult> nsIFrame::IsLogicallyAtLineEdge(
9321     nsILineIterator* aLineIterator, int32_t aLine, nsDirection aDirection) {
9322   auto line = aLineIterator->GetLine(aLine).unwrap();
9323 
9324   if (aDirection == eDirPrevious) {
9325     nsIFrame* firstFrame = line.mFirstFrameOnLine;
9326     nsIFrame::GetFirstLeaf(&firstFrame);
9327     return firstFrame == this;
9328   }
9329 
9330   // eDirNext
9331   nsIFrame* lastFrame = line.mFirstFrameOnLine;
9332   for (int32_t lineFrameCount = line.mNumFramesOnLine; lineFrameCount > 1;
9333        lineFrameCount--) {
9334     lastFrame = lastFrame->GetNextSibling();
9335     if (!lastFrame) {
9336       NS_ERROR("should not be reached nsIFrame");
9337       return Err(NS_ERROR_FAILURE);
9338     }
9339   }
9340   nsIFrame::GetLastLeaf(&lastFrame);
9341   return lastFrame == this;
9342 }
9343 
GetFrameFromDirection(nsDirection aDirection,bool aVisual,bool aJumpLines,bool aScrollViewStop,bool aForceEditableRegion)9344 nsIFrame::SelectablePeekReport nsIFrame::GetFrameFromDirection(
9345     nsDirection aDirection, bool aVisual, bool aJumpLines, bool aScrollViewStop,
9346     bool aForceEditableRegion) {
9347   SelectablePeekReport result;
9348 
9349   nsPresContext* presContext = PresContext();
9350   bool needsVisualTraversal = aVisual && presContext->BidiEnabled();
9351   nsCOMPtr<nsIFrameEnumerator> frameTraversal;
9352   MOZ_TRY(NS_NewFrameTraversal(getter_AddRefs(frameTraversal), presContext,
9353                                this, eLeaf, needsVisualTraversal,
9354                                aScrollViewStop,
9355                                true,  // aFollowOOFs
9356                                false  // aSkipPopupChecks
9357                                ));
9358 
9359   // Find the prev/next selectable frame
9360   bool selectable = false;
9361   nsIFrame* traversedFrame = this;
9362   while (!selectable) {
9363     auto [blockFrame, lineFrame] =
9364         traversedFrame->GetContainingBlockForLine(aScrollViewStop);
9365     if (!blockFrame) {
9366       return result;
9367     }
9368 
9369     nsAutoLineIterator it = blockFrame->GetLineIterator();
9370     int32_t thisLine = it->FindLineContaining(lineFrame);
9371     if (thisLine < 0) {
9372       return result;
9373     }
9374 
9375     bool atLineEdge;
9376     MOZ_TRY_VAR(
9377         atLineEdge,
9378         needsVisualTraversal
9379             ? traversedFrame->IsVisuallyAtLineEdge(it, thisLine, aDirection)
9380             : traversedFrame->IsLogicallyAtLineEdge(it, thisLine, aDirection));
9381     if (atLineEdge) {
9382       result.mJumpedLine = true;
9383       if (!aJumpLines) {
9384         return result;  // we are done. cannot jump lines
9385       }
9386       int32_t lineToCheckWrap =
9387           aDirection == eDirPrevious ? thisLine - 1 : thisLine;
9388       if (lineToCheckWrap < 0 ||
9389           !it->GetLine(lineToCheckWrap).unwrap().mIsWrapped) {
9390         result.mJumpedHardBreak = true;
9391       }
9392     }
9393 
9394     traversedFrame = frameTraversal->Traverse(aDirection == eDirNext);
9395     if (!traversedFrame) {
9396       return result;
9397     }
9398 
9399     auto IsSelectable = [aForceEditableRegion](const nsIFrame* aFrame) {
9400       if (!aFrame->IsSelectable(nullptr)) {
9401         return false;
9402       }
9403       return !aForceEditableRegion || aFrame->GetContent()->IsEditable();
9404     };
9405 
9406     // Skip br frames, but only if we can select something before hitting the
9407     // end of the line or a non-selectable region.
9408     if (atLineEdge && aDirection == eDirPrevious &&
9409         traversedFrame->IsBrFrame()) {
9410       for (nsIFrame* current = traversedFrame->GetPrevSibling(); current;
9411            current = current->GetPrevSibling()) {
9412         if (!current->IsBlockOutside() && IsSelectable(current)) {
9413           if (!current->IsBrFrame()) {
9414             result.mIgnoredBrFrame = true;
9415           }
9416           break;
9417         }
9418       }
9419       if (result.mIgnoredBrFrame) {
9420         continue;
9421       }
9422     }
9423 
9424     selectable = IsSelectable(traversedFrame);
9425     if (!selectable) {
9426       if (traversedFrame->IsSelectable(nullptr)) {
9427         result.mHasSelectableFrame = true;
9428       }
9429       result.mMovedOverNonSelectableText = true;
9430     }
9431   }  // while (!selectable)
9432 
9433   result.mOffset = (aDirection == eDirNext) ? 0 : -1;
9434 
9435   if (aVisual && nsBidiPresUtils::IsReversedDirectionFrame(traversedFrame)) {
9436     // The new frame is reverse-direction, go to the other end
9437     result.mOffset = -1 - result.mOffset;
9438   }
9439   result.mFrame = traversedFrame;
9440   return result;
9441 }
9442 
GetFrameFromDirection(const nsPeekOffsetStruct & aPos)9443 nsIFrame::SelectablePeekReport nsIFrame::GetFrameFromDirection(
9444     const nsPeekOffsetStruct& aPos) {
9445   return GetFrameFromDirection(aPos.mDirection, aPos.mVisual, aPos.mJumpLines,
9446                                aPos.mScrollViewStop, aPos.mForceEditableRegion);
9447 }
9448 
GetClosestView(nsPoint * aOffset) const9449 nsView* nsIFrame::GetClosestView(nsPoint* aOffset) const {
9450   nsPoint offset(0, 0);
9451   for (const nsIFrame* f = this; f; f = f->GetParent()) {
9452     if (f->HasView()) {
9453       if (aOffset) *aOffset = offset;
9454       return f->GetView();
9455     }
9456     offset += f->GetPosition();
9457   }
9458 
9459   MOZ_ASSERT_UNREACHABLE("No view on any parent?  How did that happen?");
9460   return nullptr;
9461 }
9462 
9463 /* virtual */
ChildIsDirty(nsIFrame * aChild)9464 void nsIFrame::ChildIsDirty(nsIFrame* aChild) {
9465   MOZ_ASSERT_UNREACHABLE(
9466       "should never be called on a frame that doesn't "
9467       "inherit from nsContainerFrame");
9468 }
9469 
9470 #ifdef ACCESSIBILITY
AccessibleType()9471 a11y::AccType nsIFrame::AccessibleType() {
9472   if (IsTableCaption() && !GetRect().IsEmpty()) {
9473     return a11y::eHTMLCaptionType;
9474   }
9475   return a11y::eNoType;
9476 }
9477 #endif
9478 
ClearOverflowRects()9479 bool nsIFrame::ClearOverflowRects() {
9480   if (mOverflow.mType == OverflowStorageType::None) {
9481     return false;
9482   }
9483   if (mOverflow.mType == OverflowStorageType::Large) {
9484     RemoveProperty(OverflowAreasProperty());
9485   }
9486   mOverflow.mType = OverflowStorageType::None;
9487   return true;
9488 }
9489 
SetOverflowAreas(const OverflowAreas & aOverflowAreas)9490 bool nsIFrame::SetOverflowAreas(const OverflowAreas& aOverflowAreas) {
9491   if (mOverflow.mType == OverflowStorageType::Large) {
9492     OverflowAreas* overflow = GetOverflowAreasProperty();
9493     bool changed = *overflow != aOverflowAreas;
9494     *overflow = aOverflowAreas;
9495 
9496     // Don't bother with converting to the deltas form if we already
9497     // have a property.
9498     return changed;
9499   }
9500 
9501   const nsRect& vis = aOverflowAreas.InkOverflow();
9502   uint32_t l = -vis.x,                 // left edge: positive delta is leftwards
9503       t = -vis.y,                      // top: positive is upwards
9504       r = vis.XMost() - mRect.width,   // right: positive is rightwards
9505       b = vis.YMost() - mRect.height;  // bottom: positive is downwards
9506   if (aOverflowAreas.ScrollableOverflow().IsEqualEdges(
9507           nsRect(nsPoint(0, 0), GetSize())) &&
9508       l <= InkOverflowDeltas::kMax && t <= InkOverflowDeltas::kMax &&
9509       r <= InkOverflowDeltas::kMax && b <= InkOverflowDeltas::kMax &&
9510       // we have to check these against zero because we *never* want to
9511       // set a frame as having no overflow in this function.  This is
9512       // because FinishAndStoreOverflow calls this function prior to
9513       // SetRect based on whether the overflow areas match aNewSize.
9514       // In the case where the overflow areas exactly match mRect but
9515       // do not match aNewSize, we need to store overflow in a property
9516       // so that our eventual SetRect/SetSize will know that it has to
9517       // reset our overflow areas.
9518       (l | t | r | b) != 0) {
9519     InkOverflowDeltas oldDeltas = mOverflow.mInkOverflowDeltas;
9520     // It's a "small" overflow area so we store the deltas for each edge
9521     // directly in the frame, rather than allocating a separate rect.
9522     // If they're all zero, that's fine; we're setting things to
9523     // no-overflow.
9524     mOverflow.mInkOverflowDeltas.mLeft = l;
9525     mOverflow.mInkOverflowDeltas.mTop = t;
9526     mOverflow.mInkOverflowDeltas.mRight = r;
9527     mOverflow.mInkOverflowDeltas.mBottom = b;
9528     // There was no scrollable overflow before, and there isn't now.
9529     return oldDeltas != mOverflow.mInkOverflowDeltas;
9530   } else {
9531     bool changed =
9532         !aOverflowAreas.ScrollableOverflow().IsEqualEdges(
9533             nsRect(nsPoint(0, 0), GetSize())) ||
9534         !aOverflowAreas.InkOverflow().IsEqualEdges(InkOverflowFromDeltas());
9535 
9536     // it's a large overflow area that we need to store as a property
9537     mOverflow.mType = OverflowStorageType::Large;
9538     AddProperty(OverflowAreasProperty(), new OverflowAreas(aOverflowAreas));
9539     return changed;
9540   }
9541 }
9542 
9543 enum class ApplyTransform : bool { No, Yes };
9544 
9545 /**
9546  * Compute the outline inner rect (so without outline-width and outline-offset)
9547  * of aFrame, maybe iterating over its descendants, in aFrame's coordinate space
9548  * or its post-transform coordinate space (depending on aApplyTransform).
9549  */
ComputeOutlineInnerRect(nsIFrame * aFrame,ApplyTransform aApplyTransform,bool & aOutValid,const nsSize * aSizeOverride=nullptr,const OverflowAreas * aOverflowOverride=nullptr)9550 static nsRect ComputeOutlineInnerRect(
9551     nsIFrame* aFrame, ApplyTransform aApplyTransform, bool& aOutValid,
9552     const nsSize* aSizeOverride = nullptr,
9553     const OverflowAreas* aOverflowOverride = nullptr) {
9554   const nsRect bounds(nsPoint(0, 0),
9555                       aSizeOverride ? *aSizeOverride : aFrame->GetSize());
9556 
9557   // The SVG container frames besides SVGTextFrame do not maintain
9558   // an accurate mRect. It will make the outline be larger than
9559   // we expect, we need to make them narrow to their children's outline.
9560   // aOutValid is set to false if the returned nsRect is not valid
9561   // and should not be included in the outline rectangle.
9562   aOutValid = !aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT) ||
9563               !aFrame->IsFrameOfType(nsIFrame::eSVGContainer) ||
9564               aFrame->IsSVGTextFrame();
9565 
9566   nsRect u;
9567 
9568   if (!aFrame->FrameMaintainsOverflow()) {
9569     return u;
9570   }
9571 
9572   // Start from our border-box, transformed.  See comment below about
9573   // transform of children.
9574   bool doTransform =
9575       aApplyTransform == ApplyTransform::Yes && aFrame->IsTransformed();
9576   TransformReferenceBox boundsRefBox(nullptr, bounds);
9577   if (doTransform) {
9578     u = nsDisplayTransform::TransformRect(bounds, aFrame, boundsRefBox);
9579   } else {
9580     u = bounds;
9581   }
9582 
9583   if (aOutValid && !StaticPrefs::layout_outline_include_overflow()) {
9584     return u;
9585   }
9586 
9587   // Only iterate through the children if the overflow areas suggest
9588   // that we might need to, and if the frame doesn't clip its overflow
9589   // anyway.
9590   if (aOverflowOverride) {
9591     if (!doTransform && bounds.IsEqualEdges(aOverflowOverride->InkOverflow()) &&
9592         bounds.IsEqualEdges(aOverflowOverride->ScrollableOverflow())) {
9593       return u;
9594     }
9595   } else {
9596     if (!doTransform && bounds.IsEqualEdges(aFrame->InkOverflowRect()) &&
9597         bounds.IsEqualEdges(aFrame->ScrollableOverflowRect())) {
9598       return u;
9599     }
9600   }
9601   const nsStyleDisplay* disp = aFrame->StyleDisplay();
9602   LayoutFrameType fType = aFrame->Type();
9603   auto overflowClipAxes = aFrame->ShouldApplyOverflowClipping(disp);
9604   if (overflowClipAxes == nsIFrame::PhysicalAxes::Both ||
9605       fType == LayoutFrameType::Scroll ||
9606       fType == LayoutFrameType::ListControl ||
9607       fType == LayoutFrameType::SVGOuterSVG) {
9608     return u;
9609   }
9610 
9611   const nsStyleEffects* effects = aFrame->StyleEffects();
9612   Maybe<nsRect> clipPropClipRect =
9613       aFrame->GetClipPropClipRect(disp, effects, bounds.Size());
9614 
9615   // Iterate over all children except pop-up, absolutely-positioned,
9616   // float, and overflow ones.
9617   const nsIFrame::ChildListIDs skip = {
9618       nsIFrame::kPopupList,    nsIFrame::kSelectPopupList,
9619       nsIFrame::kAbsoluteList, nsIFrame::kFixedList,
9620       nsIFrame::kFloatList,    nsIFrame::kOverflowList};
9621   for (const auto& [list, listID] : aFrame->ChildLists()) {
9622     if (skip.contains(listID)) {
9623       continue;
9624     }
9625 
9626     for (nsIFrame* child : list) {
9627       if (child->IsPlaceholderFrame()) {
9628         continue;
9629       }
9630 
9631       // Note that passing ApplyTransform::Yes when
9632       // child->Combines3DTransformWithAncestors() returns true is incorrect if
9633       // our aApplyTransform is No... but the opposite would be as well.
9634       // This is because elements within a preserve-3d scene are always
9635       // transformed up to the top of the scene.  This means we don't have a
9636       // mechanism for getting a transform up to an intermediate point within
9637       // the scene.  We choose to over-transform rather than under-transform
9638       // because this is consistent with other overflow areas.
9639       bool validRect = true;
9640       nsRect childRect =
9641           ComputeOutlineInnerRect(child, ApplyTransform::Yes, validRect) +
9642           child->GetPosition();
9643 
9644       if (!validRect) {
9645         continue;
9646       }
9647 
9648       if (clipPropClipRect) {
9649         // Intersect with the clip before transforming.
9650         childRect.IntersectRect(childRect, *clipPropClipRect);
9651       }
9652 
9653       // Note that we transform each child separately according to
9654       // aFrame's transform, and then union, which gives a different
9655       // (smaller) result from unioning and then transforming the
9656       // union.  This doesn't match the way we handle overflow areas
9657       // with 2-D transforms, though it does match the way we handle
9658       // overflow areas in preserve-3d 3-D scenes.
9659       if (doTransform && !child->Combines3DTransformWithAncestors()) {
9660         childRect =
9661             nsDisplayTransform::TransformRect(childRect, aFrame, boundsRefBox);
9662       }
9663 
9664       // If a SVGContainer has a non-SVGContainer child, we assign
9665       // its child's outline to this SVGContainer directly.
9666       if (!aOutValid && validRect) {
9667         u = childRect;
9668         aOutValid = true;
9669       } else {
9670         u = u.UnionEdges(childRect);
9671       }
9672     }
9673   }
9674 
9675   if (overflowClipAxes & nsIFrame::PhysicalAxes::Vertical) {
9676     u.y = bounds.y;
9677     u.height = bounds.height;
9678   }
9679   if (overflowClipAxes & nsIFrame::PhysicalAxes::Horizontal) {
9680     u.x = bounds.x;
9681     u.width = bounds.width;
9682   }
9683 
9684   return u;
9685 }
9686 
ComputeAndIncludeOutlineArea(nsIFrame * aFrame,OverflowAreas & aOverflowAreas,const nsSize & aNewSize)9687 static void ComputeAndIncludeOutlineArea(nsIFrame* aFrame,
9688                                          OverflowAreas& aOverflowAreas,
9689                                          const nsSize& aNewSize) {
9690   const nsStyleOutline* outline = aFrame->StyleOutline();
9691   if (!outline->ShouldPaintOutline()) {
9692     return;
9693   }
9694 
9695   // When the outline property is set on a :-moz-block-inside-inline-wrapper
9696   // pseudo-element, it inherited that outline from the inline that was broken
9697   // because it contained a block.  In that case, we don't want a really wide
9698   // outline if the block inside the inline is narrow, so union the actual
9699   // contents of the anonymous blocks.
9700   nsIFrame* frameForArea = aFrame;
9701   do {
9702     PseudoStyleType pseudoType = frameForArea->Style()->GetPseudoType();
9703     if (pseudoType != PseudoStyleType::mozBlockInsideInlineWrapper) break;
9704     // If we're done, we really want it and all its later siblings.
9705     frameForArea = frameForArea->PrincipalChildList().FirstChild();
9706     NS_ASSERTION(frameForArea, "anonymous block with no children?");
9707   } while (frameForArea);
9708 
9709   // Find the union of the border boxes of all descendants, or in
9710   // the block-in-inline case, all descendants we care about.
9711   //
9712   // Note that the interesting perspective-related cases are taken
9713   // care of by the code that handles those issues for overflow
9714   // calling FinishAndStoreOverflow again, which in turn calls this
9715   // function again.  We still need to deal with preserve-3d a bit.
9716   nsRect innerRect;
9717   bool validRect = false;
9718   if (frameForArea == aFrame) {
9719     innerRect = ComputeOutlineInnerRect(aFrame, ApplyTransform::No, validRect,
9720                                         &aNewSize, &aOverflowAreas);
9721   } else {
9722     for (; frameForArea; frameForArea = frameForArea->GetNextSibling()) {
9723       nsRect r =
9724           ComputeOutlineInnerRect(frameForArea, ApplyTransform::Yes, validRect);
9725 
9726       // Adjust for offsets transforms up to aFrame's pre-transform
9727       // (i.e., normal) coordinate space; see comments in
9728       // UnionBorderBoxes for some of the subtlety here.
9729       for (nsIFrame *f = frameForArea, *parent = f->GetParent();
9730            /* see middle of loop */; f = parent, parent = f->GetParent()) {
9731         r += f->GetPosition();
9732         if (parent == aFrame) {
9733           break;
9734         }
9735         if (parent->IsTransformed() && !f->Combines3DTransformWithAncestors()) {
9736           TransformReferenceBox refBox(parent);
9737           r = nsDisplayTransform::TransformRect(r, parent, refBox);
9738         }
9739       }
9740 
9741       innerRect.UnionRect(innerRect, r);
9742     }
9743   }
9744 
9745   // Keep this code in sync with nsDisplayOutline::GetInnerRect.
9746   SetOrUpdateRectValuedProperty(aFrame, nsIFrame::OutlineInnerRectProperty(),
9747                                 innerRect);
9748   const nscoord offset = outline->mOutlineOffset.ToAppUnits();
9749   nsRect outerRect(innerRect);
9750   bool useOutlineAuto = false;
9751   if (StaticPrefs::layout_css_outline_style_auto_enabled()) {
9752     useOutlineAuto = outline->mOutlineStyle.IsAuto();
9753     if (MOZ_UNLIKELY(useOutlineAuto)) {
9754       nsPresContext* presContext = aFrame->PresContext();
9755       nsITheme* theme = presContext->Theme();
9756       if (theme->ThemeSupportsWidget(presContext, aFrame,
9757                                      StyleAppearance::FocusOutline)) {
9758         outerRect.Inflate(offset);
9759         theme->GetWidgetOverflow(presContext->DeviceContext(), aFrame,
9760                                  StyleAppearance::FocusOutline, &outerRect);
9761       } else {
9762         useOutlineAuto = false;
9763       }
9764     }
9765   }
9766   if (MOZ_LIKELY(!useOutlineAuto)) {
9767     nscoord width = outline->GetOutlineWidth();
9768     outerRect.Inflate(width + offset);
9769   }
9770 
9771   nsRect& vo = aOverflowAreas.InkOverflow();
9772   vo = vo.UnionEdges(innerRect.Union(outerRect));
9773 }
9774 
FinishAndStoreOverflow(OverflowAreas & aOverflowAreas,nsSize aNewSize,nsSize * aOldSize,const nsStyleDisplay * aStyleDisplay)9775 bool nsIFrame::FinishAndStoreOverflow(OverflowAreas& aOverflowAreas,
9776                                       nsSize aNewSize, nsSize* aOldSize,
9777                                       const nsStyleDisplay* aStyleDisplay) {
9778   MOZ_ASSERT(FrameMaintainsOverflow(),
9779              "Don't call - overflow rects not maintained on these SVG frames");
9780 
9781   const nsStyleDisplay* disp = StyleDisplayWithOptionalParam(aStyleDisplay);
9782   bool hasTransform = IsTransformed();
9783 
9784   nsRect bounds(nsPoint(0, 0), aNewSize);
9785   // Store the passed in overflow area if we are a preserve-3d frame or we have
9786   // a transform, and it's not just the frame bounds.
9787   if (hasTransform || Combines3DTransformWithAncestors()) {
9788     if (!aOverflowAreas.InkOverflow().IsEqualEdges(bounds) ||
9789         !aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds)) {
9790       OverflowAreas* initial = GetProperty(nsIFrame::InitialOverflowProperty());
9791       if (!initial) {
9792         AddProperty(nsIFrame::InitialOverflowProperty(),
9793                     new OverflowAreas(aOverflowAreas));
9794       } else if (initial != &aOverflowAreas) {
9795         *initial = aOverflowAreas;
9796       }
9797     } else {
9798       RemoveProperty(nsIFrame::InitialOverflowProperty());
9799     }
9800 #ifdef DEBUG
9801     SetProperty(nsIFrame::DebugInitialOverflowPropertyApplied(), true);
9802 #endif
9803   } else {
9804 #ifdef DEBUG
9805     RemoveProperty(nsIFrame::DebugInitialOverflowPropertyApplied());
9806 #endif
9807   }
9808 
9809   nsSize oldSize = mRect.Size();
9810   bool sizeChanged = ((aOldSize ? *aOldSize : oldSize) != aNewSize);
9811 
9812   // Our frame size may not have been computed and set yet, but code under
9813   // functions such as ComputeEffectsRect (which we're about to call) use the
9814   // values that are stored in our frame rect to compute their results.  We
9815   // need the results from those functions to be based on the frame size that
9816   // we *will* have, so we temporarily set our frame size here before calling
9817   // those functions.
9818   //
9819   // XXX Someone should document here why we revert the frame size before we
9820   // return rather than just leaving it set.
9821   //
9822   // We pass false here to avoid invalidating display items for this temporary
9823   // change. We sometimes reflow frames multiple times, with the final size
9824   // being the same as the initial. The single call to SetSize after reflow is
9825   // done will take care of invalidating display items if the size has actually
9826   // changed.
9827   SetSize(aNewSize, false);
9828 
9829   const auto overflowClipAxes = ShouldApplyOverflowClipping(disp);
9830 
9831   if (ChildrenHavePerspective(disp) && sizeChanged) {
9832     RecomputePerspectiveChildrenOverflow(this);
9833 
9834     if (overflowClipAxes != PhysicalAxes::Both) {
9835       aOverflowAreas.SetAllTo(bounds);
9836       DebugOnly<bool> ok = ComputeCustomOverflow(aOverflowAreas);
9837 
9838       // ComputeCustomOverflow() should not return false, when
9839       // FrameMaintainsOverflow() returns true.
9840       MOZ_ASSERT(ok, "FrameMaintainsOverflow() != ComputeCustomOverflow()");
9841 
9842       UnionChildOverflow(aOverflowAreas);
9843     }
9844   }
9845 
9846   // This is now called FinishAndStoreOverflow() instead of
9847   // StoreOverflow() because frame-generic ways of adding overflow
9848   // can happen here, e.g. CSS2 outline and native theme.
9849   // If the overflow area width or height is nscoord_MAX, then a
9850   // saturating union may have encounted an overflow, so the overflow may not
9851   // contain the frame border-box. Don't warn in that case.
9852   // Don't warn for SVG either, since SVG doesn't need the overflow area
9853   // to contain the frame bounds.
9854   for (const auto otype : AllOverflowTypes()) {
9855     DebugOnly<nsRect*> r = &aOverflowAreas.Overflow(otype);
9856     NS_ASSERTION(aNewSize.width == 0 || aNewSize.height == 0 ||
9857                      r->width == nscoord_MAX || r->height == nscoord_MAX ||
9858                      (mState & NS_FRAME_SVG_LAYOUT) ||
9859                      r->Contains(nsRect(nsPoint(0, 0), aNewSize)),
9860                  "Computed overflow area must contain frame bounds");
9861   }
9862 
9863   // If we clip our children, clear accumulated overflow area in the affected
9864   // dimension(s). The children are actually clipped to the padding-box, but
9865   // since the overflow area should include the entire border-box, just set it
9866   // to the border-box size here.
9867   if (overflowClipAxes != PhysicalAxes::None) {
9868     nsRect& ink = aOverflowAreas.InkOverflow();
9869     nsRect& scrollable = aOverflowAreas.ScrollableOverflow();
9870     if (overflowClipAxes & PhysicalAxes::Vertical) {
9871       ink.y = bounds.y;
9872       scrollable.y = bounds.y;
9873       ink.height = bounds.height;
9874       scrollable.height = bounds.height;
9875     }
9876     if (overflowClipAxes & PhysicalAxes::Horizontal) {
9877       ink.x = bounds.x;
9878       scrollable.x = bounds.x;
9879       ink.width = bounds.width;
9880       scrollable.width = bounds.width;
9881     }
9882   }
9883 
9884   // Overflow area must always include the frame's top-left and bottom-right,
9885   // even if the frame rect is empty (so we can scroll to those positions).
9886   // Pending a real fix for bug 426879, don't do this for inline frames
9887   // with zero width.
9888   // Do not do this for SVG either, since it will usually massively increase
9889   // the area unnecessarily.
9890   if ((aNewSize.width != 0 || !IsInlineFrame()) &&
9891       !HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
9892     for (const auto otype : AllOverflowTypes()) {
9893       nsRect& o = aOverflowAreas.Overflow(otype);
9894       o = o.UnionEdges(bounds);
9895     }
9896   }
9897 
9898   // Note that StyleOverflow::Clip doesn't clip the frame
9899   // background, so we add theme background overflow here so it's not clipped.
9900   if (!::IsXULBoxWrapped(this) && IsThemed(disp)) {
9901     nsRect r(bounds);
9902     nsPresContext* presContext = PresContext();
9903     if (presContext->Theme()->GetWidgetOverflow(
9904             presContext->DeviceContext(), this, disp->EffectiveAppearance(),
9905             &r)) {
9906       nsRect& vo = aOverflowAreas.InkOverflow();
9907       vo = vo.UnionEdges(r);
9908     }
9909   }
9910 
9911   ComputeAndIncludeOutlineArea(this, aOverflowAreas, aNewSize);
9912 
9913   // Nothing in here should affect scrollable overflow.
9914   aOverflowAreas.InkOverflow() =
9915       ComputeEffectsRect(this, aOverflowAreas.InkOverflow(), aNewSize);
9916 
9917   // Absolute position clipping
9918   const nsStyleEffects* effects = StyleEffects();
9919   Maybe<nsRect> clipPropClipRect = GetClipPropClipRect(disp, effects, aNewSize);
9920   if (clipPropClipRect) {
9921     for (const auto otype : AllOverflowTypes()) {
9922       nsRect& o = aOverflowAreas.Overflow(otype);
9923       o.IntersectRect(o, *clipPropClipRect);
9924     }
9925   }
9926 
9927   /* If we're transformed, transform the overflow rect by the current
9928    * transformation. */
9929   if (hasTransform) {
9930     SetProperty(nsIFrame::PreTransformOverflowAreasProperty(),
9931                 new OverflowAreas(aOverflowAreas));
9932 
9933     if (Combines3DTransformWithAncestors()) {
9934       /* If we're a preserve-3d leaf frame, then our pre-transform overflow
9935        * should be correct. Our post-transform overflow is empty though, because
9936        * we only contribute to the overflow area of the preserve-3d root frame.
9937        * If we're an intermediate frame then the pre-transform overflow should
9938        * contain all our non-preserve-3d children, which is what we want. Again
9939        * we have no post-transform overflow.
9940        */
9941       aOverflowAreas.SetAllTo(nsRect());
9942     } else {
9943       TransformReferenceBox refBox(this);
9944       for (const auto otype : AllOverflowTypes()) {
9945         nsRect& o = aOverflowAreas.Overflow(otype);
9946         o = nsDisplayTransform::TransformRect(o, this, refBox);
9947       }
9948 
9949       /* If we're the root of the 3d context, then we want to include the
9950        * overflow areas of all the participants. This won't have happened yet as
9951        * the code above set their overflow area to empty. Manually collect these
9952        * overflow areas now.
9953        */
9954       if (Extend3DContext(disp, effects)) {
9955         ComputePreserve3DChildrenOverflow(aOverflowAreas);
9956       }
9957     }
9958   } else {
9959     RemoveProperty(nsIFrame::PreTransformOverflowAreasProperty());
9960   }
9961 
9962   /* Revert the size change in case some caller is depending on this. */
9963   SetSize(oldSize, false);
9964 
9965   bool anyOverflowChanged;
9966   if (aOverflowAreas != OverflowAreas(bounds, bounds)) {
9967     anyOverflowChanged = SetOverflowAreas(aOverflowAreas);
9968   } else {
9969     anyOverflowChanged = ClearOverflowRects();
9970   }
9971 
9972   if (anyOverflowChanged) {
9973     SVGObserverUtils::InvalidateDirectRenderingObservers(this);
9974     if (IsBlockFrameOrSubclass() &&
9975         TextOverflow::CanHaveOverflowMarkers(this)) {
9976       DiscardDisplayItems(this, [](nsDisplayItem* aItem) {
9977         return aItem->GetType() == DisplayItemType::TYPE_TEXT_OVERFLOW;
9978       });
9979       SchedulePaint(PAINT_DEFAULT);
9980     }
9981   }
9982   return anyOverflowChanged;
9983 }
9984 
RecomputePerspectiveChildrenOverflow(const nsIFrame * aStartFrame)9985 void nsIFrame::RecomputePerspectiveChildrenOverflow(
9986     const nsIFrame* aStartFrame) {
9987   for (const auto& childList : ChildLists()) {
9988     for (nsIFrame* child : childList.mList) {
9989       if (!child->FrameMaintainsOverflow()) {
9990         continue;  // frame does not maintain overflow rects
9991       }
9992       if (child->HasPerspective()) {
9993         OverflowAreas* overflow =
9994             child->GetProperty(nsIFrame::InitialOverflowProperty());
9995         nsRect bounds(nsPoint(0, 0), child->GetSize());
9996         if (overflow) {
9997           OverflowAreas overflowCopy = *overflow;
9998           child->FinishAndStoreOverflow(overflowCopy, bounds.Size());
9999         } else {
10000           OverflowAreas boundsOverflow;
10001           boundsOverflow.SetAllTo(bounds);
10002           child->FinishAndStoreOverflow(boundsOverflow, bounds.Size());
10003         }
10004       } else if (child->GetContent() == aStartFrame->GetContent() ||
10005                  child->GetClosestFlattenedTreeAncestorPrimaryFrame() ==
10006                      aStartFrame) {
10007         // If a frame is using perspective, then the size used to compute
10008         // perspective-origin is the size of the frame belonging to its parent
10009         // style. We must find any descendant frames using our size
10010         // (by recursing into frames that have the same containing block)
10011         // to update their overflow rects too.
10012         child->RecomputePerspectiveChildrenOverflow(aStartFrame);
10013       }
10014     }
10015   }
10016 }
10017 
ComputePreserve3DChildrenOverflow(OverflowAreas & aOverflowAreas)10018 void nsIFrame::ComputePreserve3DChildrenOverflow(
10019     OverflowAreas& aOverflowAreas) {
10020   // Find all descendants that participate in the 3d context, and include their
10021   // overflow. These descendants have an empty overflow, so won't have been
10022   // included in the normal overflow calculation. Any children that don't
10023   // participate have normal overflow, so will have been included already.
10024 
10025   nsRect childVisual;
10026   nsRect childScrollable;
10027   for (const auto& childList : ChildLists()) {
10028     for (nsIFrame* child : childList.mList) {
10029       // If this child participates in the 3d context, then take the
10030       // pre-transform region (which contains all descendants that aren't
10031       // participating in the 3d context) and transform it into the 3d context
10032       // root coordinate space.
10033       if (child->Combines3DTransformWithAncestors()) {
10034         OverflowAreas childOverflow = child->GetOverflowAreasRelativeToSelf();
10035         TransformReferenceBox refBox(child);
10036         for (const auto otype : AllOverflowTypes()) {
10037           nsRect& o = childOverflow.Overflow(otype);
10038           o = nsDisplayTransform::TransformRect(o, child, refBox);
10039         }
10040 
10041         aOverflowAreas.UnionWith(childOverflow);
10042 
10043         // If this child also extends the 3d context, then recurse into it
10044         // looking for more participants.
10045         if (child->Extend3DContext()) {
10046           child->ComputePreserve3DChildrenOverflow(aOverflowAreas);
10047         }
10048       }
10049     }
10050   }
10051 }
10052 
ZIndexApplies() const10053 bool nsIFrame::ZIndexApplies() const {
10054   return StyleDisplay()->IsPositionedStyle() || IsFlexOrGridItem();
10055 }
10056 
ZIndex() const10057 Maybe<int32_t> nsIFrame::ZIndex() const {
10058   if (!ZIndexApplies()) {
10059     return Nothing();
10060   }
10061   const auto& zIndex = StylePosition()->mZIndex;
10062   if (zIndex.IsAuto()) {
10063     return Nothing();
10064   }
10065   return Some(zIndex.AsInteger());
10066 }
10067 
IsScrollAnchor(ScrollAnchorContainer ** aOutContainer)10068 bool nsIFrame::IsScrollAnchor(ScrollAnchorContainer** aOutContainer) {
10069   if (!mInScrollAnchorChain) {
10070     return false;
10071   }
10072 
10073   nsIFrame* f = this;
10074 
10075   // FIXME(emilio, bug 1629280): We should find a non-null anchor if we have the
10076   // flag set, but bug 1629280 makes it so that we cannot really assert it /
10077   // make this just a `while (true)`, and uncomment the below assertion.
10078   while (auto* container = ScrollAnchorContainer::FindFor(f)) {
10079     // MOZ_ASSERT(f->IsInScrollAnchorChain());
10080     if (nsIFrame* anchor = container->AnchorNode()) {
10081       if (anchor != this) {
10082         return false;
10083       }
10084       if (aOutContainer) {
10085         *aOutContainer = container;
10086       }
10087       return true;
10088     }
10089 
10090     f = container->Frame();
10091   }
10092 
10093   return false;
10094 }
10095 
IsInScrollAnchorChain() const10096 bool nsIFrame::IsInScrollAnchorChain() const { return mInScrollAnchorChain; }
10097 
SetInScrollAnchorChain(bool aInChain)10098 void nsIFrame::SetInScrollAnchorChain(bool aInChain) {
10099   mInScrollAnchorChain = aInChain;
10100 }
10101 
GetDepthInFrameTree() const10102 uint32_t nsIFrame::GetDepthInFrameTree() const {
10103   uint32_t result = 0;
10104   for (nsContainerFrame* ancestor = GetParent(); ancestor;
10105        ancestor = ancestor->GetParent()) {
10106     result++;
10107   }
10108   return result;
10109 }
10110 
10111 /**
10112  * This function takes a frame that is part of a block-in-inline split,
10113  * and _if_ that frame is an anonymous block created by an ib split it
10114  * returns the block's preceding inline.  This is needed because the
10115  * split inline's style is the parent of the anonymous block's style.
10116  *
10117  * If aFrame is not an anonymous block, null is returned.
10118  */
GetIBSplitSiblingForAnonymousBlock(const nsIFrame * aFrame)10119 static nsIFrame* GetIBSplitSiblingForAnonymousBlock(const nsIFrame* aFrame) {
10120   MOZ_ASSERT(aFrame, "Must have a non-null frame!");
10121   NS_ASSERTION(aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT),
10122                "GetIBSplitSibling should only be called on ib-split frames");
10123 
10124   if (aFrame->Style()->GetPseudoType() !=
10125       PseudoStyleType::mozBlockInsideInlineWrapper) {
10126     // it's not an anonymous block
10127     return nullptr;
10128   }
10129 
10130   // Find the first continuation of the frame.  (Ugh.  This ends up
10131   // being O(N^2) when it is called O(N) times.)
10132   aFrame = aFrame->FirstContinuation();
10133 
10134   /*
10135    * Now look up the nsGkAtoms::IBSplitPrevSibling
10136    * property.
10137    */
10138   nsIFrame* ibSplitSibling =
10139       aFrame->GetProperty(nsIFrame::IBSplitPrevSibling());
10140   NS_ASSERTION(ibSplitSibling, "Broken frame tree?");
10141   return ibSplitSibling;
10142 }
10143 
10144 /**
10145  * Get the parent, corrected for the mangled frame tree resulting from
10146  * having a block within an inline.  The result only differs from the
10147  * result of |GetParent| when |GetParent| returns an anonymous block
10148  * that was created for an element that was 'display: inline' because
10149  * that element contained a block.
10150  *
10151  * Also skip anonymous scrolled-content parents; inherit directly from the
10152  * outer scroll frame.
10153  *
10154  * Also skip NAC parents if the child frame is NAC.
10155  */
GetCorrectedParent(const nsIFrame * aFrame)10156 static nsIFrame* GetCorrectedParent(const nsIFrame* aFrame) {
10157   nsIFrame* parent = aFrame->GetParent();
10158   if (!parent) {
10159     return nullptr;
10160   }
10161 
10162   // For a table caption we want the _inner_ table frame (unless it's anonymous)
10163   // as the style parent.
10164   if (aFrame->IsTableCaption()) {
10165     nsIFrame* innerTable = parent->PrincipalChildList().FirstChild();
10166     if (!innerTable->Style()->IsAnonBox()) {
10167       return innerTable;
10168     }
10169   }
10170 
10171   // Table wrappers are always anon boxes; if we're in here for an outer
10172   // table, that actually means its the _inner_ table that wants to
10173   // know its parent. So get the pseudo of the inner in that case.
10174   auto pseudo = aFrame->Style()->GetPseudoType();
10175   if (pseudo == PseudoStyleType::tableWrapper) {
10176     pseudo =
10177         aFrame->PrincipalChildList().FirstChild()->Style()->GetPseudoType();
10178   }
10179 
10180   // Prevent a NAC pseudo-element from inheriting from its NAC parent, and
10181   // inherit from the NAC generator element instead.
10182   if (pseudo != PseudoStyleType::NotPseudo) {
10183     MOZ_ASSERT(aFrame->GetContent());
10184     Element* element = Element::FromNode(aFrame->GetContent());
10185     // Make sure to avoid doing the fixup for non-element-backed pseudos like
10186     // ::first-line and such.
10187     if (element && !element->IsRootOfNativeAnonymousSubtree() &&
10188         element->GetPseudoElementType() == aFrame->Style()->GetPseudoType()) {
10189       while (parent->GetContent() &&
10190              !parent->GetContent()->IsRootOfNativeAnonymousSubtree()) {
10191         parent = parent->GetInFlowParent();
10192       }
10193       parent = parent->GetInFlowParent();
10194     }
10195   }
10196 
10197   return nsIFrame::CorrectStyleParentFrame(parent, pseudo);
10198 }
10199 
10200 /* static */
CorrectStyleParentFrame(nsIFrame * aProspectiveParent,PseudoStyleType aChildPseudo)10201 nsIFrame* nsIFrame::CorrectStyleParentFrame(nsIFrame* aProspectiveParent,
10202                                             PseudoStyleType aChildPseudo) {
10203   MOZ_ASSERT(aProspectiveParent, "Must have a prospective parent");
10204 
10205   if (aChildPseudo != PseudoStyleType::NotPseudo) {
10206     // Non-inheriting anon boxes have no style parent frame at all.
10207     if (PseudoStyle::IsNonInheritingAnonBox(aChildPseudo)) {
10208       return nullptr;
10209     }
10210 
10211     // Other anon boxes are parented to their actual parent already, except
10212     // for non-elements.  Those should not be treated as an anon box.
10213     if (PseudoStyle::IsAnonBox(aChildPseudo) &&
10214         !nsCSSAnonBoxes::IsNonElement(aChildPseudo)) {
10215       NS_ASSERTION(aChildPseudo != PseudoStyleType::mozBlockInsideInlineWrapper,
10216                    "Should have dealt with kids that have "
10217                    "NS_FRAME_PART_OF_IBSPLIT elsewhere");
10218       return aProspectiveParent;
10219     }
10220   }
10221 
10222   // Otherwise, walk up out of all anon boxes.  For placeholder frames, walk out
10223   // of all pseudo-elements as well.  Otherwise ReparentComputedStyle could
10224   // cause style data to be out of sync with the frame tree.
10225   nsIFrame* parent = aProspectiveParent;
10226   do {
10227     if (parent->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
10228       nsIFrame* sibling = GetIBSplitSiblingForAnonymousBlock(parent);
10229 
10230       if (sibling) {
10231         // |parent| was a block in an {ib} split; use the inline as
10232         // |the style parent.
10233         parent = sibling;
10234       }
10235     }
10236 
10237     if (!parent->Style()->IsPseudoOrAnonBox()) {
10238       return parent;
10239     }
10240 
10241     if (!parent->Style()->IsAnonBox() && aChildPseudo != PseudoStyleType::MAX) {
10242       // nsPlaceholderFrame passes in PseudoStyleType::MAX for
10243       // aChildPseudo (even though that's not a valid pseudo-type) just to
10244       // trigger this behavior of walking up to the nearest non-pseudo
10245       // ancestor.
10246       return parent;
10247     }
10248 
10249     parent = parent->GetInFlowParent();
10250   } while (parent);
10251 
10252   if (aProspectiveParent->Style()->GetPseudoType() ==
10253       PseudoStyleType::viewportScroll) {
10254     // aProspectiveParent is the scrollframe for a viewport
10255     // and the kids are the anonymous scrollbars
10256     return aProspectiveParent;
10257   }
10258 
10259   // We can get here if the root element is absolutely positioned.
10260   // We can't test for this very accurately, but it can only happen
10261   // when the prospective parent is a canvas frame.
10262   NS_ASSERTION(aProspectiveParent->IsCanvasFrame(),
10263                "Should have found a parent before this");
10264   return nullptr;
10265 }
10266 
DoGetParentComputedStyle(nsIFrame ** aProviderFrame) const10267 ComputedStyle* nsIFrame::DoGetParentComputedStyle(
10268     nsIFrame** aProviderFrame) const {
10269   *aProviderFrame = nullptr;
10270 
10271   // Handle display:contents and the root frame, when there's no parent frame
10272   // to inherit from.
10273   if (MOZ_LIKELY(mContent)) {
10274     Element* parentElement = mContent->GetFlattenedTreeParentElement();
10275     if (MOZ_LIKELY(parentElement)) {
10276       auto pseudo = Style()->GetPseudoType();
10277       if (pseudo == PseudoStyleType::NotPseudo || !mContent->IsElement() ||
10278           (!PseudoStyle::IsAnonBox(pseudo) &&
10279            // Ensure that we don't return the display:contents style
10280            // of the parent content for pseudos that have the same content
10281            // as their primary frame (like -moz-list-bullets do):
10282            IsPrimaryFrame()) ||
10283           /* if next is true then it's really a request for the table frame's
10284              parent context, see nsTable[Outer]Frame::GetParentComputedStyle. */
10285           pseudo == PseudoStyleType::tableWrapper) {
10286         // In some edge cases involving display: contents, we may end up here
10287         // for something that's pending to be reframed. In this case we return
10288         // the wrong style from here (because we've already lost track of it!),
10289         // but it's not a big deal as we're going to be reframed anyway.
10290         if (MOZ_LIKELY(parentElement->HasServoData()) &&
10291             Servo_Element_IsDisplayContents(parentElement)) {
10292           RefPtr<ComputedStyle> style =
10293               ServoStyleSet::ResolveServoStyle(*parentElement);
10294           // NOTE(emilio): we return a weak reference because the element also
10295           // holds the style context alive. This is a bit silly (we could've
10296           // returned a weak ref directly), but it's probably not worth
10297           // optimizing, given this function has just one caller which is rare,
10298           // and this path is rare itself.
10299           return style;
10300         }
10301       }
10302     } else {
10303       if (Style()->GetPseudoType() == PseudoStyleType::NotPseudo) {
10304         // We're a frame for the root.  We have no style parent.
10305         return nullptr;
10306       }
10307     }
10308   }
10309 
10310   if (!(mState & NS_FRAME_OUT_OF_FLOW)) {
10311     /*
10312      * If this frame is an anonymous block created when an inline with a block
10313      * inside it got split, then the parent style is on its preceding inline. We
10314      * can get to it using GetIBSplitSiblingForAnonymousBlock.
10315      */
10316     if (mState & NS_FRAME_PART_OF_IBSPLIT) {
10317       nsIFrame* ibSplitSibling = GetIBSplitSiblingForAnonymousBlock(this);
10318       if (ibSplitSibling) {
10319         return (*aProviderFrame = ibSplitSibling)->Style();
10320       }
10321     }
10322 
10323     // If this frame is one of the blocks that split an inline, we must
10324     // return the "special" inline parent, i.e., the parent that this
10325     // frame would have if we didn't mangle the frame structure.
10326     *aProviderFrame = GetCorrectedParent(this);
10327     return *aProviderFrame ? (*aProviderFrame)->Style() : nullptr;
10328   }
10329 
10330   // We're an out-of-flow frame.  For out-of-flow frames, we must
10331   // resolve underneath the placeholder's parent.  The placeholder is
10332   // reached from the first-in-flow.
10333   nsPlaceholderFrame* placeholder = FirstInFlow()->GetPlaceholderFrame();
10334   if (!placeholder) {
10335     MOZ_ASSERT_UNREACHABLE("no placeholder frame for out-of-flow frame");
10336     *aProviderFrame = GetCorrectedParent(this);
10337     return *aProviderFrame ? (*aProviderFrame)->Style() : nullptr;
10338   }
10339   return placeholder->GetParentComputedStyleForOutOfFlow(aProviderFrame);
10340 }
10341 
GetLastLeaf(nsIFrame ** aFrame)10342 void nsIFrame::GetLastLeaf(nsIFrame** aFrame) {
10343   if (!aFrame || !*aFrame) return;
10344   nsIFrame* child = *aFrame;
10345   // if we are a block frame then go for the last line of 'this'
10346   while (1) {
10347     child = child->PrincipalChildList().FirstChild();
10348     if (!child) return;  // nothing to do
10349     nsIFrame* siblingFrame;
10350     nsIContent* content;
10351     // ignore anonymous elements, e.g. mozTableAdd* mozTableRemove*
10352     // see bug 278197 comment #12 #13 for details
10353     while ((siblingFrame = child->GetNextSibling()) &&
10354            (content = siblingFrame->GetContent()) &&
10355            !content->IsRootOfNativeAnonymousSubtree())
10356       child = siblingFrame;
10357     *aFrame = child;
10358   }
10359 }
10360 
GetFirstLeaf(nsIFrame ** aFrame)10361 void nsIFrame::GetFirstLeaf(nsIFrame** aFrame) {
10362   if (!aFrame || !*aFrame) return;
10363   nsIFrame* child = *aFrame;
10364   while (1) {
10365     child = child->PrincipalChildList().FirstChild();
10366     if (!child) return;  // nothing to do
10367     *aFrame = child;
10368   }
10369 }
10370 
IsFocusableDueToScrollFrame()10371 bool nsIFrame::IsFocusableDueToScrollFrame() {
10372   if (!IsScrollFrame()) {
10373     if (nsFieldSetFrame* fieldset = do_QueryFrame(this)) {
10374       // TODO: Do we have similar special-cases like this where we can have
10375       // anonymous scrollable boxes hanging off a primary frame?
10376       if (nsIFrame* inner = fieldset->GetInner()) {
10377         return inner->IsFocusableDueToScrollFrame();
10378       }
10379     }
10380     return false;
10381   }
10382   if (!mContent->IsHTMLElement()) {
10383     return false;
10384   }
10385   if (mContent->IsRootOfNativeAnonymousSubtree()) {
10386     return false;
10387   }
10388   if (!mContent->GetParent()) {
10389     return false;
10390   }
10391   if (mContent->AsElement()->HasAttr(nsGkAtoms::tabindex)) {
10392     return false;
10393   }
10394   // Elements with scrollable view are focusable with script & tabbable
10395   // Otherwise you couldn't scroll them with keyboard, which is an accessibility
10396   // issue (e.g. Section 508 rules) However, we don't make them to be focusable
10397   // with the mouse, because the extra focus outlines are considered
10398   // unnecessarily ugly.  When clicked on, the selection position within the
10399   // element will be enough to make them keyboard scrollable.
10400   nsIScrollableFrame* scrollFrame = do_QueryFrame(this);
10401   if (!scrollFrame) {
10402     return false;
10403   }
10404   if (scrollFrame->IsForTextControlWithNoScrollbars()) {
10405     return false;
10406   }
10407   if (scrollFrame->GetScrollStyles().IsHiddenInBothDirections()) {
10408     return false;
10409   }
10410   if (scrollFrame->GetScrollRange().IsEqualEdges(nsRect(0, 0, 0, 0))) {
10411     return false;
10412   }
10413   return true;
10414 }
10415 
IsFocusable(bool aWithMouse)10416 nsIFrame::Focusable nsIFrame::IsFocusable(bool aWithMouse) {
10417   // cannot focus content in print preview mode. Only the root can be focused,
10418   // but that's handled elsewhere.
10419   if (PresContext()->Type() == nsPresContext::eContext_PrintPreview) {
10420     return {};
10421   }
10422 
10423   if (!mContent || !mContent->IsElement()) {
10424     return {};
10425   }
10426 
10427   if (!IsVisibleConsideringAncestors()) {
10428     return {};
10429   }
10430 
10431   const nsStyleUI& ui = *StyleUI();
10432   if (ui.IsInert()) {
10433     return {};
10434   }
10435 
10436   PseudoStyleType pseudo = Style()->GetPseudoType();
10437   if (pseudo == PseudoStyleType::anonymousFlexItem ||
10438       pseudo == PseudoStyleType::anonymousGridItem) {
10439     return {};
10440   }
10441 
10442   int32_t tabIndex = -1;
10443   if (ui.UserFocus() != StyleUserFocus::Ignore &&
10444       ui.UserFocus() != StyleUserFocus::None) {
10445     // Pass in default tabindex of -1 for nonfocusable and 0 for focusable
10446     tabIndex = 0;
10447   }
10448 
10449   if (mContent->IsFocusable(&tabIndex, aWithMouse)) {
10450     // If the content is focusable, then we're done.
10451     return {true, tabIndex};
10452   }
10453 
10454   // If we're focusing with the mouse we never focus scroll areas.
10455   if (!aWithMouse && IsFocusableDueToScrollFrame()) {
10456     return {true, 0};
10457   }
10458 
10459   return {false, tabIndex};
10460 }
10461 
10462 /**
10463  * @return true if this text frame ends with a newline character which is
10464  * treated as preformatted. It should return false if this is not a text frame.
10465  */
HasSignificantTerminalNewline() const10466 bool nsIFrame::HasSignificantTerminalNewline() const { return false; }
10467 
ConvertSVGDominantBaselineToVerticalAlign(StyleDominantBaseline aDominantBaseline)10468 static StyleVerticalAlignKeyword ConvertSVGDominantBaselineToVerticalAlign(
10469     StyleDominantBaseline aDominantBaseline) {
10470   // Most of these are approximate mappings.
10471   switch (aDominantBaseline) {
10472     case StyleDominantBaseline::Hanging:
10473     case StyleDominantBaseline::TextBeforeEdge:
10474       return StyleVerticalAlignKeyword::TextTop;
10475     case StyleDominantBaseline::TextAfterEdge:
10476     case StyleDominantBaseline::Ideographic:
10477       return StyleVerticalAlignKeyword::TextBottom;
10478     case StyleDominantBaseline::Central:
10479     case StyleDominantBaseline::Middle:
10480     case StyleDominantBaseline::Mathematical:
10481       return StyleVerticalAlignKeyword::Middle;
10482     case StyleDominantBaseline::Auto:
10483     case StyleDominantBaseline::Alphabetic:
10484       return StyleVerticalAlignKeyword::Baseline;
10485     default:
10486       MOZ_ASSERT_UNREACHABLE("unexpected aDominantBaseline value");
10487       return StyleVerticalAlignKeyword::Baseline;
10488   }
10489 }
10490 
VerticalAlignEnum() const10491 Maybe<StyleVerticalAlignKeyword> nsIFrame::VerticalAlignEnum() const {
10492   if (SVGUtils::IsInSVGTextSubtree(this)) {
10493     StyleDominantBaseline dominantBaseline = StyleSVG()->mDominantBaseline;
10494     return Some(ConvertSVGDominantBaselineToVerticalAlign(dominantBaseline));
10495   }
10496 
10497   const auto& verticalAlign = StyleDisplay()->mVerticalAlign;
10498   if (verticalAlign.IsKeyword()) {
10499     return Some(verticalAlign.AsKeyword());
10500   }
10501 
10502   return Nothing();
10503 }
10504 
10505 NS_IMETHODIMP
RefreshSizeCache(nsBoxLayoutState & aState)10506 nsIFrame::RefreshSizeCache(nsBoxLayoutState& aState) {
10507   // XXXbz this comment needs some rewriting to make sense in the
10508   // post-reflow-branch world.
10509 
10510   // Ok we need to compute our minimum, preferred, and maximum sizes.
10511   // 1) Maximum size. This is easy. Its infinite unless it is overloaded by CSS.
10512   // 2) Preferred size. This is a little harder. This is the size the
10513   //    block would be if it were laid out on an infinite canvas. So we can
10514   //    get this by reflowing the block with and INTRINSIC width and height. We
10515   //    can also do a nice optimization for incremental reflow. If the reflow is
10516   //    incremental then we can pass a flag to have the block compute the
10517   //    preferred width for us! Preferred height can just be the minimum height;
10518   // 3) Minimum size. This is a toughy. We can pass the block a flag asking for
10519   //    the max element size. That would give us the width. Unfortunately you
10520   //    can only ask for a maxElementSize during an incremental reflow. So on
10521   //    other reflows we will just have to use 0. The min height on the other
10522   //    hand is fairly easy we need to get the largest line height. This can be
10523   //    done with the line iterator.
10524 
10525   // if we do have a rendering context
10526   gfxContext* rendContext = aState.GetRenderingContext();
10527   if (rendContext) {
10528     nsPresContext* presContext = aState.PresContext();
10529 
10530     // If we don't have any HTML constraints and it's a resize, then nothing in
10531     // the block could have changed, so no refresh is necessary.
10532     nsBoxLayoutMetrics* metrics = BoxMetrics();
10533     if (!XULNeedsRecalc(metrics->mBlockPrefSize)) {
10534       return NS_OK;
10535     }
10536 
10537     // the rect we plan to size to.
10538     nsRect rect = GetRect();
10539 
10540     nsMargin bp(0, 0, 0, 0);
10541     GetXULBorderAndPadding(bp);
10542 
10543     {
10544       // If we're a container for font size inflation, then shrink
10545       // wrapping inside of us should not apply font size inflation.
10546       AutoMaybeDisableFontInflation an(this);
10547 
10548       metrics->mBlockPrefSize.width =
10549           GetPrefISize(rendContext) + bp.LeftRight();
10550       metrics->mBlockMinSize.width = GetMinISize(rendContext) + bp.LeftRight();
10551     }
10552 
10553     // do the nasty.
10554     const WritingMode wm = aState.OuterReflowInput()
10555                                ? aState.OuterReflowInput()->GetWritingMode()
10556                                : GetWritingMode();
10557     ReflowOutput desiredSize(wm);
10558     BoxReflow(aState, presContext, desiredSize, rendContext, rect.x, rect.y,
10559               metrics->mBlockPrefSize.width, NS_UNCONSTRAINEDSIZE);
10560 
10561     metrics->mBlockMinSize.height = 0;
10562     // ok we need the max ascent of the items on the line. So to do this
10563     // ask the block for its line iterator. Get the max ascent.
10564     nsAutoLineIterator lines = GetLineIterator();
10565     if (lines) {
10566       metrics->mBlockMinSize.height = 0;
10567       int32_t lineCount = lines->GetNumLines();
10568       for (int32_t i = 0; i < lineCount; ++i) {
10569         auto line = lines->GetLine(i).unwrap();
10570 
10571         if (line.mLineBounds.height > metrics->mBlockMinSize.height) {
10572           metrics->mBlockMinSize.height = line.mLineBounds.height;
10573         }
10574       }
10575     } else {
10576       metrics->mBlockMinSize.height = desiredSize.Height();
10577     }
10578 
10579     metrics->mBlockPrefSize.height = metrics->mBlockMinSize.height;
10580 
10581     if (desiredSize.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE) {
10582       if (!nsLayoutUtils::GetFirstLineBaseline(wm, this,
10583                                                &metrics->mBlockAscent))
10584         metrics->mBlockAscent = GetLogicalBaseline(wm);
10585     } else {
10586       metrics->mBlockAscent = desiredSize.BlockStartAscent();
10587     }
10588 
10589 #ifdef DEBUG_adaptor
10590     printf("min=(%d,%d), pref=(%d,%d), ascent=%d\n",
10591            metrics->mBlockMinSize.width, metrics->mBlockMinSize.height,
10592            metrics->mBlockPrefSize.width, metrics->mBlockPrefSize.height,
10593            metrics->mBlockAscent);
10594 #endif
10595   }
10596 
10597   return NS_OK;
10598 }
10599 
GetXULPrefSize(nsBoxLayoutState & aState)10600 nsSize nsIFrame::GetXULPrefSize(nsBoxLayoutState& aState) {
10601   nsSize size(0, 0);
10602   DISPLAY_PREF_SIZE(this, size);
10603   // If the size is cached, and there are no HTML constraints that we might
10604   // be depending on, then we just return the cached size.
10605   nsBoxLayoutMetrics* metrics = BoxMetrics();
10606   if (!XULNeedsRecalc(metrics->mPrefSize)) {
10607     size = metrics->mPrefSize;
10608     return size;
10609   }
10610 
10611   if (IsXULCollapsed()) return size;
10612 
10613   // get our size in CSS.
10614   bool widthSet, heightSet;
10615   bool completelyRedefined =
10616       nsIFrame::AddXULPrefSize(this, size, widthSet, heightSet);
10617 
10618   // Refresh our caches with new sizes.
10619   if (!completelyRedefined) {
10620     RefreshSizeCache(aState);
10621     nsSize blockSize = metrics->mBlockPrefSize;
10622 
10623     // notice we don't need to add our borders or padding
10624     // in. That's because the block did it for us.
10625     if (!widthSet) size.width = blockSize.width;
10626     if (!heightSet) size.height = blockSize.height;
10627   }
10628 
10629   metrics->mPrefSize = size;
10630   return size;
10631 }
10632 
GetXULMinSize(nsBoxLayoutState & aState)10633 nsSize nsIFrame::GetXULMinSize(nsBoxLayoutState& aState) {
10634   nsSize size(0, 0);
10635   DISPLAY_MIN_SIZE(this, size);
10636   // Don't use the cache if we have HTMLReflowInput constraints --- they might
10637   // have changed
10638   nsBoxLayoutMetrics* metrics = BoxMetrics();
10639   if (!XULNeedsRecalc(metrics->mMinSize)) {
10640     size = metrics->mMinSize;
10641     return size;
10642   }
10643 
10644   if (IsXULCollapsed()) return size;
10645 
10646   // get our size in CSS.
10647   bool widthSet, heightSet;
10648   bool completelyRedefined =
10649       nsIFrame::AddXULMinSize(this, size, widthSet, heightSet);
10650 
10651   // Refresh our caches with new sizes.
10652   if (!completelyRedefined) {
10653     RefreshSizeCache(aState);
10654     nsSize blockSize = metrics->mBlockMinSize;
10655 
10656     if (!widthSet) size.width = blockSize.width;
10657     if (!heightSet) size.height = blockSize.height;
10658   }
10659 
10660   metrics->mMinSize = size;
10661   return size;
10662 }
10663 
GetXULMaxSize(nsBoxLayoutState & aState)10664 nsSize nsIFrame::GetXULMaxSize(nsBoxLayoutState& aState) {
10665   nsSize size(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
10666   DISPLAY_MAX_SIZE(this, size);
10667   // Don't use the cache if we have HTMLReflowInput constraints --- they might
10668   // have changed
10669   nsBoxLayoutMetrics* metrics = BoxMetrics();
10670   if (!XULNeedsRecalc(metrics->mMaxSize)) {
10671     size = metrics->mMaxSize;
10672     return size;
10673   }
10674 
10675   if (IsXULCollapsed()) return size;
10676 
10677   size = nsIFrame::GetUncachedXULMaxSize(aState);
10678   metrics->mMaxSize = size;
10679 
10680   return size;
10681 }
10682 
GetXULFlex()10683 nscoord nsIFrame::GetXULFlex() {
10684   nsBoxLayoutMetrics* metrics = BoxMetrics();
10685   if (XULNeedsRecalc(metrics->mFlex)) {
10686     nsIFrame::AddXULFlex(this, metrics->mFlex);
10687   }
10688 
10689   return metrics->mFlex;
10690 }
10691 
GetXULBoxAscent(nsBoxLayoutState & aState)10692 nscoord nsIFrame::GetXULBoxAscent(nsBoxLayoutState& aState) {
10693   nsBoxLayoutMetrics* metrics = BoxMetrics();
10694   if (!XULNeedsRecalc(metrics->mAscent)) {
10695     return metrics->mAscent;
10696   }
10697 
10698   if (IsXULCollapsed()) {
10699     metrics->mAscent = 0;
10700   } else {
10701     // Refresh our caches with new sizes.
10702     RefreshSizeCache(aState);
10703     metrics->mAscent = metrics->mBlockAscent;
10704   }
10705 
10706   return metrics->mAscent;
10707 }
10708 
DoXULLayout(nsBoxLayoutState & aState)10709 nsresult nsIFrame::DoXULLayout(nsBoxLayoutState& aState) {
10710   nsRect ourRect(mRect);
10711 
10712   gfxContext* rendContext = aState.GetRenderingContext();
10713   nsPresContext* presContext = aState.PresContext();
10714   WritingMode ourWM = GetWritingMode();
10715   const WritingMode outerWM = aState.OuterReflowInput()
10716                                   ? aState.OuterReflowInput()->GetWritingMode()
10717                                   : ourWM;
10718   ReflowOutput desiredSize(outerWM);
10719   LogicalSize ourSize = GetLogicalSize(outerWM);
10720 
10721   if (rendContext) {
10722     BoxReflow(aState, presContext, desiredSize, rendContext, ourRect.x,
10723               ourRect.y, ourRect.width, ourRect.height);
10724 
10725     if (IsXULCollapsed()) {
10726       SetSize(nsSize(0, 0));
10727     } else {
10728       // if our child needs to be bigger. This might happend with
10729       // wrapping text. There is no way to predict its height until we
10730       // reflow it. Now that we know the height reshuffle upward.
10731       if (desiredSize.ISize(outerWM) > ourSize.ISize(outerWM) ||
10732           desiredSize.BSize(outerWM) > ourSize.BSize(outerWM)) {
10733 #ifdef DEBUG_GROW
10734         XULDumpBox(stdout);
10735         printf(" GREW from (%d,%d) -> (%d,%d)\n", ourSize.ISize(outerWM),
10736                ourSize.BSize(outerWM), desiredSize.ISize(outerWM),
10737                desiredSize.BSize(outerWM));
10738 #endif
10739 
10740         if (desiredSize.ISize(outerWM) > ourSize.ISize(outerWM)) {
10741           ourSize.ISize(outerWM) = desiredSize.ISize(outerWM);
10742         }
10743 
10744         if (desiredSize.BSize(outerWM) > ourSize.BSize(outerWM)) {
10745           ourSize.BSize(outerWM) = desiredSize.BSize(outerWM);
10746         }
10747       }
10748 
10749       // ensure our size is what we think is should be. Someone could have
10750       // reset the frame to be smaller or something dumb like that.
10751       SetSize(ourSize.ConvertTo(ourWM, outerWM));
10752     }
10753   }
10754 
10755   // Should we do this if IsXULCollapsed() is true?
10756   LogicalSize size(GetLogicalSize(outerWM));
10757   desiredSize.ISize(outerWM) = size.ISize(outerWM);
10758   desiredSize.BSize(outerWM) = size.BSize(outerWM);
10759   desiredSize.UnionOverflowAreasWithDesiredBounds();
10760 
10761   if (HasAbsolutelyPositionedChildren()) {
10762     // Set up a |reflowInput| to pass into ReflowAbsoluteFrames
10763     ReflowInput reflowInput(aState.PresContext(), this,
10764                             aState.GetRenderingContext(),
10765                             LogicalSize(ourWM, ISize(), NS_UNCONSTRAINEDSIZE),
10766                             ReflowInput::InitFlag::DummyParentReflowInput);
10767 
10768     AddStateBits(NS_FRAME_IN_REFLOW);
10769     // Set up a |reflowStatus| to pass into ReflowAbsoluteFrames
10770     // (just a dummy value; hopefully that's OK)
10771     nsReflowStatus reflowStatus;
10772     ReflowAbsoluteFrames(aState.PresContext(), desiredSize, reflowInput,
10773                          reflowStatus);
10774     RemoveStateBits(NS_FRAME_IN_REFLOW);
10775   }
10776 
10777   nsSize oldSize(ourRect.Size());
10778   FinishAndStoreOverflow(desiredSize.mOverflowAreas,
10779                          size.GetPhysicalSize(outerWM), &oldSize);
10780 
10781   SyncXULLayout(aState);
10782 
10783   return NS_OK;
10784 }
10785 
BoxReflow(nsBoxLayoutState & aState,nsPresContext * aPresContext,ReflowOutput & aDesiredSize,gfxContext * aRenderingContext,nscoord aX,nscoord aY,nscoord aWidth,nscoord aHeight,bool aMoveFrame)10786 void nsIFrame::BoxReflow(nsBoxLayoutState& aState, nsPresContext* aPresContext,
10787                          ReflowOutput& aDesiredSize,
10788                          gfxContext* aRenderingContext, nscoord aX, nscoord aY,
10789                          nscoord aWidth, nscoord aHeight, bool aMoveFrame) {
10790   DO_GLOBAL_REFLOW_COUNT("nsBoxToBlockAdaptor");
10791 
10792   nsBoxLayoutMetrics* metrics = BoxMetrics();
10793   if (MOZ_UNLIKELY(!metrics)) {
10794     // Can't proceed without BoxMetrics. This should only happen if something
10795     // is seriously broken, e.g. if we try to do XUL layout on a non-XUL frame.
10796     // (If this is a content process, we'll abort even in release builds,
10797     // because XUL layout mixup is extra surprising in content, and aborts are
10798     // less catastrophic in content vs. in chrome.)
10799     MOZ_RELEASE_ASSERT(!XRE_IsContentProcess(),
10800                        "Starting XUL BoxReflow w/o BoxMetrics (in content)?");
10801     MOZ_ASSERT_UNREACHABLE("Starting XUL BoxReflow w/o BoxMetrics?");
10802     return;
10803   }
10804 
10805   nsReflowStatus status;
10806 
10807   bool needsReflow = IsSubtreeDirty();
10808 
10809   // if we don't need a reflow then
10810   // lets see if we are already that size. Yes? then don't even reflow. We are
10811   // done.
10812   if (!needsReflow) {
10813     if (aWidth != NS_UNCONSTRAINEDSIZE && aHeight != NS_UNCONSTRAINEDSIZE) {
10814       // if the new calculated size has a 0 width or a 0 height
10815       if ((metrics->mLastSize.width == 0 || metrics->mLastSize.height == 0) &&
10816           (aWidth == 0 || aHeight == 0)) {
10817         needsReflow = false;
10818         aDesiredSize.Width() = aWidth;
10819         aDesiredSize.Height() = aHeight;
10820         SetSize(aDesiredSize.Size(GetWritingMode()));
10821       } else {
10822         aDesiredSize.Width() = metrics->mLastSize.width;
10823         aDesiredSize.Height() = metrics->mLastSize.height;
10824 
10825         // remove the margin. The rect of our child does not include it but our
10826         // calculated size does. don't reflow if we are already the right size
10827         if (metrics->mLastSize.width == aWidth &&
10828             metrics->mLastSize.height == aHeight)
10829           needsReflow = false;
10830         else
10831           needsReflow = true;
10832       }
10833     } else {
10834       // if the width or height are intrinsic alway reflow because
10835       // we don't know what it should be.
10836       needsReflow = true;
10837     }
10838   }
10839 
10840   // ok now reflow the child into the spacers calculated space
10841   if (needsReflow) {
10842     aDesiredSize.ClearSize();
10843 
10844     // create a reflow input to tell our child to flow at the given size.
10845 
10846     // Construct a bogus parent reflow input so that there's a usable reflow
10847     // input for the containing block.
10848     nsMargin margin(0, 0, 0, 0);
10849     GetXULMargin(margin);
10850 
10851     nsSize parentSize(aWidth, aHeight);
10852     if (parentSize.height != NS_UNCONSTRAINEDSIZE)
10853       parentSize.height += margin.TopBottom();
10854     if (parentSize.width != NS_UNCONSTRAINEDSIZE)
10855       parentSize.width += margin.LeftRight();
10856 
10857     nsIFrame* parentFrame = GetParent();
10858     WritingMode parentWM = parentFrame->GetWritingMode();
10859     ReflowInput parentReflowInput(
10860         aPresContext, parentFrame, aRenderingContext,
10861         LogicalSize(parentWM, parentSize),
10862         ReflowInput::InitFlag::DummyParentReflowInput);
10863 
10864     // This may not do very much useful, but it's probably worth trying.
10865     if (parentSize.width != NS_UNCONSTRAINEDSIZE)
10866       parentReflowInput.SetComputedWidth(std::max(parentSize.width, 0));
10867     if (parentSize.height != NS_UNCONSTRAINEDSIZE)
10868       parentReflowInput.SetComputedHeight(std::max(parentSize.height, 0));
10869     parentReflowInput.SetComputedLogicalMargin(parentWM,
10870                                                LogicalMargin(parentWM));
10871     // XXX use box methods
10872     nsMargin padding;
10873     parentFrame->GetXULPadding(padding);
10874     parentReflowInput.SetComputedLogicalPadding(
10875         parentWM, LogicalMargin(parentWM, padding));
10876     nsMargin border;
10877     parentFrame->GetXULBorder(border);
10878     parentReflowInput.SetComputedLogicalBorderPadding(
10879         parentWM, LogicalMargin(parentWM, border + padding));
10880 
10881     // Construct the parent chain manually since constructing it normally
10882     // messes up dimensions.
10883     const ReflowInput* outerReflowInput = aState.OuterReflowInput();
10884     NS_ASSERTION(!outerReflowInput || outerReflowInput->mFrame != this,
10885                  "in and out of XUL on a single frame?");
10886     const ReflowInput* parentRI;
10887     if (outerReflowInput && outerReflowInput->mFrame == parentFrame) {
10888       // We're a frame (such as a text control frame) that jumps into
10889       // box reflow and then straight out of it on the child frame.
10890       // This means we actually have a real parent reflow input.
10891       // nsLayoutUtils::InflationMinFontSizeFor used to need this to be
10892       // linked up correctly for text control frames, so do so here).
10893       parentRI = outerReflowInput;
10894     } else {
10895       parentRI = &parentReflowInput;
10896     }
10897 
10898     // XXX Is it OK that this reflow input has only one ancestor?
10899     // (It used to have a bogus parent, skipping all the boxes).
10900     WritingMode wm = GetWritingMode();
10901     LogicalSize logicalSize(wm, nsSize(aWidth, aHeight));
10902     logicalSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
10903     ReflowInput reflowInput(aPresContext, *parentRI, this, logicalSize,
10904                             Nothing(),
10905                             ReflowInput::InitFlag::DummyParentReflowInput);
10906 
10907     // XXX_jwir3: This is somewhat fishy. If this is actually changing the value
10908     //            here (which it might be), then we should make sure that it's
10909     //            correct the first time around, rather than changing it later.
10910     reflowInput.mCBReflowInput = parentRI;
10911 
10912     reflowInput.mReflowDepth = aState.GetReflowDepth();
10913 
10914     // mComputedWidth and mComputedHeight are content-box, not
10915     // border-box
10916     if (aWidth != NS_UNCONSTRAINEDSIZE) {
10917       nscoord computedWidth =
10918           aWidth - reflowInput.ComputedPhysicalBorderPadding().LeftRight();
10919       computedWidth = std::max(computedWidth, 0);
10920       reflowInput.SetComputedWidth(computedWidth);
10921     }
10922 
10923     // Most child frames of box frames (e.g. subdocument or scroll frames)
10924     // need to be constrained to the provided size and overflow as necessary.
10925     // The one exception are block frames, because we need to know their
10926     // natural height excluding any overflow area which may be caused by
10927     // various CSS effects such as shadow or outline.
10928     if (!IsBlockFrameOrSubclass()) {
10929       if (aHeight != NS_UNCONSTRAINEDSIZE) {
10930         nscoord computedHeight =
10931             aHeight - reflowInput.ComputedPhysicalBorderPadding().TopBottom();
10932         computedHeight = std::max(computedHeight, 0);
10933         reflowInput.SetComputedHeight(computedHeight);
10934       } else {
10935         reflowInput.SetComputedHeight(
10936             ComputeSize(
10937                 aRenderingContext, wm, logicalSize, logicalSize.ISize(wm),
10938                 reflowInput.ComputedLogicalMargin(wm).Size(wm),
10939                 reflowInput.ComputedLogicalBorderPadding(wm).Size(wm), {}, {})
10940                 .mLogicalSize.Height(wm));
10941       }
10942     }
10943 
10944     // Box layout calls SetRect before XULLayout, whereas non-box layout
10945     // calls SetRect after Reflow.
10946     // XXX Perhaps we should be doing this by twiddling the rect back to
10947     // mLastSize before calling Reflow and then switching it back, but
10948     // However, mLastSize can also be the size passed to BoxReflow by
10949     // RefreshSizeCache, so that doesn't really make sense.
10950     if (metrics->mLastSize.width != aWidth) {
10951       reflowInput.SetHResize(true);
10952 
10953       // When font size inflation is enabled, a horizontal resize
10954       // requires a full reflow.  See ReflowInput::InitResizeFlags
10955       // for more details.
10956       if (nsLayoutUtils::FontSizeInflationEnabled(aPresContext)) {
10957         this->MarkSubtreeDirty();
10958       }
10959     }
10960     if (metrics->mLastSize.height != aHeight) {
10961       reflowInput.SetVResize(true);
10962     }
10963 
10964     // place the child and reflow
10965 
10966     Reflow(aPresContext, aDesiredSize, reflowInput, status);
10967 
10968     NS_ASSERTION(status.IsComplete(), "bad status");
10969 
10970     ReflowChildFlags layoutFlags = aState.LayoutFlags();
10971     nsContainerFrame::FinishReflowChild(
10972         this, aPresContext, aDesiredSize, &reflowInput, aX, aY,
10973         layoutFlags | ReflowChildFlags::NoMoveFrame);
10974 
10975     // Save the ascent.  (bug 103925)
10976     if (IsXULCollapsed()) {
10977       metrics->mAscent = 0;
10978     } else {
10979       if (aDesiredSize.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE) {
10980         if (!nsLayoutUtils::GetFirstLineBaseline(wm, this, &metrics->mAscent))
10981           metrics->mAscent = GetLogicalBaseline(wm);
10982       } else
10983         metrics->mAscent = aDesiredSize.BlockStartAscent();
10984     }
10985 
10986   } else {
10987     aDesiredSize.SetBlockStartAscent(metrics->mBlockAscent);
10988   }
10989 
10990   metrics->mLastSize.width = aDesiredSize.Width();
10991   metrics->mLastSize.height = aDesiredSize.Height();
10992 }
10993 
BoxMetrics() const10994 nsBoxLayoutMetrics* nsIFrame::BoxMetrics() const {
10995   nsBoxLayoutMetrics* metrics = GetProperty(BoxMetricsProperty());
10996   NS_ASSERTION(
10997       metrics,
10998       "A box layout method was called but InitBoxMetrics was never called");
10999   return metrics;
11000 }
11001 
UpdateStyleOfChildAnonBox(nsIFrame * aChildFrame,ServoRestyleState & aRestyleState)11002 void nsIFrame::UpdateStyleOfChildAnonBox(nsIFrame* aChildFrame,
11003                                          ServoRestyleState& aRestyleState) {
11004 #ifdef DEBUG
11005   nsIFrame* parent = aChildFrame->GetInFlowParent();
11006   if (aChildFrame->IsTableFrame()) {
11007     parent = parent->GetParent();
11008   }
11009   if (parent->IsLineFrame()) {
11010     parent = parent->GetParent();
11011   }
11012   MOZ_ASSERT(nsLayoutUtils::FirstContinuationOrIBSplitSibling(parent) == this,
11013              "This should only be used for children!");
11014 #endif  // DEBUG
11015   MOZ_ASSERT(!GetContent() || !aChildFrame->GetContent() ||
11016                  aChildFrame->GetContent() == GetContent(),
11017              "What content node is it a frame for?");
11018   MOZ_ASSERT(!aChildFrame->GetPrevContinuation(),
11019              "Only first continuations should end up here");
11020 
11021   // We could force the caller to pass in the pseudo, since some callers know it
11022   // statically...  But this API is a bit nicer.
11023   auto pseudo = aChildFrame->Style()->GetPseudoType();
11024   MOZ_ASSERT(PseudoStyle::IsAnonBox(pseudo), "Child is not an anon box?");
11025   MOZ_ASSERT(!PseudoStyle::IsNonInheritingAnonBox(pseudo),
11026              "Why did the caller bother calling us?");
11027 
11028   // Anon boxes inherit from their parent; that's us.
11029   RefPtr<ComputedStyle> newContext =
11030       aRestyleState.StyleSet().ResolveInheritingAnonymousBoxStyle(pseudo,
11031                                                                   Style());
11032 
11033   nsChangeHint childHint =
11034       UpdateStyleOfOwnedChildFrame(aChildFrame, newContext, aRestyleState);
11035 
11036   // Now that we've updated the style on aChildFrame, check whether it itself
11037   // has anon boxes to deal with.
11038   ServoRestyleState childrenState(*aChildFrame, aRestyleState, childHint,
11039                                   ServoRestyleState::Type::InFlow);
11040   aChildFrame->UpdateStyleOfOwnedAnonBoxes(childrenState);
11041 
11042   // Assuming anon boxes don't have ::backdrop associated with them... if that
11043   // ever changes, we'd need to handle that here, like we do in
11044   // RestyleManager::ProcessPostTraversal
11045 
11046   // We do need to handle block pseudo-elements here, though.  Especially list
11047   // bullets.
11048   if (nsBlockFrame* block = do_QueryFrame(aChildFrame)) {
11049     block->UpdatePseudoElementStyles(childrenState);
11050   }
11051 }
11052 
11053 /* static */
UpdateStyleOfOwnedChildFrame(nsIFrame * aChildFrame,ComputedStyle * aNewComputedStyle,ServoRestyleState & aRestyleState,const Maybe<ComputedStyle * > & aContinuationComputedStyle)11054 nsChangeHint nsIFrame::UpdateStyleOfOwnedChildFrame(
11055     nsIFrame* aChildFrame, ComputedStyle* aNewComputedStyle,
11056     ServoRestyleState& aRestyleState,
11057     const Maybe<ComputedStyle*>& aContinuationComputedStyle) {
11058   MOZ_ASSERT(!aChildFrame->GetAdditionalComputedStyle(0),
11059              "We don't handle additional styles here");
11060 
11061   // Figure out whether we have an actual change.  It's important that we do
11062   // this, for several reasons:
11063   //
11064   // 1) Even if all the child's changes are due to properties it inherits from
11065   //    us, it's possible that no one ever asked us for those style structs and
11066   //    hence changes to them aren't reflected in the changes handled at all.
11067   //
11068   // 2) Content can change stylesheets that change the styles of pseudos, and
11069   //    extensions can add/remove stylesheets that change the styles of
11070   //    anonymous boxes directly.
11071   uint32_t equalStructs;  // Not used, actually.
11072   nsChangeHint childHint = aChildFrame->Style()->CalcStyleDifference(
11073       *aNewComputedStyle, &equalStructs);
11074 
11075   // If aChildFrame is out of flow, then aRestyleState's "changes handled by the
11076   // parent" doesn't apply to it, because it may have some other parent in the
11077   // frame tree.
11078   if (!aChildFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
11079     childHint = NS_RemoveSubsumedHints(
11080         childHint, aRestyleState.ChangesHandledFor(aChildFrame));
11081   }
11082   if (childHint) {
11083     if (childHint & nsChangeHint_ReconstructFrame) {
11084       // If we generate a reconstruct here, remove any non-reconstruct hints we
11085       // may have already generated for this content.
11086       aRestyleState.ChangeList().PopChangesForContent(
11087           aChildFrame->GetContent());
11088     }
11089     aRestyleState.ChangeList().AppendChange(
11090         aChildFrame, aChildFrame->GetContent(), childHint);
11091   }
11092 
11093   aChildFrame->SetComputedStyle(aNewComputedStyle);
11094   ComputedStyle* continuationStyle = aContinuationComputedStyle
11095                                          ? *aContinuationComputedStyle
11096                                          : aNewComputedStyle;
11097   for (nsIFrame* kid = aChildFrame->GetNextContinuation(); kid;
11098        kid = kid->GetNextContinuation()) {
11099     MOZ_ASSERT(!kid->GetAdditionalComputedStyle(0));
11100     kid->SetComputedStyle(continuationStyle);
11101   }
11102 
11103   return childHint;
11104 }
11105 
11106 /* static */
AddInPopupStateBitToDescendants(nsIFrame * aFrame)11107 void nsIFrame::AddInPopupStateBitToDescendants(nsIFrame* aFrame) {
11108   if (!aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP) &&
11109       aFrame->TrackingVisibility()) {
11110     // Assume all frames in popups are visible.
11111     aFrame->IncApproximateVisibleCount();
11112   }
11113 
11114   aFrame->AddStateBits(NS_FRAME_IN_POPUP);
11115 
11116   for (const auto& childList : aFrame->CrossDocChildLists()) {
11117     for (nsIFrame* child : childList.mList) {
11118       AddInPopupStateBitToDescendants(child);
11119     }
11120   }
11121 }
11122 
11123 /* static */
RemoveInPopupStateBitFromDescendants(nsIFrame * aFrame)11124 void nsIFrame::RemoveInPopupStateBitFromDescendants(nsIFrame* aFrame) {
11125   if (!aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP) ||
11126       nsLayoutUtils::IsPopup(aFrame)) {
11127     return;
11128   }
11129 
11130   aFrame->RemoveStateBits(NS_FRAME_IN_POPUP);
11131 
11132   if (aFrame->TrackingVisibility()) {
11133     // We assume all frames in popups are visible, so this decrement balances
11134     // out the increment in AddInPopupStateBitToDescendants above.
11135     aFrame->DecApproximateVisibleCount();
11136   }
11137   for (const auto& childList : aFrame->CrossDocChildLists()) {
11138     for (nsIFrame* child : childList.mList) {
11139       RemoveInPopupStateBitFromDescendants(child);
11140     }
11141   }
11142 }
11143 
SetParent(nsContainerFrame * aParent)11144 void nsIFrame::SetParent(nsContainerFrame* aParent) {
11145   // If our parent is a wrapper anon box, our new parent should be too.  We
11146   // _can_ change parent if our parent is a wrapper anon box, because some
11147   // wrapper anon boxes can have continuations.
11148   MOZ_ASSERT_IF(ParentIsWrapperAnonBox(),
11149                 aParent->Style()->IsInheritingAnonBox());
11150 
11151   // Note that the current mParent may already be destroyed at this point.
11152   mParent = aParent;
11153   MOZ_DIAGNOSTIC_ASSERT(!mParent || PresShell() == mParent->PresShell());
11154   if (::IsXULBoxWrapped(this)) {
11155     ::InitBoxMetrics(this, true);
11156   } else {
11157     // We could call Properties().Delete(BoxMetricsProperty()); here but
11158     // that's kind of slow and re-parenting in such a way that we were
11159     // IsXULBoxWrapped() before but not now should be very rare, so we'll just
11160     // keep this unused frame property until this frame dies instead.
11161   }
11162 
11163   if (HasAnyStateBits(NS_FRAME_HAS_VIEW | NS_FRAME_HAS_CHILD_WITH_VIEW)) {
11164     for (nsIFrame* f = aParent;
11165          f && !f->HasAnyStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
11166          f = f->GetParent()) {
11167       f->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
11168     }
11169   }
11170 
11171   if (HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
11172     for (nsIFrame* f = aParent; f; f = f->GetParent()) {
11173       if (f->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
11174         break;
11175       }
11176       f->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
11177     }
11178   }
11179 
11180   if (HasAnyStateBits(NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)) {
11181     for (nsIFrame* f = aParent; f; f = f->GetParent()) {
11182       if (f->HasAnyStateBits(
11183               NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)) {
11184         break;
11185       }
11186       f->AddStateBits(NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE);
11187     }
11188   }
11189 
11190   if (HasInvalidFrameInSubtree()) {
11191     for (nsIFrame* f = aParent;
11192          f && !f->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT |
11193                                   NS_FRAME_IS_NONDISPLAY);
11194          f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
11195       f->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
11196     }
11197   }
11198 
11199   if (aParent->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
11200     AddInPopupStateBitToDescendants(this);
11201   } else {
11202     RemoveInPopupStateBitFromDescendants(this);
11203   }
11204 
11205   // If our new parent only has invalid children, then we just invalidate
11206   // ourselves too. This is probably faster than clearing the flag all
11207   // the way up the frame tree.
11208   if (aParent->HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT)) {
11209     InvalidateFrame();
11210   } else {
11211     SchedulePaint();
11212   }
11213 }
11214 
CreateOwnLayerIfNeeded(nsDisplayListBuilder * aBuilder,nsDisplayList * aList,uint16_t aType,bool * aCreatedContainerItem)11215 void nsIFrame::CreateOwnLayerIfNeeded(nsDisplayListBuilder* aBuilder,
11216                                       nsDisplayList* aList, uint16_t aType,
11217                                       bool* aCreatedContainerItem) {
11218   if (GetContent() && GetContent()->IsXULElement() &&
11219       GetContent()->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::layer)) {
11220     aList->AppendNewToTopWithIndex<nsDisplayOwnLayer>(
11221         aBuilder, this, /* aIndex = */ aType, aList,
11222         aBuilder->CurrentActiveScrolledRoot(), nsDisplayOwnLayerFlags::None,
11223         ScrollbarData{}, true, false);
11224     if (aCreatedContainerItem) {
11225       *aCreatedContainerItem = true;
11226     }
11227   }
11228 }
11229 
IsStackingContext(const nsStyleDisplay * aStyleDisplay,const nsStyleEffects * aStyleEffects)11230 bool nsIFrame::IsStackingContext(const nsStyleDisplay* aStyleDisplay,
11231                                  const nsStyleEffects* aStyleEffects) {
11232   // Properties that influence the output of this function should be handled in
11233   // change_bits_for_longhand as well.
11234   if (HasOpacity(aStyleDisplay, aStyleEffects, nullptr)) {
11235     return true;
11236   }
11237   if (IsTransformed()) {
11238     return true;
11239   }
11240   auto willChange = aStyleDisplay->mWillChange.bits;
11241   if (aStyleDisplay->IsContainPaint() || aStyleDisplay->IsContainLayout() ||
11242       willChange & StyleWillChangeBits::CONTAIN) {
11243     if (IsFrameOfType(eSupportsContainLayoutAndPaint)) {
11244       return true;
11245     }
11246   }
11247   // strictly speaking, 'perspective' doesn't require visual atomicity,
11248   // but the spec says it acts like the rest of these
11249   if (aStyleDisplay->HasPerspectiveStyle() ||
11250       willChange & StyleWillChangeBits::PERSPECTIVE) {
11251     if (IsFrameOfType(eSupportsCSSTransforms)) {
11252       return true;
11253     }
11254   }
11255   if (!StylePosition()->mZIndex.IsAuto() ||
11256       willChange & StyleWillChangeBits::Z_INDEX) {
11257     if (ZIndexApplies()) {
11258       return true;
11259     }
11260   }
11261   return aStyleEffects->mMixBlendMode != StyleBlend::Normal ||
11262          SVGIntegrationUtils::UsingEffectsForFrame(this) ||
11263          aStyleDisplay->IsPositionForcingStackingContext() ||
11264          aStyleDisplay->mIsolation != StyleIsolation::Auto ||
11265          willChange & StyleWillChangeBits::STACKING_CONTEXT_UNCONDITIONAL;
11266 }
11267 
IsStackingContext()11268 bool nsIFrame::IsStackingContext() {
11269   return IsStackingContext(StyleDisplay(), StyleEffects());
11270 }
11271 
IsFrameScrolledOutOfView(const nsIFrame * aTarget,const nsRect & aTargetRect,const nsIFrame * aParent)11272 static bool IsFrameScrolledOutOfView(const nsIFrame* aTarget,
11273                                      const nsRect& aTargetRect,
11274                                      const nsIFrame* aParent) {
11275   // The ancestor frame we are checking if it clips out aTargetRect relative to
11276   // aTarget.
11277   nsIFrame* clipParent = nullptr;
11278 
11279   // find the first scrollable frame or root frame if we are in a fixed pos
11280   // subtree
11281   for (nsIFrame* f = const_cast<nsIFrame*>(aParent); f;
11282        f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
11283     nsIScrollableFrame* scrollableFrame = do_QueryFrame(f);
11284     if (scrollableFrame) {
11285       clipParent = f;
11286       break;
11287     }
11288     if (f->StyleDisplay()->mPosition == StylePositionProperty::Fixed &&
11289         nsLayoutUtils::IsReallyFixedPos(f)) {
11290       clipParent = f->GetParent();
11291       break;
11292     }
11293   }
11294 
11295   if (!clipParent) {
11296     // Even if we couldn't find the nearest scrollable frame, it might mean we
11297     // are in an out-of-process iframe, try to see if |aTarget| frame is
11298     // scrolled out of view in an scrollable frame in a cross-process ancestor
11299     // document.
11300     return nsLayoutUtils::FrameIsScrolledOutOfViewInCrossProcess(aTarget);
11301   }
11302 
11303   nsRect clipRect = clipParent->InkOverflowRectRelativeToSelf();
11304   // We consider that the target is scrolled out if the scrollable (or root)
11305   // frame is empty.
11306   if (clipRect.IsEmpty()) {
11307     return true;
11308   }
11309 
11310   nsRect transformedRect = nsLayoutUtils::TransformFrameRectToAncestor(
11311       aTarget, aTargetRect, clipParent);
11312 
11313   if (transformedRect.IsEmpty()) {
11314     // If the transformed rect is empty it represents a line or a point that we
11315     // should check is outside the the scrollable rect.
11316     if (transformedRect.x > clipRect.XMost() ||
11317         transformedRect.y > clipRect.YMost() ||
11318         clipRect.x > transformedRect.XMost() ||
11319         clipRect.y > transformedRect.YMost()) {
11320       return true;
11321     }
11322   } else if (!transformedRect.Intersects(clipRect)) {
11323     return true;
11324   }
11325 
11326   nsIFrame* parent = clipParent->GetParent();
11327   if (!parent) {
11328     return false;
11329   }
11330 
11331   return IsFrameScrolledOutOfView(aTarget, aTargetRect, parent);
11332 }
11333 
IsScrolledOutOfView() const11334 bool nsIFrame::IsScrolledOutOfView() const {
11335   nsRect rect = InkOverflowRectRelativeToSelf();
11336   return IsFrameScrolledOutOfView(this, rect, this);
11337 }
11338 
ComputeWidgetTransform()11339 gfx::Matrix nsIFrame::ComputeWidgetTransform() {
11340   const nsStyleUIReset* uiReset = StyleUIReset();
11341   if (uiReset->mMozWindowTransform.IsNone()) {
11342     return gfx::Matrix();
11343   }
11344 
11345   TransformReferenceBox refBox(nullptr, nsRect(nsPoint(), GetSize()));
11346 
11347   nsPresContext* presContext = PresContext();
11348   int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
11349   gfx::Matrix4x4 matrix = nsStyleTransformMatrix::ReadTransforms(
11350       uiReset->mMozWindowTransform, refBox, float(appUnitsPerDevPixel));
11351 
11352   // Apply the -moz-window-transform-origin translation to the matrix.
11353   const StyleTransformOrigin& origin = uiReset->mWindowTransformOrigin;
11354   Point transformOrigin = nsStyleTransformMatrix::Convert2DPosition(
11355       origin.horizontal, origin.vertical, refBox, appUnitsPerDevPixel);
11356   matrix.ChangeBasis(Point3D(transformOrigin.x, transformOrigin.y, 0));
11357 
11358   gfx::Matrix result2d;
11359   if (!matrix.CanDraw2D(&result2d)) {
11360     // FIXME: It would be preferable to reject non-2D transforms at parse time.
11361     NS_WARNING(
11362         "-moz-window-transform does not describe a 2D transform, "
11363         "but only 2d transforms are supported");
11364     return gfx::Matrix();
11365   }
11366 
11367   return result2d;
11368 }
11369 
DoUpdateStyleOfOwnedAnonBoxes(ServoRestyleState & aRestyleState)11370 void nsIFrame::DoUpdateStyleOfOwnedAnonBoxes(ServoRestyleState& aRestyleState) {
11371   // As a special case, we check for {ib}-split block frames here, rather
11372   // than have an nsInlineFrame::AppendDirectlyOwnedAnonBoxes implementation
11373   // that returns them.
11374   //
11375   // (If we did handle them in AppendDirectlyOwnedAnonBoxes, we would have to
11376   // return *all* of the in-flow {ib}-split block frames, not just the first
11377   // one.  For restyling, we really just need the first in flow, and the other
11378   // user of the AppendOwnedAnonBoxes API, AllChildIterator, doesn't need to
11379   // know about them at all, since these block frames never create NAC.  So we
11380   // avoid any unncessary hashtable lookups for the {ib}-split frames by calling
11381   // UpdateStyleOfOwnedAnonBoxesForIBSplit directly here.)
11382   if (IsInlineFrame()) {
11383     if (HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
11384       static_cast<nsInlineFrame*>(this)->UpdateStyleOfOwnedAnonBoxesForIBSplit(
11385           aRestyleState);
11386     }
11387     return;
11388   }
11389 
11390   AutoTArray<OwnedAnonBox, 4> frames;
11391   AppendDirectlyOwnedAnonBoxes(frames);
11392   for (OwnedAnonBox& box : frames) {
11393     if (box.mUpdateStyleFn) {
11394       box.mUpdateStyleFn(this, box.mAnonBoxFrame, aRestyleState);
11395     } else {
11396       UpdateStyleOfChildAnonBox(box.mAnonBoxFrame, aRestyleState);
11397     }
11398   }
11399 }
11400 
11401 /* virtual */
AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox> & aResult)11402 void nsIFrame::AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) {
11403   MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES));
11404   MOZ_ASSERT(false, "Why did this get called?");
11405 }
11406 
DoAppendOwnedAnonBoxes(nsTArray<OwnedAnonBox> & aResult)11407 void nsIFrame::DoAppendOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) {
11408   size_t i = aResult.Length();
11409   AppendDirectlyOwnedAnonBoxes(aResult);
11410 
11411   // After appending the directly owned anonymous boxes of this frame to
11412   // aResult above, we need to check each of them to see if they own
11413   // any anonymous boxes themselves.  Note that we keep progressing
11414   // through aResult, looking for additional entries in aResult from these
11415   // subsequent AppendDirectlyOwnedAnonBoxes calls.  (Thus we can't
11416   // use a ranged for loop here.)
11417 
11418   while (i < aResult.Length()) {
11419     nsIFrame* f = aResult[i].mAnonBoxFrame;
11420     if (f->HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES)) {
11421       f->AppendDirectlyOwnedAnonBoxes(aResult);
11422     }
11423     ++i;
11424   }
11425 }
11426 
CaretPosition()11427 nsIFrame::CaretPosition::CaretPosition() : mContentOffset(0) {}
11428 
11429 nsIFrame::CaretPosition::~CaretPosition() = default;
11430 
HasCSSAnimations()11431 bool nsIFrame::HasCSSAnimations() {
11432   auto collection =
11433       AnimationCollection<CSSAnimation>::GetAnimationCollection(this);
11434   return collection && collection->mAnimations.Length() > 0;
11435 }
11436 
HasCSSTransitions()11437 bool nsIFrame::HasCSSTransitions() {
11438   auto collection =
11439       AnimationCollection<CSSTransition>::GetAnimationCollection(this);
11440   return collection && collection->mAnimations.Length() > 0;
11441 }
11442 
AddSizeOfExcludingThisForTree(nsWindowSizes & aSizes) const11443 void nsIFrame::AddSizeOfExcludingThisForTree(nsWindowSizes& aSizes) const {
11444   aSizes.mLayoutFramePropertiesSize +=
11445       mProperties.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
11446 
11447   // We don't do this for Gecko because this stuff is stored in the nsPresArena
11448   // and so measured elsewhere.
11449   if (!aSizes.mState.HaveSeenPtr(mComputedStyle)) {
11450     mComputedStyle->AddSizeOfIncludingThis(aSizes,
11451                                            &aSizes.mLayoutComputedValuesNonDom);
11452   }
11453 
11454   // And our additional styles.
11455   int32_t index = 0;
11456   while (auto* extra = GetAdditionalComputedStyle(index++)) {
11457     if (!aSizes.mState.HaveSeenPtr(extra)) {
11458       extra->AddSizeOfIncludingThis(aSizes,
11459                                     &aSizes.mLayoutComputedValuesNonDom);
11460     }
11461   }
11462 
11463   for (const auto& childList : ChildLists()) {
11464     for (const nsIFrame* f : childList.mList) {
11465       f->AddSizeOfExcludingThisForTree(aSizes);
11466     }
11467   }
11468 }
11469 
GetCompositorHitTestArea(nsDisplayListBuilder * aBuilder)11470 nsRect nsIFrame::GetCompositorHitTestArea(nsDisplayListBuilder* aBuilder) {
11471   nsRect area;
11472 
11473   nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetScrollableFrameFor(this);
11474   if (scrollFrame) {
11475     // If the frame is content of a scrollframe, then we need to pick up the
11476     // area corresponding to the overflow rect as well. Otherwise the parts of
11477     // the overflow that are not occupied by descendants get skipped and the
11478     // APZ code sends touch events to the content underneath instead.
11479     // See https://bugzilla.mozilla.org/show_bug.cgi?id=1127773#c15.
11480     area = ScrollableOverflowRect();
11481   } else {
11482     area = GetRectRelativeToSelf();
11483   }
11484 
11485   if (!area.IsEmpty()) {
11486     return area + aBuilder->ToReferenceFrame(this);
11487   }
11488 
11489   return area;
11490 }
11491 
GetCompositorHitTestInfo(nsDisplayListBuilder * aBuilder)11492 CompositorHitTestInfo nsIFrame::GetCompositorHitTestInfo(
11493     nsDisplayListBuilder* aBuilder) {
11494   CompositorHitTestInfo result = CompositorHitTestInvisibleToHit;
11495 
11496   if (aBuilder->IsInsidePointerEventsNoneDoc()) {
11497     // Somewhere up the parent document chain is a subdocument with pointer-
11498     // events:none set on it.
11499     return result;
11500   }
11501   if (!GetParent()) {
11502     MOZ_ASSERT(IsViewportFrame());
11503     // Viewport frames are never event targets, other frames, like canvas
11504     // frames, are the event targets for any regions viewport frames may cover.
11505     return result;
11506   }
11507   if (Style()->PointerEvents() == StylePointerEvents::None) {
11508     return result;
11509   }
11510   if (!StyleVisibility()->IsVisible()) {
11511     return result;
11512   }
11513 
11514   // Anything that didn't match the above conditions is visible to hit-testing.
11515   result = CompositorHitTestFlags::eVisibleToHitTest;
11516   if (SVGIntegrationUtils::UsingMaskOrClipPathForFrame(this)) {
11517     // If WebRender is enabled, simple clip-paths can be converted into WR
11518     // clips that WR knows how to hit-test against, so we don't need to mark
11519     // it as an irregular area.
11520     if (!gfxVars::UseWebRender() ||
11521         !SVGIntegrationUtils::UsingSimpleClipPathForFrame(this)) {
11522       result += CompositorHitTestFlags::eIrregularArea;
11523     }
11524   }
11525 
11526   if (aBuilder->IsBuildingNonLayerizedScrollbar()) {
11527     // Scrollbars may be painted into a layer below the actual layer they will
11528     // scroll, and therefore wheel events may be dispatched to the outer frame
11529     // instead of the intended scrollframe. To address this, we force a d-t-c
11530     // region on scrollbar frames that won't be placed in their own layer. See
11531     // bug 1213324 for details.
11532     result += CompositorHitTestFlags::eInactiveScrollframe;
11533   } else if (aBuilder->GetAncestorHasApzAwareEventHandler()) {
11534     result += CompositorHitTestFlags::eApzAwareListeners;
11535   } else if (IsRangeFrame()) {
11536     // Range frames handle touch events directly without having a touch listener
11537     // so we need to let APZ know that this area cares about events.
11538     result += CompositorHitTestFlags::eApzAwareListeners;
11539   }
11540 
11541   if (aBuilder->IsTouchEventPrefEnabledDoc()) {
11542     // Inherit the touch-action flags from the parent, if there is one. We do
11543     // this because of how the touch-action on a frame combines the touch-action
11544     // from ancestor DOM elements. Refer to the documentation in
11545     // TouchActionHelper.cpp for details; this code is meant to be equivalent to
11546     // that code, but woven into the top-down recursive display list building
11547     // process.
11548     CompositorHitTestInfo inheritedTouchAction =
11549         aBuilder->GetCompositorHitTestInfo() & CompositorHitTestTouchActionMask;
11550 
11551     nsIFrame* touchActionFrame = this;
11552     if (nsIScrollableFrame* scrollFrame =
11553             nsLayoutUtils::GetScrollableFrameFor(this)) {
11554       ScrollStyles ss = scrollFrame->GetScrollStyles();
11555       if (ss.mVertical != StyleOverflow::Hidden ||
11556           ss.mHorizontal != StyleOverflow::Hidden) {
11557         touchActionFrame = do_QueryFrame(scrollFrame);
11558         // On scrollframes, stop inheriting the pan-x and pan-y flags; instead,
11559         // reset them back to zero to allow panning on the scrollframe unless we
11560         // encounter an element that disables it that's inside the scrollframe.
11561         // This is equivalent to the |considerPanning| variable in
11562         // TouchActionHelper.cpp, but for a top-down traversal.
11563         CompositorHitTestInfo panMask(
11564             CompositorHitTestFlags::eTouchActionPanXDisabled,
11565             CompositorHitTestFlags::eTouchActionPanYDisabled);
11566         inheritedTouchAction -= panMask;
11567       }
11568     }
11569 
11570     result += inheritedTouchAction;
11571 
11572     const StyleTouchAction touchAction = touchActionFrame->UsedTouchAction();
11573     // The CSS allows the syntax auto | none | [pan-x || pan-y] | manipulation
11574     // so we can eliminate some combinations of things.
11575     if (touchAction == StyleTouchAction::AUTO) {
11576       // nothing to do
11577     } else if (touchAction & StyleTouchAction::MANIPULATION) {
11578       result += CompositorHitTestFlags::eTouchActionDoubleTapZoomDisabled;
11579     } else {
11580       // This path handles the cases none | [pan-x || pan-y || pinch-zoom] so
11581       // double-tap is disabled in here.
11582       if (!(touchAction & StyleTouchAction::PINCH_ZOOM)) {
11583         result += CompositorHitTestFlags::eTouchActionPinchZoomDisabled;
11584       }
11585 
11586       result += CompositorHitTestFlags::eTouchActionDoubleTapZoomDisabled;
11587 
11588       if (!(touchAction & StyleTouchAction::PAN_X)) {
11589         result += CompositorHitTestFlags::eTouchActionPanXDisabled;
11590       }
11591       if (!(touchAction & StyleTouchAction::PAN_Y)) {
11592         result += CompositorHitTestFlags::eTouchActionPanYDisabled;
11593       }
11594       if (touchAction & StyleTouchAction::NONE) {
11595         // all the touch-action disabling flags will already have been set above
11596         MOZ_ASSERT(result.contains(CompositorHitTestTouchActionMask));
11597       }
11598     }
11599   }
11600 
11601   const Maybe<ScrollDirection> scrollDirection =
11602       aBuilder->GetCurrentScrollbarDirection();
11603   if (scrollDirection.isSome()) {
11604     if (GetContent()->IsXULElement(nsGkAtoms::thumb)) {
11605       const bool thumbGetsLayer = aBuilder->GetCurrentScrollbarTarget() !=
11606                                   layers::ScrollableLayerGuid::NULL_SCROLL_ID;
11607       if (thumbGetsLayer) {
11608         result += CompositorHitTestFlags::eScrollbarThumb;
11609       } else {
11610         result += CompositorHitTestFlags::eInactiveScrollframe;
11611       }
11612     }
11613 
11614     if (*scrollDirection == ScrollDirection::eVertical) {
11615       result += CompositorHitTestFlags::eScrollbarVertical;
11616     }
11617 
11618     // includes the ScrollbarFrame, SliderFrame, anything else that
11619     // might be inside the xul:scrollbar
11620     result += CompositorHitTestFlags::eScrollbar;
11621   }
11622 
11623   return result;
11624 }
11625 
11626 // Returns true if we can guarantee there is no visible descendants.
HasNoVisibleDescendants(const nsIFrame * aFrame)11627 static bool HasNoVisibleDescendants(const nsIFrame* aFrame) {
11628   for (const auto& childList : aFrame->ChildLists()) {
11629     for (nsIFrame* f : childList.mList) {
11630       if (nsPlaceholderFrame::GetRealFrameFor(f)
11631               ->IsVisibleOrMayHaveVisibleDescendants()) {
11632         return false;
11633       }
11634     }
11635   }
11636   return true;
11637 }
11638 
UpdateVisibleDescendantsState()11639 void nsIFrame::UpdateVisibleDescendantsState() {
11640   if (StyleVisibility()->IsVisible()) {
11641     // Notify invisible ancestors that a visible descendant exists now.
11642     nsIFrame* ancestor;
11643     for (ancestor = GetInFlowParent();
11644          ancestor && !ancestor->StyleVisibility()->IsVisible();
11645          ancestor = ancestor->GetInFlowParent()) {
11646       ancestor->mAllDescendantsAreInvisible = false;
11647     }
11648   } else {
11649     mAllDescendantsAreInvisible = HasNoVisibleDescendants(this);
11650   }
11651 }
11652 
ShouldApplyOverflowClipping(const nsStyleDisplay * aDisp) const11653 nsIFrame::PhysicalAxes nsIFrame::ShouldApplyOverflowClipping(
11654     const nsStyleDisplay* aDisp) const {
11655   MOZ_ASSERT(aDisp == StyleDisplay(), "Wrong display struct");
11656 
11657   // 'contain:paint', which we handle as 'overflow:clip' here. Except for
11658   // scrollframes we don't need contain:paint to add any clipping, because
11659   // the scrollable frame will already clip overflowing content, and because
11660   // 'contain:paint' should prevent all means of escaping that clipping
11661   // (e.g. because it forms a fixed-pos containing block).
11662   if (aDisp->IsContainPaint() && !IsScrollFrame() &&
11663       IsFrameOfType(eSupportsContainLayoutAndPaint)) {
11664     return PhysicalAxes::Both;
11665   }
11666 
11667   // and overflow:hidden that we should interpret as clip
11668   if (aDisp->mOverflowX == StyleOverflow::Hidden &&
11669       aDisp->mOverflowY == StyleOverflow::Hidden) {
11670     // REVIEW: these are the frame types that set up clipping.
11671     LayoutFrameType type = Type();
11672     switch (type) {
11673       case LayoutFrameType::Table:
11674       case LayoutFrameType::TableCell:
11675       case LayoutFrameType::SVGOuterSVG:
11676       case LayoutFrameType::SVGInnerSVG:
11677       case LayoutFrameType::SVGSymbol:
11678       case LayoutFrameType::SVGForeignObject:
11679         return PhysicalAxes::Both;
11680       default:
11681         if (IsFrameOfType(nsIFrame::eReplacedContainsBlock)) {
11682           if (type == mozilla::LayoutFrameType::TextInput) {
11683             // It has an anonymous scroll frame that handles any overflow.
11684             return PhysicalAxes::None;
11685           }
11686           return PhysicalAxes::Both;
11687         }
11688     }
11689   }
11690 
11691   // clip overflow:clip, except for nsListControlFrame which is
11692   // an nsHTMLScrollFrame sub-class.
11693   if (MOZ_UNLIKELY((aDisp->mOverflowX == mozilla::StyleOverflow::Clip ||
11694                     aDisp->mOverflowY == mozilla::StyleOverflow::Clip) &&
11695                    !IsListControlFrame())) {
11696     // FIXME: we could use GetViewportScrollStylesOverrideElement() here instead
11697     // if that worked correctly in a print context. (see bug 1654667)
11698     const auto* element = Element::FromNodeOrNull(GetContent());
11699     if (!element ||
11700         !PresContext()->ElementWouldPropagateScrollStyles(*element)) {
11701       uint8_t axes = uint8_t(PhysicalAxes::None);
11702       if (aDisp->mOverflowX == mozilla::StyleOverflow::Clip) {
11703         axes |= uint8_t(PhysicalAxes::Horizontal);
11704       }
11705       if (aDisp->mOverflowY == mozilla::StyleOverflow::Clip) {
11706         axes |= uint8_t(PhysicalAxes::Vertical);
11707       }
11708       return PhysicalAxes(axes);
11709     }
11710   }
11711 
11712   if (HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
11713     return PhysicalAxes::None;
11714   }
11715 
11716   // If we're paginated and a block, and have NS_BLOCK_CLIP_PAGINATED_OVERFLOW
11717   // set, then we want to clip our overflow.
11718   bool clip = HasAnyStateBits(NS_BLOCK_CLIP_PAGINATED_OVERFLOW) &&
11719               PresContext()->IsPaginated() && IsBlockFrame();
11720   return clip ? PhysicalAxes::Both : PhysicalAxes::None;
11721 }
11722 
AddPaintedPresShell(mozilla::PresShell * aPresShell)11723 void nsIFrame::AddPaintedPresShell(mozilla::PresShell* aPresShell) {
11724   PaintedPresShellList()->AppendElement(do_GetWeakReference(aPresShell));
11725 }
11726 
UpdatePaintCountForPaintedPresShells()11727 void nsIFrame::UpdatePaintCountForPaintedPresShells() {
11728   for (nsWeakPtr& item : *PaintedPresShellList()) {
11729     if (RefPtr<mozilla::PresShell> presShell = do_QueryReferent(item)) {
11730       presShell->IncrementPaintCount();
11731     }
11732   }
11733 }
11734 
DidPaintPresShell(mozilla::PresShell * aPresShell)11735 bool nsIFrame::DidPaintPresShell(mozilla::PresShell* aPresShell) {
11736   for (nsWeakPtr& item : *PaintedPresShellList()) {
11737     RefPtr<mozilla::PresShell> presShell = do_QueryReferent(item);
11738     if (presShell == aPresShell) {
11739       return true;
11740     }
11741   }
11742   return false;
11743 }
11744 
11745 #ifdef DEBUG
GetTagName(nsIFrame * aFrame,nsIContent * aContent,int aResultSize,char * aResult)11746 static void GetTagName(nsIFrame* aFrame, nsIContent* aContent, int aResultSize,
11747                        char* aResult) {
11748   if (aContent) {
11749     snprintf(aResult, aResultSize, "%s@%p",
11750              nsAtomCString(aContent->NodeInfo()->NameAtom()).get(), aFrame);
11751   } else {
11752     snprintf(aResult, aResultSize, "@%p", aFrame);
11753   }
11754 }
11755 
Trace(const char * aMethod,bool aEnter)11756 void nsIFrame::Trace(const char* aMethod, bool aEnter) {
11757   if (NS_FRAME_LOG_TEST(sFrameLogModule, NS_FRAME_TRACE_CALLS)) {
11758     char tagbuf[40];
11759     GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
11760     printf_stderr("%s: %s %s", tagbuf, aEnter ? "enter" : "exit", aMethod);
11761   }
11762 }
11763 
Trace(const char * aMethod,bool aEnter,const nsReflowStatus & aStatus)11764 void nsIFrame::Trace(const char* aMethod, bool aEnter,
11765                      const nsReflowStatus& aStatus) {
11766   if (NS_FRAME_LOG_TEST(sFrameLogModule, NS_FRAME_TRACE_CALLS)) {
11767     char tagbuf[40];
11768     GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
11769     printf_stderr("%s: %s %s, status=%scomplete%s", tagbuf,
11770                   aEnter ? "enter" : "exit", aMethod,
11771                   aStatus.IsIncomplete() ? "not" : "",
11772                   (aStatus.NextInFlowNeedsReflow()) ? "+reflow" : "");
11773   }
11774 }
11775 
TraceMsg(const char * aFormatString,...)11776 void nsIFrame::TraceMsg(const char* aFormatString, ...) {
11777   if (NS_FRAME_LOG_TEST(sFrameLogModule, NS_FRAME_TRACE_CALLS)) {
11778     // Format arguments into a buffer
11779     char argbuf[200];
11780     va_list ap;
11781     va_start(ap, aFormatString);
11782     VsprintfLiteral(argbuf, aFormatString, ap);
11783     va_end(ap);
11784 
11785     char tagbuf[40];
11786     GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
11787     printf_stderr("%s: %s", tagbuf, argbuf);
11788   }
11789 }
11790 
VerifyDirtyBitSet(const nsFrameList & aFrameList)11791 void nsIFrame::VerifyDirtyBitSet(const nsFrameList& aFrameList) {
11792   for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
11793     NS_ASSERTION(e.get()->HasAnyStateBits(NS_FRAME_IS_DIRTY),
11794                  "dirty bit not set");
11795   }
11796 }
11797 
11798 // Start Display Reflow
DR_cookie(nsPresContext * aPresContext,nsIFrame * aFrame,const ReflowInput & aReflowInput,ReflowOutput & aMetrics,nsReflowStatus & aStatus)11799 DR_cookie::DR_cookie(nsPresContext* aPresContext, nsIFrame* aFrame,
11800                      const ReflowInput& aReflowInput, ReflowOutput& aMetrics,
11801                      nsReflowStatus& aStatus)
11802     : mPresContext(aPresContext),
11803       mFrame(aFrame),
11804       mReflowInput(aReflowInput),
11805       mMetrics(aMetrics),
11806       mStatus(aStatus) {
11807   MOZ_COUNT_CTOR(DR_cookie);
11808   mValue = nsIFrame::DisplayReflowEnter(aPresContext, mFrame, mReflowInput);
11809 }
11810 
~DR_cookie()11811 DR_cookie::~DR_cookie() {
11812   MOZ_COUNT_DTOR(DR_cookie);
11813   nsIFrame::DisplayReflowExit(mPresContext, mFrame, mMetrics, mStatus, mValue);
11814 }
11815 
DR_layout_cookie(nsIFrame * aFrame)11816 DR_layout_cookie::DR_layout_cookie(nsIFrame* aFrame) : mFrame(aFrame) {
11817   MOZ_COUNT_CTOR(DR_layout_cookie);
11818   mValue = nsIFrame::DisplayLayoutEnter(mFrame);
11819 }
11820 
~DR_layout_cookie()11821 DR_layout_cookie::~DR_layout_cookie() {
11822   MOZ_COUNT_DTOR(DR_layout_cookie);
11823   nsIFrame::DisplayLayoutExit(mFrame, mValue);
11824 }
11825 
DR_intrinsic_inline_size_cookie(nsIFrame * aFrame,const char * aType,nscoord & aResult)11826 DR_intrinsic_inline_size_cookie::DR_intrinsic_inline_size_cookie(
11827     nsIFrame* aFrame, const char* aType, nscoord& aResult)
11828     : mFrame(aFrame), mType(aType), mResult(aResult) {
11829   MOZ_COUNT_CTOR(DR_intrinsic_inline_size_cookie);
11830   mValue = nsIFrame::DisplayIntrinsicISizeEnter(mFrame, mType);
11831 }
11832 
~DR_intrinsic_inline_size_cookie()11833 DR_intrinsic_inline_size_cookie::~DR_intrinsic_inline_size_cookie() {
11834   MOZ_COUNT_DTOR(DR_intrinsic_inline_size_cookie);
11835   nsIFrame::DisplayIntrinsicISizeExit(mFrame, mType, mResult, mValue);
11836 }
11837 
DR_intrinsic_size_cookie(nsIFrame * aFrame,const char * aType,nsSize & aResult)11838 DR_intrinsic_size_cookie::DR_intrinsic_size_cookie(nsIFrame* aFrame,
11839                                                    const char* aType,
11840                                                    nsSize& aResult)
11841     : mFrame(aFrame), mType(aType), mResult(aResult) {
11842   MOZ_COUNT_CTOR(DR_intrinsic_size_cookie);
11843   mValue = nsIFrame::DisplayIntrinsicSizeEnter(mFrame, mType);
11844 }
11845 
~DR_intrinsic_size_cookie()11846 DR_intrinsic_size_cookie::~DR_intrinsic_size_cookie() {
11847   MOZ_COUNT_DTOR(DR_intrinsic_size_cookie);
11848   nsIFrame::DisplayIntrinsicSizeExit(mFrame, mType, mResult, mValue);
11849 }
11850 
DR_init_constraints_cookie(nsIFrame * aFrame,ReflowInput * aState,nscoord aCBWidth,nscoord aCBHeight,const mozilla::Maybe<mozilla::LogicalMargin> aBorder,const mozilla::Maybe<mozilla::LogicalMargin> aPadding)11851 DR_init_constraints_cookie::DR_init_constraints_cookie(
11852     nsIFrame* aFrame, ReflowInput* aState, nscoord aCBWidth, nscoord aCBHeight,
11853     const mozilla::Maybe<mozilla::LogicalMargin> aBorder,
11854     const mozilla::Maybe<mozilla::LogicalMargin> aPadding)
11855     : mFrame(aFrame), mState(aState) {
11856   MOZ_COUNT_CTOR(DR_init_constraints_cookie);
11857   nsMargin border;
11858   if (aBorder) {
11859     border = aBorder->GetPhysicalMargin(aFrame->GetWritingMode());
11860   }
11861   nsMargin padding;
11862   if (aPadding) {
11863     padding = aPadding->GetPhysicalMargin(aFrame->GetWritingMode());
11864   }
11865   mValue = ReflowInput::DisplayInitConstraintsEnter(
11866       mFrame, mState, aCBWidth, aCBHeight, aBorder ? &border : nullptr,
11867       aPadding ? &padding : nullptr);
11868 }
11869 
~DR_init_constraints_cookie()11870 DR_init_constraints_cookie::~DR_init_constraints_cookie() {
11871   MOZ_COUNT_DTOR(DR_init_constraints_cookie);
11872   ReflowInput::DisplayInitConstraintsExit(mFrame, mState, mValue);
11873 }
11874 
DR_init_offsets_cookie(nsIFrame * aFrame,SizeComputationInput * aState,nscoord aPercentBasis,WritingMode aCBWritingMode,const mozilla::Maybe<mozilla::LogicalMargin> aBorder,const mozilla::Maybe<mozilla::LogicalMargin> aPadding)11875 DR_init_offsets_cookie::DR_init_offsets_cookie(
11876     nsIFrame* aFrame, SizeComputationInput* aState, nscoord aPercentBasis,
11877     WritingMode aCBWritingMode,
11878     const mozilla::Maybe<mozilla::LogicalMargin> aBorder,
11879     const mozilla::Maybe<mozilla::LogicalMargin> aPadding)
11880     : mFrame(aFrame), mState(aState) {
11881   MOZ_COUNT_CTOR(DR_init_offsets_cookie);
11882   nsMargin border;
11883   if (aBorder) {
11884     border = aBorder->GetPhysicalMargin(aFrame->GetWritingMode());
11885   }
11886   nsMargin padding;
11887   if (aPadding) {
11888     padding = aPadding->GetPhysicalMargin(aFrame->GetWritingMode());
11889   }
11890   mValue = SizeComputationInput::DisplayInitOffsetsEnter(
11891       mFrame, mState, aPercentBasis, aCBWritingMode,
11892       aBorder ? &border : nullptr, aPadding ? &padding : nullptr);
11893 }
11894 
~DR_init_offsets_cookie()11895 DR_init_offsets_cookie::~DR_init_offsets_cookie() {
11896   MOZ_COUNT_DTOR(DR_init_offsets_cookie);
11897   SizeComputationInput::DisplayInitOffsetsExit(mFrame, mState, mValue);
11898 }
11899 
11900 struct DR_Rule;
11901 
11902 struct DR_FrameTypeInfo {
11903   DR_FrameTypeInfo(LayoutFrameType aFrameType, const char* aFrameNameAbbrev,
11904                    const char* aFrameName);
11905   ~DR_FrameTypeInfo();
11906 
11907   LayoutFrameType mType;
11908   char mNameAbbrev[16];
11909   char mName[32];
11910   nsTArray<DR_Rule*> mRules;
11911 
11912  private:
11913   DR_FrameTypeInfo& operator=(const DR_FrameTypeInfo&) = delete;
11914 };
11915 
11916 struct DR_FrameTreeNode;
11917 struct DR_Rule;
11918 
11919 struct DR_State {
11920   DR_State();
11921   ~DR_State();
11922   void Init();
11923   void AddFrameTypeInfo(LayoutFrameType aFrameType,
11924                         const char* aFrameNameAbbrev, const char* aFrameName);
11925   DR_FrameTypeInfo* GetFrameTypeInfo(LayoutFrameType aFrameType);
11926   DR_FrameTypeInfo* GetFrameTypeInfo(char* aFrameName);
11927   void InitFrameTypeTable();
11928   DR_FrameTreeNode* CreateTreeNode(nsIFrame* aFrame,
11929                                    const ReflowInput* aReflowInput);
11930   void FindMatchingRule(DR_FrameTreeNode& aNode);
11931   bool RuleMatches(DR_Rule& aRule, DR_FrameTreeNode& aNode);
11932   bool GetToken(FILE* aFile, char* aBuf, size_t aBufSize);
11933   DR_Rule* ParseRule(FILE* aFile);
11934   void ParseRulesFile();
11935   void AddRule(nsTArray<DR_Rule*>& aRules, DR_Rule& aRule);
11936   bool IsWhiteSpace(int c);
11937   bool GetNumber(char* aBuf, int32_t& aNumber);
11938   void PrettyUC(nscoord aSize, char* aBuf, int aBufSize);
11939   void PrintMargin(const char* tag, const nsMargin* aMargin);
11940   void DisplayFrameTypeInfo(nsIFrame* aFrame, int32_t aIndent);
11941   void DeleteTreeNode(DR_FrameTreeNode& aNode);
11942 
11943   bool mInited;
11944   bool mActive;
11945   int32_t mCount;
11946   int32_t mAssert;
11947   int32_t mIndent;
11948   bool mIndentUndisplayedFrames;
11949   bool mDisplayPixelErrors;
11950   nsTArray<DR_Rule*> mWildRules;
11951   nsTArray<DR_FrameTypeInfo> mFrameTypeTable;
11952   // reflow specific state
11953   nsTArray<DR_FrameTreeNode*> mFrameTreeLeaves;
11954 };
11955 
11956 static DR_State* DR_state;  // the one and only DR_State
11957 
11958 struct DR_RulePart {
DR_RulePartDR_RulePart11959   explicit DR_RulePart(LayoutFrameType aFrameType)
11960       : mFrameType(aFrameType), mNext(0) {}
11961 
11962   void Destroy();
11963 
11964   LayoutFrameType mFrameType;
11965   DR_RulePart* mNext;
11966 };
11967 
Destroy()11968 void DR_RulePart::Destroy() {
11969   if (mNext) {
11970     mNext->Destroy();
11971   }
11972   delete this;
11973 }
11974 
11975 struct DR_Rule {
DR_RuleDR_Rule11976   DR_Rule() : mLength(0), mTarget(nullptr), mDisplay(false) {
11977     MOZ_COUNT_CTOR(DR_Rule);
11978   }
~DR_RuleDR_Rule11979   ~DR_Rule() {
11980     if (mTarget) mTarget->Destroy();
11981     MOZ_COUNT_DTOR(DR_Rule);
11982   }
11983   void AddPart(LayoutFrameType aFrameType);
11984 
11985   uint32_t mLength;
11986   DR_RulePart* mTarget;
11987   bool mDisplay;
11988 };
11989 
AddPart(LayoutFrameType aFrameType)11990 void DR_Rule::AddPart(LayoutFrameType aFrameType) {
11991   DR_RulePart* newPart = new DR_RulePart(aFrameType);
11992   newPart->mNext = mTarget;
11993   mTarget = newPart;
11994   mLength++;
11995 }
11996 
~DR_FrameTypeInfo()11997 DR_FrameTypeInfo::~DR_FrameTypeInfo() {
11998   int32_t numElements;
11999   numElements = mRules.Length();
12000   for (int32_t i = numElements - 1; i >= 0; i--) {
12001     delete mRules.ElementAt(i);
12002   }
12003 }
12004 
DR_FrameTypeInfo(LayoutFrameType aFrameType,const char * aFrameNameAbbrev,const char * aFrameName)12005 DR_FrameTypeInfo::DR_FrameTypeInfo(LayoutFrameType aFrameType,
12006                                    const char* aFrameNameAbbrev,
12007                                    const char* aFrameName) {
12008   mType = aFrameType;
12009   PL_strncpyz(mNameAbbrev, aFrameNameAbbrev, sizeof(mNameAbbrev));
12010   PL_strncpyz(mName, aFrameName, sizeof(mName));
12011 }
12012 
12013 struct DR_FrameTreeNode {
DR_FrameTreeNodeDR_FrameTreeNode12014   DR_FrameTreeNode(nsIFrame* aFrame, DR_FrameTreeNode* aParent)
12015       : mFrame(aFrame), mParent(aParent), mDisplay(0), mIndent(0) {
12016     MOZ_COUNT_CTOR(DR_FrameTreeNode);
12017   }
12018 
12019   MOZ_COUNTED_DTOR(DR_FrameTreeNode)
12020 
12021   nsIFrame* mFrame;
12022   DR_FrameTreeNode* mParent;
12023   bool mDisplay;
12024   uint32_t mIndent;
12025 };
12026 
12027 // DR_State implementation
12028 
DR_State()12029 DR_State::DR_State()
12030     : mInited(false),
12031       mActive(false),
12032       mCount(0),
12033       mAssert(-1),
12034       mIndent(0),
12035       mIndentUndisplayedFrames(false),
12036       mDisplayPixelErrors(false) {
12037   MOZ_COUNT_CTOR(DR_State);
12038 }
12039 
Init()12040 void DR_State::Init() {
12041   char* env = PR_GetEnv("GECKO_DISPLAY_REFLOW_ASSERT");
12042   int32_t num;
12043   if (env) {
12044     if (GetNumber(env, num))
12045       mAssert = num;
12046     else
12047       printf("GECKO_DISPLAY_REFLOW_ASSERT - invalid value = %s", env);
12048   }
12049 
12050   env = PR_GetEnv("GECKO_DISPLAY_REFLOW_INDENT_START");
12051   if (env) {
12052     if (GetNumber(env, num))
12053       mIndent = num;
12054     else
12055       printf("GECKO_DISPLAY_REFLOW_INDENT_START - invalid value = %s", env);
12056   }
12057 
12058   env = PR_GetEnv("GECKO_DISPLAY_REFLOW_INDENT_UNDISPLAYED_FRAMES");
12059   if (env) {
12060     if (GetNumber(env, num))
12061       mIndentUndisplayedFrames = num;
12062     else
12063       printf(
12064           "GECKO_DISPLAY_REFLOW_INDENT_UNDISPLAYED_FRAMES - invalid value = %s",
12065           env);
12066   }
12067 
12068   env = PR_GetEnv("GECKO_DISPLAY_REFLOW_FLAG_PIXEL_ERRORS");
12069   if (env) {
12070     if (GetNumber(env, num))
12071       mDisplayPixelErrors = num;
12072     else
12073       printf("GECKO_DISPLAY_REFLOW_FLAG_PIXEL_ERRORS - invalid value = %s",
12074              env);
12075   }
12076 
12077   InitFrameTypeTable();
12078   ParseRulesFile();
12079   mInited = true;
12080 }
12081 
~DR_State()12082 DR_State::~DR_State() {
12083   MOZ_COUNT_DTOR(DR_State);
12084   int32_t numElements, i;
12085   numElements = mWildRules.Length();
12086   for (i = numElements - 1; i >= 0; i--) {
12087     delete mWildRules.ElementAt(i);
12088   }
12089   numElements = mFrameTreeLeaves.Length();
12090   for (i = numElements - 1; i >= 0; i--) {
12091     delete mFrameTreeLeaves.ElementAt(i);
12092   }
12093 }
12094 
GetNumber(char * aBuf,int32_t & aNumber)12095 bool DR_State::GetNumber(char* aBuf, int32_t& aNumber) {
12096   if (sscanf(aBuf, "%d", &aNumber) > 0)
12097     return true;
12098   else
12099     return false;
12100 }
12101 
IsWhiteSpace(int c)12102 bool DR_State::IsWhiteSpace(int c) {
12103   return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r');
12104 }
12105 
GetToken(FILE * aFile,char * aBuf,size_t aBufSize)12106 bool DR_State::GetToken(FILE* aFile, char* aBuf, size_t aBufSize) {
12107   bool haveToken = false;
12108   aBuf[0] = 0;
12109   // get the 1st non whitespace char
12110   int c = -1;
12111   for (c = getc(aFile); (c > 0) && IsWhiteSpace(c); c = getc(aFile)) {
12112   }
12113 
12114   if (c > 0) {
12115     haveToken = true;
12116     aBuf[0] = c;
12117     // get everything up to the next whitespace char
12118     size_t cX;
12119     for (cX = 1; cX + 1 < aBufSize; cX++) {
12120       c = getc(aFile);
12121       if (c < 0) {  // EOF
12122         ungetc(' ', aFile);
12123         break;
12124       } else {
12125         if (IsWhiteSpace(c)) {
12126           break;
12127         } else {
12128           aBuf[cX] = c;
12129         }
12130       }
12131     }
12132     aBuf[cX] = 0;
12133   }
12134   return haveToken;
12135 }
12136 
ParseRule(FILE * aFile)12137 DR_Rule* DR_State::ParseRule(FILE* aFile) {
12138   char buf[128];
12139   int32_t doDisplay;
12140   DR_Rule* rule = nullptr;
12141   while (GetToken(aFile, buf, sizeof(buf))) {
12142     if (GetNumber(buf, doDisplay)) {
12143       if (rule) {
12144         rule->mDisplay = !!doDisplay;
12145         break;
12146       } else {
12147         printf("unexpected token - %s \n", buf);
12148       }
12149     } else {
12150       if (!rule) {
12151         rule = new DR_Rule;
12152       }
12153       if (strcmp(buf, "*") == 0) {
12154         rule->AddPart(LayoutFrameType::None);
12155       } else {
12156         DR_FrameTypeInfo* info = GetFrameTypeInfo(buf);
12157         if (info) {
12158           rule->AddPart(info->mType);
12159         } else {
12160           printf("invalid frame type - %s \n", buf);
12161         }
12162       }
12163     }
12164   }
12165   return rule;
12166 }
12167 
AddRule(nsTArray<DR_Rule * > & aRules,DR_Rule & aRule)12168 void DR_State::AddRule(nsTArray<DR_Rule*>& aRules, DR_Rule& aRule) {
12169   int32_t numRules = aRules.Length();
12170   for (int32_t ruleX = 0; ruleX < numRules; ruleX++) {
12171     DR_Rule* rule = aRules.ElementAt(ruleX);
12172     NS_ASSERTION(rule, "program error");
12173     if (aRule.mLength > rule->mLength) {
12174       aRules.InsertElementAt(ruleX, &aRule);
12175       return;
12176     }
12177   }
12178   aRules.AppendElement(&aRule);
12179 }
12180 
ShouldLogReflow(const char * processes)12181 static Maybe<bool> ShouldLogReflow(const char* processes) {
12182   switch (processes[0]) {
12183     case 'A':
12184     case 'a':
12185       return Some(true);
12186     case 'P':
12187     case 'p':
12188       return Some(XRE_IsParentProcess());
12189     case 'C':
12190     case 'c':
12191       return Some(XRE_IsContentProcess());
12192     default:
12193       return Nothing{};
12194   }
12195 }
12196 
ParseRulesFile()12197 void DR_State::ParseRulesFile() {
12198   char* processes = PR_GetEnv("GECKO_DISPLAY_REFLOW_PROCESSES");
12199   if (processes) {
12200     Maybe<bool> enableLog = ShouldLogReflow(processes);
12201     if (enableLog.isNothing()) {
12202       MOZ_CRASH("GECKO_DISPLAY_REFLOW_PROCESSES: [a]ll [p]arent [c]ontent");
12203     } else if (enableLog.value()) {
12204       DR_Rule* rule = new DR_Rule;
12205       rule->AddPart(LayoutFrameType::None);
12206       rule->mDisplay = true;
12207       AddRule(mWildRules, *rule);
12208       mActive = true;
12209     }
12210     return;
12211   }
12212 
12213   char* path = PR_GetEnv("GECKO_DISPLAY_REFLOW_RULES_FILE");
12214   if (path) {
12215     FILE* inFile = fopen(path, "r");
12216     if (!inFile) {
12217       MOZ_CRASH(
12218           "Failed to open the specified rules file; Try `--setpref "
12219           "security.sandbox.content.level=2` if the sandbox is at cause");
12220     }
12221     for (DR_Rule* rule = ParseRule(inFile); rule; rule = ParseRule(inFile)) {
12222       if (rule->mTarget) {
12223         LayoutFrameType fType = rule->mTarget->mFrameType;
12224         if (fType != LayoutFrameType::None) {
12225           DR_FrameTypeInfo* info = GetFrameTypeInfo(fType);
12226           AddRule(info->mRules, *rule);
12227         } else {
12228           AddRule(mWildRules, *rule);
12229         }
12230         mActive = true;
12231       }
12232     }
12233 
12234     fclose(inFile);
12235   }
12236 }
12237 
AddFrameTypeInfo(LayoutFrameType aFrameType,const char * aFrameNameAbbrev,const char * aFrameName)12238 void DR_State::AddFrameTypeInfo(LayoutFrameType aFrameType,
12239                                 const char* aFrameNameAbbrev,
12240                                 const char* aFrameName) {
12241   mFrameTypeTable.EmplaceBack(aFrameType, aFrameNameAbbrev, aFrameName);
12242 }
12243 
GetFrameTypeInfo(LayoutFrameType aFrameType)12244 DR_FrameTypeInfo* DR_State::GetFrameTypeInfo(LayoutFrameType aFrameType) {
12245   int32_t numEntries = mFrameTypeTable.Length();
12246   NS_ASSERTION(numEntries != 0, "empty FrameTypeTable");
12247   for (int32_t i = 0; i < numEntries; i++) {
12248     DR_FrameTypeInfo& info = mFrameTypeTable.ElementAt(i);
12249     if (info.mType == aFrameType) {
12250       return &info;
12251     }
12252   }
12253   return &mFrameTypeTable.ElementAt(numEntries -
12254                                     1);  // return unknown frame type
12255 }
12256 
GetFrameTypeInfo(char * aFrameName)12257 DR_FrameTypeInfo* DR_State::GetFrameTypeInfo(char* aFrameName) {
12258   int32_t numEntries = mFrameTypeTable.Length();
12259   NS_ASSERTION(numEntries != 0, "empty FrameTypeTable");
12260   for (int32_t i = 0; i < numEntries; i++) {
12261     DR_FrameTypeInfo& info = mFrameTypeTable.ElementAt(i);
12262     if ((strcmp(aFrameName, info.mName) == 0) ||
12263         (strcmp(aFrameName, info.mNameAbbrev) == 0)) {
12264       return &info;
12265     }
12266   }
12267   return &mFrameTypeTable.ElementAt(numEntries -
12268                                     1);  // return unknown frame type
12269 }
12270 
InitFrameTypeTable()12271 void DR_State::InitFrameTypeTable() {
12272   AddFrameTypeInfo(LayoutFrameType::Block, "block", "block");
12273   AddFrameTypeInfo(LayoutFrameType::Br, "br", "br");
12274   AddFrameTypeInfo(LayoutFrameType::ColorControl, "color", "colorControl");
12275   AddFrameTypeInfo(LayoutFrameType::GfxButtonControl, "button",
12276                    "gfxButtonControl");
12277   AddFrameTypeInfo(LayoutFrameType::HTMLButtonControl, "HTMLbutton",
12278                    "HTMLButtonControl");
12279   AddFrameTypeInfo(LayoutFrameType::HTMLCanvas, "HTMLCanvas", "HTMLCanvas");
12280   AddFrameTypeInfo(LayoutFrameType::SubDocument, "subdoc", "subDocument");
12281   AddFrameTypeInfo(LayoutFrameType::Image, "img", "image");
12282   AddFrameTypeInfo(LayoutFrameType::Inline, "inline", "inline");
12283   AddFrameTypeInfo(LayoutFrameType::Letter, "letter", "letter");
12284   AddFrameTypeInfo(LayoutFrameType::Line, "line", "line");
12285   AddFrameTypeInfo(LayoutFrameType::ListControl, "select", "select");
12286   AddFrameTypeInfo(LayoutFrameType::Page, "page", "page");
12287   AddFrameTypeInfo(LayoutFrameType::Placeholder, "place", "placeholder");
12288   AddFrameTypeInfo(LayoutFrameType::Canvas, "canvas", "canvas");
12289   AddFrameTypeInfo(LayoutFrameType::XULRoot, "xulroot", "xulroot");
12290   AddFrameTypeInfo(LayoutFrameType::Scroll, "scroll", "scroll");
12291   AddFrameTypeInfo(LayoutFrameType::TableCell, "cell", "tableCell");
12292   AddFrameTypeInfo(LayoutFrameType::TableCol, "col", "tableCol");
12293   AddFrameTypeInfo(LayoutFrameType::TableColGroup, "colG", "tableColGroup");
12294   AddFrameTypeInfo(LayoutFrameType::Table, "tbl", "table");
12295   AddFrameTypeInfo(LayoutFrameType::TableWrapper, "tblW", "tableWrapper");
12296   AddFrameTypeInfo(LayoutFrameType::TableRowGroup, "rowG", "tableRowGroup");
12297   AddFrameTypeInfo(LayoutFrameType::TableRow, "row", "tableRow");
12298   AddFrameTypeInfo(LayoutFrameType::TextInput, "textCtl", "textInput");
12299   AddFrameTypeInfo(LayoutFrameType::Text, "text", "text");
12300   AddFrameTypeInfo(LayoutFrameType::Viewport, "VP", "viewport");
12301   AddFrameTypeInfo(LayoutFrameType::Box, "Box", "Box");
12302   AddFrameTypeInfo(LayoutFrameType::Slider, "Slider", "Slider");
12303   AddFrameTypeInfo(LayoutFrameType::PopupSet, "PopupSet", "PopupSet");
12304   AddFrameTypeInfo(LayoutFrameType::None, "unknown", "unknown");
12305 }
12306 
DisplayFrameTypeInfo(nsIFrame * aFrame,int32_t aIndent)12307 void DR_State::DisplayFrameTypeInfo(nsIFrame* aFrame, int32_t aIndent) {
12308   DR_FrameTypeInfo* frameTypeInfo = GetFrameTypeInfo(aFrame->Type());
12309   if (frameTypeInfo) {
12310     for (int32_t i = 0; i < aIndent; i++) {
12311       printf(" ");
12312     }
12313     if (!strcmp(frameTypeInfo->mNameAbbrev, "unknown")) {
12314       if (aFrame) {
12315         nsAutoString name;
12316         aFrame->GetFrameName(name);
12317         printf("%s %p ", NS_LossyConvertUTF16toASCII(name).get(),
12318                (void*)aFrame);
12319       } else {
12320         printf("%s %p ", frameTypeInfo->mNameAbbrev, (void*)aFrame);
12321       }
12322     } else {
12323       printf("%s %p ", frameTypeInfo->mNameAbbrev, (void*)aFrame);
12324     }
12325   }
12326 }
12327 
RuleMatches(DR_Rule & aRule,DR_FrameTreeNode & aNode)12328 bool DR_State::RuleMatches(DR_Rule& aRule, DR_FrameTreeNode& aNode) {
12329   NS_ASSERTION(aRule.mTarget, "program error");
12330 
12331   DR_RulePart* rulePart;
12332   DR_FrameTreeNode* parentNode;
12333   for (rulePart = aRule.mTarget->mNext, parentNode = aNode.mParent;
12334        rulePart && parentNode;
12335        rulePart = rulePart->mNext, parentNode = parentNode->mParent) {
12336     if (rulePart->mFrameType != LayoutFrameType::None) {
12337       if (parentNode->mFrame) {
12338         if (rulePart->mFrameType != parentNode->mFrame->Type()) {
12339           return false;
12340         }
12341       } else
12342         NS_ASSERTION(false, "program error");
12343     }
12344     // else wild card match
12345   }
12346   return true;
12347 }
12348 
FindMatchingRule(DR_FrameTreeNode & aNode)12349 void DR_State::FindMatchingRule(DR_FrameTreeNode& aNode) {
12350   if (!aNode.mFrame) {
12351     NS_ASSERTION(false, "invalid DR_FrameTreeNode \n");
12352     return;
12353   }
12354 
12355   bool matchingRule = false;
12356 
12357   DR_FrameTypeInfo* info = GetFrameTypeInfo(aNode.mFrame->Type());
12358   NS_ASSERTION(info, "program error");
12359   int32_t numRules = info->mRules.Length();
12360   for (int32_t ruleX = 0; ruleX < numRules; ruleX++) {
12361     DR_Rule* rule = info->mRules.ElementAt(ruleX);
12362     if (rule && RuleMatches(*rule, aNode)) {
12363       aNode.mDisplay = rule->mDisplay;
12364       matchingRule = true;
12365       break;
12366     }
12367   }
12368   if (!matchingRule) {
12369     int32_t numWildRules = mWildRules.Length();
12370     for (int32_t ruleX = 0; ruleX < numWildRules; ruleX++) {
12371       DR_Rule* rule = mWildRules.ElementAt(ruleX);
12372       if (rule && RuleMatches(*rule, aNode)) {
12373         aNode.mDisplay = rule->mDisplay;
12374         break;
12375       }
12376     }
12377   }
12378 }
12379 
CreateTreeNode(nsIFrame * aFrame,const ReflowInput * aReflowInput)12380 DR_FrameTreeNode* DR_State::CreateTreeNode(nsIFrame* aFrame,
12381                                            const ReflowInput* aReflowInput) {
12382   // find the frame of the parent reflow input (usually just the parent of
12383   // aFrame)
12384   nsIFrame* parentFrame;
12385   if (aReflowInput) {
12386     const ReflowInput* parentRI = aReflowInput->mParentReflowInput;
12387     parentFrame = (parentRI) ? parentRI->mFrame : nullptr;
12388   } else {
12389     parentFrame = aFrame->GetParent();
12390   }
12391 
12392   // find the parent tree node leaf
12393   DR_FrameTreeNode* parentNode = nullptr;
12394 
12395   DR_FrameTreeNode* lastLeaf = nullptr;
12396   if (mFrameTreeLeaves.Length())
12397     lastLeaf = mFrameTreeLeaves.ElementAt(mFrameTreeLeaves.Length() - 1);
12398   if (lastLeaf) {
12399     for (parentNode = lastLeaf;
12400          parentNode && (parentNode->mFrame != parentFrame);
12401          parentNode = parentNode->mParent) {
12402     }
12403   }
12404   DR_FrameTreeNode* newNode = new DR_FrameTreeNode(aFrame, parentNode);
12405   FindMatchingRule(*newNode);
12406 
12407   newNode->mIndent = mIndent;
12408   if (newNode->mDisplay || mIndentUndisplayedFrames) {
12409     ++mIndent;
12410   }
12411 
12412   if (lastLeaf && (lastLeaf == parentNode)) {
12413     mFrameTreeLeaves.RemoveLastElement();
12414   }
12415   mFrameTreeLeaves.AppendElement(newNode);
12416   mCount++;
12417 
12418   return newNode;
12419 }
12420 
PrettyUC(nscoord aSize,char * aBuf,int aBufSize)12421 void DR_State::PrettyUC(nscoord aSize, char* aBuf, int aBufSize) {
12422   if (NS_UNCONSTRAINEDSIZE == aSize) {
12423     strcpy(aBuf, "UC");
12424   } else {
12425     if ((nscoord)0xdeadbeefU == aSize) {
12426       strcpy(aBuf, "deadbeef");
12427     } else {
12428       snprintf(aBuf, aBufSize, "%d", aSize);
12429     }
12430   }
12431 }
12432 
PrintMargin(const char * tag,const nsMargin * aMargin)12433 void DR_State::PrintMargin(const char* tag, const nsMargin* aMargin) {
12434   if (aMargin) {
12435     char t[16], r[16], b[16], l[16];
12436     PrettyUC(aMargin->top, t, 16);
12437     PrettyUC(aMargin->right, r, 16);
12438     PrettyUC(aMargin->bottom, b, 16);
12439     PrettyUC(aMargin->left, l, 16);
12440     printf(" %s=%s,%s,%s,%s", tag, t, r, b, l);
12441   } else {
12442     // use %p here for consistency with other null-pointer printouts
12443     printf(" %s=%p", tag, (void*)aMargin);
12444   }
12445 }
12446 
DeleteTreeNode(DR_FrameTreeNode & aNode)12447 void DR_State::DeleteTreeNode(DR_FrameTreeNode& aNode) {
12448   mFrameTreeLeaves.RemoveElement(&aNode);
12449   int32_t numLeaves = mFrameTreeLeaves.Length();
12450   if ((0 == numLeaves) ||
12451       (aNode.mParent != mFrameTreeLeaves.ElementAt(numLeaves - 1))) {
12452     mFrameTreeLeaves.AppendElement(aNode.mParent);
12453   }
12454 
12455   if (aNode.mDisplay || mIndentUndisplayedFrames) {
12456     --mIndent;
12457   }
12458   // delete the tree node
12459   delete &aNode;
12460 }
12461 
CheckPixelError(nscoord aSize,int32_t aPixelToTwips)12462 static void CheckPixelError(nscoord aSize, int32_t aPixelToTwips) {
12463   if (NS_UNCONSTRAINEDSIZE != aSize) {
12464     if ((aSize % aPixelToTwips) > 0) {
12465       printf("VALUE %d is not a whole pixel \n", aSize);
12466     }
12467   }
12468 }
12469 
DisplayReflowEnterPrint(nsPresContext * aPresContext,nsIFrame * aFrame,const ReflowInput & aReflowInput,DR_FrameTreeNode & aTreeNode,bool aChanged)12470 static void DisplayReflowEnterPrint(nsPresContext* aPresContext,
12471                                     nsIFrame* aFrame,
12472                                     const ReflowInput& aReflowInput,
12473                                     DR_FrameTreeNode& aTreeNode,
12474                                     bool aChanged) {
12475   if (aTreeNode.mDisplay) {
12476     DR_state->DisplayFrameTypeInfo(aFrame, aTreeNode.mIndent);
12477 
12478     char width[16];
12479     char height[16];
12480 
12481     DR_state->PrettyUC(aReflowInput.AvailableWidth(), width, 16);
12482     DR_state->PrettyUC(aReflowInput.AvailableHeight(), height, 16);
12483     printf("Reflow a=%s,%s ", width, height);
12484 
12485     DR_state->PrettyUC(aReflowInput.ComputedWidth(), width, 16);
12486     DR_state->PrettyUC(aReflowInput.ComputedHeight(), height, 16);
12487     printf("c=%s,%s ", width, height);
12488 
12489     if (aFrame->HasAnyStateBits(NS_FRAME_IS_DIRTY)) printf("dirty ");
12490 
12491     if (aFrame->HasAnyStateBits(NS_FRAME_HAS_DIRTY_CHILDREN))
12492       printf("dirty-children ");
12493 
12494     if (aReflowInput.mFlags.mSpecialBSizeReflow) printf("special-bsize ");
12495 
12496     if (aReflowInput.IsHResize()) printf("h-resize ");
12497 
12498     if (aReflowInput.IsVResize()) printf("v-resize ");
12499 
12500     nsIFrame* inFlow = aFrame->GetPrevInFlow();
12501     if (inFlow) {
12502       printf("pif=%p ", (void*)inFlow);
12503     }
12504     inFlow = aFrame->GetNextInFlow();
12505     if (inFlow) {
12506       printf("nif=%p ", (void*)inFlow);
12507     }
12508     if (aChanged)
12509       printf("CHANGED \n");
12510     else
12511       printf("cnt=%d \n", DR_state->mCount);
12512     if (DR_state->mDisplayPixelErrors) {
12513       int32_t d2a = aPresContext->AppUnitsPerDevPixel();
12514       CheckPixelError(aReflowInput.AvailableWidth(), d2a);
12515       CheckPixelError(aReflowInput.AvailableHeight(), d2a);
12516       CheckPixelError(aReflowInput.ComputedWidth(), d2a);
12517       CheckPixelError(aReflowInput.ComputedHeight(), d2a);
12518     }
12519   }
12520 }
12521 
DisplayReflowEnter(nsPresContext * aPresContext,nsIFrame * aFrame,const ReflowInput & aReflowInput)12522 void* nsIFrame::DisplayReflowEnter(nsPresContext* aPresContext,
12523                                    nsIFrame* aFrame,
12524                                    const ReflowInput& aReflowInput) {
12525   if (!DR_state->mInited) DR_state->Init();
12526   if (!DR_state->mActive) return nullptr;
12527 
12528   NS_ASSERTION(aFrame, "invalid call");
12529 
12530   DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, &aReflowInput);
12531   if (treeNode) {
12532     DisplayReflowEnterPrint(aPresContext, aFrame, aReflowInput, *treeNode,
12533                             false);
12534   }
12535   return treeNode;
12536 }
12537 
DisplayLayoutEnter(nsIFrame * aFrame)12538 void* nsIFrame::DisplayLayoutEnter(nsIFrame* aFrame) {
12539   if (!DR_state->mInited) DR_state->Init();
12540   if (!DR_state->mActive) return nullptr;
12541 
12542   NS_ASSERTION(aFrame, "invalid call");
12543 
12544   DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr);
12545   if (treeNode && treeNode->mDisplay) {
12546     DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12547     printf("XULLayout\n");
12548   }
12549   return treeNode;
12550 }
12551 
DisplayIntrinsicISizeEnter(nsIFrame * aFrame,const char * aType)12552 void* nsIFrame::DisplayIntrinsicISizeEnter(nsIFrame* aFrame,
12553                                            const char* aType) {
12554   if (!DR_state->mInited) DR_state->Init();
12555   if (!DR_state->mActive) return nullptr;
12556 
12557   NS_ASSERTION(aFrame, "invalid call");
12558 
12559   DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr);
12560   if (treeNode && treeNode->mDisplay) {
12561     DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12562     printf("Get%sISize\n", aType);
12563   }
12564   return treeNode;
12565 }
12566 
DisplayIntrinsicSizeEnter(nsIFrame * aFrame,const char * aType)12567 void* nsIFrame::DisplayIntrinsicSizeEnter(nsIFrame* aFrame, const char* aType) {
12568   if (!DR_state->mInited) DR_state->Init();
12569   if (!DR_state->mActive) return nullptr;
12570 
12571   NS_ASSERTION(aFrame, "invalid call");
12572 
12573   DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr);
12574   if (treeNode && treeNode->mDisplay) {
12575     DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12576     printf("Get%sSize\n", aType);
12577   }
12578   return treeNode;
12579 }
12580 
DisplayReflowExit(nsPresContext * aPresContext,nsIFrame * aFrame,ReflowOutput & aMetrics,const nsReflowStatus & aStatus,void * aFrameTreeNode)12581 void nsIFrame::DisplayReflowExit(nsPresContext* aPresContext, nsIFrame* aFrame,
12582                                  ReflowOutput& aMetrics,
12583                                  const nsReflowStatus& aStatus,
12584                                  void* aFrameTreeNode) {
12585   if (!DR_state->mActive) return;
12586 
12587   NS_ASSERTION(aFrame, "DisplayReflowExit - invalid call");
12588   if (!aFrameTreeNode) return;
12589 
12590   DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
12591   if (treeNode->mDisplay) {
12592     DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12593 
12594     char width[16];
12595     char height[16];
12596     char x[16];
12597     char y[16];
12598     DR_state->PrettyUC(aMetrics.Width(), width, 16);
12599     DR_state->PrettyUC(aMetrics.Height(), height, 16);
12600     printf("Reflow d=%s,%s", width, height);
12601 
12602     if (!aStatus.IsEmpty()) {
12603       printf(" status=%s", ToString(aStatus).c_str());
12604     }
12605     if (aFrame->HasOverflowAreas()) {
12606       DR_state->PrettyUC(aMetrics.InkOverflow().x, x, 16);
12607       DR_state->PrettyUC(aMetrics.InkOverflow().y, y, 16);
12608       DR_state->PrettyUC(aMetrics.InkOverflow().width, width, 16);
12609       DR_state->PrettyUC(aMetrics.InkOverflow().height, height, 16);
12610       printf(" vis-o=(%s,%s) %s x %s", x, y, width, height);
12611 
12612       nsRect storedOverflow = aFrame->InkOverflowRect();
12613       DR_state->PrettyUC(storedOverflow.x, x, 16);
12614       DR_state->PrettyUC(storedOverflow.y, y, 16);
12615       DR_state->PrettyUC(storedOverflow.width, width, 16);
12616       DR_state->PrettyUC(storedOverflow.height, height, 16);
12617       printf(" vis-sto=(%s,%s) %s x %s", x, y, width, height);
12618 
12619       DR_state->PrettyUC(aMetrics.ScrollableOverflow().x, x, 16);
12620       DR_state->PrettyUC(aMetrics.ScrollableOverflow().y, y, 16);
12621       DR_state->PrettyUC(aMetrics.ScrollableOverflow().width, width, 16);
12622       DR_state->PrettyUC(aMetrics.ScrollableOverflow().height, height, 16);
12623       printf(" scr-o=(%s,%s) %s x %s", x, y, width, height);
12624 
12625       storedOverflow = aFrame->ScrollableOverflowRect();
12626       DR_state->PrettyUC(storedOverflow.x, x, 16);
12627       DR_state->PrettyUC(storedOverflow.y, y, 16);
12628       DR_state->PrettyUC(storedOverflow.width, width, 16);
12629       DR_state->PrettyUC(storedOverflow.height, height, 16);
12630       printf(" scr-sto=(%s,%s) %s x %s", x, y, width, height);
12631     }
12632     printf("\n");
12633     if (DR_state->mDisplayPixelErrors) {
12634       int32_t d2a = aPresContext->AppUnitsPerDevPixel();
12635       CheckPixelError(aMetrics.Width(), d2a);
12636       CheckPixelError(aMetrics.Height(), d2a);
12637     }
12638   }
12639   DR_state->DeleteTreeNode(*treeNode);
12640 }
12641 
DisplayLayoutExit(nsIFrame * aFrame,void * aFrameTreeNode)12642 void nsIFrame::DisplayLayoutExit(nsIFrame* aFrame, void* aFrameTreeNode) {
12643   if (!DR_state->mActive) return;
12644 
12645   NS_ASSERTION(aFrame, "non-null frame required");
12646   if (!aFrameTreeNode) return;
12647 
12648   DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
12649   if (treeNode->mDisplay) {
12650     DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12651     nsRect rect = aFrame->GetRect();
12652     printf("XULLayout=%d,%d,%d,%d\n", rect.x, rect.y, rect.width, rect.height);
12653   }
12654   DR_state->DeleteTreeNode(*treeNode);
12655 }
12656 
DisplayIntrinsicISizeExit(nsIFrame * aFrame,const char * aType,nscoord aResult,void * aFrameTreeNode)12657 void nsIFrame::DisplayIntrinsicISizeExit(nsIFrame* aFrame, const char* aType,
12658                                          nscoord aResult,
12659                                          void* aFrameTreeNode) {
12660   if (!DR_state->mActive) return;
12661 
12662   NS_ASSERTION(aFrame, "non-null frame required");
12663   if (!aFrameTreeNode) return;
12664 
12665   DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
12666   if (treeNode->mDisplay) {
12667     DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12668     char iSize[16];
12669     DR_state->PrettyUC(aResult, iSize, 16);
12670     printf("Get%sISize=%s\n", aType, iSize);
12671   }
12672   DR_state->DeleteTreeNode(*treeNode);
12673 }
12674 
DisplayIntrinsicSizeExit(nsIFrame * aFrame,const char * aType,nsSize aResult,void * aFrameTreeNode)12675 void nsIFrame::DisplayIntrinsicSizeExit(nsIFrame* aFrame, const char* aType,
12676                                         nsSize aResult, void* aFrameTreeNode) {
12677   if (!DR_state->mActive) return;
12678 
12679   NS_ASSERTION(aFrame, "non-null frame required");
12680   if (!aFrameTreeNode) return;
12681 
12682   DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
12683   if (treeNode->mDisplay) {
12684     DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12685 
12686     char width[16];
12687     char height[16];
12688     DR_state->PrettyUC(aResult.width, width, 16);
12689     DR_state->PrettyUC(aResult.height, height, 16);
12690     printf("Get%sSize=%s,%s\n", aType, width, height);
12691   }
12692   DR_state->DeleteTreeNode(*treeNode);
12693 }
12694 
12695 /* static */
DisplayReflowStartup()12696 void nsIFrame::DisplayReflowStartup() { DR_state = new DR_State(); }
12697 
12698 /* static */
DisplayReflowShutdown()12699 void nsIFrame::DisplayReflowShutdown() {
12700   delete DR_state;
12701   DR_state = nullptr;
12702 }
12703 
Change() const12704 void DR_cookie::Change() const {
12705   DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)mValue;
12706   if (treeNode && treeNode->mDisplay) {
12707     DisplayReflowEnterPrint(mPresContext, mFrame, mReflowInput, *treeNode,
12708                             true);
12709   }
12710 }
12711 
12712 /* static */
DisplayInitConstraintsEnter(nsIFrame * aFrame,ReflowInput * aState,nscoord aContainingBlockWidth,nscoord aContainingBlockHeight,const nsMargin * aBorder,const nsMargin * aPadding)12713 void* ReflowInput::DisplayInitConstraintsEnter(nsIFrame* aFrame,
12714                                                ReflowInput* aState,
12715                                                nscoord aContainingBlockWidth,
12716                                                nscoord aContainingBlockHeight,
12717                                                const nsMargin* aBorder,
12718                                                const nsMargin* aPadding) {
12719   MOZ_ASSERT(aFrame, "non-null frame required");
12720   MOZ_ASSERT(aState, "non-null state required");
12721 
12722   if (!DR_state->mInited) DR_state->Init();
12723   if (!DR_state->mActive) return nullptr;
12724 
12725   DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, aState);
12726   if (treeNode && treeNode->mDisplay) {
12727     DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12728 
12729     printf("InitConstraints parent=%p", (void*)aState->mParentReflowInput);
12730 
12731     char width[16];
12732     char height[16];
12733 
12734     DR_state->PrettyUC(aContainingBlockWidth, width, 16);
12735     DR_state->PrettyUC(aContainingBlockHeight, height, 16);
12736     printf(" cb=%s,%s", width, height);
12737 
12738     DR_state->PrettyUC(aState->AvailableWidth(), width, 16);
12739     DR_state->PrettyUC(aState->AvailableHeight(), height, 16);
12740     printf(" as=%s,%s", width, height);
12741 
12742     DR_state->PrintMargin("b", aBorder);
12743     DR_state->PrintMargin("p", aPadding);
12744     putchar('\n');
12745   }
12746   return treeNode;
12747 }
12748 
12749 /* static */
DisplayInitConstraintsExit(nsIFrame * aFrame,ReflowInput * aState,void * aValue)12750 void ReflowInput::DisplayInitConstraintsExit(nsIFrame* aFrame,
12751                                              ReflowInput* aState,
12752                                              void* aValue) {
12753   MOZ_ASSERT(aFrame, "non-null frame required");
12754   MOZ_ASSERT(aState, "non-null state required");
12755 
12756   if (!DR_state->mActive) return;
12757   if (!aValue) return;
12758 
12759   DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue;
12760   if (treeNode->mDisplay) {
12761     DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12762     char cmiw[16], cw[16], cmxw[16], cmih[16], ch[16], cmxh[16];
12763     DR_state->PrettyUC(aState->ComputedMinWidth(), cmiw, 16);
12764     DR_state->PrettyUC(aState->ComputedWidth(), cw, 16);
12765     DR_state->PrettyUC(aState->ComputedMaxWidth(), cmxw, 16);
12766     DR_state->PrettyUC(aState->ComputedMinHeight(), cmih, 16);
12767     DR_state->PrettyUC(aState->ComputedHeight(), ch, 16);
12768     DR_state->PrettyUC(aState->ComputedMaxHeight(), cmxh, 16);
12769     printf("InitConstraints= cw=(%s <= %s <= %s) ch=(%s <= %s <= %s)", cmiw, cw,
12770            cmxw, cmih, ch, cmxh);
12771     const nsMargin m = aState->ComputedPhysicalOffsets();
12772     DR_state->PrintMargin("co", &m);
12773     putchar('\n');
12774   }
12775   DR_state->DeleteTreeNode(*treeNode);
12776 }
12777 
12778 /* static */
DisplayInitOffsetsEnter(nsIFrame * aFrame,SizeComputationInput * aState,nscoord aPercentBasis,WritingMode aCBWritingMode,const nsMargin * aBorder,const nsMargin * aPadding)12779 void* SizeComputationInput::DisplayInitOffsetsEnter(
12780     nsIFrame* aFrame, SizeComputationInput* aState, nscoord aPercentBasis,
12781     WritingMode aCBWritingMode, const nsMargin* aBorder,
12782     const nsMargin* aPadding) {
12783   MOZ_ASSERT(aFrame, "non-null frame required");
12784   MOZ_ASSERT(aState, "non-null state required");
12785 
12786   if (!DR_state->mInited) DR_state->Init();
12787   if (!DR_state->mActive) return nullptr;
12788 
12789   // aState is not necessarily a ReflowInput
12790   DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr);
12791   if (treeNode && treeNode->mDisplay) {
12792     DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12793 
12794     char pctBasisStr[16];
12795     DR_state->PrettyUC(aPercentBasis, pctBasisStr, 16);
12796     printf("InitOffsets pct_basis=%s", pctBasisStr);
12797 
12798     DR_state->PrintMargin("b", aBorder);
12799     DR_state->PrintMargin("p", aPadding);
12800     putchar('\n');
12801   }
12802   return treeNode;
12803 }
12804 
12805 /* static */
DisplayInitOffsetsExit(nsIFrame * aFrame,SizeComputationInput * aState,void * aValue)12806 void SizeComputationInput::DisplayInitOffsetsExit(nsIFrame* aFrame,
12807                                                   SizeComputationInput* aState,
12808                                                   void* aValue) {
12809   MOZ_ASSERT(aFrame, "non-null frame required");
12810   MOZ_ASSERT(aState, "non-null state required");
12811 
12812   if (!DR_state->mActive) return;
12813   if (!aValue) return;
12814 
12815   DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue;
12816   if (treeNode->mDisplay) {
12817     DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12818     printf("InitOffsets=");
12819     const auto m = aState->ComputedPhysicalMargin();
12820     DR_state->PrintMargin("m", &m);
12821     const auto p = aState->ComputedPhysicalPadding();
12822     DR_state->PrintMargin("p", &p);
12823     const auto bp = aState->ComputedPhysicalBorderPadding();
12824     DR_state->PrintMargin("b+p", &bp);
12825     putchar('\n');
12826   }
12827   DR_state->DeleteTreeNode(*treeNode);
12828 }
12829 
12830 // End Display Reflow
12831 
12832 // Validation of SideIsVertical.
12833 #  define CASE(side, result) \
12834     static_assert(SideIsVertical(side) == result, "SideIsVertical is wrong")
12835 CASE(eSideTop, false);
12836 CASE(eSideRight, true);
12837 CASE(eSideBottom, false);
12838 CASE(eSideLeft, true);
12839 #  undef CASE
12840 
12841 // Validation of HalfCornerIsX.
12842 #  define CASE(corner, result) \
12843     static_assert(HalfCornerIsX(corner) == result, "HalfCornerIsX is wrong")
12844 CASE(eCornerTopLeftX, true);
12845 CASE(eCornerTopLeftY, false);
12846 CASE(eCornerTopRightX, true);
12847 CASE(eCornerTopRightY, false);
12848 CASE(eCornerBottomRightX, true);
12849 CASE(eCornerBottomRightY, false);
12850 CASE(eCornerBottomLeftX, true);
12851 CASE(eCornerBottomLeftY, false);
12852 #  undef CASE
12853 
12854 // Validation of HalfToFullCorner.
12855 #  define CASE(corner, result)                        \
12856     static_assert(HalfToFullCorner(corner) == result, \
12857                   "HalfToFullCorner is "              \
12858                   "wrong")
12859 CASE(eCornerTopLeftX, eCornerTopLeft);
12860 CASE(eCornerTopLeftY, eCornerTopLeft);
12861 CASE(eCornerTopRightX, eCornerTopRight);
12862 CASE(eCornerTopRightY, eCornerTopRight);
12863 CASE(eCornerBottomRightX, eCornerBottomRight);
12864 CASE(eCornerBottomRightY, eCornerBottomRight);
12865 CASE(eCornerBottomLeftX, eCornerBottomLeft);
12866 CASE(eCornerBottomLeftY, eCornerBottomLeft);
12867 #  undef CASE
12868 
12869 // Validation of FullToHalfCorner.
12870 #  define CASE(corner, vert, result)                        \
12871     static_assert(FullToHalfCorner(corner, vert) == result, \
12872                   "FullToHalfCorner is wrong")
12873 CASE(eCornerTopLeft, false, eCornerTopLeftX);
12874 CASE(eCornerTopLeft, true, eCornerTopLeftY);
12875 CASE(eCornerTopRight, false, eCornerTopRightX);
12876 CASE(eCornerTopRight, true, eCornerTopRightY);
12877 CASE(eCornerBottomRight, false, eCornerBottomRightX);
12878 CASE(eCornerBottomRight, true, eCornerBottomRightY);
12879 CASE(eCornerBottomLeft, false, eCornerBottomLeftX);
12880 CASE(eCornerBottomLeft, true, eCornerBottomLeftY);
12881 #  undef CASE
12882 
12883 // Validation of SideToFullCorner.
12884 #  define CASE(side, second, result)                        \
12885     static_assert(SideToFullCorner(side, second) == result, \
12886                   "SideToFullCorner is wrong")
12887 CASE(eSideTop, false, eCornerTopLeft);
12888 CASE(eSideTop, true, eCornerTopRight);
12889 
12890 CASE(eSideRight, false, eCornerTopRight);
12891 CASE(eSideRight, true, eCornerBottomRight);
12892 
12893 CASE(eSideBottom, false, eCornerBottomRight);
12894 CASE(eSideBottom, true, eCornerBottomLeft);
12895 
12896 CASE(eSideLeft, false, eCornerBottomLeft);
12897 CASE(eSideLeft, true, eCornerTopLeft);
12898 #  undef CASE
12899 
12900 // Validation of SideToHalfCorner.
12901 #  define CASE(side, second, parallel, result)                        \
12902     static_assert(SideToHalfCorner(side, second, parallel) == result, \
12903                   "SideToHalfCorner is wrong")
12904 CASE(eSideTop, false, true, eCornerTopLeftX);
12905 CASE(eSideTop, false, false, eCornerTopLeftY);
12906 CASE(eSideTop, true, true, eCornerTopRightX);
12907 CASE(eSideTop, true, false, eCornerTopRightY);
12908 
12909 CASE(eSideRight, false, false, eCornerTopRightX);
12910 CASE(eSideRight, false, true, eCornerTopRightY);
12911 CASE(eSideRight, true, false, eCornerBottomRightX);
12912 CASE(eSideRight, true, true, eCornerBottomRightY);
12913 
12914 CASE(eSideBottom, false, true, eCornerBottomRightX);
12915 CASE(eSideBottom, false, false, eCornerBottomRightY);
12916 CASE(eSideBottom, true, true, eCornerBottomLeftX);
12917 CASE(eSideBottom, true, false, eCornerBottomLeftY);
12918 
12919 CASE(eSideLeft, false, false, eCornerBottomLeftX);
12920 CASE(eSideLeft, false, true, eCornerBottomLeftY);
12921 CASE(eSideLeft, true, false, eCornerTopLeftX);
12922 CASE(eSideLeft, true, true, eCornerTopLeftY);
12923 #  undef CASE
12924 
12925 #endif
12926