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 "nsFrame.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/dom/ElementInlines.h"
20 #include "mozilla/dom/ImageTracker.h"
21 #include "mozilla/dom/Selection.h"
22 #include "mozilla/gfx/2D.h"
23 #include "mozilla/gfx/gfxVars.h"
24 #include "mozilla/gfx/PathHelpers.h"
25 #include "mozilla/PresShell.h"
26 #include "mozilla/PresShellInlines.h"
27 #include "mozilla/Sprintf.h"
28 #include "mozilla/StaticPrefs_layout.h"
29 #include "mozilla/ToString.h"
30 #include "mozilla/ViewportUtils.h"
31 
32 #include "nsCOMPtr.h"
33 #include "nsFlexContainerFrame.h"
34 #include "nsFrameList.h"
35 #include "nsPlaceholderFrame.h"
36 #include "nsPluginFrame.h"
37 #include "nsIBaseWindow.h"
38 #include "nsIContent.h"
39 #include "nsIContentInlines.h"
40 #include "nsContentUtils.h"
41 #include "nsCSSFrameConstructor.h"
42 #include "nsCSSProps.h"
43 #include "nsCSSPseudoElements.h"
44 #include "nsCSSRendering.h"
45 #include "nsAtom.h"
46 #include "nsString.h"
47 #include "nsReadableUtils.h"
48 #include "nsTableWrapperFrame.h"
49 #include "nsView.h"
50 #include "nsViewManager.h"
51 #include "nsIScrollableFrame.h"
52 #include "nsPresContext.h"
53 #include "nsPresContextInlines.h"
54 #include "nsStyleConsts.h"
55 #include "mozilla/Logging.h"
56 #include "nsLayoutUtils.h"
57 #include "LayoutLogging.h"
58 #include "mozilla/RestyleManager.h"
59 #include "nsImageFrame.h"
60 #include "nsInlineFrame.h"
61 #include "nsFrameSelection.h"
62 #include "nsGkAtoms.h"
63 #include "nsCSSAnonBoxes.h"
64 #include "nsCSSClipPathInstance.h"
65 #include "nsCanvasFrame.h"
66 
67 #include "nsFrameTraversal.h"
68 #include "nsRange.h"
69 #include "nsITextControlFrame.h"
70 #include "nsNameSpaceManager.h"
71 #include "nsIPercentBSizeObserver.h"
72 #include "nsStyleStructInlines.h"
73 #include "FrameLayerBuilder.h"
74 #include "ImageLayers.h"
75 
76 #include "nsBidiPresUtils.h"
77 #include "RubyUtils.h"
78 #include "TextOverflow.h"
79 #include "nsAnimationManager.h"
80 
81 // For triple-click pref
82 #include "imgIRequest.h"
83 #include "nsError.h"
84 #include "nsContainerFrame.h"
85 #include "nsBoxLayoutState.h"
86 #include "nsBlockFrame.h"
87 #include "nsDisplayList.h"
88 #include "nsSVGIntegrationUtils.h"
89 #include "SVGObserverUtils.h"
90 #include "nsSVGMaskFrame.h"
91 #include "nsChangeHint.h"
92 #include "nsDeckFrame.h"
93 #include "nsSubDocumentFrame.h"
94 #include "SVGTextFrame.h"
95 #include "RetainedDisplayListBuilder.h"
96 
97 #include "gfxContext.h"
98 #include "nsAbsoluteContainingBlock.h"
99 #include "StickyScrollContainer.h"
100 #include "nsFontInflationData.h"
101 #include "nsRegion.h"
102 #include "nsIFrameInlines.h"
103 #include "nsStyleChangeList.h"
104 #include "nsWindowSizes.h"
105 
106 #include "mozilla/AsyncEventDispatcher.h"
107 #include "mozilla/EffectCompositor.h"
108 #include "mozilla/EffectSet.h"
109 #include "mozilla/EventListenerManager.h"
110 #include "mozilla/EventStateManager.h"
111 #include "mozilla/EventStates.h"
112 #include "mozilla/Preferences.h"
113 #include "mozilla/LookAndFeel.h"
114 #include "mozilla/MouseEvents.h"
115 #include "mozilla/ServoStyleSet.h"
116 #include "mozilla/ServoStyleSetInlines.h"
117 #include "mozilla/css/ImageLoader.h"
118 #include "mozilla/dom/HTMLBodyElement.h"
119 #include "mozilla/dom/SVGPathData.h"
120 #include "mozilla/dom/TouchEvent.h"
121 #include "mozilla/gfx/Tools.h"
122 #include "mozilla/layers/WebRenderUserData.h"
123 #include "mozilla/layout/ScrollAnchorContainer.h"
124 #include "nsPrintfCString.h"
125 #include "ActiveLayerTracker.h"
126 
127 #include "nsITheme.h"
128 
129 using namespace mozilla;
130 using namespace mozilla::css;
131 using namespace mozilla::dom;
132 using namespace mozilla::gfx;
133 using namespace mozilla::layers;
134 using namespace mozilla::layout;
135 typedef nsAbsoluteContainingBlock::AbsPosReflowFlags AbsPosReflowFlags;
136 using nsStyleTransformMatrix::TransformReferenceBox;
137 
138 const mozilla::LayoutFrameType nsIFrame::sLayoutFrameTypes[
139 #define FRAME_ID(...) 1 +
140 #define ABSTRACT_FRAME_ID(...)
141 #include "mozilla/FrameIdList.h"
142 #undef FRAME_ID
143 #undef ABSTRACT_FRAME_ID
144     0] = {
145 #define FRAME_ID(class_, type_, ...) mozilla::LayoutFrameType::type_,
146 #define ABSTRACT_FRAME_ID(...)
147 #include "mozilla/FrameIdList.h"
148 #undef FRAME_ID
149 #undef ABSTRACT_FRAME_ID
150 };
151 
152 const nsIFrame::FrameClassBits nsIFrame::sFrameClassBits[
153 #define FRAME_ID(...) 1 +
154 #define ABSTRACT_FRAME_ID(...)
155 #include "mozilla/FrameIdList.h"
156 #undef FRAME_ID
157 #undef ABSTRACT_FRAME_ID
158     0] = {
159 #define Leaf eFrameClassBitsLeaf
160 #define NotLeaf eFrameClassBitsNone
161 #define DynamicLeaf eFrameClassBitsDynamicLeaf
162 #define FRAME_ID(class_, type_, leaf_, ...) leaf_,
163 #define ABSTRACT_FRAME_ID(...)
164 #include "mozilla/FrameIdList.h"
165 #undef Leaf
166 #undef NotLeaf
167 #undef DynamicLeaf
168 #undef FRAME_ID
169 #undef ABSTRACT_FRAME_ID
170 };
171 
172 // Struct containing cached metrics for box-wrapped frames.
173 struct nsBoxLayoutMetrics {
174   nsSize mPrefSize;
175   nsSize mMinSize;
176   nsSize mMaxSize;
177 
178   nsSize mBlockMinSize;
179   nsSize mBlockPrefSize;
180   nscoord mBlockAscent;
181 
182   nscoord mFlex;
183   nscoord mAscent;
184 
185   nsSize mLastSize;
186 };
187 
188 struct nsContentAndOffset {
189   nsIContent* mContent = nullptr;
190   int32_t mOffset = 0;
191 };
192 
193 // Some Misc #defines
194 #define SELECTION_DEBUG 0
195 #define FORCE_SELECTION_UPDATE 1
196 #define CALC_DEBUG 0
197 
198 // This is faster than nsBidiPresUtils::IsFrameInParagraphDirection,
199 // because it uses the frame pointer passed in without drilling down to
200 // the leaf frame.
IsReversedDirectionFrame(nsIFrame * aFrame)201 static bool IsReversedDirectionFrame(nsIFrame* aFrame) {
202   FrameBidiData bidiData = aFrame->GetBidiData();
203   return !IS_SAME_DIRECTION(bidiData.embeddingLevel, bidiData.baseLevel);
204 }
205 
206 #include "nsILineIterator.h"
207 #include "prenv.h"
208 
NS_DECLARE_FRAME_PROPERTY_DELETABLE(BoxMetricsProperty,nsBoxLayoutMetrics)209 NS_DECLARE_FRAME_PROPERTY_DELETABLE(BoxMetricsProperty, nsBoxLayoutMetrics)
210 
211 static void InitBoxMetrics(nsIFrame* aFrame, bool aClear) {
212   if (aClear) {
213     aFrame->RemoveProperty(BoxMetricsProperty());
214   }
215 
216   nsBoxLayoutMetrics* metrics = new nsBoxLayoutMetrics();
217   aFrame->SetProperty(BoxMetricsProperty(), metrics);
218 
219   static_cast<nsFrame*>(aFrame)->nsFrame::MarkIntrinsicISizesDirty();
220   metrics->mBlockAscent = 0;
221   metrics->mLastSize.SizeTo(0, 0);
222 }
223 
224 // Utility function to set a nsRect-valued property table entry on aFrame,
225 // reusing the existing storage if the property happens to be already set.
226 template <typename T>
SetOrUpdateRectValuedProperty(nsIFrame * aFrame,FrameProperties::Descriptor<T> aProperty,const nsRect & aNewValue)227 static void SetOrUpdateRectValuedProperty(
228     nsIFrame* aFrame, FrameProperties::Descriptor<T> aProperty,
229     const nsRect& aNewValue) {
230   bool found;
231   nsRect* rectStorage = aFrame->GetProperty(aProperty, &found);
232   if (!found) {
233     rectStorage = new nsRect(aNewValue);
234     aFrame->AddProperty(aProperty, rectStorage);
235   } else {
236     *rectStorage = aNewValue;
237   }
238 }
239 
IsXULBoxWrapped(const nsIFrame * aFrame)240 static bool IsXULBoxWrapped(const nsIFrame* aFrame) {
241   return aFrame->GetParent() && aFrame->GetParent()->IsXULBoxFrame() &&
242          !aFrame->IsXULBoxFrame();
243 }
244 
UpdateTruncated(const ReflowInput & aReflowInput,const ReflowOutput & aMetrics)245 void nsReflowStatus::UpdateTruncated(const ReflowInput& aReflowInput,
246                                      const ReflowOutput& aMetrics) {
247   const WritingMode containerWM = aMetrics.GetWritingMode();
248   if (aReflowInput.GetWritingMode().IsOrthogonalTo(containerWM)) {
249     // Orthogonal flows are always reflowed with an unconstrained dimension,
250     // so should never end up truncated (see ReflowInput::Init()).
251     mTruncated = false;
252   } else if (aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE &&
253              aReflowInput.AvailableBSize() < aMetrics.BSize(containerWM) &&
254              !aReflowInput.mFlags.mIsTopOfPage) {
255     mTruncated = true;
256   } else {
257     mTruncated = false;
258   }
259 }
260 
261 /* static */
DestroyAnonymousContent(nsPresContext * aPresContext,already_AddRefed<nsIContent> && aContent)262 void nsIFrame::DestroyAnonymousContent(
263     nsPresContext* aPresContext, already_AddRefed<nsIContent>&& aContent) {
264   if (nsCOMPtr<nsIContent> content = aContent) {
265     aPresContext->EventStateManager()->NativeAnonymousContentRemoved(content);
266     aPresContext->PresShell()->NativeAnonymousContentRemoved(content);
267     content->UnbindFromTree();
268   }
269 }
270 
271 // Formerly the nsIFrameDebug interface
272 
operator <<(std::ostream & aStream,const nsReflowStatus & aStatus)273 std::ostream& operator<<(std::ostream& aStream, const nsReflowStatus& aStatus) {
274   char complete = 'Y';
275   if (aStatus.IsIncomplete()) {
276     complete = 'N';
277   } else if (aStatus.IsOverflowIncomplete()) {
278     complete = 'O';
279   }
280 
281   char brk = 'N';
282   if (aStatus.IsInlineBreakBefore()) {
283     brk = 'B';
284   } else if (aStatus.IsInlineBreakAfter()) {
285     brk = 'A';
286   }
287 
288   aStream << "["
289           << "Complete=" << complete << ","
290           << "NIF=" << (aStatus.NextInFlowNeedsReflow() ? 'Y' : 'N') << ","
291           << "Truncated=" << (aStatus.IsTruncated() ? 'Y' : 'N') << ","
292           << "Break=" << brk << ","
293           << "FirstLetter=" << (aStatus.FirstLetterComplete() ? 'Y' : 'N')
294           << "]";
295   return aStream;
296 }
297 
298 #ifdef DEBUG
299 static bool gShowFrameBorders = false;
300 
ShowFrameBorders(bool aEnable)301 void nsFrame::ShowFrameBorders(bool aEnable) { gShowFrameBorders = aEnable; }
302 
GetShowFrameBorders()303 bool nsFrame::GetShowFrameBorders() { return gShowFrameBorders; }
304 
305 static bool gShowEventTargetFrameBorder = false;
306 
ShowEventTargetFrameBorder(bool aEnable)307 void nsFrame::ShowEventTargetFrameBorder(bool aEnable) {
308   gShowEventTargetFrameBorder = aEnable;
309 }
310 
GetShowEventTargetFrameBorder()311 bool nsFrame::GetShowEventTargetFrameBorder() {
312   return gShowEventTargetFrameBorder;
313 }
314 
315 /**
316  * Note: the log module is created during library initialization which
317  * means that you cannot perform logging before then.
318  */
319 mozilla::LazyLogModule nsFrame::sFrameLogModule("frame");
320 
321 #endif
322 
NS_DECLARE_FRAME_PROPERTY_DELETABLE(AbsoluteContainingBlockProperty,nsAbsoluteContainingBlock)323 NS_DECLARE_FRAME_PROPERTY_DELETABLE(AbsoluteContainingBlockProperty,
324                                     nsAbsoluteContainingBlock)
325 
326 bool nsIFrame::HasAbsolutelyPositionedChildren() const {
327   return IsAbsoluteContainer() &&
328          GetAbsoluteContainingBlock()->HasAbsoluteFrames();
329 }
330 
GetAbsoluteContainingBlock() const331 nsAbsoluteContainingBlock* nsIFrame::GetAbsoluteContainingBlock() const {
332   NS_ASSERTION(IsAbsoluteContainer(),
333                "The frame is not marked as an abspos container correctly");
334   nsAbsoluteContainingBlock* absCB =
335       GetProperty(AbsoluteContainingBlockProperty());
336   NS_ASSERTION(absCB,
337                "The frame is marked as an abspos container but doesn't have "
338                "the property");
339   return absCB;
340 }
341 
MarkAsAbsoluteContainingBlock()342 void nsIFrame::MarkAsAbsoluteContainingBlock() {
343   MOZ_ASSERT(GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
344   NS_ASSERTION(!GetProperty(AbsoluteContainingBlockProperty()),
345                "Already has an abs-pos containing block property?");
346   NS_ASSERTION(!HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN),
347                "Already has NS_FRAME_HAS_ABSPOS_CHILDREN state bit?");
348   AddStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN);
349   SetProperty(AbsoluteContainingBlockProperty(),
350               new nsAbsoluteContainingBlock(GetAbsoluteListID()));
351 }
352 
MarkAsNotAbsoluteContainingBlock()353 void nsIFrame::MarkAsNotAbsoluteContainingBlock() {
354   NS_ASSERTION(!HasAbsolutelyPositionedChildren(), "Think of the children!");
355   NS_ASSERTION(GetProperty(AbsoluteContainingBlockProperty()),
356                "Should have an abs-pos containing block property");
357   NS_ASSERTION(HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN),
358                "Should have NS_FRAME_HAS_ABSPOS_CHILDREN state bit");
359   MOZ_ASSERT(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN));
360   RemoveStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN);
361   RemoveProperty(AbsoluteContainingBlockProperty());
362 }
363 
CheckAndClearPaintedState()364 bool nsIFrame::CheckAndClearPaintedState() {
365   bool result = (GetStateBits() & NS_FRAME_PAINTED_THEBES);
366   RemoveStateBits(NS_FRAME_PAINTED_THEBES);
367 
368   for (const auto& childList : ChildLists()) {
369     for (nsIFrame* child : childList.mList) {
370       if (child->CheckAndClearPaintedState()) {
371         result = true;
372       }
373     }
374   }
375   return result;
376 }
377 
CheckAndClearDisplayListState()378 bool nsIFrame::CheckAndClearDisplayListState() {
379   bool result = BuiltDisplayList();
380   SetBuiltDisplayList(false);
381 
382   for (const auto& childList : ChildLists()) {
383     for (nsIFrame* child : childList.mList) {
384       if (child->CheckAndClearDisplayListState()) {
385         result = true;
386       }
387     }
388   }
389   return result;
390 }
391 
IsVisibleConsideringAncestors(uint32_t aFlags) const392 bool nsIFrame::IsVisibleConsideringAncestors(uint32_t aFlags) const {
393   if (!StyleVisibility()->IsVisible()) {
394     return false;
395   }
396 
397   if (PresShell()->IsUnderHiddenEmbedderElement()) {
398     return false;
399   }
400 
401   const nsIFrame* frame = this;
402   while (frame) {
403     nsView* view = frame->GetView();
404     if (view && view->GetVisibility() == nsViewVisibility_kHide) return false;
405 
406     nsIFrame* parent = frame->GetParent();
407     nsDeckFrame* deck = do_QueryFrame(parent);
408     if (deck) {
409       if (deck->GetSelectedBox() != frame) return false;
410     }
411 
412     if (parent) {
413       frame = parent;
414     } else {
415       parent = nsLayoutUtils::GetCrossDocParentFrame(frame);
416       if (!parent) break;
417 
418       if ((aFlags & nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) == 0 &&
419           parent->PresContext()->IsChrome() &&
420           !frame->PresContext()->IsChrome()) {
421         break;
422       }
423 
424       frame = parent;
425     }
426   }
427 
428   return true;
429 }
430 
FindCloserFrameForSelection(const nsPoint & aPoint,FrameWithDistance * aCurrentBestFrame)431 void nsIFrame::FindCloserFrameForSelection(
432     const nsPoint& aPoint, FrameWithDistance* aCurrentBestFrame) {
433   if (nsLayoutUtils::PointIsCloserToRect(aPoint, mRect,
434                                          aCurrentBestFrame->mXDistance,
435                                          aCurrentBestFrame->mYDistance)) {
436     aCurrentBestFrame->mFrame = this;
437   }
438 }
439 
ContentStatesChanged(mozilla::EventStates aStates)440 void nsIFrame::ContentStatesChanged(mozilla::EventStates aStates) {}
441 
AutoWeakFrame(const WeakFrame & aOther)442 AutoWeakFrame::AutoWeakFrame(const WeakFrame& aOther)
443     : mPrev(nullptr), mFrame(nullptr) {
444   Init(aOther.GetFrame());
445 }
446 
Init(nsIFrame * aFrame)447 void AutoWeakFrame::Init(nsIFrame* aFrame) {
448   Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
449   mFrame = aFrame;
450   if (mFrame) {
451     mozilla::PresShell* presShell = mFrame->PresContext()->GetPresShell();
452     NS_WARNING_ASSERTION(presShell, "Null PresShell in AutoWeakFrame!");
453     if (presShell) {
454       presShell->AddAutoWeakFrame(this);
455     } else {
456       mFrame = nullptr;
457     }
458   }
459 }
460 
Init(nsIFrame * aFrame)461 void WeakFrame::Init(nsIFrame* aFrame) {
462   Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
463   mFrame = aFrame;
464   if (mFrame) {
465     mozilla::PresShell* presShell = mFrame->PresContext()->GetPresShell();
466     MOZ_ASSERT(presShell, "Null PresShell in WeakFrame!");
467     if (presShell) {
468       presShell->AddWeakFrame(this);
469     } else {
470       mFrame = nullptr;
471     }
472   }
473 }
474 
NS_NewEmptyFrame(PresShell * aPresShell,ComputedStyle * aStyle)475 nsIFrame* NS_NewEmptyFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
476   return new (aPresShell) nsFrame(aStyle, aPresShell->GetPresContext());
477 }
478 
nsFrame(ComputedStyle * aStyle,nsPresContext * aPresContext,ClassID aID)479 nsFrame::nsFrame(ComputedStyle* aStyle, nsPresContext* aPresContext,
480                  ClassID aID)
481     : nsIFrame(aStyle, aPresContext, aID) {
482   MOZ_COUNT_CTOR(nsFrame);
483 }
484 
~nsFrame()485 nsFrame::~nsFrame() {
486   MOZ_COUNT_DTOR(nsFrame);
487 
488   MOZ_ASSERT(GetVisibility() != Visibility::ApproximatelyVisible,
489              "Visible nsFrame is being destroyed");
490 }
491 
NS_IMPL_FRAMEARENA_HELPERS(nsFrame)492 NS_IMPL_FRAMEARENA_HELPERS(nsFrame)
493 
494 // Dummy operator delete.  Will never be called, but must be defined
495 // to satisfy some C++ ABIs.
496 void nsFrame::operator delete(void*, size_t) {
497   MOZ_CRASH("nsFrame::operator delete should never be called");
498 }
499 
500 NS_QUERYFRAME_HEAD(nsFrame)
NS_QUERYFRAME_ENTRY(nsIFrame)501   NS_QUERYFRAME_ENTRY(nsIFrame)
502 NS_QUERYFRAME_TAIL_INHERITANCE_ROOT
503 
504 /////////////////////////////////////////////////////////////////////////////
505 // nsIFrame
506 
507 static bool IsFontSizeInflationContainer(nsIFrame* aFrame,
508                                          const nsStyleDisplay* aStyleDisplay) {
509   /*
510    * Font size inflation is built around the idea that we're inflating
511    * the fonts for a pan-and-zoom UI so that when the user scales up a
512    * block or other container to fill the width of the device, the fonts
513    * will be readable.  To do this, we need to pick what counts as a
514    * container.
515    *
516    * From a code perspective, the only hard requirement is that frames
517    * that are line participants
518    * (nsIFrame::IsFrameOfType(nsIFrame::eLineParticipant)) are never
519    * containers, since line layout assumes that the inflation is
520    * consistent within a line.
521    *
522    * This is not an imposition, since we obviously want a bunch of text
523    * (possibly with inline elements) flowing within a block to count the
524    * block (or higher) as its container.
525    *
526    * We also want form controls, including the text in the anonymous
527    * content inside of them, to match each other and the text next to
528    * them, so they and their anonymous content should also not be a
529    * container.
530    *
531    * However, because we can't reliably compute sizes across XUL during
532    * reflow, any XUL frame with a XUL parent is always a container.
533    *
534    * There are contexts where it would be nice if some blocks didn't
535    * count as a container, so that, for example, an indented quotation
536    * didn't end up with a smaller font size.  However, it's hard to
537    * distinguish these situations where we really do want the indented
538    * thing to count as a container, so we don't try, and blocks are
539    * always containers.
540    */
541 
542   // The root frame should always be an inflation container.
543   if (!aFrame->GetParent()) {
544     return true;
545   }
546 
547   nsIContent* content = aFrame->GetContent();
548   if (content && content->IsInNativeAnonymousSubtree()) {
549     // Native anonymous content shouldn't be a font inflation root,
550     // except for the canvas custom content container.
551     nsCanvasFrame* canvas = aFrame->PresShell()->GetCanvasFrame();
552     return canvas && canvas->GetCustomContentContainer() == content;
553   }
554 
555   LayoutFrameType frameType = aFrame->Type();
556   bool isInline =
557       (nsStyleDisplay::IsInlineFlow(aFrame->GetDisplay()) ||
558        RubyUtils::IsRubyBox(frameType) ||
559        (aFrame->IsFloating() && frameType == LayoutFrameType::Letter) ||
560        // Given multiple frames for the same node, only the
561        // outer one should be considered a container.
562        // (Important, e.g., for nsSelectsAreaFrame.)
563        (aFrame->GetParent()->GetContent() == content) ||
564        (content &&
565         // Form controls shouldn't become inflation containers.
566         (content->IsAnyOfHTMLElements(nsGkAtoms::option, nsGkAtoms::optgroup,
567                                       nsGkAtoms::select, nsGkAtoms::input,
568                                       nsGkAtoms::button)))) &&
569       !(aFrame->IsXULBoxFrame() && aFrame->GetParent()->IsXULBoxFrame());
570   NS_ASSERTION(!aFrame->IsFrameOfType(nsIFrame::eLineParticipant) || isInline ||
571                    // br frames and mathml frames report being line
572                    // participants even when their position or display is
573                    // set
574                    aFrame->IsBrFrame() ||
575                    aFrame->IsFrameOfType(nsIFrame::eMathML),
576                "line participants must not be containers");
577   NS_ASSERTION(!aFrame->IsBulletFrame() || isInline,
578                "bullets should not be containers");
579   return !isInline;
580 }
581 
MaybeScheduleReflowSVGNonDisplayText(nsIFrame * aFrame)582 static void MaybeScheduleReflowSVGNonDisplayText(nsIFrame* aFrame) {
583   if (!nsSVGUtils::IsInSVGTextSubtree(aFrame)) {
584     return;
585   }
586 
587   // We need to ensure that any non-display SVGTextFrames get reflowed when a
588   // child text frame gets new style. Thus we need to schedule a reflow in
589   // |DidSetComputedStyle|. We also need to call it from |DestroyFrom|,
590   // because otherwise we won't get notified when style changes to
591   // "display:none".
592   SVGTextFrame* svgTextFrame = static_cast<SVGTextFrame*>(
593       nsLayoutUtils::GetClosestFrameOfType(aFrame, LayoutFrameType::SVGText));
594   nsIFrame* anonBlock = svgTextFrame->PrincipalChildList().FirstChild();
595 
596   // Note that we must check NS_FRAME_FIRST_REFLOW on our SVGTextFrame's
597   // anonymous block frame rather than our aFrame, since NS_FRAME_FIRST_REFLOW
598   // may be set on us if we're a new frame that has been inserted after the
599   // document's first reflow. (In which case this DidSetComputedStyle call may
600   // be happening under frame construction under a Reflow() call.)
601   if (!anonBlock || anonBlock->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
602     return;
603   }
604 
605   if (!svgTextFrame->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY) ||
606       svgTextFrame->HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW)) {
607     return;
608   }
609 
610   svgTextFrame->ScheduleReflowSVGNonDisplayText(IntrinsicDirty::StyleChange);
611 }
612 
IsPrimaryFrameOfRootOrBodyElement() const613 bool nsIFrame::IsPrimaryFrameOfRootOrBodyElement() const {
614   if (!IsPrimaryFrame()) {
615     return false;
616   }
617   nsIContent* content = GetContent();
618   Document* document = content->OwnerDoc();
619   return content == document->GetRootElement() ||
620          content == document->GetBodyElement();
621 }
622 
Init(nsIContent * aContent,nsContainerFrame * aParent,nsIFrame * aPrevInFlow)623 void nsFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
624                    nsIFrame* aPrevInFlow) {
625   MOZ_ASSERT(nsQueryFrame::FrameIID(mClass) == GetFrameId());
626   MOZ_ASSERT(!mContent, "Double-initing a frame?");
627   NS_ASSERTION(IsFrameOfType(eDEBUGAllFrames) && !IsFrameOfType(eDEBUGNoFrames),
628                "IsFrameOfType implementation that doesn't call base class");
629 
630   mContent = aContent;
631   mParent = aParent;
632   MOZ_DIAGNOSTIC_ASSERT(!mParent || PresShell() == mParent->PresShell());
633 
634   if (aPrevInFlow) {
635     mWritingMode = aPrevInFlow->GetWritingMode();
636 
637     // Copy some state bits from prev-in-flow (the bits that should apply
638     // throughout a continuation chain). The bits are sorted according to their
639     // order in nsFrameStateBits.h.
640 
641     // clang-format off
642     AddStateBits(aPrevInFlow->GetStateBits() &
643                  (NS_FRAME_GENERATED_CONTENT |
644                   NS_FRAME_OUT_OF_FLOW |
645                   NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN |
646                   NS_FRAME_INDEPENDENT_SELECTION |
647                   NS_FRAME_PART_OF_IBSPLIT |
648                   NS_FRAME_MAY_BE_TRANSFORMED |
649                   NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR));
650     // clang-format on
651 
652     // Copy other bits in nsIFrame from prev-in-flow.
653     mHasColumnSpanSiblings = aPrevInFlow->HasColumnSpanSiblings();
654   } else {
655     PresContext()->ConstructedFrame();
656   }
657 
658   if (GetParent()) {
659     if (MOZ_UNLIKELY(mContent == PresContext()->Document()->GetRootElement() &&
660                      mContent == GetParent()->GetContent())) {
661       // Our content is the root element and we have the same content as our
662       // parent. That is, we are the internal anonymous frame of the root
663       // element. Copy the used mWritingMode from our parent because
664       // mDocElementContainingBlock gets its mWritingMode from <body>.
665       mWritingMode = GetParent()->GetWritingMode();
666     }
667 
668     // Copy some state bits from our parent (the bits that should apply
669     // recursively throughout a subtree). The bits are sorted according to their
670     // order in nsFrameStateBits.h.
671 
672     // clang-format off
673     AddStateBits(GetParent()->GetStateBits() &
674                  (NS_FRAME_GENERATED_CONTENT |
675                   NS_FRAME_INDEPENDENT_SELECTION |
676                   NS_FRAME_IS_SVG_TEXT |
677                   NS_FRAME_IN_POPUP |
678                   NS_FRAME_IS_NONDISPLAY));
679     // clang-format on
680 
681     if (HasAnyStateBits(NS_FRAME_IN_POPUP) && TrackingVisibility()) {
682       // Assume all frames in popups are visible.
683       IncApproximateVisibleCount();
684     }
685   }
686   if (aPrevInFlow) {
687     mMayHaveOpacityAnimation = aPrevInFlow->MayHaveOpacityAnimation();
688     mMayHaveTransformAnimation = aPrevInFlow->MayHaveTransformAnimation();
689   } else if (mContent) {
690     // It's fine to fetch the EffectSet for the style frame here because in the
691     // following code we take care of the case where animations may target
692     // a different frame.
693     EffectSet* effectSet = EffectSet::GetEffectSetForStyleFrame(this);
694     if (effectSet) {
695       mMayHaveOpacityAnimation = effectSet->MayHaveOpacityAnimation();
696 
697       if (effectSet->MayHaveTransformAnimation()) {
698         // If we are the inner table frame for display:table content, then
699         // transform animations should go on our parent frame (the table wrapper
700         // frame).
701         //
702         // We do this when initializing the child frame (table inner frame),
703         // because when initializng the table wrapper frame, we don't yet have
704         // access to its children so we can't tell if we have transform
705         // animations or not.
706         if (IsFrameOfType(eSupportsCSSTransforms)) {
707           mMayHaveTransformAnimation = true;
708           AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
709         } else if (aParent && nsLayoutUtils::GetStyleFrame(aParent) == this) {
710           MOZ_ASSERT(
711               aParent->IsFrameOfType(eSupportsCSSTransforms),
712               "Style frames that don't support transforms should have parents"
713               " that do");
714           aParent->mMayHaveTransformAnimation = true;
715           aParent->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
716         }
717       }
718     }
719   }
720 
721   const nsStyleDisplay* disp = StyleDisplay();
722   if (disp->HasTransform(this)) {
723     // If 'transform' dynamically changes, RestyleManager takes care of
724     // updating this bit.
725     AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
726   }
727 
728   if (disp->IsContainLayout() && disp->IsContainSize() &&
729       // All frames that support contain:layout also support contain:size.
730       IsFrameOfType(eSupportsContainLayoutAndPaint) && !IsTableWrapperFrame()) {
731     // In general, frames that have contain:layout+size can be reflow roots.
732     // (One exception: table-wrapper frames don't work well as reflow roots,
733     // because their inner-table ReflowInput init path tries to reuse & deref
734     // the wrapper's containing block reflow input, which may be null if we
735     // initiate reflow from the table-wrapper itself.)
736     //
737     // Changes to `contain` force frame reconstructions, so this bit can be set
738     // for the whole lifetime of this frame.
739     AddStateBits(NS_FRAME_REFLOW_ROOT);
740   }
741 
742   if (nsLayoutUtils::FontSizeInflationEnabled(PresContext()) ||
743       !GetParent()
744 #ifdef DEBUG
745       // We have assertions that check inflation invariants even when
746       // font size inflation is not enabled.
747       || true
748 #endif
749   ) {
750     if (IsFontSizeInflationContainer(this, disp)) {
751       AddStateBits(NS_FRAME_FONT_INFLATION_CONTAINER);
752       if (!GetParent() ||
753           // I'd use NS_FRAME_OUT_OF_FLOW, but it's not set yet.
754           disp->IsFloating(this) || disp->IsAbsolutelyPositioned(this) ||
755           GetParent()->IsFlexContainerFrame() ||
756           GetParent()->IsGridContainerFrame()) {
757         AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
758       }
759     }
760     NS_ASSERTION(
761         GetParent() || (GetStateBits() & NS_FRAME_FONT_INFLATION_CONTAINER),
762         "root frame should always be a container");
763   }
764 
765   if (PresShell()->AssumeAllFramesVisible() && TrackingVisibility()) {
766     IncApproximateVisibleCount();
767   }
768 
769   DidSetComputedStyle(nullptr);
770 
771   if (::IsXULBoxWrapped(this)) ::InitBoxMetrics(this, false);
772 
773   // For a newly created frame, we need to update this frame's visibility state.
774   // Usually we update the state when the frame is restyled and has a
775   // VisibilityChange change hint but we don't generate any change hints for
776   // newly created frames.
777   // Note: We don't need to do this for placeholders since placeholders have
778   // different styles so that the styles don't have visibility:hidden even if
779   // the parent has visibility:hidden style. We also don't need to update the
780   // state when creating continuations because its visibility is the same as its
781   // prev-in-flow, and the animation code cares only primary frames.
782   if (!IsPlaceholderFrame() && !aPrevInFlow) {
783     UpdateVisibleDescendantsState();
784   }
785 }
786 
DestroyFrom(nsIFrame * aDestructRoot,PostDestroyData & aPostDestroyData)787 void nsFrame::DestroyFrom(nsIFrame* aDestructRoot,
788                           PostDestroyData& aPostDestroyData) {
789   NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
790                "destroy called on frame while scripts not blocked");
791   NS_ASSERTION(!GetNextSibling() && !GetPrevSibling(),
792                "Frames should be removed before destruction.");
793   NS_ASSERTION(aDestructRoot, "Must specify destruct root");
794   MOZ_ASSERT(!HasAbsolutelyPositionedChildren());
795   MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT),
796              "NS_FRAME_PART_OF_IBSPLIT set on non-nsContainerFrame?");
797 
798   MaybeScheduleReflowSVGNonDisplayText(this);
799 
800   SVGObserverUtils::InvalidateDirectRenderingObservers(this);
801 
802   if (StyleDisplay()->mPosition == StylePositionProperty::Sticky) {
803     StickyScrollContainer* ssc =
804         StickyScrollContainer::GetStickyScrollContainerForFrame(this);
805     if (ssc) {
806       ssc->RemoveFrame(this);
807     }
808   }
809 
810   nsPresContext* presContext = PresContext();
811   mozilla::PresShell* presShell = presContext->GetPresShell();
812   if (mState & NS_FRAME_OUT_OF_FLOW) {
813     nsPlaceholderFrame* placeholder = GetPlaceholderFrame();
814     NS_ASSERTION(
815         !placeholder || (aDestructRoot != this),
816         "Don't call Destroy() on OOFs, call Destroy() on the placeholder.");
817     NS_ASSERTION(!placeholder || nsLayoutUtils::IsProperAncestorFrame(
818                                      aDestructRoot, placeholder),
819                  "Placeholder relationship should have been torn down already; "
820                  "this might mean we have a stray placeholder in the tree.");
821     if (placeholder) {
822       placeholder->SetOutOfFlowFrame(nullptr);
823     }
824   }
825 
826   if (IsPrimaryFrame()) {
827     // This needs to happen before we clear our Properties() table.
828     ActiveLayerTracker::TransferActivityToContent(this, mContent);
829   }
830 
831   ScrollAnchorContainer* anchor = nullptr;
832   if (IsScrollAnchor(&anchor)) {
833     anchor->InvalidateAnchor();
834   }
835 
836   if (HasCSSAnimations() || HasCSSTransitions() ||
837       // It's fine to look up the style frame here since if we're destroying the
838       // frames for display:table content we should be destroying both wrapper
839       // and inner frame.
840       EffectSet::GetEffectSetForStyleFrame(this)) {
841     // If no new frame for this element is created by the end of the
842     // restyling process, stop animations and transitions for this frame
843     RestyleManager::AnimationsWithDestroyedFrame* adf =
844         presContext->RestyleManager()->GetAnimationsWithDestroyedFrame();
845     // AnimationsWithDestroyedFrame only lives during the restyling process.
846     if (adf) {
847       adf->Put(mContent, mComputedStyle);
848     }
849   }
850 
851   // Disable visibility tracking. Note that we have to do this before we clear
852   // frame properties and lose track of whether we were previously visible.
853   // XXX(seth): It'd be ideal to assert that we're already marked nonvisible
854   // here, but it's unfortunately tricky to guarantee in the face of things like
855   // frame reconstruction induced by style changes.
856   DisableVisibilityTracking();
857 
858   // Ensure that we're not in the approximately visible list anymore.
859   PresContext()->GetPresShell()->RemoveFrameFromApproximatelyVisibleList(this);
860 
861   presShell->NotifyDestroyingFrame(this);
862 
863   if (mState & NS_FRAME_EXTERNAL_REFERENCE) {
864     presShell->ClearFrameRefs(this);
865   }
866 
867   nsView* view = GetView();
868   if (view) {
869     view->SetFrame(nullptr);
870     view->Destroy();
871   }
872 
873   // Make sure that our deleted frame can't be returned from GetPrimaryFrame()
874   if (IsPrimaryFrame()) {
875     mContent->SetPrimaryFrame(nullptr);
876 
877     // Pass the root of a generated content subtree (e.g. ::after/::before) to
878     // aPostDestroyData to unbind it after frame destruction is done.
879     if (HasAnyStateBits(NS_FRAME_GENERATED_CONTENT) &&
880         mContent->IsRootOfNativeAnonymousSubtree()) {
881       aPostDestroyData.AddAnonymousContent(mContent.forget());
882     }
883   }
884 
885   // Remove all properties attached to the frame, to ensure any property
886   // destructors that need the frame pointer are handled properly.
887   RemoveAllProperties();
888 
889   // Must retrieve the object ID before calling destructors, so the
890   // vtable is still valid.
891   //
892   // Note to future tweakers: having the method that returns the
893   // object size call the destructor will not avoid an indirect call;
894   // the compiler cannot devirtualize the call to the destructor even
895   // if it's from a method defined in the same class.
896 
897   nsQueryFrame::FrameIID id = GetFrameId();
898   this->~nsFrame();
899 
900 #ifdef DEBUG
901   {
902     nsIFrame* rootFrame = presShell->GetRootFrame();
903     MOZ_ASSERT(rootFrame);
904     if (this != rootFrame) {
905       const RetainedDisplayListData* data =
906           GetRetainedDisplayListData(rootFrame);
907 
908       const bool inModifiedList =
909           data && (data->GetFlags(this) &
910                    RetainedDisplayListData::FrameFlags::Modified);
911 
912       MOZ_ASSERT(!inModifiedList,
913                  "A dtor added this frame to modified frames list!");
914     }
915   }
916 #endif
917 
918   // Now that we're totally cleaned out, we need to add ourselves to
919   // the presshell's recycler.
920   presShell->FreeFrame(id, this);
921 }
922 
GetOffsets(int32_t & aStart,int32_t & aEnd) const923 nsresult nsIFrame::GetOffsets(int32_t& aStart, int32_t& aEnd) const {
924   aStart = 0;
925   aEnd = 0;
926   return NS_OK;
927 }
928 
CompareLayers(const nsStyleImageLayers * aFirstLayers,const nsStyleImageLayers * aSecondLayers,const std::function<void (imgRequestProxy * aReq)> & aCallback)929 static void CompareLayers(
930     const nsStyleImageLayers* aFirstLayers,
931     const nsStyleImageLayers* aSecondLayers,
932     const std::function<void(imgRequestProxy* aReq)>& aCallback) {
933   NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, (*aFirstLayers)) {
934     const auto& image = aFirstLayers->mLayers[i].mImage;
935     if (!image.IsImageRequestType() || !image.IsResolved()) {
936       continue;
937     }
938 
939     // aCallback is called when the style image in aFirstLayers is thought to
940     // be different with the corresponded one in aSecondLayers
941     if (!aSecondLayers || i >= aSecondLayers->mImageCount ||
942         (!aSecondLayers->mLayers[i].mImage.IsResolved() ||
943          image.GetImageRequest() !=
944              aSecondLayers->mLayers[i].mImage.GetImageRequest())) {
945       if (imgRequestProxy* req = image.GetImageRequest()) {
946         aCallback(req);
947       }
948     }
949   }
950 }
951 
AddAndRemoveImageAssociations(ImageLoader & aImageLoader,nsIFrame * aFrame,const nsStyleImageLayers * aOldLayers,const nsStyleImageLayers * aNewLayers)952 static void AddAndRemoveImageAssociations(
953     ImageLoader& aImageLoader, nsIFrame* aFrame,
954     const nsStyleImageLayers* aOldLayers,
955     const nsStyleImageLayers* aNewLayers) {
956   // If the old context had a background-image image, or mask-image image,
957   // and new context does not have the same image, clear the image load
958   // notifier (which keeps the image loading, if it still is) for the frame.
959   // We want to do this conservatively because some frames paint their
960   // backgrounds from some other frame's style data, and we don't want
961   // to clear those notifiers unless we have to.  (They'll be reset
962   // when we paint, although we could miss a notification in that
963   // interval.)
964   if (aOldLayers && aFrame->HasImageRequest()) {
965     CompareLayers(aOldLayers, aNewLayers, [&](imgRequestProxy* aReq) {
966       aImageLoader.DisassociateRequestFromFrame(aReq, aFrame);
967     });
968   }
969 
970   CompareLayers(aNewLayers, aOldLayers, [&](imgRequestProxy* aReq) {
971     aImageLoader.AssociateRequestToFrame(aReq, aFrame, 0);
972   });
973 }
974 
AddDisplayItem(nsDisplayItemBase * aItem)975 void nsIFrame::AddDisplayItem(nsDisplayItemBase* aItem) {
976   DisplayItemArray* items = GetProperty(DisplayItems());
977   if (!items) {
978     items = new DisplayItemArray();
979     AddProperty(DisplayItems(), items);
980   }
981   MOZ_DIAGNOSTIC_ASSERT(!items->Contains(aItem));
982   items->AppendElement(aItem);
983 }
984 
RemoveDisplayItem(nsDisplayItemBase * aItem)985 bool nsIFrame::RemoveDisplayItem(nsDisplayItemBase* aItem) {
986   DisplayItemArray* items = GetProperty(DisplayItems());
987   if (!items) {
988     return false;
989   }
990   bool result = items->RemoveElement(aItem);
991   if (items->IsEmpty()) {
992     RemoveProperty(DisplayItems());
993   }
994   return result;
995 }
996 
HasDisplayItems()997 bool nsIFrame::HasDisplayItems() {
998   DisplayItemArray* items = GetProperty(DisplayItems());
999   return items != nullptr;
1000 }
1001 
HasDisplayItem(nsDisplayItemBase * aItem)1002 bool nsIFrame::HasDisplayItem(nsDisplayItemBase* aItem) {
1003   DisplayItemArray* items = GetProperty(DisplayItems());
1004   if (!items) {
1005     return false;
1006   }
1007   return items->Contains(aItem);
1008 }
1009 
HasDisplayItem(uint32_t aKey)1010 bool nsIFrame::HasDisplayItem(uint32_t aKey) {
1011   DisplayItemArray* items = GetProperty(DisplayItems());
1012   if (!items) {
1013     return false;
1014   }
1015 
1016   for (nsDisplayItemBase* i : *items) {
1017     if (i->GetPerFrameKey() == aKey) {
1018       return true;
1019     }
1020   }
1021   return false;
1022 }
1023 
1024 template <typename Condition>
DiscardDisplayItems(nsIFrame * aFrame,Condition aCondition)1025 static void DiscardDisplayItems(nsIFrame* aFrame, Condition aCondition) {
1026   auto* items = aFrame->GetProperty(nsIFrame::DisplayItems());
1027   if (!items) {
1028     return;
1029   }
1030 
1031   for (nsDisplayItemBase* i : *items) {
1032     // Only discard items that are invalidated by this frame, as we're only
1033     // guaranteed to rebuild those items. Table background items are created by
1034     // the relevant table part, but have the cell frame as the primary frame,
1035     // and we don't want to remove them if this is the cell.
1036     if (aCondition(i) && i->FrameForInvalidation() == aFrame) {
1037       i->SetCantBeReused();
1038     }
1039   }
1040 }
1041 
DiscardOldItems(nsIFrame * aFrame)1042 static void DiscardOldItems(nsIFrame* aFrame) {
1043   DiscardDisplayItems(
1044       aFrame, [](nsDisplayItemBase* aItem) { return aItem->IsOldItem(); });
1045 }
1046 
RemoveDisplayItemDataForDeletion()1047 void nsIFrame::RemoveDisplayItemDataForDeletion() {
1048   // Destroying a WebRenderUserDataTable can cause destruction of other objects
1049   // which can remove frame properties in their destructor. If we delete a frame
1050   // property it runs the destructor of the stored object in the middle of
1051   // updating the frame property table, so if the destruction of that object
1052   // causes another update to the frame property table it would leave the frame
1053   // property table in an inconsistent state. So we remove it from the table and
1054   // then destroy it. (bug 1530657)
1055   WebRenderUserDataTable* userDataTable =
1056       TakeProperty(WebRenderUserDataProperty::Key());
1057   if (userDataTable) {
1058     for (auto iter = userDataTable->Iter(); !iter.Done(); iter.Next()) {
1059       iter.UserData()->RemoveFromTable();
1060     }
1061     delete userDataTable;
1062   }
1063 
1064   FrameLayerBuilder::RemoveFrameFromLayerManager(this, DisplayItemData());
1065   DisplayItemData().Clear();
1066 
1067   DisplayItemArray* items = TakeProperty(DisplayItems());
1068   if (items) {
1069     for (nsDisplayItemBase* i : *items) {
1070       if (i->GetDependentFrame() == this && !i->HasDeletedFrame()) {
1071         i->Frame()->MarkNeedsDisplayItemRebuild();
1072       }
1073       i->RemoveFrame(this);
1074     }
1075     delete items;
1076   }
1077 
1078   if (!nsLayoutUtils::AreRetainedDisplayListsEnabled()) {
1079     // Retained display lists are disabled, no need to update
1080     // RetainedDisplayListData.
1081     return;
1082   }
1083 
1084   const bool updateData = IsFrameModified() || HasOverrideDirtyRegion() ||
1085                           MayHaveWillChangeBudget();
1086 
1087   if (!updateData) {
1088     // No RetainedDisplayListData to update.
1089     return;
1090   }
1091 
1092   nsIFrame* rootFrame = PresShell()->GetRootFrame();
1093   MOZ_ASSERT(rootFrame);
1094 
1095   RetainedDisplayListData* data = GetOrSetRetainedDisplayListData(rootFrame);
1096 
1097   if (MayHaveWillChangeBudget()) {
1098     // Keep the frame in list, so it can be removed from the will-change budget.
1099     data->Flags(this) = RetainedDisplayListData::FrameFlags::HadWillChange;
1100     return;
1101   }
1102 
1103   if (IsFrameModified() || HasOverrideDirtyRegion()) {
1104     // Remove deleted frames from RetainedDisplayListData.
1105     DebugOnly<bool> removed = data->Remove(this);
1106     MOZ_ASSERT(removed,
1107                "Frame had flags set, but it was not found in DisplayListData!");
1108   }
1109 }
1110 
MarkNeedsDisplayItemRebuild()1111 void nsIFrame::MarkNeedsDisplayItemRebuild() {
1112   if (!nsLayoutUtils::AreRetainedDisplayListsEnabled() || IsFrameModified() ||
1113       HasAnyStateBits(NS_FRAME_IN_POPUP)) {
1114     // Skip frames that are already marked modified.
1115     return;
1116   }
1117 
1118   if (Type() == LayoutFrameType::Placeholder) {
1119     nsIFrame* oof = static_cast<nsPlaceholderFrame*>(this)->GetOutOfFlowFrame();
1120     if (oof) {
1121       oof->MarkNeedsDisplayItemRebuild();
1122     }
1123     // Do not mark placeholder frames modified.
1124     return;
1125   }
1126 
1127   if (!nsLayoutUtils::DisplayRootHasRetainedDisplayListBuilder(this)) {
1128     return;
1129   }
1130 
1131   nsIFrame* rootFrame = PresShell()->GetRootFrame();
1132   MOZ_ASSERT(rootFrame);
1133 
1134   if (rootFrame->IsFrameModified()) {
1135     return;
1136   }
1137 
1138   RetainedDisplayListData* data = GetOrSetRetainedDisplayListData(rootFrame);
1139 
1140   if (data->ModifiedFramesCount() >
1141       StaticPrefs::layout_display_list_rebuild_frame_limit()) {
1142     // If the modified frames count is above the rebuild limit, mark the root
1143     // frame modified, and stop marking additional frames modified.
1144     data->AddModifiedFrame(rootFrame);
1145     rootFrame->SetFrameIsModified(true);
1146     return;
1147   }
1148 
1149   data->AddModifiedFrame(this);
1150   SetFrameIsModified(true);
1151 
1152   MOZ_ASSERT(
1153       PresContext()->LayoutPhaseCount(nsLayoutPhase::DisplayListBuilding) == 0);
1154 
1155   // Hopefully this is cheap, but we could use a frame state bit to note
1156   // the presence of dependencies to speed it up.
1157   DisplayItemArray* items = GetProperty(DisplayItems());
1158   if (items) {
1159     for (nsDisplayItemBase* i : *items) {
1160       if (i->HasDeletedFrame() || i->Frame() == this) {
1161         // Ignore the items with deleted frames, and the items with |this| as
1162         // the primary frame.
1163         continue;
1164       }
1165 
1166       if (i->GetDependentFrame() == this) {
1167         // For items with |this| as a dependent frame, mark the primary frame
1168         // for rebuild.
1169         i->Frame()->MarkNeedsDisplayItemRebuild();
1170       }
1171     }
1172   }
1173 }
1174 
1175 // Subclass hook for style post processing
1176 /* virtual */
DidSetComputedStyle(ComputedStyle * aOldComputedStyle)1177 void nsIFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
1178   MaybeScheduleReflowSVGNonDisplayText(this);
1179 
1180   Document* doc = PresContext()->Document();
1181   ImageLoader* loader = doc->StyleImageLoader();
1182   // Continuing text frame doesn't initialize its continuation pointer before
1183   // reaching here for the first time, so we have to exclude text frames. This
1184   // doesn't affect correctness because text can't match selectors.
1185   //
1186   // FIXME(emilio): We should consider fixing that.
1187   //
1188   // TODO(emilio): Can we avoid doing some / all of the image stuff when
1189   // isNonTextFirstContinuation is false? We should consider doing this just for
1190   // primary frames and pseudos, but the first-line reparenting code makes it
1191   // all bad, should get around to bug 1465474 eventually :(
1192   const bool isNonText = !IsTextFrame();
1193   if (isNonText) {
1194     mComputedStyle->StartImageLoads(*doc, aOldComputedStyle);
1195   }
1196 
1197   const nsStyleImageLayers* oldLayers =
1198       aOldComputedStyle ? &aOldComputedStyle->StyleBackground()->mImage
1199                         : nullptr;
1200   const nsStyleImageLayers* newLayers = &StyleBackground()->mImage;
1201   AddAndRemoveImageAssociations(*loader, this, oldLayers, newLayers);
1202 
1203   oldLayers =
1204       aOldComputedStyle ? &aOldComputedStyle->StyleSVGReset()->mMask : nullptr;
1205   newLayers = &StyleSVGReset()->mMask;
1206   AddAndRemoveImageAssociations(*loader, this, oldLayers, newLayers);
1207 
1208   const nsStyleDisplay* disp = StyleDisplay();
1209   bool handleStickyChange = false;
1210   if (aOldComputedStyle) {
1211     // Detect style changes that should trigger a scroll anchor adjustment
1212     // suppression.
1213     // https://drafts.csswg.org/css-scroll-anchoring/#suppression-triggers
1214     bool needAnchorSuppression = false;
1215 
1216     // If we detect a change on margin, padding or border, we store the old
1217     // values on the frame itself between now and reflow, so if someone
1218     // calls GetUsed(Margin|Border|Padding)() before the next reflow, we
1219     // can give an accurate answer.
1220     // We don't want to set the property if one already exists.
1221     nsMargin oldValue(0, 0, 0, 0);
1222     nsMargin newValue(0, 0, 0, 0);
1223     const nsStyleMargin* oldMargin = aOldComputedStyle->StyleMargin();
1224     if (oldMargin->GetMargin(oldValue)) {
1225       if (!StyleMargin()->GetMargin(newValue) || oldValue != newValue) {
1226         if (!HasProperty(UsedMarginProperty())) {
1227           AddProperty(UsedMarginProperty(), new nsMargin(oldValue));
1228         }
1229         needAnchorSuppression = true;
1230       }
1231     }
1232 
1233     const nsStylePadding* oldPadding = aOldComputedStyle->StylePadding();
1234     if (oldPadding->GetPadding(oldValue)) {
1235       if (!StylePadding()->GetPadding(newValue) || oldValue != newValue) {
1236         if (!HasProperty(UsedPaddingProperty())) {
1237           AddProperty(UsedPaddingProperty(), new nsMargin(oldValue));
1238         }
1239         needAnchorSuppression = true;
1240       }
1241     }
1242 
1243     const nsStyleBorder* oldBorder = aOldComputedStyle->StyleBorder();
1244     oldValue = oldBorder->GetComputedBorder();
1245     newValue = StyleBorder()->GetComputedBorder();
1246     if (oldValue != newValue && !HasProperty(UsedBorderProperty())) {
1247       AddProperty(UsedBorderProperty(), new nsMargin(oldValue));
1248     }
1249 
1250     const nsStyleDisplay* oldDisp = aOldComputedStyle->StyleDisplay();
1251     if (oldDisp->mOverflowAnchor != disp->mOverflowAnchor) {
1252       if (auto* container = ScrollAnchorContainer::FindFor(this)) {
1253         container->InvalidateAnchor();
1254       }
1255       if (nsIScrollableFrame* scrollableFrame = do_QueryFrame(this)) {
1256         scrollableFrame->Anchor()->InvalidateAnchor();
1257       }
1258     }
1259 
1260     if (mInScrollAnchorChain) {
1261       const nsStylePosition* pos = StylePosition();
1262       const nsStylePosition* oldPos = aOldComputedStyle->StylePosition();
1263       if (!needAnchorSuppression &&
1264           (oldPos->mOffset != pos->mOffset || oldPos->mWidth != pos->mWidth ||
1265            oldPos->mMinWidth != pos->mMinWidth ||
1266            oldPos->mMaxWidth != pos->mMaxWidth ||
1267            oldPos->mHeight != pos->mHeight ||
1268            oldPos->mMinHeight != pos->mMinHeight ||
1269            oldPos->mMaxHeight != pos->mMaxHeight ||
1270            oldDisp->mPosition != disp->mPosition ||
1271            oldDisp->mTransform != disp->mTransform)) {
1272         needAnchorSuppression = true;
1273       }
1274 
1275       if (needAnchorSuppression &&
1276           StaticPrefs::layout_css_scroll_anchoring_suppressions_enabled()) {
1277         ScrollAnchorContainer::FindFor(this)->SuppressAdjustments();
1278       }
1279     }
1280 
1281     if (disp->mPosition != oldDisp->mPosition) {
1282       if (!disp->IsRelativelyPositionedStyle() &&
1283           oldDisp->IsRelativelyPositionedStyle()) {
1284         RemoveProperty(NormalPositionProperty());
1285       }
1286 
1287       handleStickyChange = disp->mPosition == StylePositionProperty::Sticky ||
1288                            oldDisp->mPosition == StylePositionProperty::Sticky;
1289     }
1290   } else {  // !aOldComputedStyle
1291     handleStickyChange = disp->mPosition == StylePositionProperty::Sticky;
1292   }
1293 
1294   if (handleStickyChange && !HasAnyStateBits(NS_FRAME_IS_NONDISPLAY) &&
1295       !GetPrevInFlow()) {
1296     // Note that we only add first continuations, but we really only
1297     // want to add first continuation-or-ib-split-siblings. But since we don't
1298     // yet know if we're a later part of a block-in-inline split, we'll just
1299     // add later members of a block-in-inline split here, and then
1300     // StickyScrollContainer will remove them later.
1301     if (auto* ssc =
1302             StickyScrollContainer::GetStickyScrollContainerForFrame(this)) {
1303       if (disp->mPosition == StylePositionProperty::Sticky) {
1304         ssc->AddFrame(this);
1305       } else {
1306         ssc->RemoveFrame(this);
1307       }
1308     }
1309   }
1310 
1311   imgIRequest* oldBorderImage =
1312       aOldComputedStyle
1313           ? aOldComputedStyle->StyleBorder()->GetBorderImageRequest()
1314           : nullptr;
1315   imgIRequest* newBorderImage = StyleBorder()->GetBorderImageRequest();
1316   // FIXME (Bug 759996): The following is no longer true.
1317   // For border-images, we can't be as conservative (we need to set the
1318   // new loaders if there has been any change) since the CalcDifference
1319   // call depended on the result of GetComputedBorder() and that result
1320   // depends on whether the image has loaded, start the image load now
1321   // so that we'll get notified when it completes loading and can do a
1322   // restyle.  Otherwise, the image might finish loading from the
1323   // network before we start listening to its notifications, and then
1324   // we'll never know that it's finished loading.  Likewise, we want to
1325   // do this for freshly-created frames to prevent a similar race if the
1326   // image loads between reflow (which can depend on whether the image
1327   // is loaded) and paint.  We also don't really care about any callers who try
1328   // to paint borders with a different style, because they won't have the
1329   // correct size for the border either.
1330   if (oldBorderImage != newBorderImage) {
1331     // stop and restart the image loading/notification
1332     if (oldBorderImage && HasImageRequest()) {
1333       RemoveProperty(CachedBorderImageDataProperty());
1334       loader->DisassociateRequestFromFrame(oldBorderImage, this);
1335     }
1336     if (newBorderImage) {
1337       loader->AssociateRequestToFrame(newBorderImage, this, 0);
1338     }
1339   }
1340 
1341   auto GetShapeImageRequest = [](const ComputedStyle* aStyle) -> imgIRequest* {
1342     if (!aStyle) {
1343       return nullptr;
1344     }
1345     auto& shape = aStyle->StyleDisplay()->mShapeOutside;
1346     if (!shape.IsImage()) {
1347       return nullptr;
1348     }
1349     return shape.AsImage().GetImageRequest();
1350   };
1351 
1352   imgIRequest* oldShapeImage = GetShapeImageRequest(aOldComputedStyle);
1353   imgIRequest* newShapeImage = GetShapeImageRequest(Style());
1354   if (oldShapeImage != newShapeImage) {
1355     if (oldShapeImage && HasImageRequest()) {
1356       loader->DisassociateRequestFromFrame(oldShapeImage, this);
1357     }
1358     if (newShapeImage) {
1359       loader->AssociateRequestToFrame(newShapeImage, this,
1360                                       ImageLoader::REQUEST_REQUIRES_REFLOW);
1361     }
1362   }
1363 
1364   // SVGObserverUtils::GetEffectProperties() asserts that we only invoke it with
1365   // the first continuation so we need to check that in advance.
1366   const bool isNonTextFirstContinuation = isNonText && !GetPrevContinuation();
1367   if (isNonTextFirstContinuation) {
1368     // Kick off loading of external SVG resources referenced from properties if
1369     // any. This currently includes filter, clip-path, and mask.
1370     SVGObserverUtils::InitiateResourceDocLoads(this);
1371   }
1372 
1373   // If the page contains markup that overrides text direction, and
1374   // does not contain any characters that would activate the Unicode
1375   // bidi algorithm, we need to call |SetBidiEnabled| on the pres
1376   // context before reflow starts.  See bug 115921.
1377   if (StyleVisibility()->mDirection == StyleDirection::Rtl) {
1378     PresContext()->SetBidiEnabled();
1379   }
1380 
1381   // The following part is for caching offset-path:path(). We cache the
1382   // flatten gfx path, so we don't have to rebuild and re-flattern it at
1383   // each cycle if we have animations on offset-* with a fixed offset-path.
1384   const StyleOffsetPath* oldPath =
1385       aOldComputedStyle ? &aOldComputedStyle->StyleDisplay()->mOffsetPath
1386                         : nullptr;
1387   const StyleOffsetPath& newPath = StyleDisplay()->mOffsetPath;
1388   if (!oldPath || *oldPath != newPath) {
1389     if (newPath.IsPath()) {
1390       // Here we only need to build a valid path for motion path, so
1391       // using the default values of stroke-width, stoke-linecap, and fill-rule
1392       // is fine for now because what we want is to get the point and its normal
1393       // vector along the path, instead of rendering it.
1394       RefPtr<gfx::PathBuilder> builder =
1395           gfxPlatform::GetPlatform()
1396               ->ScreenReferenceDrawTarget()
1397               ->CreatePathBuilder(gfx::FillRule::FILL_WINDING);
1398       RefPtr<gfx::Path> path =
1399           MotionPathUtils::BuildPath(newPath.AsPath(), builder);
1400       if (path) {
1401         // The newPath could be path('') (i.e. empty path), so its gfx path
1402         // could be nullptr, and so we only set property for a non-empty path.
1403         SetProperty(nsIFrame::OffsetPathCache(), path.forget().take());
1404       } else {
1405         // May have an old cached path, so we have to delete it.
1406         RemoveProperty(nsIFrame::OffsetPathCache());
1407       }
1408     } else if (oldPath) {
1409       RemoveProperty(nsIFrame::OffsetPathCache());
1410     }
1411   }
1412 
1413   RemoveStateBits(NS_FRAME_SIMPLE_EVENT_REGIONS | NS_FRAME_SIMPLE_DISPLAYLIST);
1414 
1415   mMayHaveRoundedCorners = true;
1416 }
1417 
1418 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
AssertNewStyleIsSane(ComputedStyle & aNewStyle)1419 void nsIFrame::AssertNewStyleIsSane(ComputedStyle& aNewStyle) {
1420   MOZ_DIAGNOSTIC_ASSERT(
1421       aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() ||
1422       // ::first-line continuations are weird, this should probably be fixed via
1423       // bug 1465474.
1424       (mComputedStyle->GetPseudoType() == PseudoStyleType::firstLine &&
1425        aNewStyle.GetPseudoType() == PseudoStyleType::mozLineFrame) ||
1426       // ::first-letter continuations are broken, in particular floating ones,
1427       // see bug 1490281. The construction code tries to fix this up after the
1428       // fact, then restyling undoes it...
1429       (mComputedStyle->GetPseudoType() == PseudoStyleType::mozText &&
1430        aNewStyle.GetPseudoType() == PseudoStyleType::firstLetterContinuation) ||
1431       (mComputedStyle->GetPseudoType() ==
1432            PseudoStyleType::firstLetterContinuation &&
1433        aNewStyle.GetPseudoType() == PseudoStyleType::mozText));
1434 }
1435 #endif
1436 
ReparentFrameViewTo(nsViewManager * aViewManager,nsView * aNewParentView,nsView * aOldParentView)1437 void nsIFrame::ReparentFrameViewTo(nsViewManager* aViewManager,
1438                                    nsView* aNewParentView,
1439                                    nsView* aOldParentView) {
1440   if (HasView()) {
1441 #ifdef MOZ_XUL
1442     if (IsMenuPopupFrame()) {
1443       // This view must be parented by the root view, don't reparent it.
1444       return;
1445     }
1446 #endif
1447     nsView* view = GetView();
1448     // Verify that the current parent view is what we think it is
1449     // nsView*  parentView;
1450     // NS_ASSERTION(parentView == aOldParentView, "unexpected parent view");
1451 
1452     aViewManager->RemoveChild(view);
1453 
1454     // The view will remember the Z-order and other attributes that have been
1455     // set on it.
1456     nsView* insertBefore =
1457         nsLayoutUtils::FindSiblingViewFor(aNewParentView, this);
1458     aViewManager->InsertChild(aNewParentView, view, insertBefore,
1459                               insertBefore != nullptr);
1460   } else if (GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW) {
1461     for (const auto& childList : ChildLists()) {
1462       // Iterate the child frames, and check each child frame to see if it has
1463       // a view
1464       for (nsIFrame* child : childList.mList) {
1465         child->ReparentFrameViewTo(aViewManager, aNewParentView,
1466                                    aOldParentView);
1467       }
1468     }
1469   }
1470 }
1471 
SyncFrameViewProperties(nsView * aView)1472 void nsIFrame::SyncFrameViewProperties(nsView* aView) {
1473   if (!aView) {
1474     aView = GetView();
1475     if (!aView) {
1476       return;
1477     }
1478   }
1479 
1480   nsViewManager* vm = aView->GetViewManager();
1481 
1482   // Make sure visibility is correct. This only affects nsSubDocumentFrame.
1483   if (!SupportsVisibilityHidden()) {
1484     // See if the view should be hidden or visible
1485     ComputedStyle* sc = Style();
1486     vm->SetViewVisibility(aView, sc->StyleVisibility()->IsVisible()
1487                                      ? nsViewVisibility_kShow
1488                                      : nsViewVisibility_kHide);
1489   }
1490 
1491   int32_t zIndex = 0;
1492   bool autoZIndex = false;
1493 
1494   if (IsAbsPosContainingBlock()) {
1495     // Make sure z-index is correct
1496     ComputedStyle* sc = Style();
1497     const nsStylePosition* position = sc->StylePosition();
1498     if (position->mZIndex.IsInteger()) {
1499       zIndex = position->mZIndex.AsInteger();
1500     } else {
1501       MOZ_ASSERT(position->mZIndex.IsAuto());
1502       autoZIndex = true;
1503     }
1504   } else {
1505     autoZIndex = true;
1506   }
1507 
1508   vm->SetViewZIndex(aView, autoZIndex, zIndex);
1509 }
1510 
CreateView()1511 void nsFrame::CreateView() {
1512   MOZ_ASSERT(!HasView());
1513 
1514   nsView* parentView = GetParent()->GetClosestView();
1515   MOZ_ASSERT(parentView, "no parent with view");
1516 
1517   nsViewManager* viewManager = parentView->GetViewManager();
1518   MOZ_ASSERT(viewManager, "null view manager");
1519 
1520   nsView* view = viewManager->CreateView(GetRect(), parentView);
1521   SyncFrameViewProperties(view);
1522 
1523   nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(parentView, this);
1524   // we insert this view 'above' the insertBefore view, unless insertBefore is
1525   // null, in which case we want to call with aAbove == false to insert at the
1526   // beginning in document order
1527   viewManager->InsertChild(parentView, view, insertBefore,
1528                            insertBefore != nullptr);
1529 
1530   // REVIEW: Don't create a widget for fixed-pos elements anymore.
1531   // ComputeRepaintRegionForCopy will calculate the right area to repaint
1532   // when we scroll.
1533   // Reparent views on any child frames (or their descendants) to this
1534   // view. We can just call ReparentFrameViewTo on this frame because
1535   // we know this frame has no view, so it will crawl the children. Also,
1536   // we know that any descendants with views must have 'parentView' as their
1537   // parent view.
1538   ReparentFrameViewTo(viewManager, view, parentView);
1539 
1540   // Remember our view
1541   SetView(view);
1542 
1543   NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
1544                ("nsFrame::CreateView: frame=%p view=%p", this, view));
1545 }
1546 
1547 // MSVC fails with link error "one or more multiply defined symbols found",
1548 // gcc fails with "hidden symbol `nsIFrame::kPrincipalList' isn't defined"
1549 // etc if they are not defined.
1550 #ifndef _MSC_VER
1551 // static nsIFrame constants; initialized in the header file.
1552 const nsIFrame::ChildListID nsIFrame::kPrincipalList;
1553 const nsIFrame::ChildListID nsIFrame::kAbsoluteList;
1554 const nsIFrame::ChildListID nsIFrame::kBulletList;
1555 const nsIFrame::ChildListID nsIFrame::kCaptionList;
1556 const nsIFrame::ChildListID nsIFrame::kColGroupList;
1557 const nsIFrame::ChildListID nsIFrame::kExcessOverflowContainersList;
1558 const nsIFrame::ChildListID nsIFrame::kFixedList;
1559 const nsIFrame::ChildListID nsIFrame::kFloatList;
1560 const nsIFrame::ChildListID nsIFrame::kOverflowContainersList;
1561 const nsIFrame::ChildListID nsIFrame::kOverflowList;
1562 const nsIFrame::ChildListID nsIFrame::kOverflowOutOfFlowList;
1563 const nsIFrame::ChildListID nsIFrame::kPopupList;
1564 const nsIFrame::ChildListID nsIFrame::kPushedFloatsList;
1565 const nsIFrame::ChildListID nsIFrame::kSelectPopupList;
1566 const nsIFrame::ChildListID nsIFrame::kNoReflowPrincipalList;
1567 #endif
1568 
1569 /* virtual */
GetUsedMargin() const1570 nsMargin nsIFrame::GetUsedMargin() const {
1571   nsMargin margin(0, 0, 0, 0);
1572   if (((mState & NS_FRAME_FIRST_REFLOW) && !(mState & NS_FRAME_IN_REFLOW)) ||
1573       nsSVGUtils::IsInSVGTextSubtree(this))
1574     return margin;
1575 
1576   nsMargin* m = GetProperty(UsedMarginProperty());
1577   if (m) {
1578     margin = *m;
1579   } else {
1580     if (!StyleMargin()->GetMargin(margin)) {
1581       // If we get here, our caller probably shouldn't be calling us...
1582       NS_ERROR(
1583           "Returning bogus 0-sized margin, because this margin "
1584           "depends on layout & isn't cached!");
1585     }
1586   }
1587   return margin;
1588 }
1589 
1590 /* virtual */
GetUsedBorder() const1591 nsMargin nsIFrame::GetUsedBorder() const {
1592   nsMargin border(0, 0, 0, 0);
1593   if (((mState & NS_FRAME_FIRST_REFLOW) && !(mState & NS_FRAME_IN_REFLOW)) ||
1594       nsSVGUtils::IsInSVGTextSubtree(this))
1595     return border;
1596 
1597   // Theme methods don't use const-ness.
1598   nsIFrame* mutable_this = const_cast<nsIFrame*>(this);
1599 
1600   const nsStyleDisplay* disp = StyleDisplay();
1601   if (mutable_this->IsThemed(disp)) {
1602     nsPresContext* pc = PresContext();
1603     LayoutDeviceIntMargin widgetBorder = pc->Theme()->GetWidgetBorder(
1604         pc->DeviceContext(), mutable_this, disp->mAppearance);
1605     border =
1606         LayoutDevicePixel::ToAppUnits(widgetBorder, pc->AppUnitsPerDevPixel());
1607     return border;
1608   }
1609 
1610   nsMargin* b = GetProperty(UsedBorderProperty());
1611   if (b) {
1612     border = *b;
1613   } else {
1614     border = StyleBorder()->GetComputedBorder();
1615   }
1616   return border;
1617 }
1618 
1619 /* virtual */
GetUsedPadding() const1620 nsMargin nsIFrame::GetUsedPadding() const {
1621   nsMargin padding(0, 0, 0, 0);
1622   if (((mState & NS_FRAME_FIRST_REFLOW) && !(mState & NS_FRAME_IN_REFLOW)) ||
1623       nsSVGUtils::IsInSVGTextSubtree(this))
1624     return padding;
1625 
1626   // Theme methods don't use const-ness.
1627   nsIFrame* mutable_this = const_cast<nsIFrame*>(this);
1628 
1629   const nsStyleDisplay* disp = StyleDisplay();
1630   if (mutable_this->IsThemed(disp)) {
1631     nsPresContext* pc = PresContext();
1632     LayoutDeviceIntMargin widgetPadding;
1633     if (pc->Theme()->GetWidgetPadding(pc->DeviceContext(), mutable_this,
1634                                       disp->mAppearance, &widgetPadding)) {
1635       return LayoutDevicePixel::ToAppUnits(widgetPadding,
1636                                            pc->AppUnitsPerDevPixel());
1637     }
1638   }
1639 
1640   nsMargin* p = GetProperty(UsedPaddingProperty());
1641   if (p) {
1642     padding = *p;
1643   } else {
1644     if (!StylePadding()->GetPadding(padding)) {
1645       // If we get here, our caller probably shouldn't be calling us...
1646       NS_ERROR(
1647           "Returning bogus 0-sized padding, because this padding "
1648           "depends on layout & isn't cached!");
1649     }
1650   }
1651   return padding;
1652 }
1653 
GetSkipSides(const ReflowInput * aReflowInput) const1654 nsIFrame::Sides nsIFrame::GetSkipSides(const ReflowInput* aReflowInput) const {
1655   if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
1656                    StyleBoxDecorationBreak::Clone) &&
1657       !(GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
1658     return Sides();
1659   }
1660 
1661   // Convert the logical skip sides to physical sides using the frame's
1662   // writing mode
1663   WritingMode writingMode = GetWritingMode();
1664   LogicalSides logicalSkip = GetLogicalSkipSides(aReflowInput);
1665   Sides skip;
1666 
1667   if (logicalSkip.BStart()) {
1668     if (writingMode.IsVertical()) {
1669       skip |= writingMode.IsVerticalLR() ? SideBits::eLeft : SideBits::eRight;
1670     } else {
1671       skip |= SideBits::eTop;
1672     }
1673   }
1674 
1675   if (logicalSkip.BEnd()) {
1676     if (writingMode.IsVertical()) {
1677       skip |= writingMode.IsVerticalLR() ? SideBits::eRight : SideBits::eLeft;
1678     } else {
1679       skip |= SideBits::eBottom;
1680     }
1681   }
1682 
1683   if (logicalSkip.IStart()) {
1684     if (writingMode.IsVertical()) {
1685       skip |= SideBits::eTop;
1686     } else {
1687       skip |= writingMode.IsBidiLTR() ? SideBits::eLeft : SideBits::eRight;
1688     }
1689   }
1690 
1691   if (logicalSkip.IEnd()) {
1692     if (writingMode.IsVertical()) {
1693       skip |= SideBits::eBottom;
1694     } else {
1695       skip |= writingMode.IsBidiLTR() ? SideBits::eRight : SideBits::eLeft;
1696     }
1697   }
1698   return skip;
1699 }
1700 
GetPaddingRectRelativeToSelf() const1701 nsRect nsIFrame::GetPaddingRectRelativeToSelf() const {
1702   nsMargin border = GetUsedBorder().ApplySkipSides(GetSkipSides());
1703   nsRect r(0, 0, mRect.width, mRect.height);
1704   r.Deflate(border);
1705   return r;
1706 }
1707 
GetPaddingRect() const1708 nsRect nsIFrame::GetPaddingRect() const {
1709   return GetPaddingRectRelativeToSelf() + GetPosition();
1710 }
1711 
WritingModeForLine(WritingMode aSelfWM,nsIFrame * aSubFrame) const1712 WritingMode nsIFrame::WritingModeForLine(WritingMode aSelfWM,
1713                                          nsIFrame* aSubFrame) const {
1714   MOZ_ASSERT(aSelfWM == GetWritingMode());
1715   WritingMode writingMode = aSelfWM;
1716 
1717   if (StyleTextReset()->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_PLAINTEXT) {
1718     nsBidiLevel frameLevel = nsBidiPresUtils::GetFrameBaseLevel(aSubFrame);
1719     writingMode.SetDirectionFromBidiLevel(frameLevel);
1720   }
1721 
1722   return writingMode;
1723 }
1724 
GetMarginRectRelativeToSelf() const1725 nsRect nsIFrame::GetMarginRectRelativeToSelf() const {
1726   nsMargin m = GetUsedMargin().ApplySkipSides(GetSkipSides());
1727   nsRect r(0, 0, mRect.width, mRect.height);
1728   r.Inflate(m);
1729   return r;
1730 }
1731 
IsTransformed(const nsStyleDisplay * aStyleDisplay) const1732 bool nsIFrame::IsTransformed(const nsStyleDisplay* aStyleDisplay) const {
1733   return IsCSSTransformed(aStyleDisplay) || IsSVGTransformed();
1734 }
1735 
IsCSSTransformed(const nsStyleDisplay * aStyleDisplay) const1736 bool nsIFrame::IsCSSTransformed(const nsStyleDisplay* aStyleDisplay) const {
1737   MOZ_ASSERT(aStyleDisplay == StyleDisplay());
1738   return ((mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
1739           (aStyleDisplay->HasTransform(this) || HasAnimationOfTransform()));
1740 }
1741 
HasAnimationOfTransform() const1742 bool nsIFrame::HasAnimationOfTransform() const {
1743   return IsPrimaryFrame() &&
1744          nsLayoutUtils::HasAnimationOfTransformAndMotionPath(this) &&
1745          IsFrameOfType(eSupportsCSSTransforms);
1746 }
1747 
ChildrenHavePerspective(const nsStyleDisplay * aStyleDisplay) const1748 bool nsIFrame::ChildrenHavePerspective(
1749     const nsStyleDisplay* aStyleDisplay) const {
1750   MOZ_ASSERT(aStyleDisplay == StyleDisplay());
1751   return aStyleDisplay->HasPerspective(this);
1752 }
1753 
HasAnimationOfOpacity(EffectSet * aEffectSet) const1754 bool nsIFrame::HasAnimationOfOpacity(EffectSet* aEffectSet) const {
1755   return ((nsLayoutUtils::IsPrimaryStyleFrame(this) ||
1756            nsLayoutUtils::FirstContinuationOrIBSplitSibling(this)
1757                ->IsPrimaryFrame()) &&
1758           nsLayoutUtils::HasAnimationOfPropertySet(
1759               this, nsCSSPropertyIDSet::OpacityProperties(), aEffectSet));
1760 }
1761 
HasOpacityInternal(float aThreshold,const nsStyleDisplay * aStyleDisplay,const nsStyleEffects * aStyleEffects,EffectSet * aEffectSet) const1762 bool nsIFrame::HasOpacityInternal(float aThreshold,
1763                                   const nsStyleDisplay* aStyleDisplay,
1764                                   const nsStyleEffects* aStyleEffects,
1765                                   EffectSet* aEffectSet) const {
1766   MOZ_ASSERT(0.0 <= aThreshold && aThreshold <= 1.0, "Invalid argument");
1767   if (aStyleEffects->mOpacity < aThreshold ||
1768       (aStyleDisplay->mWillChange.bits & StyleWillChangeBits::OPACITY)) {
1769     return true;
1770   }
1771 
1772   if (!mMayHaveOpacityAnimation) {
1773     return false;
1774   }
1775 
1776   return HasAnimationOfOpacity(aEffectSet);
1777 }
1778 
IsSVGTransformed(gfx::Matrix * aOwnTransforms,gfx::Matrix * aFromParentTransforms) const1779 bool nsIFrame::IsSVGTransformed(gfx::Matrix* aOwnTransforms,
1780                                 gfx::Matrix* aFromParentTransforms) const {
1781   return false;
1782 }
1783 
Extend3DContext(const nsStyleDisplay * aStyleDisplay,const nsStyleEffects * aStyleEffects,mozilla::EffectSet * aEffectSetForOpacity) const1784 bool nsIFrame::Extend3DContext(const nsStyleDisplay* aStyleDisplay,
1785                                const nsStyleEffects* aStyleEffects,
1786                                mozilla::EffectSet* aEffectSetForOpacity) const {
1787   if (!(mState & NS_FRAME_MAY_BE_TRANSFORMED)) {
1788     return false;
1789   }
1790   const nsStyleDisplay* disp = StyleDisplayWithOptionalParam(aStyleDisplay);
1791   if (disp->mTransformStyle != StyleTransformStyle::Preserve3d ||
1792       !IsFrameOfType(nsIFrame::eSupportsCSSTransforms)) {
1793     return false;
1794   }
1795 
1796   // If we're all scroll frame, then all descendants will be clipped, so we
1797   // can't preserve 3d.
1798   if (IsScrollFrame()) {
1799     return false;
1800   }
1801 
1802   const nsStyleEffects* effects = StyleEffectsWithOptionalParam(aStyleEffects);
1803   if (HasOpacity(disp, effects, aEffectSetForOpacity)) {
1804     return false;
1805   }
1806 
1807   return !nsFrame::ShouldApplyOverflowClipping(this, disp) &&
1808          !GetClipPropClipRect(disp, effects, GetSize()) &&
1809          !nsSVGIntegrationUtils::UsingEffectsForFrame(this);
1810 }
1811 
Combines3DTransformWithAncestors(const nsStyleDisplay * aStyleDisplay) const1812 bool nsIFrame::Combines3DTransformWithAncestors(
1813     const nsStyleDisplay* aStyleDisplay) const {
1814   MOZ_ASSERT(aStyleDisplay == StyleDisplay());
1815   nsIFrame* parent = GetClosestFlattenedTreeAncestorPrimaryFrame();
1816   if (!parent || !parent->Extend3DContext()) {
1817     return false;
1818   }
1819   return IsCSSTransformed(aStyleDisplay) || BackfaceIsHidden(aStyleDisplay);
1820 }
1821 
In3DContextAndBackfaceIsHidden() const1822 bool nsIFrame::In3DContextAndBackfaceIsHidden() const {
1823   // While both tests fail most of the time, test BackfaceIsHidden()
1824   // first since it's likely to fail faster.
1825   const nsStyleDisplay* disp = StyleDisplay();
1826   return BackfaceIsHidden(disp) && Combines3DTransformWithAncestors(disp);
1827 }
1828 
HasPerspective(const nsStyleDisplay * aStyleDisplay) const1829 bool nsIFrame::HasPerspective(const nsStyleDisplay* aStyleDisplay) const {
1830   MOZ_ASSERT(aStyleDisplay == StyleDisplay());
1831   if (!IsTransformed(aStyleDisplay)) {
1832     return false;
1833   }
1834   nsIFrame* containingBlock =
1835       GetContainingBlock(SKIP_SCROLLED_FRAME, aStyleDisplay);
1836   if (!containingBlock) {
1837     return false;
1838   }
1839   return containingBlock->ChildrenHavePerspective();
1840 }
1841 
GetContentRectRelativeToSelf() const1842 nsRect nsIFrame::GetContentRectRelativeToSelf() const {
1843   nsMargin bp = GetUsedBorderAndPadding().ApplySkipSides(GetSkipSides());
1844   nsRect r(0, 0, mRect.width, mRect.height);
1845   r.Deflate(bp);
1846   return r;
1847 }
1848 
GetContentRect() const1849 nsRect nsIFrame::GetContentRect() const {
1850   return GetContentRectRelativeToSelf() + GetPosition();
1851 }
1852 
ComputeBorderRadii(const BorderRadius & aBorderRadius,const nsSize & aFrameSize,const nsSize & aBorderArea,Sides aSkipSides,nscoord aRadii[8])1853 bool nsIFrame::ComputeBorderRadii(const BorderRadius& aBorderRadius,
1854                                   const nsSize& aFrameSize,
1855                                   const nsSize& aBorderArea, Sides aSkipSides,
1856                                   nscoord aRadii[8]) {
1857   // Percentages are relative to whichever side they're on.
1858   for (const auto i : mozilla::AllPhysicalHalfCorners()) {
1859     const LengthPercentage& c = aBorderRadius.Get(i);
1860     nscoord axis = HalfCornerIsX(i) ? aFrameSize.width : aFrameSize.height;
1861     aRadii[i] = std::max(0, c.Resolve(axis));
1862   }
1863 
1864   if (aSkipSides.Top()) {
1865     aRadii[eCornerTopLeftX] = 0;
1866     aRadii[eCornerTopLeftY] = 0;
1867     aRadii[eCornerTopRightX] = 0;
1868     aRadii[eCornerTopRightY] = 0;
1869   }
1870 
1871   if (aSkipSides.Right()) {
1872     aRadii[eCornerTopRightX] = 0;
1873     aRadii[eCornerTopRightY] = 0;
1874     aRadii[eCornerBottomRightX] = 0;
1875     aRadii[eCornerBottomRightY] = 0;
1876   }
1877 
1878   if (aSkipSides.Bottom()) {
1879     aRadii[eCornerBottomRightX] = 0;
1880     aRadii[eCornerBottomRightY] = 0;
1881     aRadii[eCornerBottomLeftX] = 0;
1882     aRadii[eCornerBottomLeftY] = 0;
1883   }
1884 
1885   if (aSkipSides.Left()) {
1886     aRadii[eCornerBottomLeftX] = 0;
1887     aRadii[eCornerBottomLeftY] = 0;
1888     aRadii[eCornerTopLeftX] = 0;
1889     aRadii[eCornerTopLeftY] = 0;
1890   }
1891 
1892   // css3-background specifies this algorithm for reducing
1893   // corner radii when they are too big.
1894   bool haveRadius = false;
1895   double ratio = 1.0f;
1896   for (const auto side : mozilla::AllPhysicalSides()) {
1897     uint32_t hc1 = SideToHalfCorner(side, false, true);
1898     uint32_t hc2 = SideToHalfCorner(side, true, true);
1899     nscoord length =
1900         SideIsVertical(side) ? aBorderArea.height : aBorderArea.width;
1901     nscoord sum = aRadii[hc1] + aRadii[hc2];
1902     if (sum) {
1903       haveRadius = true;
1904       // avoid floating point division in the normal case
1905       if (length < sum) {
1906         ratio = std::min(ratio, double(length) / sum);
1907       }
1908     }
1909   }
1910   if (ratio < 1.0) {
1911     for (const auto corner : mozilla::AllPhysicalHalfCorners()) {
1912       aRadii[corner] *= ratio;
1913     }
1914   }
1915 
1916   return haveRadius;
1917 }
1918 
1919 /* static */
InsetBorderRadii(nscoord aRadii[8],const nsMargin & aOffsets)1920 void nsIFrame::InsetBorderRadii(nscoord aRadii[8], const nsMargin& aOffsets) {
1921   for (const auto side : mozilla::AllPhysicalSides()) {
1922     nscoord offset = aOffsets.Side(side);
1923     uint32_t hc1 = SideToHalfCorner(side, false, false);
1924     uint32_t hc2 = SideToHalfCorner(side, true, false);
1925     aRadii[hc1] = std::max(0, aRadii[hc1] - offset);
1926     aRadii[hc2] = std::max(0, aRadii[hc2] - offset);
1927   }
1928 }
1929 
1930 /* static */
OutsetBorderRadii(nscoord aRadii[8],const nsMargin & aOffsets)1931 void nsIFrame::OutsetBorderRadii(nscoord aRadii[8], const nsMargin& aOffsets) {
1932   auto AdjustOffset = [](const uint32_t aRadius, const nscoord aOffset) {
1933     // Implement the cubic formula to adjust offset when aOffset > 0 and
1934     // aRadius / aOffset < 1.
1935     // https://drafts.csswg.org/css-shapes/#valdef-shape-box-margin-box
1936     if (aOffset > 0) {
1937       const double ratio = aRadius / double(aOffset);
1938       if (ratio < 1.0) {
1939         return nscoord(aOffset * (1.0 + std::pow(ratio - 1, 3)));
1940       }
1941     }
1942     return aOffset;
1943   };
1944 
1945   for (const auto side : mozilla::AllPhysicalSides()) {
1946     const nscoord offset = aOffsets.Side(side);
1947     const uint32_t hc1 = SideToHalfCorner(side, false, false);
1948     const uint32_t hc2 = SideToHalfCorner(side, true, false);
1949     if (aRadii[hc1] > 0) {
1950       const nscoord offset1 = AdjustOffset(aRadii[hc1], offset);
1951       aRadii[hc1] = std::max(0, aRadii[hc1] + offset1);
1952     }
1953     if (aRadii[hc2] > 0) {
1954       const nscoord offset2 = AdjustOffset(aRadii[hc2], offset);
1955       aRadii[hc2] = std::max(0, aRadii[hc2] + offset2);
1956     }
1957   }
1958 }
1959 
RadiiAreDefinitelyZero(const BorderRadius & aBorderRadius)1960 static inline bool RadiiAreDefinitelyZero(const BorderRadius& aBorderRadius) {
1961   for (const auto corner : mozilla::AllPhysicalHalfCorners()) {
1962     if (!aBorderRadius.Get(corner).IsDefinitelyZero()) {
1963       return false;
1964     }
1965   }
1966   return true;
1967 }
1968 
1969 /* virtual */
GetBorderRadii(const nsSize & aFrameSize,const nsSize & aBorderArea,Sides aSkipSides,nscoord aRadii[8]) const1970 bool nsIFrame::GetBorderRadii(const nsSize& aFrameSize,
1971                               const nsSize& aBorderArea, Sides aSkipSides,
1972                               nscoord aRadii[8]) const {
1973   if (!mMayHaveRoundedCorners) {
1974     memset(aRadii, 0, sizeof(nscoord) * 8);
1975     return false;
1976   }
1977 
1978   if (IsThemed()) {
1979     // When we're themed, the native theme code draws the border and
1980     // background, and therefore it doesn't make sense to tell other
1981     // code that's interested in border-radius that we have any radii.
1982     //
1983     // In an ideal world, we might have a way for the them to tell us an
1984     // border radius, but since we don't, we're better off assuming
1985     // zero.
1986     for (const auto corner : mozilla::AllPhysicalHalfCorners()) {
1987       aRadii[corner] = 0;
1988     }
1989     return false;
1990   }
1991 
1992   const auto& radii = StyleBorder()->mBorderRadius;
1993   const bool hasRadii =
1994       ComputeBorderRadii(radii, aFrameSize, aBorderArea, aSkipSides, aRadii);
1995   if (!hasRadii) {
1996     // TODO(emilio): Maybe we can just remove this bit and do the
1997     // IsDefinitelyZero check unconditionally. That should still avoid most of
1998     // the work, though maybe not the cache miss of going through the style and
1999     // the border struct.
2000     const_cast<nsIFrame*>(this)->mMayHaveRoundedCorners =
2001         !RadiiAreDefinitelyZero(radii);
2002   }
2003   return hasRadii;
2004 }
2005 
GetBorderRadii(nscoord aRadii[8]) const2006 bool nsIFrame::GetBorderRadii(nscoord aRadii[8]) const {
2007   nsSize sz = GetSize();
2008   return GetBorderRadii(sz, sz, GetSkipSides(), aRadii);
2009 }
2010 
GetMarginBoxBorderRadii(nscoord aRadii[8]) const2011 bool nsIFrame::GetMarginBoxBorderRadii(nscoord aRadii[8]) const {
2012   return GetBoxBorderRadii(aRadii, GetUsedMargin(), true);
2013 }
2014 
GetPaddingBoxBorderRadii(nscoord aRadii[8]) const2015 bool nsIFrame::GetPaddingBoxBorderRadii(nscoord aRadii[8]) const {
2016   return GetBoxBorderRadii(aRadii, GetUsedBorder(), false);
2017 }
2018 
GetContentBoxBorderRadii(nscoord aRadii[8]) const2019 bool nsIFrame::GetContentBoxBorderRadii(nscoord aRadii[8]) const {
2020   return GetBoxBorderRadii(aRadii, GetUsedBorderAndPadding(), false);
2021 }
2022 
GetBoxBorderRadii(nscoord aRadii[8],nsMargin aOffset,bool aIsOutset) const2023 bool nsIFrame::GetBoxBorderRadii(nscoord aRadii[8], nsMargin aOffset,
2024                                  bool aIsOutset) const {
2025   if (!GetBorderRadii(aRadii)) return false;
2026   if (aIsOutset) {
2027     OutsetBorderRadii(aRadii, aOffset);
2028   } else {
2029     InsetBorderRadii(aRadii, aOffset);
2030   }
2031   for (const auto corner : mozilla::AllPhysicalHalfCorners()) {
2032     if (aRadii[corner]) return true;
2033   }
2034   return false;
2035 }
2036 
GetShapeBoxBorderRadii(nscoord aRadii[8]) const2037 bool nsIFrame::GetShapeBoxBorderRadii(nscoord aRadii[8]) const {
2038   using Tag = StyleShapeOutside::Tag;
2039   auto& shapeOutside = StyleDisplay()->mShapeOutside;
2040   auto box = StyleShapeBox::MarginBox;
2041   switch (shapeOutside.tag) {
2042     case Tag::Image:
2043     case Tag::None:
2044       return false;
2045     case Tag::Box:
2046       box = shapeOutside.AsBox();
2047       break;
2048     case Tag::Shape:
2049       box = shapeOutside.AsShape()._1;
2050       break;
2051   }
2052 
2053   switch (box) {
2054     case StyleShapeBox::ContentBox:
2055       return GetContentBoxBorderRadii(aRadii);
2056     case StyleShapeBox::PaddingBox:
2057       return GetPaddingBoxBorderRadii(aRadii);
2058     case StyleShapeBox::BorderBox:
2059       return GetBorderRadii(aRadii);
2060     case StyleShapeBox::MarginBox:
2061       return GetMarginBoxBorderRadii(aRadii);
2062     default:
2063       MOZ_ASSERT_UNREACHABLE("Unexpected box value");
2064       return false;
2065   }
2066 }
2067 
GetAdditionalComputedStyle(int32_t aIndex) const2068 ComputedStyle* nsIFrame::GetAdditionalComputedStyle(int32_t aIndex) const {
2069   MOZ_ASSERT(aIndex >= 0, "invalid index number");
2070   return nullptr;
2071 }
2072 
SetAdditionalComputedStyle(int32_t aIndex,ComputedStyle * aComputedStyle)2073 void nsIFrame::SetAdditionalComputedStyle(int32_t aIndex,
2074                                           ComputedStyle* aComputedStyle) {
2075   MOZ_ASSERT(aIndex >= 0, "invalid index number");
2076 }
2077 
GetLogicalBaseline(WritingMode aWritingMode) const2078 nscoord nsIFrame::GetLogicalBaseline(WritingMode aWritingMode) const {
2079   NS_ASSERTION(!NS_SUBTREE_DIRTY(this), "frame must not be dirty");
2080   // Baseline for inverted line content is the top (block-start) margin edge,
2081   // as the frame is in effect "flipped" for alignment purposes.
2082   if (aWritingMode.IsLineInverted()) {
2083     return -GetLogicalUsedMargin(aWritingMode).BStart(aWritingMode);
2084   }
2085   // Otherwise, the bottom margin edge, per CSS2.1's definition of the
2086   // 'baseline' value of 'vertical-align'.
2087   return BSize(aWritingMode) +
2088          GetLogicalUsedMargin(aWritingMode).BEnd(aWritingMode);
2089 }
2090 
GetChildList(ChildListID aListID) const2091 const nsFrameList& nsIFrame::GetChildList(ChildListID aListID) const {
2092   if (IsAbsoluteContainer() && aListID == GetAbsoluteListID()) {
2093     return GetAbsoluteContainingBlock()->GetChildList();
2094   } else {
2095     return nsFrameList::EmptyList();
2096   }
2097 }
2098 
GetChildLists(nsTArray<ChildList> * aLists) const2099 void nsIFrame::GetChildLists(nsTArray<ChildList>* aLists) const {
2100   if (IsAbsoluteContainer()) {
2101     nsFrameList absoluteList = GetAbsoluteContainingBlock()->GetChildList();
2102     absoluteList.AppendIfNonempty(aLists, GetAbsoluteListID());
2103   }
2104 }
2105 
CrossDocChildLists()2106 AutoTArray<nsIFrame::ChildList, 4> nsIFrame::CrossDocChildLists() {
2107   AutoTArray<ChildList, 4> childLists;
2108   nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(this);
2109   if (subdocumentFrame) {
2110     // Descend into the subdocument
2111     nsIFrame* root = subdocumentFrame->GetSubdocumentRootFrame();
2112     if (root) {
2113       childLists.EmplaceBack(
2114           nsFrameList(root, nsLayoutUtils::GetLastSibling(root)),
2115           nsIFrame::kPrincipalList);
2116     }
2117   }
2118 
2119   GetChildLists(&childLists);
2120   return childLists;
2121 }
2122 
GetVisibility() const2123 Visibility nsIFrame::GetVisibility() const {
2124   if (!(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED)) {
2125     return Visibility::Untracked;
2126   }
2127 
2128   bool isSet = false;
2129   uint32_t visibleCount = GetProperty(VisibilityStateProperty(), &isSet);
2130 
2131   MOZ_ASSERT(isSet,
2132              "Should have a VisibilityStateProperty value "
2133              "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
2134 
2135   return visibleCount > 0 ? Visibility::ApproximatelyVisible
2136                           : Visibility::ApproximatelyNonVisible;
2137 }
2138 
UpdateVisibilitySynchronously()2139 void nsIFrame::UpdateVisibilitySynchronously() {
2140   mozilla::PresShell* presShell = PresShell();
2141   if (!presShell) {
2142     return;
2143   }
2144 
2145   if (presShell->AssumeAllFramesVisible()) {
2146     presShell->EnsureFrameInApproximatelyVisibleList(this);
2147     return;
2148   }
2149 
2150   bool visible = StyleVisibility()->IsVisible();
2151   nsIFrame* f = GetParent();
2152   nsRect rect = GetRectRelativeToSelf();
2153   nsIFrame* rectFrame = this;
2154   while (f && visible) {
2155     nsIScrollableFrame* sf = do_QueryFrame(f);
2156     if (sf) {
2157       nsRect transformedRect =
2158           nsLayoutUtils::TransformFrameRectToAncestor(rectFrame, rect, f);
2159       if (!sf->IsRectNearlyVisible(transformedRect)) {
2160         visible = false;
2161         break;
2162       }
2163 
2164       // In this code we're trying to synchronously update *approximate*
2165       // visibility. (In the future we may update precise visibility here as
2166       // well, which is why the method name does not contain 'approximate'.) The
2167       // IsRectNearlyVisible() check above tells us that the rect we're checking
2168       // is approximately visible within the scrollframe, but we still need to
2169       // ensure that, even if it was scrolled into view, it'd be visible when we
2170       // consider the rest of the document. To do that, we move transformedRect
2171       // to be contained in the scrollport as best we can (it might not fit) to
2172       // pretend that it was scrolled into view.
2173       rect = transformedRect.MoveInsideAndClamp(sf->GetScrollPortRect());
2174       rectFrame = f;
2175     }
2176     nsIFrame* parent = f->GetParent();
2177     if (!parent) {
2178       parent = nsLayoutUtils::GetCrossDocParentFrame(f);
2179       if (parent && parent->PresContext()->IsChrome()) {
2180         break;
2181       }
2182     }
2183     f = parent;
2184   }
2185 
2186   if (visible) {
2187     presShell->EnsureFrameInApproximatelyVisibleList(this);
2188   } else {
2189     presShell->RemoveFrameFromApproximatelyVisibleList(this);
2190   }
2191 }
2192 
EnableVisibilityTracking()2193 void nsIFrame::EnableVisibilityTracking() {
2194   if (GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED) {
2195     return;  // Nothing to do.
2196   }
2197 
2198   MOZ_ASSERT(!HasProperty(VisibilityStateProperty()),
2199              "Shouldn't have a VisibilityStateProperty value "
2200              "if NS_FRAME_VISIBILITY_IS_TRACKED is not set");
2201 
2202   // Add the state bit so we know to track visibility for this frame, and
2203   // initialize the frame property.
2204   AddStateBits(NS_FRAME_VISIBILITY_IS_TRACKED);
2205   SetProperty(VisibilityStateProperty(), 0);
2206 
2207   mozilla::PresShell* presShell = PresShell();
2208   if (!presShell) {
2209     return;
2210   }
2211 
2212   // Schedule a visibility update. This method will virtually always be called
2213   // when layout has changed anyway, so it's very unlikely that any additional
2214   // visibility updates will be triggered by this, but this way we guarantee
2215   // that if this frame is currently visible we'll eventually find out.
2216   presShell->ScheduleApproximateFrameVisibilityUpdateSoon();
2217 }
2218 
DisableVisibilityTracking()2219 void nsIFrame::DisableVisibilityTracking() {
2220   if (!(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED)) {
2221     return;  // Nothing to do.
2222   }
2223 
2224   bool isSet = false;
2225   uint32_t visibleCount = TakeProperty(VisibilityStateProperty(), &isSet);
2226 
2227   MOZ_ASSERT(isSet,
2228              "Should have a VisibilityStateProperty value "
2229              "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
2230 
2231   RemoveStateBits(NS_FRAME_VISIBILITY_IS_TRACKED);
2232 
2233   if (visibleCount == 0) {
2234     return;  // We were nonvisible.
2235   }
2236 
2237   // We were visible, so send an OnVisibilityChange() notification.
2238   OnVisibilityChange(Visibility::ApproximatelyNonVisible);
2239 }
2240 
DecApproximateVisibleCount(const Maybe<OnNonvisible> & aNonvisibleAction)2241 void nsIFrame::DecApproximateVisibleCount(
2242     const Maybe<OnNonvisible>& aNonvisibleAction
2243     /* = Nothing() */) {
2244   MOZ_ASSERT(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED);
2245 
2246   bool isSet = false;
2247   uint32_t visibleCount = GetProperty(VisibilityStateProperty(), &isSet);
2248 
2249   MOZ_ASSERT(isSet,
2250              "Should have a VisibilityStateProperty value "
2251              "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
2252   MOZ_ASSERT(visibleCount > 0,
2253              "Frame is already nonvisible and we're "
2254              "decrementing its visible count?");
2255 
2256   visibleCount--;
2257   SetProperty(VisibilityStateProperty(), visibleCount);
2258   if (visibleCount > 0) {
2259     return;
2260   }
2261 
2262   // We just became nonvisible, so send an OnVisibilityChange() notification.
2263   OnVisibilityChange(Visibility::ApproximatelyNonVisible, aNonvisibleAction);
2264 }
2265 
IncApproximateVisibleCount()2266 void nsIFrame::IncApproximateVisibleCount() {
2267   MOZ_ASSERT(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED);
2268 
2269   bool isSet = false;
2270   uint32_t visibleCount = GetProperty(VisibilityStateProperty(), &isSet);
2271 
2272   MOZ_ASSERT(isSet,
2273              "Should have a VisibilityStateProperty value "
2274              "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
2275 
2276   visibleCount++;
2277   SetProperty(VisibilityStateProperty(), visibleCount);
2278   if (visibleCount > 1) {
2279     return;
2280   }
2281 
2282   // We just became visible, so send an OnVisibilityChange() notification.
2283   OnVisibilityChange(Visibility::ApproximatelyVisible);
2284 }
2285 
OnVisibilityChange(Visibility aNewVisibility,const Maybe<OnNonvisible> & aNonvisibleAction)2286 void nsIFrame::OnVisibilityChange(Visibility aNewVisibility,
2287                                   const Maybe<OnNonvisible>& aNonvisibleAction
2288                                   /* = Nothing() */) {
2289   // XXX(seth): In bug 1218990 we'll implement visibility tracking for CSS
2290   // images here.
2291 }
2292 
GetActiveSelectionFrame(nsPresContext * aPresContext,nsIFrame * aFrame)2293 static nsIFrame* GetActiveSelectionFrame(nsPresContext* aPresContext,
2294                                          nsIFrame* aFrame) {
2295   nsIContent* capturingContent = PresShell::GetCapturingContent();
2296   if (capturingContent) {
2297     nsIFrame* activeFrame = aPresContext->GetPrimaryFrameFor(capturingContent);
2298     return activeFrame ? activeFrame : aFrame;
2299   }
2300 
2301   return aFrame;
2302 }
2303 
DetermineDisplaySelection()2304 int16_t nsIFrame::DetermineDisplaySelection() {
2305   int16_t selType = nsISelectionController::SELECTION_OFF;
2306 
2307   nsCOMPtr<nsISelectionController> selCon;
2308   nsresult result =
2309       GetSelectionController(PresContext(), getter_AddRefs(selCon));
2310   if (NS_SUCCEEDED(result) && selCon) {
2311     result = selCon->GetDisplaySelection(&selType);
2312     if (NS_SUCCEEDED(result) &&
2313         (selType != nsISelectionController::SELECTION_OFF)) {
2314       // Check whether style allows selection.
2315       if (!IsSelectable(nullptr)) {
2316         selType = nsISelectionController::SELECTION_OFF;
2317       }
2318     }
2319   }
2320   return selType;
2321 }
2322 
2323 class nsDisplaySelectionOverlay : public nsPaintedDisplayItem {
2324  public:
2325   /**
2326    * @param aSelectionValue nsISelectionController::getDisplaySelection.
2327    */
nsDisplaySelectionOverlay(nsDisplayListBuilder * aBuilder,nsFrame * aFrame,int16_t aSelectionValue)2328   nsDisplaySelectionOverlay(nsDisplayListBuilder* aBuilder, nsFrame* aFrame,
2329                             int16_t aSelectionValue)
2330       : nsPaintedDisplayItem(aBuilder, aFrame),
2331         mSelectionValue(aSelectionValue) {
2332     MOZ_COUNT_CTOR(nsDisplaySelectionOverlay);
2333   }
2334   MOZ_COUNTED_DTOR_OVERRIDE(nsDisplaySelectionOverlay)
2335 
2336   virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
2337   bool CreateWebRenderCommands(
2338       mozilla::wr::DisplayListBuilder& aBuilder,
2339       mozilla::wr::IpcResourceUpdateQueue& aResources,
2340       const StackingContextHelper& aSc,
2341       mozilla::layers::RenderRootStateManager* aManager,
2342       nsDisplayListBuilder* aDisplayListBuilder) override;
2343   NS_DISPLAY_DECL_NAME("SelectionOverlay", TYPE_SELECTION_OVERLAY)
2344  private:
2345   DeviceColor ComputeColor() const;
2346 
2347   static DeviceColor ComputeColorFromSelectionStyle(ComputedStyle&);
2348   static DeviceColor ApplyTransparencyIfNecessary(nscolor);
2349 
2350   // nsISelectionController::getDisplaySelection.
2351   int16_t mSelectionValue;
2352 };
2353 
ApplyTransparencyIfNecessary(nscolor aColor)2354 DeviceColor nsDisplaySelectionOverlay::ApplyTransparencyIfNecessary(
2355     nscolor aColor) {
2356   // If it has already alpha, leave it like that.
2357   if (NS_GET_A(aColor) != 255) {
2358     return ToDeviceColor(aColor);
2359   }
2360 
2361   // NOTE(emilio): Blink and WebKit do something slightly different here, and
2362   // blend the color with white instead, both for overlays and text backgrounds.
2363   auto color = sRGBColor::FromABGR(aColor);
2364   color.a = 0.5;
2365   return ToDeviceColor(color);
2366 }
2367 
ComputeColorFromSelectionStyle(ComputedStyle & aStyle)2368 DeviceColor nsDisplaySelectionOverlay::ComputeColorFromSelectionStyle(
2369     ComputedStyle& aStyle) {
2370   return ApplyTransparencyIfNecessary(
2371       aStyle.GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor));
2372 }
2373 
ComputeColor() const2374 DeviceColor nsDisplaySelectionOverlay::ComputeColor() const {
2375   LookAndFeel::ColorID colorID;
2376   if (RefPtr<ComputedStyle> style =
2377           mFrame->ComputeSelectionStyle(mSelectionValue)) {
2378     return ComputeColorFromSelectionStyle(*style);
2379   }
2380   if (mSelectionValue == nsISelectionController::SELECTION_ON) {
2381     colorID = LookAndFeel::ColorID::TextSelectBackground;
2382   } else if (mSelectionValue == nsISelectionController::SELECTION_ATTENTION) {
2383     colorID = LookAndFeel::ColorID::TextSelectBackgroundAttention;
2384   } else {
2385     colorID = LookAndFeel::ColorID::TextSelectBackgroundDisabled;
2386   }
2387 
2388   return ApplyTransparencyIfNecessary(
2389       LookAndFeel::GetColor(colorID, NS_RGB(255, 255, 255)));
2390 }
2391 
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)2392 void nsDisplaySelectionOverlay::Paint(nsDisplayListBuilder* aBuilder,
2393                                       gfxContext* aCtx) {
2394   DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
2395   ColorPattern color(ComputeColor());
2396 
2397   nsIntRect pxRect = GetPaintRect().ToOutsidePixels(
2398       mFrame->PresContext()->AppUnitsPerDevPixel());
2399   Rect rect(pxRect.x, pxRect.y, pxRect.width, pxRect.height);
2400   MaybeSnapToDevicePixels(rect, aDrawTarget, true);
2401 
2402   aDrawTarget.FillRect(rect, color);
2403 }
2404 
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)2405 bool nsDisplaySelectionOverlay::CreateWebRenderCommands(
2406     mozilla::wr::DisplayListBuilder& aBuilder,
2407     mozilla::wr::IpcResourceUpdateQueue& aResources,
2408     const StackingContextHelper& aSc,
2409     mozilla::layers::RenderRootStateManager* aManager,
2410     nsDisplayListBuilder* aDisplayListBuilder) {
2411   wr::LayoutRect bounds = wr::ToLayoutRect(LayoutDeviceRect::FromAppUnits(
2412       nsRect(ToReferenceFrame(), Frame()->GetSize()),
2413       mFrame->PresContext()->AppUnitsPerDevPixel()));
2414   aBuilder.PushRect(bounds, bounds, !BackfaceIsHidden(),
2415                     wr::ToColorF(ComputeColor()));
2416   return true;
2417 }
2418 
FindElementAncestorForMozSelection(nsIContent * aContent)2419 static Element* FindElementAncestorForMozSelection(nsIContent* aContent) {
2420   NS_ENSURE_TRUE(aContent, nullptr);
2421   while (aContent && aContent->IsInNativeAnonymousSubtree()) {
2422     aContent = aContent->GetClosestNativeAnonymousSubtreeRootParent();
2423   }
2424   NS_ASSERTION(aContent, "aContent isn't in non-anonymous tree?");
2425   return aContent ? aContent->GetAsElementOrParentElement() : nullptr;
2426 }
2427 
ComputeSelectionStyle(int16_t aSelectionStatus) const2428 already_AddRefed<ComputedStyle> nsIFrame::ComputeSelectionStyle(
2429     int16_t aSelectionStatus) const {
2430   // Just bail out if not a selection-status that ::selection applies to.
2431   if (aSelectionStatus != nsISelectionController::SELECTION_ON &&
2432       aSelectionStatus != nsISelectionController::SELECTION_DISABLED) {
2433     return nullptr;
2434   }
2435   // When in high-contrast mode, the style system ends up ignoring the color
2436   // declarations, which means that the ::selection style becomes the inherited
2437   // color, and default background. That's no good.
2438   if (!PresContext()->PrefSheetPrefs().mUseDocumentColors) {
2439     return nullptr;
2440   }
2441   Element* element = FindElementAncestorForMozSelection(GetContent());
2442   if (!element) {
2443     return nullptr;
2444   }
2445   return PresContext()->StyleSet()->ProbePseudoElementStyle(
2446       *element, PseudoStyleType::selection, Style());
2447 }
2448 
2449 template <typename SizeOrMaxSize>
IsIntrinsicKeyword(const SizeOrMaxSize & aSize)2450 static inline bool IsIntrinsicKeyword(const SizeOrMaxSize& aSize) {
2451   if (!aSize.IsExtremumLength()) {
2452     return false;
2453   }
2454 
2455   // All of the keywords except for '-moz-available' depend on intrinsic sizes.
2456   return aSize.AsExtremumLength() != StyleExtremumLength::MozAvailable;
2457 }
2458 
CanBeDynamicReflowRoot() const2459 bool nsIFrame::CanBeDynamicReflowRoot() const {
2460   if (!StaticPrefs::layout_dynamic_reflow_roots_enabled()) {
2461     return false;
2462   }
2463 
2464   auto& display = *StyleDisplay();
2465   if (IsFrameOfType(nsIFrame::eLineParticipant) ||
2466       nsStyleDisplay::IsRubyDisplayType(display.mDisplay) ||
2467       display.DisplayOutside() == StyleDisplayOutside::InternalTable ||
2468       display.DisplayInside() == StyleDisplayInside::Table ||
2469       (GetParent() && GetParent()->IsXULBoxFrame())) {
2470     // We have a display type where 'width' and 'height' don't actually set the
2471     // width or height (i.e., the size depends on content).
2472     MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT),
2473                "should not have dynamic reflow root bit");
2474     return false;
2475   }
2476 
2477   // We can't serve as a dynamic reflow root if our used 'width' and 'height'
2478   // might be influenced by content.
2479   //
2480   // FIXME: For display:block, we should probably optimize inline-size: auto.
2481   // FIXME: Other flex and grid cases?
2482   auto& pos = *StylePosition();
2483   const auto& width = pos.mWidth;
2484   const auto& height = pos.mHeight;
2485   if (!width.IsLengthPercentage() || width.HasPercent() ||
2486       !height.IsLengthPercentage() || height.HasPercent() ||
2487       IsIntrinsicKeyword(pos.mMinWidth) || IsIntrinsicKeyword(pos.mMaxWidth) ||
2488       IsIntrinsicKeyword(pos.mMinHeight) ||
2489       IsIntrinsicKeyword(pos.mMaxHeight) ||
2490       ((pos.mMinWidth.IsAuto() || pos.mMinHeight.IsAuto()) &&
2491        IsFlexOrGridItem())) {
2492     return false;
2493   }
2494 
2495   // If our flex-basis is 'auto', it'll defer to 'width' (or 'height') which
2496   // we've already checked. Otherwise, it preempts them, so we need to
2497   // perform the same "could-this-value-be-influenced-by-content" checks that
2498   // we performed for 'width' and 'height' above.
2499   if (IsFlexItem()) {
2500     const auto& flexBasis = pos.mFlexBasis;
2501     if (!flexBasis.IsAuto()) {
2502       if (!flexBasis.IsSize() || !flexBasis.AsSize().IsLengthPercentage() ||
2503           flexBasis.AsSize().HasPercent()) {
2504         return false;
2505       }
2506     }
2507   }
2508 
2509   if (!IsFixedPosContainingBlock()) {
2510     // We can't treat this frame as a reflow root, since dynamic changes
2511     // to absolutely-positioned frames inside of it require that we
2512     // reflow the placeholder before we reflow the absolutely positioned
2513     // frame.
2514     // FIXME:  Alternatively, we could sort the reflow roots in
2515     // PresShell::ProcessReflowCommands by depth in the tree, from
2516     // deepest to least deep.  However, for performance (FIXME) we
2517     // should really be sorting them in the opposite order!
2518     return false;
2519   }
2520 
2521   // If we participate in a container's block reflow context, or margins
2522   // can collapse through us, we can't be a dynamic reflow root.
2523   if (IsBlockFrameOrSubclass() &&
2524       !HasAllStateBits(NS_BLOCK_FLOAT_MGR | NS_BLOCK_MARGIN_ROOT)) {
2525     return false;
2526   }
2527 
2528   // Subgrids are never reflow roots, but 'contain:layout/paint' prevents
2529   // creating a subgrid in the first place.
2530   if (pos.mGridTemplateColumns.IsSubgrid() ||
2531       pos.mGridTemplateRows.IsSubgrid()) {
2532     // NOTE: we could check that 'display' of our parent's primary frame is
2533     // '[inline-]grid' here but that's probably not worth it in practice.
2534     if (!display.IsContainLayout() && !display.IsContainPaint()) {
2535       return false;
2536     }
2537   }
2538 
2539   return true;
2540 }
2541 
2542 /********************************************************
2543  * Refreshes each content's frame
2544  *********************************************************/
2545 
DisplaySelectionOverlay(nsDisplayListBuilder * aBuilder,nsDisplayList * aList,uint16_t aContentType)2546 void nsContainerFrame::DisplaySelectionOverlay(nsDisplayListBuilder* aBuilder,
2547                                                nsDisplayList* aList,
2548                                                uint16_t aContentType) {
2549   if (!IsSelected() || !IsVisibleForPainting()) {
2550     return;
2551   }
2552 
2553   int16_t displaySelection = PresShell()->GetSelectionFlags();
2554   if (!(displaySelection & aContentType)) {
2555     return;
2556   }
2557 
2558   const nsFrameSelection* frameSelection = GetConstFrameSelection();
2559   int16_t selectionValue = frameSelection->GetDisplaySelection();
2560 
2561   if (selectionValue <= nsISelectionController::SELECTION_HIDDEN) {
2562     return;  // selection is hidden or off
2563   }
2564 
2565   nsIContent* newContent = mContent->GetParent();
2566 
2567   // check to see if we are anonymous content
2568   int32_t offset = 0;
2569   if (newContent) {
2570     // XXXbz there has GOT to be a better way of determining this!
2571     offset = newContent->ComputeIndexOf(mContent);
2572   }
2573 
2574   // look up to see what selection(s) are on this frame
2575   UniquePtr<SelectionDetails> details =
2576       frameSelection->LookUpSelection(newContent, offset, 1, false);
2577   if (!details) return;
2578 
2579   bool normal = false;
2580   for (SelectionDetails* sd = details.get(); sd; sd = sd->mNext.get()) {
2581     if (sd->mSelectionType == SelectionType::eNormal) {
2582       normal = true;
2583     }
2584   }
2585 
2586   if (!normal && aContentType == nsISelectionDisplay::DISPLAY_IMAGES) {
2587     // Don't overlay an image if it's not in the primary selection.
2588     return;
2589   }
2590 
2591   aList->AppendNewToTop<nsDisplaySelectionOverlay>(aBuilder, this,
2592                                                    selectionValue);
2593 }
2594 
DisplayOutlineUnconditional(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)2595 void nsFrame::DisplayOutlineUnconditional(nsDisplayListBuilder* aBuilder,
2596                                           const nsDisplayListSet& aLists) {
2597   // Per https://drafts.csswg.org/css-tables-3/#global-style-overrides:
2598   // "All css properties of table-column and table-column-group boxes are
2599   // ignored, except when explicitly specified by this specification."
2600   // CSS outlines fall into this category, so we skip them on these boxes.
2601   MOZ_ASSERT(!IsTableColGroupFrame() && !IsTableColFrame());
2602   const auto& outline = *StyleOutline();
2603 
2604   if (!outline.ShouldPaintOutline()) {
2605     return;
2606   }
2607 
2608   // Outlines are painted by the table wrapper frame.
2609   if (IsTableFrame()) {
2610     return;
2611   }
2612 
2613   if (HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT) &&
2614       GetScrollableOverflowRect().IsEmpty()) {
2615     // Skip parts of IB-splits with an empty overflow rect, see bug 434301.
2616     // We may still want to fix some of the overflow area calculations over in
2617     // that bug.
2618     return;
2619   }
2620 
2621   // We don't display outline-style: auto on themed frames that have their own
2622   // focus indicators.
2623   if (outline.mOutlineStyle.IsAuto()) {
2624     auto* disp = StyleDisplay();
2625     if (IsThemed(disp) &&
2626         PresContext()->Theme()->ThemeDrawsFocusForWidget(disp->mAppearance)) {
2627       return;
2628     }
2629   }
2630 
2631   aLists.Outlines()->AppendNewToTop<nsDisplayOutline>(aBuilder, this);
2632 }
2633 
DisplayOutline(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)2634 void nsFrame::DisplayOutline(nsDisplayListBuilder* aBuilder,
2635                              const nsDisplayListSet& aLists) {
2636   if (!IsVisibleForPainting()) return;
2637 
2638   DisplayOutlineUnconditional(aBuilder, aLists);
2639 }
2640 
DisplayInsetBoxShadowUnconditional(nsDisplayListBuilder * aBuilder,nsDisplayList * aList)2641 void nsFrame::DisplayInsetBoxShadowUnconditional(nsDisplayListBuilder* aBuilder,
2642                                                  nsDisplayList* aList) {
2643   // XXXbz should box-shadow for rows/rowgroups/columns/colgroups get painted
2644   // just because we're visible?  Or should it depend on the cell visibility
2645   // when we're not the whole table?
2646   const auto* effects = StyleEffects();
2647   if (effects->HasBoxShadowWithInset(true)) {
2648     aList->AppendNewToTop<nsDisplayBoxShadowInner>(aBuilder, this);
2649   }
2650 }
2651 
DisplayInsetBoxShadow(nsDisplayListBuilder * aBuilder,nsDisplayList * aList)2652 void nsFrame::DisplayInsetBoxShadow(nsDisplayListBuilder* aBuilder,
2653                                     nsDisplayList* aList) {
2654   if (!IsVisibleForPainting()) return;
2655 
2656   DisplayInsetBoxShadowUnconditional(aBuilder, aList);
2657 }
2658 
DisplayOutsetBoxShadowUnconditional(nsDisplayListBuilder * aBuilder,nsDisplayList * aList)2659 void nsFrame::DisplayOutsetBoxShadowUnconditional(
2660     nsDisplayListBuilder* aBuilder, nsDisplayList* aList) {
2661   // XXXbz should box-shadow for rows/rowgroups/columns/colgroups get painted
2662   // just because we're visible?  Or should it depend on the cell visibility
2663   // when we're not the whole table?
2664   const auto* effects = StyleEffects();
2665   if (effects->HasBoxShadowWithInset(false)) {
2666     aList->AppendNewToTop<nsDisplayBoxShadowOuter>(aBuilder, this);
2667   }
2668 }
2669 
DisplayOutsetBoxShadow(nsDisplayListBuilder * aBuilder,nsDisplayList * aList)2670 void nsFrame::DisplayOutsetBoxShadow(nsDisplayListBuilder* aBuilder,
2671                                      nsDisplayList* aList) {
2672   if (!IsVisibleForPainting()) return;
2673 
2674   DisplayOutsetBoxShadowUnconditional(aBuilder, aList);
2675 }
2676 
DisplayCaret(nsDisplayListBuilder * aBuilder,nsDisplayList * aList)2677 void nsIFrame::DisplayCaret(nsDisplayListBuilder* aBuilder,
2678                             nsDisplayList* aList) {
2679   if (!IsVisibleForPainting()) return;
2680 
2681   aList->AppendNewToTop<nsDisplayCaret>(aBuilder, this);
2682 }
2683 
GetCaretColorAt(int32_t aOffset)2684 nscolor nsIFrame::GetCaretColorAt(int32_t aOffset) {
2685   return nsLayoutUtils::GetColor(this, &nsStyleUI::mCaretColor);
2686 }
2687 
DisplayBackgroundUnconditional(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists,bool aForceBackground)2688 bool nsFrame::DisplayBackgroundUnconditional(nsDisplayListBuilder* aBuilder,
2689                                              const nsDisplayListSet& aLists,
2690                                              bool aForceBackground) {
2691   const bool hitTesting = aBuilder->IsForEventDelivery();
2692   if (hitTesting && !aBuilder->HitTestIsForVisibility()) {
2693     // For hit-testing, we generally just need a light-weight data structure
2694     // like nsDisplayEventReceiver. But if the hit-testing is for visibility,
2695     // then we need to know the opaque region in order to determine whether to
2696     // stop or not.
2697     aLists.BorderBackground()->AppendNewToTop<nsDisplayEventReceiver>(aBuilder,
2698                                                                       this);
2699     return false;
2700   }
2701 
2702   // Here we don't try to detect background propagation. Frames that might
2703   // receive a propagated background should just set aForceBackground to
2704   // true.
2705   if (hitTesting || aForceBackground ||
2706       !StyleBackground()->IsTransparent(this) ||
2707       StyleDisplay()->HasAppearance()) {
2708     return nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
2709         aBuilder, this,
2710         GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this),
2711         aLists.BorderBackground());
2712   }
2713 
2714   return false;
2715 }
2716 
DisplayBorderBackgroundOutline(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists,bool aForceBackground)2717 void nsFrame::DisplayBorderBackgroundOutline(nsDisplayListBuilder* aBuilder,
2718                                              const nsDisplayListSet& aLists,
2719                                              bool aForceBackground) {
2720   // The visibility check belongs here since child elements have the
2721   // opportunity to override the visibility property and display even if
2722   // their parent is hidden.
2723   if (!IsVisibleForPainting()) {
2724     return;
2725   }
2726 
2727   DisplayOutsetBoxShadowUnconditional(aBuilder, aLists.BorderBackground());
2728 
2729   bool bgIsThemed =
2730       DisplayBackgroundUnconditional(aBuilder, aLists, aForceBackground);
2731 
2732   DisplayInsetBoxShadowUnconditional(aBuilder, aLists.BorderBackground());
2733 
2734   // If there's a themed background, we should not create a border item.
2735   // It won't be rendered.
2736   // Don't paint borders for tables here, since they paint them in a different
2737   // order.
2738   if (!bgIsThemed && StyleBorder()->HasBorder() && !IsTableFrame()) {
2739     aLists.BorderBackground()->AppendNewToTop<nsDisplayBorder>(aBuilder, this);
2740   }
2741 
2742   DisplayOutlineUnconditional(aBuilder, aLists);
2743 }
2744 
IsSVGContentWithCSSClip(const nsIFrame * aFrame)2745 inline static bool IsSVGContentWithCSSClip(const nsIFrame* aFrame) {
2746   // The CSS spec says that the 'clip' property only applies to absolutely
2747   // positioned elements, whereas the SVG spec says that it applies to SVG
2748   // elements regardless of the value of the 'position' property. Here we obey
2749   // the CSS spec for outer-<svg> (since that's what we generally do), but
2750   // obey the SVG spec for other SVG elements to which 'clip' applies.
2751   return (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) &&
2752          aFrame->GetContent()->IsAnyOfSVGElements(nsGkAtoms::svg,
2753                                                   nsGkAtoms::foreignObject);
2754 }
2755 
FormsBackdropRoot(const nsStyleDisplay * aStyleDisplay,const nsStyleEffects * aStyleEffects,const nsStyleSVGReset * aStyleSVGReset)2756 bool nsIFrame::FormsBackdropRoot(const nsStyleDisplay* aStyleDisplay,
2757                                  const nsStyleEffects* aStyleEffects,
2758                                  const nsStyleSVGReset* aStyleSVGReset) {
2759   // Check if this is a root frame.
2760   if (!GetParent()) {
2761     return true;
2762   }
2763 
2764   // Check for filter effects.
2765   if (aStyleEffects->HasFilters() || aStyleEffects->HasBackdropFilters() ||
2766       aStyleEffects->HasMixBlendMode()) {
2767     return true;
2768   }
2769 
2770   // Check for opacity.
2771   if (HasOpacity(aStyleDisplay, aStyleEffects)) {
2772     return true;
2773   }
2774 
2775   // Check for mask or clip path.
2776   if (aStyleSVGReset->HasMask() || aStyleSVGReset->HasClipPath()) {
2777     return true;
2778   }
2779 
2780   // TODO(cbrewster): Check will-change attributes
2781 
2782   return false;
2783 }
2784 
GetClipPropClipRect(const nsStyleDisplay * aDisp,const nsStyleEffects * aEffects,const nsSize & aSize) const2785 Maybe<nsRect> nsIFrame::GetClipPropClipRect(const nsStyleDisplay* aDisp,
2786                                             const nsStyleEffects* aEffects,
2787                                             const nsSize& aSize) const {
2788   if (aEffects->mClip.IsAuto() ||
2789       !(aDisp->IsAbsolutelyPositioned(this) || IsSVGContentWithCSSClip(this))) {
2790     return Nothing();
2791   }
2792 
2793   auto& clipRect = aEffects->mClip.AsRect();
2794   nsRect rect = clipRect.ToLayoutRect();
2795   if (MOZ_LIKELY(StyleBorder()->mBoxDecorationBreak ==
2796                  StyleBoxDecorationBreak::Slice)) {
2797     // The clip applies to the joined boxes so it's relative the first
2798     // continuation.
2799     nscoord y = 0;
2800     for (nsIFrame* f = GetPrevContinuation(); f; f = f->GetPrevContinuation()) {
2801       y += f->GetRect().height;
2802     }
2803     rect.MoveBy(nsPoint(0, -y));
2804   }
2805 
2806   if (clipRect.right.IsAuto()) {
2807     rect.width = aSize.width - rect.x;
2808   }
2809   if (clipRect.bottom.IsAuto()) {
2810     rect.height = aSize.height - rect.y;
2811   }
2812   return Some(rect);
2813 }
2814 
2815 /**
2816  * If the CSS 'overflow' property applies to this frame, and is not
2817  * handled by constructing a dedicated nsHTML/XULScrollFrame, set up clipping
2818  * for that overflow in aBuilder->ClipState() to clip all containing-block
2819  * descendants.
2820  *
2821  * Return true if clipping was applied.
2822  */
ApplyOverflowClipping(nsDisplayListBuilder * aBuilder,const nsIFrame * aFrame,DisplayListClipState::AutoClipMultiple & aClipState)2823 static void ApplyOverflowClipping(
2824     nsDisplayListBuilder* aBuilder, const nsIFrame* aFrame,
2825     DisplayListClipState::AutoClipMultiple& aClipState) {
2826   // Only -moz-hidden-unscrollable is handled here (and 'hidden' for table
2827   // frames, and any non-visible value for blocks in a paginated context).
2828   // We allow -moz-hidden-unscrollable to apply to any kind of frame. This
2829   // is required by comboboxes which make their display text (an inline frame)
2830   // have clipping.
2831   MOZ_ASSERT(
2832       nsFrame::ShouldApplyOverflowClipping(aFrame, aFrame->StyleDisplay()));
2833 
2834   nsRect clipRect;
2835   bool haveRadii = false;
2836   nscoord radii[8];
2837   auto* disp = aFrame->StyleDisplay();
2838   // Only deflate the padding if we clip to the content-box in that axis.
2839   auto wm = aFrame->GetWritingMode();
2840   bool cbH = (wm.IsVertical() ? disp->mOverflowClipBoxBlock
2841                               : disp->mOverflowClipBoxInline) ==
2842              StyleOverflowClipBox::ContentBox;
2843   bool cbV = (wm.IsVertical() ? disp->mOverflowClipBoxInline
2844                               : disp->mOverflowClipBoxBlock) ==
2845              StyleOverflowClipBox::ContentBox;
2846   nsMargin bp = aFrame->GetUsedPadding();
2847   if (!cbH) {
2848     bp.left = bp.right = nscoord(0);
2849   }
2850   if (!cbV) {
2851     bp.top = bp.bottom = nscoord(0);
2852   }
2853 
2854   bp += aFrame->GetUsedBorder();
2855   bp.ApplySkipSides(aFrame->GetSkipSides());
2856   nsRect rect(nsPoint(0, 0), aFrame->GetSize());
2857   rect.Deflate(bp);
2858   clipRect = rect + aBuilder->ToReferenceFrame(aFrame);
2859   haveRadii = aFrame->GetBoxBorderRadii(radii, bp, false);
2860   aClipState.ClipContainingBlockDescendantsExtra(clipRect,
2861                                                  haveRadii ? radii : nullptr);
2862 }
2863 
2864 #ifdef DEBUG
PaintDebugBorder(nsIFrame * aFrame,DrawTarget * aDrawTarget,const nsRect & aDirtyRect,nsPoint aPt)2865 static void PaintDebugBorder(nsIFrame* aFrame, DrawTarget* aDrawTarget,
2866                              const nsRect& aDirtyRect, nsPoint aPt) {
2867   nsRect r(aPt, aFrame->GetSize());
2868   int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
2869   sRGBColor blueOrRed(aFrame->HasView() ? sRGBColor(0.f, 0.f, 1.f, 1.f)
2870                                         : sRGBColor(1.f, 0.f, 0.f, 1.f));
2871   aDrawTarget->StrokeRect(NSRectToRect(r, appUnitsPerDevPixel),
2872                           ColorPattern(ToDeviceColor(blueOrRed)));
2873 }
2874 
PaintEventTargetBorder(nsIFrame * aFrame,DrawTarget * aDrawTarget,const nsRect & aDirtyRect,nsPoint aPt)2875 static void PaintEventTargetBorder(nsIFrame* aFrame, DrawTarget* aDrawTarget,
2876                                    const nsRect& aDirtyRect, nsPoint aPt) {
2877   nsRect r(aPt, aFrame->GetSize());
2878   int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
2879   ColorPattern purple(ToDeviceColor(sRGBColor(.5f, 0.f, .5f, 1.f)));
2880   aDrawTarget->StrokeRect(NSRectToRect(r, appUnitsPerDevPixel), purple);
2881 }
2882 
DisplayDebugBorders(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,const nsDisplayListSet & aLists)2883 static void DisplayDebugBorders(nsDisplayListBuilder* aBuilder,
2884                                 nsIFrame* aFrame,
2885                                 const nsDisplayListSet& aLists) {
2886   // Draw a border around the child
2887   // REVIEW: From nsContainerFrame::PaintChild
2888   if (nsFrame::GetShowFrameBorders() && !aFrame->GetRect().IsEmpty()) {
2889     aLists.Outlines()->AppendNewToTop<nsDisplayGeneric>(
2890         aBuilder, aFrame, PaintDebugBorder, "DebugBorder",
2891         DisplayItemType::TYPE_DEBUG_BORDER);
2892   }
2893   // Draw a border around the current event target
2894   if (nsFrame::GetShowEventTargetFrameBorder() &&
2895       aFrame->PresShell()->GetDrawEventTargetFrame() == aFrame) {
2896     aLists.Outlines()->AppendNewToTop<nsDisplayGeneric>(
2897         aBuilder, aFrame, PaintEventTargetBorder, "EventTargetBorder",
2898         DisplayItemType::TYPE_EVENT_TARGET_BORDER);
2899   }
2900 }
2901 #endif
2902 
IsScrollFrameActive(nsDisplayListBuilder * aBuilder,nsIScrollableFrame * aScrollableFrame)2903 static bool IsScrollFrameActive(nsDisplayListBuilder* aBuilder,
2904                                 nsIScrollableFrame* aScrollableFrame) {
2905   return aScrollableFrame && aScrollableFrame->IsScrollingActive(aBuilder);
2906 }
2907 
2908 /**
2909  * Returns whether a display item that gets created with the builder's current
2910  * state will have a scrolled clip, i.e. a clip that is scrolled by a scroll
2911  * frame which does not move the item itself.
2912  */
BuilderHasScrolledClip(nsDisplayListBuilder * aBuilder)2913 static bool BuilderHasScrolledClip(nsDisplayListBuilder* aBuilder) {
2914   const DisplayItemClipChain* currentClip =
2915       aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder);
2916   if (!currentClip) {
2917     return false;
2918   }
2919 
2920   const ActiveScrolledRoot* currentClipASR = currentClip->mASR;
2921   const ActiveScrolledRoot* currentASR = aBuilder->CurrentActiveScrolledRoot();
2922   return ActiveScrolledRoot::PickDescendant(currentClipASR, currentASR) !=
2923          currentASR;
2924 }
2925 
2926 class AutoSaveRestoreContainsBlendMode {
2927   nsDisplayListBuilder& mBuilder;
2928   bool mSavedContainsBlendMode;
2929 
2930  public:
AutoSaveRestoreContainsBlendMode(nsDisplayListBuilder & aBuilder)2931   explicit AutoSaveRestoreContainsBlendMode(nsDisplayListBuilder& aBuilder)
2932       : mBuilder(aBuilder),
2933         mSavedContainsBlendMode(aBuilder.ContainsBlendMode()) {}
2934 
~AutoSaveRestoreContainsBlendMode()2935   ~AutoSaveRestoreContainsBlendMode() {
2936     mBuilder.SetContainsBlendMode(mSavedContainsBlendMode);
2937   }
2938 };
2939 
2940 class AutoSaveRestoreContainsBackdropFilter {
2941   nsDisplayListBuilder& mBuilder;
2942   bool mSavedContainsBackdropFilter;
2943 
2944  public:
AutoSaveRestoreContainsBackdropFilter(nsDisplayListBuilder & aBuilder)2945   explicit AutoSaveRestoreContainsBackdropFilter(nsDisplayListBuilder& aBuilder)
2946       : mBuilder(aBuilder),
2947         mSavedContainsBackdropFilter(aBuilder.ContainsBackdropFilter()) {}
2948 
2949   /**
2950    * This is called if a stacking context which does not form a backdrop root
2951    * contains a descendent with a backdrop filter. In this case we need to
2952    * delegate backdrop root creation to the next parent in the tree until we hit
2953    * the nearest backdrop root ancestor.
2954    */
DelegateUp(bool aContainsBackdropFilter)2955   void DelegateUp(bool aContainsBackdropFilter) {
2956     mSavedContainsBackdropFilter = aContainsBackdropFilter;
2957   }
2958 
~AutoSaveRestoreContainsBackdropFilter()2959   ~AutoSaveRestoreContainsBackdropFilter() {
2960     mBuilder.SetContainsBackdropFilter(mSavedContainsBackdropFilter);
2961   }
2962 };
2963 
CheckForApzAwareEventHandlers(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame)2964 static void CheckForApzAwareEventHandlers(nsDisplayListBuilder* aBuilder,
2965                                           nsIFrame* aFrame) {
2966   if (aBuilder->GetAncestorHasApzAwareEventHandler()) {
2967     return;
2968   }
2969 
2970   nsIContent* content = aFrame->GetContent();
2971   if (!content) {
2972     return;
2973   }
2974 
2975   if (content->IsNodeApzAware()) {
2976     aBuilder->SetAncestorHasApzAwareEventHandler(true);
2977   }
2978 }
2979 
2980 /**
2981  * True if aDescendant participates the context aAncestor participating.
2982  */
FrameParticipatesIn3DContext(nsIFrame * aAncestor,nsIFrame * aDescendant)2983 static bool FrameParticipatesIn3DContext(nsIFrame* aAncestor,
2984                                          nsIFrame* aDescendant) {
2985   MOZ_ASSERT(aAncestor != aDescendant);
2986   MOZ_ASSERT(aAncestor->GetContent() != aDescendant->GetContent());
2987   MOZ_ASSERT(aAncestor->Extend3DContext());
2988 
2989   nsIFrame* ancestor = aAncestor->FirstContinuation();
2990   MOZ_ASSERT(ancestor->IsPrimaryFrame());
2991 
2992   nsIFrame* frame;
2993   for (frame = aDescendant->GetClosestFlattenedTreeAncestorPrimaryFrame();
2994        frame && ancestor != frame;
2995        frame = frame->GetClosestFlattenedTreeAncestorPrimaryFrame()) {
2996     if (!frame->Extend3DContext()) {
2997       return false;
2998     }
2999   }
3000 
3001   MOZ_ASSERT(frame == ancestor);
3002   return true;
3003 }
3004 
ItemParticipatesIn3DContext(nsIFrame * aAncestor,nsDisplayItem * aItem)3005 static bool ItemParticipatesIn3DContext(nsIFrame* aAncestor,
3006                                         nsDisplayItem* aItem) {
3007   auto type = aItem->GetType();
3008   const bool isContainer = type == DisplayItemType::TYPE_WRAP_LIST ||
3009                            type == DisplayItemType::TYPE_CONTAINER;
3010 
3011   if (isContainer && aItem->GetChildren()->Count() == 1) {
3012     // If the wraplist has only one child item, use the type of that item.
3013     type = aItem->GetChildren()->GetBottom()->GetType();
3014   }
3015 
3016   if (type != DisplayItemType::TYPE_TRANSFORM &&
3017       type != DisplayItemType::TYPE_PERSPECTIVE) {
3018     return false;
3019   }
3020   nsIFrame* transformFrame = aItem->Frame();
3021   if (aAncestor->GetContent() == transformFrame->GetContent()) {
3022     return true;
3023   }
3024   return FrameParticipatesIn3DContext(aAncestor, transformFrame);
3025 }
3026 
WrapSeparatorTransform(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aNonParticipants,nsDisplayList * aParticipants,int aIndex,nsDisplayItem ** aSeparator)3027 static void WrapSeparatorTransform(nsDisplayListBuilder* aBuilder,
3028                                    nsIFrame* aFrame,
3029                                    nsDisplayList* aNonParticipants,
3030                                    nsDisplayList* aParticipants, int aIndex,
3031                                    nsDisplayItem** aSeparator) {
3032   if (aNonParticipants->IsEmpty()) {
3033     return;
3034   }
3035 
3036   nsDisplayTransform* item = MakeDisplayItemWithIndex<nsDisplayTransform>(
3037       aBuilder, aFrame, aIndex, aNonParticipants, aBuilder->GetVisibleRect());
3038 
3039   if (*aSeparator == nullptr && item) {
3040     *aSeparator = item;
3041   }
3042 
3043   aParticipants->AppendToTop(item);
3044 }
3045 
3046 // Try to compute a clip rect to bound the contents of the mask item
3047 // that will be built for |aMaskedFrame|. If we're not able to compute
3048 // one, return an empty Maybe.
3049 // The returned clip rect, if there is one, is relative to |aMaskedFrame|.
ComputeClipForMaskItem(nsDisplayListBuilder * aBuilder,nsIFrame * aMaskedFrame)3050 static Maybe<nsRect> ComputeClipForMaskItem(nsDisplayListBuilder* aBuilder,
3051                                             nsIFrame* aMaskedFrame) {
3052   const nsStyleSVGReset* svgReset = aMaskedFrame->StyleSVGReset();
3053 
3054   nsSVGUtils::MaskUsage maskUsage;
3055   nsSVGUtils::DetermineMaskUsage(aMaskedFrame, false, maskUsage);
3056 
3057   nsPoint offsetToUserSpace =
3058       nsLayoutUtils::ComputeOffsetToUserSpace(aBuilder, aMaskedFrame);
3059   int32_t devPixelRatio = aMaskedFrame->PresContext()->AppUnitsPerDevPixel();
3060   gfxPoint devPixelOffsetToUserSpace =
3061       nsLayoutUtils::PointToGfxPoint(offsetToUserSpace, devPixelRatio);
3062   gfxMatrix cssToDevMatrix = nsSVGUtils::GetCSSPxToDevPxMatrix(aMaskedFrame);
3063 
3064   nsPoint toReferenceFrame;
3065   aBuilder->FindReferenceFrameFor(aMaskedFrame, &toReferenceFrame);
3066 
3067   Maybe<gfxRect> combinedClip;
3068   if (maskUsage.shouldApplyBasicShapeOrPath) {
3069     Maybe<Rect> result =
3070         nsCSSClipPathInstance::GetBoundingRectForBasicShapeOrPathClip(
3071             aMaskedFrame, svgReset->mClipPath);
3072     if (result) {
3073       combinedClip = Some(ThebesRect(*result));
3074     }
3075   } else if (maskUsage.shouldApplyClipPath) {
3076     gfxRect result = nsSVGUtils::GetBBox(
3077         aMaskedFrame,
3078         nsSVGUtils::eBBoxIncludeClipped | nsSVGUtils::eBBoxIncludeFill |
3079             nsSVGUtils::eBBoxIncludeMarkers | nsSVGUtils::eBBoxIncludeStroke |
3080             nsSVGUtils::eDoNotClipToBBoxOfContentInsideClipPath);
3081     combinedClip = Some(cssToDevMatrix.TransformBounds(result));
3082   } else {
3083     // The code for this case is adapted from ComputeMaskGeometry().
3084 
3085     nsRect borderArea(toReferenceFrame, aMaskedFrame->GetSize());
3086     borderArea -= offsetToUserSpace;
3087 
3088     // Use an infinite dirty rect to pass into nsCSSRendering::
3089     // GetImageLayerClip() because we don't have an actual dirty rect to
3090     // pass in. This is fine because the only time GetImageLayerClip() will
3091     // not intersect the incoming dirty rect with something is in the "NoClip"
3092     // case, and we handle that specially.
3093     nsRect dirtyRect(nscoord_MIN / 2, nscoord_MIN / 2, nscoord_MAX,
3094                      nscoord_MAX);
3095 
3096     nsIFrame* firstFrame =
3097         nsLayoutUtils::FirstContinuationOrIBSplitSibling(aMaskedFrame);
3098     nsTArray<nsSVGMaskFrame*> maskFrames;
3099     // XXX check return value?
3100     SVGObserverUtils::GetAndObserveMasks(firstFrame, &maskFrames);
3101 
3102     for (uint32_t i = 0; i < maskFrames.Length(); ++i) {
3103       gfxRect clipArea;
3104       if (maskFrames[i]) {
3105         clipArea = maskFrames[i]->GetMaskArea(aMaskedFrame);
3106         clipArea = cssToDevMatrix.TransformBounds(clipArea);
3107       } else {
3108         const auto& layer = svgReset->mMask.mLayers[i];
3109         if (layer.mClip == StyleGeometryBox::NoClip) {
3110           return Nothing();
3111         }
3112 
3113         nsCSSRendering::ImageLayerClipState clipState;
3114         nsCSSRendering::GetImageLayerClip(
3115             layer, aMaskedFrame, *aMaskedFrame->StyleBorder(), borderArea,
3116             dirtyRect, false /* aWillPaintBorder */, devPixelRatio, &clipState);
3117         clipArea = clipState.mDirtyRectInDevPx;
3118       }
3119       combinedClip = UnionMaybeRects(combinedClip, Some(clipArea));
3120     }
3121   }
3122   if (combinedClip) {
3123     if (combinedClip->IsEmpty()) {
3124       // *clipForMask might be empty if all mask references are not resolvable
3125       // or the size of them are empty. We still need to create a transparent
3126       // mask before bug 1276834 fixed, so don't clip ctx by an empty rectangle
3127       // for for now.
3128       return Nothing();
3129     }
3130 
3131     // Convert to user space.
3132     *combinedClip += devPixelOffsetToUserSpace;
3133 
3134     // Round the clip out. In FrameLayerBuilder we round clips to nearest
3135     // pixels, and if we have a really thin clip here, that can cause the
3136     // clip to become empty if we didn't round out here.
3137     // The rounding happens in coordinates that are relative to the reference
3138     // frame, which matches what FrameLayerBuilder does.
3139     combinedClip->RoundOut();
3140 
3141     // Convert to app units.
3142     nsRect result =
3143         nsLayoutUtils::RoundGfxRectToAppRect(*combinedClip, devPixelRatio);
3144 
3145     // The resulting clip is relative to the reference frame, but the caller
3146     // expects it to be relative to the masked frame, so adjust it.
3147     result -= toReferenceFrame;
3148     return Some(result);
3149   }
3150   return Nothing();
3151 }
3152 
3153 struct AutoCheckBuilder {
AutoCheckBuilderAutoCheckBuilder3154   explicit AutoCheckBuilder(nsDisplayListBuilder* aBuilder)
3155       : mBuilder(aBuilder) {
3156     aBuilder->Check();
3157   }
3158 
~AutoCheckBuilderAutoCheckBuilder3159   ~AutoCheckBuilder() { mBuilder->Check(); }
3160 
3161   nsDisplayListBuilder* mBuilder;
3162 };
3163 
3164 /**
3165  * Helper class to track container creation. Stores the first tracked container.
3166  * Used to find the innermost container for hit test information, and to notify
3167  * callers whether a container item was created or not.
3168  */
3169 struct ContainerTracker {
TrackContainerContainerTracker3170   void TrackContainer(nsDisplayItem* aContainer) {
3171     if (!aContainer) {
3172       return;
3173     }
3174 
3175     if (!mContainer) {
3176       mContainer = aContainer;
3177     }
3178 
3179     mCreatedContainer = true;
3180   }
3181 
ResetCreatedContainerContainerTracker3182   void ResetCreatedContainer() { mCreatedContainer = false; }
3183 
3184   nsDisplayItem* mContainer = nullptr;
3185   bool mCreatedContainer = false;
3186 };
3187 
3188 /**
3189  * Adds hit test information |aHitTestInfo| on the container item |aContainer|,
3190  * or if the container item is null, creates a separate hit test item that is
3191  * added to the bottom of the display list |aList|.
3192  */
AddHitTestInfo(nsDisplayListBuilder * aBuilder,nsDisplayList * aList,nsDisplayItem * aContainer,nsIFrame * aFrame,mozilla::UniquePtr<HitTestInfo> && aHitTestInfo)3193 static void AddHitTestInfo(nsDisplayListBuilder* aBuilder, nsDisplayList* aList,
3194                            nsDisplayItem* aContainer, nsIFrame* aFrame,
3195                            mozilla::UniquePtr<HitTestInfo>&& aHitTestInfo) {
3196   nsDisplayHitTestInfoBase* hitTestItem;
3197 
3198   if (aContainer) {
3199     MOZ_ASSERT(aContainer->IsHitTestItem());
3200     hitTestItem = static_cast<nsDisplayHitTestInfoBase*>(aContainer);
3201     hitTestItem->SetHitTestInfo(std::move(aHitTestInfo));
3202   } else {
3203     // No container item was created for this frame. Create a separate
3204     // nsDisplayCompositorHitTestInfo item instead.
3205     aList->AppendNewToBottom<nsDisplayCompositorHitTestInfo>(
3206         aBuilder, aFrame, std::move(aHitTestInfo));
3207   }
3208 }
3209 
BuildDisplayListForStackingContext(nsDisplayListBuilder * aBuilder,nsDisplayList * aList,bool * aCreatedContainerItem)3210 void nsIFrame::BuildDisplayListForStackingContext(
3211     nsDisplayListBuilder* aBuilder, nsDisplayList* aList,
3212     bool* aCreatedContainerItem) {
3213   AutoCheckBuilder check(aBuilder);
3214   if (GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE) return;
3215 
3216   // Replaced elements have their visibility handled here, because
3217   // they're visually atomic
3218   if (IsFrameOfType(eReplaced) && !IsVisibleForPainting()) return;
3219 
3220   const nsStyleDisplay* disp = StyleDisplay();
3221   const nsStyleEffects* effects = StyleEffects();
3222   EffectSet* effectSetForOpacity = EffectSet::GetEffectSetForFrame(
3223       this, nsCSSPropertyIDSet::OpacityProperties());
3224   // We can stop right away if this is a zero-opacity stacking context and
3225   // we're painting, and we're not animating opacity. Don't do this
3226   // if we're going to compute plugin geometry, since opacity-0 plugins
3227   // need to have display items built for them.
3228   bool needHitTestInfo =
3229       aBuilder->BuildCompositorHitTestInfo() &&
3230       StyleUI()->GetEffectivePointerEvents(this) != StylePointerEvents::None;
3231   bool opacityItemForEventsAndPluginsOnly = false;
3232   if (effects->mOpacity == 0.0 && aBuilder->IsForPainting() &&
3233       !(disp->mWillChange.bits & StyleWillChangeBits::OPACITY) &&
3234       !nsLayoutUtils::HasAnimationOfPropertySet(
3235           this, nsCSSPropertyIDSet::OpacityProperties(), effectSetForOpacity)) {
3236     if (needHitTestInfo || aBuilder->WillComputePluginGeometry()) {
3237       opacityItemForEventsAndPluginsOnly = true;
3238     } else {
3239       return;
3240     }
3241   }
3242 
3243   if (aBuilder->IsForPainting() && disp->mWillChange.bits) {
3244     aBuilder->AddToWillChangeBudget(this, GetSize());
3245   }
3246 
3247   // For preserves3d, use the dirty rect already installed on the
3248   // builder, since aDirtyRect maybe distorted for transforms along
3249   // the chain.
3250   nsRect visibleRect = aBuilder->GetVisibleRect();
3251   nsRect dirtyRect = aBuilder->GetDirtyRect();
3252 
3253   // We build an opacity item if it's not going to be drawn by SVG content.
3254   // We could in principle skip creating an nsDisplayOpacity item if
3255   // nsDisplayOpacity::NeedsActiveLayer returns false and usingSVGEffects is
3256   // true (the nsDisplayFilter/nsDisplayMasksAndClipPaths could handle the
3257   // opacity). Since SVG has perf issues where we sometimes spend a lot of
3258   // time creating display list items that might be helpful.  We'd need to
3259   // restore our mechanism to do that (changed in bug 1482403), and we'd
3260   // need to invalidate the frame if the value that would be return from
3261   // NeedsActiveLayer was to change, which we don't currently do.
3262   const bool useOpacity =
3263       HasVisualOpacity(disp, effects, effectSetForOpacity) &&
3264       !nsSVGUtils::CanOptimizeOpacity(this);
3265 
3266   const bool isTransformed = IsTransformed(disp);
3267   const bool hasPerspective = isTransformed && HasPerspective(disp);
3268   const bool extend3DContext =
3269       Extend3DContext(disp, effects, effectSetForOpacity);
3270   const bool combines3DTransformWithAncestors =
3271       (extend3DContext || isTransformed) &&
3272       Combines3DTransformWithAncestors(disp);
3273 
3274   Maybe<nsDisplayListBuilder::AutoPreserves3DContext> autoPreserves3DContext;
3275   if (extend3DContext && !combines3DTransformWithAncestors) {
3276     // Start a new preserves3d context to keep informations on
3277     // nsDisplayListBuilder.
3278     autoPreserves3DContext.emplace(aBuilder);
3279     // Save dirty rect on the builder to avoid being distorted for
3280     // multiple transforms along the chain.
3281     aBuilder->SavePreserves3DRect();
3282 
3283     // We rebuild everything within preserve-3d and don't try
3284     // to retain, so override the dirty rect now.
3285     if (aBuilder->IsRetainingDisplayList()) {
3286       dirtyRect = visibleRect;
3287       aBuilder->SetDisablePartialUpdates(true);
3288     }
3289   }
3290 
3291   const bool useBlendMode = effects->mMixBlendMode != StyleBlend::Normal;
3292   if (useBlendMode) {
3293     aBuilder->SetContainsBlendMode(true);
3294   }
3295 
3296   // reset blend mode so we can keep track if this stacking context needs have
3297   // a nsDisplayBlendContainer. Set the blend mode back when the routine exits
3298   // so we keep track if the parent stacking context needs a container too.
3299   AutoSaveRestoreContainsBlendMode autoRestoreBlendMode(*aBuilder);
3300   aBuilder->SetContainsBlendMode(false);
3301 
3302   bool usingBackdropFilter =
3303       effects->HasBackdropFilters() &&
3304       nsDisplayBackdropFilters::CanCreateWebRenderCommands(aBuilder, this);
3305 
3306   if (usingBackdropFilter) {
3307     aBuilder->SetContainsBackdropFilter(true);
3308   }
3309 
3310   AutoSaveRestoreContainsBackdropFilter autoRestoreBackdropFilter(*aBuilder);
3311   aBuilder->SetContainsBackdropFilter(false);
3312 
3313   nsRect visibleRectOutsideTransform = visibleRect;
3314   bool allowAsyncAnimation = false;
3315   bool inTransform = aBuilder->IsInTransform();
3316   if (isTransformed) {
3317     nsDisplayTransform::PrerenderInfo decision =
3318         nsDisplayTransform::ShouldPrerenderTransformedContent(aBuilder, this,
3319                                                               &dirtyRect);
3320 
3321     switch (decision.mDecision) {
3322       case nsDisplayTransform::PrerenderDecision::Full:
3323       case nsDisplayTransform::PrerenderDecision::Partial:
3324         allowAsyncAnimation = true;
3325         visibleRect = dirtyRect;
3326         break;
3327       case nsDisplayTransform::PrerenderDecision::No: {
3328         // If we didn't prerender an animated frame in a preserve-3d context,
3329         // then we want disable async animations for the rest of the preserve-3d
3330         // (especially ancestors).
3331         if ((extend3DContext || combines3DTransformWithAncestors) &&
3332             decision.mHasAnimations) {
3333           aBuilder->SavePreserves3DAllowAsyncAnimation(false);
3334         }
3335 
3336         const nsRect overflow = GetVisualOverflowRectRelativeToSelf();
3337         if (overflow.IsEmpty() && !extend3DContext) {
3338           return;
3339         }
3340 
3341         // If we're in preserve-3d then grab the dirty rect that was given to
3342         // the root and transform using the combined transform.
3343         if (combines3DTransformWithAncestors) {
3344           visibleRect = dirtyRect = aBuilder->GetPreserves3DRect();
3345         }
3346 
3347         nsRect untransformedDirtyRect;
3348         if (nsDisplayTransform::UntransformRect(dirtyRect, overflow, this,
3349                                                 &untransformedDirtyRect)) {
3350           dirtyRect = untransformedDirtyRect;
3351           nsDisplayTransform::UntransformRect(visibleRect, overflow, this,
3352                                               &visibleRect);
3353         } else {
3354           // This should only happen if the transform is singular, in which case
3355           // nothing is visible anyway
3356           dirtyRect.SetEmpty();
3357           visibleRect.SetEmpty();
3358         }
3359       }
3360     }
3361     inTransform = true;
3362   } else if (IsFixedPosContainingBlock()) {
3363     // Restict the building area to the overflow rect for these frames, since
3364     // RetainedDisplayListBuilder uses it to know if the size of the stacking
3365     // context changed.
3366     visibleRect.IntersectRect(visibleRect, GetVisualOverflowRect());
3367     dirtyRect.IntersectRect(dirtyRect, GetVisualOverflowRect());
3368   }
3369 
3370   bool hasOverrideDirtyRect = false;
3371   // If we're doing a partial build, we're not invalid and we're capable
3372   // of having an override building rect (stacking context and fixed pos
3373   // containing block), then we should assume we have one.
3374   // Either we have an explicit one, or nothing in our subtree changed and
3375   // we have an implicit empty rect.
3376   if (aBuilder->IsPartialUpdate() && !aBuilder->InInvalidSubtree() &&
3377       !IsFrameModified() && IsFixedPosContainingBlock()) {
3378     dirtyRect = nsRect();
3379     if (HasOverrideDirtyRegion()) {
3380       nsDisplayListBuilder::DisplayListBuildingData* data =
3381           GetProperty(nsDisplayListBuilder::DisplayListBuildingRect());
3382       if (data) {
3383         dirtyRect = data->mDirtyRect.Intersect(visibleRect);
3384         hasOverrideDirtyRect = true;
3385       }
3386     }
3387   }
3388 
3389   bool usingFilter = effects->HasFilters();
3390   bool usingMask = nsSVGIntegrationUtils::UsingMaskOrClipPathForFrame(this);
3391   bool usingSVGEffects = usingFilter || usingMask;
3392 
3393   nsRect visibleRectOutsideSVGEffects = visibleRect;
3394   nsDisplayList hoistedScrollInfoItemsStorage;
3395   if (usingSVGEffects) {
3396     dirtyRect =
3397         nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, dirtyRect);
3398     visibleRect = nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(
3399         this, visibleRect);
3400     aBuilder->EnterSVGEffectsContents(this, &hoistedScrollInfoItemsStorage);
3401   }
3402 
3403   bool useStickyPosition =
3404       disp->mPosition == StylePositionProperty::Sticky &&
3405       IsScrollFrameActive(
3406           aBuilder,
3407           nsLayoutUtils::GetNearestScrollableFrame(
3408               GetParent(), nsLayoutUtils::SCROLLABLE_SAME_DOC |
3409                                nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN));
3410   bool useFixedPosition = disp->mPosition == StylePositionProperty::Fixed &&
3411                           (nsLayoutUtils::IsFixedPosFrameInDisplayPort(this) ||
3412                            BuilderHasScrolledClip(aBuilder));
3413 
3414   nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList(
3415       aBuilder, this, visibleRect, dirtyRect, isTransformed);
3416 
3417   // Depending on the effects that are applied to this frame, we can create
3418   // multiple container display items and wrap them around our contents.
3419   // This enum lists all the potential container display items, in the order
3420   // outside to inside.
3421   enum class ContainerItemType : uint8_t {
3422     None = 0,
3423     OwnLayerIfNeeded,
3424     BlendMode,
3425     FixedPosition,
3426     OwnLayerForTransformWithRoundedClip,
3427     Perspective,
3428     Transform,
3429     SeparatorTransforms,
3430     Opacity,
3431     Filter,
3432     BlendContainer
3433   };
3434 
3435   nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
3436 
3437   auto cssClip = GetClipPropClipRect(disp, effects, GetSize());
3438   auto ApplyClipProp = [&](DisplayListClipState::AutoSaveRestore& aClipState) {
3439     if (!cssClip) {
3440       return;
3441     }
3442     nsPoint offset = aBuilder->GetCurrentFrameOffsetToReferenceFrame();
3443     aBuilder->IntersectDirtyRect(*cssClip);
3444     aBuilder->IntersectVisibleRect(*cssClip);
3445     aClipState.ClipContentDescendants(*cssClip + offset);
3446   };
3447 
3448   // The CSS clip property is effectively inside the transform, but outside the
3449   // filters. So if we're not transformed we can apply it just here for
3450   // simplicity, instead of on each of the places that handle clipCapturedBy.
3451   DisplayListClipState::AutoSaveRestore untransformedCssClip(aBuilder);
3452   if (!isTransformed) {
3453     ApplyClipProp(untransformedCssClip);
3454   }
3455 
3456   // If there is a current clip, then depending on the container items we
3457   // create, different things can happen to it. Some container items simply
3458   // propagate the clip to their children and aren't clipped themselves.
3459   // But other container items, especially those that establish a different
3460   // geometry for their contents (e.g. transforms), capture the clip on
3461   // themselves and unset the clip for their contents. If we create more than
3462   // one of those container items, the clip will be captured on the outermost
3463   // one and the inner container items will be unclipped.
3464   ContainerItemType clipCapturedBy = ContainerItemType::None;
3465   if (useFixedPosition) {
3466     clipCapturedBy = ContainerItemType::FixedPosition;
3467   } else if (isTransformed) {
3468     const DisplayItemClipChain* currentClip =
3469         aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder);
3470     if ((hasPerspective || extend3DContext) &&
3471         (currentClip && currentClip->HasRoundedCorners())) {
3472       // If we're creating an nsDisplayTransform item that is going to combine
3473       // its transform with its children (preserve-3d or perspective), then we
3474       // can't have an intermediate surface. Mask layers force an intermediate
3475       // surface, so if we're going to need both then create a separate
3476       // wrapping layer for the mask.
3477       clipCapturedBy = ContainerItemType::OwnLayerForTransformWithRoundedClip;
3478     } else if (hasPerspective) {
3479       clipCapturedBy = ContainerItemType::Perspective;
3480     } else {
3481       clipCapturedBy = ContainerItemType::Transform;
3482     }
3483   } else if (usingFilter) {
3484     clipCapturedBy = ContainerItemType::Filter;
3485   }
3486 
3487   DisplayListClipState::AutoSaveRestore clipState(aBuilder);
3488   if (clipCapturedBy != ContainerItemType::None) {
3489     clipState.Clear();
3490   }
3491 
3492   DisplayListClipState::AutoSaveRestore transformedCssClip(aBuilder);
3493   if (isTransformed) {
3494     // FIXME(emilio, bug 1525159): In the case we have a both a transform _and_
3495     // filters, this clips the input to the filters as well, which is not
3496     // correct (clipping by the `clip` property is supposed to happen after
3497     // applying the filter effects, per [1].
3498     //
3499     // This is not a regression though, since we used to do that anyway before
3500     // bug 1514384, and even without the transform we get it wrong.
3501     //
3502     // [1]: https://drafts.fxtf.org/css-masking/#placement
3503     ApplyClipProp(transformedCssClip);
3504   }
3505 
3506   mozilla::UniquePtr<HitTestInfo> hitTestInfo;
3507 
3508   nsDisplayListCollection set(aBuilder);
3509   Maybe<nsRect> clipForMask;
3510   bool insertBackdropRoot;
3511   {
3512     DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
3513     nsDisplayListBuilder::AutoInTransformSetter inTransformSetter(aBuilder,
3514                                                                   inTransform);
3515     nsDisplayListBuilder::AutoEnterFilter filterASRSetter(aBuilder,
3516                                                           usingFilter);
3517     nsDisplayListBuilder::AutoInEventsAndPluginsOnly inEventsAndPluginsSetter(
3518         aBuilder, opacityItemForEventsAndPluginsOnly);
3519 
3520     CheckForApzAwareEventHandlers(aBuilder, this);
3521 
3522     if (usingMask) {
3523       clipForMask = ComputeClipForMaskItem(aBuilder, this);
3524       if (clipForMask) {
3525         aBuilder->IntersectDirtyRect(*clipForMask);
3526         aBuilder->IntersectVisibleRect(*clipForMask);
3527         nestedClipState.ClipContentDescendants(
3528             *clipForMask + aBuilder->GetCurrentFrameOffsetToReferenceFrame());
3529       }
3530     }
3531 
3532     // extend3DContext also guarantees that applyAbsPosClipping and
3533     // usingSVGEffects are false We only modify the preserve-3d rect if we are
3534     // the top of a preserve-3d heirarchy
3535     if (extend3DContext) {
3536       // Mark these first so MarkAbsoluteFramesForDisplayList knows if we are
3537       // going to be forced to descend into frames.
3538       aBuilder->MarkPreserve3DFramesForDisplayList(this);
3539     }
3540 
3541     aBuilder->AdjustWindowDraggingRegion(this);
3542 
3543     if (gfxVars::UseWebRender()) {
3544       aBuilder->BuildCompositorHitTestInfoIfNeeded(this, set.BorderBackground(),
3545                                                    true);
3546     } else {
3547       CompositorHitTestInfo info = aBuilder->BuildCompositorHitTestInfo()
3548                                        ? GetCompositorHitTestInfo(aBuilder)
3549                                        : CompositorHitTestInvisibleToHit;
3550 
3551       if (info != CompositorHitTestInvisibleToHit) {
3552         // Frame has hit test flags set, initialize the hit test info structure.
3553         hitTestInfo = mozilla::MakeUnique<HitTestInfo>(aBuilder, this, info);
3554 
3555         // Let child frames know the current hit test area and hit test flags.
3556         aBuilder->SetCompositorHitTestInfo(hitTestInfo->mArea,
3557                                            hitTestInfo->mFlags);
3558       }
3559     }
3560 
3561     MarkAbsoluteFramesForDisplayList(aBuilder);
3562     aBuilder->Check();
3563     BuildDisplayList(aBuilder, set);
3564     aBuilder->Check();
3565     aBuilder->DisplayCaret(this, set.Outlines());
3566 
3567     insertBackdropRoot = aBuilder->ContainsBackdropFilter() &&
3568                          FormsBackdropRoot(disp, effects, StyleSVGReset());
3569 
3570     // Blend modes are a real pain for retained display lists. We build a blend
3571     // container item if the built list contains any blend mode items within
3572     // the current stacking context. This can change without an invalidation
3573     // to the stacking context frame, or the blend mode frame (e.g. by moving
3574     // an intermediate frame).
3575     // When we gain/remove a blend container item, we need to mark this frame
3576     // as invalid and have the full display list for merging to track
3577     // the change correctly.
3578     // It seems really hard to track this in advance, as the bookkeeping
3579     // required to note which stacking contexts have blend descendants
3580     // is complex and likely to be buggy.
3581     // Instead we're doing the sad thing, detecting it afterwards, and just
3582     // repeating display list building if it changed.
3583     // We have to repeat building for the entire display list (or at least
3584     // the outer stacking context), since we need to mark this frame as invalid
3585     // to remove any existing content that isn't wrapped in the blend container,
3586     // and then we need to build content infront/behind the blend container
3587     // to get correct positioning during merging.
3588     if ((insertBackdropRoot || aBuilder->ContainsBlendMode()) &&
3589         aBuilder->IsRetainingDisplayList()) {
3590       if (aBuilder->IsPartialUpdate()) {
3591         aBuilder->SetPartialBuildFailed(true);
3592       } else {
3593         aBuilder->SetDisablePartialUpdates(true);
3594       }
3595     }
3596   }
3597 
3598   // If a child contains a backdrop filter, but this stacking context does not
3599   // form a backdrop root, we need to propogate up the tree until we find an
3600   // ancestor that does form a backdrop root.
3601   if (!insertBackdropRoot && aBuilder->ContainsBackdropFilter()) {
3602     autoRestoreBackdropFilter.DelegateUp(true);
3603   }
3604 
3605   if (aBuilder->IsBackgroundOnly()) {
3606     set.BlockBorderBackgrounds()->DeleteAll(aBuilder);
3607     set.Floats()->DeleteAll(aBuilder);
3608     set.Content()->DeleteAll(aBuilder);
3609     set.PositionedDescendants()->DeleteAll(aBuilder);
3610     set.Outlines()->DeleteAll(aBuilder);
3611   }
3612 
3613   if (hasOverrideDirtyRect &&
3614       StaticPrefs::layout_display_list_show_rebuild_area()) {
3615     nsDisplaySolidColor* color = MakeDisplayItem<nsDisplaySolidColor>(
3616         aBuilder, this,
3617         dirtyRect + aBuilder->GetCurrentFrameOffsetToReferenceFrame(),
3618         NS_RGBA(255, 0, 0, 64), false);
3619     if (color) {
3620       color->SetOverrideZIndex(INT32_MAX);
3621       set.PositionedDescendants()->AppendToTop(color);
3622     }
3623   }
3624 
3625   nsIContent* content = GetContent();
3626   if (!content) {
3627     content = PresContext()->Document()->GetRootElement();
3628   }
3629 
3630   nsDisplayList resultList;
3631   set.SerializeWithCorrectZOrder(&resultList, content);
3632 
3633 #ifdef DEBUG
3634   DisplayDebugBorders(aBuilder, this, set);
3635 #endif
3636 
3637   // Get the ASR to use for the container items that we create here.
3638   const ActiveScrolledRoot* containerItemASR = contASRTracker.GetContainerASR();
3639 
3640   ContainerTracker ct;
3641 
3642   /* If adding both a nsDisplayBlendContainer and a nsDisplayBlendMode to the
3643    * same list, the nsDisplayBlendContainer should be added first. This only
3644    * happens when the element creating this stacking context has mix-blend-mode
3645    * and also contains a child which has mix-blend-mode.
3646    * The nsDisplayBlendContainer must be added to the list first, so it does not
3647    * isolate the containing element blending as well.
3648    */
3649   if (aBuilder->ContainsBlendMode()) {
3650     DisplayListClipState::AutoSaveRestore blendContainerClipState(aBuilder);
3651     resultList.AppendToTop(nsDisplayBlendContainer::CreateForMixBlendMode(
3652         aBuilder, this, &resultList, containerItemASR));
3653     ct.TrackContainer(resultList.GetTop());
3654   }
3655 
3656   if (insertBackdropRoot) {
3657     DisplayListClipState::AutoSaveRestore backdropRootContainerClipState(
3658         aBuilder);
3659     resultList.AppendNewToTop<nsDisplayBackdropRootContainer>(
3660         aBuilder, this, &resultList, containerItemASR);
3661     ct.TrackContainer(resultList.GetTop());
3662   }
3663 
3664   if (usingBackdropFilter) {
3665     DisplayListClipState::AutoSaveRestore clipState(aBuilder);
3666     nsRect backdropRect =
3667         GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this);
3668     resultList.AppendNewToTop<nsDisplayBackdropFilters>(
3669         aBuilder, this, &resultList, backdropRect);
3670     ct.TrackContainer(resultList.GetTop());
3671   }
3672 
3673   /* If there are any SVG effects, wrap the list up in an SVG effects item
3674    * (which also handles CSS group opacity). Note that we create an SVG effects
3675    * item even if resultList is empty, since a filter can produce graphical
3676    * output even if the element being filtered wouldn't otherwise do so.
3677    */
3678   if (usingSVGEffects) {
3679     MOZ_ASSERT(usingFilter || usingMask,
3680                "Beside filter & mask/clip-path, what else effect do we have?");
3681 
3682     if (clipCapturedBy == ContainerItemType::Filter) {
3683       clipState.Restore();
3684     }
3685     // Revert to the post-filter dirty rect.
3686     aBuilder->SetVisibleRect(visibleRectOutsideSVGEffects);
3687 
3688     // Skip all filter effects while generating glyph mask.
3689     if (usingFilter && !aBuilder->IsForGenerateGlyphMask()) {
3690       /* List now emptied, so add the new list to the top. */
3691       resultList.AppendNewToTop<nsDisplayFilters>(aBuilder, this, &resultList);
3692       ct.TrackContainer(resultList.GetTop());
3693     }
3694 
3695     if (usingMask) {
3696       DisplayListClipState::AutoSaveRestore maskClipState(aBuilder);
3697       // The mask should move with aBuilder->CurrentActiveScrolledRoot(), so
3698       // that's the ASR we prefer to use for the mask item. However, we can
3699       // only do this if the mask if clipped with respect to that ASR, because
3700       // an item always needs to have finite bounds with respect to its ASR.
3701       // If we weren't able to compute a clip for the mask, we fall back to
3702       // using containerItemASR, which is the lowest common ancestor clip of
3703       // the mask's contents. That's not entirely correct, but it satisfies
3704       // the base requirement of the ASR system (that items have finite bounds
3705       // wrt. their ASR).
3706       const ActiveScrolledRoot* maskASR =
3707           clipForMask.isSome() ? aBuilder->CurrentActiveScrolledRoot()
3708                                : containerItemASR;
3709       /* List now emptied, so add the new list to the top. */
3710       resultList.AppendNewToTop<nsDisplayMasksAndClipPaths>(
3711           aBuilder, this, &resultList, maskASR);
3712       ct.TrackContainer(resultList.GetTop());
3713     }
3714 
3715     // TODO(miko): We could probably create a wraplist here and avoid creating
3716     // it later in |BuildDisplayListForChild()|.
3717     ct.ResetCreatedContainer();
3718 
3719     // Also add the hoisted scroll info items. We need those for APZ scrolling
3720     // because nsDisplayMasksAndClipPaths items can't build active layers.
3721     aBuilder->ExitSVGEffectsContents();
3722     resultList.AppendToTop(&hoistedScrollInfoItemsStorage);
3723   }
3724 
3725   /* If the list is non-empty and there is CSS group opacity without SVG
3726    * effects, wrap it up in an opacity item.
3727    */
3728   if (useOpacity) {
3729     // Don't clip nsDisplayOpacity items. We clip their descendants instead.
3730     // The clip we would set on an element with opacity would clip
3731     // all descendant content, but some should not be clipped.
3732     DisplayListClipState::AutoSaveRestore opacityClipState(aBuilder);
3733     const bool needsActiveOpacityLayer =
3734         nsDisplayOpacity::NeedsActiveLayer(aBuilder, this);
3735 
3736     resultList.AppendNewToTop<nsDisplayOpacity>(
3737         aBuilder, this, &resultList, containerItemASR,
3738         opacityItemForEventsAndPluginsOnly, needsActiveOpacityLayer);
3739     ct.TrackContainer(resultList.GetTop());
3740   }
3741 
3742   /* If we're going to apply a transformation and don't have preserve-3d set,
3743    * wrap everything in an nsDisplayTransform. If there's nothing in the list,
3744    * don't add anything.
3745    *
3746    * For the preserve-3d case we want to individually wrap every child in the
3747    * list with a separate nsDisplayTransform instead. When the child is already
3748    * an nsDisplayTransform, we can skip this step, as the computed transform
3749    * will already include our own.
3750    *
3751    * We also traverse into sublists created by nsDisplayWrapList, so that we
3752    * find all the correct children.
3753    */
3754   if (isTransformed && extend3DContext) {
3755     // Install dummy nsDisplayTransform as a leaf containing
3756     // descendants not participating this 3D rendering context.
3757     nsDisplayList nonparticipants;
3758     nsDisplayList participants;
3759     int index = 1;
3760 
3761     nsDisplayItem* separator = nullptr;
3762 
3763     while (nsDisplayItem* item = resultList.RemoveBottom()) {
3764       if (ItemParticipatesIn3DContext(this, item) &&
3765           !item->GetClip().HasClip()) {
3766         // The frame of this item participates the same 3D context.
3767         WrapSeparatorTransform(aBuilder, this, &nonparticipants, &participants,
3768                                index++, &separator);
3769 
3770         participants.AppendToTop(item);
3771       } else {
3772         // The frame of the item doesn't participate the current
3773         // context, or has no transform.
3774         //
3775         // For items participating but not transformed, they are add
3776         // to nonparticipants to get a separator layer for handling
3777         // clips, if there is, on an intermediate surface.
3778         // \see ContainerLayer::DefaultComputeEffectiveTransforms().
3779         nonparticipants.AppendToTop(item);
3780       }
3781     }
3782     WrapSeparatorTransform(aBuilder, this, &nonparticipants, &participants,
3783                            index++, &separator);
3784 
3785     if (separator) {
3786       ct.TrackContainer(separator);
3787     }
3788 
3789     resultList.AppendToTop(&participants);
3790   }
3791 
3792   if (isTransformed) {
3793     transformedCssClip.Restore();
3794     if (clipCapturedBy == ContainerItemType::Transform) {
3795       // Restore clip state now so nsDisplayTransform is clipped properly.
3796       clipState.Restore();
3797     }
3798     // Revert to the dirtyrect coming in from the parent, without our transform
3799     // taken into account.
3800     aBuilder->SetVisibleRect(visibleRectOutsideTransform);
3801     // Revert to the outer reference frame and offset because all display
3802     // items we create from now on are outside the transform.
3803     nsPoint toOuterReferenceFrame;
3804     const nsIFrame* outerReferenceFrame = this;
3805     if (this != aBuilder->RootReferenceFrame()) {
3806       outerReferenceFrame =
3807           aBuilder->FindReferenceFrameFor(GetParent(), &toOuterReferenceFrame);
3808     }
3809     buildingDisplayList.SetReferenceFrameAndCurrentOffset(
3810         outerReferenceFrame, GetOffsetToCrossDoc(outerReferenceFrame));
3811 
3812     // We would like to block async animations for ancestors of ones not
3813     // prerendered in the preserve-3d tree. Now that we've finished processing
3814     // all descendants, update allowAsyncAnimation to take their prerender
3815     // state into account
3816     // FIXME: We don't block async animations for previous siblings because
3817     // their prerender decisions have been made. We may have to figure out a
3818     // better way to rollback their prerender decisions.
3819     // Alternatively we could not block animations for later siblings, and only
3820     // block them for ancestors of a blocked one.
3821     if ((extend3DContext || combines3DTransformWithAncestors) &&
3822         allowAsyncAnimation) {
3823       // aBuilder->GetPreserves3DAllowAsyncAnimation() means the inner or
3824       // previous silbing frames are allowed/disallowed for async animations.
3825       allowAsyncAnimation = aBuilder->GetPreserves3DAllowAsyncAnimation();
3826     }
3827 
3828     nsDisplayTransform* transformItem = MakeDisplayItem<nsDisplayTransform>(
3829         aBuilder, this, &resultList, visibleRect, allowAsyncAnimation);
3830     if (transformItem) {
3831       resultList.AppendToTop(transformItem);
3832       ct.TrackContainer(transformItem);
3833     }
3834 
3835     if (hasPerspective) {
3836       if (clipCapturedBy == ContainerItemType::Perspective) {
3837         clipState.Restore();
3838       }
3839       resultList.AppendNewToTop<nsDisplayPerspective>(aBuilder, this,
3840                                                       &resultList);
3841       ct.TrackContainer(resultList.GetTop());
3842     }
3843   }
3844 
3845   if (clipCapturedBy ==
3846       ContainerItemType::OwnLayerForTransformWithRoundedClip) {
3847     clipState.Restore();
3848     resultList.AppendNewToTopWithIndex<nsDisplayOwnLayer>(
3849         aBuilder, this,
3850         /* aIndex = */ nsDisplayOwnLayer::OwnLayerForTransformWithRoundedClip,
3851         &resultList, aBuilder->CurrentActiveScrolledRoot(),
3852         nsDisplayOwnLayerFlags::None, ScrollbarData{},
3853         /* aForceActive = */ false, false);
3854     ct.TrackContainer(resultList.GetTop());
3855   }
3856 
3857   /* If we have sticky positioning, wrap it in a sticky position item.
3858    */
3859   if (useFixedPosition) {
3860     if (clipCapturedBy == ContainerItemType::FixedPosition) {
3861       clipState.Restore();
3862     }
3863     // The ASR for the fixed item should be the ASR of our containing block,
3864     // which has been set as the builder's current ASR, unless this frame is
3865     // invisible and we hadn't saved display item data for it. In that case,
3866     // we need to take the containerItemASR since we might have fixed children.
3867     // For WebRender, we want to the know what |containerItemASR| is for the
3868     // case where the fixed-pos item is not a "real" fixed-pos item (e.g. it's
3869     // nested inside a scrolling transform), so we stash that on the display
3870     // item as well.
3871     const ActiveScrolledRoot* fixedASR = ActiveScrolledRoot::PickAncestor(
3872         containerItemASR, aBuilder->CurrentActiveScrolledRoot());
3873     resultList.AppendNewToTop<nsDisplayFixedPosition>(
3874         aBuilder, this, &resultList, fixedASR, containerItemASR);
3875     ct.TrackContainer(resultList.GetTop());
3876   } else if (useStickyPosition) {
3877     // For position:sticky, the clip needs to be applied both to the sticky
3878     // container item and to the contents. The container item needs the clip
3879     // because a scrolled clip needs to move independently from the sticky
3880     // contents, and the contents need the clip so that they have finite
3881     // clipped bounds with respect to the container item's ASR. The latter is
3882     // a little tricky in the case where the sticky item has both fixed and
3883     // non-fixed descendants, because that means that the sticky container
3884     // item's ASR is the ASR of the fixed descendant.
3885     // For WebRender display list building, though, we still want to know the
3886     // the ASR that the sticky container item would normally have, so we stash
3887     // that on the display item as the "container ASR" (i.e. the normal ASR of
3888     // the container item, excluding the special behaviour induced by fixed
3889     // descendants).
3890     const ActiveScrolledRoot* stickyASR = ActiveScrolledRoot::PickAncestor(
3891         containerItemASR, aBuilder->CurrentActiveScrolledRoot());
3892     resultList.AppendNewToTop<nsDisplayStickyPosition>(
3893         aBuilder, this, &resultList, stickyASR,
3894         aBuilder->CurrentActiveScrolledRoot(),
3895         clipState.IsClippedToDisplayPort());
3896     ct.TrackContainer(resultList.GetTop());
3897 
3898     // If the sticky element is inside a filter, annotate the scroll frame that
3899     // scrolls the filter as having out-of-flow content inside a filter (this
3900     // inhibits paint skipping).
3901     if (aBuilder->GetFilterASR() && aBuilder->GetFilterASR() == stickyASR) {
3902       aBuilder->GetFilterASR()
3903           ->mScrollableFrame->SetHasOutOfFlowContentInsideFilter();
3904     }
3905   }
3906 
3907   /* If there's blending, wrap up the list in a blend-mode item. Note
3908    * that opacity can be applied before blending as the blend color is
3909    * not affected by foreground opacity (only background alpha).
3910    */
3911 
3912   if (useBlendMode) {
3913     DisplayListClipState::AutoSaveRestore blendModeClipState(aBuilder);
3914     resultList.AppendNewToTop<nsDisplayBlendMode>(aBuilder, this, &resultList,
3915                                                   effects->mMixBlendMode,
3916                                                   containerItemASR, false);
3917     ct.TrackContainer(resultList.GetTop());
3918   }
3919 
3920   bool createdOwnLayer = false;
3921   CreateOwnLayerIfNeeded(aBuilder, &resultList,
3922                          nsDisplayOwnLayer::OwnLayerForStackingContext,
3923                          &createdOwnLayer);
3924   if (createdOwnLayer) {
3925     ct.TrackContainer(resultList.GetTop());
3926   }
3927 
3928   if (aCreatedContainerItem) {
3929     *aCreatedContainerItem = ct.mCreatedContainer;
3930   }
3931 
3932   if (hitTestInfo) {
3933     // WebRender support is not yet implemented.
3934     MOZ_ASSERT(!gfxVars::UseWebRender());
3935     AddHitTestInfo(aBuilder, &resultList, ct.mContainer, this,
3936                    std::move(hitTestInfo));
3937   }
3938 
3939   aList->AppendToTop(&resultList);
3940 }
3941 
WrapInWrapList(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const ActiveScrolledRoot * aContainerASR,bool aBuiltContainerItem=false)3942 static nsDisplayItem* WrapInWrapList(nsDisplayListBuilder* aBuilder,
3943                                      nsIFrame* aFrame, nsDisplayList* aList,
3944                                      const ActiveScrolledRoot* aContainerASR,
3945                                      bool aBuiltContainerItem = false) {
3946   nsDisplayItem* item = aList->GetBottom();
3947   if (!item) {
3948     return nullptr;
3949   }
3950 
3951   // We need a wrap list if there are multiple items, or if the single
3952   // item has a different frame. This can change in a partial build depending
3953   // on which items we build, so we need to ensure that we don't transition
3954   // to/from a wrap list without invalidating correctly.
3955   bool needsWrapList =
3956       item->GetAbove() || item->Frame() != aFrame || item->GetChildren();
3957 
3958   // If we have an explicit container item (that can't change without an
3959   // invalidation) or we're doing a full build and don't need a wrap list, then
3960   // we can skip adding one.
3961   if (aBuiltContainerItem || (!aBuilder->IsPartialUpdate() && !needsWrapList)) {
3962     aList->RemoveBottom();
3963     return item;
3964   }
3965 
3966   // If we're doing a partial build and we didn't need a wrap list
3967   // previously then we can try to work from there.
3968   if (aBuilder->IsPartialUpdate() &&
3969       !aFrame->HasDisplayItem(uint32_t(DisplayItemType::TYPE_CONTAINER))) {
3970     // If we now need a wrap list, we must previously have had no display items
3971     // or a single one belonging to this frame. Mark the item itself as
3972     // discarded so that RetainedDisplayListBuilder uses the ones we just built.
3973     // We don't want to mark the frame as modified as that would invalidate
3974     // positioned descendants that might be outside of this list, and might not
3975     // have been rebuilt this time.
3976     if (needsWrapList) {
3977       DiscardOldItems(aFrame);
3978     } else {
3979       aList->RemoveBottom();
3980       return item;
3981     }
3982   }
3983 
3984   // The last case we could try to handle is when we previously had a wrap list,
3985   // but no longer need it. Unfortunately we can't differentiate this case from
3986   // a partial build where other children exist but we just didn't build them
3987   // this time.
3988   // TODO:RetainedDisplayListBuilder's merge phase has the full list and
3989   // could strip them out.
3990 
3991   return MakeDisplayItem<nsDisplayContainer>(aBuilder, aFrame, aContainerASR,
3992                                              aList);
3993 }
3994 
3995 /**
3996  * Check if a frame should be visited for building display list.
3997  */
DescendIntoChild(nsDisplayListBuilder * aBuilder,const nsIFrame * aChild,const nsRect & aVisible,const nsRect & aDirty)3998 static bool DescendIntoChild(nsDisplayListBuilder* aBuilder,
3999                              const nsIFrame* aChild, const nsRect& aVisible,
4000                              const nsRect& aDirty) {
4001   if (aChild->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) {
4002     return true;
4003   }
4004 
4005   // If the child is a scrollframe that we want to ignore, then we need
4006   // to descend into it because its scrolled child may intersect the dirty
4007   // area even if the scrollframe itself doesn't.
4008   if (aChild == aBuilder->GetIgnoreScrollFrame()) {
4009     return true;
4010   }
4011 
4012   // There are cases where the "ignore scroll frame" on the builder is not set
4013   // correctly, and so we additionally want to catch cases where the child is
4014   // a root scrollframe and we are ignoring scrolling on the viewport.
4015   if (aChild == aBuilder->GetPresShellIgnoreScrollFrame()) {
4016     return true;
4017   }
4018 
4019   const nsRect overflow = aChild->GetVisualOverflowRect();
4020 
4021   if (aDirty.Intersects(overflow)) {
4022     return true;
4023   }
4024 
4025   if (aChild->ForceDescendIntoIfVisible() && aVisible.Intersects(overflow)) {
4026     return true;
4027   }
4028 
4029   if (aChild->IsFrameOfType(nsIFrame::eTablePart)) {
4030     // Relative positioning and transforms can cause table parts to move, but we
4031     // will still paint the backgrounds for their ancestor parts under them at
4032     // their 'normal' position. That means that we must consider the overflow
4033     // rects at both positions.
4034 
4035     // We convert the overflow rect into the nsTableFrame's coordinate
4036     // space, applying the normal position offset at each step. Then we
4037     // compare that against the builder's cached dirty rect in table
4038     // coordinate space.
4039     const nsIFrame* f = aChild;
4040     nsRect normalPositionOverflowRelativeToTable = overflow;
4041 
4042     while (f->IsFrameOfType(nsIFrame::eTablePart)) {
4043       normalPositionOverflowRelativeToTable += f->GetNormalPosition();
4044       f = f->GetParent();
4045     }
4046 
4047     nsDisplayTableBackgroundSet* tableBGs = aBuilder->GetTableBackgroundSet();
4048     if (tableBGs && tableBGs->GetDirtyRect().Intersects(
4049                         normalPositionOverflowRelativeToTable)) {
4050       return true;
4051     }
4052   }
4053 
4054   return false;
4055 }
4056 
BuildDisplayListForSimpleChild(nsDisplayListBuilder * aBuilder,nsIFrame * aChild,const nsDisplayListSet & aLists)4057 void nsIFrame::BuildDisplayListForSimpleChild(nsDisplayListBuilder* aBuilder,
4058                                               nsIFrame* aChild,
4059                                               const nsDisplayListSet& aLists) {
4060   // This is the shortcut for frames been handled along the common
4061   // path, the most common one of THE COMMON CASE mentioned later.
4062   MOZ_ASSERT(aChild->Type() != LayoutFrameType::Placeholder);
4063   MOZ_ASSERT(!aBuilder->GetSelectedFramesOnly() &&
4064                  !aBuilder->GetIncludeAllOutOfFlows(),
4065              "It should be held for painting to window");
4066   MOZ_ASSERT(aChild->GetStateBits() & NS_FRAME_SIMPLE_DISPLAYLIST);
4067 
4068   const nsPoint offset = aChild->GetOffsetTo(this);
4069   const nsRect visible = aBuilder->GetVisibleRect() - offset;
4070   const nsRect dirty = aBuilder->GetDirtyRect() - offset;
4071 
4072   if (!DescendIntoChild(aBuilder, aChild, visible, dirty)) {
4073     return;
4074   }
4075 
4076   // Child cannot be transformed since it is not a stacking context.
4077   nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(
4078       aBuilder, aChild, visible, dirty, false);
4079 
4080   CheckForApzAwareEventHandlers(aBuilder, aChild);
4081 
4082   aBuilder->BuildCompositorHitTestInfoIfNeeded(
4083       aChild, aLists.BorderBackground(),
4084       buildingForChild.IsAnimatedGeometryRoot());
4085 
4086   aChild->MarkAbsoluteFramesForDisplayList(aBuilder);
4087   aBuilder->AdjustWindowDraggingRegion(aChild);
4088   aBuilder->Check();
4089   aChild->BuildDisplayList(aBuilder, aLists);
4090   aBuilder->Check();
4091   aBuilder->DisplayCaret(aChild, aLists.Outlines());
4092 #ifdef DEBUG
4093   DisplayDebugBorders(aBuilder, aChild, aLists);
4094 #endif
4095 }
4096 
ShouldSkipFrame(nsDisplayListBuilder * aBuilder,const nsIFrame * aFrame)4097 static bool ShouldSkipFrame(nsDisplayListBuilder* aBuilder,
4098                             const nsIFrame* aFrame) {
4099   // If painting is restricted to just the background of the top level frame,
4100   // then we have nothing to do here.
4101   if (aBuilder->IsBackgroundOnly()) {
4102     return true;
4103   }
4104 
4105   if (aBuilder->IsForGenerateGlyphMask() &&
4106       (!aFrame->IsTextFrame() && aFrame->IsLeaf())) {
4107     return true;
4108   }
4109 
4110   // The placeholder frame should have the same content as the OOF frame.
4111   if (aBuilder->GetSelectedFramesOnly() &&
4112       (aFrame->IsLeaf() && !aFrame->IsSelected())) {
4113     return true;
4114   }
4115 
4116   static const nsFrameState skipFlags =
4117       (NS_FRAME_TOO_DEEP_IN_FRAME_TREE | NS_FRAME_IS_NONDISPLAY);
4118 
4119   return (aFrame->GetStateBits() & skipFlags);
4120 }
4121 
BuildDisplayListForChild(nsDisplayListBuilder * aBuilder,nsIFrame * aChild,const nsDisplayListSet & aLists,uint32_t aFlags)4122 void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
4123                                         nsIFrame* aChild,
4124                                         const nsDisplayListSet& aLists,
4125                                         uint32_t aFlags) {
4126   AutoCheckBuilder check(aBuilder);
4127 
4128   if (ShouldSkipFrame(aBuilder, aChild)) {
4129     return;
4130   }
4131 
4132   nsIFrame* child = aChild;
4133   auto* placeholder = child->IsPlaceholderFrame()
4134                           ? static_cast<nsPlaceholderFrame*>(child)
4135                           : nullptr;
4136   nsIFrame* childOrOutOfFlow =
4137       placeholder ? placeholder->GetOutOfFlowFrame() : child;
4138 
4139   nsIFrame* parent = childOrOutOfFlow->GetParent();
4140   const bool shouldApplyOverflowClip =
4141       nsFrame::ShouldApplyOverflowClipping(parent, parent->StyleDisplay());
4142 
4143   const bool isPaintingToWindow = aBuilder->IsPaintingToWindow();
4144   const bool doingShortcut =
4145       isPaintingToWindow &&
4146       (child->GetStateBits() & NS_FRAME_SIMPLE_DISPLAYLIST) &&
4147       // Animations may change the stacking context state.
4148       // ShouldApplyOverflowClipping is affected by the parent style, which does
4149       // not invalidate the NS_FRAME_SIMPLE_DISPLAYLIST bit.
4150       !(shouldApplyOverflowClip || child->MayHaveTransformAnimation() ||
4151         child->MayHaveOpacityAnimation());
4152 
4153   if (aBuilder->IsForPainting()) {
4154     aBuilder->ClearWillChangeBudgetStatus(child);
4155   }
4156 
4157   if (StaticPrefs::layout_css_scroll_anchoring_highlight()) {
4158     if (child->FirstContinuation()->IsScrollAnchor()) {
4159       nsRect bounds = child->GetContentRectRelativeToSelf() +
4160                       aBuilder->ToReferenceFrame(child);
4161       nsDisplaySolidColor* color = MakeDisplayItem<nsDisplaySolidColor>(
4162           aBuilder, child, bounds, NS_RGBA(255, 0, 255, 64));
4163       if (color) {
4164         color->SetOverrideZIndex(INT32_MAX);
4165         aLists.PositionedDescendants()->AppendToTop(color);
4166       }
4167     }
4168   }
4169 
4170   if (doingShortcut) {
4171     BuildDisplayListForSimpleChild(aBuilder, child, aLists);
4172     return;
4173   }
4174 
4175   // dirty rect in child-relative coordinates
4176   NS_ASSERTION(aBuilder->GetCurrentFrame() == this, "Wrong coord space!");
4177   const nsPoint offset = child->GetOffsetTo(this);
4178   nsRect visible = aBuilder->GetVisibleRect() - offset;
4179   nsRect dirty = aBuilder->GetDirtyRect() - offset;
4180 
4181   nsDisplayListBuilder::OutOfFlowDisplayData* savedOutOfFlowData = nullptr;
4182   if (placeholder) {
4183     if (placeholder->GetStateBits() & PLACEHOLDER_FOR_TOPLAYER) {
4184       // If the out-of-flow frame is in the top layer, the viewport frame
4185       // will paint it. Skip it here. Note that, only out-of-flow frames
4186       // with this property should be skipped, because non-HTML elements
4187       // may stop their children from being out-of-flow. Those frames
4188       // should still be handled in the normal in-flow path.
4189       return;
4190     }
4191 
4192     child = childOrOutOfFlow;
4193     if (aBuilder->IsForPainting()) {
4194       aBuilder->ClearWillChangeBudgetStatus(child);
4195     }
4196 
4197     // If 'child' is a pushed float then it's owned by a block that's not an
4198     // ancestor of the placeholder, and it will be painted by that block and
4199     // should not be painted through the placeholder. Also recheck
4200     // NS_FRAME_TOO_DEEP_IN_FRAME_TREE and NS_FRAME_IS_NONDISPLAY.
4201     static const nsFrameState skipFlags =
4202         (NS_FRAME_IS_PUSHED_FLOAT | NS_FRAME_TOO_DEEP_IN_FRAME_TREE |
4203          NS_FRAME_IS_NONDISPLAY);
4204     if (child->HasAnyStateBits(skipFlags) || nsLayoutUtils::IsPopup(child)) {
4205       return;
4206     }
4207 
4208     MOZ_ASSERT(child->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW));
4209     savedOutOfFlowData = nsDisplayListBuilder::GetOutOfFlowData(child);
4210 
4211     if (aBuilder->GetIncludeAllOutOfFlows()) {
4212       visible = child->GetVisualOverflowRect();
4213       dirty = child->GetVisualOverflowRect();
4214     } else if (savedOutOfFlowData) {
4215       visible =
4216           savedOutOfFlowData->GetVisibleRectForFrame(aBuilder, child, &dirty);
4217     } else {
4218       // The out-of-flow frame did not intersect the dirty area. We may still
4219       // need to traverse into it, since it may contain placeholders we need
4220       // to enter to reach other out-of-flow frames that are visible.
4221       visible.SetEmpty();
4222       dirty.SetEmpty();
4223     }
4224   }
4225 
4226   NS_ASSERTION(!child->IsPlaceholderFrame(),
4227                "Should have dealt with placeholders already");
4228 
4229   if (!DescendIntoChild(aBuilder, child, visible, dirty)) {
4230     return;
4231   }
4232 
4233   const bool isSVG = child->GetStateBits() & NS_FRAME_SVG_LAYOUT;
4234 
4235   // This flag is raised if the control flow strays off the common path.
4236   // The common path is the most common one of THE COMMON CASE mentioned later.
4237   bool awayFromCommonPath = !isPaintingToWindow;
4238 
4239   // true if this is a real or pseudo stacking context
4240   bool pseudoStackingContext =
4241       (aFlags & DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT) != 0;
4242 
4243   if (!pseudoStackingContext && !isSVG && (aFlags & DISPLAY_CHILD_INLINE) &&
4244       !child->IsFrameOfType(eLineParticipant)) {
4245     // child is a non-inline frame in an inline context, i.e.,
4246     // it acts like inline-block or inline-table. Therefore it is a
4247     // pseudo-stacking-context.
4248     pseudoStackingContext = true;
4249   }
4250 
4251   const nsStyleDisplay* ourDisp = StyleDisplay();
4252   // REVIEW: Taken from nsBoxFrame::Paint
4253   // Don't paint our children if the theme object is a leaf.
4254   if (IsThemed(ourDisp) &&
4255       !PresContext()->Theme()->WidgetIsContainer(ourDisp->mAppearance))
4256     return;
4257 
4258   // Since we're now sure that we're adding this frame to the display list
4259   // (which means we're painting it, modulo occlusion), mark it as visible
4260   // within the displayport.
4261   if (isPaintingToWindow && child->TrackingVisibility()) {
4262     child->PresShell()->EnsureFrameInApproximatelyVisibleList(child);
4263     awayFromCommonPath = true;
4264   }
4265 
4266   child->SetBuiltDisplayList(true);
4267 
4268   // Child is composited if it's transformed, partially transparent, or has
4269   // SVG effects or a blend mode..
4270   const nsStyleDisplay* disp = child->StyleDisplay();
4271   const nsStyleEffects* effects = child->StyleEffects();
4272   const nsStylePosition* pos = child->StylePosition();
4273 
4274   const bool isPositioned = disp->IsAbsPosContainingBlock(child);
4275 
4276   const bool isStackingContext =
4277       (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT) ||
4278       child->IsStackingContext(disp, pos, effects, isPositioned);
4279 
4280   if (pseudoStackingContext || isStackingContext || isPositioned ||
4281       placeholder || (!isSVG && disp->IsFloating(child)) ||
4282       (isSVG && effects->mClip.IsRect() && IsSVGContentWithCSSClip(child))) {
4283     pseudoStackingContext = true;
4284     awayFromCommonPath = true;
4285   }
4286 
4287   NS_ASSERTION(!isStackingContext || pseudoStackingContext,
4288                "Stacking contexts must also be pseudo-stacking-contexts");
4289 
4290   nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(
4291       aBuilder, child, visible, dirty);
4292   DisplayListClipState::AutoClipMultiple clipState(aBuilder);
4293   nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(aBuilder);
4294   CheckForApzAwareEventHandlers(aBuilder, child);
4295 
4296   if (savedOutOfFlowData) {
4297     aBuilder->SetBuildingInvisibleItems(false);
4298 
4299     clipState.SetClipChainForContainingBlockDescendants(
4300         savedOutOfFlowData->mContainingBlockClipChain);
4301     asrSetter.SetCurrentActiveScrolledRoot(
4302         savedOutOfFlowData->mContainingBlockActiveScrolledRoot);
4303     MOZ_ASSERT(awayFromCommonPath,
4304                "It is impossible when savedOutOfFlowData is true");
4305   } else if (HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) &&
4306              placeholder) {
4307     NS_ASSERTION(visible.IsEmpty(), "should have empty visible rect");
4308     // Every item we build from now until we descent into an out of flow that
4309     // does have saved out of flow data should be invisible. This state gets
4310     // restored when AutoBuildingDisplayList gets out of scope.
4311     aBuilder->SetBuildingInvisibleItems(true);
4312 
4313     // If we have nested out-of-flow frames and the outer one isn't visible
4314     // then we won't have stored clip data for it. We can just clear the clip
4315     // instead since we know we won't render anything, and the inner out-of-flow
4316     // frame will setup the correct clip for itself.
4317     clipState.SetClipChainForContainingBlockDescendants(nullptr);
4318   }
4319 
4320   // Setup clipping for the parent's overflow:-moz-hidden-unscrollable,
4321   // or overflow:hidden on elements that don't support scrolling (and therefore
4322   // don't create nsHTML/XULScrollFrame). This clipping needs to not clip
4323   // anything directly rendered by the parent, only the rendering of its
4324   // children.
4325   // Don't use overflowClip to restrict the dirty rect, since some of the
4326   // descendants may not be clipped by it. Even if we end up with unnecessary
4327   // display items, they'll be pruned during ComputeVisibility.
4328   //
4329   // FIXME(emilio): Why can't we handle this more similarly to `clip` (on the
4330   // parent, rather than on the children)? Would ClipContentDescendants do what
4331   // we want?
4332   if (shouldApplyOverflowClip) {
4333     ApplyOverflowClipping(aBuilder, parent, clipState);
4334     awayFromCommonPath = true;
4335   }
4336 
4337   nsDisplayList list;
4338   nsDisplayList extraPositionedDescendants;
4339   const ActiveScrolledRoot* wrapListASR;
4340   bool builtContainerItem = false;
4341   if (isStackingContext) {
4342     // True stacking context.
4343     // For stacking contexts, BuildDisplayListForStackingContext handles
4344     // clipping and MarkAbsoluteFramesForDisplayList.
4345     nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
4346     child->BuildDisplayListForStackingContext(aBuilder, &list,
4347                                               &builtContainerItem);
4348     wrapListASR = contASRTracker.GetContainerASR();
4349     if (aBuilder->GetCaretFrame() == child) {
4350       builtContainerItem = false;
4351     }
4352   } else {
4353     Maybe<nsRect> clipPropClip =
4354         child->GetClipPropClipRect(disp, effects, child->GetSize());
4355     if (clipPropClip) {
4356       aBuilder->IntersectVisibleRect(*clipPropClip);
4357       aBuilder->IntersectDirtyRect(*clipPropClip);
4358       clipState.ClipContentDescendants(*clipPropClip +
4359                                        aBuilder->ToReferenceFrame(child));
4360       awayFromCommonPath = true;
4361     }
4362 
4363     child->MarkAbsoluteFramesForDisplayList(aBuilder);
4364 
4365     const bool differentAGR = buildingForChild.IsAnimatedGeometryRoot();
4366 
4367     if (!awayFromCommonPath &&
4368         // Some SVG frames might change opacity without invalidating the frame,
4369         // so exclude them from the fast-path.
4370         !child->IsFrameOfType(nsIFrame::eSVG)) {
4371       // The shortcut is available for the child for next time.
4372       child->AddStateBits(NS_FRAME_SIMPLE_DISPLAYLIST);
4373     }
4374 
4375     if (!pseudoStackingContext) {
4376       // THIS IS THE COMMON CASE.
4377       // Not a pseudo or real stacking context. Do the simple thing and
4378       // return early.
4379 
4380       aBuilder->BuildCompositorHitTestInfoIfNeeded(
4381           child, aLists.BorderBackground(), differentAGR);
4382 
4383       aBuilder->AdjustWindowDraggingRegion(child);
4384       aBuilder->Check();
4385       child->BuildDisplayList(aBuilder, aLists);
4386       aBuilder->Check();
4387       aBuilder->DisplayCaret(child, aLists.Outlines());
4388 #ifdef DEBUG
4389       DisplayDebugBorders(aBuilder, child, aLists);
4390 #endif
4391       return;
4392     }
4393 
4394     // A pseudo-stacking context (e.g., a positioned element with z-index auto).
4395     // We allow positioned descendants of the child to escape to our parent
4396     // stacking context's positioned descendant list, because they might be
4397     // z-index:non-auto
4398     nsDisplayListCollection pseudoStack(aBuilder);
4399 
4400     // If this frame has z-index != 0, then the display item might get sorted
4401     // into a different place in the list, and we can't rely on the previous
4402     // hit test info to still be behind us. Force a new hit test info for this
4403     // item, and for the item after it, so that we always have the right hit
4404     // test info.
4405     bool mayBeSorted = isPositioned && (ZIndex() != 0);
4406 
4407     aBuilder->BuildCompositorHitTestInfoIfNeeded(
4408         child, pseudoStack.BorderBackground(), differentAGR || mayBeSorted);
4409 
4410     aBuilder->AdjustWindowDraggingRegion(child);
4411     nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
4412     aBuilder->Check();
4413     child->BuildDisplayList(aBuilder, pseudoStack);
4414     aBuilder->Check();
4415     if (aBuilder->DisplayCaret(child, pseudoStack.Outlines())) {
4416       builtContainerItem = false;
4417     }
4418 
4419     // If we forced a new hit-test info because this frame is going to be
4420     // sorted, then clear the 'previous' data on the builder so that the next
4421     // item also gets a new hit test info. That way we're guaranteeing hit-test
4422     // info before and after each item that might get moved to a different spot.
4423     if (mayBeSorted) {
4424       aBuilder->SetCompositorHitTestInfo(
4425           nsRect(), CompositorHitTestFlags::eVisibleToHitTest);
4426     }
4427 
4428     wrapListASR = contASRTracker.GetContainerASR();
4429 
4430     list.AppendToTop(pseudoStack.BorderBackground());
4431     list.AppendToTop(pseudoStack.BlockBorderBackgrounds());
4432     list.AppendToTop(pseudoStack.Floats());
4433     list.AppendToTop(pseudoStack.Content());
4434     list.AppendToTop(pseudoStack.Outlines());
4435     extraPositionedDescendants.AppendToTop(pseudoStack.PositionedDescendants());
4436 #ifdef DEBUG
4437     DisplayDebugBorders(aBuilder, child, aLists);
4438 #endif
4439   }
4440 
4441   buildingForChild.RestoreBuildingInvisibleItemsValue();
4442 
4443   if (isPositioned || isStackingContext ||
4444       (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) {
4445     // Genuine stacking contexts, and positioned pseudo-stacking-contexts,
4446     // go in this level.
4447     if (!list.IsEmpty()) {
4448       nsDisplayItem* item = WrapInWrapList(aBuilder, child, &list, wrapListASR,
4449                                            builtContainerItem);
4450       if (isSVG) {
4451         aLists.Content()->AppendToTop(item);
4452       } else {
4453         aLists.PositionedDescendants()->AppendToTop(item);
4454       }
4455     }
4456   } else if (!isSVG && disp->IsFloating(child)) {
4457     if (!list.IsEmpty()) {
4458       aLists.Floats()->AppendToTop(
4459           WrapInWrapList(aBuilder, child, &list, wrapListASR));
4460     }
4461   } else {
4462     aLists.Content()->AppendToTop(&list);
4463   }
4464   // We delay placing the positioned descendants of positioned frames to here,
4465   // because in the absence of z-index this is the correct order for them.
4466   // This doesn't affect correctness because the positioned descendants list
4467   // is sorted by z-order and content in BuildDisplayListForStackingContext,
4468   // but it means that sort routine needs to do less work.
4469   aLists.PositionedDescendants()->AppendToTop(&extraPositionedDescendants);
4470 }
4471 
MarkAbsoluteFramesForDisplayList(nsDisplayListBuilder * aBuilder)4472 void nsIFrame::MarkAbsoluteFramesForDisplayList(
4473     nsDisplayListBuilder* aBuilder) {
4474   if (IsAbsoluteContainer()) {
4475     aBuilder->MarkFramesForDisplayList(
4476         this, GetAbsoluteContainingBlock()->GetChildList());
4477   }
4478 }
4479 
GetContentForEvent(WidgetEvent * aEvent,nsIContent ** aContent)4480 nsresult nsIFrame::GetContentForEvent(WidgetEvent* aEvent,
4481                                       nsIContent** aContent) {
4482   nsIFrame* f = nsLayoutUtils::GetNonGeneratedAncestor(this);
4483   *aContent = f->GetContent();
4484   NS_IF_ADDREF(*aContent);
4485   return NS_OK;
4486 }
4487 
FireDOMEvent(const nsAString & aDOMEventName,nsIContent * aContent)4488 void nsFrame::FireDOMEvent(const nsAString& aDOMEventName,
4489                            nsIContent* aContent) {
4490   nsIContent* target = aContent ? aContent : GetContent();
4491 
4492   if (target) {
4493     RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(
4494         target, aDOMEventName, CanBubble::eYes, ChromeOnlyDispatch::eNo);
4495     DebugOnly<nsresult> rv = asyncDispatcher->PostDOMEvent();
4496     NS_ASSERTION(NS_SUCCEEDED(rv), "AsyncEventDispatcher failed to dispatch");
4497   }
4498 }
4499 
HandleEvent(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus)4500 nsresult nsIFrame::HandleEvent(nsPresContext* aPresContext,
4501                                WidgetGUIEvent* aEvent,
4502                                nsEventStatus* aEventStatus) {
4503   if (aEvent->mMessage == eMouseMove) {
4504     // XXX If the second argument of HandleDrag() is WidgetMouseEvent,
4505     //     the implementation becomes simpler.
4506     return HandleDrag(aPresContext, aEvent, aEventStatus);
4507   }
4508 
4509   if ((aEvent->mClass == eMouseEventClass &&
4510        aEvent->AsMouseEvent()->mButton == MouseButton::eLeft) ||
4511       aEvent->mClass == eTouchEventClass) {
4512     if (aEvent->mMessage == eMouseDown || aEvent->mMessage == eTouchStart) {
4513       HandlePress(aPresContext, aEvent, aEventStatus);
4514     } else if (aEvent->mMessage == eMouseUp || aEvent->mMessage == eTouchEnd) {
4515       HandleRelease(aPresContext, aEvent, aEventStatus);
4516     }
4517   }
4518   return NS_OK;
4519 }
4520 
GetDataForTableSelection(const nsFrameSelection * aFrameSelection,mozilla::PresShell * aPresShell,WidgetMouseEvent * aMouseEvent,nsIContent ** aParentContent,int32_t * aContentOffset,TableSelectionMode * aTarget)4521 nsresult nsIFrame::GetDataForTableSelection(
4522     const nsFrameSelection* aFrameSelection, mozilla::PresShell* aPresShell,
4523     WidgetMouseEvent* aMouseEvent, nsIContent** aParentContent,
4524     int32_t* aContentOffset, TableSelectionMode* aTarget) {
4525   if (!aFrameSelection || !aPresShell || !aMouseEvent || !aParentContent ||
4526       !aContentOffset || !aTarget)
4527     return NS_ERROR_NULL_POINTER;
4528 
4529   *aParentContent = nullptr;
4530   *aContentOffset = 0;
4531   *aTarget = TableSelectionMode::None;
4532 
4533   int16_t displaySelection = aPresShell->GetSelectionFlags();
4534 
4535   bool selectingTableCells = aFrameSelection->IsInTableSelectionMode();
4536 
4537   // DISPLAY_ALL means we're in an editor.
4538   // If already in cell selection mode,
4539   //  continue selecting with mouse drag or end on mouse up,
4540   //  or when using shift key to extend block of cells
4541   //  (Mouse down does normal selection unless Ctrl/Cmd is pressed)
4542   bool doTableSelection =
4543       displaySelection == nsISelectionDisplay::DISPLAY_ALL &&
4544       selectingTableCells &&
4545       (aMouseEvent->mMessage == eMouseMove ||
4546        (aMouseEvent->mMessage == eMouseUp &&
4547         aMouseEvent->mButton == MouseButton::eLeft) ||
4548        aMouseEvent->IsShift());
4549 
4550   if (!doTableSelection) {
4551     // In Browser, special 'table selection' key must be pressed for table
4552     // selection or when just Shift is pressed and we're already in table/cell
4553     // selection mode
4554 #ifdef XP_MACOSX
4555     doTableSelection = aMouseEvent->IsMeta() ||
4556                        (aMouseEvent->IsShift() && selectingTableCells);
4557 #else
4558     doTableSelection = aMouseEvent->IsControl() ||
4559                        (aMouseEvent->IsShift() && selectingTableCells);
4560 #endif
4561   }
4562   if (!doTableSelection) return NS_OK;
4563 
4564   // Get the cell frame or table frame (or parent) of the current content node
4565   nsIFrame* frame = this;
4566   bool foundCell = false;
4567   bool foundTable = false;
4568 
4569   // Get the limiting node to stop parent frame search
4570   nsIContent* limiter = aFrameSelection->GetLimiter();
4571 
4572   // If our content node is an ancestor of the limiting node,
4573   // we should stop the search right now.
4574   if (limiter && limiter->IsInclusiveDescendantOf(GetContent())) return NS_OK;
4575 
4576   // We don't initiate row/col selection from here now,
4577   //  but we may in future
4578   // bool selectColumn = false;
4579   // bool selectRow = false;
4580 
4581   while (frame) {
4582     // Check for a table cell by querying to a known CellFrame interface
4583     nsITableCellLayout* cellElement = do_QueryFrame(frame);
4584     if (cellElement) {
4585       foundCell = true;
4586       // TODO: If we want to use proximity to top or left border
4587       //      for row and column selection, this is the place to do it
4588       break;
4589     } else {
4590       // If not a cell, check for table
4591       // This will happen when starting frame is the table or child of a table,
4592       //  such as a row (we were inbetween cells or in table border)
4593       nsTableWrapperFrame* tableFrame = do_QueryFrame(frame);
4594       if (tableFrame) {
4595         foundTable = true;
4596         // TODO: How can we select row when along left table edge
4597         //  or select column when along top edge?
4598         break;
4599       } else {
4600         frame = frame->GetParent();
4601         // Stop if we have hit the selection's limiting content node
4602         if (frame && frame->GetContent() == limiter) break;
4603       }
4604     }
4605   }
4606   // We aren't in a cell or table
4607   if (!foundCell && !foundTable) return NS_OK;
4608 
4609   nsIContent* tableOrCellContent = frame->GetContent();
4610   if (!tableOrCellContent) return NS_ERROR_FAILURE;
4611 
4612   nsCOMPtr<nsIContent> parentContent = tableOrCellContent->GetParent();
4613   if (!parentContent) return NS_ERROR_FAILURE;
4614 
4615   int32_t offset = parentContent->ComputeIndexOf(tableOrCellContent);
4616   // Not likely?
4617   if (offset < 0) return NS_ERROR_FAILURE;
4618 
4619   // Everything is OK -- set the return values
4620   parentContent.forget(aParentContent);
4621 
4622   *aContentOffset = offset;
4623 
4624 #if 0
4625   if (selectRow)
4626     *aTarget = TableSelectionMode::Row;
4627   else if (selectColumn)
4628     *aTarget = TableSelectionMode::Column;
4629   else
4630 #endif
4631   if (foundCell) {
4632     *aTarget = TableSelectionMode::Cell;
4633   } else if (foundTable) {
4634     *aTarget = TableSelectionMode::Table;
4635   }
4636 
4637   return NS_OK;
4638 }
4639 
IsEditingHost(const nsIFrame * aFrame)4640 static bool IsEditingHost(const nsIFrame* aFrame) {
4641   auto* element = nsGenericHTMLElement::FromNodeOrNull(aFrame->GetContent());
4642   return element && element->IsEditableRoot();
4643 }
4644 
UsedUserSelect(const nsIFrame * aFrame)4645 static StyleUserSelect UsedUserSelect(const nsIFrame* aFrame) {
4646   if (aFrame->HasAnyStateBits(NS_FRAME_GENERATED_CONTENT)) {
4647     return StyleUserSelect::None;
4648   }
4649 
4650   // Per https://drafts.csswg.org/css-ui-4/#content-selection:
4651   //
4652   // The computed value is the specified value, except:
4653   //
4654   //   1 - on editable elements where the computed value is always 'contain'
4655   //       regardless of the specified value.
4656   //   2 - when the specified value is auto, which computes to one of the other
4657   //       values [...]
4658   //
4659   // See https://github.com/w3c/csswg-drafts/issues/3344 to see why we do this
4660   // at used-value time instead of at computed-value time.
4661   //
4662   // Also, we check for auto first to allow explicitly overriding the value for
4663   // the editing host.
4664   auto style = aFrame->StyleUIReset()->mUserSelect;
4665   if (style != StyleUserSelect::Auto) {
4666     return style;
4667   }
4668 
4669   if (aFrame->IsTextInputFrame() || IsEditingHost(aFrame)) {
4670     // We don't implement 'contain' itself, but we make 'text' behave as
4671     // 'contain' for contenteditable and <input> / <textarea> elements anyway so
4672     // this is ok.
4673     return StyleUserSelect::Text;
4674   }
4675 
4676   auto* parent = nsLayoutUtils::GetParentOrPlaceholderFor(aFrame);
4677   return parent ? UsedUserSelect(parent) : StyleUserSelect::Text;
4678 }
4679 
IsSelectable(StyleUserSelect * aSelectStyle) const4680 bool nsIFrame::IsSelectable(StyleUserSelect* aSelectStyle) const {
4681   auto style = UsedUserSelect(this);
4682   if (aSelectStyle) {
4683     *aSelectStyle = style;
4684   }
4685   return style != StyleUserSelect::None;
4686 }
4687 
ShouldHaveLineIfEmpty() const4688 bool nsIFrame::ShouldHaveLineIfEmpty() const {
4689   if (Style()->IsPseudoOrAnonBox()) {
4690     return false;
4691   }
4692   return IsEditingHost(this);
4693 }
4694 
4695 /**
4696  * Handles the Mouse Press Event for the frame
4697  */
4698 NS_IMETHODIMP
HandlePress(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus)4699 nsIFrame::HandlePress(nsPresContext* aPresContext, WidgetGUIEvent* aEvent,
4700                       nsEventStatus* aEventStatus) {
4701   NS_ENSURE_ARG_POINTER(aEventStatus);
4702   if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
4703     return NS_OK;
4704   }
4705 
4706   NS_ENSURE_ARG_POINTER(aEvent);
4707   if (aEvent->mClass == eTouchEventClass) {
4708     return NS_OK;
4709   }
4710 
4711   // We often get out of sync state issues with mousedown events that
4712   // get interrupted by alerts/dialogs.
4713   // Check with the ESM to see if we should process this one
4714   if (!aPresContext->EventStateManager()->EventStatusOK(aEvent)) return NS_OK;
4715 
4716   mozilla::PresShell* presShell = aPresContext->GetPresShell();
4717   if (!presShell) {
4718     return NS_ERROR_FAILURE;
4719   }
4720 
4721   // if we are in Navigator and the click is in a draggable node, we don't want
4722   // to start selection because we don't want to interfere with a potential
4723   // drag of said node and steal all its glory.
4724   int16_t isEditor = presShell->GetSelectionFlags();
4725   // weaaak. only the editor can display frame selection not just text and
4726   // images
4727   isEditor = isEditor == nsISelectionDisplay::DISPLAY_ALL;
4728 
4729   WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
4730 
4731   if (!mouseEvent->IsAlt()) {
4732     for (nsIContent* content = mContent; content;
4733          content = content->GetFlattenedTreeParent()) {
4734       if (nsContentUtils::ContentIsDraggable(content) &&
4735           !content->IsEditable()) {
4736         // coordinate stuff is the fix for bug #55921
4737         if ((mRect - GetPosition())
4738                 .Contains(nsLayoutUtils::GetEventCoordinatesRelativeTo(
4739                     mouseEvent, RelativeTo{this}))) {
4740           return NS_OK;
4741         }
4742       }
4743     }
4744   }
4745 
4746   // check whether style allows selection
4747   // if not, don't tell selection the mouse event even occurred.
4748   StyleUserSelect selectStyle;
4749   // check for select: none
4750   if (!IsSelectable(&selectStyle)) {
4751     return NS_OK;
4752   }
4753 
4754   bool useFrameSelection = (selectStyle == StyleUserSelect::Text);
4755 
4756   // If the mouse is dragged outside the nearest enclosing scrollable area
4757   // while making a selection, the area will be scrolled. To do this, capture
4758   // the mouse on the nearest scrollable frame. If there isn't a scrollable
4759   // frame, or something else is already capturing the mouse, there's no
4760   // reason to capture.
4761   if (!PresShell::GetCapturingContent()) {
4762     nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetNearestScrollableFrame(
4763         this, nsLayoutUtils::SCROLLABLE_SAME_DOC |
4764                   nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
4765     if (scrollFrame) {
4766       nsIFrame* capturingFrame = do_QueryFrame(scrollFrame);
4767       PresShell::SetCapturingContent(capturingFrame->GetContent(),
4768                                      CaptureFlags::IgnoreAllowedState);
4769     }
4770   }
4771 
4772   // XXX This is screwy; it really should use the selection frame, not the
4773   // event frame
4774   const nsFrameSelection* frameselection = nullptr;
4775   if (useFrameSelection)
4776     frameselection = GetConstFrameSelection();
4777   else
4778     frameselection = presShell->ConstFrameSelection();
4779 
4780   if (!frameselection || frameselection->GetDisplaySelection() ==
4781                              nsISelectionController::SELECTION_OFF)
4782     return NS_OK;  // nothing to do we cannot affect selection from here
4783 
4784 #ifdef XP_MACOSX
4785   if (mouseEvent->IsControl())
4786     return NS_OK;  // short circuit. hard coded for mac due to time restraints.
4787   bool control = mouseEvent->IsMeta();
4788 #else
4789   bool control = mouseEvent->IsControl();
4790 #endif
4791 
4792   RefPtr<nsFrameSelection> fc = const_cast<nsFrameSelection*>(frameselection);
4793   if (mouseEvent->mClickCount > 1) {
4794     // These methods aren't const but can't actually delete anything,
4795     // so no need for AutoWeakFrame.
4796     fc->SetDragState(true);
4797     return HandleMultiplePress(aPresContext, mouseEvent, aEventStatus, control);
4798   }
4799 
4800   nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent,
4801                                                             RelativeTo{this});
4802   ContentOffsets offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN);
4803 
4804   if (!offsets.content) return NS_ERROR_FAILURE;
4805 
4806   // Let Ctrl/Cmd+mouse down do table selection instead of drag initiation
4807   nsCOMPtr<nsIContent> parentContent;
4808   int32_t contentOffset;
4809   TableSelectionMode target;
4810   nsresult rv;
4811   rv = GetDataForTableSelection(frameselection, presShell, mouseEvent,
4812                                 getter_AddRefs(parentContent), &contentOffset,
4813                                 &target);
4814   if (NS_SUCCEEDED(rv) && parentContent) {
4815     fc->SetDragState(true);
4816     return fc->HandleTableSelection(parentContent, contentOffset, target,
4817                                     mouseEvent);
4818   }
4819 
4820   fc->SetDelayedCaretData(0);
4821 
4822   // Check if any part of this frame is selected, and if the
4823   // user clicked inside the selected region. If so, we delay
4824   // starting a new selection since the user may be trying to
4825   // drag the selected region to some other app.
4826 
4827   if (GetContent() && GetContent()->IsMaybeSelected()) {
4828     bool inSelection = false;
4829     UniquePtr<SelectionDetails> details = frameselection->LookUpSelection(
4830         offsets.content, 0, offsets.EndOffset(), false);
4831 
4832     //
4833     // If there are any details, check to see if the user clicked
4834     // within any selected region of the frame.
4835     //
4836 
4837     for (SelectionDetails* curDetail = details.get(); curDetail;
4838          curDetail = curDetail->mNext.get()) {
4839       //
4840       // If the user clicked inside a selection, then just
4841       // return without doing anything. We will handle placing
4842       // the caret later on when the mouse is released. We ignore
4843       // the spellcheck, find and url formatting selections.
4844       //
4845       if (curDetail->mSelectionType != SelectionType::eSpellCheck &&
4846           curDetail->mSelectionType != SelectionType::eFind &&
4847           curDetail->mSelectionType != SelectionType::eURLSecondary &&
4848           curDetail->mSelectionType != SelectionType::eURLStrikeout &&
4849           curDetail->mStart <= offsets.StartOffset() &&
4850           offsets.EndOffset() <= curDetail->mEnd) {
4851         inSelection = true;
4852       }
4853     }
4854 
4855     if (inSelection) {
4856       fc->SetDragState(false);
4857       fc->SetDelayedCaretData(mouseEvent);
4858       return NS_OK;
4859     }
4860   }
4861 
4862   fc->SetDragState(true);
4863 
4864   // Do not touch any nsFrame members after this point without adding
4865   // weakFrame checks.
4866   const nsFrameSelection::FocusMode focusMode = [&]() {
4867     // If "Shift" and "Ctrl" are both pressed, "Shift" is given precedence. This
4868     // mimics the old behaviour.
4869     if (mouseEvent->IsShift()) {
4870       return nsFrameSelection::FocusMode::kExtendSelection;
4871     }
4872 
4873     if (control) {
4874       return nsFrameSelection::FocusMode::kMultiRangeSelection;
4875     }
4876 
4877     return nsFrameSelection::FocusMode::kCollapseToNewPoint;
4878   }();
4879 
4880   rv = fc->HandleClick(offsets.content, offsets.StartOffset(),
4881                        offsets.EndOffset(), focusMode, offsets.associate);
4882 
4883   if (NS_FAILED(rv)) return rv;
4884 
4885   if (offsets.offset != offsets.secondaryOffset) fc->MaintainSelection();
4886 
4887   if (isEditor && !mouseEvent->IsShift() &&
4888       (offsets.EndOffset() - offsets.StartOffset()) == 1) {
4889     // A single node is selected and we aren't extending an existing
4890     // selection, which means the user clicked directly on an object (either
4891     // -moz-user-select: all or a non-text node without children).
4892     // Therefore, disable selection extension during mouse moves.
4893     // XXX This is a bit hacky; shouldn't editor be able to deal with this?
4894     fc->SetDragState(false);
4895   }
4896 
4897   return rv;
4898 }
4899 
4900 /*
4901  * SelectByTypeAtPoint
4902  *
4903  * Search for selectable content at point and attempt to select
4904  * based on the start and end selection behaviours.
4905  *
4906  * @param aPresContext Presentation context
4907  * @param aPoint Point at which selection will occur. Coordinates
4908  * should be relaitve to this frame.
4909  * @param aBeginAmountType, aEndAmountType Selection behavior, see
4910  * nsIFrame for definitions.
4911  * @param aSelectFlags Selection flags defined in nsFame.h.
4912  * @return success or failure at finding suitable content to select.
4913  */
SelectByTypeAtPoint(nsPresContext * aPresContext,const nsPoint & aPoint,nsSelectionAmount aBeginAmountType,nsSelectionAmount aEndAmountType,uint32_t aSelectFlags)4914 nsresult nsIFrame::SelectByTypeAtPoint(nsPresContext* aPresContext,
4915                                        const nsPoint& aPoint,
4916                                        nsSelectionAmount aBeginAmountType,
4917                                        nsSelectionAmount aEndAmountType,
4918                                        uint32_t aSelectFlags) {
4919   NS_ENSURE_ARG_POINTER(aPresContext);
4920 
4921   // No point in selecting if selection is turned off
4922   if (DetermineDisplaySelection() == nsISelectionController::SELECTION_OFF) {
4923     return NS_OK;
4924   }
4925 
4926   ContentOffsets offsets = GetContentOffsetsFromPoint(aPoint, SKIP_HIDDEN);
4927   if (!offsets.content) return NS_ERROR_FAILURE;
4928 
4929   int32_t offset;
4930   nsIFrame* theFrame = nsFrameSelection::GetFrameForNodeOffset(
4931       offsets.content, offsets.offset, offsets.associate, &offset);
4932   if (!theFrame) return NS_ERROR_FAILURE;
4933 
4934   nsFrame* frame = static_cast<nsFrame*>(theFrame);
4935   return frame->PeekBackwardAndForward(aBeginAmountType, aEndAmountType, offset,
4936                                        aBeginAmountType != eSelectWord,
4937                                        aSelectFlags);
4938 }
4939 
4940 /**
4941  * Multiple Mouse Press -- line or paragraph selection -- for the frame.
4942  * Wouldn't it be nice if this didn't have to be hardwired into Frame code?
4943  */
4944 NS_IMETHODIMP
HandleMultiplePress(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus,bool aControlHeld)4945 nsIFrame::HandleMultiplePress(nsPresContext* aPresContext,
4946                               WidgetGUIEvent* aEvent,
4947                               nsEventStatus* aEventStatus, bool aControlHeld) {
4948   NS_ENSURE_ARG_POINTER(aEvent);
4949   NS_ENSURE_ARG_POINTER(aEventStatus);
4950 
4951   if (nsEventStatus_eConsumeNoDefault == *aEventStatus ||
4952       DetermineDisplaySelection() == nsISelectionController::SELECTION_OFF) {
4953     return NS_OK;
4954   }
4955 
4956   // Find out whether we're doing line or paragraph selection.
4957   // If browser.triple_click_selects_paragraph is true, triple-click selects
4958   // paragraph. Otherwise, triple-click selects line, and quadruple-click
4959   // selects paragraph (on platforms that support quadruple-click).
4960   nsSelectionAmount beginAmount, endAmount;
4961   WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
4962   if (!mouseEvent) {
4963     return NS_OK;
4964   }
4965 
4966   if (mouseEvent->mClickCount == 4) {
4967     beginAmount = endAmount = eSelectParagraph;
4968   } else if (mouseEvent->mClickCount == 3) {
4969     if (Preferences::GetBool("browser.triple_click_selects_paragraph")) {
4970       beginAmount = endAmount = eSelectParagraph;
4971     } else {
4972       beginAmount = eSelectBeginLine;
4973       endAmount = eSelectEndLine;
4974     }
4975   } else if (mouseEvent->mClickCount == 2) {
4976     // We only want inline frames; PeekBackwardAndForward dislikes blocks
4977     beginAmount = endAmount = eSelectWord;
4978   } else {
4979     return NS_OK;
4980   }
4981 
4982   nsPoint relPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(
4983       mouseEvent, RelativeTo{this});
4984   return SelectByTypeAtPoint(aPresContext, relPoint, beginAmount, endAmount,
4985                              (aControlHeld ? SELECT_ACCUMULATE : 0));
4986 }
4987 
PeekBackwardAndForward(nsSelectionAmount aAmountBack,nsSelectionAmount aAmountForward,int32_t aStartPos,bool aJumpLines,uint32_t aSelectFlags)4988 nsresult nsFrame::PeekBackwardAndForward(nsSelectionAmount aAmountBack,
4989                                          nsSelectionAmount aAmountForward,
4990                                          int32_t aStartPos, bool aJumpLines,
4991                                          uint32_t aSelectFlags) {
4992   nsIFrame* baseFrame = this;
4993   int32_t baseOffset = aStartPos;
4994   nsresult rv;
4995 
4996   if (aAmountBack == eSelectWord) {
4997     // To avoid selecting the previous word when at start of word,
4998     // first move one character forward.
4999     nsPeekOffsetStruct pos(eSelectCharacter, eDirNext, aStartPos, nsPoint(0, 0),
5000                            aJumpLines,
5001                            true,  // limit on scrolled views
5002                            false, false, false);
5003     rv = PeekOffset(&pos);
5004     if (NS_SUCCEEDED(rv)) {
5005       baseFrame = pos.mResultFrame;
5006       baseOffset = pos.mContentOffset;
5007     }
5008   }
5009 
5010   // Use peek offset one way then the other:
5011   nsPeekOffsetStruct startpos(aAmountBack, eDirPrevious, baseOffset,
5012                               nsPoint(0, 0), aJumpLines,
5013                               true,  // limit on scrolled views
5014                               false, false, false);
5015   rv = baseFrame->PeekOffset(&startpos);
5016   if (NS_FAILED(rv)) return rv;
5017 
5018   nsPeekOffsetStruct endpos(aAmountForward, eDirNext, aStartPos, nsPoint(0, 0),
5019                             aJumpLines,
5020                             true,  // limit on scrolled views
5021                             false, false, false);
5022   rv = PeekOffset(&endpos);
5023   if (NS_FAILED(rv)) return rv;
5024 
5025   // Keep frameSelection alive.
5026   RefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
5027 
5028   const nsFrameSelection::FocusMode focusMode =
5029       (aSelectFlags & SELECT_ACCUMULATE)
5030           ? nsFrameSelection::FocusMode::kMultiRangeSelection
5031           : nsFrameSelection::FocusMode::kCollapseToNewPoint;
5032   rv = frameSelection->HandleClick(
5033       startpos.mResultContent, startpos.mContentOffset, startpos.mContentOffset,
5034       focusMode, CARET_ASSOCIATE_AFTER);
5035   if (NS_FAILED(rv)) return rv;
5036 
5037   rv = frameSelection->HandleClick(
5038       endpos.mResultContent, endpos.mContentOffset, endpos.mContentOffset,
5039       nsFrameSelection::FocusMode::kExtendSelection, CARET_ASSOCIATE_BEFORE);
5040   if (NS_FAILED(rv)) return rv;
5041 
5042   // maintain selection
5043   return frameSelection->MaintainSelection(aAmountBack);
5044 }
5045 
HandleDrag(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus)5046 NS_IMETHODIMP nsIFrame::HandleDrag(nsPresContext* aPresContext,
5047                                    WidgetGUIEvent* aEvent,
5048                                    nsEventStatus* aEventStatus) {
5049   MOZ_ASSERT(aEvent->mClass == eMouseEventClass,
5050              "HandleDrag can only handle mouse event");
5051 
5052   RefPtr<nsFrameSelection> frameselection = GetFrameSelection();
5053   if (!frameselection) {
5054     return NS_OK;
5055   }
5056 
5057   bool mouseDown = frameselection->GetDragState();
5058   if (!mouseDown) {
5059     return NS_OK;
5060   }
5061 
5062   nsIFrame* scrollbar =
5063       nsLayoutUtils::GetClosestFrameOfType(this, LayoutFrameType::Scrollbar);
5064   if (!scrollbar) {
5065     // XXX Do we really need to exclude non-selectable content here?
5066     // GetContentOffsetsFromPoint can handle it just fine, although some
5067     // other stuff might not like it.
5068     // NOTE: DetermineDisplaySelection() returns SELECTION_OFF for
5069     // non-selectable frames.
5070     if (DetermineDisplaySelection() == nsISelectionController::SELECTION_OFF) {
5071       return NS_OK;
5072     }
5073   }
5074 
5075   frameselection->StopAutoScrollTimer();
5076 
5077   // Check if we are dragging in a table cell
5078   nsCOMPtr<nsIContent> parentContent;
5079   int32_t contentOffset;
5080   TableSelectionMode target;
5081   WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
5082   mozilla::PresShell* presShell = aPresContext->PresShell();
5083   nsresult result;
5084   result = GetDataForTableSelection(frameselection, presShell, mouseEvent,
5085                                     getter_AddRefs(parentContent),
5086                                     &contentOffset, &target);
5087 
5088   AutoWeakFrame weakThis = this;
5089   if (NS_SUCCEEDED(result) && parentContent) {
5090     result = frameselection->HandleTableSelection(parentContent, contentOffset,
5091                                                   target, mouseEvent);
5092     if (NS_WARN_IF(NS_FAILED(result))) {
5093       return result;
5094     }
5095   } else {
5096     nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent,
5097                                                               RelativeTo{this});
5098     frameselection->HandleDrag(this, pt);
5099   }
5100 
5101   // The frameselection object notifies selection listeners synchronously above
5102   // which might have killed us.
5103   if (!weakThis.IsAlive()) {
5104     return NS_OK;
5105   }
5106 
5107   // get the nearest scrollframe
5108   nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetNearestScrollableFrame(
5109       this, nsLayoutUtils::SCROLLABLE_SAME_DOC |
5110                 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
5111 
5112   if (scrollFrame) {
5113     nsIFrame* capturingFrame = scrollFrame->GetScrolledFrame();
5114     if (capturingFrame) {
5115       nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(
5116           mouseEvent, RelativeTo{capturingFrame});
5117       frameselection->StartAutoScrollTimer(capturingFrame, pt, 30);
5118     }
5119   }
5120 
5121   return NS_OK;
5122 }
5123 
5124 /**
5125  * This static method handles part of the nsFrame::HandleRelease in a way
5126  * which doesn't rely on the nsFrame object to stay alive.
5127  */
HandleFrameSelection(nsFrameSelection * aFrameSelection,nsIFrame::ContentOffsets & aOffsets,bool aHandleTableSel,int32_t aContentOffsetForTableSel,TableSelectionMode aTargetForTableSel,nsIContent * aParentContentForTableSel,WidgetGUIEvent * aEvent,const nsEventStatus * aEventStatus)5128 static nsresult HandleFrameSelection(nsFrameSelection* aFrameSelection,
5129                                      nsIFrame::ContentOffsets& aOffsets,
5130                                      bool aHandleTableSel,
5131                                      int32_t aContentOffsetForTableSel,
5132                                      TableSelectionMode aTargetForTableSel,
5133                                      nsIContent* aParentContentForTableSel,
5134                                      WidgetGUIEvent* aEvent,
5135                                      const nsEventStatus* aEventStatus) {
5136   if (!aFrameSelection) {
5137     return NS_OK;
5138   }
5139 
5140   nsresult rv = NS_OK;
5141 
5142   if (nsEventStatus_eConsumeNoDefault != *aEventStatus) {
5143     if (!aHandleTableSel) {
5144       if (!aOffsets.content || !aFrameSelection->HasDelayedCaretData()) {
5145         return NS_ERROR_FAILURE;
5146       }
5147 
5148       // We are doing this to simulate what we would have done on HandlePress.
5149       // We didn't do it there to give the user an opportunity to drag
5150       // the text, but since they didn't drag, we want to place the
5151       // caret.
5152       // However, we'll use the mouse position from the release, since:
5153       //  * it's easier
5154       //  * that's the normal click position to use (although really, in
5155       //    the normal case, small movements that don't count as a drag
5156       //    can do selection)
5157       aFrameSelection->SetDragState(true);
5158 
5159       const nsFrameSelection::FocusMode focusMode =
5160           aFrameSelection->IsShiftDownInDelayedCaretData()
5161               ? nsFrameSelection::FocusMode::kExtendSelection
5162               : nsFrameSelection::FocusMode::kCollapseToNewPoint;
5163       rv = aFrameSelection->HandleClick(
5164           aOffsets.content, aOffsets.StartOffset(), aOffsets.EndOffset(),
5165           focusMode, aOffsets.associate);
5166       if (NS_FAILED(rv)) {
5167         return rv;
5168       }
5169     } else if (aParentContentForTableSel) {
5170       aFrameSelection->SetDragState(false);
5171       rv = aFrameSelection->HandleTableSelection(
5172           aParentContentForTableSel, aContentOffsetForTableSel,
5173           aTargetForTableSel, aEvent->AsMouseEvent());
5174       if (NS_FAILED(rv)) {
5175         return rv;
5176       }
5177     }
5178     aFrameSelection->SetDelayedCaretData(0);
5179   }
5180 
5181   aFrameSelection->SetDragState(false);
5182   aFrameSelection->StopAutoScrollTimer();
5183 
5184   return NS_OK;
5185 }
5186 
HandleRelease(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus)5187 NS_IMETHODIMP nsIFrame::HandleRelease(nsPresContext* aPresContext,
5188                                       WidgetGUIEvent* aEvent,
5189                                       nsEventStatus* aEventStatus) {
5190   if (aEvent->mClass != eMouseEventClass) {
5191     return NS_OK;
5192   }
5193 
5194   nsIFrame* activeFrame = GetActiveSelectionFrame(aPresContext, this);
5195 
5196   nsCOMPtr<nsIContent> captureContent = PresShell::GetCapturingContent();
5197 
5198   // We can unconditionally stop capturing because
5199   // we should never be capturing when the mouse button is up
5200   PresShell::ReleaseCapturingContent();
5201 
5202   bool selectionOff =
5203       (DetermineDisplaySelection() == nsISelectionController::SELECTION_OFF);
5204 
5205   RefPtr<nsFrameSelection> frameselection;
5206   ContentOffsets offsets;
5207   nsCOMPtr<nsIContent> parentContent;
5208   int32_t contentOffsetForTableSel = 0;
5209   TableSelectionMode targetForTableSel = TableSelectionMode::None;
5210   bool handleTableSelection = true;
5211 
5212   if (!selectionOff) {
5213     frameselection = GetFrameSelection();
5214     if (nsEventStatus_eConsumeNoDefault != *aEventStatus && frameselection) {
5215       // Check if the frameselection recorded the mouse going down.
5216       // If not, the user must have clicked in a part of the selection.
5217       // Place the caret before continuing!
5218 
5219       if (frameselection->MouseDownRecorded()) {
5220         nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(
5221             aEvent, RelativeTo{this});
5222         offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN);
5223         handleTableSelection = false;
5224       } else {
5225         GetDataForTableSelection(frameselection, PresShell(),
5226                                  aEvent->AsMouseEvent(),
5227                                  getter_AddRefs(parentContent),
5228                                  &contentOffsetForTableSel, &targetForTableSel);
5229       }
5230     }
5231   }
5232 
5233   // We might be capturing in some other document and the event just happened to
5234   // trickle down here. Make sure that document's frame selection is notified.
5235   // Note, this may cause the current nsFrame object to be deleted, bug 336592.
5236   RefPtr<nsFrameSelection> frameSelection;
5237   if (activeFrame != this &&
5238       static_cast<nsFrame*>(activeFrame)->DetermineDisplaySelection() !=
5239           nsISelectionController::SELECTION_OFF) {
5240     frameSelection = activeFrame->GetFrameSelection();
5241   }
5242 
5243   // Also check the selection of the capturing content which might be in a
5244   // different document.
5245   if (!frameSelection && captureContent) {
5246     Document* doc = captureContent->GetUncomposedDoc();
5247     if (doc) {
5248       mozilla::PresShell* capturingPresShell = doc->GetPresShell();
5249       if (capturingPresShell &&
5250           capturingPresShell != PresContext()->GetPresShell()) {
5251         frameSelection = capturingPresShell->FrameSelection();
5252       }
5253     }
5254   }
5255 
5256   if (frameSelection) {
5257     frameSelection->SetDragState(false);
5258     frameSelection->StopAutoScrollTimer();
5259     nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetNearestScrollableFrame(
5260         this, nsLayoutUtils::SCROLLABLE_SAME_DOC |
5261                   nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
5262     if (scrollFrame) {
5263       // Perform any additional scrolling needed to maintain CSS snap point
5264       // requirements when autoscrolling is over.
5265       scrollFrame->ScrollSnap();
5266     }
5267   }
5268 
5269   // Do not call any methods of the current object after this point!!!
5270   // The object is perhaps dead!
5271 
5272   return selectionOff ? NS_OK
5273                       : HandleFrameSelection(
5274                             frameselection, offsets, handleTableSelection,
5275                             contentOffsetForTableSel, targetForTableSel,
5276                             parentContent, aEvent, aEventStatus);
5277 }
5278 
5279 struct MOZ_STACK_CLASS FrameContentRange {
FrameContentRangeFrameContentRange5280   FrameContentRange(nsIContent* aContent, int32_t aStart, int32_t aEnd)
5281       : content(aContent), start(aStart), end(aEnd) {}
5282   nsCOMPtr<nsIContent> content;
5283   int32_t start;
5284   int32_t end;
5285 };
5286 
5287 // Retrieve the content offsets of a frame
GetRangeForFrame(nsIFrame * aFrame)5288 static FrameContentRange GetRangeForFrame(nsIFrame* aFrame) {
5289   nsIContent* content = aFrame->GetContent();
5290   if (!content) {
5291     NS_WARNING("Frame has no content");
5292     return FrameContentRange(nullptr, -1, -1);
5293   }
5294 
5295   LayoutFrameType type = aFrame->Type();
5296   if (type == LayoutFrameType::Text) {
5297     int32_t offset, offsetEnd;
5298     aFrame->GetOffsets(offset, offsetEnd);
5299     return FrameContentRange(content, offset, offsetEnd);
5300   }
5301 
5302   if (type == LayoutFrameType::Br) {
5303     nsIContent* parent = content->GetParent();
5304     int32_t beginOffset = parent->ComputeIndexOf(content);
5305     return FrameContentRange(parent, beginOffset, beginOffset);
5306   }
5307 
5308   while (content->IsRootOfNativeAnonymousSubtree()) {
5309     content = content->GetParent();
5310   }
5311 
5312   nsIContent* parent = content->GetParent();
5313   if (aFrame->IsBlockFrameOrSubclass() || !parent) {
5314     return FrameContentRange(content, 0, content->GetChildCount());
5315   }
5316 
5317   // TODO(emilio): Revise this in presence of Shadow DOM / display: contents,
5318   // it's likely that we don't want to just walk the light tree, and we need to
5319   // change the representation of FrameContentRange.
5320   int32_t index = parent->ComputeIndexOf(content);
5321   MOZ_ASSERT(index >= 0);
5322   return FrameContentRange(parent, index, index + 1);
5323 }
5324 
5325 // The FrameTarget represents the closest frame to a point that can be selected
5326 // The frame is the frame represented, frameEdge says whether one end of the
5327 // frame is the result (in which case different handling is needed), and
5328 // afterFrame says which end is repersented if frameEdge is true
5329 struct FrameTarget {
FrameTargetFrameTarget5330   FrameTarget(nsIFrame* aFrame, bool aFrameEdge, bool aAfterFrame)
5331       : frame(aFrame), frameEdge(aFrameEdge), afterFrame(aAfterFrame) {}
5332 
NullFrameTarget5333   static FrameTarget Null() { return FrameTarget(nullptr, false, false); }
5334 
IsNullFrameTarget5335   bool IsNull() { return !frame; }
5336   nsIFrame* frame;
5337   bool frameEdge;
5338   bool afterFrame;
5339 };
5340 
5341 // See function implementation for information
5342 static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame,
5343                                             const nsPoint& aPoint,
5344                                             uint32_t aFlags);
5345 
SelfIsSelectable(nsIFrame * aFrame,uint32_t aFlags)5346 static bool SelfIsSelectable(nsIFrame* aFrame, uint32_t aFlags) {
5347   if ((aFlags & nsIFrame::SKIP_HIDDEN) &&
5348       !aFrame->StyleVisibility()->IsVisible()) {
5349     return false;
5350   }
5351   return !aFrame->IsGeneratedContentFrame() &&
5352          aFrame->StyleUIReset()->mUserSelect != StyleUserSelect::None;
5353 }
5354 
SelectionDescendToKids(nsIFrame * aFrame)5355 static bool SelectionDescendToKids(nsIFrame* aFrame) {
5356   StyleUserSelect style = aFrame->StyleUIReset()->mUserSelect;
5357   nsIFrame* parent = aFrame->GetParent();
5358   // If we are only near (not directly over) then don't traverse
5359   // frames with independent selection (e.g. text and list controls)
5360   // unless we're already inside such a frame (see bug 268497).  Note that this
5361   // prevents any of the users of this method from entering form controls.
5362   // XXX We might want some way to allow using the up-arrow to go into a form
5363   // control, but the focus didn't work right anyway; it'd probably be enough
5364   // if the left and right arrows could enter textboxes (which I don't believe
5365   // they can at the moment)
5366   return !aFrame->IsGeneratedContentFrame() && style != StyleUserSelect::All &&
5367          style != StyleUserSelect::None &&
5368          ((parent->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION) ||
5369           !(aFrame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION));
5370 }
5371 
GetSelectionClosestFrameForChild(nsIFrame * aChild,const nsPoint & aPoint,uint32_t aFlags)5372 static FrameTarget GetSelectionClosestFrameForChild(nsIFrame* aChild,
5373                                                     const nsPoint& aPoint,
5374                                                     uint32_t aFlags) {
5375   nsIFrame* parent = aChild->GetParent();
5376   if (SelectionDescendToKids(aChild)) {
5377     nsPoint pt = aPoint - aChild->GetOffsetTo(parent);
5378     return GetSelectionClosestFrame(aChild, pt, aFlags);
5379   }
5380   return FrameTarget(aChild, false, false);
5381 }
5382 
5383 // When the cursor needs to be at the beginning of a block, it shouldn't be
5384 // before the first child.  A click on a block whose first child is a block
5385 // should put the cursor in the child.  The cursor shouldn't be between the
5386 // blocks, because that's not where it's expected.
5387 // Note that this method is guaranteed to succeed.
DrillDownToSelectionFrame(nsIFrame * aFrame,bool aEndFrame,uint32_t aFlags)5388 static FrameTarget DrillDownToSelectionFrame(nsIFrame* aFrame, bool aEndFrame,
5389                                              uint32_t aFlags) {
5390   if (SelectionDescendToKids(aFrame)) {
5391     nsIFrame* result = nullptr;
5392     nsIFrame* frame = aFrame->PrincipalChildList().FirstChild();
5393     if (!aEndFrame) {
5394       while (frame && (!SelfIsSelectable(frame, aFlags) || frame->IsEmpty()))
5395         frame = frame->GetNextSibling();
5396       if (frame) result = frame;
5397     } else {
5398       // Because the frame tree is singly linked, to find the last frame,
5399       // we have to iterate through all the frames
5400       // XXX I have a feeling this could be slow for long blocks, although
5401       //     I can't find any slowdowns
5402       while (frame) {
5403         if (!frame->IsEmpty() && SelfIsSelectable(frame, aFlags))
5404           result = frame;
5405         frame = frame->GetNextSibling();
5406       }
5407     }
5408     if (result) return DrillDownToSelectionFrame(result, aEndFrame, aFlags);
5409   }
5410   // If the current frame has no targetable children, target the current frame
5411   return FrameTarget(aFrame, true, aEndFrame);
5412 }
5413 
5414 // This method finds the closest valid FrameTarget on a given line; if there is
5415 // no valid FrameTarget on the line, it returns a null FrameTarget
GetSelectionClosestFrameForLine(nsBlockFrame * aParent,nsBlockFrame::LineIterator aLine,const nsPoint & aPoint,uint32_t aFlags)5416 static FrameTarget GetSelectionClosestFrameForLine(
5417     nsBlockFrame* aParent, nsBlockFrame::LineIterator aLine,
5418     const nsPoint& aPoint, uint32_t aFlags) {
5419   // Account for end of lines (any iterator from the block is valid)
5420   if (aLine == aParent->LinesEnd())
5421     return DrillDownToSelectionFrame(aParent, true, aFlags);
5422   nsIFrame* frame = aLine->mFirstChild;
5423   nsIFrame* closestFromIStart = nullptr;
5424   nsIFrame* closestFromIEnd = nullptr;
5425   nscoord closestIStart = aLine->IStart(), closestIEnd = aLine->IEnd();
5426   WritingMode wm = aLine->mWritingMode;
5427   LogicalPoint pt(wm, aPoint, aLine->mContainerSize);
5428   bool canSkipBr = false;
5429   bool lastFrameWasEditable = false;
5430   for (int32_t n = aLine->GetChildCount(); n;
5431        --n, frame = frame->GetNextSibling()) {
5432     // Skip brFrames. Can only skip if the line contains at least
5433     // one selectable and non-empty frame before. Also, avoid skipping brs if
5434     // the previous thing had a different editableness than us, since then we
5435     // may end up not being able to select after it if the br is the last thing
5436     // on the line.
5437     if (!SelfIsSelectable(frame, aFlags) || frame->IsEmpty() ||
5438         (canSkipBr && frame->IsBrFrame() &&
5439          lastFrameWasEditable == frame->GetContent()->IsEditable())) {
5440       continue;
5441     }
5442     canSkipBr = true;
5443     lastFrameWasEditable =
5444         frame->GetContent() && frame->GetContent()->IsEditable();
5445     LogicalRect frameRect =
5446         LogicalRect(wm, frame->GetRect(), aLine->mContainerSize);
5447     if (pt.I(wm) >= frameRect.IStart(wm)) {
5448       if (pt.I(wm) < frameRect.IEnd(wm)) {
5449         return GetSelectionClosestFrameForChild(frame, aPoint, aFlags);
5450       }
5451       if (frameRect.IEnd(wm) >= closestIStart) {
5452         closestFromIStart = frame;
5453         closestIStart = frameRect.IEnd(wm);
5454       }
5455     } else {
5456       if (frameRect.IStart(wm) <= closestIEnd) {
5457         closestFromIEnd = frame;
5458         closestIEnd = frameRect.IStart(wm);
5459       }
5460     }
5461   }
5462   if (!closestFromIStart && !closestFromIEnd) {
5463     // We should only get here if there are no selectable frames on a line
5464     // XXX Do we need more elaborate handling here?
5465     return FrameTarget::Null();
5466   }
5467   if (closestFromIStart &&
5468       (!closestFromIEnd ||
5469        (abs(pt.I(wm) - closestIStart) <= abs(pt.I(wm) - closestIEnd)))) {
5470     return GetSelectionClosestFrameForChild(closestFromIStart, aPoint, aFlags);
5471   }
5472   return GetSelectionClosestFrameForChild(closestFromIEnd, aPoint, aFlags);
5473 }
5474 
5475 // This method is for the special handling we do for block frames; they're
5476 // special because they represent paragraphs and because they are organized
5477 // into lines, which have bounds that are not stored elsewhere in the
5478 // frame tree.  Returns a null FrameTarget for frames which are not
5479 // blocks or blocks with no lines except editable one.
GetSelectionClosestFrameForBlock(nsIFrame * aFrame,const nsPoint & aPoint,uint32_t aFlags)5480 static FrameTarget GetSelectionClosestFrameForBlock(nsIFrame* aFrame,
5481                                                     const nsPoint& aPoint,
5482                                                     uint32_t aFlags) {
5483   nsBlockFrame* bf = do_QueryFrame(aFrame);
5484   if (!bf) return FrameTarget::Null();
5485 
5486   // This code searches for the correct line
5487   nsBlockFrame::LineIterator end = bf->LinesEnd();
5488   nsBlockFrame::LineIterator curLine = bf->LinesBegin();
5489   nsBlockFrame::LineIterator closestLine = end;
5490 
5491   if (curLine != end) {
5492     // Convert aPoint into a LogicalPoint in the writing-mode of this block
5493     WritingMode wm = curLine->mWritingMode;
5494     LogicalPoint pt(wm, aPoint, curLine->mContainerSize);
5495     do {
5496       // Check to see if our point lies within the line's block-direction bounds
5497       nscoord BCoord = pt.B(wm) - curLine->BStart();
5498       nscoord BSize = curLine->BSize();
5499       if (BCoord >= 0 && BCoord < BSize) {
5500         closestLine = curLine;
5501         break;  // We found the line; stop looking
5502       }
5503       if (BCoord < 0) break;
5504       ++curLine;
5505     } while (curLine != end);
5506 
5507     if (closestLine == end) {
5508       nsBlockFrame::LineIterator prevLine = curLine.prev();
5509       nsBlockFrame::LineIterator nextLine = curLine;
5510       // Avoid empty lines
5511       while (nextLine != end && nextLine->IsEmpty()) ++nextLine;
5512       while (prevLine != end && prevLine->IsEmpty()) --prevLine;
5513 
5514       // This hidden pref dictates whether a point above or below all lines
5515       // comes up with a line or the beginning or end of the frame; 0 on
5516       // Windows, 1 on other platforms by default at the writing of this code
5517       int32_t dragOutOfFrame =
5518           Preferences::GetInt("browser.drag_out_of_frame_style");
5519 
5520       if (prevLine == end) {
5521         if (dragOutOfFrame == 1 || nextLine == end)
5522           return DrillDownToSelectionFrame(aFrame, false, aFlags);
5523         closestLine = nextLine;
5524       } else if (nextLine == end) {
5525         if (dragOutOfFrame == 1)
5526           return DrillDownToSelectionFrame(aFrame, true, aFlags);
5527         closestLine = prevLine;
5528       } else {  // Figure out which line is closer
5529         if (pt.B(wm) - prevLine->BEnd() < nextLine->BStart() - pt.B(wm))
5530           closestLine = prevLine;
5531         else
5532           closestLine = nextLine;
5533       }
5534     }
5535   }
5536 
5537   do {
5538     FrameTarget target =
5539         GetSelectionClosestFrameForLine(bf, closestLine, aPoint, aFlags);
5540     if (!target.IsNull()) return target;
5541     ++closestLine;
5542   } while (closestLine != end);
5543 
5544   // Fall back to just targeting the last targetable place
5545   return DrillDownToSelectionFrame(aFrame, true, aFlags);
5546 }
5547 
5548 // GetSelectionClosestFrame is the helper function that calculates the closest
5549 // frame to the given point.
5550 // It doesn't completely account for offset styles, so needs to be used in
5551 // restricted environments.
5552 // Cannot handle overlapping frames correctly, so it should receive the output
5553 // of GetFrameForPoint
5554 // Guaranteed to return a valid FrameTarget
GetSelectionClosestFrame(nsIFrame * aFrame,const nsPoint & aPoint,uint32_t aFlags)5555 static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame,
5556                                             const nsPoint& aPoint,
5557                                             uint32_t aFlags) {
5558   {
5559     // Handle blocks; if the frame isn't a block, the method fails
5560     FrameTarget target =
5561         GetSelectionClosestFrameForBlock(aFrame, aPoint, aFlags);
5562     if (!target.IsNull()) return target;
5563   }
5564 
5565   if (nsIFrame* kid = aFrame->PrincipalChildList().FirstChild()) {
5566     // Go through all the child frames to find the closest one
5567     nsIFrame::FrameWithDistance closest = {nullptr, nscoord_MAX, nscoord_MAX};
5568     for (; kid; kid = kid->GetNextSibling()) {
5569       if (!SelfIsSelectable(kid, aFlags) || kid->IsEmpty()) continue;
5570 
5571       kid->FindCloserFrameForSelection(aPoint, &closest);
5572     }
5573     if (closest.mFrame) {
5574       if (nsSVGUtils::IsInSVGTextSubtree(closest.mFrame))
5575         return FrameTarget(closest.mFrame, false, false);
5576       return GetSelectionClosestFrameForChild(closest.mFrame, aPoint, aFlags);
5577     }
5578   }
5579 
5580   // Use frame edge for grid, flex, table, and non-editable image frames.
5581   const bool useFrameEdge =
5582       aFrame->IsFlexOrGridContainer() || aFrame->IsTableFrame() ||
5583       (static_cast<nsImageFrame*>(do_QueryFrame(aFrame)) &&
5584        !aFrame->GetContent()->IsEditable());
5585   return FrameTarget(aFrame, useFrameEdge, false);
5586 }
5587 
OffsetsForSingleFrame(nsIFrame * aFrame,const nsPoint & aPoint)5588 static nsIFrame::ContentOffsets OffsetsForSingleFrame(nsIFrame* aFrame,
5589                                                       const nsPoint& aPoint) {
5590   nsIFrame::ContentOffsets offsets;
5591   FrameContentRange range = GetRangeForFrame(aFrame);
5592   offsets.content = range.content;
5593   // If there are continuations (meaning it's not one rectangle), this is the
5594   // best this function can do
5595   if (aFrame->GetNextContinuation() || aFrame->GetPrevContinuation()) {
5596     offsets.offset = range.start;
5597     offsets.secondaryOffset = range.end;
5598     offsets.associate = CARET_ASSOCIATE_AFTER;
5599     return offsets;
5600   }
5601 
5602   // Figure out whether the offsets should be over, after, or before the frame
5603   nsRect rect(nsPoint(0, 0), aFrame->GetSize());
5604 
5605   bool isBlock = !aFrame->StyleDisplay()->IsInlineFlow();
5606   bool isRtl = (aFrame->StyleVisibility()->mDirection == StyleDirection::Rtl);
5607   if ((isBlock && rect.y < aPoint.y) ||
5608       (!isBlock && ((isRtl && rect.x + rect.width / 2 > aPoint.x) ||
5609                     (!isRtl && rect.x + rect.width / 2 < aPoint.x)))) {
5610     offsets.offset = range.end;
5611     if (rect.Contains(aPoint))
5612       offsets.secondaryOffset = range.start;
5613     else
5614       offsets.secondaryOffset = range.end;
5615   } else {
5616     offsets.offset = range.start;
5617     if (rect.Contains(aPoint))
5618       offsets.secondaryOffset = range.end;
5619     else
5620       offsets.secondaryOffset = range.start;
5621   }
5622   offsets.associate = offsets.offset == range.start ? CARET_ASSOCIATE_AFTER
5623                                                     : CARET_ASSOCIATE_BEFORE;
5624   return offsets;
5625 }
5626 
AdjustFrameForSelectionStyles(nsIFrame * aFrame)5627 static nsIFrame* AdjustFrameForSelectionStyles(nsIFrame* aFrame) {
5628   nsIFrame* adjustedFrame = aFrame;
5629   for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
5630     // These are the conditions that make all children not able to handle
5631     // a cursor.
5632     StyleUserSelect userSelect = frame->StyleUIReset()->mUserSelect;
5633     if (userSelect != StyleUserSelect::Auto &&
5634         userSelect != StyleUserSelect::All) {
5635       break;
5636     }
5637     if (userSelect == StyleUserSelect::All ||
5638         frame->IsGeneratedContentFrame()) {
5639       adjustedFrame = frame;
5640     }
5641   }
5642   return adjustedFrame;
5643 }
5644 
GetContentOffsetsFromPoint(const nsPoint & aPoint,uint32_t aFlags)5645 nsIFrame::ContentOffsets nsIFrame::GetContentOffsetsFromPoint(
5646     const nsPoint& aPoint, uint32_t aFlags) {
5647   nsIFrame* adjustedFrame;
5648   if (aFlags & IGNORE_SELECTION_STYLE) {
5649     adjustedFrame = this;
5650   } else {
5651     // This section of code deals with special selection styles.  Note that
5652     // -moz-all exists, even though it doesn't need to be explicitly handled.
5653     //
5654     // The offset is forced not to end up in generated content; content offsets
5655     // cannot represent content outside of the document's content tree.
5656 
5657     adjustedFrame = AdjustFrameForSelectionStyles(this);
5658 
5659     // -moz-user-select: all needs special handling, because clicking on it
5660     // should lead to the whole frame being selected
5661     if (adjustedFrame->StyleUIReset()->mUserSelect == StyleUserSelect::All) {
5662       nsPoint adjustedPoint = aPoint + this->GetOffsetTo(adjustedFrame);
5663       return OffsetsForSingleFrame(adjustedFrame, adjustedPoint);
5664     }
5665 
5666     // For other cases, try to find a closest frame starting from the parent of
5667     // the unselectable frame
5668     if (adjustedFrame != this) adjustedFrame = adjustedFrame->GetParent();
5669   }
5670 
5671   nsPoint adjustedPoint = aPoint + this->GetOffsetTo(adjustedFrame);
5672 
5673   FrameTarget closest =
5674       GetSelectionClosestFrame(adjustedFrame, adjustedPoint, aFlags);
5675 
5676   // If the correct offset is at one end of a frame, use offset-based
5677   // calculation method
5678   if (closest.frameEdge) {
5679     ContentOffsets offsets;
5680     FrameContentRange range = GetRangeForFrame(closest.frame);
5681     offsets.content = range.content;
5682     if (closest.afterFrame)
5683       offsets.offset = range.end;
5684     else
5685       offsets.offset = range.start;
5686     offsets.secondaryOffset = offsets.offset;
5687     offsets.associate = offsets.offset == range.start ? CARET_ASSOCIATE_AFTER
5688                                                       : CARET_ASSOCIATE_BEFORE;
5689     return offsets;
5690   }
5691 
5692   nsPoint pt;
5693   if (closest.frame != this) {
5694     if (nsSVGUtils::IsInSVGTextSubtree(closest.frame)) {
5695       pt = nsLayoutUtils::TransformAncestorPointToFrame(
5696           RelativeTo{closest.frame}, aPoint, RelativeTo{this});
5697     } else {
5698       pt = aPoint - closest.frame->GetOffsetTo(this);
5699     }
5700   } else {
5701     pt = aPoint;
5702   }
5703   return static_cast<nsFrame*>(closest.frame)
5704       ->CalcContentOffsetsFromFramePoint(pt);
5705 
5706   // XXX should I add some kind of offset standardization?
5707   // consider <b>xxxxx</b><i>zzzzz</i>; should any click between the last
5708   // x and first z put the cursor in the same logical position in addition
5709   // to the same visual position?
5710 }
5711 
CalcContentOffsetsFromFramePoint(const nsPoint & aPoint)5712 nsIFrame::ContentOffsets nsFrame::CalcContentOffsetsFromFramePoint(
5713     const nsPoint& aPoint) {
5714   return OffsetsForSingleFrame(this, aPoint);
5715 }
5716 
AssociateImage(const StyleImage & aImage)5717 bool nsIFrame::AssociateImage(const StyleImage& aImage) {
5718   imgRequestProxy* req = aImage.GetImageRequest();
5719   if (!req) {
5720     return false;
5721   }
5722 
5723   mozilla::css::ImageLoader* loader =
5724       PresContext()->Document()->StyleImageLoader();
5725 
5726   loader->AssociateRequestToFrame(req, this, 0);
5727   return true;
5728 }
5729 
DisassociateImage(const StyleImage & aImage)5730 void nsIFrame::DisassociateImage(const StyleImage& aImage) {
5731   imgRequestProxy* req = aImage.GetImageRequest();
5732   if (!req) {
5733     return;
5734   }
5735 
5736   mozilla::css::ImageLoader* loader =
5737       PresContext()->Document()->StyleImageLoader();
5738 
5739   loader->DisassociateRequestFromFrame(req, this);
5740 }
5741 
GetCursor(const nsPoint &)5742 Maybe<nsIFrame::Cursor> nsIFrame::GetCursor(const nsPoint&) {
5743   StyleCursorKind kind = StyleUI()->mCursor.keyword;
5744   if (kind == StyleCursorKind::Auto) {
5745     // If this is editable, I-beam cursor is better for most elements.
5746     kind = (mContent && mContent->IsEditable()) ? StyleCursorKind::Text
5747                                                 : StyleCursorKind::Default;
5748   }
5749   if (kind == StyleCursorKind::Text && GetWritingMode().IsVertical()) {
5750     // Per CSS UI spec, UA may treat value 'text' as
5751     // 'vertical-text' for vertical text.
5752     kind = StyleCursorKind::VerticalText;
5753   }
5754 
5755   return Some(Cursor{kind, AllowCustomCursorImage::Yes});
5756 }
5757 
5758 // Resize and incremental reflow
5759 
5760 /* virtual */
MarkIntrinsicISizesDirty()5761 void nsIFrame::MarkIntrinsicISizesDirty() {
5762   // This version is meant only for what used to be box-to-block adaptors.
5763   // It should not be called by other derived classes.
5764   if (::IsXULBoxWrapped(this)) {
5765     nsBoxLayoutMetrics* metrics = BoxMetrics();
5766 
5767     XULSizeNeedsRecalc(metrics->mPrefSize);
5768     XULSizeNeedsRecalc(metrics->mMinSize);
5769     XULSizeNeedsRecalc(metrics->mMaxSize);
5770     XULSizeNeedsRecalc(metrics->mBlockPrefSize);
5771     XULSizeNeedsRecalc(metrics->mBlockMinSize);
5772     XULCoordNeedsRecalc(metrics->mFlex);
5773     XULCoordNeedsRecalc(metrics->mAscent);
5774   }
5775 
5776   // If we're a flex item, clear our flex-item-specific cached measurements
5777   // (which likely depended on our now-stale intrinsic isize).
5778   auto* parentFrame = GetParent();
5779   if (parentFrame && parentFrame->IsFlexContainerFrame()) {
5780     nsFlexContainerFrame::MarkCachedFlexMeasurementsDirty(this);
5781   }
5782 
5783   if (GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) {
5784     nsFontInflationData::MarkFontInflationDataTextDirty(this);
5785   }
5786 }
5787 
MarkSubtreeDirty()5788 void nsIFrame::MarkSubtreeDirty() {
5789   if (HasAnyStateBits(NS_FRAME_IS_DIRTY)) {
5790     return;
5791   }
5792   // Unconditionally mark given frame dirty.
5793   AddStateBits(NS_FRAME_IS_DIRTY);
5794 
5795   // Mark all descendants dirty, unless:
5796   // - Already dirty.
5797   // - TableColGroup
5798   // - XULBox
5799   AutoTArray<nsIFrame*, 32> stack;
5800   for (const auto& childLists : ChildLists()) {
5801     for (nsIFrame* kid : childLists.mList) {
5802       stack.AppendElement(kid);
5803     }
5804   }
5805   while (!stack.IsEmpty()) {
5806     nsIFrame* f = stack.PopLastElement();
5807     if (f->HasAnyStateBits(NS_FRAME_IS_DIRTY) || f->IsTableColGroupFrame() ||
5808         f->IsXULBoxFrame()) {
5809       continue;
5810     }
5811 
5812     f->AddStateBits(NS_FRAME_IS_DIRTY);
5813 
5814     for (const auto& childLists : f->ChildLists()) {
5815       for (nsIFrame* kid : childLists.mList) {
5816         stack.AppendElement(kid);
5817       }
5818     }
5819   }
5820 }
5821 
5822 /* virtual */
GetMinISize(gfxContext * aRenderingContext)5823 nscoord nsIFrame::GetMinISize(gfxContext* aRenderingContext) {
5824   nscoord result = 0;
5825   DISPLAY_MIN_INLINE_SIZE(this, result);
5826   return result;
5827 }
5828 
5829 /* virtual */
GetPrefISize(gfxContext * aRenderingContext)5830 nscoord nsIFrame::GetPrefISize(gfxContext* aRenderingContext) {
5831   nscoord result = 0;
5832   DISPLAY_PREF_INLINE_SIZE(this, result);
5833   return result;
5834 }
5835 
5836 /* virtual */
AddInlineMinISize(gfxContext * aRenderingContext,nsIFrame::InlineMinISizeData * aData)5837 void nsIFrame::AddInlineMinISize(gfxContext* aRenderingContext,
5838                                  nsIFrame::InlineMinISizeData* aData) {
5839   nscoord isize = nsLayoutUtils::IntrinsicForContainer(
5840       aRenderingContext, this, nsLayoutUtils::MIN_ISIZE);
5841   aData->DefaultAddInlineMinISize(this, isize);
5842 }
5843 
5844 /* virtual */
AddInlinePrefISize(gfxContext * aRenderingContext,nsIFrame::InlinePrefISizeData * aData)5845 void nsIFrame::AddInlinePrefISize(gfxContext* aRenderingContext,
5846                                   nsIFrame::InlinePrefISizeData* aData) {
5847   nscoord isize = nsLayoutUtils::IntrinsicForContainer(
5848       aRenderingContext, this, nsLayoutUtils::PREF_ISIZE);
5849   aData->DefaultAddInlinePrefISize(isize);
5850 }
5851 
DefaultAddInlineMinISize(nsIFrame * aFrame,nscoord aISize,bool aAllowBreak)5852 void nsIFrame::InlineMinISizeData::DefaultAddInlineMinISize(nsIFrame* aFrame,
5853                                                             nscoord aISize,
5854                                                             bool aAllowBreak) {
5855   auto parent = aFrame->GetParent();
5856   MOZ_ASSERT(parent, "Must have a parent if we get here!");
5857   const bool mayBreak = aAllowBreak && !aFrame->CanContinueTextRun() &&
5858                         !parent->Style()->ShouldSuppressLineBreak() &&
5859                         parent->StyleText()->WhiteSpaceCanWrap(parent);
5860   if (mayBreak) {
5861     OptionallyBreak();
5862   }
5863   mTrailingWhitespace = 0;
5864   mSkipWhitespace = false;
5865   mCurrentLine += aISize;
5866   mAtStartOfLine = false;
5867   if (mayBreak) {
5868     OptionallyBreak();
5869   }
5870 }
5871 
DefaultAddInlinePrefISize(nscoord aISize)5872 void nsIFrame::InlinePrefISizeData::DefaultAddInlinePrefISize(nscoord aISize) {
5873   mCurrentLine = NSCoordSaturatingAdd(mCurrentLine, aISize);
5874   mTrailingWhitespace = 0;
5875   mSkipWhitespace = false;
5876   mLineIsEmpty = false;
5877 }
5878 
ForceBreak()5879 void nsIFrame::InlineMinISizeData::ForceBreak() {
5880   mCurrentLine -= mTrailingWhitespace;
5881   mPrevLines = std::max(mPrevLines, mCurrentLine);
5882   mCurrentLine = mTrailingWhitespace = 0;
5883 
5884   for (uint32_t i = 0, i_end = mFloats.Length(); i != i_end; ++i) {
5885     nscoord float_min = mFloats[i].Width();
5886     if (float_min > mPrevLines) mPrevLines = float_min;
5887   }
5888   mFloats.Clear();
5889   mSkipWhitespace = true;
5890 }
5891 
OptionallyBreak(nscoord aHyphenWidth)5892 void nsIFrame::InlineMinISizeData::OptionallyBreak(nscoord aHyphenWidth) {
5893   // If we can fit more content into a smaller width by staying on this
5894   // line (because we're still at a negative offset due to negative
5895   // text-indent or negative margin), don't break.  Otherwise, do the
5896   // same as ForceBreak.  it doesn't really matter when we accumulate
5897   // floats.
5898   if (mCurrentLine + aHyphenWidth < 0 || mAtStartOfLine) return;
5899   mCurrentLine += aHyphenWidth;
5900   ForceBreak();
5901 }
5902 
ForceBreak(StyleClear aBreakType)5903 void nsIFrame::InlinePrefISizeData::ForceBreak(StyleClear aBreakType) {
5904   MOZ_ASSERT(aBreakType == StyleClear::None || aBreakType == StyleClear::Both ||
5905                  aBreakType == StyleClear::Left ||
5906                  aBreakType == StyleClear::Right,
5907              "Must be a physical break type");
5908 
5909   // If this force break is not clearing any float, we can leave all the
5910   // floats to the next force break.
5911   if (mFloats.Length() != 0 && aBreakType != StyleClear::None) {
5912     // preferred widths accumulated for floats that have already
5913     // been cleared past
5914     nscoord floats_done = 0,
5915             // preferred widths accumulated for floats that have not yet
5916             // been cleared past
5917         floats_cur_left = 0, floats_cur_right = 0;
5918 
5919     for (uint32_t i = 0, i_end = mFloats.Length(); i != i_end; ++i) {
5920       const FloatInfo& floatInfo = mFloats[i];
5921       const nsStyleDisplay* floatDisp = floatInfo.Frame()->StyleDisplay();
5922       StyleClear breakType = floatDisp->mBreakType;
5923       if (breakType == StyleClear::Left || breakType == StyleClear::Right ||
5924           breakType == StyleClear::Both) {
5925         nscoord floats_cur =
5926             NSCoordSaturatingAdd(floats_cur_left, floats_cur_right);
5927         if (floats_cur > floats_done) {
5928           floats_done = floats_cur;
5929         }
5930         if (breakType != StyleClear::Right) {
5931           floats_cur_left = 0;
5932         }
5933         if (breakType != StyleClear::Left) {
5934           floats_cur_right = 0;
5935         }
5936       }
5937 
5938       StyleFloat floatStyle = floatDisp->mFloat;
5939       nscoord& floats_cur =
5940           floatStyle == StyleFloat::Left ? floats_cur_left : floats_cur_right;
5941       nscoord floatWidth = floatInfo.Width();
5942       // Negative-width floats don't change the available space so they
5943       // shouldn't change our intrinsic line width either.
5944       floats_cur = NSCoordSaturatingAdd(floats_cur, std::max(0, floatWidth));
5945     }
5946 
5947     nscoord floats_cur =
5948         NSCoordSaturatingAdd(floats_cur_left, floats_cur_right);
5949     if (floats_cur > floats_done) floats_done = floats_cur;
5950 
5951     mCurrentLine = NSCoordSaturatingAdd(mCurrentLine, floats_done);
5952 
5953     if (aBreakType == StyleClear::Both) {
5954       mFloats.Clear();
5955     } else {
5956       // If the break type does not clear all floats, it means there may
5957       // be some floats whose isize should contribute to the intrinsic
5958       // isize of the next line. The code here scans the current mFloats
5959       // and keeps floats which are not cleared by this break. Note that
5960       // floats may be cleared directly or indirectly. See below.
5961       nsTArray<FloatInfo> newFloats;
5962       MOZ_ASSERT(
5963           aBreakType == StyleClear::Left || aBreakType == StyleClear::Right,
5964           "Other values should have been handled in other branches");
5965       StyleFloat clearFloatType =
5966           aBreakType == StyleClear::Left ? StyleFloat::Left : StyleFloat::Right;
5967       // Iterate the array in reverse so that we can stop when there are
5968       // no longer any floats we need to keep. See below.
5969       for (FloatInfo& floatInfo : Reversed(mFloats)) {
5970         const nsStyleDisplay* floatDisp = floatInfo.Frame()->StyleDisplay();
5971         if (floatDisp->mFloat != clearFloatType) {
5972           newFloats.AppendElement(floatInfo);
5973         } else {
5974           // This is a float on the side that this break directly clears
5975           // which means we're not keeping it in mFloats. However, if
5976           // this float clears floats on the opposite side (via a value
5977           // of either 'both' or one of 'left'/'right'), any remaining
5978           // (earlier) floats on that side would be indirectly cleared
5979           // as well. Thus, we should break out of this loop and stop
5980           // considering earlier floats to be kept in mFloats.
5981           StyleClear floatBreakType = floatDisp->mBreakType;
5982           if (floatBreakType != aBreakType &&
5983               floatBreakType != StyleClear::None) {
5984             break;
5985           }
5986         }
5987       }
5988       newFloats.Reverse();
5989       mFloats = std::move(newFloats);
5990     }
5991   }
5992 
5993   mCurrentLine =
5994       NSCoordSaturatingSubtract(mCurrentLine, mTrailingWhitespace, nscoord_MAX);
5995   mPrevLines = std::max(mPrevLines, mCurrentLine);
5996   mCurrentLine = mTrailingWhitespace = 0;
5997   mSkipWhitespace = true;
5998   mLineIsEmpty = true;
5999 }
6000 
ResolveMargin(const LengthPercentageOrAuto & aStyle,nscoord aPercentageBasis)6001 static nscoord ResolveMargin(const LengthPercentageOrAuto& aStyle,
6002                              nscoord aPercentageBasis) {
6003   if (aStyle.IsAuto()) {
6004     return nscoord(0);
6005   }
6006   return nsLayoutUtils::ResolveToLength<false>(aStyle.AsLengthPercentage(),
6007                                                aPercentageBasis);
6008 }
6009 
ResolvePadding(const LengthPercentage & aStyle,nscoord aPercentageBasis)6010 static nscoord ResolvePadding(const LengthPercentage& aStyle,
6011                               nscoord aPercentageBasis) {
6012   return nsLayoutUtils::ResolveToLength<true>(aStyle, aPercentageBasis);
6013 }
6014 
IntrinsicSizeOffsets(nsIFrame * aFrame,nscoord aPercentageBasis,bool aForISize)6015 static nsIFrame::IntrinsicSizeOffsetData IntrinsicSizeOffsets(
6016     nsIFrame* aFrame, nscoord aPercentageBasis, bool aForISize) {
6017   nsIFrame::IntrinsicSizeOffsetData result;
6018   WritingMode wm = aFrame->GetWritingMode();
6019   const auto& margin = aFrame->StyleMargin()->mMargin;
6020   bool verticalAxis = aForISize == wm.IsVertical();
6021   if (verticalAxis) {
6022     result.margin += ResolveMargin(margin.Get(eSideTop), aPercentageBasis);
6023     result.margin += ResolveMargin(margin.Get(eSideBottom), aPercentageBasis);
6024   } else {
6025     result.margin += ResolveMargin(margin.Get(eSideLeft), aPercentageBasis);
6026     result.margin += ResolveMargin(margin.Get(eSideRight), aPercentageBasis);
6027   }
6028 
6029   const auto& padding = aFrame->StylePadding()->mPadding;
6030   if (verticalAxis) {
6031     result.padding += ResolvePadding(padding.Get(eSideTop), aPercentageBasis);
6032     result.padding +=
6033         ResolvePadding(padding.Get(eSideBottom), aPercentageBasis);
6034   } else {
6035     result.padding += ResolvePadding(padding.Get(eSideLeft), aPercentageBasis);
6036     result.padding += ResolvePadding(padding.Get(eSideRight), aPercentageBasis);
6037   }
6038 
6039   const nsStyleBorder* styleBorder = aFrame->StyleBorder();
6040   if (verticalAxis) {
6041     result.border += styleBorder->GetComputedBorderWidth(eSideTop);
6042     result.border += styleBorder->GetComputedBorderWidth(eSideBottom);
6043   } else {
6044     result.border += styleBorder->GetComputedBorderWidth(eSideLeft);
6045     result.border += styleBorder->GetComputedBorderWidth(eSideRight);
6046   }
6047 
6048   const nsStyleDisplay* disp = aFrame->StyleDisplay();
6049   if (aFrame->IsThemed(disp)) {
6050     nsPresContext* presContext = aFrame->PresContext();
6051 
6052     LayoutDeviceIntMargin border = presContext->Theme()->GetWidgetBorder(
6053         presContext->DeviceContext(), aFrame, disp->mAppearance);
6054     result.border = presContext->DevPixelsToAppUnits(
6055         verticalAxis ? border.TopBottom() : border.LeftRight());
6056 
6057     LayoutDeviceIntMargin padding;
6058     if (presContext->Theme()->GetWidgetPadding(presContext->DeviceContext(),
6059                                                aFrame, disp->mAppearance,
6060                                                &padding)) {
6061       result.padding = presContext->DevPixelsToAppUnits(
6062           verticalAxis ? padding.TopBottom() : padding.LeftRight());
6063     }
6064   }
6065   return result;
6066 }
6067 
IntrinsicISizeOffsets(nscoord aPercentageBasis)6068 /* virtual */ nsIFrame::IntrinsicSizeOffsetData nsIFrame::IntrinsicISizeOffsets(
6069     nscoord aPercentageBasis) {
6070   return IntrinsicSizeOffsets(this, aPercentageBasis, true);
6071 }
6072 
IntrinsicBSizeOffsets(nscoord aPercentageBasis)6073 nsIFrame::IntrinsicSizeOffsetData nsIFrame::IntrinsicBSizeOffsets(
6074     nscoord aPercentageBasis) {
6075   return IntrinsicSizeOffsets(this, aPercentageBasis, false);
6076 }
6077 
6078 /* virtual */
GetIntrinsicSize()6079 IntrinsicSize nsIFrame::GetIntrinsicSize() {
6080   return IntrinsicSize();  // default is width/height set to eStyleUnit_None
6081 }
6082 
6083 /* virtual */
GetIntrinsicRatio()6084 AspectRatio nsIFrame::GetIntrinsicRatio() { return AspectRatio(); }
6085 
6086 /* virtual */
ComputeSize(gfxContext * aRenderingContext,WritingMode aWM,const LogicalSize & aCBSize,nscoord aAvailableISize,const LogicalSize & aMargin,const LogicalSize & aBorder,const LogicalSize & aPadding,ComputeSizeFlags aFlags)6087 LogicalSize nsIFrame::ComputeSize(gfxContext* aRenderingContext,
6088                                   WritingMode aWM, const LogicalSize& aCBSize,
6089                                   nscoord aAvailableISize,
6090                                   const LogicalSize& aMargin,
6091                                   const LogicalSize& aBorder,
6092                                   const LogicalSize& aPadding,
6093                                   ComputeSizeFlags aFlags) {
6094   MOZ_ASSERT(!GetIntrinsicRatio(),
6095              "Please override this method and call "
6096              "nsFrame::ComputeSizeWithIntrinsicDimensions instead.");
6097   LogicalSize result =
6098       ComputeAutoSize(aRenderingContext, aWM, aCBSize, aAvailableISize, aMargin,
6099                       aBorder, aPadding, aFlags);
6100   const nsStylePosition* stylePos = StylePosition();
6101 
6102   LogicalSize boxSizingAdjust(aWM);
6103   if (stylePos->mBoxSizing == StyleBoxSizing::Border) {
6104     boxSizingAdjust = aBorder + aPadding;
6105   }
6106   nscoord boxSizingToMarginEdgeISize = aMargin.ISize(aWM) + aBorder.ISize(aWM) +
6107                                        aPadding.ISize(aWM) -
6108                                        boxSizingAdjust.ISize(aWM);
6109 
6110   const auto* inlineStyleCoord = &stylePos->ISize(aWM);
6111   const auto* blockStyleCoord = &stylePos->BSize(aWM);
6112 
6113   auto parentFrame = GetParent();
6114   auto alignCB = parentFrame;
6115   bool isGridItem = parentFrame && parentFrame->IsGridContainerFrame() &&
6116                     !HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
6117   if (parentFrame && parentFrame->IsTableWrapperFrame() && IsTableFrame()) {
6118     // An inner table frame is sized as a grid item if its table wrapper is,
6119     // because they actually have the same CB (the wrapper's CB).
6120     // @see ReflowInput::InitCBReflowInput
6121     auto tableWrapper = GetParent();
6122     auto grandParent = tableWrapper->GetParent();
6123     isGridItem = (grandParent->IsGridContainerFrame() &&
6124                   !(tableWrapper->GetStateBits() & NS_FRAME_OUT_OF_FLOW));
6125     if (isGridItem) {
6126       // When resolving justify/align-self below, we want to use the grid
6127       // container's justify/align-items value and WritingMode.
6128       alignCB = grandParent;
6129     }
6130   }
6131   bool isFlexItem =
6132       parentFrame && parentFrame->IsFlexContainerFrame() &&
6133       !parentFrame->HasAnyStateBits(NS_STATE_FLEX_IS_EMULATING_LEGACY_BOX) &&
6134       !HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
6135   // This variable only gets set (and used) if isFlexItem is true.  It
6136   // indicates which axis (in this frame's own WM) corresponds to its
6137   // flex container's main axis.
6138   LogicalAxis flexMainAxis =
6139       eLogicalAxisInline;  // (init to make valgrind happy)
6140   if (isFlexItem) {
6141     // Flex items use their "flex-basis" property in place of their main-size
6142     // property for sizing purposes, *unless* they have "flex-basis:auto", in
6143     // which case they use their main-size property after all.
6144     flexMainAxis = nsFlexContainerFrame::IsItemInlineAxisMainAxis(this)
6145                        ? eLogicalAxisInline
6146                        : eLogicalAxisBlock;
6147 
6148     // NOTE: The logic here should match the similar chunk for updating
6149     // mainAxisCoord in nsFrame::ComputeSizeWithIntrinsicDimensions() (aside
6150     // from using a different dummy value in the IsUsedFlexBasisContent() case).
6151     const auto* flexBasis = &stylePos->mFlexBasis;
6152     auto& mainAxisCoord =
6153         (flexMainAxis == eLogicalAxisInline ? inlineStyleCoord
6154                                             : blockStyleCoord);
6155 
6156     // NOTE: If we're a table-wrapper frame, we skip this clause and just stick
6157     // with 'main-size:auto' behavior (which -- unlike 'content'
6158     // i.e. 'max-content' -- will give us the ability to honor percent sizes on
6159     // our table-box child when resolving the flex base size). The flexbox spec
6160     // doesn't call for this special case, but webcompat & regression-avoidance
6161     // seems to require it, for the time being... Tables sure are special.
6162     if (nsFlexContainerFrame::IsUsedFlexBasisContent(*flexBasis,
6163                                                      *mainAxisCoord) &&
6164         MOZ_LIKELY(!IsTableWrapperFrame())) {
6165       static const StyleSize maxContStyleCoord(
6166           StyleSize::ExtremumLength(StyleExtremumLength::MaxContent));
6167       mainAxisCoord = &maxContStyleCoord;
6168       // (Note: if our main axis is the block axis, then this 'max-content'
6169       // value will be treated like 'auto', via the IsAutoBSize() call below.)
6170     } else if (flexBasis->IsSize() && !flexBasis->IsAuto()) {
6171       // For all other non-'auto' flex-basis values, we just swap in the
6172       // flex-basis itself for the main-size property.
6173       mainAxisCoord = &flexBasis->AsSize();
6174     }  // else: flex-basis is 'auto', which is deferring to some explicit value
6175        // in mainAxisCoord. So we proceed w/o touching mainAxisCoord.
6176   }
6177 
6178   const bool isOrthogonal = aWM.IsOrthogonalTo(alignCB->GetWritingMode());
6179   // Compute inline-axis size
6180   if (!inlineStyleCoord->IsAuto()) {
6181     result.ISize(aWM) = ComputeISizeValue(
6182         aRenderingContext, aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM),
6183         boxSizingToMarginEdgeISize, *inlineStyleCoord, aFlags);
6184   } else if (MOZ_UNLIKELY(isGridItem) && !IS_TRUE_OVERFLOW_CONTAINER(this)) {
6185     // 'auto' inline-size for grid-level box - fill the CB for 'stretch' /
6186     // 'normal' and clamp it to the CB if requested:
6187     bool stretch = false;
6188     if (!(aFlags & nsIFrame::eShrinkWrap) &&
6189         !StyleMargin()->HasInlineAxisAuto(aWM) &&
6190         !alignCB->IsMasonry(isOrthogonal ? eLogicalAxisBlock
6191                                          : eLogicalAxisInline)) {
6192       auto inlineAxisAlignment =
6193           isOrthogonal ? StylePosition()->UsedAlignSelf(alignCB->Style())._0
6194                        : StylePosition()->UsedJustifySelf(alignCB->Style())._0;
6195       stretch = inlineAxisAlignment == StyleAlignFlags::NORMAL ||
6196                 inlineAxisAlignment == StyleAlignFlags::STRETCH;
6197     }
6198     if (stretch || (aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize)) {
6199       auto iSizeToFillCB =
6200           std::max(nscoord(0), aCBSize.ISize(aWM) - aPadding.ISize(aWM) -
6201                                    aBorder.ISize(aWM) - aMargin.ISize(aWM));
6202       if (stretch || result.ISize(aWM) > iSizeToFillCB) {
6203         result.ISize(aWM) = iSizeToFillCB;
6204       }
6205     }
6206   }
6207 
6208   // Flex items ignore their min & max sizing properties in their
6209   // flex container's main-axis.  (Those properties get applied later in
6210   // the flexbox algorithm.)
6211   const auto& maxISizeCoord = stylePos->MaxISize(aWM);
6212   nscoord maxISize = NS_UNCONSTRAINEDSIZE;
6213   if (!maxISizeCoord.IsNone() &&
6214       !(isFlexItem && flexMainAxis == eLogicalAxisInline)) {
6215     maxISize = ComputeISizeValue(
6216         aRenderingContext, aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM),
6217         boxSizingToMarginEdgeISize, maxISizeCoord, aFlags);
6218     result.ISize(aWM) = std::min(maxISize, result.ISize(aWM));
6219   }
6220 
6221   const auto& minISizeCoord = stylePos->MinISize(aWM);
6222   nscoord minISize;
6223   if (!minISizeCoord.IsAuto() &&
6224       !(isFlexItem && flexMainAxis == eLogicalAxisInline)) {
6225     minISize = ComputeISizeValue(
6226         aRenderingContext, aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM),
6227         boxSizingToMarginEdgeISize, minISizeCoord, aFlags);
6228   } else if (MOZ_UNLIKELY(aFlags & eIApplyAutoMinSize)) {
6229     // This implements "Implied Minimum Size of Grid Items".
6230     // https://drafts.csswg.org/css-grid/#min-size-auto
6231     minISize = std::min(maxISize, GetMinISize(aRenderingContext));
6232     if (inlineStyleCoord->IsLengthPercentage()) {
6233       minISize = std::min(minISize, result.ISize(aWM));
6234     } else if (aFlags & eIClampMarginBoxMinSize) {
6235       // "if the grid item spans only grid tracks that have a fixed max track
6236       // sizing function, its automatic minimum size in that dimension is
6237       // further clamped to less than or equal to the size necessary to fit
6238       // its margin box within the resulting grid area (flooring at zero)"
6239       // https://drafts.csswg.org/css-grid/#min-size-auto
6240       auto maxMinISize =
6241           std::max(nscoord(0), aCBSize.ISize(aWM) - aPadding.ISize(aWM) -
6242                                    aBorder.ISize(aWM) - aMargin.ISize(aWM));
6243       minISize = std::min(minISize, maxMinISize);
6244     }
6245   } else {
6246     // Treat "min-width: auto" as 0.
6247     // NOTE: Technically, "auto" is supposed to behave like "min-content" on
6248     // flex items. However, we don't need to worry about that here, because
6249     // flex items' min-sizes are intentionally ignored until the flex
6250     // container explicitly considers them during space distribution.
6251     minISize = 0;
6252   }
6253   result.ISize(aWM) = std::max(minISize, result.ISize(aWM));
6254 
6255   // Compute block-axis size
6256   // (but not if we have auto bsize or if we received the "eUseAutoBSize"
6257   // flag -- then, we'll just stick with the bsize that we already calculated
6258   // in the initial ComputeAutoSize() call.)
6259   if (!(aFlags & nsIFrame::eUseAutoBSize)) {
6260     if (!nsLayoutUtils::IsAutoBSize(*blockStyleCoord, aCBSize.BSize(aWM))) {
6261       result.BSize(aWM) = nsLayoutUtils::ComputeBSizeValue(
6262           aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
6263           blockStyleCoord->AsLengthPercentage());
6264     } else if (MOZ_UNLIKELY(isGridItem) && blockStyleCoord->IsAuto() &&
6265                !IS_TRUE_OVERFLOW_CONTAINER(this) &&
6266                !alignCB->IsMasonry(isOrthogonal ? eLogicalAxisInline
6267                                                 : eLogicalAxisBlock)) {
6268       auto cbSize = aCBSize.BSize(aWM);
6269       if (cbSize != NS_UNCONSTRAINEDSIZE) {
6270         // 'auto' block-size for grid-level box - fill the CB for 'stretch' /
6271         // 'normal' and clamp it to the CB if requested:
6272         bool stretch = false;
6273         if (!StyleMargin()->HasBlockAxisAuto(aWM)) {
6274           auto blockAxisAlignment =
6275               isOrthogonal
6276                   ? StylePosition()->UsedJustifySelf(alignCB->Style())._0
6277                   : StylePosition()->UsedAlignSelf(alignCB->Style())._0;
6278           stretch = blockAxisAlignment == StyleAlignFlags::NORMAL ||
6279                     blockAxisAlignment == StyleAlignFlags::STRETCH;
6280         }
6281         if (stretch || (aFlags & ComputeSizeFlags::eBClampMarginBoxMinSize)) {
6282           auto bSizeToFillCB =
6283               std::max(nscoord(0), cbSize - aPadding.BSize(aWM) -
6284                                        aBorder.BSize(aWM) - aMargin.BSize(aWM));
6285           if (stretch || (result.BSize(aWM) != NS_UNCONSTRAINEDSIZE &&
6286                           result.BSize(aWM) > bSizeToFillCB)) {
6287             result.BSize(aWM) = bSizeToFillCB;
6288           }
6289         }
6290       }
6291     }
6292   }
6293 
6294   const auto& maxBSizeCoord = stylePos->MaxBSize(aWM);
6295 
6296   if (result.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
6297     if (!nsLayoutUtils::IsAutoBSize(maxBSizeCoord, aCBSize.BSize(aWM)) &&
6298         !(isFlexItem && flexMainAxis == eLogicalAxisBlock)) {
6299       nscoord maxBSize = nsLayoutUtils::ComputeBSizeValue(
6300           aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
6301           maxBSizeCoord.AsLengthPercentage());
6302       result.BSize(aWM) = std::min(maxBSize, result.BSize(aWM));
6303     }
6304 
6305     const auto& minBSizeCoord = stylePos->MinBSize(aWM);
6306 
6307     if (!nsLayoutUtils::IsAutoBSize(minBSizeCoord, aCBSize.BSize(aWM)) &&
6308         !(isFlexItem && flexMainAxis == eLogicalAxisBlock)) {
6309       nscoord minBSize = nsLayoutUtils::ComputeBSizeValue(
6310           aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
6311           minBSizeCoord.AsLengthPercentage());
6312       result.BSize(aWM) = std::max(minBSize, result.BSize(aWM));
6313     }
6314   }
6315 
6316   const nsStyleDisplay* disp = StyleDisplay();
6317   if (IsThemed(disp)) {
6318     LayoutDeviceIntSize widget;
6319     bool canOverride = true;
6320     nsPresContext* presContext = PresContext();
6321     presContext->Theme()->GetMinimumWidgetSize(
6322         presContext, this, disp->mAppearance, &widget, &canOverride);
6323 
6324     // Convert themed widget's physical dimensions to logical coords
6325     LogicalSize size(aWM,
6326                      nsSize(presContext->DevPixelsToAppUnits(widget.width),
6327                             presContext->DevPixelsToAppUnits(widget.height)));
6328 
6329     // GMWS() returns border-box; we need content-box
6330     size.ISize(aWM) -= aBorder.ISize(aWM) + aPadding.ISize(aWM);
6331     size.BSize(aWM) -= aBorder.BSize(aWM) + aPadding.BSize(aWM);
6332 
6333     if (size.BSize(aWM) > result.BSize(aWM) || !canOverride) {
6334       result.BSize(aWM) = size.BSize(aWM);
6335     }
6336     if (size.ISize(aWM) > result.ISize(aWM) || !canOverride) {
6337       result.ISize(aWM) = size.ISize(aWM);
6338     }
6339   }
6340 
6341   result.ISize(aWM) = std::max(0, result.ISize(aWM));
6342   result.BSize(aWM) = std::max(0, result.BSize(aWM));
6343 
6344   return result;
6345 }
6346 
ComputeSizeWithIntrinsicDimensions(gfxContext * aRenderingContext,WritingMode aWM,const IntrinsicSize & aIntrinsicSize,const AspectRatio & aIntrinsicRatio,const LogicalSize & aCBSize,const LogicalSize & aMargin,const LogicalSize & aBorder,const LogicalSize & aPadding,ComputeSizeFlags aFlags)6347 LogicalSize nsContainerFrame::ComputeSizeWithIntrinsicDimensions(
6348     gfxContext* aRenderingContext, WritingMode aWM,
6349     const IntrinsicSize& aIntrinsicSize, const AspectRatio& aIntrinsicRatio,
6350     const LogicalSize& aCBSize, const LogicalSize& aMargin,
6351     const LogicalSize& aBorder, const LogicalSize& aPadding,
6352     ComputeSizeFlags aFlags) {
6353   auto logicalRatio =
6354       aWM.IsVertical() ? aIntrinsicRatio.Inverted() : aIntrinsicRatio;
6355   const nsStylePosition* stylePos = StylePosition();
6356   const auto* inlineStyleCoord = &stylePos->ISize(aWM);
6357   const auto* blockStyleCoord = &stylePos->BSize(aWM);
6358   auto* parentFrame = GetParent();
6359   const bool isGridItem = parentFrame && parentFrame->IsGridContainerFrame() &&
6360                           !HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
6361   const bool isFlexItem =
6362       parentFrame && parentFrame->IsFlexContainerFrame() &&
6363       !parentFrame->HasAnyStateBits(NS_STATE_FLEX_IS_EMULATING_LEGACY_BOX) &&
6364       !HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
6365   // This variable only gets set (and used) if isFlexItem is true.  It
6366   // indicates which axis (in this frame's own WM) corresponds to its
6367   // flex container's main axis.
6368   LogicalAxis flexMainAxis =
6369       eLogicalAxisInline;  // (init to make valgrind happy)
6370   Maybe<StyleSize> imposedMainSizeStyleCoord;
6371 
6372   // If this is a flex item, and we're measuring its cross size after flexing
6373   // to resolve its main size, then we need to use the resolved main size
6374   // that the container provides to us *instead of* the main-size coordinate
6375   // from our style struct. (Otherwise, we'll be using an irrelevant value in
6376   // the aspect-ratio calculations below.)
6377   if (isFlexItem) {
6378     flexMainAxis = nsFlexContainerFrame::IsItemInlineAxisMainAxis(this)
6379                        ? eLogicalAxisInline
6380                        : eLogicalAxisBlock;
6381 
6382     // If FlexItemMainSizeOverride frame-property is set, then that means the
6383     // flex container is imposing a main-size on this flex item for it to use
6384     // as its size in the container's main axis.
6385     bool didImposeMainSize;
6386     nscoord imposedMainSize =
6387         GetProperty(nsIFrame::FlexItemMainSizeOverride(), &didImposeMainSize);
6388     if (didImposeMainSize) {
6389       imposedMainSizeStyleCoord = Some(StyleSize::LengthPercentage(
6390           LengthPercentage::FromAppUnits(imposedMainSize)));
6391       if (flexMainAxis == eLogicalAxisInline) {
6392         inlineStyleCoord = imposedMainSizeStyleCoord.ptr();
6393       } else {
6394         blockStyleCoord = imposedMainSizeStyleCoord.ptr();
6395       }
6396 
6397     } else {
6398       // Flex items use their "flex-basis" property in place of their main-size
6399       // property (e.g. "width") for sizing purposes, *unless* they have
6400       // "flex-basis:auto", in which case they use their main-size property
6401       // after all.
6402       // NOTE: The logic here should match the similar chunk for updating
6403       // mainAxisCoord in nsFrame::ComputeSize() (aside from using a different
6404       // dummy value in the IsUsedFlexBasisContent() case).
6405       const auto* flexBasis = &stylePos->mFlexBasis;
6406       auto& mainAxisCoord =
6407           (flexMainAxis == eLogicalAxisInline ? inlineStyleCoord
6408                                               : blockStyleCoord);
6409 
6410       if (nsFlexContainerFrame::IsUsedFlexBasisContent(*flexBasis,
6411                                                        *mainAxisCoord)) {
6412         // If we get here, we're resolving the flex base size for a flex item,
6413         // and we fall into the flexbox spec section 9.2 step 3, substep C (if
6414         // we have a definite cross size) or E (if not). And specifically:
6415         //
6416         // * If we have a definite cross size, we're supposed to resolve our
6417         //   main-size based on that and our intrinsic ratio.
6418         // * Otherwise, we're supposed to produce our max-content size.
6419         //
6420         // Conveniently, we can handle both of those scenarios (regardless of
6421         // which substep we fall into) by using the 'auto' keyword for our
6422         // main-axis coordinate here. (This makes sense, because the spec is
6423         // effectively trying to produce the 'auto' sizing behavior).
6424         static const StyleSize autoSize(StyleSize::Auto());
6425         mainAxisCoord = &autoSize;
6426       } else if (flexBasis->IsSize() && !flexBasis->IsAuto()) {
6427         // For all other non-'auto' flex-basis values, we just swap in the
6428         // flex-basis itself for the main-size property.
6429         mainAxisCoord = &flexBasis->AsSize();
6430       }  // else: flex-basis is 'auto', which is deferring to some explicit
6431          // value in mainAxisCoord. So we proceed w/o touching mainAxisCoord.
6432     }
6433   }
6434 
6435   // Handle intrinsic sizes and their interaction with
6436   // {min-,max-,}{width,height} according to the rules in
6437   // http://www.w3.org/TR/CSS21/visudet.html#min-max-widths
6438 
6439   // Note: throughout the following section of the function, I avoid
6440   // a * (b / c) because of its reduced accuracy relative to a * b / c
6441   // or (a * b) / c (which are equivalent).
6442 
6443   const bool isAutoISize = inlineStyleCoord->IsAuto();
6444   const bool isAutoBSize =
6445       nsLayoutUtils::IsAutoBSize(*blockStyleCoord, aCBSize.BSize(aWM));
6446 
6447   LogicalSize boxSizingAdjust(aWM);
6448   if (stylePos->mBoxSizing == StyleBoxSizing::Border) {
6449     boxSizingAdjust = aBorder + aPadding;
6450   }
6451   nscoord boxSizingToMarginEdgeISize = aMargin.ISize(aWM) + aBorder.ISize(aWM) +
6452                                        aPadding.ISize(aWM) -
6453                                        boxSizingAdjust.ISize(aWM);
6454 
6455   nscoord iSize, minISize, maxISize, bSize, minBSize, maxBSize;
6456   enum class Stretch {
6457     // stretch to fill the CB (preserving intrinsic ratio) in the relevant axis
6458     StretchPreservingRatio,  // XXX not used yet
6459     // stretch to fill the CB in the relevant axis
6460     Stretch,
6461     // no stretching in the relevant axis
6462     NoStretch,
6463   };
6464   // just to avoid having to type these out everywhere:
6465   const auto eStretchPreservingRatio = Stretch::StretchPreservingRatio;
6466   const auto eStretch = Stretch::Stretch;
6467   const auto eNoStretch = Stretch::NoStretch;
6468 
6469   Stretch stretchI = eNoStretch;  // stretch behavior in the inline axis
6470   Stretch stretchB = eNoStretch;  // stretch behavior in the block axis
6471 
6472   const bool isOrthogonal = aWM.IsOrthogonalTo(parentFrame->GetWritingMode());
6473   const bool isVertical = aWM.IsVertical();
6474   const auto& isizeCoord =
6475       isVertical ? aIntrinsicSize.height : aIntrinsicSize.width;
6476   const bool hasIntrinsicISize = isizeCoord.isSome();
6477   nscoord intrinsicISize = std::max(0, isizeCoord.valueOr(0));
6478 
6479   const auto& bsizeCoord =
6480       isVertical ? aIntrinsicSize.width : aIntrinsicSize.height;
6481   const bool hasIntrinsicBSize = bsizeCoord.isSome();
6482   nscoord intrinsicBSize = std::max(0, bsizeCoord.valueOr(0));
6483 
6484   if (!isAutoISize) {
6485     iSize = ComputeISizeValue(
6486         aRenderingContext, aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM),
6487         boxSizingToMarginEdgeISize, *inlineStyleCoord, aFlags);
6488   } else if (MOZ_UNLIKELY(isGridItem) &&
6489              !parentFrame->IsMasonry(isOrthogonal ? eLogicalAxisBlock
6490                                                   : eLogicalAxisInline)) {
6491     MOZ_ASSERT(!IS_TRUE_OVERFLOW_CONTAINER(this));
6492     // 'auto' inline-size for grid-level box - apply 'stretch' as needed:
6493     auto cbSize = aCBSize.ISize(aWM);
6494     if (cbSize != NS_UNCONSTRAINEDSIZE) {
6495       if (!StyleMargin()->HasInlineAxisAuto(aWM)) {
6496         auto inlineAxisAlignment =
6497             isOrthogonal ? stylePos->UsedAlignSelf(GetParent()->Style())._0
6498                          : stylePos->UsedJustifySelf(GetParent()->Style())._0;
6499         // Note: 'normal' means 'start' for elements with an intrinsic size
6500         // or ratio in the relevant dimension, otherwise 'stretch'.
6501         // https://drafts.csswg.org/css-grid/#grid-item-sizing
6502         if ((inlineAxisAlignment == StyleAlignFlags::NORMAL &&
6503              !hasIntrinsicISize && !logicalRatio) ||
6504             inlineAxisAlignment == StyleAlignFlags::STRETCH) {
6505           stretchI = eStretch;
6506         }
6507       }
6508       if (stretchI != eNoStretch ||
6509           (aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize)) {
6510         iSize =
6511             std::max(nscoord(0), cbSize - aPadding.ISize(aWM) -
6512                                      aBorder.ISize(aWM) - aMargin.ISize(aWM));
6513       }
6514     } else {
6515       // Reset this flag to avoid applying the clamping below.
6516       aFlags =
6517           ComputeSizeFlags(aFlags & ~ComputeSizeFlags::eIClampMarginBoxMinSize);
6518     }
6519   }
6520 
6521   const auto& maxISizeCoord = stylePos->MaxISize(aWM);
6522 
6523   if (!maxISizeCoord.IsNone() &&
6524       !(isFlexItem && flexMainAxis == eLogicalAxisInline)) {
6525     maxISize = ComputeISizeValue(
6526         aRenderingContext, aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM),
6527         boxSizingToMarginEdgeISize, maxISizeCoord, aFlags);
6528   } else {
6529     maxISize = nscoord_MAX;
6530   }
6531 
6532   // NOTE: Flex items ignore their min & max sizing properties in their
6533   // flex container's main-axis.  (Those properties get applied later in
6534   // the flexbox algorithm.)
6535 
6536   const auto& minISizeCoord = stylePos->MinISize(aWM);
6537 
6538   if (!minISizeCoord.IsAuto() &&
6539       !(isFlexItem && flexMainAxis == eLogicalAxisInline)) {
6540     minISize = ComputeISizeValue(
6541         aRenderingContext, aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM),
6542         boxSizingToMarginEdgeISize, minISizeCoord, aFlags);
6543   } else {
6544     // Treat "min-width: auto" as 0.
6545     // NOTE: Technically, "auto" is supposed to behave like "min-content" on
6546     // flex items. However, we don't need to worry about that here, because
6547     // flex items' min-sizes are intentionally ignored until the flex
6548     // container explicitly considers them during space distribution.
6549     minISize = 0;
6550   }
6551 
6552   if (!isAutoBSize) {
6553     bSize = nsLayoutUtils::ComputeBSizeValue(
6554         aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
6555         blockStyleCoord->AsLengthPercentage());
6556   } else if (MOZ_UNLIKELY(isGridItem) &&
6557              !parentFrame->IsMasonry(isOrthogonal ? eLogicalAxisInline
6558                                                   : eLogicalAxisBlock)) {
6559     MOZ_ASSERT(!IS_TRUE_OVERFLOW_CONTAINER(this));
6560     // 'auto' block-size for grid-level box - apply 'stretch' as needed:
6561     auto cbSize = aCBSize.BSize(aWM);
6562     if (cbSize != NS_UNCONSTRAINEDSIZE) {
6563       if (!StyleMargin()->HasBlockAxisAuto(aWM)) {
6564         auto blockAxisAlignment =
6565             !isOrthogonal ? stylePos->UsedAlignSelf(GetParent()->Style())._0
6566                           : stylePos->UsedJustifySelf(GetParent()->Style())._0;
6567         // Note: 'normal' means 'start' for elements with an intrinsic size
6568         // or ratio in the relevant dimension, otherwise 'stretch'.
6569         // https://drafts.csswg.org/css-grid/#grid-item-sizing
6570         if ((blockAxisAlignment == StyleAlignFlags::NORMAL &&
6571              !hasIntrinsicBSize && !logicalRatio) ||
6572             blockAxisAlignment == StyleAlignFlags::STRETCH) {
6573           stretchB = eStretch;
6574         }
6575       }
6576       if (stretchB != eNoStretch ||
6577           (aFlags & ComputeSizeFlags::eBClampMarginBoxMinSize)) {
6578         bSize =
6579             std::max(nscoord(0), cbSize - aPadding.BSize(aWM) -
6580                                      aBorder.BSize(aWM) - aMargin.BSize(aWM));
6581       }
6582     } else {
6583       // Reset this flag to avoid applying the clamping below.
6584       aFlags =
6585           ComputeSizeFlags(aFlags & ~ComputeSizeFlags::eBClampMarginBoxMinSize);
6586     }
6587   }
6588 
6589   const auto& maxBSizeCoord = stylePos->MaxBSize(aWM);
6590 
6591   if (!nsLayoutUtils::IsAutoBSize(maxBSizeCoord, aCBSize.BSize(aWM)) &&
6592       !(isFlexItem && flexMainAxis == eLogicalAxisBlock)) {
6593     maxBSize = nsLayoutUtils::ComputeBSizeValue(
6594         aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
6595         maxBSizeCoord.AsLengthPercentage());
6596   } else {
6597     maxBSize = nscoord_MAX;
6598   }
6599 
6600   const auto& minBSizeCoord = stylePos->MinBSize(aWM);
6601 
6602   if (!nsLayoutUtils::IsAutoBSize(minBSizeCoord, aCBSize.BSize(aWM)) &&
6603       !(isFlexItem && flexMainAxis == eLogicalAxisBlock)) {
6604     minBSize = nsLayoutUtils::ComputeBSizeValue(
6605         aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
6606         minBSizeCoord.AsLengthPercentage());
6607   } else {
6608     minBSize = 0;
6609   }
6610 
6611   NS_ASSERTION(aCBSize.ISize(aWM) != NS_UNCONSTRAINEDSIZE,
6612                "Our containing block must not have unconstrained inline-size!");
6613 
6614   // Now calculate the used values for iSize and bSize:
6615 
6616   if (isAutoISize) {
6617     if (isAutoBSize) {
6618       // 'auto' iSize, 'auto' bSize
6619 
6620       // Get tentative values - CSS 2.1 sections 10.3.2 and 10.6.2:
6621 
6622       nscoord tentISize, tentBSize;
6623 
6624       if (hasIntrinsicISize) {
6625         tentISize = intrinsicISize;
6626       } else if (hasIntrinsicBSize && logicalRatio) {
6627         tentISize = logicalRatio.ApplyTo(intrinsicBSize);
6628       } else if (logicalRatio) {
6629         tentISize =
6630             aCBSize.ISize(aWM) - boxSizingToMarginEdgeISize;  // XXX scrollbar?
6631         if (tentISize < 0) tentISize = 0;
6632       } else {
6633         tentISize = nsPresContext::CSSPixelsToAppUnits(300);
6634       }
6635 
6636       // If we need to clamp the inline size to fit the CB, we use the 'stretch'
6637       // or 'normal' codepath.  We use the ratio-preserving 'normal' codepath
6638       // unless we have 'stretch' in the other axis.
6639       if ((aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize) &&
6640           stretchI != eStretch && tentISize > iSize) {
6641         stretchI = (stretchB == eStretch ? eStretch : eStretchPreservingRatio);
6642       }
6643 
6644       if (hasIntrinsicBSize) {
6645         tentBSize = intrinsicBSize;
6646       } else if (logicalRatio) {
6647         tentBSize = logicalRatio.Inverted().ApplyTo(tentISize);
6648       } else {
6649         tentBSize = nsPresContext::CSSPixelsToAppUnits(150);
6650       }
6651 
6652       // (ditto the comment about clamping the inline size above)
6653       if ((aFlags & ComputeSizeFlags::eBClampMarginBoxMinSize) &&
6654           stretchB != eStretch && tentBSize > bSize) {
6655         stretchB = (stretchI == eStretch ? eStretch : eStretchPreservingRatio);
6656       }
6657 
6658       if (logicalRatio) {
6659         if (stretchI == eStretch) {
6660           tentISize = iSize;  // * / 'stretch'
6661           if (stretchB == eStretch) {
6662             tentBSize = bSize;  // 'stretch' / 'stretch'
6663           } else if (stretchB == eStretchPreservingRatio) {
6664             // 'normal' / 'stretch'
6665             tentBSize = logicalRatio.Inverted().ApplyTo(iSize);
6666           }
6667         } else if (stretchB == eStretch) {
6668           tentBSize = bSize;  // 'stretch' / * (except 'stretch')
6669           if (stretchI == eStretchPreservingRatio) {
6670             // 'stretch' / 'normal'
6671             tentISize = logicalRatio.ApplyTo(bSize);
6672           }
6673         } else if (stretchI == eStretchPreservingRatio) {
6674           tentISize = iSize;  // * (except 'stretch') / 'normal'
6675           tentBSize = logicalRatio.Inverted().ApplyTo(iSize);
6676           if (stretchB == eStretchPreservingRatio && tentBSize > bSize) {
6677             // Stretch within the CB size with preserved intrinsic ratio.
6678             tentBSize = bSize;  // 'normal' / 'normal'
6679             tentISize = logicalRatio.ApplyTo(bSize);
6680           }
6681         } else if (stretchB == eStretchPreservingRatio) {
6682           tentBSize = bSize;  // 'normal' / * (except 'normal' and 'stretch')
6683           tentISize = logicalRatio.ApplyTo(bSize);
6684         }
6685       }
6686 
6687       // ComputeAutoSizeWithIntrinsicDimensions preserves the ratio when
6688       // applying the min/max-size.  We don't want that when we have 'stretch'
6689       // in either axis because tentISize/tentBSize is likely not according to
6690       // ratio now.
6691       if (logicalRatio && stretchI != eStretch && stretchB != eStretch) {
6692         nsSize autoSize = nsLayoutUtils::ComputeAutoSizeWithIntrinsicDimensions(
6693             minISize, minBSize, maxISize, maxBSize, tentISize, tentBSize);
6694         // The nsSize that ComputeAutoSizeWithIntrinsicDimensions returns will
6695         // actually contain logical values if the parameters passed to it were
6696         // logical coordinates, so we do NOT perform a physical-to-logical
6697         // conversion here, but just assign the fields directly to our result.
6698         iSize = autoSize.width;
6699         bSize = autoSize.height;
6700       } else {
6701         // Not honoring an intrinsic ratio: clamp the dimensions independently.
6702         iSize = NS_CSS_MINMAX(tentISize, minISize, maxISize);
6703         bSize = NS_CSS_MINMAX(tentBSize, minBSize, maxBSize);
6704       }
6705     } else {
6706       // 'auto' iSize, non-'auto' bSize
6707       bSize = NS_CSS_MINMAX(bSize, minBSize, maxBSize);
6708       if (stretchI != eStretch) {
6709         if (logicalRatio) {
6710           iSize = logicalRatio.ApplyTo(bSize);
6711         } else if (hasIntrinsicISize) {
6712           if (!((aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize) &&
6713                 intrinsicISize > iSize)) {
6714             iSize = intrinsicISize;
6715           }  // else - leave iSize as is to fill the CB
6716         } else {
6717           iSize = nsPresContext::CSSPixelsToAppUnits(300);
6718         }
6719       }  // else - leave iSize as is to fill the CB
6720       iSize = NS_CSS_MINMAX(iSize, minISize, maxISize);
6721     }
6722   } else {
6723     if (isAutoBSize) {
6724       // non-'auto' iSize, 'auto' bSize
6725       iSize = NS_CSS_MINMAX(iSize, minISize, maxISize);
6726       if (stretchB != eStretch) {
6727         if (logicalRatio) {
6728           bSize = logicalRatio.Inverted().ApplyTo(iSize);
6729         } else if (hasIntrinsicBSize) {
6730           if (!((aFlags & ComputeSizeFlags::eBClampMarginBoxMinSize) &&
6731                 intrinsicBSize > bSize)) {
6732             bSize = intrinsicBSize;
6733           }  // else - leave bSize as is to fill the CB
6734         } else {
6735           bSize = nsPresContext::CSSPixelsToAppUnits(150);
6736         }
6737       }  // else - leave bSize as is to fill the CB
6738       bSize = NS_CSS_MINMAX(bSize, minBSize, maxBSize);
6739 
6740     } else {
6741       // non-'auto' iSize, non-'auto' bSize
6742       iSize = NS_CSS_MINMAX(iSize, minISize, maxISize);
6743       bSize = NS_CSS_MINMAX(bSize, minBSize, maxBSize);
6744     }
6745   }
6746 
6747   return LogicalSize(aWM, iSize, bSize);
6748 }
6749 
ComputeTightBounds(DrawTarget * aDrawTarget) const6750 nsRect nsIFrame::ComputeTightBounds(DrawTarget* aDrawTarget) const {
6751   return GetVisualOverflowRect();
6752 }
6753 
ComputeSimpleTightBounds(DrawTarget * aDrawTarget) const6754 nsRect nsContainerFrame::ComputeSimpleTightBounds(
6755     DrawTarget* aDrawTarget) const {
6756   if (StyleOutline()->ShouldPaintOutline() || StyleBorder()->HasBorder() ||
6757       !StyleBackground()->IsTransparent(this) ||
6758       StyleDisplay()->HasAppearance()) {
6759     // Not necessarily tight, due to clipping, negative
6760     // outline-offset, and lots of other issues, but that's OK
6761     return GetVisualOverflowRect();
6762   }
6763 
6764   nsRect r(0, 0, 0, 0);
6765   for (const auto& childLists : ChildLists()) {
6766     for (nsIFrame* child : childLists.mList) {
6767       r.UnionRect(
6768           r, child->ComputeTightBounds(aDrawTarget) + child->GetPosition());
6769     }
6770   }
6771   return r;
6772 }
6773 
6774 /* virtual */
GetPrefWidthTightBounds(gfxContext * aContext,nscoord * aX,nscoord * aXMost)6775 nsresult nsIFrame::GetPrefWidthTightBounds(gfxContext* aContext, nscoord* aX,
6776                                            nscoord* aXMost) {
6777   return NS_ERROR_NOT_IMPLEMENTED;
6778 }
6779 
6780 /* virtual */
ComputeAutoSize(gfxContext * aRenderingContext,WritingMode aWM,const mozilla::LogicalSize & aCBSize,nscoord aAvailableISize,const mozilla::LogicalSize & aMargin,const mozilla::LogicalSize & aBorder,const mozilla::LogicalSize & aPadding,ComputeSizeFlags aFlags)6781 LogicalSize nsIFrame::ComputeAutoSize(
6782     gfxContext* aRenderingContext, WritingMode aWM,
6783     const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize,
6784     const mozilla::LogicalSize& aMargin, const mozilla::LogicalSize& aBorder,
6785     const mozilla::LogicalSize& aPadding, ComputeSizeFlags aFlags) {
6786   // Use basic shrink-wrapping as a default implementation.
6787   LogicalSize result(aWM, 0xdeadbeef, NS_UNCONSTRAINEDSIZE);
6788 
6789   // don't bother setting it if the result won't be used
6790   if (StylePosition()->ISize(aWM).IsAuto()) {
6791     nscoord availBased = aAvailableISize - aMargin.ISize(aWM) -
6792                          aBorder.ISize(aWM) - aPadding.ISize(aWM);
6793     result.ISize(aWM) = ShrinkWidthToFit(aRenderingContext, availBased, aFlags);
6794   }
6795   return result;
6796 }
6797 
ShrinkWidthToFit(gfxContext * aRenderingContext,nscoord aISizeInCB,ComputeSizeFlags aFlags)6798 nscoord nsIFrame::ShrinkWidthToFit(gfxContext* aRenderingContext,
6799                                    nscoord aISizeInCB,
6800                                    ComputeSizeFlags aFlags) {
6801   // If we're a container for font size inflation, then shrink
6802   // wrapping inside of us should not apply font size inflation.
6803   AutoMaybeDisableFontInflation an(this);
6804 
6805   nscoord result;
6806   nscoord minISize = GetMinISize(aRenderingContext);
6807   if (minISize > aISizeInCB) {
6808     const bool clamp = aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize;
6809     result = MOZ_UNLIKELY(clamp) ? aISizeInCB : minISize;
6810   } else {
6811     nscoord prefISize = GetPrefISize(aRenderingContext);
6812     if (prefISize > aISizeInCB) {
6813       result = aISizeInCB;
6814     } else {
6815       result = prefISize;
6816     }
6817   }
6818   return result;
6819 }
6820 
ComputeISizeValue(gfxContext * aRenderingContext,nscoord aContainingBlockISize,nscoord aContentEdgeToBoxSizing,nscoord aBoxSizingToMarginEdge,StyleExtremumLength aSize,ComputeSizeFlags aFlags)6821 nscoord nsIFrame::ComputeISizeValue(gfxContext* aRenderingContext,
6822                                     nscoord aContainingBlockISize,
6823                                     nscoord aContentEdgeToBoxSizing,
6824                                     nscoord aBoxSizingToMarginEdge,
6825                                     StyleExtremumLength aSize,
6826                                     ComputeSizeFlags aFlags) {
6827   // If 'this' is a container for font size inflation, then shrink
6828   // wrapping inside of it should not apply font size inflation.
6829   AutoMaybeDisableFontInflation an(this);
6830   nscoord result;
6831   switch (aSize) {
6832     case StyleExtremumLength::MaxContent:
6833       result = GetPrefISize(aRenderingContext);
6834       NS_ASSERTION(result >= 0, "inline-size less than zero");
6835       return result;
6836     case StyleExtremumLength::MinContent:
6837       result = GetMinISize(aRenderingContext);
6838       NS_ASSERTION(result >= 0, "inline-size less than zero");
6839       if (MOZ_UNLIKELY(aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize)) {
6840         auto available = aContainingBlockISize -
6841                          (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing);
6842         result = std::min(available, result);
6843       }
6844       return result;
6845     case StyleExtremumLength::MozFitContent: {
6846       nscoord pref = GetPrefISize(aRenderingContext),
6847               min = GetMinISize(aRenderingContext),
6848               fill = aContainingBlockISize -
6849                      (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing);
6850       if (MOZ_UNLIKELY(aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize)) {
6851         min = std::min(min, fill);
6852       }
6853       result = std::max(min, std::min(pref, fill));
6854       NS_ASSERTION(result >= 0, "inline-size less than zero");
6855       return result;
6856     }
6857     case StyleExtremumLength::MozAvailable:
6858       return aContainingBlockISize -
6859              (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing);
6860   }
6861   MOZ_ASSERT_UNREACHABLE("Unknown extremum length?");
6862   return 0;
6863 }
6864 
ComputeISizeValue(gfxContext * aRenderingContext,nscoord aContainingBlockISize,nscoord aContentEdgeToBoxSizing,nscoord aBoxSizingToMarginEdge,const LengthPercentage & aCoord,ComputeSizeFlags aFlags)6865 nscoord nsIFrame::ComputeISizeValue(gfxContext* aRenderingContext,
6866                                     nscoord aContainingBlockISize,
6867                                     nscoord aContentEdgeToBoxSizing,
6868                                     nscoord aBoxSizingToMarginEdge,
6869                                     const LengthPercentage& aCoord,
6870                                     ComputeSizeFlags aFlags) {
6871   MOZ_ASSERT(aRenderingContext, "non-null rendering context expected");
6872   LAYOUT_WARN_IF_FALSE(
6873       aContainingBlockISize != NS_UNCONSTRAINEDSIZE,
6874       "have unconstrained inline-size; this should only result from "
6875       "very large sizes, not attempts at intrinsic inline-size "
6876       "calculation");
6877   MOZ_ASSERT(aContainingBlockISize >= 0, "inline-size less than zero");
6878 
6879   nscoord result = aCoord.Resolve(aContainingBlockISize);
6880   // The result of a calc() expression might be less than 0; we
6881   // should clamp at runtime (below).  (Percentages and coords that
6882   // are less than 0 have already been dropped by the parser.)
6883   result -= aContentEdgeToBoxSizing;
6884   return std::max(0, result);
6885 }
6886 
DidReflow(nsPresContext * aPresContext,const ReflowInput * aReflowInput)6887 void nsFrame::DidReflow(nsPresContext* aPresContext,
6888                         const ReflowInput* aReflowInput) {
6889   NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("nsFrame::DidReflow"));
6890 
6891   SVGObserverUtils::InvalidateDirectRenderingObservers(
6892       this, SVGObserverUtils::INVALIDATE_REFLOW);
6893 
6894   RemoveStateBits(NS_FRAME_IN_REFLOW | NS_FRAME_FIRST_REFLOW |
6895                   NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
6896 
6897   // Clear state that was used in ReflowInput::InitResizeFlags (see
6898   // comment there for why we can't clear it there).
6899   SetHasBSizeChange(false);
6900 
6901   // Notify the percent bsize observer if there is a percent bsize.
6902   // The observer may be able to initiate another reflow with a computed
6903   // bsize. This happens in the case where a table cell has no computed
6904   // bsize but can fabricate one when the cell bsize is known.
6905   if (aReflowInput && aReflowInput->mPercentBSizeObserver && !GetPrevInFlow()) {
6906     const auto& bsize =
6907         aReflowInput->mStylePosition->BSize(aReflowInput->GetWritingMode());
6908     if (bsize.HasPercent()) {
6909       aReflowInput->mPercentBSizeObserver->NotifyPercentBSize(*aReflowInput);
6910     }
6911   }
6912 
6913   aPresContext->ReflowedFrame();
6914 }
6915 
FinishReflowWithAbsoluteFrames(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus,bool aConstrainBSize)6916 void nsFrame::FinishReflowWithAbsoluteFrames(nsPresContext* aPresContext,
6917                                              ReflowOutput& aDesiredSize,
6918                                              const ReflowInput& aReflowInput,
6919                                              nsReflowStatus& aStatus,
6920                                              bool aConstrainBSize) {
6921   ReflowAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput, aStatus,
6922                        aConstrainBSize);
6923 
6924   FinishAndStoreOverflow(&aDesiredSize, aReflowInput.mStyleDisplay);
6925 }
6926 
ReflowAbsoluteFrames(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus,bool aConstrainBSize)6927 void nsIFrame::ReflowAbsoluteFrames(nsPresContext* aPresContext,
6928                                     ReflowOutput& aDesiredSize,
6929                                     const ReflowInput& aReflowInput,
6930                                     nsReflowStatus& aStatus,
6931                                     bool aConstrainBSize) {
6932   if (HasAbsolutelyPositionedChildren()) {
6933     nsAbsoluteContainingBlock* absoluteContainer = GetAbsoluteContainingBlock();
6934 
6935     // Let the absolutely positioned container reflow any absolutely positioned
6936     // child frames that need to be reflowed
6937 
6938     // The containing block for the abs pos kids is formed by our padding edge.
6939     nsMargin usedBorder = GetUsedBorder();
6940     nscoord containingBlockWidth =
6941         std::max(0, aDesiredSize.Width() - usedBorder.LeftRight());
6942     nscoord containingBlockHeight =
6943         std::max(0, aDesiredSize.Height() - usedBorder.TopBottom());
6944     nsContainerFrame* container = do_QueryFrame(this);
6945     NS_ASSERTION(container,
6946                  "Abs-pos children only supported on container frames for now");
6947 
6948     nsRect containingBlock(0, 0, containingBlockWidth, containingBlockHeight);
6949     AbsPosReflowFlags flags =
6950         AbsPosReflowFlags::CBWidthAndHeightChanged;  // XXX could be optimized
6951     if (aConstrainBSize) {
6952       flags |= AbsPosReflowFlags::ConstrainHeight;
6953     }
6954     absoluteContainer->Reflow(container, aPresContext, aReflowInput, aStatus,
6955                               containingBlock, flags,
6956                               &aDesiredSize.mOverflowAreas);
6957   }
6958 }
6959 
PushDirtyBitToAbsoluteFrames()6960 void nsContainerFrame::PushDirtyBitToAbsoluteFrames() {
6961   if (!(GetStateBits() & NS_FRAME_IS_DIRTY)) {
6962     return;  // No dirty bit to push.
6963   }
6964   if (!HasAbsolutelyPositionedChildren()) {
6965     return;  // No absolute children to push to.
6966   }
6967   GetAbsoluteContainingBlock()->MarkAllFramesDirty();
6968 }
6969 
6970 /* virtual */
CanContinueTextRun() const6971 bool nsIFrame::CanContinueTextRun() const {
6972   // By default, a frame will *not* allow a text run to be continued
6973   // through it.
6974   return false;
6975 }
6976 
Reflow(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)6977 void nsIFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
6978                       const ReflowInput& aReflowInput,
6979                       nsReflowStatus& aStatus) {
6980   MarkInReflow();
6981   DO_GLOBAL_REFLOW_COUNT("nsFrame");
6982   MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
6983   aDesiredSize.ClearSize();
6984   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
6985 }
6986 
IsContentDisabled() const6987 bool nsIFrame::IsContentDisabled() const {
6988   // FIXME(emilio): Doing this via CSS means callers must ensure the style is up
6989   // to date, and they don't!
6990   if (StyleUI()->mUserInput == StyleUserInput::None) {
6991     return true;
6992   }
6993 
6994   auto* element = nsGenericHTMLElement::FromNodeOrNull(GetContent());
6995   return element && element->IsDisabled();
6996 }
6997 
CharacterDataChanged(const CharacterDataChangeInfo &)6998 nsresult nsIFrame::CharacterDataChanged(const CharacterDataChangeInfo&) {
6999   MOZ_ASSERT_UNREACHABLE("should only be called for text frames");
7000   return NS_OK;
7001 }
7002 
AttributeChanged(int32_t aNameSpaceID,nsAtom * aAttribute,int32_t aModType)7003 nsresult nsIFrame::AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute,
7004                                     int32_t aModType) {
7005   return NS_OK;
7006 }
7007 
7008 // Flow member functions
7009 
GetPrevContinuation() const7010 nsIFrame* nsIFrame::GetPrevContinuation() const { return nullptr; }
7011 
SetPrevContinuation(nsIFrame * aPrevContinuation)7012 void nsIFrame::SetPrevContinuation(nsIFrame* aPrevContinuation) {
7013   MOZ_ASSERT(false, "not splittable");
7014 }
7015 
GetNextContinuation() const7016 nsIFrame* nsIFrame::GetNextContinuation() const { return nullptr; }
7017 
SetNextContinuation(nsIFrame *)7018 void nsIFrame::SetNextContinuation(nsIFrame*) {
7019   MOZ_ASSERT(false, "not splittable");
7020 }
7021 
GetPrevInFlow() const7022 nsIFrame* nsIFrame::GetPrevInFlow() const { return nullptr; }
7023 
SetPrevInFlow(nsIFrame * aPrevInFlow)7024 void nsIFrame::SetPrevInFlow(nsIFrame* aPrevInFlow) {
7025   MOZ_ASSERT(false, "not splittable");
7026 }
7027 
GetNextInFlow() const7028 nsIFrame* nsIFrame::GetNextInFlow() const { return nullptr; }
7029 
SetNextInFlow(nsIFrame *)7030 void nsIFrame::SetNextInFlow(nsIFrame*) { MOZ_ASSERT(false, "not splittable"); }
7031 
GetTailContinuation()7032 nsIFrame* nsIFrame::GetTailContinuation() {
7033   nsIFrame* frame = this;
7034   while (frame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
7035     frame = frame->GetPrevContinuation();
7036     NS_ASSERTION(frame, "first continuation can't be overflow container");
7037   }
7038   for (nsIFrame* next = frame->GetNextContinuation();
7039        next && !(next->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER);
7040        next = frame->GetNextContinuation()) {
7041     frame = next;
7042   }
7043 
7044   MOZ_ASSERT(frame, "illegal state in continuation chain.");
7045   return frame;
7046 }
7047 
7048 // Associated view object
SetView(nsView * aView)7049 void nsIFrame::SetView(nsView* aView) {
7050   if (aView) {
7051     aView->SetFrame(this);
7052 
7053 #ifdef DEBUG
7054     LayoutFrameType frameType = Type();
7055     NS_ASSERTION(frameType == LayoutFrameType::SubDocument ||
7056                      frameType == LayoutFrameType::ListControl ||
7057                      frameType == LayoutFrameType::Object ||
7058                      frameType == LayoutFrameType::Viewport ||
7059                      frameType == LayoutFrameType::MenuPopup,
7060                  "Only specific frame types can have an nsView");
7061 #endif
7062 
7063     // Store the view on the frame.
7064     SetViewInternal(aView);
7065 
7066     // Set the frame state bit that says the frame has a view
7067     AddStateBits(NS_FRAME_HAS_VIEW);
7068 
7069     // Let all of the ancestors know they have a descendant with a view.
7070     for (nsIFrame* f = GetParent();
7071          f && !(f->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW);
7072          f = f->GetParent())
7073       f->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
7074   } else {
7075     MOZ_ASSERT_UNREACHABLE("Destroying a view while the frame is alive?");
7076     RemoveStateBits(NS_FRAME_HAS_VIEW);
7077     SetViewInternal(nullptr);
7078   }
7079 }
7080 
7081 // Find the first geometric parent that has a view
GetAncestorWithView() const7082 nsIFrame* nsIFrame::GetAncestorWithView() const {
7083   for (nsIFrame* f = GetParent(); nullptr != f; f = f->GetParent()) {
7084     if (f->HasView()) {
7085       return f;
7086     }
7087   }
7088   return nullptr;
7089 }
7090 
7091 template <nsPoint (nsIFrame::*PositionGetter)() const>
OffsetCalculator(const nsIFrame * aThis,const nsIFrame * aOther)7092 static nsPoint OffsetCalculator(const nsIFrame* aThis, const nsIFrame* aOther) {
7093   MOZ_ASSERT(aOther, "Must have frame for destination coordinate system!");
7094 
7095   NS_ASSERTION(aThis->PresContext() == aOther->PresContext(),
7096                "GetOffsetTo called on frames in different documents");
7097 
7098   nsPoint offset(0, 0);
7099   const nsIFrame* f;
7100   for (f = aThis; f != aOther && f; f = f->GetParent()) {
7101     offset += (f->*PositionGetter)();
7102   }
7103 
7104   if (f != aOther) {
7105     // Looks like aOther wasn't an ancestor of |this|.  So now we have
7106     // the root-frame-relative position of |this| in |offset|.  Convert back
7107     // to the coordinates of aOther
7108     while (aOther) {
7109       offset -= (aOther->*PositionGetter)();
7110       aOther = aOther->GetParent();
7111     }
7112   }
7113 
7114   return offset;
7115 }
7116 
GetOffsetTo(const nsIFrame * aOther) const7117 nsPoint nsIFrame::GetOffsetTo(const nsIFrame* aOther) const {
7118   return OffsetCalculator<&nsIFrame::GetPosition>(this, aOther);
7119 }
7120 
GetOffsetToIgnoringScrolling(const nsIFrame * aOther) const7121 nsPoint nsIFrame::GetOffsetToIgnoringScrolling(const nsIFrame* aOther) const {
7122   return OffsetCalculator<&nsIFrame::GetPositionIgnoringScrolling>(this,
7123                                                                    aOther);
7124 }
7125 
GetOffsetToCrossDoc(const nsIFrame * aOther) const7126 nsPoint nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther) const {
7127   return GetOffsetToCrossDoc(aOther, PresContext()->AppUnitsPerDevPixel());
7128 }
7129 
GetOffsetToCrossDoc(const nsIFrame * aOther,const int32_t aAPD) const7130 nsPoint nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther,
7131                                       const int32_t aAPD) const {
7132   MOZ_ASSERT(aOther, "Must have frame for destination coordinate system!");
7133   NS_ASSERTION(PresContext()->GetRootPresContext() ==
7134                    aOther->PresContext()->GetRootPresContext(),
7135                "trying to get the offset between frames in different document "
7136                "hierarchies?");
7137   if (PresContext()->GetRootPresContext() !=
7138       aOther->PresContext()->GetRootPresContext()) {
7139     // crash right away, we are almost certainly going to crash anyway.
7140     MOZ_CRASH(
7141         "trying to get the offset between frames in different "
7142         "document hierarchies?");
7143   }
7144 
7145   const nsIFrame* root = nullptr;
7146   // offset will hold the final offset
7147   // docOffset holds the currently accumulated offset at the current APD, it
7148   // will be converted and added to offset when the current APD changes.
7149   nsPoint offset(0, 0), docOffset(0, 0);
7150   const nsIFrame* f = this;
7151   int32_t currAPD = PresContext()->AppUnitsPerDevPixel();
7152   while (f && f != aOther) {
7153     docOffset += f->GetPosition();
7154     nsIFrame* parent = f->GetParent();
7155     if (parent) {
7156       f = parent;
7157     } else {
7158       nsPoint newOffset(0, 0);
7159       root = f;
7160       f = nsLayoutUtils::GetCrossDocParentFrame(f, &newOffset);
7161       int32_t newAPD = f ? f->PresContext()->AppUnitsPerDevPixel() : 0;
7162       if (!f || newAPD != currAPD) {
7163         // Convert docOffset to the right APD and add it to offset.
7164         offset += docOffset.ScaleToOtherAppUnits(currAPD, aAPD);
7165         docOffset.x = docOffset.y = 0;
7166       }
7167       currAPD = newAPD;
7168       docOffset += newOffset;
7169     }
7170   }
7171   if (f == aOther) {
7172     offset += docOffset.ScaleToOtherAppUnits(currAPD, aAPD);
7173   } else {
7174     // Looks like aOther wasn't an ancestor of |this|.  So now we have
7175     // the root-document-relative position of |this| in |offset|. Subtract the
7176     // root-document-relative position of |aOther| from |offset|.
7177     // This call won't try to recurse again because root is an ancestor of
7178     // aOther.
7179     nsPoint negOffset = aOther->GetOffsetToCrossDoc(root, aAPD);
7180     offset -= negOffset;
7181   }
7182 
7183   return offset;
7184 }
7185 
GetScreenRect() const7186 CSSIntRect nsIFrame::GetScreenRect() const {
7187   return CSSIntRect::FromAppUnitsToNearest(GetScreenRectInAppUnits());
7188 }
7189 
GetScreenRectInAppUnits() const7190 nsRect nsIFrame::GetScreenRectInAppUnits() const {
7191   nsPresContext* presContext = PresContext();
7192   nsIFrame* rootFrame = presContext->PresShell()->GetRootFrame();
7193   nsPoint rootScreenPos(0, 0);
7194   nsPoint rootFrameOffsetInParent(0, 0);
7195   nsIFrame* rootFrameParent = nsLayoutUtils::GetCrossDocParentFrame(
7196       rootFrame, &rootFrameOffsetInParent);
7197   if (rootFrameParent) {
7198     nsRect parentScreenRectAppUnits =
7199         rootFrameParent->GetScreenRectInAppUnits();
7200     nsPresContext* parentPresContext = rootFrameParent->PresContext();
7201     double parentScale = double(presContext->AppUnitsPerDevPixel()) /
7202                          parentPresContext->AppUnitsPerDevPixel();
7203     nsPoint rootPt =
7204         parentScreenRectAppUnits.TopLeft() + rootFrameOffsetInParent;
7205     rootScreenPos.x = NS_round(parentScale * rootPt.x);
7206     rootScreenPos.y = NS_round(parentScale * rootPt.y);
7207   } else {
7208     nsCOMPtr<nsIWidget> rootWidget;
7209     presContext->PresShell()->GetViewManager()->GetRootWidget(
7210         getter_AddRefs(rootWidget));
7211     if (rootWidget) {
7212       LayoutDeviceIntPoint rootDevPx = rootWidget->WidgetToScreenOffset();
7213       rootScreenPos.x = presContext->DevPixelsToAppUnits(rootDevPx.x);
7214       rootScreenPos.y = presContext->DevPixelsToAppUnits(rootDevPx.y);
7215     }
7216   }
7217 
7218   return nsRect(rootScreenPos + GetOffsetTo(rootFrame), GetSize());
7219 }
7220 
7221 // Returns the offset from this frame to the closest geometric parent that
7222 // has a view. Also returns the containing view or null in case of error
GetOffsetFromView(nsPoint & aOffset,nsView ** aView) const7223 void nsIFrame::GetOffsetFromView(nsPoint& aOffset, nsView** aView) const {
7224   MOZ_ASSERT(nullptr != aView, "null OUT parameter pointer");
7225   nsIFrame* frame = const_cast<nsIFrame*>(this);
7226 
7227   *aView = nullptr;
7228   aOffset.MoveTo(0, 0);
7229   do {
7230     aOffset += frame->GetPosition();
7231     frame = frame->GetParent();
7232   } while (frame && !frame->HasView());
7233 
7234   if (frame) {
7235     *aView = frame->GetView();
7236   }
7237 }
7238 
GetNearestWidget() const7239 nsIWidget* nsIFrame::GetNearestWidget() const {
7240   return GetClosestView()->GetNearestWidget(nullptr);
7241 }
7242 
GetNearestWidget(nsPoint & aOffset) const7243 nsIWidget* nsIFrame::GetNearestWidget(nsPoint& aOffset) const {
7244   nsPoint offsetToView;
7245   nsPoint offsetToWidget;
7246   nsIWidget* widget =
7247       GetClosestView(&offsetToView)->GetNearestWidget(&offsetToWidget);
7248   aOffset = offsetToView + offsetToWidget;
7249   return widget;
7250 }
7251 
GetTransformMatrix(ViewportType aViewportType,RelativeTo aStopAtAncestor,nsIFrame ** aOutAncestor,uint32_t aFlags) const7252 Matrix4x4Flagged nsIFrame::GetTransformMatrix(ViewportType aViewportType,
7253                                               RelativeTo aStopAtAncestor,
7254                                               nsIFrame** aOutAncestor,
7255                                               uint32_t aFlags) const {
7256   MOZ_ASSERT(aOutAncestor, "Need a place to put the ancestor!");
7257 
7258   /* If we're transformed, we want to hand back the combination
7259    * transform/translate matrix that will apply our current transform, then
7260    * shift us to our parent.
7261    */
7262   bool isTransformed = IsTransformed();
7263   const nsIFrame* zoomedContentRoot = nullptr;
7264   if (aStopAtAncestor.mViewportType == ViewportType::Visual) {
7265     zoomedContentRoot = ViewportUtils::IsZoomedContentRoot(this);
7266     if (zoomedContentRoot) {
7267       MOZ_ASSERT(aViewportType != ViewportType::Visual);
7268     }
7269   }
7270 
7271   if (isTransformed || zoomedContentRoot) {
7272     Matrix4x4 result;
7273     int32_t scaleFactor =
7274         ((aFlags & IN_CSS_UNITS) ? AppUnitsPerCSSPixel()
7275                                  : PresContext()->AppUnitsPerDevPixel());
7276 
7277     /* Compute the delta to the parent, which we need because we are converting
7278      * coordinates to our parent.
7279      */
7280     if (isTransformed) {
7281       NS_ASSERTION(nsLayoutUtils::GetCrossDocParentFrame(this),
7282                    "Cannot transform the viewport frame!");
7283 
7284       result = result * nsDisplayTransform::GetResultingTransformMatrix(
7285                             this, nsPoint(0, 0), scaleFactor,
7286                             nsDisplayTransform::INCLUDE_PERSPECTIVE |
7287                                 nsDisplayTransform::OFFSET_BY_ORIGIN);
7288     }
7289 
7290     // The offset from a zoomed content root to its parent (e.g. from
7291     // a canvas frame to a scroll frame) is in layout coordinates, so
7292     // apply it before applying any layout-to-visual transform.
7293     *aOutAncestor = nsLayoutUtils::GetCrossDocParentFrame(this);
7294     nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
7295     /* Combine the raw transform with a translation to our parent. */
7296     result.PostTranslate(NSAppUnitsToFloatPixels(delta.x, scaleFactor),
7297                          NSAppUnitsToFloatPixels(delta.y, scaleFactor), 0.0f);
7298 
7299     if (zoomedContentRoot) {
7300       Matrix4x4 layoutToVisual;
7301       ScrollableLayerGuid::ViewID targetScrollId =
7302           nsLayoutUtils::FindOrCreateIDFor(zoomedContentRoot->GetContent());
7303       if (aFlags & nsIFrame::IN_CSS_UNITS) {
7304         layoutToVisual =
7305             ViewportUtils::GetVisualToLayoutTransform(targetScrollId)
7306                 .Inverse()
7307                 .ToUnknownMatrix();
7308       } else {
7309         layoutToVisual =
7310             ViewportUtils::GetVisualToLayoutTransform<LayoutDevicePixel>(
7311                 targetScrollId)
7312                 .Inverse()
7313                 .ToUnknownMatrix();
7314       }
7315       result = result * layoutToVisual;
7316     }
7317 
7318     return result;
7319   }
7320 
7321   if (nsLayoutUtils::IsPopup(this) && IsListControlFrame()) {
7322     nsPresContext* presContext = PresContext();
7323     nsIFrame* docRootFrame = presContext->PresShell()->GetRootFrame();
7324 
7325     // Compute a matrix that transforms from the popup widget to the toplevel
7326     // widget. We use the widgets because they're the simplest and most
7327     // accurate approach --- this should work no matter how the widget position
7328     // was chosen.
7329     nsIWidget* widget = GetView()->GetWidget();
7330     nsPresContext* rootPresContext = PresContext()->GetRootPresContext();
7331     // Maybe the widget hasn't been created yet? Popups without widgets are
7332     // treated as regular frames. That should work since they'll be rendered
7333     // as part of the page if they're rendered at all.
7334     if (widget && rootPresContext) {
7335       nsIWidget* toplevel = rootPresContext->GetNearestWidget();
7336       if (toplevel) {
7337         LayoutDeviceIntRect screenBounds = widget->GetClientBounds();
7338         LayoutDeviceIntRect toplevelScreenBounds = toplevel->GetClientBounds();
7339         LayoutDeviceIntPoint translation =
7340             screenBounds.TopLeft() - toplevelScreenBounds.TopLeft();
7341 
7342         Matrix4x4 transformToTop;
7343         transformToTop._41 = translation.x;
7344         transformToTop._42 = translation.y;
7345 
7346         *aOutAncestor = docRootFrame;
7347         Matrix4x4 docRootTransformToTop =
7348             nsLayoutUtils::GetTransformToAncestor(RelativeTo{docRootFrame},
7349                                                   RelativeTo{nullptr})
7350                 .GetMatrix();
7351         if (docRootTransformToTop.IsSingular()) {
7352           NS_WARNING(
7353               "Containing document is invisible, we can't compute a valid "
7354               "transform");
7355         } else {
7356           docRootTransformToTop.Invert();
7357           return transformToTop * docRootTransformToTop;
7358         }
7359       }
7360     }
7361   }
7362 
7363   *aOutAncestor = nsLayoutUtils::GetCrossDocParentFrame(this);
7364 
7365   /* Otherwise, we're not transformed.  In that case, we'll walk up the frame
7366    * tree until we either hit the root frame or something that may be
7367    * transformed.  We'll then change coordinates into that frame, since we're
7368    * guaranteed that nothing in-between can be transformed.  First, however,
7369    * we have to check to see if we have a parent.  If not, we'll set the
7370    * outparam to null (indicating that there's nothing left) and will hand back
7371    * the identity matrix.
7372    */
7373   if (!*aOutAncestor) return Matrix4x4();
7374 
7375   /* Keep iterating while the frame can't possibly be transformed. */
7376   const nsIFrame* current = this;
7377   auto shouldStopAt = [](const nsIFrame* aCurrent, nsIFrame* aAncestor,
7378                          uint32_t aFlags) {
7379     return aAncestor->IsTransformed() || nsLayoutUtils::IsPopup(aAncestor) ||
7380            ViewportUtils::IsZoomedContentRoot(aAncestor) ||
7381            ((aFlags & STOP_AT_STACKING_CONTEXT_AND_DISPLAY_PORT) &&
7382             (aAncestor->IsStackingContext() ||
7383              nsLayoutUtils::FrameHasDisplayPort(aAncestor, aCurrent)));
7384   };
7385   while (*aOutAncestor != aStopAtAncestor.mFrame &&
7386          !shouldStopAt(current, *aOutAncestor, aFlags)) {
7387     /* If no parent, stop iterating.  Otherwise, update the ancestor. */
7388     nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(*aOutAncestor);
7389     if (!parent) break;
7390 
7391     current = *aOutAncestor;
7392     *aOutAncestor = parent;
7393   }
7394 
7395   NS_ASSERTION(*aOutAncestor, "Somehow ended up with a null ancestor...?");
7396 
7397   /* Translate from this frame to our ancestor, if it exists.  That's the
7398    * entire transform, so we're done.
7399    */
7400   nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
7401   int32_t scaleFactor =
7402       ((aFlags & IN_CSS_UNITS) ? AppUnitsPerCSSPixel()
7403                                : PresContext()->AppUnitsPerDevPixel());
7404   return Matrix4x4::Translation(NSAppUnitsToFloatPixels(delta.x, scaleFactor),
7405                                 NSAppUnitsToFloatPixels(delta.y, scaleFactor),
7406                                 0.0f);
7407 }
7408 
InvalidateRenderingObservers(nsIFrame * aDisplayRoot,nsIFrame * aFrame,bool aFrameChanged=true)7409 static void InvalidateRenderingObservers(nsIFrame* aDisplayRoot,
7410                                          nsIFrame* aFrame,
7411                                          bool aFrameChanged = true) {
7412   MOZ_ASSERT(aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame));
7413   SVGObserverUtils::InvalidateDirectRenderingObservers(aFrame);
7414   nsIFrame* parent = aFrame;
7415   while (parent != aDisplayRoot &&
7416          (parent = nsLayoutUtils::GetCrossDocParentFrame(parent)) &&
7417          !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
7418     SVGObserverUtils::InvalidateDirectRenderingObservers(parent);
7419   }
7420 
7421   if (!aFrameChanged) {
7422     return;
7423   }
7424 
7425   aFrame->MarkNeedsDisplayItemRebuild();
7426 }
7427 
SchedulePaintInternal(nsIFrame * aDisplayRoot,nsIFrame * aFrame,nsIFrame::PaintType aType=nsIFrame::PAINT_DEFAULT)7428 static void SchedulePaintInternal(
7429     nsIFrame* aDisplayRoot, nsIFrame* aFrame,
7430     nsIFrame::PaintType aType = nsIFrame::PAINT_DEFAULT) {
7431   MOZ_ASSERT(aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame));
7432   nsPresContext* pres = aDisplayRoot->PresContext()->GetRootPresContext();
7433 
7434   // No need to schedule a paint for an external document since they aren't
7435   // painted directly.
7436   if (!pres || (pres->Document() && pres->Document()->IsResourceDoc())) {
7437     return;
7438   }
7439   if (!pres->GetContainerWeak()) {
7440     NS_WARNING("Shouldn't call SchedulePaint in a detached pres context");
7441     return;
7442   }
7443 
7444   pres->PresShell()->ScheduleViewManagerFlush(
7445       aType == nsIFrame::PAINT_DELAYED_COMPRESS ? PaintType::DelayedCompress
7446                                                 : PaintType::Default);
7447 
7448   if (aType == nsIFrame::PAINT_DELAYED_COMPRESS) {
7449     return;
7450   }
7451 
7452   if (aType == nsIFrame::PAINT_DEFAULT) {
7453     aDisplayRoot->AddStateBits(NS_FRAME_UPDATE_LAYER_TREE);
7454   }
7455 }
7456 
InvalidateFrameInternal(nsIFrame * aFrame,bool aHasDisplayItem,bool aRebuildDisplayItems)7457 static void InvalidateFrameInternal(nsIFrame* aFrame, bool aHasDisplayItem,
7458                                     bool aRebuildDisplayItems) {
7459   if (aHasDisplayItem) {
7460     aFrame->AddStateBits(NS_FRAME_NEEDS_PAINT);
7461   }
7462 
7463   if (aRebuildDisplayItems) {
7464     aFrame->MarkNeedsDisplayItemRebuild();
7465   }
7466   SVGObserverUtils::InvalidateDirectRenderingObservers(aFrame);
7467   bool needsSchedulePaint = false;
7468   if (nsLayoutUtils::IsPopup(aFrame)) {
7469     needsSchedulePaint = true;
7470   } else {
7471     nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(aFrame);
7472     while (parent &&
7473            !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
7474       if (aHasDisplayItem && !parent->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
7475         parent->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
7476       }
7477       SVGObserverUtils::InvalidateDirectRenderingObservers(parent);
7478 
7479       // If we're inside a popup, then we need to make sure that we
7480       // call schedule paint so that the NS_FRAME_UPDATE_LAYER_TREE
7481       // flag gets added to the popup display root frame.
7482       if (nsLayoutUtils::IsPopup(parent)) {
7483         needsSchedulePaint = true;
7484         break;
7485       }
7486       parent = nsLayoutUtils::GetCrossDocParentFrame(parent);
7487     }
7488     if (!parent) {
7489       needsSchedulePaint = true;
7490     }
7491   }
7492   if (!aHasDisplayItem) {
7493     return;
7494   }
7495   if (needsSchedulePaint) {
7496     nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
7497     SchedulePaintInternal(displayRoot, aFrame);
7498   }
7499   if (aFrame->HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
7500     aFrame->RemoveProperty(nsIFrame::InvalidationRect());
7501     aFrame->RemoveStateBits(NS_FRAME_HAS_INVALID_RECT);
7502   }
7503 }
7504 
InvalidateFrameSubtree(bool aRebuildDisplayItems)7505 void nsIFrame::InvalidateFrameSubtree(bool aRebuildDisplayItems /* = true */) {
7506   InvalidateFrame(0, aRebuildDisplayItems);
7507 
7508   if (HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT)) {
7509     return;
7510   }
7511 
7512   AddStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT);
7513 
7514   for (const auto& childList : CrossDocChildLists()) {
7515     for (nsIFrame* child : childList.mList) {
7516       // Don't explicitly rebuild display items for our descendants,
7517       // since we should be marked and it implicitly includes all
7518       // descendants.
7519       child->InvalidateFrameSubtree(false);
7520     }
7521   }
7522 }
7523 
ClearInvalidationStateBits()7524 void nsIFrame::ClearInvalidationStateBits() {
7525   if (HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
7526     for (const auto& childList : CrossDocChildLists()) {
7527       for (nsIFrame* child : childList.mList) {
7528         child->ClearInvalidationStateBits();
7529       }
7530     }
7531   }
7532 
7533   RemoveStateBits(NS_FRAME_NEEDS_PAINT | NS_FRAME_DESCENDANT_NEEDS_PAINT |
7534                   NS_FRAME_ALL_DESCENDANTS_NEED_PAINT);
7535 }
7536 
InvalidateFrame(uint32_t aDisplayItemKey,bool aRebuildDisplayItems)7537 void nsIFrame::InvalidateFrame(uint32_t aDisplayItemKey,
7538                                bool aRebuildDisplayItems /* = true */) {
7539   bool hasDisplayItem =
7540       !aDisplayItemKey ||
7541       FrameLayerBuilder::HasRetainedDataFor(this, aDisplayItemKey);
7542   InvalidateFrameInternal(this, hasDisplayItem, aRebuildDisplayItems);
7543 }
7544 
InvalidateFrameWithRect(const nsRect & aRect,uint32_t aDisplayItemKey,bool aRebuildDisplayItems)7545 void nsIFrame::InvalidateFrameWithRect(const nsRect& aRect,
7546                                        uint32_t aDisplayItemKey,
7547                                        bool aRebuildDisplayItems /* = true */) {
7548   if (aRect.IsEmpty()) {
7549     return;
7550   }
7551   bool hasDisplayItem =
7552       !aDisplayItemKey ||
7553       FrameLayerBuilder::HasRetainedDataFor(this, aDisplayItemKey);
7554   bool alreadyInvalid = false;
7555   if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) {
7556     InvalidateFrameInternal(this, hasDisplayItem, aRebuildDisplayItems);
7557   } else {
7558     alreadyInvalid = true;
7559   }
7560 
7561   if (!hasDisplayItem) {
7562     return;
7563   }
7564 
7565   nsRect* rect;
7566   if (HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
7567     rect = GetProperty(InvalidationRect());
7568     MOZ_ASSERT(rect);
7569   } else {
7570     if (alreadyInvalid) {
7571       return;
7572     }
7573     rect = new nsRect();
7574     AddProperty(InvalidationRect(), rect);
7575     AddStateBits(NS_FRAME_HAS_INVALID_RECT);
7576   }
7577 
7578   *rect = rect->Union(aRect);
7579 }
7580 
7581 /*static*/
7582 uint8_t nsIFrame::sLayerIsPrerenderedDataKey;
7583 
DoesLayerHaveOutOfDateFrameMetrics(Layer * aLayer)7584 static bool DoesLayerHaveOutOfDateFrameMetrics(Layer* aLayer) {
7585   for (uint32_t i = 0; i < aLayer->GetScrollMetadataCount(); i++) {
7586     const FrameMetrics& metrics = aLayer->GetFrameMetrics(i);
7587     if (!metrics.IsScrollable()) {
7588       continue;
7589     }
7590     nsIScrollableFrame* scrollableFrame =
7591         nsLayoutUtils::FindScrollableFrameFor(metrics.GetScrollId());
7592     if (!scrollableFrame) {
7593       // This shouldn't happen, so let's do the safe thing and trigger a full
7594       // paint if it does.
7595       return true;
7596     }
7597     nsPoint scrollPosition = scrollableFrame->GetScrollPosition();
7598     if (metrics.GetScrollOffset() != CSSPoint::FromAppUnits(scrollPosition)) {
7599       return true;
7600     }
7601   }
7602   return false;
7603 }
7604 
DoesLayerOrAncestorsHaveOutOfDateFrameMetrics(Layer * aLayer)7605 static bool DoesLayerOrAncestorsHaveOutOfDateFrameMetrics(Layer* aLayer) {
7606   for (Layer* layer = aLayer; layer; layer = layer->GetParent()) {
7607     if (DoesLayerHaveOutOfDateFrameMetrics(layer)) {
7608       return true;
7609     }
7610   }
7611   return false;
7612 }
7613 
TryUpdateTransformOnly(Layer ** aLayerResult)7614 bool nsIFrame::TryUpdateTransformOnly(Layer** aLayerResult) {
7615   // If we move a transformed layer when we have a merged display
7616   // list, then it can end up intersecting other items for which
7617   // we don't have a defined ordering.
7618   // We could allow this if the display list is in the canonical
7619   // ordering (correctly sorted for all intersections), but we
7620   // don't have a way to check that yet.
7621   if (nsLayoutUtils::AreRetainedDisplayListsEnabled()) {
7622     return false;
7623   }
7624 
7625   Layer* layer = FrameLayerBuilder::GetDedicatedLayer(
7626       this, DisplayItemType::TYPE_TRANSFORM);
7627   if (!layer || !layer->HasUserData(LayerIsPrerenderedDataKey())) {
7628     // If this layer isn't prerendered or we clip composites to our OS
7629     // window, then we can't correctly optimize to an empty
7630     // transaction in general.
7631     return false;
7632   }
7633 
7634   if (DoesLayerOrAncestorsHaveOutOfDateFrameMetrics(layer)) {
7635     // At least one scroll frame that can affect the position of this layer
7636     // has changed its scroll offset since the last paint. Schedule a full
7637     // paint to make sure that this layer's transform and all the frame
7638     // metrics that affect it are in sync.
7639     return false;
7640   }
7641 
7642   gfx::Matrix4x4Flagged transform3d;
7643   if (!nsLayoutUtils::GetLayerTransformForFrame(this, &transform3d)) {
7644     // We're not able to compute a layer transform that we know would
7645     // be used at the next layers transaction, so we can't only update
7646     // the transform and will need to schedule an invalidating paint.
7647     return false;
7648   }
7649   gfx::Matrix transform;
7650   gfx::Matrix previousTransform;
7651   // FIXME/bug 796690 and 796705: in general, changes to 3D
7652   // transforms, or transform changes to properties other than
7653   // translation, may lead us to choose a different rendering
7654   // resolution for our layer.  So if the transform is 3D or has a
7655   // non-translation change, bail and schedule an invalidating paint.
7656   // (We can often do better than this, for example for scale-down
7657   // changes.)
7658   static const gfx::Float kError = 0.0001f;
7659   if (!transform3d.Is2D(&transform) ||
7660       !layer->GetBaseTransform().Is2D(&previousTransform) ||
7661       !gfx::FuzzyEqual(transform._11, previousTransform._11, kError) ||
7662       !gfx::FuzzyEqual(transform._22, previousTransform._22, kError) ||
7663       !gfx::FuzzyEqual(transform._21, previousTransform._21, kError) ||
7664       !gfx::FuzzyEqual(transform._12, previousTransform._12, kError)) {
7665     return false;
7666   }
7667   layer->SetBaseTransformForNextTransaction(transform3d.GetMatrix());
7668   *aLayerResult = layer;
7669   return true;
7670 }
7671 
IsInvalid(nsRect & aRect)7672 bool nsIFrame::IsInvalid(nsRect& aRect) {
7673   if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) {
7674     return false;
7675   }
7676 
7677   if (HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
7678     nsRect* rect = GetProperty(InvalidationRect());
7679     NS_ASSERTION(
7680         rect, "Must have an invalid rect if NS_FRAME_HAS_INVALID_RECT is set!");
7681     aRect = *rect;
7682   } else {
7683     aRect.SetEmpty();
7684   }
7685   return true;
7686 }
7687 
SchedulePaint(PaintType aType,bool aFrameChanged)7688 void nsIFrame::SchedulePaint(PaintType aType, bool aFrameChanged) {
7689   nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
7690   InvalidateRenderingObservers(displayRoot, this, aFrameChanged);
7691   SchedulePaintInternal(displayRoot, this, aType);
7692 }
7693 
SchedulePaintWithoutInvalidatingObservers(PaintType aType)7694 void nsIFrame::SchedulePaintWithoutInvalidatingObservers(PaintType aType) {
7695   nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
7696   SchedulePaintInternal(displayRoot, this, aType);
7697 }
7698 
InvalidateLayer(DisplayItemType aDisplayItemKey,const nsIntRect * aDamageRect,const nsRect * aFrameDamageRect,uint32_t aFlags)7699 Layer* nsIFrame::InvalidateLayer(DisplayItemType aDisplayItemKey,
7700                                  const nsIntRect* aDamageRect,
7701                                  const nsRect* aFrameDamageRect,
7702                                  uint32_t aFlags /* = 0 */) {
7703   NS_ASSERTION(aDisplayItemKey > DisplayItemType::TYPE_ZERO, "Need a key");
7704 
7705   Layer* layer = FrameLayerBuilder::GetDedicatedLayer(this, aDisplayItemKey);
7706 
7707   nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
7708   InvalidateRenderingObservers(displayRoot, this, false);
7709 
7710   // Check if frame supports WebRender's async update
7711   if ((aFlags & UPDATE_IS_ASYNC) &&
7712       WebRenderUserData::SupportsAsyncUpdate(this)) {
7713     // WebRender does not use layer, then return nullptr.
7714     return nullptr;
7715   }
7716 
7717   // If the layer is being updated asynchronously, and it's being forwarded
7718   // to a compositor, then we don't need to invalidate.
7719   if ((aFlags & UPDATE_IS_ASYNC) && layer && layer->SupportsAsyncUpdate()) {
7720     return layer;
7721   }
7722 
7723   if (!layer) {
7724     if (aFrameDamageRect && aFrameDamageRect->IsEmpty()) {
7725       return nullptr;
7726     }
7727 
7728     // Plugins can transition from not rendering anything to rendering,
7729     // and still only call this. So always invalidate, with specifying
7730     // the display item type just in case.
7731     //
7732     // In the bug 930056, dialer app startup but not shown on the
7733     // screen because sometimes we don't have any retainned data
7734     // for remote type displayitem and thus Repaint event is not
7735     // triggered. So, always invalidate here as well.
7736     DisplayItemType displayItemKey = aDisplayItemKey;
7737     if (aDisplayItemKey == DisplayItemType::TYPE_PLUGIN ||
7738         aDisplayItemKey == DisplayItemType::TYPE_REMOTE) {
7739       displayItemKey = DisplayItemType::TYPE_ZERO;
7740     }
7741 
7742     if (aFrameDamageRect) {
7743       InvalidateFrameWithRect(*aFrameDamageRect,
7744                               static_cast<uint32_t>(displayItemKey));
7745     } else {
7746       InvalidateFrame(static_cast<uint32_t>(displayItemKey));
7747     }
7748 
7749     return nullptr;
7750   }
7751 
7752   if (aDamageRect && aDamageRect->IsEmpty()) {
7753     return layer;
7754   }
7755 
7756   if (aDamageRect) {
7757     layer->AddInvalidRect(*aDamageRect);
7758   } else {
7759     layer->SetInvalidRectToVisibleRegion();
7760   }
7761 
7762   SchedulePaintInternal(displayRoot, this, PAINT_COMPOSITE_ONLY);
7763   return layer;
7764 }
7765 
ComputeEffectsRect(nsIFrame * aFrame,const nsRect & aOverflowRect,const nsSize & aNewSize)7766 static nsRect ComputeEffectsRect(nsIFrame* aFrame, const nsRect& aOverflowRect,
7767                                  const nsSize& aNewSize) {
7768   nsRect r = aOverflowRect;
7769 
7770   if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
7771     // For SVG frames, we only need to account for filters.
7772     // TODO: We could also take account of clipPath and mask to reduce the
7773     // visual overflow, but that's not essential.
7774     if (aFrame->StyleEffects()->HasFilters()) {
7775       SetOrUpdateRectValuedProperty(aFrame, nsIFrame::PreEffectsBBoxProperty(),
7776                                     r);
7777       r = nsSVGUtils::GetPostFilterVisualOverflowRect(aFrame, aOverflowRect);
7778     }
7779     return r;
7780   }
7781 
7782   // box-shadow
7783   r.UnionRect(r, nsLayoutUtils::GetBoxShadowRectForFrame(aFrame, aNewSize));
7784 
7785   // border-image-outset.
7786   // We need to include border-image-outset because it can cause the
7787   // border image to be drawn beyond the border box.
7788 
7789   // (1) It's important we not check whether there's a border-image
7790   //     since the style hint for a change in border image doesn't cause
7791   //     reflow, and that's probably more important than optimizing the
7792   //     overflow areas for the silly case of border-image-outset without
7793   //     border-image
7794   // (2) It's important that we not check whether the border-image
7795   //     is actually loaded, since that would require us to reflow when
7796   //     the image loads.
7797   const nsStyleBorder* styleBorder = aFrame->StyleBorder();
7798   nsMargin outsetMargin = styleBorder->GetImageOutset();
7799 
7800   if (outsetMargin != nsMargin(0, 0, 0, 0)) {
7801     nsRect outsetRect(nsPoint(0, 0), aNewSize);
7802     outsetRect.Inflate(outsetMargin);
7803     r.UnionRect(r, outsetRect);
7804   }
7805 
7806   // Note that we don't remove the outlineInnerRect if a frame loses outline
7807   // style. That would require an extra property lookup for every frame,
7808   // or a new frame state bit to track whether a property had been stored,
7809   // or something like that. It's not worth doing that here. At most it's
7810   // only one heap-allocated rect per frame and it will be cleaned up when
7811   // the frame dies.
7812 
7813   if (nsSVGIntegrationUtils::UsingOverflowAffectingEffects(aFrame)) {
7814     SetOrUpdateRectValuedProperty(aFrame, nsIFrame::PreEffectsBBoxProperty(),
7815                                   r);
7816     r = nsSVGIntegrationUtils::ComputePostEffectsVisualOverflowRect(aFrame, r);
7817   }
7818 
7819   return r;
7820 }
7821 
MovePositionBy(const nsPoint & aTranslation)7822 void nsIFrame::MovePositionBy(const nsPoint& aTranslation) {
7823   nsPoint position = GetNormalPosition() + aTranslation;
7824 
7825   const nsMargin* computedOffsets = nullptr;
7826   if (IsRelativelyPositioned()) {
7827     computedOffsets = GetProperty(nsIFrame::ComputedOffsetProperty());
7828   }
7829   ReflowInput::ApplyRelativePositioning(
7830       this, computedOffsets ? *computedOffsets : nsMargin(), &position);
7831   SetPosition(position);
7832 }
7833 
GetNormalRect() const7834 nsRect nsIFrame::GetNormalRect() const {
7835   // It might be faster to first check
7836   // StyleDisplay()->IsRelativelyPositionedStyle().
7837   nsPoint* normalPosition = GetProperty(NormalPositionProperty());
7838   if (normalPosition) {
7839     return nsRect(*normalPosition, GetSize());
7840   }
7841   return GetRect();
7842 }
7843 
GetPositionIgnoringScrolling() const7844 nsPoint nsIFrame::GetPositionIgnoringScrolling() const {
7845   return GetParent() ? GetParent()->GetPositionOfChildIgnoringScrolling(this)
7846                      : GetPosition();
7847 }
7848 
GetOverflowRect(nsOverflowType aType) const7849 nsRect nsIFrame::GetOverflowRect(nsOverflowType aType) const {
7850   MOZ_ASSERT(aType == eVisualOverflow || aType == eScrollableOverflow,
7851              "unexpected type");
7852 
7853   // Note that in some cases the overflow area might not have been
7854   // updated (yet) to reflect any outline set on the frame or the area
7855   // of child frames. That's OK because any reflow that updates these
7856   // areas will invalidate the appropriate area, so any (mis)uses of
7857   // this method will be fixed up.
7858 
7859   if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
7860     // there is an overflow rect, and it's not stored as deltas but as
7861     // a separately-allocated rect
7862     return GetOverflowAreasProperty()->Overflow(aType);
7863   }
7864 
7865   if (aType == eVisualOverflow && mOverflow.mType != NS_FRAME_OVERFLOW_NONE) {
7866     return GetVisualOverflowFromDeltas();
7867   }
7868 
7869   return nsRect(nsPoint(0, 0), GetSize());
7870 }
7871 
GetOverflowAreas() const7872 nsOverflowAreas nsIFrame::GetOverflowAreas() const {
7873   if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
7874     // there is an overflow rect, and it's not stored as deltas but as
7875     // a separately-allocated rect
7876     return *GetOverflowAreasProperty();
7877   }
7878 
7879   return nsOverflowAreas(GetVisualOverflowFromDeltas(),
7880                          nsRect(nsPoint(0, 0), GetSize()));
7881 }
7882 
GetOverflowAreasRelativeToSelf() const7883 nsOverflowAreas nsIFrame::GetOverflowAreasRelativeToSelf() const {
7884   if (IsTransformed()) {
7885     nsOverflowAreas* preTransformOverflows =
7886         GetProperty(PreTransformOverflowAreasProperty());
7887     if (preTransformOverflows) {
7888       return nsOverflowAreas(preTransformOverflows->VisualOverflow(),
7889                              preTransformOverflows->ScrollableOverflow());
7890     }
7891   }
7892   return nsOverflowAreas(GetVisualOverflowRect(), GetScrollableOverflowRect());
7893 }
7894 
GetScrollableOverflowRectRelativeToParent() const7895 nsRect nsIFrame::GetScrollableOverflowRectRelativeToParent() const {
7896   return GetScrollableOverflowRect() + mRect.TopLeft();
7897 }
7898 
GetVisualOverflowRectRelativeToParent() const7899 nsRect nsIFrame::GetVisualOverflowRectRelativeToParent() const {
7900   return GetVisualOverflowRect() + mRect.TopLeft();
7901 }
7902 
GetScrollableOverflowRectRelativeToSelf() const7903 nsRect nsIFrame::GetScrollableOverflowRectRelativeToSelf() const {
7904   if (IsTransformed()) {
7905     nsOverflowAreas* preTransformOverflows =
7906         GetProperty(PreTransformOverflowAreasProperty());
7907     if (preTransformOverflows)
7908       return preTransformOverflows->ScrollableOverflow();
7909   }
7910   return GetScrollableOverflowRect();
7911 }
7912 
GetVisualOverflowRectRelativeToSelf() const7913 nsRect nsIFrame::GetVisualOverflowRectRelativeToSelf() const {
7914   if (IsTransformed()) {
7915     nsOverflowAreas* preTransformOverflows =
7916         GetProperty(PreTransformOverflowAreasProperty());
7917     if (preTransformOverflows) return preTransformOverflows->VisualOverflow();
7918   }
7919   return GetVisualOverflowRect();
7920 }
7921 
GetPreEffectsVisualOverflowRect() const7922 nsRect nsIFrame::GetPreEffectsVisualOverflowRect() const {
7923   nsRect* r = GetProperty(nsIFrame::PreEffectsBBoxProperty());
7924   return r ? *r : GetVisualOverflowRectRelativeToSelf();
7925 }
7926 
UpdateOverflow()7927 bool nsIFrame::UpdateOverflow() {
7928   MOZ_ASSERT(FrameMaintainsOverflow(),
7929              "Non-display SVG do not maintain visual overflow rects");
7930 
7931   nsRect rect(nsPoint(0, 0), GetSize());
7932   nsOverflowAreas overflowAreas(rect, rect);
7933 
7934   if (!ComputeCustomOverflow(overflowAreas)) {
7935     // If updating overflow wasn't supported by this frame, then it should
7936     // have scheduled any necessary reflows. We can return false to say nothing
7937     // changed, and wait for reflow to correct it.
7938     return false;
7939   }
7940 
7941   UnionChildOverflow(overflowAreas);
7942 
7943   if (FinishAndStoreOverflow(overflowAreas, GetSize())) {
7944     nsView* view = GetView();
7945     if (view) {
7946       ReflowChildFlags flags = GetXULLayoutFlags();
7947       if (!(flags & ReflowChildFlags::NoSizeView)) {
7948         // Make sure the frame's view is properly sized.
7949         nsViewManager* vm = view->GetViewManager();
7950         vm->ResizeView(view, overflowAreas.VisualOverflow(), true);
7951       }
7952     }
7953 
7954     return true;
7955   }
7956 
7957   // Frames that combine their 3d transform with their ancestors
7958   // only compute a pre-transform overflow rect, and then contribute
7959   // to the normal overflow rect of the preserve-3d root. Always return
7960   // true here so that we propagate changes up to the root for final
7961   // calculation.
7962   return Combines3DTransformWithAncestors();
7963 }
7964 
7965 /* virtual */
ComputeCustomOverflow(nsOverflowAreas & aOverflowAreas)7966 bool nsIFrame::ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) {
7967   return true;
7968 }
7969 
7970 /* virtual */
UnionChildOverflow(nsOverflowAreas & aOverflowAreas)7971 void nsIFrame::UnionChildOverflow(nsOverflowAreas& aOverflowAreas) {
7972   if (!DoesClipChildren() &&
7973       !(IsXULCollapsed() && (IsXULBoxFrame() || ::IsXULBoxWrapped(this)))) {
7974     nsLayoutUtils::UnionChildOverflow(this, aOverflowAreas);
7975   }
7976 }
7977 
7978 // Define the MAX_FRAME_DEPTH to be the ContentSink's MAX_REFLOW_DEPTH plus
7979 // 4 for the frames above the document's frames:
7980 //  the Viewport, GFXScroll, ScrollPort, and Canvas
7981 #define MAX_FRAME_DEPTH (MAX_REFLOW_DEPTH + 4)
7982 
IsFrameTreeTooDeep(const ReflowInput & aReflowInput,ReflowOutput & aMetrics,nsReflowStatus & aStatus)7983 bool nsContainerFrame::IsFrameTreeTooDeep(const ReflowInput& aReflowInput,
7984                                           ReflowOutput& aMetrics,
7985                                           nsReflowStatus& aStatus) {
7986   if (aReflowInput.mReflowDepth > MAX_FRAME_DEPTH) {
7987     NS_WARNING("frame tree too deep; setting zero size and returning");
7988     AddStateBits(NS_FRAME_TOO_DEEP_IN_FRAME_TREE);
7989     ClearOverflowRects();
7990     aMetrics.ClearSize();
7991     aMetrics.SetBlockStartAscent(0);
7992     aMetrics.mCarriedOutBEndMargin.Zero();
7993     aMetrics.mOverflowAreas.Clear();
7994 
7995     aStatus.Reset();
7996     if (GetNextInFlow()) {
7997       // Reflow depth might vary between reflows, so we might have
7998       // successfully reflowed and split this frame before.  If so, we
7999       // shouldn't delete its continuations.
8000       aStatus.SetIncomplete();
8001     }
8002 
8003     return true;
8004   }
8005   RemoveStateBits(NS_FRAME_TOO_DEEP_IN_FRAME_TREE);
8006   return false;
8007 }
8008 
IsBlockWrapper() const8009 bool nsIFrame::IsBlockWrapper() const {
8010   auto pseudoType = Style()->GetPseudoType();
8011   return pseudoType == PseudoStyleType::mozBlockInsideInlineWrapper ||
8012          pseudoType == PseudoStyleType::buttonContent ||
8013          pseudoType == PseudoStyleType::cellContent ||
8014          pseudoType == PseudoStyleType::columnSpanWrapper;
8015 }
8016 
IsBlockFrameOrSubclass() const8017 bool nsIFrame::IsBlockFrameOrSubclass() const {
8018   const nsBlockFrame* thisAsBlock = do_QueryFrame(this);
8019   return !!thisAsBlock;
8020 }
8021 
GetNearestBlockContainer(nsIFrame * frame)8022 static nsIFrame* GetNearestBlockContainer(nsIFrame* frame) {
8023   // The block wrappers we use to wrap blocks inside inlines aren't
8024   // described in the CSS spec.  We need to make them not be containing
8025   // blocks.
8026   // Since the parent of such a block is either a normal block or
8027   // another such pseudo, this shouldn't cause anything bad to happen.
8028   // Also the anonymous blocks inside table cells are not containing blocks.
8029   //
8030   // If we ever start skipping table row groups from being containing blocks,
8031   // you need to remove the StickyScrollContainer hack referencing bug 1421660.
8032   while (frame->IsFrameOfType(nsIFrame::eLineParticipant) ||
8033          frame->IsBlockWrapper() ||
8034          // Table rows are not containing blocks either
8035          frame->IsTableRowFrame()) {
8036     frame = frame->GetParent();
8037     NS_ASSERTION(
8038         frame,
8039         "How come we got to the root frame without seeing a containing block?");
8040   }
8041   return frame;
8042 }
8043 
GetContainingBlock(uint32_t aFlags,const nsStyleDisplay * aStyleDisplay) const8044 nsIFrame* nsIFrame::GetContainingBlock(
8045     uint32_t aFlags, const nsStyleDisplay* aStyleDisplay) const {
8046   MOZ_ASSERT(aStyleDisplay == StyleDisplay());
8047   if (!GetParent()) {
8048     return nullptr;
8049   }
8050   // MathML frames might have absolute positioning style, but they would
8051   // still be in-flow.  So we have to check to make sure that the frame
8052   // is really out-of-flow too.
8053   nsIFrame* f;
8054   if (IsAbsolutelyPositioned(aStyleDisplay) &&
8055       (GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
8056     f = GetParent();  // the parent is always the containing block
8057   } else {
8058     f = GetNearestBlockContainer(GetParent());
8059   }
8060 
8061   if (aFlags & SKIP_SCROLLED_FRAME && f &&
8062       f->Style()->GetPseudoType() == PseudoStyleType::scrolledContent) {
8063     f = f->GetParent();
8064   }
8065   return f;
8066 }
8067 
8068 #ifdef DEBUG_FRAME_DUMP
8069 
ContentIndexInContainer(const nsIFrame * aFrame)8070 int32_t nsFrame::ContentIndexInContainer(const nsIFrame* aFrame) {
8071   int32_t result = -1;
8072 
8073   nsIContent* content = aFrame->GetContent();
8074   if (content) {
8075     nsIContent* parentContent = content->GetParent();
8076     if (parentContent) {
8077       result = parentContent->ComputeIndexOf(content);
8078     }
8079   }
8080 
8081   return result;
8082 }
8083 
8084 /**
8085  * List a frame tree to stderr. Meant to be called from gdb.
8086  */
DebugListFrameTree(nsIFrame * aFrame)8087 void DebugListFrameTree(nsIFrame* aFrame) { ((nsFrame*)aFrame)->List(stderr); }
8088 
ListTag() const8089 nsAutoCString nsIFrame::ListTag() const {
8090   nsAutoString tmp;
8091   GetFrameName(tmp);
8092 
8093   nsAutoCString tag;
8094   tag += NS_ConvertUTF16toUTF8(tmp);
8095   tag += nsPrintfCString("@%p", static_cast<const void*>(this));
8096   return tag;
8097 }
8098 
ConvertToString(const LogicalRect & aRect,const WritingMode aWM,ListFlags aFlags)8099 std::string nsIFrame::ConvertToString(const LogicalRect& aRect,
8100                                       const WritingMode aWM, ListFlags aFlags) {
8101   if (aFlags.contains(ListFlag::DisplayInCSSPixels)) {
8102     // Abuse CSSRect to store all LogicalRect's dimensions in CSS pixels.
8103     return ToString(mozilla::CSSRect(CSSPixel::FromAppUnits(aRect.IStart(aWM)),
8104                                      CSSPixel::FromAppUnits(aRect.BStart(aWM)),
8105                                      CSSPixel::FromAppUnits(aRect.ISize(aWM)),
8106                                      CSSPixel::FromAppUnits(aRect.BSize(aWM))));
8107   }
8108   return ToString(aRect);
8109 }
8110 
ConvertToString(const LogicalSize & aSize,const WritingMode aWM,ListFlags aFlags)8111 std::string nsIFrame::ConvertToString(const LogicalSize& aSize,
8112                                       const WritingMode aWM, ListFlags aFlags) {
8113   if (aFlags.contains(ListFlag::DisplayInCSSPixels)) {
8114     // Abuse CSSSize to store all LogicalSize's dimensions in CSS pixels.
8115     return ToString(CSSSize(CSSPixel::FromAppUnits(aSize.ISize(aWM)),
8116                             CSSPixel::FromAppUnits(aSize.BSize(aWM))));
8117   }
8118   return ToString(aSize);
8119 }
8120 
8121 // Debugging
ListGeneric(nsACString & aTo,const char * aPrefix,ListFlags aFlags) const8122 void nsIFrame::ListGeneric(nsACString& aTo, const char* aPrefix,
8123                            ListFlags aFlags) const {
8124   aTo += aPrefix;
8125   aTo += ListTag();
8126   if (HasView()) {
8127     aTo += nsPrintfCString(" [view=%p]", static_cast<void*>(GetView()));
8128   }
8129   if (GetParent()) {
8130     aTo += nsPrintfCString(" parent=%p", static_cast<void*>(GetParent()));
8131   }
8132   if (GetNextSibling()) {
8133     aTo += nsPrintfCString(" next=%p", static_cast<void*>(GetNextSibling()));
8134   }
8135   if (GetPrevContinuation()) {
8136     bool fluid = GetPrevInFlow() == GetPrevContinuation();
8137     aTo += nsPrintfCString(" prev-%s=%p", fluid ? "in-flow" : "continuation",
8138                            static_cast<void*>(GetPrevContinuation()));
8139   }
8140   if (GetNextContinuation()) {
8141     bool fluid = GetNextInFlow() == GetNextContinuation();
8142     aTo += nsPrintfCString(" next-%s=%p", fluid ? "in-flow" : "continuation",
8143                            static_cast<void*>(GetNextContinuation()));
8144   }
8145   void* IBsibling = GetProperty(IBSplitSibling());
8146   if (IBsibling) {
8147     aTo += nsPrintfCString(" IBSplitSibling=%p", IBsibling);
8148   }
8149   void* IBprevsibling = GetProperty(IBSplitPrevSibling());
8150   if (IBprevsibling) {
8151     aTo += nsPrintfCString(" IBSplitPrevSibling=%p", IBprevsibling);
8152   }
8153   if (nsLayoutUtils::FontSizeInflationEnabled(PresContext())) {
8154     if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT)) {
8155       aTo += nsPrintfCString(" FFR");
8156       if (nsFontInflationData* data =
8157               nsFontInflationData::FindFontInflationDataFor(this)) {
8158         aTo += nsPrintfCString(
8159             ",enabled=%s,UIS=%s", data->InflationEnabled() ? "yes" : "no",
8160             ConvertToString(data->UsableISize(), aFlags).c_str());
8161       }
8162     }
8163     if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER)) {
8164       aTo += nsPrintfCString(" FIC");
8165     }
8166     aTo += nsPrintfCString(" FI=%f", nsLayoutUtils::FontSizeInflationFor(this));
8167   }
8168   aTo += nsPrintfCString(" %s", ConvertToString(mRect, aFlags).c_str());
8169 
8170   mozilla::WritingMode wm = GetWritingMode();
8171   if (wm.IsVertical() || wm.IsBidiRTL()) {
8172     aTo +=
8173         nsPrintfCString(" wm=%s logical-size=(%s)", ToString(wm).c_str(),
8174                         ConvertToString(GetLogicalSize(), wm, aFlags).c_str());
8175   }
8176 
8177   nsIFrame* parent = GetParent();
8178   if (parent) {
8179     WritingMode pWM = parent->GetWritingMode();
8180     if (pWM.IsVertical() || pWM.IsBidiRTL()) {
8181       nsSize containerSize = parent->mRect.Size();
8182       LogicalRect lr(pWM, mRect, containerSize);
8183       aTo += nsPrintfCString(" parent-wm=%s cs=(%s) logical-rect=%s",
8184                              ToString(pWM).c_str(),
8185                              ConvertToString(containerSize, aFlags).c_str(),
8186                              ConvertToString(lr, pWM, aFlags).c_str());
8187     }
8188   }
8189   nsIFrame* f = const_cast<nsIFrame*>(this);
8190   if (f->HasOverflowAreas()) {
8191     nsRect vo = f->GetVisualOverflowRect();
8192     if (!vo.IsEqualEdges(mRect)) {
8193       aTo += nsPrintfCString(" vis-overflow=%s",
8194                              ConvertToString(vo, aFlags).c_str());
8195     }
8196     nsRect so = f->GetScrollableOverflowRect();
8197     if (!so.IsEqualEdges(mRect)) {
8198       aTo += nsPrintfCString(" scr-overflow=%s",
8199                              ConvertToString(so, aFlags).c_str());
8200     }
8201   }
8202   bool hasNormalPosition;
8203   nsPoint normalPosition = GetNormalPosition(&hasNormalPosition);
8204   if (hasNormalPosition) {
8205     aTo += nsPrintfCString(" normal-position=%s",
8206                            ConvertToString(normalPosition, aFlags).c_str());
8207   }
8208   if (0 != mState) {
8209     aTo += nsPrintfCString(" [state=%016llx]", (unsigned long long)mState);
8210   }
8211   if (HasProperty(BidiDataProperty())) {
8212     FrameBidiData bidi = GetBidiData();
8213     aTo += nsPrintfCString(" bidi(%d,%d,%d)", bidi.baseLevel,
8214                            bidi.embeddingLevel, bidi.precedingControl);
8215   }
8216   if (IsTransformed()) {
8217     aTo += nsPrintfCString(" transformed");
8218   }
8219   if (ChildrenHavePerspective()) {
8220     aTo += nsPrintfCString(" perspective");
8221   }
8222   if (Extend3DContext()) {
8223     aTo += nsPrintfCString(" extend-3d");
8224   }
8225   if (Combines3DTransformWithAncestors()) {
8226     aTo += nsPrintfCString(" combines-3d-transform-with-ancestors");
8227   }
8228   if (mContent) {
8229     aTo += nsPrintfCString(" [content=%p]", static_cast<void*>(mContent));
8230   }
8231   aTo += nsPrintfCString(" [cs=%p", static_cast<void*>(mComputedStyle));
8232   if (mComputedStyle) {
8233     auto pseudoType = mComputedStyle->GetPseudoType();
8234     aTo += ToString(pseudoType).c_str();
8235   }
8236   aTo += "]";
8237 }
8238 
List(FILE * out,const char * aPrefix,ListFlags aFlags) const8239 void nsIFrame::List(FILE* out, const char* aPrefix, ListFlags aFlags) const {
8240   nsCString str;
8241   ListGeneric(str, aPrefix, aFlags);
8242   fprintf_stderr(out, "%s\n", str.get());
8243 }
8244 
ListMatchedRules(FILE * out,const char * aPrefix) const8245 void nsIFrame::ListMatchedRules(FILE* out, const char* aPrefix) const {
8246   nsTArray<const RawServoStyleRule*> rawRuleList;
8247   Servo_ComputedValues_GetStyleRuleList(mComputedStyle, &rawRuleList);
8248   for (const RawServoStyleRule* rawRule : rawRuleList) {
8249     nsString ruleText;
8250     Servo_StyleRule_GetCssText(rawRule, &ruleText);
8251     fprintf_stderr(out, "%s%s\n", aPrefix,
8252                    NS_ConvertUTF16toUTF8(ruleText).get());
8253   }
8254 }
8255 
ListWithMatchedRules(FILE * out,const char * aPrefix) const8256 void nsIFrame::ListWithMatchedRules(FILE* out, const char* aPrefix) const {
8257   fprintf_stderr(out, "%s%s\n", aPrefix, ListTag().get());
8258 
8259   nsCString rulePrefix;
8260   rulePrefix += aPrefix;
8261   rulePrefix += "    ";
8262   ListMatchedRules(out, rulePrefix.get());
8263 }
8264 
GetFrameName(nsAString & aResult) const8265 nsresult nsFrame::GetFrameName(nsAString& aResult) const {
8266   return MakeFrameName(NS_LITERAL_STRING("Frame"), aResult);
8267 }
8268 
MakeFrameName(const nsAString & aType,nsAString & aResult) const8269 nsresult nsFrame::MakeFrameName(const nsAString& aType,
8270                                 nsAString& aResult) const {
8271   aResult = aType;
8272   if (mContent && !mContent->IsText()) {
8273     nsAutoString buf;
8274     mContent->NodeInfo()->NameAtom()->ToString(buf);
8275     if (IsSubDocumentFrame()) {
8276       nsAutoString src;
8277       mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src);
8278       buf.AppendLiteral(" src=");
8279       buf.Append(src);
8280     }
8281     aResult.Append('(');
8282     aResult.Append(buf);
8283     aResult.Append(')');
8284   }
8285   aResult.Append('(');
8286   aResult.AppendInt(ContentIndexInContainer(this));
8287   aResult.Append(')');
8288   return NS_OK;
8289 }
8290 
DumpFrameTree() const8291 void nsIFrame::DumpFrameTree() const {
8292   PresShell()->GetRootFrame()->List(stderr);
8293 }
8294 
DumpFrameTreeInCSSPixels() const8295 void nsIFrame::DumpFrameTreeInCSSPixels() const {
8296   PresShell()->GetRootFrame()->List(stderr, "", ListFlag::DisplayInCSSPixels);
8297 }
8298 
DumpFrameTreeLimited() const8299 void nsIFrame::DumpFrameTreeLimited() const { List(stderr); }
DumpFrameTreeLimitedInCSSPixels() const8300 void nsIFrame::DumpFrameTreeLimitedInCSSPixels() const {
8301   List(stderr, "", ListFlag::DisplayInCSSPixels);
8302 }
8303 
8304 #endif
8305 
IsVisibleForPainting()8306 bool nsIFrame::IsVisibleForPainting() { return StyleVisibility()->IsVisible(); }
8307 
IsVisibleOrCollapsedForPainting()8308 bool nsIFrame::IsVisibleOrCollapsedForPainting() {
8309   return StyleVisibility()->IsVisibleOrCollapsed();
8310 }
8311 
8312 /* virtual */
IsEmpty()8313 bool nsIFrame::IsEmpty() { return false; }
8314 
CachedIsEmpty()8315 bool nsIFrame::CachedIsEmpty() {
8316   MOZ_ASSERT(!(GetStateBits() & NS_FRAME_IS_DIRTY),
8317              "Must only be called on reflowed lines");
8318   return IsEmpty();
8319 }
8320 
8321 /* virtual */
IsSelfEmpty()8322 bool nsIFrame::IsSelfEmpty() { return false; }
8323 
GetSelectionController(nsPresContext * aPresContext,nsISelectionController ** aSelCon)8324 nsresult nsIFrame::GetSelectionController(nsPresContext* aPresContext,
8325                                           nsISelectionController** aSelCon) {
8326   if (!aPresContext || !aSelCon) return NS_ERROR_INVALID_ARG;
8327 
8328   nsIFrame* frame = this;
8329   while (frame && (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION)) {
8330     nsITextControlFrame* tcf = do_QueryFrame(frame);
8331     if (tcf) {
8332       return tcf->GetOwnedSelectionController(aSelCon);
8333     }
8334     frame = frame->GetParent();
8335   }
8336 
8337   *aSelCon = do_AddRef(aPresContext->PresShell()).take();
8338   return NS_OK;
8339 }
8340 
GetFrameSelection()8341 already_AddRefed<nsFrameSelection> nsIFrame::GetFrameSelection() {
8342   RefPtr<nsFrameSelection> fs =
8343       const_cast<nsFrameSelection*>(GetConstFrameSelection());
8344   return fs.forget();
8345 }
8346 
GetConstFrameSelection() const8347 const nsFrameSelection* nsIFrame::GetConstFrameSelection() const {
8348   nsIFrame* frame = const_cast<nsIFrame*>(this);
8349   while (frame && (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION)) {
8350     nsITextControlFrame* tcf = do_QueryFrame(frame);
8351     if (tcf) {
8352       return tcf->GetOwnedFrameSelection();
8353     }
8354     frame = frame->GetParent();
8355   }
8356 
8357   return PresShell()->ConstFrameSelection();
8358 }
8359 
IsFrameSelected() const8360 bool nsIFrame::IsFrameSelected() const {
8361   NS_ASSERTION(!GetContent() || GetContent()->IsMaybeSelected(),
8362                "use the public IsSelected() instead");
8363   return GetContent()->IsSelected(0, GetContent()->GetChildCount());
8364 }
8365 
GetPointFromOffset(int32_t inOffset,nsPoint * outPoint)8366 nsresult nsIFrame::GetPointFromOffset(int32_t inOffset, nsPoint* outPoint) {
8367   MOZ_ASSERT(outPoint != nullptr, "Null parameter");
8368   nsRect contentRect = GetContentRectRelativeToSelf();
8369   nsPoint pt = contentRect.TopLeft();
8370   if (mContent) {
8371     nsIContent* newContent = mContent->GetParent();
8372     if (newContent) {
8373       int32_t newOffset = newContent->ComputeIndexOf(mContent);
8374 
8375       // Find the direction of the frame from the EmbeddingLevelProperty,
8376       // which is the resolved bidi level set in
8377       // nsBidiPresUtils::ResolveParagraph (odd levels = right-to-left).
8378       // If the embedding level isn't set, just use the CSS direction
8379       // property.
8380       bool hasBidiData;
8381       FrameBidiData bidiData = GetProperty(BidiDataProperty(), &hasBidiData);
8382       bool isRTL = hasBidiData
8383                        ? IS_LEVEL_RTL(bidiData.embeddingLevel)
8384                        : StyleVisibility()->mDirection == StyleDirection::Rtl;
8385       if ((!isRTL && inOffset > newOffset) ||
8386           (isRTL && inOffset <= newOffset)) {
8387         pt = contentRect.TopRight();
8388       }
8389     }
8390   }
8391   *outPoint = pt;
8392   return NS_OK;
8393 }
8394 
GetCharacterRectsInRange(int32_t aInOffset,int32_t aLength,nsTArray<nsRect> & aOutRect)8395 nsresult nsIFrame::GetCharacterRectsInRange(int32_t aInOffset, int32_t aLength,
8396                                             nsTArray<nsRect>& aOutRect) {
8397   /* no text */
8398   return NS_ERROR_FAILURE;
8399 }
8400 
GetChildFrameContainingOffset(int32_t inContentOffset,bool inHint,int32_t * outFrameContentOffset,nsIFrame ** outChildFrame)8401 nsresult nsIFrame::GetChildFrameContainingOffset(int32_t inContentOffset,
8402                                                  bool inHint,
8403                                                  int32_t* outFrameContentOffset,
8404                                                  nsIFrame** outChildFrame) {
8405   MOZ_ASSERT(outChildFrame && outFrameContentOffset, "Null parameter");
8406   *outFrameContentOffset = (int32_t)inHint;
8407   // the best frame to reflect any given offset would be a visible frame if
8408   // possible i.e. we are looking for a valid frame to place the blinking caret
8409   nsRect rect = GetRect();
8410   if (!rect.width || !rect.height) {
8411     // if we have a 0 width or height then lets look for another frame that
8412     // possibly has the same content.  If we have no frames in flow then just
8413     // let us return 'this' frame
8414     nsIFrame* nextFlow = GetNextInFlow();
8415     if (nextFlow)
8416       return nextFlow->GetChildFrameContainingOffset(
8417           inContentOffset, inHint, outFrameContentOffset, outChildFrame);
8418   }
8419   *outChildFrame = this;
8420   return NS_OK;
8421 }
8422 
8423 //
8424 // What I've pieced together about this routine:
8425 // Starting with a block frame (from which a line frame can be gotten)
8426 // and a line number, drill down and get the first/last selectable
8427 // frame on that line, depending on aPos->mDirection.
8428 // aOutSideLimit != 0 means ignore aLineStart, instead work from
8429 // the end (if > 0) or beginning (if < 0).
8430 //
GetNextPrevLineFromeBlockFrame(nsPresContext * aPresContext,nsPeekOffsetStruct * aPos,nsIFrame * aBlockFrame,int32_t aLineStart,int8_t aOutSideLimit)8431 nsresult nsFrame::GetNextPrevLineFromeBlockFrame(nsPresContext* aPresContext,
8432                                                  nsPeekOffsetStruct* aPos,
8433                                                  nsIFrame* aBlockFrame,
8434                                                  int32_t aLineStart,
8435                                                  int8_t aOutSideLimit) {
8436   // magic numbers aLineStart will be -1 for end of block 0 will be start of
8437   // block
8438   if (!aBlockFrame || !aPos) return NS_ERROR_NULL_POINTER;
8439 
8440   aPos->mResultFrame = nullptr;
8441   aPos->mResultContent = nullptr;
8442   aPos->mAttach = aPos->mDirection == eDirNext ? CARET_ASSOCIATE_AFTER
8443                                                : CARET_ASSOCIATE_BEFORE;
8444 
8445   const nsAutoLineIterator it = aBlockFrame->GetLineIterator();
8446   if (!it) {
8447     return NS_ERROR_FAILURE;
8448   }
8449   int32_t searchingLine = aLineStart;
8450   int32_t countLines = it->GetNumLines();
8451   if (aOutSideLimit > 0)  // start at end
8452     searchingLine = countLines;
8453   else if (aOutSideLimit < 0)  // start at beginning
8454     searchingLine = -1;        //"next" will be 0
8455   else if ((aPos->mDirection == eDirPrevious && searchingLine == 0) ||
8456            (aPos->mDirection == eDirNext &&
8457             searchingLine >= (countLines - 1))) {
8458     // we need to jump to new block frame.
8459     return NS_ERROR_FAILURE;
8460   }
8461   int32_t lineFrameCount;
8462   nsIFrame* resultFrame = nullptr;
8463   nsIFrame* farStoppingFrame = nullptr;  // we keep searching until we find a
8464                                          // "this" frame then we go to next line
8465   nsIFrame* nearStoppingFrame = nullptr;  // if we are backing up from edge,
8466                                           // stop here
8467   nsIFrame* firstFrame;
8468   nsIFrame* lastFrame;
8469   nsRect rect;
8470   bool isBeforeFirstFrame, isAfterLastFrame;
8471   bool found = false;
8472 
8473   nsresult result = NS_OK;
8474   while (!found) {
8475     if (aPos->mDirection == eDirPrevious)
8476       searchingLine--;
8477     else
8478       searchingLine++;
8479     if ((aPos->mDirection == eDirPrevious && searchingLine < 0) ||
8480         (aPos->mDirection == eDirNext && searchingLine >= countLines)) {
8481       // we need to jump to new block frame.
8482       return NS_ERROR_FAILURE;
8483     }
8484     result = it->GetLine(searchingLine, &firstFrame, &lineFrameCount, rect);
8485     if (!lineFrameCount) continue;
8486     if (NS_SUCCEEDED(result)) {
8487       lastFrame = firstFrame;
8488       for (; lineFrameCount > 1; lineFrameCount--) {
8489         // result = lastFrame->GetNextSibling(&lastFrame, searchingLine);
8490         result = it->GetNextSiblingOnLine(lastFrame, searchingLine);
8491         if (NS_FAILED(result) || !lastFrame) {
8492           NS_ERROR("GetLine promised more frames than could be found");
8493           return NS_ERROR_FAILURE;
8494         }
8495       }
8496       GetLastLeaf(aPresContext, &lastFrame);
8497 
8498       if (aPos->mDirection == eDirNext) {
8499         nearStoppingFrame = firstFrame;
8500         farStoppingFrame = lastFrame;
8501       } else {
8502         nearStoppingFrame = lastFrame;
8503         farStoppingFrame = firstFrame;
8504       }
8505       nsPoint offset;
8506       nsView* view;  // used for call of get offset from view
8507       aBlockFrame->GetOffsetFromView(offset, &view);
8508       nsPoint newDesiredPos =
8509           aPos->mDesiredCaretPos -
8510           offset;  // get desired position into blockframe coords
8511       result = it->FindFrameAt(searchingLine, newDesiredPos, &resultFrame,
8512                                &isBeforeFirstFrame, &isAfterLastFrame);
8513       if (NS_FAILED(result)) continue;
8514     }
8515 
8516     if (NS_SUCCEEDED(result) && resultFrame) {
8517       // check to see if this is ANOTHER blockframe inside the other one if so
8518       // then call into its lines
8519       nsAutoLineIterator newIt = resultFrame->GetLineIterator();
8520       if (newIt) {
8521         aPos->mResultFrame = resultFrame;
8522         return NS_OK;
8523       }
8524       // resultFrame is not a block frame
8525       result = NS_ERROR_FAILURE;
8526 
8527       nsCOMPtr<nsIFrameEnumerator> frameTraversal;
8528       result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
8529                                     aPresContext, resultFrame, ePostOrder,
8530                                     false,  // aVisual
8531                                     aPos->mScrollViewStop,
8532                                     false,  // aFollowOOFs
8533                                     false   // aSkipPopupChecks
8534       );
8535       if (NS_FAILED(result)) return result;
8536 
8537       auto FoundValidFrame = [aPos](const ContentOffsets& aOffsets,
8538                                     const nsIFrame* aFrame) {
8539         if (!aOffsets.content) {
8540           return false;
8541         }
8542         if (!aFrame->IsSelectable(nullptr)) {
8543           return false;
8544         }
8545         if (aPos->mForceEditableRegion && !aOffsets.content->IsEditable()) {
8546           return false;
8547         }
8548         return true;
8549       };
8550 
8551       nsIFrame* storeOldResultFrame = resultFrame;
8552       while (!found) {
8553         nsPoint point;
8554         nsRect tempRect = resultFrame->GetRect();
8555         nsPoint offset;
8556         nsView* view;  // used for call of get offset from view
8557         resultFrame->GetOffsetFromView(offset, &view);
8558         if (!view) {
8559           return NS_ERROR_FAILURE;
8560         }
8561         if (resultFrame->GetWritingMode().IsVertical()) {
8562           point.y = aPos->mDesiredCaretPos.y;
8563           point.x = tempRect.width + offset.x;
8564         } else {
8565           point.y = tempRect.height + offset.y;
8566           point.x = aPos->mDesiredCaretPos.x;
8567         }
8568 
8569         // special check. if we allow non-text selection then we can allow a hit
8570         // location to fall before a table. otherwise there is no way to get and
8571         // click signal to fall before a table (it being a line iterator itself)
8572         mozilla::PresShell* presShell = aPresContext->GetPresShell();
8573         if (!presShell) {
8574           return NS_ERROR_FAILURE;
8575         }
8576         int16_t isEditor = presShell->GetSelectionFlags();
8577         isEditor = isEditor == nsISelectionDisplay::DISPLAY_ALL;
8578         if (isEditor) {
8579           if (resultFrame->IsTableWrapperFrame()) {
8580             if (((point.x - offset.x + tempRect.x) < 0) ||
8581                 ((point.x - offset.x + tempRect.x) >
8582                  tempRect.width))  // off left/right side
8583             {
8584               nsIContent* content = resultFrame->GetContent();
8585               if (content) {
8586                 nsIContent* parent = content->GetParent();
8587                 if (parent) {
8588                   aPos->mResultContent = parent;
8589                   aPos->mContentOffset = parent->ComputeIndexOf(content);
8590                   aPos->mAttach = CARET_ASSOCIATE_BEFORE;
8591                   if ((point.x - offset.x + tempRect.x) > tempRect.width) {
8592                     aPos->mContentOffset++;  // go to end of this frame
8593                     aPos->mAttach = CARET_ASSOCIATE_AFTER;
8594                   }
8595                   // result frame is the result frames parent.
8596                   aPos->mResultFrame = resultFrame->GetParent();
8597                   return NS_POSITION_BEFORE_TABLE;
8598                 }
8599               }
8600             }
8601           }
8602         }
8603 
8604         if (!resultFrame->HasView()) {
8605           nsView* view;
8606           nsPoint offset;
8607           resultFrame->GetOffsetFromView(offset, &view);
8608           ContentOffsets offsets =
8609               resultFrame->GetContentOffsetsFromPoint(point - offset);
8610           aPos->mResultContent = offsets.content;
8611           aPos->mContentOffset = offsets.offset;
8612           aPos->mAttach = offsets.associate;
8613           if (FoundValidFrame(offsets, resultFrame)) {
8614             found = true;
8615             break;
8616           }
8617         }
8618 
8619         if (aPos->mDirection == eDirPrevious &&
8620             (resultFrame == farStoppingFrame))
8621           break;
8622         if (aPos->mDirection == eDirNext && (resultFrame == nearStoppingFrame))
8623           break;
8624         // always try previous on THAT line if that fails go the other way
8625         frameTraversal->Prev();
8626         resultFrame = frameTraversal->CurrentItem();
8627         if (!resultFrame) return NS_ERROR_FAILURE;
8628       }
8629 
8630       if (!found) {
8631         resultFrame = storeOldResultFrame;
8632 
8633         result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
8634                                       aPresContext, resultFrame, eLeaf,
8635                                       false,  // aVisual
8636                                       aPos->mScrollViewStop,
8637                                       false,  // aFollowOOFs
8638                                       false   // aSkipPopupChecks
8639         );
8640       }
8641       while (!found) {
8642         nsPoint point = aPos->mDesiredCaretPos;
8643         nsView* view;
8644         nsPoint offset;
8645         resultFrame->GetOffsetFromView(offset, &view);
8646         ContentOffsets offsets =
8647             resultFrame->GetContentOffsetsFromPoint(point - offset);
8648         aPos->mResultContent = offsets.content;
8649         aPos->mContentOffset = offsets.offset;
8650         aPos->mAttach = offsets.associate;
8651         if (FoundValidFrame(offsets, resultFrame)) {
8652           found = true;
8653           if (resultFrame == farStoppingFrame)
8654             aPos->mAttach = CARET_ASSOCIATE_BEFORE;
8655           else
8656             aPos->mAttach = CARET_ASSOCIATE_AFTER;
8657           break;
8658         }
8659         if (aPos->mDirection == eDirPrevious &&
8660             (resultFrame == nearStoppingFrame))
8661           break;
8662         if (aPos->mDirection == eDirNext && (resultFrame == farStoppingFrame))
8663           break;
8664         // previous didnt work now we try "next"
8665         frameTraversal->Next();
8666         nsIFrame* tempFrame = frameTraversal->CurrentItem();
8667         if (!tempFrame) break;
8668         resultFrame = tempFrame;
8669       }
8670       aPos->mResultFrame = resultFrame;
8671     } else {
8672       // we need to jump to new block frame.
8673       aPos->mAmount = eSelectLine;
8674       aPos->mStartOffset = 0;
8675       aPos->mAttach = aPos->mDirection == eDirNext ? CARET_ASSOCIATE_BEFORE
8676                                                    : CARET_ASSOCIATE_AFTER;
8677       if (aPos->mDirection == eDirPrevious)
8678         aPos->mStartOffset = -1;  // start from end
8679       return aBlockFrame->PeekOffset(aPos);
8680     }
8681   }
8682   return NS_OK;
8683 }
8684 
GetExtremeCaretPosition(bool aStart)8685 nsIFrame::CaretPosition nsIFrame::GetExtremeCaretPosition(bool aStart) {
8686   CaretPosition result;
8687 
8688   FrameTarget targetFrame = DrillDownToSelectionFrame(this, !aStart, 0);
8689   FrameContentRange range = GetRangeForFrame(targetFrame.frame);
8690   result.mResultContent = range.content;
8691   result.mContentOffset = aStart ? range.start : range.end;
8692   return result;
8693 }
8694 
8695 // If this is a preformatted text frame, see if it ends with a newline
FindLineBreakInText(nsIFrame * aFrame,nsDirection aDirection)8696 static nsContentAndOffset FindLineBreakInText(nsIFrame* aFrame,
8697                                               nsDirection aDirection) {
8698   nsContentAndOffset result;
8699 
8700   if (aFrame->IsGeneratedContentFrame() ||
8701       !aFrame->HasSignificantTerminalNewline()) {
8702     return result;
8703   }
8704 
8705   int32_t startOffset, endOffset;
8706   aFrame->GetOffsets(startOffset, endOffset);
8707   result.mContent = aFrame->GetContent();
8708   result.mOffset = endOffset - (aDirection == eDirPrevious ? 0 : 1);
8709   return result;
8710 }
8711 
8712 // Find the first (or last) descendant of the given frame
8713 // which is either a block-level frame or a BRFrame, or some other kind of break
8714 // which stops the line.
FindLineBreakingFrame(nsIFrame * aFrame,nsDirection aDirection)8715 static nsContentAndOffset FindLineBreakingFrame(nsIFrame* aFrame,
8716                                                 nsDirection aDirection) {
8717   nsContentAndOffset result;
8718 
8719   if (aFrame->IsGeneratedContentFrame()) {
8720     return result;
8721   }
8722 
8723   // Treat form controls as inline leaves
8724   // XXX we really need a way to determine whether a frame is inline-level
8725   if (static_cast<nsIFormControlFrame*>(do_QueryFrame(aFrame))) {
8726     return result;
8727   }
8728 
8729   // Check the frame itself
8730   // Fall through block-in-inline split frames because their mContent is
8731   // the content of the inline frames they were created from. The
8732   // first/last child of such frames is the real block frame we're
8733   // looking for.
8734   if ((aFrame->IsBlockOutside() &&
8735        !(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) ||
8736       aFrame->IsBrFrame()) {
8737     nsIContent* content = aFrame->GetContent();
8738     result.mContent = content->GetParent();
8739     // In some cases (bug 310589, bug 370174) we end up here with a null
8740     // content. This probably shouldn't ever happen, but since it sometimes
8741     // does, we want to avoid crashing here.
8742     NS_ASSERTION(result.mContent, "Unexpected orphan content");
8743     if (result.mContent)
8744       result.mOffset = result.mContent->ComputeIndexOf(content) +
8745                        (aDirection == eDirPrevious ? 1 : 0);
8746     return result;
8747   }
8748 
8749   result = FindLineBreakInText(aFrame, aDirection);
8750   if (result.mContent) {
8751     return result;
8752   }
8753 
8754   // Iterate over children and call ourselves recursively
8755   if (aDirection == eDirPrevious) {
8756     nsIFrame* child =
8757         aFrame->GetChildList(nsIFrame::kPrincipalList).LastChild();
8758     while (child && !result.mContent) {
8759       result = FindLineBreakingFrame(child, aDirection);
8760       child = child->GetPrevSibling();
8761     }
8762   } else {  // eDirNext
8763     nsIFrame* child = aFrame->PrincipalChildList().FirstChild();
8764     while (child && !result.mContent) {
8765       result = FindLineBreakingFrame(child, aDirection);
8766       child = child->GetNextSibling();
8767     }
8768   }
8769   return result;
8770 }
8771 
PeekOffsetParagraph(nsPeekOffsetStruct * aPos)8772 nsresult nsIFrame::PeekOffsetParagraph(nsPeekOffsetStruct* aPos) {
8773   nsIFrame* frame = this;
8774   nsContentAndOffset blockFrameOrBR;
8775   blockFrameOrBR.mContent = nullptr;
8776   bool reachedBlockAncestor = frame->IsBlockOutside();
8777 
8778   auto traverse = [&aPos](nsIFrame* current) {
8779     return aPos->mDirection == eDirPrevious ? current->GetPrevSibling()
8780                                             : current->GetNextSibling();
8781   };
8782 
8783   // Go through containing frames until reaching a block frame.
8784   // In each step, search the previous (or next) siblings for the closest
8785   // "stop frame" (a block frame or a BRFrame).
8786   // If found, set it to be the selection boundary and abort.
8787   while (!reachedBlockAncestor) {
8788     nsIFrame* parent = frame->GetParent();
8789     // Treat a frame associated with the root content as if it were a block
8790     // frame.
8791     if (!frame->mContent || !frame->mContent->GetParent()) {
8792       reachedBlockAncestor = true;
8793       break;
8794     }
8795 
8796     if (aPos->mDirection == eDirNext) {
8797       // Try to find our own line-break before looking at our siblings.
8798       blockFrameOrBR = FindLineBreakInText(frame, eDirNext);
8799     }
8800 
8801     nsIFrame* sibling = traverse(frame);
8802     while (sibling && !blockFrameOrBR.mContent) {
8803       blockFrameOrBR = FindLineBreakingFrame(sibling, aPos->mDirection);
8804       sibling = traverse(sibling);
8805     }
8806     if (blockFrameOrBR.mContent) {
8807       aPos->mResultContent = blockFrameOrBR.mContent;
8808       aPos->mContentOffset = blockFrameOrBR.mOffset;
8809       break;
8810     }
8811     frame = parent;
8812     reachedBlockAncestor = frame && frame->IsBlockOutside();
8813   }
8814 
8815   if (reachedBlockAncestor) {  // no "stop frame" found
8816     aPos->mResultContent = frame->GetContent();
8817     if (aPos->mDirection == eDirPrevious) {
8818       aPos->mContentOffset = 0;
8819     } else if (aPos->mResultContent) {
8820       aPos->mContentOffset = aPos->mResultContent->GetChildCount();
8821     }
8822   }
8823   return NS_OK;
8824 }
8825 
8826 // Determine movement direction relative to frame
IsMovingInFrameDirection(nsIFrame * frame,nsDirection aDirection,bool aVisual)8827 static bool IsMovingInFrameDirection(nsIFrame* frame, nsDirection aDirection,
8828                                      bool aVisual) {
8829   bool isReverseDirection = aVisual && IsReversedDirectionFrame(frame);
8830   return aDirection == (isReverseDirection ? eDirPrevious : eDirNext);
8831 }
8832 
PeekOffset(nsPeekOffsetStruct * aPos)8833 nsresult nsIFrame::PeekOffset(nsPeekOffsetStruct* aPos) {
8834   if (!aPos) return NS_ERROR_NULL_POINTER;
8835   nsresult result = NS_ERROR_FAILURE;
8836 
8837   if (mState & NS_FRAME_IS_DIRTY) return NS_ERROR_UNEXPECTED;
8838 
8839   // Translate content offset to be relative to frame
8840   FrameContentRange range = GetRangeForFrame(this);
8841   int32_t offset = aPos->mStartOffset - range.start;
8842   nsIFrame* current = this;
8843 
8844   switch (aPos->mAmount) {
8845     case eSelectCharacter:
8846     case eSelectCluster: {
8847       bool eatingNonRenderableWS = false;
8848       nsIFrame::FrameSearchResult peekSearchState = CONTINUE;
8849       bool jumpedLine = false;
8850       bool movedOverNonSelectableText = false;
8851 
8852       while (peekSearchState != FOUND) {
8853         bool movingInFrameDirection =
8854             IsMovingInFrameDirection(current, aPos->mDirection, aPos->mVisual);
8855 
8856         if (eatingNonRenderableWS) {
8857           peekSearchState =
8858               current->PeekOffsetNoAmount(movingInFrameDirection, &offset);
8859         } else {
8860           PeekOffsetCharacterOptions options;
8861           options.mRespectClusters = aPos->mAmount == eSelectCluster;
8862           peekSearchState = current->PeekOffsetCharacter(movingInFrameDirection,
8863                                                          &offset, options);
8864         }
8865 
8866         movedOverNonSelectableText |=
8867             (peekSearchState == CONTINUE_UNSELECTABLE);
8868 
8869         if (peekSearchState != FOUND) {
8870           bool movedOverNonSelectable = false;
8871           result = current->GetFrameFromDirection(
8872               *aPos, &current, &offset, &jumpedLine, &movedOverNonSelectable);
8873           if (NS_FAILED(result)) return result;
8874 
8875           // If we jumped lines, it's as if we found a character, but we still
8876           // need to eat non-renderable content on the new line.
8877           if (jumpedLine) eatingNonRenderableWS = true;
8878 
8879           // Remember if we moved over non-selectable text when finding another
8880           // frame.
8881           movedOverNonSelectableText |= movedOverNonSelectable;
8882         }
8883 
8884         // Found frame, but because we moved over non selectable text we want
8885         // the offset to be at the frame edge. Note that if we are extending the
8886         // selection, this doesn't matter.
8887         if (peekSearchState == FOUND && movedOverNonSelectableText &&
8888             !aPos->mExtend) {
8889           int32_t start, end;
8890           current->GetOffsets(start, end);
8891           offset = aPos->mDirection == eDirNext ? 0 : end - start;
8892         }
8893       }
8894 
8895       // Set outputs
8896       range = GetRangeForFrame(current);
8897       aPos->mResultFrame = current;
8898       aPos->mResultContent = range.content;
8899       // Output offset is relative to content, not frame
8900       aPos->mContentOffset = offset < 0 ? range.end : range.start + offset;
8901       // If we're dealing with a text frame and moving backward positions us at
8902       // the end of that line, decrease the offset by one to make sure that
8903       // we're placed before the linefeed character on the previous line.
8904       if (offset < 0 && jumpedLine && aPos->mDirection == eDirPrevious &&
8905           current->HasSignificantTerminalNewline()) {
8906         --aPos->mContentOffset;
8907       }
8908 
8909       break;
8910     }
8911     case eSelectWordNoSpace:
8912       // eSelectWordNoSpace means that we should not be eating any whitespace
8913       // when moving to the adjacent word.  This means that we should set aPos->
8914       // mWordMovementType to eEndWord if we're moving forwards, and to
8915       // eStartWord if we're moving backwards.
8916       if (aPos->mDirection == eDirPrevious) {
8917         aPos->mWordMovementType = eStartWord;
8918       } else {
8919         aPos->mWordMovementType = eEndWord;
8920       }
8921       // Intentionally fall through the eSelectWord case.
8922       [[fallthrough]];
8923     case eSelectWord: {
8924       // wordSelectEatSpace means "are we looking for a boundary between
8925       // whitespace and non-whitespace (in the direction we're moving in)". It
8926       // is true when moving forward and looking for a beginning of a word, or
8927       // when moving backwards and looking for an end of a word.
8928       bool wordSelectEatSpace;
8929       if (aPos->mWordMovementType != eDefaultBehavior) {
8930         // aPos->mWordMovementType possible values:
8931         //       eEndWord: eat the space if we're moving backwards
8932         //       eStartWord: eat the space if we're moving forwards
8933         wordSelectEatSpace = ((aPos->mWordMovementType == eEndWord) ==
8934                               (aPos->mDirection == eDirPrevious));
8935       } else {
8936         // Use the hidden preference which is based on operating system
8937         // behavior. This pref only affects whether moving forward by word
8938         // should go to the end of this word or start of the next word. When
8939         // going backwards, the start of the word is always used, on every
8940         // operating system.
8941         wordSelectEatSpace =
8942             aPos->mDirection == eDirNext &&
8943             Preferences::GetBool("layout.word_select.eat_space_to_next_word");
8944       }
8945 
8946       // mSawBeforeType means "we already saw characters of the type
8947       // before the boundary we're looking for". Examples:
8948       // 1. If we're moving forward, looking for a word beginning (i.e. a
8949       //    boundary between whitespace and non-whitespace), then
8950       //    eatingWS==true means "we already saw some whitespace".
8951       // 2. If we're moving backward, looking for a word beginning (i.e. a
8952       //    boundary between non-whitespace and whitespace), then
8953       //     eatingWS==true means "we already saw some non-whitespace".
8954       PeekWordState state;
8955       int32_t offsetAdjustment = 0;
8956       bool done = false;
8957       while (!done) {
8958         bool movingInFrameDirection =
8959             IsMovingInFrameDirection(current, aPos->mDirection, aPos->mVisual);
8960 
8961         done =
8962             current->PeekOffsetWord(movingInFrameDirection, wordSelectEatSpace,
8963                                     aPos->mIsKeyboardSelect, &offset, &state,
8964                                     aPos->mTrimSpaces) == FOUND;
8965 
8966         if (!done) {
8967           nsIFrame* nextFrame;
8968           int32_t nextFrameOffset;
8969           bool jumpedLine, movedOverNonSelectableText;
8970           result = current->GetFrameFromDirection(*aPos, &nextFrame,
8971                                                   &nextFrameOffset, &jumpedLine,
8972                                                   &movedOverNonSelectableText);
8973           // We can't jump lines if we're looking for whitespace following
8974           // non-whitespace, and we already encountered non-whitespace.
8975           if (NS_FAILED(result) ||
8976               (jumpedLine && !wordSelectEatSpace && state.mSawBeforeType)) {
8977             done = true;
8978             // If we've crossed the line boundary, check to make sure that we
8979             // have not consumed a trailing newline as whitesapce if it's
8980             // significant.
8981             if (jumpedLine && wordSelectEatSpace &&
8982                 current->HasSignificantTerminalNewline()) {
8983               offsetAdjustment = -1;
8984             }
8985           } else {
8986             if (jumpedLine) {
8987               state.mContext.Truncate();
8988             }
8989             current = nextFrame;
8990             offset = nextFrameOffset;
8991             // Jumping a line is equivalent to encountering whitespace
8992             if (wordSelectEatSpace && jumpedLine) state.SetSawBeforeType();
8993           }
8994         }
8995       }
8996 
8997       // Set outputs
8998       range = GetRangeForFrame(current);
8999       aPos->mResultFrame = current;
9000       aPos->mResultContent = range.content;
9001       // Output offset is relative to content, not frame
9002       aPos->mContentOffset =
9003           (offset < 0 ? range.end : range.start + offset) + offsetAdjustment;
9004       break;
9005     }
9006     case eSelectLine: {
9007       nsAutoLineIterator iter;
9008       nsIFrame* blockFrame = this;
9009 
9010       while (NS_FAILED(result)) {
9011         int32_t thisLine = nsFrame::GetLineNumber(
9012             blockFrame, aPos->mScrollViewStop, &blockFrame);
9013         if (thisLine < 0) return NS_ERROR_FAILURE;
9014         iter = blockFrame->GetLineIterator();
9015         NS_ASSERTION(iter, "GetLineNumber() succeeded but no block frame?");
9016 
9017         int edgeCase = 0;  // no edge case. this should look at thisLine
9018 
9019         bool doneLooping = false;  // tells us when no more block frames hit.
9020         // this part will find a frame or a block frame. if it's a block frame
9021         // it will "drill down" to find a viable frame or it will return an
9022         // error.
9023         nsIFrame* lastFrame = this;
9024         do {
9025           result = nsFrame::GetNextPrevLineFromeBlockFrame(
9026               PresContext(), aPos, blockFrame, thisLine,
9027               edgeCase);  // start from thisLine
9028 
9029           // we came back to same spot! keep going
9030           if (NS_SUCCEEDED(result) &&
9031               (!aPos->mResultFrame || aPos->mResultFrame == lastFrame)) {
9032             aPos->mResultFrame = nullptr;
9033             if (aPos->mDirection == eDirPrevious)
9034               thisLine--;
9035             else
9036               thisLine++;
9037           } else                 // if failure or success with different frame.
9038             doneLooping = true;  // do not continue with while loop
9039 
9040           lastFrame = aPos->mResultFrame;  // set last frame
9041 
9042           // make sure block element is not the same as the one we had before
9043           if (NS_SUCCEEDED(result) && aPos->mResultFrame &&
9044               blockFrame != aPos->mResultFrame) {
9045             /* SPECIAL CHECK FOR TABLE NAVIGATION
9046                tables need to navigate also and the frame that supports it is
9047                nsTableRowGroupFrame which is INSIDE nsTableWrapperFrame.
9048                If we have stumbled onto an nsTableWrapperFrame we need to drill
9049                into nsTableRowGroup if we hit a header or footer that's ok just
9050                go into them.
9051              */
9052             bool searchTableBool = false;
9053             if (aPos->mResultFrame->IsTableWrapperFrame() ||
9054                 aPos->mResultFrame->IsTableCellFrame()) {
9055               nsIFrame* frame =
9056                   aPos->mResultFrame->PrincipalChildList().FirstChild();
9057               // got the table frame now
9058               // ok time to drill down to find iterator
9059               while (frame) {
9060                 iter = frame->GetLineIterator();
9061                 if (iter) {
9062                   aPos->mResultFrame = frame;
9063                   searchTableBool = true;
9064                   result = NS_OK;
9065                   break;  // while(frame)
9066                 }
9067                 result = NS_ERROR_FAILURE;
9068                 frame = frame->PrincipalChildList().FirstChild();
9069               }
9070             }
9071 
9072             if (!searchTableBool) {
9073               iter = aPos->mResultFrame->GetLineIterator();
9074               result = iter ? NS_OK : NS_ERROR_FAILURE;
9075             }
9076 
9077             // we've struck another block element!
9078             if (NS_SUCCEEDED(result) && iter) {
9079               doneLooping = false;
9080               if (aPos->mDirection == eDirPrevious)
9081                 edgeCase = 1;  // far edge, search from end backwards
9082               else
9083                 edgeCase = -1;  // near edge search from beginning onwards
9084               thisLine = 0;     // this line means nothing now.
9085               // everything else means something so keep looking "inside" the
9086               // block
9087               blockFrame = aPos->mResultFrame;
9088             } else {
9089               // THIS is to mean that everything is ok to the containing while
9090               // loop
9091               result = NS_OK;
9092               break;
9093             }
9094           }
9095         } while (!doneLooping);
9096       }
9097       return result;
9098     }
9099 
9100     case eSelectParagraph:
9101       return PeekOffsetParagraph(aPos);
9102 
9103     case eSelectBeginLine:
9104     case eSelectEndLine: {
9105       // Adjusted so that the caret can't get confused when content changes
9106       nsIFrame* blockFrame = AdjustFrameForSelectionStyles(this);
9107       int32_t thisLine = nsFrame::GetLineNumber(
9108           blockFrame, aPos->mScrollViewStop, &blockFrame);
9109       if (thisLine < 0) return NS_ERROR_FAILURE;
9110       nsAutoLineIterator it = blockFrame->GetLineIterator();
9111       NS_ASSERTION(it, "GetLineNumber() succeeded but no block frame?");
9112 
9113       int32_t lineFrameCount;
9114       nsIFrame* firstFrame;
9115       nsRect usedRect;
9116       nsIFrame* baseFrame = nullptr;
9117       bool endOfLine = (eSelectEndLine == aPos->mAmount);
9118 
9119       if (aPos->mVisual && PresContext()->BidiEnabled()) {
9120         bool lineIsRTL = it->GetDirection();
9121         bool isReordered;
9122         nsIFrame* lastFrame;
9123         result =
9124             it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame);
9125         baseFrame = endOfLine ? lastFrame : firstFrame;
9126         if (baseFrame) {
9127           bool frameIsRTL =
9128               (nsBidiPresUtils::FrameDirection(baseFrame) == NSBIDI_RTL);
9129           // If the direction of the frame on the edge is opposite to
9130           // that of the line, we'll need to drill down to its opposite
9131           // end, so reverse endOfLine.
9132           if (frameIsRTL != lineIsRTL) {
9133             endOfLine = !endOfLine;
9134           }
9135         }
9136       } else {
9137         it->GetLine(thisLine, &firstFrame, &lineFrameCount, usedRect);
9138 
9139         nsIFrame* frame = firstFrame;
9140         bool lastFrameWasEditable = false;
9141         for (int32_t count = lineFrameCount; count;
9142              --count, frame = frame->GetNextSibling()) {
9143           if (frame->IsGeneratedContentFrame()) {
9144             continue;
9145           }
9146           // When jumping to the end of the line with the "end" key,
9147           // try to skip over brFrames
9148           if (endOfLine && lineFrameCount > 1 && frame->IsBrFrame() &&
9149               lastFrameWasEditable == frame->GetContent()->IsEditable()) {
9150             continue;
9151           }
9152           lastFrameWasEditable =
9153               frame->GetContent() && frame->GetContent()->IsEditable();
9154           baseFrame = frame;
9155           if (!endOfLine) {
9156             break;
9157           }
9158         }
9159       }
9160       if (!baseFrame) return NS_ERROR_FAILURE;
9161       FrameTarget targetFrame =
9162           DrillDownToSelectionFrame(baseFrame, endOfLine, 0);
9163       FrameContentRange range = GetRangeForFrame(targetFrame.frame);
9164       aPos->mResultContent = range.content;
9165       aPos->mContentOffset = endOfLine ? range.end : range.start;
9166       if (endOfLine && targetFrame.frame->HasSignificantTerminalNewline()) {
9167         // Do not position the caret after the terminating newline if we're
9168         // trying to move to the end of line (see bug 596506)
9169         --aPos->mContentOffset;
9170       }
9171       aPos->mResultFrame = targetFrame.frame;
9172       aPos->mAttach = aPos->mContentOffset == range.start
9173                           ? CARET_ASSOCIATE_AFTER
9174                           : CARET_ASSOCIATE_BEFORE;
9175       if (!range.content) return NS_ERROR_FAILURE;
9176       return NS_OK;
9177     }
9178 
9179     default: {
9180       NS_ASSERTION(false, "Invalid amount");
9181       return NS_ERROR_FAILURE;
9182     }
9183   }
9184   return NS_OK;
9185 }
9186 
PeekOffsetNoAmount(bool aForward,int32_t * aOffset)9187 nsIFrame::FrameSearchResult nsIFrame::PeekOffsetNoAmount(bool aForward,
9188                                                          int32_t* aOffset) {
9189   NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range");
9190   // Sure, we can stop right here.
9191   return FOUND;
9192 }
9193 
PeekOffsetCharacter(bool aForward,int32_t * aOffset,PeekOffsetCharacterOptions aOptions)9194 nsIFrame::FrameSearchResult nsIFrame::PeekOffsetCharacter(
9195     bool aForward, int32_t* aOffset, PeekOffsetCharacterOptions aOptions) {
9196   NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range");
9197   int32_t startOffset = *aOffset;
9198   // A negative offset means "end of frame", which in our case means offset 1.
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: skip to the other side and we're done.
9203     *aOffset = 1 - startOffset;
9204     return FOUND;
9205   }
9206   return CONTINUE;
9207 }
9208 
PeekOffsetWord(bool aForward,bool aWordSelectEatSpace,bool aIsKeyboardSelect,int32_t * aOffset,PeekWordState * aState,bool)9209 nsIFrame::FrameSearchResult nsIFrame::PeekOffsetWord(
9210     bool aForward, bool aWordSelectEatSpace, bool aIsKeyboardSelect,
9211     int32_t* aOffset, PeekWordState* aState, bool /*aTrimSpaces*/) {
9212   NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range");
9213   int32_t startOffset = *aOffset;
9214   // This isn't text, so truncate the context
9215   aState->mContext.Truncate();
9216   if (startOffset < 0) startOffset = 1;
9217   if (aForward == (startOffset == 0)) {
9218     // We're before the frame and moving forward, or after it and moving
9219     // backwards. If we're looking for non-whitespace, we found it (without
9220     // skipping this frame).
9221     if (!aState->mAtStart) {
9222       if (aState->mLastCharWasPunctuation) {
9223         // We're not punctuation, so this is a punctuation boundary.
9224         if (BreakWordBetweenPunctuation(aState, aForward, false, false,
9225                                         aIsKeyboardSelect))
9226           return FOUND;
9227       } else {
9228         // This is not a punctuation boundary.
9229         if (aWordSelectEatSpace && aState->mSawBeforeType) return FOUND;
9230       }
9231     }
9232     // Otherwise skip to the other side and note that we encountered
9233     // non-whitespace.
9234     *aOffset = 1 - startOffset;
9235     aState->Update(false,  // not punctuation
9236                    false   // not whitespace
9237     );
9238     if (!aWordSelectEatSpace) aState->SetSawBeforeType();
9239   }
9240   return CONTINUE;
9241 }
9242 
9243 // static
BreakWordBetweenPunctuation(const PeekWordState * aState,bool aForward,bool aPunctAfter,bool aWhitespaceAfter,bool aIsKeyboardSelect)9244 bool nsIFrame::BreakWordBetweenPunctuation(const PeekWordState* aState,
9245                                            bool aForward, bool aPunctAfter,
9246                                            bool aWhitespaceAfter,
9247                                            bool aIsKeyboardSelect) {
9248   NS_ASSERTION(aPunctAfter != aState->mLastCharWasPunctuation,
9249                "Call this only at punctuation boundaries");
9250   if (aState->mLastCharWasWhitespace) {
9251     // We always stop between whitespace and punctuation
9252     return true;
9253   }
9254   if (!Preferences::GetBool("layout.word_select.stop_at_punctuation")) {
9255     // When this pref is false, we never stop at a punctuation boundary unless
9256     // it's followed by whitespace (in the relevant direction).
9257     return aWhitespaceAfter;
9258   }
9259   if (!aIsKeyboardSelect) {
9260     // mouse caret movement (e.g. word selection) always stops at every
9261     // punctuation boundary
9262     return true;
9263   }
9264   bool afterPunct = aForward ? aState->mLastCharWasPunctuation : aPunctAfter;
9265   if (!afterPunct) {
9266     // keyboard caret movement only stops after punctuation (in content order)
9267     return false;
9268   }
9269   // Stop only if we've seen some non-punctuation since the last whitespace;
9270   // don't stop after punctuation that follows whitespace.
9271   return aState->mSeenNonPunctuationSinceWhitespace;
9272 }
9273 
CheckVisibility(nsPresContext *,int32_t,int32_t,bool,bool *,bool *)9274 nsresult nsIFrame::CheckVisibility(nsPresContext*, int32_t, int32_t, bool,
9275                                    bool*, bool*) {
9276   return NS_ERROR_NOT_IMPLEMENTED;
9277 }
9278 
GetLineNumber(nsIFrame * aFrame,bool aLockScroll,nsIFrame ** aContainingBlock)9279 int32_t nsFrame::GetLineNumber(nsIFrame* aFrame, bool aLockScroll,
9280                                nsIFrame** aContainingBlock) {
9281   NS_ASSERTION(aFrame, "null aFrame");
9282   nsIFrame* blockFrame = aFrame;
9283   nsIFrame* thisBlock;
9284   nsAutoLineIterator it;
9285   nsresult result = NS_ERROR_FAILURE;
9286   while (NS_FAILED(result) && blockFrame) {
9287     thisBlock = blockFrame;
9288     if (thisBlock->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
9289       // if we are searching for a frame that is not in flow we will not find
9290       // it. we must instead look for its placeholder
9291       if (thisBlock->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
9292         // abspos continuations don't have placeholders, get the fif
9293         thisBlock = thisBlock->FirstInFlow();
9294       }
9295       thisBlock = thisBlock->GetPlaceholderFrame();
9296       if (!thisBlock) return -1;
9297     }
9298     blockFrame = thisBlock->GetParent();
9299     result = NS_OK;
9300     if (blockFrame) {
9301       if (aLockScroll && blockFrame->IsScrollFrame()) return -1;
9302       it = blockFrame->GetLineIterator();
9303       if (!it) result = NS_ERROR_FAILURE;
9304     }
9305   }
9306   if (!blockFrame || !it) return -1;
9307 
9308   if (aContainingBlock) *aContainingBlock = blockFrame;
9309   return it->FindLineContaining(thisBlock);
9310 }
9311 
GetFrameFromDirection(nsDirection aDirection,bool aVisual,bool aJumpLines,bool aScrollViewStop,bool aForceEditableRegion,nsIFrame ** aOutFrame,int32_t * aOutOffset,bool * aOutJumpedLine,bool * aOutMovedOverNonSelectableText)9312 nsresult nsIFrame::GetFrameFromDirection(
9313     nsDirection aDirection, bool aVisual, bool aJumpLines, bool aScrollViewStop,
9314     bool aForceEditableRegion, nsIFrame** aOutFrame, int32_t* aOutOffset,
9315     bool* aOutJumpedLine, bool* aOutMovedOverNonSelectableText) {
9316   nsresult result;
9317 
9318   if (!aOutFrame || !aOutOffset || !aOutJumpedLine)
9319     return NS_ERROR_NULL_POINTER;
9320 
9321   nsPresContext* presContext = PresContext();
9322   *aOutFrame = nullptr;
9323   *aOutOffset = 0;
9324   *aOutJumpedLine = false;
9325   *aOutMovedOverNonSelectableText = false;
9326 
9327   // Find the prev/next selectable frame
9328   bool selectable = false;
9329   nsIFrame* traversedFrame = this;
9330   while (!selectable) {
9331     nsIFrame* blockFrame;
9332 
9333     int32_t thisLine =
9334         nsFrame::GetLineNumber(traversedFrame, aScrollViewStop, &blockFrame);
9335     if (thisLine < 0) return NS_ERROR_FAILURE;
9336 
9337     nsAutoLineIterator it = blockFrame->GetLineIterator();
9338     NS_ASSERTION(it, "GetLineNumber() succeeded but no block frame?");
9339 
9340     bool atLineEdge;
9341     nsIFrame* firstFrame;
9342     nsIFrame* lastFrame;
9343     if (aVisual && presContext->BidiEnabled()) {
9344       bool lineIsRTL = it->GetDirection();
9345       bool isReordered;
9346       result =
9347           it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame);
9348       nsIFrame** framePtr =
9349           aDirection == eDirPrevious ? &firstFrame : &lastFrame;
9350       if (*framePtr) {
9351         bool frameIsRTL =
9352             (nsBidiPresUtils::FrameDirection(*framePtr) == NSBIDI_RTL);
9353         if ((frameIsRTL == lineIsRTL) == (aDirection == eDirPrevious)) {
9354           nsFrame::GetFirstLeaf(presContext, framePtr);
9355         } else {
9356           nsFrame::GetLastLeaf(presContext, framePtr);
9357         }
9358         atLineEdge = *framePtr == traversedFrame;
9359       } else {
9360         atLineEdge = true;
9361       }
9362     } else {
9363       nsRect nonUsedRect;
9364       int32_t lineFrameCount;
9365       result = it->GetLine(thisLine, &firstFrame, &lineFrameCount, nonUsedRect);
9366       if (NS_FAILED(result)) return result;
9367 
9368       if (aDirection == eDirPrevious) {
9369         nsFrame::GetFirstLeaf(presContext, &firstFrame);
9370         atLineEdge = firstFrame == traversedFrame;
9371       } else {  // eDirNext
9372         lastFrame = firstFrame;
9373         for (; lineFrameCount > 1; lineFrameCount--) {
9374           result = it->GetNextSiblingOnLine(lastFrame, thisLine);
9375           if (NS_FAILED(result) || !lastFrame) {
9376             NS_ERROR("should not be reached nsFrame");
9377             return NS_ERROR_FAILURE;
9378           }
9379         }
9380         nsFrame::GetLastLeaf(presContext, &lastFrame);
9381         atLineEdge = lastFrame == traversedFrame;
9382       }
9383     }
9384 
9385     if (atLineEdge) {
9386       *aOutJumpedLine = true;
9387       if (!aJumpLines)
9388         return NS_ERROR_FAILURE;  // we are done. cannot jump lines
9389     }
9390 
9391     nsCOMPtr<nsIFrameEnumerator> frameTraversal;
9392     result = NS_NewFrameTraversal(
9393         getter_AddRefs(frameTraversal), presContext, traversedFrame, eLeaf,
9394         aVisual && presContext->BidiEnabled(), aScrollViewStop,
9395         true,  // aFollowOOFs
9396         false  // aSkipPopupChecks
9397     );
9398     if (NS_FAILED(result)) return result;
9399 
9400     if (aDirection == eDirNext)
9401       frameTraversal->Next();
9402     else
9403       frameTraversal->Prev();
9404 
9405     traversedFrame = frameTraversal->CurrentItem();
9406     if (!traversedFrame) {
9407       return NS_ERROR_FAILURE;
9408     }
9409 
9410     auto IsSelectable = [aForceEditableRegion](const nsIFrame* aFrame) {
9411       if (!aFrame->IsSelectable(nullptr)) {
9412         return false;
9413       }
9414       return !aForceEditableRegion || aFrame->GetContent()->IsEditable();
9415     };
9416 
9417     // Skip brFrames, but only we can select something before hitting the end of
9418     // the line or a non-selectable region.
9419     if (atLineEdge && aDirection == eDirPrevious &&
9420         traversedFrame->IsBrFrame()) {
9421       bool canSkipBr = false;
9422       for (nsIFrame* current = traversedFrame->GetPrevSibling(); current;
9423            current = current->GetPrevSibling()) {
9424         if (!current->IsBlockOutside() && IsSelectable(current)) {
9425           canSkipBr = true;
9426           break;
9427         }
9428       }
9429       if (canSkipBr) {
9430         continue;
9431       }
9432     }
9433 
9434     selectable = IsSelectable(traversedFrame);
9435     if (!selectable) {
9436       *aOutMovedOverNonSelectableText = true;
9437     }
9438   }  // while (!selectable)
9439 
9440   *aOutOffset = (aDirection == eDirNext) ? 0 : -1;
9441 
9442   if (aVisual && IsReversedDirectionFrame(traversedFrame)) {
9443     // The new frame is reverse-direction, go to the other end
9444     *aOutOffset = -1 - *aOutOffset;
9445   }
9446   *aOutFrame = traversedFrame;
9447   return NS_OK;
9448 }
9449 
GetFrameFromDirection(const nsPeekOffsetStruct & aPos,nsIFrame ** aOutFrame,int32_t * aOutOffset,bool * aOutJumpedLine,bool * aOutMovedOverNonSelectableText)9450 nsresult nsIFrame::GetFrameFromDirection(const nsPeekOffsetStruct& aPos,
9451                                          nsIFrame** aOutFrame,
9452                                          int32_t* aOutOffset,
9453                                          bool* aOutJumpedLine,
9454                                          bool* aOutMovedOverNonSelectableText) {
9455   return GetFrameFromDirection(aPos.mDirection, aPos.mVisual, aPos.mJumpLines,
9456                                aPos.mScrollViewStop, aPos.mForceEditableRegion,
9457                                aOutFrame, aOutOffset, aOutJumpedLine,
9458                                aOutMovedOverNonSelectableText);
9459 }
9460 
GetClosestView(nsPoint * aOffset) const9461 nsView* nsIFrame::GetClosestView(nsPoint* aOffset) const {
9462   nsPoint offset(0, 0);
9463   for (const nsIFrame* f = this; f; f = f->GetParent()) {
9464     if (f->HasView()) {
9465       if (aOffset) *aOffset = offset;
9466       return f->GetView();
9467     }
9468     offset += f->GetPosition();
9469   }
9470 
9471   MOZ_ASSERT_UNREACHABLE("No view on any parent?  How did that happen?");
9472   return nullptr;
9473 }
9474 
9475 /* virtual */
ChildIsDirty(nsIFrame * aChild)9476 void nsIFrame::ChildIsDirty(nsIFrame* aChild) {
9477   MOZ_ASSERT_UNREACHABLE(
9478       "should never be called on a frame that doesn't "
9479       "inherit from nsContainerFrame");
9480 }
9481 
9482 #ifdef ACCESSIBILITY
AccessibleType()9483 a11y::AccType nsIFrame::AccessibleType() {
9484   if (IsTableCaption() && !GetRect().IsEmpty()) {
9485     return a11y::eHTMLCaptionType;
9486   }
9487   return a11y::eNoType;
9488 }
9489 #endif
9490 
ClearOverflowRects()9491 bool nsIFrame::ClearOverflowRects() {
9492   if (mOverflow.mType == NS_FRAME_OVERFLOW_NONE) {
9493     return false;
9494   }
9495   if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
9496     RemoveProperty(OverflowAreasProperty());
9497   }
9498   mOverflow.mType = NS_FRAME_OVERFLOW_NONE;
9499   return true;
9500 }
9501 
9502 /** Set the overflowArea rect, storing it as deltas or a separate rect
9503  * depending on its size in relation to the primary frame rect.
9504  */
SetOverflowAreas(const nsOverflowAreas & aOverflowAreas)9505 bool nsIFrame::SetOverflowAreas(const nsOverflowAreas& aOverflowAreas) {
9506   if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
9507     nsOverflowAreas* overflow = GetOverflowAreasProperty();
9508     bool changed = *overflow != aOverflowAreas;
9509     *overflow = aOverflowAreas;
9510 
9511     // Don't bother with converting to the deltas form if we already
9512     // have a property.
9513     return changed;
9514   }
9515 
9516   const nsRect& vis = aOverflowAreas.VisualOverflow();
9517   uint32_t l = -vis.x,                 // left edge: positive delta is leftwards
9518       t = -vis.y,                      // top: positive is upwards
9519       r = vis.XMost() - mRect.width,   // right: positive is rightwards
9520       b = vis.YMost() - mRect.height;  // bottom: positive is downwards
9521   if (aOverflowAreas.ScrollableOverflow().IsEqualEdges(
9522           nsRect(nsPoint(0, 0), GetSize())) &&
9523       l <= NS_FRAME_OVERFLOW_DELTA_MAX && t <= NS_FRAME_OVERFLOW_DELTA_MAX &&
9524       r <= NS_FRAME_OVERFLOW_DELTA_MAX && b <= NS_FRAME_OVERFLOW_DELTA_MAX &&
9525       // we have to check these against zero because we *never* want to
9526       // set a frame as having no overflow in this function.  This is
9527       // because FinishAndStoreOverflow calls this function prior to
9528       // SetRect based on whether the overflow areas match aNewSize.
9529       // In the case where the overflow areas exactly match mRect but
9530       // do not match aNewSize, we need to store overflow in a property
9531       // so that our eventual SetRect/SetSize will know that it has to
9532       // reset our overflow areas.
9533       (l | t | r | b) != 0) {
9534     VisualDeltas oldDeltas = mOverflow.mVisualDeltas;
9535     // It's a "small" overflow area so we store the deltas for each edge
9536     // directly in the frame, rather than allocating a separate rect.
9537     // If they're all zero, that's fine; we're setting things to
9538     // no-overflow.
9539     mOverflow.mVisualDeltas.mLeft = l;
9540     mOverflow.mVisualDeltas.mTop = t;
9541     mOverflow.mVisualDeltas.mRight = r;
9542     mOverflow.mVisualDeltas.mBottom = b;
9543     // There was no scrollable overflow before, and there isn't now.
9544     return oldDeltas != mOverflow.mVisualDeltas;
9545   } else {
9546     bool changed = !aOverflowAreas.ScrollableOverflow().IsEqualEdges(
9547                        nsRect(nsPoint(0, 0), GetSize())) ||
9548                    !aOverflowAreas.VisualOverflow().IsEqualEdges(
9549                        GetVisualOverflowFromDeltas());
9550 
9551     // it's a large overflow area that we need to store as a property
9552     mOverflow.mType = NS_FRAME_OVERFLOW_LARGE;
9553     AddProperty(OverflowAreasProperty(), new nsOverflowAreas(aOverflowAreas));
9554     return changed;
9555   }
9556 }
9557 
9558 /**
9559  * Compute the union of the border boxes of aFrame and its descendants,
9560  * in aFrame's coordinate space (if aApplyTransform is false) or its
9561  * post-transform coordinate space (if aApplyTransform is true).
9562  */
UnionBorderBoxes(nsIFrame * aFrame,bool aApplyTransform,bool & aOutValid,const nsSize * aSizeOverride=nullptr,const nsOverflowAreas * aOverflowOverride=nullptr)9563 static nsRect UnionBorderBoxes(
9564     nsIFrame* aFrame, bool aApplyTransform, bool& aOutValid,
9565     const nsSize* aSizeOverride = nullptr,
9566     const nsOverflowAreas* aOverflowOverride = nullptr) {
9567   const nsRect bounds(nsPoint(0, 0),
9568                       aSizeOverride ? *aSizeOverride : aFrame->GetSize());
9569 
9570   // The SVG container frames besides SVGTextFrame do not maintain
9571   // an accurate mRect. It will make the outline be larger than
9572   // we expect, we need to make them narrow to their children's outline.
9573   // aOutValid is set to false if the returned nsRect is not valid
9574   // and should not be included in the outline rectangle.
9575   aOutValid = !(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) ||
9576               !aFrame->IsFrameOfType(nsIFrame::eSVGContainer) ||
9577               aFrame->IsSVGTextFrame();
9578 
9579   nsRect u;
9580 
9581   if (!aFrame->FrameMaintainsOverflow()) {
9582     return u;
9583   }
9584 
9585   // Start from our border-box, transformed.  See comment below about
9586   // transform of children.
9587   bool doTransform = aApplyTransform && aFrame->IsTransformed();
9588   TransformReferenceBox boundsRefBox(nullptr, bounds);
9589   if (doTransform) {
9590     u = nsDisplayTransform::TransformRect(bounds, aFrame, boundsRefBox);
9591   } else {
9592     u = bounds;
9593   }
9594 
9595   // Only iterate through the children if the overflow areas suggest
9596   // that we might need to, and if the frame doesn't clip its overflow
9597   // anyway.
9598   if (aOverflowOverride) {
9599     if (!doTransform &&
9600         bounds.IsEqualEdges(aOverflowOverride->VisualOverflow()) &&
9601         bounds.IsEqualEdges(aOverflowOverride->ScrollableOverflow())) {
9602       return u;
9603     }
9604   } else {
9605     if (!doTransform && bounds.IsEqualEdges(aFrame->GetVisualOverflowRect()) &&
9606         bounds.IsEqualEdges(aFrame->GetScrollableOverflowRect())) {
9607       return u;
9608     }
9609   }
9610   const nsStyleDisplay* disp = aFrame->StyleDisplay();
9611   LayoutFrameType fType = aFrame->Type();
9612   if (nsFrame::ShouldApplyOverflowClipping(aFrame, disp) ||
9613       fType == LayoutFrameType::Scroll ||
9614       fType == LayoutFrameType::ListControl ||
9615       fType == LayoutFrameType::SVGOuterSVG) {
9616     return u;
9617   }
9618 
9619   const nsStyleEffects* effects = aFrame->StyleEffects();
9620   Maybe<nsRect> clipPropClipRect =
9621       aFrame->GetClipPropClipRect(disp, effects, bounds.Size());
9622 
9623   // Iterate over all children except pop-up, absolutely-positioned,
9624   // float, and overflow ones.
9625   const nsIFrame::ChildListIDs skip = {
9626       nsIFrame::kPopupList,    nsIFrame::kSelectPopupList,
9627       nsIFrame::kAbsoluteList, nsIFrame::kFixedList,
9628       nsIFrame::kFloatList,    nsIFrame::kOverflowList};
9629   for (const auto& [list, listID] : aFrame->ChildLists()) {
9630     if (skip.contains(listID)) {
9631       continue;
9632     }
9633 
9634     for (nsIFrame* child : list) {
9635       if (child->IsPlaceholderFrame()) {
9636         continue;
9637       }
9638 
9639       // Note that passing |true| for aApplyTransform when
9640       // child->Combines3DTransformWithAncestors() is incorrect if our
9641       // aApplyTransform is false... but the opposite would be as
9642       // well.  This is because elements within a preserve-3d scene
9643       // are always transformed up to the top of the scene.  This
9644       // means we don't have a mechanism for getting a transform up to
9645       // an intermediate point within the scene.  We choose to
9646       // over-transform rather than under-transform because this is
9647       // consistent with other overflow areas.
9648       bool validRect = true;
9649       nsRect childRect =
9650           UnionBorderBoxes(child, true, validRect) + child->GetPosition();
9651 
9652       if (!validRect) {
9653         continue;
9654       }
9655 
9656       if (clipPropClipRect) {
9657         // Intersect with the clip before transforming.
9658         childRect.IntersectRect(childRect, *clipPropClipRect);
9659       }
9660 
9661       // Note that we transform each child separately according to
9662       // aFrame's transform, and then union, which gives a different
9663       // (smaller) result from unioning and then transforming the
9664       // union.  This doesn't match the way we handle overflow areas
9665       // with 2-D transforms, though it does match the way we handle
9666       // overflow areas in preserve-3d 3-D scenes.
9667       if (doTransform && !child->Combines3DTransformWithAncestors()) {
9668         childRect =
9669             nsDisplayTransform::TransformRect(childRect, aFrame, boundsRefBox);
9670       }
9671 
9672       // If a SVGContainer has a non-SVGContainer child, we assign
9673       // its child's outline to this SVGContainer directly.
9674       if (!aOutValid && validRect) {
9675         u = childRect;
9676         aOutValid = true;
9677       } else {
9678         u.UnionRectEdges(u, childRect);
9679       }
9680     }
9681   }
9682 
9683   return u;
9684 }
9685 
ComputeAndIncludeOutlineArea(nsIFrame * aFrame,nsOverflowAreas & aOverflowAreas,const nsSize & aNewSize)9686 static void ComputeAndIncludeOutlineArea(nsIFrame* aFrame,
9687                                          nsOverflowAreas& aOverflowAreas,
9688                                          const nsSize& aNewSize) {
9689   const nsStyleOutline* outline = aFrame->StyleOutline();
9690   if (!outline->ShouldPaintOutline()) {
9691     return;
9692   }
9693 
9694   // When the outline property is set on a :-moz-block-inside-inline-wrapper
9695   // pseudo-element, it inherited that outline from the inline that was broken
9696   // because it contained a block.  In that case, we don't want a really wide
9697   // outline if the block inside the inline is narrow, so union the actual
9698   // contents of the anonymous blocks.
9699   nsIFrame* frameForArea = aFrame;
9700   do {
9701     PseudoStyleType pseudoType = frameForArea->Style()->GetPseudoType();
9702     if (pseudoType != PseudoStyleType::mozBlockInsideInlineWrapper) break;
9703     // If we're done, we really want it and all its later siblings.
9704     frameForArea = frameForArea->PrincipalChildList().FirstChild();
9705     NS_ASSERTION(frameForArea, "anonymous block with no children?");
9706   } while (frameForArea);
9707 
9708   // Find the union of the border boxes of all descendants, or in
9709   // the block-in-inline case, all descendants we care about.
9710   //
9711   // Note that the interesting perspective-related cases are taken
9712   // care of by the code that handles those issues for overflow
9713   // calling FinishAndStoreOverflow again, which in turn calls this
9714   // function again.  We still need to deal with preserve-3d a bit.
9715   nsRect innerRect;
9716   bool validRect;
9717   if (frameForArea == aFrame) {
9718     innerRect =
9719         UnionBorderBoxes(aFrame, false, validRect, &aNewSize, &aOverflowAreas);
9720   } else {
9721     for (; frameForArea; frameForArea = frameForArea->GetNextSibling()) {
9722       nsRect r(UnionBorderBoxes(frameForArea, true, validRect));
9723 
9724       // Adjust for offsets transforms up to aFrame's pre-transform
9725       // (i.e., normal) coordinate space; see comments in
9726       // UnionBorderBoxes for some of the subtlety here.
9727       for (nsIFrame *f = frameForArea, *parent = f->GetParent();
9728            /* see middle of loop */; f = parent, parent = f->GetParent()) {
9729         r += f->GetPosition();
9730         if (parent == aFrame) {
9731           break;
9732         }
9733         if (parent->IsTransformed() && !f->Combines3DTransformWithAncestors()) {
9734           TransformReferenceBox refBox(parent);
9735           r = nsDisplayTransform::TransformRect(r, parent, refBox);
9736         }
9737       }
9738 
9739       innerRect.UnionRect(innerRect, r);
9740     }
9741   }
9742 
9743   // Keep this code in sync with GetOutlineInnerRect in nsCSSRendering.cpp.
9744   SetOrUpdateRectValuedProperty(aFrame, nsIFrame::OutlineInnerRectProperty(),
9745                                 innerRect);
9746   const nscoord offset = outline->mOutlineOffset.ToAppUnits();
9747   nsRect outerRect(innerRect);
9748   bool useOutlineAuto = false;
9749   if (StaticPrefs::layout_css_outline_style_auto_enabled()) {
9750     useOutlineAuto = outline->mOutlineStyle.IsAuto();
9751     if (MOZ_UNLIKELY(useOutlineAuto)) {
9752       nsPresContext* presContext = aFrame->PresContext();
9753       nsITheme* theme = presContext->Theme();
9754       if (theme->ThemeSupportsWidget(presContext, aFrame,
9755                                      StyleAppearance::FocusOutline)) {
9756         outerRect.Inflate(offset);
9757         theme->GetWidgetOverflow(presContext->DeviceContext(), aFrame,
9758                                  StyleAppearance::FocusOutline, &outerRect);
9759       } else {
9760         useOutlineAuto = false;
9761       }
9762     }
9763   }
9764   if (MOZ_LIKELY(!useOutlineAuto)) {
9765     nscoord width = outline->GetOutlineWidth();
9766     outerRect.Inflate(width + offset);
9767   }
9768 
9769   nsRect& vo = aOverflowAreas.VisualOverflow();
9770   vo.UnionRectEdges(vo, innerRect.Union(outerRect));
9771 }
9772 
FinishAndStoreOverflow(nsOverflowAreas & aOverflowAreas,nsSize aNewSize,nsSize * aOldSize,const nsStyleDisplay * aStyleDisplay)9773 bool nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas,
9774                                       nsSize aNewSize, nsSize* aOldSize,
9775                                       const nsStyleDisplay* aStyleDisplay) {
9776   MOZ_ASSERT(FrameMaintainsOverflow(),
9777              "Don't call - overflow rects not maintained on these SVG frames");
9778 
9779   const nsStyleDisplay* disp = StyleDisplayWithOptionalParam(aStyleDisplay);
9780   bool hasTransform = IsTransformed(disp);
9781 
9782   nsRect bounds(nsPoint(0, 0), aNewSize);
9783   // Store the passed in overflow area if we are a preserve-3d frame or we have
9784   // a transform, and it's not just the frame bounds.
9785   if (hasTransform || Combines3DTransformWithAncestors(disp)) {
9786     if (!aOverflowAreas.VisualOverflow().IsEqualEdges(bounds) ||
9787         !aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds)) {
9788       nsOverflowAreas* initial =
9789           GetProperty(nsIFrame::InitialOverflowProperty());
9790       if (!initial) {
9791         AddProperty(nsIFrame::InitialOverflowProperty(),
9792                     new nsOverflowAreas(aOverflowAreas));
9793       } else if (initial != &aOverflowAreas) {
9794         *initial = aOverflowAreas;
9795       }
9796     } else {
9797       RemoveProperty(nsIFrame::InitialOverflowProperty());
9798     }
9799 #ifdef DEBUG
9800     SetProperty(nsIFrame::DebugInitialOverflowPropertyApplied(), true);
9801 #endif
9802   } else {
9803 #ifdef DEBUG
9804     RemoveProperty(nsIFrame::DebugInitialOverflowPropertyApplied());
9805 #endif
9806   }
9807 
9808   nsSize oldSize = mRect.Size();
9809   bool sizeChanged = ((aOldSize ? *aOldSize : oldSize) != aNewSize);
9810 
9811   // Our frame size may not have been computed and set yet, but code under
9812   // functions such as ComputeEffectsRect (which we're about to call) use the
9813   // values that are stored in our frame rect to compute their results.  We
9814   // need the results from those functions to be based on the frame size that
9815   // we *will* have, so we temporarily set our frame size here before calling
9816   // those functions.
9817   //
9818   // XXX Someone should document here why we revert the frame size before we
9819   // return rather than just leaving it set.
9820   //
9821   // We pass false here to avoid invalidating display items for this temporary
9822   // change. We sometimes reflow frames multiple times, with the final size
9823   // being the same as the initial. The single call to SetSize after reflow is
9824   // done will take care of invalidating display items if the size has actually
9825   // changed.
9826   SetSize(aNewSize, false);
9827 
9828   const bool applyOverflowClipping =
9829       nsFrame::ShouldApplyOverflowClipping(this, disp);
9830 
9831   if (ChildrenHavePerspective(disp) && sizeChanged) {
9832     RecomputePerspectiveChildrenOverflow(this);
9833 
9834     if (!applyOverflowClipping) {
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   NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
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. The
9864   // children are actually clipped to the padding-box, but since the
9865   // overflow area should include the entire border-box, just set it to
9866   // the border-box here.
9867   NS_ASSERTION((disp->mOverflowY == StyleOverflow::MozHiddenUnscrollable) ==
9868                    (disp->mOverflowX == StyleOverflow::MozHiddenUnscrollable),
9869                "If one overflow is clip, the other should be too");
9870   if (applyOverflowClipping) {
9871     // The contents are actually clipped to the padding area
9872     aOverflowAreas.SetAllTo(bounds);
9873   }
9874 
9875   // Overflow area must always include the frame's top-left and bottom-right,
9876   // even if the frame rect is empty (so we can scroll to those positions).
9877   // Pending a real fix for bug 426879, don't do this for inline frames
9878   // with zero width.
9879   // Do not do this for SVG either, since it will usually massively increase
9880   // the area unnecessarily.
9881   if ((aNewSize.width != 0 || !IsInlineFrame()) &&
9882       !(GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
9883     NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
9884       nsRect& o = aOverflowAreas.Overflow(otype);
9885       o.UnionRectEdges(o, bounds);
9886     }
9887   }
9888 
9889   // Note that StyleOverflow::MozHiddenUnscrollable doesn't clip the frame
9890   // background, so we add theme background overflow here so it's not clipped.
9891   if (!::IsXULBoxWrapped(this) && IsThemed(disp)) {
9892     nsRect r(bounds);
9893     nsPresContext* presContext = PresContext();
9894     if (presContext->Theme()->GetWidgetOverflow(presContext->DeviceContext(),
9895                                                 this, disp->mAppearance, &r)) {
9896       nsRect& vo = aOverflowAreas.VisualOverflow();
9897       vo.UnionRectEdges(vo, r);
9898     }
9899   }
9900 
9901   ComputeAndIncludeOutlineArea(this, aOverflowAreas, aNewSize);
9902 
9903   // Nothing in here should affect scrollable overflow.
9904   aOverflowAreas.VisualOverflow() =
9905       ComputeEffectsRect(this, aOverflowAreas.VisualOverflow(), aNewSize);
9906 
9907   // Absolute position clipping
9908   const nsStyleEffects* effects = StyleEffects();
9909   Maybe<nsRect> clipPropClipRect = GetClipPropClipRect(disp, effects, aNewSize);
9910   if (clipPropClipRect) {
9911     NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
9912       nsRect& o = aOverflowAreas.Overflow(otype);
9913       o.IntersectRect(o, *clipPropClipRect);
9914     }
9915   }
9916 
9917   /* If we're transformed, transform the overflow rect by the current
9918    * transformation. */
9919   if (hasTransform) {
9920     SetProperty(nsIFrame::PreTransformOverflowAreasProperty(),
9921                 new nsOverflowAreas(aOverflowAreas));
9922 
9923     if (Combines3DTransformWithAncestors(disp)) {
9924       /* If we're a preserve-3d leaf frame, then our pre-transform overflow
9925        * should be correct. Our post-transform overflow is empty though, because
9926        * we only contribute to the overflow area of the preserve-3d root frame.
9927        * If we're an intermediate frame then the pre-transform overflow should
9928        * contain all our non-preserve-3d children, which is what we want. Again
9929        * we have no post-transform overflow.
9930        */
9931       aOverflowAreas.SetAllTo(nsRect());
9932     } else {
9933       TransformReferenceBox refBox(this);
9934       NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
9935         nsRect& o = aOverflowAreas.Overflow(otype);
9936         o = nsDisplayTransform::TransformRect(o, this, refBox);
9937       }
9938 
9939       /* If we're the root of the 3d context, then we want to include the
9940        * overflow areas of all the participants. This won't have happened yet as
9941        * the code above set their overflow area to empty. Manually collect these
9942        * overflow areas now.
9943        */
9944       if (Extend3DContext(disp, effects)) {
9945         ComputePreserve3DChildrenOverflow(aOverflowAreas);
9946       }
9947     }
9948   } else {
9949     RemoveProperty(nsIFrame::PreTransformOverflowAreasProperty());
9950   }
9951 
9952   /* Revert the size change in case some caller is depending on this. */
9953   SetSize(oldSize, false);
9954 
9955   bool anyOverflowChanged;
9956   if (aOverflowAreas != nsOverflowAreas(bounds, bounds)) {
9957     anyOverflowChanged = SetOverflowAreas(aOverflowAreas);
9958   } else {
9959     anyOverflowChanged = ClearOverflowRects();
9960   }
9961 
9962   if (anyOverflowChanged) {
9963     SVGObserverUtils::InvalidateDirectRenderingObservers(this);
9964     if (IsBlockFrameOrSubclass() &&
9965         TextOverflow::CanHaveOverflowMarkers(this)) {
9966       DiscardDisplayItems(this, [](nsDisplayItemBase* aItem) {
9967         return aItem->GetType() == DisplayItemType::TYPE_TEXT_OVERFLOW;
9968       });
9969       SchedulePaint(PAINT_DEFAULT);
9970     }
9971   }
9972   return anyOverflowChanged;
9973 }
9974 
RecomputePerspectiveChildrenOverflow(const nsIFrame * aStartFrame)9975 void nsIFrame::RecomputePerspectiveChildrenOverflow(
9976     const nsIFrame* aStartFrame) {
9977   for (const auto& childList : ChildLists()) {
9978     for (nsIFrame* child : childList.mList) {
9979       if (!child->FrameMaintainsOverflow()) {
9980         continue;  // frame does not maintain overflow rects
9981       }
9982       if (child->HasPerspective()) {
9983         nsOverflowAreas* overflow =
9984             child->GetProperty(nsIFrame::InitialOverflowProperty());
9985         nsRect bounds(nsPoint(0, 0), child->GetSize());
9986         if (overflow) {
9987           nsOverflowAreas overflowCopy = *overflow;
9988           child->FinishAndStoreOverflow(overflowCopy, bounds.Size());
9989         } else {
9990           nsOverflowAreas boundsOverflow;
9991           boundsOverflow.SetAllTo(bounds);
9992           child->FinishAndStoreOverflow(boundsOverflow, bounds.Size());
9993         }
9994       } else if (child->GetContainingBlock(SKIP_SCROLLED_FRAME) ==
9995                  aStartFrame) {
9996         // If a frame is using perspective, then the size used to compute
9997         // perspective-origin is the size of the frame belonging to its parent
9998         // style. We must find any descendant frames using our size
9999         // (by recursing into frames that have the same containing block)
10000         // to update their overflow rects too.
10001         child->RecomputePerspectiveChildrenOverflow(aStartFrame);
10002       }
10003     }
10004   }
10005 }
10006 
ComputePreserve3DChildrenOverflow(nsOverflowAreas & aOverflowAreas)10007 void nsIFrame::ComputePreserve3DChildrenOverflow(
10008     nsOverflowAreas& aOverflowAreas) {
10009   // Find all descendants that participate in the 3d context, and include their
10010   // overflow. These descendants have an empty overflow, so won't have been
10011   // included in the normal overflow calculation. Any children that don't
10012   // participate have normal overflow, so will have been included already.
10013 
10014   nsRect childVisual;
10015   nsRect childScrollable;
10016   for (const auto& childList : ChildLists()) {
10017     for (nsIFrame* child : childList.mList) {
10018       // If this child participates in the 3d context, then take the
10019       // pre-transform region (which contains all descendants that aren't
10020       // participating in the 3d context) and transform it into the 3d context
10021       // root coordinate space.
10022       const nsStyleDisplay* childDisp = child->StyleDisplay();
10023       if (child->Combines3DTransformWithAncestors(childDisp)) {
10024         nsOverflowAreas childOverflow = child->GetOverflowAreasRelativeToSelf();
10025         TransformReferenceBox refBox(child);
10026         NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
10027           nsRect& o = childOverflow.Overflow(otype);
10028           o = nsDisplayTransform::TransformRect(o, child, refBox);
10029         }
10030 
10031         aOverflowAreas.UnionWith(childOverflow);
10032 
10033         // If this child also extends the 3d context, then recurse into it
10034         // looking for more participants.
10035         if (child->Extend3DContext(childDisp, child->StyleEffects())) {
10036           child->ComputePreserve3DChildrenOverflow(aOverflowAreas);
10037         }
10038       }
10039     }
10040   }
10041 }
10042 
ZIndex() const10043 int32_t nsIFrame::ZIndex() const {
10044   if (!IsAbsPosContainingBlock() && !IsFlexOrGridItem()) {
10045     return 0;
10046   }
10047 
10048   const nsStylePosition* position = StylePosition();
10049   if (position->mZIndex.IsInteger()) {
10050     return position->mZIndex.AsInteger();
10051   }
10052   MOZ_ASSERT(position->mZIndex.IsAuto());
10053   // sort the auto and 0 elements together
10054   return 0;
10055 }
10056 
IsScrollAnchor(ScrollAnchorContainer ** aOutContainer)10057 bool nsIFrame::IsScrollAnchor(ScrollAnchorContainer** aOutContainer) {
10058   if (!mInScrollAnchorChain) {
10059     return false;
10060   }
10061 
10062   nsIFrame* f = this;
10063 
10064   // FIXME(emilio, bug 1629280): We should find a non-null anchor if we have the
10065   // flag set, but bug 1629280 makes it so that we cannot really assert it /
10066   // make this just a `while (true)`, and uncomment the below assertion.
10067   while (auto* container = ScrollAnchorContainer::FindFor(f)) {
10068     // MOZ_ASSERT(f->IsInScrollAnchorChain());
10069     if (nsIFrame* anchor = container->AnchorNode()) {
10070       if (anchor != this) {
10071         return false;
10072       }
10073       if (aOutContainer) {
10074         *aOutContainer = container;
10075       }
10076       return true;
10077     }
10078 
10079     f = container->Frame();
10080   }
10081 
10082   return false;
10083 }
10084 
IsInScrollAnchorChain() const10085 bool nsIFrame::IsInScrollAnchorChain() const { return mInScrollAnchorChain; }
10086 
SetInScrollAnchorChain(bool aInChain)10087 void nsIFrame::SetInScrollAnchorChain(bool aInChain) {
10088   mInScrollAnchorChain = aInChain;
10089 }
10090 
GetDepthInFrameTree() const10091 uint32_t nsIFrame::GetDepthInFrameTree() const {
10092   uint32_t result = 0;
10093   for (nsContainerFrame* ancestor = GetParent(); ancestor;
10094        ancestor = ancestor->GetParent()) {
10095     result++;
10096   }
10097   return result;
10098 }
10099 
ConsiderChildOverflow(nsOverflowAreas & aOverflowAreas,nsIFrame * aChildFrame)10100 void nsContainerFrame::ConsiderChildOverflow(nsOverflowAreas& aOverflowAreas,
10101                                              nsIFrame* aChildFrame) {
10102   if (StyleDisplay()->IsContainLayout() &&
10103       IsFrameOfType(eSupportsContainLayoutAndPaint)) {
10104     // If we have layout containment and are not a non-atomic, inline-level
10105     // principal box, we should only consider our child's visual (ink) overflow,
10106     // leaving the scrollable regions of the parent unaffected.
10107     // Note: scrollable overflow is a subset of visual overflow,
10108     // so this has the same affect as unioning the child's visual and
10109     // scrollable overflow with the parent's visual overflow.
10110     nsRect childVisual = aChildFrame->GetVisualOverflowRect();
10111     nsOverflowAreas combined = nsOverflowAreas(childVisual, nsRect());
10112     aOverflowAreas.UnionWith(combined + aChildFrame->GetPosition());
10113   } else {
10114     aOverflowAreas.UnionWith(aChildFrame->GetOverflowAreas() +
10115                              aChildFrame->GetPosition());
10116   }
10117 }
10118 
ShouldAvoidBreakInside(const ReflowInput & aReflowInput) const10119 bool nsContainerFrame::ShouldAvoidBreakInside(
10120     const ReflowInput& aReflowInput) const {
10121   const auto* disp = StyleDisplay();
10122   return !aReflowInput.mFlags.mIsTopOfPage &&
10123          StyleBreakWithin::Avoid == disp->mBreakInside &&
10124          !(HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) &&
10125            IsAbsolutelyPositioned(disp)) &&
10126          !GetPrevInFlow();
10127 }
10128 
10129 /**
10130  * This function takes a frame that is part of a block-in-inline split,
10131  * and _if_ that frame is an anonymous block created by an ib split it
10132  * returns the block's preceding inline.  This is needed because the
10133  * split inline's style is the parent of the anonymous block's style.
10134  *
10135  * If aFrame is not an anonymous block, null is returned.
10136  */
GetIBSplitSiblingForAnonymousBlock(const nsIFrame * aFrame)10137 static nsIFrame* GetIBSplitSiblingForAnonymousBlock(const nsIFrame* aFrame) {
10138   MOZ_ASSERT(aFrame, "Must have a non-null frame!");
10139   NS_ASSERTION(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT,
10140                "GetIBSplitSibling should only be called on ib-split frames");
10141 
10142   if (aFrame->Style()->GetPseudoType() !=
10143       PseudoStyleType::mozBlockInsideInlineWrapper) {
10144     // it's not an anonymous block
10145     return nullptr;
10146   }
10147 
10148   // Find the first continuation of the frame.  (Ugh.  This ends up
10149   // being O(N^2) when it is called O(N) times.)
10150   aFrame = aFrame->FirstContinuation();
10151 
10152   /*
10153    * Now look up the nsGkAtoms::IBSplitPrevSibling
10154    * property.
10155    */
10156   nsIFrame* ibSplitSibling =
10157       aFrame->GetProperty(nsIFrame::IBSplitPrevSibling());
10158   NS_ASSERTION(ibSplitSibling, "Broken frame tree?");
10159   return ibSplitSibling;
10160 }
10161 
10162 /**
10163  * Get the parent, corrected for the mangled frame tree resulting from
10164  * having a block within an inline.  The result only differs from the
10165  * result of |GetParent| when |GetParent| returns an anonymous block
10166  * that was created for an element that was 'display: inline' because
10167  * that element contained a block.
10168  *
10169  * Also skip anonymous scrolled-content parents; inherit directly from the
10170  * outer scroll frame.
10171  *
10172  * Also skip NAC parents if the child frame is NAC.
10173  */
GetCorrectedParent(const nsIFrame * aFrame)10174 static nsIFrame* GetCorrectedParent(const nsIFrame* aFrame) {
10175   nsIFrame* parent = aFrame->GetParent();
10176   if (!parent) {
10177     return nullptr;
10178   }
10179 
10180   // For a table caption we want the _inner_ table frame (unless it's anonymous)
10181   // as the style parent.
10182   if (aFrame->IsTableCaption()) {
10183     nsIFrame* innerTable = parent->PrincipalChildList().FirstChild();
10184     if (!innerTable->Style()->IsAnonBox()) {
10185       return innerTable;
10186     }
10187   }
10188 
10189   // Table wrappers are always anon boxes; if we're in here for an outer
10190   // table, that actually means its the _inner_ table that wants to
10191   // know its parent. So get the pseudo of the inner in that case.
10192   auto pseudo = aFrame->Style()->GetPseudoType();
10193   if (pseudo == PseudoStyleType::tableWrapper) {
10194     pseudo =
10195         aFrame->PrincipalChildList().FirstChild()->Style()->GetPseudoType();
10196   }
10197 
10198   // Prevent a NAC pseudo-element from inheriting from its NAC parent, and
10199   // inherit from the NAC generator element instead.
10200   if (pseudo != PseudoStyleType::NotPseudo) {
10201     MOZ_ASSERT(aFrame->GetContent());
10202     Element* element = Element::FromNode(aFrame->GetContent());
10203     // Make sure to avoid doing the fixup for non-element-backed pseudos like
10204     // ::first-line and such.
10205     if (element && !element->IsRootOfNativeAnonymousSubtree() &&
10206         element->GetPseudoElementType() == aFrame->Style()->GetPseudoType()) {
10207       while (parent->GetContent() &&
10208              !parent->GetContent()->IsRootOfNativeAnonymousSubtree()) {
10209         parent = parent->GetInFlowParent();
10210       }
10211       parent = parent->GetInFlowParent();
10212     }
10213   }
10214 
10215   return nsFrame::CorrectStyleParentFrame(parent, pseudo);
10216 }
10217 
10218 /* static */
CorrectStyleParentFrame(nsIFrame * aProspectiveParent,PseudoStyleType aChildPseudo)10219 nsIFrame* nsFrame::CorrectStyleParentFrame(nsIFrame* aProspectiveParent,
10220                                            PseudoStyleType aChildPseudo) {
10221   MOZ_ASSERT(aProspectiveParent, "Must have a prospective parent");
10222 
10223   if (aChildPseudo != PseudoStyleType::NotPseudo) {
10224     // Non-inheriting anon boxes have no style parent frame at all.
10225     if (PseudoStyle::IsNonInheritingAnonBox(aChildPseudo)) {
10226       return nullptr;
10227     }
10228 
10229     // Other anon boxes are parented to their actual parent already, except
10230     // for non-elements.  Those should not be treated as an anon box.
10231     if (PseudoStyle::IsAnonBox(aChildPseudo) &&
10232         !nsCSSAnonBoxes::IsNonElement(aChildPseudo)) {
10233       NS_ASSERTION(aChildPseudo != PseudoStyleType::mozBlockInsideInlineWrapper,
10234                    "Should have dealt with kids that have "
10235                    "NS_FRAME_PART_OF_IBSPLIT elsewhere");
10236       return aProspectiveParent;
10237     }
10238   }
10239 
10240   // Otherwise, walk up out of all anon boxes.  For placeholder frames, walk out
10241   // of all pseudo-elements as well.  Otherwise ReparentComputedStyle could
10242   // cause style data to be out of sync with the frame tree.
10243   nsIFrame* parent = aProspectiveParent;
10244   do {
10245     if (parent->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) {
10246       nsIFrame* sibling = GetIBSplitSiblingForAnonymousBlock(parent);
10247 
10248       if (sibling) {
10249         // |parent| was a block in an {ib} split; use the inline as
10250         // |the style parent.
10251         parent = sibling;
10252       }
10253     }
10254 
10255     if (!parent->Style()->IsPseudoOrAnonBox()) {
10256       return parent;
10257     }
10258 
10259     if (!parent->Style()->IsAnonBox() && aChildPseudo != PseudoStyleType::MAX) {
10260       // nsPlaceholderFrame passes in PseudoStyleType::MAX for
10261       // aChildPseudo (even though that's not a valid pseudo-type) just to
10262       // trigger this behavior of walking up to the nearest non-pseudo
10263       // ancestor.
10264       return parent;
10265     }
10266 
10267     parent = parent->GetInFlowParent();
10268   } while (parent);
10269 
10270   if (aProspectiveParent->Style()->GetPseudoType() ==
10271       PseudoStyleType::viewportScroll) {
10272     // aProspectiveParent is the scrollframe for a viewport
10273     // and the kids are the anonymous scrollbars
10274     return aProspectiveParent;
10275   }
10276 
10277   // We can get here if the root element is absolutely positioned.
10278   // We can't test for this very accurately, but it can only happen
10279   // when the prospective parent is a canvas frame.
10280   NS_ASSERTION(aProspectiveParent->IsCanvasFrame(),
10281                "Should have found a parent before this");
10282   return nullptr;
10283 }
10284 
DoGetParentComputedStyle(nsIFrame ** aProviderFrame) const10285 ComputedStyle* nsIFrame::DoGetParentComputedStyle(
10286     nsIFrame** aProviderFrame) const {
10287   *aProviderFrame = nullptr;
10288 
10289   // Handle display:contents and the root frame, when there's no parent frame
10290   // to inherit from.
10291   if (MOZ_LIKELY(mContent)) {
10292     Element* parentElement = mContent->GetFlattenedTreeParentElement();
10293     if (MOZ_LIKELY(parentElement)) {
10294       auto pseudo = Style()->GetPseudoType();
10295       if (pseudo == PseudoStyleType::NotPseudo || !mContent->IsElement() ||
10296           (!PseudoStyle::IsAnonBox(pseudo) &&
10297            // Ensure that we don't return the display:contents style
10298            // of the parent content for pseudos that have the same content
10299            // as their primary frame (like -moz-list-bullets do):
10300            IsPrimaryFrame()) ||
10301           /* if next is true then it's really a request for the table frame's
10302              parent context, see nsTable[Outer]Frame::GetParentComputedStyle. */
10303           pseudo == PseudoStyleType::tableWrapper) {
10304         // In some edge cases involving display: contents, we may end up here
10305         // for something that's pending to be reframed. In this case we return
10306         // the wrong style from here (because we've already lost track of it!),
10307         // but it's not a big deal as we're going to be reframed anyway.
10308         if (MOZ_LIKELY(parentElement->HasServoData()) &&
10309             Servo_Element_IsDisplayContents(parentElement)) {
10310           RefPtr<ComputedStyle> style =
10311               ServoStyleSet::ResolveServoStyle(*parentElement);
10312           // NOTE(emilio): we return a weak reference because the element also
10313           // holds the style context alive. This is a bit silly (we could've
10314           // returned a weak ref directly), but it's probably not worth
10315           // optimizing, given this function has just one caller which is rare,
10316           // and this path is rare itself.
10317           return style;
10318         }
10319       }
10320     } else {
10321       if (Style()->GetPseudoType() == PseudoStyleType::NotPseudo) {
10322         // We're a frame for the root.  We have no style parent.
10323         return nullptr;
10324       }
10325     }
10326   }
10327 
10328   if (!(mState & NS_FRAME_OUT_OF_FLOW)) {
10329     /*
10330      * If this frame is an anonymous block created when an inline with a block
10331      * inside it got split, then the parent style is on its preceding inline. We
10332      * can get to it using GetIBSplitSiblingForAnonymousBlock.
10333      */
10334     if (mState & NS_FRAME_PART_OF_IBSPLIT) {
10335       nsIFrame* ibSplitSibling = GetIBSplitSiblingForAnonymousBlock(this);
10336       if (ibSplitSibling) {
10337         return (*aProviderFrame = ibSplitSibling)->Style();
10338       }
10339     }
10340 
10341     // If this frame is one of the blocks that split an inline, we must
10342     // return the "special" inline parent, i.e., the parent that this
10343     // frame would have if we didn't mangle the frame structure.
10344     *aProviderFrame = GetCorrectedParent(this);
10345     return *aProviderFrame ? (*aProviderFrame)->Style() : nullptr;
10346   }
10347 
10348   // We're an out-of-flow frame.  For out-of-flow frames, we must
10349   // resolve underneath the placeholder's parent.  The placeholder is
10350   // reached from the first-in-flow.
10351   nsPlaceholderFrame* placeholder = FirstInFlow()->GetPlaceholderFrame();
10352   if (!placeholder) {
10353     MOZ_ASSERT_UNREACHABLE("no placeholder frame for out-of-flow frame");
10354     *aProviderFrame = GetCorrectedParent(this);
10355     return *aProviderFrame ? (*aProviderFrame)->Style() : nullptr;
10356   }
10357   return placeholder->GetParentComputedStyleForOutOfFlow(aProviderFrame);
10358 }
10359 
GetLastLeaf(nsPresContext * aPresContext,nsIFrame ** aFrame)10360 void nsFrame::GetLastLeaf(nsPresContext* aPresContext, nsIFrame** aFrame) {
10361   if (!aFrame || !*aFrame) return;
10362   nsIFrame* child = *aFrame;
10363   // if we are a block frame then go for the last line of 'this'
10364   while (1) {
10365     child = child->PrincipalChildList().FirstChild();
10366     if (!child) return;  // nothing to do
10367     nsIFrame* siblingFrame;
10368     nsIContent* content;
10369     // ignore anonymous elements, e.g. mozTableAdd* mozTableRemove*
10370     // see bug 278197 comment #12 #13 for details
10371     while ((siblingFrame = child->GetNextSibling()) &&
10372            (content = siblingFrame->GetContent()) &&
10373            !content->IsRootOfNativeAnonymousSubtree())
10374       child = siblingFrame;
10375     *aFrame = child;
10376   }
10377 }
10378 
GetFirstLeaf(nsPresContext * aPresContext,nsIFrame ** aFrame)10379 void nsFrame::GetFirstLeaf(nsPresContext* aPresContext, nsIFrame** aFrame) {
10380   if (!aFrame || !*aFrame) return;
10381   nsIFrame* child = *aFrame;
10382   while (1) {
10383     child = child->PrincipalChildList().FirstChild();
10384     if (!child) return;  // nothing to do
10385     *aFrame = child;
10386   }
10387 }
10388 
10389 /* virtual */
IsFocusable(int32_t * aTabIndex,bool aWithMouse)10390 bool nsIFrame::IsFocusable(int32_t* aTabIndex, bool aWithMouse) {
10391   int32_t tabIndex = -1;
10392   if (aTabIndex) {
10393     *aTabIndex = -1;  // Default for early return is not focusable
10394   }
10395   bool isFocusable = false;
10396 
10397   if (mContent && mContent->IsElement() && IsVisibleConsideringAncestors() &&
10398       Style()->GetPseudoType() != PseudoStyleType::anonymousFlexItem &&
10399       Style()->GetPseudoType() != PseudoStyleType::anonymousGridItem) {
10400     const nsStyleUI* ui = StyleUI();
10401     if (ui->mUserFocus != StyleUserFocus::Ignore &&
10402         ui->mUserFocus != StyleUserFocus::None) {
10403       // Pass in default tabindex of -1 for nonfocusable and 0 for focusable
10404       tabIndex = 0;
10405     }
10406     isFocusable = mContent->IsFocusable(&tabIndex, aWithMouse);
10407     if (!isFocusable && !aWithMouse && IsScrollFrame() &&
10408         mContent->IsHTMLElement() &&
10409         !mContent->IsRootOfNativeAnonymousSubtree() && mContent->GetParent() &&
10410         !mContent->AsElement()->HasAttr(kNameSpaceID_None,
10411                                         nsGkAtoms::tabindex)) {
10412       // Elements with scrollable view are focusable with script & tabbable
10413       // Otherwise you couldn't scroll them with keyboard, which is
10414       // an accessibility issue (e.g. Section 508 rules)
10415       // However, we don't make them to be focusable with the mouse,
10416       // because the extra focus outlines are considered unnecessarily ugly.
10417       // When clicked on, the selection position within the element
10418       // will be enough to make them keyboard scrollable.
10419       nsIScrollableFrame* scrollFrame = do_QueryFrame(this);
10420       if (scrollFrame && !scrollFrame->IsForTextControlWithNoScrollbars() &&
10421           !scrollFrame->GetScrollStyles().IsHiddenInBothDirections() &&
10422           !scrollFrame->GetScrollRange().IsEqualEdges(nsRect(0, 0, 0, 0))) {
10423         // Scroll bars will be used for overflow
10424         isFocusable = true;
10425         tabIndex = 0;
10426       }
10427     }
10428   }
10429 
10430   if (aTabIndex) {
10431     *aTabIndex = tabIndex;
10432   }
10433   return isFocusable;
10434 }
10435 
10436 /**
10437  * @return true if this text frame ends with a newline character which is
10438  * treated as preformatted. It should return false if this is not a text frame.
10439  */
HasSignificantTerminalNewline() const10440 bool nsIFrame::HasSignificantTerminalNewline() const { return false; }
10441 
ConvertSVGDominantBaselineToVerticalAlign(StyleDominantBaseline aDominantBaseline)10442 static StyleVerticalAlignKeyword ConvertSVGDominantBaselineToVerticalAlign(
10443     StyleDominantBaseline aDominantBaseline) {
10444   // Most of these are approximate mappings.
10445   switch (aDominantBaseline) {
10446     case StyleDominantBaseline::Hanging:
10447     case StyleDominantBaseline::TextBeforeEdge:
10448       return StyleVerticalAlignKeyword::TextTop;
10449     case StyleDominantBaseline::TextAfterEdge:
10450     case StyleDominantBaseline::Ideographic:
10451       return StyleVerticalAlignKeyword::TextBottom;
10452     case StyleDominantBaseline::Central:
10453     case StyleDominantBaseline::Middle:
10454     case StyleDominantBaseline::Mathematical:
10455       return StyleVerticalAlignKeyword::Middle;
10456     case StyleDominantBaseline::Auto:
10457     case StyleDominantBaseline::Alphabetic:
10458       return StyleVerticalAlignKeyword::Baseline;
10459     default:
10460       MOZ_ASSERT_UNREACHABLE("unexpected aDominantBaseline value");
10461       return StyleVerticalAlignKeyword::Baseline;
10462   }
10463 }
10464 
VerticalAlignEnum() const10465 Maybe<StyleVerticalAlignKeyword> nsIFrame::VerticalAlignEnum() const {
10466   if (nsSVGUtils::IsInSVGTextSubtree(this)) {
10467     StyleDominantBaseline dominantBaseline = StyleSVG()->mDominantBaseline;
10468     return Some(ConvertSVGDominantBaselineToVerticalAlign(dominantBaseline));
10469   }
10470 
10471   const auto& verticalAlign = StyleDisplay()->mVerticalAlign;
10472   if (verticalAlign.IsKeyword()) {
10473     return Some(verticalAlign.AsKeyword());
10474   }
10475 
10476   return Nothing();
10477 }
10478 
10479 NS_IMETHODIMP
RefreshSizeCache(nsBoxLayoutState & aState)10480 nsIFrame::RefreshSizeCache(nsBoxLayoutState& aState) {
10481   // XXXbz this comment needs some rewriting to make sense in the
10482   // post-reflow-branch world.
10483 
10484   // Ok we need to compute our minimum, preferred, and maximum sizes.
10485   // 1) Maximum size. This is easy. Its infinite unless it is overloaded by CSS.
10486   // 2) Preferred size. This is a little harder. This is the size the
10487   //    block would be if it were laid out on an infinite canvas. So we can
10488   //    get this by reflowing the block with and INTRINSIC width and height. We
10489   //    can also do a nice optimization for incremental reflow. If the reflow is
10490   //    incremental then we can pass a flag to have the block compute the
10491   //    preferred width for us! Preferred height can just be the minimum height;
10492   // 3) Minimum size. This is a toughy. We can pass the block a flag asking for
10493   //    the max element size. That would give us the width. Unfortunately you
10494   //    can only ask for a maxElementSize during an incremental reflow. So on
10495   //    other reflows we will just have to use 0. The min height on the other
10496   //    hand is fairly easy we need to get the largest line height. This can be
10497   //    done with the line iterator.
10498 
10499   // if we do have a rendering context
10500   gfxContext* rendContext = aState.GetRenderingContext();
10501   if (rendContext) {
10502     nsPresContext* presContext = aState.PresContext();
10503 
10504     // If we don't have any HTML constraints and it's a resize, then nothing in
10505     // the block could have changed, so no refresh is necessary.
10506     nsBoxLayoutMetrics* metrics = BoxMetrics();
10507     if (!XULNeedsRecalc(metrics->mBlockPrefSize)) {
10508       return NS_OK;
10509     }
10510 
10511     // the rect we plan to size to.
10512     nsRect rect = GetRect();
10513 
10514     nsMargin bp(0, 0, 0, 0);
10515     GetXULBorderAndPadding(bp);
10516 
10517     {
10518       // If we're a container for font size inflation, then shrink
10519       // wrapping inside of us should not apply font size inflation.
10520       AutoMaybeDisableFontInflation an(this);
10521 
10522       metrics->mBlockPrefSize.width =
10523           GetPrefISize(rendContext) + bp.LeftRight();
10524       metrics->mBlockMinSize.width = GetMinISize(rendContext) + bp.LeftRight();
10525     }
10526 
10527     // do the nasty.
10528     const WritingMode wm = aState.OuterReflowInput()
10529                                ? aState.OuterReflowInput()->GetWritingMode()
10530                                : GetWritingMode();
10531     ReflowOutput desiredSize(wm);
10532     BoxReflow(aState, presContext, desiredSize, rendContext, rect.x, rect.y,
10533               metrics->mBlockPrefSize.width, NS_UNCONSTRAINEDSIZE);
10534 
10535     metrics->mBlockMinSize.height = 0;
10536     // ok we need the max ascent of the items on the line. So to do this
10537     // ask the block for its line iterator. Get the max ascent.
10538     nsAutoLineIterator lines = GetLineIterator();
10539     if (lines) {
10540       metrics->mBlockMinSize.height = 0;
10541       int count = 0;
10542       nsIFrame* firstFrame = nullptr;
10543       int32_t framesOnLine;
10544       nsRect lineBounds;
10545 
10546       do {
10547         lines->GetLine(count, &firstFrame, &framesOnLine, lineBounds);
10548 
10549         if (lineBounds.height > metrics->mBlockMinSize.height)
10550           metrics->mBlockMinSize.height = lineBounds.height;
10551 
10552         count++;
10553       } while (firstFrame);
10554     } else {
10555       metrics->mBlockMinSize.height = desiredSize.Height();
10556     }
10557 
10558     metrics->mBlockPrefSize.height = metrics->mBlockMinSize.height;
10559 
10560     if (desiredSize.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE) {
10561       if (!nsLayoutUtils::GetFirstLineBaseline(wm, this,
10562                                                &metrics->mBlockAscent))
10563         metrics->mBlockAscent = GetLogicalBaseline(wm);
10564     } else {
10565       metrics->mBlockAscent = desiredSize.BlockStartAscent();
10566     }
10567 
10568 #ifdef DEBUG_adaptor
10569     printf("min=(%d,%d), pref=(%d,%d), ascent=%d\n",
10570            metrics->mBlockMinSize.width, metrics->mBlockMinSize.height,
10571            metrics->mBlockPrefSize.width, metrics->mBlockPrefSize.height,
10572            metrics->mBlockAscent);
10573 #endif
10574   }
10575 
10576   return NS_OK;
10577 }
10578 
GetXULPrefSize(nsBoxLayoutState & aState)10579 nsSize nsIFrame::GetXULPrefSize(nsBoxLayoutState& aState) {
10580   nsSize size(0, 0);
10581   DISPLAY_PREF_SIZE(this, size);
10582   // If the size is cached, and there are no HTML constraints that we might
10583   // be depending on, then we just return the cached size.
10584   nsBoxLayoutMetrics* metrics = BoxMetrics();
10585   if (!XULNeedsRecalc(metrics->mPrefSize)) {
10586     size = metrics->mPrefSize;
10587     return size;
10588   }
10589 
10590   if (IsXULCollapsed()) return size;
10591 
10592   // get our size in CSS.
10593   bool widthSet, heightSet;
10594   bool completelyRedefined =
10595       nsIFrame::AddXULPrefSize(this, size, widthSet, heightSet);
10596 
10597   // Refresh our caches with new sizes.
10598   if (!completelyRedefined) {
10599     RefreshSizeCache(aState);
10600     nsSize blockSize = metrics->mBlockPrefSize;
10601 
10602     // notice we don't need to add our borders or padding
10603     // in. That's because the block did it for us.
10604     if (!widthSet) size.width = blockSize.width;
10605     if (!heightSet) size.height = blockSize.height;
10606   }
10607 
10608   metrics->mPrefSize = size;
10609   return size;
10610 }
10611 
GetXULMinSize(nsBoxLayoutState & aState)10612 nsSize nsIFrame::GetXULMinSize(nsBoxLayoutState& aState) {
10613   nsSize size(0, 0);
10614   DISPLAY_MIN_SIZE(this, size);
10615   // Don't use the cache if we have HTMLReflowInput constraints --- they might
10616   // have changed
10617   nsBoxLayoutMetrics* metrics = BoxMetrics();
10618   if (!XULNeedsRecalc(metrics->mMinSize)) {
10619     size = metrics->mMinSize;
10620     return size;
10621   }
10622 
10623   if (IsXULCollapsed()) return size;
10624 
10625   // get our size in CSS.
10626   bool widthSet, heightSet;
10627   bool completelyRedefined =
10628       nsIFrame::AddXULMinSize(this, size, widthSet, heightSet);
10629 
10630   // Refresh our caches with new sizes.
10631   if (!completelyRedefined) {
10632     RefreshSizeCache(aState);
10633     nsSize blockSize = metrics->mBlockMinSize;
10634 
10635     if (!widthSet) size.width = blockSize.width;
10636     if (!heightSet) size.height = blockSize.height;
10637   }
10638 
10639   metrics->mMinSize = size;
10640   return size;
10641 }
10642 
GetXULMaxSize(nsBoxLayoutState & aState)10643 nsSize nsIFrame::GetXULMaxSize(nsBoxLayoutState& aState) {
10644   nsSize size(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
10645   DISPLAY_MAX_SIZE(this, size);
10646   // Don't use the cache if we have HTMLReflowInput constraints --- they might
10647   // have changed
10648   nsBoxLayoutMetrics* metrics = BoxMetrics();
10649   if (!XULNeedsRecalc(metrics->mMaxSize)) {
10650     size = metrics->mMaxSize;
10651     return size;
10652   }
10653 
10654   if (IsXULCollapsed()) return size;
10655 
10656   size = nsIFrame::GetUncachedXULMaxSize(aState);
10657   metrics->mMaxSize = size;
10658 
10659   return size;
10660 }
10661 
GetXULFlex()10662 nscoord nsIFrame::GetXULFlex() {
10663   nsBoxLayoutMetrics* metrics = BoxMetrics();
10664   if (XULNeedsRecalc(metrics->mFlex)) {
10665     nsIFrame::AddXULFlex(this, metrics->mFlex);
10666   }
10667 
10668   return metrics->mFlex;
10669 }
10670 
GetXULBoxAscent(nsBoxLayoutState & aState)10671 nscoord nsIFrame::GetXULBoxAscent(nsBoxLayoutState& aState) {
10672   nsBoxLayoutMetrics* metrics = BoxMetrics();
10673   if (!XULNeedsRecalc(metrics->mAscent)) {
10674     return metrics->mAscent;
10675   }
10676 
10677   if (IsXULCollapsed()) {
10678     metrics->mAscent = 0;
10679   } else {
10680     // Refresh our caches with new sizes.
10681     RefreshSizeCache(aState);
10682     metrics->mAscent = metrics->mBlockAscent;
10683   }
10684 
10685   return metrics->mAscent;
10686 }
10687 
DoXULLayout(nsBoxLayoutState & aState)10688 nsresult nsIFrame::DoXULLayout(nsBoxLayoutState& aState) {
10689   nsRect ourRect(mRect);
10690 
10691   gfxContext* rendContext = aState.GetRenderingContext();
10692   nsPresContext* presContext = aState.PresContext();
10693   WritingMode ourWM = GetWritingMode();
10694   const WritingMode outerWM = aState.OuterReflowInput()
10695                                   ? aState.OuterReflowInput()->GetWritingMode()
10696                                   : ourWM;
10697   ReflowOutput desiredSize(outerWM);
10698   LogicalSize ourSize = GetLogicalSize(outerWM);
10699 
10700   if (rendContext) {
10701     BoxReflow(aState, presContext, desiredSize, rendContext, ourRect.x,
10702               ourRect.y, ourRect.width, ourRect.height);
10703 
10704     if (IsXULCollapsed()) {
10705       SetSize(nsSize(0, 0));
10706     } else {
10707       // if our child needs to be bigger. This might happend with
10708       // wrapping text. There is no way to predict its height until we
10709       // reflow it. Now that we know the height reshuffle upward.
10710       if (desiredSize.ISize(outerWM) > ourSize.ISize(outerWM) ||
10711           desiredSize.BSize(outerWM) > ourSize.BSize(outerWM)) {
10712 #ifdef DEBUG_GROW
10713         XULDumpBox(stdout);
10714         printf(" GREW from (%d,%d) -> (%d,%d)\n", ourSize.ISize(outerWM),
10715                ourSize.BSize(outerWM), desiredSize.ISize(outerWM),
10716                desiredSize.BSize(outerWM));
10717 #endif
10718 
10719         if (desiredSize.ISize(outerWM) > ourSize.ISize(outerWM)) {
10720           ourSize.ISize(outerWM) = desiredSize.ISize(outerWM);
10721         }
10722 
10723         if (desiredSize.BSize(outerWM) > ourSize.BSize(outerWM)) {
10724           ourSize.BSize(outerWM) = desiredSize.BSize(outerWM);
10725         }
10726       }
10727 
10728       // ensure our size is what we think is should be. Someone could have
10729       // reset the frame to be smaller or something dumb like that.
10730       SetSize(ourSize.ConvertTo(ourWM, outerWM));
10731     }
10732   }
10733 
10734   // Should we do this if IsXULCollapsed() is true?
10735   LogicalSize size(GetLogicalSize(outerWM));
10736   desiredSize.ISize(outerWM) = size.ISize(outerWM);
10737   desiredSize.BSize(outerWM) = size.BSize(outerWM);
10738   desiredSize.UnionOverflowAreasWithDesiredBounds();
10739 
10740   if (HasAbsolutelyPositionedChildren()) {
10741     // Set up a |reflowInput| to pass into ReflowAbsoluteFrames
10742     ReflowInput reflowInput(aState.PresContext(), this,
10743                             aState.GetRenderingContext(),
10744                             LogicalSize(ourWM, ISize(), NS_UNCONSTRAINEDSIZE),
10745                             ReflowInput::DUMMY_PARENT_REFLOW_INPUT);
10746 
10747     AddStateBits(NS_FRAME_IN_REFLOW);
10748     // Set up a |reflowStatus| to pass into ReflowAbsoluteFrames
10749     // (just a dummy value; hopefully that's OK)
10750     nsReflowStatus reflowStatus;
10751     ReflowAbsoluteFrames(aState.PresContext(), desiredSize, reflowInput,
10752                          reflowStatus);
10753     RemoveStateBits(NS_FRAME_IN_REFLOW);
10754   }
10755 
10756   nsSize oldSize(ourRect.Size());
10757   FinishAndStoreOverflow(desiredSize.mOverflowAreas,
10758                          size.GetPhysicalSize(outerWM), &oldSize);
10759 
10760   SyncXULLayout(aState);
10761 
10762   return NS_OK;
10763 }
10764 
BoxReflow(nsBoxLayoutState & aState,nsPresContext * aPresContext,ReflowOutput & aDesiredSize,gfxContext * aRenderingContext,nscoord aX,nscoord aY,nscoord aWidth,nscoord aHeight,bool aMoveFrame)10765 void nsIFrame::BoxReflow(nsBoxLayoutState& aState, nsPresContext* aPresContext,
10766                          ReflowOutput& aDesiredSize,
10767                          gfxContext* aRenderingContext, nscoord aX, nscoord aY,
10768                          nscoord aWidth, nscoord aHeight, bool aMoveFrame) {
10769   DO_GLOBAL_REFLOW_COUNT("nsBoxToBlockAdaptor");
10770 
10771 #ifdef DEBUG_REFLOW
10772   nsAdaptorAddIndents();
10773   printf("Reflowing: ");
10774   mFrame->ListTag(stdout);
10775   printf("\n");
10776   gIndent2++;
10777 #endif
10778 
10779   nsBoxLayoutMetrics* metrics = BoxMetrics();
10780   if (MOZ_UNLIKELY(!metrics)) {
10781     // Can't proceed without BoxMetrics. This should only happen if something
10782     // is seriously broken, e.g. if we try to do XUL layout on a non-XUL frame.
10783     // (If this is a content process, we'll abort even in release builds,
10784     // because XUL layout mixup is extra surprising in content, and aborts are
10785     // less catastrophic in content vs. in chrome.)
10786     MOZ_RELEASE_ASSERT(!XRE_IsContentProcess(),
10787                        "Starting XUL BoxReflow w/o BoxMetrics (in content)?");
10788     MOZ_ASSERT_UNREACHABLE("Starting XUL BoxReflow w/o BoxMetrics?");
10789     return;
10790   }
10791 
10792   nsReflowStatus status;
10793   WritingMode wm = aDesiredSize.GetWritingMode();
10794 
10795   bool needsReflow = NS_SUBTREE_DIRTY(this);
10796 
10797   // if we don't need a reflow then
10798   // lets see if we are already that size. Yes? then don't even reflow. We are
10799   // done.
10800   if (!needsReflow) {
10801     if (aWidth != NS_UNCONSTRAINEDSIZE && aHeight != NS_UNCONSTRAINEDSIZE) {
10802       // if the new calculated size has a 0 width or a 0 height
10803       if ((metrics->mLastSize.width == 0 || metrics->mLastSize.height == 0) &&
10804           (aWidth == 0 || aHeight == 0)) {
10805         needsReflow = false;
10806         aDesiredSize.Width() = aWidth;
10807         aDesiredSize.Height() = aHeight;
10808         SetSize(aDesiredSize.Size(wm).ConvertTo(GetWritingMode(), wm));
10809       } else {
10810         aDesiredSize.Width() = metrics->mLastSize.width;
10811         aDesiredSize.Height() = metrics->mLastSize.height;
10812 
10813         // remove the margin. The rect of our child does not include it but our
10814         // calculated size does. don't reflow if we are already the right size
10815         if (metrics->mLastSize.width == aWidth &&
10816             metrics->mLastSize.height == aHeight)
10817           needsReflow = false;
10818         else
10819           needsReflow = true;
10820       }
10821     } else {
10822       // if the width or height are intrinsic alway reflow because
10823       // we don't know what it should be.
10824       needsReflow = true;
10825     }
10826   }
10827 
10828   // ok now reflow the child into the spacers calculated space
10829   if (needsReflow) {
10830     aDesiredSize.ClearSize();
10831 
10832     // create a reflow input to tell our child to flow at the given size.
10833 
10834     // Construct a bogus parent reflow input so that there's a usable
10835     // containing block reflow input.
10836     nsMargin margin(0, 0, 0, 0);
10837     GetXULMargin(margin);
10838 
10839     nsSize parentSize(aWidth, aHeight);
10840     if (parentSize.height != NS_UNCONSTRAINEDSIZE)
10841       parentSize.height += margin.TopBottom();
10842     if (parentSize.width != NS_UNCONSTRAINEDSIZE)
10843       parentSize.width += margin.LeftRight();
10844 
10845     nsIFrame* parentFrame = GetParent();
10846     WritingMode parentWM = parentFrame->GetWritingMode();
10847     ReflowInput parentReflowInput(aPresContext, parentFrame, aRenderingContext,
10848                                   LogicalSize(parentWM, parentSize),
10849                                   ReflowInput::DUMMY_PARENT_REFLOW_INPUT);
10850 
10851     // This may not do very much useful, but it's probably worth trying.
10852     if (parentSize.width != NS_UNCONSTRAINEDSIZE)
10853       parentReflowInput.SetComputedWidth(std::max(parentSize.width, 0));
10854     if (parentSize.height != NS_UNCONSTRAINEDSIZE)
10855       parentReflowInput.SetComputedHeight(std::max(parentSize.height, 0));
10856     parentReflowInput.ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
10857     // XXX use box methods
10858     parentFrame->GetXULPadding(parentReflowInput.ComputedPhysicalPadding());
10859     parentFrame->GetXULBorder(
10860         parentReflowInput.ComputedPhysicalBorderPadding());
10861     parentReflowInput.ComputedPhysicalBorderPadding() +=
10862         parentReflowInput.ComputedPhysicalPadding();
10863 
10864     // Construct the parent chain manually since constructing it normally
10865     // messes up dimensions.
10866     const ReflowInput* outerReflowInput = aState.OuterReflowInput();
10867     NS_ASSERTION(!outerReflowInput || outerReflowInput->mFrame != this,
10868                  "in and out of XUL on a single frame?");
10869     const ReflowInput* parentRI;
10870     if (outerReflowInput && outerReflowInput->mFrame == parentFrame) {
10871       // We're a frame (such as a text control frame) that jumps into
10872       // box reflow and then straight out of it on the child frame.
10873       // This means we actually have a real parent reflow input.
10874       // nsLayoutUtils::InflationMinFontSizeFor used to need this to be
10875       // linked up correctly for text control frames, so do so here).
10876       parentRI = outerReflowInput;
10877     } else {
10878       parentRI = &parentReflowInput;
10879     }
10880 
10881     // XXX Is it OK that this reflow input has only one ancestor?
10882     // (It used to have a bogus parent, skipping all the boxes).
10883     WritingMode wm = GetWritingMode();
10884     LogicalSize logicalSize(wm, nsSize(aWidth, aHeight));
10885     logicalSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
10886     ReflowInput reflowInput(aPresContext, *parentRI, this, logicalSize,
10887                             Nothing(), ReflowInput::DUMMY_PARENT_REFLOW_INPUT);
10888 
10889     // XXX_jwir3: This is somewhat fishy. If this is actually changing the value
10890     //            here (which it might be), then we should make sure that it's
10891     //            correct the first time around, rather than changing it later.
10892     reflowInput.mCBReflowInput = parentRI;
10893 
10894     reflowInput.mReflowDepth = aState.GetReflowDepth();
10895 
10896     // mComputedWidth and mComputedHeight are content-box, not
10897     // border-box
10898     if (aWidth != NS_UNCONSTRAINEDSIZE) {
10899       nscoord computedWidth =
10900           aWidth - reflowInput.ComputedPhysicalBorderPadding().LeftRight();
10901       computedWidth = std::max(computedWidth, 0);
10902       reflowInput.SetComputedWidth(computedWidth);
10903     }
10904 
10905     // Most child frames of box frames (e.g. subdocument or scroll frames)
10906     // need to be constrained to the provided size and overflow as necessary.
10907     // The one exception are block frames, because we need to know their
10908     // natural height excluding any overflow area which may be caused by
10909     // various CSS effects such as shadow or outline.
10910     if (!IsBlockFrameOrSubclass()) {
10911       if (aHeight != NS_UNCONSTRAINEDSIZE) {
10912         nscoord computedHeight =
10913             aHeight - reflowInput.ComputedPhysicalBorderPadding().TopBottom();
10914         computedHeight = std::max(computedHeight, 0);
10915         reflowInput.SetComputedHeight(computedHeight);
10916       } else {
10917         reflowInput.SetComputedHeight(
10918             ComputeSize(aRenderingContext, wm, logicalSize,
10919                         logicalSize.ISize(wm),
10920                         reflowInput.ComputedLogicalMargin().Size(wm),
10921                         reflowInput.ComputedLogicalBorderPadding().Size(wm) -
10922                             reflowInput.ComputedLogicalPadding().Size(wm),
10923                         reflowInput.ComputedLogicalPadding().Size(wm),
10924                         ComputeSizeFlags::eDefault)
10925                 .Height(wm));
10926       }
10927     }
10928 
10929     // Box layout calls SetRect before XULLayout, whereas non-box layout
10930     // calls SetRect after Reflow.
10931     // XXX Perhaps we should be doing this by twiddling the rect back to
10932     // mLastSize before calling Reflow and then switching it back, but
10933     // However, mLastSize can also be the size passed to BoxReflow by
10934     // RefreshSizeCache, so that doesn't really make sense.
10935     if (metrics->mLastSize.width != aWidth) {
10936       reflowInput.SetHResize(true);
10937 
10938       // When font size inflation is enabled, a horizontal resize
10939       // requires a full reflow.  See ReflowInput::InitResizeFlags
10940       // for more details.
10941       if (nsLayoutUtils::FontSizeInflationEnabled(aPresContext)) {
10942         this->MarkSubtreeDirty();
10943       }
10944     }
10945     if (metrics->mLastSize.height != aHeight) {
10946       reflowInput.SetVResize(true);
10947     }
10948 
10949 #ifdef DEBUG_REFLOW
10950     nsAdaptorAddIndents();
10951     printf("Size=(%d,%d)\n", reflowInput.ComputedWidth(),
10952            reflowInput.ComputedHeight());
10953     nsAdaptorAddIndents();
10954     nsAdaptorPrintReason(reflowInput);
10955     printf("\n");
10956 #endif
10957 
10958     // place the child and reflow
10959 
10960     Reflow(aPresContext, aDesiredSize, reflowInput, status);
10961 
10962     NS_ASSERTION(status.IsComplete(), "bad status");
10963 
10964     ReflowChildFlags layoutFlags = aState.LayoutFlags();
10965     nsContainerFrame::FinishReflowChild(
10966         this, aPresContext, aDesiredSize, &reflowInput, aX, aY,
10967         layoutFlags | ReflowChildFlags::NoMoveFrame);
10968 
10969     // Save the ascent.  (bug 103925)
10970     if (IsXULCollapsed()) {
10971       metrics->mAscent = 0;
10972     } else {
10973       if (aDesiredSize.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE) {
10974         if (!nsLayoutUtils::GetFirstLineBaseline(wm, this, &metrics->mAscent))
10975           metrics->mAscent = GetLogicalBaseline(wm);
10976       } else
10977         metrics->mAscent = aDesiredSize.BlockStartAscent();
10978     }
10979 
10980   } else {
10981     aDesiredSize.SetBlockStartAscent(metrics->mBlockAscent);
10982   }
10983 
10984 #ifdef DEBUG_REFLOW
10985   if (aHeight != NS_UNCONSTRAINEDSIZE && aDesiredSize.Height() != aHeight) {
10986     nsAdaptorAddIndents();
10987     printf("*****got taller!*****\n");
10988   }
10989   if (aWidth != NS_UNCONSTRAINEDSIZE && aDesiredSize.Width() != aWidth) {
10990     nsAdaptorAddIndents();
10991     printf("*****got wider!******\n");
10992   }
10993 #endif
10994 
10995   metrics->mLastSize.width = aDesiredSize.Width();
10996   metrics->mLastSize.height = aDesiredSize.Height();
10997 
10998 #ifdef DEBUG_REFLOW
10999   gIndent2--;
11000 #endif
11001 }
11002 
BoxMetrics() const11003 nsBoxLayoutMetrics* nsIFrame::BoxMetrics() const {
11004   nsBoxLayoutMetrics* metrics = GetProperty(BoxMetricsProperty());
11005   NS_ASSERTION(
11006       metrics,
11007       "A box layout method was called but InitBoxMetrics was never called");
11008   return metrics;
11009 }
11010 
UpdateStyleOfChildAnonBox(nsIFrame * aChildFrame,ServoRestyleState & aRestyleState)11011 void nsIFrame::UpdateStyleOfChildAnonBox(nsIFrame* aChildFrame,
11012                                          ServoRestyleState& aRestyleState) {
11013 #ifdef DEBUG
11014   nsIFrame* parent = aChildFrame->GetInFlowParent();
11015   if (aChildFrame->IsTableFrame()) {
11016     parent = parent->GetParent();
11017   }
11018   if (parent->IsLineFrame()) {
11019     parent = parent->GetParent();
11020   }
11021   MOZ_ASSERT(nsLayoutUtils::FirstContinuationOrIBSplitSibling(parent) == this,
11022              "This should only be used for children!");
11023 #endif  // DEBUG
11024   MOZ_ASSERT(!GetContent() || !aChildFrame->GetContent() ||
11025                  aChildFrame->GetContent() == GetContent(),
11026              "What content node is it a frame for?");
11027   MOZ_ASSERT(!aChildFrame->GetPrevContinuation(),
11028              "Only first continuations should end up here");
11029 
11030   // We could force the caller to pass in the pseudo, since some callers know it
11031   // statically...  But this API is a bit nicer.
11032   auto pseudo = aChildFrame->Style()->GetPseudoType();
11033   MOZ_ASSERT(PseudoStyle::IsAnonBox(pseudo), "Child is not an anon box?");
11034   MOZ_ASSERT(!PseudoStyle::IsNonInheritingAnonBox(pseudo),
11035              "Why did the caller bother calling us?");
11036 
11037   // Anon boxes inherit from their parent; that's us.
11038   RefPtr<ComputedStyle> newContext =
11039       aRestyleState.StyleSet().ResolveInheritingAnonymousBoxStyle(pseudo,
11040                                                                   Style());
11041 
11042   nsChangeHint childHint =
11043       UpdateStyleOfOwnedChildFrame(aChildFrame, newContext, aRestyleState);
11044 
11045   // Now that we've updated the style on aChildFrame, check whether it itself
11046   // has anon boxes to deal with.
11047   ServoRestyleState childrenState(*aChildFrame, aRestyleState, childHint,
11048                                   ServoRestyleState::Type::InFlow);
11049   aChildFrame->UpdateStyleOfOwnedAnonBoxes(childrenState);
11050 
11051   // Assuming anon boxes don't have ::backdrop associated with them... if that
11052   // ever changes, we'd need to handle that here, like we do in
11053   // RestyleManager::ProcessPostTraversal
11054 
11055   // We do need to handle block pseudo-elements here, though.  Especially list
11056   // bullets.
11057   if (nsBlockFrame* block = do_QueryFrame(aChildFrame)) {
11058     block->UpdatePseudoElementStyles(childrenState);
11059   }
11060 }
11061 
11062 /* static */
UpdateStyleOfOwnedChildFrame(nsIFrame * aChildFrame,ComputedStyle * aNewComputedStyle,ServoRestyleState & aRestyleState,const Maybe<ComputedStyle * > & aContinuationComputedStyle)11063 nsChangeHint nsIFrame::UpdateStyleOfOwnedChildFrame(
11064     nsIFrame* aChildFrame, ComputedStyle* aNewComputedStyle,
11065     ServoRestyleState& aRestyleState,
11066     const Maybe<ComputedStyle*>& aContinuationComputedStyle) {
11067   MOZ_ASSERT(!aChildFrame->GetAdditionalComputedStyle(0),
11068              "We don't handle additional styles here");
11069 
11070   // Figure out whether we have an actual change.  It's important that we do
11071   // this, for several reasons:
11072   //
11073   // 1) Even if all the child's changes are due to properties it inherits from
11074   //    us, it's possible that no one ever asked us for those style structs and
11075   //    hence changes to them aren't reflected in the changes handled at all.
11076   //
11077   // 2) Content can change stylesheets that change the styles of pseudos, and
11078   //    extensions can add/remove stylesheets that change the styles of
11079   //    anonymous boxes directly.
11080   uint32_t equalStructs;  // Not used, actually.
11081   nsChangeHint childHint = aChildFrame->Style()->CalcStyleDifference(
11082       *aNewComputedStyle, &equalStructs);
11083 
11084   // If aChildFrame is out of flow, then aRestyleState's "changes handled by the
11085   // parent" doesn't apply to it, because it may have some other parent in the
11086   // frame tree.
11087   if (!aChildFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
11088     childHint = NS_RemoveSubsumedHints(
11089         childHint, aRestyleState.ChangesHandledFor(aChildFrame));
11090   }
11091   if (childHint) {
11092     if (childHint & nsChangeHint_ReconstructFrame) {
11093       // If we generate a reconstruct here, remove any non-reconstruct hints we
11094       // may have already generated for this content.
11095       aRestyleState.ChangeList().PopChangesForContent(
11096           aChildFrame->GetContent());
11097     }
11098     aRestyleState.ChangeList().AppendChange(
11099         aChildFrame, aChildFrame->GetContent(), childHint);
11100   }
11101 
11102   aChildFrame->SetComputedStyle(aNewComputedStyle);
11103   ComputedStyle* continuationStyle = aContinuationComputedStyle
11104                                          ? *aContinuationComputedStyle
11105                                          : aNewComputedStyle;
11106   for (nsIFrame* kid = aChildFrame->GetNextContinuation(); kid;
11107        kid = kid->GetNextContinuation()) {
11108     MOZ_ASSERT(!kid->GetAdditionalComputedStyle(0));
11109     kid->SetComputedStyle(continuationStyle);
11110   }
11111 
11112   return childHint;
11113 }
11114 
11115 /* static */
AddInPopupStateBitToDescendants(nsIFrame * aFrame)11116 void nsIFrame::AddInPopupStateBitToDescendants(nsIFrame* aFrame) {
11117   if (!aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP) &&
11118       aFrame->TrackingVisibility()) {
11119     // Assume all frames in popups are visible.
11120     aFrame->IncApproximateVisibleCount();
11121   }
11122 
11123   aFrame->AddStateBits(NS_FRAME_IN_POPUP);
11124 
11125   for (const auto& childList : aFrame->CrossDocChildLists()) {
11126     for (nsIFrame* child : childList.mList) {
11127       AddInPopupStateBitToDescendants(child);
11128     }
11129   }
11130 }
11131 
11132 /* static */
RemoveInPopupStateBitFromDescendants(nsIFrame * aFrame)11133 void nsIFrame::RemoveInPopupStateBitFromDescendants(nsIFrame* aFrame) {
11134   if (!aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP) ||
11135       nsLayoutUtils::IsPopup(aFrame)) {
11136     return;
11137   }
11138 
11139   aFrame->RemoveStateBits(NS_FRAME_IN_POPUP);
11140 
11141   if (aFrame->TrackingVisibility()) {
11142     // We assume all frames in popups are visible, so this decrement balances
11143     // out the increment in AddInPopupStateBitToDescendants above.
11144     aFrame->DecApproximateVisibleCount();
11145   }
11146   for (const auto& childList : aFrame->CrossDocChildLists()) {
11147     for (nsIFrame* child : childList.mList) {
11148       RemoveInPopupStateBitFromDescendants(child);
11149     }
11150   }
11151 }
11152 
SetParent(nsContainerFrame * aParent)11153 void nsIFrame::SetParent(nsContainerFrame* aParent) {
11154   // If our parent is a wrapper anon box, our new parent should be too.  We
11155   // _can_ change parent if our parent is a wrapper anon box, because some
11156   // wrapper anon boxes can have continuations.
11157   MOZ_ASSERT_IF(ParentIsWrapperAnonBox(),
11158                 aParent->Style()->IsInheritingAnonBox());
11159 
11160   // Note that the current mParent may already be destroyed at this point.
11161   mParent = aParent;
11162   MOZ_DIAGNOSTIC_ASSERT(!mParent || PresShell() == mParent->PresShell());
11163   if (::IsXULBoxWrapped(this)) {
11164     ::InitBoxMetrics(this, true);
11165   } else {
11166     // We could call Properties().Delete(BoxMetricsProperty()); here but
11167     // that's kind of slow and re-parenting in such a way that we were
11168     // IsXULBoxWrapped() before but not now should be very rare, so we'll just
11169     // keep this unused frame property until this frame dies instead.
11170   }
11171 
11172   if (GetStateBits() & (NS_FRAME_HAS_VIEW | NS_FRAME_HAS_CHILD_WITH_VIEW)) {
11173     for (nsIFrame* f = aParent;
11174          f && !(f->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW);
11175          f = f->GetParent()) {
11176       f->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
11177     }
11178   }
11179 
11180   if (HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
11181     for (nsIFrame* f = aParent; f; f = f->GetParent()) {
11182       if (f->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
11183         break;
11184       }
11185       f->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
11186     }
11187   }
11188 
11189   if (HasAnyStateBits(NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)) {
11190     for (nsIFrame* f = aParent; f; f = f->GetParent()) {
11191       if (f->HasAnyStateBits(
11192               NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)) {
11193         break;
11194       }
11195       f->AddStateBits(NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE);
11196     }
11197   }
11198 
11199   if (HasInvalidFrameInSubtree()) {
11200     for (nsIFrame* f = aParent;
11201          f && !f->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT |
11202                                   NS_FRAME_IS_NONDISPLAY);
11203          f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
11204       f->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
11205     }
11206   }
11207 
11208   if (aParent->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
11209     AddInPopupStateBitToDescendants(this);
11210   } else {
11211     RemoveInPopupStateBitFromDescendants(this);
11212   }
11213 
11214   // If our new parent only has invalid children, then we just invalidate
11215   // ourselves too. This is probably faster than clearing the flag all
11216   // the way up the frame tree.
11217   if (aParent->HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT)) {
11218     InvalidateFrame();
11219   } else {
11220     SchedulePaint();
11221   }
11222 }
11223 
CreateOwnLayerIfNeeded(nsDisplayListBuilder * aBuilder,nsDisplayList * aList,uint16_t aType,bool * aCreatedContainerItem)11224 void nsIFrame::CreateOwnLayerIfNeeded(nsDisplayListBuilder* aBuilder,
11225                                       nsDisplayList* aList, uint16_t aType,
11226                                       bool* aCreatedContainerItem) {
11227   if (GetContent() && GetContent()->IsXULElement() &&
11228       GetContent()->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::layer)) {
11229     aList->AppendNewToTopWithIndex<nsDisplayOwnLayer>(
11230         aBuilder, this, /* aIndex = */ aType, aList,
11231         aBuilder->CurrentActiveScrolledRoot(), nsDisplayOwnLayerFlags::None,
11232         ScrollbarData{}, true, false);
11233     if (aCreatedContainerItem) {
11234       *aCreatedContainerItem = true;
11235     }
11236   }
11237 }
11238 
IsStackingContext(const nsStyleDisplay * aStyleDisplay,const nsStylePosition * aStylePosition,const nsStyleEffects * aStyleEffects,bool aIsPositioned)11239 bool nsIFrame::IsStackingContext(const nsStyleDisplay* aStyleDisplay,
11240                                  const nsStylePosition* aStylePosition,
11241                                  const nsStyleEffects* aStyleEffects,
11242                                  bool aIsPositioned) {
11243   return HasOpacity(aStyleDisplay, aStyleEffects, nullptr) ||
11244          IsTransformed(aStyleDisplay) ||
11245          ((aStyleDisplay->IsContainPaint() ||
11246            aStyleDisplay->IsContainLayout()) &&
11247           IsFrameOfType(eSupportsContainLayoutAndPaint)) ||
11248          // strictly speaking, 'perspective' doesn't require visual atomicity,
11249          // but the spec says it acts like the rest of these
11250          ChildrenHavePerspective(aStyleDisplay) ||
11251          aStyleEffects->mMixBlendMode != StyleBlend::Normal ||
11252          nsSVGIntegrationUtils::UsingEffectsForFrame(this) ||
11253          (aIsPositioned && (aStyleDisplay->IsPositionForcingStackingContext() ||
11254                             aStylePosition->mZIndex.IsInteger())) ||
11255          (aStyleDisplay->mWillChange.bits &
11256           StyleWillChangeBits::STACKING_CONTEXT) ||
11257          aStyleDisplay->mIsolation != StyleIsolation::Auto ||
11258          aStyleEffects->HasBackdropFilters();
11259 }
11260 
IsStackingContext()11261 bool nsIFrame::IsStackingContext() {
11262   const nsStyleDisplay* disp = StyleDisplay();
11263   const bool isPositioned = disp->IsAbsPosContainingBlock(this);
11264   return IsStackingContext(disp, StylePosition(), StyleEffects(), isPositioned);
11265 }
11266 
IsFrameScrolledOutOfView(const nsIFrame * aTarget,const nsRect & aTargetRect,const nsIFrame * aParent)11267 static bool IsFrameScrolledOutOfView(const nsIFrame* aTarget,
11268                                      const nsRect& aTargetRect,
11269                                      const nsIFrame* aParent) {
11270   // The ancestor frame we are checking if it clips out aTargetRect relative to
11271   // aTarget.
11272   nsIFrame* clipParent = nullptr;
11273 
11274   // find the first scrollable frame or root frame if we are in a fixed pos
11275   // subtree
11276   for (nsIFrame* f = const_cast<nsIFrame*>(aParent); f;
11277        f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
11278     nsIScrollableFrame* scrollableFrame = do_QueryFrame(f);
11279     if (scrollableFrame) {
11280       clipParent = f;
11281       break;
11282     }
11283     if (f->StyleDisplay()->mPosition == StylePositionProperty::Fixed &&
11284         nsLayoutUtils::IsReallyFixedPos(f)) {
11285       clipParent = f->GetParent();
11286       break;
11287     }
11288   }
11289 
11290   if (!clipParent) {
11291     // Even if we couldn't find the nearest scrollable frame, it might mean we
11292     // are in an out-of-process iframe, try to see if |aTarget| frame is
11293     // scrolled out of view in an scrollable frame in a cross-process ancestor
11294     // document.
11295     return nsLayoutUtils::FrameIsScrolledOutOfViewInCrossProcess(aTarget);
11296   }
11297 
11298   nsRect clipRect = clipParent->GetVisualOverflowRectRelativeToSelf();
11299   // We consider that the target is scrolled out if the scrollable (or root)
11300   // frame is empty.
11301   if (clipRect.IsEmpty()) {
11302     return true;
11303   }
11304 
11305   nsRect transformedRect = nsLayoutUtils::TransformFrameRectToAncestor(
11306       aTarget, aTargetRect, clipParent);
11307 
11308   if (transformedRect.IsEmpty()) {
11309     // If the transformed rect is empty it represents a line or a point that we
11310     // should check is outside the the scrollable rect.
11311     if (transformedRect.x > clipRect.XMost() ||
11312         transformedRect.y > clipRect.YMost() ||
11313         clipRect.x > transformedRect.XMost() ||
11314         clipRect.y > transformedRect.YMost()) {
11315       return true;
11316     }
11317   } else if (!transformedRect.Intersects(clipRect)) {
11318     return true;
11319   }
11320 
11321   nsIFrame* parent = clipParent->GetParent();
11322   if (!parent) {
11323     return false;
11324   }
11325 
11326   return IsFrameScrolledOutOfView(aTarget, aTargetRect, parent);
11327 }
11328 
IsScrolledOutOfView() const11329 bool nsIFrame::IsScrolledOutOfView() const {
11330   nsRect rect = GetVisualOverflowRectRelativeToSelf();
11331   return IsFrameScrolledOutOfView(this, rect, this);
11332 }
11333 
ComputeWidgetTransform()11334 gfx::Matrix nsIFrame::ComputeWidgetTransform() {
11335   const nsStyleUIReset* uiReset = StyleUIReset();
11336   if (uiReset->mMozWindowTransform.IsNone()) {
11337     return gfx::Matrix();
11338   }
11339 
11340   TransformReferenceBox refBox(nullptr, nsRect(nsPoint(), GetSize()));
11341 
11342   nsPresContext* presContext = PresContext();
11343   int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
11344   gfx::Matrix4x4 matrix = nsStyleTransformMatrix::ReadTransforms(
11345       uiReset->mMozWindowTransform, refBox, float(appUnitsPerDevPixel));
11346 
11347   // Apply the -moz-window-transform-origin translation to the matrix.
11348   const StyleTransformOrigin& origin = uiReset->mWindowTransformOrigin;
11349   Point transformOrigin = nsStyleTransformMatrix::Convert2DPosition(
11350       origin.horizontal, origin.vertical, refBox, appUnitsPerDevPixel);
11351   matrix.ChangeBasis(Point3D(transformOrigin.x, transformOrigin.y, 0));
11352 
11353   gfx::Matrix result2d;
11354   if (!matrix.CanDraw2D(&result2d)) {
11355     // FIXME: It would be preferable to reject non-2D transforms at parse time.
11356     NS_WARNING(
11357         "-moz-window-transform does not describe a 2D transform, "
11358         "but only 2d transforms are supported");
11359     return gfx::Matrix();
11360   }
11361 
11362   return result2d;
11363 }
11364 
DoUpdateStyleOfOwnedAnonBoxes(ServoRestyleState & aRestyleState)11365 void nsIFrame::DoUpdateStyleOfOwnedAnonBoxes(ServoRestyleState& aRestyleState) {
11366   // As a special case, we check for {ib}-split block frames here, rather
11367   // than have an nsInlineFrame::AppendDirectlyOwnedAnonBoxes implementation
11368   // that returns them.
11369   //
11370   // (If we did handle them in AppendDirectlyOwnedAnonBoxes, we would have to
11371   // return *all* of the in-flow {ib}-split block frames, not just the first
11372   // one.  For restyling, we really just need the first in flow, and the other
11373   // user of the AppendOwnedAnonBoxes API, AllChildIterator, doesn't need to
11374   // know about them at all, since these block frames never create NAC.  So we
11375   // avoid any unncessary hashtable lookups for the {ib}-split frames by calling
11376   // UpdateStyleOfOwnedAnonBoxesForIBSplit directly here.)
11377   if (IsInlineFrame()) {
11378     if ((GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
11379       static_cast<nsInlineFrame*>(this)->UpdateStyleOfOwnedAnonBoxesForIBSplit(
11380           aRestyleState);
11381     }
11382     return;
11383   }
11384 
11385   AutoTArray<OwnedAnonBox, 4> frames;
11386   AppendDirectlyOwnedAnonBoxes(frames);
11387   for (OwnedAnonBox& box : frames) {
11388     if (box.mUpdateStyleFn) {
11389       box.mUpdateStyleFn(this, box.mAnonBoxFrame, aRestyleState);
11390     } else {
11391       UpdateStyleOfChildAnonBox(box.mAnonBoxFrame, aRestyleState);
11392     }
11393   }
11394 }
11395 
11396 /* virtual */
AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox> & aResult)11397 void nsIFrame::AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) {
11398   MOZ_ASSERT(!(GetStateBits() & NS_FRAME_OWNS_ANON_BOXES));
11399   MOZ_ASSERT(false, "Why did this get called?");
11400 }
11401 
DoAppendOwnedAnonBoxes(nsTArray<OwnedAnonBox> & aResult)11402 void nsIFrame::DoAppendOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) {
11403   size_t i = aResult.Length();
11404   AppendDirectlyOwnedAnonBoxes(aResult);
11405 
11406   // After appending the directly owned anonymous boxes of this frame to
11407   // aResult above, we need to check each of them to see if they own
11408   // any anonymous boxes themselves.  Note that we keep progressing
11409   // through aResult, looking for additional entries in aResult from these
11410   // subsequent AppendDirectlyOwnedAnonBoxes calls.  (Thus we can't
11411   // use a ranged for loop here.)
11412 
11413   while (i < aResult.Length()) {
11414     nsIFrame* f = aResult[i].mAnonBoxFrame;
11415     if (f->GetStateBits() & NS_FRAME_OWNS_ANON_BOXES) {
11416       f->AppendDirectlyOwnedAnonBoxes(aResult);
11417     }
11418     ++i;
11419   }
11420 }
11421 
CaretPosition()11422 nsIFrame::CaretPosition::CaretPosition() : mContentOffset(0) {}
11423 
11424 nsIFrame::CaretPosition::~CaretPosition() = default;
11425 
HasCSSAnimations()11426 bool nsFrame::HasCSSAnimations() {
11427   auto collection =
11428       AnimationCollection<CSSAnimation>::GetAnimationCollection(this);
11429   return collection && collection->mAnimations.Length() > 0;
11430 }
11431 
HasCSSTransitions()11432 bool nsFrame::HasCSSTransitions() {
11433   auto collection =
11434       AnimationCollection<CSSTransition>::GetAnimationCollection(this);
11435   return collection && collection->mAnimations.Length() > 0;
11436 }
11437 
AddSizeOfExcludingThisForTree(nsWindowSizes & aSizes) const11438 void nsIFrame::AddSizeOfExcludingThisForTree(nsWindowSizes& aSizes) const {
11439   aSizes.mLayoutFramePropertiesSize +=
11440       mProperties.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
11441 
11442   // We don't do this for Gecko because this stuff is stored in the nsPresArena
11443   // and so measured elsewhere.
11444   if (!aSizes.mState.HaveSeenPtr(mComputedStyle)) {
11445     mComputedStyle->AddSizeOfIncludingThis(aSizes,
11446                                            &aSizes.mLayoutComputedValuesNonDom);
11447   }
11448 
11449   // And our additional styles.
11450   int32_t index = 0;
11451   while (auto* extra = GetAdditionalComputedStyle(index++)) {
11452     if (!aSizes.mState.HaveSeenPtr(extra)) {
11453       extra->AddSizeOfIncludingThis(aSizes,
11454                                     &aSizes.mLayoutComputedValuesNonDom);
11455     }
11456   }
11457 
11458   for (const auto& childList : ChildLists()) {
11459     for (const nsIFrame* f : childList.mList) {
11460       f->AddSizeOfExcludingThisForTree(aSizes);
11461     }
11462   }
11463 }
11464 
GetCompositorHitTestArea(nsDisplayListBuilder * aBuilder)11465 nsRect nsIFrame::GetCompositorHitTestArea(nsDisplayListBuilder* aBuilder) {
11466   nsRect area;
11467 
11468   nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetScrollableFrameFor(this);
11469   if (scrollFrame) {
11470     // If the frame is content of a scrollframe, then we need to pick up the
11471     // area corresponding to the overflow rect as well. Otherwise the parts of
11472     // the overflow that are not occupied by descendants get skipped and the
11473     // APZ code sends touch events to the content underneath instead.
11474     // See https://bugzilla.mozilla.org/show_bug.cgi?id=1127773#c15.
11475     area = GetScrollableOverflowRect();
11476   } else {
11477     area = nsRect(nsPoint(0, 0), GetSize());
11478   }
11479 
11480   if (!area.IsEmpty()) {
11481     return area + aBuilder->ToReferenceFrame(this);
11482   }
11483 
11484   return area;
11485 }
11486 
GetCompositorHitTestInfo(nsDisplayListBuilder * aBuilder)11487 CompositorHitTestInfo nsIFrame::GetCompositorHitTestInfo(
11488     nsDisplayListBuilder* aBuilder) {
11489   CompositorHitTestInfo result = CompositorHitTestInvisibleToHit;
11490 
11491   if (aBuilder->IsInsidePointerEventsNoneDoc()) {
11492     // Somewhere up the parent document chain is a subdocument with pointer-
11493     // events:none set on it.
11494     return result;
11495   }
11496   if (!GetParent()) {
11497     MOZ_ASSERT(IsViewportFrame());
11498     // Viewport frames are never event targets, other frames, like canvas
11499     // frames, are the event targets for any regions viewport frames may cover.
11500     return result;
11501   }
11502   const StylePointerEvents pointerEvents =
11503       StyleUI()->GetEffectivePointerEvents(this);
11504   if (pointerEvents == StylePointerEvents::None) {
11505     return result;
11506   }
11507   if (!StyleVisibility()->IsVisible()) {
11508     return result;
11509   }
11510 
11511   // Anything that didn't match the above conditions is visible to hit-testing.
11512   result = CompositorHitTestFlags::eVisibleToHitTest;
11513   if (nsSVGIntegrationUtils::UsingMaskOrClipPathForFrame(this)) {
11514     // If WebRender is enabled, simple clip-paths can be converted into WR
11515     // clips that WR knows how to hit-test against, so we don't need to mark
11516     // it as an irregular area.
11517     if (!gfxVars::UseWebRender() ||
11518         !nsSVGIntegrationUtils::UsingSimpleClipPathForFrame(this)) {
11519       result += CompositorHitTestFlags::eIrregularArea;
11520     }
11521   }
11522 
11523   if (aBuilder->IsBuildingNonLayerizedScrollbar()) {
11524     // Scrollbars may be painted into a layer below the actual layer they will
11525     // scroll, and therefore wheel events may be dispatched to the outer frame
11526     // instead of the intended scrollframe. To address this, we force a d-t-c
11527     // region on scrollbar frames that won't be placed in their own layer. See
11528     // bug 1213324 for details.
11529     result += CompositorHitTestFlags::eInactiveScrollframe;
11530   } else if (aBuilder->GetAncestorHasApzAwareEventHandler()) {
11531     result += CompositorHitTestFlags::eApzAwareListeners;
11532   } else if (IsObjectFrame()) {
11533     // If the frame is a plugin frame and wants to handle wheel events as
11534     // default action, we should add the frame to dispatch-to-content region.
11535     nsPluginFrame* pluginFrame = do_QueryFrame(this);
11536     if (pluginFrame && pluginFrame->WantsToHandleWheelEventAsDefaultAction()) {
11537       result += CompositorHitTestFlags::eApzAwareListeners;
11538     }
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->GetHitTestInfo() & 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 =
11573         nsLayoutUtils::GetTouchActionFromFrame(touchActionFrame);
11574     // The CSS allows the syntax auto | none | [pan-x || pan-y] | manipulation
11575     // so we can eliminate some combinations of things.
11576     if (touchAction == StyleTouchAction::AUTO) {
11577       // nothing to do
11578     } else if (touchAction & StyleTouchAction::MANIPULATION) {
11579       result += CompositorHitTestFlags::eTouchActionDoubleTapZoomDisabled;
11580     } else {
11581       // This path handles the cases none | [pan-x || pan-y] and so both
11582       // double-tap and pinch zoom are disabled in here.
11583       result += CompositorHitTestFlags::eTouchActionPinchZoomDisabled;
11584       result += CompositorHitTestFlags::eTouchActionDoubleTapZoomDisabled;
11585 
11586       if (!(touchAction & StyleTouchAction::PAN_X)) {
11587         result += CompositorHitTestFlags::eTouchActionPanXDisabled;
11588       }
11589       if (!(touchAction & StyleTouchAction::PAN_Y)) {
11590         result += CompositorHitTestFlags::eTouchActionPanYDisabled;
11591       }
11592       if (touchAction & StyleTouchAction::NONE) {
11593         // all the touch-action disabling flags will already have been set above
11594         MOZ_ASSERT(result.contains(CompositorHitTestTouchActionMask));
11595       }
11596     }
11597   }
11598 
11599   const Maybe<ScrollDirection> scrollDirection =
11600       aBuilder->GetCurrentScrollbarDirection();
11601   if (scrollDirection.isSome()) {
11602     if (GetContent()->IsXULElement(nsGkAtoms::thumb)) {
11603       const bool thumbGetsLayer = aBuilder->GetCurrentScrollbarTarget() !=
11604                                   layers::ScrollableLayerGuid::NULL_SCROLL_ID;
11605       if (thumbGetsLayer) {
11606         result += CompositorHitTestFlags::eScrollbarThumb;
11607       } else {
11608         result += CompositorHitTestFlags::eInactiveScrollframe;
11609       }
11610     }
11611 
11612     if (*scrollDirection == ScrollDirection::eVertical) {
11613       result += CompositorHitTestFlags::eScrollbarVertical;
11614     }
11615 
11616     // includes the ScrollbarFrame, SliderFrame, anything else that
11617     // might be inside the xul:scrollbar
11618     result += CompositorHitTestFlags::eScrollbar;
11619   }
11620 
11621   return result;
11622 }
11623 
11624 // Returns true if we can guarantee there is no visible descendants.
HasNoVisibleDescendants(const nsIFrame * aFrame)11625 static bool HasNoVisibleDescendants(const nsIFrame* aFrame) {
11626   for (const auto& childList : aFrame->ChildLists()) {
11627     for (nsIFrame* f : childList.mList) {
11628       if (nsPlaceholderFrame::GetRealFrameFor(f)
11629               ->IsVisibleOrMayHaveVisibleDescendants()) {
11630         return false;
11631       }
11632     }
11633   }
11634   return true;
11635 }
11636 
UpdateVisibleDescendantsState()11637 void nsIFrame::UpdateVisibleDescendantsState() {
11638   if (StyleVisibility()->IsVisible()) {
11639     // Notify invisible ancestors that a visible descendant exists now.
11640     nsIFrame* ancestor;
11641     for (ancestor = GetInFlowParent();
11642          ancestor && !ancestor->StyleVisibility()->IsVisible();
11643          ancestor = ancestor->GetInFlowParent()) {
11644       ancestor->mAllDescendantsAreInvisible = false;
11645     }
11646   } else {
11647     mAllDescendantsAreInvisible = HasNoVisibleDescendants(this);
11648   }
11649 }
11650 
11651 // Box layout debugging
11652 #ifdef DEBUG_REFLOW
11653 int32_t gIndent2 = 0;
11654 
nsAdaptorAddIndents()11655 void nsAdaptorAddIndents() {
11656   for (int32_t i = 0; i < gIndent2; i++) {
11657     printf(" ");
11658   }
11659 }
11660 
nsAdaptorPrintReason(ReflowInput & aReflowInput)11661 void nsAdaptorPrintReason(ReflowInput& aReflowInput) {
11662   char* reflowReasonString;
11663 
11664   switch (aReflowInput.reason) {
11665     case eReflowReason_Initial:
11666       reflowReasonString = "initial";
11667       break;
11668 
11669     case eReflowReason_Resize:
11670       reflowReasonString = "resize";
11671       break;
11672     case eReflowReason_Dirty:
11673       reflowReasonString = "dirty";
11674       break;
11675     case eReflowReason_StyleChange:
11676       reflowReasonString = "stylechange";
11677       break;
11678     case eReflowReason_Incremental: {
11679       switch (aReflowInput.reflowCommand->Type()) {
11680         case eReflowType_StyleChanged:
11681           reflowReasonString = "incremental (StyleChanged)";
11682           break;
11683         case eReflowType_ReflowDirty:
11684           reflowReasonString = "incremental (ReflowDirty)";
11685           break;
11686         default:
11687           reflowReasonString = "incremental (Unknown)";
11688       }
11689     } break;
11690     default:
11691       reflowReasonString = "unknown";
11692       break;
11693   }
11694 
11695   printf("%s", reflowReasonString);
11696 }
11697 
11698 #endif
11699 
11700 #ifdef DEBUG
GetTagName(nsFrame * aFrame,nsIContent * aContent,int aResultSize,char * aResult)11701 static void GetTagName(nsFrame* aFrame, nsIContent* aContent, int aResultSize,
11702                        char* aResult) {
11703   if (aContent) {
11704     snprintf(aResult, aResultSize, "%s@%p",
11705              nsAtomCString(aContent->NodeInfo()->NameAtom()).get(), aFrame);
11706   } else {
11707     snprintf(aResult, aResultSize, "@%p", aFrame);
11708   }
11709 }
11710 
Trace(const char * aMethod,bool aEnter)11711 void nsFrame::Trace(const char* aMethod, bool aEnter) {
11712   if (NS_FRAME_LOG_TEST(sFrameLogModule, NS_FRAME_TRACE_CALLS)) {
11713     char tagbuf[40];
11714     GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
11715     printf_stderr("%s: %s %s", tagbuf, aEnter ? "enter" : "exit", aMethod);
11716   }
11717 }
11718 
Trace(const char * aMethod,bool aEnter,const nsReflowStatus & aStatus)11719 void nsFrame::Trace(const char* aMethod, bool aEnter,
11720                     const nsReflowStatus& aStatus) {
11721   if (NS_FRAME_LOG_TEST(sFrameLogModule, NS_FRAME_TRACE_CALLS)) {
11722     char tagbuf[40];
11723     GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
11724     printf_stderr("%s: %s %s, status=%scomplete%s", tagbuf,
11725                   aEnter ? "enter" : "exit", aMethod,
11726                   aStatus.IsIncomplete() ? "not" : "",
11727                   (aStatus.NextInFlowNeedsReflow()) ? "+reflow" : "");
11728   }
11729 }
11730 
TraceMsg(const char * aFormatString,...)11731 void nsFrame::TraceMsg(const char* aFormatString, ...) {
11732   if (NS_FRAME_LOG_TEST(sFrameLogModule, NS_FRAME_TRACE_CALLS)) {
11733     // Format arguments into a buffer
11734     char argbuf[200];
11735     va_list ap;
11736     va_start(ap, aFormatString);
11737     VsprintfLiteral(argbuf, aFormatString, ap);
11738     va_end(ap);
11739 
11740     char tagbuf[40];
11741     GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
11742     printf_stderr("%s: %s", tagbuf, argbuf);
11743   }
11744 }
11745 
VerifyDirtyBitSet(const nsFrameList & aFrameList)11746 void nsFrame::VerifyDirtyBitSet(const nsFrameList& aFrameList) {
11747   for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
11748     NS_ASSERTION(e.get()->GetStateBits() & NS_FRAME_IS_DIRTY,
11749                  "dirty bit not set");
11750   }
11751 }
11752 
11753 // Start Display Reflow
DR_cookie(nsPresContext * aPresContext,nsIFrame * aFrame,const ReflowInput & aReflowInput,ReflowOutput & aMetrics,nsReflowStatus & aStatus)11754 DR_cookie::DR_cookie(nsPresContext* aPresContext, nsIFrame* aFrame,
11755                      const ReflowInput& aReflowInput, ReflowOutput& aMetrics,
11756                      nsReflowStatus& aStatus)
11757     : mPresContext(aPresContext),
11758       mFrame(aFrame),
11759       mReflowInput(aReflowInput),
11760       mMetrics(aMetrics),
11761       mStatus(aStatus) {
11762   MOZ_COUNT_CTOR(DR_cookie);
11763   mValue = nsFrame::DisplayReflowEnter(aPresContext, mFrame, mReflowInput);
11764 }
11765 
~DR_cookie()11766 DR_cookie::~DR_cookie() {
11767   MOZ_COUNT_DTOR(DR_cookie);
11768   nsFrame::DisplayReflowExit(mPresContext, mFrame, mMetrics, mStatus, mValue);
11769 }
11770 
DR_layout_cookie(nsIFrame * aFrame)11771 DR_layout_cookie::DR_layout_cookie(nsIFrame* aFrame) : mFrame(aFrame) {
11772   MOZ_COUNT_CTOR(DR_layout_cookie);
11773   mValue = nsFrame::DisplayLayoutEnter(mFrame);
11774 }
11775 
~DR_layout_cookie()11776 DR_layout_cookie::~DR_layout_cookie() {
11777   MOZ_COUNT_DTOR(DR_layout_cookie);
11778   nsFrame::DisplayLayoutExit(mFrame, mValue);
11779 }
11780 
DR_intrinsic_inline_size_cookie(nsIFrame * aFrame,const char * aType,nscoord & aResult)11781 DR_intrinsic_inline_size_cookie::DR_intrinsic_inline_size_cookie(
11782     nsIFrame* aFrame, const char* aType, nscoord& aResult)
11783     : mFrame(aFrame), mType(aType), mResult(aResult) {
11784   MOZ_COUNT_CTOR(DR_intrinsic_inline_size_cookie);
11785   mValue = nsFrame::DisplayIntrinsicISizeEnter(mFrame, mType);
11786 }
11787 
~DR_intrinsic_inline_size_cookie()11788 DR_intrinsic_inline_size_cookie::~DR_intrinsic_inline_size_cookie() {
11789   MOZ_COUNT_DTOR(DR_intrinsic_inline_size_cookie);
11790   nsFrame::DisplayIntrinsicISizeExit(mFrame, mType, mResult, mValue);
11791 }
11792 
DR_intrinsic_size_cookie(nsIFrame * aFrame,const char * aType,nsSize & aResult)11793 DR_intrinsic_size_cookie::DR_intrinsic_size_cookie(nsIFrame* aFrame,
11794                                                    const char* aType,
11795                                                    nsSize& aResult)
11796     : mFrame(aFrame), mType(aType), mResult(aResult) {
11797   MOZ_COUNT_CTOR(DR_intrinsic_size_cookie);
11798   mValue = nsFrame::DisplayIntrinsicSizeEnter(mFrame, mType);
11799 }
11800 
~DR_intrinsic_size_cookie()11801 DR_intrinsic_size_cookie::~DR_intrinsic_size_cookie() {
11802   MOZ_COUNT_DTOR(DR_intrinsic_size_cookie);
11803   nsFrame::DisplayIntrinsicSizeExit(mFrame, mType, mResult, mValue);
11804 }
11805 
DR_init_constraints_cookie(nsIFrame * aFrame,ReflowInput * aState,nscoord aCBWidth,nscoord aCBHeight,const nsMargin * aMargin,const nsMargin * aPadding)11806 DR_init_constraints_cookie::DR_init_constraints_cookie(
11807     nsIFrame* aFrame, ReflowInput* aState, nscoord aCBWidth, nscoord aCBHeight,
11808     const nsMargin* aMargin, const nsMargin* aPadding)
11809     : mFrame(aFrame), mState(aState) {
11810   MOZ_COUNT_CTOR(DR_init_constraints_cookie);
11811   mValue = ReflowInput::DisplayInitConstraintsEnter(
11812       mFrame, mState, aCBWidth, aCBHeight, aMargin, aPadding);
11813 }
11814 
~DR_init_constraints_cookie()11815 DR_init_constraints_cookie::~DR_init_constraints_cookie() {
11816   MOZ_COUNT_DTOR(DR_init_constraints_cookie);
11817   ReflowInput::DisplayInitConstraintsExit(mFrame, mState, mValue);
11818 }
11819 
DR_init_offsets_cookie(nsIFrame * aFrame,SizeComputationInput * aState,nscoord aPercentBasis,WritingMode aCBWritingMode,const nsMargin * aMargin,const nsMargin * aPadding)11820 DR_init_offsets_cookie::DR_init_offsets_cookie(nsIFrame* aFrame,
11821                                                SizeComputationInput* aState,
11822                                                nscoord aPercentBasis,
11823                                                WritingMode aCBWritingMode,
11824                                                const nsMargin* aMargin,
11825                                                const nsMargin* aPadding)
11826     : mFrame(aFrame), mState(aState) {
11827   MOZ_COUNT_CTOR(DR_init_offsets_cookie);
11828   mValue = SizeComputationInput::DisplayInitOffsetsEnter(
11829       mFrame, mState, aPercentBasis, aCBWritingMode, aMargin, aPadding);
11830 }
11831 
~DR_init_offsets_cookie()11832 DR_init_offsets_cookie::~DR_init_offsets_cookie() {
11833   MOZ_COUNT_DTOR(DR_init_offsets_cookie);
11834   SizeComputationInput::DisplayInitOffsetsExit(mFrame, mState, mValue);
11835 }
11836 
DR_init_type_cookie(nsIFrame * aFrame,ReflowInput * aState)11837 DR_init_type_cookie::DR_init_type_cookie(nsIFrame* aFrame, ReflowInput* aState)
11838     : mFrame(aFrame), mState(aState) {
11839   MOZ_COUNT_CTOR(DR_init_type_cookie);
11840   mValue = ReflowInput::DisplayInitFrameTypeEnter(mFrame, mState);
11841 }
11842 
~DR_init_type_cookie()11843 DR_init_type_cookie::~DR_init_type_cookie() {
11844   MOZ_COUNT_DTOR(DR_init_type_cookie);
11845   ReflowInput::DisplayInitFrameTypeExit(mFrame, mState, mValue);
11846 }
11847 
11848 struct DR_Rule;
11849 
11850 struct DR_FrameTypeInfo {
11851   DR_FrameTypeInfo(LayoutFrameType aFrameType, const char* aFrameNameAbbrev,
11852                    const char* aFrameName);
11853   ~DR_FrameTypeInfo();
11854 
11855   LayoutFrameType mType;
11856   char mNameAbbrev[16];
11857   char mName[32];
11858   nsTArray<DR_Rule*> mRules;
11859 
11860  private:
11861   DR_FrameTypeInfo& operator=(const DR_FrameTypeInfo&) = delete;
11862 };
11863 
11864 struct DR_FrameTreeNode;
11865 struct DR_Rule;
11866 
11867 struct DR_State {
11868   DR_State();
11869   ~DR_State();
11870   void Init();
11871   void AddFrameTypeInfo(LayoutFrameType aFrameType,
11872                         const char* aFrameNameAbbrev, const char* aFrameName);
11873   DR_FrameTypeInfo* GetFrameTypeInfo(LayoutFrameType aFrameType);
11874   DR_FrameTypeInfo* GetFrameTypeInfo(char* aFrameName);
11875   void InitFrameTypeTable();
11876   DR_FrameTreeNode* CreateTreeNode(nsIFrame* aFrame,
11877                                    const ReflowInput* aReflowInput);
11878   void FindMatchingRule(DR_FrameTreeNode& aNode);
11879   bool RuleMatches(DR_Rule& aRule, DR_FrameTreeNode& aNode);
11880   bool GetToken(FILE* aFile, char* aBuf, size_t aBufSize);
11881   DR_Rule* ParseRule(FILE* aFile);
11882   void ParseRulesFile();
11883   void AddRule(nsTArray<DR_Rule*>& aRules, DR_Rule& aRule);
11884   bool IsWhiteSpace(int c);
11885   bool GetNumber(char* aBuf, int32_t& aNumber);
11886   void PrettyUC(nscoord aSize, char* aBuf, int aBufSize);
11887   void PrintMargin(const char* tag, const nsMargin* aMargin);
11888   void DisplayFrameTypeInfo(nsIFrame* aFrame, int32_t aIndent);
11889   void DeleteTreeNode(DR_FrameTreeNode& aNode);
11890 
11891   bool mInited;
11892   bool mActive;
11893   int32_t mCount;
11894   int32_t mAssert;
11895   int32_t mIndent;
11896   bool mIndentUndisplayedFrames;
11897   bool mDisplayPixelErrors;
11898   nsTArray<DR_Rule*> mWildRules;
11899   nsTArray<DR_FrameTypeInfo> mFrameTypeTable;
11900   // reflow specific state
11901   nsTArray<DR_FrameTreeNode*> mFrameTreeLeaves;
11902 };
11903 
11904 static DR_State* DR_state;  // the one and only DR_State
11905 
11906 struct DR_RulePart {
DR_RulePartDR_RulePart11907   explicit DR_RulePart(LayoutFrameType aFrameType)
11908       : mFrameType(aFrameType), mNext(0) {}
11909 
11910   void Destroy();
11911 
11912   LayoutFrameType mFrameType;
11913   DR_RulePart* mNext;
11914 };
11915 
Destroy()11916 void DR_RulePart::Destroy() {
11917   if (mNext) {
11918     mNext->Destroy();
11919   }
11920   delete this;
11921 }
11922 
11923 struct DR_Rule {
DR_RuleDR_Rule11924   DR_Rule() : mLength(0), mTarget(nullptr), mDisplay(false) {
11925     MOZ_COUNT_CTOR(DR_Rule);
11926   }
~DR_RuleDR_Rule11927   ~DR_Rule() {
11928     if (mTarget) mTarget->Destroy();
11929     MOZ_COUNT_DTOR(DR_Rule);
11930   }
11931   void AddPart(LayoutFrameType aFrameType);
11932 
11933   uint32_t mLength;
11934   DR_RulePart* mTarget;
11935   bool mDisplay;
11936 };
11937 
AddPart(LayoutFrameType aFrameType)11938 void DR_Rule::AddPart(LayoutFrameType aFrameType) {
11939   DR_RulePart* newPart = new DR_RulePart(aFrameType);
11940   newPart->mNext = mTarget;
11941   mTarget = newPart;
11942   mLength++;
11943 }
11944 
~DR_FrameTypeInfo()11945 DR_FrameTypeInfo::~DR_FrameTypeInfo() {
11946   int32_t numElements;
11947   numElements = mRules.Length();
11948   for (int32_t i = numElements - 1; i >= 0; i--) {
11949     delete mRules.ElementAt(i);
11950   }
11951 }
11952 
DR_FrameTypeInfo(LayoutFrameType aFrameType,const char * aFrameNameAbbrev,const char * aFrameName)11953 DR_FrameTypeInfo::DR_FrameTypeInfo(LayoutFrameType aFrameType,
11954                                    const char* aFrameNameAbbrev,
11955                                    const char* aFrameName) {
11956   mType = aFrameType;
11957   PL_strncpyz(mNameAbbrev, aFrameNameAbbrev, sizeof(mNameAbbrev));
11958   PL_strncpyz(mName, aFrameName, sizeof(mName));
11959 }
11960 
11961 struct DR_FrameTreeNode {
DR_FrameTreeNodeDR_FrameTreeNode11962   DR_FrameTreeNode(nsIFrame* aFrame, DR_FrameTreeNode* aParent)
11963       : mFrame(aFrame), mParent(aParent), mDisplay(0), mIndent(0) {
11964     MOZ_COUNT_CTOR(DR_FrameTreeNode);
11965   }
11966 
11967   MOZ_COUNTED_DTOR(DR_FrameTreeNode)
11968 
11969   nsIFrame* mFrame;
11970   DR_FrameTreeNode* mParent;
11971   bool mDisplay;
11972   uint32_t mIndent;
11973 };
11974 
11975 // DR_State implementation
11976 
DR_State()11977 DR_State::DR_State()
11978     : mInited(false),
11979       mActive(false),
11980       mCount(0),
11981       mAssert(-1),
11982       mIndent(0),
11983       mIndentUndisplayedFrames(false),
11984       mDisplayPixelErrors(false) {
11985   MOZ_COUNT_CTOR(DR_State);
11986 }
11987 
Init()11988 void DR_State::Init() {
11989   char* env = PR_GetEnv("GECKO_DISPLAY_REFLOW_ASSERT");
11990   int32_t num;
11991   if (env) {
11992     if (GetNumber(env, num))
11993       mAssert = num;
11994     else
11995       printf("GECKO_DISPLAY_REFLOW_ASSERT - invalid value = %s", env);
11996   }
11997 
11998   env = PR_GetEnv("GECKO_DISPLAY_REFLOW_INDENT_START");
11999   if (env) {
12000     if (GetNumber(env, num))
12001       mIndent = num;
12002     else
12003       printf("GECKO_DISPLAY_REFLOW_INDENT_START - invalid value = %s", env);
12004   }
12005 
12006   env = PR_GetEnv("GECKO_DISPLAY_REFLOW_INDENT_UNDISPLAYED_FRAMES");
12007   if (env) {
12008     if (GetNumber(env, num))
12009       mIndentUndisplayedFrames = num;
12010     else
12011       printf(
12012           "GECKO_DISPLAY_REFLOW_INDENT_UNDISPLAYED_FRAMES - invalid value = %s",
12013           env);
12014   }
12015 
12016   env = PR_GetEnv("GECKO_DISPLAY_REFLOW_FLAG_PIXEL_ERRORS");
12017   if (env) {
12018     if (GetNumber(env, num))
12019       mDisplayPixelErrors = num;
12020     else
12021       printf("GECKO_DISPLAY_REFLOW_FLAG_PIXEL_ERRORS - invalid value = %s",
12022              env);
12023   }
12024 
12025   InitFrameTypeTable();
12026   ParseRulesFile();
12027   mInited = true;
12028 }
12029 
~DR_State()12030 DR_State::~DR_State() {
12031   MOZ_COUNT_DTOR(DR_State);
12032   int32_t numElements, i;
12033   numElements = mWildRules.Length();
12034   for (i = numElements - 1; i >= 0; i--) {
12035     delete mWildRules.ElementAt(i);
12036   }
12037   numElements = mFrameTreeLeaves.Length();
12038   for (i = numElements - 1; i >= 0; i--) {
12039     delete mFrameTreeLeaves.ElementAt(i);
12040   }
12041 }
12042 
GetNumber(char * aBuf,int32_t & aNumber)12043 bool DR_State::GetNumber(char* aBuf, int32_t& aNumber) {
12044   if (sscanf(aBuf, "%d", &aNumber) > 0)
12045     return true;
12046   else
12047     return false;
12048 }
12049 
IsWhiteSpace(int c)12050 bool DR_State::IsWhiteSpace(int c) {
12051   return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r');
12052 }
12053 
GetToken(FILE * aFile,char * aBuf,size_t aBufSize)12054 bool DR_State::GetToken(FILE* aFile, char* aBuf, size_t aBufSize) {
12055   bool haveToken = false;
12056   aBuf[0] = 0;
12057   // get the 1st non whitespace char
12058   int c = -1;
12059   for (c = getc(aFile); (c > 0) && IsWhiteSpace(c); c = getc(aFile)) {
12060   }
12061 
12062   if (c > 0) {
12063     haveToken = true;
12064     aBuf[0] = c;
12065     // get everything up to the next whitespace char
12066     size_t cX;
12067     for (cX = 1; cX + 1 < aBufSize; cX++) {
12068       c = getc(aFile);
12069       if (c < 0) {  // EOF
12070         ungetc(' ', aFile);
12071         break;
12072       } else {
12073         if (IsWhiteSpace(c)) {
12074           break;
12075         } else {
12076           aBuf[cX] = c;
12077         }
12078       }
12079     }
12080     aBuf[cX] = 0;
12081   }
12082   return haveToken;
12083 }
12084 
ParseRule(FILE * aFile)12085 DR_Rule* DR_State::ParseRule(FILE* aFile) {
12086   char buf[128];
12087   int32_t doDisplay;
12088   DR_Rule* rule = nullptr;
12089   while (GetToken(aFile, buf, sizeof(buf))) {
12090     if (GetNumber(buf, doDisplay)) {
12091       if (rule) {
12092         rule->mDisplay = !!doDisplay;
12093         break;
12094       } else {
12095         printf("unexpected token - %s \n", buf);
12096       }
12097     } else {
12098       if (!rule) {
12099         rule = new DR_Rule;
12100       }
12101       if (strcmp(buf, "*") == 0) {
12102         rule->AddPart(LayoutFrameType::None);
12103       } else {
12104         DR_FrameTypeInfo* info = GetFrameTypeInfo(buf);
12105         if (info) {
12106           rule->AddPart(info->mType);
12107         } else {
12108           printf("invalid frame type - %s \n", buf);
12109         }
12110       }
12111     }
12112   }
12113   return rule;
12114 }
12115 
AddRule(nsTArray<DR_Rule * > & aRules,DR_Rule & aRule)12116 void DR_State::AddRule(nsTArray<DR_Rule*>& aRules, DR_Rule& aRule) {
12117   int32_t numRules = aRules.Length();
12118   for (int32_t ruleX = 0; ruleX < numRules; ruleX++) {
12119     DR_Rule* rule = aRules.ElementAt(ruleX);
12120     NS_ASSERTION(rule, "program error");
12121     if (aRule.mLength > rule->mLength) {
12122       aRules.InsertElementAt(ruleX, &aRule);
12123       return;
12124     }
12125   }
12126   aRules.AppendElement(&aRule);
12127 }
12128 
ShouldLogReflow(const char * processes)12129 static Maybe<bool> ShouldLogReflow(const char* processes) {
12130   switch (processes[0]) {
12131     case 'A':
12132     case 'a':
12133       return Some(true);
12134     case 'P':
12135     case 'p':
12136       return Some(XRE_IsParentProcess());
12137     case 'C':
12138     case 'c':
12139       return Some(XRE_IsContentProcess());
12140     default:
12141       return Nothing{};
12142   }
12143 }
12144 
ParseRulesFile()12145 void DR_State::ParseRulesFile() {
12146   char* processes = PR_GetEnv("GECKO_DISPLAY_REFLOW_PROCESSES");
12147   if (processes) {
12148     Maybe<bool> enableLog = ShouldLogReflow(processes);
12149     if (enableLog.isNothing()) {
12150       MOZ_CRASH("GECKO_DISPLAY_REFLOW_PROCESSES: [a]ll [p]arent [c]ontent");
12151     } else if (enableLog.value()) {
12152       DR_Rule* rule = new DR_Rule;
12153       rule->AddPart(LayoutFrameType::None);
12154       rule->mDisplay = true;
12155       AddRule(mWildRules, *rule);
12156       mActive = true;
12157     }
12158     return;
12159   }
12160 
12161   char* path = PR_GetEnv("GECKO_DISPLAY_REFLOW_RULES_FILE");
12162   if (path) {
12163     FILE* inFile = fopen(path, "r");
12164     if (!inFile) {
12165       MOZ_CRASH(
12166           "Failed to open the specified rules file; Try `--setpref "
12167           "security.sandbox.content.level=2` if the sandbox is at cause");
12168     }
12169     for (DR_Rule* rule = ParseRule(inFile); rule; rule = ParseRule(inFile)) {
12170       if (rule->mTarget) {
12171         LayoutFrameType fType = rule->mTarget->mFrameType;
12172         if (fType != LayoutFrameType::None) {
12173           DR_FrameTypeInfo* info = GetFrameTypeInfo(fType);
12174           AddRule(info->mRules, *rule);
12175         } else {
12176           AddRule(mWildRules, *rule);
12177         }
12178         mActive = true;
12179       }
12180     }
12181 
12182     fclose(inFile);
12183   }
12184 }
12185 
AddFrameTypeInfo(LayoutFrameType aFrameType,const char * aFrameNameAbbrev,const char * aFrameName)12186 void DR_State::AddFrameTypeInfo(LayoutFrameType aFrameType,
12187                                 const char* aFrameNameAbbrev,
12188                                 const char* aFrameName) {
12189   mFrameTypeTable.EmplaceBack(aFrameType, aFrameNameAbbrev, aFrameName);
12190 }
12191 
GetFrameTypeInfo(LayoutFrameType aFrameType)12192 DR_FrameTypeInfo* DR_State::GetFrameTypeInfo(LayoutFrameType aFrameType) {
12193   int32_t numEntries = mFrameTypeTable.Length();
12194   NS_ASSERTION(numEntries != 0, "empty FrameTypeTable");
12195   for (int32_t i = 0; i < numEntries; i++) {
12196     DR_FrameTypeInfo& info = mFrameTypeTable.ElementAt(i);
12197     if (info.mType == aFrameType) {
12198       return &info;
12199     }
12200   }
12201   return &mFrameTypeTable.ElementAt(numEntries -
12202                                     1);  // return unknown frame type
12203 }
12204 
GetFrameTypeInfo(char * aFrameName)12205 DR_FrameTypeInfo* DR_State::GetFrameTypeInfo(char* aFrameName) {
12206   int32_t numEntries = mFrameTypeTable.Length();
12207   NS_ASSERTION(numEntries != 0, "empty FrameTypeTable");
12208   for (int32_t i = 0; i < numEntries; i++) {
12209     DR_FrameTypeInfo& info = mFrameTypeTable.ElementAt(i);
12210     if ((strcmp(aFrameName, info.mName) == 0) ||
12211         (strcmp(aFrameName, info.mNameAbbrev) == 0)) {
12212       return &info;
12213     }
12214   }
12215   return &mFrameTypeTable.ElementAt(numEntries -
12216                                     1);  // return unknown frame type
12217 }
12218 
InitFrameTypeTable()12219 void DR_State::InitFrameTypeTable() {
12220   AddFrameTypeInfo(LayoutFrameType::Block, "block", "block");
12221   AddFrameTypeInfo(LayoutFrameType::Br, "br", "br");
12222   AddFrameTypeInfo(LayoutFrameType::Bullet, "bullet", "bullet");
12223   AddFrameTypeInfo(LayoutFrameType::ColorControl, "color", "colorControl");
12224   AddFrameTypeInfo(LayoutFrameType::GfxButtonControl, "button",
12225                    "gfxButtonControl");
12226   AddFrameTypeInfo(LayoutFrameType::HTMLButtonControl, "HTMLbutton",
12227                    "HTMLButtonControl");
12228   AddFrameTypeInfo(LayoutFrameType::HTMLCanvas, "HTMLCanvas", "HTMLCanvas");
12229   AddFrameTypeInfo(LayoutFrameType::SubDocument, "subdoc", "subDocument");
12230   AddFrameTypeInfo(LayoutFrameType::Image, "img", "image");
12231   AddFrameTypeInfo(LayoutFrameType::Inline, "inline", "inline");
12232   AddFrameTypeInfo(LayoutFrameType::Letter, "letter", "letter");
12233   AddFrameTypeInfo(LayoutFrameType::Line, "line", "line");
12234   AddFrameTypeInfo(LayoutFrameType::ListControl, "select", "select");
12235   AddFrameTypeInfo(LayoutFrameType::Object, "obj", "object");
12236   AddFrameTypeInfo(LayoutFrameType::Page, "page", "page");
12237   AddFrameTypeInfo(LayoutFrameType::Placeholder, "place", "placeholder");
12238   AddFrameTypeInfo(LayoutFrameType::Canvas, "canvas", "canvas");
12239   AddFrameTypeInfo(LayoutFrameType::Root, "root", "root");
12240   AddFrameTypeInfo(LayoutFrameType::Scroll, "scroll", "scroll");
12241   AddFrameTypeInfo(LayoutFrameType::TableCell, "cell", "tableCell");
12242   AddFrameTypeInfo(LayoutFrameType::TableCol, "col", "tableCol");
12243   AddFrameTypeInfo(LayoutFrameType::TableColGroup, "colG", "tableColGroup");
12244   AddFrameTypeInfo(LayoutFrameType::Table, "tbl", "table");
12245   AddFrameTypeInfo(LayoutFrameType::TableWrapper, "tblW", "tableWrapper");
12246   AddFrameTypeInfo(LayoutFrameType::TableRowGroup, "rowG", "tableRowGroup");
12247   AddFrameTypeInfo(LayoutFrameType::TableRow, "row", "tableRow");
12248   AddFrameTypeInfo(LayoutFrameType::TextInput, "textCtl", "textInput");
12249   AddFrameTypeInfo(LayoutFrameType::Text, "text", "text");
12250   AddFrameTypeInfo(LayoutFrameType::Viewport, "VP", "viewport");
12251 #  ifdef MOZ_XUL
12252   AddFrameTypeInfo(LayoutFrameType::XULLabel, "XULLabel", "XULLabel");
12253   AddFrameTypeInfo(LayoutFrameType::Box, "Box", "Box");
12254   AddFrameTypeInfo(LayoutFrameType::Slider, "Slider", "Slider");
12255   AddFrameTypeInfo(LayoutFrameType::PopupSet, "PopupSet", "PopupSet");
12256 #  endif
12257   AddFrameTypeInfo(LayoutFrameType::None, "unknown", "unknown");
12258 }
12259 
DisplayFrameTypeInfo(nsIFrame * aFrame,int32_t aIndent)12260 void DR_State::DisplayFrameTypeInfo(nsIFrame* aFrame, int32_t aIndent) {
12261   DR_FrameTypeInfo* frameTypeInfo = GetFrameTypeInfo(aFrame->Type());
12262   if (frameTypeInfo) {
12263     for (int32_t i = 0; i < aIndent; i++) {
12264       printf(" ");
12265     }
12266     if (!strcmp(frameTypeInfo->mNameAbbrev, "unknown")) {
12267       if (aFrame) {
12268         nsAutoString name;
12269         aFrame->GetFrameName(name);
12270         printf("%s %p ", NS_LossyConvertUTF16toASCII(name).get(),
12271                (void*)aFrame);
12272       } else {
12273         printf("%s %p ", frameTypeInfo->mNameAbbrev, (void*)aFrame);
12274       }
12275     } else {
12276       printf("%s %p ", frameTypeInfo->mNameAbbrev, (void*)aFrame);
12277     }
12278   }
12279 }
12280 
RuleMatches(DR_Rule & aRule,DR_FrameTreeNode & aNode)12281 bool DR_State::RuleMatches(DR_Rule& aRule, DR_FrameTreeNode& aNode) {
12282   NS_ASSERTION(aRule.mTarget, "program error");
12283 
12284   DR_RulePart* rulePart;
12285   DR_FrameTreeNode* parentNode;
12286   for (rulePart = aRule.mTarget->mNext, parentNode = aNode.mParent;
12287        rulePart && parentNode;
12288        rulePart = rulePart->mNext, parentNode = parentNode->mParent) {
12289     if (rulePart->mFrameType != LayoutFrameType::None) {
12290       if (parentNode->mFrame) {
12291         if (rulePart->mFrameType != parentNode->mFrame->Type()) {
12292           return false;
12293         }
12294       } else
12295         NS_ASSERTION(false, "program error");
12296     }
12297     // else wild card match
12298   }
12299   return true;
12300 }
12301 
FindMatchingRule(DR_FrameTreeNode & aNode)12302 void DR_State::FindMatchingRule(DR_FrameTreeNode& aNode) {
12303   if (!aNode.mFrame) {
12304     NS_ASSERTION(false, "invalid DR_FrameTreeNode \n");
12305     return;
12306   }
12307 
12308   bool matchingRule = false;
12309 
12310   DR_FrameTypeInfo* info = GetFrameTypeInfo(aNode.mFrame->Type());
12311   NS_ASSERTION(info, "program error");
12312   int32_t numRules = info->mRules.Length();
12313   for (int32_t ruleX = 0; ruleX < numRules; ruleX++) {
12314     DR_Rule* rule = info->mRules.ElementAt(ruleX);
12315     if (rule && RuleMatches(*rule, aNode)) {
12316       aNode.mDisplay = rule->mDisplay;
12317       matchingRule = true;
12318       break;
12319     }
12320   }
12321   if (!matchingRule) {
12322     int32_t numWildRules = mWildRules.Length();
12323     for (int32_t ruleX = 0; ruleX < numWildRules; ruleX++) {
12324       DR_Rule* rule = mWildRules.ElementAt(ruleX);
12325       if (rule && RuleMatches(*rule, aNode)) {
12326         aNode.mDisplay = rule->mDisplay;
12327         break;
12328       }
12329     }
12330   }
12331 }
12332 
CreateTreeNode(nsIFrame * aFrame,const ReflowInput * aReflowInput)12333 DR_FrameTreeNode* DR_State::CreateTreeNode(nsIFrame* aFrame,
12334                                            const ReflowInput* aReflowInput) {
12335   // find the frame of the parent reflow input (usually just the parent of
12336   // aFrame)
12337   nsIFrame* parentFrame;
12338   if (aReflowInput) {
12339     const ReflowInput* parentRI = aReflowInput->mParentReflowInput;
12340     parentFrame = (parentRI) ? parentRI->mFrame : nullptr;
12341   } else {
12342     parentFrame = aFrame->GetParent();
12343   }
12344 
12345   // find the parent tree node leaf
12346   DR_FrameTreeNode* parentNode = nullptr;
12347 
12348   DR_FrameTreeNode* lastLeaf = nullptr;
12349   if (mFrameTreeLeaves.Length())
12350     lastLeaf = mFrameTreeLeaves.ElementAt(mFrameTreeLeaves.Length() - 1);
12351   if (lastLeaf) {
12352     for (parentNode = lastLeaf;
12353          parentNode && (parentNode->mFrame != parentFrame);
12354          parentNode = parentNode->mParent) {
12355     }
12356   }
12357   DR_FrameTreeNode* newNode = new DR_FrameTreeNode(aFrame, parentNode);
12358   FindMatchingRule(*newNode);
12359 
12360   newNode->mIndent = mIndent;
12361   if (newNode->mDisplay || mIndentUndisplayedFrames) {
12362     ++mIndent;
12363   }
12364 
12365   if (lastLeaf && (lastLeaf == parentNode)) {
12366     mFrameTreeLeaves.RemoveLastElement();
12367   }
12368   mFrameTreeLeaves.AppendElement(newNode);
12369   mCount++;
12370 
12371   return newNode;
12372 }
12373 
PrettyUC(nscoord aSize,char * aBuf,int aBufSize)12374 void DR_State::PrettyUC(nscoord aSize, char* aBuf, int aBufSize) {
12375   if (NS_UNCONSTRAINEDSIZE == aSize) {
12376     strcpy(aBuf, "UC");
12377   } else {
12378     if ((nscoord)0xdeadbeefU == aSize) {
12379       strcpy(aBuf, "deadbeef");
12380     } else {
12381       snprintf(aBuf, aBufSize, "%d", aSize);
12382     }
12383   }
12384 }
12385 
PrintMargin(const char * tag,const nsMargin * aMargin)12386 void DR_State::PrintMargin(const char* tag, const nsMargin* aMargin) {
12387   if (aMargin) {
12388     char t[16], r[16], b[16], l[16];
12389     PrettyUC(aMargin->top, t, 16);
12390     PrettyUC(aMargin->right, r, 16);
12391     PrettyUC(aMargin->bottom, b, 16);
12392     PrettyUC(aMargin->left, l, 16);
12393     printf(" %s=%s,%s,%s,%s", tag, t, r, b, l);
12394   } else {
12395     // use %p here for consistency with other null-pointer printouts
12396     printf(" %s=%p", tag, (void*)aMargin);
12397   }
12398 }
12399 
DeleteTreeNode(DR_FrameTreeNode & aNode)12400 void DR_State::DeleteTreeNode(DR_FrameTreeNode& aNode) {
12401   mFrameTreeLeaves.RemoveElement(&aNode);
12402   int32_t numLeaves = mFrameTreeLeaves.Length();
12403   if ((0 == numLeaves) ||
12404       (aNode.mParent != mFrameTreeLeaves.ElementAt(numLeaves - 1))) {
12405     mFrameTreeLeaves.AppendElement(aNode.mParent);
12406   }
12407 
12408   if (aNode.mDisplay || mIndentUndisplayedFrames) {
12409     --mIndent;
12410   }
12411   // delete the tree node
12412   delete &aNode;
12413 }
12414 
CheckPixelError(nscoord aSize,int32_t aPixelToTwips)12415 static void CheckPixelError(nscoord aSize, int32_t aPixelToTwips) {
12416   if (NS_UNCONSTRAINEDSIZE != aSize) {
12417     if ((aSize % aPixelToTwips) > 0) {
12418       printf("VALUE %d is not a whole pixel \n", aSize);
12419     }
12420   }
12421 }
12422 
DisplayReflowEnterPrint(nsPresContext * aPresContext,nsIFrame * aFrame,const ReflowInput & aReflowInput,DR_FrameTreeNode & aTreeNode,bool aChanged)12423 static void DisplayReflowEnterPrint(nsPresContext* aPresContext,
12424                                     nsIFrame* aFrame,
12425                                     const ReflowInput& aReflowInput,
12426                                     DR_FrameTreeNode& aTreeNode,
12427                                     bool aChanged) {
12428   if (aTreeNode.mDisplay) {
12429     DR_state->DisplayFrameTypeInfo(aFrame, aTreeNode.mIndent);
12430 
12431     char width[16];
12432     char height[16];
12433 
12434     DR_state->PrettyUC(aReflowInput.AvailableWidth(), width, 16);
12435     DR_state->PrettyUC(aReflowInput.AvailableHeight(), height, 16);
12436     printf("Reflow a=%s,%s ", width, height);
12437 
12438     DR_state->PrettyUC(aReflowInput.ComputedWidth(), width, 16);
12439     DR_state->PrettyUC(aReflowInput.ComputedHeight(), height, 16);
12440     printf("c=%s,%s ", width, height);
12441 
12442     if (aFrame->GetStateBits() & NS_FRAME_IS_DIRTY) printf("dirty ");
12443 
12444     if (aFrame->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN)
12445       printf("dirty-children ");
12446 
12447     if (aReflowInput.mFlags.mSpecialBSizeReflow) printf("special-bsize ");
12448 
12449     if (aReflowInput.IsHResize()) printf("h-resize ");
12450 
12451     if (aReflowInput.IsVResize()) printf("v-resize ");
12452 
12453     nsIFrame* inFlow = aFrame->GetPrevInFlow();
12454     if (inFlow) {
12455       printf("pif=%p ", (void*)inFlow);
12456     }
12457     inFlow = aFrame->GetNextInFlow();
12458     if (inFlow) {
12459       printf("nif=%p ", (void*)inFlow);
12460     }
12461     if (aChanged)
12462       printf("CHANGED \n");
12463     else
12464       printf("cnt=%d \n", DR_state->mCount);
12465     if (DR_state->mDisplayPixelErrors) {
12466       int32_t d2a = aPresContext->AppUnitsPerDevPixel();
12467       CheckPixelError(aReflowInput.AvailableWidth(), d2a);
12468       CheckPixelError(aReflowInput.AvailableHeight(), d2a);
12469       CheckPixelError(aReflowInput.ComputedWidth(), d2a);
12470       CheckPixelError(aReflowInput.ComputedHeight(), d2a);
12471     }
12472   }
12473 }
12474 
DisplayReflowEnter(nsPresContext * aPresContext,nsIFrame * aFrame,const ReflowInput & aReflowInput)12475 void* nsFrame::DisplayReflowEnter(nsPresContext* aPresContext, nsIFrame* aFrame,
12476                                   const ReflowInput& aReflowInput) {
12477   if (!DR_state->mInited) DR_state->Init();
12478   if (!DR_state->mActive) return nullptr;
12479 
12480   NS_ASSERTION(aFrame, "invalid call");
12481 
12482   DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, &aReflowInput);
12483   if (treeNode) {
12484     DisplayReflowEnterPrint(aPresContext, aFrame, aReflowInput, *treeNode,
12485                             false);
12486   }
12487   return treeNode;
12488 }
12489 
DisplayLayoutEnter(nsIFrame * aFrame)12490 void* nsFrame::DisplayLayoutEnter(nsIFrame* aFrame) {
12491   if (!DR_state->mInited) DR_state->Init();
12492   if (!DR_state->mActive) return nullptr;
12493 
12494   NS_ASSERTION(aFrame, "invalid call");
12495 
12496   DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr);
12497   if (treeNode && treeNode->mDisplay) {
12498     DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12499     printf("XULLayout\n");
12500   }
12501   return treeNode;
12502 }
12503 
DisplayIntrinsicISizeEnter(nsIFrame * aFrame,const char * aType)12504 void* nsFrame::DisplayIntrinsicISizeEnter(nsIFrame* aFrame, const char* aType) {
12505   if (!DR_state->mInited) DR_state->Init();
12506   if (!DR_state->mActive) return nullptr;
12507 
12508   NS_ASSERTION(aFrame, "invalid call");
12509 
12510   DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr);
12511   if (treeNode && treeNode->mDisplay) {
12512     DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12513     printf("Get%sISize\n", aType);
12514   }
12515   return treeNode;
12516 }
12517 
DisplayIntrinsicSizeEnter(nsIFrame * aFrame,const char * aType)12518 void* nsFrame::DisplayIntrinsicSizeEnter(nsIFrame* aFrame, const char* aType) {
12519   if (!DR_state->mInited) DR_state->Init();
12520   if (!DR_state->mActive) return nullptr;
12521 
12522   NS_ASSERTION(aFrame, "invalid call");
12523 
12524   DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr);
12525   if (treeNode && treeNode->mDisplay) {
12526     DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12527     printf("Get%sSize\n", aType);
12528   }
12529   return treeNode;
12530 }
12531 
DisplayReflowExit(nsPresContext * aPresContext,nsIFrame * aFrame,ReflowOutput & aMetrics,const nsReflowStatus & aStatus,void * aFrameTreeNode)12532 void nsFrame::DisplayReflowExit(nsPresContext* aPresContext, nsIFrame* aFrame,
12533                                 ReflowOutput& aMetrics,
12534                                 const nsReflowStatus& aStatus,
12535                                 void* aFrameTreeNode) {
12536   if (!DR_state->mActive) return;
12537 
12538   NS_ASSERTION(aFrame, "DisplayReflowExit - invalid call");
12539   if (!aFrameTreeNode) return;
12540 
12541   DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
12542   if (treeNode->mDisplay) {
12543     DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12544 
12545     char width[16];
12546     char height[16];
12547     char x[16];
12548     char y[16];
12549     DR_state->PrettyUC(aMetrics.Width(), width, 16);
12550     DR_state->PrettyUC(aMetrics.Height(), height, 16);
12551     printf("Reflow d=%s,%s", width, height);
12552 
12553     if (!aStatus.IsEmpty()) {
12554       printf(" status=%s", ToString(aStatus).c_str());
12555     }
12556     if (aFrame->HasOverflowAreas()) {
12557       DR_state->PrettyUC(aMetrics.VisualOverflow().x, x, 16);
12558       DR_state->PrettyUC(aMetrics.VisualOverflow().y, y, 16);
12559       DR_state->PrettyUC(aMetrics.VisualOverflow().width, width, 16);
12560       DR_state->PrettyUC(aMetrics.VisualOverflow().height, height, 16);
12561       printf(" vis-o=(%s,%s) %s x %s", x, y, width, height);
12562 
12563       nsRect storedOverflow = aFrame->GetVisualOverflowRect();
12564       DR_state->PrettyUC(storedOverflow.x, x, 16);
12565       DR_state->PrettyUC(storedOverflow.y, y, 16);
12566       DR_state->PrettyUC(storedOverflow.width, width, 16);
12567       DR_state->PrettyUC(storedOverflow.height, height, 16);
12568       printf(" vis-sto=(%s,%s) %s x %s", x, y, width, height);
12569 
12570       DR_state->PrettyUC(aMetrics.ScrollableOverflow().x, x, 16);
12571       DR_state->PrettyUC(aMetrics.ScrollableOverflow().y, y, 16);
12572       DR_state->PrettyUC(aMetrics.ScrollableOverflow().width, width, 16);
12573       DR_state->PrettyUC(aMetrics.ScrollableOverflow().height, height, 16);
12574       printf(" scr-o=(%s,%s) %s x %s", x, y, width, height);
12575 
12576       storedOverflow = aFrame->GetScrollableOverflowRect();
12577       DR_state->PrettyUC(storedOverflow.x, x, 16);
12578       DR_state->PrettyUC(storedOverflow.y, y, 16);
12579       DR_state->PrettyUC(storedOverflow.width, width, 16);
12580       DR_state->PrettyUC(storedOverflow.height, height, 16);
12581       printf(" scr-sto=(%s,%s) %s x %s", x, y, width, height);
12582     }
12583     printf("\n");
12584     if (DR_state->mDisplayPixelErrors) {
12585       int32_t d2a = aPresContext->AppUnitsPerDevPixel();
12586       CheckPixelError(aMetrics.Width(), d2a);
12587       CheckPixelError(aMetrics.Height(), d2a);
12588     }
12589   }
12590   DR_state->DeleteTreeNode(*treeNode);
12591 }
12592 
DisplayLayoutExit(nsIFrame * aFrame,void * aFrameTreeNode)12593 void nsFrame::DisplayLayoutExit(nsIFrame* aFrame, void* aFrameTreeNode) {
12594   if (!DR_state->mActive) return;
12595 
12596   NS_ASSERTION(aFrame, "non-null frame required");
12597   if (!aFrameTreeNode) return;
12598 
12599   DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
12600   if (treeNode->mDisplay) {
12601     DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12602     nsRect rect = aFrame->GetRect();
12603     printf("XULLayout=%d,%d,%d,%d\n", rect.x, rect.y, rect.width, rect.height);
12604   }
12605   DR_state->DeleteTreeNode(*treeNode);
12606 }
12607 
DisplayIntrinsicISizeExit(nsIFrame * aFrame,const char * aType,nscoord aResult,void * aFrameTreeNode)12608 void nsFrame::DisplayIntrinsicISizeExit(nsIFrame* aFrame, const char* aType,
12609                                         nscoord aResult, void* aFrameTreeNode) {
12610   if (!DR_state->mActive) return;
12611 
12612   NS_ASSERTION(aFrame, "non-null frame required");
12613   if (!aFrameTreeNode) return;
12614 
12615   DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
12616   if (treeNode->mDisplay) {
12617     DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12618     char iSize[16];
12619     DR_state->PrettyUC(aResult, iSize, 16);
12620     printf("Get%sISize=%s\n", aType, iSize);
12621   }
12622   DR_state->DeleteTreeNode(*treeNode);
12623 }
12624 
DisplayIntrinsicSizeExit(nsIFrame * aFrame,const char * aType,nsSize aResult,void * aFrameTreeNode)12625 void nsFrame::DisplayIntrinsicSizeExit(nsIFrame* aFrame, const char* aType,
12626                                        nsSize aResult, void* aFrameTreeNode) {
12627   if (!DR_state->mActive) return;
12628 
12629   NS_ASSERTION(aFrame, "non-null frame required");
12630   if (!aFrameTreeNode) return;
12631 
12632   DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
12633   if (treeNode->mDisplay) {
12634     DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12635 
12636     char width[16];
12637     char height[16];
12638     DR_state->PrettyUC(aResult.width, width, 16);
12639     DR_state->PrettyUC(aResult.height, height, 16);
12640     printf("Get%sSize=%s,%s\n", aType, width, height);
12641   }
12642   DR_state->DeleteTreeNode(*treeNode);
12643 }
12644 
12645 /* static */
DisplayReflowStartup()12646 void nsFrame::DisplayReflowStartup() { DR_state = new DR_State(); }
12647 
12648 /* static */
DisplayReflowShutdown()12649 void nsFrame::DisplayReflowShutdown() {
12650   delete DR_state;
12651   DR_state = nullptr;
12652 }
12653 
Change() const12654 void DR_cookie::Change() const {
12655   DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)mValue;
12656   if (treeNode && treeNode->mDisplay) {
12657     DisplayReflowEnterPrint(mPresContext, mFrame, mReflowInput, *treeNode,
12658                             true);
12659   }
12660 }
12661 
12662 /* static */
DisplayInitConstraintsEnter(nsIFrame * aFrame,ReflowInput * aState,nscoord aContainingBlockWidth,nscoord aContainingBlockHeight,const nsMargin * aBorder,const nsMargin * aPadding)12663 void* ReflowInput::DisplayInitConstraintsEnter(nsIFrame* aFrame,
12664                                                ReflowInput* aState,
12665                                                nscoord aContainingBlockWidth,
12666                                                nscoord aContainingBlockHeight,
12667                                                const nsMargin* aBorder,
12668                                                const nsMargin* aPadding) {
12669   MOZ_ASSERT(aFrame, "non-null frame required");
12670   MOZ_ASSERT(aState, "non-null state required");
12671 
12672   if (!DR_state->mInited) DR_state->Init();
12673   if (!DR_state->mActive) return nullptr;
12674 
12675   DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, aState);
12676   if (treeNode && treeNode->mDisplay) {
12677     DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12678 
12679     printf("InitConstraints parent=%p", (void*)aState->mParentReflowInput);
12680 
12681     char width[16];
12682     char height[16];
12683 
12684     DR_state->PrettyUC(aContainingBlockWidth, width, 16);
12685     DR_state->PrettyUC(aContainingBlockHeight, height, 16);
12686     printf(" cb=%s,%s", width, height);
12687 
12688     DR_state->PrettyUC(aState->AvailableWidth(), width, 16);
12689     DR_state->PrettyUC(aState->AvailableHeight(), height, 16);
12690     printf(" as=%s,%s", width, height);
12691 
12692     DR_state->PrintMargin("b", aBorder);
12693     DR_state->PrintMargin("p", aPadding);
12694     putchar('\n');
12695   }
12696   return treeNode;
12697 }
12698 
12699 /* static */
DisplayInitConstraintsExit(nsIFrame * aFrame,ReflowInput * aState,void * aValue)12700 void ReflowInput::DisplayInitConstraintsExit(nsIFrame* aFrame,
12701                                              ReflowInput* aState,
12702                                              void* aValue) {
12703   MOZ_ASSERT(aFrame, "non-null frame required");
12704   MOZ_ASSERT(aState, "non-null state required");
12705 
12706   if (!DR_state->mActive) return;
12707   if (!aValue) return;
12708 
12709   DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue;
12710   if (treeNode->mDisplay) {
12711     DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12712     char cmiw[16], cw[16], cmxw[16], cmih[16], ch[16], cmxh[16];
12713     DR_state->PrettyUC(aState->ComputedMinWidth(), cmiw, 16);
12714     DR_state->PrettyUC(aState->ComputedWidth(), cw, 16);
12715     DR_state->PrettyUC(aState->ComputedMaxWidth(), cmxw, 16);
12716     DR_state->PrettyUC(aState->ComputedMinHeight(), cmih, 16);
12717     DR_state->PrettyUC(aState->ComputedHeight(), ch, 16);
12718     DR_state->PrettyUC(aState->ComputedMaxHeight(), cmxh, 16);
12719     printf("InitConstraints= cw=(%s <= %s <= %s) ch=(%s <= %s <= %s)", cmiw, cw,
12720            cmxw, cmih, ch, cmxh);
12721     DR_state->PrintMargin("co", &aState->ComputedPhysicalOffsets());
12722     putchar('\n');
12723   }
12724   DR_state->DeleteTreeNode(*treeNode);
12725 }
12726 
12727 /* static */
DisplayInitOffsetsEnter(nsIFrame * aFrame,SizeComputationInput * aState,nscoord aPercentBasis,WritingMode aCBWritingMode,const nsMargin * aBorder,const nsMargin * aPadding)12728 void* SizeComputationInput::DisplayInitOffsetsEnter(
12729     nsIFrame* aFrame, SizeComputationInput* aState, nscoord aPercentBasis,
12730     WritingMode aCBWritingMode, const nsMargin* aBorder,
12731     const nsMargin* aPadding) {
12732   MOZ_ASSERT(aFrame, "non-null frame required");
12733   MOZ_ASSERT(aState, "non-null state required");
12734 
12735   if (!DR_state->mInited) DR_state->Init();
12736   if (!DR_state->mActive) return nullptr;
12737 
12738   // aState is not necessarily a ReflowInput
12739   DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr);
12740   if (treeNode && treeNode->mDisplay) {
12741     DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12742 
12743     char pctBasisStr[16];
12744     DR_state->PrettyUC(aPercentBasis, pctBasisStr, 16);
12745     printf("InitOffsets pct_basis=%s", pctBasisStr);
12746 
12747     DR_state->PrintMargin("b", aBorder);
12748     DR_state->PrintMargin("p", aPadding);
12749     putchar('\n');
12750   }
12751   return treeNode;
12752 }
12753 
12754 /* static */
DisplayInitOffsetsExit(nsIFrame * aFrame,SizeComputationInput * aState,void * aValue)12755 void SizeComputationInput::DisplayInitOffsetsExit(nsIFrame* aFrame,
12756                                                   SizeComputationInput* aState,
12757                                                   void* aValue) {
12758   MOZ_ASSERT(aFrame, "non-null frame required");
12759   MOZ_ASSERT(aState, "non-null state required");
12760 
12761   if (!DR_state->mActive) return;
12762   if (!aValue) return;
12763 
12764   DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue;
12765   if (treeNode->mDisplay) {
12766     DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12767     printf("InitOffsets=");
12768     DR_state->PrintMargin("m", &aState->ComputedPhysicalMargin());
12769     DR_state->PrintMargin("p", &aState->ComputedPhysicalPadding());
12770     DR_state->PrintMargin("p+b", &aState->ComputedPhysicalBorderPadding());
12771     putchar('\n');
12772   }
12773   DR_state->DeleteTreeNode(*treeNode);
12774 }
12775 
12776 /* static */
DisplayInitFrameTypeEnter(nsIFrame * aFrame,ReflowInput * aState)12777 void* ReflowInput::DisplayInitFrameTypeEnter(nsIFrame* aFrame,
12778                                              ReflowInput* aState) {
12779   MOZ_ASSERT(aFrame, "non-null frame required");
12780   MOZ_ASSERT(aState, "non-null state required");
12781 
12782   if (!DR_state->mInited) DR_state->Init();
12783   if (!DR_state->mActive) return nullptr;
12784 
12785   // we don't print anything here
12786   return DR_state->CreateTreeNode(aFrame, aState);
12787 }
12788 
12789 /* static */
DisplayInitFrameTypeExit(nsIFrame * aFrame,ReflowInput * aState,void * aValue)12790 void ReflowInput::DisplayInitFrameTypeExit(nsIFrame* aFrame,
12791                                            ReflowInput* aState, void* aValue) {
12792   MOZ_ASSERT(aFrame, "non-null frame required");
12793   MOZ_ASSERT(aState, "non-null state required");
12794 
12795   if (!DR_state->mActive) return;
12796   if (!aValue) return;
12797 
12798   DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue;
12799   if (treeNode->mDisplay) {
12800     DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12801     printf("InitFrameType");
12802 
12803     if (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) printf(" out-of-flow");
12804     if (aFrame->GetPrevInFlow()) printf(" prev-in-flow");
12805     if (aFrame->IsAbsolutelyPositioned()) printf(" abspos");
12806     if (aFrame->IsFloating()) printf(" float");
12807 
12808     {
12809       nsAutoString result;
12810       aFrame->Style()->GetComputedPropertyValue(eCSSProperty_display, result);
12811       printf(" display=%s", NS_ConvertUTF16toUTF8(result).get());
12812     }
12813 
12814     // This array must exactly match the NS_CSS_FRAME_TYPE constants.
12815     const char* const cssFrameTypes[] = {
12816         "unknown", "inline", "block", "floating", "absolute", "internal-table"};
12817     nsCSSFrameType bareType = NS_FRAME_GET_TYPE(aState->mFrameType);
12818     bool repNoBlock = NS_FRAME_IS_REPLACED_NOBLOCK(aState->mFrameType);
12819     bool repBlock = NS_FRAME_IS_REPLACED_CONTAINS_BLOCK(aState->mFrameType);
12820 
12821     if (bareType >= ArrayLength(cssFrameTypes)) {
12822       printf(" result=type %u", bareType);
12823     } else {
12824       printf(" result=%s", cssFrameTypes[bareType]);
12825     }
12826     printf("%s%s\n", repNoBlock ? " +rep" : "", repBlock ? " +repBlk" : "");
12827   }
12828   DR_state->DeleteTreeNode(*treeNode);
12829 }
12830 
12831 // End Display Reflow
12832 
12833 // Validation of SideIsVertical.
12834 #  define CASE(side, result) \
12835     static_assert(SideIsVertical(side) == result, "SideIsVertical is wrong")
12836 CASE(eSideTop, false);
12837 CASE(eSideRight, true);
12838 CASE(eSideBottom, false);
12839 CASE(eSideLeft, true);
12840 #  undef CASE
12841 
12842 // Validation of HalfCornerIsX.
12843 #  define CASE(corner, result) \
12844     static_assert(HalfCornerIsX(corner) == result, "HalfCornerIsX is wrong")
12845 CASE(eCornerTopLeftX, true);
12846 CASE(eCornerTopLeftY, false);
12847 CASE(eCornerTopRightX, true);
12848 CASE(eCornerTopRightY, false);
12849 CASE(eCornerBottomRightX, true);
12850 CASE(eCornerBottomRightY, false);
12851 CASE(eCornerBottomLeftX, true);
12852 CASE(eCornerBottomLeftY, false);
12853 #  undef CASE
12854 
12855 // Validation of HalfToFullCorner.
12856 #  define CASE(corner, result)                        \
12857     static_assert(HalfToFullCorner(corner) == result, \
12858                   "HalfToFullCorner is "              \
12859                   "wrong")
12860 CASE(eCornerTopLeftX, eCornerTopLeft);
12861 CASE(eCornerTopLeftY, eCornerTopLeft);
12862 CASE(eCornerTopRightX, eCornerTopRight);
12863 CASE(eCornerTopRightY, eCornerTopRight);
12864 CASE(eCornerBottomRightX, eCornerBottomRight);
12865 CASE(eCornerBottomRightY, eCornerBottomRight);
12866 CASE(eCornerBottomLeftX, eCornerBottomLeft);
12867 CASE(eCornerBottomLeftY, eCornerBottomLeft);
12868 #  undef CASE
12869 
12870 // Validation of FullToHalfCorner.
12871 #  define CASE(corner, vert, result)                        \
12872     static_assert(FullToHalfCorner(corner, vert) == result, \
12873                   "FullToHalfCorner is wrong")
12874 CASE(eCornerTopLeft, false, eCornerTopLeftX);
12875 CASE(eCornerTopLeft, true, eCornerTopLeftY);
12876 CASE(eCornerTopRight, false, eCornerTopRightX);
12877 CASE(eCornerTopRight, true, eCornerTopRightY);
12878 CASE(eCornerBottomRight, false, eCornerBottomRightX);
12879 CASE(eCornerBottomRight, true, eCornerBottomRightY);
12880 CASE(eCornerBottomLeft, false, eCornerBottomLeftX);
12881 CASE(eCornerBottomLeft, true, eCornerBottomLeftY);
12882 #  undef CASE
12883 
12884 // Validation of SideToFullCorner.
12885 #  define CASE(side, second, result)                        \
12886     static_assert(SideToFullCorner(side, second) == result, \
12887                   "SideToFullCorner is wrong")
12888 CASE(eSideTop, false, eCornerTopLeft);
12889 CASE(eSideTop, true, eCornerTopRight);
12890 
12891 CASE(eSideRight, false, eCornerTopRight);
12892 CASE(eSideRight, true, eCornerBottomRight);
12893 
12894 CASE(eSideBottom, false, eCornerBottomRight);
12895 CASE(eSideBottom, true, eCornerBottomLeft);
12896 
12897 CASE(eSideLeft, false, eCornerBottomLeft);
12898 CASE(eSideLeft, true, eCornerTopLeft);
12899 #  undef CASE
12900 
12901 // Validation of SideToHalfCorner.
12902 #  define CASE(side, second, parallel, result)                        \
12903     static_assert(SideToHalfCorner(side, second, parallel) == result, \
12904                   "SideToHalfCorner is wrong")
12905 CASE(eSideTop, false, true, eCornerTopLeftX);
12906 CASE(eSideTop, false, false, eCornerTopLeftY);
12907 CASE(eSideTop, true, true, eCornerTopRightX);
12908 CASE(eSideTop, true, false, eCornerTopRightY);
12909 
12910 CASE(eSideRight, false, false, eCornerTopRightX);
12911 CASE(eSideRight, false, true, eCornerTopRightY);
12912 CASE(eSideRight, true, false, eCornerBottomRightX);
12913 CASE(eSideRight, true, true, eCornerBottomRightY);
12914 
12915 CASE(eSideBottom, false, true, eCornerBottomRightX);
12916 CASE(eSideBottom, false, false, eCornerBottomRightY);
12917 CASE(eSideBottom, true, true, eCornerBottomLeftX);
12918 CASE(eSideBottom, true, false, eCornerBottomLeftY);
12919 
12920 CASE(eSideLeft, false, false, eCornerBottomLeftX);
12921 CASE(eSideLeft, false, true, eCornerBottomLeftY);
12922 CASE(eSideLeft, true, false, eCornerTopLeftX);
12923 CASE(eSideLeft, true, true, eCornerTopLeftY);
12924 #  undef CASE
12925 
12926 #endif
12927