1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 /* base class of all rendering objects */
8
9 #include "nsIFrame.h"
10
11 #include <stdarg.h>
12 #include <algorithm>
13
14 #include "gfx2DGlue.h"
15 #include "gfxUtils.h"
16 #include "mozilla/Attributes.h"
17 #include "mozilla/ComputedStyle.h"
18 #include "mozilla/DebugOnly.h"
19 #include "mozilla/DisplayPortUtils.h"
20 #include "mozilla/dom/DocumentInlines.h"
21 #include "mozilla/dom/AncestorIterator.h"
22 #include "mozilla/dom/ElementInlines.h"
23 #include "mozilla/dom/ImageTracker.h"
24 #include "mozilla/dom/Selection.h"
25 #include "mozilla/gfx/2D.h"
26 #include "mozilla/gfx/gfxVars.h"
27 #include "mozilla/gfx/PathHelpers.h"
28 #include "mozilla/intl/BidiEmbeddingLevel.h"
29 #include "mozilla/Maybe.h"
30 #include "mozilla/PresShell.h"
31 #include "mozilla/PresShellInlines.h"
32 #include "mozilla/ResultExtensions.h"
33 #include "mozilla/Sprintf.h"
34 #include "mozilla/StaticAnalysisFunctions.h"
35 #include "mozilla/StaticPrefs_layout.h"
36 #include "mozilla/StaticPrefs_print.h"
37 #include "mozilla/SVGMaskFrame.h"
38 #include "mozilla/SVGObserverUtils.h"
39 #include "mozilla/SVGTextFrame.h"
40 #include "mozilla/SVGIntegrationUtils.h"
41 #include "mozilla/SVGUtils.h"
42 #include "mozilla/ToString.h"
43 #include "mozilla/ViewportUtils.h"
44
45 #include "nsCOMPtr.h"
46 #include "nsFieldSetFrame.h"
47 #include "nsFlexContainerFrame.h"
48 #include "nsFrameList.h"
49 #include "nsPlaceholderFrame.h"
50 #include "nsIBaseWindow.h"
51 #include "nsIContent.h"
52 #include "nsIContentInlines.h"
53 #include "nsContentUtils.h"
54 #include "nsCSSFrameConstructor.h"
55 #include "nsCSSProps.h"
56 #include "nsCSSPseudoElements.h"
57 #include "nsCSSRendering.h"
58 #include "nsAtom.h"
59 #include "nsString.h"
60 #include "nsReadableUtils.h"
61 #include "nsTableWrapperFrame.h"
62 #include "nsView.h"
63 #include "nsViewManager.h"
64 #include "nsIScrollableFrame.h"
65 #include "nsPresContext.h"
66 #include "nsPresContextInlines.h"
67 #include "nsStyleConsts.h"
68 #include "mozilla/Logging.h"
69 #include "nsLayoutUtils.h"
70 #include "LayoutLogging.h"
71 #include "mozilla/RestyleManager.h"
72 #include "nsImageFrame.h"
73 #include "nsInlineFrame.h"
74 #include "nsFrameSelection.h"
75 #include "nsGkAtoms.h"
76 #include "nsGridContainerFrame.h"
77 #include "nsCSSAnonBoxes.h"
78 #include "nsCanvasFrame.h"
79
80 #include "nsFieldSetFrame.h"
81 #include "nsFrameTraversal.h"
82 #include "nsRange.h"
83 #include "nsITextControlFrame.h"
84 #include "nsNameSpaceManager.h"
85 #include "nsIPercentBSizeObserver.h"
86 #include "nsStyleStructInlines.h"
87
88 #include "nsBidiPresUtils.h"
89 #include "RubyUtils.h"
90 #include "TextOverflow.h"
91 #include "nsAnimationManager.h"
92
93 // For triple-click pref
94 #include "imgIRequest.h"
95 #include "nsError.h"
96 #include "nsContainerFrame.h"
97 #include "nsBoxLayoutState.h"
98 #include "nsBlockFrame.h"
99 #include "nsDisplayList.h"
100 #include "nsChangeHint.h"
101 #include "nsDeckFrame.h"
102 #include "nsSubDocumentFrame.h"
103 #include "RetainedDisplayListBuilder.h"
104
105 #include "gfxContext.h"
106 #include "nsAbsoluteContainingBlock.h"
107 #include "StickyScrollContainer.h"
108 #include "nsFontInflationData.h"
109 #include "nsRegion.h"
110 #include "nsIFrameInlines.h"
111 #include "nsStyleChangeList.h"
112 #include "nsWindowSizes.h"
113
114 #ifdef ACCESSIBILITY
115 # include "nsAccessibilityService.h"
116 #endif
117
118 #include "mozilla/AsyncEventDispatcher.h"
119 #include "mozilla/CSSClipPathInstance.h"
120 #include "mozilla/EffectCompositor.h"
121 #include "mozilla/EffectSet.h"
122 #include "mozilla/EventListenerManager.h"
123 #include "mozilla/EventStateManager.h"
124 #include "mozilla/EventStates.h"
125 #include "mozilla/Preferences.h"
126 #include "mozilla/LookAndFeel.h"
127 #include "mozilla/MouseEvents.h"
128 #include "mozilla/ServoStyleSet.h"
129 #include "mozilla/ServoStyleSetInlines.h"
130 #include "mozilla/css/ImageLoader.h"
131 #include "mozilla/dom/HTMLBodyElement.h"
132 #include "mozilla/dom/SVGPathData.h"
133 #include "mozilla/dom/TouchEvent.h"
134 #include "mozilla/gfx/Tools.h"
135 #include "mozilla/layers/WebRenderUserData.h"
136 #include "mozilla/layout/ScrollAnchorContainer.h"
137 #include "nsPrintfCString.h"
138 #include "ActiveLayerTracker.h"
139
140 #include "nsITheme.h"
141
142 using namespace mozilla;
143 using namespace mozilla::css;
144 using namespace mozilla::dom;
145 using namespace mozilla::gfx;
146 using namespace mozilla::layers;
147 using namespace mozilla::layout;
148 typedef nsAbsoluteContainingBlock::AbsPosReflowFlags AbsPosReflowFlags;
149 using nsStyleTransformMatrix::TransformReferenceBox;
150
151 const mozilla::LayoutFrameType nsIFrame::sLayoutFrameTypes[
152 #define FRAME_ID(...) 1 +
153 #define ABSTRACT_FRAME_ID(...)
154 #include "mozilla/FrameIdList.h"
155 #undef FRAME_ID
156 #undef ABSTRACT_FRAME_ID
157 0] = {
158 #define FRAME_ID(class_, type_, ...) mozilla::LayoutFrameType::type_,
159 #define ABSTRACT_FRAME_ID(...)
160 #include "mozilla/FrameIdList.h"
161 #undef FRAME_ID
162 #undef ABSTRACT_FRAME_ID
163 };
164
165 const nsIFrame::FrameClassBits nsIFrame::sFrameClassBits[
166 #define FRAME_ID(...) 1 +
167 #define ABSTRACT_FRAME_ID(...)
168 #include "mozilla/FrameIdList.h"
169 #undef FRAME_ID
170 #undef ABSTRACT_FRAME_ID
171 0] = {
172 #define Leaf eFrameClassBitsLeaf
173 #define NotLeaf eFrameClassBitsNone
174 #define FRAME_ID(class_, type_, leaf_, ...) leaf_,
175 #define ABSTRACT_FRAME_ID(...)
176 #include "mozilla/FrameIdList.h"
177 #undef Leaf
178 #undef NotLeaf
179 #undef FRAME_ID
180 #undef ABSTRACT_FRAME_ID
181 };
182
183 // Struct containing cached metrics for box-wrapped frames.
184 struct nsBoxLayoutMetrics {
185 nsSize mPrefSize;
186 nsSize mMinSize;
187 nsSize mMaxSize;
188
189 nsSize mBlockMinSize;
190 nsSize mBlockPrefSize;
191 nscoord mBlockAscent;
192
193 nscoord mFlex;
194 nscoord mAscent;
195
196 nsSize mLastSize;
197 };
198
199 struct nsContentAndOffset {
200 nsIContent* mContent = nullptr;
201 int32_t mOffset = 0;
202 };
203
204 // Some Misc #defines
205 #define SELECTION_DEBUG 0
206 #define FORCE_SELECTION_UPDATE 1
207 #define CALC_DEBUG 0
208
209 #include "nsILineIterator.h"
210 #include "prenv.h"
211
NS_DECLARE_FRAME_PROPERTY_DELETABLE(BoxMetricsProperty,nsBoxLayoutMetrics)212 NS_DECLARE_FRAME_PROPERTY_DELETABLE(BoxMetricsProperty, nsBoxLayoutMetrics)
213
214 static void InitBoxMetrics(nsIFrame* aFrame, bool aClear) {
215 if (aClear) {
216 aFrame->RemoveProperty(BoxMetricsProperty());
217 }
218
219 nsBoxLayoutMetrics* metrics = new nsBoxLayoutMetrics();
220 aFrame->SetProperty(BoxMetricsProperty(), metrics);
221
222 aFrame->nsIFrame::MarkIntrinsicISizesDirty();
223 metrics->mBlockAscent = 0;
224 metrics->mLastSize.SizeTo(0, 0);
225 }
226
227 // Utility function to set a nsRect-valued property table entry on aFrame,
228 // reusing the existing storage if the property happens to be already set.
229 template <typename T>
SetOrUpdateRectValuedProperty(nsIFrame * aFrame,FrameProperties::Descriptor<T> aProperty,const nsRect & aNewValue)230 static void SetOrUpdateRectValuedProperty(
231 nsIFrame* aFrame, FrameProperties::Descriptor<T> aProperty,
232 const nsRect& aNewValue) {
233 bool found;
234 nsRect* rectStorage = aFrame->GetProperty(aProperty, &found);
235 if (!found) {
236 rectStorage = new nsRect(aNewValue);
237 aFrame->AddProperty(aProperty, rectStorage);
238 } else {
239 *rectStorage = aNewValue;
240 }
241 }
242
IsXULBoxWrapped(const nsIFrame * aFrame)243 static bool IsXULBoxWrapped(const nsIFrame* aFrame) {
244 return aFrame->GetParent() && aFrame->GetParent()->IsXULBoxFrame() &&
245 !aFrame->IsXULBoxFrame();
246 }
247
UpdateTruncated(const ReflowInput & aReflowInput,const ReflowOutput & aMetrics)248 void nsReflowStatus::UpdateTruncated(const ReflowInput& aReflowInput,
249 const ReflowOutput& aMetrics) {
250 const WritingMode containerWM = aMetrics.GetWritingMode();
251 if (aReflowInput.GetWritingMode().IsOrthogonalTo(containerWM)) {
252 // Orthogonal flows are always reflowed with an unconstrained dimension,
253 // so should never end up truncated (see ReflowInput::Init()).
254 mTruncated = false;
255 } else if (aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE &&
256 aReflowInput.AvailableBSize() < aMetrics.BSize(containerWM) &&
257 !aReflowInput.mFlags.mIsTopOfPage) {
258 mTruncated = true;
259 } else {
260 mTruncated = false;
261 }
262 }
263
264 /* static */
DestroyAnonymousContent(nsPresContext * aPresContext,already_AddRefed<nsIContent> && aContent)265 void nsIFrame::DestroyAnonymousContent(
266 nsPresContext* aPresContext, already_AddRefed<nsIContent>&& aContent) {
267 if (nsCOMPtr<nsIContent> content = aContent) {
268 aPresContext->EventStateManager()->NativeAnonymousContentRemoved(content);
269 aPresContext->PresShell()->NativeAnonymousContentRemoved(content);
270 content->UnbindFromTree();
271 }
272 }
273
274 // Formerly the nsIFrameDebug interface
275
operator <<(std::ostream & aStream,const nsReflowStatus & aStatus)276 std::ostream& operator<<(std::ostream& aStream, const nsReflowStatus& aStatus) {
277 char complete = 'Y';
278 if (aStatus.IsIncomplete()) {
279 complete = 'N';
280 } else if (aStatus.IsOverflowIncomplete()) {
281 complete = 'O';
282 }
283
284 char brk = 'N';
285 if (aStatus.IsInlineBreakBefore()) {
286 brk = 'B';
287 } else if (aStatus.IsInlineBreakAfter()) {
288 brk = 'A';
289 }
290
291 aStream << "["
292 << "Complete=" << complete << ","
293 << "NIF=" << (aStatus.NextInFlowNeedsReflow() ? 'Y' : 'N') << ","
294 << "Truncated=" << (aStatus.IsTruncated() ? 'Y' : 'N') << ","
295 << "Break=" << brk << ","
296 << "FirstLetter=" << (aStatus.FirstLetterComplete() ? 'Y' : 'N')
297 << "]";
298 return aStream;
299 }
300
301 #ifdef DEBUG
302 static bool gShowFrameBorders = false;
303
ShowFrameBorders(bool aEnable)304 void nsIFrame::ShowFrameBorders(bool aEnable) { gShowFrameBorders = aEnable; }
305
GetShowFrameBorders()306 bool nsIFrame::GetShowFrameBorders() { return gShowFrameBorders; }
307
308 static bool gShowEventTargetFrameBorder = false;
309
ShowEventTargetFrameBorder(bool aEnable)310 void nsIFrame::ShowEventTargetFrameBorder(bool aEnable) {
311 gShowEventTargetFrameBorder = aEnable;
312 }
313
GetShowEventTargetFrameBorder()314 bool nsIFrame::GetShowEventTargetFrameBorder() {
315 return gShowEventTargetFrameBorder;
316 }
317
318 /**
319 * Note: the log module is created during library initialization which
320 * means that you cannot perform logging before then.
321 */
322 mozilla::LazyLogModule nsIFrame::sFrameLogModule("frame");
323
324 #endif
325
NS_DECLARE_FRAME_PROPERTY_DELETABLE(AbsoluteContainingBlockProperty,nsAbsoluteContainingBlock)326 NS_DECLARE_FRAME_PROPERTY_DELETABLE(AbsoluteContainingBlockProperty,
327 nsAbsoluteContainingBlock)
328
329 bool nsIFrame::HasAbsolutelyPositionedChildren() const {
330 return IsAbsoluteContainer() &&
331 GetAbsoluteContainingBlock()->HasAbsoluteFrames();
332 }
333
GetAbsoluteContainingBlock() const334 nsAbsoluteContainingBlock* nsIFrame::GetAbsoluteContainingBlock() const {
335 NS_ASSERTION(IsAbsoluteContainer(),
336 "The frame is not marked as an abspos container correctly");
337 nsAbsoluteContainingBlock* absCB =
338 GetProperty(AbsoluteContainingBlockProperty());
339 NS_ASSERTION(absCB,
340 "The frame is marked as an abspos container but doesn't have "
341 "the property");
342 return absCB;
343 }
344
MarkAsAbsoluteContainingBlock()345 void nsIFrame::MarkAsAbsoluteContainingBlock() {
346 MOZ_ASSERT(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN));
347 NS_ASSERTION(!GetProperty(AbsoluteContainingBlockProperty()),
348 "Already has an abs-pos containing block property?");
349 NS_ASSERTION(!HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN),
350 "Already has NS_FRAME_HAS_ABSPOS_CHILDREN state bit?");
351 AddStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN);
352 SetProperty(AbsoluteContainingBlockProperty(),
353 new nsAbsoluteContainingBlock(GetAbsoluteListID()));
354 }
355
MarkAsNotAbsoluteContainingBlock()356 void nsIFrame::MarkAsNotAbsoluteContainingBlock() {
357 NS_ASSERTION(!HasAbsolutelyPositionedChildren(), "Think of the children!");
358 NS_ASSERTION(GetProperty(AbsoluteContainingBlockProperty()),
359 "Should have an abs-pos containing block property");
360 NS_ASSERTION(HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN),
361 "Should have NS_FRAME_HAS_ABSPOS_CHILDREN state bit");
362 MOZ_ASSERT(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN));
363 RemoveStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN);
364 RemoveProperty(AbsoluteContainingBlockProperty());
365 }
366
CheckAndClearPaintedState()367 bool nsIFrame::CheckAndClearPaintedState() {
368 bool result = HasAnyStateBits(NS_FRAME_PAINTED_THEBES);
369 RemoveStateBits(NS_FRAME_PAINTED_THEBES);
370
371 for (const auto& childList : ChildLists()) {
372 for (nsIFrame* child : childList.mList) {
373 if (child->CheckAndClearPaintedState()) {
374 result = true;
375 }
376 }
377 }
378 return result;
379 }
380
CheckAndClearDisplayListState()381 bool nsIFrame::CheckAndClearDisplayListState() {
382 bool result = BuiltDisplayList();
383 SetBuiltDisplayList(false);
384
385 for (const auto& childList : ChildLists()) {
386 for (nsIFrame* child : childList.mList) {
387 if (child->CheckAndClearDisplayListState()) {
388 result = true;
389 }
390 }
391 }
392 return result;
393 }
394
IsVisibleConsideringAncestors(uint32_t aFlags) const395 bool nsIFrame::IsVisibleConsideringAncestors(uint32_t aFlags) const {
396 if (!StyleVisibility()->IsVisible()) {
397 return false;
398 }
399
400 if (PresShell()->IsUnderHiddenEmbedderElement()) {
401 return false;
402 }
403
404 const nsIFrame* frame = this;
405 while (frame) {
406 nsView* view = frame->GetView();
407 if (view && view->GetVisibility() == nsViewVisibility_kHide) return false;
408
409 nsIFrame* parent = frame->GetParent();
410 nsDeckFrame* deck = do_QueryFrame(parent);
411 if (deck) {
412 if (deck->GetSelectedBox() != frame) return false;
413 }
414
415 if (parent) {
416 frame = parent;
417 } else {
418 parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(frame);
419 if (!parent) break;
420
421 if ((aFlags & nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) == 0 &&
422 parent->PresContext()->IsChrome() &&
423 !frame->PresContext()->IsChrome()) {
424 break;
425 }
426
427 frame = parent;
428 }
429 }
430
431 return true;
432 }
433
FindCloserFrameForSelection(const nsPoint & aPoint,FrameWithDistance * aCurrentBestFrame)434 void nsIFrame::FindCloserFrameForSelection(
435 const nsPoint& aPoint, FrameWithDistance* aCurrentBestFrame) {
436 if (nsLayoutUtils::PointIsCloserToRect(aPoint, mRect,
437 aCurrentBestFrame->mXDistance,
438 aCurrentBestFrame->mYDistance)) {
439 aCurrentBestFrame->mFrame = this;
440 }
441 }
442
ContentStatesChanged(mozilla::EventStates aStates)443 void nsIFrame::ContentStatesChanged(mozilla::EventStates aStates) {}
444
Clear(mozilla::PresShell * aPresShell)445 void WeakFrame::Clear(mozilla::PresShell* aPresShell) {
446 if (aPresShell) {
447 aPresShell->RemoveWeakFrame(this);
448 }
449 mFrame = nullptr;
450 }
451
AutoWeakFrame(const WeakFrame & aOther)452 AutoWeakFrame::AutoWeakFrame(const WeakFrame& aOther)
453 : mPrev(nullptr), mFrame(nullptr) {
454 Init(aOther.GetFrame());
455 }
456
Clear(mozilla::PresShell * aPresShell)457 void AutoWeakFrame::Clear(mozilla::PresShell* aPresShell) {
458 if (aPresShell) {
459 aPresShell->RemoveAutoWeakFrame(this);
460 }
461 mFrame = nullptr;
462 mPrev = nullptr;
463 }
464
~AutoWeakFrame()465 AutoWeakFrame::~AutoWeakFrame() {
466 Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
467 }
468
Init(nsIFrame * aFrame)469 void AutoWeakFrame::Init(nsIFrame* aFrame) {
470 Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
471 mFrame = aFrame;
472 if (mFrame) {
473 mozilla::PresShell* presShell = mFrame->PresContext()->GetPresShell();
474 NS_WARNING_ASSERTION(presShell, "Null PresShell in AutoWeakFrame!");
475 if (presShell) {
476 presShell->AddAutoWeakFrame(this);
477 } else {
478 mFrame = nullptr;
479 }
480 }
481 }
482
Init(nsIFrame * aFrame)483 void WeakFrame::Init(nsIFrame* aFrame) {
484 Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
485 mFrame = aFrame;
486 if (mFrame) {
487 mozilla::PresShell* presShell = mFrame->PresContext()->GetPresShell();
488 MOZ_ASSERT(presShell, "Null PresShell in WeakFrame!");
489 if (presShell) {
490 presShell->AddWeakFrame(this);
491 } else {
492 mFrame = nullptr;
493 }
494 }
495 }
496
NS_NewEmptyFrame(PresShell * aPresShell,ComputedStyle * aStyle)497 nsIFrame* NS_NewEmptyFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
498 return new (aPresShell) nsIFrame(aStyle, aPresShell->GetPresContext());
499 }
500
~nsIFrame()501 nsIFrame::~nsIFrame() {
502 MOZ_COUNT_DTOR(nsIFrame);
503
504 MOZ_ASSERT(GetVisibility() != Visibility::ApproximatelyVisible,
505 "Visible nsFrame is being destroyed");
506 }
507
NS_IMPL_FRAMEARENA_HELPERS(nsIFrame)508 NS_IMPL_FRAMEARENA_HELPERS(nsIFrame)
509
510 // Dummy operator delete. Will never be called, but must be defined
511 // to satisfy some C++ ABIs.
512 void nsIFrame::operator delete(void*, size_t) {
513 MOZ_CRASH("nsIFrame::operator delete should never be called");
514 }
515
516 NS_QUERYFRAME_HEAD(nsIFrame)
NS_QUERYFRAME_ENTRY(nsIFrame)517 NS_QUERYFRAME_ENTRY(nsIFrame)
518 NS_QUERYFRAME_TAIL_INHERITANCE_ROOT
519
520 /////////////////////////////////////////////////////////////////////////////
521 // nsIFrame
522
523 static bool IsFontSizeInflationContainer(nsIFrame* aFrame,
524 const nsStyleDisplay* aStyleDisplay) {
525 /*
526 * Font size inflation is built around the idea that we're inflating
527 * the fonts for a pan-and-zoom UI so that when the user scales up a
528 * block or other container to fill the width of the device, the fonts
529 * will be readable. To do this, we need to pick what counts as a
530 * container.
531 *
532 * From a code perspective, the only hard requirement is that frames
533 * that are line participants
534 * (nsIFrame::IsFrameOfType(nsIFrame::eLineParticipant)) are never
535 * containers, since line layout assumes that the inflation is
536 * consistent within a line.
537 *
538 * This is not an imposition, since we obviously want a bunch of text
539 * (possibly with inline elements) flowing within a block to count the
540 * block (or higher) as its container.
541 *
542 * We also want form controls, including the text in the anonymous
543 * content inside of them, to match each other and the text next to
544 * them, so they and their anonymous content should also not be a
545 * container.
546 *
547 * However, because we can't reliably compute sizes across XUL during
548 * reflow, any XUL frame with a XUL parent is always a container.
549 *
550 * There are contexts where it would be nice if some blocks didn't
551 * count as a container, so that, for example, an indented quotation
552 * didn't end up with a smaller font size. However, it's hard to
553 * distinguish these situations where we really do want the indented
554 * thing to count as a container, so we don't try, and blocks are
555 * always containers.
556 */
557
558 // The root frame should always be an inflation container.
559 if (!aFrame->GetParent()) {
560 return true;
561 }
562
563 nsIContent* content = aFrame->GetContent();
564 if (content && content->IsInNativeAnonymousSubtree()) {
565 // Native anonymous content shouldn't be a font inflation root,
566 // except for the canvas custom content container.
567 nsCanvasFrame* canvas = aFrame->PresShell()->GetCanvasFrame();
568 return canvas && canvas->GetCustomContentContainer() == content;
569 }
570
571 LayoutFrameType frameType = aFrame->Type();
572 bool isInline =
573 (nsStyleDisplay::IsInlineFlow(aFrame->GetDisplay()) ||
574 RubyUtils::IsRubyBox(frameType) ||
575 (aStyleDisplay->IsFloatingStyle() &&
576 frameType == LayoutFrameType::Letter) ||
577 // Given multiple frames for the same node, only the
578 // outer one should be considered a container.
579 // (Important, e.g., for nsSelectsAreaFrame.)
580 (aFrame->GetParent()->GetContent() == content) ||
581 (content &&
582 // Form controls shouldn't become inflation containers.
583 (content->IsAnyOfHTMLElements(
584 nsGkAtoms::option, nsGkAtoms::optgroup, nsGkAtoms::select,
585 nsGkAtoms::input, nsGkAtoms::button, nsGkAtoms::textarea)))) &&
586 !(aFrame->IsXULBoxFrame() && aFrame->GetParent()->IsXULBoxFrame());
587 NS_ASSERTION(!aFrame->IsFrameOfType(nsIFrame::eLineParticipant) || isInline ||
588 // br frames and mathml frames report being line
589 // participants even when their position or display is
590 // set
591 aFrame->IsBrFrame() ||
592 aFrame->IsFrameOfType(nsIFrame::eMathML),
593 "line participants must not be containers");
594 return !isInline;
595 }
596
MaybeScheduleReflowSVGNonDisplayText(nsIFrame * aFrame)597 static void MaybeScheduleReflowSVGNonDisplayText(nsIFrame* aFrame) {
598 if (!SVGUtils::IsInSVGTextSubtree(aFrame)) {
599 return;
600 }
601
602 // We need to ensure that any non-display SVGTextFrames get reflowed when a
603 // child text frame gets new style. Thus we need to schedule a reflow in
604 // |DidSetComputedStyle|. We also need to call it from |DestroyFrom|,
605 // because otherwise we won't get notified when style changes to
606 // "display:none".
607 SVGTextFrame* svgTextFrame = static_cast<SVGTextFrame*>(
608 nsLayoutUtils::GetClosestFrameOfType(aFrame, LayoutFrameType::SVGText));
609 nsIFrame* anonBlock = svgTextFrame->PrincipalChildList().FirstChild();
610
611 // Note that we must check NS_FRAME_FIRST_REFLOW on our SVGTextFrame's
612 // anonymous block frame rather than our aFrame, since NS_FRAME_FIRST_REFLOW
613 // may be set on us if we're a new frame that has been inserted after the
614 // document's first reflow. (In which case this DidSetComputedStyle call may
615 // be happening under frame construction under a Reflow() call.)
616 if (!anonBlock || anonBlock->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
617 return;
618 }
619
620 if (!svgTextFrame->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY) ||
621 svgTextFrame->HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW)) {
622 return;
623 }
624
625 svgTextFrame->ScheduleReflowSVGNonDisplayText(IntrinsicDirty::StyleChange);
626 }
627
IsPrimaryFrameOfRootOrBodyElement() const628 bool nsIFrame::IsPrimaryFrameOfRootOrBodyElement() const {
629 if (!IsPrimaryFrame()) {
630 return false;
631 }
632 nsIContent* content = GetContent();
633 Document* document = content->OwnerDoc();
634 return content == document->GetRootElement() ||
635 content == document->GetBodyElement();
636 }
637
IsRenderedLegend() const638 bool nsIFrame::IsRenderedLegend() const {
639 if (auto* parent = GetParent(); parent && parent->IsFieldSetFrame()) {
640 return static_cast<nsFieldSetFrame*>(parent)->GetLegend() == this;
641 }
642 return false;
643 }
644
Init(nsIContent * aContent,nsContainerFrame * aParent,nsIFrame * aPrevInFlow)645 void nsIFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
646 nsIFrame* aPrevInFlow) {
647 MOZ_ASSERT(nsQueryFrame::FrameIID(mClass) == GetFrameId());
648 MOZ_ASSERT(!mContent, "Double-initing a frame?");
649 NS_ASSERTION(IsFrameOfType(eDEBUGAllFrames) && !IsFrameOfType(eDEBUGNoFrames),
650 "IsFrameOfType implementation that doesn't call base class");
651
652 mContent = aContent;
653 mParent = aParent;
654 MOZ_DIAGNOSTIC_ASSERT(!mParent || PresShell() == mParent->PresShell());
655
656 if (aPrevInFlow) {
657 mWritingMode = aPrevInFlow->GetWritingMode();
658
659 // Copy some state bits from prev-in-flow (the bits that should apply
660 // throughout a continuation chain). The bits are sorted according to their
661 // order in nsFrameStateBits.h.
662
663 // clang-format off
664 AddStateBits(aPrevInFlow->GetStateBits() &
665 (NS_FRAME_GENERATED_CONTENT |
666 NS_FRAME_OUT_OF_FLOW |
667 NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN |
668 NS_FRAME_INDEPENDENT_SELECTION |
669 NS_FRAME_PART_OF_IBSPLIT |
670 NS_FRAME_MAY_BE_TRANSFORMED |
671 NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR));
672 // clang-format on
673
674 // Copy other bits in nsIFrame from prev-in-flow.
675 mHasColumnSpanSiblings = aPrevInFlow->HasColumnSpanSiblings();
676 } else {
677 PresContext()->ConstructedFrame();
678 }
679
680 if (GetParent()) {
681 if (MOZ_UNLIKELY(mContent == PresContext()->Document()->GetRootElement() &&
682 mContent == GetParent()->GetContent())) {
683 // Our content is the root element and we have the same content as our
684 // parent. That is, we are the internal anonymous frame of the root
685 // element. Copy the used mWritingMode from our parent because
686 // mDocElementContainingBlock gets its mWritingMode from <body>.
687 mWritingMode = GetParent()->GetWritingMode();
688 }
689
690 // Copy some state bits from our parent (the bits that should apply
691 // recursively throughout a subtree). The bits are sorted according to their
692 // order in nsFrameStateBits.h.
693
694 // clang-format off
695 AddStateBits(GetParent()->GetStateBits() &
696 (NS_FRAME_GENERATED_CONTENT |
697 NS_FRAME_INDEPENDENT_SELECTION |
698 NS_FRAME_IS_SVG_TEXT |
699 NS_FRAME_IN_POPUP |
700 NS_FRAME_IS_NONDISPLAY));
701 // clang-format on
702
703 if (HasAnyStateBits(NS_FRAME_IN_POPUP) && TrackingVisibility()) {
704 // Assume all frames in popups are visible.
705 IncApproximateVisibleCount();
706 }
707 }
708 if (aPrevInFlow) {
709 mMayHaveOpacityAnimation = aPrevInFlow->MayHaveOpacityAnimation();
710 mMayHaveTransformAnimation = aPrevInFlow->MayHaveTransformAnimation();
711 } else if (mContent) {
712 // It's fine to fetch the EffectSet for the style frame here because in the
713 // following code we take care of the case where animations may target
714 // a different frame.
715 EffectSet* effectSet = EffectSet::GetEffectSetForStyleFrame(this);
716 if (effectSet) {
717 mMayHaveOpacityAnimation = effectSet->MayHaveOpacityAnimation();
718
719 if (effectSet->MayHaveTransformAnimation()) {
720 // If we are the inner table frame for display:table content, then
721 // transform animations should go on our parent frame (the table wrapper
722 // frame).
723 //
724 // We do this when initializing the child frame (table inner frame),
725 // because when initializng the table wrapper frame, we don't yet have
726 // access to its children so we can't tell if we have transform
727 // animations or not.
728 if (IsFrameOfType(eSupportsCSSTransforms)) {
729 mMayHaveTransformAnimation = true;
730 AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
731 } else if (aParent && nsLayoutUtils::GetStyleFrame(aParent) == this) {
732 MOZ_ASSERT(
733 aParent->IsFrameOfType(eSupportsCSSTransforms),
734 "Style frames that don't support transforms should have parents"
735 " that do");
736 aParent->mMayHaveTransformAnimation = true;
737 aParent->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
738 }
739 }
740 }
741 }
742
743 const nsStyleDisplay* disp = StyleDisplay();
744 if (disp->HasTransform(this)) {
745 // If 'transform' dynamically changes, RestyleManager takes care of
746 // updating this bit.
747 AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
748 }
749
750 if (disp->IsContainLayout() && disp->IsContainSize() &&
751 // All frames that support contain:layout also support contain:size.
752 IsFrameOfType(eSupportsContainLayoutAndPaint) && !IsTableWrapperFrame()) {
753 // In general, frames that have contain:layout+size can be reflow roots.
754 // (One exception: table-wrapper frames don't work well as reflow roots,
755 // because their inner-table ReflowInput init path tries to reuse & deref
756 // the wrapper's containing block's reflow input, which may be null if we
757 // initiate reflow from the table-wrapper itself.)
758 //
759 // Changes to `contain` force frame reconstructions, so this bit can be set
760 // for the whole lifetime of this frame.
761 AddStateBits(NS_FRAME_REFLOW_ROOT);
762 }
763
764 if (nsLayoutUtils::FontSizeInflationEnabled(PresContext()) ||
765 !GetParent()
766 #ifdef DEBUG
767 // We have assertions that check inflation invariants even when
768 // font size inflation is not enabled.
769 || true
770 #endif
771 ) {
772 if (IsFontSizeInflationContainer(this, disp)) {
773 AddStateBits(NS_FRAME_FONT_INFLATION_CONTAINER);
774 if (!GetParent() ||
775 // I'd use NS_FRAME_OUT_OF_FLOW, but it's not set yet.
776 disp->IsFloating(this) || disp->IsAbsolutelyPositioned(this) ||
777 GetParent()->IsFlexContainerFrame() ||
778 GetParent()->IsGridContainerFrame()) {
779 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
780 }
781 }
782 NS_ASSERTION(
783 GetParent() || HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER),
784 "root frame should always be a container");
785 }
786
787 if (PresShell()->AssumeAllFramesVisible() && TrackingVisibility()) {
788 IncApproximateVisibleCount();
789 }
790
791 DidSetComputedStyle(nullptr);
792
793 if (::IsXULBoxWrapped(this)) ::InitBoxMetrics(this, false);
794
795 // For a newly created frame, we need to update this frame's visibility state.
796 // Usually we update the state when the frame is restyled and has a
797 // VisibilityChange change hint but we don't generate any change hints for
798 // newly created frames.
799 // Note: We don't need to do this for placeholders since placeholders have
800 // different styles so that the styles don't have visibility:hidden even if
801 // the parent has visibility:hidden style. We also don't need to update the
802 // state when creating continuations because its visibility is the same as its
803 // prev-in-flow, and the animation code cares only primary frames.
804 if (!IsPlaceholderFrame() && !aPrevInFlow) {
805 UpdateVisibleDescendantsState();
806 }
807 }
808
DestroyFrom(nsIFrame * aDestructRoot,PostDestroyData & aPostDestroyData)809 void nsIFrame::DestroyFrom(nsIFrame* aDestructRoot,
810 PostDestroyData& aPostDestroyData) {
811 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
812 "destroy called on frame while scripts not blocked");
813 NS_ASSERTION(!GetNextSibling() && !GetPrevSibling(),
814 "Frames should be removed before destruction.");
815 NS_ASSERTION(aDestructRoot, "Must specify destruct root");
816 MOZ_ASSERT(!HasAbsolutelyPositionedChildren());
817 MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT),
818 "NS_FRAME_PART_OF_IBSPLIT set on non-nsContainerFrame?");
819
820 MaybeScheduleReflowSVGNonDisplayText(this);
821
822 SVGObserverUtils::InvalidateDirectRenderingObservers(this);
823
824 if (StyleDisplay()->mPosition == StylePositionProperty::Sticky) {
825 StickyScrollContainer* ssc =
826 StickyScrollContainer::GetStickyScrollContainerForFrame(this);
827 if (ssc) {
828 ssc->RemoveFrame(this);
829 }
830 }
831
832 nsPresContext* presContext = PresContext();
833 mozilla::PresShell* presShell = presContext->GetPresShell();
834 if (mState & NS_FRAME_OUT_OF_FLOW) {
835 nsPlaceholderFrame* placeholder = GetPlaceholderFrame();
836 NS_ASSERTION(
837 !placeholder || (aDestructRoot != this),
838 "Don't call Destroy() on OOFs, call Destroy() on the placeholder.");
839 NS_ASSERTION(!placeholder || nsLayoutUtils::IsProperAncestorFrame(
840 aDestructRoot, placeholder),
841 "Placeholder relationship should have been torn down already; "
842 "this might mean we have a stray placeholder in the tree.");
843 if (placeholder) {
844 placeholder->SetOutOfFlowFrame(nullptr);
845 }
846 }
847
848 if (IsPrimaryFrame()) {
849 // This needs to happen before we clear our Properties() table.
850 ActiveLayerTracker::TransferActivityToContent(this, mContent);
851 }
852
853 ScrollAnchorContainer* anchor = nullptr;
854 if (IsScrollAnchor(&anchor)) {
855 anchor->InvalidateAnchor();
856 }
857
858 if (HasCSSAnimations() || HasCSSTransitions() ||
859 // It's fine to look up the style frame here since if we're destroying the
860 // frames for display:table content we should be destroying both wrapper
861 // and inner frame.
862 EffectSet::GetEffectSetForStyleFrame(this)) {
863 // If no new frame for this element is created by the end of the
864 // restyling process, stop animations and transitions for this frame
865 RestyleManager::AnimationsWithDestroyedFrame* adf =
866 presContext->RestyleManager()->GetAnimationsWithDestroyedFrame();
867 // AnimationsWithDestroyedFrame only lives during the restyling process.
868 if (adf) {
869 adf->Put(mContent, mComputedStyle);
870 }
871 }
872
873 // Disable visibility tracking. Note that we have to do this before we clear
874 // frame properties and lose track of whether we were previously visible.
875 // XXX(seth): It'd be ideal to assert that we're already marked nonvisible
876 // here, but it's unfortunately tricky to guarantee in the face of things like
877 // frame reconstruction induced by style changes.
878 DisableVisibilityTracking();
879
880 // Ensure that we're not in the approximately visible list anymore.
881 PresContext()->GetPresShell()->RemoveFrameFromApproximatelyVisibleList(this);
882
883 presShell->NotifyDestroyingFrame(this);
884
885 if (mState & NS_FRAME_EXTERNAL_REFERENCE) {
886 presShell->ClearFrameRefs(this);
887 }
888
889 nsView* view = GetView();
890 if (view) {
891 view->SetFrame(nullptr);
892 view->Destroy();
893 }
894
895 // Make sure that our deleted frame can't be returned from GetPrimaryFrame()
896 if (IsPrimaryFrame()) {
897 mContent->SetPrimaryFrame(nullptr);
898
899 // Pass the root of a generated content subtree (e.g. ::after/::before) to
900 // aPostDestroyData to unbind it after frame destruction is done.
901 if (HasAnyStateBits(NS_FRAME_GENERATED_CONTENT) &&
902 mContent->IsRootOfNativeAnonymousSubtree()) {
903 aPostDestroyData.AddAnonymousContent(mContent.forget());
904 }
905 }
906
907 // Remove all properties attached to the frame, to ensure any property
908 // destructors that need the frame pointer are handled properly.
909 RemoveAllProperties();
910
911 // Must retrieve the object ID before calling destructors, so the
912 // vtable is still valid.
913 //
914 // Note to future tweakers: having the method that returns the
915 // object size call the destructor will not avoid an indirect call;
916 // the compiler cannot devirtualize the call to the destructor even
917 // if it's from a method defined in the same class.
918
919 nsQueryFrame::FrameIID id = GetFrameId();
920 this->~nsIFrame();
921
922 #ifdef DEBUG
923 {
924 nsIFrame* rootFrame = presShell->GetRootFrame();
925 MOZ_ASSERT(rootFrame);
926 if (this != rootFrame) {
927 const RetainedDisplayListData* data =
928 GetRetainedDisplayListData(rootFrame);
929
930 const bool inModifiedList = data && data->IsModified(this);
931
932 if (inModifiedList) {
933 DL_LOG(LogLevel::Warning, "Frame %p found in modified list", this);
934 }
935
936 MOZ_ASSERT(!inModifiedList,
937 "A dtor added this frame to modified frames list!");
938 }
939 }
940 #endif
941
942 // Now that we're totally cleaned out, we need to add ourselves to
943 // the presshell's recycler.
944 presShell->FreeFrame(id, this);
945 }
946
GetOffsets() const947 std::pair<int32_t, int32_t> nsIFrame::GetOffsets() const {
948 return std::make_pair(0, 0);
949 }
950
CompareLayers(const nsStyleImageLayers * aFirstLayers,const nsStyleImageLayers * aSecondLayers,const std::function<void (imgRequestProxy * aReq)> & aCallback)951 static void CompareLayers(
952 const nsStyleImageLayers* aFirstLayers,
953 const nsStyleImageLayers* aSecondLayers,
954 const std::function<void(imgRequestProxy* aReq)>& aCallback) {
955 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, (*aFirstLayers)) {
956 const auto& image = aFirstLayers->mLayers[i].mImage;
957 if (!image.IsImageRequestType() || !image.IsResolved()) {
958 continue;
959 }
960
961 // aCallback is called when the style image in aFirstLayers is thought to
962 // be different with the corresponded one in aSecondLayers
963 if (!aSecondLayers || i >= aSecondLayers->mImageCount ||
964 (!aSecondLayers->mLayers[i].mImage.IsResolved() ||
965 image.GetImageRequest() !=
966 aSecondLayers->mLayers[i].mImage.GetImageRequest())) {
967 if (imgRequestProxy* req = image.GetImageRequest()) {
968 aCallback(req);
969 }
970 }
971 }
972 }
973
AddAndRemoveImageAssociations(ImageLoader & aImageLoader,nsIFrame * aFrame,const nsStyleImageLayers * aOldLayers,const nsStyleImageLayers * aNewLayers)974 static void AddAndRemoveImageAssociations(
975 ImageLoader& aImageLoader, nsIFrame* aFrame,
976 const nsStyleImageLayers* aOldLayers,
977 const nsStyleImageLayers* aNewLayers) {
978 // If the old context had a background-image image, or mask-image image,
979 // and new context does not have the same image, clear the image load
980 // notifier (which keeps the image loading, if it still is) for the frame.
981 // We want to do this conservatively because some frames paint their
982 // backgrounds from some other frame's style data, and we don't want
983 // to clear those notifiers unless we have to. (They'll be reset
984 // when we paint, although we could miss a notification in that
985 // interval.)
986 if (aOldLayers && aFrame->HasImageRequest()) {
987 CompareLayers(aOldLayers, aNewLayers, [&](imgRequestProxy* aReq) {
988 aImageLoader.DisassociateRequestFromFrame(aReq, aFrame);
989 });
990 }
991
992 CompareLayers(aNewLayers, aOldLayers, [&](imgRequestProxy* aReq) {
993 aImageLoader.AssociateRequestToFrame(aReq, aFrame);
994 });
995 }
996
AddDisplayItem(nsDisplayItem * aItem)997 void nsIFrame::AddDisplayItem(nsDisplayItem* aItem) {
998 MOZ_DIAGNOSTIC_ASSERT(!mDisplayItems.Contains(aItem));
999 mDisplayItems.AppendElement(aItem);
1000 }
1001
RemoveDisplayItem(nsDisplayItem * aItem)1002 bool nsIFrame::RemoveDisplayItem(nsDisplayItem* aItem) {
1003 return mDisplayItems.RemoveElement(aItem);
1004 }
1005
HasDisplayItems()1006 bool nsIFrame::HasDisplayItems() { return !mDisplayItems.IsEmpty(); }
1007
HasDisplayItem(nsDisplayItem * aItem)1008 bool nsIFrame::HasDisplayItem(nsDisplayItem* aItem) {
1009 return mDisplayItems.Contains(aItem);
1010 }
1011
HasDisplayItem(uint32_t aKey)1012 bool nsIFrame::HasDisplayItem(uint32_t aKey) {
1013 for (nsDisplayItem* i : mDisplayItems) {
1014 if (i->GetPerFrameKey() == aKey) {
1015 return true;
1016 }
1017 }
1018 return false;
1019 }
1020
1021 template <typename Condition>
DiscardDisplayItems(nsIFrame * aFrame,Condition aCondition)1022 static void DiscardDisplayItems(nsIFrame* aFrame, Condition aCondition) {
1023 for (nsDisplayItem* i : aFrame->DisplayItems()) {
1024 // Only discard items that are invalidated by this frame, as we're only
1025 // guaranteed to rebuild those items. Table background items are created by
1026 // the relevant table part, but have the cell frame as the primary frame,
1027 // and we don't want to remove them if this is the cell.
1028 if (aCondition(i) && i->FrameForInvalidation() == aFrame) {
1029 i->SetCantBeReused();
1030 }
1031 }
1032 }
1033
DiscardOldItems(nsIFrame * aFrame)1034 static void DiscardOldItems(nsIFrame* aFrame) {
1035 DiscardDisplayItems(aFrame,
1036 [](nsDisplayItem* aItem) { return aItem->IsOldItem(); });
1037 }
1038
RemoveDisplayItemDataForDeletion()1039 void nsIFrame::RemoveDisplayItemDataForDeletion() {
1040 nsAutoString name;
1041 #ifdef DEBUG_FRAME_DUMP
1042 if (DL_LOG_TEST(LogLevel::Debug)) {
1043 GetFrameName(name);
1044 }
1045 #endif
1046 DL_LOGV("Removing display item data for frame %p (%s)", this,
1047 NS_ConvertUTF16toUTF8(name).get());
1048
1049 // Destroying a WebRenderUserDataTable can cause destruction of other objects
1050 // which can remove frame properties in their destructor. If we delete a frame
1051 // property it runs the destructor of the stored object in the middle of
1052 // updating the frame property table, so if the destruction of that object
1053 // causes another update to the frame property table it would leave the frame
1054 // property table in an inconsistent state. So we remove it from the table and
1055 // then destroy it. (bug 1530657)
1056 WebRenderUserDataTable* userDataTable =
1057 TakeProperty(WebRenderUserDataProperty::Key());
1058 if (userDataTable) {
1059 for (const auto& data : userDataTable->Values()) {
1060 data->RemoveFromTable();
1061 }
1062 delete userDataTable;
1063 }
1064
1065 for (nsDisplayItem* i : DisplayItems()) {
1066 if (i->GetDependentFrame() == this && !i->HasDeletedFrame()) {
1067 i->Frame()->MarkNeedsDisplayItemRebuild();
1068 }
1069 i->RemoveFrame(this);
1070 }
1071
1072 DisplayItems().Clear();
1073
1074 if (!nsLayoutUtils::AreRetainedDisplayListsEnabled()) {
1075 // Retained display lists are disabled, no need to update
1076 // RetainedDisplayListData.
1077 return;
1078 }
1079
1080 nsIFrame* rootFrame = PresShell()->GetRootFrame();
1081 MOZ_ASSERT(rootFrame);
1082
1083 RetainedDisplayListData* data = GetOrSetRetainedDisplayListData(rootFrame);
1084
1085 const bool updateData = IsFrameModified() || HasOverrideDirtyRegion() ||
1086 MayHaveWillChangeBudget();
1087
1088 if (!updateData) {
1089 // No RetainedDisplayListData to update.
1090 MOZ_DIAGNOSTIC_ASSERT(!data->IsModified(this),
1091 "Deleted frame is in modified frame list");
1092 return;
1093 }
1094
1095 if (MayHaveWillChangeBudget()) {
1096 // Keep the frame in list, so it can be removed from the will-change budget.
1097 data->Flags(this) = RetainedDisplayListData::FrameFlag::HadWillChange;
1098 return;
1099 }
1100
1101 if (IsFrameModified() || HasOverrideDirtyRegion()) {
1102 // Remove deleted frames from RetainedDisplayListData.
1103 DebugOnly<bool> removed = data->Remove(this);
1104 MOZ_ASSERT(removed,
1105 "Frame had flags set, but it was not found in DisplayListData!");
1106 }
1107 }
1108
MarkNeedsDisplayItemRebuild()1109 void nsIFrame::MarkNeedsDisplayItemRebuild() {
1110 if (!nsLayoutUtils::AreRetainedDisplayListsEnabled() || IsFrameModified() ||
1111 HasAnyStateBits(NS_FRAME_IN_POPUP)) {
1112 // Skip frames that are already marked modified.
1113 return;
1114 }
1115
1116 if (Type() == LayoutFrameType::Placeholder) {
1117 nsIFrame* oof = static_cast<nsPlaceholderFrame*>(this)->GetOutOfFlowFrame();
1118 if (oof) {
1119 oof->MarkNeedsDisplayItemRebuild();
1120 }
1121 // Do not mark placeholder frames modified.
1122 return;
1123 }
1124
1125 if (!nsLayoutUtils::DisplayRootHasRetainedDisplayListBuilder(this)) {
1126 return;
1127 }
1128
1129 nsIFrame* rootFrame = PresShell()->GetRootFrame();
1130 MOZ_ASSERT(rootFrame);
1131
1132 if (rootFrame->IsFrameModified()) {
1133 return;
1134 }
1135
1136 nsAutoString name;
1137 #ifdef DEBUG_FRAME_DUMP
1138 if (DL_LOG_TEST(LogLevel::Debug)) {
1139 GetFrameName(name);
1140 }
1141 #endif
1142
1143 DL_LOGV("RDL - Rebuilding display items for frame %p (%s)", this,
1144 NS_ConvertUTF16toUTF8(name).get());
1145
1146 RetainedDisplayListData* data = GetOrSetRetainedDisplayListData(rootFrame);
1147 if (data->ModifiedFramesCount() >
1148 StaticPrefs::layout_display_list_rebuild_frame_limit()) {
1149 // If the modified frames count is above the rebuild limit, mark the root
1150 // frame modified, and stop marking additional frames modified.
1151 data->AddModifiedFrame(rootFrame);
1152 rootFrame->SetFrameIsModified(true);
1153 return;
1154 }
1155
1156 data->AddModifiedFrame(this);
1157 SetFrameIsModified(true);
1158
1159 MOZ_ASSERT(
1160 PresContext()->LayoutPhaseCount(nsLayoutPhase::DisplayListBuilding) == 0);
1161
1162 // Hopefully this is cheap, but we could use a frame state bit to note
1163 // the presence of dependencies to speed it up.
1164 for (nsDisplayItem* i : DisplayItems()) {
1165 if (i->HasDeletedFrame() || i->Frame() == this) {
1166 // Ignore the items with deleted frames, and the items with |this| as
1167 // the primary frame.
1168 continue;
1169 }
1170
1171 if (i->GetDependentFrame() == this) {
1172 // For items with |this| as a dependent frame, mark the primary frame
1173 // for rebuild.
1174 i->Frame()->MarkNeedsDisplayItemRebuild();
1175 }
1176 }
1177 }
1178
1179 // Subclass hook for style post processing
1180 /* virtual */
DidSetComputedStyle(ComputedStyle * aOldComputedStyle)1181 void nsIFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
1182 #ifdef ACCESSIBILITY
1183 // Don't notify for reconstructed frames here, since the frame is still being
1184 // constructed at this point and so LocalAccessible::GetFrame() will return
1185 // null. Style changes for reconstructed frames are handled in
1186 // DocAccessible::PruneOrInsertSubtree.
1187 if (aOldComputedStyle) {
1188 if (nsAccessibilityService* accService = GetAccService()) {
1189 accService->NotifyOfComputedStyleChange(PresShell(), mContent);
1190 }
1191 }
1192 #endif
1193
1194 MaybeScheduleReflowSVGNonDisplayText(this);
1195
1196 Document* doc = PresContext()->Document();
1197 ImageLoader* loader = doc->StyleImageLoader();
1198 // Continuing text frame doesn't initialize its continuation pointer before
1199 // reaching here for the first time, so we have to exclude text frames. This
1200 // doesn't affect correctness because text can't match selectors.
1201 //
1202 // FIXME(emilio): We should consider fixing that.
1203 //
1204 // TODO(emilio): Can we avoid doing some / all of the image stuff when
1205 // isNonTextFirstContinuation is false? We should consider doing this just for
1206 // primary frames and pseudos, but the first-line reparenting code makes it
1207 // all bad, should get around to bug 1465474 eventually :(
1208 const bool isNonText = !IsTextFrame();
1209 if (isNonText) {
1210 mComputedStyle->StartImageLoads(*doc, aOldComputedStyle);
1211 }
1212
1213 const nsStyleImageLayers* oldLayers =
1214 aOldComputedStyle ? &aOldComputedStyle->StyleBackground()->mImage
1215 : nullptr;
1216 const nsStyleImageLayers* newLayers = &StyleBackground()->mImage;
1217 AddAndRemoveImageAssociations(*loader, this, oldLayers, newLayers);
1218
1219 oldLayers =
1220 aOldComputedStyle ? &aOldComputedStyle->StyleSVGReset()->mMask : nullptr;
1221 newLayers = &StyleSVGReset()->mMask;
1222 AddAndRemoveImageAssociations(*loader, this, oldLayers, newLayers);
1223
1224 const nsStyleDisplay* disp = StyleDisplay();
1225 bool handleStickyChange = false;
1226 if (aOldComputedStyle) {
1227 // Detect style changes that should trigger a scroll anchor adjustment
1228 // suppression.
1229 // https://drafts.csswg.org/css-scroll-anchoring/#suppression-triggers
1230 bool needAnchorSuppression = false;
1231
1232 // If we detect a change on margin, padding or border, we store the old
1233 // values on the frame itself between now and reflow, so if someone
1234 // calls GetUsed(Margin|Border|Padding)() before the next reflow, we
1235 // can give an accurate answer.
1236 // We don't want to set the property if one already exists.
1237 nsMargin oldValue(0, 0, 0, 0);
1238 nsMargin newValue(0, 0, 0, 0);
1239 const nsStyleMargin* oldMargin = aOldComputedStyle->StyleMargin();
1240 if (oldMargin->GetMargin(oldValue)) {
1241 if (!StyleMargin()->GetMargin(newValue) || oldValue != newValue) {
1242 if (!HasProperty(UsedMarginProperty())) {
1243 AddProperty(UsedMarginProperty(), new nsMargin(oldValue));
1244 }
1245 needAnchorSuppression = true;
1246 }
1247 }
1248
1249 const nsStylePadding* oldPadding = aOldComputedStyle->StylePadding();
1250 if (oldPadding->GetPadding(oldValue)) {
1251 if (!StylePadding()->GetPadding(newValue) || oldValue != newValue) {
1252 if (!HasProperty(UsedPaddingProperty())) {
1253 AddProperty(UsedPaddingProperty(), new nsMargin(oldValue));
1254 }
1255 needAnchorSuppression = true;
1256 }
1257 }
1258
1259 const nsStyleBorder* oldBorder = aOldComputedStyle->StyleBorder();
1260 oldValue = oldBorder->GetComputedBorder();
1261 newValue = StyleBorder()->GetComputedBorder();
1262 if (oldValue != newValue && !HasProperty(UsedBorderProperty())) {
1263 AddProperty(UsedBorderProperty(), new nsMargin(oldValue));
1264 }
1265
1266 const nsStyleDisplay* oldDisp = aOldComputedStyle->StyleDisplay();
1267 if (oldDisp->mOverflowAnchor != disp->mOverflowAnchor) {
1268 if (auto* container = ScrollAnchorContainer::FindFor(this)) {
1269 container->InvalidateAnchor();
1270 }
1271 if (nsIScrollableFrame* scrollableFrame = do_QueryFrame(this)) {
1272 scrollableFrame->Anchor()->InvalidateAnchor();
1273 }
1274 }
1275
1276 if (mInScrollAnchorChain) {
1277 const nsStylePosition* pos = StylePosition();
1278 const nsStylePosition* oldPos = aOldComputedStyle->StylePosition();
1279 if (!needAnchorSuppression &&
1280 (oldPos->mOffset != pos->mOffset || oldPos->mWidth != pos->mWidth ||
1281 oldPos->mMinWidth != pos->mMinWidth ||
1282 oldPos->mMaxWidth != pos->mMaxWidth ||
1283 oldPos->mHeight != pos->mHeight ||
1284 oldPos->mMinHeight != pos->mMinHeight ||
1285 oldPos->mMaxHeight != pos->mMaxHeight ||
1286 oldDisp->mPosition != disp->mPosition ||
1287 oldDisp->mTransform != disp->mTransform)) {
1288 needAnchorSuppression = true;
1289 }
1290
1291 if (needAnchorSuppression &&
1292 StaticPrefs::layout_css_scroll_anchoring_suppressions_enabled()) {
1293 ScrollAnchorContainer::FindFor(this)->SuppressAdjustments();
1294 }
1295 }
1296
1297 if (disp->mPosition != oldDisp->mPosition) {
1298 if (!disp->IsRelativelyOrStickyPositionedStyle() &&
1299 oldDisp->IsRelativelyOrStickyPositionedStyle()) {
1300 RemoveProperty(NormalPositionProperty());
1301 }
1302
1303 handleStickyChange = disp->mPosition == StylePositionProperty::Sticky ||
1304 oldDisp->mPosition == StylePositionProperty::Sticky;
1305 }
1306 } else { // !aOldComputedStyle
1307 handleStickyChange = disp->mPosition == StylePositionProperty::Sticky;
1308 }
1309
1310 if (handleStickyChange && !HasAnyStateBits(NS_FRAME_IS_NONDISPLAY) &&
1311 !GetPrevInFlow()) {
1312 // Note that we only add first continuations, but we really only
1313 // want to add first continuation-or-ib-split-siblings. But since we don't
1314 // yet know if we're a later part of a block-in-inline split, we'll just
1315 // add later members of a block-in-inline split here, and then
1316 // StickyScrollContainer will remove them later.
1317 if (auto* ssc =
1318 StickyScrollContainer::GetStickyScrollContainerForFrame(this)) {
1319 if (disp->mPosition == StylePositionProperty::Sticky) {
1320 ssc->AddFrame(this);
1321 } else {
1322 ssc->RemoveFrame(this);
1323 }
1324 }
1325 }
1326
1327 imgIRequest* oldBorderImage =
1328 aOldComputedStyle
1329 ? aOldComputedStyle->StyleBorder()->GetBorderImageRequest()
1330 : nullptr;
1331 imgIRequest* newBorderImage = StyleBorder()->GetBorderImageRequest();
1332 // FIXME (Bug 759996): The following is no longer true.
1333 // For border-images, we can't be as conservative (we need to set the
1334 // new loaders if there has been any change) since the CalcDifference
1335 // call depended on the result of GetComputedBorder() and that result
1336 // depends on whether the image has loaded, start the image load now
1337 // so that we'll get notified when it completes loading and can do a
1338 // restyle. Otherwise, the image might finish loading from the
1339 // network before we start listening to its notifications, and then
1340 // we'll never know that it's finished loading. Likewise, we want to
1341 // do this for freshly-created frames to prevent a similar race if the
1342 // image loads between reflow (which can depend on whether the image
1343 // is loaded) and paint. We also don't really care about any callers who try
1344 // to paint borders with a different style, because they won't have the
1345 // correct size for the border either.
1346 if (oldBorderImage != newBorderImage) {
1347 // stop and restart the image loading/notification
1348 if (oldBorderImage && HasImageRequest()) {
1349 RemoveProperty(CachedBorderImageDataProperty());
1350 loader->DisassociateRequestFromFrame(oldBorderImage, this);
1351 }
1352 if (newBorderImage) {
1353 loader->AssociateRequestToFrame(newBorderImage, this);
1354 }
1355 }
1356
1357 auto GetShapeImageRequest = [](const ComputedStyle* aStyle) -> imgIRequest* {
1358 if (!aStyle) {
1359 return nullptr;
1360 }
1361 auto& shape = aStyle->StyleDisplay()->mShapeOutside;
1362 if (!shape.IsImage()) {
1363 return nullptr;
1364 }
1365 return shape.AsImage().GetImageRequest();
1366 };
1367
1368 imgIRequest* oldShapeImage = GetShapeImageRequest(aOldComputedStyle);
1369 imgIRequest* newShapeImage = GetShapeImageRequest(Style());
1370 if (oldShapeImage != newShapeImage) {
1371 if (oldShapeImage && HasImageRequest()) {
1372 loader->DisassociateRequestFromFrame(oldShapeImage, this);
1373 }
1374 if (newShapeImage) {
1375 loader->AssociateRequestToFrame(
1376 newShapeImage, this,
1377 ImageLoader::Flags::
1378 RequiresReflowOnFirstFrameCompleteAndLoadEventBlocking);
1379 }
1380 }
1381
1382 // SVGObserverUtils::GetEffectProperties() asserts that we only invoke it with
1383 // the first continuation so we need to check that in advance.
1384 const bool isNonTextFirstContinuation = isNonText && !GetPrevContinuation();
1385 if (isNonTextFirstContinuation) {
1386 // Kick off loading of external SVG resources referenced from properties if
1387 // any. This currently includes filter, clip-path, and mask.
1388 SVGObserverUtils::InitiateResourceDocLoads(this);
1389 }
1390
1391 // If the page contains markup that overrides text direction, and
1392 // does not contain any characters that would activate the Unicode
1393 // bidi algorithm, we need to call |SetBidiEnabled| on the pres
1394 // context before reflow starts. See bug 115921.
1395 if (StyleVisibility()->mDirection == StyleDirection::Rtl) {
1396 PresContext()->SetBidiEnabled();
1397 }
1398
1399 // The following part is for caching offset-path:path(). We cache the
1400 // flatten gfx path, so we don't have to rebuild and re-flattern it at
1401 // each cycle if we have animations on offset-* with a fixed offset-path.
1402 const StyleOffsetPath* oldPath =
1403 aOldComputedStyle ? &aOldComputedStyle->StyleDisplay()->mOffsetPath
1404 : nullptr;
1405 const StyleOffsetPath& newPath = StyleDisplay()->mOffsetPath;
1406 if (!oldPath || *oldPath != newPath) {
1407 if (newPath.IsPath()) {
1408 // Here we only need to build a valid path for motion path, so
1409 // using the default values of stroke-width, stoke-linecap, and fill-rule
1410 // is fine for now because what we want is to get the point and its normal
1411 // vector along the path, instead of rendering it.
1412 RefPtr<gfx::PathBuilder> builder =
1413 gfxPlatform::GetPlatform()
1414 ->ScreenReferenceDrawTarget()
1415 ->CreatePathBuilder(gfx::FillRule::FILL_WINDING);
1416 RefPtr<gfx::Path> path =
1417 MotionPathUtils::BuildPath(newPath.AsPath(), builder);
1418 if (path) {
1419 // The newPath could be path('') (i.e. empty path), so its gfx path
1420 // could be nullptr, and so we only set property for a non-empty path.
1421 SetProperty(nsIFrame::OffsetPathCache(), path.forget().take());
1422 } else {
1423 // May have an old cached path, so we have to delete it.
1424 RemoveProperty(nsIFrame::OffsetPathCache());
1425 }
1426 } else if (oldPath) {
1427 RemoveProperty(nsIFrame::OffsetPathCache());
1428 }
1429 }
1430
1431 RemoveStateBits(NS_FRAME_SIMPLE_EVENT_REGIONS | NS_FRAME_SIMPLE_DISPLAYLIST);
1432
1433 mMayHaveRoundedCorners = true;
1434 }
1435
1436 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
AssertNewStyleIsSane(ComputedStyle & aNewStyle)1437 void nsIFrame::AssertNewStyleIsSane(ComputedStyle& aNewStyle) {
1438 MOZ_DIAGNOSTIC_ASSERT(
1439 aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() ||
1440 // ::first-line continuations are weird, this should probably be fixed via
1441 // bug 1465474.
1442 (mComputedStyle->GetPseudoType() == PseudoStyleType::firstLine &&
1443 aNewStyle.GetPseudoType() == PseudoStyleType::mozLineFrame) ||
1444 // ::first-letter continuations are broken, in particular floating ones,
1445 // see bug 1490281. The construction code tries to fix this up after the
1446 // fact, then restyling undoes it...
1447 (mComputedStyle->GetPseudoType() == PseudoStyleType::mozText &&
1448 aNewStyle.GetPseudoType() == PseudoStyleType::firstLetterContinuation) ||
1449 (mComputedStyle->GetPseudoType() ==
1450 PseudoStyleType::firstLetterContinuation &&
1451 aNewStyle.GetPseudoType() == PseudoStyleType::mozText));
1452 }
1453 #endif
1454
ReparentFrameViewTo(nsViewManager * aViewManager,nsView * aNewParentView,nsView * aOldParentView)1455 void nsIFrame::ReparentFrameViewTo(nsViewManager* aViewManager,
1456 nsView* aNewParentView,
1457 nsView* aOldParentView) {
1458 if (HasView()) {
1459 if (IsMenuPopupFrame()) {
1460 // This view must be parented by the root view, don't reparent it.
1461 return;
1462 }
1463 nsView* view = GetView();
1464 // Verify that the current parent view is what we think it is
1465 // nsView* parentView;
1466 // NS_ASSERTION(parentView == aOldParentView, "unexpected parent view");
1467
1468 aViewManager->RemoveChild(view);
1469
1470 // The view will remember the Z-order and other attributes that have been
1471 // set on it.
1472 nsView* insertBefore =
1473 nsLayoutUtils::FindSiblingViewFor(aNewParentView, this);
1474 aViewManager->InsertChild(aNewParentView, view, insertBefore,
1475 insertBefore != nullptr);
1476 } else if (HasAnyStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW)) {
1477 for (const auto& childList : ChildLists()) {
1478 // Iterate the child frames, and check each child frame to see if it has
1479 // a view
1480 for (nsIFrame* child : childList.mList) {
1481 child->ReparentFrameViewTo(aViewManager, aNewParentView,
1482 aOldParentView);
1483 }
1484 }
1485 }
1486 }
1487
SyncFrameViewProperties(nsView * aView)1488 void nsIFrame::SyncFrameViewProperties(nsView* aView) {
1489 if (!aView) {
1490 aView = GetView();
1491 if (!aView) {
1492 return;
1493 }
1494 }
1495
1496 nsViewManager* vm = aView->GetViewManager();
1497
1498 // Make sure visibility is correct. This only affects nsSubDocumentFrame.
1499 if (!SupportsVisibilityHidden()) {
1500 // See if the view should be hidden or visible
1501 ComputedStyle* sc = Style();
1502 vm->SetViewVisibility(aView, sc->StyleVisibility()->IsVisible()
1503 ? nsViewVisibility_kShow
1504 : nsViewVisibility_kHide);
1505 }
1506
1507 const auto zIndex = ZIndex();
1508 const bool autoZIndex = !zIndex;
1509 vm->SetViewZIndex(aView, autoZIndex, zIndex.valueOr(0));
1510 }
1511
CreateView()1512 void nsIFrame::CreateView() {
1513 MOZ_ASSERT(!HasView());
1514
1515 nsView* parentView = GetParent()->GetClosestView();
1516 MOZ_ASSERT(parentView, "no parent with view");
1517
1518 nsViewManager* viewManager = parentView->GetViewManager();
1519 MOZ_ASSERT(viewManager, "null view manager");
1520
1521 nsView* view = viewManager->CreateView(GetRect(), parentView);
1522 SyncFrameViewProperties(view);
1523
1524 nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(parentView, this);
1525 // we insert this view 'above' the insertBefore view, unless insertBefore is
1526 // null, in which case we want to call with aAbove == false to insert at the
1527 // beginning in document order
1528 viewManager->InsertChild(parentView, view, insertBefore,
1529 insertBefore != nullptr);
1530
1531 // REVIEW: Don't create a widget for fixed-pos elements anymore.
1532 // ComputeRepaintRegionForCopy will calculate the right area to repaint
1533 // when we scroll.
1534 // Reparent views on any child frames (or their descendants) to this
1535 // view. We can just call ReparentFrameViewTo on this frame because
1536 // we know this frame has no view, so it will crawl the children. Also,
1537 // we know that any descendants with views must have 'parentView' as their
1538 // parent view.
1539 ReparentFrameViewTo(viewManager, view, parentView);
1540
1541 // Remember our view
1542 SetView(view);
1543
1544 NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
1545 ("nsIFrame::CreateView: frame=%p view=%p", this, view));
1546 }
1547
1548 // MSVC fails with link error "one or more multiply defined symbols found",
1549 // gcc fails with "hidden symbol `nsIFrame::kPrincipalList' isn't defined"
1550 // etc if they are not defined.
1551 #ifndef _MSC_VER
1552 // static nsIFrame constants; initialized in the header file.
1553 const nsIFrame::ChildListID nsIFrame::kPrincipalList;
1554 const nsIFrame::ChildListID nsIFrame::kAbsoluteList;
1555 const nsIFrame::ChildListID nsIFrame::kBulletList;
1556 const nsIFrame::ChildListID nsIFrame::kCaptionList;
1557 const nsIFrame::ChildListID nsIFrame::kColGroupList;
1558 const nsIFrame::ChildListID nsIFrame::kExcessOverflowContainersList;
1559 const nsIFrame::ChildListID nsIFrame::kFixedList;
1560 const nsIFrame::ChildListID nsIFrame::kFloatList;
1561 const nsIFrame::ChildListID nsIFrame::kOverflowContainersList;
1562 const nsIFrame::ChildListID nsIFrame::kOverflowList;
1563 const nsIFrame::ChildListID nsIFrame::kOverflowOutOfFlowList;
1564 const nsIFrame::ChildListID nsIFrame::kPopupList;
1565 const nsIFrame::ChildListID nsIFrame::kPushedFloatsList;
1566 const nsIFrame::ChildListID nsIFrame::kSelectPopupList;
1567 const nsIFrame::ChildListID nsIFrame::kNoReflowPrincipalList;
1568 #endif
1569
1570 /* virtual */
GetUsedMargin() const1571 nsMargin nsIFrame::GetUsedMargin() const {
1572 nsMargin margin(0, 0, 0, 0);
1573 if (((mState & NS_FRAME_FIRST_REFLOW) && !(mState & NS_FRAME_IN_REFLOW)) ||
1574 SVGUtils::IsInSVGTextSubtree(this))
1575 return margin;
1576
1577 nsMargin* m = GetProperty(UsedMarginProperty());
1578 if (m) {
1579 margin = *m;
1580 } else {
1581 if (!StyleMargin()->GetMargin(margin)) {
1582 // If we get here, our caller probably shouldn't be calling us...
1583 NS_ERROR(
1584 "Returning bogus 0-sized margin, because this margin "
1585 "depends on layout & isn't cached!");
1586 }
1587 }
1588 return margin;
1589 }
1590
1591 /* virtual */
GetUsedBorder() const1592 nsMargin nsIFrame::GetUsedBorder() const {
1593 nsMargin border(0, 0, 0, 0);
1594 if (((mState & NS_FRAME_FIRST_REFLOW) && !(mState & NS_FRAME_IN_REFLOW)) ||
1595 SVGUtils::IsInSVGTextSubtree(this))
1596 return border;
1597
1598 // Theme methods don't use const-ness.
1599 nsIFrame* mutable_this = const_cast<nsIFrame*>(this);
1600
1601 const nsStyleDisplay* disp = StyleDisplay();
1602 if (mutable_this->IsThemed(disp)) {
1603 nsPresContext* pc = PresContext();
1604 LayoutDeviceIntMargin widgetBorder = pc->Theme()->GetWidgetBorder(
1605 pc->DeviceContext(), mutable_this, disp->EffectiveAppearance());
1606 border =
1607 LayoutDevicePixel::ToAppUnits(widgetBorder, pc->AppUnitsPerDevPixel());
1608 return border;
1609 }
1610
1611 nsMargin* b = GetProperty(UsedBorderProperty());
1612 if (b) {
1613 border = *b;
1614 } else {
1615 border = StyleBorder()->GetComputedBorder();
1616 }
1617 return border;
1618 }
1619
1620 /* virtual */
GetUsedPadding() const1621 nsMargin nsIFrame::GetUsedPadding() const {
1622 nsMargin padding(0, 0, 0, 0);
1623 if (((mState & NS_FRAME_FIRST_REFLOW) && !(mState & NS_FRAME_IN_REFLOW)) ||
1624 SVGUtils::IsInSVGTextSubtree(this))
1625 return padding;
1626
1627 // Theme methods don't use const-ness.
1628 nsIFrame* mutable_this = const_cast<nsIFrame*>(this);
1629
1630 const nsStyleDisplay* disp = StyleDisplay();
1631 if (mutable_this->IsThemed(disp)) {
1632 nsPresContext* pc = PresContext();
1633 LayoutDeviceIntMargin widgetPadding;
1634 if (pc->Theme()->GetWidgetPadding(pc->DeviceContext(), mutable_this,
1635 disp->EffectiveAppearance(),
1636 &widgetPadding)) {
1637 return LayoutDevicePixel::ToAppUnits(widgetPadding,
1638 pc->AppUnitsPerDevPixel());
1639 }
1640 }
1641
1642 nsMargin* p = GetProperty(UsedPaddingProperty());
1643 if (p) {
1644 padding = *p;
1645 } else {
1646 if (!StylePadding()->GetPadding(padding)) {
1647 // If we get here, our caller probably shouldn't be calling us...
1648 NS_ERROR(
1649 "Returning bogus 0-sized padding, because this padding "
1650 "depends on layout & isn't cached!");
1651 }
1652 }
1653 return padding;
1654 }
1655
GetSkipSides() const1656 nsIFrame::Sides nsIFrame::GetSkipSides() const {
1657 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
1658 StyleBoxDecorationBreak::Clone) &&
1659 !HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
1660 return Sides();
1661 }
1662
1663 // Convert the logical skip sides to physical sides using the frame's
1664 // writing mode
1665 WritingMode writingMode = GetWritingMode();
1666 LogicalSides logicalSkip = GetLogicalSkipSides();
1667 Sides skip;
1668
1669 if (logicalSkip.BStart()) {
1670 if (writingMode.IsVertical()) {
1671 skip |= writingMode.IsVerticalLR() ? SideBits::eLeft : SideBits::eRight;
1672 } else {
1673 skip |= SideBits::eTop;
1674 }
1675 }
1676
1677 if (logicalSkip.BEnd()) {
1678 if (writingMode.IsVertical()) {
1679 skip |= writingMode.IsVerticalLR() ? SideBits::eRight : SideBits::eLeft;
1680 } else {
1681 skip |= SideBits::eBottom;
1682 }
1683 }
1684
1685 if (logicalSkip.IStart()) {
1686 if (writingMode.IsVertical()) {
1687 skip |= SideBits::eTop;
1688 } else {
1689 skip |= writingMode.IsBidiLTR() ? SideBits::eLeft : SideBits::eRight;
1690 }
1691 }
1692
1693 if (logicalSkip.IEnd()) {
1694 if (writingMode.IsVertical()) {
1695 skip |= SideBits::eBottom;
1696 } else {
1697 skip |= writingMode.IsBidiLTR() ? SideBits::eRight : SideBits::eLeft;
1698 }
1699 }
1700 return skip;
1701 }
1702
GetPaddingRectRelativeToSelf() const1703 nsRect nsIFrame::GetPaddingRectRelativeToSelf() const {
1704 nsMargin border = GetUsedBorder().ApplySkipSides(GetSkipSides());
1705 nsRect r(0, 0, mRect.width, mRect.height);
1706 r.Deflate(border);
1707 return r;
1708 }
1709
GetPaddingRect() const1710 nsRect nsIFrame::GetPaddingRect() const {
1711 return GetPaddingRectRelativeToSelf() + GetPosition();
1712 }
1713
WritingModeForLine(WritingMode aSelfWM,nsIFrame * aSubFrame) const1714 WritingMode nsIFrame::WritingModeForLine(WritingMode aSelfWM,
1715 nsIFrame* aSubFrame) const {
1716 MOZ_ASSERT(aSelfWM == GetWritingMode());
1717 WritingMode writingMode = aSelfWM;
1718
1719 if (StyleTextReset()->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_PLAINTEXT) {
1720 mozilla::intl::BidiEmbeddingLevel frameLevel =
1721 nsBidiPresUtils::GetFrameBaseLevel(aSubFrame);
1722 writingMode.SetDirectionFromBidiLevel(frameLevel);
1723 }
1724
1725 return writingMode;
1726 }
1727
GetMarginRect() const1728 nsRect nsIFrame::GetMarginRect() const {
1729 return GetMarginRectRelativeToSelf() + GetPosition();
1730 }
1731
GetMarginRectRelativeToSelf() const1732 nsRect nsIFrame::GetMarginRectRelativeToSelf() const {
1733 nsMargin m = GetUsedMargin().ApplySkipSides(GetSkipSides());
1734 nsRect r(0, 0, mRect.width, mRect.height);
1735 r.Inflate(m);
1736 return r;
1737 }
1738
IsTransformed() const1739 bool nsIFrame::IsTransformed() const {
1740 if (!HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED)) {
1741 MOZ_ASSERT(!IsCSSTransformed());
1742 MOZ_ASSERT(!IsSVGTransformed());
1743 return false;
1744 }
1745 return IsCSSTransformed() || IsSVGTransformed();
1746 }
1747
IsCSSTransformed() const1748 bool nsIFrame::IsCSSTransformed() const {
1749 return HasAnyStateBits(NS_FRAME_MAY_BE_TRANSFORMED) &&
1750 (StyleDisplay()->HasTransform(this) || HasAnimationOfTransform());
1751 }
1752
HasAnimationOfTransform() const1753 bool nsIFrame::HasAnimationOfTransform() const {
1754 return IsPrimaryFrame() &&
1755 nsLayoutUtils::HasAnimationOfTransformAndMotionPath(this) &&
1756 IsFrameOfType(eSupportsCSSTransforms);
1757 }
1758
ChildrenHavePerspective(const nsStyleDisplay * aStyleDisplay) const1759 bool nsIFrame::ChildrenHavePerspective(
1760 const nsStyleDisplay* aStyleDisplay) const {
1761 MOZ_ASSERT(aStyleDisplay == StyleDisplay());
1762 return aStyleDisplay->HasPerspective(this);
1763 }
1764
HasAnimationOfOpacity(EffectSet * aEffectSet) const1765 bool nsIFrame::HasAnimationOfOpacity(EffectSet* aEffectSet) const {
1766 return ((nsLayoutUtils::IsPrimaryStyleFrame(this) ||
1767 nsLayoutUtils::FirstContinuationOrIBSplitSibling(this)
1768 ->IsPrimaryFrame()) &&
1769 nsLayoutUtils::HasAnimationOfPropertySet(
1770 this, nsCSSPropertyIDSet::OpacityProperties(), aEffectSet));
1771 }
1772
HasOpacityInternal(float aThreshold,const nsStyleDisplay * aStyleDisplay,const nsStyleEffects * aStyleEffects,EffectSet * aEffectSet) const1773 bool nsIFrame::HasOpacityInternal(float aThreshold,
1774 const nsStyleDisplay* aStyleDisplay,
1775 const nsStyleEffects* aStyleEffects,
1776 EffectSet* aEffectSet) const {
1777 MOZ_ASSERT(0.0 <= aThreshold && aThreshold <= 1.0, "Invalid argument");
1778 if (aStyleEffects->mOpacity < aThreshold ||
1779 (aStyleDisplay->mWillChange.bits & StyleWillChangeBits::OPACITY)) {
1780 return true;
1781 }
1782
1783 if (!mMayHaveOpacityAnimation) {
1784 return false;
1785 }
1786
1787 return HasAnimationOfOpacity(aEffectSet);
1788 }
1789
IsSVGTransformed(gfx::Matrix * aOwnTransforms,gfx::Matrix * aFromParentTransforms) const1790 bool nsIFrame::IsSVGTransformed(gfx::Matrix* aOwnTransforms,
1791 gfx::Matrix* aFromParentTransforms) const {
1792 return false;
1793 }
1794
Extend3DContext(const nsStyleDisplay * aStyleDisplay,const nsStyleEffects * aStyleEffects,mozilla::EffectSet * aEffectSetForOpacity) const1795 bool nsIFrame::Extend3DContext(const nsStyleDisplay* aStyleDisplay,
1796 const nsStyleEffects* aStyleEffects,
1797 mozilla::EffectSet* aEffectSetForOpacity) const {
1798 if (!(mState & NS_FRAME_MAY_BE_TRANSFORMED)) {
1799 return false;
1800 }
1801 const nsStyleDisplay* disp = StyleDisplayWithOptionalParam(aStyleDisplay);
1802 if (disp->mTransformStyle != StyleTransformStyle::Preserve3d ||
1803 !IsFrameOfType(nsIFrame::eSupportsCSSTransforms)) {
1804 return false;
1805 }
1806
1807 // If we're all scroll frame, then all descendants will be clipped, so we
1808 // can't preserve 3d.
1809 if (IsScrollFrame()) {
1810 return false;
1811 }
1812
1813 const nsStyleEffects* effects = StyleEffectsWithOptionalParam(aStyleEffects);
1814 if (HasOpacity(disp, effects, aEffectSetForOpacity)) {
1815 return false;
1816 }
1817
1818 return ShouldApplyOverflowClipping(disp) == PhysicalAxes::None &&
1819 !GetClipPropClipRect(disp, effects, GetSize()) &&
1820 !SVGIntegrationUtils::UsingEffectsForFrame(this) &&
1821 !effects->HasMixBlendMode() &&
1822 disp->mIsolation != StyleIsolation::Isolate;
1823 }
1824
Combines3DTransformWithAncestors() const1825 bool nsIFrame::Combines3DTransformWithAncestors() const {
1826 nsIFrame* parent = GetClosestFlattenedTreeAncestorPrimaryFrame();
1827 if (!parent || !parent->Extend3DContext()) {
1828 return false;
1829 }
1830 return IsCSSTransformed() || BackfaceIsHidden();
1831 }
1832
In3DContextAndBackfaceIsHidden() const1833 bool nsIFrame::In3DContextAndBackfaceIsHidden() const {
1834 // While both tests fail most of the time, test BackfaceIsHidden()
1835 // first since it's likely to fail faster.
1836 return BackfaceIsHidden() && Combines3DTransformWithAncestors();
1837 }
1838
HasPerspective() const1839 bool nsIFrame::HasPerspective() const {
1840 if (!IsCSSTransformed()) {
1841 return false;
1842 }
1843 nsIFrame* parent = GetClosestFlattenedTreeAncestorPrimaryFrame();
1844 if (!parent) {
1845 return false;
1846 }
1847 return parent->ChildrenHavePerspective();
1848 }
1849
GetContentRectRelativeToSelf() const1850 nsRect nsIFrame::GetContentRectRelativeToSelf() const {
1851 nsMargin bp = GetUsedBorderAndPadding().ApplySkipSides(GetSkipSides());
1852 nsRect r(0, 0, mRect.width, mRect.height);
1853 r.Deflate(bp);
1854 return r;
1855 }
1856
GetContentRect() const1857 nsRect nsIFrame::GetContentRect() const {
1858 return GetContentRectRelativeToSelf() + GetPosition();
1859 }
1860
ComputeBorderRadii(const BorderRadius & aBorderRadius,const nsSize & aFrameSize,const nsSize & aBorderArea,Sides aSkipSides,nscoord aRadii[8])1861 bool nsIFrame::ComputeBorderRadii(const BorderRadius& aBorderRadius,
1862 const nsSize& aFrameSize,
1863 const nsSize& aBorderArea, Sides aSkipSides,
1864 nscoord aRadii[8]) {
1865 // Percentages are relative to whichever side they're on.
1866 for (const auto i : mozilla::AllPhysicalHalfCorners()) {
1867 const LengthPercentage& c = aBorderRadius.Get(i);
1868 nscoord axis = HalfCornerIsX(i) ? aFrameSize.width : aFrameSize.height;
1869 aRadii[i] = std::max(0, c.Resolve(axis));
1870 }
1871
1872 if (aSkipSides.Top()) {
1873 aRadii[eCornerTopLeftX] = 0;
1874 aRadii[eCornerTopLeftY] = 0;
1875 aRadii[eCornerTopRightX] = 0;
1876 aRadii[eCornerTopRightY] = 0;
1877 }
1878
1879 if (aSkipSides.Right()) {
1880 aRadii[eCornerTopRightX] = 0;
1881 aRadii[eCornerTopRightY] = 0;
1882 aRadii[eCornerBottomRightX] = 0;
1883 aRadii[eCornerBottomRightY] = 0;
1884 }
1885
1886 if (aSkipSides.Bottom()) {
1887 aRadii[eCornerBottomRightX] = 0;
1888 aRadii[eCornerBottomRightY] = 0;
1889 aRadii[eCornerBottomLeftX] = 0;
1890 aRadii[eCornerBottomLeftY] = 0;
1891 }
1892
1893 if (aSkipSides.Left()) {
1894 aRadii[eCornerBottomLeftX] = 0;
1895 aRadii[eCornerBottomLeftY] = 0;
1896 aRadii[eCornerTopLeftX] = 0;
1897 aRadii[eCornerTopLeftY] = 0;
1898 }
1899
1900 // css3-background specifies this algorithm for reducing
1901 // corner radii when they are too big.
1902 bool haveRadius = false;
1903 double ratio = 1.0f;
1904 for (const auto side : mozilla::AllPhysicalSides()) {
1905 uint32_t hc1 = SideToHalfCorner(side, false, true);
1906 uint32_t hc2 = SideToHalfCorner(side, true, true);
1907 nscoord length =
1908 SideIsVertical(side) ? aBorderArea.height : aBorderArea.width;
1909 nscoord sum = aRadii[hc1] + aRadii[hc2];
1910 if (sum) {
1911 haveRadius = true;
1912 // avoid floating point division in the normal case
1913 if (length < sum) {
1914 ratio = std::min(ratio, double(length) / sum);
1915 }
1916 }
1917 }
1918 if (ratio < 1.0) {
1919 for (const auto corner : mozilla::AllPhysicalHalfCorners()) {
1920 aRadii[corner] *= ratio;
1921 }
1922 }
1923
1924 return haveRadius;
1925 }
1926
1927 /* static */
InsetBorderRadii(nscoord aRadii[8],const nsMargin & aOffsets)1928 void nsIFrame::InsetBorderRadii(nscoord aRadii[8], const nsMargin& aOffsets) {
1929 for (const auto side : mozilla::AllPhysicalSides()) {
1930 nscoord offset = aOffsets.Side(side);
1931 uint32_t hc1 = SideToHalfCorner(side, false, false);
1932 uint32_t hc2 = SideToHalfCorner(side, true, false);
1933 aRadii[hc1] = std::max(0, aRadii[hc1] - offset);
1934 aRadii[hc2] = std::max(0, aRadii[hc2] - offset);
1935 }
1936 }
1937
1938 /* static */
OutsetBorderRadii(nscoord aRadii[8],const nsMargin & aOffsets)1939 void nsIFrame::OutsetBorderRadii(nscoord aRadii[8], const nsMargin& aOffsets) {
1940 auto AdjustOffset = [](const uint32_t aRadius, const nscoord aOffset) {
1941 // Implement the cubic formula to adjust offset when aOffset > 0 and
1942 // aRadius / aOffset < 1.
1943 // https://drafts.csswg.org/css-shapes/#valdef-shape-box-margin-box
1944 if (aOffset > 0) {
1945 const double ratio = aRadius / double(aOffset);
1946 if (ratio < 1.0) {
1947 return nscoord(aOffset * (1.0 + std::pow(ratio - 1, 3)));
1948 }
1949 }
1950 return aOffset;
1951 };
1952
1953 for (const auto side : mozilla::AllPhysicalSides()) {
1954 const nscoord offset = aOffsets.Side(side);
1955 const uint32_t hc1 = SideToHalfCorner(side, false, false);
1956 const uint32_t hc2 = SideToHalfCorner(side, true, false);
1957 if (aRadii[hc1] > 0) {
1958 const nscoord offset1 = AdjustOffset(aRadii[hc1], offset);
1959 aRadii[hc1] = std::max(0, aRadii[hc1] + offset1);
1960 }
1961 if (aRadii[hc2] > 0) {
1962 const nscoord offset2 = AdjustOffset(aRadii[hc2], offset);
1963 aRadii[hc2] = std::max(0, aRadii[hc2] + offset2);
1964 }
1965 }
1966 }
1967
RadiiAreDefinitelyZero(const BorderRadius & aBorderRadius)1968 static inline bool RadiiAreDefinitelyZero(const BorderRadius& aBorderRadius) {
1969 for (const auto corner : mozilla::AllPhysicalHalfCorners()) {
1970 if (!aBorderRadius.Get(corner).IsDefinitelyZero()) {
1971 return false;
1972 }
1973 }
1974 return true;
1975 }
1976
1977 /* virtual */
GetBorderRadii(const nsSize & aFrameSize,const nsSize & aBorderArea,Sides aSkipSides,nscoord aRadii[8]) const1978 bool nsIFrame::GetBorderRadii(const nsSize& aFrameSize,
1979 const nsSize& aBorderArea, Sides aSkipSides,
1980 nscoord aRadii[8]) const {
1981 if (!mMayHaveRoundedCorners) {
1982 memset(aRadii, 0, sizeof(nscoord) * 8);
1983 return false;
1984 }
1985
1986 if (IsThemed()) {
1987 // When we're themed, the native theme code draws the border and
1988 // background, and therefore it doesn't make sense to tell other
1989 // code that's interested in border-radius that we have any radii.
1990 //
1991 // In an ideal world, we might have a way for the them to tell us an
1992 // border radius, but since we don't, we're better off assuming
1993 // zero.
1994 for (const auto corner : mozilla::AllPhysicalHalfCorners()) {
1995 aRadii[corner] = 0;
1996 }
1997 return false;
1998 }
1999
2000 const auto& radii = StyleBorder()->mBorderRadius;
2001 const bool hasRadii =
2002 ComputeBorderRadii(radii, aFrameSize, aBorderArea, aSkipSides, aRadii);
2003 if (!hasRadii) {
2004 // TODO(emilio): Maybe we can just remove this bit and do the
2005 // IsDefinitelyZero check unconditionally. That should still avoid most of
2006 // the work, though maybe not the cache miss of going through the style and
2007 // the border struct.
2008 const_cast<nsIFrame*>(this)->mMayHaveRoundedCorners =
2009 !RadiiAreDefinitelyZero(radii);
2010 }
2011 return hasRadii;
2012 }
2013
GetBorderRadii(nscoord aRadii[8]) const2014 bool nsIFrame::GetBorderRadii(nscoord aRadii[8]) const {
2015 nsSize sz = GetSize();
2016 return GetBorderRadii(sz, sz, GetSkipSides(), aRadii);
2017 }
2018
GetMarginBoxBorderRadii(nscoord aRadii[8]) const2019 bool nsIFrame::GetMarginBoxBorderRadii(nscoord aRadii[8]) const {
2020 return GetBoxBorderRadii(aRadii, GetUsedMargin(), true);
2021 }
2022
GetPaddingBoxBorderRadii(nscoord aRadii[8]) const2023 bool nsIFrame::GetPaddingBoxBorderRadii(nscoord aRadii[8]) const {
2024 return GetBoxBorderRadii(aRadii, GetUsedBorder(), false);
2025 }
2026
GetContentBoxBorderRadii(nscoord aRadii[8]) const2027 bool nsIFrame::GetContentBoxBorderRadii(nscoord aRadii[8]) const {
2028 return GetBoxBorderRadii(aRadii, GetUsedBorderAndPadding(), false);
2029 }
2030
GetBoxBorderRadii(nscoord aRadii[8],nsMargin aOffset,bool aIsOutset) const2031 bool nsIFrame::GetBoxBorderRadii(nscoord aRadii[8], nsMargin aOffset,
2032 bool aIsOutset) const {
2033 if (!GetBorderRadii(aRadii)) return false;
2034 if (aIsOutset) {
2035 OutsetBorderRadii(aRadii, aOffset);
2036 } else {
2037 InsetBorderRadii(aRadii, aOffset);
2038 }
2039 for (const auto corner : mozilla::AllPhysicalHalfCorners()) {
2040 if (aRadii[corner]) return true;
2041 }
2042 return false;
2043 }
2044
GetShapeBoxBorderRadii(nscoord aRadii[8]) const2045 bool nsIFrame::GetShapeBoxBorderRadii(nscoord aRadii[8]) const {
2046 using Tag = StyleShapeOutside::Tag;
2047 auto& shapeOutside = StyleDisplay()->mShapeOutside;
2048 auto box = StyleShapeBox::MarginBox;
2049 switch (shapeOutside.tag) {
2050 case Tag::Image:
2051 case Tag::None:
2052 return false;
2053 case Tag::Box:
2054 box = shapeOutside.AsBox();
2055 break;
2056 case Tag::Shape:
2057 box = shapeOutside.AsShape()._1;
2058 break;
2059 }
2060
2061 switch (box) {
2062 case StyleShapeBox::ContentBox:
2063 return GetContentBoxBorderRadii(aRadii);
2064 case StyleShapeBox::PaddingBox:
2065 return GetPaddingBoxBorderRadii(aRadii);
2066 case StyleShapeBox::BorderBox:
2067 return GetBorderRadii(aRadii);
2068 case StyleShapeBox::MarginBox:
2069 return GetMarginBoxBorderRadii(aRadii);
2070 default:
2071 MOZ_ASSERT_UNREACHABLE("Unexpected box value");
2072 return false;
2073 }
2074 }
2075
GetAdditionalComputedStyle(int32_t aIndex) const2076 ComputedStyle* nsIFrame::GetAdditionalComputedStyle(int32_t aIndex) const {
2077 MOZ_ASSERT(aIndex >= 0, "invalid index number");
2078 return nullptr;
2079 }
2080
SetAdditionalComputedStyle(int32_t aIndex,ComputedStyle * aComputedStyle)2081 void nsIFrame::SetAdditionalComputedStyle(int32_t aIndex,
2082 ComputedStyle* aComputedStyle) {
2083 MOZ_ASSERT(aIndex >= 0, "invalid index number");
2084 }
2085
GetLogicalBaseline(WritingMode aWritingMode) const2086 nscoord nsIFrame::GetLogicalBaseline(WritingMode aWritingMode) const {
2087 NS_ASSERTION(!IsSubtreeDirty(), "frame must not be dirty");
2088 // Baseline for inverted line content is the top (block-start) margin edge,
2089 // as the frame is in effect "flipped" for alignment purposes.
2090 if (aWritingMode.IsLineInverted()) {
2091 return -GetLogicalUsedMargin(aWritingMode).BStart(aWritingMode);
2092 }
2093 // Otherwise, the bottom margin edge, per CSS2.1's definition of the
2094 // 'baseline' value of 'vertical-align'.
2095 return BSize(aWritingMode) +
2096 GetLogicalUsedMargin(aWritingMode).BEnd(aWritingMode);
2097 }
2098
GetChildList(ChildListID aListID) const2099 const nsFrameList& nsIFrame::GetChildList(ChildListID aListID) const {
2100 if (IsAbsoluteContainer() && aListID == GetAbsoluteListID()) {
2101 return GetAbsoluteContainingBlock()->GetChildList();
2102 } else {
2103 return nsFrameList::EmptyList();
2104 }
2105 }
2106
GetChildLists(nsTArray<ChildList> * aLists) const2107 void nsIFrame::GetChildLists(nsTArray<ChildList>* aLists) const {
2108 if (IsAbsoluteContainer()) {
2109 nsFrameList absoluteList = GetAbsoluteContainingBlock()->GetChildList();
2110 absoluteList.AppendIfNonempty(aLists, GetAbsoluteListID());
2111 }
2112 }
2113
CrossDocChildLists()2114 AutoTArray<nsIFrame::ChildList, 4> nsIFrame::CrossDocChildLists() {
2115 AutoTArray<ChildList, 4> childLists;
2116 nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(this);
2117 if (subdocumentFrame) {
2118 // Descend into the subdocument
2119 nsIFrame* root = subdocumentFrame->GetSubdocumentRootFrame();
2120 if (root) {
2121 childLists.EmplaceBack(
2122 nsFrameList(root, nsLayoutUtils::GetLastSibling(root)),
2123 nsIFrame::kPrincipalList);
2124 }
2125 }
2126
2127 GetChildLists(&childLists);
2128 return childLists;
2129 }
2130
GetVisibility() const2131 Visibility nsIFrame::GetVisibility() const {
2132 if (!HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED)) {
2133 return Visibility::Untracked;
2134 }
2135
2136 bool isSet = false;
2137 uint32_t visibleCount = GetProperty(VisibilityStateProperty(), &isSet);
2138
2139 MOZ_ASSERT(isSet,
2140 "Should have a VisibilityStateProperty value "
2141 "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
2142
2143 return visibleCount > 0 ? Visibility::ApproximatelyVisible
2144 : Visibility::ApproximatelyNonVisible;
2145 }
2146
UpdateVisibilitySynchronously()2147 void nsIFrame::UpdateVisibilitySynchronously() {
2148 mozilla::PresShell* presShell = PresShell();
2149 if (!presShell) {
2150 return;
2151 }
2152
2153 if (presShell->AssumeAllFramesVisible()) {
2154 presShell->EnsureFrameInApproximatelyVisibleList(this);
2155 return;
2156 }
2157
2158 bool visible = StyleVisibility()->IsVisible();
2159 nsIFrame* f = GetParent();
2160 nsRect rect = GetRectRelativeToSelf();
2161 nsIFrame* rectFrame = this;
2162 while (f && visible) {
2163 nsIScrollableFrame* sf = do_QueryFrame(f);
2164 if (sf) {
2165 nsRect transformedRect =
2166 nsLayoutUtils::TransformFrameRectToAncestor(rectFrame, rect, f);
2167 if (!sf->IsRectNearlyVisible(transformedRect)) {
2168 visible = false;
2169 break;
2170 }
2171
2172 // In this code we're trying to synchronously update *approximate*
2173 // visibility. (In the future we may update precise visibility here as
2174 // well, which is why the method name does not contain 'approximate'.) The
2175 // IsRectNearlyVisible() check above tells us that the rect we're checking
2176 // is approximately visible within the scrollframe, but we still need to
2177 // ensure that, even if it was scrolled into view, it'd be visible when we
2178 // consider the rest of the document. To do that, we move transformedRect
2179 // to be contained in the scrollport as best we can (it might not fit) to
2180 // pretend that it was scrolled into view.
2181 rect = transformedRect.MoveInsideAndClamp(sf->GetScrollPortRect());
2182 rectFrame = f;
2183 }
2184 nsIFrame* parent = f->GetParent();
2185 if (!parent) {
2186 parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(f);
2187 if (parent && parent->PresContext()->IsChrome()) {
2188 break;
2189 }
2190 }
2191 f = parent;
2192 }
2193
2194 if (visible) {
2195 presShell->EnsureFrameInApproximatelyVisibleList(this);
2196 } else {
2197 presShell->RemoveFrameFromApproximatelyVisibleList(this);
2198 }
2199 }
2200
EnableVisibilityTracking()2201 void nsIFrame::EnableVisibilityTracking() {
2202 if (HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED)) {
2203 return; // Nothing to do.
2204 }
2205
2206 MOZ_ASSERT(!HasProperty(VisibilityStateProperty()),
2207 "Shouldn't have a VisibilityStateProperty value "
2208 "if NS_FRAME_VISIBILITY_IS_TRACKED is not set");
2209
2210 // Add the state bit so we know to track visibility for this frame, and
2211 // initialize the frame property.
2212 AddStateBits(NS_FRAME_VISIBILITY_IS_TRACKED);
2213 SetProperty(VisibilityStateProperty(), 0);
2214
2215 mozilla::PresShell* presShell = PresShell();
2216 if (!presShell) {
2217 return;
2218 }
2219
2220 // Schedule a visibility update. This method will virtually always be called
2221 // when layout has changed anyway, so it's very unlikely that any additional
2222 // visibility updates will be triggered by this, but this way we guarantee
2223 // that if this frame is currently visible we'll eventually find out.
2224 presShell->ScheduleApproximateFrameVisibilityUpdateSoon();
2225 }
2226
DisableVisibilityTracking()2227 void nsIFrame::DisableVisibilityTracking() {
2228 if (!HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED)) {
2229 return; // Nothing to do.
2230 }
2231
2232 bool isSet = false;
2233 uint32_t visibleCount = TakeProperty(VisibilityStateProperty(), &isSet);
2234
2235 MOZ_ASSERT(isSet,
2236 "Should have a VisibilityStateProperty value "
2237 "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
2238
2239 RemoveStateBits(NS_FRAME_VISIBILITY_IS_TRACKED);
2240
2241 if (visibleCount == 0) {
2242 return; // We were nonvisible.
2243 }
2244
2245 // We were visible, so send an OnVisibilityChange() notification.
2246 OnVisibilityChange(Visibility::ApproximatelyNonVisible);
2247 }
2248
DecApproximateVisibleCount(const Maybe<OnNonvisible> & aNonvisibleAction)2249 void nsIFrame::DecApproximateVisibleCount(
2250 const Maybe<OnNonvisible>& aNonvisibleAction
2251 /* = Nothing() */) {
2252 MOZ_ASSERT(HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED));
2253
2254 bool isSet = false;
2255 uint32_t visibleCount = GetProperty(VisibilityStateProperty(), &isSet);
2256
2257 MOZ_ASSERT(isSet,
2258 "Should have a VisibilityStateProperty value "
2259 "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
2260 MOZ_ASSERT(visibleCount > 0,
2261 "Frame is already nonvisible and we're "
2262 "decrementing its visible count?");
2263
2264 visibleCount--;
2265 SetProperty(VisibilityStateProperty(), visibleCount);
2266 if (visibleCount > 0) {
2267 return;
2268 }
2269
2270 // We just became nonvisible, so send an OnVisibilityChange() notification.
2271 OnVisibilityChange(Visibility::ApproximatelyNonVisible, aNonvisibleAction);
2272 }
2273
IncApproximateVisibleCount()2274 void nsIFrame::IncApproximateVisibleCount() {
2275 MOZ_ASSERT(HasAnyStateBits(NS_FRAME_VISIBILITY_IS_TRACKED));
2276
2277 bool isSet = false;
2278 uint32_t visibleCount = GetProperty(VisibilityStateProperty(), &isSet);
2279
2280 MOZ_ASSERT(isSet,
2281 "Should have a VisibilityStateProperty value "
2282 "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
2283
2284 visibleCount++;
2285 SetProperty(VisibilityStateProperty(), visibleCount);
2286 if (visibleCount > 1) {
2287 return;
2288 }
2289
2290 // We just became visible, so send an OnVisibilityChange() notification.
2291 OnVisibilityChange(Visibility::ApproximatelyVisible);
2292 }
2293
OnVisibilityChange(Visibility aNewVisibility,const Maybe<OnNonvisible> & aNonvisibleAction)2294 void nsIFrame::OnVisibilityChange(Visibility aNewVisibility,
2295 const Maybe<OnNonvisible>& aNonvisibleAction
2296 /* = Nothing() */) {
2297 // XXX(seth): In bug 1218990 we'll implement visibility tracking for CSS
2298 // images here.
2299 }
2300
GetActiveSelectionFrame(nsPresContext * aPresContext,nsIFrame * aFrame)2301 static nsIFrame* GetActiveSelectionFrame(nsPresContext* aPresContext,
2302 nsIFrame* aFrame) {
2303 nsIContent* capturingContent = PresShell::GetCapturingContent();
2304 if (capturingContent) {
2305 nsIFrame* activeFrame = aPresContext->GetPrimaryFrameFor(capturingContent);
2306 return activeFrame ? activeFrame : aFrame;
2307 }
2308
2309 return aFrame;
2310 }
2311
DetermineDisplaySelection()2312 int16_t nsIFrame::DetermineDisplaySelection() {
2313 int16_t selType = nsISelectionController::SELECTION_OFF;
2314
2315 nsCOMPtr<nsISelectionController> selCon;
2316 nsresult result =
2317 GetSelectionController(PresContext(), getter_AddRefs(selCon));
2318 if (NS_SUCCEEDED(result) && selCon) {
2319 result = selCon->GetDisplaySelection(&selType);
2320 if (NS_SUCCEEDED(result) &&
2321 (selType != nsISelectionController::SELECTION_OFF)) {
2322 // Check whether style allows selection.
2323 if (!IsSelectable(nullptr)) {
2324 selType = nsISelectionController::SELECTION_OFF;
2325 }
2326 }
2327 }
2328 return selType;
2329 }
2330
FindElementAncestorForMozSelection(nsIContent * aContent)2331 static Element* FindElementAncestorForMozSelection(nsIContent* aContent) {
2332 NS_ENSURE_TRUE(aContent, nullptr);
2333 while (aContent && aContent->IsInNativeAnonymousSubtree()) {
2334 aContent = aContent->GetClosestNativeAnonymousSubtreeRootParent();
2335 }
2336 NS_ASSERTION(aContent, "aContent isn't in non-anonymous tree?");
2337 return aContent ? aContent->GetAsElementOrParentElement() : nullptr;
2338 }
2339
ComputeSelectionStyle(int16_t aSelectionStatus) const2340 already_AddRefed<ComputedStyle> nsIFrame::ComputeSelectionStyle(
2341 int16_t aSelectionStatus) const {
2342 // Just bail out if not a selection-status that ::selection applies to.
2343 if (aSelectionStatus != nsISelectionController::SELECTION_ON &&
2344 aSelectionStatus != nsISelectionController::SELECTION_DISABLED) {
2345 return nullptr;
2346 }
2347 // When in high-contrast mode, the style system ends up ignoring the color
2348 // declarations, which means that the ::selection style becomes the inherited
2349 // color, and default background. That's no good.
2350 if (PresContext()->ForcingColors()) {
2351 return nullptr;
2352 }
2353 Element* element = FindElementAncestorForMozSelection(GetContent());
2354 if (!element) {
2355 return nullptr;
2356 }
2357 return PresContext()->StyleSet()->ProbePseudoElementStyle(
2358 *element, PseudoStyleType::selection, Style());
2359 }
2360
2361 template <typename SizeOrMaxSize>
IsIntrinsicKeyword(const SizeOrMaxSize & aSize)2362 static inline bool IsIntrinsicKeyword(const SizeOrMaxSize& aSize) {
2363 // All keywords other than auto/none/-moz-available depend on intrinsic sizes.
2364 return aSize.IsMaxContent() || aSize.IsMinContent() || aSize.IsFitContent() ||
2365 aSize.IsFitContentFunction();
2366 }
2367
CanBeDynamicReflowRoot() const2368 bool nsIFrame::CanBeDynamicReflowRoot() const {
2369 if (!StaticPrefs::layout_dynamic_reflow_roots_enabled()) {
2370 return false;
2371 }
2372
2373 auto& display = *StyleDisplay();
2374 if (IsFrameOfType(nsIFrame::eLineParticipant) ||
2375 nsStyleDisplay::IsRubyDisplayType(display.mDisplay) ||
2376 display.DisplayOutside() == StyleDisplayOutside::InternalTable ||
2377 display.DisplayInside() == StyleDisplayInside::Table ||
2378 (GetParent() && GetParent()->IsXULBoxFrame())) {
2379 // We have a display type where 'width' and 'height' don't actually set the
2380 // width or height (i.e., the size depends on content).
2381 MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT),
2382 "should not have dynamic reflow root bit");
2383 return false;
2384 }
2385
2386 // We can't serve as a dynamic reflow root if our used 'width' and 'height'
2387 // might be influenced by content.
2388 //
2389 // FIXME: For display:block, we should probably optimize inline-size: auto.
2390 // FIXME: Other flex and grid cases?
2391 auto& pos = *StylePosition();
2392 const auto& width = pos.mWidth;
2393 const auto& height = pos.mHeight;
2394 if (!width.IsLengthPercentage() || width.HasPercent() ||
2395 !height.IsLengthPercentage() || height.HasPercent() ||
2396 IsIntrinsicKeyword(pos.mMinWidth) || IsIntrinsicKeyword(pos.mMaxWidth) ||
2397 IsIntrinsicKeyword(pos.mMinHeight) ||
2398 IsIntrinsicKeyword(pos.mMaxHeight) ||
2399 ((pos.mMinWidth.IsAuto() || pos.mMinHeight.IsAuto()) &&
2400 IsFlexOrGridItem())) {
2401 return false;
2402 }
2403
2404 // If our flex-basis is 'auto', it'll defer to 'width' (or 'height') which
2405 // we've already checked. Otherwise, it preempts them, so we need to
2406 // perform the same "could-this-value-be-influenced-by-content" checks that
2407 // we performed for 'width' and 'height' above.
2408 if (IsFlexItem()) {
2409 const auto& flexBasis = pos.mFlexBasis;
2410 if (!flexBasis.IsAuto()) {
2411 if (!flexBasis.IsSize() || !flexBasis.AsSize().IsLengthPercentage() ||
2412 flexBasis.AsSize().HasPercent()) {
2413 return false;
2414 }
2415 }
2416 }
2417
2418 if (!IsFixedPosContainingBlock()) {
2419 // We can't treat this frame as a reflow root, since dynamic changes
2420 // to absolutely-positioned frames inside of it require that we
2421 // reflow the placeholder before we reflow the absolutely positioned
2422 // frame.
2423 // FIXME: Alternatively, we could sort the reflow roots in
2424 // PresShell::ProcessReflowCommands by depth in the tree, from
2425 // deepest to least deep. However, for performance (FIXME) we
2426 // should really be sorting them in the opposite order!
2427 return false;
2428 }
2429
2430 // If we participate in a container's block reflow context, or margins
2431 // can collapse through us, we can't be a dynamic reflow root.
2432 if (IsBlockFrameOrSubclass() &&
2433 !HasAllStateBits(NS_BLOCK_FLOAT_MGR | NS_BLOCK_MARGIN_ROOT)) {
2434 return false;
2435 }
2436
2437 // Subgrids are never reflow roots, but 'contain:layout/paint' prevents
2438 // creating a subgrid in the first place.
2439 if (pos.mGridTemplateColumns.IsSubgrid() ||
2440 pos.mGridTemplateRows.IsSubgrid()) {
2441 // NOTE: we could check that 'display' of our parent's primary frame is
2442 // '[inline-]grid' here but that's probably not worth it in practice.
2443 if (!display.IsContainLayout() && !display.IsContainPaint()) {
2444 return false;
2445 }
2446 }
2447
2448 // If we are split, we can't be a dynamic reflow root. Our reflow status may
2449 // change after reflow, and our parent is responsible to create or delete our
2450 // next-in-flow.
2451 if (GetPrevContinuation() || GetNextContinuation()) {
2452 return false;
2453 }
2454
2455 return true;
2456 }
2457
2458 /********************************************************
2459 * Refreshes each content's frame
2460 *********************************************************/
2461
DisplayOutlineUnconditional(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)2462 void nsIFrame::DisplayOutlineUnconditional(nsDisplayListBuilder* aBuilder,
2463 const nsDisplayListSet& aLists) {
2464 // Per https://drafts.csswg.org/css-tables-3/#global-style-overrides:
2465 // "All css properties of table-column and table-column-group boxes are
2466 // ignored, except when explicitly specified by this specification."
2467 // CSS outlines fall into this category, so we skip them on these boxes.
2468 MOZ_ASSERT(!IsTableColGroupFrame() && !IsTableColFrame());
2469 const auto& outline = *StyleOutline();
2470
2471 if (!outline.ShouldPaintOutline()) {
2472 return;
2473 }
2474
2475 // Outlines are painted by the table wrapper frame.
2476 if (IsTableFrame()) {
2477 return;
2478 }
2479
2480 if (HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT) &&
2481 ScrollableOverflowRect().IsEmpty()) {
2482 // Skip parts of IB-splits with an empty overflow rect, see bug 434301.
2483 // We may still want to fix some of the overflow area calculations over in
2484 // that bug.
2485 return;
2486 }
2487
2488 // We don't display outline-style: auto on themed frames that have their own
2489 // focus indicators.
2490 if (outline.mOutlineStyle.IsAuto()) {
2491 auto* disp = StyleDisplay();
2492 if (IsThemed(disp) && PresContext()->Theme()->ThemeDrawsFocusForWidget(
2493 this, disp->EffectiveAppearance())) {
2494 return;
2495 }
2496 }
2497
2498 aLists.Outlines()->AppendNewToTop<nsDisplayOutline>(aBuilder, this);
2499 }
2500
DisplayOutline(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)2501 void nsIFrame::DisplayOutline(nsDisplayListBuilder* aBuilder,
2502 const nsDisplayListSet& aLists) {
2503 if (!IsVisibleForPainting()) return;
2504
2505 DisplayOutlineUnconditional(aBuilder, aLists);
2506 }
2507
DisplayInsetBoxShadowUnconditional(nsDisplayListBuilder * aBuilder,nsDisplayList * aList)2508 void nsIFrame::DisplayInsetBoxShadowUnconditional(
2509 nsDisplayListBuilder* aBuilder, nsDisplayList* aList) {
2510 // XXXbz should box-shadow for rows/rowgroups/columns/colgroups get painted
2511 // just because we're visible? Or should it depend on the cell visibility
2512 // when we're not the whole table?
2513 const auto* effects = StyleEffects();
2514 if (effects->HasBoxShadowWithInset(true)) {
2515 aList->AppendNewToTop<nsDisplayBoxShadowInner>(aBuilder, this);
2516 }
2517 }
2518
DisplayInsetBoxShadow(nsDisplayListBuilder * aBuilder,nsDisplayList * aList)2519 void nsIFrame::DisplayInsetBoxShadow(nsDisplayListBuilder* aBuilder,
2520 nsDisplayList* aList) {
2521 if (!IsVisibleForPainting()) return;
2522
2523 DisplayInsetBoxShadowUnconditional(aBuilder, aList);
2524 }
2525
DisplayOutsetBoxShadowUnconditional(nsDisplayListBuilder * aBuilder,nsDisplayList * aList)2526 void nsIFrame::DisplayOutsetBoxShadowUnconditional(
2527 nsDisplayListBuilder* aBuilder, nsDisplayList* aList) {
2528 // XXXbz should box-shadow for rows/rowgroups/columns/colgroups get painted
2529 // just because we're visible? Or should it depend on the cell visibility
2530 // when we're not the whole table?
2531 const auto* effects = StyleEffects();
2532 if (effects->HasBoxShadowWithInset(false)) {
2533 aList->AppendNewToTop<nsDisplayBoxShadowOuter>(aBuilder, this);
2534 }
2535 }
2536
DisplayOutsetBoxShadow(nsDisplayListBuilder * aBuilder,nsDisplayList * aList)2537 void nsIFrame::DisplayOutsetBoxShadow(nsDisplayListBuilder* aBuilder,
2538 nsDisplayList* aList) {
2539 if (!IsVisibleForPainting()) return;
2540
2541 DisplayOutsetBoxShadowUnconditional(aBuilder, aList);
2542 }
2543
DisplayCaret(nsDisplayListBuilder * aBuilder,nsDisplayList * aList)2544 void nsIFrame::DisplayCaret(nsDisplayListBuilder* aBuilder,
2545 nsDisplayList* aList) {
2546 if (!IsVisibleForPainting()) return;
2547
2548 aList->AppendNewToTop<nsDisplayCaret>(aBuilder, this);
2549 }
2550
GetCaretColorAt(int32_t aOffset)2551 nscolor nsIFrame::GetCaretColorAt(int32_t aOffset) {
2552 return nsLayoutUtils::GetColor(this, &nsStyleUI::mCaretColor);
2553 }
2554
ComputeShouldPaintBackground() const2555 auto nsIFrame::ComputeShouldPaintBackground() const -> ShouldPaintBackground {
2556 nsPresContext* pc = PresContext();
2557 ShouldPaintBackground settings{pc->GetBackgroundColorDraw(),
2558 pc->GetBackgroundImageDraw()};
2559 if (settings.mColor && settings.mImage) {
2560 return settings;
2561 }
2562
2563 if (!HonorPrintBackgroundSettings() ||
2564 StyleVisibility()->mPrintColorAdjust == StylePrintColorAdjust::Exact) {
2565 return {true, true};
2566 }
2567
2568 return settings;
2569 }
2570
DisplayBackgroundUnconditional(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists,bool aForceBackground)2571 bool nsIFrame::DisplayBackgroundUnconditional(nsDisplayListBuilder* aBuilder,
2572 const nsDisplayListSet& aLists,
2573 bool aForceBackground) {
2574 const bool hitTesting = aBuilder->IsForEventDelivery();
2575 if (hitTesting && !aBuilder->HitTestIsForVisibility()) {
2576 // For hit-testing, we generally just need a light-weight data structure
2577 // like nsDisplayEventReceiver. But if the hit-testing is for visibility,
2578 // then we need to know the opaque region in order to determine whether to
2579 // stop or not.
2580 aLists.BorderBackground()->AppendNewToTop<nsDisplayEventReceiver>(aBuilder,
2581 this);
2582 return false;
2583 }
2584
2585 AppendedBackgroundType result = AppendedBackgroundType::None;
2586
2587 // Here we don't try to detect background propagation. Frames that might
2588 // receive a propagated background should just set aForceBackground to
2589 // true.
2590 if (hitTesting || aForceBackground ||
2591 !StyleBackground()->IsTransparent(this) ||
2592 StyleDisplay()->HasAppearance() ||
2593 // We do forcibly create a display item for background color animations
2594 // even if the current background-color is transparent so that we can
2595 // run the animations on the compositor.
2596 EffectCompositor::HasAnimationsForCompositor(
2597 this, DisplayItemType::TYPE_BACKGROUND_COLOR)) {
2598 result = nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
2599 aBuilder, this,
2600 GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this),
2601 aLists.BorderBackground());
2602 }
2603
2604 if (result == AppendedBackgroundType::None) {
2605 aBuilder->BuildCompositorHitTestInfoIfNeeded(this,
2606 aLists.BorderBackground());
2607 }
2608
2609 return result == AppendedBackgroundType::ThemedBackground;
2610 }
2611
DisplayBorderBackgroundOutline(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists,bool aForceBackground)2612 void nsIFrame::DisplayBorderBackgroundOutline(nsDisplayListBuilder* aBuilder,
2613 const nsDisplayListSet& aLists,
2614 bool aForceBackground) {
2615 // The visibility check belongs here since child elements have the
2616 // opportunity to override the visibility property and display even if
2617 // their parent is hidden.
2618 if (!IsVisibleForPainting()) {
2619 return;
2620 }
2621
2622 DisplayOutsetBoxShadowUnconditional(aBuilder, aLists.BorderBackground());
2623
2624 bool bgIsThemed =
2625 DisplayBackgroundUnconditional(aBuilder, aLists, aForceBackground);
2626
2627 DisplayInsetBoxShadowUnconditional(aBuilder, aLists.BorderBackground());
2628
2629 // If there's a themed background, we should not create a border item.
2630 // It won't be rendered.
2631 // Don't paint borders for tables here, since they paint them in a different
2632 // order.
2633 if (!bgIsThemed && StyleBorder()->HasBorder() && !IsTableFrame()) {
2634 aLists.BorderBackground()->AppendNewToTop<nsDisplayBorder>(aBuilder, this);
2635 }
2636
2637 DisplayOutlineUnconditional(aBuilder, aLists);
2638 }
2639
IsSVGContentWithCSSClip(const nsIFrame * aFrame)2640 inline static bool IsSVGContentWithCSSClip(const nsIFrame* aFrame) {
2641 // The CSS spec says that the 'clip' property only applies to absolutely
2642 // positioned elements, whereas the SVG spec says that it applies to SVG
2643 // elements regardless of the value of the 'position' property. Here we obey
2644 // the CSS spec for outer-<svg> (since that's what we generally do), but
2645 // obey the SVG spec for other SVG elements to which 'clip' applies.
2646 return aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT) &&
2647 aFrame->GetContent()->IsAnyOfSVGElements(nsGkAtoms::svg,
2648 nsGkAtoms::foreignObject);
2649 }
2650
FormsBackdropRoot(const nsStyleDisplay * aStyleDisplay,const nsStyleEffects * aStyleEffects,const nsStyleSVGReset * aStyleSVGReset)2651 bool nsIFrame::FormsBackdropRoot(const nsStyleDisplay* aStyleDisplay,
2652 const nsStyleEffects* aStyleEffects,
2653 const nsStyleSVGReset* aStyleSVGReset) {
2654 // Check if this is a root frame.
2655 if (!GetParent()) {
2656 return true;
2657 }
2658
2659 // Check for filter effects.
2660 if (aStyleEffects->HasFilters() || aStyleEffects->HasBackdropFilters() ||
2661 aStyleEffects->HasMixBlendMode()) {
2662 return true;
2663 }
2664
2665 // Check for opacity.
2666 if (HasOpacity(aStyleDisplay, aStyleEffects)) {
2667 return true;
2668 }
2669
2670 // Check for mask or clip path.
2671 if (aStyleSVGReset->HasMask() || aStyleSVGReset->HasClipPath()) {
2672 return true;
2673 }
2674
2675 // TODO(cbrewster): Check will-change attributes
2676
2677 return false;
2678 }
2679
GetClipPropClipRect(const nsStyleDisplay * aDisp,const nsStyleEffects * aEffects,const nsSize & aSize) const2680 Maybe<nsRect> nsIFrame::GetClipPropClipRect(const nsStyleDisplay* aDisp,
2681 const nsStyleEffects* aEffects,
2682 const nsSize& aSize) const {
2683 if (aEffects->mClip.IsAuto() ||
2684 !(aDisp->IsAbsolutelyPositioned(this) || IsSVGContentWithCSSClip(this))) {
2685 return Nothing();
2686 }
2687
2688 auto& clipRect = aEffects->mClip.AsRect();
2689 nsRect rect = clipRect.ToLayoutRect();
2690 if (MOZ_LIKELY(StyleBorder()->mBoxDecorationBreak ==
2691 StyleBoxDecorationBreak::Slice)) {
2692 // The clip applies to the joined boxes so it's relative the first
2693 // continuation.
2694 nscoord y = 0;
2695 for (nsIFrame* f = GetPrevContinuation(); f; f = f->GetPrevContinuation()) {
2696 y += f->GetRect().height;
2697 }
2698 rect.MoveBy(nsPoint(0, -y));
2699 }
2700
2701 if (clipRect.right.IsAuto()) {
2702 rect.width = aSize.width - rect.x;
2703 }
2704 if (clipRect.bottom.IsAuto()) {
2705 rect.height = aSize.height - rect.y;
2706 }
2707 return Some(rect);
2708 }
2709
2710 /**
2711 * If the CSS 'overflow' property applies to this frame, and is not
2712 * handled by constructing a dedicated nsHTML/XULScrollFrame, set up clipping
2713 * for that overflow in aBuilder->ClipState() to clip all containing-block
2714 * descendants.
2715 */
ApplyOverflowClipping(nsDisplayListBuilder * aBuilder,const nsIFrame * aFrame,nsIFrame::PhysicalAxes aClipAxes,DisplayListClipState::AutoClipMultiple & aClipState)2716 static void ApplyOverflowClipping(
2717 nsDisplayListBuilder* aBuilder, const nsIFrame* aFrame,
2718 nsIFrame::PhysicalAxes aClipAxes,
2719 DisplayListClipState::AutoClipMultiple& aClipState) {
2720 // Only 'clip' is handled here (and 'hidden' for table frames, and any
2721 // non-'visible' value for blocks in a paginated context).
2722 // We allow 'clip' to apply to any kind of frame. This is required by
2723 // comboboxes which make their display text (an inline frame) have clipping.
2724 MOZ_ASSERT(aClipAxes != nsIFrame::PhysicalAxes::None);
2725 MOZ_ASSERT(aFrame->ShouldApplyOverflowClipping(aFrame->StyleDisplay()) ==
2726 aClipAxes);
2727
2728 nsRect clipRect;
2729 bool haveRadii = false;
2730 nscoord radii[8];
2731 auto* disp = aFrame->StyleDisplay();
2732 // Only deflate the padding if we clip to the content-box in that axis.
2733 auto wm = aFrame->GetWritingMode();
2734 bool cbH = (wm.IsVertical() ? disp->mOverflowClipBoxBlock
2735 : disp->mOverflowClipBoxInline) ==
2736 StyleOverflowClipBox::ContentBox;
2737 bool cbV = (wm.IsVertical() ? disp->mOverflowClipBoxInline
2738 : disp->mOverflowClipBoxBlock) ==
2739 StyleOverflowClipBox::ContentBox;
2740 nsMargin bp = aFrame->GetUsedPadding();
2741 if (!cbH) {
2742 bp.left = bp.right = nscoord(0);
2743 }
2744 if (!cbV) {
2745 bp.top = bp.bottom = nscoord(0);
2746 }
2747
2748 bp += aFrame->GetUsedBorder();
2749 bp.ApplySkipSides(aFrame->GetSkipSides());
2750 nsRect rect(nsPoint(0, 0), aFrame->GetSize());
2751 rect.Deflate(bp);
2752 if (MOZ_UNLIKELY(!(aClipAxes & nsIFrame::PhysicalAxes::Horizontal))) {
2753 // NOTE(mats) We shouldn't be clipping at all in this dimension really,
2754 // but clipping in just one axis isn't supported by our GFX APIs so we
2755 // clip to our visual overflow rect instead.
2756 nsRect o = aFrame->InkOverflowRect();
2757 rect.x = o.x;
2758 rect.width = o.width;
2759 }
2760 if (MOZ_UNLIKELY(!(aClipAxes & nsIFrame::PhysicalAxes::Vertical))) {
2761 // See the note above.
2762 nsRect o = aFrame->InkOverflowRect();
2763 rect.y = o.y;
2764 rect.height = o.height;
2765 }
2766 clipRect = rect + aBuilder->ToReferenceFrame(aFrame);
2767 haveRadii = aFrame->GetBoxBorderRadii(radii, bp, false);
2768 aClipState.ClipContainingBlockDescendantsExtra(clipRect,
2769 haveRadii ? radii : nullptr);
2770 }
2771
2772 #ifdef DEBUG
PaintDebugBorder(nsIFrame * aFrame,DrawTarget * aDrawTarget,const nsRect & aDirtyRect,nsPoint aPt)2773 static void PaintDebugBorder(nsIFrame* aFrame, DrawTarget* aDrawTarget,
2774 const nsRect& aDirtyRect, nsPoint aPt) {
2775 nsRect r(aPt, aFrame->GetSize());
2776 int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
2777 sRGBColor blueOrRed(aFrame->HasView() ? sRGBColor(0.f, 0.f, 1.f, 1.f)
2778 : sRGBColor(1.f, 0.f, 0.f, 1.f));
2779 aDrawTarget->StrokeRect(NSRectToRect(r, appUnitsPerDevPixel),
2780 ColorPattern(ToDeviceColor(blueOrRed)));
2781 }
2782
PaintEventTargetBorder(nsIFrame * aFrame,DrawTarget * aDrawTarget,const nsRect & aDirtyRect,nsPoint aPt)2783 static void PaintEventTargetBorder(nsIFrame* aFrame, DrawTarget* aDrawTarget,
2784 const nsRect& aDirtyRect, nsPoint aPt) {
2785 nsRect r(aPt, aFrame->GetSize());
2786 int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
2787 ColorPattern purple(ToDeviceColor(sRGBColor(.5f, 0.f, .5f, 1.f)));
2788 aDrawTarget->StrokeRect(NSRectToRect(r, appUnitsPerDevPixel), purple);
2789 }
2790
DisplayDebugBorders(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,const nsDisplayListSet & aLists)2791 static void DisplayDebugBorders(nsDisplayListBuilder* aBuilder,
2792 nsIFrame* aFrame,
2793 const nsDisplayListSet& aLists) {
2794 // Draw a border around the child
2795 // REVIEW: From nsContainerFrame::PaintChild
2796 if (nsIFrame::GetShowFrameBorders() && !aFrame->GetRect().IsEmpty()) {
2797 aLists.Outlines()->AppendNewToTop<nsDisplayGeneric>(
2798 aBuilder, aFrame, PaintDebugBorder, "DebugBorder",
2799 DisplayItemType::TYPE_DEBUG_BORDER);
2800 }
2801 // Draw a border around the current event target
2802 if (nsIFrame::GetShowEventTargetFrameBorder() &&
2803 aFrame->PresShell()->GetDrawEventTargetFrame() == aFrame) {
2804 aLists.Outlines()->AppendNewToTop<nsDisplayGeneric>(
2805 aBuilder, aFrame, PaintEventTargetBorder, "EventTargetBorder",
2806 DisplayItemType::TYPE_EVENT_TARGET_BORDER);
2807 }
2808 }
2809 #endif
2810
2811 /**
2812 * Returns whether a display item that gets created with the builder's current
2813 * state will have a scrolled clip, i.e. a clip that is scrolled by a scroll
2814 * frame which does not move the item itself.
2815 */
BuilderHasScrolledClip(nsDisplayListBuilder * aBuilder)2816 static bool BuilderHasScrolledClip(nsDisplayListBuilder* aBuilder) {
2817 const DisplayItemClipChain* currentClip =
2818 aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder);
2819 if (!currentClip) {
2820 return false;
2821 }
2822
2823 const ActiveScrolledRoot* currentClipASR = currentClip->mASR;
2824 const ActiveScrolledRoot* currentASR = aBuilder->CurrentActiveScrolledRoot();
2825 return ActiveScrolledRoot::PickDescendant(currentClipASR, currentASR) !=
2826 currentASR;
2827 }
2828
2829 class AutoSaveRestoreContainsBlendMode {
2830 nsDisplayListBuilder& mBuilder;
2831 bool mSavedContainsBlendMode;
2832
2833 public:
AutoSaveRestoreContainsBlendMode(nsDisplayListBuilder & aBuilder)2834 explicit AutoSaveRestoreContainsBlendMode(nsDisplayListBuilder& aBuilder)
2835 : mBuilder(aBuilder),
2836 mSavedContainsBlendMode(aBuilder.ContainsBlendMode()) {}
2837
~AutoSaveRestoreContainsBlendMode()2838 ~AutoSaveRestoreContainsBlendMode() {
2839 mBuilder.SetContainsBlendMode(mSavedContainsBlendMode);
2840 }
2841 };
2842
2843 class AutoSaveRestoreContainsBackdropFilter {
2844 nsDisplayListBuilder& mBuilder;
2845 bool mSavedContainsBackdropFilter;
2846
2847 public:
AutoSaveRestoreContainsBackdropFilter(nsDisplayListBuilder & aBuilder)2848 explicit AutoSaveRestoreContainsBackdropFilter(nsDisplayListBuilder& aBuilder)
2849 : mBuilder(aBuilder),
2850 mSavedContainsBackdropFilter(aBuilder.ContainsBackdropFilter()) {}
2851
2852 /**
2853 * This is called if a stacking context which does not form a backdrop root
2854 * contains a descendent with a backdrop filter. In this case we need to
2855 * delegate backdrop root creation to the next parent in the tree until we hit
2856 * the nearest backdrop root ancestor.
2857 */
DelegateUp(bool aContainsBackdropFilter)2858 void DelegateUp(bool aContainsBackdropFilter) {
2859 mSavedContainsBackdropFilter = aContainsBackdropFilter;
2860 }
2861
~AutoSaveRestoreContainsBackdropFilter()2862 ~AutoSaveRestoreContainsBackdropFilter() {
2863 mBuilder.SetContainsBackdropFilter(mSavedContainsBackdropFilter);
2864 }
2865 };
2866
CheckForApzAwareEventHandlers(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame)2867 static void CheckForApzAwareEventHandlers(nsDisplayListBuilder* aBuilder,
2868 nsIFrame* aFrame) {
2869 if (aBuilder->GetAncestorHasApzAwareEventHandler()) {
2870 return;
2871 }
2872
2873 nsIContent* content = aFrame->GetContent();
2874 if (!content) {
2875 return;
2876 }
2877
2878 if (content->IsNodeApzAware()) {
2879 aBuilder->SetAncestorHasApzAwareEventHandler(true);
2880 }
2881 }
2882
UpdateCurrentHitTestInfo(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame)2883 static void UpdateCurrentHitTestInfo(nsDisplayListBuilder* aBuilder,
2884 nsIFrame* aFrame) {
2885 if (!aBuilder->BuildCompositorHitTestInfo()) {
2886 // Compositor hit test info is not used.
2887 return;
2888 }
2889
2890 CheckForApzAwareEventHandlers(aBuilder, aFrame);
2891
2892 const CompositorHitTestInfo info = aFrame->GetCompositorHitTestInfo(aBuilder);
2893 aBuilder->SetCompositorHitTestInfo(info);
2894 }
2895
2896 /**
2897 * True if aDescendant participates the context aAncestor participating.
2898 */
FrameParticipatesIn3DContext(nsIFrame * aAncestor,nsIFrame * aDescendant)2899 static bool FrameParticipatesIn3DContext(nsIFrame* aAncestor,
2900 nsIFrame* aDescendant) {
2901 MOZ_ASSERT(aAncestor != aDescendant);
2902 MOZ_ASSERT(aAncestor->GetContent() != aDescendant->GetContent());
2903 MOZ_ASSERT(aAncestor->Extend3DContext());
2904
2905 nsIFrame* ancestor = aAncestor->FirstContinuation();
2906 MOZ_ASSERT(ancestor->IsPrimaryFrame());
2907
2908 nsIFrame* frame;
2909 for (frame = aDescendant->GetClosestFlattenedTreeAncestorPrimaryFrame();
2910 frame && ancestor != frame;
2911 frame = frame->GetClosestFlattenedTreeAncestorPrimaryFrame()) {
2912 if (!frame->Extend3DContext()) {
2913 return false;
2914 }
2915 }
2916
2917 MOZ_ASSERT(frame == ancestor);
2918 return true;
2919 }
2920
ItemParticipatesIn3DContext(nsIFrame * aAncestor,nsDisplayItem * aItem)2921 static bool ItemParticipatesIn3DContext(nsIFrame* aAncestor,
2922 nsDisplayItem* aItem) {
2923 auto type = aItem->GetType();
2924 const bool isContainer = type == DisplayItemType::TYPE_WRAP_LIST ||
2925 type == DisplayItemType::TYPE_CONTAINER;
2926
2927 if (isContainer && aItem->GetChildren()->Length() == 1) {
2928 // If the wraplist has only one child item, use the type of that item.
2929 type = aItem->GetChildren()->GetBottom()->GetType();
2930 }
2931
2932 if (type != DisplayItemType::TYPE_TRANSFORM &&
2933 type != DisplayItemType::TYPE_PERSPECTIVE) {
2934 return false;
2935 }
2936 nsIFrame* transformFrame = aItem->Frame();
2937 if (aAncestor->GetContent() == transformFrame->GetContent()) {
2938 return true;
2939 }
2940 return FrameParticipatesIn3DContext(aAncestor, transformFrame);
2941 }
2942
WrapSeparatorTransform(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aNonParticipants,nsDisplayList * aParticipants,int aIndex,nsDisplayItem ** aSeparator)2943 static void WrapSeparatorTransform(nsDisplayListBuilder* aBuilder,
2944 nsIFrame* aFrame,
2945 nsDisplayList* aNonParticipants,
2946 nsDisplayList* aParticipants, int aIndex,
2947 nsDisplayItem** aSeparator) {
2948 if (aNonParticipants->IsEmpty()) {
2949 return;
2950 }
2951
2952 nsDisplayTransform* item = MakeDisplayItemWithIndex<nsDisplayTransform>(
2953 aBuilder, aFrame, aIndex, aNonParticipants, aBuilder->GetVisibleRect());
2954
2955 if (*aSeparator == nullptr && item) {
2956 *aSeparator = item;
2957 }
2958
2959 aParticipants->AppendToTop(item);
2960 }
2961
2962 // Try to compute a clip rect to bound the contents of the mask item
2963 // that will be built for |aMaskedFrame|. If we're not able to compute
2964 // one, return an empty Maybe.
2965 // The returned clip rect, if there is one, is relative to |aMaskedFrame|.
ComputeClipForMaskItem(nsDisplayListBuilder * aBuilder,nsIFrame * aMaskedFrame)2966 static Maybe<nsRect> ComputeClipForMaskItem(nsDisplayListBuilder* aBuilder,
2967 nsIFrame* aMaskedFrame) {
2968 const nsStyleSVGReset* svgReset = aMaskedFrame->StyleSVGReset();
2969
2970 SVGUtils::MaskUsage maskUsage;
2971 SVGUtils::DetermineMaskUsage(aMaskedFrame, false, maskUsage);
2972
2973 nsPoint offsetToUserSpace =
2974 nsLayoutUtils::ComputeOffsetToUserSpace(aBuilder, aMaskedFrame);
2975 int32_t devPixelRatio = aMaskedFrame->PresContext()->AppUnitsPerDevPixel();
2976 gfxPoint devPixelOffsetToUserSpace =
2977 nsLayoutUtils::PointToGfxPoint(offsetToUserSpace, devPixelRatio);
2978 CSSToLayoutDeviceScale cssToDevScale =
2979 aMaskedFrame->PresContext()->CSSToDevPixelScale();
2980
2981 nsPoint toReferenceFrame;
2982 aBuilder->FindReferenceFrameFor(aMaskedFrame, &toReferenceFrame);
2983
2984 Maybe<gfxRect> combinedClip;
2985 if (maskUsage.shouldApplyBasicShapeOrPath) {
2986 Maybe<Rect> result =
2987 CSSClipPathInstance::GetBoundingRectForBasicShapeOrPathClip(
2988 aMaskedFrame, svgReset->mClipPath);
2989 if (result) {
2990 combinedClip = Some(ThebesRect(*result));
2991 }
2992 } else if (maskUsage.shouldApplyClipPath) {
2993 gfxRect result = SVGUtils::GetBBox(
2994 aMaskedFrame,
2995 SVGUtils::eBBoxIncludeClipped | SVGUtils::eBBoxIncludeFill |
2996 SVGUtils::eBBoxIncludeMarkers | SVGUtils::eBBoxIncludeStroke |
2997 SVGUtils::eDoNotClipToBBoxOfContentInsideClipPath);
2998 combinedClip = Some(
2999 ThebesRect((CSSRect::FromUnknownRect(ToRect(result)) * cssToDevScale)
3000 .ToUnknownRect()));
3001 } else {
3002 // The code for this case is adapted from ComputeMaskGeometry().
3003
3004 nsRect borderArea(toReferenceFrame, aMaskedFrame->GetSize());
3005 borderArea -= offsetToUserSpace;
3006
3007 // Use an infinite dirty rect to pass into nsCSSRendering::
3008 // GetImageLayerClip() because we don't have an actual dirty rect to
3009 // pass in. This is fine because the only time GetImageLayerClip() will
3010 // not intersect the incoming dirty rect with something is in the "NoClip"
3011 // case, and we handle that specially.
3012 nsRect dirtyRect(nscoord_MIN / 2, nscoord_MIN / 2, nscoord_MAX,
3013 nscoord_MAX);
3014
3015 nsIFrame* firstFrame =
3016 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aMaskedFrame);
3017 nsTArray<SVGMaskFrame*> maskFrames;
3018 // XXX check return value?
3019 SVGObserverUtils::GetAndObserveMasks(firstFrame, &maskFrames);
3020
3021 for (uint32_t i = 0; i < maskFrames.Length(); ++i) {
3022 gfxRect clipArea;
3023 if (maskFrames[i]) {
3024 clipArea = maskFrames[i]->GetMaskArea(aMaskedFrame);
3025 clipArea = ThebesRect(
3026 (CSSRect::FromUnknownRect(ToRect(clipArea)) * cssToDevScale)
3027 .ToUnknownRect());
3028 } else {
3029 const auto& layer = svgReset->mMask.mLayers[i];
3030 if (layer.mClip == StyleGeometryBox::NoClip) {
3031 return Nothing();
3032 }
3033
3034 nsCSSRendering::ImageLayerClipState clipState;
3035 nsCSSRendering::GetImageLayerClip(
3036 layer, aMaskedFrame, *aMaskedFrame->StyleBorder(), borderArea,
3037 dirtyRect, false /* aWillPaintBorder */, devPixelRatio, &clipState);
3038 clipArea = clipState.mDirtyRectInDevPx;
3039 }
3040 combinedClip = UnionMaybeRects(combinedClip, Some(clipArea));
3041 }
3042 }
3043 if (combinedClip) {
3044 if (combinedClip->IsEmpty()) {
3045 // *clipForMask might be empty if all mask references are not resolvable
3046 // or the size of them are empty. We still need to create a transparent
3047 // mask before bug 1276834 fixed, so don't clip ctx by an empty rectangle
3048 // for for now.
3049 return Nothing();
3050 }
3051
3052 // Convert to user space.
3053 *combinedClip += devPixelOffsetToUserSpace;
3054
3055 // Round the clip out. In FrameLayerBuilder we round clips to nearest
3056 // pixels, and if we have a really thin clip here, that can cause the
3057 // clip to become empty if we didn't round out here.
3058 // The rounding happens in coordinates that are relative to the reference
3059 // frame, which matches what FrameLayerBuilder does.
3060 combinedClip->RoundOut();
3061
3062 // Convert to app units.
3063 nsRect result =
3064 nsLayoutUtils::RoundGfxRectToAppRect(*combinedClip, devPixelRatio);
3065
3066 // The resulting clip is relative to the reference frame, but the caller
3067 // expects it to be relative to the masked frame, so adjust it.
3068 result -= toReferenceFrame;
3069 return Some(result);
3070 }
3071 return Nothing();
3072 }
3073
3074 struct AutoCheckBuilder {
AutoCheckBuilderAutoCheckBuilder3075 explicit AutoCheckBuilder(nsDisplayListBuilder* aBuilder)
3076 : mBuilder(aBuilder) {
3077 aBuilder->Check();
3078 }
3079
~AutoCheckBuilderAutoCheckBuilder3080 ~AutoCheckBuilder() { mBuilder->Check(); }
3081
3082 nsDisplayListBuilder* mBuilder;
3083 };
3084
3085 /**
3086 * Helper class to track container creation. Stores the first tracked container.
3087 * Used to find the innermost container for hit test information, and to notify
3088 * callers whether a container item was created or not.
3089 */
3090 struct ContainerTracker {
TrackContainerContainerTracker3091 void TrackContainer(nsDisplayItem* aContainer) {
3092 if (!aContainer) {
3093 return;
3094 }
3095
3096 if (!mContainer) {
3097 mContainer = aContainer;
3098 }
3099
3100 mCreatedContainer = true;
3101 }
3102
ResetCreatedContainerContainerTracker3103 void ResetCreatedContainer() { mCreatedContainer = false; }
3104
3105 nsDisplayItem* mContainer = nullptr;
3106 bool mCreatedContainer = false;
3107 };
3108
3109 /**
3110 * Tries to reuse a top-level stacking context item from the previous paint.
3111 * Returns true if an item was reused, otherwise false.
3112 */
TryToReuseStackingContextItem(nsDisplayListBuilder * aBuilder,nsDisplayList * aList,nsIFrame * aFrame)3113 bool TryToReuseStackingContextItem(nsDisplayListBuilder* aBuilder,
3114 nsDisplayList* aList, nsIFrame* aFrame) {
3115 if (!aBuilder->IsForPainting() || !aBuilder->IsPartialUpdate() ||
3116 aBuilder->InInvalidSubtree()) {
3117 return false;
3118 }
3119
3120 if (aFrame->IsFrameModified() || aFrame->HasModifiedDescendants()) {
3121 return false;
3122 }
3123
3124 auto& items = aFrame->DisplayItems();
3125 auto* res = std::find_if(
3126 items.begin(), items.end(),
3127 [](nsDisplayItem* aItem) { return aItem->IsPreProcessed(); });
3128
3129 if (res == items.end()) {
3130 return false;
3131 }
3132
3133 nsDisplayItem* container = *res;
3134 MOZ_ASSERT(container->Frame() == aFrame);
3135 DL_LOGD("RDL - Found SC item %p (%s) (frame: %p)", container,
3136 container->Name(), container->Frame());
3137
3138 aList->AppendToTop(container);
3139 aBuilder->ReuseDisplayItem(container);
3140 return true;
3141 }
3142
BuildDisplayListForStackingContext(nsDisplayListBuilder * aBuilder,nsDisplayList * aList,bool * aCreatedContainerItem)3143 void nsIFrame::BuildDisplayListForStackingContext(
3144 nsDisplayListBuilder* aBuilder, nsDisplayList* aList,
3145 bool* aCreatedContainerItem) {
3146 if (aBuilder->IsForContent()) {
3147 DL_LOGV("BuildDisplayListForStackingContext (%p) <", this);
3148 }
3149
3150 ScopeExit e([this, aBuilder]() {
3151 if (aBuilder->IsForContent()) {
3152 DL_LOGV("> BuildDisplayListForStackingContext (%p)", this);
3153 }
3154 });
3155
3156 AutoCheckBuilder check(aBuilder);
3157
3158 if (aBuilder->IsReusingStackingContextItems() &&
3159 TryToReuseStackingContextItem(aBuilder, aList, this)) {
3160 if (aCreatedContainerItem) {
3161 *aCreatedContainerItem = true;
3162 }
3163 return;
3164 }
3165
3166 if (HasAnyStateBits(NS_FRAME_TOO_DEEP_IN_FRAME_TREE)) {
3167 return;
3168 }
3169
3170 const nsStyleDisplay* disp = StyleDisplay();
3171 const nsStyleEffects* effects = StyleEffects();
3172 EffectSet* effectSetForOpacity = EffectSet::GetEffectSetForFrame(
3173 this, nsCSSPropertyIDSet::OpacityProperties());
3174 // We can stop right away if this is a zero-opacity stacking context and
3175 // we're painting, and we're not animating opacity.
3176 bool needHitTestInfo = aBuilder->BuildCompositorHitTestInfo() &&
3177 Style()->PointerEvents() != StylePointerEvents::None;
3178 bool opacityItemForEventsOnly = false;
3179 if (effects->mOpacity == 0.0 && aBuilder->IsForPainting() &&
3180 !(disp->mWillChange.bits & StyleWillChangeBits::OPACITY) &&
3181 !nsLayoutUtils::HasAnimationOfPropertySet(
3182 this, nsCSSPropertyIDSet::OpacityProperties(), effectSetForOpacity)) {
3183 if (needHitTestInfo) {
3184 opacityItemForEventsOnly = true;
3185 } else {
3186 return;
3187 }
3188 }
3189
3190 if (aBuilder->IsForPainting() && disp->mWillChange.bits) {
3191 aBuilder->AddToWillChangeBudget(this, GetSize());
3192 }
3193
3194 // For preserves3d, use the dirty rect already installed on the
3195 // builder, since aDirtyRect maybe distorted for transforms along
3196 // the chain.
3197 nsRect visibleRect = aBuilder->GetVisibleRect();
3198 nsRect dirtyRect = aBuilder->GetDirtyRect();
3199
3200 // We build an opacity item if it's not going to be drawn by SVG content.
3201 // We could in principle skip creating an nsDisplayOpacity item if
3202 // nsDisplayOpacity::NeedsActiveLayer returns false and usingSVGEffects is
3203 // true (the nsDisplayFilter/nsDisplayMasksAndClipPaths could handle the
3204 // opacity). Since SVG has perf issues where we sometimes spend a lot of
3205 // time creating display list items that might be helpful. We'd need to
3206 // restore our mechanism to do that (changed in bug 1482403), and we'd
3207 // need to invalidate the frame if the value that would be return from
3208 // NeedsActiveLayer was to change, which we don't currently do.
3209 const bool useOpacity =
3210 HasVisualOpacity(disp, effects, effectSetForOpacity) &&
3211 !SVGUtils::CanOptimizeOpacity(this);
3212
3213 const bool isTransformed = IsTransformed();
3214 const bool hasPerspective = isTransformed && HasPerspective();
3215 const bool extend3DContext =
3216 Extend3DContext(disp, effects, effectSetForOpacity);
3217 const bool combines3DTransformWithAncestors =
3218 (extend3DContext || isTransformed) && Combines3DTransformWithAncestors();
3219
3220 Maybe<nsDisplayListBuilder::AutoPreserves3DContext> autoPreserves3DContext;
3221 if (extend3DContext && !combines3DTransformWithAncestors) {
3222 // Start a new preserves3d context to keep informations on
3223 // nsDisplayListBuilder.
3224 autoPreserves3DContext.emplace(aBuilder);
3225 // Save dirty rect on the builder to avoid being distorted for
3226 // multiple transforms along the chain.
3227 aBuilder->SavePreserves3DRect();
3228
3229 // We rebuild everything within preserve-3d and don't try
3230 // to retain, so override the dirty rect now.
3231 if (aBuilder->IsRetainingDisplayList()) {
3232 dirtyRect = visibleRect;
3233 aBuilder->SetDisablePartialUpdates(true);
3234 }
3235 }
3236
3237 const bool useBlendMode = effects->mMixBlendMode != StyleBlend::Normal;
3238 if (useBlendMode) {
3239 aBuilder->SetContainsBlendMode(true);
3240 }
3241
3242 // reset blend mode so we can keep track if this stacking context needs have
3243 // a nsDisplayBlendContainer. Set the blend mode back when the routine exits
3244 // so we keep track if the parent stacking context needs a container too.
3245 AutoSaveRestoreContainsBlendMode autoRestoreBlendMode(*aBuilder);
3246 aBuilder->SetContainsBlendMode(false);
3247
3248 bool usingBackdropFilter =
3249 effects->HasBackdropFilters() &&
3250 nsDisplayBackdropFilters::CanCreateWebRenderCommands(aBuilder, this);
3251
3252 if (usingBackdropFilter) {
3253 aBuilder->SetContainsBackdropFilter(true);
3254 }
3255
3256 AutoSaveRestoreContainsBackdropFilter autoRestoreBackdropFilter(*aBuilder);
3257 aBuilder->SetContainsBackdropFilter(false);
3258
3259 nsRect visibleRectOutsideTransform = visibleRect;
3260 nsDisplayTransform::PrerenderInfo prerenderInfo;
3261 bool inTransform = aBuilder->IsInTransform();
3262 if (isTransformed) {
3263 prerenderInfo = nsDisplayTransform::ShouldPrerenderTransformedContent(
3264 aBuilder, this, &visibleRect);
3265
3266 switch (prerenderInfo.mDecision) {
3267 case nsDisplayTransform::PrerenderDecision::Full:
3268 case nsDisplayTransform::PrerenderDecision::Partial:
3269 dirtyRect = visibleRect;
3270 break;
3271 case nsDisplayTransform::PrerenderDecision::No: {
3272 // If we didn't prerender an animated frame in a preserve-3d context,
3273 // then we want disable async animations for the rest of the preserve-3d
3274 // (especially ancestors).
3275 if ((extend3DContext || combines3DTransformWithAncestors) &&
3276 prerenderInfo.mHasAnimations) {
3277 aBuilder->SavePreserves3DAllowAsyncAnimation(false);
3278 }
3279
3280 const nsRect overflow = InkOverflowRectRelativeToSelf();
3281 if (overflow.IsEmpty() && !extend3DContext) {
3282 return;
3283 }
3284
3285 // If we're in preserve-3d then grab the dirty rect that was given to
3286 // the root and transform using the combined transform.
3287 if (combines3DTransformWithAncestors) {
3288 visibleRect = dirtyRect = aBuilder->GetPreserves3DRect();
3289 }
3290
3291 nsRect untransformedDirtyRect;
3292 if (nsDisplayTransform::UntransformRect(dirtyRect, overflow, this,
3293 &untransformedDirtyRect)) {
3294 dirtyRect = untransformedDirtyRect;
3295 nsDisplayTransform::UntransformRect(visibleRect, overflow, this,
3296 &visibleRect);
3297 } else {
3298 // This should only happen if the transform is singular, in which case
3299 // nothing is visible anyway
3300 dirtyRect.SetEmpty();
3301 visibleRect.SetEmpty();
3302 }
3303 }
3304 }
3305 inTransform = true;
3306 } else if (IsFixedPosContainingBlock()) {
3307 // Restict the building area to the overflow rect for these frames, since
3308 // RetainedDisplayListBuilder uses it to know if the size of the stacking
3309 // context changed.
3310 visibleRect.IntersectRect(visibleRect, InkOverflowRect());
3311 dirtyRect.IntersectRect(dirtyRect, InkOverflowRect());
3312 }
3313
3314 bool hasOverrideDirtyRect = false;
3315 // If we're doing a partial build, we're not invalid and we're capable
3316 // of having an override building rect (stacking context and fixed pos
3317 // containing block), then we should assume we have one.
3318 // Either we have an explicit one, or nothing in our subtree changed and
3319 // we have an implicit empty rect.
3320 //
3321 // These conditions should match |CanStoreDisplayListBuildingRect()| in
3322 // RetainedDisplayListBuilder.cpp
3323 if (!aBuilder->IsReusingStackingContextItems() &&
3324 aBuilder->IsPartialUpdate() && !aBuilder->InInvalidSubtree() &&
3325 !IsFrameModified() && IsFixedPosContainingBlock() &&
3326 !GetPrevContinuation() && !GetNextContinuation()) {
3327 dirtyRect = nsRect();
3328 if (HasOverrideDirtyRegion()) {
3329 nsDisplayListBuilder::DisplayListBuildingData* data =
3330 GetProperty(nsDisplayListBuilder::DisplayListBuildingRect());
3331 if (data) {
3332 dirtyRect = data->mDirtyRect.Intersect(visibleRect);
3333 hasOverrideDirtyRect = true;
3334 }
3335 }
3336 }
3337
3338 bool usingFilter = effects->HasFilters();
3339 bool usingMask = SVGIntegrationUtils::UsingMaskOrClipPathForFrame(this);
3340 bool usingSVGEffects = usingFilter || usingMask;
3341
3342 nsRect visibleRectOutsideSVGEffects = visibleRect;
3343 nsDisplayList hoistedScrollInfoItemsStorage(aBuilder);
3344 if (usingSVGEffects) {
3345 dirtyRect =
3346 SVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, dirtyRect);
3347 visibleRect =
3348 SVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, visibleRect);
3349 aBuilder->EnterSVGEffectsContents(this, &hoistedScrollInfoItemsStorage);
3350 }
3351
3352 bool useStickyPosition = disp->mPosition == StylePositionProperty::Sticky;
3353
3354 bool useFixedPosition =
3355 disp->mPosition == StylePositionProperty::Fixed &&
3356 (DisplayPortUtils::IsFixedPosFrameInDisplayPort(this) ||
3357 BuilderHasScrolledClip(aBuilder));
3358
3359 nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList(
3360 aBuilder, this, visibleRect, dirtyRect, isTransformed);
3361
3362 UpdateCurrentHitTestInfo(aBuilder, this);
3363
3364 // Depending on the effects that are applied to this frame, we can create
3365 // multiple container display items and wrap them around our contents.
3366 // This enum lists all the potential container display items, in the order
3367 // outside to inside.
3368 enum class ContainerItemType : uint8_t {
3369 None = 0,
3370 OwnLayerIfNeeded,
3371 BlendMode,
3372 FixedPosition,
3373 OwnLayerForTransformWithRoundedClip,
3374 Perspective,
3375 Transform,
3376 SeparatorTransforms,
3377 Opacity,
3378 Filter,
3379 BlendContainer
3380 };
3381
3382 nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
3383
3384 auto cssClip = GetClipPropClipRect(disp, effects, GetSize());
3385 auto ApplyClipProp = [&](DisplayListClipState::AutoSaveRestore& aClipState) {
3386 if (!cssClip) {
3387 return;
3388 }
3389 nsPoint offset = aBuilder->GetCurrentFrameOffsetToReferenceFrame();
3390 aBuilder->IntersectDirtyRect(*cssClip);
3391 aBuilder->IntersectVisibleRect(*cssClip);
3392 aClipState.ClipContentDescendants(*cssClip + offset);
3393 };
3394
3395 // The CSS clip property is effectively inside the transform, but outside the
3396 // filters. So if we're not transformed we can apply it just here for
3397 // simplicity, instead of on each of the places that handle clipCapturedBy.
3398 DisplayListClipState::AutoSaveRestore untransformedCssClip(aBuilder);
3399 if (!isTransformed) {
3400 ApplyClipProp(untransformedCssClip);
3401 }
3402
3403 // If there is a current clip, then depending on the container items we
3404 // create, different things can happen to it. Some container items simply
3405 // propagate the clip to their children and aren't clipped themselves.
3406 // But other container items, especially those that establish a different
3407 // geometry for their contents (e.g. transforms), capture the clip on
3408 // themselves and unset the clip for their contents. If we create more than
3409 // one of those container items, the clip will be captured on the outermost
3410 // one and the inner container items will be unclipped.
3411 ContainerItemType clipCapturedBy = ContainerItemType::None;
3412 if (useFixedPosition) {
3413 clipCapturedBy = ContainerItemType::FixedPosition;
3414 } else if (isTransformed) {
3415 const DisplayItemClipChain* currentClip =
3416 aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder);
3417 if ((hasPerspective || extend3DContext) &&
3418 (currentClip && currentClip->HasRoundedCorners())) {
3419 // If we're creating an nsDisplayTransform item that is going to combine
3420 // its transform with its children (preserve-3d or perspective), then we
3421 // can't have an intermediate surface. Mask layers force an intermediate
3422 // surface, so if we're going to need both then create a separate
3423 // wrapping layer for the mask.
3424 clipCapturedBy = ContainerItemType::OwnLayerForTransformWithRoundedClip;
3425 } else if (hasPerspective) {
3426 clipCapturedBy = ContainerItemType::Perspective;
3427 } else {
3428 clipCapturedBy = ContainerItemType::Transform;
3429 }
3430 } else if (usingFilter) {
3431 clipCapturedBy = ContainerItemType::Filter;
3432 }
3433
3434 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
3435 if (clipCapturedBy != ContainerItemType::None) {
3436 clipState.Clear();
3437 }
3438
3439 DisplayListClipState::AutoSaveRestore transformedCssClip(aBuilder);
3440 if (isTransformed) {
3441 // FIXME(emilio, bug 1525159): In the case we have a both a transform _and_
3442 // filters, this clips the input to the filters as well, which is not
3443 // correct (clipping by the `clip` property is supposed to happen after
3444 // applying the filter effects, per [1].
3445 //
3446 // This is not a regression though, since we used to do that anyway before
3447 // bug 1514384, and even without the transform we get it wrong.
3448 //
3449 // [1]: https://drafts.fxtf.org/css-masking/#placement
3450 ApplyClipProp(transformedCssClip);
3451 }
3452
3453 nsDisplayListCollection set(aBuilder);
3454 Maybe<nsRect> clipForMask;
3455 bool insertBackdropRoot;
3456 {
3457 DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
3458 nsDisplayListBuilder::AutoInTransformSetter inTransformSetter(aBuilder,
3459 inTransform);
3460 nsDisplayListBuilder::AutoEnterFilter filterASRSetter(aBuilder,
3461 usingFilter);
3462 nsDisplayListBuilder::AutoInEventsOnly inEventsSetter(
3463 aBuilder, opacityItemForEventsOnly);
3464
3465 // If we have a mask, compute a clip to bound the masked content.
3466 // This is necessary in case the content moves with an ancestor
3467 // ASR of the mask.
3468 // Don't do this if we also have a filter, because then the clip
3469 // would be applied before the filter, violating
3470 // https://www.w3.org/TR/filter-effects-1/#placement.
3471 // Filters are a containing block for fixed and absolute descendants,
3472 // so the masked content cannot move with an ancestor ASR.
3473 if (usingMask && !usingFilter) {
3474 clipForMask = ComputeClipForMaskItem(aBuilder, this);
3475 if (clipForMask) {
3476 aBuilder->IntersectDirtyRect(*clipForMask);
3477 aBuilder->IntersectVisibleRect(*clipForMask);
3478 nestedClipState.ClipContentDescendants(
3479 *clipForMask + aBuilder->GetCurrentFrameOffsetToReferenceFrame());
3480 }
3481 }
3482
3483 // extend3DContext also guarantees that applyAbsPosClipping and
3484 // usingSVGEffects are false We only modify the preserve-3d rect if we are
3485 // the top of a preserve-3d heirarchy
3486 if (extend3DContext) {
3487 // Mark these first so MarkAbsoluteFramesForDisplayList knows if we are
3488 // going to be forced to descend into frames.
3489 aBuilder->MarkPreserve3DFramesForDisplayList(this);
3490 }
3491
3492 aBuilder->AdjustWindowDraggingRegion(this);
3493
3494 MarkAbsoluteFramesForDisplayList(aBuilder);
3495 aBuilder->Check();
3496 BuildDisplayList(aBuilder, set);
3497 SetBuiltDisplayList(true);
3498 aBuilder->Check();
3499 aBuilder->DisplayCaret(this, set.Outlines());
3500
3501 insertBackdropRoot = aBuilder->ContainsBackdropFilter() &&
3502 FormsBackdropRoot(disp, effects, StyleSVGReset());
3503
3504 // Blend modes are a real pain for retained display lists. We build a blend
3505 // container item if the built list contains any blend mode items within
3506 // the current stacking context. This can change without an invalidation
3507 // to the stacking context frame, or the blend mode frame (e.g. by moving
3508 // an intermediate frame).
3509 // When we gain/remove a blend container item, we need to mark this frame
3510 // as invalid and have the full display list for merging to track
3511 // the change correctly.
3512 // It seems really hard to track this in advance, as the bookkeeping
3513 // required to note which stacking contexts have blend descendants
3514 // is complex and likely to be buggy.
3515 // Instead we're doing the sad thing, detecting it afterwards, and just
3516 // repeating display list building if it changed.
3517 // We have to repeat building for the entire display list (or at least
3518 // the outer stacking context), since we need to mark this frame as invalid
3519 // to remove any existing content that isn't wrapped in the blend container,
3520 // and then we need to build content infront/behind the blend container
3521 // to get correct positioning during merging.
3522 if ((insertBackdropRoot || aBuilder->ContainsBlendMode()) &&
3523 aBuilder->IsRetainingDisplayList()) {
3524 if (aBuilder->IsPartialUpdate()) {
3525 aBuilder->SetPartialBuildFailed(true);
3526 } else {
3527 aBuilder->SetDisablePartialUpdates(true);
3528 }
3529 }
3530 }
3531
3532 // If a child contains a backdrop filter, but this stacking context does not
3533 // form a backdrop root, we need to propogate up the tree until we find an
3534 // ancestor that does form a backdrop root.
3535 if (!insertBackdropRoot && aBuilder->ContainsBackdropFilter()) {
3536 autoRestoreBackdropFilter.DelegateUp(true);
3537 }
3538
3539 if (aBuilder->IsBackgroundOnly()) {
3540 set.BlockBorderBackgrounds()->DeleteAll(aBuilder);
3541 set.Floats()->DeleteAll(aBuilder);
3542 set.Content()->DeleteAll(aBuilder);
3543 set.PositionedDescendants()->DeleteAll(aBuilder);
3544 set.Outlines()->DeleteAll(aBuilder);
3545 }
3546
3547 if (hasOverrideDirtyRect &&
3548 StaticPrefs::layout_display_list_show_rebuild_area()) {
3549 nsDisplaySolidColor* color = MakeDisplayItem<nsDisplaySolidColor>(
3550 aBuilder, this,
3551 dirtyRect + aBuilder->GetCurrentFrameOffsetToReferenceFrame(),
3552 NS_RGBA(255, 0, 0, 64), false);
3553 if (color) {
3554 color->SetOverrideZIndex(INT32_MAX);
3555 set.PositionedDescendants()->AppendToTop(color);
3556 }
3557 }
3558
3559 nsIContent* content = GetContent();
3560 if (!content) {
3561 content = PresContext()->Document()->GetRootElement();
3562 }
3563
3564 nsDisplayList resultList(aBuilder);
3565 set.SerializeWithCorrectZOrder(&resultList, content);
3566
3567 #ifdef DEBUG
3568 DisplayDebugBorders(aBuilder, this, set);
3569 #endif
3570
3571 // Get the ASR to use for the container items that we create here.
3572 const ActiveScrolledRoot* containerItemASR = contASRTracker.GetContainerASR();
3573
3574 ContainerTracker ct;
3575
3576 /* If adding both a nsDisplayBlendContainer and a nsDisplayBlendMode to the
3577 * same list, the nsDisplayBlendContainer should be added first. This only
3578 * happens when the element creating this stacking context has mix-blend-mode
3579 * and also contains a child which has mix-blend-mode.
3580 * The nsDisplayBlendContainer must be added to the list first, so it does not
3581 * isolate the containing element blending as well.
3582 */
3583 if (aBuilder->ContainsBlendMode()) {
3584 DisplayListClipState::AutoSaveRestore blendContainerClipState(aBuilder);
3585 resultList.AppendToTop(nsDisplayBlendContainer::CreateForMixBlendMode(
3586 aBuilder, this, &resultList, containerItemASR));
3587 ct.TrackContainer(resultList.GetTop());
3588 }
3589
3590 if (insertBackdropRoot) {
3591 DisplayListClipState::AutoSaveRestore backdropRootContainerClipState(
3592 aBuilder);
3593 resultList.AppendNewToTop<nsDisplayBackdropRootContainer>(
3594 aBuilder, this, &resultList, containerItemASR);
3595 ct.TrackContainer(resultList.GetTop());
3596 }
3597
3598 if (usingBackdropFilter) {
3599 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
3600 nsRect backdropRect =
3601 GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this);
3602 resultList.AppendNewToTop<nsDisplayBackdropFilters>(
3603 aBuilder, this, &resultList, backdropRect);
3604 ct.TrackContainer(resultList.GetTop());
3605 }
3606
3607 /* If there are any SVG effects, wrap the list up in an SVG effects item
3608 * (which also handles CSS group opacity). Note that we create an SVG effects
3609 * item even if resultList is empty, since a filter can produce graphical
3610 * output even if the element being filtered wouldn't otherwise do so.
3611 */
3612 if (usingSVGEffects) {
3613 MOZ_ASSERT(usingFilter || usingMask,
3614 "Beside filter & mask/clip-path, what else effect do we have?");
3615
3616 if (clipCapturedBy == ContainerItemType::Filter) {
3617 clipState.Restore();
3618 }
3619 // Revert to the post-filter dirty rect.
3620 aBuilder->SetVisibleRect(visibleRectOutsideSVGEffects);
3621
3622 // Skip all filter effects while generating glyph mask.
3623 if (usingFilter && !aBuilder->IsForGenerateGlyphMask()) {
3624 /* List now emptied, so add the new list to the top. */
3625 resultList.AppendNewToTop<nsDisplayFilters>(aBuilder, this, &resultList);
3626 ct.TrackContainer(resultList.GetTop());
3627 }
3628
3629 if (usingMask) {
3630 DisplayListClipState::AutoSaveRestore maskClipState(aBuilder);
3631 // The mask should move with aBuilder->CurrentActiveScrolledRoot(), so
3632 // that's the ASR we prefer to use for the mask item. However, we can
3633 // only do this if the mask if clipped with respect to that ASR, because
3634 // an item always needs to have finite bounds with respect to its ASR.
3635 // If we weren't able to compute a clip for the mask, we fall back to
3636 // using containerItemASR, which is the lowest common ancestor clip of
3637 // the mask's contents. That's not entirely correct, but it satisfies
3638 // the base requirement of the ASR system (that items have finite bounds
3639 // wrt. their ASR).
3640 const ActiveScrolledRoot* maskASR =
3641 clipForMask.isSome() ? aBuilder->CurrentActiveScrolledRoot()
3642 : containerItemASR;
3643 /* List now emptied, so add the new list to the top. */
3644 resultList.AppendNewToTop<nsDisplayMasksAndClipPaths>(
3645 aBuilder, this, &resultList, maskASR);
3646 ct.TrackContainer(resultList.GetTop());
3647 }
3648
3649 // TODO(miko): We could probably create a wraplist here and avoid creating
3650 // it later in |BuildDisplayListForChild()|.
3651 ct.ResetCreatedContainer();
3652
3653 // Also add the hoisted scroll info items. We need those for APZ scrolling
3654 // because nsDisplayMasksAndClipPaths items can't build active layers.
3655 aBuilder->ExitSVGEffectsContents();
3656 resultList.AppendToTop(&hoistedScrollInfoItemsStorage);
3657 }
3658
3659 /* If the list is non-empty and there is CSS group opacity without SVG
3660 * effects, wrap it up in an opacity item.
3661 */
3662 if (useOpacity) {
3663 // Don't clip nsDisplayOpacity items. We clip their descendants instead.
3664 // The clip we would set on an element with opacity would clip
3665 // all descendant content, but some should not be clipped.
3666 DisplayListClipState::AutoSaveRestore opacityClipState(aBuilder);
3667 const bool needsActiveOpacityLayer =
3668 nsDisplayOpacity::NeedsActiveLayer(aBuilder, this);
3669
3670 resultList.AppendNewToTop<nsDisplayOpacity>(
3671 aBuilder, this, &resultList, containerItemASR, opacityItemForEventsOnly,
3672 needsActiveOpacityLayer);
3673 ct.TrackContainer(resultList.GetTop());
3674 }
3675
3676 /* If we're going to apply a transformation and don't have preserve-3d set,
3677 * wrap everything in an nsDisplayTransform. If there's nothing in the list,
3678 * don't add anything.
3679 *
3680 * For the preserve-3d case we want to individually wrap every child in the
3681 * list with a separate nsDisplayTransform instead. When the child is already
3682 * an nsDisplayTransform, we can skip this step, as the computed transform
3683 * will already include our own.
3684 *
3685 * We also traverse into sublists created by nsDisplayWrapList, so that we
3686 * find all the correct children.
3687 */
3688 if (isTransformed && extend3DContext) {
3689 // Install dummy nsDisplayTransform as a leaf containing
3690 // descendants not participating this 3D rendering context.
3691 nsDisplayList nonparticipants(aBuilder);
3692 nsDisplayList participants(aBuilder);
3693 int index = 1;
3694
3695 nsDisplayItem* separator = nullptr;
3696
3697 // TODO: This can be simplified: |participants| is just |resultList|.
3698 for (nsDisplayItem* item : resultList.TakeItems()) {
3699 if (ItemParticipatesIn3DContext(this, item) &&
3700 !item->GetClip().HasClip()) {
3701 // The frame of this item participates the same 3D context.
3702 WrapSeparatorTransform(aBuilder, this, &nonparticipants, &participants,
3703 index++, &separator);
3704
3705 participants.AppendToTop(item);
3706 } else {
3707 // The frame of the item doesn't participate the current
3708 // context, or has no transform.
3709 //
3710 // For items participating but not transformed, they are add
3711 // to nonparticipants to get a separator layer for handling
3712 // clips, if there is, on an intermediate surface.
3713 // \see ContainerLayer::DefaultComputeEffectiveTransforms().
3714 nonparticipants.AppendToTop(item);
3715 }
3716 }
3717 WrapSeparatorTransform(aBuilder, this, &nonparticipants, &participants,
3718 index++, &separator);
3719
3720 if (separator) {
3721 ct.TrackContainer(separator);
3722 }
3723
3724 resultList.AppendToTop(&participants);
3725 }
3726
3727 if (isTransformed) {
3728 transformedCssClip.Restore();
3729 if (clipCapturedBy == ContainerItemType::Transform) {
3730 // Restore clip state now so nsDisplayTransform is clipped properly.
3731 clipState.Restore();
3732 }
3733 // Revert to the dirtyrect coming in from the parent, without our transform
3734 // taken into account.
3735 aBuilder->SetVisibleRect(visibleRectOutsideTransform);
3736
3737 if (this != aBuilder->RootReferenceFrame()) {
3738 // Revert to the outer reference frame and offset because all display
3739 // items we create from now on are outside the transform.
3740 nsPoint toOuterReferenceFrame;
3741 const nsIFrame* outerReferenceFrame =
3742 aBuilder->FindReferenceFrameFor(GetParent(), &toOuterReferenceFrame);
3743 toOuterReferenceFrame += GetPosition();
3744
3745 buildingDisplayList.SetReferenceFrameAndCurrentOffset(
3746 outerReferenceFrame, toOuterReferenceFrame);
3747 }
3748
3749 // We would like to block async animations for ancestors of ones not
3750 // prerendered in the preserve-3d tree. Now that we've finished processing
3751 // all descendants, update allowAsyncAnimation to take their prerender
3752 // state into account
3753 // FIXME: We don't block async animations for previous siblings because
3754 // their prerender decisions have been made. We may have to figure out a
3755 // better way to rollback their prerender decisions.
3756 // Alternatively we could not block animations for later siblings, and only
3757 // block them for ancestors of a blocked one.
3758 if ((extend3DContext || combines3DTransformWithAncestors) &&
3759 prerenderInfo.CanUseAsyncAnimations() &&
3760 !aBuilder->GetPreserves3DAllowAsyncAnimation()) {
3761 // aBuilder->GetPreserves3DAllowAsyncAnimation() means the inner or
3762 // previous silbing frames are allowed/disallowed for async animations.
3763 prerenderInfo.mDecision = nsDisplayTransform::PrerenderDecision::No;
3764 }
3765
3766 nsDisplayTransform* transformItem = MakeDisplayItem<nsDisplayTransform>(
3767 aBuilder, this, &resultList, visibleRect, prerenderInfo.mDecision);
3768 if (transformItem) {
3769 resultList.AppendToTop(transformItem);
3770 ct.TrackContainer(transformItem);
3771 }
3772
3773 if (hasPerspective) {
3774 transformItem->MarkWithAssociatedPerspective();
3775
3776 if (clipCapturedBy == ContainerItemType::Perspective) {
3777 clipState.Restore();
3778 }
3779 resultList.AppendNewToTop<nsDisplayPerspective>(aBuilder, this,
3780 &resultList);
3781 ct.TrackContainer(resultList.GetTop());
3782 }
3783 }
3784
3785 if (clipCapturedBy ==
3786 ContainerItemType::OwnLayerForTransformWithRoundedClip) {
3787 clipState.Restore();
3788 resultList.AppendNewToTopWithIndex<nsDisplayOwnLayer>(
3789 aBuilder, this,
3790 /* aIndex = */ nsDisplayOwnLayer::OwnLayerForTransformWithRoundedClip,
3791 &resultList, aBuilder->CurrentActiveScrolledRoot(),
3792 nsDisplayOwnLayerFlags::None, ScrollbarData{},
3793 /* aForceActive = */ false, false);
3794 ct.TrackContainer(resultList.GetTop());
3795 }
3796
3797 /* If we have sticky positioning, wrap it in a sticky position item.
3798 */
3799 if (useFixedPosition) {
3800 if (clipCapturedBy == ContainerItemType::FixedPosition) {
3801 clipState.Restore();
3802 }
3803 // The ASR for the fixed item should be the ASR of our containing block,
3804 // which has been set as the builder's current ASR, unless this frame is
3805 // invisible and we hadn't saved display item data for it. In that case,
3806 // we need to take the containerItemASR since we might have fixed children.
3807 // For WebRender, we want to the know what |containerItemASR| is for the
3808 // case where the fixed-pos item is not a "real" fixed-pos item (e.g. it's
3809 // nested inside a scrolling transform), so we stash that on the display
3810 // item as well.
3811 const ActiveScrolledRoot* fixedASR = ActiveScrolledRoot::PickAncestor(
3812 containerItemASR, aBuilder->CurrentActiveScrolledRoot());
3813 resultList.AppendNewToTop<nsDisplayFixedPosition>(
3814 aBuilder, this, &resultList, fixedASR, containerItemASR);
3815 ct.TrackContainer(resultList.GetTop());
3816 } else if (useStickyPosition) {
3817 // For position:sticky, the clip needs to be applied both to the sticky
3818 // container item and to the contents. The container item needs the clip
3819 // because a scrolled clip needs to move independently from the sticky
3820 // contents, and the contents need the clip so that they have finite
3821 // clipped bounds with respect to the container item's ASR. The latter is
3822 // a little tricky in the case where the sticky item has both fixed and
3823 // non-fixed descendants, because that means that the sticky container
3824 // item's ASR is the ASR of the fixed descendant.
3825 // For WebRender display list building, though, we still want to know the
3826 // the ASR that the sticky container item would normally have, so we stash
3827 // that on the display item as the "container ASR" (i.e. the normal ASR of
3828 // the container item, excluding the special behaviour induced by fixed
3829 // descendants).
3830 const ActiveScrolledRoot* stickyASR = ActiveScrolledRoot::PickAncestor(
3831 containerItemASR, aBuilder->CurrentActiveScrolledRoot());
3832
3833 auto* stickyItem = MakeDisplayItem<nsDisplayStickyPosition>(
3834 aBuilder, this, &resultList, stickyASR,
3835 aBuilder->CurrentActiveScrolledRoot(),
3836 clipState.IsClippedToDisplayPort());
3837
3838 bool shouldFlatten = true;
3839
3840 StickyScrollContainer* stickyScrollContainer =
3841 StickyScrollContainer::GetStickyScrollContainerForFrame(this);
3842 if (stickyScrollContainer &&
3843 stickyScrollContainer->ScrollFrame()->IsMaybeAsynchronouslyScrolled()) {
3844 shouldFlatten = false;
3845 }
3846
3847 stickyItem->SetShouldFlatten(shouldFlatten);
3848
3849 resultList.AppendToTop(stickyItem);
3850 ct.TrackContainer(stickyItem);
3851
3852 // If the sticky element is inside a filter, annotate the scroll frame that
3853 // scrolls the filter as having out-of-flow content inside a filter (this
3854 // inhibits paint skipping).
3855 if (aBuilder->GetFilterASR() && aBuilder->GetFilterASR() == stickyASR) {
3856 aBuilder->GetFilterASR()
3857 ->mScrollableFrame->SetHasOutOfFlowContentInsideFilter();
3858 }
3859 }
3860
3861 /* If there's blending, wrap up the list in a blend-mode item. Note
3862 * that opacity can be applied before blending as the blend color is
3863 * not affected by foreground opacity (only background alpha).
3864 */
3865
3866 if (useBlendMode) {
3867 DisplayListClipState::AutoSaveRestore blendModeClipState(aBuilder);
3868 resultList.AppendNewToTop<nsDisplayBlendMode>(aBuilder, this, &resultList,
3869 effects->mMixBlendMode,
3870 containerItemASR, false);
3871 ct.TrackContainer(resultList.GetTop());
3872 }
3873
3874 bool createdOwnLayer = false;
3875 CreateOwnLayerIfNeeded(aBuilder, &resultList,
3876 nsDisplayOwnLayer::OwnLayerForStackingContext,
3877 &createdOwnLayer);
3878
3879 if (createdOwnLayer) {
3880 ct.TrackContainer(resultList.GetTop());
3881 }
3882
3883 if (aBuilder->IsReusingStackingContextItems()) {
3884 if (resultList.IsEmpty()) {
3885 return;
3886 }
3887
3888 nsDisplayItem* container = resultList.GetBottom();
3889 if (resultList.Length() > 1 || container->Frame() != this) {
3890 container = MakeDisplayItem<nsDisplayContainer>(
3891 aBuilder, this, containerItemASR, &resultList);
3892 } else {
3893 MOZ_ASSERT(resultList.Length() == 1);
3894 resultList.Clear();
3895 }
3896
3897 // Mark the outermost display item as reusable. These display items and
3898 // their chidren can be reused during the next paint if no ancestor or
3899 // descendant frames have been modified.
3900 if (!container->IsReusedItem()) {
3901 container->SetReusable();
3902 }
3903 aList->AppendToTop(container);
3904 ct.TrackContainer(container);
3905 } else {
3906 aList->AppendToTop(&resultList);
3907 }
3908
3909 if (aCreatedContainerItem) {
3910 *aCreatedContainerItem = ct.mCreatedContainer;
3911 }
3912 }
3913
WrapInWrapList(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const ActiveScrolledRoot * aContainerASR,bool aBuiltContainerItem=false)3914 static nsDisplayItem* WrapInWrapList(nsDisplayListBuilder* aBuilder,
3915 nsIFrame* aFrame, nsDisplayList* aList,
3916 const ActiveScrolledRoot* aContainerASR,
3917 bool aBuiltContainerItem = false) {
3918 nsDisplayItem* item = aList->GetBottom();
3919 if (!item) {
3920 return nullptr;
3921 }
3922
3923 // We need a wrap list if there are multiple items, or if the single
3924 // item has a different frame. This can change in a partial build depending
3925 // on which items we build, so we need to ensure that we don't transition
3926 // to/from a wrap list without invalidating correctly.
3927 bool needsWrapList =
3928 aList->Length() > 1 || item->Frame() != aFrame || item->GetChildren();
3929
3930 // If we have an explicit container item (that can't change without an
3931 // invalidation) or we're doing a full build and don't need a wrap list, then
3932 // we can skip adding one.
3933 if (aBuiltContainerItem || (!aBuilder->IsPartialUpdate() && !needsWrapList)) {
3934 MOZ_ASSERT(aList->Length() == 1);
3935 aList->Clear();
3936 return item;
3937 }
3938
3939 // If we're doing a partial build and we didn't need a wrap list
3940 // previously then we can try to work from there.
3941 if (aBuilder->IsPartialUpdate() &&
3942 !aFrame->HasDisplayItem(uint32_t(DisplayItemType::TYPE_CONTAINER))) {
3943 // If we now need a wrap list, we must previously have had no display items
3944 // or a single one belonging to this frame. Mark the item itself as
3945 // discarded so that RetainedDisplayListBuilder uses the ones we just built.
3946 // We don't want to mark the frame as modified as that would invalidate
3947 // positioned descendants that might be outside of this list, and might not
3948 // have been rebuilt this time.
3949 if (needsWrapList) {
3950 DiscardOldItems(aFrame);
3951 } else {
3952 MOZ_ASSERT(aList->Length() == 1);
3953 aList->Clear();
3954 return item;
3955 }
3956 }
3957
3958 // The last case we could try to handle is when we previously had a wrap list,
3959 // but no longer need it. Unfortunately we can't differentiate this case from
3960 // a partial build where other children exist but we just didn't build them
3961 // this time.
3962 // TODO:RetainedDisplayListBuilder's merge phase has the full list and
3963 // could strip them out.
3964
3965 return MakeDisplayItem<nsDisplayContainer>(aBuilder, aFrame, aContainerASR,
3966 aList);
3967 }
3968
3969 /**
3970 * Check if a frame should be visited for building display list.
3971 */
DescendIntoChild(nsDisplayListBuilder * aBuilder,const nsIFrame * aChild,const nsRect & aVisible,const nsRect & aDirty)3972 static bool DescendIntoChild(nsDisplayListBuilder* aBuilder,
3973 const nsIFrame* aChild, const nsRect& aVisible,
3974 const nsRect& aDirty) {
3975 if (aChild->HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) {
3976 return true;
3977 }
3978
3979 // If the child is a scrollframe that we want to ignore, then we need
3980 // to descend into it because its scrolled child may intersect the dirty
3981 // area even if the scrollframe itself doesn't.
3982 if (aChild == aBuilder->GetIgnoreScrollFrame()) {
3983 return true;
3984 }
3985
3986 // There are cases where the "ignore scroll frame" on the builder is not set
3987 // correctly, and so we additionally want to catch cases where the child is
3988 // a root scrollframe and we are ignoring scrolling on the viewport.
3989 if (aChild == aBuilder->GetPresShellIgnoreScrollFrame()) {
3990 return true;
3991 }
3992
3993 nsRect overflow = aChild->InkOverflowRect();
3994
3995 // On mobile, there may be a dynamic toolbar. The root content document's
3996 // root scroll frame's ink overflow rect does not include the toolbar
3997 // height, but if the toolbar is hidden, we still want to be able to target
3998 // content underneath the toolbar, so expand the overflow rect here to
3999 // allow display list building to descend into the scroll frame.
4000 if (aBuilder->IsForEventDelivery() &&
4001 aChild == aChild->PresShell()->GetRootScrollFrame() &&
4002 aChild->PresContext()->IsRootContentDocumentCrossProcess() &&
4003 aChild->PresContext()->HasDynamicToolbar()) {
4004 overflow.SizeTo(nsLayoutUtils::ExpandHeightForDynamicToolbar(
4005 aChild->PresContext(), overflow.Size()));
4006 }
4007
4008 if (aDirty.Intersects(overflow)) {
4009 return true;
4010 }
4011
4012 if (aChild->ForceDescendIntoIfVisible() && aVisible.Intersects(overflow)) {
4013 return true;
4014 }
4015
4016 if (aChild->IsFrameOfType(nsIFrame::eTablePart)) {
4017 // Relative positioning and transforms can cause table parts to move, but we
4018 // will still paint the backgrounds for their ancestor parts under them at
4019 // their 'normal' position. That means that we must consider the overflow
4020 // rects at both positions.
4021
4022 // We convert the overflow rect into the nsTableFrame's coordinate
4023 // space, applying the normal position offset at each step. Then we
4024 // compare that against the builder's cached dirty rect in table
4025 // coordinate space.
4026 const nsIFrame* f = aChild;
4027 nsRect normalPositionOverflowRelativeToTable = overflow;
4028
4029 while (f->IsFrameOfType(nsIFrame::eTablePart)) {
4030 normalPositionOverflowRelativeToTable += f->GetNormalPosition();
4031 f = f->GetParent();
4032 }
4033
4034 nsDisplayTableBackgroundSet* tableBGs = aBuilder->GetTableBackgroundSet();
4035 if (tableBGs && tableBGs->GetDirtyRect().Intersects(
4036 normalPositionOverflowRelativeToTable)) {
4037 return true;
4038 }
4039 }
4040
4041 return false;
4042 }
4043
BuildDisplayListForSimpleChild(nsDisplayListBuilder * aBuilder,nsIFrame * aChild,const nsDisplayListSet & aLists)4044 void nsIFrame::BuildDisplayListForSimpleChild(nsDisplayListBuilder* aBuilder,
4045 nsIFrame* aChild,
4046 const nsDisplayListSet& aLists) {
4047 // This is the shortcut for frames been handled along the common
4048 // path, the most common one of THE COMMON CASE mentioned later.
4049 MOZ_ASSERT(aChild->Type() != LayoutFrameType::Placeholder);
4050 MOZ_ASSERT(!aBuilder->GetSelectedFramesOnly() &&
4051 !aBuilder->GetIncludeAllOutOfFlows(),
4052 "It should be held for painting to window");
4053 MOZ_ASSERT(aChild->HasAnyStateBits(NS_FRAME_SIMPLE_DISPLAYLIST));
4054
4055 const nsPoint offset = aChild->GetOffsetTo(this);
4056 const nsRect visible = aBuilder->GetVisibleRect() - offset;
4057 const nsRect dirty = aBuilder->GetDirtyRect() - offset;
4058
4059 if (!DescendIntoChild(aBuilder, aChild, visible, dirty)) {
4060 DL_LOGV("Skipped frame %p", aChild);
4061 return;
4062 }
4063
4064 // Child cannot be transformed since it is not a stacking context.
4065 nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(
4066 aBuilder, aChild, visible, dirty, false);
4067
4068 UpdateCurrentHitTestInfo(aBuilder, aChild);
4069
4070 aChild->MarkAbsoluteFramesForDisplayList(aBuilder);
4071 aBuilder->AdjustWindowDraggingRegion(aChild);
4072 aBuilder->Check();
4073 aChild->BuildDisplayList(aBuilder, aLists);
4074 aChild->SetBuiltDisplayList(true);
4075 aBuilder->Check();
4076 aBuilder->DisplayCaret(aChild, aLists.Outlines());
4077 #ifdef DEBUG
4078 DisplayDebugBorders(aBuilder, aChild, aLists);
4079 #endif
4080 }
4081
DisplayFlagForFlexOrGridItem() const4082 nsIFrame::DisplayChildFlag nsIFrame::DisplayFlagForFlexOrGridItem() const {
4083 MOZ_ASSERT(IsFlexOrGridItem(),
4084 "Should only be called on flex or grid items!");
4085 return DisplayChildFlag::ForcePseudoStackingContext;
4086 }
4087
ShouldSkipFrame(nsDisplayListBuilder * aBuilder,const nsIFrame * aFrame)4088 static bool ShouldSkipFrame(nsDisplayListBuilder* aBuilder,
4089 const nsIFrame* aFrame) {
4090 // If painting is restricted to just the background of the top level frame,
4091 // then we have nothing to do here.
4092 if (aBuilder->IsBackgroundOnly()) {
4093 return true;
4094 }
4095
4096 if (aBuilder->IsForGenerateGlyphMask() &&
4097 (!aFrame->IsTextFrame() && aFrame->IsLeaf())) {
4098 return true;
4099 }
4100
4101 // The placeholder frame should have the same content as the OOF frame.
4102 if (aBuilder->GetSelectedFramesOnly() &&
4103 (aFrame->IsLeaf() && !aFrame->IsSelected())) {
4104 return true;
4105 }
4106
4107 static const nsFrameState skipFlags =
4108 (NS_FRAME_TOO_DEEP_IN_FRAME_TREE | NS_FRAME_IS_NONDISPLAY);
4109
4110 return aFrame->HasAnyStateBits(skipFlags);
4111 }
4112
BuildDisplayListForChild(nsDisplayListBuilder * aBuilder,nsIFrame * aChild,const nsDisplayListSet & aLists,DisplayChildFlags aFlags)4113 void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
4114 nsIFrame* aChild,
4115 const nsDisplayListSet& aLists,
4116 DisplayChildFlags aFlags) {
4117 AutoCheckBuilder check(aBuilder);
4118 if (aBuilder->IsForContent()) {
4119 DL_LOGV("BuildDisplayListForChild (%p) <", aChild);
4120 }
4121 ScopeExit e([aChild, aBuilder]() {
4122 if (aBuilder->IsForContent()) {
4123 DL_LOGV("> BuildDisplayListForChild (%p)", aChild);
4124 }
4125 });
4126
4127 if (ShouldSkipFrame(aBuilder, aChild)) {
4128 return;
4129 }
4130
4131 // If we're generating a display list for printing, include Link items for
4132 // frames that correspond to HTML link elements so that we can have active
4133 // links in saved PDF output. Note that the state of "within a link" is
4134 // set on the display-list builder, such that all descendants of the link
4135 // element will generate display-list links.
4136 // TODO: we should be able to optimize this so as to avoid creating links
4137 // for the same destination that entirely overlap each other, which adds
4138 // nothing useful to the final PDF.
4139 Maybe<nsDisplayListBuilder::Linkifier> linkifier;
4140 if (StaticPrefs::print_save_as_pdf_links_enabled() &&
4141 aBuilder->IsForPrinting()) {
4142 linkifier.emplace(aBuilder, aChild, aLists.Content());
4143 linkifier->MaybeAppendLink(aBuilder, aChild);
4144 }
4145
4146 nsIFrame* child = aChild;
4147 auto* placeholder = child->IsPlaceholderFrame()
4148 ? static_cast<nsPlaceholderFrame*>(child)
4149 : nullptr;
4150 nsIFrame* childOrOutOfFlow =
4151 placeholder ? placeholder->GetOutOfFlowFrame() : child;
4152
4153 nsIFrame* parent = childOrOutOfFlow->GetParent();
4154 const auto* parentDisplay = parent->StyleDisplay();
4155 const auto overflowClipAxes =
4156 parent->ShouldApplyOverflowClipping(parentDisplay);
4157
4158 const bool isPaintingToWindow = aBuilder->IsPaintingToWindow();
4159 const bool doingShortcut =
4160 isPaintingToWindow &&
4161 child->HasAnyStateBits(NS_FRAME_SIMPLE_DISPLAYLIST) &&
4162 // Animations may change the stacking context state.
4163 // ShouldApplyOverflowClipping is affected by the parent style, which does
4164 // not invalidate the NS_FRAME_SIMPLE_DISPLAYLIST bit.
4165 !(overflowClipAxes != PhysicalAxes::None ||
4166 child->MayHaveTransformAnimation() || child->MayHaveOpacityAnimation());
4167
4168 if (aBuilder->IsForPainting()) {
4169 aBuilder->ClearWillChangeBudgetStatus(child);
4170 }
4171
4172 if (StaticPrefs::layout_css_scroll_anchoring_highlight()) {
4173 if (child->FirstContinuation()->IsScrollAnchor()) {
4174 nsRect bounds = child->GetContentRectRelativeToSelf() +
4175 aBuilder->ToReferenceFrame(child);
4176 nsDisplaySolidColor* color = MakeDisplayItem<nsDisplaySolidColor>(
4177 aBuilder, child, bounds, NS_RGBA(255, 0, 255, 64));
4178 if (color) {
4179 color->SetOverrideZIndex(INT32_MAX);
4180 aLists.PositionedDescendants()->AppendToTop(color);
4181 }
4182 }
4183 }
4184
4185 if (doingShortcut) {
4186 BuildDisplayListForSimpleChild(aBuilder, child, aLists);
4187 return;
4188 }
4189
4190 // dirty rect in child-relative coordinates
4191 NS_ASSERTION(aBuilder->GetCurrentFrame() == this, "Wrong coord space!");
4192 const nsPoint offset = child->GetOffsetTo(this);
4193 nsRect visible = aBuilder->GetVisibleRect() - offset;
4194 nsRect dirty = aBuilder->GetDirtyRect() - offset;
4195
4196 nsDisplayListBuilder::OutOfFlowDisplayData* savedOutOfFlowData = nullptr;
4197 if (placeholder) {
4198 if (placeholder->HasAnyStateBits(PLACEHOLDER_FOR_TOPLAYER)) {
4199 // If the out-of-flow frame is in the top layer, the viewport frame
4200 // will paint it. Skip it here. Note that, only out-of-flow frames
4201 // with this property should be skipped, because non-HTML elements
4202 // may stop their children from being out-of-flow. Those frames
4203 // should still be handled in the normal in-flow path.
4204 return;
4205 }
4206
4207 child = childOrOutOfFlow;
4208 if (aBuilder->IsForPainting()) {
4209 aBuilder->ClearWillChangeBudgetStatus(child);
4210 }
4211
4212 // If 'child' is a pushed float then it's owned by a block that's not an
4213 // ancestor of the placeholder, and it will be painted by that block and
4214 // should not be painted through the placeholder. Also recheck
4215 // NS_FRAME_TOO_DEEP_IN_FRAME_TREE and NS_FRAME_IS_NONDISPLAY.
4216 static const nsFrameState skipFlags =
4217 (NS_FRAME_IS_PUSHED_FLOAT | NS_FRAME_TOO_DEEP_IN_FRAME_TREE |
4218 NS_FRAME_IS_NONDISPLAY);
4219 if (child->HasAnyStateBits(skipFlags) || nsLayoutUtils::IsPopup(child)) {
4220 return;
4221 }
4222
4223 MOZ_ASSERT(child->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW));
4224 savedOutOfFlowData = nsDisplayListBuilder::GetOutOfFlowData(child);
4225
4226 if (aBuilder->GetIncludeAllOutOfFlows()) {
4227 visible = child->InkOverflowRect();
4228 dirty = child->InkOverflowRect();
4229 } else if (savedOutOfFlowData) {
4230 visible =
4231 savedOutOfFlowData->GetVisibleRectForFrame(aBuilder, child, &dirty);
4232 } else {
4233 // The out-of-flow frame did not intersect the dirty area. We may still
4234 // need to traverse into it, since it may contain placeholders we need
4235 // to enter to reach other out-of-flow frames that are visible.
4236 visible.SetEmpty();
4237 dirty.SetEmpty();
4238 }
4239 }
4240
4241 NS_ASSERTION(!child->IsPlaceholderFrame(),
4242 "Should have dealt with placeholders already");
4243
4244 if (!DescendIntoChild(aBuilder, child, visible, dirty)) {
4245 DL_LOGV("Skipped frame %p", child);
4246 return;
4247 }
4248
4249 const bool isSVG = child->HasAnyStateBits(NS_FRAME_SVG_LAYOUT);
4250
4251 // This flag is raised if the control flow strays off the common path.
4252 // The common path is the most common one of THE COMMON CASE mentioned later.
4253 bool awayFromCommonPath = !isPaintingToWindow;
4254
4255 // true if this is a real or pseudo stacking context
4256 bool pseudoStackingContext =
4257 aFlags.contains(DisplayChildFlag::ForcePseudoStackingContext);
4258
4259 if (!pseudoStackingContext && !isSVG &&
4260 aFlags.contains(DisplayChildFlag::Inline) &&
4261 !child->IsFrameOfType(eLineParticipant)) {
4262 // child is a non-inline frame in an inline context, i.e.,
4263 // it acts like inline-block or inline-table. Therefore it is a
4264 // pseudo-stacking-context.
4265 pseudoStackingContext = true;
4266 }
4267
4268 const nsStyleDisplay* ourDisp = StyleDisplay();
4269 // REVIEW: Taken from nsBoxFrame::Paint
4270 // Don't paint our children if the theme object is a leaf.
4271 if (IsThemed(ourDisp) && !PresContext()->Theme()->WidgetIsContainer(
4272 ourDisp->EffectiveAppearance()))
4273 return;
4274
4275 // Since we're now sure that we're adding this frame to the display list
4276 // (which means we're painting it, modulo occlusion), mark it as visible
4277 // within the displayport.
4278 if (isPaintingToWindow && child->TrackingVisibility()) {
4279 child->PresShell()->EnsureFrameInApproximatelyVisibleList(child);
4280 awayFromCommonPath = true;
4281 }
4282
4283 // Child is composited if it's transformed, partially transparent, or has
4284 // SVG effects or a blend mode..
4285 const nsStyleDisplay* disp = child->StyleDisplay();
4286 const nsStyleEffects* effects = child->StyleEffects();
4287
4288 const bool isPositioned = disp->IsPositionedStyle();
4289 const bool isStackingContext =
4290 aFlags.contains(DisplayChildFlag::ForceStackingContext) ||
4291 child->IsStackingContext(disp, effects);
4292
4293 if (pseudoStackingContext || isStackingContext || isPositioned ||
4294 placeholder || (!isSVG && disp->IsFloating(child)) ||
4295 (isSVG && effects->mClip.IsRect() && IsSVGContentWithCSSClip(child))) {
4296 pseudoStackingContext = true;
4297 awayFromCommonPath = true;
4298 }
4299
4300 NS_ASSERTION(!isStackingContext || pseudoStackingContext,
4301 "Stacking contexts must also be pseudo-stacking-contexts");
4302
4303 nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(
4304 aBuilder, child, visible, dirty);
4305
4306 UpdateCurrentHitTestInfo(aBuilder, child);
4307
4308 DisplayListClipState::AutoClipMultiple clipState(aBuilder);
4309 nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(aBuilder);
4310
4311 if (savedOutOfFlowData) {
4312 aBuilder->SetBuildingInvisibleItems(false);
4313
4314 clipState.SetClipChainForContainingBlockDescendants(
4315 savedOutOfFlowData->mContainingBlockClipChain);
4316 asrSetter.SetCurrentActiveScrolledRoot(
4317 savedOutOfFlowData->mContainingBlockActiveScrolledRoot);
4318 MOZ_ASSERT(awayFromCommonPath,
4319 "It is impossible when savedOutOfFlowData is true");
4320 } else if (HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) &&
4321 placeholder) {
4322 NS_ASSERTION(visible.IsEmpty(), "should have empty visible rect");
4323 // Every item we build from now until we descent into an out of flow that
4324 // does have saved out of flow data should be invisible. This state gets
4325 // restored when AutoBuildingDisplayList gets out of scope.
4326 aBuilder->SetBuildingInvisibleItems(true);
4327
4328 // If we have nested out-of-flow frames and the outer one isn't visible
4329 // then we won't have stored clip data for it. We can just clear the clip
4330 // instead since we know we won't render anything, and the inner out-of-flow
4331 // frame will setup the correct clip for itself.
4332 clipState.SetClipChainForContainingBlockDescendants(nullptr);
4333 }
4334
4335 // Setup clipping for the parent's overflow:clip,
4336 // or overflow:hidden on elements that don't support scrolling (and therefore
4337 // don't create nsHTML/XULScrollFrame). This clipping needs to not clip
4338 // anything directly rendered by the parent, only the rendering of its
4339 // children.
4340 // Don't use overflowClip to restrict the dirty rect, since some of the
4341 // descendants may not be clipped by it. Even if we end up with unnecessary
4342 // display items, they'll be pruned during ComputeVisibility.
4343 //
4344 // FIXME(emilio): Why can't we handle this more similarly to `clip` (on the
4345 // parent, rather than on the children)? Would ClipContentDescendants do what
4346 // we want?
4347 if (overflowClipAxes != PhysicalAxes::None) {
4348 ApplyOverflowClipping(aBuilder, parent, overflowClipAxes, clipState);
4349 awayFromCommonPath = true;
4350 }
4351
4352 nsDisplayList list(aBuilder);
4353 nsDisplayList extraPositionedDescendants(aBuilder);
4354 const ActiveScrolledRoot* wrapListASR;
4355 bool builtContainerItem = false;
4356 if (isStackingContext) {
4357 // True stacking context.
4358 // For stacking contexts, BuildDisplayListForStackingContext handles
4359 // clipping and MarkAbsoluteFramesForDisplayList.
4360 nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
4361 child->BuildDisplayListForStackingContext(aBuilder, &list,
4362 &builtContainerItem);
4363 wrapListASR = contASRTracker.GetContainerASR();
4364 if (!aBuilder->IsReusingStackingContextItems() &&
4365 aBuilder->GetCaretFrame() == child) {
4366 builtContainerItem = false;
4367 }
4368 } else {
4369 Maybe<nsRect> clipPropClip =
4370 child->GetClipPropClipRect(disp, effects, child->GetSize());
4371 if (clipPropClip) {
4372 aBuilder->IntersectVisibleRect(*clipPropClip);
4373 aBuilder->IntersectDirtyRect(*clipPropClip);
4374 clipState.ClipContentDescendants(*clipPropClip +
4375 aBuilder->ToReferenceFrame(child));
4376 awayFromCommonPath = true;
4377 }
4378
4379 child->MarkAbsoluteFramesForDisplayList(aBuilder);
4380 child->SetBuiltDisplayList(true);
4381
4382 if (!awayFromCommonPath &&
4383 // Some SVG frames might change opacity without invalidating the frame,
4384 // so exclude them from the fast-path.
4385 !child->IsFrameOfType(nsIFrame::eSVG)) {
4386 // The shortcut is available for the child for next time.
4387 child->AddStateBits(NS_FRAME_SIMPLE_DISPLAYLIST);
4388 }
4389
4390 if (!pseudoStackingContext) {
4391 // THIS IS THE COMMON CASE.
4392 // Not a pseudo or real stacking context. Do the simple thing and
4393 // return early.
4394 aBuilder->AdjustWindowDraggingRegion(child);
4395 aBuilder->Check();
4396 child->BuildDisplayList(aBuilder, aLists);
4397 aBuilder->Check();
4398 aBuilder->DisplayCaret(child, aLists.Outlines());
4399 #ifdef DEBUG
4400 DisplayDebugBorders(aBuilder, child, aLists);
4401 #endif
4402 return;
4403 }
4404
4405 // A pseudo-stacking context (e.g., a positioned element with z-index auto).
4406 // We allow positioned descendants of the child to escape to our parent
4407 // stacking context's positioned descendant list, because they might be
4408 // z-index:non-auto
4409 nsDisplayListCollection pseudoStack(aBuilder);
4410
4411 aBuilder->AdjustWindowDraggingRegion(child);
4412 nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
4413 aBuilder->Check();
4414 child->BuildDisplayList(aBuilder, pseudoStack);
4415 aBuilder->Check();
4416 if (aBuilder->DisplayCaret(child, pseudoStack.Outlines())) {
4417 builtContainerItem = false;
4418 }
4419 wrapListASR = contASRTracker.GetContainerASR();
4420
4421 list.AppendToTop(pseudoStack.BorderBackground());
4422 list.AppendToTop(pseudoStack.BlockBorderBackgrounds());
4423 list.AppendToTop(pseudoStack.Floats());
4424 list.AppendToTop(pseudoStack.Content());
4425 list.AppendToTop(pseudoStack.Outlines());
4426 extraPositionedDescendants.AppendToTop(pseudoStack.PositionedDescendants());
4427 #ifdef DEBUG
4428 DisplayDebugBorders(aBuilder, child, aLists);
4429 #endif
4430 }
4431
4432 buildingForChild.RestoreBuildingInvisibleItemsValue();
4433
4434 if (!list.IsEmpty()) {
4435 if (isPositioned || isStackingContext) {
4436 // Genuine stacking contexts, and positioned pseudo-stacking-contexts,
4437 // go in this level.
4438 nsDisplayItem* item = WrapInWrapList(aBuilder, child, &list, wrapListASR,
4439 builtContainerItem);
4440 if (isSVG) {
4441 aLists.Content()->AppendToTop(item);
4442 } else {
4443 aLists.PositionedDescendants()->AppendToTop(item);
4444 }
4445 } else if (!isSVG && disp->IsFloating(child)) {
4446 aLists.Floats()->AppendToTop(
4447 WrapInWrapList(aBuilder, child, &list, wrapListASR));
4448 } else {
4449 aLists.Content()->AppendToTop(&list);
4450 }
4451 }
4452 // We delay placing the positioned descendants of positioned frames to here,
4453 // because in the absence of z-index this is the correct order for them.
4454 // This doesn't affect correctness because the positioned descendants list
4455 // is sorted by z-order and content in BuildDisplayListForStackingContext,
4456 // but it means that sort routine needs to do less work.
4457 aLists.PositionedDescendants()->AppendToTop(&extraPositionedDescendants);
4458 }
4459
MarkAbsoluteFramesForDisplayList(nsDisplayListBuilder * aBuilder)4460 void nsIFrame::MarkAbsoluteFramesForDisplayList(
4461 nsDisplayListBuilder* aBuilder) {
4462 if (IsAbsoluteContainer()) {
4463 aBuilder->MarkFramesForDisplayList(
4464 this, GetAbsoluteContainingBlock()->GetChildList());
4465 }
4466 }
4467
GetContentForEvent(WidgetEvent * aEvent,nsIContent ** aContent)4468 nsresult nsIFrame::GetContentForEvent(WidgetEvent* aEvent,
4469 nsIContent** aContent) {
4470 nsIFrame* f = nsLayoutUtils::GetNonGeneratedAncestor(this);
4471 *aContent = f->GetContent();
4472 NS_IF_ADDREF(*aContent);
4473 return NS_OK;
4474 }
4475
FireDOMEvent(const nsAString & aDOMEventName,nsIContent * aContent)4476 void nsIFrame::FireDOMEvent(const nsAString& aDOMEventName,
4477 nsIContent* aContent) {
4478 nsIContent* target = aContent ? aContent : GetContent();
4479
4480 if (target) {
4481 RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(
4482 target, aDOMEventName, CanBubble::eYes, ChromeOnlyDispatch::eNo);
4483 DebugOnly<nsresult> rv = asyncDispatcher->PostDOMEvent();
4484 NS_ASSERTION(NS_SUCCEEDED(rv), "AsyncEventDispatcher failed to dispatch");
4485 }
4486 }
4487
HandleEvent(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus)4488 nsresult nsIFrame::HandleEvent(nsPresContext* aPresContext,
4489 WidgetGUIEvent* aEvent,
4490 nsEventStatus* aEventStatus) {
4491 if (aEvent->mMessage == eMouseMove) {
4492 // XXX If the second argument of HandleDrag() is WidgetMouseEvent,
4493 // the implementation becomes simpler.
4494 return HandleDrag(aPresContext, aEvent, aEventStatus);
4495 }
4496
4497 if ((aEvent->mClass == eMouseEventClass &&
4498 aEvent->AsMouseEvent()->mButton == MouseButton::ePrimary) ||
4499 aEvent->mClass == eTouchEventClass) {
4500 if (aEvent->mMessage == eMouseDown || aEvent->mMessage == eTouchStart) {
4501 HandlePress(aPresContext, aEvent, aEventStatus);
4502 } else if (aEvent->mMessage == eMouseUp || aEvent->mMessage == eTouchEnd) {
4503 HandleRelease(aPresContext, aEvent, aEventStatus);
4504 }
4505 return NS_OK;
4506 }
4507
4508 // When middle button is down, we need to just move selection and focus at
4509 // the clicked point. Note that even if middle click paste is not enabled,
4510 // Chrome moves selection at middle mouse button down. So, we should follow
4511 // the behavior for the compatibility.
4512 if (aEvent->mMessage == eMouseDown) {
4513 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
4514 if (mouseEvent && mouseEvent->mButton == MouseButton::eMiddle) {
4515 if (*aEventStatus == nsEventStatus_eConsumeNoDefault) {
4516 return NS_OK;
4517 }
4518 return MoveCaretToEventPoint(aPresContext, mouseEvent, aEventStatus);
4519 }
4520 }
4521
4522 return NS_OK;
4523 }
4524
GetDataForTableSelection(const nsFrameSelection * aFrameSelection,mozilla::PresShell * aPresShell,WidgetMouseEvent * aMouseEvent,nsIContent ** aParentContent,int32_t * aContentOffset,TableSelectionMode * aTarget)4525 nsresult nsIFrame::GetDataForTableSelection(
4526 const nsFrameSelection* aFrameSelection, mozilla::PresShell* aPresShell,
4527 WidgetMouseEvent* aMouseEvent, nsIContent** aParentContent,
4528 int32_t* aContentOffset, TableSelectionMode* aTarget) {
4529 if (!aFrameSelection || !aPresShell || !aMouseEvent || !aParentContent ||
4530 !aContentOffset || !aTarget)
4531 return NS_ERROR_NULL_POINTER;
4532
4533 *aParentContent = nullptr;
4534 *aContentOffset = 0;
4535 *aTarget = TableSelectionMode::None;
4536
4537 int16_t displaySelection = aPresShell->GetSelectionFlags();
4538
4539 bool selectingTableCells = aFrameSelection->IsInTableSelectionMode();
4540
4541 // DISPLAY_ALL means we're in an editor.
4542 // If already in cell selection mode,
4543 // continue selecting with mouse drag or end on mouse up,
4544 // or when using shift key to extend block of cells
4545 // (Mouse down does normal selection unless Ctrl/Cmd is pressed)
4546 bool doTableSelection =
4547 displaySelection == nsISelectionDisplay::DISPLAY_ALL &&
4548 selectingTableCells &&
4549 (aMouseEvent->mMessage == eMouseMove ||
4550 (aMouseEvent->mMessage == eMouseUp &&
4551 aMouseEvent->mButton == MouseButton::ePrimary) ||
4552 aMouseEvent->IsShift());
4553
4554 if (!doTableSelection) {
4555 // In Browser, special 'table selection' key must be pressed for table
4556 // selection or when just Shift is pressed and we're already in table/cell
4557 // selection mode
4558 #ifdef XP_MACOSX
4559 doTableSelection = aMouseEvent->IsMeta() ||
4560 (aMouseEvent->IsShift() && selectingTableCells);
4561 #else
4562 doTableSelection = aMouseEvent->IsControl() ||
4563 (aMouseEvent->IsShift() && selectingTableCells);
4564 #endif
4565 }
4566 if (!doTableSelection) return NS_OK;
4567
4568 // Get the cell frame or table frame (or parent) of the current content node
4569 nsIFrame* frame = this;
4570 bool foundCell = false;
4571 bool foundTable = false;
4572
4573 // Get the limiting node to stop parent frame search
4574 nsIContent* limiter = aFrameSelection->GetLimiter();
4575
4576 // If our content node is an ancestor of the limiting node,
4577 // we should stop the search right now.
4578 if (limiter && limiter->IsInclusiveDescendantOf(GetContent())) return NS_OK;
4579
4580 // We don't initiate row/col selection from here now,
4581 // but we may in future
4582 // bool selectColumn = false;
4583 // bool selectRow = false;
4584
4585 while (frame) {
4586 // Check for a table cell by querying to a known CellFrame interface
4587 nsITableCellLayout* cellElement = do_QueryFrame(frame);
4588 if (cellElement) {
4589 foundCell = true;
4590 // TODO: If we want to use proximity to top or left border
4591 // for row and column selection, this is the place to do it
4592 break;
4593 } else {
4594 // If not a cell, check for table
4595 // This will happen when starting frame is the table or child of a table,
4596 // such as a row (we were inbetween cells or in table border)
4597 nsTableWrapperFrame* tableFrame = do_QueryFrame(frame);
4598 if (tableFrame) {
4599 foundTable = true;
4600 // TODO: How can we select row when along left table edge
4601 // or select column when along top edge?
4602 break;
4603 } else {
4604 frame = frame->GetParent();
4605 // Stop if we have hit the selection's limiting content node
4606 if (frame && frame->GetContent() == limiter) break;
4607 }
4608 }
4609 }
4610 // We aren't in a cell or table
4611 if (!foundCell && !foundTable) return NS_OK;
4612
4613 nsIContent* tableOrCellContent = frame->GetContent();
4614 if (!tableOrCellContent) return NS_ERROR_FAILURE;
4615
4616 nsCOMPtr<nsIContent> parentContent = tableOrCellContent->GetParent();
4617 if (!parentContent) return NS_ERROR_FAILURE;
4618
4619 const int32_t offset =
4620 parentContent->ComputeIndexOf_Deprecated(tableOrCellContent);
4621 // Not likely?
4622 if (offset < 0) {
4623 return NS_ERROR_FAILURE;
4624 }
4625
4626 // Everything is OK -- set the return values
4627 parentContent.forget(aParentContent);
4628
4629 *aContentOffset = offset;
4630
4631 #if 0
4632 if (selectRow)
4633 *aTarget = TableSelectionMode::Row;
4634 else if (selectColumn)
4635 *aTarget = TableSelectionMode::Column;
4636 else
4637 #endif
4638 if (foundCell) {
4639 *aTarget = TableSelectionMode::Cell;
4640 } else if (foundTable) {
4641 *aTarget = TableSelectionMode::Table;
4642 }
4643
4644 return NS_OK;
4645 }
4646
IsEditingHost(const nsIFrame * aFrame)4647 static bool IsEditingHost(const nsIFrame* aFrame) {
4648 auto* element = nsGenericHTMLElement::FromNodeOrNull(aFrame->GetContent());
4649 return element && element->IsEditableRoot();
4650 }
4651
IsTopmostModalDialog(const nsIFrame * aFrame)4652 static bool IsTopmostModalDialog(const nsIFrame* aFrame) {
4653 auto* element = Element::FromNodeOrNull(aFrame->GetContent());
4654 return element &&
4655 element->State().HasState(NS_EVENT_STATE_TOPMOST_MODAL_DIALOG);
4656 }
4657
UsedUserSelect(const nsIFrame * aFrame)4658 static StyleUserSelect UsedUserSelect(const nsIFrame* aFrame) {
4659 if (aFrame->IsGeneratedContentFrame()) {
4660 return StyleUserSelect::None;
4661 }
4662
4663 // Per https://drafts.csswg.org/css-ui-4/#content-selection:
4664 //
4665 // The computed value is the specified value, except:
4666 //
4667 // 1 - on editable elements where the computed value is always 'contain'
4668 // regardless of the specified value.
4669 // 2 - when the specified value is auto, which computes to one of the other
4670 // values [...]
4671 //
4672 // See https://github.com/w3c/csswg-drafts/issues/3344 to see why we do this
4673 // at used-value time instead of at computed-value time.
4674 //
4675 // Also, we check for auto first to allow explicitly overriding the value for
4676 // the editing host.
4677 auto style = aFrame->Style()->UserSelect();
4678 if (style != StyleUserSelect::Auto) {
4679 return style;
4680 }
4681
4682 if (aFrame->IsTextInputFrame() || IsEditingHost(aFrame) ||
4683 IsTopmostModalDialog(aFrame)) {
4684 // We don't implement 'contain' itself, but we make 'text' behave as
4685 // 'contain' for contenteditable and <input> / <textarea> elements anyway so
4686 // this is ok.
4687 //
4688 // Topmost modal dialogs need to behave like `text` too, because they're
4689 // supposed to be selectable even if their ancestors are inert.
4690 return StyleUserSelect::Text;
4691 }
4692
4693 auto* parent = nsLayoutUtils::GetParentOrPlaceholderFor(aFrame);
4694 return parent ? UsedUserSelect(parent) : StyleUserSelect::Text;
4695 }
4696
IsSelectable(StyleUserSelect * aSelectStyle) const4697 bool nsIFrame::IsSelectable(StyleUserSelect* aSelectStyle) const {
4698 auto style = UsedUserSelect(this);
4699 if (aSelectStyle) {
4700 *aSelectStyle = style;
4701 }
4702 return style != StyleUserSelect::None;
4703 }
4704
ShouldHaveLineIfEmpty() const4705 bool nsIFrame::ShouldHaveLineIfEmpty() const {
4706 if (Style()->IsPseudoOrAnonBox() &&
4707 Style()->GetPseudoType() != PseudoStyleType::scrolledContent) {
4708 return false;
4709 }
4710 return IsEditingHost(this);
4711 }
4712
4713 /**
4714 * Handles the Mouse Press Event for the frame
4715 */
4716 NS_IMETHODIMP
HandlePress(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus)4717 nsIFrame::HandlePress(nsPresContext* aPresContext, WidgetGUIEvent* aEvent,
4718 nsEventStatus* aEventStatus) {
4719 NS_ENSURE_ARG_POINTER(aEventStatus);
4720 if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
4721 return NS_OK;
4722 }
4723
4724 NS_ENSURE_ARG_POINTER(aEvent);
4725 if (aEvent->mClass == eTouchEventClass) {
4726 return NS_OK;
4727 }
4728
4729 return MoveCaretToEventPoint(aPresContext, aEvent->AsMouseEvent(),
4730 aEventStatus);
4731 }
4732
MoveCaretToEventPoint(nsPresContext * aPresContext,WidgetMouseEvent * aMouseEvent,nsEventStatus * aEventStatus)4733 nsresult nsIFrame::MoveCaretToEventPoint(nsPresContext* aPresContext,
4734 WidgetMouseEvent* aMouseEvent,
4735 nsEventStatus* aEventStatus) {
4736 MOZ_ASSERT(aPresContext);
4737 MOZ_ASSERT(aMouseEvent);
4738 MOZ_ASSERT(aMouseEvent->mMessage == eMouseDown);
4739 MOZ_ASSERT(aMouseEvent->mButton == MouseButton::ePrimary ||
4740 aMouseEvent->mButton == MouseButton::eMiddle);
4741 MOZ_ASSERT(aEventStatus);
4742 MOZ_ASSERT(nsEventStatus_eConsumeNoDefault != *aEventStatus);
4743
4744 mozilla::PresShell* presShell = aPresContext->GetPresShell();
4745 if (!presShell) {
4746 return NS_ERROR_FAILURE;
4747 }
4748
4749 // We often get out of sync state issues with mousedown events that
4750 // get interrupted by alerts/dialogs.
4751 // Check with the ESM to see if we should process this one
4752 if (!aPresContext->EventStateManager()->EventStatusOK(aMouseEvent)) {
4753 return NS_OK;
4754 }
4755
4756 const nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(
4757 aMouseEvent, RelativeTo{this});
4758
4759 // When not using `alt`, and clicking on a draggable, but non-editable
4760 // element, don't do anything, and let d&d handle the event.
4761 //
4762 // See bug 48876, bug 388659 and bug 55921 for context here.
4763 //
4764 // FIXME(emilio): The .Contains(pt) check looks a bit fishy. When would it be
4765 // false given we're the event target? If it is needed, why not checking the
4766 // actual draggable node rect instead?
4767 if (!aMouseEvent->IsAlt() && GetRectRelativeToSelf().Contains(pt)) {
4768 for (nsIContent* content = mContent; content;
4769 content = content->GetFlattenedTreeParent()) {
4770 if (nsContentUtils::ContentIsDraggable(content) &&
4771 !content->IsEditable()) {
4772 return NS_OK;
4773 }
4774 }
4775 }
4776
4777 // If we are in Navigator and the click is in a draggable node, we don't want
4778 // to start selection because we don't want to interfere with a potential
4779 // drag of said node and steal all its glory.
4780 const bool isEditor =
4781 presShell->GetSelectionFlags() == nsISelectionDisplay::DISPLAY_ALL;
4782
4783 // Don't do something if it's middle button down event.
4784 const bool isPrimaryButtonDown =
4785 aMouseEvent->mButton == MouseButton::ePrimary;
4786
4787 // check whether style allows selection
4788 // if not, don't tell selection the mouse event even occurred.
4789 StyleUserSelect selectStyle;
4790 // check for select: none
4791 if (!IsSelectable(&selectStyle)) {
4792 return NS_OK;
4793 }
4794
4795 if (isPrimaryButtonDown) {
4796 // If the mouse is dragged outside the nearest enclosing scrollable area
4797 // while making a selection, the area will be scrolled. To do this, capture
4798 // the mouse on the nearest scrollable frame. If there isn't a scrollable
4799 // frame, or something else is already capturing the mouse, there's no
4800 // reason to capture.
4801 if (!PresShell::GetCapturingContent()) {
4802 nsIScrollableFrame* scrollFrame =
4803 nsLayoutUtils::GetNearestScrollableFrame(
4804 this, nsLayoutUtils::SCROLLABLE_SAME_DOC |
4805 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
4806 if (scrollFrame) {
4807 nsIFrame* capturingFrame = do_QueryFrame(scrollFrame);
4808 PresShell::SetCapturingContent(capturingFrame->GetContent(),
4809 CaptureFlags::IgnoreAllowedState);
4810 }
4811 }
4812 }
4813
4814 // XXX This is screwy; it really should use the selection frame, not the
4815 // event frame
4816 const nsFrameSelection* frameselection =
4817 selectStyle == StyleUserSelect::Text ? GetConstFrameSelection()
4818 : presShell->ConstFrameSelection();
4819
4820 if (!frameselection || frameselection->GetDisplaySelection() ==
4821 nsISelectionController::SELECTION_OFF) {
4822 return NS_OK; // nothing to do we cannot affect selection from here
4823 }
4824
4825 #ifdef XP_MACOSX
4826 // If Control key is pressed on macOS, it should be treated as right click.
4827 // So, don't change selection.
4828 if (aMouseEvent->IsControl()) {
4829 return NS_OK;
4830 }
4831 const bool control = aMouseEvent->IsMeta();
4832 #else
4833 const bool control = aMouseEvent->IsControl();
4834 #endif
4835
4836 RefPtr<nsFrameSelection> fc = const_cast<nsFrameSelection*>(frameselection);
4837 if (isPrimaryButtonDown && aMouseEvent->mClickCount > 1) {
4838 // These methods aren't const but can't actually delete anything,
4839 // so no need for AutoWeakFrame.
4840 fc->SetDragState(true);
4841 return HandleMultiplePress(aPresContext, aMouseEvent, aEventStatus,
4842 control);
4843 }
4844
4845 ContentOffsets offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN);
4846
4847 if (!offsets.content) {
4848 return NS_ERROR_FAILURE;
4849 }
4850
4851 if (aMouseEvent->mMessage == eMouseDown &&
4852 aMouseEvent->mButton == MouseButton::eMiddle &&
4853 !offsets.content->IsEditable()) {
4854 // However, some users don't like the Chrome compatible behavior of
4855 // middle mouse click. They want to keep selection after starting
4856 // autoscroll. However, the selection change is important for middle
4857 // mouse past. Therefore, we should allow users to take the traditional
4858 // behavior back by themselves unless middle click paste is enabled or
4859 // autoscrolling is disabled.
4860 if (!Preferences::GetBool("middlemouse.paste", false) &&
4861 Preferences::GetBool("general.autoScroll", false) &&
4862 Preferences::GetBool("general.autoscroll.prevent_to_collapse_selection_"
4863 "by_middle_mouse_down",
4864 false)) {
4865 return NS_OK;
4866 }
4867 }
4868
4869 if (isPrimaryButtonDown) {
4870 // Let Ctrl/Cmd + left mouse down do table selection instead of drag
4871 // initiation.
4872 nsCOMPtr<nsIContent> parentContent;
4873 int32_t contentOffset;
4874 TableSelectionMode target;
4875 nsresult rv = GetDataForTableSelection(
4876 frameselection, presShell, aMouseEvent, getter_AddRefs(parentContent),
4877 &contentOffset, &target);
4878 if (NS_SUCCEEDED(rv) && parentContent) {
4879 fc->SetDragState(true);
4880 return fc->HandleTableSelection(parentContent, contentOffset, target,
4881 aMouseEvent);
4882 }
4883 }
4884
4885 fc->SetDelayedCaretData(0);
4886
4887 if (isPrimaryButtonDown) {
4888 // Check if any part of this frame is selected, and if the user clicked
4889 // inside the selected region, and if it's the left button. If so, we delay
4890 // starting a new selection since the user may be trying to drag the
4891 // selected region to some other app.
4892
4893 if (GetContent() && GetContent()->IsMaybeSelected()) {
4894 bool inSelection = false;
4895 UniquePtr<SelectionDetails> details = frameselection->LookUpSelection(
4896 offsets.content, 0, offsets.EndOffset(), false);
4897
4898 //
4899 // If there are any details, check to see if the user clicked
4900 // within any selected region of the frame.
4901 //
4902
4903 for (SelectionDetails* curDetail = details.get(); curDetail;
4904 curDetail = curDetail->mNext.get()) {
4905 //
4906 // If the user clicked inside a selection, then just
4907 // return without doing anything. We will handle placing
4908 // the caret later on when the mouse is released. We ignore
4909 // the spellcheck, find and url formatting selections.
4910 //
4911 if (curDetail->mSelectionType != SelectionType::eSpellCheck &&
4912 curDetail->mSelectionType != SelectionType::eFind &&
4913 curDetail->mSelectionType != SelectionType::eURLSecondary &&
4914 curDetail->mSelectionType != SelectionType::eURLStrikeout &&
4915 curDetail->mStart <= offsets.StartOffset() &&
4916 offsets.EndOffset() <= curDetail->mEnd) {
4917 inSelection = true;
4918 }
4919 }
4920
4921 if (inSelection) {
4922 fc->SetDragState(false);
4923 fc->SetDelayedCaretData(aMouseEvent);
4924 return NS_OK;
4925 }
4926 }
4927
4928 fc->SetDragState(true);
4929 }
4930
4931 // Do not touch any nsFrame members after this point without adding
4932 // weakFrame checks.
4933 const nsFrameSelection::FocusMode focusMode = [&]() {
4934 // If "Shift" and "Ctrl" are both pressed, "Shift" is given precedence. This
4935 // mimics the old behaviour.
4936 if (aMouseEvent->IsShift()) {
4937 // If clicked in a link when focused content is editable, we should
4938 // collapse selection in the link for compatibility with Blink.
4939 if (isEditor) {
4940 nsCOMPtr<nsIURI> uri;
4941 for (Element* element : mContent->InclusiveAncestorsOfType<Element>()) {
4942 if (element->IsLink(getter_AddRefs(uri))) {
4943 return nsFrameSelection::FocusMode::kCollapseToNewPoint;
4944 }
4945 }
4946 }
4947 return nsFrameSelection::FocusMode::kExtendSelection;
4948 }
4949
4950 if (isPrimaryButtonDown && control) {
4951 return nsFrameSelection::FocusMode::kMultiRangeSelection;
4952 }
4953
4954 return nsFrameSelection::FocusMode::kCollapseToNewPoint;
4955 }();
4956
4957 nsresult rv = fc->HandleClick(
4958 MOZ_KnownLive(offsets.content) /* bug 1636889 */, offsets.StartOffset(),
4959 offsets.EndOffset(), focusMode, offsets.associate);
4960 if (NS_FAILED(rv)) {
4961 return rv;
4962 }
4963
4964 // We don't handle mouse button up if it's middle button.
4965 if (isPrimaryButtonDown && offsets.offset != offsets.secondaryOffset) {
4966 fc->MaintainSelection();
4967 }
4968
4969 if (isPrimaryButtonDown && isEditor && !aMouseEvent->IsShift() &&
4970 (offsets.EndOffset() - offsets.StartOffset()) == 1) {
4971 // A single node is selected and we aren't extending an existing selection,
4972 // which means the user clicked directly on an object (either
4973 // `user-select: all` or a non-text node without children). Therefore,
4974 // disable selection extension during mouse moves.
4975 // XXX This is a bit hacky; shouldn't editor be able to deal with this?
4976 fc->SetDragState(false);
4977 }
4978
4979 return NS_OK;
4980 }
4981
SelectByTypeAtPoint(nsPresContext * aPresContext,const nsPoint & aPoint,nsSelectionAmount aBeginAmountType,nsSelectionAmount aEndAmountType,uint32_t aSelectFlags)4982 nsresult nsIFrame::SelectByTypeAtPoint(nsPresContext* aPresContext,
4983 const nsPoint& aPoint,
4984 nsSelectionAmount aBeginAmountType,
4985 nsSelectionAmount aEndAmountType,
4986 uint32_t aSelectFlags) {
4987 NS_ENSURE_ARG_POINTER(aPresContext);
4988
4989 // No point in selecting if selection is turned off
4990 if (DetermineDisplaySelection() == nsISelectionController::SELECTION_OFF) {
4991 return NS_OK;
4992 }
4993
4994 ContentOffsets offsets = GetContentOffsetsFromPoint(aPoint, SKIP_HIDDEN);
4995 if (!offsets.content) {
4996 return NS_ERROR_FAILURE;
4997 }
4998
4999 int32_t offset;
5000 nsIFrame* frame = nsFrameSelection::GetFrameForNodeOffset(
5001 offsets.content, offsets.offset, offsets.associate, &offset);
5002 if (!frame) {
5003 return NS_ERROR_FAILURE;
5004 }
5005 return frame->PeekBackwardAndForward(aBeginAmountType, aEndAmountType, offset,
5006 aBeginAmountType != eSelectWord,
5007 aSelectFlags);
5008 }
5009
5010 /**
5011 * Multiple Mouse Press -- line or paragraph selection -- for the frame.
5012 * Wouldn't it be nice if this didn't have to be hardwired into Frame code?
5013 */
5014 NS_IMETHODIMP
HandleMultiplePress(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus,bool aControlHeld)5015 nsIFrame::HandleMultiplePress(nsPresContext* aPresContext,
5016 WidgetGUIEvent* aEvent,
5017 nsEventStatus* aEventStatus, bool aControlHeld) {
5018 NS_ENSURE_ARG_POINTER(aEvent);
5019 NS_ENSURE_ARG_POINTER(aEventStatus);
5020
5021 if (nsEventStatus_eConsumeNoDefault == *aEventStatus ||
5022 DetermineDisplaySelection() == nsISelectionController::SELECTION_OFF) {
5023 return NS_OK;
5024 }
5025
5026 // Find out whether we're doing line or paragraph selection.
5027 // If browser.triple_click_selects_paragraph is true, triple-click selects
5028 // paragraph. Otherwise, triple-click selects line, and quadruple-click
5029 // selects paragraph (on platforms that support quadruple-click).
5030 nsSelectionAmount beginAmount, endAmount;
5031 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
5032 if (!mouseEvent) {
5033 return NS_OK;
5034 }
5035
5036 if (mouseEvent->mClickCount == 4) {
5037 beginAmount = endAmount = eSelectParagraph;
5038 } else if (mouseEvent->mClickCount == 3) {
5039 if (Preferences::GetBool("browser.triple_click_selects_paragraph")) {
5040 beginAmount = endAmount = eSelectParagraph;
5041 } else {
5042 beginAmount = eSelectBeginLine;
5043 endAmount = eSelectEndLine;
5044 }
5045 } else if (mouseEvent->mClickCount == 2) {
5046 // We only want inline frames; PeekBackwardAndForward dislikes blocks
5047 beginAmount = endAmount = eSelectWord;
5048 } else {
5049 return NS_OK;
5050 }
5051
5052 nsPoint relPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(
5053 mouseEvent, RelativeTo{this});
5054 return SelectByTypeAtPoint(aPresContext, relPoint, beginAmount, endAmount,
5055 (aControlHeld ? SELECT_ACCUMULATE : 0));
5056 }
5057
PeekBackwardAndForward(nsSelectionAmount aAmountBack,nsSelectionAmount aAmountForward,int32_t aStartPos,bool aJumpLines,uint32_t aSelectFlags)5058 nsresult nsIFrame::PeekBackwardAndForward(nsSelectionAmount aAmountBack,
5059 nsSelectionAmount aAmountForward,
5060 int32_t aStartPos, bool aJumpLines,
5061 uint32_t aSelectFlags) {
5062 nsIFrame* baseFrame = this;
5063 int32_t baseOffset = aStartPos;
5064 nsresult rv;
5065
5066 if (aAmountBack == eSelectWord) {
5067 // To avoid selecting the previous word when at start of word,
5068 // first move one character forward.
5069 nsPeekOffsetStruct pos(eSelectCharacter, eDirNext, aStartPos, nsPoint(0, 0),
5070 aJumpLines,
5071 true, // limit on scrolled views
5072 false, false, false);
5073 rv = PeekOffset(&pos);
5074 if (NS_SUCCEEDED(rv)) {
5075 baseFrame = pos.mResultFrame;
5076 baseOffset = pos.mContentOffset;
5077 }
5078 }
5079
5080 // Search backward for a boundary.
5081 nsPeekOffsetStruct startpos(aAmountBack, eDirPrevious, baseOffset,
5082 nsPoint(0, 0), aJumpLines,
5083 true, // limit on scrolled views
5084 false, false, false);
5085 rv = baseFrame->PeekOffset(&startpos);
5086 if (NS_FAILED(rv)) {
5087 return rv;
5088 }
5089
5090 // If the backward search stayed within the same frame, search forward from
5091 // that position for the end boundary; but if it crossed out to a sibling or
5092 // ancestor, start from the original position.
5093 if (startpos.mResultFrame == baseFrame) {
5094 baseOffset = startpos.mContentOffset;
5095 } else {
5096 baseFrame = this;
5097 baseOffset = aStartPos;
5098 }
5099
5100 nsPeekOffsetStruct endpos(aAmountForward, eDirNext, baseOffset, nsPoint(0, 0),
5101 aJumpLines,
5102 true, // limit on scrolled views
5103 false, false, false);
5104 rv = baseFrame->PeekOffset(&endpos);
5105 if (NS_FAILED(rv)) {
5106 return rv;
5107 }
5108
5109 // Keep frameSelection alive.
5110 RefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
5111
5112 const nsFrameSelection::FocusMode focusMode =
5113 (aSelectFlags & SELECT_ACCUMULATE)
5114 ? nsFrameSelection::FocusMode::kMultiRangeSelection
5115 : nsFrameSelection::FocusMode::kCollapseToNewPoint;
5116 rv = frameSelection->HandleClick(
5117 MOZ_KnownLive(startpos.mResultContent) /* bug 1636889 */,
5118 startpos.mContentOffset, startpos.mContentOffset, focusMode,
5119 CARET_ASSOCIATE_AFTER);
5120 if (NS_FAILED(rv)) {
5121 return rv;
5122 }
5123
5124 rv = frameSelection->HandleClick(
5125 MOZ_KnownLive(endpos.mResultContent) /* bug 1636889 */,
5126 endpos.mContentOffset, endpos.mContentOffset,
5127 nsFrameSelection::FocusMode::kExtendSelection, CARET_ASSOCIATE_BEFORE);
5128 if (NS_FAILED(rv)) {
5129 return rv;
5130 }
5131
5132 // maintain selection
5133 return frameSelection->MaintainSelection(aAmountBack);
5134 }
5135
HandleDrag(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus)5136 NS_IMETHODIMP nsIFrame::HandleDrag(nsPresContext* aPresContext,
5137 WidgetGUIEvent* aEvent,
5138 nsEventStatus* aEventStatus) {
5139 MOZ_ASSERT(aEvent->mClass == eMouseEventClass,
5140 "HandleDrag can only handle mouse event");
5141
5142 NS_ENSURE_ARG_POINTER(aEventStatus);
5143
5144 RefPtr<nsFrameSelection> frameselection = GetFrameSelection();
5145 if (!frameselection) {
5146 return NS_OK;
5147 }
5148
5149 bool mouseDown = frameselection->GetDragState();
5150 if (!mouseDown) {
5151 return NS_OK;
5152 }
5153
5154 nsIFrame* scrollbar =
5155 nsLayoutUtils::GetClosestFrameOfType(this, LayoutFrameType::Scrollbar);
5156 if (!scrollbar) {
5157 // XXX Do we really need to exclude non-selectable content here?
5158 // GetContentOffsetsFromPoint can handle it just fine, although some
5159 // other stuff might not like it.
5160 // NOTE: DetermineDisplaySelection() returns SELECTION_OFF for
5161 // non-selectable frames.
5162 if (DetermineDisplaySelection() == nsISelectionController::SELECTION_OFF) {
5163 return NS_OK;
5164 }
5165 }
5166
5167 frameselection->StopAutoScrollTimer();
5168
5169 // Check if we are dragging in a table cell
5170 nsCOMPtr<nsIContent> parentContent;
5171 int32_t contentOffset;
5172 TableSelectionMode target;
5173 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
5174 mozilla::PresShell* presShell = aPresContext->PresShell();
5175 nsresult result;
5176 result = GetDataForTableSelection(frameselection, presShell, mouseEvent,
5177 getter_AddRefs(parentContent),
5178 &contentOffset, &target);
5179
5180 AutoWeakFrame weakThis = this;
5181 if (NS_SUCCEEDED(result) && parentContent) {
5182 result = frameselection->HandleTableSelection(parentContent, contentOffset,
5183 target, mouseEvent);
5184 if (NS_WARN_IF(NS_FAILED(result))) {
5185 return result;
5186 }
5187 } else {
5188 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent,
5189 RelativeTo{this});
5190 frameselection->HandleDrag(this, pt);
5191 }
5192
5193 // The frameselection object notifies selection listeners synchronously above
5194 // which might have killed us.
5195 if (!weakThis.IsAlive()) {
5196 return NS_OK;
5197 }
5198
5199 // get the nearest scrollframe
5200 nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetNearestScrollableFrame(
5201 this, nsLayoutUtils::SCROLLABLE_SAME_DOC |
5202 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
5203
5204 if (scrollFrame) {
5205 nsIFrame* capturingFrame = scrollFrame->GetScrolledFrame();
5206 if (capturingFrame) {
5207 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(
5208 mouseEvent, RelativeTo{capturingFrame});
5209 frameselection->StartAutoScrollTimer(capturingFrame, pt, 30);
5210 }
5211 }
5212
5213 return NS_OK;
5214 }
5215
5216 /**
5217 * This static method handles part of the nsIFrame::HandleRelease in a way
5218 * which doesn't rely on the nsFrame object to stay alive.
5219 */
HandleFrameSelection(nsFrameSelection * aFrameSelection,nsIFrame::ContentOffsets & aOffsets,bool aHandleTableSel,int32_t aContentOffsetForTableSel,TableSelectionMode aTargetForTableSel,nsIContent * aParentContentForTableSel,WidgetGUIEvent * aEvent,const nsEventStatus * aEventStatus)5220 MOZ_CAN_RUN_SCRIPT_BOUNDARY static nsresult HandleFrameSelection(
5221 nsFrameSelection* aFrameSelection, nsIFrame::ContentOffsets& aOffsets,
5222 bool aHandleTableSel, int32_t aContentOffsetForTableSel,
5223 TableSelectionMode aTargetForTableSel,
5224 nsIContent* aParentContentForTableSel, WidgetGUIEvent* aEvent,
5225 const nsEventStatus* aEventStatus) {
5226 if (!aFrameSelection) {
5227 return NS_OK;
5228 }
5229
5230 nsresult rv = NS_OK;
5231
5232 if (nsEventStatus_eConsumeNoDefault != *aEventStatus) {
5233 if (!aHandleTableSel) {
5234 if (!aOffsets.content || !aFrameSelection->HasDelayedCaretData()) {
5235 return NS_ERROR_FAILURE;
5236 }
5237
5238 // We are doing this to simulate what we would have done on HandlePress.
5239 // We didn't do it there to give the user an opportunity to drag
5240 // the text, but since they didn't drag, we want to place the
5241 // caret.
5242 // However, we'll use the mouse position from the release, since:
5243 // * it's easier
5244 // * that's the normal click position to use (although really, in
5245 // the normal case, small movements that don't count as a drag
5246 // can do selection)
5247 aFrameSelection->SetDragState(true);
5248
5249 const nsFrameSelection::FocusMode focusMode =
5250 aFrameSelection->IsShiftDownInDelayedCaretData()
5251 ? nsFrameSelection::FocusMode::kExtendSelection
5252 : nsFrameSelection::FocusMode::kCollapseToNewPoint;
5253 rv = aFrameSelection->HandleClick(
5254 MOZ_KnownLive(aOffsets.content) /* bug 1636889 */,
5255 aOffsets.StartOffset(), aOffsets.EndOffset(), focusMode,
5256 aOffsets.associate);
5257 if (NS_FAILED(rv)) {
5258 return rv;
5259 }
5260 } else if (aParentContentForTableSel) {
5261 aFrameSelection->SetDragState(false);
5262 rv = aFrameSelection->HandleTableSelection(
5263 aParentContentForTableSel, aContentOffsetForTableSel,
5264 aTargetForTableSel, aEvent->AsMouseEvent());
5265 if (NS_FAILED(rv)) {
5266 return rv;
5267 }
5268 }
5269 aFrameSelection->SetDelayedCaretData(0);
5270 }
5271
5272 aFrameSelection->SetDragState(false);
5273 aFrameSelection->StopAutoScrollTimer();
5274
5275 return NS_OK;
5276 }
5277
HandleRelease(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus)5278 NS_IMETHODIMP nsIFrame::HandleRelease(nsPresContext* aPresContext,
5279 WidgetGUIEvent* aEvent,
5280 nsEventStatus* aEventStatus) {
5281 if (aEvent->mClass != eMouseEventClass) {
5282 return NS_OK;
5283 }
5284
5285 nsIFrame* activeFrame = GetActiveSelectionFrame(aPresContext, this);
5286
5287 nsCOMPtr<nsIContent> captureContent = PresShell::GetCapturingContent();
5288
5289 bool selectionOff =
5290 (DetermineDisplaySelection() == nsISelectionController::SELECTION_OFF);
5291
5292 RefPtr<nsFrameSelection> frameselection;
5293 ContentOffsets offsets;
5294 nsCOMPtr<nsIContent> parentContent;
5295 int32_t contentOffsetForTableSel = 0;
5296 TableSelectionMode targetForTableSel = TableSelectionMode::None;
5297 bool handleTableSelection = true;
5298
5299 if (!selectionOff) {
5300 frameselection = GetFrameSelection();
5301 if (nsEventStatus_eConsumeNoDefault != *aEventStatus && frameselection) {
5302 // Check if the frameselection recorded the mouse going down.
5303 // If not, the user must have clicked in a part of the selection.
5304 // Place the caret before continuing!
5305
5306 if (frameselection->MouseDownRecorded()) {
5307 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(
5308 aEvent, RelativeTo{this});
5309 offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN);
5310 handleTableSelection = false;
5311 } else {
5312 GetDataForTableSelection(frameselection, PresShell(),
5313 aEvent->AsMouseEvent(),
5314 getter_AddRefs(parentContent),
5315 &contentOffsetForTableSel, &targetForTableSel);
5316 }
5317 }
5318 }
5319
5320 // We might be capturing in some other document and the event just happened to
5321 // trickle down here. Make sure that document's frame selection is notified.
5322 // Note, this may cause the current nsFrame object to be deleted, bug 336592.
5323 RefPtr<nsFrameSelection> frameSelection;
5324 if (activeFrame != this && activeFrame->DetermineDisplaySelection() !=
5325 nsISelectionController::SELECTION_OFF) {
5326 frameSelection = activeFrame->GetFrameSelection();
5327 }
5328
5329 // Also check the selection of the capturing content which might be in a
5330 // different document.
5331 if (!frameSelection && captureContent) {
5332 if (Document* doc = captureContent->GetComposedDoc()) {
5333 mozilla::PresShell* capturingPresShell = doc->GetPresShell();
5334 if (capturingPresShell &&
5335 capturingPresShell != PresContext()->GetPresShell()) {
5336 frameSelection = capturingPresShell->FrameSelection();
5337 }
5338 }
5339 }
5340
5341 if (frameSelection) {
5342 AutoWeakFrame wf(this);
5343 frameSelection->SetDragState(false);
5344 frameSelection->StopAutoScrollTimer();
5345 if (wf.IsAlive()) {
5346 nsIScrollableFrame* scrollFrame =
5347 nsLayoutUtils::GetNearestScrollableFrame(
5348 this, nsLayoutUtils::SCROLLABLE_SAME_DOC |
5349 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
5350 if (scrollFrame) {
5351 // Perform any additional scrolling needed to maintain CSS snap point
5352 // requirements when autoscrolling is over.
5353 scrollFrame->ScrollSnap();
5354 }
5355 }
5356 }
5357
5358 // Do not call any methods of the current object after this point!!!
5359 // The object is perhaps dead!
5360
5361 return selectionOff ? NS_OK
5362 : HandleFrameSelection(
5363 frameselection, offsets, handleTableSelection,
5364 contentOffsetForTableSel, targetForTableSel,
5365 parentContent, aEvent, aEventStatus);
5366 }
5367
5368 struct MOZ_STACK_CLASS FrameContentRange {
FrameContentRangeFrameContentRange5369 FrameContentRange(nsIContent* aContent, int32_t aStart, int32_t aEnd)
5370 : content(aContent), start(aStart), end(aEnd) {}
5371 nsCOMPtr<nsIContent> content;
5372 int32_t start;
5373 int32_t end;
5374 };
5375
5376 // Retrieve the content offsets of a frame
GetRangeForFrame(const nsIFrame * aFrame)5377 static FrameContentRange GetRangeForFrame(const nsIFrame* aFrame) {
5378 nsIContent* content = aFrame->GetContent();
5379 if (!content) {
5380 NS_WARNING("Frame has no content");
5381 return FrameContentRange(nullptr, -1, -1);
5382 }
5383
5384 LayoutFrameType type = aFrame->Type();
5385 if (type == LayoutFrameType::Text) {
5386 auto [offset, offsetEnd] = aFrame->GetOffsets();
5387 return FrameContentRange(content, offset, offsetEnd);
5388 }
5389
5390 if (type == LayoutFrameType::Br) {
5391 nsIContent* parent = content->GetParent();
5392 const int32_t beginOffset = parent->ComputeIndexOf_Deprecated(content);
5393 return FrameContentRange(parent, beginOffset, beginOffset);
5394 }
5395
5396 while (content->IsRootOfNativeAnonymousSubtree()) {
5397 content = content->GetParent();
5398 }
5399
5400 nsIContent* parent = content->GetParent();
5401 if (aFrame->IsBlockOutside() || !parent) {
5402 return FrameContentRange(content, 0, content->GetChildCount());
5403 }
5404
5405 // TODO(emilio): Revise this in presence of Shadow DOM / display: contents,
5406 // it's likely that we don't want to just walk the light tree, and we need to
5407 // change the representation of FrameContentRange.
5408 const int32_t index = parent->ComputeIndexOf_Deprecated(content);
5409 MOZ_ASSERT(index >= 0);
5410 return FrameContentRange(parent, index, index + 1);
5411 }
5412
5413 // The FrameTarget represents the closest frame to a point that can be selected
5414 // The frame is the frame represented, frameEdge says whether one end of the
5415 // frame is the result (in which case different handling is needed), and
5416 // afterFrame says which end is represented if frameEdge is true
5417 struct FrameTarget {
operator boolFrameTarget5418 explicit operator bool() const { return !!frame; }
5419
5420 nsIFrame* frame = nullptr;
5421 bool frameEdge = false;
5422 bool afterFrame = false;
5423 };
5424
5425 // See function implementation for information
5426 static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame,
5427 const nsPoint& aPoint,
5428 uint32_t aFlags);
5429
SelfIsSelectable(nsIFrame * aFrame,uint32_t aFlags)5430 static bool SelfIsSelectable(nsIFrame* aFrame, uint32_t aFlags) {
5431 if ((aFlags & nsIFrame::SKIP_HIDDEN) &&
5432 !aFrame->StyleVisibility()->IsVisible()) {
5433 return false;
5434 }
5435 return !aFrame->IsGeneratedContentFrame() &&
5436 aFrame->Style()->UserSelect() != StyleUserSelect::None;
5437 }
5438
SelectionDescendToKids(nsIFrame * aFrame)5439 static bool SelectionDescendToKids(nsIFrame* aFrame) {
5440 // If we are only near (not directly over) then don't traverse
5441 // frames with independent selection (e.g. text and list controls, see bug
5442 // 268497). Note that this prevents any of the users of this method from
5443 // entering form controls.
5444 // XXX We might want some way to allow using the up-arrow to go into a form
5445 // control, but the focus didn't work right anyway; it'd probably be enough
5446 // if the left and right arrows could enter textboxes (which I don't believe
5447 // they can at the moment)
5448 if (aFrame->IsTextInputFrame() || aFrame->IsListControlFrame()) {
5449 MOZ_ASSERT(aFrame->HasAnyStateBits(NS_FRAME_INDEPENDENT_SELECTION));
5450 return false;
5451 }
5452
5453 // Failure in this assertion means a new type of frame forms the root of an
5454 // NS_FRAME_INDEPENDENT_SELECTION subtree. In such case, the condition above
5455 // should be changed to handle it.
5456 MOZ_ASSERT_IF(
5457 aFrame->HasAnyStateBits(NS_FRAME_INDEPENDENT_SELECTION),
5458 aFrame->GetParent()->HasAnyStateBits(NS_FRAME_INDEPENDENT_SELECTION));
5459
5460 if (aFrame->IsGeneratedContentFrame()) {
5461 return false;
5462 }
5463
5464 auto style = aFrame->Style()->UserSelect();
5465 return style != StyleUserSelect::All && style != StyleUserSelect::None;
5466 }
5467
GetSelectionClosestFrameForChild(nsIFrame * aChild,const nsPoint & aPoint,uint32_t aFlags)5468 static FrameTarget GetSelectionClosestFrameForChild(nsIFrame* aChild,
5469 const nsPoint& aPoint,
5470 uint32_t aFlags) {
5471 nsIFrame* parent = aChild->GetParent();
5472 if (SelectionDescendToKids(aChild)) {
5473 nsPoint pt = aPoint - aChild->GetOffsetTo(parent);
5474 return GetSelectionClosestFrame(aChild, pt, aFlags);
5475 }
5476 return FrameTarget{aChild, false, false};
5477 }
5478
5479 // When the cursor needs to be at the beginning of a block, it shouldn't be
5480 // before the first child. A click on a block whose first child is a block
5481 // should put the cursor in the child. The cursor shouldn't be between the
5482 // blocks, because that's not where it's expected.
5483 // Note that this method is guaranteed to succeed.
DrillDownToSelectionFrame(nsIFrame * aFrame,bool aEndFrame,uint32_t aFlags)5484 static FrameTarget DrillDownToSelectionFrame(nsIFrame* aFrame, bool aEndFrame,
5485 uint32_t aFlags) {
5486 if (SelectionDescendToKids(aFrame)) {
5487 nsIFrame* result = nullptr;
5488 nsIFrame* frame = aFrame->PrincipalChildList().FirstChild();
5489 if (!aEndFrame) {
5490 while (frame && (!SelfIsSelectable(frame, aFlags) || frame->IsEmpty()))
5491 frame = frame->GetNextSibling();
5492 if (frame) result = frame;
5493 } else {
5494 // Because the frame tree is singly linked, to find the last frame,
5495 // we have to iterate through all the frames
5496 // XXX I have a feeling this could be slow for long blocks, although
5497 // I can't find any slowdowns
5498 while (frame) {
5499 if (!frame->IsEmpty() && SelfIsSelectable(frame, aFlags))
5500 result = frame;
5501 frame = frame->GetNextSibling();
5502 }
5503 }
5504 if (result) return DrillDownToSelectionFrame(result, aEndFrame, aFlags);
5505 }
5506 // If the current frame has no targetable children, target the current frame
5507 return FrameTarget{aFrame, true, aEndFrame};
5508 }
5509
5510 // This method finds the closest valid FrameTarget on a given line; if there is
5511 // no valid FrameTarget on the line, it returns a null FrameTarget
GetSelectionClosestFrameForLine(nsBlockFrame * aParent,nsBlockFrame::LineIterator aLine,const nsPoint & aPoint,uint32_t aFlags)5512 static FrameTarget GetSelectionClosestFrameForLine(
5513 nsBlockFrame* aParent, nsBlockFrame::LineIterator aLine,
5514 const nsPoint& aPoint, uint32_t aFlags) {
5515 // Account for end of lines (any iterator from the block is valid)
5516 if (aLine == aParent->LinesEnd())
5517 return DrillDownToSelectionFrame(aParent, true, aFlags);
5518 nsIFrame* frame = aLine->mFirstChild;
5519 nsIFrame* closestFromIStart = nullptr;
5520 nsIFrame* closestFromIEnd = nullptr;
5521 nscoord closestIStart = aLine->IStart(), closestIEnd = aLine->IEnd();
5522 WritingMode wm = aLine->mWritingMode;
5523 LogicalPoint pt(wm, aPoint, aLine->mContainerSize);
5524 bool canSkipBr = false;
5525 bool lastFrameWasEditable = false;
5526 for (int32_t n = aLine->GetChildCount(); n;
5527 --n, frame = frame->GetNextSibling()) {
5528 // Skip brFrames. Can only skip if the line contains at least
5529 // one selectable and non-empty frame before. Also, avoid skipping brs if
5530 // the previous thing had a different editableness than us, since then we
5531 // may end up not being able to select after it if the br is the last thing
5532 // on the line.
5533 if (!SelfIsSelectable(frame, aFlags) || frame->IsEmpty() ||
5534 (canSkipBr && frame->IsBrFrame() &&
5535 lastFrameWasEditable == frame->GetContent()->IsEditable())) {
5536 continue;
5537 }
5538 canSkipBr = true;
5539 lastFrameWasEditable =
5540 frame->GetContent() && frame->GetContent()->IsEditable();
5541 LogicalRect frameRect =
5542 LogicalRect(wm, frame->GetRect(), aLine->mContainerSize);
5543 if (pt.I(wm) >= frameRect.IStart(wm)) {
5544 if (pt.I(wm) < frameRect.IEnd(wm)) {
5545 return GetSelectionClosestFrameForChild(frame, aPoint, aFlags);
5546 }
5547 if (frameRect.IEnd(wm) >= closestIStart) {
5548 closestFromIStart = frame;
5549 closestIStart = frameRect.IEnd(wm);
5550 }
5551 } else {
5552 if (frameRect.IStart(wm) <= closestIEnd) {
5553 closestFromIEnd = frame;
5554 closestIEnd = frameRect.IStart(wm);
5555 }
5556 }
5557 }
5558 if (!closestFromIStart && !closestFromIEnd) {
5559 // We should only get here if there are no selectable frames on a line
5560 // XXX Do we need more elaborate handling here?
5561 return FrameTarget();
5562 }
5563 if (closestFromIStart &&
5564 (!closestFromIEnd ||
5565 (abs(pt.I(wm) - closestIStart) <= abs(pt.I(wm) - closestIEnd)))) {
5566 return GetSelectionClosestFrameForChild(closestFromIStart, aPoint, aFlags);
5567 }
5568 return GetSelectionClosestFrameForChild(closestFromIEnd, aPoint, aFlags);
5569 }
5570
5571 // This method is for the special handling we do for block frames; they're
5572 // special because they represent paragraphs and because they are organized
5573 // into lines, which have bounds that are not stored elsewhere in the
5574 // frame tree. Returns a null FrameTarget for frames which are not
5575 // blocks or blocks with no lines except editable one.
GetSelectionClosestFrameForBlock(nsIFrame * aFrame,const nsPoint & aPoint,uint32_t aFlags)5576 static FrameTarget GetSelectionClosestFrameForBlock(nsIFrame* aFrame,
5577 const nsPoint& aPoint,
5578 uint32_t aFlags) {
5579 nsBlockFrame* bf = do_QueryFrame(aFrame);
5580 if (!bf) {
5581 return FrameTarget();
5582 }
5583
5584 // This code searches for the correct line
5585 nsBlockFrame::LineIterator end = bf->LinesEnd();
5586 nsBlockFrame::LineIterator curLine = bf->LinesBegin();
5587 nsBlockFrame::LineIterator closestLine = end;
5588
5589 if (curLine != end) {
5590 // Convert aPoint into a LogicalPoint in the writing-mode of this block
5591 WritingMode wm = curLine->mWritingMode;
5592 LogicalPoint pt(wm, aPoint, curLine->mContainerSize);
5593 do {
5594 // Check to see if our point lies within the line's block-direction bounds
5595 nscoord BCoord = pt.B(wm) - curLine->BStart();
5596 nscoord BSize = curLine->BSize();
5597 if (BCoord >= 0 && BCoord < BSize) {
5598 closestLine = curLine;
5599 break; // We found the line; stop looking
5600 }
5601 if (BCoord < 0) break;
5602 ++curLine;
5603 } while (curLine != end);
5604
5605 if (closestLine == end) {
5606 nsBlockFrame::LineIterator prevLine = curLine.prev();
5607 nsBlockFrame::LineIterator nextLine = curLine;
5608 // Avoid empty lines
5609 while (nextLine != end && nextLine->IsEmpty()) ++nextLine;
5610 while (prevLine != end && prevLine->IsEmpty()) --prevLine;
5611
5612 // This hidden pref dictates whether a point above or below all lines
5613 // comes up with a line or the beginning or end of the frame; 0 on
5614 // Windows, 1 on other platforms by default at the writing of this code
5615 int32_t dragOutOfFrame =
5616 Preferences::GetInt("browser.drag_out_of_frame_style");
5617
5618 if (prevLine == end) {
5619 if (dragOutOfFrame == 1 || nextLine == end)
5620 return DrillDownToSelectionFrame(aFrame, false, aFlags);
5621 closestLine = nextLine;
5622 } else if (nextLine == end) {
5623 if (dragOutOfFrame == 1)
5624 return DrillDownToSelectionFrame(aFrame, true, aFlags);
5625 closestLine = prevLine;
5626 } else { // Figure out which line is closer
5627 if (pt.B(wm) - prevLine->BEnd() < nextLine->BStart() - pt.B(wm))
5628 closestLine = prevLine;
5629 else
5630 closestLine = nextLine;
5631 }
5632 }
5633 }
5634
5635 do {
5636 if (auto target =
5637 GetSelectionClosestFrameForLine(bf, closestLine, aPoint, aFlags)) {
5638 return target;
5639 }
5640 ++closestLine;
5641 } while (closestLine != end);
5642
5643 // Fall back to just targeting the last targetable place
5644 return DrillDownToSelectionFrame(aFrame, true, aFlags);
5645 }
5646
5647 // Use frame edge for grid, flex, table, and non-editable images. Choose the
5648 // edge based on the point position past the frame rect. If past the middle,
5649 // caret should be at end, otherwise at start. This behavior matches Blink.
5650 //
5651 // TODO(emilio): Can we use this code path for other replaced elements other
5652 // than images? Or even all other frames? We only get there when we didn't find
5653 // selectable children... At least one XUL test fails if we make this apply to
5654 // XUL labels. Also, editable images need _not_ to use the frame edge, see
5655 // below.
UseFrameEdge(nsIFrame * aFrame)5656 static bool UseFrameEdge(nsIFrame* aFrame) {
5657 if (aFrame->IsFlexOrGridContainer() || aFrame->IsTableFrame()) {
5658 return true;
5659 }
5660 const nsImageFrame* image = do_QueryFrame(aFrame);
5661 if (image && !aFrame->GetContent()->IsEditable()) {
5662 // Editable images are a special-case because editing relies on clicking on
5663 // an editable image selecting it, for it to show resizers.
5664 return true;
5665 }
5666 return false;
5667 }
5668
LastResortFrameTargetForFrame(nsIFrame * aFrame,const nsPoint & aPoint)5669 static FrameTarget LastResortFrameTargetForFrame(nsIFrame* aFrame,
5670 const nsPoint& aPoint) {
5671 if (!UseFrameEdge(aFrame)) {
5672 return {aFrame, false, false};
5673 }
5674 const auto& rect = aFrame->GetRectRelativeToSelf();
5675 nscoord reference;
5676 nscoord middle;
5677 if (aFrame->GetWritingMode().IsVertical()) {
5678 reference = aPoint.y;
5679 middle = rect.Height() / 2;
5680 } else {
5681 reference = aPoint.x;
5682 middle = rect.Width() / 2;
5683 }
5684 const bool afterFrame = reference > middle;
5685 return {aFrame, true, afterFrame};
5686 }
5687
5688 // GetSelectionClosestFrame is the helper function that calculates the closest
5689 // frame to the given point.
5690 // It doesn't completely account for offset styles, so needs to be used in
5691 // restricted environments.
5692 // Cannot handle overlapping frames correctly, so it should receive the output
5693 // of GetFrameForPoint
5694 // Guaranteed to return a valid FrameTarget.
5695 // aPoint is relative to aFrame.
GetSelectionClosestFrame(nsIFrame * aFrame,const nsPoint & aPoint,uint32_t aFlags)5696 static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame,
5697 const nsPoint& aPoint,
5698 uint32_t aFlags) {
5699 // Handle blocks; if the frame isn't a block, the method fails
5700 if (auto target = GetSelectionClosestFrameForBlock(aFrame, aPoint, aFlags)) {
5701 return target;
5702 }
5703
5704 if (nsIFrame* kid = aFrame->PrincipalChildList().FirstChild()) {
5705 // Go through all the child frames to find the closest one
5706 nsIFrame::FrameWithDistance closest = {nullptr, nscoord_MAX, nscoord_MAX};
5707 for (; kid; kid = kid->GetNextSibling()) {
5708 if (!SelfIsSelectable(kid, aFlags) || kid->IsEmpty()) continue;
5709
5710 kid->FindCloserFrameForSelection(aPoint, &closest);
5711 }
5712 if (closest.mFrame) {
5713 if (SVGUtils::IsInSVGTextSubtree(closest.mFrame))
5714 return FrameTarget{closest.mFrame, false, false};
5715 return GetSelectionClosestFrameForChild(closest.mFrame, aPoint, aFlags);
5716 }
5717 }
5718
5719 return LastResortFrameTargetForFrame(aFrame, aPoint);
5720 }
5721
OffsetsForSingleFrame(nsIFrame * aFrame,const nsPoint & aPoint)5722 static nsIFrame::ContentOffsets OffsetsForSingleFrame(nsIFrame* aFrame,
5723 const nsPoint& aPoint) {
5724 nsIFrame::ContentOffsets offsets;
5725 FrameContentRange range = GetRangeForFrame(aFrame);
5726 offsets.content = range.content;
5727 // If there are continuations (meaning it's not one rectangle), this is the
5728 // best this function can do
5729 if (aFrame->GetNextContinuation() || aFrame->GetPrevContinuation()) {
5730 offsets.offset = range.start;
5731 offsets.secondaryOffset = range.end;
5732 offsets.associate = CARET_ASSOCIATE_AFTER;
5733 return offsets;
5734 }
5735
5736 // Figure out whether the offsets should be over, after, or before the frame
5737 nsRect rect(nsPoint(0, 0), aFrame->GetSize());
5738
5739 bool isBlock = !aFrame->StyleDisplay()->IsInlineFlow();
5740 bool isRtl = (aFrame->StyleVisibility()->mDirection == StyleDirection::Rtl);
5741 if ((isBlock && rect.y < aPoint.y) ||
5742 (!isBlock && ((isRtl && rect.x + rect.width / 2 > aPoint.x) ||
5743 (!isRtl && rect.x + rect.width / 2 < aPoint.x)))) {
5744 offsets.offset = range.end;
5745 if (rect.Contains(aPoint))
5746 offsets.secondaryOffset = range.start;
5747 else
5748 offsets.secondaryOffset = range.end;
5749 } else {
5750 offsets.offset = range.start;
5751 if (rect.Contains(aPoint))
5752 offsets.secondaryOffset = range.end;
5753 else
5754 offsets.secondaryOffset = range.start;
5755 }
5756 offsets.associate = offsets.offset == range.start ? CARET_ASSOCIATE_AFTER
5757 : CARET_ASSOCIATE_BEFORE;
5758 return offsets;
5759 }
5760
AdjustFrameForSelectionStyles(nsIFrame * aFrame)5761 static nsIFrame* AdjustFrameForSelectionStyles(nsIFrame* aFrame) {
5762 nsIFrame* adjustedFrame = aFrame;
5763 for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
5764 // These are the conditions that make all children not able to handle
5765 // a cursor.
5766 auto userSelect = frame->Style()->UserSelect();
5767 if (userSelect != StyleUserSelect::Auto &&
5768 userSelect != StyleUserSelect::All) {
5769 break;
5770 }
5771 if (userSelect == StyleUserSelect::All ||
5772 frame->IsGeneratedContentFrame()) {
5773 adjustedFrame = frame;
5774 }
5775 }
5776 return adjustedFrame;
5777 }
5778
GetContentOffsetsFromPoint(const nsPoint & aPoint,uint32_t aFlags)5779 nsIFrame::ContentOffsets nsIFrame::GetContentOffsetsFromPoint(
5780 const nsPoint& aPoint, uint32_t aFlags) {
5781 nsIFrame* adjustedFrame;
5782 if (aFlags & IGNORE_SELECTION_STYLE) {
5783 adjustedFrame = this;
5784 } else {
5785 // This section of code deals with special selection styles. Note that
5786 // -moz-all exists, even though it doesn't need to be explicitly handled.
5787 //
5788 // The offset is forced not to end up in generated content; content offsets
5789 // cannot represent content outside of the document's content tree.
5790
5791 adjustedFrame = AdjustFrameForSelectionStyles(this);
5792
5793 // `user-select: all` needs special handling, because clicking on it should
5794 // lead to the whole frame being selected.
5795 if (adjustedFrame->Style()->UserSelect() == StyleUserSelect::All) {
5796 nsPoint adjustedPoint = aPoint + GetOffsetTo(adjustedFrame);
5797 return OffsetsForSingleFrame(adjustedFrame, adjustedPoint);
5798 }
5799
5800 // For other cases, try to find a closest frame starting from the parent of
5801 // the unselectable frame
5802 if (adjustedFrame != this) {
5803 adjustedFrame = adjustedFrame->GetParent();
5804 }
5805 }
5806
5807 nsPoint adjustedPoint = aPoint + GetOffsetTo(adjustedFrame);
5808
5809 FrameTarget closest =
5810 GetSelectionClosestFrame(adjustedFrame, adjustedPoint, aFlags);
5811
5812 // If the correct offset is at one end of a frame, use offset-based
5813 // calculation method
5814 if (closest.frameEdge) {
5815 ContentOffsets offsets;
5816 FrameContentRange range = GetRangeForFrame(closest.frame);
5817 offsets.content = range.content;
5818 if (closest.afterFrame)
5819 offsets.offset = range.end;
5820 else
5821 offsets.offset = range.start;
5822 offsets.secondaryOffset = offsets.offset;
5823 offsets.associate = offsets.offset == range.start ? CARET_ASSOCIATE_AFTER
5824 : CARET_ASSOCIATE_BEFORE;
5825 return offsets;
5826 }
5827
5828 nsPoint pt;
5829 if (closest.frame != this) {
5830 if (SVGUtils::IsInSVGTextSubtree(closest.frame)) {
5831 pt = nsLayoutUtils::TransformAncestorPointToFrame(
5832 RelativeTo{closest.frame}, aPoint, RelativeTo{this});
5833 } else {
5834 pt = aPoint - closest.frame->GetOffsetTo(this);
5835 }
5836 } else {
5837 pt = aPoint;
5838 }
5839 return closest.frame->CalcContentOffsetsFromFramePoint(pt);
5840
5841 // XXX should I add some kind of offset standardization?
5842 // consider <b>xxxxx</b><i>zzzzz</i>; should any click between the last
5843 // x and first z put the cursor in the same logical position in addition
5844 // to the same visual position?
5845 }
5846
CalcContentOffsetsFromFramePoint(const nsPoint & aPoint)5847 nsIFrame::ContentOffsets nsIFrame::CalcContentOffsetsFromFramePoint(
5848 const nsPoint& aPoint) {
5849 return OffsetsForSingleFrame(this, aPoint);
5850 }
5851
AssociateImage(const StyleImage & aImage)5852 bool nsIFrame::AssociateImage(const StyleImage& aImage) {
5853 imgRequestProxy* req = aImage.GetImageRequest();
5854 if (!req) {
5855 return false;
5856 }
5857
5858 mozilla::css::ImageLoader* loader =
5859 PresContext()->Document()->StyleImageLoader();
5860
5861 loader->AssociateRequestToFrame(req, this);
5862 return true;
5863 }
5864
DisassociateImage(const StyleImage & aImage)5865 void nsIFrame::DisassociateImage(const StyleImage& aImage) {
5866 imgRequestProxy* req = aImage.GetImageRequest();
5867 if (!req) {
5868 return;
5869 }
5870
5871 mozilla::css::ImageLoader* loader =
5872 PresContext()->Document()->StyleImageLoader();
5873
5874 loader->DisassociateRequestFromFrame(req, this);
5875 }
5876
UsedImageRendering() const5877 StyleImageRendering nsIFrame::UsedImageRendering() const {
5878 ComputedStyle* style;
5879 if (nsCSSRendering::IsCanvasFrame(this)) {
5880 nsCSSRendering::FindBackground(this, &style);
5881 } else {
5882 style = Style();
5883 }
5884 return style->StyleVisibility()->mImageRendering;
5885 }
5886
5887 // The touch-action CSS property applies to: all elements except: non-replaced
5888 // inline elements, table rows, row groups, table columns, and column groups.
UsedTouchAction() const5889 StyleTouchAction nsIFrame::UsedTouchAction() const {
5890 if (IsFrameOfType(eLineParticipant)) {
5891 return StyleTouchAction::AUTO;
5892 }
5893 auto& disp = *StyleDisplay();
5894 if (disp.IsInternalTableStyleExceptCell()) {
5895 return StyleTouchAction::AUTO;
5896 }
5897 return disp.mTouchAction;
5898 }
5899
GetCursor(const nsPoint &)5900 Maybe<nsIFrame::Cursor> nsIFrame::GetCursor(const nsPoint&) {
5901 StyleCursorKind kind = StyleUI()->Cursor().keyword;
5902 if (kind == StyleCursorKind::Auto) {
5903 // If this is editable, I-beam cursor is better for most elements.
5904 kind = (mContent && mContent->IsEditable()) ? StyleCursorKind::Text
5905 : StyleCursorKind::Default;
5906 }
5907 if (kind == StyleCursorKind::Text && GetWritingMode().IsVertical()) {
5908 // Per CSS UI spec, UA may treat value 'text' as
5909 // 'vertical-text' for vertical text.
5910 kind = StyleCursorKind::VerticalText;
5911 }
5912
5913 return Some(Cursor{kind, AllowCustomCursorImage::Yes});
5914 }
5915
5916 // Resize and incremental reflow
5917
5918 /* virtual */
MarkIntrinsicISizesDirty()5919 void nsIFrame::MarkIntrinsicISizesDirty() {
5920 // This version is meant only for what used to be box-to-block adaptors.
5921 // It should not be called by other derived classes.
5922 if (::IsXULBoxWrapped(this)) {
5923 nsBoxLayoutMetrics* metrics = BoxMetrics();
5924
5925 XULSizeNeedsRecalc(metrics->mPrefSize);
5926 XULSizeNeedsRecalc(metrics->mMinSize);
5927 XULSizeNeedsRecalc(metrics->mMaxSize);
5928 XULSizeNeedsRecalc(metrics->mBlockPrefSize);
5929 XULSizeNeedsRecalc(metrics->mBlockMinSize);
5930 XULCoordNeedsRecalc(metrics->mFlex);
5931 XULCoordNeedsRecalc(metrics->mAscent);
5932 }
5933
5934 // If we're a flex item, clear our flex-item-specific cached measurements
5935 // (which likely depended on our now-stale intrinsic isize).
5936 if (IsFlexItem()) {
5937 nsFlexContainerFrame::MarkCachedFlexMeasurementsDirty(this);
5938 }
5939
5940 if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT)) {
5941 nsFontInflationData::MarkFontInflationDataTextDirty(this);
5942 }
5943
5944 if (StaticPrefs::layout_css_grid_item_baxis_measurement_enabled()) {
5945 RemoveProperty(nsGridContainerFrame::CachedBAxisMeasurement::Prop());
5946 }
5947 }
5948
MarkSubtreeDirty()5949 void nsIFrame::MarkSubtreeDirty() {
5950 if (HasAnyStateBits(NS_FRAME_IS_DIRTY)) {
5951 return;
5952 }
5953 // Unconditionally mark given frame dirty.
5954 AddStateBits(NS_FRAME_IS_DIRTY);
5955
5956 // Mark all descendants dirty, unless:
5957 // - Already dirty.
5958 // - TableColGroup
5959 // - XULBox
5960 AutoTArray<nsIFrame*, 32> stack;
5961 for (const auto& childLists : ChildLists()) {
5962 for (nsIFrame* kid : childLists.mList) {
5963 stack.AppendElement(kid);
5964 }
5965 }
5966 while (!stack.IsEmpty()) {
5967 nsIFrame* f = stack.PopLastElement();
5968 if (f->HasAnyStateBits(NS_FRAME_IS_DIRTY) || f->IsTableColGroupFrame() ||
5969 f->IsXULBoxFrame()) {
5970 continue;
5971 }
5972
5973 f->AddStateBits(NS_FRAME_IS_DIRTY);
5974
5975 for (const auto& childLists : f->ChildLists()) {
5976 for (nsIFrame* kid : childLists.mList) {
5977 stack.AppendElement(kid);
5978 }
5979 }
5980 }
5981 }
5982
5983 /* virtual */
GetMinISize(gfxContext * aRenderingContext)5984 nscoord nsIFrame::GetMinISize(gfxContext* aRenderingContext) {
5985 nscoord result = 0;
5986 DISPLAY_MIN_INLINE_SIZE(this, result);
5987 return result;
5988 }
5989
5990 /* virtual */
GetPrefISize(gfxContext * aRenderingContext)5991 nscoord nsIFrame::GetPrefISize(gfxContext* aRenderingContext) {
5992 nscoord result = 0;
5993 DISPLAY_PREF_INLINE_SIZE(this, result);
5994 return result;
5995 }
5996
5997 /* virtual */
AddInlineMinISize(gfxContext * aRenderingContext,nsIFrame::InlineMinISizeData * aData)5998 void nsIFrame::AddInlineMinISize(gfxContext* aRenderingContext,
5999 nsIFrame::InlineMinISizeData* aData) {
6000 nscoord isize = nsLayoutUtils::IntrinsicForContainer(
6001 aRenderingContext, this, IntrinsicISizeType::MinISize);
6002 aData->DefaultAddInlineMinISize(this, isize);
6003 }
6004
6005 /* virtual */
AddInlinePrefISize(gfxContext * aRenderingContext,nsIFrame::InlinePrefISizeData * aData)6006 void nsIFrame::AddInlinePrefISize(gfxContext* aRenderingContext,
6007 nsIFrame::InlinePrefISizeData* aData) {
6008 nscoord isize = nsLayoutUtils::IntrinsicForContainer(
6009 aRenderingContext, this, IntrinsicISizeType::PrefISize);
6010 aData->DefaultAddInlinePrefISize(isize);
6011 }
6012
DefaultAddInlineMinISize(nsIFrame * aFrame,nscoord aISize,bool aAllowBreak)6013 void nsIFrame::InlineMinISizeData::DefaultAddInlineMinISize(nsIFrame* aFrame,
6014 nscoord aISize,
6015 bool aAllowBreak) {
6016 auto parent = aFrame->GetParent();
6017 MOZ_ASSERT(parent, "Must have a parent if we get here!");
6018 const bool mayBreak = aAllowBreak && !aFrame->CanContinueTextRun() &&
6019 !parent->Style()->ShouldSuppressLineBreak() &&
6020 parent->StyleText()->WhiteSpaceCanWrap(parent);
6021 if (mayBreak) {
6022 OptionallyBreak();
6023 }
6024 mTrailingWhitespace = 0;
6025 mSkipWhitespace = false;
6026 mCurrentLine += aISize;
6027 mAtStartOfLine = false;
6028 if (mayBreak) {
6029 OptionallyBreak();
6030 }
6031 }
6032
DefaultAddInlinePrefISize(nscoord aISize)6033 void nsIFrame::InlinePrefISizeData::DefaultAddInlinePrefISize(nscoord aISize) {
6034 mCurrentLine = NSCoordSaturatingAdd(mCurrentLine, aISize);
6035 mTrailingWhitespace = 0;
6036 mSkipWhitespace = false;
6037 mLineIsEmpty = false;
6038 }
6039
ForceBreak()6040 void nsIFrame::InlineMinISizeData::ForceBreak() {
6041 mCurrentLine -= mTrailingWhitespace;
6042 mPrevLines = std::max(mPrevLines, mCurrentLine);
6043 mCurrentLine = mTrailingWhitespace = 0;
6044
6045 for (uint32_t i = 0, i_end = mFloats.Length(); i != i_end; ++i) {
6046 nscoord float_min = mFloats[i].Width();
6047 if (float_min > mPrevLines) mPrevLines = float_min;
6048 }
6049 mFloats.Clear();
6050 mSkipWhitespace = true;
6051 }
6052
OptionallyBreak(nscoord aHyphenWidth)6053 void nsIFrame::InlineMinISizeData::OptionallyBreak(nscoord aHyphenWidth) {
6054 // If we can fit more content into a smaller width by staying on this
6055 // line (because we're still at a negative offset due to negative
6056 // text-indent or negative margin), don't break. Otherwise, do the
6057 // same as ForceBreak. it doesn't really matter when we accumulate
6058 // floats.
6059 if (mCurrentLine + aHyphenWidth < 0 || mAtStartOfLine) return;
6060 mCurrentLine += aHyphenWidth;
6061 ForceBreak();
6062 }
6063
ForceBreak(StyleClear aBreakType)6064 void nsIFrame::InlinePrefISizeData::ForceBreak(StyleClear aBreakType) {
6065 MOZ_ASSERT(aBreakType == StyleClear::None || aBreakType == StyleClear::Both ||
6066 aBreakType == StyleClear::Left ||
6067 aBreakType == StyleClear::Right,
6068 "Must be a physical break type");
6069
6070 // If this force break is not clearing any float, we can leave all the
6071 // floats to the next force break.
6072 if (mFloats.Length() != 0 && aBreakType != StyleClear::None) {
6073 // preferred widths accumulated for floats that have already
6074 // been cleared past
6075 nscoord floats_done = 0,
6076 // preferred widths accumulated for floats that have not yet
6077 // been cleared past
6078 floats_cur_left = 0, floats_cur_right = 0;
6079
6080 for (uint32_t i = 0, i_end = mFloats.Length(); i != i_end; ++i) {
6081 const FloatInfo& floatInfo = mFloats[i];
6082 const nsStyleDisplay* floatDisp = floatInfo.Frame()->StyleDisplay();
6083 StyleClear breakType = floatDisp->mBreakType;
6084 if (breakType == StyleClear::Left || breakType == StyleClear::Right ||
6085 breakType == StyleClear::Both) {
6086 nscoord floats_cur =
6087 NSCoordSaturatingAdd(floats_cur_left, floats_cur_right);
6088 if (floats_cur > floats_done) {
6089 floats_done = floats_cur;
6090 }
6091 if (breakType != StyleClear::Right) {
6092 floats_cur_left = 0;
6093 }
6094 if (breakType != StyleClear::Left) {
6095 floats_cur_right = 0;
6096 }
6097 }
6098
6099 StyleFloat floatStyle = floatDisp->mFloat;
6100 nscoord& floats_cur =
6101 floatStyle == StyleFloat::Left ? floats_cur_left : floats_cur_right;
6102 nscoord floatWidth = floatInfo.Width();
6103 // Negative-width floats don't change the available space so they
6104 // shouldn't change our intrinsic line width either.
6105 floats_cur = NSCoordSaturatingAdd(floats_cur, std::max(0, floatWidth));
6106 }
6107
6108 nscoord floats_cur =
6109 NSCoordSaturatingAdd(floats_cur_left, floats_cur_right);
6110 if (floats_cur > floats_done) floats_done = floats_cur;
6111
6112 mCurrentLine = NSCoordSaturatingAdd(mCurrentLine, floats_done);
6113
6114 if (aBreakType == StyleClear::Both) {
6115 mFloats.Clear();
6116 } else {
6117 // If the break type does not clear all floats, it means there may
6118 // be some floats whose isize should contribute to the intrinsic
6119 // isize of the next line. The code here scans the current mFloats
6120 // and keeps floats which are not cleared by this break. Note that
6121 // floats may be cleared directly or indirectly. See below.
6122 nsTArray<FloatInfo> newFloats;
6123 MOZ_ASSERT(
6124 aBreakType == StyleClear::Left || aBreakType == StyleClear::Right,
6125 "Other values should have been handled in other branches");
6126 StyleFloat clearFloatType =
6127 aBreakType == StyleClear::Left ? StyleFloat::Left : StyleFloat::Right;
6128 // Iterate the array in reverse so that we can stop when there are
6129 // no longer any floats we need to keep. See below.
6130 for (FloatInfo& floatInfo : Reversed(mFloats)) {
6131 const nsStyleDisplay* floatDisp = floatInfo.Frame()->StyleDisplay();
6132 if (floatDisp->mFloat != clearFloatType) {
6133 newFloats.AppendElement(floatInfo);
6134 } else {
6135 // This is a float on the side that this break directly clears
6136 // which means we're not keeping it in mFloats. However, if
6137 // this float clears floats on the opposite side (via a value
6138 // of either 'both' or one of 'left'/'right'), any remaining
6139 // (earlier) floats on that side would be indirectly cleared
6140 // as well. Thus, we should break out of this loop and stop
6141 // considering earlier floats to be kept in mFloats.
6142 StyleClear floatBreakType = floatDisp->mBreakType;
6143 if (floatBreakType != aBreakType &&
6144 floatBreakType != StyleClear::None) {
6145 break;
6146 }
6147 }
6148 }
6149 newFloats.Reverse();
6150 mFloats = std::move(newFloats);
6151 }
6152 }
6153
6154 mCurrentLine =
6155 NSCoordSaturatingSubtract(mCurrentLine, mTrailingWhitespace, nscoord_MAX);
6156 mPrevLines = std::max(mPrevLines, mCurrentLine);
6157 mCurrentLine = mTrailingWhitespace = 0;
6158 mSkipWhitespace = true;
6159 mLineIsEmpty = true;
6160 }
6161
ResolveMargin(const LengthPercentageOrAuto & aStyle,nscoord aPercentageBasis)6162 static nscoord ResolveMargin(const LengthPercentageOrAuto& aStyle,
6163 nscoord aPercentageBasis) {
6164 if (aStyle.IsAuto()) {
6165 return nscoord(0);
6166 }
6167 return nsLayoutUtils::ResolveToLength<false>(aStyle.AsLengthPercentage(),
6168 aPercentageBasis);
6169 }
6170
ResolvePadding(const LengthPercentage & aStyle,nscoord aPercentageBasis)6171 static nscoord ResolvePadding(const LengthPercentage& aStyle,
6172 nscoord aPercentageBasis) {
6173 return nsLayoutUtils::ResolveToLength<true>(aStyle, aPercentageBasis);
6174 }
6175
IntrinsicSizeOffsets(nsIFrame * aFrame,nscoord aPercentageBasis,bool aForISize)6176 static nsIFrame::IntrinsicSizeOffsetData IntrinsicSizeOffsets(
6177 nsIFrame* aFrame, nscoord aPercentageBasis, bool aForISize) {
6178 nsIFrame::IntrinsicSizeOffsetData result;
6179 WritingMode wm = aFrame->GetWritingMode();
6180 const auto& margin = aFrame->StyleMargin()->mMargin;
6181 bool verticalAxis = aForISize == wm.IsVertical();
6182 if (verticalAxis) {
6183 result.margin += ResolveMargin(margin.Get(eSideTop), aPercentageBasis);
6184 result.margin += ResolveMargin(margin.Get(eSideBottom), aPercentageBasis);
6185 } else {
6186 result.margin += ResolveMargin(margin.Get(eSideLeft), aPercentageBasis);
6187 result.margin += ResolveMargin(margin.Get(eSideRight), aPercentageBasis);
6188 }
6189
6190 const auto& padding = aFrame->StylePadding()->mPadding;
6191 if (verticalAxis) {
6192 result.padding += ResolvePadding(padding.Get(eSideTop), aPercentageBasis);
6193 result.padding +=
6194 ResolvePadding(padding.Get(eSideBottom), aPercentageBasis);
6195 } else {
6196 result.padding += ResolvePadding(padding.Get(eSideLeft), aPercentageBasis);
6197 result.padding += ResolvePadding(padding.Get(eSideRight), aPercentageBasis);
6198 }
6199
6200 const nsStyleBorder* styleBorder = aFrame->StyleBorder();
6201 if (verticalAxis) {
6202 result.border += styleBorder->GetComputedBorderWidth(eSideTop);
6203 result.border += styleBorder->GetComputedBorderWidth(eSideBottom);
6204 } else {
6205 result.border += styleBorder->GetComputedBorderWidth(eSideLeft);
6206 result.border += styleBorder->GetComputedBorderWidth(eSideRight);
6207 }
6208
6209 const nsStyleDisplay* disp = aFrame->StyleDisplay();
6210 if (aFrame->IsThemed(disp)) {
6211 nsPresContext* presContext = aFrame->PresContext();
6212
6213 LayoutDeviceIntMargin border = presContext->Theme()->GetWidgetBorder(
6214 presContext->DeviceContext(), aFrame, disp->EffectiveAppearance());
6215 result.border = presContext->DevPixelsToAppUnits(
6216 verticalAxis ? border.TopBottom() : border.LeftRight());
6217
6218 LayoutDeviceIntMargin padding;
6219 if (presContext->Theme()->GetWidgetPadding(
6220 presContext->DeviceContext(), aFrame, disp->EffectiveAppearance(),
6221 &padding)) {
6222 result.padding = presContext->DevPixelsToAppUnits(
6223 verticalAxis ? padding.TopBottom() : padding.LeftRight());
6224 }
6225 }
6226 return result;
6227 }
6228
IntrinsicISizeOffsets(nscoord aPercentageBasis)6229 /* virtual */ nsIFrame::IntrinsicSizeOffsetData nsIFrame::IntrinsicISizeOffsets(
6230 nscoord aPercentageBasis) {
6231 return IntrinsicSizeOffsets(this, aPercentageBasis, true);
6232 }
6233
IntrinsicBSizeOffsets(nscoord aPercentageBasis)6234 nsIFrame::IntrinsicSizeOffsetData nsIFrame::IntrinsicBSizeOffsets(
6235 nscoord aPercentageBasis) {
6236 return IntrinsicSizeOffsets(this, aPercentageBasis, false);
6237 }
6238
6239 /* virtual */
GetIntrinsicSize()6240 IntrinsicSize nsIFrame::GetIntrinsicSize() {
6241 return IntrinsicSize(); // default is width/height set to eStyleUnit_None
6242 }
6243
GetAspectRatio() const6244 AspectRatio nsIFrame::GetAspectRatio() const {
6245 // Per spec, 'aspect-ratio' property applies to all elements except inline
6246 // boxes and internal ruby or table boxes.
6247 // https://drafts.csswg.org/css-sizing-4/#aspect-ratio
6248 // For those frame types that don't support aspect-ratio, they must not have
6249 // the natural ratio, so this early return is fine.
6250 if (!IsFrameOfType(eSupportsAspectRatio)) {
6251 return AspectRatio();
6252 }
6253
6254 const StyleAspectRatio& aspectRatio = StylePosition()->mAspectRatio;
6255 // If aspect-ratio is zero or infinite, it's a degenerate ratio and behaves
6256 // as auto.
6257 // https://drafts.csswg.org/css-sizing-4/#valdef-aspect-ratio-ratio
6258 if (!aspectRatio.BehavesAsAuto()) {
6259 // Non-auto. Return the preferred aspect ratio from the aspect-ratio style.
6260 return aspectRatio.ratio.AsRatio().ToLayoutRatio(UseBoxSizing::Yes);
6261 }
6262
6263 // The rest of the cases are when aspect-ratio has 'auto'.
6264 if (auto intrinsicRatio = GetIntrinsicRatio()) {
6265 return intrinsicRatio;
6266 }
6267
6268 if (aspectRatio.HasRatio()) {
6269 // If it's a degenerate ratio, this returns 0. Just the same as the auto
6270 // case.
6271 return aspectRatio.ratio.AsRatio().ToLayoutRatio(UseBoxSizing::No);
6272 }
6273
6274 return AspectRatio();
6275 }
6276
6277 /* virtual */
GetIntrinsicRatio() const6278 AspectRatio nsIFrame::GetIntrinsicRatio() const { return AspectRatio(); }
6279
ShouldApplyAutomaticMinimumOnInlineAxis(WritingMode aWM,const nsStyleDisplay * aDisplay,const nsStylePosition * aPosition)6280 static bool ShouldApplyAutomaticMinimumOnInlineAxis(
6281 WritingMode aWM, const nsStyleDisplay* aDisplay,
6282 const nsStylePosition* aPosition) {
6283 // Apply the automatic minimum size for aspect ratio:
6284 // Note: The replaced elements shouldn't be here, so we only check the scroll
6285 // container.
6286 // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-minimum
6287 return !aDisplay->IsScrollableOverflow() && aPosition->MinISize(aWM).IsAuto();
6288 }
6289
6290 struct MinMaxSize {
6291 nscoord mMinSize = 0;
6292 nscoord mMaxSize = NS_UNCONSTRAINEDSIZE;
6293
ClampSizeToMinAndMaxMinMaxSize6294 nscoord ClampSizeToMinAndMax(nscoord aSize) const {
6295 return NS_CSS_MINMAX(aSize, mMinSize, mMaxSize);
6296 }
6297 };
ComputeTransferredMinMaxInlineSize(const WritingMode aWM,const AspectRatio & aAspectRatio,const MinMaxSize & aMinMaxBSize,const LogicalSize & aBoxSizingAdjustment)6298 static MinMaxSize ComputeTransferredMinMaxInlineSize(
6299 const WritingMode aWM, const AspectRatio& aAspectRatio,
6300 const MinMaxSize& aMinMaxBSize, const LogicalSize& aBoxSizingAdjustment) {
6301 // Note: the spec mentions that
6302 // 1. This transferred minimum is capped by any definite preferred or maximum
6303 // size in the destination axis.
6304 // 2. This transferred maximum is floored by any definite preferred or minimum
6305 // size in the destination axis
6306 //
6307 // https://drafts.csswg.org/css-sizing-4/#aspect-ratio
6308 //
6309 // The spec requires us to clamp these by the specified size (it calls it the
6310 // preferred size). However, we actually don't need to worry about that,
6311 // because we only use this if the inline size is indefinite.
6312 //
6313 // We do not need to clamp the transferred minimum and maximum as long as we
6314 // always apply the transferred min/max size before the explicit min/max size,
6315 // the result will be identical.
6316
6317 MinMaxSize transferredISize;
6318
6319 if (aMinMaxBSize.mMinSize > 0) {
6320 transferredISize.mMinSize = aAspectRatio.ComputeRatioDependentSize(
6321 LogicalAxis::eLogicalAxisInline, aWM, aMinMaxBSize.mMinSize,
6322 aBoxSizingAdjustment);
6323 }
6324
6325 if (aMinMaxBSize.mMaxSize != NS_UNCONSTRAINEDSIZE) {
6326 transferredISize.mMaxSize = aAspectRatio.ComputeRatioDependentSize(
6327 LogicalAxis::eLogicalAxisInline, aWM, aMinMaxBSize.mMaxSize,
6328 aBoxSizingAdjustment);
6329 }
6330
6331 // Minimum size wins over maximum size.
6332 transferredISize.mMaxSize =
6333 std::max(transferredISize.mMinSize, transferredISize.mMaxSize);
6334 return transferredISize;
6335 }
6336
6337 /* virtual */
ComputeSize(gfxContext * aRenderingContext,WritingMode aWM,const LogicalSize & aCBSize,nscoord aAvailableISize,const LogicalSize & aMargin,const LogicalSize & aBorderPadding,const StyleSizeOverrides & aSizeOverrides,ComputeSizeFlags aFlags)6338 nsIFrame::SizeComputationResult nsIFrame::ComputeSize(
6339 gfxContext* aRenderingContext, WritingMode aWM, const LogicalSize& aCBSize,
6340 nscoord aAvailableISize, const LogicalSize& aMargin,
6341 const LogicalSize& aBorderPadding, const StyleSizeOverrides& aSizeOverrides,
6342 ComputeSizeFlags aFlags) {
6343 MOZ_ASSERT(!GetIntrinsicRatio(),
6344 "Please override this method and call "
6345 "nsContainerFrame::ComputeSizeWithIntrinsicDimensions instead.");
6346 LogicalSize result =
6347 ComputeAutoSize(aRenderingContext, aWM, aCBSize, aAvailableISize, aMargin,
6348 aBorderPadding, aSizeOverrides, aFlags);
6349 const nsStylePosition* stylePos = StylePosition();
6350 const nsStyleDisplay* disp = StyleDisplay();
6351 auto aspectRatioUsage = AspectRatioUsage::None;
6352
6353 const auto boxSizingAdjust = stylePos->mBoxSizing == StyleBoxSizing::Border
6354 ? aBorderPadding
6355 : LogicalSize(aWM);
6356 nscoord boxSizingToMarginEdgeISize = aMargin.ISize(aWM) +
6357 aBorderPadding.ISize(aWM) -
6358 boxSizingAdjust.ISize(aWM);
6359
6360 const auto& styleISize = aSizeOverrides.mStyleISize
6361 ? *aSizeOverrides.mStyleISize
6362 : stylePos->ISize(aWM);
6363 const auto& styleBSize = aSizeOverrides.mStyleBSize
6364 ? *aSizeOverrides.mStyleBSize
6365 : stylePos->BSize(aWM);
6366 const auto& aspectRatio = aSizeOverrides.mAspectRatio
6367 ? *aSizeOverrides.mAspectRatio
6368 : GetAspectRatio();
6369
6370 auto parentFrame = GetParent();
6371 auto alignCB = parentFrame;
6372 bool isGridItem = IsGridItem();
6373 if (parentFrame && parentFrame->IsTableWrapperFrame() && IsTableFrame()) {
6374 // An inner table frame is sized as a grid item if its table wrapper is,
6375 // because they actually have the same CB (the wrapper's CB).
6376 // @see ReflowInput::InitCBReflowInput
6377 auto tableWrapper = GetParent();
6378 auto grandParent = tableWrapper->GetParent();
6379 isGridItem = grandParent->IsGridContainerFrame() &&
6380 !tableWrapper->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
6381 if (isGridItem) {
6382 // When resolving justify/align-self below, we want to use the grid
6383 // container's justify/align-items value and WritingMode.
6384 alignCB = grandParent;
6385 }
6386 }
6387 const bool isFlexItem =
6388 IsFlexItem() &&
6389 !parentFrame->HasAnyStateBits(NS_STATE_FLEX_IS_EMULATING_LEGACY_BOX);
6390 // This variable only gets set (and used) if isFlexItem is true. It
6391 // indicates which axis (in this frame's own WM) corresponds to its
6392 // flex container's main axis.
6393 LogicalAxis flexMainAxis =
6394 eLogicalAxisInline; // (init to make valgrind happy)
6395 if (isFlexItem) {
6396 flexMainAxis = nsFlexContainerFrame::IsItemInlineAxisMainAxis(this)
6397 ? eLogicalAxisInline
6398 : eLogicalAxisBlock;
6399 }
6400
6401 const bool isOrthogonal = aWM.IsOrthogonalTo(alignCB->GetWritingMode());
6402 const bool isAutoISize = styleISize.IsAuto();
6403 const bool isAutoBSize =
6404 nsLayoutUtils::IsAutoBSize(styleBSize, aCBSize.BSize(aWM)) ||
6405 aFlags.contains(ComputeSizeFlag::UseAutoBSize);
6406 // Compute inline-axis size
6407 if (!isAutoISize) {
6408 auto iSizeResult = ComputeISizeValue(
6409 aRenderingContext, aWM, aCBSize, boxSizingAdjust,
6410 boxSizingToMarginEdgeISize, styleISize, aSizeOverrides, aFlags);
6411 result.ISize(aWM) = iSizeResult.mISize;
6412 aspectRatioUsage = iSizeResult.mAspectRatioUsage;
6413 } else if (MOZ_UNLIKELY(isGridItem) && !IsTrueOverflowContainer()) {
6414 // 'auto' inline-size for grid-level box - fill the CB for 'stretch' /
6415 // 'normal' and clamp it to the CB if requested:
6416 bool stretch = false;
6417 bool mayUseAspectRatio = aspectRatio && !isAutoBSize;
6418 if (!aFlags.contains(ComputeSizeFlag::ShrinkWrap) &&
6419 !StyleMargin()->HasInlineAxisAuto(aWM) &&
6420 !alignCB->IsMasonry(isOrthogonal ? eLogicalAxisBlock
6421 : eLogicalAxisInline)) {
6422 auto inlineAxisAlignment =
6423 isOrthogonal ? StylePosition()->UsedAlignSelf(alignCB->Style())._0
6424 : StylePosition()->UsedJustifySelf(alignCB->Style())._0;
6425 stretch = inlineAxisAlignment == StyleAlignFlags::STRETCH ||
6426 (inlineAxisAlignment == StyleAlignFlags::NORMAL &&
6427 !mayUseAspectRatio);
6428 }
6429
6430 // Apply the preferred aspect ratio for alignments other than *stretch* and
6431 // *normal without aspect ratio*.
6432 // The spec says all other values should size the items as fit-content, and
6433 // the intrinsic size should respect the preferred aspect ratio, so we also
6434 // apply aspect ratio for all other values.
6435 // https://drafts.csswg.org/css-grid/#grid-item-sizing
6436 if (!stretch && mayUseAspectRatio) {
6437 // Note: we don't need to handle aspect ratio for inline axis if both
6438 // width/height are auto. The default ratio-dependent axis is block axis
6439 // in this case, so we can simply get the block size from the non-auto
6440 // |styleBSize|.
6441 auto bSize = nsLayoutUtils::ComputeBSizeValue(
6442 aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
6443 styleBSize.AsLengthPercentage());
6444 result.ISize(aWM) = aspectRatio.ComputeRatioDependentSize(
6445 LogicalAxis::eLogicalAxisInline, aWM, bSize, boxSizingAdjust);
6446 aspectRatioUsage = AspectRatioUsage::ToComputeISize;
6447 }
6448
6449 if (stretch || aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize)) {
6450 auto iSizeToFillCB =
6451 std::max(nscoord(0), aCBSize.ISize(aWM) - aBorderPadding.ISize(aWM) -
6452 aMargin.ISize(aWM));
6453 if (stretch || result.ISize(aWM) > iSizeToFillCB) {
6454 result.ISize(aWM) = iSizeToFillCB;
6455 }
6456 }
6457 } else if (aspectRatio && !isAutoBSize) {
6458 auto bSize = nsLayoutUtils::ComputeBSizeValue(
6459 aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
6460 styleBSize.AsLengthPercentage());
6461 result.ISize(aWM) = aspectRatio.ComputeRatioDependentSize(
6462 LogicalAxis::eLogicalAxisInline, aWM, bSize, boxSizingAdjust);
6463 aspectRatioUsage = AspectRatioUsage::ToComputeISize;
6464 }
6465
6466 // Calculate and apply transferred min & max size contraints.
6467 // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-size-transfers
6468 //
6469 // Note: The basic principle is that sizing constraints transfer through the
6470 // aspect-ratio to the other side to preserve the aspect ratio to the extent
6471 // that they can without violating any sizes specified explicitly on that
6472 // affected axis.
6473 const bool isDefiniteISize = styleISize.IsLengthPercentage();
6474 const bool isFlexItemInlineAxisMainAxis =
6475 isFlexItem && flexMainAxis == eLogicalAxisInline;
6476 const auto& minBSizeCoord = stylePos->MinBSize(aWM);
6477 const auto& maxBSizeCoord = stylePos->MaxBSize(aWM);
6478 const bool isAutoMinBSize =
6479 nsLayoutUtils::IsAutoBSize(minBSizeCoord, aCBSize.BSize(aWM));
6480 const bool isAutoMaxBSize =
6481 nsLayoutUtils::IsAutoBSize(maxBSizeCoord, aCBSize.BSize(aWM));
6482 if (aspectRatio && !isDefiniteISize && !isFlexItemInlineAxisMainAxis) {
6483 const MinMaxSize minMaxBSize{
6484 isAutoMinBSize ? 0
6485 : nsLayoutUtils::ComputeBSizeValue(
6486 aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
6487 minBSizeCoord.AsLengthPercentage()),
6488 isAutoMaxBSize ? NS_UNCONSTRAINEDSIZE
6489 : nsLayoutUtils::ComputeBSizeValue(
6490 aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
6491 maxBSizeCoord.AsLengthPercentage())};
6492 MinMaxSize transferredMinMaxISize = ComputeTransferredMinMaxInlineSize(
6493 aWM, aspectRatio, minMaxBSize, boxSizingAdjust);
6494
6495 result.ISize(aWM) =
6496 transferredMinMaxISize.ClampSizeToMinAndMax(result.ISize(aWM));
6497 }
6498
6499 // Flex items ignore their min & max sizing properties in their
6500 // flex container's main-axis. (Those properties get applied later in
6501 // the flexbox algorithm.)
6502 const auto& maxISizeCoord = stylePos->MaxISize(aWM);
6503 nscoord maxISize = NS_UNCONSTRAINEDSIZE;
6504 if (!maxISizeCoord.IsNone() && !isFlexItemInlineAxisMainAxis) {
6505 maxISize = ComputeISizeValue(aRenderingContext, aWM, aCBSize,
6506 boxSizingAdjust, boxSizingToMarginEdgeISize,
6507 maxISizeCoord, aSizeOverrides, aFlags)
6508 .mISize;
6509 result.ISize(aWM) = std::min(maxISize, result.ISize(aWM));
6510 }
6511
6512 const auto& minISizeCoord = stylePos->MinISize(aWM);
6513 nscoord minISize;
6514 if (!minISizeCoord.IsAuto() && !isFlexItemInlineAxisMainAxis) {
6515 minISize = ComputeISizeValue(aRenderingContext, aWM, aCBSize,
6516 boxSizingAdjust, boxSizingToMarginEdgeISize,
6517 minISizeCoord, aSizeOverrides, aFlags)
6518 .mISize;
6519 } else if (MOZ_UNLIKELY(
6520 aFlags.contains(ComputeSizeFlag::IApplyAutoMinSize))) {
6521 // This implements "Implied Minimum Size of Grid Items".
6522 // https://drafts.csswg.org/css-grid/#min-size-auto
6523 minISize = std::min(maxISize, GetMinISize(aRenderingContext));
6524 if (styleISize.IsLengthPercentage()) {
6525 minISize = std::min(minISize, result.ISize(aWM));
6526 } else if (aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize)) {
6527 // "if the grid item spans only grid tracks that have a fixed max track
6528 // sizing function, its automatic minimum size in that dimension is
6529 // further clamped to less than or equal to the size necessary to fit
6530 // its margin box within the resulting grid area (flooring at zero)"
6531 // https://drafts.csswg.org/css-grid/#min-size-auto
6532 auto maxMinISize =
6533 std::max(nscoord(0), aCBSize.ISize(aWM) - aBorderPadding.ISize(aWM) -
6534 aMargin.ISize(aWM));
6535 minISize = std::min(minISize, maxMinISize);
6536 }
6537 } else if (aspectRatioUsage == AspectRatioUsage::ToComputeISize &&
6538 ShouldApplyAutomaticMinimumOnInlineAxis(aWM, disp, stylePos)) {
6539 // This means we successfully applied aspect-ratio and now need to check
6540 // if we need to apply the implied minimum size:
6541 // https://drafts.csswg.org/css-sizing-4/#aspect-ratio-minimum
6542 MOZ_ASSERT(!IsFrameOfType(eReplacedSizing),
6543 "aspect-ratio minimums should not apply to replaced elements");
6544 // The inline size computed by aspect-ratio shouldn't less than the content
6545 // size.
6546 minISize = GetMinISize(aRenderingContext);
6547 } else {
6548 // Treat "min-width: auto" as 0.
6549 // NOTE: Technically, "auto" is supposed to behave like "min-content" on
6550 // flex items. However, we don't need to worry about that here, because
6551 // flex items' min-sizes are intentionally ignored until the flex
6552 // container explicitly considers them during space distribution.
6553 minISize = 0;
6554 }
6555 result.ISize(aWM) = std::max(minISize, result.ISize(aWM));
6556
6557 // Compute block-axis size
6558 // (but not if we have auto bsize or if we received the "UseAutoBSize"
6559 // flag -- then, we'll just stick with the bsize that we already calculated
6560 // in the initial ComputeAutoSize() call. However, if we have a valid
6561 // preferred aspect ratio, we still have to compute the block size because
6562 // aspect ratio affects the intrinsic content size.)
6563 if (!isAutoBSize) {
6564 result.BSize(aWM) = nsLayoutUtils::ComputeBSizeValue(
6565 aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
6566 styleBSize.AsLengthPercentage());
6567 } else if (MOZ_UNLIKELY(isGridItem) &&
6568 // FIXME: Any better way to refine the auto check here?
6569 styleBSize.IsAuto() &&
6570 !aFlags.contains(ComputeSizeFlag::UseAutoBSize) &&
6571 !IsTrueOverflowContainer() &&
6572 !alignCB->IsMasonry(isOrthogonal ? eLogicalAxisInline
6573 : eLogicalAxisBlock)) {
6574 auto cbSize = aCBSize.BSize(aWM);
6575 if (cbSize != NS_UNCONSTRAINEDSIZE) {
6576 // 'auto' block-size for grid-level box - fill the CB for 'stretch' /
6577 // 'normal' and clamp it to the CB if requested:
6578 bool stretch = false;
6579 bool mayUseAspectRatio =
6580 aspectRatio && result.ISize(aWM) != NS_UNCONSTRAINEDSIZE;
6581 if (!StyleMargin()->HasBlockAxisAuto(aWM)) {
6582 auto blockAxisAlignment =
6583 isOrthogonal ? StylePosition()->UsedJustifySelf(alignCB->Style())._0
6584 : StylePosition()->UsedAlignSelf(alignCB->Style())._0;
6585 stretch = blockAxisAlignment == StyleAlignFlags::STRETCH ||
6586 (blockAxisAlignment == StyleAlignFlags::NORMAL &&
6587 !mayUseAspectRatio);
6588 }
6589
6590 // Apply the preferred aspect ratio for alignments other than *stretch*
6591 // and *normal without aspect ratio*.
6592 // The spec says all other values should size the items as fit-content,
6593 // and the intrinsic size should respect the preferred aspect ratio, so
6594 // we also apply aspect ratio for all other values.
6595 // https://drafts.csswg.org/css-grid/#grid-item-sizing
6596 if (!stretch && mayUseAspectRatio) {
6597 result.BSize(aWM) = aspectRatio.ComputeRatioDependentSize(
6598 LogicalAxis::eLogicalAxisBlock, aWM, result.ISize(aWM),
6599 boxSizingAdjust);
6600 MOZ_ASSERT(aspectRatioUsage == AspectRatioUsage::None);
6601 aspectRatioUsage = AspectRatioUsage::ToComputeBSize;
6602 }
6603
6604 if (stretch || aFlags.contains(ComputeSizeFlag::BClampMarginBoxMinSize)) {
6605 auto bSizeToFillCB =
6606 std::max(nscoord(0),
6607 cbSize - aBorderPadding.BSize(aWM) - aMargin.BSize(aWM));
6608 if (stretch || (result.BSize(aWM) != NS_UNCONSTRAINEDSIZE &&
6609 result.BSize(aWM) > bSizeToFillCB)) {
6610 result.BSize(aWM) = bSizeToFillCB;
6611 }
6612 }
6613 }
6614 } else if (aspectRatio) {
6615 // If both inline and block dimensions are auto, the block axis is the
6616 // ratio-dependent axis by default.
6617 // If we have a super large inline size, aspect-ratio should still be
6618 // applied (so aspectRatioUsage flag is set as expected). That's why we
6619 // apply aspect-ratio unconditionally for auto block size here.
6620 result.BSize(aWM) = aspectRatio.ComputeRatioDependentSize(
6621 LogicalAxis::eLogicalAxisBlock, aWM, result.ISize(aWM),
6622 boxSizingAdjust);
6623 MOZ_ASSERT(aspectRatioUsage == AspectRatioUsage::None);
6624 aspectRatioUsage = AspectRatioUsage::ToComputeBSize;
6625 }
6626
6627 if (result.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
6628 const bool isFlexItemBlockAxisMainAxis =
6629 isFlexItem && flexMainAxis == eLogicalAxisBlock;
6630 if (!isAutoMaxBSize && !isFlexItemBlockAxisMainAxis) {
6631 nscoord maxBSize = nsLayoutUtils::ComputeBSizeValue(
6632 aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
6633 maxBSizeCoord.AsLengthPercentage());
6634 result.BSize(aWM) = std::min(maxBSize, result.BSize(aWM));
6635 }
6636
6637 if (!isAutoMinBSize && !isFlexItemBlockAxisMainAxis) {
6638 nscoord minBSize = nsLayoutUtils::ComputeBSizeValue(
6639 aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
6640 minBSizeCoord.AsLengthPercentage());
6641 result.BSize(aWM) = std::max(minBSize, result.BSize(aWM));
6642 }
6643 }
6644
6645 if (IsThemed(disp)) {
6646 LayoutDeviceIntSize widget;
6647 bool canOverride = true;
6648 nsPresContext* presContext = PresContext();
6649 presContext->Theme()->GetMinimumWidgetSize(
6650 presContext, this, disp->EffectiveAppearance(), &widget, &canOverride);
6651
6652 // Convert themed widget's physical dimensions to logical coords
6653 LogicalSize size(aWM,
6654 nsSize(presContext->DevPixelsToAppUnits(widget.width),
6655 presContext->DevPixelsToAppUnits(widget.height)));
6656
6657 // GetMinimumWidgetSize() returns border-box; we need content-box.
6658 size -= aBorderPadding;
6659
6660 if (size.BSize(aWM) > result.BSize(aWM) || !canOverride) {
6661 result.BSize(aWM) = size.BSize(aWM);
6662 }
6663 if (size.ISize(aWM) > result.ISize(aWM) || !canOverride) {
6664 result.ISize(aWM) = size.ISize(aWM);
6665 }
6666 }
6667
6668 result.ISize(aWM) = std::max(0, result.ISize(aWM));
6669 result.BSize(aWM) = std::max(0, result.BSize(aWM));
6670
6671 return {result, aspectRatioUsage};
6672 }
6673
ComputeTightBounds(DrawTarget * aDrawTarget) const6674 nsRect nsIFrame::ComputeTightBounds(DrawTarget* aDrawTarget) const {
6675 return InkOverflowRect();
6676 }
6677
6678 /* virtual */
GetPrefWidthTightBounds(gfxContext * aContext,nscoord * aX,nscoord * aXMost)6679 nsresult nsIFrame::GetPrefWidthTightBounds(gfxContext* aContext, nscoord* aX,
6680 nscoord* aXMost) {
6681 return NS_ERROR_NOT_IMPLEMENTED;
6682 }
6683
6684 /* virtual */
ComputeAutoSize(gfxContext * aRenderingContext,WritingMode aWM,const mozilla::LogicalSize & aCBSize,nscoord aAvailableISize,const mozilla::LogicalSize & aMargin,const mozilla::LogicalSize & aBorderPadding,const StyleSizeOverrides & aSizeOverrides,ComputeSizeFlags aFlags)6685 LogicalSize nsIFrame::ComputeAutoSize(
6686 gfxContext* aRenderingContext, WritingMode aWM,
6687 const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize,
6688 const mozilla::LogicalSize& aMargin,
6689 const mozilla::LogicalSize& aBorderPadding,
6690 const StyleSizeOverrides& aSizeOverrides, ComputeSizeFlags aFlags) {
6691 // Use basic shrink-wrapping as a default implementation.
6692 LogicalSize result(aWM, 0xdeadbeef, NS_UNCONSTRAINEDSIZE);
6693
6694 // don't bother setting it if the result won't be used
6695 const auto& styleISize = aSizeOverrides.mStyleISize
6696 ? *aSizeOverrides.mStyleISize
6697 : StylePosition()->ISize(aWM);
6698 if (styleISize.IsAuto()) {
6699 nscoord availBased =
6700 aAvailableISize - aMargin.ISize(aWM) - aBorderPadding.ISize(aWM);
6701 result.ISize(aWM) = ShrinkWidthToFit(aRenderingContext, availBased, aFlags);
6702 }
6703 return result;
6704 }
6705
ShrinkWidthToFit(gfxContext * aRenderingContext,nscoord aISizeInCB,ComputeSizeFlags aFlags)6706 nscoord nsIFrame::ShrinkWidthToFit(gfxContext* aRenderingContext,
6707 nscoord aISizeInCB,
6708 ComputeSizeFlags aFlags) {
6709 // If we're a container for font size inflation, then shrink
6710 // wrapping inside of us should not apply font size inflation.
6711 AutoMaybeDisableFontInflation an(this);
6712
6713 nscoord result;
6714 nscoord minISize = GetMinISize(aRenderingContext);
6715 if (minISize > aISizeInCB) {
6716 const bool clamp = aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize);
6717 result = MOZ_UNLIKELY(clamp) ? aISizeInCB : minISize;
6718 } else {
6719 nscoord prefISize = GetPrefISize(aRenderingContext);
6720 if (prefISize > aISizeInCB) {
6721 result = aISizeInCB;
6722 } else {
6723 result = prefISize;
6724 }
6725 }
6726 return result;
6727 }
6728
ComputeInlineSizeFromAspectRatio(WritingMode aWM,const LogicalSize & aCBSize,const LogicalSize & aContentEdgeToBoxSizing,const StyleSizeOverrides & aSizeOverrides,ComputeSizeFlags aFlags) const6729 Maybe<nscoord> nsIFrame::ComputeInlineSizeFromAspectRatio(
6730 WritingMode aWM, const LogicalSize& aCBSize,
6731 const LogicalSize& aContentEdgeToBoxSizing,
6732 const StyleSizeOverrides& aSizeOverrides, ComputeSizeFlags aFlags) const {
6733 // FIXME: Bug 1670151: Use GetAspectRatio() to cover replaced elements (and
6734 // then we can drop the check of eSupportsAspectRatio).
6735 const AspectRatio aspectRatio =
6736 aSizeOverrides.mAspectRatio
6737 ? *aSizeOverrides.mAspectRatio
6738 : StylePosition()->mAspectRatio.ToLayoutRatio();
6739 if (!IsFrameOfType(eSupportsAspectRatio) || !aspectRatio) {
6740 return Nothing();
6741 }
6742
6743 const StyleSize& styleBSize = aSizeOverrides.mStyleBSize
6744 ? *aSizeOverrides.mStyleBSize
6745 : StylePosition()->BSize(aWM);
6746 if (aFlags.contains(ComputeSizeFlag::UseAutoBSize) ||
6747 nsLayoutUtils::IsAutoBSize(styleBSize, aCBSize.BSize(aWM))) {
6748 return Nothing();
6749 }
6750
6751 MOZ_ASSERT(styleBSize.IsLengthPercentage());
6752 nscoord bSize = nsLayoutUtils::ComputeBSizeValue(
6753 aCBSize.BSize(aWM), aContentEdgeToBoxSizing.BSize(aWM),
6754 styleBSize.AsLengthPercentage());
6755 return Some(aspectRatio.ComputeRatioDependentSize(
6756 LogicalAxis::eLogicalAxisInline, aWM, bSize, aContentEdgeToBoxSizing));
6757 }
6758
ComputeISizeValue(gfxContext * aRenderingContext,const WritingMode aWM,const LogicalSize & aContainingBlockSize,const LogicalSize & aContentEdgeToBoxSizing,nscoord aBoxSizingToMarginEdge,ExtremumLength aSize,Maybe<nscoord> aAvailableISizeOverride,const StyleSizeOverrides & aSizeOverrides,ComputeSizeFlags aFlags)6759 nsIFrame::ISizeComputationResult nsIFrame::ComputeISizeValue(
6760 gfxContext* aRenderingContext, const WritingMode aWM,
6761 const LogicalSize& aContainingBlockSize,
6762 const LogicalSize& aContentEdgeToBoxSizing, nscoord aBoxSizingToMarginEdge,
6763 ExtremumLength aSize, Maybe<nscoord> aAvailableISizeOverride,
6764 const StyleSizeOverrides& aSizeOverrides, ComputeSizeFlags aFlags) {
6765 // If 'this' is a container for font size inflation, then shrink
6766 // wrapping inside of it should not apply font size inflation.
6767 AutoMaybeDisableFontInflation an(this);
6768 // If we have an aspect-ratio and a definite block size, we resolve the
6769 // min-content and max-content size by the aspect-ratio and the block size.
6770 // https://github.com/w3c/csswg-drafts/issues/5032
6771 Maybe<nscoord> intrinsicSizeFromAspectRatio =
6772 aSize == ExtremumLength::MozAvailable
6773 ? Nothing()
6774 : ComputeInlineSizeFromAspectRatio(aWM, aContainingBlockSize,
6775 aContentEdgeToBoxSizing,
6776 aSizeOverrides, aFlags);
6777 nscoord result;
6778 switch (aSize) {
6779 case ExtremumLength::MaxContent:
6780 result = intrinsicSizeFromAspectRatio ? *intrinsicSizeFromAspectRatio
6781 : GetPrefISize(aRenderingContext);
6782 NS_ASSERTION(result >= 0, "inline-size less than zero");
6783 return {result, intrinsicSizeFromAspectRatio
6784 ? AspectRatioUsage::ToComputeISize
6785 : AspectRatioUsage::None};
6786 case ExtremumLength::MinContent:
6787 result = intrinsicSizeFromAspectRatio ? *intrinsicSizeFromAspectRatio
6788 : GetMinISize(aRenderingContext);
6789 NS_ASSERTION(result >= 0, "inline-size less than zero");
6790 if (MOZ_UNLIKELY(
6791 aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize))) {
6792 auto available =
6793 aContainingBlockSize.ISize(aWM) -
6794 (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing.ISize(aWM));
6795 result = std::min(available, result);
6796 }
6797 return {result, intrinsicSizeFromAspectRatio
6798 ? AspectRatioUsage::ToComputeISize
6799 : AspectRatioUsage::None};
6800 case ExtremumLength::FitContentFunction:
6801 case ExtremumLength::FitContent: {
6802 nscoord pref = NS_UNCONSTRAINEDSIZE;
6803 nscoord min = 0;
6804 if (intrinsicSizeFromAspectRatio) {
6805 // The min-content and max-content size are identical and equal to the
6806 // size computed from the block size and the aspect ratio.
6807 pref = min = *intrinsicSizeFromAspectRatio;
6808 } else {
6809 pref = GetPrefISize(aRenderingContext);
6810 min = GetMinISize(aRenderingContext);
6811 }
6812
6813 nscoord fill = aAvailableISizeOverride
6814 ? *aAvailableISizeOverride
6815 : aContainingBlockSize.ISize(aWM) -
6816 (aBoxSizingToMarginEdge +
6817 aContentEdgeToBoxSizing.ISize(aWM));
6818
6819 if (MOZ_UNLIKELY(
6820 aFlags.contains(ComputeSizeFlag::IClampMarginBoxMinSize))) {
6821 min = std::min(min, fill);
6822 }
6823 result = std::max(min, std::min(pref, fill));
6824 NS_ASSERTION(result >= 0, "inline-size less than zero");
6825 return {result};
6826 }
6827 case ExtremumLength::MozAvailable:
6828 return {aContainingBlockSize.ISize(aWM) -
6829 (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing.ISize(aWM))};
6830 }
6831 MOZ_ASSERT_UNREACHABLE("Unknown extremum length?");
6832 return {};
6833 }
6834
ComputeISizeValue(const WritingMode aWM,const LogicalSize & aContainingBlockSize,const LogicalSize & aContentEdgeToBoxSizing,const LengthPercentage & aSize)6835 nscoord nsIFrame::ComputeISizeValue(const WritingMode aWM,
6836 const LogicalSize& aContainingBlockSize,
6837 const LogicalSize& aContentEdgeToBoxSizing,
6838 const LengthPercentage& aSize) {
6839 LAYOUT_WARN_IF_FALSE(
6840 aContainingBlockSize.ISize(aWM) != NS_UNCONSTRAINEDSIZE,
6841 "have unconstrained inline-size; this should only result from "
6842 "very large sizes, not attempts at intrinsic inline-size "
6843 "calculation");
6844 NS_ASSERTION(aContainingBlockSize.ISize(aWM) >= 0,
6845 "inline-size less than zero");
6846
6847 nscoord result = aSize.Resolve(aContainingBlockSize.ISize(aWM));
6848 // The result of a calc() expression might be less than 0; we
6849 // should clamp at runtime (below). (Percentages and coords that
6850 // are less than 0 have already been dropped by the parser.)
6851 result -= aContentEdgeToBoxSizing.ISize(aWM);
6852 return std::max(0, result);
6853 }
6854
DidReflow(nsPresContext * aPresContext,const ReflowInput * aReflowInput)6855 void nsIFrame::DidReflow(nsPresContext* aPresContext,
6856 const ReflowInput* aReflowInput) {
6857 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("nsIFrame::DidReflow"));
6858
6859 SVGObserverUtils::InvalidateDirectRenderingObservers(
6860 this, SVGObserverUtils::INVALIDATE_REFLOW);
6861
6862 RemoveStateBits(NS_FRAME_IN_REFLOW | NS_FRAME_FIRST_REFLOW |
6863 NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
6864
6865 // Clear bits that were used in ReflowInput::InitResizeFlags (see
6866 // comment there for why we can't clear it there).
6867 SetHasBSizeChange(false);
6868 SetHasPaddingChange(false);
6869
6870 // Notify the percent bsize observer if there is a percent bsize.
6871 // The observer may be able to initiate another reflow with a computed
6872 // bsize. This happens in the case where a table cell has no computed
6873 // bsize but can fabricate one when the cell bsize is known.
6874 if (aReflowInput && aReflowInput->mPercentBSizeObserver && !GetPrevInFlow()) {
6875 const auto& bsize =
6876 aReflowInput->mStylePosition->BSize(aReflowInput->GetWritingMode());
6877 if (bsize.HasPercent()) {
6878 aReflowInput->mPercentBSizeObserver->NotifyPercentBSize(*aReflowInput);
6879 }
6880 }
6881
6882 aPresContext->ReflowedFrame();
6883
6884 #ifdef ACCESSIBILITY
6885 if (nsAccessibilityService* accService =
6886 PresShell::GetAccessibilityService()) {
6887 accService->NotifyOfPossibleBoundsChange(PresShell(), mContent);
6888 }
6889 #endif
6890 }
6891
FinishReflowWithAbsoluteFrames(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus,bool aConstrainBSize)6892 void nsIFrame::FinishReflowWithAbsoluteFrames(nsPresContext* aPresContext,
6893 ReflowOutput& aDesiredSize,
6894 const ReflowInput& aReflowInput,
6895 nsReflowStatus& aStatus,
6896 bool aConstrainBSize) {
6897 ReflowAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput, aStatus,
6898 aConstrainBSize);
6899
6900 FinishAndStoreOverflow(&aDesiredSize, aReflowInput.mStyleDisplay);
6901 }
6902
ReflowAbsoluteFrames(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus,bool aConstrainBSize)6903 void nsIFrame::ReflowAbsoluteFrames(nsPresContext* aPresContext,
6904 ReflowOutput& aDesiredSize,
6905 const ReflowInput& aReflowInput,
6906 nsReflowStatus& aStatus,
6907 bool aConstrainBSize) {
6908 if (HasAbsolutelyPositionedChildren()) {
6909 nsAbsoluteContainingBlock* absoluteContainer = GetAbsoluteContainingBlock();
6910
6911 // Let the absolutely positioned container reflow any absolutely positioned
6912 // child frames that need to be reflowed
6913
6914 // The containing block for the abs pos kids is formed by our padding edge.
6915 nsMargin usedBorder = GetUsedBorder();
6916 nscoord containingBlockWidth =
6917 std::max(0, aDesiredSize.Width() - usedBorder.LeftRight());
6918 nscoord containingBlockHeight =
6919 std::max(0, aDesiredSize.Height() - usedBorder.TopBottom());
6920 nsContainerFrame* container = do_QueryFrame(this);
6921 NS_ASSERTION(container,
6922 "Abs-pos children only supported on container frames for now");
6923
6924 nsRect containingBlock(0, 0, containingBlockWidth, containingBlockHeight);
6925 AbsPosReflowFlags flags =
6926 AbsPosReflowFlags::CBWidthAndHeightChanged; // XXX could be optimized
6927 if (aConstrainBSize) {
6928 flags |= AbsPosReflowFlags::ConstrainHeight;
6929 }
6930 absoluteContainer->Reflow(container, aPresContext, aReflowInput, aStatus,
6931 containingBlock, flags,
6932 &aDesiredSize.mOverflowAreas);
6933 }
6934 }
6935
6936 /* virtual */
CanContinueTextRun() const6937 bool nsIFrame::CanContinueTextRun() const {
6938 // By default, a frame will *not* allow a text run to be continued
6939 // through it.
6940 return false;
6941 }
6942
Reflow(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)6943 void nsIFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
6944 const ReflowInput& aReflowInput,
6945 nsReflowStatus& aStatus) {
6946 MarkInReflow();
6947 DO_GLOBAL_REFLOW_COUNT("nsFrame");
6948 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
6949 aDesiredSize.ClearSize();
6950 NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
6951 }
6952
IsContentDisabled() const6953 bool nsIFrame::IsContentDisabled() const {
6954 // FIXME(emilio): Doing this via CSS means callers must ensure the style is up
6955 // to date, and they don't!
6956 if (StyleUI()->UserInput() == StyleUserInput::None) {
6957 return true;
6958 }
6959
6960 auto* element = nsGenericHTMLElement::FromNodeOrNull(GetContent());
6961 return element && element->IsDisabled();
6962 }
6963
CharacterDataChanged(const CharacterDataChangeInfo &)6964 nsresult nsIFrame::CharacterDataChanged(const CharacterDataChangeInfo&) {
6965 MOZ_ASSERT_UNREACHABLE("should only be called for text frames");
6966 return NS_OK;
6967 }
6968
AttributeChanged(int32_t aNameSpaceID,nsAtom * aAttribute,int32_t aModType)6969 nsresult nsIFrame::AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute,
6970 int32_t aModType) {
6971 return NS_OK;
6972 }
6973
6974 // Flow member functions
6975
GetPrevContinuation() const6976 nsIFrame* nsIFrame::GetPrevContinuation() const { return nullptr; }
6977
SetPrevContinuation(nsIFrame * aPrevContinuation)6978 void nsIFrame::SetPrevContinuation(nsIFrame* aPrevContinuation) {
6979 MOZ_ASSERT(false, "not splittable");
6980 }
6981
GetNextContinuation() const6982 nsIFrame* nsIFrame::GetNextContinuation() const { return nullptr; }
6983
SetNextContinuation(nsIFrame *)6984 void nsIFrame::SetNextContinuation(nsIFrame*) {
6985 MOZ_ASSERT(false, "not splittable");
6986 }
6987
GetPrevInFlow() const6988 nsIFrame* nsIFrame::GetPrevInFlow() const { return nullptr; }
6989
SetPrevInFlow(nsIFrame * aPrevInFlow)6990 void nsIFrame::SetPrevInFlow(nsIFrame* aPrevInFlow) {
6991 MOZ_ASSERT(false, "not splittable");
6992 }
6993
GetNextInFlow() const6994 nsIFrame* nsIFrame::GetNextInFlow() const { return nullptr; }
6995
SetNextInFlow(nsIFrame *)6996 void nsIFrame::SetNextInFlow(nsIFrame*) { MOZ_ASSERT(false, "not splittable"); }
6997
GetTailContinuation()6998 nsIFrame* nsIFrame::GetTailContinuation() {
6999 nsIFrame* frame = this;
7000 while (frame->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
7001 frame = frame->GetPrevContinuation();
7002 NS_ASSERTION(frame, "first continuation can't be overflow container");
7003 }
7004 for (nsIFrame* next = frame->GetNextContinuation();
7005 next && !next->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
7006 next = frame->GetNextContinuation()) {
7007 frame = next;
7008 }
7009
7010 MOZ_ASSERT(frame, "illegal state in continuation chain.");
7011 return frame;
7012 }
7013
7014 // Associated view object
SetView(nsView * aView)7015 void nsIFrame::SetView(nsView* aView) {
7016 if (aView) {
7017 aView->SetFrame(this);
7018
7019 #ifdef DEBUG
7020 LayoutFrameType frameType = Type();
7021 NS_ASSERTION(frameType == LayoutFrameType::SubDocument ||
7022 frameType == LayoutFrameType::ListControl ||
7023 frameType == LayoutFrameType::Viewport ||
7024 frameType == LayoutFrameType::MenuPopup,
7025 "Only specific frame types can have an nsView");
7026 #endif
7027
7028 // Store the view on the frame.
7029 SetViewInternal(aView);
7030
7031 // Set the frame state bit that says the frame has a view
7032 AddStateBits(NS_FRAME_HAS_VIEW);
7033
7034 // Let all of the ancestors know they have a descendant with a view.
7035 for (nsIFrame* f = GetParent();
7036 f && !f->HasAnyStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
7037 f = f->GetParent())
7038 f->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
7039 } else {
7040 MOZ_ASSERT_UNREACHABLE("Destroying a view while the frame is alive?");
7041 RemoveStateBits(NS_FRAME_HAS_VIEW);
7042 SetViewInternal(nullptr);
7043 }
7044 }
7045
7046 // Find the first geometric parent that has a view
GetAncestorWithView() const7047 nsIFrame* nsIFrame::GetAncestorWithView() const {
7048 for (nsIFrame* f = GetParent(); nullptr != f; f = f->GetParent()) {
7049 if (f->HasView()) {
7050 return f;
7051 }
7052 }
7053 return nullptr;
7054 }
7055
7056 template <nsPoint (nsIFrame::*PositionGetter)() const>
OffsetCalculator(const nsIFrame * aThis,const nsIFrame * aOther)7057 static nsPoint OffsetCalculator(const nsIFrame* aThis, const nsIFrame* aOther) {
7058 MOZ_ASSERT(aOther, "Must have frame for destination coordinate system!");
7059
7060 NS_ASSERTION(aThis->PresContext() == aOther->PresContext(),
7061 "GetOffsetTo called on frames in different documents");
7062
7063 nsPoint offset(0, 0);
7064 const nsIFrame* f;
7065 for (f = aThis; f != aOther && f; f = f->GetParent()) {
7066 offset += (f->*PositionGetter)();
7067 }
7068
7069 if (f != aOther) {
7070 // Looks like aOther wasn't an ancestor of |this|. So now we have
7071 // the root-frame-relative position of |this| in |offset|. Convert back
7072 // to the coordinates of aOther
7073 while (aOther) {
7074 offset -= (aOther->*PositionGetter)();
7075 aOther = aOther->GetParent();
7076 }
7077 }
7078
7079 return offset;
7080 }
7081
GetOffsetTo(const nsIFrame * aOther) const7082 nsPoint nsIFrame::GetOffsetTo(const nsIFrame* aOther) const {
7083 return OffsetCalculator<&nsIFrame::GetPosition>(this, aOther);
7084 }
7085
GetOffsetToIgnoringScrolling(const nsIFrame * aOther) const7086 nsPoint nsIFrame::GetOffsetToIgnoringScrolling(const nsIFrame* aOther) const {
7087 return OffsetCalculator<&nsIFrame::GetPositionIgnoringScrolling>(this,
7088 aOther);
7089 }
7090
GetOffsetToCrossDoc(const nsIFrame * aOther) const7091 nsPoint nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther) const {
7092 return GetOffsetToCrossDoc(aOther, PresContext()->AppUnitsPerDevPixel());
7093 }
7094
GetOffsetToCrossDoc(const nsIFrame * aOther,const int32_t aAPD) const7095 nsPoint nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther,
7096 const int32_t aAPD) const {
7097 MOZ_ASSERT(aOther, "Must have frame for destination coordinate system!");
7098 NS_ASSERTION(PresContext()->GetRootPresContext() ==
7099 aOther->PresContext()->GetRootPresContext(),
7100 "trying to get the offset between frames in different document "
7101 "hierarchies?");
7102 if (PresContext()->GetRootPresContext() !=
7103 aOther->PresContext()->GetRootPresContext()) {
7104 // crash right away, we are almost certainly going to crash anyway.
7105 MOZ_CRASH(
7106 "trying to get the offset between frames in different "
7107 "document hierarchies?");
7108 }
7109
7110 const nsIFrame* root = nullptr;
7111 // offset will hold the final offset
7112 // docOffset holds the currently accumulated offset at the current APD, it
7113 // will be converted and added to offset when the current APD changes.
7114 nsPoint offset(0, 0), docOffset(0, 0);
7115 const nsIFrame* f = this;
7116 int32_t currAPD = PresContext()->AppUnitsPerDevPixel();
7117 while (f && f != aOther) {
7118 docOffset += f->GetPosition();
7119 nsIFrame* parent = f->GetParent();
7120 if (parent) {
7121 f = parent;
7122 } else {
7123 nsPoint newOffset(0, 0);
7124 root = f;
7125 f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f, &newOffset);
7126 int32_t newAPD = f ? f->PresContext()->AppUnitsPerDevPixel() : 0;
7127 if (!f || newAPD != currAPD) {
7128 // Convert docOffset to the right APD and add it to offset.
7129 offset += docOffset.ScaleToOtherAppUnits(currAPD, aAPD);
7130 docOffset.x = docOffset.y = 0;
7131 }
7132 currAPD = newAPD;
7133 docOffset += newOffset;
7134 }
7135 }
7136 if (f == aOther) {
7137 offset += docOffset.ScaleToOtherAppUnits(currAPD, aAPD);
7138 } else {
7139 // Looks like aOther wasn't an ancestor of |this|. So now we have
7140 // the root-document-relative position of |this| in |offset|. Subtract the
7141 // root-document-relative position of |aOther| from |offset|.
7142 // This call won't try to recurse again because root is an ancestor of
7143 // aOther.
7144 nsPoint negOffset = aOther->GetOffsetToCrossDoc(root, aAPD);
7145 offset -= negOffset;
7146 }
7147
7148 return offset;
7149 }
7150
GetScreenRect() const7151 CSSIntRect nsIFrame::GetScreenRect() const {
7152 return CSSIntRect::FromAppUnitsToNearest(GetScreenRectInAppUnits());
7153 }
7154
GetScreenRectInAppUnits() const7155 nsRect nsIFrame::GetScreenRectInAppUnits() const {
7156 nsPresContext* presContext = PresContext();
7157 nsIFrame* rootFrame = presContext->PresShell()->GetRootFrame();
7158 nsPoint rootScreenPos(0, 0);
7159 nsPoint rootFrameOffsetInParent(0, 0);
7160 nsIFrame* rootFrameParent = nsLayoutUtils::GetCrossDocParentFrameInProcess(
7161 rootFrame, &rootFrameOffsetInParent);
7162 if (rootFrameParent) {
7163 nsRect parentScreenRectAppUnits =
7164 rootFrameParent->GetScreenRectInAppUnits();
7165 nsPresContext* parentPresContext = rootFrameParent->PresContext();
7166 double parentScale = double(presContext->AppUnitsPerDevPixel()) /
7167 parentPresContext->AppUnitsPerDevPixel();
7168 nsPoint rootPt =
7169 parentScreenRectAppUnits.TopLeft() + rootFrameOffsetInParent;
7170 rootScreenPos.x = NS_round(parentScale * rootPt.x);
7171 rootScreenPos.y = NS_round(parentScale * rootPt.y);
7172 } else {
7173 nsCOMPtr<nsIWidget> rootWidget =
7174 presContext->PresShell()->GetViewManager()->GetRootWidget();
7175 if (rootWidget) {
7176 LayoutDeviceIntPoint rootDevPx = rootWidget->WidgetToScreenOffset();
7177 rootScreenPos.x = presContext->DevPixelsToAppUnits(rootDevPx.x);
7178 rootScreenPos.y = presContext->DevPixelsToAppUnits(rootDevPx.y);
7179 }
7180 }
7181
7182 return nsRect(rootScreenPos + GetOffsetTo(rootFrame), GetSize());
7183 }
7184
7185 // Returns the offset from this frame to the closest geometric parent that
7186 // has a view. Also returns the containing view or null in case of error
GetOffsetFromView(nsPoint & aOffset,nsView ** aView) const7187 void nsIFrame::GetOffsetFromView(nsPoint& aOffset, nsView** aView) const {
7188 MOZ_ASSERT(nullptr != aView, "null OUT parameter pointer");
7189 nsIFrame* frame = const_cast<nsIFrame*>(this);
7190
7191 *aView = nullptr;
7192 aOffset.MoveTo(0, 0);
7193 do {
7194 aOffset += frame->GetPosition();
7195 frame = frame->GetParent();
7196 } while (frame && !frame->HasView());
7197
7198 if (frame) {
7199 *aView = frame->GetView();
7200 }
7201 }
7202
GetNearestWidget() const7203 nsIWidget* nsIFrame::GetNearestWidget() const {
7204 return GetClosestView()->GetNearestWidget(nullptr);
7205 }
7206
GetNearestWidget(nsPoint & aOffset) const7207 nsIWidget* nsIFrame::GetNearestWidget(nsPoint& aOffset) const {
7208 nsPoint offsetToView;
7209 nsPoint offsetToWidget;
7210 nsIWidget* widget =
7211 GetClosestView(&offsetToView)->GetNearestWidget(&offsetToWidget);
7212 aOffset = offsetToView + offsetToWidget;
7213 return widget;
7214 }
7215
GetTransformMatrix(ViewportType aViewportType,RelativeTo aStopAtAncestor,nsIFrame ** aOutAncestor,uint32_t aFlags) const7216 Matrix4x4Flagged nsIFrame::GetTransformMatrix(ViewportType aViewportType,
7217 RelativeTo aStopAtAncestor,
7218 nsIFrame** aOutAncestor,
7219 uint32_t aFlags) const {
7220 MOZ_ASSERT(aOutAncestor, "Need a place to put the ancestor!");
7221
7222 /* If we're transformed, we want to hand back the combination
7223 * transform/translate matrix that will apply our current transform, then
7224 * shift us to our parent.
7225 */
7226 const bool isTransformed = IsTransformed();
7227 const nsIFrame* zoomedContentRoot = nullptr;
7228 if (aStopAtAncestor.mViewportType == ViewportType::Visual) {
7229 zoomedContentRoot = ViewportUtils::IsZoomedContentRoot(this);
7230 if (zoomedContentRoot) {
7231 MOZ_ASSERT(aViewportType != ViewportType::Visual);
7232 }
7233 }
7234
7235 if (isTransformed || zoomedContentRoot) {
7236 Matrix4x4 result;
7237 int32_t scaleFactor =
7238 ((aFlags & IN_CSS_UNITS) ? AppUnitsPerCSSPixel()
7239 : PresContext()->AppUnitsPerDevPixel());
7240
7241 /* Compute the delta to the parent, which we need because we are converting
7242 * coordinates to our parent.
7243 */
7244 if (isTransformed) {
7245 NS_ASSERTION(nsLayoutUtils::GetCrossDocParentFrameInProcess(this),
7246 "Cannot transform the viewport frame!");
7247
7248 result = result * nsDisplayTransform::GetResultingTransformMatrix(
7249 this, nsPoint(0, 0), scaleFactor,
7250 nsDisplayTransform::INCLUDE_PERSPECTIVE |
7251 nsDisplayTransform::OFFSET_BY_ORIGIN);
7252 }
7253
7254 // The offset from a zoomed content root to its parent (e.g. from
7255 // a canvas frame to a scroll frame) is in layout coordinates, so
7256 // apply it before applying any layout-to-visual transform.
7257 *aOutAncestor = nsLayoutUtils::GetCrossDocParentFrameInProcess(this);
7258 nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
7259 /* Combine the raw transform with a translation to our parent. */
7260 result.PostTranslate(NSAppUnitsToFloatPixels(delta.x, scaleFactor),
7261 NSAppUnitsToFloatPixels(delta.y, scaleFactor), 0.0f);
7262
7263 if (zoomedContentRoot) {
7264 Matrix4x4 layoutToVisual;
7265 ScrollableLayerGuid::ViewID targetScrollId =
7266 nsLayoutUtils::FindOrCreateIDFor(zoomedContentRoot->GetContent());
7267 if (aFlags & nsIFrame::IN_CSS_UNITS) {
7268 layoutToVisual =
7269 ViewportUtils::GetVisualToLayoutTransform(targetScrollId)
7270 .Inverse()
7271 .ToUnknownMatrix();
7272 } else {
7273 layoutToVisual =
7274 ViewportUtils::GetVisualToLayoutTransform<LayoutDevicePixel>(
7275 targetScrollId)
7276 .Inverse()
7277 .ToUnknownMatrix();
7278 }
7279 result = result * layoutToVisual;
7280 }
7281
7282 return result;
7283 }
7284
7285 if (nsLayoutUtils::IsPopup(this) && IsListControlFrame()) {
7286 nsPresContext* presContext = PresContext();
7287 nsIFrame* docRootFrame = presContext->PresShell()->GetRootFrame();
7288
7289 // Compute a matrix that transforms from the popup widget to the toplevel
7290 // widget. We use the widgets because they're the simplest and most
7291 // accurate approach --- this should work no matter how the widget position
7292 // was chosen.
7293 nsIWidget* widget = GetView()->GetWidget();
7294 nsPresContext* rootPresContext = PresContext()->GetRootPresContext();
7295 // Maybe the widget hasn't been created yet? Popups without widgets are
7296 // treated as regular frames. That should work since they'll be rendered
7297 // as part of the page if they're rendered at all.
7298 if (widget && rootPresContext) {
7299 nsIWidget* toplevel = rootPresContext->GetNearestWidget();
7300 if (toplevel) {
7301 LayoutDeviceIntRect screenBounds = widget->GetClientBounds();
7302 LayoutDeviceIntRect toplevelScreenBounds = toplevel->GetClientBounds();
7303 LayoutDeviceIntPoint translation =
7304 screenBounds.TopLeft() - toplevelScreenBounds.TopLeft();
7305
7306 Matrix4x4 transformToTop;
7307 transformToTop._41 = translation.x;
7308 transformToTop._42 = translation.y;
7309
7310 *aOutAncestor = docRootFrame;
7311 Matrix4x4 docRootTransformToTop =
7312 nsLayoutUtils::GetTransformToAncestor(RelativeTo{docRootFrame},
7313 RelativeTo{nullptr})
7314 .GetMatrix();
7315 if (docRootTransformToTop.IsSingular()) {
7316 NS_WARNING(
7317 "Containing document is invisible, we can't compute a valid "
7318 "transform");
7319 } else {
7320 docRootTransformToTop.Invert();
7321 return transformToTop * docRootTransformToTop;
7322 }
7323 }
7324 }
7325 }
7326
7327 *aOutAncestor = nsLayoutUtils::GetCrossDocParentFrameInProcess(this);
7328
7329 /* Otherwise, we're not transformed. In that case, we'll walk up the frame
7330 * tree until we either hit the root frame or something that may be
7331 * transformed. We'll then change coordinates into that frame, since we're
7332 * guaranteed that nothing in-between can be transformed. First, however,
7333 * we have to check to see if we have a parent. If not, we'll set the
7334 * outparam to null (indicating that there's nothing left) and will hand back
7335 * the identity matrix.
7336 */
7337 if (!*aOutAncestor) return Matrix4x4();
7338
7339 /* Keep iterating while the frame can't possibly be transformed. */
7340 const nsIFrame* current = this;
7341 auto shouldStopAt = [](const nsIFrame* aCurrent, nsIFrame* aAncestor,
7342 uint32_t aFlags) {
7343 return aAncestor->IsTransformed() || nsLayoutUtils::IsPopup(aAncestor) ||
7344 ViewportUtils::IsZoomedContentRoot(aAncestor) ||
7345 ((aFlags & STOP_AT_STACKING_CONTEXT_AND_DISPLAY_PORT) &&
7346 (aAncestor->IsStackingContext() ||
7347 DisplayPortUtils::FrameHasDisplayPort(aAncestor, aCurrent)));
7348 };
7349 while (*aOutAncestor != aStopAtAncestor.mFrame &&
7350 !shouldStopAt(current, *aOutAncestor, aFlags)) {
7351 /* If no parent, stop iterating. Otherwise, update the ancestor. */
7352 nsIFrame* parent =
7353 nsLayoutUtils::GetCrossDocParentFrameInProcess(*aOutAncestor);
7354 if (!parent) break;
7355
7356 current = *aOutAncestor;
7357 *aOutAncestor = parent;
7358 }
7359
7360 NS_ASSERTION(*aOutAncestor, "Somehow ended up with a null ancestor...?");
7361
7362 /* Translate from this frame to our ancestor, if it exists. That's the
7363 * entire transform, so we're done.
7364 */
7365 nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
7366 int32_t scaleFactor =
7367 ((aFlags & IN_CSS_UNITS) ? AppUnitsPerCSSPixel()
7368 : PresContext()->AppUnitsPerDevPixel());
7369 return Matrix4x4::Translation(NSAppUnitsToFloatPixels(delta.x, scaleFactor),
7370 NSAppUnitsToFloatPixels(delta.y, scaleFactor),
7371 0.0f);
7372 }
7373
InvalidateRenderingObservers(nsIFrame * aDisplayRoot,nsIFrame * aFrame,bool aFrameChanged=true)7374 static void InvalidateRenderingObservers(nsIFrame* aDisplayRoot,
7375 nsIFrame* aFrame,
7376 bool aFrameChanged = true) {
7377 MOZ_ASSERT(aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame));
7378 SVGObserverUtils::InvalidateDirectRenderingObservers(aFrame);
7379 nsIFrame* parent = aFrame;
7380 while (parent != aDisplayRoot &&
7381 (parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(parent)) &&
7382 !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
7383 SVGObserverUtils::InvalidateDirectRenderingObservers(parent);
7384 }
7385
7386 if (!aFrameChanged) {
7387 return;
7388 }
7389
7390 aFrame->MarkNeedsDisplayItemRebuild();
7391 }
7392
SchedulePaintInternal(nsIFrame * aDisplayRoot,nsIFrame * aFrame,nsIFrame::PaintType aType=nsIFrame::PAINT_DEFAULT)7393 static void SchedulePaintInternal(
7394 nsIFrame* aDisplayRoot, nsIFrame* aFrame,
7395 nsIFrame::PaintType aType = nsIFrame::PAINT_DEFAULT) {
7396 MOZ_ASSERT(aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame));
7397 nsPresContext* pres = aDisplayRoot->PresContext()->GetRootPresContext();
7398
7399 // No need to schedule a paint for an external document since they aren't
7400 // painted directly.
7401 if (!pres || (pres->Document() && pres->Document()->IsResourceDoc())) {
7402 return;
7403 }
7404 if (!pres->GetContainerWeak()) {
7405 NS_WARNING("Shouldn't call SchedulePaint in a detached pres context");
7406 return;
7407 }
7408
7409 pres->PresShell()->ScheduleViewManagerFlush();
7410
7411 if (aType == nsIFrame::PAINT_DEFAULT) {
7412 aDisplayRoot->AddStateBits(NS_FRAME_UPDATE_LAYER_TREE);
7413 }
7414 }
7415
InvalidateFrameInternal(nsIFrame * aFrame,bool aHasDisplayItem,bool aRebuildDisplayItems)7416 static void InvalidateFrameInternal(nsIFrame* aFrame, bool aHasDisplayItem,
7417 bool aRebuildDisplayItems) {
7418 if (aHasDisplayItem) {
7419 aFrame->AddStateBits(NS_FRAME_NEEDS_PAINT);
7420 }
7421
7422 if (aRebuildDisplayItems) {
7423 aFrame->MarkNeedsDisplayItemRebuild();
7424 }
7425 SVGObserverUtils::InvalidateDirectRenderingObservers(aFrame);
7426 bool needsSchedulePaint = false;
7427 if (nsLayoutUtils::IsPopup(aFrame)) {
7428 needsSchedulePaint = true;
7429 } else {
7430 nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(aFrame);
7431 while (parent &&
7432 !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
7433 if (aHasDisplayItem && !parent->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
7434 parent->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
7435 }
7436 SVGObserverUtils::InvalidateDirectRenderingObservers(parent);
7437
7438 // If we're inside a popup, then we need to make sure that we
7439 // call schedule paint so that the NS_FRAME_UPDATE_LAYER_TREE
7440 // flag gets added to the popup display root frame.
7441 if (nsLayoutUtils::IsPopup(parent)) {
7442 needsSchedulePaint = true;
7443 break;
7444 }
7445 parent = nsLayoutUtils::GetCrossDocParentFrameInProcess(parent);
7446 }
7447 if (!parent) {
7448 needsSchedulePaint = true;
7449 }
7450 }
7451 if (!aHasDisplayItem) {
7452 return;
7453 }
7454 if (needsSchedulePaint) {
7455 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
7456 SchedulePaintInternal(displayRoot, aFrame);
7457 }
7458 if (aFrame->HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
7459 aFrame->RemoveProperty(nsIFrame::InvalidationRect());
7460 aFrame->RemoveStateBits(NS_FRAME_HAS_INVALID_RECT);
7461 }
7462 }
7463
InvalidateFrameSubtree(bool aRebuildDisplayItems)7464 void nsIFrame::InvalidateFrameSubtree(bool aRebuildDisplayItems /* = true */) {
7465 InvalidateFrame(0, aRebuildDisplayItems);
7466
7467 if (HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT)) {
7468 return;
7469 }
7470
7471 AddStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT);
7472
7473 for (const auto& childList : CrossDocChildLists()) {
7474 for (nsIFrame* child : childList.mList) {
7475 // Don't explicitly rebuild display items for our descendants,
7476 // since we should be marked and it implicitly includes all
7477 // descendants.
7478 child->InvalidateFrameSubtree(false);
7479 }
7480 }
7481 }
7482
ClearInvalidationStateBits()7483 void nsIFrame::ClearInvalidationStateBits() {
7484 if (HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
7485 for (const auto& childList : CrossDocChildLists()) {
7486 for (nsIFrame* child : childList.mList) {
7487 child->ClearInvalidationStateBits();
7488 }
7489 }
7490 }
7491
7492 RemoveStateBits(NS_FRAME_NEEDS_PAINT | NS_FRAME_DESCENDANT_NEEDS_PAINT |
7493 NS_FRAME_ALL_DESCENDANTS_NEED_PAINT);
7494 }
7495
HasRetainedDataFor(const nsIFrame * aFrame,uint32_t aDisplayItemKey)7496 bool HasRetainedDataFor(const nsIFrame* aFrame, uint32_t aDisplayItemKey) {
7497 if (RefPtr<WebRenderUserData> data =
7498 GetWebRenderUserData<WebRenderFallbackData>(aFrame,
7499 aDisplayItemKey)) {
7500 return true;
7501 }
7502
7503 return false;
7504 }
7505
InvalidateFrame(uint32_t aDisplayItemKey,bool aRebuildDisplayItems)7506 void nsIFrame::InvalidateFrame(uint32_t aDisplayItemKey,
7507 bool aRebuildDisplayItems /* = true */) {
7508 bool hasDisplayItem =
7509 !aDisplayItemKey || HasRetainedDataFor(this, aDisplayItemKey);
7510 InvalidateFrameInternal(this, hasDisplayItem, aRebuildDisplayItems);
7511 }
7512
InvalidateFrameWithRect(const nsRect & aRect,uint32_t aDisplayItemKey,bool aRebuildDisplayItems)7513 void nsIFrame::InvalidateFrameWithRect(const nsRect& aRect,
7514 uint32_t aDisplayItemKey,
7515 bool aRebuildDisplayItems /* = true */) {
7516 if (aRect.IsEmpty()) {
7517 return;
7518 }
7519 bool hasDisplayItem =
7520 !aDisplayItemKey || HasRetainedDataFor(this, aDisplayItemKey);
7521 bool alreadyInvalid = false;
7522 if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) {
7523 InvalidateFrameInternal(this, hasDisplayItem, aRebuildDisplayItems);
7524 } else {
7525 alreadyInvalid = true;
7526 }
7527
7528 if (!hasDisplayItem) {
7529 return;
7530 }
7531
7532 nsRect* rect;
7533 if (HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
7534 rect = GetProperty(InvalidationRect());
7535 MOZ_ASSERT(rect);
7536 } else {
7537 if (alreadyInvalid) {
7538 return;
7539 }
7540 rect = new nsRect();
7541 AddProperty(InvalidationRect(), rect);
7542 AddStateBits(NS_FRAME_HAS_INVALID_RECT);
7543 }
7544
7545 *rect = rect->Union(aRect);
7546 }
7547
7548 /*static*/
7549 uint8_t nsIFrame::sLayerIsPrerenderedDataKey;
7550
IsInvalid(nsRect & aRect)7551 bool nsIFrame::IsInvalid(nsRect& aRect) {
7552 if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) {
7553 return false;
7554 }
7555
7556 if (HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
7557 nsRect* rect = GetProperty(InvalidationRect());
7558 NS_ASSERTION(
7559 rect, "Must have an invalid rect if NS_FRAME_HAS_INVALID_RECT is set!");
7560 aRect = *rect;
7561 } else {
7562 aRect.SetEmpty();
7563 }
7564 return true;
7565 }
7566
SchedulePaint(PaintType aType,bool aFrameChanged)7567 void nsIFrame::SchedulePaint(PaintType aType, bool aFrameChanged) {
7568 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
7569 InvalidateRenderingObservers(displayRoot, this, aFrameChanged);
7570 SchedulePaintInternal(displayRoot, this, aType);
7571 }
7572
SchedulePaintWithoutInvalidatingObservers(PaintType aType)7573 void nsIFrame::SchedulePaintWithoutInvalidatingObservers(PaintType aType) {
7574 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
7575 SchedulePaintInternal(displayRoot, this, aType);
7576 }
7577
InvalidateLayer(DisplayItemType aDisplayItemKey,const nsIntRect * aDamageRect,const nsRect * aFrameDamageRect,uint32_t aFlags)7578 void nsIFrame::InvalidateLayer(DisplayItemType aDisplayItemKey,
7579 const nsIntRect* aDamageRect,
7580 const nsRect* aFrameDamageRect,
7581 uint32_t aFlags /* = 0 */) {
7582 NS_ASSERTION(aDisplayItemKey > DisplayItemType::TYPE_ZERO, "Need a key");
7583
7584 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
7585 InvalidateRenderingObservers(displayRoot, this, false);
7586
7587 // Check if frame supports WebRender's async update
7588 if ((aFlags & UPDATE_IS_ASYNC) &&
7589 WebRenderUserData::SupportsAsyncUpdate(this)) {
7590 // WebRender does not use layer, then return nullptr.
7591 return;
7592 }
7593
7594 if (aFrameDamageRect && aFrameDamageRect->IsEmpty()) {
7595 return;
7596 }
7597
7598 // In the bug 930056, dialer app startup but not shown on the
7599 // screen because sometimes we don't have any retainned data
7600 // for remote type displayitem and thus Repaint event is not
7601 // triggered. So, always invalidate in this case.
7602 DisplayItemType displayItemKey = aDisplayItemKey;
7603 if (aDisplayItemKey == DisplayItemType::TYPE_REMOTE) {
7604 displayItemKey = DisplayItemType::TYPE_ZERO;
7605 }
7606
7607 if (aFrameDamageRect) {
7608 InvalidateFrameWithRect(*aFrameDamageRect,
7609 static_cast<uint32_t>(displayItemKey));
7610 } else {
7611 InvalidateFrame(static_cast<uint32_t>(displayItemKey));
7612 }
7613 }
7614
ComputeEffectsRect(nsIFrame * aFrame,const nsRect & aOverflowRect,const nsSize & aNewSize)7615 static nsRect ComputeEffectsRect(nsIFrame* aFrame, const nsRect& aOverflowRect,
7616 const nsSize& aNewSize) {
7617 nsRect r = aOverflowRect;
7618
7619 if (aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
7620 // For SVG frames, we only need to account for filters.
7621 // TODO: We could also take account of clipPath and mask to reduce the
7622 // ink overflow, but that's not essential.
7623 if (aFrame->StyleEffects()->HasFilters()) {
7624 SetOrUpdateRectValuedProperty(aFrame, nsIFrame::PreEffectsBBoxProperty(),
7625 r);
7626 r = SVGUtils::GetPostFilterInkOverflowRect(aFrame, aOverflowRect);
7627 }
7628 return r;
7629 }
7630
7631 // box-shadow
7632 r.UnionRect(r, nsLayoutUtils::GetBoxShadowRectForFrame(aFrame, aNewSize));
7633
7634 // border-image-outset.
7635 // We need to include border-image-outset because it can cause the
7636 // border image to be drawn beyond the border box.
7637
7638 // (1) It's important we not check whether there's a border-image
7639 // since the style hint for a change in border image doesn't cause
7640 // reflow, and that's probably more important than optimizing the
7641 // overflow areas for the silly case of border-image-outset without
7642 // border-image
7643 // (2) It's important that we not check whether the border-image
7644 // is actually loaded, since that would require us to reflow when
7645 // the image loads.
7646 const nsStyleBorder* styleBorder = aFrame->StyleBorder();
7647 nsMargin outsetMargin = styleBorder->GetImageOutset();
7648
7649 if (outsetMargin != nsMargin(0, 0, 0, 0)) {
7650 nsRect outsetRect(nsPoint(0, 0), aNewSize);
7651 outsetRect.Inflate(outsetMargin);
7652 r.UnionRect(r, outsetRect);
7653 }
7654
7655 // Note that we don't remove the outlineInnerRect if a frame loses outline
7656 // style. That would require an extra property lookup for every frame,
7657 // or a new frame state bit to track whether a property had been stored,
7658 // or something like that. It's not worth doing that here. At most it's
7659 // only one heap-allocated rect per frame and it will be cleaned up when
7660 // the frame dies.
7661
7662 if (SVGIntegrationUtils::UsingOverflowAffectingEffects(aFrame)) {
7663 SetOrUpdateRectValuedProperty(aFrame, nsIFrame::PreEffectsBBoxProperty(),
7664 r);
7665 r = SVGIntegrationUtils::ComputePostEffectsInkOverflowRect(aFrame, r);
7666 }
7667
7668 return r;
7669 }
7670
MovePositionBy(const nsPoint & aTranslation)7671 void nsIFrame::MovePositionBy(const nsPoint& aTranslation) {
7672 nsPoint position = GetNormalPosition() + aTranslation;
7673
7674 const nsMargin* computedOffsets = nullptr;
7675 if (IsRelativelyOrStickyPositioned()) {
7676 computedOffsets = GetProperty(nsIFrame::ComputedOffsetProperty());
7677 }
7678 ReflowInput::ApplyRelativePositioning(
7679 this, computedOffsets ? *computedOffsets : nsMargin(), &position);
7680 SetPosition(position);
7681 }
7682
GetNormalRect() const7683 nsRect nsIFrame::GetNormalRect() const {
7684 // It might be faster to first check
7685 // StyleDisplay()->IsRelativelyPositionedStyle().
7686 bool hasProperty;
7687 nsPoint normalPosition = GetProperty(NormalPositionProperty(), &hasProperty);
7688 if (hasProperty) {
7689 return nsRect(normalPosition, GetSize());
7690 }
7691 return GetRect();
7692 }
7693
GetBoundingClientRect()7694 nsRect nsIFrame::GetBoundingClientRect() {
7695 return nsLayoutUtils::GetAllInFlowRectsUnion(
7696 this, nsLayoutUtils::GetContainingBlockForClientRect(this),
7697 nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
7698 }
7699
GetPositionIgnoringScrolling() const7700 nsPoint nsIFrame::GetPositionIgnoringScrolling() const {
7701 return GetParent() ? GetParent()->GetPositionOfChildIgnoringScrolling(this)
7702 : GetPosition();
7703 }
7704
GetOverflowRect(OverflowType aType) const7705 nsRect nsIFrame::GetOverflowRect(OverflowType aType) const {
7706 // Note that in some cases the overflow area might not have been
7707 // updated (yet) to reflect any outline set on the frame or the area
7708 // of child frames. That's OK because any reflow that updates these
7709 // areas will invalidate the appropriate area, so any (mis)uses of
7710 // this method will be fixed up.
7711
7712 if (mOverflow.mType == OverflowStorageType::Large) {
7713 // there is an overflow rect, and it's not stored as deltas but as
7714 // a separately-allocated rect
7715 return GetOverflowAreasProperty()->Overflow(aType);
7716 }
7717
7718 if (aType == OverflowType::Ink &&
7719 mOverflow.mType != OverflowStorageType::None) {
7720 return InkOverflowFromDeltas();
7721 }
7722
7723 return GetRectRelativeToSelf();
7724 }
7725
GetOverflowAreas() const7726 OverflowAreas nsIFrame::GetOverflowAreas() const {
7727 if (mOverflow.mType == OverflowStorageType::Large) {
7728 // there is an overflow rect, and it's not stored as deltas but as
7729 // a separately-allocated rect
7730 return *GetOverflowAreasProperty();
7731 }
7732
7733 return OverflowAreas(InkOverflowFromDeltas(),
7734 nsRect(nsPoint(0, 0), GetSize()));
7735 }
7736
GetOverflowAreasRelativeToSelf() const7737 OverflowAreas nsIFrame::GetOverflowAreasRelativeToSelf() const {
7738 if (IsTransformed()) {
7739 if (OverflowAreas* preTransformOverflows =
7740 GetProperty(PreTransformOverflowAreasProperty())) {
7741 return *preTransformOverflows;
7742 }
7743 }
7744 return GetOverflowAreas();
7745 }
7746
GetOverflowAreasRelativeToParent() const7747 OverflowAreas nsIFrame::GetOverflowAreasRelativeToParent() const {
7748 return GetOverflowAreas() + GetPosition();
7749 }
7750
GetActualAndNormalOverflowAreasRelativeToParent() const7751 OverflowAreas nsIFrame::GetActualAndNormalOverflowAreasRelativeToParent()
7752 const {
7753 if (MOZ_LIKELY(!IsRelativelyOrStickyPositioned())) {
7754 return GetOverflowAreasRelativeToParent();
7755 }
7756
7757 const OverflowAreas overflows = GetOverflowAreas();
7758 OverflowAreas actualAndNormalOverflows = overflows + GetPosition();
7759 actualAndNormalOverflows.UnionWith(overflows + GetNormalPosition());
7760 return actualAndNormalOverflows;
7761 }
7762
ScrollableOverflowRectRelativeToParent() const7763 nsRect nsIFrame::ScrollableOverflowRectRelativeToParent() const {
7764 return ScrollableOverflowRect() + GetPosition();
7765 }
7766
InkOverflowRectRelativeToParent() const7767 nsRect nsIFrame::InkOverflowRectRelativeToParent() const {
7768 return InkOverflowRect() + GetPosition();
7769 }
7770
ScrollableOverflowRectRelativeToSelf() const7771 nsRect nsIFrame::ScrollableOverflowRectRelativeToSelf() const {
7772 if (IsTransformed()) {
7773 if (OverflowAreas* preTransformOverflows =
7774 GetProperty(PreTransformOverflowAreasProperty())) {
7775 return preTransformOverflows->ScrollableOverflow();
7776 }
7777 }
7778 return ScrollableOverflowRect();
7779 }
7780
InkOverflowRectRelativeToSelf() const7781 nsRect nsIFrame::InkOverflowRectRelativeToSelf() const {
7782 if (IsTransformed()) {
7783 if (OverflowAreas* preTransformOverflows =
7784 GetProperty(PreTransformOverflowAreasProperty())) {
7785 return preTransformOverflows->InkOverflow();
7786 }
7787 }
7788 return InkOverflowRect();
7789 }
7790
PreEffectsInkOverflowRect() const7791 nsRect nsIFrame::PreEffectsInkOverflowRect() const {
7792 nsRect* r = GetProperty(nsIFrame::PreEffectsBBoxProperty());
7793 return r ? *r : InkOverflowRectRelativeToSelf();
7794 }
7795
UpdateOverflow()7796 bool nsIFrame::UpdateOverflow() {
7797 MOZ_ASSERT(FrameMaintainsOverflow(),
7798 "Non-display SVG do not maintain ink overflow rects");
7799
7800 nsRect rect(nsPoint(0, 0), GetSize());
7801 OverflowAreas overflowAreas(rect, rect);
7802
7803 if (!ComputeCustomOverflow(overflowAreas)) {
7804 // If updating overflow wasn't supported by this frame, then it should
7805 // have scheduled any necessary reflows. We can return false to say nothing
7806 // changed, and wait for reflow to correct it.
7807 return false;
7808 }
7809
7810 UnionChildOverflow(overflowAreas);
7811
7812 if (FinishAndStoreOverflow(overflowAreas, GetSize())) {
7813 nsView* view = GetView();
7814 if (view) {
7815 ReflowChildFlags flags = GetXULLayoutFlags();
7816 if (!(flags & ReflowChildFlags::NoSizeView)) {
7817 // Make sure the frame's view is properly sized.
7818 nsViewManager* vm = view->GetViewManager();
7819 vm->ResizeView(view, overflowAreas.InkOverflow(), true);
7820 }
7821 }
7822
7823 return true;
7824 }
7825
7826 // Frames that combine their 3d transform with their ancestors
7827 // only compute a pre-transform overflow rect, and then contribute
7828 // to the normal overflow rect of the preserve-3d root. Always return
7829 // true here so that we propagate changes up to the root for final
7830 // calculation.
7831 return Combines3DTransformWithAncestors();
7832 }
7833
7834 /* virtual */
ComputeCustomOverflow(OverflowAreas & aOverflowAreas)7835 bool nsIFrame::ComputeCustomOverflow(OverflowAreas& aOverflowAreas) {
7836 return true;
7837 }
7838
7839 /* virtual */
UnionChildOverflow(OverflowAreas & aOverflowAreas)7840 void nsIFrame::UnionChildOverflow(OverflowAreas& aOverflowAreas) {
7841 if (!DoesClipChildrenInBothAxes() &&
7842 !(IsXULCollapsed() && (IsXULBoxFrame() || ::IsXULBoxWrapped(this)))) {
7843 nsLayoutUtils::UnionChildOverflow(this, aOverflowAreas);
7844 }
7845 }
7846
7847 // Return true if this form control element's preferred size property (but not
7848 // percentage max size property) contains a percentage value that should be
7849 // resolved against zero when calculating its min-content contribution in the
7850 // corresponding axis.
7851 //
7852 // For proper replaced elements, the percentage value in both their max size
7853 // property or preferred size property should be resolved against zero. This is
7854 // handled in IsPercentageResolvedAgainstZero().
FormControlShrinksForPercentSize(const nsIFrame * aFrame)7855 inline static bool FormControlShrinksForPercentSize(const nsIFrame* aFrame) {
7856 if (!aFrame->IsFrameOfType(nsIFrame::eReplaced)) {
7857 // Quick test to reject most frames.
7858 return false;
7859 }
7860
7861 LayoutFrameType fType = aFrame->Type();
7862 if (fType == LayoutFrameType::Meter || fType == LayoutFrameType::Progress ||
7863 fType == LayoutFrameType::Range) {
7864 // progress, meter and range do have this shrinking behavior
7865 // FIXME: Maybe these should be nsIFormControlFrame?
7866 return true;
7867 }
7868
7869 if (!static_cast<nsIFormControlFrame*>(do_QueryFrame(aFrame))) {
7870 // Not a form control. This includes fieldsets, which do not
7871 // shrink.
7872 return false;
7873 }
7874
7875 if (fType == LayoutFrameType::GfxButtonControl ||
7876 fType == LayoutFrameType::HTMLButtonControl) {
7877 // Buttons don't have this shrinking behavior. (Note that color
7878 // inputs do, even though they inherit from button, so we can't use
7879 // do_QueryFrame here.)
7880 return false;
7881 }
7882
7883 return true;
7884 }
7885
IsPercentageResolvedAgainstZero(const StyleSize & aStyleSize,const StyleMaxSize & aStyleMaxSize) const7886 bool nsIFrame::IsPercentageResolvedAgainstZero(
7887 const StyleSize& aStyleSize, const StyleMaxSize& aStyleMaxSize) const {
7888 const bool sizeHasPercent = aStyleSize.HasPercent();
7889 return ((sizeHasPercent || aStyleMaxSize.HasPercent()) &&
7890 IsFrameOfType(nsIFrame::eReplacedSizing)) ||
7891 (sizeHasPercent && FormControlShrinksForPercentSize(this));
7892 }
7893
7894 // Summary of the Cyclic-Percentage Intrinsic Size Contribution Rules:
7895 //
7896 // Element Type | Replaced | Non-replaced
7897 // Contribution Type | min-content max-content | min-content max-content
7898 // ---------------------------------------------------------------------------
7899 // min size | zero zero | zero zero
7900 // max & preferred size | zero initial | initial initial
7901 //
7902 // https://drafts.csswg.org/css-sizing-3/#cyclic-percentage-contribution
IsPercentageResolvedAgainstZero(const LengthPercentage & aSize,SizeProperty aProperty) const7903 bool nsIFrame::IsPercentageResolvedAgainstZero(const LengthPercentage& aSize,
7904 SizeProperty aProperty) const {
7905 // Early return to avoid calling the virtual function, IsFrameOfType().
7906 if (aProperty == SizeProperty::MinSize) {
7907 return true;
7908 }
7909
7910 const bool hasPercentOnReplaced =
7911 aSize.HasPercent() && IsFrameOfType(nsIFrame::eReplacedSizing);
7912 if (aProperty == SizeProperty::MaxSize) {
7913 return hasPercentOnReplaced;
7914 }
7915
7916 MOZ_ASSERT(aProperty == SizeProperty::Size);
7917 return hasPercentOnReplaced ||
7918 (aSize.HasPercent() && FormControlShrinksForPercentSize(this));
7919 }
7920
IsBlockWrapper() const7921 bool nsIFrame::IsBlockWrapper() const {
7922 auto pseudoType = Style()->GetPseudoType();
7923 return pseudoType == PseudoStyleType::mozBlockInsideInlineWrapper ||
7924 pseudoType == PseudoStyleType::buttonContent ||
7925 pseudoType == PseudoStyleType::cellContent ||
7926 pseudoType == PseudoStyleType::columnSpanWrapper;
7927 }
7928
IsBlockFrameOrSubclass() const7929 bool nsIFrame::IsBlockFrameOrSubclass() const {
7930 const nsBlockFrame* thisAsBlock = do_QueryFrame(this);
7931 return !!thisAsBlock;
7932 }
7933
GetNearestBlockContainer(nsIFrame * frame)7934 static nsIFrame* GetNearestBlockContainer(nsIFrame* frame) {
7935 // The block wrappers we use to wrap blocks inside inlines aren't
7936 // described in the CSS spec. We need to make them not be containing
7937 // blocks.
7938 // Since the parent of such a block is either a normal block or
7939 // another such pseudo, this shouldn't cause anything bad to happen.
7940 // Also the anonymous blocks inside table cells are not containing blocks.
7941 //
7942 // If we ever start skipping table row groups from being containing blocks,
7943 // you need to remove the StickyScrollContainer hack referencing bug 1421660.
7944 while (frame->IsFrameOfType(nsIFrame::eLineParticipant) ||
7945 frame->IsBlockWrapper() ||
7946 // Table rows are not containing blocks either
7947 frame->IsTableRowFrame()) {
7948 frame = frame->GetParent();
7949 NS_ASSERTION(
7950 frame,
7951 "How come we got to the root frame without seeing a containing block?");
7952 }
7953 return frame;
7954 }
7955
GetContainingBlock(uint32_t aFlags,const nsStyleDisplay * aStyleDisplay) const7956 nsIFrame* nsIFrame::GetContainingBlock(
7957 uint32_t aFlags, const nsStyleDisplay* aStyleDisplay) const {
7958 MOZ_ASSERT(aStyleDisplay == StyleDisplay());
7959 if (!GetParent()) {
7960 return nullptr;
7961 }
7962 // MathML frames might have absolute positioning style, but they would
7963 // still be in-flow. So we have to check to make sure that the frame
7964 // is really out-of-flow too.
7965 nsIFrame* f;
7966 if (IsAbsolutelyPositioned(aStyleDisplay)) {
7967 f = GetParent(); // the parent is always the containing block
7968 } else {
7969 f = GetNearestBlockContainer(GetParent());
7970 }
7971
7972 if (aFlags & SKIP_SCROLLED_FRAME && f &&
7973 f->Style()->GetPseudoType() == PseudoStyleType::scrolledContent) {
7974 f = f->GetParent();
7975 }
7976 return f;
7977 }
7978
7979 #ifdef DEBUG_FRAME_DUMP
7980
ContentIndexInContainer(const nsIFrame * aFrame)7981 Maybe<uint32_t> nsIFrame::ContentIndexInContainer(const nsIFrame* aFrame) {
7982 if (nsIContent* content = aFrame->GetContent()) {
7983 return content->ComputeIndexInParentContent();
7984 }
7985 return Nothing();
7986 }
7987
ListTag() const7988 nsAutoCString nsIFrame::ListTag() const {
7989 nsAutoString tmp;
7990 GetFrameName(tmp);
7991
7992 nsAutoCString tag;
7993 tag += NS_ConvertUTF16toUTF8(tmp);
7994 tag += nsPrintfCString("@%p", static_cast<const void*>(this));
7995 return tag;
7996 }
7997
ConvertToString(const LogicalRect & aRect,const WritingMode aWM,ListFlags aFlags)7998 std::string nsIFrame::ConvertToString(const LogicalRect& aRect,
7999 const WritingMode aWM, ListFlags aFlags) {
8000 if (aFlags.contains(ListFlag::DisplayInCSSPixels)) {
8001 // Abuse CSSRect to store all LogicalRect's dimensions in CSS pixels.
8002 return ToString(mozilla::CSSRect(CSSPixel::FromAppUnits(aRect.IStart(aWM)),
8003 CSSPixel::FromAppUnits(aRect.BStart(aWM)),
8004 CSSPixel::FromAppUnits(aRect.ISize(aWM)),
8005 CSSPixel::FromAppUnits(aRect.BSize(aWM))));
8006 }
8007 return ToString(aRect);
8008 }
8009
ConvertToString(const LogicalSize & aSize,const WritingMode aWM,ListFlags aFlags)8010 std::string nsIFrame::ConvertToString(const LogicalSize& aSize,
8011 const WritingMode aWM, ListFlags aFlags) {
8012 if (aFlags.contains(ListFlag::DisplayInCSSPixels)) {
8013 // Abuse CSSSize to store all LogicalSize's dimensions in CSS pixels.
8014 return ToString(CSSSize(CSSPixel::FromAppUnits(aSize.ISize(aWM)),
8015 CSSPixel::FromAppUnits(aSize.BSize(aWM))));
8016 }
8017 return ToString(aSize);
8018 }
8019
8020 // Debugging
ListGeneric(nsACString & aTo,const char * aPrefix,ListFlags aFlags) const8021 void nsIFrame::ListGeneric(nsACString& aTo, const char* aPrefix,
8022 ListFlags aFlags) const {
8023 aTo += aPrefix;
8024 aTo += ListTag();
8025 if (HasView()) {
8026 aTo += nsPrintfCString(" [view=%p]", static_cast<void*>(GetView()));
8027 }
8028 if (GetParent()) {
8029 aTo += nsPrintfCString(" parent=%p", static_cast<void*>(GetParent()));
8030 }
8031 if (GetNextSibling()) {
8032 aTo += nsPrintfCString(" next=%p", static_cast<void*>(GetNextSibling()));
8033 }
8034 if (GetPrevContinuation()) {
8035 bool fluid = GetPrevInFlow() == GetPrevContinuation();
8036 aTo += nsPrintfCString(" prev-%s=%p", fluid ? "in-flow" : "continuation",
8037 static_cast<void*>(GetPrevContinuation()));
8038 }
8039 if (GetNextContinuation()) {
8040 bool fluid = GetNextInFlow() == GetNextContinuation();
8041 aTo += nsPrintfCString(" next-%s=%p", fluid ? "in-flow" : "continuation",
8042 static_cast<void*>(GetNextContinuation()));
8043 }
8044 void* IBsibling = GetProperty(IBSplitSibling());
8045 if (IBsibling) {
8046 aTo += nsPrintfCString(" IBSplitSibling=%p", IBsibling);
8047 }
8048 void* IBprevsibling = GetProperty(IBSplitPrevSibling());
8049 if (IBprevsibling) {
8050 aTo += nsPrintfCString(" IBSplitPrevSibling=%p", IBprevsibling);
8051 }
8052 if (nsLayoutUtils::FontSizeInflationEnabled(PresContext())) {
8053 if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT)) {
8054 aTo += nsPrintfCString(" FFR");
8055 if (nsFontInflationData* data =
8056 nsFontInflationData::FindFontInflationDataFor(this)) {
8057 aTo += nsPrintfCString(
8058 ",enabled=%s,UIS=%s", data->InflationEnabled() ? "yes" : "no",
8059 ConvertToString(data->UsableISize(), aFlags).c_str());
8060 }
8061 }
8062 if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER)) {
8063 aTo += nsPrintfCString(" FIC");
8064 }
8065 aTo += nsPrintfCString(" FI=%f", nsLayoutUtils::FontSizeInflationFor(this));
8066 }
8067 aTo += nsPrintfCString(" %s", ConvertToString(mRect, aFlags).c_str());
8068
8069 mozilla::WritingMode wm = GetWritingMode();
8070 if (wm.IsVertical() || wm.IsBidiRTL()) {
8071 aTo +=
8072 nsPrintfCString(" wm=%s logical-size=(%s)", ToString(wm).c_str(),
8073 ConvertToString(GetLogicalSize(), wm, aFlags).c_str());
8074 }
8075
8076 nsIFrame* parent = GetParent();
8077 if (parent) {
8078 WritingMode pWM = parent->GetWritingMode();
8079 if (pWM.IsVertical() || pWM.IsBidiRTL()) {
8080 nsSize containerSize = parent->mRect.Size();
8081 LogicalRect lr(pWM, mRect, containerSize);
8082 aTo += nsPrintfCString(" parent-wm=%s cs=(%s) logical-rect=%s",
8083 ToString(pWM).c_str(),
8084 ConvertToString(containerSize, aFlags).c_str(),
8085 ConvertToString(lr, pWM, aFlags).c_str());
8086 }
8087 }
8088 nsIFrame* f = const_cast<nsIFrame*>(this);
8089 if (f->HasOverflowAreas()) {
8090 nsRect vo = f->InkOverflowRect();
8091 if (!vo.IsEqualEdges(mRect)) {
8092 aTo += nsPrintfCString(" ink-overflow=%s",
8093 ConvertToString(vo, aFlags).c_str());
8094 }
8095 nsRect so = f->ScrollableOverflowRect();
8096 if (!so.IsEqualEdges(mRect)) {
8097 aTo += nsPrintfCString(" scr-overflow=%s",
8098 ConvertToString(so, aFlags).c_str());
8099 }
8100 }
8101 bool hasNormalPosition;
8102 nsPoint normalPosition = GetNormalPosition(&hasNormalPosition);
8103 if (hasNormalPosition) {
8104 aTo += nsPrintfCString(" normal-position=%s",
8105 ConvertToString(normalPosition, aFlags).c_str());
8106 }
8107 if (HasProperty(BidiDataProperty())) {
8108 FrameBidiData bidi = GetBidiData();
8109 aTo += nsPrintfCString(" bidi(%d,%d,%d)", bidi.baseLevel.Value(),
8110 bidi.embeddingLevel.Value(),
8111 bidi.precedingControl.Value());
8112 }
8113 if (IsTransformed()) {
8114 aTo += nsPrintfCString(" transformed");
8115 }
8116 if (ChildrenHavePerspective()) {
8117 aTo += nsPrintfCString(" perspective");
8118 }
8119 if (Extend3DContext()) {
8120 aTo += nsPrintfCString(" extend-3d");
8121 }
8122 if (Combines3DTransformWithAncestors()) {
8123 aTo += nsPrintfCString(" combines-3d-transform-with-ancestors");
8124 }
8125 if (mContent) {
8126 aTo += nsPrintfCString(" [content=%p]", static_cast<void*>(mContent));
8127 }
8128 aTo += nsPrintfCString(" [cs=%p", static_cast<void*>(mComputedStyle));
8129 if (mComputedStyle) {
8130 auto pseudoType = mComputedStyle->GetPseudoType();
8131 aTo += ToString(pseudoType).c_str();
8132 }
8133 aTo += "]";
8134
8135 if (IsFrameModified()) {
8136 aTo += nsPrintfCString(" modified");
8137 }
8138
8139 if (HasModifiedDescendants()) {
8140 aTo += nsPrintfCString(" has-modified-descendants");
8141 }
8142 }
8143
List(FILE * out,const char * aPrefix,ListFlags aFlags) const8144 void nsIFrame::List(FILE* out, const char* aPrefix, ListFlags aFlags) const {
8145 nsCString str;
8146 ListGeneric(str, aPrefix, aFlags);
8147 fprintf_stderr(out, "%s\n", str.get());
8148 }
8149
ListTextRuns(FILE * out) const8150 void nsIFrame::ListTextRuns(FILE* out) const {
8151 nsTHashSet<const void*> seen;
8152 ListTextRuns(out, seen);
8153 }
8154
ListTextRuns(FILE * out,nsTHashSet<const void * > & aSeen) const8155 void nsIFrame::ListTextRuns(FILE* out, nsTHashSet<const void*>& aSeen) const {
8156 for (const auto& childList : ChildLists()) {
8157 for (const nsIFrame* kid : childList.mList) {
8158 kid->ListTextRuns(out, aSeen);
8159 }
8160 }
8161 }
8162
ListMatchedRules(FILE * out,const char * aPrefix) const8163 void nsIFrame::ListMatchedRules(FILE* out, const char* aPrefix) const {
8164 nsTArray<const RawServoStyleRule*> rawRuleList;
8165 Servo_ComputedValues_GetStyleRuleList(mComputedStyle, &rawRuleList);
8166 for (const RawServoStyleRule* rawRule : rawRuleList) {
8167 nsAutoCString ruleText;
8168 Servo_StyleRule_GetCssText(rawRule, &ruleText);
8169 fprintf_stderr(out, "%s%s\n", aPrefix, ruleText.get());
8170 }
8171 }
8172
ListWithMatchedRules(FILE * out,const char * aPrefix) const8173 void nsIFrame::ListWithMatchedRules(FILE* out, const char* aPrefix) const {
8174 fprintf_stderr(out, "%s%s\n", aPrefix, ListTag().get());
8175
8176 nsCString rulePrefix;
8177 rulePrefix += aPrefix;
8178 rulePrefix += " ";
8179 ListMatchedRules(out, rulePrefix.get());
8180 }
8181
GetFrameName(nsAString & aResult) const8182 nsresult nsIFrame::GetFrameName(nsAString& aResult) const {
8183 return MakeFrameName(u"Frame"_ns, aResult);
8184 }
8185
MakeFrameName(const nsAString & aType,nsAString & aResult) const8186 nsresult nsIFrame::MakeFrameName(const nsAString& aType,
8187 nsAString& aResult) const {
8188 aResult = aType;
8189 if (mContent && !mContent->IsText()) {
8190 nsAutoString buf;
8191 mContent->NodeInfo()->NameAtom()->ToString(buf);
8192 if (nsAtom* id = mContent->GetID()) {
8193 buf.AppendLiteral(" id=");
8194 buf.Append(nsDependentAtomString(id));
8195 }
8196 if (IsSubDocumentFrame()) {
8197 nsAutoString src;
8198 mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src);
8199 buf.AppendLiteral(" src=");
8200 buf.Append(src);
8201 }
8202 aResult.Append('(');
8203 aResult.Append(buf);
8204 aResult.Append(')');
8205 }
8206 aResult.Append('(');
8207 Maybe<uint32_t> index = ContentIndexInContainer(this);
8208 if (index.isSome()) {
8209 aResult.AppendInt(*index);
8210 } else {
8211 aResult.AppendInt(-1);
8212 }
8213 aResult.Append(')');
8214 return NS_OK;
8215 }
8216
DumpFrameTree() const8217 void nsIFrame::DumpFrameTree() const {
8218 PresShell()->GetRootFrame()->List(stderr);
8219 }
8220
DumpFrameTreeInCSSPixels() const8221 void nsIFrame::DumpFrameTreeInCSSPixels() const {
8222 PresShell()->GetRootFrame()->List(stderr, "", ListFlag::DisplayInCSSPixels);
8223 }
8224
DumpFrameTreeLimited() const8225 void nsIFrame::DumpFrameTreeLimited() const { List(stderr); }
DumpFrameTreeLimitedInCSSPixels() const8226 void nsIFrame::DumpFrameTreeLimitedInCSSPixels() const {
8227 List(stderr, "", ListFlag::DisplayInCSSPixels);
8228 }
8229
8230 #endif
8231
IsVisibleForPainting()8232 bool nsIFrame::IsVisibleForPainting() { return StyleVisibility()->IsVisible(); }
8233
IsVisibleOrCollapsedForPainting()8234 bool nsIFrame::IsVisibleOrCollapsedForPainting() {
8235 return StyleVisibility()->IsVisibleOrCollapsed();
8236 }
8237
8238 /* virtual */
IsEmpty()8239 bool nsIFrame::IsEmpty() { return false; }
8240
CachedIsEmpty()8241 bool nsIFrame::CachedIsEmpty() {
8242 MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_IS_DIRTY),
8243 "Must only be called on reflowed lines");
8244 return IsEmpty();
8245 }
8246
8247 /* virtual */
IsSelfEmpty()8248 bool nsIFrame::IsSelfEmpty() { return false; }
8249
GetSelectionController(nsPresContext * aPresContext,nsISelectionController ** aSelCon)8250 nsresult nsIFrame::GetSelectionController(nsPresContext* aPresContext,
8251 nsISelectionController** aSelCon) {
8252 if (!aPresContext || !aSelCon) return NS_ERROR_INVALID_ARG;
8253
8254 nsIFrame* frame = this;
8255 while (frame && frame->HasAnyStateBits(NS_FRAME_INDEPENDENT_SELECTION)) {
8256 nsITextControlFrame* tcf = do_QueryFrame(frame);
8257 if (tcf) {
8258 return tcf->GetOwnedSelectionController(aSelCon);
8259 }
8260 frame = frame->GetParent();
8261 }
8262
8263 *aSelCon = do_AddRef(aPresContext->PresShell()).take();
8264 return NS_OK;
8265 }
8266
GetFrameSelection()8267 already_AddRefed<nsFrameSelection> nsIFrame::GetFrameSelection() {
8268 RefPtr<nsFrameSelection> fs =
8269 const_cast<nsFrameSelection*>(GetConstFrameSelection());
8270 return fs.forget();
8271 }
8272
GetConstFrameSelection() const8273 const nsFrameSelection* nsIFrame::GetConstFrameSelection() const {
8274 nsIFrame* frame = const_cast<nsIFrame*>(this);
8275 while (frame && frame->HasAnyStateBits(NS_FRAME_INDEPENDENT_SELECTION)) {
8276 nsITextControlFrame* tcf = do_QueryFrame(frame);
8277 if (tcf) {
8278 return tcf->GetOwnedFrameSelection();
8279 }
8280 frame = frame->GetParent();
8281 }
8282
8283 return PresShell()->ConstFrameSelection();
8284 }
8285
IsFrameSelected() const8286 bool nsIFrame::IsFrameSelected() const {
8287 NS_ASSERTION(!GetContent() || GetContent()->IsMaybeSelected(),
8288 "use the public IsSelected() instead");
8289 return GetContent()->IsSelected(0, GetContent()->GetChildCount());
8290 }
8291
GetPointFromOffset(int32_t inOffset,nsPoint * outPoint)8292 nsresult nsIFrame::GetPointFromOffset(int32_t inOffset, nsPoint* outPoint) {
8293 MOZ_ASSERT(outPoint != nullptr, "Null parameter");
8294 nsRect contentRect = GetContentRectRelativeToSelf();
8295 nsPoint pt = contentRect.TopLeft();
8296 if (mContent) {
8297 nsIContent* newContent = mContent->GetParent();
8298 if (newContent) {
8299 const int32_t newOffset = newContent->ComputeIndexOf_Deprecated(mContent);
8300
8301 // Find the direction of the frame from the EmbeddingLevelProperty,
8302 // which is the resolved bidi level set in
8303 // nsBidiPresUtils::ResolveParagraph (odd levels = right-to-left).
8304 // If the embedding level isn't set, just use the CSS direction
8305 // property.
8306 bool hasBidiData;
8307 FrameBidiData bidiData = GetProperty(BidiDataProperty(), &hasBidiData);
8308 bool isRTL = hasBidiData
8309 ? bidiData.embeddingLevel.IsRTL()
8310 : StyleVisibility()->mDirection == StyleDirection::Rtl;
8311 if ((!isRTL && inOffset > newOffset) ||
8312 (isRTL && inOffset <= newOffset)) {
8313 pt = contentRect.TopRight();
8314 }
8315 }
8316 }
8317 *outPoint = pt;
8318 return NS_OK;
8319 }
8320
GetCharacterRectsInRange(int32_t aInOffset,int32_t aLength,nsTArray<nsRect> & aOutRect)8321 nsresult nsIFrame::GetCharacterRectsInRange(int32_t aInOffset, int32_t aLength,
8322 nsTArray<nsRect>& aOutRect) {
8323 /* no text */
8324 return NS_ERROR_FAILURE;
8325 }
8326
GetChildFrameContainingOffset(int32_t inContentOffset,bool inHint,int32_t * outFrameContentOffset,nsIFrame ** outChildFrame)8327 nsresult nsIFrame::GetChildFrameContainingOffset(int32_t inContentOffset,
8328 bool inHint,
8329 int32_t* outFrameContentOffset,
8330 nsIFrame** outChildFrame) {
8331 MOZ_ASSERT(outChildFrame && outFrameContentOffset, "Null parameter");
8332 *outFrameContentOffset = (int32_t)inHint;
8333 // the best frame to reflect any given offset would be a visible frame if
8334 // possible i.e. we are looking for a valid frame to place the blinking caret
8335 nsRect rect = GetRect();
8336 if (!rect.width || !rect.height) {
8337 // if we have a 0 width or height then lets look for another frame that
8338 // possibly has the same content. If we have no frames in flow then just
8339 // let us return 'this' frame
8340 nsIFrame* nextFlow = GetNextInFlow();
8341 if (nextFlow)
8342 return nextFlow->GetChildFrameContainingOffset(
8343 inContentOffset, inHint, outFrameContentOffset, outChildFrame);
8344 }
8345 *outChildFrame = this;
8346 return NS_OK;
8347 }
8348
8349 //
8350 // What I've pieced together about this routine:
8351 // Starting with a block frame (from which a line frame can be gotten)
8352 // and a line number, drill down and get the first/last selectable
8353 // frame on that line, depending on aPos->mDirection.
8354 // aOutSideLimit != 0 means ignore aLineStart, instead work from
8355 // the end (if > 0) or beginning (if < 0).
8356 //
GetNextPrevLineFromeBlockFrame(nsPresContext * aPresContext,nsPeekOffsetStruct * aPos,nsIFrame * aBlockFrame,int32_t aLineStart,int8_t aOutSideLimit)8357 nsresult nsIFrame::GetNextPrevLineFromeBlockFrame(nsPresContext* aPresContext,
8358 nsPeekOffsetStruct* aPos,
8359 nsIFrame* aBlockFrame,
8360 int32_t aLineStart,
8361 int8_t aOutSideLimit) {
8362 // magic numbers aLineStart will be -1 for end of block 0 will be start of
8363 // block
8364 if (!aBlockFrame || !aPos) return NS_ERROR_NULL_POINTER;
8365
8366 aPos->mResultFrame = nullptr;
8367 aPos->mResultContent = nullptr;
8368 aPos->mAttach = aPos->mDirection == eDirNext ? CARET_ASSOCIATE_AFTER
8369 : CARET_ASSOCIATE_BEFORE;
8370
8371 nsAutoLineIterator it = aBlockFrame->GetLineIterator();
8372 if (!it) {
8373 return NS_ERROR_FAILURE;
8374 }
8375 int32_t searchingLine = aLineStart;
8376 int32_t countLines = it->GetNumLines();
8377 if (aOutSideLimit > 0) // start at end
8378 searchingLine = countLines;
8379 else if (aOutSideLimit < 0) // start at beginning
8380 searchingLine = -1; //"next" will be 0
8381 else if ((aPos->mDirection == eDirPrevious && searchingLine == 0) ||
8382 (aPos->mDirection == eDirNext &&
8383 searchingLine >= (countLines - 1))) {
8384 // we need to jump to new block frame.
8385 return NS_ERROR_FAILURE;
8386 }
8387 nsIFrame* resultFrame = nullptr;
8388 nsIFrame* farStoppingFrame = nullptr; // we keep searching until we find a
8389 // "this" frame then we go to next line
8390 nsIFrame* nearStoppingFrame = nullptr; // if we are backing up from edge,
8391 // stop here
8392 nsIFrame* firstFrame;
8393 nsIFrame* lastFrame;
8394 bool isBeforeFirstFrame, isAfterLastFrame;
8395 bool found = false;
8396
8397 nsresult result = NS_OK;
8398 while (!found) {
8399 if (aPos->mDirection == eDirPrevious)
8400 searchingLine--;
8401 else
8402 searchingLine++;
8403 if ((aPos->mDirection == eDirPrevious && searchingLine < 0) ||
8404 (aPos->mDirection == eDirNext && searchingLine >= countLines)) {
8405 // we need to jump to new block frame.
8406 return NS_ERROR_FAILURE;
8407 }
8408 auto line = it->GetLine(searchingLine).unwrap();
8409 if (!line.mNumFramesOnLine) {
8410 continue;
8411 }
8412 lastFrame = firstFrame = line.mFirstFrameOnLine;
8413 for (int32_t lineFrameCount = line.mNumFramesOnLine; lineFrameCount > 1;
8414 lineFrameCount--) {
8415 lastFrame = lastFrame->GetNextSibling();
8416 if (!lastFrame) {
8417 NS_ERROR("GetLine promised more frames than could be found");
8418 return NS_ERROR_FAILURE;
8419 }
8420 }
8421 GetLastLeaf(&lastFrame);
8422
8423 if (aPos->mDirection == eDirNext) {
8424 nearStoppingFrame = firstFrame;
8425 farStoppingFrame = lastFrame;
8426 } else {
8427 nearStoppingFrame = lastFrame;
8428 farStoppingFrame = firstFrame;
8429 }
8430 nsPoint offset;
8431 nsView* view; // used for call of get offset from view
8432 aBlockFrame->GetOffsetFromView(offset, &view);
8433 nsPoint newDesiredPos =
8434 aPos->mDesiredCaretPos -
8435 offset; // get desired position into blockframe coords
8436 result = it->FindFrameAt(searchingLine, newDesiredPos, &resultFrame,
8437 &isBeforeFirstFrame, &isAfterLastFrame);
8438 if (NS_FAILED(result)) {
8439 continue;
8440 }
8441
8442 if (resultFrame) {
8443 // check to see if this is ANOTHER blockframe inside the other one if so
8444 // then call into its lines
8445 if (resultFrame->CanProvideLineIterator()) {
8446 aPos->mResultFrame = resultFrame;
8447 return NS_OK;
8448 }
8449 // resultFrame is not a block frame
8450 result = NS_ERROR_FAILURE;
8451
8452 nsCOMPtr<nsIFrameEnumerator> frameTraversal;
8453 result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
8454 aPresContext, resultFrame, ePostOrder,
8455 false, // aVisual
8456 aPos->mScrollViewStop,
8457 false, // aFollowOOFs
8458 false // aSkipPopupChecks
8459 );
8460 if (NS_FAILED(result)) return result;
8461
8462 auto FoundValidFrame = [aPos](const ContentOffsets& aOffsets,
8463 const nsIFrame* aFrame) {
8464 if (!aOffsets.content) {
8465 return false;
8466 }
8467 if (!aFrame->IsSelectable(nullptr)) {
8468 return false;
8469 }
8470 if (aPos->mForceEditableRegion && !aOffsets.content->IsEditable()) {
8471 return false;
8472 }
8473 return true;
8474 };
8475
8476 nsIFrame* storeOldResultFrame = resultFrame;
8477 while (!found) {
8478 nsPoint point;
8479 nsRect tempRect = resultFrame->GetRect();
8480 nsPoint offset;
8481 nsView* view; // used for call of get offset from view
8482 resultFrame->GetOffsetFromView(offset, &view);
8483 if (!view) {
8484 return NS_ERROR_FAILURE;
8485 }
8486 if (resultFrame->GetWritingMode().IsVertical()) {
8487 point.y = aPos->mDesiredCaretPos.y;
8488 point.x = tempRect.width + offset.x;
8489 } else {
8490 point.y = tempRect.height + offset.y;
8491 point.x = aPos->mDesiredCaretPos.x;
8492 }
8493
8494 // special check. if we allow non-text selection then we can allow a hit
8495 // location to fall before a table. otherwise there is no way to get and
8496 // click signal to fall before a table (it being a line iterator itself)
8497 mozilla::PresShell* presShell = aPresContext->GetPresShell();
8498 if (!presShell) {
8499 return NS_ERROR_FAILURE;
8500 }
8501 int16_t isEditor = presShell->GetSelectionFlags();
8502 isEditor = isEditor == nsISelectionDisplay::DISPLAY_ALL;
8503 if (isEditor) {
8504 if (resultFrame->IsTableWrapperFrame()) {
8505 if (((point.x - offset.x + tempRect.x) < 0) ||
8506 ((point.x - offset.x + tempRect.x) >
8507 tempRect.width)) // off left/right side
8508 {
8509 nsIContent* content = resultFrame->GetContent();
8510 if (content) {
8511 nsIContent* parent = content->GetParent();
8512 if (parent) {
8513 aPos->mResultContent = parent;
8514 aPos->mContentOffset =
8515 parent->ComputeIndexOf_Deprecated(content);
8516 aPos->mAttach = CARET_ASSOCIATE_BEFORE;
8517 if ((point.x - offset.x + tempRect.x) > tempRect.width) {
8518 aPos->mContentOffset++; // go to end of this frame
8519 aPos->mAttach = CARET_ASSOCIATE_AFTER;
8520 }
8521 // result frame is the result frames parent.
8522 aPos->mResultFrame = resultFrame->GetParent();
8523 return NS_POSITION_BEFORE_TABLE;
8524 }
8525 }
8526 }
8527 }
8528 }
8529
8530 if (!resultFrame->HasView()) {
8531 nsView* view;
8532 nsPoint offset;
8533 resultFrame->GetOffsetFromView(offset, &view);
8534 ContentOffsets offsets =
8535 resultFrame->GetContentOffsetsFromPoint(point - offset);
8536 aPos->mResultContent = offsets.content;
8537 aPos->mContentOffset = offsets.offset;
8538 aPos->mAttach = offsets.associate;
8539 if (FoundValidFrame(offsets, resultFrame)) {
8540 found = true;
8541 break;
8542 }
8543 }
8544
8545 if (aPos->mDirection == eDirPrevious &&
8546 (resultFrame == farStoppingFrame))
8547 break;
8548 if (aPos->mDirection == eDirNext && (resultFrame == nearStoppingFrame))
8549 break;
8550 // always try previous on THAT line if that fails go the other way
8551 resultFrame = frameTraversal->Traverse(/* aForward = */ false);
8552 if (!resultFrame) return NS_ERROR_FAILURE;
8553 }
8554
8555 if (!found) {
8556 resultFrame = storeOldResultFrame;
8557
8558 result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
8559 aPresContext, resultFrame, eLeaf,
8560 false, // aVisual
8561 aPos->mScrollViewStop,
8562 false, // aFollowOOFs
8563 false // aSkipPopupChecks
8564 );
8565 }
8566 while (!found) {
8567 nsPoint point = aPos->mDesiredCaretPos;
8568 nsView* view;
8569 nsPoint offset;
8570 resultFrame->GetOffsetFromView(offset, &view);
8571 ContentOffsets offsets =
8572 resultFrame->GetContentOffsetsFromPoint(point - offset);
8573 aPos->mResultContent = offsets.content;
8574 aPos->mContentOffset = offsets.offset;
8575 aPos->mAttach = offsets.associate;
8576 if (FoundValidFrame(offsets, resultFrame)) {
8577 found = true;
8578 if (resultFrame == farStoppingFrame)
8579 aPos->mAttach = CARET_ASSOCIATE_BEFORE;
8580 else
8581 aPos->mAttach = CARET_ASSOCIATE_AFTER;
8582 break;
8583 }
8584 if (aPos->mDirection == eDirPrevious &&
8585 (resultFrame == nearStoppingFrame))
8586 break;
8587 if (aPos->mDirection == eDirNext && (resultFrame == farStoppingFrame))
8588 break;
8589 // previous didnt work now we try "next"
8590 nsIFrame* tempFrame = frameTraversal->Traverse(/* aForward = */ true);
8591 if (!tempFrame) break;
8592 resultFrame = tempFrame;
8593 }
8594 aPos->mResultFrame = resultFrame;
8595 } else {
8596 // we need to jump to new block frame.
8597 aPos->mAmount = eSelectLine;
8598 aPos->mStartOffset = 0;
8599 aPos->mAttach = aPos->mDirection == eDirNext ? CARET_ASSOCIATE_BEFORE
8600 : CARET_ASSOCIATE_AFTER;
8601 if (aPos->mDirection == eDirPrevious)
8602 aPos->mStartOffset = -1; // start from end
8603 return aBlockFrame->PeekOffset(aPos);
8604 }
8605 }
8606 return NS_OK;
8607 }
8608
GetExtremeCaretPosition(bool aStart)8609 nsIFrame::CaretPosition nsIFrame::GetExtremeCaretPosition(bool aStart) {
8610 CaretPosition result;
8611
8612 FrameTarget targetFrame = DrillDownToSelectionFrame(this, !aStart, 0);
8613 FrameContentRange range = GetRangeForFrame(targetFrame.frame);
8614 result.mResultContent = range.content;
8615 result.mContentOffset = aStart ? range.start : range.end;
8616 return result;
8617 }
8618
8619 // If this is a preformatted text frame, see if it ends with a newline
FindLineBreakInText(nsIFrame * aFrame,nsDirection aDirection)8620 static nsContentAndOffset FindLineBreakInText(nsIFrame* aFrame,
8621 nsDirection aDirection) {
8622 nsContentAndOffset result;
8623
8624 if (aFrame->IsGeneratedContentFrame() ||
8625 !aFrame->HasSignificantTerminalNewline()) {
8626 return result;
8627 }
8628
8629 int32_t endOffset = aFrame->GetOffsets().second;
8630 result.mContent = aFrame->GetContent();
8631 result.mOffset = endOffset - (aDirection == eDirPrevious ? 0 : 1);
8632 return result;
8633 }
8634
8635 // Find the first (or last) descendant of the given frame
8636 // which is either a block-level frame or a BRFrame, or some other kind of break
8637 // which stops the line.
FindLineBreakingFrame(nsIFrame * aFrame,nsDirection aDirection)8638 static nsContentAndOffset FindLineBreakingFrame(nsIFrame* aFrame,
8639 nsDirection aDirection) {
8640 nsContentAndOffset result;
8641
8642 if (aFrame->IsGeneratedContentFrame()) {
8643 return result;
8644 }
8645
8646 // Treat form controls as inline leaves
8647 // XXX we really need a way to determine whether a frame is inline-level
8648 if (static_cast<nsIFormControlFrame*>(do_QueryFrame(aFrame))) {
8649 return result;
8650 }
8651
8652 // Check the frame itself
8653 // Fall through block-in-inline split frames because their mContent is
8654 // the content of the inline frames they were created from. The
8655 // first/last child of such frames is the real block frame we're
8656 // looking for.
8657 if ((aFrame->IsBlockOutside() &&
8658 !aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) ||
8659 aFrame->IsBrFrame()) {
8660 nsIContent* content = aFrame->GetContent();
8661 result.mContent = content->GetParent();
8662 // In some cases (bug 310589, bug 370174) we end up here with a null
8663 // content. This probably shouldn't ever happen, but since it sometimes
8664 // does, we want to avoid crashing here.
8665 NS_ASSERTION(result.mContent, "Unexpected orphan content");
8666 if (result.mContent) {
8667 result.mOffset = result.mContent->ComputeIndexOf_Deprecated(content) +
8668 (aDirection == eDirPrevious ? 1 : 0);
8669 }
8670 return result;
8671 }
8672
8673 result = FindLineBreakInText(aFrame, aDirection);
8674 if (result.mContent) {
8675 return result;
8676 }
8677
8678 // Iterate over children and call ourselves recursively
8679 if (aDirection == eDirPrevious) {
8680 nsIFrame* child =
8681 aFrame->GetChildList(nsIFrame::kPrincipalList).LastChild();
8682 while (child && !result.mContent) {
8683 result = FindLineBreakingFrame(child, aDirection);
8684 child = child->GetPrevSibling();
8685 }
8686 } else { // eDirNext
8687 nsIFrame* child = aFrame->PrincipalChildList().FirstChild();
8688 while (child && !result.mContent) {
8689 result = FindLineBreakingFrame(child, aDirection);
8690 child = child->GetNextSibling();
8691 }
8692 }
8693 return result;
8694 }
8695
PeekOffsetForParagraph(nsPeekOffsetStruct * aPos)8696 nsresult nsIFrame::PeekOffsetForParagraph(nsPeekOffsetStruct* aPos) {
8697 nsIFrame* frame = this;
8698 nsContentAndOffset blockFrameOrBR;
8699 blockFrameOrBR.mContent = nullptr;
8700 bool reachedLimit = frame->IsBlockOutside() || IsEditingHost(frame);
8701
8702 auto traverse = [&aPos](nsIFrame* current) {
8703 return aPos->mDirection == eDirPrevious ? current->GetPrevSibling()
8704 : current->GetNextSibling();
8705 };
8706
8707 // Go through containing frames until reaching a block frame.
8708 // In each step, search the previous (or next) siblings for the closest
8709 // "stop frame" (a block frame or a BRFrame).
8710 // If found, set it to be the selection boundary and abort.
8711 while (!reachedLimit) {
8712 nsIFrame* parent = frame->GetParent();
8713 // Treat a frame associated with the root content as if it were a block
8714 // frame.
8715 if (!frame->mContent || !frame->mContent->GetParent()) {
8716 reachedLimit = true;
8717 break;
8718 }
8719
8720 if (aPos->mDirection == eDirNext) {
8721 // Try to find our own line-break before looking at our siblings.
8722 blockFrameOrBR = FindLineBreakInText(frame, eDirNext);
8723 }
8724
8725 nsIFrame* sibling = traverse(frame);
8726 while (sibling && !blockFrameOrBR.mContent) {
8727 blockFrameOrBR = FindLineBreakingFrame(sibling, aPos->mDirection);
8728 sibling = traverse(sibling);
8729 }
8730 if (blockFrameOrBR.mContent) {
8731 aPos->mResultContent = blockFrameOrBR.mContent;
8732 aPos->mContentOffset = blockFrameOrBR.mOffset;
8733 break;
8734 }
8735 frame = parent;
8736 reachedLimit = frame && (frame->IsBlockOutside() || IsEditingHost(frame));
8737 }
8738
8739 if (reachedLimit) { // no "stop frame" found
8740 aPos->mResultContent = frame->GetContent();
8741 if (aPos->mDirection == eDirPrevious) {
8742 aPos->mContentOffset = 0;
8743 } else if (aPos->mResultContent) {
8744 aPos->mContentOffset = aPos->mResultContent->GetChildCount();
8745 }
8746 }
8747 return NS_OK;
8748 }
8749
8750 // Determine movement direction relative to frame
IsMovingInFrameDirection(const nsIFrame * frame,nsDirection aDirection,bool aVisual)8751 static bool IsMovingInFrameDirection(const nsIFrame* frame,
8752 nsDirection aDirection, bool aVisual) {
8753 bool isReverseDirection =
8754 aVisual && nsBidiPresUtils::IsReversedDirectionFrame(frame);
8755 return aDirection == (isReverseDirection ? eDirPrevious : eDirNext);
8756 }
8757
8758 // Determines "are we looking for a boundary between whitespace and
8759 // non-whitespace (in the direction we're moving in)". It is true when moving
8760 // forward and looking for a beginning of a word, or when moving backwards and
8761 // looking for an end of a word.
ShouldWordSelectionEatSpace(const nsPeekOffsetStruct & aPos)8762 static bool ShouldWordSelectionEatSpace(const nsPeekOffsetStruct& aPos) {
8763 if (aPos.mWordMovementType != eDefaultBehavior) {
8764 // aPos->mWordMovementType possible values:
8765 // eEndWord: eat the space if we're moving backwards
8766 // eStartWord: eat the space if we're moving forwards
8767 return (aPos.mWordMovementType == eEndWord) ==
8768 (aPos.mDirection == eDirPrevious);
8769 }
8770 // Use the hidden preference which is based on operating system
8771 // behavior. This pref only affects whether moving forward by word
8772 // should go to the end of this word or start of the next word. When
8773 // going backwards, the start of the word is always used, on every
8774 // operating system.
8775 return aPos.mDirection == eDirNext &&
8776 StaticPrefs::layout_word_select_eat_space_to_next_word();
8777 }
8778
8779 enum class OffsetIsAtLineEdge : bool { No, Yes };
8780
SetPeekResultFromFrame(nsPeekOffsetStruct & aPos,nsIFrame * aFrame,int32_t aOffset,OffsetIsAtLineEdge aAtLineEdge)8781 static void SetPeekResultFromFrame(nsPeekOffsetStruct& aPos, nsIFrame* aFrame,
8782 int32_t aOffset,
8783 OffsetIsAtLineEdge aAtLineEdge) {
8784 FrameContentRange range = GetRangeForFrame(aFrame);
8785 aPos.mResultFrame = aFrame;
8786 aPos.mResultContent = range.content;
8787 // Output offset is relative to content, not frame
8788 aPos.mContentOffset =
8789 aOffset < 0 ? range.end + aOffset + 1 : range.start + aOffset;
8790 if (aAtLineEdge == OffsetIsAtLineEdge::Yes) {
8791 aPos.mAttach = aPos.mContentOffset == range.start ? CARET_ASSOCIATE_AFTER
8792 : CARET_ASSOCIATE_BEFORE;
8793 }
8794 }
8795
TransferTo(nsPeekOffsetStruct & aPos) const8796 void nsIFrame::SelectablePeekReport::TransferTo(
8797 nsPeekOffsetStruct& aPos) const {
8798 return SetPeekResultFromFrame(aPos, mFrame, mOffset, OffsetIsAtLineEdge::No);
8799 }
8800
SelectablePeekReport(const mozilla::GenericErrorResult<nsresult> && aErr)8801 nsIFrame::SelectablePeekReport::SelectablePeekReport(
8802 const mozilla::GenericErrorResult<nsresult>&& aErr) {
8803 MOZ_ASSERT(NS_FAILED(aErr.operator nsresult()));
8804 // Return an empty report
8805 }
8806
PeekOffsetForCharacter(nsPeekOffsetStruct * aPos,int32_t aOffset)8807 nsresult nsIFrame::PeekOffsetForCharacter(nsPeekOffsetStruct* aPos,
8808 int32_t aOffset) {
8809 SelectablePeekReport current{this, aOffset};
8810
8811 nsIFrame::FrameSearchResult peekSearchState = CONTINUE;
8812
8813 while (peekSearchState != FOUND) {
8814 bool movingInFrameDirection = IsMovingInFrameDirection(
8815 current.mFrame, aPos->mDirection, aPos->mVisual);
8816
8817 if (current.mJumpedLine) {
8818 // If we jumped lines, it's as if we found a character, but we still need
8819 // to eat non-renderable content on the new line.
8820 peekSearchState = current.PeekOffsetNoAmount(movingInFrameDirection);
8821 } else {
8822 PeekOffsetCharacterOptions options;
8823 options.mRespectClusters = aPos->mAmount == eSelectCluster;
8824 peekSearchState =
8825 current.PeekOffsetCharacter(movingInFrameDirection, options);
8826 }
8827
8828 current.mMovedOverNonSelectableText |=
8829 peekSearchState == CONTINUE_UNSELECTABLE;
8830
8831 if (peekSearchState != FOUND) {
8832 SelectablePeekReport next = current.mFrame->GetFrameFromDirection(*aPos);
8833 if (next.Failed()) {
8834 return NS_ERROR_FAILURE;
8835 }
8836 next.mJumpedLine |= current.mJumpedLine;
8837 next.mMovedOverNonSelectableText |= current.mMovedOverNonSelectableText;
8838 next.mHasSelectableFrame |= current.mHasSelectableFrame;
8839 current = next;
8840 }
8841
8842 // Found frame, but because we moved over non selectable text we want
8843 // the offset to be at the frame edge. Note that if we are extending the
8844 // selection, this doesn't matter.
8845 if (peekSearchState == FOUND && current.mMovedOverNonSelectableText &&
8846 (!aPos->mExtend || current.mHasSelectableFrame)) {
8847 auto [start, end] = current.mFrame->GetOffsets();
8848 current.mOffset = aPos->mDirection == eDirNext ? 0 : end - start;
8849 }
8850 }
8851
8852 // Set outputs
8853 current.TransferTo(*aPos);
8854 // If we're dealing with a text frame and moving backward positions us at
8855 // the end of that line, decrease the offset by one to make sure that
8856 // we're placed before the linefeed character on the previous line.
8857 if (current.mOffset < 0 && current.mJumpedLine &&
8858 aPos->mDirection == eDirPrevious &&
8859 current.mFrame->HasSignificantTerminalNewline() &&
8860 !current.mIgnoredBrFrame) {
8861 --aPos->mContentOffset;
8862 }
8863 return NS_OK;
8864 }
8865
PeekOffsetForWord(nsPeekOffsetStruct * aPos,int32_t aOffset)8866 nsresult nsIFrame::PeekOffsetForWord(nsPeekOffsetStruct* aPos,
8867 int32_t aOffset) {
8868 SelectablePeekReport current{this, aOffset};
8869 bool shouldStopAtHardBreak =
8870 aPos->mWordMovementType == eDefaultBehavior &&
8871 StaticPrefs::layout_word_select_eat_space_to_next_word();
8872 bool wordSelectEatSpace = ShouldWordSelectionEatSpace(*aPos);
8873
8874 PeekWordState state;
8875 while (true) {
8876 bool movingInFrameDirection = IsMovingInFrameDirection(
8877 current.mFrame, aPos->mDirection, aPos->mVisual);
8878
8879 FrameSearchResult searchResult = current.mFrame->PeekOffsetWord(
8880 movingInFrameDirection, wordSelectEatSpace, aPos->mIsKeyboardSelect,
8881 ¤t.mOffset, &state, aPos->mTrimSpaces);
8882 if (searchResult == FOUND) {
8883 break;
8884 }
8885
8886 SelectablePeekReport next = current.mFrame->GetFrameFromDirection(*aPos);
8887 if (next.Failed()) {
8888 // If we've crossed the line boundary, check to make sure that we
8889 // have not consumed a trailing newline as whitespace if it's
8890 // significant.
8891 if (next.mJumpedLine && wordSelectEatSpace &&
8892 current.mFrame->HasSignificantTerminalNewline() &&
8893 current.mFrame->StyleText()->mWhiteSpace !=
8894 StyleWhiteSpace::PreLine) {
8895 current.mOffset -= 1;
8896 }
8897 break;
8898 }
8899
8900 if (next.mJumpedLine && !wordSelectEatSpace && state.mSawBeforeType) {
8901 // We can't jump lines if we're looking for whitespace following
8902 // non-whitespace, and we already encountered non-whitespace.
8903 break;
8904 }
8905
8906 if (shouldStopAtHardBreak && next.mJumpedHardBreak) {
8907 /**
8908 * Prev, always: Jump and stop right there
8909 * Next, saw inline: just stop
8910 * Next, no inline: Jump and consume whitespaces
8911 */
8912 if (aPos->mDirection == eDirPrevious) {
8913 // Try moving to the previous line if exists
8914 current.TransferTo(*aPos);
8915 current.mFrame->PeekOffsetForCharacter(aPos, current.mOffset);
8916 return NS_OK;
8917 }
8918 if (state.mSawInlineCharacter || current.mJumpedHardBreak) {
8919 if (current.mFrame->HasSignificantTerminalNewline()) {
8920 current.mOffset -= 1;
8921 }
8922 current.TransferTo(*aPos);
8923 return NS_OK;
8924 }
8925 // Mark the state as whitespace and continue
8926 state.Update(false, true);
8927 }
8928
8929 if (next.mJumpedLine) {
8930 state.mContext.Truncate();
8931 }
8932 current = next;
8933 // Jumping a line is equivalent to encountering whitespace
8934 // This affects only when it already met an actual character
8935 if (wordSelectEatSpace && next.mJumpedLine) {
8936 state.SetSawBeforeType();
8937 }
8938 }
8939
8940 // Set outputs
8941 current.TransferTo(*aPos);
8942 return NS_OK;
8943 }
8944
PeekOffsetForLine(nsPeekOffsetStruct * aPos)8945 nsresult nsIFrame::PeekOffsetForLine(nsPeekOffsetStruct* aPos) {
8946 nsIFrame* blockFrame = this;
8947 nsresult result = NS_ERROR_FAILURE;
8948
8949 while (NS_FAILED(result)) {
8950 auto [newBlock, lineFrame] =
8951 blockFrame->GetContainingBlockForLine(aPos->mScrollViewStop);
8952 if (!newBlock) {
8953 return NS_ERROR_FAILURE;
8954 }
8955 blockFrame = newBlock;
8956 nsAutoLineIterator iter = blockFrame->GetLineIterator();
8957 int32_t thisLine = iter->FindLineContaining(lineFrame);
8958 if (NS_WARN_IF(thisLine < 0)) {
8959 return NS_ERROR_FAILURE;
8960 }
8961
8962 int edgeCase = 0; // no edge case. this should look at thisLine
8963
8964 bool doneLooping = false; // tells us when no more block frames hit.
8965 // this part will find a frame or a block frame. if it's a block frame
8966 // it will "drill down" to find a viable frame or it will return an
8967 // error.
8968 nsIFrame* lastFrame = this;
8969 do {
8970 result = nsIFrame::GetNextPrevLineFromeBlockFrame(
8971 PresContext(), aPos, blockFrame, thisLine,
8972 edgeCase); // start from thisLine
8973
8974 // we came back to same spot! keep going
8975 if (NS_SUCCEEDED(result) &&
8976 (!aPos->mResultFrame || aPos->mResultFrame == lastFrame)) {
8977 aPos->mResultFrame = nullptr;
8978 if (aPos->mDirection == eDirPrevious) {
8979 thisLine--;
8980 } else {
8981 thisLine++;
8982 }
8983 } else { // if failure or success with different frame.
8984 doneLooping = true; // do not continue with while loop
8985 }
8986
8987 lastFrame = aPos->mResultFrame; // set last frame
8988
8989 // make sure block element is not the same as the one we had before
8990 if (NS_SUCCEEDED(result) && aPos->mResultFrame &&
8991 blockFrame != aPos->mResultFrame) {
8992 /* SPECIAL CHECK FOR TABLE NAVIGATION
8993 tables need to navigate also and the frame that supports it is
8994 nsTableRowGroupFrame which is INSIDE nsTableWrapperFrame.
8995 If we have stumbled onto an nsTableWrapperFrame we need to drill
8996 into nsTableRowGroup if we hit a header or footer that's ok just
8997 go into them.
8998 */
8999 bool searchTableBool = false;
9000 if (aPos->mResultFrame->IsTableWrapperFrame() ||
9001 aPos->mResultFrame->IsTableCellFrame()) {
9002 nsIFrame* frame =
9003 aPos->mResultFrame->PrincipalChildList().FirstChild();
9004 // got the table frame now
9005 // ok time to drill down to find iterator
9006 while (frame) {
9007 if (frame->CanProvideLineIterator()) {
9008 aPos->mResultFrame = frame;
9009 searchTableBool = true;
9010 result = NS_OK;
9011 break; // while(frame)
9012 }
9013 result = NS_ERROR_FAILURE;
9014 frame = frame->PrincipalChildList().FirstChild();
9015 }
9016 }
9017
9018 if (!searchTableBool) {
9019 result = aPos->mResultFrame->CanProvideLineIterator()
9020 ? NS_OK
9021 : NS_ERROR_FAILURE;
9022 }
9023
9024 // we've struck another block element!
9025 if (NS_SUCCEEDED(result)) {
9026 doneLooping = false;
9027 if (aPos->mDirection == eDirPrevious) {
9028 edgeCase = 1; // far edge, search from end backwards
9029 } else {
9030 edgeCase = -1; // near edge search from beginning onwards
9031 }
9032 thisLine = 0; // this line means nothing now.
9033 // everything else means something so keep looking "inside" the
9034 // block
9035 blockFrame = aPos->mResultFrame;
9036 } else {
9037 // THIS is to mean that everything is ok to the containing while
9038 // loop
9039 result = NS_OK;
9040 break;
9041 }
9042 }
9043 } while (!doneLooping);
9044 }
9045 return result;
9046 }
9047
PeekOffsetForLineEdge(nsPeekOffsetStruct * aPos)9048 nsresult nsIFrame::PeekOffsetForLineEdge(nsPeekOffsetStruct* aPos) {
9049 // Adjusted so that the caret can't get confused when content changes
9050 nsIFrame* frame = AdjustFrameForSelectionStyles(this);
9051 Element* editingHost = frame->GetContent()->GetEditingHost();
9052
9053 auto [blockFrame, lineFrame] =
9054 frame->GetContainingBlockForLine(aPos->mScrollViewStop);
9055 if (!blockFrame) {
9056 return NS_ERROR_FAILURE;
9057 }
9058 nsAutoLineIterator it = blockFrame->GetLineIterator();
9059 int32_t thisLine = it->FindLineContaining(lineFrame);
9060 if (thisLine < 0) {
9061 return NS_ERROR_FAILURE;
9062 }
9063
9064 nsIFrame* baseFrame = nullptr;
9065 bool endOfLine = (eSelectEndLine == aPos->mAmount);
9066
9067 if (aPos->mVisual && PresContext()->BidiEnabled()) {
9068 nsIFrame* firstFrame;
9069 bool isReordered;
9070 nsIFrame* lastFrame;
9071 MOZ_TRY(
9072 it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame));
9073 baseFrame = endOfLine ? lastFrame : firstFrame;
9074 } else {
9075 auto line = it->GetLine(thisLine).unwrap();
9076
9077 nsIFrame* frame = line.mFirstFrameOnLine;
9078 bool lastFrameWasEditable = false;
9079 for (int32_t count = line.mNumFramesOnLine; count;
9080 --count, frame = frame->GetNextSibling()) {
9081 if (frame->IsGeneratedContentFrame()) {
9082 continue;
9083 }
9084 // When jumping to the end of the line with the "end" key,
9085 // try to skip over brFrames
9086 if (endOfLine && line.mNumFramesOnLine > 1 && frame->IsBrFrame() &&
9087 lastFrameWasEditable == frame->GetContent()->IsEditable()) {
9088 continue;
9089 }
9090 lastFrameWasEditable =
9091 frame->GetContent() && frame->GetContent()->IsEditable();
9092 baseFrame = frame;
9093 if (!endOfLine) {
9094 break;
9095 }
9096 }
9097 }
9098 if (!baseFrame) {
9099 return NS_ERROR_FAILURE;
9100 }
9101 // Make sure we are not leaving our inline editing host if exists
9102 if (editingHost) {
9103 if (nsIFrame* frame = editingHost->GetPrimaryFrame()) {
9104 if (frame->IsInlineOutside() &&
9105 !editingHost->Contains(baseFrame->GetContent())) {
9106 baseFrame = frame;
9107 if (endOfLine) {
9108 baseFrame = baseFrame->LastContinuation();
9109 }
9110 }
9111 }
9112 }
9113 FrameTarget targetFrame = DrillDownToSelectionFrame(baseFrame, endOfLine, 0);
9114 SetPeekResultFromFrame(*aPos, targetFrame.frame, endOfLine ? -1 : 0,
9115 OffsetIsAtLineEdge::Yes);
9116 if (endOfLine && targetFrame.frame->HasSignificantTerminalNewline()) {
9117 // Do not position the caret after the terminating newline if we're
9118 // trying to move to the end of line (see bug 596506)
9119 --aPos->mContentOffset;
9120 }
9121 if (!aPos->mResultContent) {
9122 return NS_ERROR_FAILURE;
9123 }
9124 return NS_OK;
9125 }
9126
PeekOffset(nsPeekOffsetStruct * aPos)9127 nsresult nsIFrame::PeekOffset(nsPeekOffsetStruct* aPos) {
9128 MOZ_ASSERT(aPos);
9129
9130 if (NS_WARN_IF(HasAnyStateBits(NS_FRAME_IS_DIRTY))) {
9131 // FIXME(Bug 1654362): <caption> currently can remain dirty.
9132 return NS_ERROR_UNEXPECTED;
9133 }
9134
9135 // Translate content offset to be relative to frame
9136 int32_t offset = aPos->mStartOffset - GetRangeForFrame(this).start;
9137
9138 switch (aPos->mAmount) {
9139 case eSelectCharacter:
9140 case eSelectCluster:
9141 return PeekOffsetForCharacter(aPos, offset);
9142 case eSelectWordNoSpace:
9143 // eSelectWordNoSpace means that we should not be eating any whitespace
9144 // when moving to the adjacent word. This means that we should set aPos->
9145 // mWordMovementType to eEndWord if we're moving forwards, and to
9146 // eStartWord if we're moving backwards.
9147 if (aPos->mDirection == eDirPrevious) {
9148 aPos->mWordMovementType = eStartWord;
9149 } else {
9150 aPos->mWordMovementType = eEndWord;
9151 }
9152 // Intentionally fall through the eSelectWord case.
9153 [[fallthrough]];
9154 case eSelectWord:
9155 return PeekOffsetForWord(aPos, offset);
9156 case eSelectLine:
9157 return PeekOffsetForLine(aPos);
9158 case eSelectBeginLine:
9159 case eSelectEndLine:
9160 return PeekOffsetForLineEdge(aPos);
9161 case eSelectParagraph:
9162 return PeekOffsetForParagraph(aPos);
9163 default: {
9164 NS_ASSERTION(false, "Invalid amount");
9165 return NS_ERROR_FAILURE;
9166 }
9167 }
9168 }
9169
PeekOffsetNoAmount(bool aForward,int32_t * aOffset)9170 nsIFrame::FrameSearchResult nsIFrame::PeekOffsetNoAmount(bool aForward,
9171 int32_t* aOffset) {
9172 NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range");
9173 // Sure, we can stop right here.
9174 return FOUND;
9175 }
9176
PeekOffsetCharacter(bool aForward,int32_t * aOffset,PeekOffsetCharacterOptions aOptions)9177 nsIFrame::FrameSearchResult nsIFrame::PeekOffsetCharacter(
9178 bool aForward, int32_t* aOffset, PeekOffsetCharacterOptions aOptions) {
9179 NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range");
9180 int32_t startOffset = *aOffset;
9181 // A negative offset means "end of frame", which in our case means offset 1.
9182 if (startOffset < 0) startOffset = 1;
9183 if (aForward == (startOffset == 0)) {
9184 // We're before the frame and moving forward, or after it and moving
9185 // backwards: skip to the other side and we're done.
9186 *aOffset = 1 - startOffset;
9187 return FOUND;
9188 }
9189 return CONTINUE;
9190 }
9191
PeekOffsetWord(bool aForward,bool aWordSelectEatSpace,bool aIsKeyboardSelect,int32_t * aOffset,PeekWordState * aState,bool)9192 nsIFrame::FrameSearchResult nsIFrame::PeekOffsetWord(
9193 bool aForward, bool aWordSelectEatSpace, bool aIsKeyboardSelect,
9194 int32_t* aOffset, PeekWordState* aState, bool /*aTrimSpaces*/) {
9195 NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range");
9196 int32_t startOffset = *aOffset;
9197 // This isn't text, so truncate the context
9198 aState->mContext.Truncate();
9199 if (startOffset < 0) startOffset = 1;
9200 if (aForward == (startOffset == 0)) {
9201 // We're before the frame and moving forward, or after it and moving
9202 // backwards. If we're looking for non-whitespace, we found it (without
9203 // skipping this frame).
9204 if (!aState->mAtStart) {
9205 if (aState->mLastCharWasPunctuation) {
9206 // We're not punctuation, so this is a punctuation boundary.
9207 if (BreakWordBetweenPunctuation(aState, aForward, false, false,
9208 aIsKeyboardSelect))
9209 return FOUND;
9210 } else {
9211 // This is not a punctuation boundary.
9212 if (aWordSelectEatSpace && aState->mSawBeforeType) return FOUND;
9213 }
9214 }
9215 // Otherwise skip to the other side and note that we encountered
9216 // non-whitespace.
9217 *aOffset = 1 - startOffset;
9218 aState->Update(false, // not punctuation
9219 false // not whitespace
9220 );
9221 if (!aWordSelectEatSpace) aState->SetSawBeforeType();
9222 }
9223 return CONTINUE;
9224 }
9225
9226 // static
BreakWordBetweenPunctuation(const PeekWordState * aState,bool aForward,bool aPunctAfter,bool aWhitespaceAfter,bool aIsKeyboardSelect)9227 bool nsIFrame::BreakWordBetweenPunctuation(const PeekWordState* aState,
9228 bool aForward, bool aPunctAfter,
9229 bool aWhitespaceAfter,
9230 bool aIsKeyboardSelect) {
9231 NS_ASSERTION(aPunctAfter != aState->mLastCharWasPunctuation,
9232 "Call this only at punctuation boundaries");
9233 if (aState->mLastCharWasWhitespace) {
9234 // We always stop between whitespace and punctuation
9235 return true;
9236 }
9237 if (!StaticPrefs::layout_word_select_stop_at_punctuation()) {
9238 // When this pref is false, we never stop at a punctuation boundary unless
9239 // it's followed by whitespace (in the relevant direction).
9240 return aWhitespaceAfter;
9241 }
9242 if (!aIsKeyboardSelect) {
9243 // mouse caret movement (e.g. word selection) always stops at every
9244 // punctuation boundary
9245 return true;
9246 }
9247 bool afterPunct = aForward ? aState->mLastCharWasPunctuation : aPunctAfter;
9248 if (!afterPunct) {
9249 // keyboard caret movement only stops after punctuation (in content order)
9250 return false;
9251 }
9252 // Stop only if we've seen some non-punctuation since the last whitespace;
9253 // don't stop after punctuation that follows whitespace.
9254 return aState->mSeenNonPunctuationSinceWhitespace;
9255 }
9256
CheckVisibility(nsPresContext *,int32_t,int32_t,bool,bool *,bool *)9257 nsresult nsIFrame::CheckVisibility(nsPresContext*, int32_t, int32_t, bool,
9258 bool*, bool*) {
9259 return NS_ERROR_NOT_IMPLEMENTED;
9260 }
9261
GetContainingBlockForLine(bool aLockScroll) const9262 std::pair<nsIFrame*, nsIFrame*> nsIFrame::GetContainingBlockForLine(
9263 bool aLockScroll) const {
9264 const nsIFrame* parentFrame = this;
9265 const nsIFrame* frame;
9266 while (parentFrame) {
9267 frame = parentFrame;
9268 if (frame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
9269 // if we are searching for a frame that is not in flow we will not find
9270 // it. we must instead look for its placeholder
9271 if (frame->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER)) {
9272 // abspos continuations don't have placeholders, get the fif
9273 frame = frame->FirstInFlow();
9274 }
9275 frame = frame->GetPlaceholderFrame();
9276 if (!frame) {
9277 return std::pair(nullptr, nullptr);
9278 }
9279 }
9280 parentFrame = frame->GetParent();
9281 if (parentFrame) {
9282 if (aLockScroll && parentFrame->IsScrollFrame()) {
9283 return std::pair(nullptr, nullptr);
9284 }
9285 if (parentFrame->CanProvideLineIterator()) {
9286 return std::pair(const_cast<nsIFrame*>(parentFrame),
9287 const_cast<nsIFrame*>(frame));
9288 }
9289 }
9290 }
9291 return std::pair(nullptr, nullptr);
9292 }
9293
IsVisuallyAtLineEdge(nsILineIterator * aLineIterator,int32_t aLine,nsDirection aDirection)9294 Result<bool, nsresult> nsIFrame::IsVisuallyAtLineEdge(
9295 nsILineIterator* aLineIterator, int32_t aLine, nsDirection aDirection) {
9296 nsIFrame* firstFrame;
9297 nsIFrame* lastFrame;
9298
9299 bool lineIsRTL = aLineIterator->GetDirection();
9300 bool isReordered;
9301
9302 MOZ_TRY(aLineIterator->CheckLineOrder(aLine, &isReordered, &firstFrame,
9303 &lastFrame));
9304
9305 nsIFrame** framePtr = aDirection == eDirPrevious ? &firstFrame : &lastFrame;
9306 if (!*framePtr) {
9307 return true;
9308 }
9309
9310 bool frameIsRTL = (nsBidiPresUtils::FrameDirection(*framePtr) ==
9311 mozilla::intl::BidiDirection::RTL);
9312 if ((frameIsRTL == lineIsRTL) == (aDirection == eDirPrevious)) {
9313 nsIFrame::GetFirstLeaf(framePtr);
9314 } else {
9315 nsIFrame::GetLastLeaf(framePtr);
9316 }
9317 return *framePtr == this;
9318 }
9319
IsLogicallyAtLineEdge(nsILineIterator * aLineIterator,int32_t aLine,nsDirection aDirection)9320 Result<bool, nsresult> nsIFrame::IsLogicallyAtLineEdge(
9321 nsILineIterator* aLineIterator, int32_t aLine, nsDirection aDirection) {
9322 auto line = aLineIterator->GetLine(aLine).unwrap();
9323
9324 if (aDirection == eDirPrevious) {
9325 nsIFrame* firstFrame = line.mFirstFrameOnLine;
9326 nsIFrame::GetFirstLeaf(&firstFrame);
9327 return firstFrame == this;
9328 }
9329
9330 // eDirNext
9331 nsIFrame* lastFrame = line.mFirstFrameOnLine;
9332 for (int32_t lineFrameCount = line.mNumFramesOnLine; lineFrameCount > 1;
9333 lineFrameCount--) {
9334 lastFrame = lastFrame->GetNextSibling();
9335 if (!lastFrame) {
9336 NS_ERROR("should not be reached nsIFrame");
9337 return Err(NS_ERROR_FAILURE);
9338 }
9339 }
9340 nsIFrame::GetLastLeaf(&lastFrame);
9341 return lastFrame == this;
9342 }
9343
GetFrameFromDirection(nsDirection aDirection,bool aVisual,bool aJumpLines,bool aScrollViewStop,bool aForceEditableRegion)9344 nsIFrame::SelectablePeekReport nsIFrame::GetFrameFromDirection(
9345 nsDirection aDirection, bool aVisual, bool aJumpLines, bool aScrollViewStop,
9346 bool aForceEditableRegion) {
9347 SelectablePeekReport result;
9348
9349 nsPresContext* presContext = PresContext();
9350 bool needsVisualTraversal = aVisual && presContext->BidiEnabled();
9351 nsCOMPtr<nsIFrameEnumerator> frameTraversal;
9352 MOZ_TRY(NS_NewFrameTraversal(getter_AddRefs(frameTraversal), presContext,
9353 this, eLeaf, needsVisualTraversal,
9354 aScrollViewStop,
9355 true, // aFollowOOFs
9356 false // aSkipPopupChecks
9357 ));
9358
9359 // Find the prev/next selectable frame
9360 bool selectable = false;
9361 nsIFrame* traversedFrame = this;
9362 while (!selectable) {
9363 auto [blockFrame, lineFrame] =
9364 traversedFrame->GetContainingBlockForLine(aScrollViewStop);
9365 if (!blockFrame) {
9366 return result;
9367 }
9368
9369 nsAutoLineIterator it = blockFrame->GetLineIterator();
9370 int32_t thisLine = it->FindLineContaining(lineFrame);
9371 if (thisLine < 0) {
9372 return result;
9373 }
9374
9375 bool atLineEdge;
9376 MOZ_TRY_VAR(
9377 atLineEdge,
9378 needsVisualTraversal
9379 ? traversedFrame->IsVisuallyAtLineEdge(it, thisLine, aDirection)
9380 : traversedFrame->IsLogicallyAtLineEdge(it, thisLine, aDirection));
9381 if (atLineEdge) {
9382 result.mJumpedLine = true;
9383 if (!aJumpLines) {
9384 return result; // we are done. cannot jump lines
9385 }
9386 int32_t lineToCheckWrap =
9387 aDirection == eDirPrevious ? thisLine - 1 : thisLine;
9388 if (lineToCheckWrap < 0 ||
9389 !it->GetLine(lineToCheckWrap).unwrap().mIsWrapped) {
9390 result.mJumpedHardBreak = true;
9391 }
9392 }
9393
9394 traversedFrame = frameTraversal->Traverse(aDirection == eDirNext);
9395 if (!traversedFrame) {
9396 return result;
9397 }
9398
9399 auto IsSelectable = [aForceEditableRegion](const nsIFrame* aFrame) {
9400 if (!aFrame->IsSelectable(nullptr)) {
9401 return false;
9402 }
9403 return !aForceEditableRegion || aFrame->GetContent()->IsEditable();
9404 };
9405
9406 // Skip br frames, but only if we can select something before hitting the
9407 // end of the line or a non-selectable region.
9408 if (atLineEdge && aDirection == eDirPrevious &&
9409 traversedFrame->IsBrFrame()) {
9410 for (nsIFrame* current = traversedFrame->GetPrevSibling(); current;
9411 current = current->GetPrevSibling()) {
9412 if (!current->IsBlockOutside() && IsSelectable(current)) {
9413 if (!current->IsBrFrame()) {
9414 result.mIgnoredBrFrame = true;
9415 }
9416 break;
9417 }
9418 }
9419 if (result.mIgnoredBrFrame) {
9420 continue;
9421 }
9422 }
9423
9424 selectable = IsSelectable(traversedFrame);
9425 if (!selectable) {
9426 if (traversedFrame->IsSelectable(nullptr)) {
9427 result.mHasSelectableFrame = true;
9428 }
9429 result.mMovedOverNonSelectableText = true;
9430 }
9431 } // while (!selectable)
9432
9433 result.mOffset = (aDirection == eDirNext) ? 0 : -1;
9434
9435 if (aVisual && nsBidiPresUtils::IsReversedDirectionFrame(traversedFrame)) {
9436 // The new frame is reverse-direction, go to the other end
9437 result.mOffset = -1 - result.mOffset;
9438 }
9439 result.mFrame = traversedFrame;
9440 return result;
9441 }
9442
GetFrameFromDirection(const nsPeekOffsetStruct & aPos)9443 nsIFrame::SelectablePeekReport nsIFrame::GetFrameFromDirection(
9444 const nsPeekOffsetStruct& aPos) {
9445 return GetFrameFromDirection(aPos.mDirection, aPos.mVisual, aPos.mJumpLines,
9446 aPos.mScrollViewStop, aPos.mForceEditableRegion);
9447 }
9448
GetClosestView(nsPoint * aOffset) const9449 nsView* nsIFrame::GetClosestView(nsPoint* aOffset) const {
9450 nsPoint offset(0, 0);
9451 for (const nsIFrame* f = this; f; f = f->GetParent()) {
9452 if (f->HasView()) {
9453 if (aOffset) *aOffset = offset;
9454 return f->GetView();
9455 }
9456 offset += f->GetPosition();
9457 }
9458
9459 MOZ_ASSERT_UNREACHABLE("No view on any parent? How did that happen?");
9460 return nullptr;
9461 }
9462
9463 /* virtual */
ChildIsDirty(nsIFrame * aChild)9464 void nsIFrame::ChildIsDirty(nsIFrame* aChild) {
9465 MOZ_ASSERT_UNREACHABLE(
9466 "should never be called on a frame that doesn't "
9467 "inherit from nsContainerFrame");
9468 }
9469
9470 #ifdef ACCESSIBILITY
AccessibleType()9471 a11y::AccType nsIFrame::AccessibleType() {
9472 if (IsTableCaption() && !GetRect().IsEmpty()) {
9473 return a11y::eHTMLCaptionType;
9474 }
9475 return a11y::eNoType;
9476 }
9477 #endif
9478
ClearOverflowRects()9479 bool nsIFrame::ClearOverflowRects() {
9480 if (mOverflow.mType == OverflowStorageType::None) {
9481 return false;
9482 }
9483 if (mOverflow.mType == OverflowStorageType::Large) {
9484 RemoveProperty(OverflowAreasProperty());
9485 }
9486 mOverflow.mType = OverflowStorageType::None;
9487 return true;
9488 }
9489
SetOverflowAreas(const OverflowAreas & aOverflowAreas)9490 bool nsIFrame::SetOverflowAreas(const OverflowAreas& aOverflowAreas) {
9491 if (mOverflow.mType == OverflowStorageType::Large) {
9492 OverflowAreas* overflow = GetOverflowAreasProperty();
9493 bool changed = *overflow != aOverflowAreas;
9494 *overflow = aOverflowAreas;
9495
9496 // Don't bother with converting to the deltas form if we already
9497 // have a property.
9498 return changed;
9499 }
9500
9501 const nsRect& vis = aOverflowAreas.InkOverflow();
9502 uint32_t l = -vis.x, // left edge: positive delta is leftwards
9503 t = -vis.y, // top: positive is upwards
9504 r = vis.XMost() - mRect.width, // right: positive is rightwards
9505 b = vis.YMost() - mRect.height; // bottom: positive is downwards
9506 if (aOverflowAreas.ScrollableOverflow().IsEqualEdges(
9507 nsRect(nsPoint(0, 0), GetSize())) &&
9508 l <= InkOverflowDeltas::kMax && t <= InkOverflowDeltas::kMax &&
9509 r <= InkOverflowDeltas::kMax && b <= InkOverflowDeltas::kMax &&
9510 // we have to check these against zero because we *never* want to
9511 // set a frame as having no overflow in this function. This is
9512 // because FinishAndStoreOverflow calls this function prior to
9513 // SetRect based on whether the overflow areas match aNewSize.
9514 // In the case where the overflow areas exactly match mRect but
9515 // do not match aNewSize, we need to store overflow in a property
9516 // so that our eventual SetRect/SetSize will know that it has to
9517 // reset our overflow areas.
9518 (l | t | r | b) != 0) {
9519 InkOverflowDeltas oldDeltas = mOverflow.mInkOverflowDeltas;
9520 // It's a "small" overflow area so we store the deltas for each edge
9521 // directly in the frame, rather than allocating a separate rect.
9522 // If they're all zero, that's fine; we're setting things to
9523 // no-overflow.
9524 mOverflow.mInkOverflowDeltas.mLeft = l;
9525 mOverflow.mInkOverflowDeltas.mTop = t;
9526 mOverflow.mInkOverflowDeltas.mRight = r;
9527 mOverflow.mInkOverflowDeltas.mBottom = b;
9528 // There was no scrollable overflow before, and there isn't now.
9529 return oldDeltas != mOverflow.mInkOverflowDeltas;
9530 } else {
9531 bool changed =
9532 !aOverflowAreas.ScrollableOverflow().IsEqualEdges(
9533 nsRect(nsPoint(0, 0), GetSize())) ||
9534 !aOverflowAreas.InkOverflow().IsEqualEdges(InkOverflowFromDeltas());
9535
9536 // it's a large overflow area that we need to store as a property
9537 mOverflow.mType = OverflowStorageType::Large;
9538 AddProperty(OverflowAreasProperty(), new OverflowAreas(aOverflowAreas));
9539 return changed;
9540 }
9541 }
9542
9543 enum class ApplyTransform : bool { No, Yes };
9544
9545 /**
9546 * Compute the outline inner rect (so without outline-width and outline-offset)
9547 * of aFrame, maybe iterating over its descendants, in aFrame's coordinate space
9548 * or its post-transform coordinate space (depending on aApplyTransform).
9549 */
ComputeOutlineInnerRect(nsIFrame * aFrame,ApplyTransform aApplyTransform,bool & aOutValid,const nsSize * aSizeOverride=nullptr,const OverflowAreas * aOverflowOverride=nullptr)9550 static nsRect ComputeOutlineInnerRect(
9551 nsIFrame* aFrame, ApplyTransform aApplyTransform, bool& aOutValid,
9552 const nsSize* aSizeOverride = nullptr,
9553 const OverflowAreas* aOverflowOverride = nullptr) {
9554 const nsRect bounds(nsPoint(0, 0),
9555 aSizeOverride ? *aSizeOverride : aFrame->GetSize());
9556
9557 // The SVG container frames besides SVGTextFrame do not maintain
9558 // an accurate mRect. It will make the outline be larger than
9559 // we expect, we need to make them narrow to their children's outline.
9560 // aOutValid is set to false if the returned nsRect is not valid
9561 // and should not be included in the outline rectangle.
9562 aOutValid = !aFrame->HasAnyStateBits(NS_FRAME_SVG_LAYOUT) ||
9563 !aFrame->IsFrameOfType(nsIFrame::eSVGContainer) ||
9564 aFrame->IsSVGTextFrame();
9565
9566 nsRect u;
9567
9568 if (!aFrame->FrameMaintainsOverflow()) {
9569 return u;
9570 }
9571
9572 // Start from our border-box, transformed. See comment below about
9573 // transform of children.
9574 bool doTransform =
9575 aApplyTransform == ApplyTransform::Yes && aFrame->IsTransformed();
9576 TransformReferenceBox boundsRefBox(nullptr, bounds);
9577 if (doTransform) {
9578 u = nsDisplayTransform::TransformRect(bounds, aFrame, boundsRefBox);
9579 } else {
9580 u = bounds;
9581 }
9582
9583 if (aOutValid && !StaticPrefs::layout_outline_include_overflow()) {
9584 return u;
9585 }
9586
9587 // Only iterate through the children if the overflow areas suggest
9588 // that we might need to, and if the frame doesn't clip its overflow
9589 // anyway.
9590 if (aOverflowOverride) {
9591 if (!doTransform && bounds.IsEqualEdges(aOverflowOverride->InkOverflow()) &&
9592 bounds.IsEqualEdges(aOverflowOverride->ScrollableOverflow())) {
9593 return u;
9594 }
9595 } else {
9596 if (!doTransform && bounds.IsEqualEdges(aFrame->InkOverflowRect()) &&
9597 bounds.IsEqualEdges(aFrame->ScrollableOverflowRect())) {
9598 return u;
9599 }
9600 }
9601 const nsStyleDisplay* disp = aFrame->StyleDisplay();
9602 LayoutFrameType fType = aFrame->Type();
9603 auto overflowClipAxes = aFrame->ShouldApplyOverflowClipping(disp);
9604 if (overflowClipAxes == nsIFrame::PhysicalAxes::Both ||
9605 fType == LayoutFrameType::Scroll ||
9606 fType == LayoutFrameType::ListControl ||
9607 fType == LayoutFrameType::SVGOuterSVG) {
9608 return u;
9609 }
9610
9611 const nsStyleEffects* effects = aFrame->StyleEffects();
9612 Maybe<nsRect> clipPropClipRect =
9613 aFrame->GetClipPropClipRect(disp, effects, bounds.Size());
9614
9615 // Iterate over all children except pop-up, absolutely-positioned,
9616 // float, and overflow ones.
9617 const nsIFrame::ChildListIDs skip = {
9618 nsIFrame::kPopupList, nsIFrame::kSelectPopupList,
9619 nsIFrame::kAbsoluteList, nsIFrame::kFixedList,
9620 nsIFrame::kFloatList, nsIFrame::kOverflowList};
9621 for (const auto& [list, listID] : aFrame->ChildLists()) {
9622 if (skip.contains(listID)) {
9623 continue;
9624 }
9625
9626 for (nsIFrame* child : list) {
9627 if (child->IsPlaceholderFrame()) {
9628 continue;
9629 }
9630
9631 // Note that passing ApplyTransform::Yes when
9632 // child->Combines3DTransformWithAncestors() returns true is incorrect if
9633 // our aApplyTransform is No... but the opposite would be as well.
9634 // This is because elements within a preserve-3d scene are always
9635 // transformed up to the top of the scene. This means we don't have a
9636 // mechanism for getting a transform up to an intermediate point within
9637 // the scene. We choose to over-transform rather than under-transform
9638 // because this is consistent with other overflow areas.
9639 bool validRect = true;
9640 nsRect childRect =
9641 ComputeOutlineInnerRect(child, ApplyTransform::Yes, validRect) +
9642 child->GetPosition();
9643
9644 if (!validRect) {
9645 continue;
9646 }
9647
9648 if (clipPropClipRect) {
9649 // Intersect with the clip before transforming.
9650 childRect.IntersectRect(childRect, *clipPropClipRect);
9651 }
9652
9653 // Note that we transform each child separately according to
9654 // aFrame's transform, and then union, which gives a different
9655 // (smaller) result from unioning and then transforming the
9656 // union. This doesn't match the way we handle overflow areas
9657 // with 2-D transforms, though it does match the way we handle
9658 // overflow areas in preserve-3d 3-D scenes.
9659 if (doTransform && !child->Combines3DTransformWithAncestors()) {
9660 childRect =
9661 nsDisplayTransform::TransformRect(childRect, aFrame, boundsRefBox);
9662 }
9663
9664 // If a SVGContainer has a non-SVGContainer child, we assign
9665 // its child's outline to this SVGContainer directly.
9666 if (!aOutValid && validRect) {
9667 u = childRect;
9668 aOutValid = true;
9669 } else {
9670 u = u.UnionEdges(childRect);
9671 }
9672 }
9673 }
9674
9675 if (overflowClipAxes & nsIFrame::PhysicalAxes::Vertical) {
9676 u.y = bounds.y;
9677 u.height = bounds.height;
9678 }
9679 if (overflowClipAxes & nsIFrame::PhysicalAxes::Horizontal) {
9680 u.x = bounds.x;
9681 u.width = bounds.width;
9682 }
9683
9684 return u;
9685 }
9686
ComputeAndIncludeOutlineArea(nsIFrame * aFrame,OverflowAreas & aOverflowAreas,const nsSize & aNewSize)9687 static void ComputeAndIncludeOutlineArea(nsIFrame* aFrame,
9688 OverflowAreas& aOverflowAreas,
9689 const nsSize& aNewSize) {
9690 const nsStyleOutline* outline = aFrame->StyleOutline();
9691 if (!outline->ShouldPaintOutline()) {
9692 return;
9693 }
9694
9695 // When the outline property is set on a :-moz-block-inside-inline-wrapper
9696 // pseudo-element, it inherited that outline from the inline that was broken
9697 // because it contained a block. In that case, we don't want a really wide
9698 // outline if the block inside the inline is narrow, so union the actual
9699 // contents of the anonymous blocks.
9700 nsIFrame* frameForArea = aFrame;
9701 do {
9702 PseudoStyleType pseudoType = frameForArea->Style()->GetPseudoType();
9703 if (pseudoType != PseudoStyleType::mozBlockInsideInlineWrapper) break;
9704 // If we're done, we really want it and all its later siblings.
9705 frameForArea = frameForArea->PrincipalChildList().FirstChild();
9706 NS_ASSERTION(frameForArea, "anonymous block with no children?");
9707 } while (frameForArea);
9708
9709 // Find the union of the border boxes of all descendants, or in
9710 // the block-in-inline case, all descendants we care about.
9711 //
9712 // Note that the interesting perspective-related cases are taken
9713 // care of by the code that handles those issues for overflow
9714 // calling FinishAndStoreOverflow again, which in turn calls this
9715 // function again. We still need to deal with preserve-3d a bit.
9716 nsRect innerRect;
9717 bool validRect = false;
9718 if (frameForArea == aFrame) {
9719 innerRect = ComputeOutlineInnerRect(aFrame, ApplyTransform::No, validRect,
9720 &aNewSize, &aOverflowAreas);
9721 } else {
9722 for (; frameForArea; frameForArea = frameForArea->GetNextSibling()) {
9723 nsRect r =
9724 ComputeOutlineInnerRect(frameForArea, ApplyTransform::Yes, validRect);
9725
9726 // Adjust for offsets transforms up to aFrame's pre-transform
9727 // (i.e., normal) coordinate space; see comments in
9728 // UnionBorderBoxes for some of the subtlety here.
9729 for (nsIFrame *f = frameForArea, *parent = f->GetParent();
9730 /* see middle of loop */; f = parent, parent = f->GetParent()) {
9731 r += f->GetPosition();
9732 if (parent == aFrame) {
9733 break;
9734 }
9735 if (parent->IsTransformed() && !f->Combines3DTransformWithAncestors()) {
9736 TransformReferenceBox refBox(parent);
9737 r = nsDisplayTransform::TransformRect(r, parent, refBox);
9738 }
9739 }
9740
9741 innerRect.UnionRect(innerRect, r);
9742 }
9743 }
9744
9745 // Keep this code in sync with nsDisplayOutline::GetInnerRect.
9746 SetOrUpdateRectValuedProperty(aFrame, nsIFrame::OutlineInnerRectProperty(),
9747 innerRect);
9748 const nscoord offset = outline->mOutlineOffset.ToAppUnits();
9749 nsRect outerRect(innerRect);
9750 bool useOutlineAuto = false;
9751 if (StaticPrefs::layout_css_outline_style_auto_enabled()) {
9752 useOutlineAuto = outline->mOutlineStyle.IsAuto();
9753 if (MOZ_UNLIKELY(useOutlineAuto)) {
9754 nsPresContext* presContext = aFrame->PresContext();
9755 nsITheme* theme = presContext->Theme();
9756 if (theme->ThemeSupportsWidget(presContext, aFrame,
9757 StyleAppearance::FocusOutline)) {
9758 outerRect.Inflate(offset);
9759 theme->GetWidgetOverflow(presContext->DeviceContext(), aFrame,
9760 StyleAppearance::FocusOutline, &outerRect);
9761 } else {
9762 useOutlineAuto = false;
9763 }
9764 }
9765 }
9766 if (MOZ_LIKELY(!useOutlineAuto)) {
9767 nscoord width = outline->GetOutlineWidth();
9768 outerRect.Inflate(width + offset);
9769 }
9770
9771 nsRect& vo = aOverflowAreas.InkOverflow();
9772 vo = vo.UnionEdges(innerRect.Union(outerRect));
9773 }
9774
FinishAndStoreOverflow(OverflowAreas & aOverflowAreas,nsSize aNewSize,nsSize * aOldSize,const nsStyleDisplay * aStyleDisplay)9775 bool nsIFrame::FinishAndStoreOverflow(OverflowAreas& aOverflowAreas,
9776 nsSize aNewSize, nsSize* aOldSize,
9777 const nsStyleDisplay* aStyleDisplay) {
9778 MOZ_ASSERT(FrameMaintainsOverflow(),
9779 "Don't call - overflow rects not maintained on these SVG frames");
9780
9781 const nsStyleDisplay* disp = StyleDisplayWithOptionalParam(aStyleDisplay);
9782 bool hasTransform = IsTransformed();
9783
9784 nsRect bounds(nsPoint(0, 0), aNewSize);
9785 // Store the passed in overflow area if we are a preserve-3d frame or we have
9786 // a transform, and it's not just the frame bounds.
9787 if (hasTransform || Combines3DTransformWithAncestors()) {
9788 if (!aOverflowAreas.InkOverflow().IsEqualEdges(bounds) ||
9789 !aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds)) {
9790 OverflowAreas* initial = GetProperty(nsIFrame::InitialOverflowProperty());
9791 if (!initial) {
9792 AddProperty(nsIFrame::InitialOverflowProperty(),
9793 new OverflowAreas(aOverflowAreas));
9794 } else if (initial != &aOverflowAreas) {
9795 *initial = aOverflowAreas;
9796 }
9797 } else {
9798 RemoveProperty(nsIFrame::InitialOverflowProperty());
9799 }
9800 #ifdef DEBUG
9801 SetProperty(nsIFrame::DebugInitialOverflowPropertyApplied(), true);
9802 #endif
9803 } else {
9804 #ifdef DEBUG
9805 RemoveProperty(nsIFrame::DebugInitialOverflowPropertyApplied());
9806 #endif
9807 }
9808
9809 nsSize oldSize = mRect.Size();
9810 bool sizeChanged = ((aOldSize ? *aOldSize : oldSize) != aNewSize);
9811
9812 // Our frame size may not have been computed and set yet, but code under
9813 // functions such as ComputeEffectsRect (which we're about to call) use the
9814 // values that are stored in our frame rect to compute their results. We
9815 // need the results from those functions to be based on the frame size that
9816 // we *will* have, so we temporarily set our frame size here before calling
9817 // those functions.
9818 //
9819 // XXX Someone should document here why we revert the frame size before we
9820 // return rather than just leaving it set.
9821 //
9822 // We pass false here to avoid invalidating display items for this temporary
9823 // change. We sometimes reflow frames multiple times, with the final size
9824 // being the same as the initial. The single call to SetSize after reflow is
9825 // done will take care of invalidating display items if the size has actually
9826 // changed.
9827 SetSize(aNewSize, false);
9828
9829 const auto overflowClipAxes = ShouldApplyOverflowClipping(disp);
9830
9831 if (ChildrenHavePerspective(disp) && sizeChanged) {
9832 RecomputePerspectiveChildrenOverflow(this);
9833
9834 if (overflowClipAxes != PhysicalAxes::Both) {
9835 aOverflowAreas.SetAllTo(bounds);
9836 DebugOnly<bool> ok = ComputeCustomOverflow(aOverflowAreas);
9837
9838 // ComputeCustomOverflow() should not return false, when
9839 // FrameMaintainsOverflow() returns true.
9840 MOZ_ASSERT(ok, "FrameMaintainsOverflow() != ComputeCustomOverflow()");
9841
9842 UnionChildOverflow(aOverflowAreas);
9843 }
9844 }
9845
9846 // This is now called FinishAndStoreOverflow() instead of
9847 // StoreOverflow() because frame-generic ways of adding overflow
9848 // can happen here, e.g. CSS2 outline and native theme.
9849 // If the overflow area width or height is nscoord_MAX, then a
9850 // saturating union may have encounted an overflow, so the overflow may not
9851 // contain the frame border-box. Don't warn in that case.
9852 // Don't warn for SVG either, since SVG doesn't need the overflow area
9853 // to contain the frame bounds.
9854 for (const auto otype : AllOverflowTypes()) {
9855 DebugOnly<nsRect*> r = &aOverflowAreas.Overflow(otype);
9856 NS_ASSERTION(aNewSize.width == 0 || aNewSize.height == 0 ||
9857 r->width == nscoord_MAX || r->height == nscoord_MAX ||
9858 (mState & NS_FRAME_SVG_LAYOUT) ||
9859 r->Contains(nsRect(nsPoint(0, 0), aNewSize)),
9860 "Computed overflow area must contain frame bounds");
9861 }
9862
9863 // If we clip our children, clear accumulated overflow area in the affected
9864 // dimension(s). The children are actually clipped to the padding-box, but
9865 // since the overflow area should include the entire border-box, just set it
9866 // to the border-box size here.
9867 if (overflowClipAxes != PhysicalAxes::None) {
9868 nsRect& ink = aOverflowAreas.InkOverflow();
9869 nsRect& scrollable = aOverflowAreas.ScrollableOverflow();
9870 if (overflowClipAxes & PhysicalAxes::Vertical) {
9871 ink.y = bounds.y;
9872 scrollable.y = bounds.y;
9873 ink.height = bounds.height;
9874 scrollable.height = bounds.height;
9875 }
9876 if (overflowClipAxes & PhysicalAxes::Horizontal) {
9877 ink.x = bounds.x;
9878 scrollable.x = bounds.x;
9879 ink.width = bounds.width;
9880 scrollable.width = bounds.width;
9881 }
9882 }
9883
9884 // Overflow area must always include the frame's top-left and bottom-right,
9885 // even if the frame rect is empty (so we can scroll to those positions).
9886 // Pending a real fix for bug 426879, don't do this for inline frames
9887 // with zero width.
9888 // Do not do this for SVG either, since it will usually massively increase
9889 // the area unnecessarily.
9890 if ((aNewSize.width != 0 || !IsInlineFrame()) &&
9891 !HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
9892 for (const auto otype : AllOverflowTypes()) {
9893 nsRect& o = aOverflowAreas.Overflow(otype);
9894 o = o.UnionEdges(bounds);
9895 }
9896 }
9897
9898 // Note that StyleOverflow::Clip doesn't clip the frame
9899 // background, so we add theme background overflow here so it's not clipped.
9900 if (!::IsXULBoxWrapped(this) && IsThemed(disp)) {
9901 nsRect r(bounds);
9902 nsPresContext* presContext = PresContext();
9903 if (presContext->Theme()->GetWidgetOverflow(
9904 presContext->DeviceContext(), this, disp->EffectiveAppearance(),
9905 &r)) {
9906 nsRect& vo = aOverflowAreas.InkOverflow();
9907 vo = vo.UnionEdges(r);
9908 }
9909 }
9910
9911 ComputeAndIncludeOutlineArea(this, aOverflowAreas, aNewSize);
9912
9913 // Nothing in here should affect scrollable overflow.
9914 aOverflowAreas.InkOverflow() =
9915 ComputeEffectsRect(this, aOverflowAreas.InkOverflow(), aNewSize);
9916
9917 // Absolute position clipping
9918 const nsStyleEffects* effects = StyleEffects();
9919 Maybe<nsRect> clipPropClipRect = GetClipPropClipRect(disp, effects, aNewSize);
9920 if (clipPropClipRect) {
9921 for (const auto otype : AllOverflowTypes()) {
9922 nsRect& o = aOverflowAreas.Overflow(otype);
9923 o.IntersectRect(o, *clipPropClipRect);
9924 }
9925 }
9926
9927 /* If we're transformed, transform the overflow rect by the current
9928 * transformation. */
9929 if (hasTransform) {
9930 SetProperty(nsIFrame::PreTransformOverflowAreasProperty(),
9931 new OverflowAreas(aOverflowAreas));
9932
9933 if (Combines3DTransformWithAncestors()) {
9934 /* If we're a preserve-3d leaf frame, then our pre-transform overflow
9935 * should be correct. Our post-transform overflow is empty though, because
9936 * we only contribute to the overflow area of the preserve-3d root frame.
9937 * If we're an intermediate frame then the pre-transform overflow should
9938 * contain all our non-preserve-3d children, which is what we want. Again
9939 * we have no post-transform overflow.
9940 */
9941 aOverflowAreas.SetAllTo(nsRect());
9942 } else {
9943 TransformReferenceBox refBox(this);
9944 for (const auto otype : AllOverflowTypes()) {
9945 nsRect& o = aOverflowAreas.Overflow(otype);
9946 o = nsDisplayTransform::TransformRect(o, this, refBox);
9947 }
9948
9949 /* If we're the root of the 3d context, then we want to include the
9950 * overflow areas of all the participants. This won't have happened yet as
9951 * the code above set their overflow area to empty. Manually collect these
9952 * overflow areas now.
9953 */
9954 if (Extend3DContext(disp, effects)) {
9955 ComputePreserve3DChildrenOverflow(aOverflowAreas);
9956 }
9957 }
9958 } else {
9959 RemoveProperty(nsIFrame::PreTransformOverflowAreasProperty());
9960 }
9961
9962 /* Revert the size change in case some caller is depending on this. */
9963 SetSize(oldSize, false);
9964
9965 bool anyOverflowChanged;
9966 if (aOverflowAreas != OverflowAreas(bounds, bounds)) {
9967 anyOverflowChanged = SetOverflowAreas(aOverflowAreas);
9968 } else {
9969 anyOverflowChanged = ClearOverflowRects();
9970 }
9971
9972 if (anyOverflowChanged) {
9973 SVGObserverUtils::InvalidateDirectRenderingObservers(this);
9974 if (IsBlockFrameOrSubclass() &&
9975 TextOverflow::CanHaveOverflowMarkers(this)) {
9976 DiscardDisplayItems(this, [](nsDisplayItem* aItem) {
9977 return aItem->GetType() == DisplayItemType::TYPE_TEXT_OVERFLOW;
9978 });
9979 SchedulePaint(PAINT_DEFAULT);
9980 }
9981 }
9982 return anyOverflowChanged;
9983 }
9984
RecomputePerspectiveChildrenOverflow(const nsIFrame * aStartFrame)9985 void nsIFrame::RecomputePerspectiveChildrenOverflow(
9986 const nsIFrame* aStartFrame) {
9987 for (const auto& childList : ChildLists()) {
9988 for (nsIFrame* child : childList.mList) {
9989 if (!child->FrameMaintainsOverflow()) {
9990 continue; // frame does not maintain overflow rects
9991 }
9992 if (child->HasPerspective()) {
9993 OverflowAreas* overflow =
9994 child->GetProperty(nsIFrame::InitialOverflowProperty());
9995 nsRect bounds(nsPoint(0, 0), child->GetSize());
9996 if (overflow) {
9997 OverflowAreas overflowCopy = *overflow;
9998 child->FinishAndStoreOverflow(overflowCopy, bounds.Size());
9999 } else {
10000 OverflowAreas boundsOverflow;
10001 boundsOverflow.SetAllTo(bounds);
10002 child->FinishAndStoreOverflow(boundsOverflow, bounds.Size());
10003 }
10004 } else if (child->GetContent() == aStartFrame->GetContent() ||
10005 child->GetClosestFlattenedTreeAncestorPrimaryFrame() ==
10006 aStartFrame) {
10007 // If a frame is using perspective, then the size used to compute
10008 // perspective-origin is the size of the frame belonging to its parent
10009 // style. We must find any descendant frames using our size
10010 // (by recursing into frames that have the same containing block)
10011 // to update their overflow rects too.
10012 child->RecomputePerspectiveChildrenOverflow(aStartFrame);
10013 }
10014 }
10015 }
10016 }
10017
ComputePreserve3DChildrenOverflow(OverflowAreas & aOverflowAreas)10018 void nsIFrame::ComputePreserve3DChildrenOverflow(
10019 OverflowAreas& aOverflowAreas) {
10020 // Find all descendants that participate in the 3d context, and include their
10021 // overflow. These descendants have an empty overflow, so won't have been
10022 // included in the normal overflow calculation. Any children that don't
10023 // participate have normal overflow, so will have been included already.
10024
10025 nsRect childVisual;
10026 nsRect childScrollable;
10027 for (const auto& childList : ChildLists()) {
10028 for (nsIFrame* child : childList.mList) {
10029 // If this child participates in the 3d context, then take the
10030 // pre-transform region (which contains all descendants that aren't
10031 // participating in the 3d context) and transform it into the 3d context
10032 // root coordinate space.
10033 if (child->Combines3DTransformWithAncestors()) {
10034 OverflowAreas childOverflow = child->GetOverflowAreasRelativeToSelf();
10035 TransformReferenceBox refBox(child);
10036 for (const auto otype : AllOverflowTypes()) {
10037 nsRect& o = childOverflow.Overflow(otype);
10038 o = nsDisplayTransform::TransformRect(o, child, refBox);
10039 }
10040
10041 aOverflowAreas.UnionWith(childOverflow);
10042
10043 // If this child also extends the 3d context, then recurse into it
10044 // looking for more participants.
10045 if (child->Extend3DContext()) {
10046 child->ComputePreserve3DChildrenOverflow(aOverflowAreas);
10047 }
10048 }
10049 }
10050 }
10051 }
10052
ZIndexApplies() const10053 bool nsIFrame::ZIndexApplies() const {
10054 return StyleDisplay()->IsPositionedStyle() || IsFlexOrGridItem();
10055 }
10056
ZIndex() const10057 Maybe<int32_t> nsIFrame::ZIndex() const {
10058 if (!ZIndexApplies()) {
10059 return Nothing();
10060 }
10061 const auto& zIndex = StylePosition()->mZIndex;
10062 if (zIndex.IsAuto()) {
10063 return Nothing();
10064 }
10065 return Some(zIndex.AsInteger());
10066 }
10067
IsScrollAnchor(ScrollAnchorContainer ** aOutContainer)10068 bool nsIFrame::IsScrollAnchor(ScrollAnchorContainer** aOutContainer) {
10069 if (!mInScrollAnchorChain) {
10070 return false;
10071 }
10072
10073 nsIFrame* f = this;
10074
10075 // FIXME(emilio, bug 1629280): We should find a non-null anchor if we have the
10076 // flag set, but bug 1629280 makes it so that we cannot really assert it /
10077 // make this just a `while (true)`, and uncomment the below assertion.
10078 while (auto* container = ScrollAnchorContainer::FindFor(f)) {
10079 // MOZ_ASSERT(f->IsInScrollAnchorChain());
10080 if (nsIFrame* anchor = container->AnchorNode()) {
10081 if (anchor != this) {
10082 return false;
10083 }
10084 if (aOutContainer) {
10085 *aOutContainer = container;
10086 }
10087 return true;
10088 }
10089
10090 f = container->Frame();
10091 }
10092
10093 return false;
10094 }
10095
IsInScrollAnchorChain() const10096 bool nsIFrame::IsInScrollAnchorChain() const { return mInScrollAnchorChain; }
10097
SetInScrollAnchorChain(bool aInChain)10098 void nsIFrame::SetInScrollAnchorChain(bool aInChain) {
10099 mInScrollAnchorChain = aInChain;
10100 }
10101
GetDepthInFrameTree() const10102 uint32_t nsIFrame::GetDepthInFrameTree() const {
10103 uint32_t result = 0;
10104 for (nsContainerFrame* ancestor = GetParent(); ancestor;
10105 ancestor = ancestor->GetParent()) {
10106 result++;
10107 }
10108 return result;
10109 }
10110
10111 /**
10112 * This function takes a frame that is part of a block-in-inline split,
10113 * and _if_ that frame is an anonymous block created by an ib split it
10114 * returns the block's preceding inline. This is needed because the
10115 * split inline's style is the parent of the anonymous block's style.
10116 *
10117 * If aFrame is not an anonymous block, null is returned.
10118 */
GetIBSplitSiblingForAnonymousBlock(const nsIFrame * aFrame)10119 static nsIFrame* GetIBSplitSiblingForAnonymousBlock(const nsIFrame* aFrame) {
10120 MOZ_ASSERT(aFrame, "Must have a non-null frame!");
10121 NS_ASSERTION(aFrame->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT),
10122 "GetIBSplitSibling should only be called on ib-split frames");
10123
10124 if (aFrame->Style()->GetPseudoType() !=
10125 PseudoStyleType::mozBlockInsideInlineWrapper) {
10126 // it's not an anonymous block
10127 return nullptr;
10128 }
10129
10130 // Find the first continuation of the frame. (Ugh. This ends up
10131 // being O(N^2) when it is called O(N) times.)
10132 aFrame = aFrame->FirstContinuation();
10133
10134 /*
10135 * Now look up the nsGkAtoms::IBSplitPrevSibling
10136 * property.
10137 */
10138 nsIFrame* ibSplitSibling =
10139 aFrame->GetProperty(nsIFrame::IBSplitPrevSibling());
10140 NS_ASSERTION(ibSplitSibling, "Broken frame tree?");
10141 return ibSplitSibling;
10142 }
10143
10144 /**
10145 * Get the parent, corrected for the mangled frame tree resulting from
10146 * having a block within an inline. The result only differs from the
10147 * result of |GetParent| when |GetParent| returns an anonymous block
10148 * that was created for an element that was 'display: inline' because
10149 * that element contained a block.
10150 *
10151 * Also skip anonymous scrolled-content parents; inherit directly from the
10152 * outer scroll frame.
10153 *
10154 * Also skip NAC parents if the child frame is NAC.
10155 */
GetCorrectedParent(const nsIFrame * aFrame)10156 static nsIFrame* GetCorrectedParent(const nsIFrame* aFrame) {
10157 nsIFrame* parent = aFrame->GetParent();
10158 if (!parent) {
10159 return nullptr;
10160 }
10161
10162 // For a table caption we want the _inner_ table frame (unless it's anonymous)
10163 // as the style parent.
10164 if (aFrame->IsTableCaption()) {
10165 nsIFrame* innerTable = parent->PrincipalChildList().FirstChild();
10166 if (!innerTable->Style()->IsAnonBox()) {
10167 return innerTable;
10168 }
10169 }
10170
10171 // Table wrappers are always anon boxes; if we're in here for an outer
10172 // table, that actually means its the _inner_ table that wants to
10173 // know its parent. So get the pseudo of the inner in that case.
10174 auto pseudo = aFrame->Style()->GetPseudoType();
10175 if (pseudo == PseudoStyleType::tableWrapper) {
10176 pseudo =
10177 aFrame->PrincipalChildList().FirstChild()->Style()->GetPseudoType();
10178 }
10179
10180 // Prevent a NAC pseudo-element from inheriting from its NAC parent, and
10181 // inherit from the NAC generator element instead.
10182 if (pseudo != PseudoStyleType::NotPseudo) {
10183 MOZ_ASSERT(aFrame->GetContent());
10184 Element* element = Element::FromNode(aFrame->GetContent());
10185 // Make sure to avoid doing the fixup for non-element-backed pseudos like
10186 // ::first-line and such.
10187 if (element && !element->IsRootOfNativeAnonymousSubtree() &&
10188 element->GetPseudoElementType() == aFrame->Style()->GetPseudoType()) {
10189 while (parent->GetContent() &&
10190 !parent->GetContent()->IsRootOfNativeAnonymousSubtree()) {
10191 parent = parent->GetInFlowParent();
10192 }
10193 parent = parent->GetInFlowParent();
10194 }
10195 }
10196
10197 return nsIFrame::CorrectStyleParentFrame(parent, pseudo);
10198 }
10199
10200 /* static */
CorrectStyleParentFrame(nsIFrame * aProspectiveParent,PseudoStyleType aChildPseudo)10201 nsIFrame* nsIFrame::CorrectStyleParentFrame(nsIFrame* aProspectiveParent,
10202 PseudoStyleType aChildPseudo) {
10203 MOZ_ASSERT(aProspectiveParent, "Must have a prospective parent");
10204
10205 if (aChildPseudo != PseudoStyleType::NotPseudo) {
10206 // Non-inheriting anon boxes have no style parent frame at all.
10207 if (PseudoStyle::IsNonInheritingAnonBox(aChildPseudo)) {
10208 return nullptr;
10209 }
10210
10211 // Other anon boxes are parented to their actual parent already, except
10212 // for non-elements. Those should not be treated as an anon box.
10213 if (PseudoStyle::IsAnonBox(aChildPseudo) &&
10214 !nsCSSAnonBoxes::IsNonElement(aChildPseudo)) {
10215 NS_ASSERTION(aChildPseudo != PseudoStyleType::mozBlockInsideInlineWrapper,
10216 "Should have dealt with kids that have "
10217 "NS_FRAME_PART_OF_IBSPLIT elsewhere");
10218 return aProspectiveParent;
10219 }
10220 }
10221
10222 // Otherwise, walk up out of all anon boxes. For placeholder frames, walk out
10223 // of all pseudo-elements as well. Otherwise ReparentComputedStyle could
10224 // cause style data to be out of sync with the frame tree.
10225 nsIFrame* parent = aProspectiveParent;
10226 do {
10227 if (parent->HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
10228 nsIFrame* sibling = GetIBSplitSiblingForAnonymousBlock(parent);
10229
10230 if (sibling) {
10231 // |parent| was a block in an {ib} split; use the inline as
10232 // |the style parent.
10233 parent = sibling;
10234 }
10235 }
10236
10237 if (!parent->Style()->IsPseudoOrAnonBox()) {
10238 return parent;
10239 }
10240
10241 if (!parent->Style()->IsAnonBox() && aChildPseudo != PseudoStyleType::MAX) {
10242 // nsPlaceholderFrame passes in PseudoStyleType::MAX for
10243 // aChildPseudo (even though that's not a valid pseudo-type) just to
10244 // trigger this behavior of walking up to the nearest non-pseudo
10245 // ancestor.
10246 return parent;
10247 }
10248
10249 parent = parent->GetInFlowParent();
10250 } while (parent);
10251
10252 if (aProspectiveParent->Style()->GetPseudoType() ==
10253 PseudoStyleType::viewportScroll) {
10254 // aProspectiveParent is the scrollframe for a viewport
10255 // and the kids are the anonymous scrollbars
10256 return aProspectiveParent;
10257 }
10258
10259 // We can get here if the root element is absolutely positioned.
10260 // We can't test for this very accurately, but it can only happen
10261 // when the prospective parent is a canvas frame.
10262 NS_ASSERTION(aProspectiveParent->IsCanvasFrame(),
10263 "Should have found a parent before this");
10264 return nullptr;
10265 }
10266
DoGetParentComputedStyle(nsIFrame ** aProviderFrame) const10267 ComputedStyle* nsIFrame::DoGetParentComputedStyle(
10268 nsIFrame** aProviderFrame) const {
10269 *aProviderFrame = nullptr;
10270
10271 // Handle display:contents and the root frame, when there's no parent frame
10272 // to inherit from.
10273 if (MOZ_LIKELY(mContent)) {
10274 Element* parentElement = mContent->GetFlattenedTreeParentElement();
10275 if (MOZ_LIKELY(parentElement)) {
10276 auto pseudo = Style()->GetPseudoType();
10277 if (pseudo == PseudoStyleType::NotPseudo || !mContent->IsElement() ||
10278 (!PseudoStyle::IsAnonBox(pseudo) &&
10279 // Ensure that we don't return the display:contents style
10280 // of the parent content for pseudos that have the same content
10281 // as their primary frame (like -moz-list-bullets do):
10282 IsPrimaryFrame()) ||
10283 /* if next is true then it's really a request for the table frame's
10284 parent context, see nsTable[Outer]Frame::GetParentComputedStyle. */
10285 pseudo == PseudoStyleType::tableWrapper) {
10286 // In some edge cases involving display: contents, we may end up here
10287 // for something that's pending to be reframed. In this case we return
10288 // the wrong style from here (because we've already lost track of it!),
10289 // but it's not a big deal as we're going to be reframed anyway.
10290 if (MOZ_LIKELY(parentElement->HasServoData()) &&
10291 Servo_Element_IsDisplayContents(parentElement)) {
10292 RefPtr<ComputedStyle> style =
10293 ServoStyleSet::ResolveServoStyle(*parentElement);
10294 // NOTE(emilio): we return a weak reference because the element also
10295 // holds the style context alive. This is a bit silly (we could've
10296 // returned a weak ref directly), but it's probably not worth
10297 // optimizing, given this function has just one caller which is rare,
10298 // and this path is rare itself.
10299 return style;
10300 }
10301 }
10302 } else {
10303 if (Style()->GetPseudoType() == PseudoStyleType::NotPseudo) {
10304 // We're a frame for the root. We have no style parent.
10305 return nullptr;
10306 }
10307 }
10308 }
10309
10310 if (!(mState & NS_FRAME_OUT_OF_FLOW)) {
10311 /*
10312 * If this frame is an anonymous block created when an inline with a block
10313 * inside it got split, then the parent style is on its preceding inline. We
10314 * can get to it using GetIBSplitSiblingForAnonymousBlock.
10315 */
10316 if (mState & NS_FRAME_PART_OF_IBSPLIT) {
10317 nsIFrame* ibSplitSibling = GetIBSplitSiblingForAnonymousBlock(this);
10318 if (ibSplitSibling) {
10319 return (*aProviderFrame = ibSplitSibling)->Style();
10320 }
10321 }
10322
10323 // If this frame is one of the blocks that split an inline, we must
10324 // return the "special" inline parent, i.e., the parent that this
10325 // frame would have if we didn't mangle the frame structure.
10326 *aProviderFrame = GetCorrectedParent(this);
10327 return *aProviderFrame ? (*aProviderFrame)->Style() : nullptr;
10328 }
10329
10330 // We're an out-of-flow frame. For out-of-flow frames, we must
10331 // resolve underneath the placeholder's parent. The placeholder is
10332 // reached from the first-in-flow.
10333 nsPlaceholderFrame* placeholder = FirstInFlow()->GetPlaceholderFrame();
10334 if (!placeholder) {
10335 MOZ_ASSERT_UNREACHABLE("no placeholder frame for out-of-flow frame");
10336 *aProviderFrame = GetCorrectedParent(this);
10337 return *aProviderFrame ? (*aProviderFrame)->Style() : nullptr;
10338 }
10339 return placeholder->GetParentComputedStyleForOutOfFlow(aProviderFrame);
10340 }
10341
GetLastLeaf(nsIFrame ** aFrame)10342 void nsIFrame::GetLastLeaf(nsIFrame** aFrame) {
10343 if (!aFrame || !*aFrame) return;
10344 nsIFrame* child = *aFrame;
10345 // if we are a block frame then go for the last line of 'this'
10346 while (1) {
10347 child = child->PrincipalChildList().FirstChild();
10348 if (!child) return; // nothing to do
10349 nsIFrame* siblingFrame;
10350 nsIContent* content;
10351 // ignore anonymous elements, e.g. mozTableAdd* mozTableRemove*
10352 // see bug 278197 comment #12 #13 for details
10353 while ((siblingFrame = child->GetNextSibling()) &&
10354 (content = siblingFrame->GetContent()) &&
10355 !content->IsRootOfNativeAnonymousSubtree())
10356 child = siblingFrame;
10357 *aFrame = child;
10358 }
10359 }
10360
GetFirstLeaf(nsIFrame ** aFrame)10361 void nsIFrame::GetFirstLeaf(nsIFrame** aFrame) {
10362 if (!aFrame || !*aFrame) return;
10363 nsIFrame* child = *aFrame;
10364 while (1) {
10365 child = child->PrincipalChildList().FirstChild();
10366 if (!child) return; // nothing to do
10367 *aFrame = child;
10368 }
10369 }
10370
IsFocusableDueToScrollFrame()10371 bool nsIFrame::IsFocusableDueToScrollFrame() {
10372 if (!IsScrollFrame()) {
10373 if (nsFieldSetFrame* fieldset = do_QueryFrame(this)) {
10374 // TODO: Do we have similar special-cases like this where we can have
10375 // anonymous scrollable boxes hanging off a primary frame?
10376 if (nsIFrame* inner = fieldset->GetInner()) {
10377 return inner->IsFocusableDueToScrollFrame();
10378 }
10379 }
10380 return false;
10381 }
10382 if (!mContent->IsHTMLElement()) {
10383 return false;
10384 }
10385 if (mContent->IsRootOfNativeAnonymousSubtree()) {
10386 return false;
10387 }
10388 if (!mContent->GetParent()) {
10389 return false;
10390 }
10391 if (mContent->AsElement()->HasAttr(nsGkAtoms::tabindex)) {
10392 return false;
10393 }
10394 // Elements with scrollable view are focusable with script & tabbable
10395 // Otherwise you couldn't scroll them with keyboard, which is an accessibility
10396 // issue (e.g. Section 508 rules) However, we don't make them to be focusable
10397 // with the mouse, because the extra focus outlines are considered
10398 // unnecessarily ugly. When clicked on, the selection position within the
10399 // element will be enough to make them keyboard scrollable.
10400 nsIScrollableFrame* scrollFrame = do_QueryFrame(this);
10401 if (!scrollFrame) {
10402 return false;
10403 }
10404 if (scrollFrame->IsForTextControlWithNoScrollbars()) {
10405 return false;
10406 }
10407 if (scrollFrame->GetScrollStyles().IsHiddenInBothDirections()) {
10408 return false;
10409 }
10410 if (scrollFrame->GetScrollRange().IsEqualEdges(nsRect(0, 0, 0, 0))) {
10411 return false;
10412 }
10413 return true;
10414 }
10415
IsFocusable(bool aWithMouse)10416 nsIFrame::Focusable nsIFrame::IsFocusable(bool aWithMouse) {
10417 // cannot focus content in print preview mode. Only the root can be focused,
10418 // but that's handled elsewhere.
10419 if (PresContext()->Type() == nsPresContext::eContext_PrintPreview) {
10420 return {};
10421 }
10422
10423 if (!mContent || !mContent->IsElement()) {
10424 return {};
10425 }
10426
10427 if (!IsVisibleConsideringAncestors()) {
10428 return {};
10429 }
10430
10431 const nsStyleUI& ui = *StyleUI();
10432 if (ui.IsInert()) {
10433 return {};
10434 }
10435
10436 PseudoStyleType pseudo = Style()->GetPseudoType();
10437 if (pseudo == PseudoStyleType::anonymousFlexItem ||
10438 pseudo == PseudoStyleType::anonymousGridItem) {
10439 return {};
10440 }
10441
10442 int32_t tabIndex = -1;
10443 if (ui.UserFocus() != StyleUserFocus::Ignore &&
10444 ui.UserFocus() != StyleUserFocus::None) {
10445 // Pass in default tabindex of -1 for nonfocusable and 0 for focusable
10446 tabIndex = 0;
10447 }
10448
10449 if (mContent->IsFocusable(&tabIndex, aWithMouse)) {
10450 // If the content is focusable, then we're done.
10451 return {true, tabIndex};
10452 }
10453
10454 // If we're focusing with the mouse we never focus scroll areas.
10455 if (!aWithMouse && IsFocusableDueToScrollFrame()) {
10456 return {true, 0};
10457 }
10458
10459 return {false, tabIndex};
10460 }
10461
10462 /**
10463 * @return true if this text frame ends with a newline character which is
10464 * treated as preformatted. It should return false if this is not a text frame.
10465 */
HasSignificantTerminalNewline() const10466 bool nsIFrame::HasSignificantTerminalNewline() const { return false; }
10467
ConvertSVGDominantBaselineToVerticalAlign(StyleDominantBaseline aDominantBaseline)10468 static StyleVerticalAlignKeyword ConvertSVGDominantBaselineToVerticalAlign(
10469 StyleDominantBaseline aDominantBaseline) {
10470 // Most of these are approximate mappings.
10471 switch (aDominantBaseline) {
10472 case StyleDominantBaseline::Hanging:
10473 case StyleDominantBaseline::TextBeforeEdge:
10474 return StyleVerticalAlignKeyword::TextTop;
10475 case StyleDominantBaseline::TextAfterEdge:
10476 case StyleDominantBaseline::Ideographic:
10477 return StyleVerticalAlignKeyword::TextBottom;
10478 case StyleDominantBaseline::Central:
10479 case StyleDominantBaseline::Middle:
10480 case StyleDominantBaseline::Mathematical:
10481 return StyleVerticalAlignKeyword::Middle;
10482 case StyleDominantBaseline::Auto:
10483 case StyleDominantBaseline::Alphabetic:
10484 return StyleVerticalAlignKeyword::Baseline;
10485 default:
10486 MOZ_ASSERT_UNREACHABLE("unexpected aDominantBaseline value");
10487 return StyleVerticalAlignKeyword::Baseline;
10488 }
10489 }
10490
VerticalAlignEnum() const10491 Maybe<StyleVerticalAlignKeyword> nsIFrame::VerticalAlignEnum() const {
10492 if (SVGUtils::IsInSVGTextSubtree(this)) {
10493 StyleDominantBaseline dominantBaseline = StyleSVG()->mDominantBaseline;
10494 return Some(ConvertSVGDominantBaselineToVerticalAlign(dominantBaseline));
10495 }
10496
10497 const auto& verticalAlign = StyleDisplay()->mVerticalAlign;
10498 if (verticalAlign.IsKeyword()) {
10499 return Some(verticalAlign.AsKeyword());
10500 }
10501
10502 return Nothing();
10503 }
10504
10505 NS_IMETHODIMP
RefreshSizeCache(nsBoxLayoutState & aState)10506 nsIFrame::RefreshSizeCache(nsBoxLayoutState& aState) {
10507 // XXXbz this comment needs some rewriting to make sense in the
10508 // post-reflow-branch world.
10509
10510 // Ok we need to compute our minimum, preferred, and maximum sizes.
10511 // 1) Maximum size. This is easy. Its infinite unless it is overloaded by CSS.
10512 // 2) Preferred size. This is a little harder. This is the size the
10513 // block would be if it were laid out on an infinite canvas. So we can
10514 // get this by reflowing the block with and INTRINSIC width and height. We
10515 // can also do a nice optimization for incremental reflow. If the reflow is
10516 // incremental then we can pass a flag to have the block compute the
10517 // preferred width for us! Preferred height can just be the minimum height;
10518 // 3) Minimum size. This is a toughy. We can pass the block a flag asking for
10519 // the max element size. That would give us the width. Unfortunately you
10520 // can only ask for a maxElementSize during an incremental reflow. So on
10521 // other reflows we will just have to use 0. The min height on the other
10522 // hand is fairly easy we need to get the largest line height. This can be
10523 // done with the line iterator.
10524
10525 // if we do have a rendering context
10526 gfxContext* rendContext = aState.GetRenderingContext();
10527 if (rendContext) {
10528 nsPresContext* presContext = aState.PresContext();
10529
10530 // If we don't have any HTML constraints and it's a resize, then nothing in
10531 // the block could have changed, so no refresh is necessary.
10532 nsBoxLayoutMetrics* metrics = BoxMetrics();
10533 if (!XULNeedsRecalc(metrics->mBlockPrefSize)) {
10534 return NS_OK;
10535 }
10536
10537 // the rect we plan to size to.
10538 nsRect rect = GetRect();
10539
10540 nsMargin bp(0, 0, 0, 0);
10541 GetXULBorderAndPadding(bp);
10542
10543 {
10544 // If we're a container for font size inflation, then shrink
10545 // wrapping inside of us should not apply font size inflation.
10546 AutoMaybeDisableFontInflation an(this);
10547
10548 metrics->mBlockPrefSize.width =
10549 GetPrefISize(rendContext) + bp.LeftRight();
10550 metrics->mBlockMinSize.width = GetMinISize(rendContext) + bp.LeftRight();
10551 }
10552
10553 // do the nasty.
10554 const WritingMode wm = aState.OuterReflowInput()
10555 ? aState.OuterReflowInput()->GetWritingMode()
10556 : GetWritingMode();
10557 ReflowOutput desiredSize(wm);
10558 BoxReflow(aState, presContext, desiredSize, rendContext, rect.x, rect.y,
10559 metrics->mBlockPrefSize.width, NS_UNCONSTRAINEDSIZE);
10560
10561 metrics->mBlockMinSize.height = 0;
10562 // ok we need the max ascent of the items on the line. So to do this
10563 // ask the block for its line iterator. Get the max ascent.
10564 nsAutoLineIterator lines = GetLineIterator();
10565 if (lines) {
10566 metrics->mBlockMinSize.height = 0;
10567 int32_t lineCount = lines->GetNumLines();
10568 for (int32_t i = 0; i < lineCount; ++i) {
10569 auto line = lines->GetLine(i).unwrap();
10570
10571 if (line.mLineBounds.height > metrics->mBlockMinSize.height) {
10572 metrics->mBlockMinSize.height = line.mLineBounds.height;
10573 }
10574 }
10575 } else {
10576 metrics->mBlockMinSize.height = desiredSize.Height();
10577 }
10578
10579 metrics->mBlockPrefSize.height = metrics->mBlockMinSize.height;
10580
10581 if (desiredSize.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE) {
10582 if (!nsLayoutUtils::GetFirstLineBaseline(wm, this,
10583 &metrics->mBlockAscent))
10584 metrics->mBlockAscent = GetLogicalBaseline(wm);
10585 } else {
10586 metrics->mBlockAscent = desiredSize.BlockStartAscent();
10587 }
10588
10589 #ifdef DEBUG_adaptor
10590 printf("min=(%d,%d), pref=(%d,%d), ascent=%d\n",
10591 metrics->mBlockMinSize.width, metrics->mBlockMinSize.height,
10592 metrics->mBlockPrefSize.width, metrics->mBlockPrefSize.height,
10593 metrics->mBlockAscent);
10594 #endif
10595 }
10596
10597 return NS_OK;
10598 }
10599
GetXULPrefSize(nsBoxLayoutState & aState)10600 nsSize nsIFrame::GetXULPrefSize(nsBoxLayoutState& aState) {
10601 nsSize size(0, 0);
10602 DISPLAY_PREF_SIZE(this, size);
10603 // If the size is cached, and there are no HTML constraints that we might
10604 // be depending on, then we just return the cached size.
10605 nsBoxLayoutMetrics* metrics = BoxMetrics();
10606 if (!XULNeedsRecalc(metrics->mPrefSize)) {
10607 size = metrics->mPrefSize;
10608 return size;
10609 }
10610
10611 if (IsXULCollapsed()) return size;
10612
10613 // get our size in CSS.
10614 bool widthSet, heightSet;
10615 bool completelyRedefined =
10616 nsIFrame::AddXULPrefSize(this, size, widthSet, heightSet);
10617
10618 // Refresh our caches with new sizes.
10619 if (!completelyRedefined) {
10620 RefreshSizeCache(aState);
10621 nsSize blockSize = metrics->mBlockPrefSize;
10622
10623 // notice we don't need to add our borders or padding
10624 // in. That's because the block did it for us.
10625 if (!widthSet) size.width = blockSize.width;
10626 if (!heightSet) size.height = blockSize.height;
10627 }
10628
10629 metrics->mPrefSize = size;
10630 return size;
10631 }
10632
GetXULMinSize(nsBoxLayoutState & aState)10633 nsSize nsIFrame::GetXULMinSize(nsBoxLayoutState& aState) {
10634 nsSize size(0, 0);
10635 DISPLAY_MIN_SIZE(this, size);
10636 // Don't use the cache if we have HTMLReflowInput constraints --- they might
10637 // have changed
10638 nsBoxLayoutMetrics* metrics = BoxMetrics();
10639 if (!XULNeedsRecalc(metrics->mMinSize)) {
10640 size = metrics->mMinSize;
10641 return size;
10642 }
10643
10644 if (IsXULCollapsed()) return size;
10645
10646 // get our size in CSS.
10647 bool widthSet, heightSet;
10648 bool completelyRedefined =
10649 nsIFrame::AddXULMinSize(this, size, widthSet, heightSet);
10650
10651 // Refresh our caches with new sizes.
10652 if (!completelyRedefined) {
10653 RefreshSizeCache(aState);
10654 nsSize blockSize = metrics->mBlockMinSize;
10655
10656 if (!widthSet) size.width = blockSize.width;
10657 if (!heightSet) size.height = blockSize.height;
10658 }
10659
10660 metrics->mMinSize = size;
10661 return size;
10662 }
10663
GetXULMaxSize(nsBoxLayoutState & aState)10664 nsSize nsIFrame::GetXULMaxSize(nsBoxLayoutState& aState) {
10665 nsSize size(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
10666 DISPLAY_MAX_SIZE(this, size);
10667 // Don't use the cache if we have HTMLReflowInput constraints --- they might
10668 // have changed
10669 nsBoxLayoutMetrics* metrics = BoxMetrics();
10670 if (!XULNeedsRecalc(metrics->mMaxSize)) {
10671 size = metrics->mMaxSize;
10672 return size;
10673 }
10674
10675 if (IsXULCollapsed()) return size;
10676
10677 size = nsIFrame::GetUncachedXULMaxSize(aState);
10678 metrics->mMaxSize = size;
10679
10680 return size;
10681 }
10682
GetXULFlex()10683 nscoord nsIFrame::GetXULFlex() {
10684 nsBoxLayoutMetrics* metrics = BoxMetrics();
10685 if (XULNeedsRecalc(metrics->mFlex)) {
10686 nsIFrame::AddXULFlex(this, metrics->mFlex);
10687 }
10688
10689 return metrics->mFlex;
10690 }
10691
GetXULBoxAscent(nsBoxLayoutState & aState)10692 nscoord nsIFrame::GetXULBoxAscent(nsBoxLayoutState& aState) {
10693 nsBoxLayoutMetrics* metrics = BoxMetrics();
10694 if (!XULNeedsRecalc(metrics->mAscent)) {
10695 return metrics->mAscent;
10696 }
10697
10698 if (IsXULCollapsed()) {
10699 metrics->mAscent = 0;
10700 } else {
10701 // Refresh our caches with new sizes.
10702 RefreshSizeCache(aState);
10703 metrics->mAscent = metrics->mBlockAscent;
10704 }
10705
10706 return metrics->mAscent;
10707 }
10708
DoXULLayout(nsBoxLayoutState & aState)10709 nsresult nsIFrame::DoXULLayout(nsBoxLayoutState& aState) {
10710 nsRect ourRect(mRect);
10711
10712 gfxContext* rendContext = aState.GetRenderingContext();
10713 nsPresContext* presContext = aState.PresContext();
10714 WritingMode ourWM = GetWritingMode();
10715 const WritingMode outerWM = aState.OuterReflowInput()
10716 ? aState.OuterReflowInput()->GetWritingMode()
10717 : ourWM;
10718 ReflowOutput desiredSize(outerWM);
10719 LogicalSize ourSize = GetLogicalSize(outerWM);
10720
10721 if (rendContext) {
10722 BoxReflow(aState, presContext, desiredSize, rendContext, ourRect.x,
10723 ourRect.y, ourRect.width, ourRect.height);
10724
10725 if (IsXULCollapsed()) {
10726 SetSize(nsSize(0, 0));
10727 } else {
10728 // if our child needs to be bigger. This might happend with
10729 // wrapping text. There is no way to predict its height until we
10730 // reflow it. Now that we know the height reshuffle upward.
10731 if (desiredSize.ISize(outerWM) > ourSize.ISize(outerWM) ||
10732 desiredSize.BSize(outerWM) > ourSize.BSize(outerWM)) {
10733 #ifdef DEBUG_GROW
10734 XULDumpBox(stdout);
10735 printf(" GREW from (%d,%d) -> (%d,%d)\n", ourSize.ISize(outerWM),
10736 ourSize.BSize(outerWM), desiredSize.ISize(outerWM),
10737 desiredSize.BSize(outerWM));
10738 #endif
10739
10740 if (desiredSize.ISize(outerWM) > ourSize.ISize(outerWM)) {
10741 ourSize.ISize(outerWM) = desiredSize.ISize(outerWM);
10742 }
10743
10744 if (desiredSize.BSize(outerWM) > ourSize.BSize(outerWM)) {
10745 ourSize.BSize(outerWM) = desiredSize.BSize(outerWM);
10746 }
10747 }
10748
10749 // ensure our size is what we think is should be. Someone could have
10750 // reset the frame to be smaller or something dumb like that.
10751 SetSize(ourSize.ConvertTo(ourWM, outerWM));
10752 }
10753 }
10754
10755 // Should we do this if IsXULCollapsed() is true?
10756 LogicalSize size(GetLogicalSize(outerWM));
10757 desiredSize.ISize(outerWM) = size.ISize(outerWM);
10758 desiredSize.BSize(outerWM) = size.BSize(outerWM);
10759 desiredSize.UnionOverflowAreasWithDesiredBounds();
10760
10761 if (HasAbsolutelyPositionedChildren()) {
10762 // Set up a |reflowInput| to pass into ReflowAbsoluteFrames
10763 ReflowInput reflowInput(aState.PresContext(), this,
10764 aState.GetRenderingContext(),
10765 LogicalSize(ourWM, ISize(), NS_UNCONSTRAINEDSIZE),
10766 ReflowInput::InitFlag::DummyParentReflowInput);
10767
10768 AddStateBits(NS_FRAME_IN_REFLOW);
10769 // Set up a |reflowStatus| to pass into ReflowAbsoluteFrames
10770 // (just a dummy value; hopefully that's OK)
10771 nsReflowStatus reflowStatus;
10772 ReflowAbsoluteFrames(aState.PresContext(), desiredSize, reflowInput,
10773 reflowStatus);
10774 RemoveStateBits(NS_FRAME_IN_REFLOW);
10775 }
10776
10777 nsSize oldSize(ourRect.Size());
10778 FinishAndStoreOverflow(desiredSize.mOverflowAreas,
10779 size.GetPhysicalSize(outerWM), &oldSize);
10780
10781 SyncXULLayout(aState);
10782
10783 return NS_OK;
10784 }
10785
BoxReflow(nsBoxLayoutState & aState,nsPresContext * aPresContext,ReflowOutput & aDesiredSize,gfxContext * aRenderingContext,nscoord aX,nscoord aY,nscoord aWidth,nscoord aHeight,bool aMoveFrame)10786 void nsIFrame::BoxReflow(nsBoxLayoutState& aState, nsPresContext* aPresContext,
10787 ReflowOutput& aDesiredSize,
10788 gfxContext* aRenderingContext, nscoord aX, nscoord aY,
10789 nscoord aWidth, nscoord aHeight, bool aMoveFrame) {
10790 DO_GLOBAL_REFLOW_COUNT("nsBoxToBlockAdaptor");
10791
10792 nsBoxLayoutMetrics* metrics = BoxMetrics();
10793 if (MOZ_UNLIKELY(!metrics)) {
10794 // Can't proceed without BoxMetrics. This should only happen if something
10795 // is seriously broken, e.g. if we try to do XUL layout on a non-XUL frame.
10796 // (If this is a content process, we'll abort even in release builds,
10797 // because XUL layout mixup is extra surprising in content, and aborts are
10798 // less catastrophic in content vs. in chrome.)
10799 MOZ_RELEASE_ASSERT(!XRE_IsContentProcess(),
10800 "Starting XUL BoxReflow w/o BoxMetrics (in content)?");
10801 MOZ_ASSERT_UNREACHABLE("Starting XUL BoxReflow w/o BoxMetrics?");
10802 return;
10803 }
10804
10805 nsReflowStatus status;
10806
10807 bool needsReflow = IsSubtreeDirty();
10808
10809 // if we don't need a reflow then
10810 // lets see if we are already that size. Yes? then don't even reflow. We are
10811 // done.
10812 if (!needsReflow) {
10813 if (aWidth != NS_UNCONSTRAINEDSIZE && aHeight != NS_UNCONSTRAINEDSIZE) {
10814 // if the new calculated size has a 0 width or a 0 height
10815 if ((metrics->mLastSize.width == 0 || metrics->mLastSize.height == 0) &&
10816 (aWidth == 0 || aHeight == 0)) {
10817 needsReflow = false;
10818 aDesiredSize.Width() = aWidth;
10819 aDesiredSize.Height() = aHeight;
10820 SetSize(aDesiredSize.Size(GetWritingMode()));
10821 } else {
10822 aDesiredSize.Width() = metrics->mLastSize.width;
10823 aDesiredSize.Height() = metrics->mLastSize.height;
10824
10825 // remove the margin. The rect of our child does not include it but our
10826 // calculated size does. don't reflow if we are already the right size
10827 if (metrics->mLastSize.width == aWidth &&
10828 metrics->mLastSize.height == aHeight)
10829 needsReflow = false;
10830 else
10831 needsReflow = true;
10832 }
10833 } else {
10834 // if the width or height are intrinsic alway reflow because
10835 // we don't know what it should be.
10836 needsReflow = true;
10837 }
10838 }
10839
10840 // ok now reflow the child into the spacers calculated space
10841 if (needsReflow) {
10842 aDesiredSize.ClearSize();
10843
10844 // create a reflow input to tell our child to flow at the given size.
10845
10846 // Construct a bogus parent reflow input so that there's a usable reflow
10847 // input for the containing block.
10848 nsMargin margin(0, 0, 0, 0);
10849 GetXULMargin(margin);
10850
10851 nsSize parentSize(aWidth, aHeight);
10852 if (parentSize.height != NS_UNCONSTRAINEDSIZE)
10853 parentSize.height += margin.TopBottom();
10854 if (parentSize.width != NS_UNCONSTRAINEDSIZE)
10855 parentSize.width += margin.LeftRight();
10856
10857 nsIFrame* parentFrame = GetParent();
10858 WritingMode parentWM = parentFrame->GetWritingMode();
10859 ReflowInput parentReflowInput(
10860 aPresContext, parentFrame, aRenderingContext,
10861 LogicalSize(parentWM, parentSize),
10862 ReflowInput::InitFlag::DummyParentReflowInput);
10863
10864 // This may not do very much useful, but it's probably worth trying.
10865 if (parentSize.width != NS_UNCONSTRAINEDSIZE)
10866 parentReflowInput.SetComputedWidth(std::max(parentSize.width, 0));
10867 if (parentSize.height != NS_UNCONSTRAINEDSIZE)
10868 parentReflowInput.SetComputedHeight(std::max(parentSize.height, 0));
10869 parentReflowInput.SetComputedLogicalMargin(parentWM,
10870 LogicalMargin(parentWM));
10871 // XXX use box methods
10872 nsMargin padding;
10873 parentFrame->GetXULPadding(padding);
10874 parentReflowInput.SetComputedLogicalPadding(
10875 parentWM, LogicalMargin(parentWM, padding));
10876 nsMargin border;
10877 parentFrame->GetXULBorder(border);
10878 parentReflowInput.SetComputedLogicalBorderPadding(
10879 parentWM, LogicalMargin(parentWM, border + padding));
10880
10881 // Construct the parent chain manually since constructing it normally
10882 // messes up dimensions.
10883 const ReflowInput* outerReflowInput = aState.OuterReflowInput();
10884 NS_ASSERTION(!outerReflowInput || outerReflowInput->mFrame != this,
10885 "in and out of XUL on a single frame?");
10886 const ReflowInput* parentRI;
10887 if (outerReflowInput && outerReflowInput->mFrame == parentFrame) {
10888 // We're a frame (such as a text control frame) that jumps into
10889 // box reflow and then straight out of it on the child frame.
10890 // This means we actually have a real parent reflow input.
10891 // nsLayoutUtils::InflationMinFontSizeFor used to need this to be
10892 // linked up correctly for text control frames, so do so here).
10893 parentRI = outerReflowInput;
10894 } else {
10895 parentRI = &parentReflowInput;
10896 }
10897
10898 // XXX Is it OK that this reflow input has only one ancestor?
10899 // (It used to have a bogus parent, skipping all the boxes).
10900 WritingMode wm = GetWritingMode();
10901 LogicalSize logicalSize(wm, nsSize(aWidth, aHeight));
10902 logicalSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
10903 ReflowInput reflowInput(aPresContext, *parentRI, this, logicalSize,
10904 Nothing(),
10905 ReflowInput::InitFlag::DummyParentReflowInput);
10906
10907 // XXX_jwir3: This is somewhat fishy. If this is actually changing the value
10908 // here (which it might be), then we should make sure that it's
10909 // correct the first time around, rather than changing it later.
10910 reflowInput.mCBReflowInput = parentRI;
10911
10912 reflowInput.mReflowDepth = aState.GetReflowDepth();
10913
10914 // mComputedWidth and mComputedHeight are content-box, not
10915 // border-box
10916 if (aWidth != NS_UNCONSTRAINEDSIZE) {
10917 nscoord computedWidth =
10918 aWidth - reflowInput.ComputedPhysicalBorderPadding().LeftRight();
10919 computedWidth = std::max(computedWidth, 0);
10920 reflowInput.SetComputedWidth(computedWidth);
10921 }
10922
10923 // Most child frames of box frames (e.g. subdocument or scroll frames)
10924 // need to be constrained to the provided size and overflow as necessary.
10925 // The one exception are block frames, because we need to know their
10926 // natural height excluding any overflow area which may be caused by
10927 // various CSS effects such as shadow or outline.
10928 if (!IsBlockFrameOrSubclass()) {
10929 if (aHeight != NS_UNCONSTRAINEDSIZE) {
10930 nscoord computedHeight =
10931 aHeight - reflowInput.ComputedPhysicalBorderPadding().TopBottom();
10932 computedHeight = std::max(computedHeight, 0);
10933 reflowInput.SetComputedHeight(computedHeight);
10934 } else {
10935 reflowInput.SetComputedHeight(
10936 ComputeSize(
10937 aRenderingContext, wm, logicalSize, logicalSize.ISize(wm),
10938 reflowInput.ComputedLogicalMargin(wm).Size(wm),
10939 reflowInput.ComputedLogicalBorderPadding(wm).Size(wm), {}, {})
10940 .mLogicalSize.Height(wm));
10941 }
10942 }
10943
10944 // Box layout calls SetRect before XULLayout, whereas non-box layout
10945 // calls SetRect after Reflow.
10946 // XXX Perhaps we should be doing this by twiddling the rect back to
10947 // mLastSize before calling Reflow and then switching it back, but
10948 // However, mLastSize can also be the size passed to BoxReflow by
10949 // RefreshSizeCache, so that doesn't really make sense.
10950 if (metrics->mLastSize.width != aWidth) {
10951 reflowInput.SetHResize(true);
10952
10953 // When font size inflation is enabled, a horizontal resize
10954 // requires a full reflow. See ReflowInput::InitResizeFlags
10955 // for more details.
10956 if (nsLayoutUtils::FontSizeInflationEnabled(aPresContext)) {
10957 this->MarkSubtreeDirty();
10958 }
10959 }
10960 if (metrics->mLastSize.height != aHeight) {
10961 reflowInput.SetVResize(true);
10962 }
10963
10964 // place the child and reflow
10965
10966 Reflow(aPresContext, aDesiredSize, reflowInput, status);
10967
10968 NS_ASSERTION(status.IsComplete(), "bad status");
10969
10970 ReflowChildFlags layoutFlags = aState.LayoutFlags();
10971 nsContainerFrame::FinishReflowChild(
10972 this, aPresContext, aDesiredSize, &reflowInput, aX, aY,
10973 layoutFlags | ReflowChildFlags::NoMoveFrame);
10974
10975 // Save the ascent. (bug 103925)
10976 if (IsXULCollapsed()) {
10977 metrics->mAscent = 0;
10978 } else {
10979 if (aDesiredSize.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE) {
10980 if (!nsLayoutUtils::GetFirstLineBaseline(wm, this, &metrics->mAscent))
10981 metrics->mAscent = GetLogicalBaseline(wm);
10982 } else
10983 metrics->mAscent = aDesiredSize.BlockStartAscent();
10984 }
10985
10986 } else {
10987 aDesiredSize.SetBlockStartAscent(metrics->mBlockAscent);
10988 }
10989
10990 metrics->mLastSize.width = aDesiredSize.Width();
10991 metrics->mLastSize.height = aDesiredSize.Height();
10992 }
10993
BoxMetrics() const10994 nsBoxLayoutMetrics* nsIFrame::BoxMetrics() const {
10995 nsBoxLayoutMetrics* metrics = GetProperty(BoxMetricsProperty());
10996 NS_ASSERTION(
10997 metrics,
10998 "A box layout method was called but InitBoxMetrics was never called");
10999 return metrics;
11000 }
11001
UpdateStyleOfChildAnonBox(nsIFrame * aChildFrame,ServoRestyleState & aRestyleState)11002 void nsIFrame::UpdateStyleOfChildAnonBox(nsIFrame* aChildFrame,
11003 ServoRestyleState& aRestyleState) {
11004 #ifdef DEBUG
11005 nsIFrame* parent = aChildFrame->GetInFlowParent();
11006 if (aChildFrame->IsTableFrame()) {
11007 parent = parent->GetParent();
11008 }
11009 if (parent->IsLineFrame()) {
11010 parent = parent->GetParent();
11011 }
11012 MOZ_ASSERT(nsLayoutUtils::FirstContinuationOrIBSplitSibling(parent) == this,
11013 "This should only be used for children!");
11014 #endif // DEBUG
11015 MOZ_ASSERT(!GetContent() || !aChildFrame->GetContent() ||
11016 aChildFrame->GetContent() == GetContent(),
11017 "What content node is it a frame for?");
11018 MOZ_ASSERT(!aChildFrame->GetPrevContinuation(),
11019 "Only first continuations should end up here");
11020
11021 // We could force the caller to pass in the pseudo, since some callers know it
11022 // statically... But this API is a bit nicer.
11023 auto pseudo = aChildFrame->Style()->GetPseudoType();
11024 MOZ_ASSERT(PseudoStyle::IsAnonBox(pseudo), "Child is not an anon box?");
11025 MOZ_ASSERT(!PseudoStyle::IsNonInheritingAnonBox(pseudo),
11026 "Why did the caller bother calling us?");
11027
11028 // Anon boxes inherit from their parent; that's us.
11029 RefPtr<ComputedStyle> newContext =
11030 aRestyleState.StyleSet().ResolveInheritingAnonymousBoxStyle(pseudo,
11031 Style());
11032
11033 nsChangeHint childHint =
11034 UpdateStyleOfOwnedChildFrame(aChildFrame, newContext, aRestyleState);
11035
11036 // Now that we've updated the style on aChildFrame, check whether it itself
11037 // has anon boxes to deal with.
11038 ServoRestyleState childrenState(*aChildFrame, aRestyleState, childHint,
11039 ServoRestyleState::Type::InFlow);
11040 aChildFrame->UpdateStyleOfOwnedAnonBoxes(childrenState);
11041
11042 // Assuming anon boxes don't have ::backdrop associated with them... if that
11043 // ever changes, we'd need to handle that here, like we do in
11044 // RestyleManager::ProcessPostTraversal
11045
11046 // We do need to handle block pseudo-elements here, though. Especially list
11047 // bullets.
11048 if (nsBlockFrame* block = do_QueryFrame(aChildFrame)) {
11049 block->UpdatePseudoElementStyles(childrenState);
11050 }
11051 }
11052
11053 /* static */
UpdateStyleOfOwnedChildFrame(nsIFrame * aChildFrame,ComputedStyle * aNewComputedStyle,ServoRestyleState & aRestyleState,const Maybe<ComputedStyle * > & aContinuationComputedStyle)11054 nsChangeHint nsIFrame::UpdateStyleOfOwnedChildFrame(
11055 nsIFrame* aChildFrame, ComputedStyle* aNewComputedStyle,
11056 ServoRestyleState& aRestyleState,
11057 const Maybe<ComputedStyle*>& aContinuationComputedStyle) {
11058 MOZ_ASSERT(!aChildFrame->GetAdditionalComputedStyle(0),
11059 "We don't handle additional styles here");
11060
11061 // Figure out whether we have an actual change. It's important that we do
11062 // this, for several reasons:
11063 //
11064 // 1) Even if all the child's changes are due to properties it inherits from
11065 // us, it's possible that no one ever asked us for those style structs and
11066 // hence changes to them aren't reflected in the changes handled at all.
11067 //
11068 // 2) Content can change stylesheets that change the styles of pseudos, and
11069 // extensions can add/remove stylesheets that change the styles of
11070 // anonymous boxes directly.
11071 uint32_t equalStructs; // Not used, actually.
11072 nsChangeHint childHint = aChildFrame->Style()->CalcStyleDifference(
11073 *aNewComputedStyle, &equalStructs);
11074
11075 // If aChildFrame is out of flow, then aRestyleState's "changes handled by the
11076 // parent" doesn't apply to it, because it may have some other parent in the
11077 // frame tree.
11078 if (!aChildFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
11079 childHint = NS_RemoveSubsumedHints(
11080 childHint, aRestyleState.ChangesHandledFor(aChildFrame));
11081 }
11082 if (childHint) {
11083 if (childHint & nsChangeHint_ReconstructFrame) {
11084 // If we generate a reconstruct here, remove any non-reconstruct hints we
11085 // may have already generated for this content.
11086 aRestyleState.ChangeList().PopChangesForContent(
11087 aChildFrame->GetContent());
11088 }
11089 aRestyleState.ChangeList().AppendChange(
11090 aChildFrame, aChildFrame->GetContent(), childHint);
11091 }
11092
11093 aChildFrame->SetComputedStyle(aNewComputedStyle);
11094 ComputedStyle* continuationStyle = aContinuationComputedStyle
11095 ? *aContinuationComputedStyle
11096 : aNewComputedStyle;
11097 for (nsIFrame* kid = aChildFrame->GetNextContinuation(); kid;
11098 kid = kid->GetNextContinuation()) {
11099 MOZ_ASSERT(!kid->GetAdditionalComputedStyle(0));
11100 kid->SetComputedStyle(continuationStyle);
11101 }
11102
11103 return childHint;
11104 }
11105
11106 /* static */
AddInPopupStateBitToDescendants(nsIFrame * aFrame)11107 void nsIFrame::AddInPopupStateBitToDescendants(nsIFrame* aFrame) {
11108 if (!aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP) &&
11109 aFrame->TrackingVisibility()) {
11110 // Assume all frames in popups are visible.
11111 aFrame->IncApproximateVisibleCount();
11112 }
11113
11114 aFrame->AddStateBits(NS_FRAME_IN_POPUP);
11115
11116 for (const auto& childList : aFrame->CrossDocChildLists()) {
11117 for (nsIFrame* child : childList.mList) {
11118 AddInPopupStateBitToDescendants(child);
11119 }
11120 }
11121 }
11122
11123 /* static */
RemoveInPopupStateBitFromDescendants(nsIFrame * aFrame)11124 void nsIFrame::RemoveInPopupStateBitFromDescendants(nsIFrame* aFrame) {
11125 if (!aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP) ||
11126 nsLayoutUtils::IsPopup(aFrame)) {
11127 return;
11128 }
11129
11130 aFrame->RemoveStateBits(NS_FRAME_IN_POPUP);
11131
11132 if (aFrame->TrackingVisibility()) {
11133 // We assume all frames in popups are visible, so this decrement balances
11134 // out the increment in AddInPopupStateBitToDescendants above.
11135 aFrame->DecApproximateVisibleCount();
11136 }
11137 for (const auto& childList : aFrame->CrossDocChildLists()) {
11138 for (nsIFrame* child : childList.mList) {
11139 RemoveInPopupStateBitFromDescendants(child);
11140 }
11141 }
11142 }
11143
SetParent(nsContainerFrame * aParent)11144 void nsIFrame::SetParent(nsContainerFrame* aParent) {
11145 // If our parent is a wrapper anon box, our new parent should be too. We
11146 // _can_ change parent if our parent is a wrapper anon box, because some
11147 // wrapper anon boxes can have continuations.
11148 MOZ_ASSERT_IF(ParentIsWrapperAnonBox(),
11149 aParent->Style()->IsInheritingAnonBox());
11150
11151 // Note that the current mParent may already be destroyed at this point.
11152 mParent = aParent;
11153 MOZ_DIAGNOSTIC_ASSERT(!mParent || PresShell() == mParent->PresShell());
11154 if (::IsXULBoxWrapped(this)) {
11155 ::InitBoxMetrics(this, true);
11156 } else {
11157 // We could call Properties().Delete(BoxMetricsProperty()); here but
11158 // that's kind of slow and re-parenting in such a way that we were
11159 // IsXULBoxWrapped() before but not now should be very rare, so we'll just
11160 // keep this unused frame property until this frame dies instead.
11161 }
11162
11163 if (HasAnyStateBits(NS_FRAME_HAS_VIEW | NS_FRAME_HAS_CHILD_WITH_VIEW)) {
11164 for (nsIFrame* f = aParent;
11165 f && !f->HasAnyStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
11166 f = f->GetParent()) {
11167 f->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
11168 }
11169 }
11170
11171 if (HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
11172 for (nsIFrame* f = aParent; f; f = f->GetParent()) {
11173 if (f->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
11174 break;
11175 }
11176 f->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
11177 }
11178 }
11179
11180 if (HasAnyStateBits(NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)) {
11181 for (nsIFrame* f = aParent; f; f = f->GetParent()) {
11182 if (f->HasAnyStateBits(
11183 NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)) {
11184 break;
11185 }
11186 f->AddStateBits(NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE);
11187 }
11188 }
11189
11190 if (HasInvalidFrameInSubtree()) {
11191 for (nsIFrame* f = aParent;
11192 f && !f->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT |
11193 NS_FRAME_IS_NONDISPLAY);
11194 f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
11195 f->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
11196 }
11197 }
11198
11199 if (aParent->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
11200 AddInPopupStateBitToDescendants(this);
11201 } else {
11202 RemoveInPopupStateBitFromDescendants(this);
11203 }
11204
11205 // If our new parent only has invalid children, then we just invalidate
11206 // ourselves too. This is probably faster than clearing the flag all
11207 // the way up the frame tree.
11208 if (aParent->HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT)) {
11209 InvalidateFrame();
11210 } else {
11211 SchedulePaint();
11212 }
11213 }
11214
CreateOwnLayerIfNeeded(nsDisplayListBuilder * aBuilder,nsDisplayList * aList,uint16_t aType,bool * aCreatedContainerItem)11215 void nsIFrame::CreateOwnLayerIfNeeded(nsDisplayListBuilder* aBuilder,
11216 nsDisplayList* aList, uint16_t aType,
11217 bool* aCreatedContainerItem) {
11218 if (GetContent() && GetContent()->IsXULElement() &&
11219 GetContent()->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::layer)) {
11220 aList->AppendNewToTopWithIndex<nsDisplayOwnLayer>(
11221 aBuilder, this, /* aIndex = */ aType, aList,
11222 aBuilder->CurrentActiveScrolledRoot(), nsDisplayOwnLayerFlags::None,
11223 ScrollbarData{}, true, false);
11224 if (aCreatedContainerItem) {
11225 *aCreatedContainerItem = true;
11226 }
11227 }
11228 }
11229
IsStackingContext(const nsStyleDisplay * aStyleDisplay,const nsStyleEffects * aStyleEffects)11230 bool nsIFrame::IsStackingContext(const nsStyleDisplay* aStyleDisplay,
11231 const nsStyleEffects* aStyleEffects) {
11232 // Properties that influence the output of this function should be handled in
11233 // change_bits_for_longhand as well.
11234 if (HasOpacity(aStyleDisplay, aStyleEffects, nullptr)) {
11235 return true;
11236 }
11237 if (IsTransformed()) {
11238 return true;
11239 }
11240 auto willChange = aStyleDisplay->mWillChange.bits;
11241 if (aStyleDisplay->IsContainPaint() || aStyleDisplay->IsContainLayout() ||
11242 willChange & StyleWillChangeBits::CONTAIN) {
11243 if (IsFrameOfType(eSupportsContainLayoutAndPaint)) {
11244 return true;
11245 }
11246 }
11247 // strictly speaking, 'perspective' doesn't require visual atomicity,
11248 // but the spec says it acts like the rest of these
11249 if (aStyleDisplay->HasPerspectiveStyle() ||
11250 willChange & StyleWillChangeBits::PERSPECTIVE) {
11251 if (IsFrameOfType(eSupportsCSSTransforms)) {
11252 return true;
11253 }
11254 }
11255 if (!StylePosition()->mZIndex.IsAuto() ||
11256 willChange & StyleWillChangeBits::Z_INDEX) {
11257 if (ZIndexApplies()) {
11258 return true;
11259 }
11260 }
11261 return aStyleEffects->mMixBlendMode != StyleBlend::Normal ||
11262 SVGIntegrationUtils::UsingEffectsForFrame(this) ||
11263 aStyleDisplay->IsPositionForcingStackingContext() ||
11264 aStyleDisplay->mIsolation != StyleIsolation::Auto ||
11265 willChange & StyleWillChangeBits::STACKING_CONTEXT_UNCONDITIONAL;
11266 }
11267
IsStackingContext()11268 bool nsIFrame::IsStackingContext() {
11269 return IsStackingContext(StyleDisplay(), StyleEffects());
11270 }
11271
IsFrameScrolledOutOfView(const nsIFrame * aTarget,const nsRect & aTargetRect,const nsIFrame * aParent)11272 static bool IsFrameScrolledOutOfView(const nsIFrame* aTarget,
11273 const nsRect& aTargetRect,
11274 const nsIFrame* aParent) {
11275 // The ancestor frame we are checking if it clips out aTargetRect relative to
11276 // aTarget.
11277 nsIFrame* clipParent = nullptr;
11278
11279 // find the first scrollable frame or root frame if we are in a fixed pos
11280 // subtree
11281 for (nsIFrame* f = const_cast<nsIFrame*>(aParent); f;
11282 f = nsLayoutUtils::GetCrossDocParentFrameInProcess(f)) {
11283 nsIScrollableFrame* scrollableFrame = do_QueryFrame(f);
11284 if (scrollableFrame) {
11285 clipParent = f;
11286 break;
11287 }
11288 if (f->StyleDisplay()->mPosition == StylePositionProperty::Fixed &&
11289 nsLayoutUtils::IsReallyFixedPos(f)) {
11290 clipParent = f->GetParent();
11291 break;
11292 }
11293 }
11294
11295 if (!clipParent) {
11296 // Even if we couldn't find the nearest scrollable frame, it might mean we
11297 // are in an out-of-process iframe, try to see if |aTarget| frame is
11298 // scrolled out of view in an scrollable frame in a cross-process ancestor
11299 // document.
11300 return nsLayoutUtils::FrameIsScrolledOutOfViewInCrossProcess(aTarget);
11301 }
11302
11303 nsRect clipRect = clipParent->InkOverflowRectRelativeToSelf();
11304 // We consider that the target is scrolled out if the scrollable (or root)
11305 // frame is empty.
11306 if (clipRect.IsEmpty()) {
11307 return true;
11308 }
11309
11310 nsRect transformedRect = nsLayoutUtils::TransformFrameRectToAncestor(
11311 aTarget, aTargetRect, clipParent);
11312
11313 if (transformedRect.IsEmpty()) {
11314 // If the transformed rect is empty it represents a line or a point that we
11315 // should check is outside the the scrollable rect.
11316 if (transformedRect.x > clipRect.XMost() ||
11317 transformedRect.y > clipRect.YMost() ||
11318 clipRect.x > transformedRect.XMost() ||
11319 clipRect.y > transformedRect.YMost()) {
11320 return true;
11321 }
11322 } else if (!transformedRect.Intersects(clipRect)) {
11323 return true;
11324 }
11325
11326 nsIFrame* parent = clipParent->GetParent();
11327 if (!parent) {
11328 return false;
11329 }
11330
11331 return IsFrameScrolledOutOfView(aTarget, aTargetRect, parent);
11332 }
11333
IsScrolledOutOfView() const11334 bool nsIFrame::IsScrolledOutOfView() const {
11335 nsRect rect = InkOverflowRectRelativeToSelf();
11336 return IsFrameScrolledOutOfView(this, rect, this);
11337 }
11338
ComputeWidgetTransform()11339 gfx::Matrix nsIFrame::ComputeWidgetTransform() {
11340 const nsStyleUIReset* uiReset = StyleUIReset();
11341 if (uiReset->mMozWindowTransform.IsNone()) {
11342 return gfx::Matrix();
11343 }
11344
11345 TransformReferenceBox refBox(nullptr, nsRect(nsPoint(), GetSize()));
11346
11347 nsPresContext* presContext = PresContext();
11348 int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
11349 gfx::Matrix4x4 matrix = nsStyleTransformMatrix::ReadTransforms(
11350 uiReset->mMozWindowTransform, refBox, float(appUnitsPerDevPixel));
11351
11352 // Apply the -moz-window-transform-origin translation to the matrix.
11353 const StyleTransformOrigin& origin = uiReset->mWindowTransformOrigin;
11354 Point transformOrigin = nsStyleTransformMatrix::Convert2DPosition(
11355 origin.horizontal, origin.vertical, refBox, appUnitsPerDevPixel);
11356 matrix.ChangeBasis(Point3D(transformOrigin.x, transformOrigin.y, 0));
11357
11358 gfx::Matrix result2d;
11359 if (!matrix.CanDraw2D(&result2d)) {
11360 // FIXME: It would be preferable to reject non-2D transforms at parse time.
11361 NS_WARNING(
11362 "-moz-window-transform does not describe a 2D transform, "
11363 "but only 2d transforms are supported");
11364 return gfx::Matrix();
11365 }
11366
11367 return result2d;
11368 }
11369
DoUpdateStyleOfOwnedAnonBoxes(ServoRestyleState & aRestyleState)11370 void nsIFrame::DoUpdateStyleOfOwnedAnonBoxes(ServoRestyleState& aRestyleState) {
11371 // As a special case, we check for {ib}-split block frames here, rather
11372 // than have an nsInlineFrame::AppendDirectlyOwnedAnonBoxes implementation
11373 // that returns them.
11374 //
11375 // (If we did handle them in AppendDirectlyOwnedAnonBoxes, we would have to
11376 // return *all* of the in-flow {ib}-split block frames, not just the first
11377 // one. For restyling, we really just need the first in flow, and the other
11378 // user of the AppendOwnedAnonBoxes API, AllChildIterator, doesn't need to
11379 // know about them at all, since these block frames never create NAC. So we
11380 // avoid any unncessary hashtable lookups for the {ib}-split frames by calling
11381 // UpdateStyleOfOwnedAnonBoxesForIBSplit directly here.)
11382 if (IsInlineFrame()) {
11383 if (HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT)) {
11384 static_cast<nsInlineFrame*>(this)->UpdateStyleOfOwnedAnonBoxesForIBSplit(
11385 aRestyleState);
11386 }
11387 return;
11388 }
11389
11390 AutoTArray<OwnedAnonBox, 4> frames;
11391 AppendDirectlyOwnedAnonBoxes(frames);
11392 for (OwnedAnonBox& box : frames) {
11393 if (box.mUpdateStyleFn) {
11394 box.mUpdateStyleFn(this, box.mAnonBoxFrame, aRestyleState);
11395 } else {
11396 UpdateStyleOfChildAnonBox(box.mAnonBoxFrame, aRestyleState);
11397 }
11398 }
11399 }
11400
11401 /* virtual */
AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox> & aResult)11402 void nsIFrame::AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) {
11403 MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES));
11404 MOZ_ASSERT(false, "Why did this get called?");
11405 }
11406
DoAppendOwnedAnonBoxes(nsTArray<OwnedAnonBox> & aResult)11407 void nsIFrame::DoAppendOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) {
11408 size_t i = aResult.Length();
11409 AppendDirectlyOwnedAnonBoxes(aResult);
11410
11411 // After appending the directly owned anonymous boxes of this frame to
11412 // aResult above, we need to check each of them to see if they own
11413 // any anonymous boxes themselves. Note that we keep progressing
11414 // through aResult, looking for additional entries in aResult from these
11415 // subsequent AppendDirectlyOwnedAnonBoxes calls. (Thus we can't
11416 // use a ranged for loop here.)
11417
11418 while (i < aResult.Length()) {
11419 nsIFrame* f = aResult[i].mAnonBoxFrame;
11420 if (f->HasAnyStateBits(NS_FRAME_OWNS_ANON_BOXES)) {
11421 f->AppendDirectlyOwnedAnonBoxes(aResult);
11422 }
11423 ++i;
11424 }
11425 }
11426
CaretPosition()11427 nsIFrame::CaretPosition::CaretPosition() : mContentOffset(0) {}
11428
11429 nsIFrame::CaretPosition::~CaretPosition() = default;
11430
HasCSSAnimations()11431 bool nsIFrame::HasCSSAnimations() {
11432 auto collection =
11433 AnimationCollection<CSSAnimation>::GetAnimationCollection(this);
11434 return collection && collection->mAnimations.Length() > 0;
11435 }
11436
HasCSSTransitions()11437 bool nsIFrame::HasCSSTransitions() {
11438 auto collection =
11439 AnimationCollection<CSSTransition>::GetAnimationCollection(this);
11440 return collection && collection->mAnimations.Length() > 0;
11441 }
11442
AddSizeOfExcludingThisForTree(nsWindowSizes & aSizes) const11443 void nsIFrame::AddSizeOfExcludingThisForTree(nsWindowSizes& aSizes) const {
11444 aSizes.mLayoutFramePropertiesSize +=
11445 mProperties.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
11446
11447 // We don't do this for Gecko because this stuff is stored in the nsPresArena
11448 // and so measured elsewhere.
11449 if (!aSizes.mState.HaveSeenPtr(mComputedStyle)) {
11450 mComputedStyle->AddSizeOfIncludingThis(aSizes,
11451 &aSizes.mLayoutComputedValuesNonDom);
11452 }
11453
11454 // And our additional styles.
11455 int32_t index = 0;
11456 while (auto* extra = GetAdditionalComputedStyle(index++)) {
11457 if (!aSizes.mState.HaveSeenPtr(extra)) {
11458 extra->AddSizeOfIncludingThis(aSizes,
11459 &aSizes.mLayoutComputedValuesNonDom);
11460 }
11461 }
11462
11463 for (const auto& childList : ChildLists()) {
11464 for (const nsIFrame* f : childList.mList) {
11465 f->AddSizeOfExcludingThisForTree(aSizes);
11466 }
11467 }
11468 }
11469
GetCompositorHitTestArea(nsDisplayListBuilder * aBuilder)11470 nsRect nsIFrame::GetCompositorHitTestArea(nsDisplayListBuilder* aBuilder) {
11471 nsRect area;
11472
11473 nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetScrollableFrameFor(this);
11474 if (scrollFrame) {
11475 // If the frame is content of a scrollframe, then we need to pick up the
11476 // area corresponding to the overflow rect as well. Otherwise the parts of
11477 // the overflow that are not occupied by descendants get skipped and the
11478 // APZ code sends touch events to the content underneath instead.
11479 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1127773#c15.
11480 area = ScrollableOverflowRect();
11481 } else {
11482 area = GetRectRelativeToSelf();
11483 }
11484
11485 if (!area.IsEmpty()) {
11486 return area + aBuilder->ToReferenceFrame(this);
11487 }
11488
11489 return area;
11490 }
11491
GetCompositorHitTestInfo(nsDisplayListBuilder * aBuilder)11492 CompositorHitTestInfo nsIFrame::GetCompositorHitTestInfo(
11493 nsDisplayListBuilder* aBuilder) {
11494 CompositorHitTestInfo result = CompositorHitTestInvisibleToHit;
11495
11496 if (aBuilder->IsInsidePointerEventsNoneDoc()) {
11497 // Somewhere up the parent document chain is a subdocument with pointer-
11498 // events:none set on it.
11499 return result;
11500 }
11501 if (!GetParent()) {
11502 MOZ_ASSERT(IsViewportFrame());
11503 // Viewport frames are never event targets, other frames, like canvas
11504 // frames, are the event targets for any regions viewport frames may cover.
11505 return result;
11506 }
11507 if (Style()->PointerEvents() == StylePointerEvents::None) {
11508 return result;
11509 }
11510 if (!StyleVisibility()->IsVisible()) {
11511 return result;
11512 }
11513
11514 // Anything that didn't match the above conditions is visible to hit-testing.
11515 result = CompositorHitTestFlags::eVisibleToHitTest;
11516 if (SVGIntegrationUtils::UsingMaskOrClipPathForFrame(this)) {
11517 // If WebRender is enabled, simple clip-paths can be converted into WR
11518 // clips that WR knows how to hit-test against, so we don't need to mark
11519 // it as an irregular area.
11520 if (!gfxVars::UseWebRender() ||
11521 !SVGIntegrationUtils::UsingSimpleClipPathForFrame(this)) {
11522 result += CompositorHitTestFlags::eIrregularArea;
11523 }
11524 }
11525
11526 if (aBuilder->IsBuildingNonLayerizedScrollbar()) {
11527 // Scrollbars may be painted into a layer below the actual layer they will
11528 // scroll, and therefore wheel events may be dispatched to the outer frame
11529 // instead of the intended scrollframe. To address this, we force a d-t-c
11530 // region on scrollbar frames that won't be placed in their own layer. See
11531 // bug 1213324 for details.
11532 result += CompositorHitTestFlags::eInactiveScrollframe;
11533 } else if (aBuilder->GetAncestorHasApzAwareEventHandler()) {
11534 result += CompositorHitTestFlags::eApzAwareListeners;
11535 } else if (IsRangeFrame()) {
11536 // Range frames handle touch events directly without having a touch listener
11537 // so we need to let APZ know that this area cares about events.
11538 result += CompositorHitTestFlags::eApzAwareListeners;
11539 }
11540
11541 if (aBuilder->IsTouchEventPrefEnabledDoc()) {
11542 // Inherit the touch-action flags from the parent, if there is one. We do
11543 // this because of how the touch-action on a frame combines the touch-action
11544 // from ancestor DOM elements. Refer to the documentation in
11545 // TouchActionHelper.cpp for details; this code is meant to be equivalent to
11546 // that code, but woven into the top-down recursive display list building
11547 // process.
11548 CompositorHitTestInfo inheritedTouchAction =
11549 aBuilder->GetCompositorHitTestInfo() & CompositorHitTestTouchActionMask;
11550
11551 nsIFrame* touchActionFrame = this;
11552 if (nsIScrollableFrame* scrollFrame =
11553 nsLayoutUtils::GetScrollableFrameFor(this)) {
11554 ScrollStyles ss = scrollFrame->GetScrollStyles();
11555 if (ss.mVertical != StyleOverflow::Hidden ||
11556 ss.mHorizontal != StyleOverflow::Hidden) {
11557 touchActionFrame = do_QueryFrame(scrollFrame);
11558 // On scrollframes, stop inheriting the pan-x and pan-y flags; instead,
11559 // reset them back to zero to allow panning on the scrollframe unless we
11560 // encounter an element that disables it that's inside the scrollframe.
11561 // This is equivalent to the |considerPanning| variable in
11562 // TouchActionHelper.cpp, but for a top-down traversal.
11563 CompositorHitTestInfo panMask(
11564 CompositorHitTestFlags::eTouchActionPanXDisabled,
11565 CompositorHitTestFlags::eTouchActionPanYDisabled);
11566 inheritedTouchAction -= panMask;
11567 }
11568 }
11569
11570 result += inheritedTouchAction;
11571
11572 const StyleTouchAction touchAction = touchActionFrame->UsedTouchAction();
11573 // The CSS allows the syntax auto | none | [pan-x || pan-y] | manipulation
11574 // so we can eliminate some combinations of things.
11575 if (touchAction == StyleTouchAction::AUTO) {
11576 // nothing to do
11577 } else if (touchAction & StyleTouchAction::MANIPULATION) {
11578 result += CompositorHitTestFlags::eTouchActionDoubleTapZoomDisabled;
11579 } else {
11580 // This path handles the cases none | [pan-x || pan-y || pinch-zoom] so
11581 // double-tap is disabled in here.
11582 if (!(touchAction & StyleTouchAction::PINCH_ZOOM)) {
11583 result += CompositorHitTestFlags::eTouchActionPinchZoomDisabled;
11584 }
11585
11586 result += CompositorHitTestFlags::eTouchActionDoubleTapZoomDisabled;
11587
11588 if (!(touchAction & StyleTouchAction::PAN_X)) {
11589 result += CompositorHitTestFlags::eTouchActionPanXDisabled;
11590 }
11591 if (!(touchAction & StyleTouchAction::PAN_Y)) {
11592 result += CompositorHitTestFlags::eTouchActionPanYDisabled;
11593 }
11594 if (touchAction & StyleTouchAction::NONE) {
11595 // all the touch-action disabling flags will already have been set above
11596 MOZ_ASSERT(result.contains(CompositorHitTestTouchActionMask));
11597 }
11598 }
11599 }
11600
11601 const Maybe<ScrollDirection> scrollDirection =
11602 aBuilder->GetCurrentScrollbarDirection();
11603 if (scrollDirection.isSome()) {
11604 if (GetContent()->IsXULElement(nsGkAtoms::thumb)) {
11605 const bool thumbGetsLayer = aBuilder->GetCurrentScrollbarTarget() !=
11606 layers::ScrollableLayerGuid::NULL_SCROLL_ID;
11607 if (thumbGetsLayer) {
11608 result += CompositorHitTestFlags::eScrollbarThumb;
11609 } else {
11610 result += CompositorHitTestFlags::eInactiveScrollframe;
11611 }
11612 }
11613
11614 if (*scrollDirection == ScrollDirection::eVertical) {
11615 result += CompositorHitTestFlags::eScrollbarVertical;
11616 }
11617
11618 // includes the ScrollbarFrame, SliderFrame, anything else that
11619 // might be inside the xul:scrollbar
11620 result += CompositorHitTestFlags::eScrollbar;
11621 }
11622
11623 return result;
11624 }
11625
11626 // Returns true if we can guarantee there is no visible descendants.
HasNoVisibleDescendants(const nsIFrame * aFrame)11627 static bool HasNoVisibleDescendants(const nsIFrame* aFrame) {
11628 for (const auto& childList : aFrame->ChildLists()) {
11629 for (nsIFrame* f : childList.mList) {
11630 if (nsPlaceholderFrame::GetRealFrameFor(f)
11631 ->IsVisibleOrMayHaveVisibleDescendants()) {
11632 return false;
11633 }
11634 }
11635 }
11636 return true;
11637 }
11638
UpdateVisibleDescendantsState()11639 void nsIFrame::UpdateVisibleDescendantsState() {
11640 if (StyleVisibility()->IsVisible()) {
11641 // Notify invisible ancestors that a visible descendant exists now.
11642 nsIFrame* ancestor;
11643 for (ancestor = GetInFlowParent();
11644 ancestor && !ancestor->StyleVisibility()->IsVisible();
11645 ancestor = ancestor->GetInFlowParent()) {
11646 ancestor->mAllDescendantsAreInvisible = false;
11647 }
11648 } else {
11649 mAllDescendantsAreInvisible = HasNoVisibleDescendants(this);
11650 }
11651 }
11652
ShouldApplyOverflowClipping(const nsStyleDisplay * aDisp) const11653 nsIFrame::PhysicalAxes nsIFrame::ShouldApplyOverflowClipping(
11654 const nsStyleDisplay* aDisp) const {
11655 MOZ_ASSERT(aDisp == StyleDisplay(), "Wrong display struct");
11656
11657 // 'contain:paint', which we handle as 'overflow:clip' here. Except for
11658 // scrollframes we don't need contain:paint to add any clipping, because
11659 // the scrollable frame will already clip overflowing content, and because
11660 // 'contain:paint' should prevent all means of escaping that clipping
11661 // (e.g. because it forms a fixed-pos containing block).
11662 if (aDisp->IsContainPaint() && !IsScrollFrame() &&
11663 IsFrameOfType(eSupportsContainLayoutAndPaint)) {
11664 return PhysicalAxes::Both;
11665 }
11666
11667 // and overflow:hidden that we should interpret as clip
11668 if (aDisp->mOverflowX == StyleOverflow::Hidden &&
11669 aDisp->mOverflowY == StyleOverflow::Hidden) {
11670 // REVIEW: these are the frame types that set up clipping.
11671 LayoutFrameType type = Type();
11672 switch (type) {
11673 case LayoutFrameType::Table:
11674 case LayoutFrameType::TableCell:
11675 case LayoutFrameType::SVGOuterSVG:
11676 case LayoutFrameType::SVGInnerSVG:
11677 case LayoutFrameType::SVGSymbol:
11678 case LayoutFrameType::SVGForeignObject:
11679 return PhysicalAxes::Both;
11680 default:
11681 if (IsFrameOfType(nsIFrame::eReplacedContainsBlock)) {
11682 if (type == mozilla::LayoutFrameType::TextInput) {
11683 // It has an anonymous scroll frame that handles any overflow.
11684 return PhysicalAxes::None;
11685 }
11686 return PhysicalAxes::Both;
11687 }
11688 }
11689 }
11690
11691 // clip overflow:clip, except for nsListControlFrame which is
11692 // an nsHTMLScrollFrame sub-class.
11693 if (MOZ_UNLIKELY((aDisp->mOverflowX == mozilla::StyleOverflow::Clip ||
11694 aDisp->mOverflowY == mozilla::StyleOverflow::Clip) &&
11695 !IsListControlFrame())) {
11696 // FIXME: we could use GetViewportScrollStylesOverrideElement() here instead
11697 // if that worked correctly in a print context. (see bug 1654667)
11698 const auto* element = Element::FromNodeOrNull(GetContent());
11699 if (!element ||
11700 !PresContext()->ElementWouldPropagateScrollStyles(*element)) {
11701 uint8_t axes = uint8_t(PhysicalAxes::None);
11702 if (aDisp->mOverflowX == mozilla::StyleOverflow::Clip) {
11703 axes |= uint8_t(PhysicalAxes::Horizontal);
11704 }
11705 if (aDisp->mOverflowY == mozilla::StyleOverflow::Clip) {
11706 axes |= uint8_t(PhysicalAxes::Vertical);
11707 }
11708 return PhysicalAxes(axes);
11709 }
11710 }
11711
11712 if (HasAnyStateBits(NS_FRAME_SVG_LAYOUT)) {
11713 return PhysicalAxes::None;
11714 }
11715
11716 // If we're paginated and a block, and have NS_BLOCK_CLIP_PAGINATED_OVERFLOW
11717 // set, then we want to clip our overflow.
11718 bool clip = HasAnyStateBits(NS_BLOCK_CLIP_PAGINATED_OVERFLOW) &&
11719 PresContext()->IsPaginated() && IsBlockFrame();
11720 return clip ? PhysicalAxes::Both : PhysicalAxes::None;
11721 }
11722
AddPaintedPresShell(mozilla::PresShell * aPresShell)11723 void nsIFrame::AddPaintedPresShell(mozilla::PresShell* aPresShell) {
11724 PaintedPresShellList()->AppendElement(do_GetWeakReference(aPresShell));
11725 }
11726
UpdatePaintCountForPaintedPresShells()11727 void nsIFrame::UpdatePaintCountForPaintedPresShells() {
11728 for (nsWeakPtr& item : *PaintedPresShellList()) {
11729 if (RefPtr<mozilla::PresShell> presShell = do_QueryReferent(item)) {
11730 presShell->IncrementPaintCount();
11731 }
11732 }
11733 }
11734
DidPaintPresShell(mozilla::PresShell * aPresShell)11735 bool nsIFrame::DidPaintPresShell(mozilla::PresShell* aPresShell) {
11736 for (nsWeakPtr& item : *PaintedPresShellList()) {
11737 RefPtr<mozilla::PresShell> presShell = do_QueryReferent(item);
11738 if (presShell == aPresShell) {
11739 return true;
11740 }
11741 }
11742 return false;
11743 }
11744
11745 #ifdef DEBUG
GetTagName(nsIFrame * aFrame,nsIContent * aContent,int aResultSize,char * aResult)11746 static void GetTagName(nsIFrame* aFrame, nsIContent* aContent, int aResultSize,
11747 char* aResult) {
11748 if (aContent) {
11749 snprintf(aResult, aResultSize, "%s@%p",
11750 nsAtomCString(aContent->NodeInfo()->NameAtom()).get(), aFrame);
11751 } else {
11752 snprintf(aResult, aResultSize, "@%p", aFrame);
11753 }
11754 }
11755
Trace(const char * aMethod,bool aEnter)11756 void nsIFrame::Trace(const char* aMethod, bool aEnter) {
11757 if (NS_FRAME_LOG_TEST(sFrameLogModule, NS_FRAME_TRACE_CALLS)) {
11758 char tagbuf[40];
11759 GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
11760 printf_stderr("%s: %s %s", tagbuf, aEnter ? "enter" : "exit", aMethod);
11761 }
11762 }
11763
Trace(const char * aMethod,bool aEnter,const nsReflowStatus & aStatus)11764 void nsIFrame::Trace(const char* aMethod, bool aEnter,
11765 const nsReflowStatus& aStatus) {
11766 if (NS_FRAME_LOG_TEST(sFrameLogModule, NS_FRAME_TRACE_CALLS)) {
11767 char tagbuf[40];
11768 GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
11769 printf_stderr("%s: %s %s, status=%scomplete%s", tagbuf,
11770 aEnter ? "enter" : "exit", aMethod,
11771 aStatus.IsIncomplete() ? "not" : "",
11772 (aStatus.NextInFlowNeedsReflow()) ? "+reflow" : "");
11773 }
11774 }
11775
TraceMsg(const char * aFormatString,...)11776 void nsIFrame::TraceMsg(const char* aFormatString, ...) {
11777 if (NS_FRAME_LOG_TEST(sFrameLogModule, NS_FRAME_TRACE_CALLS)) {
11778 // Format arguments into a buffer
11779 char argbuf[200];
11780 va_list ap;
11781 va_start(ap, aFormatString);
11782 VsprintfLiteral(argbuf, aFormatString, ap);
11783 va_end(ap);
11784
11785 char tagbuf[40];
11786 GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
11787 printf_stderr("%s: %s", tagbuf, argbuf);
11788 }
11789 }
11790
VerifyDirtyBitSet(const nsFrameList & aFrameList)11791 void nsIFrame::VerifyDirtyBitSet(const nsFrameList& aFrameList) {
11792 for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
11793 NS_ASSERTION(e.get()->HasAnyStateBits(NS_FRAME_IS_DIRTY),
11794 "dirty bit not set");
11795 }
11796 }
11797
11798 // Start Display Reflow
DR_cookie(nsPresContext * aPresContext,nsIFrame * aFrame,const ReflowInput & aReflowInput,ReflowOutput & aMetrics,nsReflowStatus & aStatus)11799 DR_cookie::DR_cookie(nsPresContext* aPresContext, nsIFrame* aFrame,
11800 const ReflowInput& aReflowInput, ReflowOutput& aMetrics,
11801 nsReflowStatus& aStatus)
11802 : mPresContext(aPresContext),
11803 mFrame(aFrame),
11804 mReflowInput(aReflowInput),
11805 mMetrics(aMetrics),
11806 mStatus(aStatus) {
11807 MOZ_COUNT_CTOR(DR_cookie);
11808 mValue = nsIFrame::DisplayReflowEnter(aPresContext, mFrame, mReflowInput);
11809 }
11810
~DR_cookie()11811 DR_cookie::~DR_cookie() {
11812 MOZ_COUNT_DTOR(DR_cookie);
11813 nsIFrame::DisplayReflowExit(mPresContext, mFrame, mMetrics, mStatus, mValue);
11814 }
11815
DR_layout_cookie(nsIFrame * aFrame)11816 DR_layout_cookie::DR_layout_cookie(nsIFrame* aFrame) : mFrame(aFrame) {
11817 MOZ_COUNT_CTOR(DR_layout_cookie);
11818 mValue = nsIFrame::DisplayLayoutEnter(mFrame);
11819 }
11820
~DR_layout_cookie()11821 DR_layout_cookie::~DR_layout_cookie() {
11822 MOZ_COUNT_DTOR(DR_layout_cookie);
11823 nsIFrame::DisplayLayoutExit(mFrame, mValue);
11824 }
11825
DR_intrinsic_inline_size_cookie(nsIFrame * aFrame,const char * aType,nscoord & aResult)11826 DR_intrinsic_inline_size_cookie::DR_intrinsic_inline_size_cookie(
11827 nsIFrame* aFrame, const char* aType, nscoord& aResult)
11828 : mFrame(aFrame), mType(aType), mResult(aResult) {
11829 MOZ_COUNT_CTOR(DR_intrinsic_inline_size_cookie);
11830 mValue = nsIFrame::DisplayIntrinsicISizeEnter(mFrame, mType);
11831 }
11832
~DR_intrinsic_inline_size_cookie()11833 DR_intrinsic_inline_size_cookie::~DR_intrinsic_inline_size_cookie() {
11834 MOZ_COUNT_DTOR(DR_intrinsic_inline_size_cookie);
11835 nsIFrame::DisplayIntrinsicISizeExit(mFrame, mType, mResult, mValue);
11836 }
11837
DR_intrinsic_size_cookie(nsIFrame * aFrame,const char * aType,nsSize & aResult)11838 DR_intrinsic_size_cookie::DR_intrinsic_size_cookie(nsIFrame* aFrame,
11839 const char* aType,
11840 nsSize& aResult)
11841 : mFrame(aFrame), mType(aType), mResult(aResult) {
11842 MOZ_COUNT_CTOR(DR_intrinsic_size_cookie);
11843 mValue = nsIFrame::DisplayIntrinsicSizeEnter(mFrame, mType);
11844 }
11845
~DR_intrinsic_size_cookie()11846 DR_intrinsic_size_cookie::~DR_intrinsic_size_cookie() {
11847 MOZ_COUNT_DTOR(DR_intrinsic_size_cookie);
11848 nsIFrame::DisplayIntrinsicSizeExit(mFrame, mType, mResult, mValue);
11849 }
11850
DR_init_constraints_cookie(nsIFrame * aFrame,ReflowInput * aState,nscoord aCBWidth,nscoord aCBHeight,const mozilla::Maybe<mozilla::LogicalMargin> aBorder,const mozilla::Maybe<mozilla::LogicalMargin> aPadding)11851 DR_init_constraints_cookie::DR_init_constraints_cookie(
11852 nsIFrame* aFrame, ReflowInput* aState, nscoord aCBWidth, nscoord aCBHeight,
11853 const mozilla::Maybe<mozilla::LogicalMargin> aBorder,
11854 const mozilla::Maybe<mozilla::LogicalMargin> aPadding)
11855 : mFrame(aFrame), mState(aState) {
11856 MOZ_COUNT_CTOR(DR_init_constraints_cookie);
11857 nsMargin border;
11858 if (aBorder) {
11859 border = aBorder->GetPhysicalMargin(aFrame->GetWritingMode());
11860 }
11861 nsMargin padding;
11862 if (aPadding) {
11863 padding = aPadding->GetPhysicalMargin(aFrame->GetWritingMode());
11864 }
11865 mValue = ReflowInput::DisplayInitConstraintsEnter(
11866 mFrame, mState, aCBWidth, aCBHeight, aBorder ? &border : nullptr,
11867 aPadding ? &padding : nullptr);
11868 }
11869
~DR_init_constraints_cookie()11870 DR_init_constraints_cookie::~DR_init_constraints_cookie() {
11871 MOZ_COUNT_DTOR(DR_init_constraints_cookie);
11872 ReflowInput::DisplayInitConstraintsExit(mFrame, mState, mValue);
11873 }
11874
DR_init_offsets_cookie(nsIFrame * aFrame,SizeComputationInput * aState,nscoord aPercentBasis,WritingMode aCBWritingMode,const mozilla::Maybe<mozilla::LogicalMargin> aBorder,const mozilla::Maybe<mozilla::LogicalMargin> aPadding)11875 DR_init_offsets_cookie::DR_init_offsets_cookie(
11876 nsIFrame* aFrame, SizeComputationInput* aState, nscoord aPercentBasis,
11877 WritingMode aCBWritingMode,
11878 const mozilla::Maybe<mozilla::LogicalMargin> aBorder,
11879 const mozilla::Maybe<mozilla::LogicalMargin> aPadding)
11880 : mFrame(aFrame), mState(aState) {
11881 MOZ_COUNT_CTOR(DR_init_offsets_cookie);
11882 nsMargin border;
11883 if (aBorder) {
11884 border = aBorder->GetPhysicalMargin(aFrame->GetWritingMode());
11885 }
11886 nsMargin padding;
11887 if (aPadding) {
11888 padding = aPadding->GetPhysicalMargin(aFrame->GetWritingMode());
11889 }
11890 mValue = SizeComputationInput::DisplayInitOffsetsEnter(
11891 mFrame, mState, aPercentBasis, aCBWritingMode,
11892 aBorder ? &border : nullptr, aPadding ? &padding : nullptr);
11893 }
11894
~DR_init_offsets_cookie()11895 DR_init_offsets_cookie::~DR_init_offsets_cookie() {
11896 MOZ_COUNT_DTOR(DR_init_offsets_cookie);
11897 SizeComputationInput::DisplayInitOffsetsExit(mFrame, mState, mValue);
11898 }
11899
11900 struct DR_Rule;
11901
11902 struct DR_FrameTypeInfo {
11903 DR_FrameTypeInfo(LayoutFrameType aFrameType, const char* aFrameNameAbbrev,
11904 const char* aFrameName);
11905 ~DR_FrameTypeInfo();
11906
11907 LayoutFrameType mType;
11908 char mNameAbbrev[16];
11909 char mName[32];
11910 nsTArray<DR_Rule*> mRules;
11911
11912 private:
11913 DR_FrameTypeInfo& operator=(const DR_FrameTypeInfo&) = delete;
11914 };
11915
11916 struct DR_FrameTreeNode;
11917 struct DR_Rule;
11918
11919 struct DR_State {
11920 DR_State();
11921 ~DR_State();
11922 void Init();
11923 void AddFrameTypeInfo(LayoutFrameType aFrameType,
11924 const char* aFrameNameAbbrev, const char* aFrameName);
11925 DR_FrameTypeInfo* GetFrameTypeInfo(LayoutFrameType aFrameType);
11926 DR_FrameTypeInfo* GetFrameTypeInfo(char* aFrameName);
11927 void InitFrameTypeTable();
11928 DR_FrameTreeNode* CreateTreeNode(nsIFrame* aFrame,
11929 const ReflowInput* aReflowInput);
11930 void FindMatchingRule(DR_FrameTreeNode& aNode);
11931 bool RuleMatches(DR_Rule& aRule, DR_FrameTreeNode& aNode);
11932 bool GetToken(FILE* aFile, char* aBuf, size_t aBufSize);
11933 DR_Rule* ParseRule(FILE* aFile);
11934 void ParseRulesFile();
11935 void AddRule(nsTArray<DR_Rule*>& aRules, DR_Rule& aRule);
11936 bool IsWhiteSpace(int c);
11937 bool GetNumber(char* aBuf, int32_t& aNumber);
11938 void PrettyUC(nscoord aSize, char* aBuf, int aBufSize);
11939 void PrintMargin(const char* tag, const nsMargin* aMargin);
11940 void DisplayFrameTypeInfo(nsIFrame* aFrame, int32_t aIndent);
11941 void DeleteTreeNode(DR_FrameTreeNode& aNode);
11942
11943 bool mInited;
11944 bool mActive;
11945 int32_t mCount;
11946 int32_t mAssert;
11947 int32_t mIndent;
11948 bool mIndentUndisplayedFrames;
11949 bool mDisplayPixelErrors;
11950 nsTArray<DR_Rule*> mWildRules;
11951 nsTArray<DR_FrameTypeInfo> mFrameTypeTable;
11952 // reflow specific state
11953 nsTArray<DR_FrameTreeNode*> mFrameTreeLeaves;
11954 };
11955
11956 static DR_State* DR_state; // the one and only DR_State
11957
11958 struct DR_RulePart {
DR_RulePartDR_RulePart11959 explicit DR_RulePart(LayoutFrameType aFrameType)
11960 : mFrameType(aFrameType), mNext(0) {}
11961
11962 void Destroy();
11963
11964 LayoutFrameType mFrameType;
11965 DR_RulePart* mNext;
11966 };
11967
Destroy()11968 void DR_RulePart::Destroy() {
11969 if (mNext) {
11970 mNext->Destroy();
11971 }
11972 delete this;
11973 }
11974
11975 struct DR_Rule {
DR_RuleDR_Rule11976 DR_Rule() : mLength(0), mTarget(nullptr), mDisplay(false) {
11977 MOZ_COUNT_CTOR(DR_Rule);
11978 }
~DR_RuleDR_Rule11979 ~DR_Rule() {
11980 if (mTarget) mTarget->Destroy();
11981 MOZ_COUNT_DTOR(DR_Rule);
11982 }
11983 void AddPart(LayoutFrameType aFrameType);
11984
11985 uint32_t mLength;
11986 DR_RulePart* mTarget;
11987 bool mDisplay;
11988 };
11989
AddPart(LayoutFrameType aFrameType)11990 void DR_Rule::AddPart(LayoutFrameType aFrameType) {
11991 DR_RulePart* newPart = new DR_RulePart(aFrameType);
11992 newPart->mNext = mTarget;
11993 mTarget = newPart;
11994 mLength++;
11995 }
11996
~DR_FrameTypeInfo()11997 DR_FrameTypeInfo::~DR_FrameTypeInfo() {
11998 int32_t numElements;
11999 numElements = mRules.Length();
12000 for (int32_t i = numElements - 1; i >= 0; i--) {
12001 delete mRules.ElementAt(i);
12002 }
12003 }
12004
DR_FrameTypeInfo(LayoutFrameType aFrameType,const char * aFrameNameAbbrev,const char * aFrameName)12005 DR_FrameTypeInfo::DR_FrameTypeInfo(LayoutFrameType aFrameType,
12006 const char* aFrameNameAbbrev,
12007 const char* aFrameName) {
12008 mType = aFrameType;
12009 PL_strncpyz(mNameAbbrev, aFrameNameAbbrev, sizeof(mNameAbbrev));
12010 PL_strncpyz(mName, aFrameName, sizeof(mName));
12011 }
12012
12013 struct DR_FrameTreeNode {
DR_FrameTreeNodeDR_FrameTreeNode12014 DR_FrameTreeNode(nsIFrame* aFrame, DR_FrameTreeNode* aParent)
12015 : mFrame(aFrame), mParent(aParent), mDisplay(0), mIndent(0) {
12016 MOZ_COUNT_CTOR(DR_FrameTreeNode);
12017 }
12018
12019 MOZ_COUNTED_DTOR(DR_FrameTreeNode)
12020
12021 nsIFrame* mFrame;
12022 DR_FrameTreeNode* mParent;
12023 bool mDisplay;
12024 uint32_t mIndent;
12025 };
12026
12027 // DR_State implementation
12028
DR_State()12029 DR_State::DR_State()
12030 : mInited(false),
12031 mActive(false),
12032 mCount(0),
12033 mAssert(-1),
12034 mIndent(0),
12035 mIndentUndisplayedFrames(false),
12036 mDisplayPixelErrors(false) {
12037 MOZ_COUNT_CTOR(DR_State);
12038 }
12039
Init()12040 void DR_State::Init() {
12041 char* env = PR_GetEnv("GECKO_DISPLAY_REFLOW_ASSERT");
12042 int32_t num;
12043 if (env) {
12044 if (GetNumber(env, num))
12045 mAssert = num;
12046 else
12047 printf("GECKO_DISPLAY_REFLOW_ASSERT - invalid value = %s", env);
12048 }
12049
12050 env = PR_GetEnv("GECKO_DISPLAY_REFLOW_INDENT_START");
12051 if (env) {
12052 if (GetNumber(env, num))
12053 mIndent = num;
12054 else
12055 printf("GECKO_DISPLAY_REFLOW_INDENT_START - invalid value = %s", env);
12056 }
12057
12058 env = PR_GetEnv("GECKO_DISPLAY_REFLOW_INDENT_UNDISPLAYED_FRAMES");
12059 if (env) {
12060 if (GetNumber(env, num))
12061 mIndentUndisplayedFrames = num;
12062 else
12063 printf(
12064 "GECKO_DISPLAY_REFLOW_INDENT_UNDISPLAYED_FRAMES - invalid value = %s",
12065 env);
12066 }
12067
12068 env = PR_GetEnv("GECKO_DISPLAY_REFLOW_FLAG_PIXEL_ERRORS");
12069 if (env) {
12070 if (GetNumber(env, num))
12071 mDisplayPixelErrors = num;
12072 else
12073 printf("GECKO_DISPLAY_REFLOW_FLAG_PIXEL_ERRORS - invalid value = %s",
12074 env);
12075 }
12076
12077 InitFrameTypeTable();
12078 ParseRulesFile();
12079 mInited = true;
12080 }
12081
~DR_State()12082 DR_State::~DR_State() {
12083 MOZ_COUNT_DTOR(DR_State);
12084 int32_t numElements, i;
12085 numElements = mWildRules.Length();
12086 for (i = numElements - 1; i >= 0; i--) {
12087 delete mWildRules.ElementAt(i);
12088 }
12089 numElements = mFrameTreeLeaves.Length();
12090 for (i = numElements - 1; i >= 0; i--) {
12091 delete mFrameTreeLeaves.ElementAt(i);
12092 }
12093 }
12094
GetNumber(char * aBuf,int32_t & aNumber)12095 bool DR_State::GetNumber(char* aBuf, int32_t& aNumber) {
12096 if (sscanf(aBuf, "%d", &aNumber) > 0)
12097 return true;
12098 else
12099 return false;
12100 }
12101
IsWhiteSpace(int c)12102 bool DR_State::IsWhiteSpace(int c) {
12103 return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r');
12104 }
12105
GetToken(FILE * aFile,char * aBuf,size_t aBufSize)12106 bool DR_State::GetToken(FILE* aFile, char* aBuf, size_t aBufSize) {
12107 bool haveToken = false;
12108 aBuf[0] = 0;
12109 // get the 1st non whitespace char
12110 int c = -1;
12111 for (c = getc(aFile); (c > 0) && IsWhiteSpace(c); c = getc(aFile)) {
12112 }
12113
12114 if (c > 0) {
12115 haveToken = true;
12116 aBuf[0] = c;
12117 // get everything up to the next whitespace char
12118 size_t cX;
12119 for (cX = 1; cX + 1 < aBufSize; cX++) {
12120 c = getc(aFile);
12121 if (c < 0) { // EOF
12122 ungetc(' ', aFile);
12123 break;
12124 } else {
12125 if (IsWhiteSpace(c)) {
12126 break;
12127 } else {
12128 aBuf[cX] = c;
12129 }
12130 }
12131 }
12132 aBuf[cX] = 0;
12133 }
12134 return haveToken;
12135 }
12136
ParseRule(FILE * aFile)12137 DR_Rule* DR_State::ParseRule(FILE* aFile) {
12138 char buf[128];
12139 int32_t doDisplay;
12140 DR_Rule* rule = nullptr;
12141 while (GetToken(aFile, buf, sizeof(buf))) {
12142 if (GetNumber(buf, doDisplay)) {
12143 if (rule) {
12144 rule->mDisplay = !!doDisplay;
12145 break;
12146 } else {
12147 printf("unexpected token - %s \n", buf);
12148 }
12149 } else {
12150 if (!rule) {
12151 rule = new DR_Rule;
12152 }
12153 if (strcmp(buf, "*") == 0) {
12154 rule->AddPart(LayoutFrameType::None);
12155 } else {
12156 DR_FrameTypeInfo* info = GetFrameTypeInfo(buf);
12157 if (info) {
12158 rule->AddPart(info->mType);
12159 } else {
12160 printf("invalid frame type - %s \n", buf);
12161 }
12162 }
12163 }
12164 }
12165 return rule;
12166 }
12167
AddRule(nsTArray<DR_Rule * > & aRules,DR_Rule & aRule)12168 void DR_State::AddRule(nsTArray<DR_Rule*>& aRules, DR_Rule& aRule) {
12169 int32_t numRules = aRules.Length();
12170 for (int32_t ruleX = 0; ruleX < numRules; ruleX++) {
12171 DR_Rule* rule = aRules.ElementAt(ruleX);
12172 NS_ASSERTION(rule, "program error");
12173 if (aRule.mLength > rule->mLength) {
12174 aRules.InsertElementAt(ruleX, &aRule);
12175 return;
12176 }
12177 }
12178 aRules.AppendElement(&aRule);
12179 }
12180
ShouldLogReflow(const char * processes)12181 static Maybe<bool> ShouldLogReflow(const char* processes) {
12182 switch (processes[0]) {
12183 case 'A':
12184 case 'a':
12185 return Some(true);
12186 case 'P':
12187 case 'p':
12188 return Some(XRE_IsParentProcess());
12189 case 'C':
12190 case 'c':
12191 return Some(XRE_IsContentProcess());
12192 default:
12193 return Nothing{};
12194 }
12195 }
12196
ParseRulesFile()12197 void DR_State::ParseRulesFile() {
12198 char* processes = PR_GetEnv("GECKO_DISPLAY_REFLOW_PROCESSES");
12199 if (processes) {
12200 Maybe<bool> enableLog = ShouldLogReflow(processes);
12201 if (enableLog.isNothing()) {
12202 MOZ_CRASH("GECKO_DISPLAY_REFLOW_PROCESSES: [a]ll [p]arent [c]ontent");
12203 } else if (enableLog.value()) {
12204 DR_Rule* rule = new DR_Rule;
12205 rule->AddPart(LayoutFrameType::None);
12206 rule->mDisplay = true;
12207 AddRule(mWildRules, *rule);
12208 mActive = true;
12209 }
12210 return;
12211 }
12212
12213 char* path = PR_GetEnv("GECKO_DISPLAY_REFLOW_RULES_FILE");
12214 if (path) {
12215 FILE* inFile = fopen(path, "r");
12216 if (!inFile) {
12217 MOZ_CRASH(
12218 "Failed to open the specified rules file; Try `--setpref "
12219 "security.sandbox.content.level=2` if the sandbox is at cause");
12220 }
12221 for (DR_Rule* rule = ParseRule(inFile); rule; rule = ParseRule(inFile)) {
12222 if (rule->mTarget) {
12223 LayoutFrameType fType = rule->mTarget->mFrameType;
12224 if (fType != LayoutFrameType::None) {
12225 DR_FrameTypeInfo* info = GetFrameTypeInfo(fType);
12226 AddRule(info->mRules, *rule);
12227 } else {
12228 AddRule(mWildRules, *rule);
12229 }
12230 mActive = true;
12231 }
12232 }
12233
12234 fclose(inFile);
12235 }
12236 }
12237
AddFrameTypeInfo(LayoutFrameType aFrameType,const char * aFrameNameAbbrev,const char * aFrameName)12238 void DR_State::AddFrameTypeInfo(LayoutFrameType aFrameType,
12239 const char* aFrameNameAbbrev,
12240 const char* aFrameName) {
12241 mFrameTypeTable.EmplaceBack(aFrameType, aFrameNameAbbrev, aFrameName);
12242 }
12243
GetFrameTypeInfo(LayoutFrameType aFrameType)12244 DR_FrameTypeInfo* DR_State::GetFrameTypeInfo(LayoutFrameType aFrameType) {
12245 int32_t numEntries = mFrameTypeTable.Length();
12246 NS_ASSERTION(numEntries != 0, "empty FrameTypeTable");
12247 for (int32_t i = 0; i < numEntries; i++) {
12248 DR_FrameTypeInfo& info = mFrameTypeTable.ElementAt(i);
12249 if (info.mType == aFrameType) {
12250 return &info;
12251 }
12252 }
12253 return &mFrameTypeTable.ElementAt(numEntries -
12254 1); // return unknown frame type
12255 }
12256
GetFrameTypeInfo(char * aFrameName)12257 DR_FrameTypeInfo* DR_State::GetFrameTypeInfo(char* aFrameName) {
12258 int32_t numEntries = mFrameTypeTable.Length();
12259 NS_ASSERTION(numEntries != 0, "empty FrameTypeTable");
12260 for (int32_t i = 0; i < numEntries; i++) {
12261 DR_FrameTypeInfo& info = mFrameTypeTable.ElementAt(i);
12262 if ((strcmp(aFrameName, info.mName) == 0) ||
12263 (strcmp(aFrameName, info.mNameAbbrev) == 0)) {
12264 return &info;
12265 }
12266 }
12267 return &mFrameTypeTable.ElementAt(numEntries -
12268 1); // return unknown frame type
12269 }
12270
InitFrameTypeTable()12271 void DR_State::InitFrameTypeTable() {
12272 AddFrameTypeInfo(LayoutFrameType::Block, "block", "block");
12273 AddFrameTypeInfo(LayoutFrameType::Br, "br", "br");
12274 AddFrameTypeInfo(LayoutFrameType::ColorControl, "color", "colorControl");
12275 AddFrameTypeInfo(LayoutFrameType::GfxButtonControl, "button",
12276 "gfxButtonControl");
12277 AddFrameTypeInfo(LayoutFrameType::HTMLButtonControl, "HTMLbutton",
12278 "HTMLButtonControl");
12279 AddFrameTypeInfo(LayoutFrameType::HTMLCanvas, "HTMLCanvas", "HTMLCanvas");
12280 AddFrameTypeInfo(LayoutFrameType::SubDocument, "subdoc", "subDocument");
12281 AddFrameTypeInfo(LayoutFrameType::Image, "img", "image");
12282 AddFrameTypeInfo(LayoutFrameType::Inline, "inline", "inline");
12283 AddFrameTypeInfo(LayoutFrameType::Letter, "letter", "letter");
12284 AddFrameTypeInfo(LayoutFrameType::Line, "line", "line");
12285 AddFrameTypeInfo(LayoutFrameType::ListControl, "select", "select");
12286 AddFrameTypeInfo(LayoutFrameType::Page, "page", "page");
12287 AddFrameTypeInfo(LayoutFrameType::Placeholder, "place", "placeholder");
12288 AddFrameTypeInfo(LayoutFrameType::Canvas, "canvas", "canvas");
12289 AddFrameTypeInfo(LayoutFrameType::XULRoot, "xulroot", "xulroot");
12290 AddFrameTypeInfo(LayoutFrameType::Scroll, "scroll", "scroll");
12291 AddFrameTypeInfo(LayoutFrameType::TableCell, "cell", "tableCell");
12292 AddFrameTypeInfo(LayoutFrameType::TableCol, "col", "tableCol");
12293 AddFrameTypeInfo(LayoutFrameType::TableColGroup, "colG", "tableColGroup");
12294 AddFrameTypeInfo(LayoutFrameType::Table, "tbl", "table");
12295 AddFrameTypeInfo(LayoutFrameType::TableWrapper, "tblW", "tableWrapper");
12296 AddFrameTypeInfo(LayoutFrameType::TableRowGroup, "rowG", "tableRowGroup");
12297 AddFrameTypeInfo(LayoutFrameType::TableRow, "row", "tableRow");
12298 AddFrameTypeInfo(LayoutFrameType::TextInput, "textCtl", "textInput");
12299 AddFrameTypeInfo(LayoutFrameType::Text, "text", "text");
12300 AddFrameTypeInfo(LayoutFrameType::Viewport, "VP", "viewport");
12301 AddFrameTypeInfo(LayoutFrameType::Box, "Box", "Box");
12302 AddFrameTypeInfo(LayoutFrameType::Slider, "Slider", "Slider");
12303 AddFrameTypeInfo(LayoutFrameType::PopupSet, "PopupSet", "PopupSet");
12304 AddFrameTypeInfo(LayoutFrameType::None, "unknown", "unknown");
12305 }
12306
DisplayFrameTypeInfo(nsIFrame * aFrame,int32_t aIndent)12307 void DR_State::DisplayFrameTypeInfo(nsIFrame* aFrame, int32_t aIndent) {
12308 DR_FrameTypeInfo* frameTypeInfo = GetFrameTypeInfo(aFrame->Type());
12309 if (frameTypeInfo) {
12310 for (int32_t i = 0; i < aIndent; i++) {
12311 printf(" ");
12312 }
12313 if (!strcmp(frameTypeInfo->mNameAbbrev, "unknown")) {
12314 if (aFrame) {
12315 nsAutoString name;
12316 aFrame->GetFrameName(name);
12317 printf("%s %p ", NS_LossyConvertUTF16toASCII(name).get(),
12318 (void*)aFrame);
12319 } else {
12320 printf("%s %p ", frameTypeInfo->mNameAbbrev, (void*)aFrame);
12321 }
12322 } else {
12323 printf("%s %p ", frameTypeInfo->mNameAbbrev, (void*)aFrame);
12324 }
12325 }
12326 }
12327
RuleMatches(DR_Rule & aRule,DR_FrameTreeNode & aNode)12328 bool DR_State::RuleMatches(DR_Rule& aRule, DR_FrameTreeNode& aNode) {
12329 NS_ASSERTION(aRule.mTarget, "program error");
12330
12331 DR_RulePart* rulePart;
12332 DR_FrameTreeNode* parentNode;
12333 for (rulePart = aRule.mTarget->mNext, parentNode = aNode.mParent;
12334 rulePart && parentNode;
12335 rulePart = rulePart->mNext, parentNode = parentNode->mParent) {
12336 if (rulePart->mFrameType != LayoutFrameType::None) {
12337 if (parentNode->mFrame) {
12338 if (rulePart->mFrameType != parentNode->mFrame->Type()) {
12339 return false;
12340 }
12341 } else
12342 NS_ASSERTION(false, "program error");
12343 }
12344 // else wild card match
12345 }
12346 return true;
12347 }
12348
FindMatchingRule(DR_FrameTreeNode & aNode)12349 void DR_State::FindMatchingRule(DR_FrameTreeNode& aNode) {
12350 if (!aNode.mFrame) {
12351 NS_ASSERTION(false, "invalid DR_FrameTreeNode \n");
12352 return;
12353 }
12354
12355 bool matchingRule = false;
12356
12357 DR_FrameTypeInfo* info = GetFrameTypeInfo(aNode.mFrame->Type());
12358 NS_ASSERTION(info, "program error");
12359 int32_t numRules = info->mRules.Length();
12360 for (int32_t ruleX = 0; ruleX < numRules; ruleX++) {
12361 DR_Rule* rule = info->mRules.ElementAt(ruleX);
12362 if (rule && RuleMatches(*rule, aNode)) {
12363 aNode.mDisplay = rule->mDisplay;
12364 matchingRule = true;
12365 break;
12366 }
12367 }
12368 if (!matchingRule) {
12369 int32_t numWildRules = mWildRules.Length();
12370 for (int32_t ruleX = 0; ruleX < numWildRules; ruleX++) {
12371 DR_Rule* rule = mWildRules.ElementAt(ruleX);
12372 if (rule && RuleMatches(*rule, aNode)) {
12373 aNode.mDisplay = rule->mDisplay;
12374 break;
12375 }
12376 }
12377 }
12378 }
12379
CreateTreeNode(nsIFrame * aFrame,const ReflowInput * aReflowInput)12380 DR_FrameTreeNode* DR_State::CreateTreeNode(nsIFrame* aFrame,
12381 const ReflowInput* aReflowInput) {
12382 // find the frame of the parent reflow input (usually just the parent of
12383 // aFrame)
12384 nsIFrame* parentFrame;
12385 if (aReflowInput) {
12386 const ReflowInput* parentRI = aReflowInput->mParentReflowInput;
12387 parentFrame = (parentRI) ? parentRI->mFrame : nullptr;
12388 } else {
12389 parentFrame = aFrame->GetParent();
12390 }
12391
12392 // find the parent tree node leaf
12393 DR_FrameTreeNode* parentNode = nullptr;
12394
12395 DR_FrameTreeNode* lastLeaf = nullptr;
12396 if (mFrameTreeLeaves.Length())
12397 lastLeaf = mFrameTreeLeaves.ElementAt(mFrameTreeLeaves.Length() - 1);
12398 if (lastLeaf) {
12399 for (parentNode = lastLeaf;
12400 parentNode && (parentNode->mFrame != parentFrame);
12401 parentNode = parentNode->mParent) {
12402 }
12403 }
12404 DR_FrameTreeNode* newNode = new DR_FrameTreeNode(aFrame, parentNode);
12405 FindMatchingRule(*newNode);
12406
12407 newNode->mIndent = mIndent;
12408 if (newNode->mDisplay || mIndentUndisplayedFrames) {
12409 ++mIndent;
12410 }
12411
12412 if (lastLeaf && (lastLeaf == parentNode)) {
12413 mFrameTreeLeaves.RemoveLastElement();
12414 }
12415 mFrameTreeLeaves.AppendElement(newNode);
12416 mCount++;
12417
12418 return newNode;
12419 }
12420
PrettyUC(nscoord aSize,char * aBuf,int aBufSize)12421 void DR_State::PrettyUC(nscoord aSize, char* aBuf, int aBufSize) {
12422 if (NS_UNCONSTRAINEDSIZE == aSize) {
12423 strcpy(aBuf, "UC");
12424 } else {
12425 if ((nscoord)0xdeadbeefU == aSize) {
12426 strcpy(aBuf, "deadbeef");
12427 } else {
12428 snprintf(aBuf, aBufSize, "%d", aSize);
12429 }
12430 }
12431 }
12432
PrintMargin(const char * tag,const nsMargin * aMargin)12433 void DR_State::PrintMargin(const char* tag, const nsMargin* aMargin) {
12434 if (aMargin) {
12435 char t[16], r[16], b[16], l[16];
12436 PrettyUC(aMargin->top, t, 16);
12437 PrettyUC(aMargin->right, r, 16);
12438 PrettyUC(aMargin->bottom, b, 16);
12439 PrettyUC(aMargin->left, l, 16);
12440 printf(" %s=%s,%s,%s,%s", tag, t, r, b, l);
12441 } else {
12442 // use %p here for consistency with other null-pointer printouts
12443 printf(" %s=%p", tag, (void*)aMargin);
12444 }
12445 }
12446
DeleteTreeNode(DR_FrameTreeNode & aNode)12447 void DR_State::DeleteTreeNode(DR_FrameTreeNode& aNode) {
12448 mFrameTreeLeaves.RemoveElement(&aNode);
12449 int32_t numLeaves = mFrameTreeLeaves.Length();
12450 if ((0 == numLeaves) ||
12451 (aNode.mParent != mFrameTreeLeaves.ElementAt(numLeaves - 1))) {
12452 mFrameTreeLeaves.AppendElement(aNode.mParent);
12453 }
12454
12455 if (aNode.mDisplay || mIndentUndisplayedFrames) {
12456 --mIndent;
12457 }
12458 // delete the tree node
12459 delete &aNode;
12460 }
12461
CheckPixelError(nscoord aSize,int32_t aPixelToTwips)12462 static void CheckPixelError(nscoord aSize, int32_t aPixelToTwips) {
12463 if (NS_UNCONSTRAINEDSIZE != aSize) {
12464 if ((aSize % aPixelToTwips) > 0) {
12465 printf("VALUE %d is not a whole pixel \n", aSize);
12466 }
12467 }
12468 }
12469
DisplayReflowEnterPrint(nsPresContext * aPresContext,nsIFrame * aFrame,const ReflowInput & aReflowInput,DR_FrameTreeNode & aTreeNode,bool aChanged)12470 static void DisplayReflowEnterPrint(nsPresContext* aPresContext,
12471 nsIFrame* aFrame,
12472 const ReflowInput& aReflowInput,
12473 DR_FrameTreeNode& aTreeNode,
12474 bool aChanged) {
12475 if (aTreeNode.mDisplay) {
12476 DR_state->DisplayFrameTypeInfo(aFrame, aTreeNode.mIndent);
12477
12478 char width[16];
12479 char height[16];
12480
12481 DR_state->PrettyUC(aReflowInput.AvailableWidth(), width, 16);
12482 DR_state->PrettyUC(aReflowInput.AvailableHeight(), height, 16);
12483 printf("Reflow a=%s,%s ", width, height);
12484
12485 DR_state->PrettyUC(aReflowInput.ComputedWidth(), width, 16);
12486 DR_state->PrettyUC(aReflowInput.ComputedHeight(), height, 16);
12487 printf("c=%s,%s ", width, height);
12488
12489 if (aFrame->HasAnyStateBits(NS_FRAME_IS_DIRTY)) printf("dirty ");
12490
12491 if (aFrame->HasAnyStateBits(NS_FRAME_HAS_DIRTY_CHILDREN))
12492 printf("dirty-children ");
12493
12494 if (aReflowInput.mFlags.mSpecialBSizeReflow) printf("special-bsize ");
12495
12496 if (aReflowInput.IsHResize()) printf("h-resize ");
12497
12498 if (aReflowInput.IsVResize()) printf("v-resize ");
12499
12500 nsIFrame* inFlow = aFrame->GetPrevInFlow();
12501 if (inFlow) {
12502 printf("pif=%p ", (void*)inFlow);
12503 }
12504 inFlow = aFrame->GetNextInFlow();
12505 if (inFlow) {
12506 printf("nif=%p ", (void*)inFlow);
12507 }
12508 if (aChanged)
12509 printf("CHANGED \n");
12510 else
12511 printf("cnt=%d \n", DR_state->mCount);
12512 if (DR_state->mDisplayPixelErrors) {
12513 int32_t d2a = aPresContext->AppUnitsPerDevPixel();
12514 CheckPixelError(aReflowInput.AvailableWidth(), d2a);
12515 CheckPixelError(aReflowInput.AvailableHeight(), d2a);
12516 CheckPixelError(aReflowInput.ComputedWidth(), d2a);
12517 CheckPixelError(aReflowInput.ComputedHeight(), d2a);
12518 }
12519 }
12520 }
12521
DisplayReflowEnter(nsPresContext * aPresContext,nsIFrame * aFrame,const ReflowInput & aReflowInput)12522 void* nsIFrame::DisplayReflowEnter(nsPresContext* aPresContext,
12523 nsIFrame* aFrame,
12524 const ReflowInput& aReflowInput) {
12525 if (!DR_state->mInited) DR_state->Init();
12526 if (!DR_state->mActive) return nullptr;
12527
12528 NS_ASSERTION(aFrame, "invalid call");
12529
12530 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, &aReflowInput);
12531 if (treeNode) {
12532 DisplayReflowEnterPrint(aPresContext, aFrame, aReflowInput, *treeNode,
12533 false);
12534 }
12535 return treeNode;
12536 }
12537
DisplayLayoutEnter(nsIFrame * aFrame)12538 void* nsIFrame::DisplayLayoutEnter(nsIFrame* aFrame) {
12539 if (!DR_state->mInited) DR_state->Init();
12540 if (!DR_state->mActive) return nullptr;
12541
12542 NS_ASSERTION(aFrame, "invalid call");
12543
12544 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr);
12545 if (treeNode && treeNode->mDisplay) {
12546 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12547 printf("XULLayout\n");
12548 }
12549 return treeNode;
12550 }
12551
DisplayIntrinsicISizeEnter(nsIFrame * aFrame,const char * aType)12552 void* nsIFrame::DisplayIntrinsicISizeEnter(nsIFrame* aFrame,
12553 const char* aType) {
12554 if (!DR_state->mInited) DR_state->Init();
12555 if (!DR_state->mActive) return nullptr;
12556
12557 NS_ASSERTION(aFrame, "invalid call");
12558
12559 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr);
12560 if (treeNode && treeNode->mDisplay) {
12561 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12562 printf("Get%sISize\n", aType);
12563 }
12564 return treeNode;
12565 }
12566
DisplayIntrinsicSizeEnter(nsIFrame * aFrame,const char * aType)12567 void* nsIFrame::DisplayIntrinsicSizeEnter(nsIFrame* aFrame, const char* aType) {
12568 if (!DR_state->mInited) DR_state->Init();
12569 if (!DR_state->mActive) return nullptr;
12570
12571 NS_ASSERTION(aFrame, "invalid call");
12572
12573 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr);
12574 if (treeNode && treeNode->mDisplay) {
12575 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12576 printf("Get%sSize\n", aType);
12577 }
12578 return treeNode;
12579 }
12580
DisplayReflowExit(nsPresContext * aPresContext,nsIFrame * aFrame,ReflowOutput & aMetrics,const nsReflowStatus & aStatus,void * aFrameTreeNode)12581 void nsIFrame::DisplayReflowExit(nsPresContext* aPresContext, nsIFrame* aFrame,
12582 ReflowOutput& aMetrics,
12583 const nsReflowStatus& aStatus,
12584 void* aFrameTreeNode) {
12585 if (!DR_state->mActive) return;
12586
12587 NS_ASSERTION(aFrame, "DisplayReflowExit - invalid call");
12588 if (!aFrameTreeNode) return;
12589
12590 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
12591 if (treeNode->mDisplay) {
12592 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12593
12594 char width[16];
12595 char height[16];
12596 char x[16];
12597 char y[16];
12598 DR_state->PrettyUC(aMetrics.Width(), width, 16);
12599 DR_state->PrettyUC(aMetrics.Height(), height, 16);
12600 printf("Reflow d=%s,%s", width, height);
12601
12602 if (!aStatus.IsEmpty()) {
12603 printf(" status=%s", ToString(aStatus).c_str());
12604 }
12605 if (aFrame->HasOverflowAreas()) {
12606 DR_state->PrettyUC(aMetrics.InkOverflow().x, x, 16);
12607 DR_state->PrettyUC(aMetrics.InkOverflow().y, y, 16);
12608 DR_state->PrettyUC(aMetrics.InkOverflow().width, width, 16);
12609 DR_state->PrettyUC(aMetrics.InkOverflow().height, height, 16);
12610 printf(" vis-o=(%s,%s) %s x %s", x, y, width, height);
12611
12612 nsRect storedOverflow = aFrame->InkOverflowRect();
12613 DR_state->PrettyUC(storedOverflow.x, x, 16);
12614 DR_state->PrettyUC(storedOverflow.y, y, 16);
12615 DR_state->PrettyUC(storedOverflow.width, width, 16);
12616 DR_state->PrettyUC(storedOverflow.height, height, 16);
12617 printf(" vis-sto=(%s,%s) %s x %s", x, y, width, height);
12618
12619 DR_state->PrettyUC(aMetrics.ScrollableOverflow().x, x, 16);
12620 DR_state->PrettyUC(aMetrics.ScrollableOverflow().y, y, 16);
12621 DR_state->PrettyUC(aMetrics.ScrollableOverflow().width, width, 16);
12622 DR_state->PrettyUC(aMetrics.ScrollableOverflow().height, height, 16);
12623 printf(" scr-o=(%s,%s) %s x %s", x, y, width, height);
12624
12625 storedOverflow = aFrame->ScrollableOverflowRect();
12626 DR_state->PrettyUC(storedOverflow.x, x, 16);
12627 DR_state->PrettyUC(storedOverflow.y, y, 16);
12628 DR_state->PrettyUC(storedOverflow.width, width, 16);
12629 DR_state->PrettyUC(storedOverflow.height, height, 16);
12630 printf(" scr-sto=(%s,%s) %s x %s", x, y, width, height);
12631 }
12632 printf("\n");
12633 if (DR_state->mDisplayPixelErrors) {
12634 int32_t d2a = aPresContext->AppUnitsPerDevPixel();
12635 CheckPixelError(aMetrics.Width(), d2a);
12636 CheckPixelError(aMetrics.Height(), d2a);
12637 }
12638 }
12639 DR_state->DeleteTreeNode(*treeNode);
12640 }
12641
DisplayLayoutExit(nsIFrame * aFrame,void * aFrameTreeNode)12642 void nsIFrame::DisplayLayoutExit(nsIFrame* aFrame, void* aFrameTreeNode) {
12643 if (!DR_state->mActive) return;
12644
12645 NS_ASSERTION(aFrame, "non-null frame required");
12646 if (!aFrameTreeNode) return;
12647
12648 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
12649 if (treeNode->mDisplay) {
12650 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12651 nsRect rect = aFrame->GetRect();
12652 printf("XULLayout=%d,%d,%d,%d\n", rect.x, rect.y, rect.width, rect.height);
12653 }
12654 DR_state->DeleteTreeNode(*treeNode);
12655 }
12656
DisplayIntrinsicISizeExit(nsIFrame * aFrame,const char * aType,nscoord aResult,void * aFrameTreeNode)12657 void nsIFrame::DisplayIntrinsicISizeExit(nsIFrame* aFrame, const char* aType,
12658 nscoord aResult,
12659 void* aFrameTreeNode) {
12660 if (!DR_state->mActive) return;
12661
12662 NS_ASSERTION(aFrame, "non-null frame required");
12663 if (!aFrameTreeNode) return;
12664
12665 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
12666 if (treeNode->mDisplay) {
12667 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12668 char iSize[16];
12669 DR_state->PrettyUC(aResult, iSize, 16);
12670 printf("Get%sISize=%s\n", aType, iSize);
12671 }
12672 DR_state->DeleteTreeNode(*treeNode);
12673 }
12674
DisplayIntrinsicSizeExit(nsIFrame * aFrame,const char * aType,nsSize aResult,void * aFrameTreeNode)12675 void nsIFrame::DisplayIntrinsicSizeExit(nsIFrame* aFrame, const char* aType,
12676 nsSize aResult, void* aFrameTreeNode) {
12677 if (!DR_state->mActive) return;
12678
12679 NS_ASSERTION(aFrame, "non-null frame required");
12680 if (!aFrameTreeNode) return;
12681
12682 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
12683 if (treeNode->mDisplay) {
12684 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12685
12686 char width[16];
12687 char height[16];
12688 DR_state->PrettyUC(aResult.width, width, 16);
12689 DR_state->PrettyUC(aResult.height, height, 16);
12690 printf("Get%sSize=%s,%s\n", aType, width, height);
12691 }
12692 DR_state->DeleteTreeNode(*treeNode);
12693 }
12694
12695 /* static */
DisplayReflowStartup()12696 void nsIFrame::DisplayReflowStartup() { DR_state = new DR_State(); }
12697
12698 /* static */
DisplayReflowShutdown()12699 void nsIFrame::DisplayReflowShutdown() {
12700 delete DR_state;
12701 DR_state = nullptr;
12702 }
12703
Change() const12704 void DR_cookie::Change() const {
12705 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)mValue;
12706 if (treeNode && treeNode->mDisplay) {
12707 DisplayReflowEnterPrint(mPresContext, mFrame, mReflowInput, *treeNode,
12708 true);
12709 }
12710 }
12711
12712 /* static */
DisplayInitConstraintsEnter(nsIFrame * aFrame,ReflowInput * aState,nscoord aContainingBlockWidth,nscoord aContainingBlockHeight,const nsMargin * aBorder,const nsMargin * aPadding)12713 void* ReflowInput::DisplayInitConstraintsEnter(nsIFrame* aFrame,
12714 ReflowInput* aState,
12715 nscoord aContainingBlockWidth,
12716 nscoord aContainingBlockHeight,
12717 const nsMargin* aBorder,
12718 const nsMargin* aPadding) {
12719 MOZ_ASSERT(aFrame, "non-null frame required");
12720 MOZ_ASSERT(aState, "non-null state required");
12721
12722 if (!DR_state->mInited) DR_state->Init();
12723 if (!DR_state->mActive) return nullptr;
12724
12725 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, aState);
12726 if (treeNode && treeNode->mDisplay) {
12727 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12728
12729 printf("InitConstraints parent=%p", (void*)aState->mParentReflowInput);
12730
12731 char width[16];
12732 char height[16];
12733
12734 DR_state->PrettyUC(aContainingBlockWidth, width, 16);
12735 DR_state->PrettyUC(aContainingBlockHeight, height, 16);
12736 printf(" cb=%s,%s", width, height);
12737
12738 DR_state->PrettyUC(aState->AvailableWidth(), width, 16);
12739 DR_state->PrettyUC(aState->AvailableHeight(), height, 16);
12740 printf(" as=%s,%s", width, height);
12741
12742 DR_state->PrintMargin("b", aBorder);
12743 DR_state->PrintMargin("p", aPadding);
12744 putchar('\n');
12745 }
12746 return treeNode;
12747 }
12748
12749 /* static */
DisplayInitConstraintsExit(nsIFrame * aFrame,ReflowInput * aState,void * aValue)12750 void ReflowInput::DisplayInitConstraintsExit(nsIFrame* aFrame,
12751 ReflowInput* aState,
12752 void* aValue) {
12753 MOZ_ASSERT(aFrame, "non-null frame required");
12754 MOZ_ASSERT(aState, "non-null state required");
12755
12756 if (!DR_state->mActive) return;
12757 if (!aValue) return;
12758
12759 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue;
12760 if (treeNode->mDisplay) {
12761 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12762 char cmiw[16], cw[16], cmxw[16], cmih[16], ch[16], cmxh[16];
12763 DR_state->PrettyUC(aState->ComputedMinWidth(), cmiw, 16);
12764 DR_state->PrettyUC(aState->ComputedWidth(), cw, 16);
12765 DR_state->PrettyUC(aState->ComputedMaxWidth(), cmxw, 16);
12766 DR_state->PrettyUC(aState->ComputedMinHeight(), cmih, 16);
12767 DR_state->PrettyUC(aState->ComputedHeight(), ch, 16);
12768 DR_state->PrettyUC(aState->ComputedMaxHeight(), cmxh, 16);
12769 printf("InitConstraints= cw=(%s <= %s <= %s) ch=(%s <= %s <= %s)", cmiw, cw,
12770 cmxw, cmih, ch, cmxh);
12771 const nsMargin m = aState->ComputedPhysicalOffsets();
12772 DR_state->PrintMargin("co", &m);
12773 putchar('\n');
12774 }
12775 DR_state->DeleteTreeNode(*treeNode);
12776 }
12777
12778 /* static */
DisplayInitOffsetsEnter(nsIFrame * aFrame,SizeComputationInput * aState,nscoord aPercentBasis,WritingMode aCBWritingMode,const nsMargin * aBorder,const nsMargin * aPadding)12779 void* SizeComputationInput::DisplayInitOffsetsEnter(
12780 nsIFrame* aFrame, SizeComputationInput* aState, nscoord aPercentBasis,
12781 WritingMode aCBWritingMode, const nsMargin* aBorder,
12782 const nsMargin* aPadding) {
12783 MOZ_ASSERT(aFrame, "non-null frame required");
12784 MOZ_ASSERT(aState, "non-null state required");
12785
12786 if (!DR_state->mInited) DR_state->Init();
12787 if (!DR_state->mActive) return nullptr;
12788
12789 // aState is not necessarily a ReflowInput
12790 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr);
12791 if (treeNode && treeNode->mDisplay) {
12792 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12793
12794 char pctBasisStr[16];
12795 DR_state->PrettyUC(aPercentBasis, pctBasisStr, 16);
12796 printf("InitOffsets pct_basis=%s", pctBasisStr);
12797
12798 DR_state->PrintMargin("b", aBorder);
12799 DR_state->PrintMargin("p", aPadding);
12800 putchar('\n');
12801 }
12802 return treeNode;
12803 }
12804
12805 /* static */
DisplayInitOffsetsExit(nsIFrame * aFrame,SizeComputationInput * aState,void * aValue)12806 void SizeComputationInput::DisplayInitOffsetsExit(nsIFrame* aFrame,
12807 SizeComputationInput* aState,
12808 void* aValue) {
12809 MOZ_ASSERT(aFrame, "non-null frame required");
12810 MOZ_ASSERT(aState, "non-null state required");
12811
12812 if (!DR_state->mActive) return;
12813 if (!aValue) return;
12814
12815 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue;
12816 if (treeNode->mDisplay) {
12817 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12818 printf("InitOffsets=");
12819 const auto m = aState->ComputedPhysicalMargin();
12820 DR_state->PrintMargin("m", &m);
12821 const auto p = aState->ComputedPhysicalPadding();
12822 DR_state->PrintMargin("p", &p);
12823 const auto bp = aState->ComputedPhysicalBorderPadding();
12824 DR_state->PrintMargin("b+p", &bp);
12825 putchar('\n');
12826 }
12827 DR_state->DeleteTreeNode(*treeNode);
12828 }
12829
12830 // End Display Reflow
12831
12832 // Validation of SideIsVertical.
12833 # define CASE(side, result) \
12834 static_assert(SideIsVertical(side) == result, "SideIsVertical is wrong")
12835 CASE(eSideTop, false);
12836 CASE(eSideRight, true);
12837 CASE(eSideBottom, false);
12838 CASE(eSideLeft, true);
12839 # undef CASE
12840
12841 // Validation of HalfCornerIsX.
12842 # define CASE(corner, result) \
12843 static_assert(HalfCornerIsX(corner) == result, "HalfCornerIsX is wrong")
12844 CASE(eCornerTopLeftX, true);
12845 CASE(eCornerTopLeftY, false);
12846 CASE(eCornerTopRightX, true);
12847 CASE(eCornerTopRightY, false);
12848 CASE(eCornerBottomRightX, true);
12849 CASE(eCornerBottomRightY, false);
12850 CASE(eCornerBottomLeftX, true);
12851 CASE(eCornerBottomLeftY, false);
12852 # undef CASE
12853
12854 // Validation of HalfToFullCorner.
12855 # define CASE(corner, result) \
12856 static_assert(HalfToFullCorner(corner) == result, \
12857 "HalfToFullCorner is " \
12858 "wrong")
12859 CASE(eCornerTopLeftX, eCornerTopLeft);
12860 CASE(eCornerTopLeftY, eCornerTopLeft);
12861 CASE(eCornerTopRightX, eCornerTopRight);
12862 CASE(eCornerTopRightY, eCornerTopRight);
12863 CASE(eCornerBottomRightX, eCornerBottomRight);
12864 CASE(eCornerBottomRightY, eCornerBottomRight);
12865 CASE(eCornerBottomLeftX, eCornerBottomLeft);
12866 CASE(eCornerBottomLeftY, eCornerBottomLeft);
12867 # undef CASE
12868
12869 // Validation of FullToHalfCorner.
12870 # define CASE(corner, vert, result) \
12871 static_assert(FullToHalfCorner(corner, vert) == result, \
12872 "FullToHalfCorner is wrong")
12873 CASE(eCornerTopLeft, false, eCornerTopLeftX);
12874 CASE(eCornerTopLeft, true, eCornerTopLeftY);
12875 CASE(eCornerTopRight, false, eCornerTopRightX);
12876 CASE(eCornerTopRight, true, eCornerTopRightY);
12877 CASE(eCornerBottomRight, false, eCornerBottomRightX);
12878 CASE(eCornerBottomRight, true, eCornerBottomRightY);
12879 CASE(eCornerBottomLeft, false, eCornerBottomLeftX);
12880 CASE(eCornerBottomLeft, true, eCornerBottomLeftY);
12881 # undef CASE
12882
12883 // Validation of SideToFullCorner.
12884 # define CASE(side, second, result) \
12885 static_assert(SideToFullCorner(side, second) == result, \
12886 "SideToFullCorner is wrong")
12887 CASE(eSideTop, false, eCornerTopLeft);
12888 CASE(eSideTop, true, eCornerTopRight);
12889
12890 CASE(eSideRight, false, eCornerTopRight);
12891 CASE(eSideRight, true, eCornerBottomRight);
12892
12893 CASE(eSideBottom, false, eCornerBottomRight);
12894 CASE(eSideBottom, true, eCornerBottomLeft);
12895
12896 CASE(eSideLeft, false, eCornerBottomLeft);
12897 CASE(eSideLeft, true, eCornerTopLeft);
12898 # undef CASE
12899
12900 // Validation of SideToHalfCorner.
12901 # define CASE(side, second, parallel, result) \
12902 static_assert(SideToHalfCorner(side, second, parallel) == result, \
12903 "SideToHalfCorner is wrong")
12904 CASE(eSideTop, false, true, eCornerTopLeftX);
12905 CASE(eSideTop, false, false, eCornerTopLeftY);
12906 CASE(eSideTop, true, true, eCornerTopRightX);
12907 CASE(eSideTop, true, false, eCornerTopRightY);
12908
12909 CASE(eSideRight, false, false, eCornerTopRightX);
12910 CASE(eSideRight, false, true, eCornerTopRightY);
12911 CASE(eSideRight, true, false, eCornerBottomRightX);
12912 CASE(eSideRight, true, true, eCornerBottomRightY);
12913
12914 CASE(eSideBottom, false, true, eCornerBottomRightX);
12915 CASE(eSideBottom, false, false, eCornerBottomRightY);
12916 CASE(eSideBottom, true, true, eCornerBottomLeftX);
12917 CASE(eSideBottom, true, false, eCornerBottomLeftY);
12918
12919 CASE(eSideLeft, false, false, eCornerBottomLeftX);
12920 CASE(eSideLeft, false, true, eCornerBottomLeftY);
12921 CASE(eSideLeft, true, false, eCornerTopLeftX);
12922 CASE(eSideLeft, true, true, eCornerTopLeftY);
12923 # undef CASE
12924
12925 #endif
12926