1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 /* base class of all rendering objects */
8
9 #include "nsFrame.h"
10
11 #include <stdarg.h>
12 #include <algorithm>
13
14 #include "gfx2DGlue.h"
15 #include "gfxUtils.h"
16 #include "mozilla/Attributes.h"
17 #include "mozilla/ComputedStyle.h"
18 #include "mozilla/DebugOnly.h"
19 #include "mozilla/dom/ElementInlines.h"
20 #include "mozilla/dom/ImageTracker.h"
21 #include "mozilla/dom/Selection.h"
22 #include "mozilla/gfx/2D.h"
23 #include "mozilla/gfx/gfxVars.h"
24 #include "mozilla/gfx/PathHelpers.h"
25 #include "mozilla/PresShell.h"
26 #include "mozilla/PresShellInlines.h"
27 #include "mozilla/Sprintf.h"
28 #include "mozilla/StaticPrefs_layout.h"
29 #include "mozilla/ToString.h"
30 #include "mozilla/ViewportUtils.h"
31
32 #include "nsCOMPtr.h"
33 #include "nsFlexContainerFrame.h"
34 #include "nsFrameList.h"
35 #include "nsPlaceholderFrame.h"
36 #include "nsPluginFrame.h"
37 #include "nsIBaseWindow.h"
38 #include "nsIContent.h"
39 #include "nsIContentInlines.h"
40 #include "nsContentUtils.h"
41 #include "nsCSSFrameConstructor.h"
42 #include "nsCSSProps.h"
43 #include "nsCSSPseudoElements.h"
44 #include "nsCSSRendering.h"
45 #include "nsAtom.h"
46 #include "nsString.h"
47 #include "nsReadableUtils.h"
48 #include "nsTableWrapperFrame.h"
49 #include "nsView.h"
50 #include "nsViewManager.h"
51 #include "nsIScrollableFrame.h"
52 #include "nsPresContext.h"
53 #include "nsPresContextInlines.h"
54 #include "nsStyleConsts.h"
55 #include "mozilla/Logging.h"
56 #include "nsLayoutUtils.h"
57 #include "LayoutLogging.h"
58 #include "mozilla/RestyleManager.h"
59 #include "nsImageFrame.h"
60 #include "nsInlineFrame.h"
61 #include "nsFrameSelection.h"
62 #include "nsGkAtoms.h"
63 #include "nsCSSAnonBoxes.h"
64 #include "nsCSSClipPathInstance.h"
65 #include "nsCanvasFrame.h"
66
67 #include "nsFrameTraversal.h"
68 #include "nsRange.h"
69 #include "nsITextControlFrame.h"
70 #include "nsNameSpaceManager.h"
71 #include "nsIPercentBSizeObserver.h"
72 #include "nsStyleStructInlines.h"
73 #include "FrameLayerBuilder.h"
74 #include "ImageLayers.h"
75
76 #include "nsBidiPresUtils.h"
77 #include "RubyUtils.h"
78 #include "TextOverflow.h"
79 #include "nsAnimationManager.h"
80
81 // For triple-click pref
82 #include "imgIRequest.h"
83 #include "nsError.h"
84 #include "nsContainerFrame.h"
85 #include "nsBoxLayoutState.h"
86 #include "nsBlockFrame.h"
87 #include "nsDisplayList.h"
88 #include "nsSVGIntegrationUtils.h"
89 #include "SVGObserverUtils.h"
90 #include "nsSVGMaskFrame.h"
91 #include "nsChangeHint.h"
92 #include "nsDeckFrame.h"
93 #include "nsSubDocumentFrame.h"
94 #include "SVGTextFrame.h"
95 #include "RetainedDisplayListBuilder.h"
96
97 #include "gfxContext.h"
98 #include "nsAbsoluteContainingBlock.h"
99 #include "StickyScrollContainer.h"
100 #include "nsFontInflationData.h"
101 #include "nsRegion.h"
102 #include "nsIFrameInlines.h"
103 #include "nsStyleChangeList.h"
104 #include "nsWindowSizes.h"
105
106 #include "mozilla/AsyncEventDispatcher.h"
107 #include "mozilla/EffectCompositor.h"
108 #include "mozilla/EffectSet.h"
109 #include "mozilla/EventListenerManager.h"
110 #include "mozilla/EventStateManager.h"
111 #include "mozilla/EventStates.h"
112 #include "mozilla/Preferences.h"
113 #include "mozilla/LookAndFeel.h"
114 #include "mozilla/MouseEvents.h"
115 #include "mozilla/ServoStyleSet.h"
116 #include "mozilla/ServoStyleSetInlines.h"
117 #include "mozilla/css/ImageLoader.h"
118 #include "mozilla/dom/HTMLBodyElement.h"
119 #include "mozilla/dom/SVGPathData.h"
120 #include "mozilla/dom/TouchEvent.h"
121 #include "mozilla/gfx/Tools.h"
122 #include "mozilla/layers/WebRenderUserData.h"
123 #include "mozilla/layout/ScrollAnchorContainer.h"
124 #include "nsPrintfCString.h"
125 #include "ActiveLayerTracker.h"
126
127 #include "nsITheme.h"
128
129 using namespace mozilla;
130 using namespace mozilla::css;
131 using namespace mozilla::dom;
132 using namespace mozilla::gfx;
133 using namespace mozilla::layers;
134 using namespace mozilla::layout;
135 typedef nsAbsoluteContainingBlock::AbsPosReflowFlags AbsPosReflowFlags;
136 using nsStyleTransformMatrix::TransformReferenceBox;
137
138 const mozilla::LayoutFrameType nsIFrame::sLayoutFrameTypes[
139 #define FRAME_ID(...) 1 +
140 #define ABSTRACT_FRAME_ID(...)
141 #include "mozilla/FrameIdList.h"
142 #undef FRAME_ID
143 #undef ABSTRACT_FRAME_ID
144 0] = {
145 #define FRAME_ID(class_, type_, ...) mozilla::LayoutFrameType::type_,
146 #define ABSTRACT_FRAME_ID(...)
147 #include "mozilla/FrameIdList.h"
148 #undef FRAME_ID
149 #undef ABSTRACT_FRAME_ID
150 };
151
152 const nsIFrame::FrameClassBits nsIFrame::sFrameClassBits[
153 #define FRAME_ID(...) 1 +
154 #define ABSTRACT_FRAME_ID(...)
155 #include "mozilla/FrameIdList.h"
156 #undef FRAME_ID
157 #undef ABSTRACT_FRAME_ID
158 0] = {
159 #define Leaf eFrameClassBitsLeaf
160 #define NotLeaf eFrameClassBitsNone
161 #define DynamicLeaf eFrameClassBitsDynamicLeaf
162 #define FRAME_ID(class_, type_, leaf_, ...) leaf_,
163 #define ABSTRACT_FRAME_ID(...)
164 #include "mozilla/FrameIdList.h"
165 #undef Leaf
166 #undef NotLeaf
167 #undef DynamicLeaf
168 #undef FRAME_ID
169 #undef ABSTRACT_FRAME_ID
170 };
171
172 // Struct containing cached metrics for box-wrapped frames.
173 struct nsBoxLayoutMetrics {
174 nsSize mPrefSize;
175 nsSize mMinSize;
176 nsSize mMaxSize;
177
178 nsSize mBlockMinSize;
179 nsSize mBlockPrefSize;
180 nscoord mBlockAscent;
181
182 nscoord mFlex;
183 nscoord mAscent;
184
185 nsSize mLastSize;
186 };
187
188 struct nsContentAndOffset {
189 nsIContent* mContent = nullptr;
190 int32_t mOffset = 0;
191 };
192
193 // Some Misc #defines
194 #define SELECTION_DEBUG 0
195 #define FORCE_SELECTION_UPDATE 1
196 #define CALC_DEBUG 0
197
198 // This is faster than nsBidiPresUtils::IsFrameInParagraphDirection,
199 // because it uses the frame pointer passed in without drilling down to
200 // the leaf frame.
IsReversedDirectionFrame(nsIFrame * aFrame)201 static bool IsReversedDirectionFrame(nsIFrame* aFrame) {
202 FrameBidiData bidiData = aFrame->GetBidiData();
203 return !IS_SAME_DIRECTION(bidiData.embeddingLevel, bidiData.baseLevel);
204 }
205
206 #include "nsILineIterator.h"
207 #include "prenv.h"
208
NS_DECLARE_FRAME_PROPERTY_DELETABLE(BoxMetricsProperty,nsBoxLayoutMetrics)209 NS_DECLARE_FRAME_PROPERTY_DELETABLE(BoxMetricsProperty, nsBoxLayoutMetrics)
210
211 static void InitBoxMetrics(nsIFrame* aFrame, bool aClear) {
212 if (aClear) {
213 aFrame->RemoveProperty(BoxMetricsProperty());
214 }
215
216 nsBoxLayoutMetrics* metrics = new nsBoxLayoutMetrics();
217 aFrame->SetProperty(BoxMetricsProperty(), metrics);
218
219 static_cast<nsFrame*>(aFrame)->nsFrame::MarkIntrinsicISizesDirty();
220 metrics->mBlockAscent = 0;
221 metrics->mLastSize.SizeTo(0, 0);
222 }
223
224 // Utility function to set a nsRect-valued property table entry on aFrame,
225 // reusing the existing storage if the property happens to be already set.
226 template <typename T>
SetOrUpdateRectValuedProperty(nsIFrame * aFrame,FrameProperties::Descriptor<T> aProperty,const nsRect & aNewValue)227 static void SetOrUpdateRectValuedProperty(
228 nsIFrame* aFrame, FrameProperties::Descriptor<T> aProperty,
229 const nsRect& aNewValue) {
230 bool found;
231 nsRect* rectStorage = aFrame->GetProperty(aProperty, &found);
232 if (!found) {
233 rectStorage = new nsRect(aNewValue);
234 aFrame->AddProperty(aProperty, rectStorage);
235 } else {
236 *rectStorage = aNewValue;
237 }
238 }
239
IsXULBoxWrapped(const nsIFrame * aFrame)240 static bool IsXULBoxWrapped(const nsIFrame* aFrame) {
241 return aFrame->GetParent() && aFrame->GetParent()->IsXULBoxFrame() &&
242 !aFrame->IsXULBoxFrame();
243 }
244
UpdateTruncated(const ReflowInput & aReflowInput,const ReflowOutput & aMetrics)245 void nsReflowStatus::UpdateTruncated(const ReflowInput& aReflowInput,
246 const ReflowOutput& aMetrics) {
247 const WritingMode containerWM = aMetrics.GetWritingMode();
248 if (aReflowInput.GetWritingMode().IsOrthogonalTo(containerWM)) {
249 // Orthogonal flows are always reflowed with an unconstrained dimension,
250 // so should never end up truncated (see ReflowInput::Init()).
251 mTruncated = false;
252 } else if (aReflowInput.AvailableBSize() != NS_UNCONSTRAINEDSIZE &&
253 aReflowInput.AvailableBSize() < aMetrics.BSize(containerWM) &&
254 !aReflowInput.mFlags.mIsTopOfPage) {
255 mTruncated = true;
256 } else {
257 mTruncated = false;
258 }
259 }
260
261 /* static */
DestroyAnonymousContent(nsPresContext * aPresContext,already_AddRefed<nsIContent> && aContent)262 void nsIFrame::DestroyAnonymousContent(
263 nsPresContext* aPresContext, already_AddRefed<nsIContent>&& aContent) {
264 if (nsCOMPtr<nsIContent> content = aContent) {
265 aPresContext->EventStateManager()->NativeAnonymousContentRemoved(content);
266 aPresContext->PresShell()->NativeAnonymousContentRemoved(content);
267 content->UnbindFromTree();
268 }
269 }
270
271 // Formerly the nsIFrameDebug interface
272
operator <<(std::ostream & aStream,const nsReflowStatus & aStatus)273 std::ostream& operator<<(std::ostream& aStream, const nsReflowStatus& aStatus) {
274 char complete = 'Y';
275 if (aStatus.IsIncomplete()) {
276 complete = 'N';
277 } else if (aStatus.IsOverflowIncomplete()) {
278 complete = 'O';
279 }
280
281 char brk = 'N';
282 if (aStatus.IsInlineBreakBefore()) {
283 brk = 'B';
284 } else if (aStatus.IsInlineBreakAfter()) {
285 brk = 'A';
286 }
287
288 aStream << "["
289 << "Complete=" << complete << ","
290 << "NIF=" << (aStatus.NextInFlowNeedsReflow() ? 'Y' : 'N') << ","
291 << "Truncated=" << (aStatus.IsTruncated() ? 'Y' : 'N') << ","
292 << "Break=" << brk << ","
293 << "FirstLetter=" << (aStatus.FirstLetterComplete() ? 'Y' : 'N')
294 << "]";
295 return aStream;
296 }
297
298 #ifdef DEBUG
299 static bool gShowFrameBorders = false;
300
ShowFrameBorders(bool aEnable)301 void nsFrame::ShowFrameBorders(bool aEnable) { gShowFrameBorders = aEnable; }
302
GetShowFrameBorders()303 bool nsFrame::GetShowFrameBorders() { return gShowFrameBorders; }
304
305 static bool gShowEventTargetFrameBorder = false;
306
ShowEventTargetFrameBorder(bool aEnable)307 void nsFrame::ShowEventTargetFrameBorder(bool aEnable) {
308 gShowEventTargetFrameBorder = aEnable;
309 }
310
GetShowEventTargetFrameBorder()311 bool nsFrame::GetShowEventTargetFrameBorder() {
312 return gShowEventTargetFrameBorder;
313 }
314
315 /**
316 * Note: the log module is created during library initialization which
317 * means that you cannot perform logging before then.
318 */
319 mozilla::LazyLogModule nsFrame::sFrameLogModule("frame");
320
321 #endif
322
NS_DECLARE_FRAME_PROPERTY_DELETABLE(AbsoluteContainingBlockProperty,nsAbsoluteContainingBlock)323 NS_DECLARE_FRAME_PROPERTY_DELETABLE(AbsoluteContainingBlockProperty,
324 nsAbsoluteContainingBlock)
325
326 bool nsIFrame::HasAbsolutelyPositionedChildren() const {
327 return IsAbsoluteContainer() &&
328 GetAbsoluteContainingBlock()->HasAbsoluteFrames();
329 }
330
GetAbsoluteContainingBlock() const331 nsAbsoluteContainingBlock* nsIFrame::GetAbsoluteContainingBlock() const {
332 NS_ASSERTION(IsAbsoluteContainer(),
333 "The frame is not marked as an abspos container correctly");
334 nsAbsoluteContainingBlock* absCB =
335 GetProperty(AbsoluteContainingBlockProperty());
336 NS_ASSERTION(absCB,
337 "The frame is marked as an abspos container but doesn't have "
338 "the property");
339 return absCB;
340 }
341
MarkAsAbsoluteContainingBlock()342 void nsIFrame::MarkAsAbsoluteContainingBlock() {
343 MOZ_ASSERT(GetStateBits() & NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
344 NS_ASSERTION(!GetProperty(AbsoluteContainingBlockProperty()),
345 "Already has an abs-pos containing block property?");
346 NS_ASSERTION(!HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN),
347 "Already has NS_FRAME_HAS_ABSPOS_CHILDREN state bit?");
348 AddStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN);
349 SetProperty(AbsoluteContainingBlockProperty(),
350 new nsAbsoluteContainingBlock(GetAbsoluteListID()));
351 }
352
MarkAsNotAbsoluteContainingBlock()353 void nsIFrame::MarkAsNotAbsoluteContainingBlock() {
354 NS_ASSERTION(!HasAbsolutelyPositionedChildren(), "Think of the children!");
355 NS_ASSERTION(GetProperty(AbsoluteContainingBlockProperty()),
356 "Should have an abs-pos containing block property");
357 NS_ASSERTION(HasAnyStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN),
358 "Should have NS_FRAME_HAS_ABSPOS_CHILDREN state bit");
359 MOZ_ASSERT(HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN));
360 RemoveStateBits(NS_FRAME_HAS_ABSPOS_CHILDREN);
361 RemoveProperty(AbsoluteContainingBlockProperty());
362 }
363
CheckAndClearPaintedState()364 bool nsIFrame::CheckAndClearPaintedState() {
365 bool result = (GetStateBits() & NS_FRAME_PAINTED_THEBES);
366 RemoveStateBits(NS_FRAME_PAINTED_THEBES);
367
368 for (const auto& childList : ChildLists()) {
369 for (nsIFrame* child : childList.mList) {
370 if (child->CheckAndClearPaintedState()) {
371 result = true;
372 }
373 }
374 }
375 return result;
376 }
377
CheckAndClearDisplayListState()378 bool nsIFrame::CheckAndClearDisplayListState() {
379 bool result = BuiltDisplayList();
380 SetBuiltDisplayList(false);
381
382 for (const auto& childList : ChildLists()) {
383 for (nsIFrame* child : childList.mList) {
384 if (child->CheckAndClearDisplayListState()) {
385 result = true;
386 }
387 }
388 }
389 return result;
390 }
391
IsVisibleConsideringAncestors(uint32_t aFlags) const392 bool nsIFrame::IsVisibleConsideringAncestors(uint32_t aFlags) const {
393 if (!StyleVisibility()->IsVisible()) {
394 return false;
395 }
396
397 if (PresShell()->IsUnderHiddenEmbedderElement()) {
398 return false;
399 }
400
401 const nsIFrame* frame = this;
402 while (frame) {
403 nsView* view = frame->GetView();
404 if (view && view->GetVisibility() == nsViewVisibility_kHide) return false;
405
406 nsIFrame* parent = frame->GetParent();
407 nsDeckFrame* deck = do_QueryFrame(parent);
408 if (deck) {
409 if (deck->GetSelectedBox() != frame) return false;
410 }
411
412 if (parent) {
413 frame = parent;
414 } else {
415 parent = nsLayoutUtils::GetCrossDocParentFrame(frame);
416 if (!parent) break;
417
418 if ((aFlags & nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY) == 0 &&
419 parent->PresContext()->IsChrome() &&
420 !frame->PresContext()->IsChrome()) {
421 break;
422 }
423
424 frame = parent;
425 }
426 }
427
428 return true;
429 }
430
FindCloserFrameForSelection(const nsPoint & aPoint,FrameWithDistance * aCurrentBestFrame)431 void nsIFrame::FindCloserFrameForSelection(
432 const nsPoint& aPoint, FrameWithDistance* aCurrentBestFrame) {
433 if (nsLayoutUtils::PointIsCloserToRect(aPoint, mRect,
434 aCurrentBestFrame->mXDistance,
435 aCurrentBestFrame->mYDistance)) {
436 aCurrentBestFrame->mFrame = this;
437 }
438 }
439
ContentStatesChanged(mozilla::EventStates aStates)440 void nsIFrame::ContentStatesChanged(mozilla::EventStates aStates) {}
441
AutoWeakFrame(const WeakFrame & aOther)442 AutoWeakFrame::AutoWeakFrame(const WeakFrame& aOther)
443 : mPrev(nullptr), mFrame(nullptr) {
444 Init(aOther.GetFrame());
445 }
446
Init(nsIFrame * aFrame)447 void AutoWeakFrame::Init(nsIFrame* aFrame) {
448 Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
449 mFrame = aFrame;
450 if (mFrame) {
451 mozilla::PresShell* presShell = mFrame->PresContext()->GetPresShell();
452 NS_WARNING_ASSERTION(presShell, "Null PresShell in AutoWeakFrame!");
453 if (presShell) {
454 presShell->AddAutoWeakFrame(this);
455 } else {
456 mFrame = nullptr;
457 }
458 }
459 }
460
Init(nsIFrame * aFrame)461 void WeakFrame::Init(nsIFrame* aFrame) {
462 Clear(mFrame ? mFrame->PresContext()->GetPresShell() : nullptr);
463 mFrame = aFrame;
464 if (mFrame) {
465 mozilla::PresShell* presShell = mFrame->PresContext()->GetPresShell();
466 MOZ_ASSERT(presShell, "Null PresShell in WeakFrame!");
467 if (presShell) {
468 presShell->AddWeakFrame(this);
469 } else {
470 mFrame = nullptr;
471 }
472 }
473 }
474
NS_NewEmptyFrame(PresShell * aPresShell,ComputedStyle * aStyle)475 nsIFrame* NS_NewEmptyFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
476 return new (aPresShell) nsFrame(aStyle, aPresShell->GetPresContext());
477 }
478
nsFrame(ComputedStyle * aStyle,nsPresContext * aPresContext,ClassID aID)479 nsFrame::nsFrame(ComputedStyle* aStyle, nsPresContext* aPresContext,
480 ClassID aID)
481 : nsIFrame(aStyle, aPresContext, aID) {
482 MOZ_COUNT_CTOR(nsFrame);
483 }
484
~nsFrame()485 nsFrame::~nsFrame() {
486 MOZ_COUNT_DTOR(nsFrame);
487
488 MOZ_ASSERT(GetVisibility() != Visibility::ApproximatelyVisible,
489 "Visible nsFrame is being destroyed");
490 }
491
NS_IMPL_FRAMEARENA_HELPERS(nsFrame)492 NS_IMPL_FRAMEARENA_HELPERS(nsFrame)
493
494 // Dummy operator delete. Will never be called, but must be defined
495 // to satisfy some C++ ABIs.
496 void nsFrame::operator delete(void*, size_t) {
497 MOZ_CRASH("nsFrame::operator delete should never be called");
498 }
499
500 NS_QUERYFRAME_HEAD(nsFrame)
NS_QUERYFRAME_ENTRY(nsIFrame)501 NS_QUERYFRAME_ENTRY(nsIFrame)
502 NS_QUERYFRAME_TAIL_INHERITANCE_ROOT
503
504 /////////////////////////////////////////////////////////////////////////////
505 // nsIFrame
506
507 static bool IsFontSizeInflationContainer(nsIFrame* aFrame,
508 const nsStyleDisplay* aStyleDisplay) {
509 /*
510 * Font size inflation is built around the idea that we're inflating
511 * the fonts for a pan-and-zoom UI so that when the user scales up a
512 * block or other container to fill the width of the device, the fonts
513 * will be readable. To do this, we need to pick what counts as a
514 * container.
515 *
516 * From a code perspective, the only hard requirement is that frames
517 * that are line participants
518 * (nsIFrame::IsFrameOfType(nsIFrame::eLineParticipant)) are never
519 * containers, since line layout assumes that the inflation is
520 * consistent within a line.
521 *
522 * This is not an imposition, since we obviously want a bunch of text
523 * (possibly with inline elements) flowing within a block to count the
524 * block (or higher) as its container.
525 *
526 * We also want form controls, including the text in the anonymous
527 * content inside of them, to match each other and the text next to
528 * them, so they and their anonymous content should also not be a
529 * container.
530 *
531 * However, because we can't reliably compute sizes across XUL during
532 * reflow, any XUL frame with a XUL parent is always a container.
533 *
534 * There are contexts where it would be nice if some blocks didn't
535 * count as a container, so that, for example, an indented quotation
536 * didn't end up with a smaller font size. However, it's hard to
537 * distinguish these situations where we really do want the indented
538 * thing to count as a container, so we don't try, and blocks are
539 * always containers.
540 */
541
542 // The root frame should always be an inflation container.
543 if (!aFrame->GetParent()) {
544 return true;
545 }
546
547 nsIContent* content = aFrame->GetContent();
548 if (content && content->IsInNativeAnonymousSubtree()) {
549 // Native anonymous content shouldn't be a font inflation root,
550 // except for the canvas custom content container.
551 nsCanvasFrame* canvas = aFrame->PresShell()->GetCanvasFrame();
552 return canvas && canvas->GetCustomContentContainer() == content;
553 }
554
555 LayoutFrameType frameType = aFrame->Type();
556 bool isInline =
557 (nsStyleDisplay::IsInlineFlow(aFrame->GetDisplay()) ||
558 RubyUtils::IsRubyBox(frameType) ||
559 (aFrame->IsFloating() && frameType == LayoutFrameType::Letter) ||
560 // Given multiple frames for the same node, only the
561 // outer one should be considered a container.
562 // (Important, e.g., for nsSelectsAreaFrame.)
563 (aFrame->GetParent()->GetContent() == content) ||
564 (content &&
565 // Form controls shouldn't become inflation containers.
566 (content->IsAnyOfHTMLElements(nsGkAtoms::option, nsGkAtoms::optgroup,
567 nsGkAtoms::select, nsGkAtoms::input,
568 nsGkAtoms::button)))) &&
569 !(aFrame->IsXULBoxFrame() && aFrame->GetParent()->IsXULBoxFrame());
570 NS_ASSERTION(!aFrame->IsFrameOfType(nsIFrame::eLineParticipant) || isInline ||
571 // br frames and mathml frames report being line
572 // participants even when their position or display is
573 // set
574 aFrame->IsBrFrame() ||
575 aFrame->IsFrameOfType(nsIFrame::eMathML),
576 "line participants must not be containers");
577 NS_ASSERTION(!aFrame->IsBulletFrame() || isInline,
578 "bullets should not be containers");
579 return !isInline;
580 }
581
MaybeScheduleReflowSVGNonDisplayText(nsIFrame * aFrame)582 static void MaybeScheduleReflowSVGNonDisplayText(nsIFrame* aFrame) {
583 if (!nsSVGUtils::IsInSVGTextSubtree(aFrame)) {
584 return;
585 }
586
587 // We need to ensure that any non-display SVGTextFrames get reflowed when a
588 // child text frame gets new style. Thus we need to schedule a reflow in
589 // |DidSetComputedStyle|. We also need to call it from |DestroyFrom|,
590 // because otherwise we won't get notified when style changes to
591 // "display:none".
592 SVGTextFrame* svgTextFrame = static_cast<SVGTextFrame*>(
593 nsLayoutUtils::GetClosestFrameOfType(aFrame, LayoutFrameType::SVGText));
594 nsIFrame* anonBlock = svgTextFrame->PrincipalChildList().FirstChild();
595
596 // Note that we must check NS_FRAME_FIRST_REFLOW on our SVGTextFrame's
597 // anonymous block frame rather than our aFrame, since NS_FRAME_FIRST_REFLOW
598 // may be set on us if we're a new frame that has been inserted after the
599 // document's first reflow. (In which case this DidSetComputedStyle call may
600 // be happening under frame construction under a Reflow() call.)
601 if (!anonBlock || anonBlock->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
602 return;
603 }
604
605 if (!svgTextFrame->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY) ||
606 svgTextFrame->HasAnyStateBits(NS_STATE_SVG_TEXT_IN_REFLOW)) {
607 return;
608 }
609
610 svgTextFrame->ScheduleReflowSVGNonDisplayText(IntrinsicDirty::StyleChange);
611 }
612
IsPrimaryFrameOfRootOrBodyElement() const613 bool nsIFrame::IsPrimaryFrameOfRootOrBodyElement() const {
614 if (!IsPrimaryFrame()) {
615 return false;
616 }
617 nsIContent* content = GetContent();
618 Document* document = content->OwnerDoc();
619 return content == document->GetRootElement() ||
620 content == document->GetBodyElement();
621 }
622
Init(nsIContent * aContent,nsContainerFrame * aParent,nsIFrame * aPrevInFlow)623 void nsFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
624 nsIFrame* aPrevInFlow) {
625 MOZ_ASSERT(nsQueryFrame::FrameIID(mClass) == GetFrameId());
626 MOZ_ASSERT(!mContent, "Double-initing a frame?");
627 NS_ASSERTION(IsFrameOfType(eDEBUGAllFrames) && !IsFrameOfType(eDEBUGNoFrames),
628 "IsFrameOfType implementation that doesn't call base class");
629
630 mContent = aContent;
631 mParent = aParent;
632 MOZ_DIAGNOSTIC_ASSERT(!mParent || PresShell() == mParent->PresShell());
633
634 if (aPrevInFlow) {
635 mWritingMode = aPrevInFlow->GetWritingMode();
636
637 // Copy some state bits from prev-in-flow (the bits that should apply
638 // throughout a continuation chain). The bits are sorted according to their
639 // order in nsFrameStateBits.h.
640
641 // clang-format off
642 AddStateBits(aPrevInFlow->GetStateBits() &
643 (NS_FRAME_GENERATED_CONTENT |
644 NS_FRAME_OUT_OF_FLOW |
645 NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN |
646 NS_FRAME_INDEPENDENT_SELECTION |
647 NS_FRAME_PART_OF_IBSPLIT |
648 NS_FRAME_MAY_BE_TRANSFORMED |
649 NS_FRAME_HAS_MULTI_COLUMN_ANCESTOR));
650 // clang-format on
651
652 // Copy other bits in nsIFrame from prev-in-flow.
653 mHasColumnSpanSiblings = aPrevInFlow->HasColumnSpanSiblings();
654 } else {
655 PresContext()->ConstructedFrame();
656 }
657
658 if (GetParent()) {
659 if (MOZ_UNLIKELY(mContent == PresContext()->Document()->GetRootElement() &&
660 mContent == GetParent()->GetContent())) {
661 // Our content is the root element and we have the same content as our
662 // parent. That is, we are the internal anonymous frame of the root
663 // element. Copy the used mWritingMode from our parent because
664 // mDocElementContainingBlock gets its mWritingMode from <body>.
665 mWritingMode = GetParent()->GetWritingMode();
666 }
667
668 // Copy some state bits from our parent (the bits that should apply
669 // recursively throughout a subtree). The bits are sorted according to their
670 // order in nsFrameStateBits.h.
671
672 // clang-format off
673 AddStateBits(GetParent()->GetStateBits() &
674 (NS_FRAME_GENERATED_CONTENT |
675 NS_FRAME_INDEPENDENT_SELECTION |
676 NS_FRAME_IS_SVG_TEXT |
677 NS_FRAME_IN_POPUP |
678 NS_FRAME_IS_NONDISPLAY));
679 // clang-format on
680
681 if (HasAnyStateBits(NS_FRAME_IN_POPUP) && TrackingVisibility()) {
682 // Assume all frames in popups are visible.
683 IncApproximateVisibleCount();
684 }
685 }
686 if (aPrevInFlow) {
687 mMayHaveOpacityAnimation = aPrevInFlow->MayHaveOpacityAnimation();
688 mMayHaveTransformAnimation = aPrevInFlow->MayHaveTransformAnimation();
689 } else if (mContent) {
690 // It's fine to fetch the EffectSet for the style frame here because in the
691 // following code we take care of the case where animations may target
692 // a different frame.
693 EffectSet* effectSet = EffectSet::GetEffectSetForStyleFrame(this);
694 if (effectSet) {
695 mMayHaveOpacityAnimation = effectSet->MayHaveOpacityAnimation();
696
697 if (effectSet->MayHaveTransformAnimation()) {
698 // If we are the inner table frame for display:table content, then
699 // transform animations should go on our parent frame (the table wrapper
700 // frame).
701 //
702 // We do this when initializing the child frame (table inner frame),
703 // because when initializng the table wrapper frame, we don't yet have
704 // access to its children so we can't tell if we have transform
705 // animations or not.
706 if (IsFrameOfType(eSupportsCSSTransforms)) {
707 mMayHaveTransformAnimation = true;
708 AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
709 } else if (aParent && nsLayoutUtils::GetStyleFrame(aParent) == this) {
710 MOZ_ASSERT(
711 aParent->IsFrameOfType(eSupportsCSSTransforms),
712 "Style frames that don't support transforms should have parents"
713 " that do");
714 aParent->mMayHaveTransformAnimation = true;
715 aParent->AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
716 }
717 }
718 }
719 }
720
721 const nsStyleDisplay* disp = StyleDisplay();
722 if (disp->HasTransform(this)) {
723 // If 'transform' dynamically changes, RestyleManager takes care of
724 // updating this bit.
725 AddStateBits(NS_FRAME_MAY_BE_TRANSFORMED);
726 }
727
728 if (disp->IsContainLayout() && disp->IsContainSize() &&
729 // All frames that support contain:layout also support contain:size.
730 IsFrameOfType(eSupportsContainLayoutAndPaint) && !IsTableWrapperFrame()) {
731 // In general, frames that have contain:layout+size can be reflow roots.
732 // (One exception: table-wrapper frames don't work well as reflow roots,
733 // because their inner-table ReflowInput init path tries to reuse & deref
734 // the wrapper's containing block reflow input, which may be null if we
735 // initiate reflow from the table-wrapper itself.)
736 //
737 // Changes to `contain` force frame reconstructions, so this bit can be set
738 // for the whole lifetime of this frame.
739 AddStateBits(NS_FRAME_REFLOW_ROOT);
740 }
741
742 if (nsLayoutUtils::FontSizeInflationEnabled(PresContext()) ||
743 !GetParent()
744 #ifdef DEBUG
745 // We have assertions that check inflation invariants even when
746 // font size inflation is not enabled.
747 || true
748 #endif
749 ) {
750 if (IsFontSizeInflationContainer(this, disp)) {
751 AddStateBits(NS_FRAME_FONT_INFLATION_CONTAINER);
752 if (!GetParent() ||
753 // I'd use NS_FRAME_OUT_OF_FLOW, but it's not set yet.
754 disp->IsFloating(this) || disp->IsAbsolutelyPositioned(this) ||
755 GetParent()->IsFlexContainerFrame() ||
756 GetParent()->IsGridContainerFrame()) {
757 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
758 }
759 }
760 NS_ASSERTION(
761 GetParent() || (GetStateBits() & NS_FRAME_FONT_INFLATION_CONTAINER),
762 "root frame should always be a container");
763 }
764
765 if (PresShell()->AssumeAllFramesVisible() && TrackingVisibility()) {
766 IncApproximateVisibleCount();
767 }
768
769 DidSetComputedStyle(nullptr);
770
771 if (::IsXULBoxWrapped(this)) ::InitBoxMetrics(this, false);
772
773 // For a newly created frame, we need to update this frame's visibility state.
774 // Usually we update the state when the frame is restyled and has a
775 // VisibilityChange change hint but we don't generate any change hints for
776 // newly created frames.
777 // Note: We don't need to do this for placeholders since placeholders have
778 // different styles so that the styles don't have visibility:hidden even if
779 // the parent has visibility:hidden style. We also don't need to update the
780 // state when creating continuations because its visibility is the same as its
781 // prev-in-flow, and the animation code cares only primary frames.
782 if (!IsPlaceholderFrame() && !aPrevInFlow) {
783 UpdateVisibleDescendantsState();
784 }
785 }
786
DestroyFrom(nsIFrame * aDestructRoot,PostDestroyData & aPostDestroyData)787 void nsFrame::DestroyFrom(nsIFrame* aDestructRoot,
788 PostDestroyData& aPostDestroyData) {
789 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
790 "destroy called on frame while scripts not blocked");
791 NS_ASSERTION(!GetNextSibling() && !GetPrevSibling(),
792 "Frames should be removed before destruction.");
793 NS_ASSERTION(aDestructRoot, "Must specify destruct root");
794 MOZ_ASSERT(!HasAbsolutelyPositionedChildren());
795 MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT),
796 "NS_FRAME_PART_OF_IBSPLIT set on non-nsContainerFrame?");
797
798 MaybeScheduleReflowSVGNonDisplayText(this);
799
800 SVGObserverUtils::InvalidateDirectRenderingObservers(this);
801
802 if (StyleDisplay()->mPosition == StylePositionProperty::Sticky) {
803 StickyScrollContainer* ssc =
804 StickyScrollContainer::GetStickyScrollContainerForFrame(this);
805 if (ssc) {
806 ssc->RemoveFrame(this);
807 }
808 }
809
810 nsPresContext* presContext = PresContext();
811 mozilla::PresShell* presShell = presContext->GetPresShell();
812 if (mState & NS_FRAME_OUT_OF_FLOW) {
813 nsPlaceholderFrame* placeholder = GetPlaceholderFrame();
814 NS_ASSERTION(
815 !placeholder || (aDestructRoot != this),
816 "Don't call Destroy() on OOFs, call Destroy() on the placeholder.");
817 NS_ASSERTION(!placeholder || nsLayoutUtils::IsProperAncestorFrame(
818 aDestructRoot, placeholder),
819 "Placeholder relationship should have been torn down already; "
820 "this might mean we have a stray placeholder in the tree.");
821 if (placeholder) {
822 placeholder->SetOutOfFlowFrame(nullptr);
823 }
824 }
825
826 if (IsPrimaryFrame()) {
827 // This needs to happen before we clear our Properties() table.
828 ActiveLayerTracker::TransferActivityToContent(this, mContent);
829 }
830
831 ScrollAnchorContainer* anchor = nullptr;
832 if (IsScrollAnchor(&anchor)) {
833 anchor->InvalidateAnchor();
834 }
835
836 if (HasCSSAnimations() || HasCSSTransitions() ||
837 // It's fine to look up the style frame here since if we're destroying the
838 // frames for display:table content we should be destroying both wrapper
839 // and inner frame.
840 EffectSet::GetEffectSetForStyleFrame(this)) {
841 // If no new frame for this element is created by the end of the
842 // restyling process, stop animations and transitions for this frame
843 RestyleManager::AnimationsWithDestroyedFrame* adf =
844 presContext->RestyleManager()->GetAnimationsWithDestroyedFrame();
845 // AnimationsWithDestroyedFrame only lives during the restyling process.
846 if (adf) {
847 adf->Put(mContent, mComputedStyle);
848 }
849 }
850
851 // Disable visibility tracking. Note that we have to do this before we clear
852 // frame properties and lose track of whether we were previously visible.
853 // XXX(seth): It'd be ideal to assert that we're already marked nonvisible
854 // here, but it's unfortunately tricky to guarantee in the face of things like
855 // frame reconstruction induced by style changes.
856 DisableVisibilityTracking();
857
858 // Ensure that we're not in the approximately visible list anymore.
859 PresContext()->GetPresShell()->RemoveFrameFromApproximatelyVisibleList(this);
860
861 presShell->NotifyDestroyingFrame(this);
862
863 if (mState & NS_FRAME_EXTERNAL_REFERENCE) {
864 presShell->ClearFrameRefs(this);
865 }
866
867 nsView* view = GetView();
868 if (view) {
869 view->SetFrame(nullptr);
870 view->Destroy();
871 }
872
873 // Make sure that our deleted frame can't be returned from GetPrimaryFrame()
874 if (IsPrimaryFrame()) {
875 mContent->SetPrimaryFrame(nullptr);
876
877 // Pass the root of a generated content subtree (e.g. ::after/::before) to
878 // aPostDestroyData to unbind it after frame destruction is done.
879 if (HasAnyStateBits(NS_FRAME_GENERATED_CONTENT) &&
880 mContent->IsRootOfNativeAnonymousSubtree()) {
881 aPostDestroyData.AddAnonymousContent(mContent.forget());
882 }
883 }
884
885 // Remove all properties attached to the frame, to ensure any property
886 // destructors that need the frame pointer are handled properly.
887 RemoveAllProperties();
888
889 // Must retrieve the object ID before calling destructors, so the
890 // vtable is still valid.
891 //
892 // Note to future tweakers: having the method that returns the
893 // object size call the destructor will not avoid an indirect call;
894 // the compiler cannot devirtualize the call to the destructor even
895 // if it's from a method defined in the same class.
896
897 nsQueryFrame::FrameIID id = GetFrameId();
898 this->~nsFrame();
899
900 #ifdef DEBUG
901 {
902 nsIFrame* rootFrame = presShell->GetRootFrame();
903 MOZ_ASSERT(rootFrame);
904 if (this != rootFrame) {
905 const RetainedDisplayListData* data =
906 GetRetainedDisplayListData(rootFrame);
907
908 const bool inModifiedList =
909 data && (data->GetFlags(this) &
910 RetainedDisplayListData::FrameFlags::Modified);
911
912 MOZ_ASSERT(!inModifiedList,
913 "A dtor added this frame to modified frames list!");
914 }
915 }
916 #endif
917
918 // Now that we're totally cleaned out, we need to add ourselves to
919 // the presshell's recycler.
920 presShell->FreeFrame(id, this);
921 }
922
GetOffsets(int32_t & aStart,int32_t & aEnd) const923 nsresult nsIFrame::GetOffsets(int32_t& aStart, int32_t& aEnd) const {
924 aStart = 0;
925 aEnd = 0;
926 return NS_OK;
927 }
928
CompareLayers(const nsStyleImageLayers * aFirstLayers,const nsStyleImageLayers * aSecondLayers,const std::function<void (imgRequestProxy * aReq)> & aCallback)929 static void CompareLayers(
930 const nsStyleImageLayers* aFirstLayers,
931 const nsStyleImageLayers* aSecondLayers,
932 const std::function<void(imgRequestProxy* aReq)>& aCallback) {
933 NS_FOR_VISIBLE_IMAGE_LAYERS_BACK_TO_FRONT(i, (*aFirstLayers)) {
934 const auto& image = aFirstLayers->mLayers[i].mImage;
935 if (!image.IsImageRequestType() || !image.IsResolved()) {
936 continue;
937 }
938
939 // aCallback is called when the style image in aFirstLayers is thought to
940 // be different with the corresponded one in aSecondLayers
941 if (!aSecondLayers || i >= aSecondLayers->mImageCount ||
942 (!aSecondLayers->mLayers[i].mImage.IsResolved() ||
943 image.GetImageRequest() !=
944 aSecondLayers->mLayers[i].mImage.GetImageRequest())) {
945 if (imgRequestProxy* req = image.GetImageRequest()) {
946 aCallback(req);
947 }
948 }
949 }
950 }
951
AddAndRemoveImageAssociations(ImageLoader & aImageLoader,nsIFrame * aFrame,const nsStyleImageLayers * aOldLayers,const nsStyleImageLayers * aNewLayers)952 static void AddAndRemoveImageAssociations(
953 ImageLoader& aImageLoader, nsIFrame* aFrame,
954 const nsStyleImageLayers* aOldLayers,
955 const nsStyleImageLayers* aNewLayers) {
956 // If the old context had a background-image image, or mask-image image,
957 // and new context does not have the same image, clear the image load
958 // notifier (which keeps the image loading, if it still is) for the frame.
959 // We want to do this conservatively because some frames paint their
960 // backgrounds from some other frame's style data, and we don't want
961 // to clear those notifiers unless we have to. (They'll be reset
962 // when we paint, although we could miss a notification in that
963 // interval.)
964 if (aOldLayers && aFrame->HasImageRequest()) {
965 CompareLayers(aOldLayers, aNewLayers, [&](imgRequestProxy* aReq) {
966 aImageLoader.DisassociateRequestFromFrame(aReq, aFrame);
967 });
968 }
969
970 CompareLayers(aNewLayers, aOldLayers, [&](imgRequestProxy* aReq) {
971 aImageLoader.AssociateRequestToFrame(aReq, aFrame, 0);
972 });
973 }
974
AddDisplayItem(nsDisplayItemBase * aItem)975 void nsIFrame::AddDisplayItem(nsDisplayItemBase* aItem) {
976 DisplayItemArray* items = GetProperty(DisplayItems());
977 if (!items) {
978 items = new DisplayItemArray();
979 AddProperty(DisplayItems(), items);
980 }
981 MOZ_DIAGNOSTIC_ASSERT(!items->Contains(aItem));
982 items->AppendElement(aItem);
983 }
984
RemoveDisplayItem(nsDisplayItemBase * aItem)985 bool nsIFrame::RemoveDisplayItem(nsDisplayItemBase* aItem) {
986 DisplayItemArray* items = GetProperty(DisplayItems());
987 if (!items) {
988 return false;
989 }
990 bool result = items->RemoveElement(aItem);
991 if (items->IsEmpty()) {
992 RemoveProperty(DisplayItems());
993 }
994 return result;
995 }
996
HasDisplayItems()997 bool nsIFrame::HasDisplayItems() {
998 DisplayItemArray* items = GetProperty(DisplayItems());
999 return items != nullptr;
1000 }
1001
HasDisplayItem(nsDisplayItemBase * aItem)1002 bool nsIFrame::HasDisplayItem(nsDisplayItemBase* aItem) {
1003 DisplayItemArray* items = GetProperty(DisplayItems());
1004 if (!items) {
1005 return false;
1006 }
1007 return items->Contains(aItem);
1008 }
1009
HasDisplayItem(uint32_t aKey)1010 bool nsIFrame::HasDisplayItem(uint32_t aKey) {
1011 DisplayItemArray* items = GetProperty(DisplayItems());
1012 if (!items) {
1013 return false;
1014 }
1015
1016 for (nsDisplayItemBase* i : *items) {
1017 if (i->GetPerFrameKey() == aKey) {
1018 return true;
1019 }
1020 }
1021 return false;
1022 }
1023
1024 template <typename Condition>
DiscardDisplayItems(nsIFrame * aFrame,Condition aCondition)1025 static void DiscardDisplayItems(nsIFrame* aFrame, Condition aCondition) {
1026 auto* items = aFrame->GetProperty(nsIFrame::DisplayItems());
1027 if (!items) {
1028 return;
1029 }
1030
1031 for (nsDisplayItemBase* i : *items) {
1032 // Only discard items that are invalidated by this frame, as we're only
1033 // guaranteed to rebuild those items. Table background items are created by
1034 // the relevant table part, but have the cell frame as the primary frame,
1035 // and we don't want to remove them if this is the cell.
1036 if (aCondition(i) && i->FrameForInvalidation() == aFrame) {
1037 i->SetCantBeReused();
1038 }
1039 }
1040 }
1041
DiscardOldItems(nsIFrame * aFrame)1042 static void DiscardOldItems(nsIFrame* aFrame) {
1043 DiscardDisplayItems(
1044 aFrame, [](nsDisplayItemBase* aItem) { return aItem->IsOldItem(); });
1045 }
1046
RemoveDisplayItemDataForDeletion()1047 void nsIFrame::RemoveDisplayItemDataForDeletion() {
1048 // Destroying a WebRenderUserDataTable can cause destruction of other objects
1049 // which can remove frame properties in their destructor. If we delete a frame
1050 // property it runs the destructor of the stored object in the middle of
1051 // updating the frame property table, so if the destruction of that object
1052 // causes another update to the frame property table it would leave the frame
1053 // property table in an inconsistent state. So we remove it from the table and
1054 // then destroy it. (bug 1530657)
1055 WebRenderUserDataTable* userDataTable =
1056 TakeProperty(WebRenderUserDataProperty::Key());
1057 if (userDataTable) {
1058 for (auto iter = userDataTable->Iter(); !iter.Done(); iter.Next()) {
1059 iter.UserData()->RemoveFromTable();
1060 }
1061 delete userDataTable;
1062 }
1063
1064 FrameLayerBuilder::RemoveFrameFromLayerManager(this, DisplayItemData());
1065 DisplayItemData().Clear();
1066
1067 DisplayItemArray* items = TakeProperty(DisplayItems());
1068 if (items) {
1069 for (nsDisplayItemBase* i : *items) {
1070 if (i->GetDependentFrame() == this && !i->HasDeletedFrame()) {
1071 i->Frame()->MarkNeedsDisplayItemRebuild();
1072 }
1073 i->RemoveFrame(this);
1074 }
1075 delete items;
1076 }
1077
1078 if (!nsLayoutUtils::AreRetainedDisplayListsEnabled()) {
1079 // Retained display lists are disabled, no need to update
1080 // RetainedDisplayListData.
1081 return;
1082 }
1083
1084 const bool updateData = IsFrameModified() || HasOverrideDirtyRegion() ||
1085 MayHaveWillChangeBudget();
1086
1087 if (!updateData) {
1088 // No RetainedDisplayListData to update.
1089 return;
1090 }
1091
1092 nsIFrame* rootFrame = PresShell()->GetRootFrame();
1093 MOZ_ASSERT(rootFrame);
1094
1095 RetainedDisplayListData* data = GetOrSetRetainedDisplayListData(rootFrame);
1096
1097 if (MayHaveWillChangeBudget()) {
1098 // Keep the frame in list, so it can be removed from the will-change budget.
1099 data->Flags(this) = RetainedDisplayListData::FrameFlags::HadWillChange;
1100 return;
1101 }
1102
1103 if (IsFrameModified() || HasOverrideDirtyRegion()) {
1104 // Remove deleted frames from RetainedDisplayListData.
1105 DebugOnly<bool> removed = data->Remove(this);
1106 MOZ_ASSERT(removed,
1107 "Frame had flags set, but it was not found in DisplayListData!");
1108 }
1109 }
1110
MarkNeedsDisplayItemRebuild()1111 void nsIFrame::MarkNeedsDisplayItemRebuild() {
1112 if (!nsLayoutUtils::AreRetainedDisplayListsEnabled() || IsFrameModified() ||
1113 HasAnyStateBits(NS_FRAME_IN_POPUP)) {
1114 // Skip frames that are already marked modified.
1115 return;
1116 }
1117
1118 if (Type() == LayoutFrameType::Placeholder) {
1119 nsIFrame* oof = static_cast<nsPlaceholderFrame*>(this)->GetOutOfFlowFrame();
1120 if (oof) {
1121 oof->MarkNeedsDisplayItemRebuild();
1122 }
1123 // Do not mark placeholder frames modified.
1124 return;
1125 }
1126
1127 if (!nsLayoutUtils::DisplayRootHasRetainedDisplayListBuilder(this)) {
1128 return;
1129 }
1130
1131 nsIFrame* rootFrame = PresShell()->GetRootFrame();
1132 MOZ_ASSERT(rootFrame);
1133
1134 if (rootFrame->IsFrameModified()) {
1135 return;
1136 }
1137
1138 RetainedDisplayListData* data = GetOrSetRetainedDisplayListData(rootFrame);
1139
1140 if (data->ModifiedFramesCount() >
1141 StaticPrefs::layout_display_list_rebuild_frame_limit()) {
1142 // If the modified frames count is above the rebuild limit, mark the root
1143 // frame modified, and stop marking additional frames modified.
1144 data->AddModifiedFrame(rootFrame);
1145 rootFrame->SetFrameIsModified(true);
1146 return;
1147 }
1148
1149 data->AddModifiedFrame(this);
1150 SetFrameIsModified(true);
1151
1152 MOZ_ASSERT(
1153 PresContext()->LayoutPhaseCount(nsLayoutPhase::DisplayListBuilding) == 0);
1154
1155 // Hopefully this is cheap, but we could use a frame state bit to note
1156 // the presence of dependencies to speed it up.
1157 DisplayItemArray* items = GetProperty(DisplayItems());
1158 if (items) {
1159 for (nsDisplayItemBase* i : *items) {
1160 if (i->HasDeletedFrame() || i->Frame() == this) {
1161 // Ignore the items with deleted frames, and the items with |this| as
1162 // the primary frame.
1163 continue;
1164 }
1165
1166 if (i->GetDependentFrame() == this) {
1167 // For items with |this| as a dependent frame, mark the primary frame
1168 // for rebuild.
1169 i->Frame()->MarkNeedsDisplayItemRebuild();
1170 }
1171 }
1172 }
1173 }
1174
1175 // Subclass hook for style post processing
1176 /* virtual */
DidSetComputedStyle(ComputedStyle * aOldComputedStyle)1177 void nsIFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
1178 MaybeScheduleReflowSVGNonDisplayText(this);
1179
1180 Document* doc = PresContext()->Document();
1181 ImageLoader* loader = doc->StyleImageLoader();
1182 // Continuing text frame doesn't initialize its continuation pointer before
1183 // reaching here for the first time, so we have to exclude text frames. This
1184 // doesn't affect correctness because text can't match selectors.
1185 //
1186 // FIXME(emilio): We should consider fixing that.
1187 //
1188 // TODO(emilio): Can we avoid doing some / all of the image stuff when
1189 // isNonTextFirstContinuation is false? We should consider doing this just for
1190 // primary frames and pseudos, but the first-line reparenting code makes it
1191 // all bad, should get around to bug 1465474 eventually :(
1192 const bool isNonText = !IsTextFrame();
1193 if (isNonText) {
1194 mComputedStyle->StartImageLoads(*doc, aOldComputedStyle);
1195 }
1196
1197 const nsStyleImageLayers* oldLayers =
1198 aOldComputedStyle ? &aOldComputedStyle->StyleBackground()->mImage
1199 : nullptr;
1200 const nsStyleImageLayers* newLayers = &StyleBackground()->mImage;
1201 AddAndRemoveImageAssociations(*loader, this, oldLayers, newLayers);
1202
1203 oldLayers =
1204 aOldComputedStyle ? &aOldComputedStyle->StyleSVGReset()->mMask : nullptr;
1205 newLayers = &StyleSVGReset()->mMask;
1206 AddAndRemoveImageAssociations(*loader, this, oldLayers, newLayers);
1207
1208 const nsStyleDisplay* disp = StyleDisplay();
1209 bool handleStickyChange = false;
1210 if (aOldComputedStyle) {
1211 // Detect style changes that should trigger a scroll anchor adjustment
1212 // suppression.
1213 // https://drafts.csswg.org/css-scroll-anchoring/#suppression-triggers
1214 bool needAnchorSuppression = false;
1215
1216 // If we detect a change on margin, padding or border, we store the old
1217 // values on the frame itself between now and reflow, so if someone
1218 // calls GetUsed(Margin|Border|Padding)() before the next reflow, we
1219 // can give an accurate answer.
1220 // We don't want to set the property if one already exists.
1221 nsMargin oldValue(0, 0, 0, 0);
1222 nsMargin newValue(0, 0, 0, 0);
1223 const nsStyleMargin* oldMargin = aOldComputedStyle->StyleMargin();
1224 if (oldMargin->GetMargin(oldValue)) {
1225 if (!StyleMargin()->GetMargin(newValue) || oldValue != newValue) {
1226 if (!HasProperty(UsedMarginProperty())) {
1227 AddProperty(UsedMarginProperty(), new nsMargin(oldValue));
1228 }
1229 needAnchorSuppression = true;
1230 }
1231 }
1232
1233 const nsStylePadding* oldPadding = aOldComputedStyle->StylePadding();
1234 if (oldPadding->GetPadding(oldValue)) {
1235 if (!StylePadding()->GetPadding(newValue) || oldValue != newValue) {
1236 if (!HasProperty(UsedPaddingProperty())) {
1237 AddProperty(UsedPaddingProperty(), new nsMargin(oldValue));
1238 }
1239 needAnchorSuppression = true;
1240 }
1241 }
1242
1243 const nsStyleBorder* oldBorder = aOldComputedStyle->StyleBorder();
1244 oldValue = oldBorder->GetComputedBorder();
1245 newValue = StyleBorder()->GetComputedBorder();
1246 if (oldValue != newValue && !HasProperty(UsedBorderProperty())) {
1247 AddProperty(UsedBorderProperty(), new nsMargin(oldValue));
1248 }
1249
1250 const nsStyleDisplay* oldDisp = aOldComputedStyle->StyleDisplay();
1251 if (oldDisp->mOverflowAnchor != disp->mOverflowAnchor) {
1252 if (auto* container = ScrollAnchorContainer::FindFor(this)) {
1253 container->InvalidateAnchor();
1254 }
1255 if (nsIScrollableFrame* scrollableFrame = do_QueryFrame(this)) {
1256 scrollableFrame->Anchor()->InvalidateAnchor();
1257 }
1258 }
1259
1260 if (mInScrollAnchorChain) {
1261 const nsStylePosition* pos = StylePosition();
1262 const nsStylePosition* oldPos = aOldComputedStyle->StylePosition();
1263 if (!needAnchorSuppression &&
1264 (oldPos->mOffset != pos->mOffset || oldPos->mWidth != pos->mWidth ||
1265 oldPos->mMinWidth != pos->mMinWidth ||
1266 oldPos->mMaxWidth != pos->mMaxWidth ||
1267 oldPos->mHeight != pos->mHeight ||
1268 oldPos->mMinHeight != pos->mMinHeight ||
1269 oldPos->mMaxHeight != pos->mMaxHeight ||
1270 oldDisp->mPosition != disp->mPosition ||
1271 oldDisp->mTransform != disp->mTransform)) {
1272 needAnchorSuppression = true;
1273 }
1274
1275 if (needAnchorSuppression &&
1276 StaticPrefs::layout_css_scroll_anchoring_suppressions_enabled()) {
1277 ScrollAnchorContainer::FindFor(this)->SuppressAdjustments();
1278 }
1279 }
1280
1281 if (disp->mPosition != oldDisp->mPosition) {
1282 if (!disp->IsRelativelyPositionedStyle() &&
1283 oldDisp->IsRelativelyPositionedStyle()) {
1284 RemoveProperty(NormalPositionProperty());
1285 }
1286
1287 handleStickyChange = disp->mPosition == StylePositionProperty::Sticky ||
1288 oldDisp->mPosition == StylePositionProperty::Sticky;
1289 }
1290 } else { // !aOldComputedStyle
1291 handleStickyChange = disp->mPosition == StylePositionProperty::Sticky;
1292 }
1293
1294 if (handleStickyChange && !HasAnyStateBits(NS_FRAME_IS_NONDISPLAY) &&
1295 !GetPrevInFlow()) {
1296 // Note that we only add first continuations, but we really only
1297 // want to add first continuation-or-ib-split-siblings. But since we don't
1298 // yet know if we're a later part of a block-in-inline split, we'll just
1299 // add later members of a block-in-inline split here, and then
1300 // StickyScrollContainer will remove them later.
1301 if (auto* ssc =
1302 StickyScrollContainer::GetStickyScrollContainerForFrame(this)) {
1303 if (disp->mPosition == StylePositionProperty::Sticky) {
1304 ssc->AddFrame(this);
1305 } else {
1306 ssc->RemoveFrame(this);
1307 }
1308 }
1309 }
1310
1311 imgIRequest* oldBorderImage =
1312 aOldComputedStyle
1313 ? aOldComputedStyle->StyleBorder()->GetBorderImageRequest()
1314 : nullptr;
1315 imgIRequest* newBorderImage = StyleBorder()->GetBorderImageRequest();
1316 // FIXME (Bug 759996): The following is no longer true.
1317 // For border-images, we can't be as conservative (we need to set the
1318 // new loaders if there has been any change) since the CalcDifference
1319 // call depended on the result of GetComputedBorder() and that result
1320 // depends on whether the image has loaded, start the image load now
1321 // so that we'll get notified when it completes loading and can do a
1322 // restyle. Otherwise, the image might finish loading from the
1323 // network before we start listening to its notifications, and then
1324 // we'll never know that it's finished loading. Likewise, we want to
1325 // do this for freshly-created frames to prevent a similar race if the
1326 // image loads between reflow (which can depend on whether the image
1327 // is loaded) and paint. We also don't really care about any callers who try
1328 // to paint borders with a different style, because they won't have the
1329 // correct size for the border either.
1330 if (oldBorderImage != newBorderImage) {
1331 // stop and restart the image loading/notification
1332 if (oldBorderImage && HasImageRequest()) {
1333 RemoveProperty(CachedBorderImageDataProperty());
1334 loader->DisassociateRequestFromFrame(oldBorderImage, this);
1335 }
1336 if (newBorderImage) {
1337 loader->AssociateRequestToFrame(newBorderImage, this, 0);
1338 }
1339 }
1340
1341 auto GetShapeImageRequest = [](const ComputedStyle* aStyle) -> imgIRequest* {
1342 if (!aStyle) {
1343 return nullptr;
1344 }
1345 auto& shape = aStyle->StyleDisplay()->mShapeOutside;
1346 if (!shape.IsImage()) {
1347 return nullptr;
1348 }
1349 return shape.AsImage().GetImageRequest();
1350 };
1351
1352 imgIRequest* oldShapeImage = GetShapeImageRequest(aOldComputedStyle);
1353 imgIRequest* newShapeImage = GetShapeImageRequest(Style());
1354 if (oldShapeImage != newShapeImage) {
1355 if (oldShapeImage && HasImageRequest()) {
1356 loader->DisassociateRequestFromFrame(oldShapeImage, this);
1357 }
1358 if (newShapeImage) {
1359 loader->AssociateRequestToFrame(newShapeImage, this,
1360 ImageLoader::REQUEST_REQUIRES_REFLOW);
1361 }
1362 }
1363
1364 // SVGObserverUtils::GetEffectProperties() asserts that we only invoke it with
1365 // the first continuation so we need to check that in advance.
1366 const bool isNonTextFirstContinuation = isNonText && !GetPrevContinuation();
1367 if (isNonTextFirstContinuation) {
1368 // Kick off loading of external SVG resources referenced from properties if
1369 // any. This currently includes filter, clip-path, and mask.
1370 SVGObserverUtils::InitiateResourceDocLoads(this);
1371 }
1372
1373 // If the page contains markup that overrides text direction, and
1374 // does not contain any characters that would activate the Unicode
1375 // bidi algorithm, we need to call |SetBidiEnabled| on the pres
1376 // context before reflow starts. See bug 115921.
1377 if (StyleVisibility()->mDirection == StyleDirection::Rtl) {
1378 PresContext()->SetBidiEnabled();
1379 }
1380
1381 // The following part is for caching offset-path:path(). We cache the
1382 // flatten gfx path, so we don't have to rebuild and re-flattern it at
1383 // each cycle if we have animations on offset-* with a fixed offset-path.
1384 const StyleOffsetPath* oldPath =
1385 aOldComputedStyle ? &aOldComputedStyle->StyleDisplay()->mOffsetPath
1386 : nullptr;
1387 const StyleOffsetPath& newPath = StyleDisplay()->mOffsetPath;
1388 if (!oldPath || *oldPath != newPath) {
1389 if (newPath.IsPath()) {
1390 // Here we only need to build a valid path for motion path, so
1391 // using the default values of stroke-width, stoke-linecap, and fill-rule
1392 // is fine for now because what we want is to get the point and its normal
1393 // vector along the path, instead of rendering it.
1394 RefPtr<gfx::PathBuilder> builder =
1395 gfxPlatform::GetPlatform()
1396 ->ScreenReferenceDrawTarget()
1397 ->CreatePathBuilder(gfx::FillRule::FILL_WINDING);
1398 RefPtr<gfx::Path> path =
1399 MotionPathUtils::BuildPath(newPath.AsPath(), builder);
1400 if (path) {
1401 // The newPath could be path('') (i.e. empty path), so its gfx path
1402 // could be nullptr, and so we only set property for a non-empty path.
1403 SetProperty(nsIFrame::OffsetPathCache(), path.forget().take());
1404 } else {
1405 // May have an old cached path, so we have to delete it.
1406 RemoveProperty(nsIFrame::OffsetPathCache());
1407 }
1408 } else if (oldPath) {
1409 RemoveProperty(nsIFrame::OffsetPathCache());
1410 }
1411 }
1412
1413 RemoveStateBits(NS_FRAME_SIMPLE_EVENT_REGIONS | NS_FRAME_SIMPLE_DISPLAYLIST);
1414
1415 mMayHaveRoundedCorners = true;
1416 }
1417
1418 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
AssertNewStyleIsSane(ComputedStyle & aNewStyle)1419 void nsIFrame::AssertNewStyleIsSane(ComputedStyle& aNewStyle) {
1420 MOZ_DIAGNOSTIC_ASSERT(
1421 aNewStyle.GetPseudoType() == mComputedStyle->GetPseudoType() ||
1422 // ::first-line continuations are weird, this should probably be fixed via
1423 // bug 1465474.
1424 (mComputedStyle->GetPseudoType() == PseudoStyleType::firstLine &&
1425 aNewStyle.GetPseudoType() == PseudoStyleType::mozLineFrame) ||
1426 // ::first-letter continuations are broken, in particular floating ones,
1427 // see bug 1490281. The construction code tries to fix this up after the
1428 // fact, then restyling undoes it...
1429 (mComputedStyle->GetPseudoType() == PseudoStyleType::mozText &&
1430 aNewStyle.GetPseudoType() == PseudoStyleType::firstLetterContinuation) ||
1431 (mComputedStyle->GetPseudoType() ==
1432 PseudoStyleType::firstLetterContinuation &&
1433 aNewStyle.GetPseudoType() == PseudoStyleType::mozText));
1434 }
1435 #endif
1436
ReparentFrameViewTo(nsViewManager * aViewManager,nsView * aNewParentView,nsView * aOldParentView)1437 void nsIFrame::ReparentFrameViewTo(nsViewManager* aViewManager,
1438 nsView* aNewParentView,
1439 nsView* aOldParentView) {
1440 if (HasView()) {
1441 #ifdef MOZ_XUL
1442 if (IsMenuPopupFrame()) {
1443 // This view must be parented by the root view, don't reparent it.
1444 return;
1445 }
1446 #endif
1447 nsView* view = GetView();
1448 // Verify that the current parent view is what we think it is
1449 // nsView* parentView;
1450 // NS_ASSERTION(parentView == aOldParentView, "unexpected parent view");
1451
1452 aViewManager->RemoveChild(view);
1453
1454 // The view will remember the Z-order and other attributes that have been
1455 // set on it.
1456 nsView* insertBefore =
1457 nsLayoutUtils::FindSiblingViewFor(aNewParentView, this);
1458 aViewManager->InsertChild(aNewParentView, view, insertBefore,
1459 insertBefore != nullptr);
1460 } else if (GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW) {
1461 for (const auto& childList : ChildLists()) {
1462 // Iterate the child frames, and check each child frame to see if it has
1463 // a view
1464 for (nsIFrame* child : childList.mList) {
1465 child->ReparentFrameViewTo(aViewManager, aNewParentView,
1466 aOldParentView);
1467 }
1468 }
1469 }
1470 }
1471
SyncFrameViewProperties(nsView * aView)1472 void nsIFrame::SyncFrameViewProperties(nsView* aView) {
1473 if (!aView) {
1474 aView = GetView();
1475 if (!aView) {
1476 return;
1477 }
1478 }
1479
1480 nsViewManager* vm = aView->GetViewManager();
1481
1482 // Make sure visibility is correct. This only affects nsSubDocumentFrame.
1483 if (!SupportsVisibilityHidden()) {
1484 // See if the view should be hidden or visible
1485 ComputedStyle* sc = Style();
1486 vm->SetViewVisibility(aView, sc->StyleVisibility()->IsVisible()
1487 ? nsViewVisibility_kShow
1488 : nsViewVisibility_kHide);
1489 }
1490
1491 int32_t zIndex = 0;
1492 bool autoZIndex = false;
1493
1494 if (IsAbsPosContainingBlock()) {
1495 // Make sure z-index is correct
1496 ComputedStyle* sc = Style();
1497 const nsStylePosition* position = sc->StylePosition();
1498 if (position->mZIndex.IsInteger()) {
1499 zIndex = position->mZIndex.AsInteger();
1500 } else {
1501 MOZ_ASSERT(position->mZIndex.IsAuto());
1502 autoZIndex = true;
1503 }
1504 } else {
1505 autoZIndex = true;
1506 }
1507
1508 vm->SetViewZIndex(aView, autoZIndex, zIndex);
1509 }
1510
CreateView()1511 void nsFrame::CreateView() {
1512 MOZ_ASSERT(!HasView());
1513
1514 nsView* parentView = GetParent()->GetClosestView();
1515 MOZ_ASSERT(parentView, "no parent with view");
1516
1517 nsViewManager* viewManager = parentView->GetViewManager();
1518 MOZ_ASSERT(viewManager, "null view manager");
1519
1520 nsView* view = viewManager->CreateView(GetRect(), parentView);
1521 SyncFrameViewProperties(view);
1522
1523 nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(parentView, this);
1524 // we insert this view 'above' the insertBefore view, unless insertBefore is
1525 // null, in which case we want to call with aAbove == false to insert at the
1526 // beginning in document order
1527 viewManager->InsertChild(parentView, view, insertBefore,
1528 insertBefore != nullptr);
1529
1530 // REVIEW: Don't create a widget for fixed-pos elements anymore.
1531 // ComputeRepaintRegionForCopy will calculate the right area to repaint
1532 // when we scroll.
1533 // Reparent views on any child frames (or their descendants) to this
1534 // view. We can just call ReparentFrameViewTo on this frame because
1535 // we know this frame has no view, so it will crawl the children. Also,
1536 // we know that any descendants with views must have 'parentView' as their
1537 // parent view.
1538 ReparentFrameViewTo(viewManager, view, parentView);
1539
1540 // Remember our view
1541 SetView(view);
1542
1543 NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
1544 ("nsFrame::CreateView: frame=%p view=%p", this, view));
1545 }
1546
1547 // MSVC fails with link error "one or more multiply defined symbols found",
1548 // gcc fails with "hidden symbol `nsIFrame::kPrincipalList' isn't defined"
1549 // etc if they are not defined.
1550 #ifndef _MSC_VER
1551 // static nsIFrame constants; initialized in the header file.
1552 const nsIFrame::ChildListID nsIFrame::kPrincipalList;
1553 const nsIFrame::ChildListID nsIFrame::kAbsoluteList;
1554 const nsIFrame::ChildListID nsIFrame::kBulletList;
1555 const nsIFrame::ChildListID nsIFrame::kCaptionList;
1556 const nsIFrame::ChildListID nsIFrame::kColGroupList;
1557 const nsIFrame::ChildListID nsIFrame::kExcessOverflowContainersList;
1558 const nsIFrame::ChildListID nsIFrame::kFixedList;
1559 const nsIFrame::ChildListID nsIFrame::kFloatList;
1560 const nsIFrame::ChildListID nsIFrame::kOverflowContainersList;
1561 const nsIFrame::ChildListID nsIFrame::kOverflowList;
1562 const nsIFrame::ChildListID nsIFrame::kOverflowOutOfFlowList;
1563 const nsIFrame::ChildListID nsIFrame::kPopupList;
1564 const nsIFrame::ChildListID nsIFrame::kPushedFloatsList;
1565 const nsIFrame::ChildListID nsIFrame::kSelectPopupList;
1566 const nsIFrame::ChildListID nsIFrame::kNoReflowPrincipalList;
1567 #endif
1568
1569 /* virtual */
GetUsedMargin() const1570 nsMargin nsIFrame::GetUsedMargin() const {
1571 nsMargin margin(0, 0, 0, 0);
1572 if (((mState & NS_FRAME_FIRST_REFLOW) && !(mState & NS_FRAME_IN_REFLOW)) ||
1573 nsSVGUtils::IsInSVGTextSubtree(this))
1574 return margin;
1575
1576 nsMargin* m = GetProperty(UsedMarginProperty());
1577 if (m) {
1578 margin = *m;
1579 } else {
1580 if (!StyleMargin()->GetMargin(margin)) {
1581 // If we get here, our caller probably shouldn't be calling us...
1582 NS_ERROR(
1583 "Returning bogus 0-sized margin, because this margin "
1584 "depends on layout & isn't cached!");
1585 }
1586 }
1587 return margin;
1588 }
1589
1590 /* virtual */
GetUsedBorder() const1591 nsMargin nsIFrame::GetUsedBorder() const {
1592 nsMargin border(0, 0, 0, 0);
1593 if (((mState & NS_FRAME_FIRST_REFLOW) && !(mState & NS_FRAME_IN_REFLOW)) ||
1594 nsSVGUtils::IsInSVGTextSubtree(this))
1595 return border;
1596
1597 // Theme methods don't use const-ness.
1598 nsIFrame* mutable_this = const_cast<nsIFrame*>(this);
1599
1600 const nsStyleDisplay* disp = StyleDisplay();
1601 if (mutable_this->IsThemed(disp)) {
1602 nsPresContext* pc = PresContext();
1603 LayoutDeviceIntMargin widgetBorder = pc->Theme()->GetWidgetBorder(
1604 pc->DeviceContext(), mutable_this, disp->mAppearance);
1605 border =
1606 LayoutDevicePixel::ToAppUnits(widgetBorder, pc->AppUnitsPerDevPixel());
1607 return border;
1608 }
1609
1610 nsMargin* b = GetProperty(UsedBorderProperty());
1611 if (b) {
1612 border = *b;
1613 } else {
1614 border = StyleBorder()->GetComputedBorder();
1615 }
1616 return border;
1617 }
1618
1619 /* virtual */
GetUsedPadding() const1620 nsMargin nsIFrame::GetUsedPadding() const {
1621 nsMargin padding(0, 0, 0, 0);
1622 if (((mState & NS_FRAME_FIRST_REFLOW) && !(mState & NS_FRAME_IN_REFLOW)) ||
1623 nsSVGUtils::IsInSVGTextSubtree(this))
1624 return padding;
1625
1626 // Theme methods don't use const-ness.
1627 nsIFrame* mutable_this = const_cast<nsIFrame*>(this);
1628
1629 const nsStyleDisplay* disp = StyleDisplay();
1630 if (mutable_this->IsThemed(disp)) {
1631 nsPresContext* pc = PresContext();
1632 LayoutDeviceIntMargin widgetPadding;
1633 if (pc->Theme()->GetWidgetPadding(pc->DeviceContext(), mutable_this,
1634 disp->mAppearance, &widgetPadding)) {
1635 return LayoutDevicePixel::ToAppUnits(widgetPadding,
1636 pc->AppUnitsPerDevPixel());
1637 }
1638 }
1639
1640 nsMargin* p = GetProperty(UsedPaddingProperty());
1641 if (p) {
1642 padding = *p;
1643 } else {
1644 if (!StylePadding()->GetPadding(padding)) {
1645 // If we get here, our caller probably shouldn't be calling us...
1646 NS_ERROR(
1647 "Returning bogus 0-sized padding, because this padding "
1648 "depends on layout & isn't cached!");
1649 }
1650 }
1651 return padding;
1652 }
1653
GetSkipSides(const ReflowInput * aReflowInput) const1654 nsIFrame::Sides nsIFrame::GetSkipSides(const ReflowInput* aReflowInput) const {
1655 if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
1656 StyleBoxDecorationBreak::Clone) &&
1657 !(GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER)) {
1658 return Sides();
1659 }
1660
1661 // Convert the logical skip sides to physical sides using the frame's
1662 // writing mode
1663 WritingMode writingMode = GetWritingMode();
1664 LogicalSides logicalSkip = GetLogicalSkipSides(aReflowInput);
1665 Sides skip;
1666
1667 if (logicalSkip.BStart()) {
1668 if (writingMode.IsVertical()) {
1669 skip |= writingMode.IsVerticalLR() ? SideBits::eLeft : SideBits::eRight;
1670 } else {
1671 skip |= SideBits::eTop;
1672 }
1673 }
1674
1675 if (logicalSkip.BEnd()) {
1676 if (writingMode.IsVertical()) {
1677 skip |= writingMode.IsVerticalLR() ? SideBits::eRight : SideBits::eLeft;
1678 } else {
1679 skip |= SideBits::eBottom;
1680 }
1681 }
1682
1683 if (logicalSkip.IStart()) {
1684 if (writingMode.IsVertical()) {
1685 skip |= SideBits::eTop;
1686 } else {
1687 skip |= writingMode.IsBidiLTR() ? SideBits::eLeft : SideBits::eRight;
1688 }
1689 }
1690
1691 if (logicalSkip.IEnd()) {
1692 if (writingMode.IsVertical()) {
1693 skip |= SideBits::eBottom;
1694 } else {
1695 skip |= writingMode.IsBidiLTR() ? SideBits::eRight : SideBits::eLeft;
1696 }
1697 }
1698 return skip;
1699 }
1700
GetPaddingRectRelativeToSelf() const1701 nsRect nsIFrame::GetPaddingRectRelativeToSelf() const {
1702 nsMargin border = GetUsedBorder().ApplySkipSides(GetSkipSides());
1703 nsRect r(0, 0, mRect.width, mRect.height);
1704 r.Deflate(border);
1705 return r;
1706 }
1707
GetPaddingRect() const1708 nsRect nsIFrame::GetPaddingRect() const {
1709 return GetPaddingRectRelativeToSelf() + GetPosition();
1710 }
1711
WritingModeForLine(WritingMode aSelfWM,nsIFrame * aSubFrame) const1712 WritingMode nsIFrame::WritingModeForLine(WritingMode aSelfWM,
1713 nsIFrame* aSubFrame) const {
1714 MOZ_ASSERT(aSelfWM == GetWritingMode());
1715 WritingMode writingMode = aSelfWM;
1716
1717 if (StyleTextReset()->mUnicodeBidi & NS_STYLE_UNICODE_BIDI_PLAINTEXT) {
1718 nsBidiLevel frameLevel = nsBidiPresUtils::GetFrameBaseLevel(aSubFrame);
1719 writingMode.SetDirectionFromBidiLevel(frameLevel);
1720 }
1721
1722 return writingMode;
1723 }
1724
GetMarginRectRelativeToSelf() const1725 nsRect nsIFrame::GetMarginRectRelativeToSelf() const {
1726 nsMargin m = GetUsedMargin().ApplySkipSides(GetSkipSides());
1727 nsRect r(0, 0, mRect.width, mRect.height);
1728 r.Inflate(m);
1729 return r;
1730 }
1731
IsTransformed(const nsStyleDisplay * aStyleDisplay) const1732 bool nsIFrame::IsTransformed(const nsStyleDisplay* aStyleDisplay) const {
1733 return IsCSSTransformed(aStyleDisplay) || IsSVGTransformed();
1734 }
1735
IsCSSTransformed(const nsStyleDisplay * aStyleDisplay) const1736 bool nsIFrame::IsCSSTransformed(const nsStyleDisplay* aStyleDisplay) const {
1737 MOZ_ASSERT(aStyleDisplay == StyleDisplay());
1738 return ((mState & NS_FRAME_MAY_BE_TRANSFORMED) &&
1739 (aStyleDisplay->HasTransform(this) || HasAnimationOfTransform()));
1740 }
1741
HasAnimationOfTransform() const1742 bool nsIFrame::HasAnimationOfTransform() const {
1743 return IsPrimaryFrame() &&
1744 nsLayoutUtils::HasAnimationOfTransformAndMotionPath(this) &&
1745 IsFrameOfType(eSupportsCSSTransforms);
1746 }
1747
ChildrenHavePerspective(const nsStyleDisplay * aStyleDisplay) const1748 bool nsIFrame::ChildrenHavePerspective(
1749 const nsStyleDisplay* aStyleDisplay) const {
1750 MOZ_ASSERT(aStyleDisplay == StyleDisplay());
1751 return aStyleDisplay->HasPerspective(this);
1752 }
1753
HasAnimationOfOpacity(EffectSet * aEffectSet) const1754 bool nsIFrame::HasAnimationOfOpacity(EffectSet* aEffectSet) const {
1755 return ((nsLayoutUtils::IsPrimaryStyleFrame(this) ||
1756 nsLayoutUtils::FirstContinuationOrIBSplitSibling(this)
1757 ->IsPrimaryFrame()) &&
1758 nsLayoutUtils::HasAnimationOfPropertySet(
1759 this, nsCSSPropertyIDSet::OpacityProperties(), aEffectSet));
1760 }
1761
HasOpacityInternal(float aThreshold,const nsStyleDisplay * aStyleDisplay,const nsStyleEffects * aStyleEffects,EffectSet * aEffectSet) const1762 bool nsIFrame::HasOpacityInternal(float aThreshold,
1763 const nsStyleDisplay* aStyleDisplay,
1764 const nsStyleEffects* aStyleEffects,
1765 EffectSet* aEffectSet) const {
1766 MOZ_ASSERT(0.0 <= aThreshold && aThreshold <= 1.0, "Invalid argument");
1767 if (aStyleEffects->mOpacity < aThreshold ||
1768 (aStyleDisplay->mWillChange.bits & StyleWillChangeBits::OPACITY)) {
1769 return true;
1770 }
1771
1772 if (!mMayHaveOpacityAnimation) {
1773 return false;
1774 }
1775
1776 return HasAnimationOfOpacity(aEffectSet);
1777 }
1778
IsSVGTransformed(gfx::Matrix * aOwnTransforms,gfx::Matrix * aFromParentTransforms) const1779 bool nsIFrame::IsSVGTransformed(gfx::Matrix* aOwnTransforms,
1780 gfx::Matrix* aFromParentTransforms) const {
1781 return false;
1782 }
1783
Extend3DContext(const nsStyleDisplay * aStyleDisplay,const nsStyleEffects * aStyleEffects,mozilla::EffectSet * aEffectSetForOpacity) const1784 bool nsIFrame::Extend3DContext(const nsStyleDisplay* aStyleDisplay,
1785 const nsStyleEffects* aStyleEffects,
1786 mozilla::EffectSet* aEffectSetForOpacity) const {
1787 if (!(mState & NS_FRAME_MAY_BE_TRANSFORMED)) {
1788 return false;
1789 }
1790 const nsStyleDisplay* disp = StyleDisplayWithOptionalParam(aStyleDisplay);
1791 if (disp->mTransformStyle != StyleTransformStyle::Preserve3d ||
1792 !IsFrameOfType(nsIFrame::eSupportsCSSTransforms)) {
1793 return false;
1794 }
1795
1796 // If we're all scroll frame, then all descendants will be clipped, so we
1797 // can't preserve 3d.
1798 if (IsScrollFrame()) {
1799 return false;
1800 }
1801
1802 const nsStyleEffects* effects = StyleEffectsWithOptionalParam(aStyleEffects);
1803 if (HasOpacity(disp, effects, aEffectSetForOpacity)) {
1804 return false;
1805 }
1806
1807 return !nsFrame::ShouldApplyOverflowClipping(this, disp) &&
1808 !GetClipPropClipRect(disp, effects, GetSize()) &&
1809 !nsSVGIntegrationUtils::UsingEffectsForFrame(this);
1810 }
1811
Combines3DTransformWithAncestors(const nsStyleDisplay * aStyleDisplay) const1812 bool nsIFrame::Combines3DTransformWithAncestors(
1813 const nsStyleDisplay* aStyleDisplay) const {
1814 MOZ_ASSERT(aStyleDisplay == StyleDisplay());
1815 nsIFrame* parent = GetClosestFlattenedTreeAncestorPrimaryFrame();
1816 if (!parent || !parent->Extend3DContext()) {
1817 return false;
1818 }
1819 return IsCSSTransformed(aStyleDisplay) || BackfaceIsHidden(aStyleDisplay);
1820 }
1821
In3DContextAndBackfaceIsHidden() const1822 bool nsIFrame::In3DContextAndBackfaceIsHidden() const {
1823 // While both tests fail most of the time, test BackfaceIsHidden()
1824 // first since it's likely to fail faster.
1825 const nsStyleDisplay* disp = StyleDisplay();
1826 return BackfaceIsHidden(disp) && Combines3DTransformWithAncestors(disp);
1827 }
1828
HasPerspective(const nsStyleDisplay * aStyleDisplay) const1829 bool nsIFrame::HasPerspective(const nsStyleDisplay* aStyleDisplay) const {
1830 MOZ_ASSERT(aStyleDisplay == StyleDisplay());
1831 if (!IsTransformed(aStyleDisplay)) {
1832 return false;
1833 }
1834 nsIFrame* containingBlock =
1835 GetContainingBlock(SKIP_SCROLLED_FRAME, aStyleDisplay);
1836 if (!containingBlock) {
1837 return false;
1838 }
1839 return containingBlock->ChildrenHavePerspective();
1840 }
1841
GetContentRectRelativeToSelf() const1842 nsRect nsIFrame::GetContentRectRelativeToSelf() const {
1843 nsMargin bp = GetUsedBorderAndPadding().ApplySkipSides(GetSkipSides());
1844 nsRect r(0, 0, mRect.width, mRect.height);
1845 r.Deflate(bp);
1846 return r;
1847 }
1848
GetContentRect() const1849 nsRect nsIFrame::GetContentRect() const {
1850 return GetContentRectRelativeToSelf() + GetPosition();
1851 }
1852
ComputeBorderRadii(const BorderRadius & aBorderRadius,const nsSize & aFrameSize,const nsSize & aBorderArea,Sides aSkipSides,nscoord aRadii[8])1853 bool nsIFrame::ComputeBorderRadii(const BorderRadius& aBorderRadius,
1854 const nsSize& aFrameSize,
1855 const nsSize& aBorderArea, Sides aSkipSides,
1856 nscoord aRadii[8]) {
1857 // Percentages are relative to whichever side they're on.
1858 for (const auto i : mozilla::AllPhysicalHalfCorners()) {
1859 const LengthPercentage& c = aBorderRadius.Get(i);
1860 nscoord axis = HalfCornerIsX(i) ? aFrameSize.width : aFrameSize.height;
1861 aRadii[i] = std::max(0, c.Resolve(axis));
1862 }
1863
1864 if (aSkipSides.Top()) {
1865 aRadii[eCornerTopLeftX] = 0;
1866 aRadii[eCornerTopLeftY] = 0;
1867 aRadii[eCornerTopRightX] = 0;
1868 aRadii[eCornerTopRightY] = 0;
1869 }
1870
1871 if (aSkipSides.Right()) {
1872 aRadii[eCornerTopRightX] = 0;
1873 aRadii[eCornerTopRightY] = 0;
1874 aRadii[eCornerBottomRightX] = 0;
1875 aRadii[eCornerBottomRightY] = 0;
1876 }
1877
1878 if (aSkipSides.Bottom()) {
1879 aRadii[eCornerBottomRightX] = 0;
1880 aRadii[eCornerBottomRightY] = 0;
1881 aRadii[eCornerBottomLeftX] = 0;
1882 aRadii[eCornerBottomLeftY] = 0;
1883 }
1884
1885 if (aSkipSides.Left()) {
1886 aRadii[eCornerBottomLeftX] = 0;
1887 aRadii[eCornerBottomLeftY] = 0;
1888 aRadii[eCornerTopLeftX] = 0;
1889 aRadii[eCornerTopLeftY] = 0;
1890 }
1891
1892 // css3-background specifies this algorithm for reducing
1893 // corner radii when they are too big.
1894 bool haveRadius = false;
1895 double ratio = 1.0f;
1896 for (const auto side : mozilla::AllPhysicalSides()) {
1897 uint32_t hc1 = SideToHalfCorner(side, false, true);
1898 uint32_t hc2 = SideToHalfCorner(side, true, true);
1899 nscoord length =
1900 SideIsVertical(side) ? aBorderArea.height : aBorderArea.width;
1901 nscoord sum = aRadii[hc1] + aRadii[hc2];
1902 if (sum) {
1903 haveRadius = true;
1904 // avoid floating point division in the normal case
1905 if (length < sum) {
1906 ratio = std::min(ratio, double(length) / sum);
1907 }
1908 }
1909 }
1910 if (ratio < 1.0) {
1911 for (const auto corner : mozilla::AllPhysicalHalfCorners()) {
1912 aRadii[corner] *= ratio;
1913 }
1914 }
1915
1916 return haveRadius;
1917 }
1918
1919 /* static */
InsetBorderRadii(nscoord aRadii[8],const nsMargin & aOffsets)1920 void nsIFrame::InsetBorderRadii(nscoord aRadii[8], const nsMargin& aOffsets) {
1921 for (const auto side : mozilla::AllPhysicalSides()) {
1922 nscoord offset = aOffsets.Side(side);
1923 uint32_t hc1 = SideToHalfCorner(side, false, false);
1924 uint32_t hc2 = SideToHalfCorner(side, true, false);
1925 aRadii[hc1] = std::max(0, aRadii[hc1] - offset);
1926 aRadii[hc2] = std::max(0, aRadii[hc2] - offset);
1927 }
1928 }
1929
1930 /* static */
OutsetBorderRadii(nscoord aRadii[8],const nsMargin & aOffsets)1931 void nsIFrame::OutsetBorderRadii(nscoord aRadii[8], const nsMargin& aOffsets) {
1932 auto AdjustOffset = [](const uint32_t aRadius, const nscoord aOffset) {
1933 // Implement the cubic formula to adjust offset when aOffset > 0 and
1934 // aRadius / aOffset < 1.
1935 // https://drafts.csswg.org/css-shapes/#valdef-shape-box-margin-box
1936 if (aOffset > 0) {
1937 const double ratio = aRadius / double(aOffset);
1938 if (ratio < 1.0) {
1939 return nscoord(aOffset * (1.0 + std::pow(ratio - 1, 3)));
1940 }
1941 }
1942 return aOffset;
1943 };
1944
1945 for (const auto side : mozilla::AllPhysicalSides()) {
1946 const nscoord offset = aOffsets.Side(side);
1947 const uint32_t hc1 = SideToHalfCorner(side, false, false);
1948 const uint32_t hc2 = SideToHalfCorner(side, true, false);
1949 if (aRadii[hc1] > 0) {
1950 const nscoord offset1 = AdjustOffset(aRadii[hc1], offset);
1951 aRadii[hc1] = std::max(0, aRadii[hc1] + offset1);
1952 }
1953 if (aRadii[hc2] > 0) {
1954 const nscoord offset2 = AdjustOffset(aRadii[hc2], offset);
1955 aRadii[hc2] = std::max(0, aRadii[hc2] + offset2);
1956 }
1957 }
1958 }
1959
RadiiAreDefinitelyZero(const BorderRadius & aBorderRadius)1960 static inline bool RadiiAreDefinitelyZero(const BorderRadius& aBorderRadius) {
1961 for (const auto corner : mozilla::AllPhysicalHalfCorners()) {
1962 if (!aBorderRadius.Get(corner).IsDefinitelyZero()) {
1963 return false;
1964 }
1965 }
1966 return true;
1967 }
1968
1969 /* virtual */
GetBorderRadii(const nsSize & aFrameSize,const nsSize & aBorderArea,Sides aSkipSides,nscoord aRadii[8]) const1970 bool nsIFrame::GetBorderRadii(const nsSize& aFrameSize,
1971 const nsSize& aBorderArea, Sides aSkipSides,
1972 nscoord aRadii[8]) const {
1973 if (!mMayHaveRoundedCorners) {
1974 memset(aRadii, 0, sizeof(nscoord) * 8);
1975 return false;
1976 }
1977
1978 if (IsThemed()) {
1979 // When we're themed, the native theme code draws the border and
1980 // background, and therefore it doesn't make sense to tell other
1981 // code that's interested in border-radius that we have any radii.
1982 //
1983 // In an ideal world, we might have a way for the them to tell us an
1984 // border radius, but since we don't, we're better off assuming
1985 // zero.
1986 for (const auto corner : mozilla::AllPhysicalHalfCorners()) {
1987 aRadii[corner] = 0;
1988 }
1989 return false;
1990 }
1991
1992 const auto& radii = StyleBorder()->mBorderRadius;
1993 const bool hasRadii =
1994 ComputeBorderRadii(radii, aFrameSize, aBorderArea, aSkipSides, aRadii);
1995 if (!hasRadii) {
1996 // TODO(emilio): Maybe we can just remove this bit and do the
1997 // IsDefinitelyZero check unconditionally. That should still avoid most of
1998 // the work, though maybe not the cache miss of going through the style and
1999 // the border struct.
2000 const_cast<nsIFrame*>(this)->mMayHaveRoundedCorners =
2001 !RadiiAreDefinitelyZero(radii);
2002 }
2003 return hasRadii;
2004 }
2005
GetBorderRadii(nscoord aRadii[8]) const2006 bool nsIFrame::GetBorderRadii(nscoord aRadii[8]) const {
2007 nsSize sz = GetSize();
2008 return GetBorderRadii(sz, sz, GetSkipSides(), aRadii);
2009 }
2010
GetMarginBoxBorderRadii(nscoord aRadii[8]) const2011 bool nsIFrame::GetMarginBoxBorderRadii(nscoord aRadii[8]) const {
2012 return GetBoxBorderRadii(aRadii, GetUsedMargin(), true);
2013 }
2014
GetPaddingBoxBorderRadii(nscoord aRadii[8]) const2015 bool nsIFrame::GetPaddingBoxBorderRadii(nscoord aRadii[8]) const {
2016 return GetBoxBorderRadii(aRadii, GetUsedBorder(), false);
2017 }
2018
GetContentBoxBorderRadii(nscoord aRadii[8]) const2019 bool nsIFrame::GetContentBoxBorderRadii(nscoord aRadii[8]) const {
2020 return GetBoxBorderRadii(aRadii, GetUsedBorderAndPadding(), false);
2021 }
2022
GetBoxBorderRadii(nscoord aRadii[8],nsMargin aOffset,bool aIsOutset) const2023 bool nsIFrame::GetBoxBorderRadii(nscoord aRadii[8], nsMargin aOffset,
2024 bool aIsOutset) const {
2025 if (!GetBorderRadii(aRadii)) return false;
2026 if (aIsOutset) {
2027 OutsetBorderRadii(aRadii, aOffset);
2028 } else {
2029 InsetBorderRadii(aRadii, aOffset);
2030 }
2031 for (const auto corner : mozilla::AllPhysicalHalfCorners()) {
2032 if (aRadii[corner]) return true;
2033 }
2034 return false;
2035 }
2036
GetShapeBoxBorderRadii(nscoord aRadii[8]) const2037 bool nsIFrame::GetShapeBoxBorderRadii(nscoord aRadii[8]) const {
2038 using Tag = StyleShapeOutside::Tag;
2039 auto& shapeOutside = StyleDisplay()->mShapeOutside;
2040 auto box = StyleShapeBox::MarginBox;
2041 switch (shapeOutside.tag) {
2042 case Tag::Image:
2043 case Tag::None:
2044 return false;
2045 case Tag::Box:
2046 box = shapeOutside.AsBox();
2047 break;
2048 case Tag::Shape:
2049 box = shapeOutside.AsShape()._1;
2050 break;
2051 }
2052
2053 switch (box) {
2054 case StyleShapeBox::ContentBox:
2055 return GetContentBoxBorderRadii(aRadii);
2056 case StyleShapeBox::PaddingBox:
2057 return GetPaddingBoxBorderRadii(aRadii);
2058 case StyleShapeBox::BorderBox:
2059 return GetBorderRadii(aRadii);
2060 case StyleShapeBox::MarginBox:
2061 return GetMarginBoxBorderRadii(aRadii);
2062 default:
2063 MOZ_ASSERT_UNREACHABLE("Unexpected box value");
2064 return false;
2065 }
2066 }
2067
GetAdditionalComputedStyle(int32_t aIndex) const2068 ComputedStyle* nsIFrame::GetAdditionalComputedStyle(int32_t aIndex) const {
2069 MOZ_ASSERT(aIndex >= 0, "invalid index number");
2070 return nullptr;
2071 }
2072
SetAdditionalComputedStyle(int32_t aIndex,ComputedStyle * aComputedStyle)2073 void nsIFrame::SetAdditionalComputedStyle(int32_t aIndex,
2074 ComputedStyle* aComputedStyle) {
2075 MOZ_ASSERT(aIndex >= 0, "invalid index number");
2076 }
2077
GetLogicalBaseline(WritingMode aWritingMode) const2078 nscoord nsIFrame::GetLogicalBaseline(WritingMode aWritingMode) const {
2079 NS_ASSERTION(!NS_SUBTREE_DIRTY(this), "frame must not be dirty");
2080 // Baseline for inverted line content is the top (block-start) margin edge,
2081 // as the frame is in effect "flipped" for alignment purposes.
2082 if (aWritingMode.IsLineInverted()) {
2083 return -GetLogicalUsedMargin(aWritingMode).BStart(aWritingMode);
2084 }
2085 // Otherwise, the bottom margin edge, per CSS2.1's definition of the
2086 // 'baseline' value of 'vertical-align'.
2087 return BSize(aWritingMode) +
2088 GetLogicalUsedMargin(aWritingMode).BEnd(aWritingMode);
2089 }
2090
GetChildList(ChildListID aListID) const2091 const nsFrameList& nsIFrame::GetChildList(ChildListID aListID) const {
2092 if (IsAbsoluteContainer() && aListID == GetAbsoluteListID()) {
2093 return GetAbsoluteContainingBlock()->GetChildList();
2094 } else {
2095 return nsFrameList::EmptyList();
2096 }
2097 }
2098
GetChildLists(nsTArray<ChildList> * aLists) const2099 void nsIFrame::GetChildLists(nsTArray<ChildList>* aLists) const {
2100 if (IsAbsoluteContainer()) {
2101 nsFrameList absoluteList = GetAbsoluteContainingBlock()->GetChildList();
2102 absoluteList.AppendIfNonempty(aLists, GetAbsoluteListID());
2103 }
2104 }
2105
CrossDocChildLists()2106 AutoTArray<nsIFrame::ChildList, 4> nsIFrame::CrossDocChildLists() {
2107 AutoTArray<ChildList, 4> childLists;
2108 nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(this);
2109 if (subdocumentFrame) {
2110 // Descend into the subdocument
2111 nsIFrame* root = subdocumentFrame->GetSubdocumentRootFrame();
2112 if (root) {
2113 childLists.EmplaceBack(
2114 nsFrameList(root, nsLayoutUtils::GetLastSibling(root)),
2115 nsIFrame::kPrincipalList);
2116 }
2117 }
2118
2119 GetChildLists(&childLists);
2120 return childLists;
2121 }
2122
GetVisibility() const2123 Visibility nsIFrame::GetVisibility() const {
2124 if (!(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED)) {
2125 return Visibility::Untracked;
2126 }
2127
2128 bool isSet = false;
2129 uint32_t visibleCount = GetProperty(VisibilityStateProperty(), &isSet);
2130
2131 MOZ_ASSERT(isSet,
2132 "Should have a VisibilityStateProperty value "
2133 "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
2134
2135 return visibleCount > 0 ? Visibility::ApproximatelyVisible
2136 : Visibility::ApproximatelyNonVisible;
2137 }
2138
UpdateVisibilitySynchronously()2139 void nsIFrame::UpdateVisibilitySynchronously() {
2140 mozilla::PresShell* presShell = PresShell();
2141 if (!presShell) {
2142 return;
2143 }
2144
2145 if (presShell->AssumeAllFramesVisible()) {
2146 presShell->EnsureFrameInApproximatelyVisibleList(this);
2147 return;
2148 }
2149
2150 bool visible = StyleVisibility()->IsVisible();
2151 nsIFrame* f = GetParent();
2152 nsRect rect = GetRectRelativeToSelf();
2153 nsIFrame* rectFrame = this;
2154 while (f && visible) {
2155 nsIScrollableFrame* sf = do_QueryFrame(f);
2156 if (sf) {
2157 nsRect transformedRect =
2158 nsLayoutUtils::TransformFrameRectToAncestor(rectFrame, rect, f);
2159 if (!sf->IsRectNearlyVisible(transformedRect)) {
2160 visible = false;
2161 break;
2162 }
2163
2164 // In this code we're trying to synchronously update *approximate*
2165 // visibility. (In the future we may update precise visibility here as
2166 // well, which is why the method name does not contain 'approximate'.) The
2167 // IsRectNearlyVisible() check above tells us that the rect we're checking
2168 // is approximately visible within the scrollframe, but we still need to
2169 // ensure that, even if it was scrolled into view, it'd be visible when we
2170 // consider the rest of the document. To do that, we move transformedRect
2171 // to be contained in the scrollport as best we can (it might not fit) to
2172 // pretend that it was scrolled into view.
2173 rect = transformedRect.MoveInsideAndClamp(sf->GetScrollPortRect());
2174 rectFrame = f;
2175 }
2176 nsIFrame* parent = f->GetParent();
2177 if (!parent) {
2178 parent = nsLayoutUtils::GetCrossDocParentFrame(f);
2179 if (parent && parent->PresContext()->IsChrome()) {
2180 break;
2181 }
2182 }
2183 f = parent;
2184 }
2185
2186 if (visible) {
2187 presShell->EnsureFrameInApproximatelyVisibleList(this);
2188 } else {
2189 presShell->RemoveFrameFromApproximatelyVisibleList(this);
2190 }
2191 }
2192
EnableVisibilityTracking()2193 void nsIFrame::EnableVisibilityTracking() {
2194 if (GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED) {
2195 return; // Nothing to do.
2196 }
2197
2198 MOZ_ASSERT(!HasProperty(VisibilityStateProperty()),
2199 "Shouldn't have a VisibilityStateProperty value "
2200 "if NS_FRAME_VISIBILITY_IS_TRACKED is not set");
2201
2202 // Add the state bit so we know to track visibility for this frame, and
2203 // initialize the frame property.
2204 AddStateBits(NS_FRAME_VISIBILITY_IS_TRACKED);
2205 SetProperty(VisibilityStateProperty(), 0);
2206
2207 mozilla::PresShell* presShell = PresShell();
2208 if (!presShell) {
2209 return;
2210 }
2211
2212 // Schedule a visibility update. This method will virtually always be called
2213 // when layout has changed anyway, so it's very unlikely that any additional
2214 // visibility updates will be triggered by this, but this way we guarantee
2215 // that if this frame is currently visible we'll eventually find out.
2216 presShell->ScheduleApproximateFrameVisibilityUpdateSoon();
2217 }
2218
DisableVisibilityTracking()2219 void nsIFrame::DisableVisibilityTracking() {
2220 if (!(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED)) {
2221 return; // Nothing to do.
2222 }
2223
2224 bool isSet = false;
2225 uint32_t visibleCount = TakeProperty(VisibilityStateProperty(), &isSet);
2226
2227 MOZ_ASSERT(isSet,
2228 "Should have a VisibilityStateProperty value "
2229 "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
2230
2231 RemoveStateBits(NS_FRAME_VISIBILITY_IS_TRACKED);
2232
2233 if (visibleCount == 0) {
2234 return; // We were nonvisible.
2235 }
2236
2237 // We were visible, so send an OnVisibilityChange() notification.
2238 OnVisibilityChange(Visibility::ApproximatelyNonVisible);
2239 }
2240
DecApproximateVisibleCount(const Maybe<OnNonvisible> & aNonvisibleAction)2241 void nsIFrame::DecApproximateVisibleCount(
2242 const Maybe<OnNonvisible>& aNonvisibleAction
2243 /* = Nothing() */) {
2244 MOZ_ASSERT(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED);
2245
2246 bool isSet = false;
2247 uint32_t visibleCount = GetProperty(VisibilityStateProperty(), &isSet);
2248
2249 MOZ_ASSERT(isSet,
2250 "Should have a VisibilityStateProperty value "
2251 "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
2252 MOZ_ASSERT(visibleCount > 0,
2253 "Frame is already nonvisible and we're "
2254 "decrementing its visible count?");
2255
2256 visibleCount--;
2257 SetProperty(VisibilityStateProperty(), visibleCount);
2258 if (visibleCount > 0) {
2259 return;
2260 }
2261
2262 // We just became nonvisible, so send an OnVisibilityChange() notification.
2263 OnVisibilityChange(Visibility::ApproximatelyNonVisible, aNonvisibleAction);
2264 }
2265
IncApproximateVisibleCount()2266 void nsIFrame::IncApproximateVisibleCount() {
2267 MOZ_ASSERT(GetStateBits() & NS_FRAME_VISIBILITY_IS_TRACKED);
2268
2269 bool isSet = false;
2270 uint32_t visibleCount = GetProperty(VisibilityStateProperty(), &isSet);
2271
2272 MOZ_ASSERT(isSet,
2273 "Should have a VisibilityStateProperty value "
2274 "if NS_FRAME_VISIBILITY_IS_TRACKED is set");
2275
2276 visibleCount++;
2277 SetProperty(VisibilityStateProperty(), visibleCount);
2278 if (visibleCount > 1) {
2279 return;
2280 }
2281
2282 // We just became visible, so send an OnVisibilityChange() notification.
2283 OnVisibilityChange(Visibility::ApproximatelyVisible);
2284 }
2285
OnVisibilityChange(Visibility aNewVisibility,const Maybe<OnNonvisible> & aNonvisibleAction)2286 void nsIFrame::OnVisibilityChange(Visibility aNewVisibility,
2287 const Maybe<OnNonvisible>& aNonvisibleAction
2288 /* = Nothing() */) {
2289 // XXX(seth): In bug 1218990 we'll implement visibility tracking for CSS
2290 // images here.
2291 }
2292
GetActiveSelectionFrame(nsPresContext * aPresContext,nsIFrame * aFrame)2293 static nsIFrame* GetActiveSelectionFrame(nsPresContext* aPresContext,
2294 nsIFrame* aFrame) {
2295 nsIContent* capturingContent = PresShell::GetCapturingContent();
2296 if (capturingContent) {
2297 nsIFrame* activeFrame = aPresContext->GetPrimaryFrameFor(capturingContent);
2298 return activeFrame ? activeFrame : aFrame;
2299 }
2300
2301 return aFrame;
2302 }
2303
DetermineDisplaySelection()2304 int16_t nsIFrame::DetermineDisplaySelection() {
2305 int16_t selType = nsISelectionController::SELECTION_OFF;
2306
2307 nsCOMPtr<nsISelectionController> selCon;
2308 nsresult result =
2309 GetSelectionController(PresContext(), getter_AddRefs(selCon));
2310 if (NS_SUCCEEDED(result) && selCon) {
2311 result = selCon->GetDisplaySelection(&selType);
2312 if (NS_SUCCEEDED(result) &&
2313 (selType != nsISelectionController::SELECTION_OFF)) {
2314 // Check whether style allows selection.
2315 if (!IsSelectable(nullptr)) {
2316 selType = nsISelectionController::SELECTION_OFF;
2317 }
2318 }
2319 }
2320 return selType;
2321 }
2322
2323 class nsDisplaySelectionOverlay : public nsPaintedDisplayItem {
2324 public:
2325 /**
2326 * @param aSelectionValue nsISelectionController::getDisplaySelection.
2327 */
nsDisplaySelectionOverlay(nsDisplayListBuilder * aBuilder,nsFrame * aFrame,int16_t aSelectionValue)2328 nsDisplaySelectionOverlay(nsDisplayListBuilder* aBuilder, nsFrame* aFrame,
2329 int16_t aSelectionValue)
2330 : nsPaintedDisplayItem(aBuilder, aFrame),
2331 mSelectionValue(aSelectionValue) {
2332 MOZ_COUNT_CTOR(nsDisplaySelectionOverlay);
2333 }
2334 MOZ_COUNTED_DTOR_OVERRIDE(nsDisplaySelectionOverlay)
2335
2336 virtual void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
2337 bool CreateWebRenderCommands(
2338 mozilla::wr::DisplayListBuilder& aBuilder,
2339 mozilla::wr::IpcResourceUpdateQueue& aResources,
2340 const StackingContextHelper& aSc,
2341 mozilla::layers::RenderRootStateManager* aManager,
2342 nsDisplayListBuilder* aDisplayListBuilder) override;
2343 NS_DISPLAY_DECL_NAME("SelectionOverlay", TYPE_SELECTION_OVERLAY)
2344 private:
2345 DeviceColor ComputeColor() const;
2346
2347 static DeviceColor ComputeColorFromSelectionStyle(ComputedStyle&);
2348 static DeviceColor ApplyTransparencyIfNecessary(nscolor);
2349
2350 // nsISelectionController::getDisplaySelection.
2351 int16_t mSelectionValue;
2352 };
2353
ApplyTransparencyIfNecessary(nscolor aColor)2354 DeviceColor nsDisplaySelectionOverlay::ApplyTransparencyIfNecessary(
2355 nscolor aColor) {
2356 // If it has already alpha, leave it like that.
2357 if (NS_GET_A(aColor) != 255) {
2358 return ToDeviceColor(aColor);
2359 }
2360
2361 // NOTE(emilio): Blink and WebKit do something slightly different here, and
2362 // blend the color with white instead, both for overlays and text backgrounds.
2363 auto color = sRGBColor::FromABGR(aColor);
2364 color.a = 0.5;
2365 return ToDeviceColor(color);
2366 }
2367
ComputeColorFromSelectionStyle(ComputedStyle & aStyle)2368 DeviceColor nsDisplaySelectionOverlay::ComputeColorFromSelectionStyle(
2369 ComputedStyle& aStyle) {
2370 return ApplyTransparencyIfNecessary(
2371 aStyle.GetVisitedDependentColor(&nsStyleBackground::mBackgroundColor));
2372 }
2373
ComputeColor() const2374 DeviceColor nsDisplaySelectionOverlay::ComputeColor() const {
2375 LookAndFeel::ColorID colorID;
2376 if (RefPtr<ComputedStyle> style =
2377 mFrame->ComputeSelectionStyle(mSelectionValue)) {
2378 return ComputeColorFromSelectionStyle(*style);
2379 }
2380 if (mSelectionValue == nsISelectionController::SELECTION_ON) {
2381 colorID = LookAndFeel::ColorID::TextSelectBackground;
2382 } else if (mSelectionValue == nsISelectionController::SELECTION_ATTENTION) {
2383 colorID = LookAndFeel::ColorID::TextSelectBackgroundAttention;
2384 } else {
2385 colorID = LookAndFeel::ColorID::TextSelectBackgroundDisabled;
2386 }
2387
2388 return ApplyTransparencyIfNecessary(
2389 LookAndFeel::GetColor(colorID, NS_RGB(255, 255, 255)));
2390 }
2391
Paint(nsDisplayListBuilder * aBuilder,gfxContext * aCtx)2392 void nsDisplaySelectionOverlay::Paint(nsDisplayListBuilder* aBuilder,
2393 gfxContext* aCtx) {
2394 DrawTarget& aDrawTarget = *aCtx->GetDrawTarget();
2395 ColorPattern color(ComputeColor());
2396
2397 nsIntRect pxRect = GetPaintRect().ToOutsidePixels(
2398 mFrame->PresContext()->AppUnitsPerDevPixel());
2399 Rect rect(pxRect.x, pxRect.y, pxRect.width, pxRect.height);
2400 MaybeSnapToDevicePixels(rect, aDrawTarget, true);
2401
2402 aDrawTarget.FillRect(rect, color);
2403 }
2404
CreateWebRenderCommands(mozilla::wr::DisplayListBuilder & aBuilder,mozilla::wr::IpcResourceUpdateQueue & aResources,const StackingContextHelper & aSc,mozilla::layers::RenderRootStateManager * aManager,nsDisplayListBuilder * aDisplayListBuilder)2405 bool nsDisplaySelectionOverlay::CreateWebRenderCommands(
2406 mozilla::wr::DisplayListBuilder& aBuilder,
2407 mozilla::wr::IpcResourceUpdateQueue& aResources,
2408 const StackingContextHelper& aSc,
2409 mozilla::layers::RenderRootStateManager* aManager,
2410 nsDisplayListBuilder* aDisplayListBuilder) {
2411 wr::LayoutRect bounds = wr::ToLayoutRect(LayoutDeviceRect::FromAppUnits(
2412 nsRect(ToReferenceFrame(), Frame()->GetSize()),
2413 mFrame->PresContext()->AppUnitsPerDevPixel()));
2414 aBuilder.PushRect(bounds, bounds, !BackfaceIsHidden(),
2415 wr::ToColorF(ComputeColor()));
2416 return true;
2417 }
2418
FindElementAncestorForMozSelection(nsIContent * aContent)2419 static Element* FindElementAncestorForMozSelection(nsIContent* aContent) {
2420 NS_ENSURE_TRUE(aContent, nullptr);
2421 while (aContent && aContent->IsInNativeAnonymousSubtree()) {
2422 aContent = aContent->GetClosestNativeAnonymousSubtreeRootParent();
2423 }
2424 NS_ASSERTION(aContent, "aContent isn't in non-anonymous tree?");
2425 return aContent ? aContent->GetAsElementOrParentElement() : nullptr;
2426 }
2427
ComputeSelectionStyle(int16_t aSelectionStatus) const2428 already_AddRefed<ComputedStyle> nsIFrame::ComputeSelectionStyle(
2429 int16_t aSelectionStatus) const {
2430 // Just bail out if not a selection-status that ::selection applies to.
2431 if (aSelectionStatus != nsISelectionController::SELECTION_ON &&
2432 aSelectionStatus != nsISelectionController::SELECTION_DISABLED) {
2433 return nullptr;
2434 }
2435 // When in high-contrast mode, the style system ends up ignoring the color
2436 // declarations, which means that the ::selection style becomes the inherited
2437 // color, and default background. That's no good.
2438 if (!PresContext()->PrefSheetPrefs().mUseDocumentColors) {
2439 return nullptr;
2440 }
2441 Element* element = FindElementAncestorForMozSelection(GetContent());
2442 if (!element) {
2443 return nullptr;
2444 }
2445 return PresContext()->StyleSet()->ProbePseudoElementStyle(
2446 *element, PseudoStyleType::selection, Style());
2447 }
2448
2449 template <typename SizeOrMaxSize>
IsIntrinsicKeyword(const SizeOrMaxSize & aSize)2450 static inline bool IsIntrinsicKeyword(const SizeOrMaxSize& aSize) {
2451 if (!aSize.IsExtremumLength()) {
2452 return false;
2453 }
2454
2455 // All of the keywords except for '-moz-available' depend on intrinsic sizes.
2456 return aSize.AsExtremumLength() != StyleExtremumLength::MozAvailable;
2457 }
2458
CanBeDynamicReflowRoot() const2459 bool nsIFrame::CanBeDynamicReflowRoot() const {
2460 if (!StaticPrefs::layout_dynamic_reflow_roots_enabled()) {
2461 return false;
2462 }
2463
2464 auto& display = *StyleDisplay();
2465 if (IsFrameOfType(nsIFrame::eLineParticipant) ||
2466 nsStyleDisplay::IsRubyDisplayType(display.mDisplay) ||
2467 display.DisplayOutside() == StyleDisplayOutside::InternalTable ||
2468 display.DisplayInside() == StyleDisplayInside::Table ||
2469 (GetParent() && GetParent()->IsXULBoxFrame())) {
2470 // We have a display type where 'width' and 'height' don't actually set the
2471 // width or height (i.e., the size depends on content).
2472 MOZ_ASSERT(!HasAnyStateBits(NS_FRAME_DYNAMIC_REFLOW_ROOT),
2473 "should not have dynamic reflow root bit");
2474 return false;
2475 }
2476
2477 // We can't serve as a dynamic reflow root if our used 'width' and 'height'
2478 // might be influenced by content.
2479 //
2480 // FIXME: For display:block, we should probably optimize inline-size: auto.
2481 // FIXME: Other flex and grid cases?
2482 auto& pos = *StylePosition();
2483 const auto& width = pos.mWidth;
2484 const auto& height = pos.mHeight;
2485 if (!width.IsLengthPercentage() || width.HasPercent() ||
2486 !height.IsLengthPercentage() || height.HasPercent() ||
2487 IsIntrinsicKeyword(pos.mMinWidth) || IsIntrinsicKeyword(pos.mMaxWidth) ||
2488 IsIntrinsicKeyword(pos.mMinHeight) ||
2489 IsIntrinsicKeyword(pos.mMaxHeight) ||
2490 ((pos.mMinWidth.IsAuto() || pos.mMinHeight.IsAuto()) &&
2491 IsFlexOrGridItem())) {
2492 return false;
2493 }
2494
2495 // If our flex-basis is 'auto', it'll defer to 'width' (or 'height') which
2496 // we've already checked. Otherwise, it preempts them, so we need to
2497 // perform the same "could-this-value-be-influenced-by-content" checks that
2498 // we performed for 'width' and 'height' above.
2499 if (IsFlexItem()) {
2500 const auto& flexBasis = pos.mFlexBasis;
2501 if (!flexBasis.IsAuto()) {
2502 if (!flexBasis.IsSize() || !flexBasis.AsSize().IsLengthPercentage() ||
2503 flexBasis.AsSize().HasPercent()) {
2504 return false;
2505 }
2506 }
2507 }
2508
2509 if (!IsFixedPosContainingBlock()) {
2510 // We can't treat this frame as a reflow root, since dynamic changes
2511 // to absolutely-positioned frames inside of it require that we
2512 // reflow the placeholder before we reflow the absolutely positioned
2513 // frame.
2514 // FIXME: Alternatively, we could sort the reflow roots in
2515 // PresShell::ProcessReflowCommands by depth in the tree, from
2516 // deepest to least deep. However, for performance (FIXME) we
2517 // should really be sorting them in the opposite order!
2518 return false;
2519 }
2520
2521 // If we participate in a container's block reflow context, or margins
2522 // can collapse through us, we can't be a dynamic reflow root.
2523 if (IsBlockFrameOrSubclass() &&
2524 !HasAllStateBits(NS_BLOCK_FLOAT_MGR | NS_BLOCK_MARGIN_ROOT)) {
2525 return false;
2526 }
2527
2528 // Subgrids are never reflow roots, but 'contain:layout/paint' prevents
2529 // creating a subgrid in the first place.
2530 if (pos.mGridTemplateColumns.IsSubgrid() ||
2531 pos.mGridTemplateRows.IsSubgrid()) {
2532 // NOTE: we could check that 'display' of our parent's primary frame is
2533 // '[inline-]grid' here but that's probably not worth it in practice.
2534 if (!display.IsContainLayout() && !display.IsContainPaint()) {
2535 return false;
2536 }
2537 }
2538
2539 return true;
2540 }
2541
2542 /********************************************************
2543 * Refreshes each content's frame
2544 *********************************************************/
2545
DisplaySelectionOverlay(nsDisplayListBuilder * aBuilder,nsDisplayList * aList,uint16_t aContentType)2546 void nsContainerFrame::DisplaySelectionOverlay(nsDisplayListBuilder* aBuilder,
2547 nsDisplayList* aList,
2548 uint16_t aContentType) {
2549 if (!IsSelected() || !IsVisibleForPainting()) {
2550 return;
2551 }
2552
2553 int16_t displaySelection = PresShell()->GetSelectionFlags();
2554 if (!(displaySelection & aContentType)) {
2555 return;
2556 }
2557
2558 const nsFrameSelection* frameSelection = GetConstFrameSelection();
2559 int16_t selectionValue = frameSelection->GetDisplaySelection();
2560
2561 if (selectionValue <= nsISelectionController::SELECTION_HIDDEN) {
2562 return; // selection is hidden or off
2563 }
2564
2565 nsIContent* newContent = mContent->GetParent();
2566
2567 // check to see if we are anonymous content
2568 int32_t offset = 0;
2569 if (newContent) {
2570 // XXXbz there has GOT to be a better way of determining this!
2571 offset = newContent->ComputeIndexOf(mContent);
2572 }
2573
2574 // look up to see what selection(s) are on this frame
2575 UniquePtr<SelectionDetails> details =
2576 frameSelection->LookUpSelection(newContent, offset, 1, false);
2577 if (!details) return;
2578
2579 bool normal = false;
2580 for (SelectionDetails* sd = details.get(); sd; sd = sd->mNext.get()) {
2581 if (sd->mSelectionType == SelectionType::eNormal) {
2582 normal = true;
2583 }
2584 }
2585
2586 if (!normal && aContentType == nsISelectionDisplay::DISPLAY_IMAGES) {
2587 // Don't overlay an image if it's not in the primary selection.
2588 return;
2589 }
2590
2591 aList->AppendNewToTop<nsDisplaySelectionOverlay>(aBuilder, this,
2592 selectionValue);
2593 }
2594
DisplayOutlineUnconditional(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)2595 void nsFrame::DisplayOutlineUnconditional(nsDisplayListBuilder* aBuilder,
2596 const nsDisplayListSet& aLists) {
2597 // Per https://drafts.csswg.org/css-tables-3/#global-style-overrides:
2598 // "All css properties of table-column and table-column-group boxes are
2599 // ignored, except when explicitly specified by this specification."
2600 // CSS outlines fall into this category, so we skip them on these boxes.
2601 MOZ_ASSERT(!IsTableColGroupFrame() && !IsTableColFrame());
2602 const auto& outline = *StyleOutline();
2603
2604 if (!outline.ShouldPaintOutline()) {
2605 return;
2606 }
2607
2608 // Outlines are painted by the table wrapper frame.
2609 if (IsTableFrame()) {
2610 return;
2611 }
2612
2613 if (HasAnyStateBits(NS_FRAME_PART_OF_IBSPLIT) &&
2614 GetScrollableOverflowRect().IsEmpty()) {
2615 // Skip parts of IB-splits with an empty overflow rect, see bug 434301.
2616 // We may still want to fix some of the overflow area calculations over in
2617 // that bug.
2618 return;
2619 }
2620
2621 // We don't display outline-style: auto on themed frames that have their own
2622 // focus indicators.
2623 if (outline.mOutlineStyle.IsAuto()) {
2624 auto* disp = StyleDisplay();
2625 if (IsThemed(disp) &&
2626 PresContext()->Theme()->ThemeDrawsFocusForWidget(disp->mAppearance)) {
2627 return;
2628 }
2629 }
2630
2631 aLists.Outlines()->AppendNewToTop<nsDisplayOutline>(aBuilder, this);
2632 }
2633
DisplayOutline(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists)2634 void nsFrame::DisplayOutline(nsDisplayListBuilder* aBuilder,
2635 const nsDisplayListSet& aLists) {
2636 if (!IsVisibleForPainting()) return;
2637
2638 DisplayOutlineUnconditional(aBuilder, aLists);
2639 }
2640
DisplayInsetBoxShadowUnconditional(nsDisplayListBuilder * aBuilder,nsDisplayList * aList)2641 void nsFrame::DisplayInsetBoxShadowUnconditional(nsDisplayListBuilder* aBuilder,
2642 nsDisplayList* aList) {
2643 // XXXbz should box-shadow for rows/rowgroups/columns/colgroups get painted
2644 // just because we're visible? Or should it depend on the cell visibility
2645 // when we're not the whole table?
2646 const auto* effects = StyleEffects();
2647 if (effects->HasBoxShadowWithInset(true)) {
2648 aList->AppendNewToTop<nsDisplayBoxShadowInner>(aBuilder, this);
2649 }
2650 }
2651
DisplayInsetBoxShadow(nsDisplayListBuilder * aBuilder,nsDisplayList * aList)2652 void nsFrame::DisplayInsetBoxShadow(nsDisplayListBuilder* aBuilder,
2653 nsDisplayList* aList) {
2654 if (!IsVisibleForPainting()) return;
2655
2656 DisplayInsetBoxShadowUnconditional(aBuilder, aList);
2657 }
2658
DisplayOutsetBoxShadowUnconditional(nsDisplayListBuilder * aBuilder,nsDisplayList * aList)2659 void nsFrame::DisplayOutsetBoxShadowUnconditional(
2660 nsDisplayListBuilder* aBuilder, nsDisplayList* aList) {
2661 // XXXbz should box-shadow for rows/rowgroups/columns/colgroups get painted
2662 // just because we're visible? Or should it depend on the cell visibility
2663 // when we're not the whole table?
2664 const auto* effects = StyleEffects();
2665 if (effects->HasBoxShadowWithInset(false)) {
2666 aList->AppendNewToTop<nsDisplayBoxShadowOuter>(aBuilder, this);
2667 }
2668 }
2669
DisplayOutsetBoxShadow(nsDisplayListBuilder * aBuilder,nsDisplayList * aList)2670 void nsFrame::DisplayOutsetBoxShadow(nsDisplayListBuilder* aBuilder,
2671 nsDisplayList* aList) {
2672 if (!IsVisibleForPainting()) return;
2673
2674 DisplayOutsetBoxShadowUnconditional(aBuilder, aList);
2675 }
2676
DisplayCaret(nsDisplayListBuilder * aBuilder,nsDisplayList * aList)2677 void nsIFrame::DisplayCaret(nsDisplayListBuilder* aBuilder,
2678 nsDisplayList* aList) {
2679 if (!IsVisibleForPainting()) return;
2680
2681 aList->AppendNewToTop<nsDisplayCaret>(aBuilder, this);
2682 }
2683
GetCaretColorAt(int32_t aOffset)2684 nscolor nsIFrame::GetCaretColorAt(int32_t aOffset) {
2685 return nsLayoutUtils::GetColor(this, &nsStyleUI::mCaretColor);
2686 }
2687
DisplayBackgroundUnconditional(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists,bool aForceBackground)2688 bool nsFrame::DisplayBackgroundUnconditional(nsDisplayListBuilder* aBuilder,
2689 const nsDisplayListSet& aLists,
2690 bool aForceBackground) {
2691 const bool hitTesting = aBuilder->IsForEventDelivery();
2692 if (hitTesting && !aBuilder->HitTestIsForVisibility()) {
2693 // For hit-testing, we generally just need a light-weight data structure
2694 // like nsDisplayEventReceiver. But if the hit-testing is for visibility,
2695 // then we need to know the opaque region in order to determine whether to
2696 // stop or not.
2697 aLists.BorderBackground()->AppendNewToTop<nsDisplayEventReceiver>(aBuilder,
2698 this);
2699 return false;
2700 }
2701
2702 // Here we don't try to detect background propagation. Frames that might
2703 // receive a propagated background should just set aForceBackground to
2704 // true.
2705 if (hitTesting || aForceBackground ||
2706 !StyleBackground()->IsTransparent(this) ||
2707 StyleDisplay()->HasAppearance()) {
2708 return nsDisplayBackgroundImage::AppendBackgroundItemsToTop(
2709 aBuilder, this,
2710 GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this),
2711 aLists.BorderBackground());
2712 }
2713
2714 return false;
2715 }
2716
DisplayBorderBackgroundOutline(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aLists,bool aForceBackground)2717 void nsFrame::DisplayBorderBackgroundOutline(nsDisplayListBuilder* aBuilder,
2718 const nsDisplayListSet& aLists,
2719 bool aForceBackground) {
2720 // The visibility check belongs here since child elements have the
2721 // opportunity to override the visibility property and display even if
2722 // their parent is hidden.
2723 if (!IsVisibleForPainting()) {
2724 return;
2725 }
2726
2727 DisplayOutsetBoxShadowUnconditional(aBuilder, aLists.BorderBackground());
2728
2729 bool bgIsThemed =
2730 DisplayBackgroundUnconditional(aBuilder, aLists, aForceBackground);
2731
2732 DisplayInsetBoxShadowUnconditional(aBuilder, aLists.BorderBackground());
2733
2734 // If there's a themed background, we should not create a border item.
2735 // It won't be rendered.
2736 // Don't paint borders for tables here, since they paint them in a different
2737 // order.
2738 if (!bgIsThemed && StyleBorder()->HasBorder() && !IsTableFrame()) {
2739 aLists.BorderBackground()->AppendNewToTop<nsDisplayBorder>(aBuilder, this);
2740 }
2741
2742 DisplayOutlineUnconditional(aBuilder, aLists);
2743 }
2744
IsSVGContentWithCSSClip(const nsIFrame * aFrame)2745 inline static bool IsSVGContentWithCSSClip(const nsIFrame* aFrame) {
2746 // The CSS spec says that the 'clip' property only applies to absolutely
2747 // positioned elements, whereas the SVG spec says that it applies to SVG
2748 // elements regardless of the value of the 'position' property. Here we obey
2749 // the CSS spec for outer-<svg> (since that's what we generally do), but
2750 // obey the SVG spec for other SVG elements to which 'clip' applies.
2751 return (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) &&
2752 aFrame->GetContent()->IsAnyOfSVGElements(nsGkAtoms::svg,
2753 nsGkAtoms::foreignObject);
2754 }
2755
FormsBackdropRoot(const nsStyleDisplay * aStyleDisplay,const nsStyleEffects * aStyleEffects,const nsStyleSVGReset * aStyleSVGReset)2756 bool nsIFrame::FormsBackdropRoot(const nsStyleDisplay* aStyleDisplay,
2757 const nsStyleEffects* aStyleEffects,
2758 const nsStyleSVGReset* aStyleSVGReset) {
2759 // Check if this is a root frame.
2760 if (!GetParent()) {
2761 return true;
2762 }
2763
2764 // Check for filter effects.
2765 if (aStyleEffects->HasFilters() || aStyleEffects->HasBackdropFilters() ||
2766 aStyleEffects->HasMixBlendMode()) {
2767 return true;
2768 }
2769
2770 // Check for opacity.
2771 if (HasOpacity(aStyleDisplay, aStyleEffects)) {
2772 return true;
2773 }
2774
2775 // Check for mask or clip path.
2776 if (aStyleSVGReset->HasMask() || aStyleSVGReset->HasClipPath()) {
2777 return true;
2778 }
2779
2780 // TODO(cbrewster): Check will-change attributes
2781
2782 return false;
2783 }
2784
GetClipPropClipRect(const nsStyleDisplay * aDisp,const nsStyleEffects * aEffects,const nsSize & aSize) const2785 Maybe<nsRect> nsIFrame::GetClipPropClipRect(const nsStyleDisplay* aDisp,
2786 const nsStyleEffects* aEffects,
2787 const nsSize& aSize) const {
2788 if (aEffects->mClip.IsAuto() ||
2789 !(aDisp->IsAbsolutelyPositioned(this) || IsSVGContentWithCSSClip(this))) {
2790 return Nothing();
2791 }
2792
2793 auto& clipRect = aEffects->mClip.AsRect();
2794 nsRect rect = clipRect.ToLayoutRect();
2795 if (MOZ_LIKELY(StyleBorder()->mBoxDecorationBreak ==
2796 StyleBoxDecorationBreak::Slice)) {
2797 // The clip applies to the joined boxes so it's relative the first
2798 // continuation.
2799 nscoord y = 0;
2800 for (nsIFrame* f = GetPrevContinuation(); f; f = f->GetPrevContinuation()) {
2801 y += f->GetRect().height;
2802 }
2803 rect.MoveBy(nsPoint(0, -y));
2804 }
2805
2806 if (clipRect.right.IsAuto()) {
2807 rect.width = aSize.width - rect.x;
2808 }
2809 if (clipRect.bottom.IsAuto()) {
2810 rect.height = aSize.height - rect.y;
2811 }
2812 return Some(rect);
2813 }
2814
2815 /**
2816 * If the CSS 'overflow' property applies to this frame, and is not
2817 * handled by constructing a dedicated nsHTML/XULScrollFrame, set up clipping
2818 * for that overflow in aBuilder->ClipState() to clip all containing-block
2819 * descendants.
2820 *
2821 * Return true if clipping was applied.
2822 */
ApplyOverflowClipping(nsDisplayListBuilder * aBuilder,const nsIFrame * aFrame,DisplayListClipState::AutoClipMultiple & aClipState)2823 static void ApplyOverflowClipping(
2824 nsDisplayListBuilder* aBuilder, const nsIFrame* aFrame,
2825 DisplayListClipState::AutoClipMultiple& aClipState) {
2826 // Only -moz-hidden-unscrollable is handled here (and 'hidden' for table
2827 // frames, and any non-visible value for blocks in a paginated context).
2828 // We allow -moz-hidden-unscrollable to apply to any kind of frame. This
2829 // is required by comboboxes which make their display text (an inline frame)
2830 // have clipping.
2831 MOZ_ASSERT(
2832 nsFrame::ShouldApplyOverflowClipping(aFrame, aFrame->StyleDisplay()));
2833
2834 nsRect clipRect;
2835 bool haveRadii = false;
2836 nscoord radii[8];
2837 auto* disp = aFrame->StyleDisplay();
2838 // Only deflate the padding if we clip to the content-box in that axis.
2839 auto wm = aFrame->GetWritingMode();
2840 bool cbH = (wm.IsVertical() ? disp->mOverflowClipBoxBlock
2841 : disp->mOverflowClipBoxInline) ==
2842 StyleOverflowClipBox::ContentBox;
2843 bool cbV = (wm.IsVertical() ? disp->mOverflowClipBoxInline
2844 : disp->mOverflowClipBoxBlock) ==
2845 StyleOverflowClipBox::ContentBox;
2846 nsMargin bp = aFrame->GetUsedPadding();
2847 if (!cbH) {
2848 bp.left = bp.right = nscoord(0);
2849 }
2850 if (!cbV) {
2851 bp.top = bp.bottom = nscoord(0);
2852 }
2853
2854 bp += aFrame->GetUsedBorder();
2855 bp.ApplySkipSides(aFrame->GetSkipSides());
2856 nsRect rect(nsPoint(0, 0), aFrame->GetSize());
2857 rect.Deflate(bp);
2858 clipRect = rect + aBuilder->ToReferenceFrame(aFrame);
2859 haveRadii = aFrame->GetBoxBorderRadii(radii, bp, false);
2860 aClipState.ClipContainingBlockDescendantsExtra(clipRect,
2861 haveRadii ? radii : nullptr);
2862 }
2863
2864 #ifdef DEBUG
PaintDebugBorder(nsIFrame * aFrame,DrawTarget * aDrawTarget,const nsRect & aDirtyRect,nsPoint aPt)2865 static void PaintDebugBorder(nsIFrame* aFrame, DrawTarget* aDrawTarget,
2866 const nsRect& aDirtyRect, nsPoint aPt) {
2867 nsRect r(aPt, aFrame->GetSize());
2868 int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
2869 sRGBColor blueOrRed(aFrame->HasView() ? sRGBColor(0.f, 0.f, 1.f, 1.f)
2870 : sRGBColor(1.f, 0.f, 0.f, 1.f));
2871 aDrawTarget->StrokeRect(NSRectToRect(r, appUnitsPerDevPixel),
2872 ColorPattern(ToDeviceColor(blueOrRed)));
2873 }
2874
PaintEventTargetBorder(nsIFrame * aFrame,DrawTarget * aDrawTarget,const nsRect & aDirtyRect,nsPoint aPt)2875 static void PaintEventTargetBorder(nsIFrame* aFrame, DrawTarget* aDrawTarget,
2876 const nsRect& aDirtyRect, nsPoint aPt) {
2877 nsRect r(aPt, aFrame->GetSize());
2878 int32_t appUnitsPerDevPixel = aFrame->PresContext()->AppUnitsPerDevPixel();
2879 ColorPattern purple(ToDeviceColor(sRGBColor(.5f, 0.f, .5f, 1.f)));
2880 aDrawTarget->StrokeRect(NSRectToRect(r, appUnitsPerDevPixel), purple);
2881 }
2882
DisplayDebugBorders(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,const nsDisplayListSet & aLists)2883 static void DisplayDebugBorders(nsDisplayListBuilder* aBuilder,
2884 nsIFrame* aFrame,
2885 const nsDisplayListSet& aLists) {
2886 // Draw a border around the child
2887 // REVIEW: From nsContainerFrame::PaintChild
2888 if (nsFrame::GetShowFrameBorders() && !aFrame->GetRect().IsEmpty()) {
2889 aLists.Outlines()->AppendNewToTop<nsDisplayGeneric>(
2890 aBuilder, aFrame, PaintDebugBorder, "DebugBorder",
2891 DisplayItemType::TYPE_DEBUG_BORDER);
2892 }
2893 // Draw a border around the current event target
2894 if (nsFrame::GetShowEventTargetFrameBorder() &&
2895 aFrame->PresShell()->GetDrawEventTargetFrame() == aFrame) {
2896 aLists.Outlines()->AppendNewToTop<nsDisplayGeneric>(
2897 aBuilder, aFrame, PaintEventTargetBorder, "EventTargetBorder",
2898 DisplayItemType::TYPE_EVENT_TARGET_BORDER);
2899 }
2900 }
2901 #endif
2902
IsScrollFrameActive(nsDisplayListBuilder * aBuilder,nsIScrollableFrame * aScrollableFrame)2903 static bool IsScrollFrameActive(nsDisplayListBuilder* aBuilder,
2904 nsIScrollableFrame* aScrollableFrame) {
2905 return aScrollableFrame && aScrollableFrame->IsScrollingActive(aBuilder);
2906 }
2907
2908 /**
2909 * Returns whether a display item that gets created with the builder's current
2910 * state will have a scrolled clip, i.e. a clip that is scrolled by a scroll
2911 * frame which does not move the item itself.
2912 */
BuilderHasScrolledClip(nsDisplayListBuilder * aBuilder)2913 static bool BuilderHasScrolledClip(nsDisplayListBuilder* aBuilder) {
2914 const DisplayItemClipChain* currentClip =
2915 aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder);
2916 if (!currentClip) {
2917 return false;
2918 }
2919
2920 const ActiveScrolledRoot* currentClipASR = currentClip->mASR;
2921 const ActiveScrolledRoot* currentASR = aBuilder->CurrentActiveScrolledRoot();
2922 return ActiveScrolledRoot::PickDescendant(currentClipASR, currentASR) !=
2923 currentASR;
2924 }
2925
2926 class AutoSaveRestoreContainsBlendMode {
2927 nsDisplayListBuilder& mBuilder;
2928 bool mSavedContainsBlendMode;
2929
2930 public:
AutoSaveRestoreContainsBlendMode(nsDisplayListBuilder & aBuilder)2931 explicit AutoSaveRestoreContainsBlendMode(nsDisplayListBuilder& aBuilder)
2932 : mBuilder(aBuilder),
2933 mSavedContainsBlendMode(aBuilder.ContainsBlendMode()) {}
2934
~AutoSaveRestoreContainsBlendMode()2935 ~AutoSaveRestoreContainsBlendMode() {
2936 mBuilder.SetContainsBlendMode(mSavedContainsBlendMode);
2937 }
2938 };
2939
2940 class AutoSaveRestoreContainsBackdropFilter {
2941 nsDisplayListBuilder& mBuilder;
2942 bool mSavedContainsBackdropFilter;
2943
2944 public:
AutoSaveRestoreContainsBackdropFilter(nsDisplayListBuilder & aBuilder)2945 explicit AutoSaveRestoreContainsBackdropFilter(nsDisplayListBuilder& aBuilder)
2946 : mBuilder(aBuilder),
2947 mSavedContainsBackdropFilter(aBuilder.ContainsBackdropFilter()) {}
2948
2949 /**
2950 * This is called if a stacking context which does not form a backdrop root
2951 * contains a descendent with a backdrop filter. In this case we need to
2952 * delegate backdrop root creation to the next parent in the tree until we hit
2953 * the nearest backdrop root ancestor.
2954 */
DelegateUp(bool aContainsBackdropFilter)2955 void DelegateUp(bool aContainsBackdropFilter) {
2956 mSavedContainsBackdropFilter = aContainsBackdropFilter;
2957 }
2958
~AutoSaveRestoreContainsBackdropFilter()2959 ~AutoSaveRestoreContainsBackdropFilter() {
2960 mBuilder.SetContainsBackdropFilter(mSavedContainsBackdropFilter);
2961 }
2962 };
2963
CheckForApzAwareEventHandlers(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame)2964 static void CheckForApzAwareEventHandlers(nsDisplayListBuilder* aBuilder,
2965 nsIFrame* aFrame) {
2966 if (aBuilder->GetAncestorHasApzAwareEventHandler()) {
2967 return;
2968 }
2969
2970 nsIContent* content = aFrame->GetContent();
2971 if (!content) {
2972 return;
2973 }
2974
2975 if (content->IsNodeApzAware()) {
2976 aBuilder->SetAncestorHasApzAwareEventHandler(true);
2977 }
2978 }
2979
2980 /**
2981 * True if aDescendant participates the context aAncestor participating.
2982 */
FrameParticipatesIn3DContext(nsIFrame * aAncestor,nsIFrame * aDescendant)2983 static bool FrameParticipatesIn3DContext(nsIFrame* aAncestor,
2984 nsIFrame* aDescendant) {
2985 MOZ_ASSERT(aAncestor != aDescendant);
2986 MOZ_ASSERT(aAncestor->GetContent() != aDescendant->GetContent());
2987 MOZ_ASSERT(aAncestor->Extend3DContext());
2988
2989 nsIFrame* ancestor = aAncestor->FirstContinuation();
2990 MOZ_ASSERT(ancestor->IsPrimaryFrame());
2991
2992 nsIFrame* frame;
2993 for (frame = aDescendant->GetClosestFlattenedTreeAncestorPrimaryFrame();
2994 frame && ancestor != frame;
2995 frame = frame->GetClosestFlattenedTreeAncestorPrimaryFrame()) {
2996 if (!frame->Extend3DContext()) {
2997 return false;
2998 }
2999 }
3000
3001 MOZ_ASSERT(frame == ancestor);
3002 return true;
3003 }
3004
ItemParticipatesIn3DContext(nsIFrame * aAncestor,nsDisplayItem * aItem)3005 static bool ItemParticipatesIn3DContext(nsIFrame* aAncestor,
3006 nsDisplayItem* aItem) {
3007 auto type = aItem->GetType();
3008 const bool isContainer = type == DisplayItemType::TYPE_WRAP_LIST ||
3009 type == DisplayItemType::TYPE_CONTAINER;
3010
3011 if (isContainer && aItem->GetChildren()->Count() == 1) {
3012 // If the wraplist has only one child item, use the type of that item.
3013 type = aItem->GetChildren()->GetBottom()->GetType();
3014 }
3015
3016 if (type != DisplayItemType::TYPE_TRANSFORM &&
3017 type != DisplayItemType::TYPE_PERSPECTIVE) {
3018 return false;
3019 }
3020 nsIFrame* transformFrame = aItem->Frame();
3021 if (aAncestor->GetContent() == transformFrame->GetContent()) {
3022 return true;
3023 }
3024 return FrameParticipatesIn3DContext(aAncestor, transformFrame);
3025 }
3026
WrapSeparatorTransform(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aNonParticipants,nsDisplayList * aParticipants,int aIndex,nsDisplayItem ** aSeparator)3027 static void WrapSeparatorTransform(nsDisplayListBuilder* aBuilder,
3028 nsIFrame* aFrame,
3029 nsDisplayList* aNonParticipants,
3030 nsDisplayList* aParticipants, int aIndex,
3031 nsDisplayItem** aSeparator) {
3032 if (aNonParticipants->IsEmpty()) {
3033 return;
3034 }
3035
3036 nsDisplayTransform* item = MakeDisplayItemWithIndex<nsDisplayTransform>(
3037 aBuilder, aFrame, aIndex, aNonParticipants, aBuilder->GetVisibleRect());
3038
3039 if (*aSeparator == nullptr && item) {
3040 *aSeparator = item;
3041 }
3042
3043 aParticipants->AppendToTop(item);
3044 }
3045
3046 // Try to compute a clip rect to bound the contents of the mask item
3047 // that will be built for |aMaskedFrame|. If we're not able to compute
3048 // one, return an empty Maybe.
3049 // The returned clip rect, if there is one, is relative to |aMaskedFrame|.
ComputeClipForMaskItem(nsDisplayListBuilder * aBuilder,nsIFrame * aMaskedFrame)3050 static Maybe<nsRect> ComputeClipForMaskItem(nsDisplayListBuilder* aBuilder,
3051 nsIFrame* aMaskedFrame) {
3052 const nsStyleSVGReset* svgReset = aMaskedFrame->StyleSVGReset();
3053
3054 nsSVGUtils::MaskUsage maskUsage;
3055 nsSVGUtils::DetermineMaskUsage(aMaskedFrame, false, maskUsage);
3056
3057 nsPoint offsetToUserSpace =
3058 nsLayoutUtils::ComputeOffsetToUserSpace(aBuilder, aMaskedFrame);
3059 int32_t devPixelRatio = aMaskedFrame->PresContext()->AppUnitsPerDevPixel();
3060 gfxPoint devPixelOffsetToUserSpace =
3061 nsLayoutUtils::PointToGfxPoint(offsetToUserSpace, devPixelRatio);
3062 gfxMatrix cssToDevMatrix = nsSVGUtils::GetCSSPxToDevPxMatrix(aMaskedFrame);
3063
3064 nsPoint toReferenceFrame;
3065 aBuilder->FindReferenceFrameFor(aMaskedFrame, &toReferenceFrame);
3066
3067 Maybe<gfxRect> combinedClip;
3068 if (maskUsage.shouldApplyBasicShapeOrPath) {
3069 Maybe<Rect> result =
3070 nsCSSClipPathInstance::GetBoundingRectForBasicShapeOrPathClip(
3071 aMaskedFrame, svgReset->mClipPath);
3072 if (result) {
3073 combinedClip = Some(ThebesRect(*result));
3074 }
3075 } else if (maskUsage.shouldApplyClipPath) {
3076 gfxRect result = nsSVGUtils::GetBBox(
3077 aMaskedFrame,
3078 nsSVGUtils::eBBoxIncludeClipped | nsSVGUtils::eBBoxIncludeFill |
3079 nsSVGUtils::eBBoxIncludeMarkers | nsSVGUtils::eBBoxIncludeStroke |
3080 nsSVGUtils::eDoNotClipToBBoxOfContentInsideClipPath);
3081 combinedClip = Some(cssToDevMatrix.TransformBounds(result));
3082 } else {
3083 // The code for this case is adapted from ComputeMaskGeometry().
3084
3085 nsRect borderArea(toReferenceFrame, aMaskedFrame->GetSize());
3086 borderArea -= offsetToUserSpace;
3087
3088 // Use an infinite dirty rect to pass into nsCSSRendering::
3089 // GetImageLayerClip() because we don't have an actual dirty rect to
3090 // pass in. This is fine because the only time GetImageLayerClip() will
3091 // not intersect the incoming dirty rect with something is in the "NoClip"
3092 // case, and we handle that specially.
3093 nsRect dirtyRect(nscoord_MIN / 2, nscoord_MIN / 2, nscoord_MAX,
3094 nscoord_MAX);
3095
3096 nsIFrame* firstFrame =
3097 nsLayoutUtils::FirstContinuationOrIBSplitSibling(aMaskedFrame);
3098 nsTArray<nsSVGMaskFrame*> maskFrames;
3099 // XXX check return value?
3100 SVGObserverUtils::GetAndObserveMasks(firstFrame, &maskFrames);
3101
3102 for (uint32_t i = 0; i < maskFrames.Length(); ++i) {
3103 gfxRect clipArea;
3104 if (maskFrames[i]) {
3105 clipArea = maskFrames[i]->GetMaskArea(aMaskedFrame);
3106 clipArea = cssToDevMatrix.TransformBounds(clipArea);
3107 } else {
3108 const auto& layer = svgReset->mMask.mLayers[i];
3109 if (layer.mClip == StyleGeometryBox::NoClip) {
3110 return Nothing();
3111 }
3112
3113 nsCSSRendering::ImageLayerClipState clipState;
3114 nsCSSRendering::GetImageLayerClip(
3115 layer, aMaskedFrame, *aMaskedFrame->StyleBorder(), borderArea,
3116 dirtyRect, false /* aWillPaintBorder */, devPixelRatio, &clipState);
3117 clipArea = clipState.mDirtyRectInDevPx;
3118 }
3119 combinedClip = UnionMaybeRects(combinedClip, Some(clipArea));
3120 }
3121 }
3122 if (combinedClip) {
3123 if (combinedClip->IsEmpty()) {
3124 // *clipForMask might be empty if all mask references are not resolvable
3125 // or the size of them are empty. We still need to create a transparent
3126 // mask before bug 1276834 fixed, so don't clip ctx by an empty rectangle
3127 // for for now.
3128 return Nothing();
3129 }
3130
3131 // Convert to user space.
3132 *combinedClip += devPixelOffsetToUserSpace;
3133
3134 // Round the clip out. In FrameLayerBuilder we round clips to nearest
3135 // pixels, and if we have a really thin clip here, that can cause the
3136 // clip to become empty if we didn't round out here.
3137 // The rounding happens in coordinates that are relative to the reference
3138 // frame, which matches what FrameLayerBuilder does.
3139 combinedClip->RoundOut();
3140
3141 // Convert to app units.
3142 nsRect result =
3143 nsLayoutUtils::RoundGfxRectToAppRect(*combinedClip, devPixelRatio);
3144
3145 // The resulting clip is relative to the reference frame, but the caller
3146 // expects it to be relative to the masked frame, so adjust it.
3147 result -= toReferenceFrame;
3148 return Some(result);
3149 }
3150 return Nothing();
3151 }
3152
3153 struct AutoCheckBuilder {
AutoCheckBuilderAutoCheckBuilder3154 explicit AutoCheckBuilder(nsDisplayListBuilder* aBuilder)
3155 : mBuilder(aBuilder) {
3156 aBuilder->Check();
3157 }
3158
~AutoCheckBuilderAutoCheckBuilder3159 ~AutoCheckBuilder() { mBuilder->Check(); }
3160
3161 nsDisplayListBuilder* mBuilder;
3162 };
3163
3164 /**
3165 * Helper class to track container creation. Stores the first tracked container.
3166 * Used to find the innermost container for hit test information, and to notify
3167 * callers whether a container item was created or not.
3168 */
3169 struct ContainerTracker {
TrackContainerContainerTracker3170 void TrackContainer(nsDisplayItem* aContainer) {
3171 if (!aContainer) {
3172 return;
3173 }
3174
3175 if (!mContainer) {
3176 mContainer = aContainer;
3177 }
3178
3179 mCreatedContainer = true;
3180 }
3181
ResetCreatedContainerContainerTracker3182 void ResetCreatedContainer() { mCreatedContainer = false; }
3183
3184 nsDisplayItem* mContainer = nullptr;
3185 bool mCreatedContainer = false;
3186 };
3187
3188 /**
3189 * Adds hit test information |aHitTestInfo| on the container item |aContainer|,
3190 * or if the container item is null, creates a separate hit test item that is
3191 * added to the bottom of the display list |aList|.
3192 */
AddHitTestInfo(nsDisplayListBuilder * aBuilder,nsDisplayList * aList,nsDisplayItem * aContainer,nsIFrame * aFrame,mozilla::UniquePtr<HitTestInfo> && aHitTestInfo)3193 static void AddHitTestInfo(nsDisplayListBuilder* aBuilder, nsDisplayList* aList,
3194 nsDisplayItem* aContainer, nsIFrame* aFrame,
3195 mozilla::UniquePtr<HitTestInfo>&& aHitTestInfo) {
3196 nsDisplayHitTestInfoBase* hitTestItem;
3197
3198 if (aContainer) {
3199 MOZ_ASSERT(aContainer->IsHitTestItem());
3200 hitTestItem = static_cast<nsDisplayHitTestInfoBase*>(aContainer);
3201 hitTestItem->SetHitTestInfo(std::move(aHitTestInfo));
3202 } else {
3203 // No container item was created for this frame. Create a separate
3204 // nsDisplayCompositorHitTestInfo item instead.
3205 aList->AppendNewToBottom<nsDisplayCompositorHitTestInfo>(
3206 aBuilder, aFrame, std::move(aHitTestInfo));
3207 }
3208 }
3209
BuildDisplayListForStackingContext(nsDisplayListBuilder * aBuilder,nsDisplayList * aList,bool * aCreatedContainerItem)3210 void nsIFrame::BuildDisplayListForStackingContext(
3211 nsDisplayListBuilder* aBuilder, nsDisplayList* aList,
3212 bool* aCreatedContainerItem) {
3213 AutoCheckBuilder check(aBuilder);
3214 if (GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE) return;
3215
3216 // Replaced elements have their visibility handled here, because
3217 // they're visually atomic
3218 if (IsFrameOfType(eReplaced) && !IsVisibleForPainting()) return;
3219
3220 const nsStyleDisplay* disp = StyleDisplay();
3221 const nsStyleEffects* effects = StyleEffects();
3222 EffectSet* effectSetForOpacity = EffectSet::GetEffectSetForFrame(
3223 this, nsCSSPropertyIDSet::OpacityProperties());
3224 // We can stop right away if this is a zero-opacity stacking context and
3225 // we're painting, and we're not animating opacity. Don't do this
3226 // if we're going to compute plugin geometry, since opacity-0 plugins
3227 // need to have display items built for them.
3228 bool needHitTestInfo =
3229 aBuilder->BuildCompositorHitTestInfo() &&
3230 StyleUI()->GetEffectivePointerEvents(this) != StylePointerEvents::None;
3231 bool opacityItemForEventsAndPluginsOnly = false;
3232 if (effects->mOpacity == 0.0 && aBuilder->IsForPainting() &&
3233 !(disp->mWillChange.bits & StyleWillChangeBits::OPACITY) &&
3234 !nsLayoutUtils::HasAnimationOfPropertySet(
3235 this, nsCSSPropertyIDSet::OpacityProperties(), effectSetForOpacity)) {
3236 if (needHitTestInfo || aBuilder->WillComputePluginGeometry()) {
3237 opacityItemForEventsAndPluginsOnly = true;
3238 } else {
3239 return;
3240 }
3241 }
3242
3243 if (aBuilder->IsForPainting() && disp->mWillChange.bits) {
3244 aBuilder->AddToWillChangeBudget(this, GetSize());
3245 }
3246
3247 // For preserves3d, use the dirty rect already installed on the
3248 // builder, since aDirtyRect maybe distorted for transforms along
3249 // the chain.
3250 nsRect visibleRect = aBuilder->GetVisibleRect();
3251 nsRect dirtyRect = aBuilder->GetDirtyRect();
3252
3253 // We build an opacity item if it's not going to be drawn by SVG content.
3254 // We could in principle skip creating an nsDisplayOpacity item if
3255 // nsDisplayOpacity::NeedsActiveLayer returns false and usingSVGEffects is
3256 // true (the nsDisplayFilter/nsDisplayMasksAndClipPaths could handle the
3257 // opacity). Since SVG has perf issues where we sometimes spend a lot of
3258 // time creating display list items that might be helpful. We'd need to
3259 // restore our mechanism to do that (changed in bug 1482403), and we'd
3260 // need to invalidate the frame if the value that would be return from
3261 // NeedsActiveLayer was to change, which we don't currently do.
3262 const bool useOpacity =
3263 HasVisualOpacity(disp, effects, effectSetForOpacity) &&
3264 !nsSVGUtils::CanOptimizeOpacity(this);
3265
3266 const bool isTransformed = IsTransformed(disp);
3267 const bool hasPerspective = isTransformed && HasPerspective(disp);
3268 const bool extend3DContext =
3269 Extend3DContext(disp, effects, effectSetForOpacity);
3270 const bool combines3DTransformWithAncestors =
3271 (extend3DContext || isTransformed) &&
3272 Combines3DTransformWithAncestors(disp);
3273
3274 Maybe<nsDisplayListBuilder::AutoPreserves3DContext> autoPreserves3DContext;
3275 if (extend3DContext && !combines3DTransformWithAncestors) {
3276 // Start a new preserves3d context to keep informations on
3277 // nsDisplayListBuilder.
3278 autoPreserves3DContext.emplace(aBuilder);
3279 // Save dirty rect on the builder to avoid being distorted for
3280 // multiple transforms along the chain.
3281 aBuilder->SavePreserves3DRect();
3282
3283 // We rebuild everything within preserve-3d and don't try
3284 // to retain, so override the dirty rect now.
3285 if (aBuilder->IsRetainingDisplayList()) {
3286 dirtyRect = visibleRect;
3287 aBuilder->SetDisablePartialUpdates(true);
3288 }
3289 }
3290
3291 const bool useBlendMode = effects->mMixBlendMode != StyleBlend::Normal;
3292 if (useBlendMode) {
3293 aBuilder->SetContainsBlendMode(true);
3294 }
3295
3296 // reset blend mode so we can keep track if this stacking context needs have
3297 // a nsDisplayBlendContainer. Set the blend mode back when the routine exits
3298 // so we keep track if the parent stacking context needs a container too.
3299 AutoSaveRestoreContainsBlendMode autoRestoreBlendMode(*aBuilder);
3300 aBuilder->SetContainsBlendMode(false);
3301
3302 bool usingBackdropFilter =
3303 effects->HasBackdropFilters() &&
3304 nsDisplayBackdropFilters::CanCreateWebRenderCommands(aBuilder, this);
3305
3306 if (usingBackdropFilter) {
3307 aBuilder->SetContainsBackdropFilter(true);
3308 }
3309
3310 AutoSaveRestoreContainsBackdropFilter autoRestoreBackdropFilter(*aBuilder);
3311 aBuilder->SetContainsBackdropFilter(false);
3312
3313 nsRect visibleRectOutsideTransform = visibleRect;
3314 bool allowAsyncAnimation = false;
3315 bool inTransform = aBuilder->IsInTransform();
3316 if (isTransformed) {
3317 nsDisplayTransform::PrerenderInfo decision =
3318 nsDisplayTransform::ShouldPrerenderTransformedContent(aBuilder, this,
3319 &dirtyRect);
3320
3321 switch (decision.mDecision) {
3322 case nsDisplayTransform::PrerenderDecision::Full:
3323 case nsDisplayTransform::PrerenderDecision::Partial:
3324 allowAsyncAnimation = true;
3325 visibleRect = dirtyRect;
3326 break;
3327 case nsDisplayTransform::PrerenderDecision::No: {
3328 // If we didn't prerender an animated frame in a preserve-3d context,
3329 // then we want disable async animations for the rest of the preserve-3d
3330 // (especially ancestors).
3331 if ((extend3DContext || combines3DTransformWithAncestors) &&
3332 decision.mHasAnimations) {
3333 aBuilder->SavePreserves3DAllowAsyncAnimation(false);
3334 }
3335
3336 const nsRect overflow = GetVisualOverflowRectRelativeToSelf();
3337 if (overflow.IsEmpty() && !extend3DContext) {
3338 return;
3339 }
3340
3341 // If we're in preserve-3d then grab the dirty rect that was given to
3342 // the root and transform using the combined transform.
3343 if (combines3DTransformWithAncestors) {
3344 visibleRect = dirtyRect = aBuilder->GetPreserves3DRect();
3345 }
3346
3347 nsRect untransformedDirtyRect;
3348 if (nsDisplayTransform::UntransformRect(dirtyRect, overflow, this,
3349 &untransformedDirtyRect)) {
3350 dirtyRect = untransformedDirtyRect;
3351 nsDisplayTransform::UntransformRect(visibleRect, overflow, this,
3352 &visibleRect);
3353 } else {
3354 // This should only happen if the transform is singular, in which case
3355 // nothing is visible anyway
3356 dirtyRect.SetEmpty();
3357 visibleRect.SetEmpty();
3358 }
3359 }
3360 }
3361 inTransform = true;
3362 } else if (IsFixedPosContainingBlock()) {
3363 // Restict the building area to the overflow rect for these frames, since
3364 // RetainedDisplayListBuilder uses it to know if the size of the stacking
3365 // context changed.
3366 visibleRect.IntersectRect(visibleRect, GetVisualOverflowRect());
3367 dirtyRect.IntersectRect(dirtyRect, GetVisualOverflowRect());
3368 }
3369
3370 bool hasOverrideDirtyRect = false;
3371 // If we're doing a partial build, we're not invalid and we're capable
3372 // of having an override building rect (stacking context and fixed pos
3373 // containing block), then we should assume we have one.
3374 // Either we have an explicit one, or nothing in our subtree changed and
3375 // we have an implicit empty rect.
3376 if (aBuilder->IsPartialUpdate() && !aBuilder->InInvalidSubtree() &&
3377 !IsFrameModified() && IsFixedPosContainingBlock()) {
3378 dirtyRect = nsRect();
3379 if (HasOverrideDirtyRegion()) {
3380 nsDisplayListBuilder::DisplayListBuildingData* data =
3381 GetProperty(nsDisplayListBuilder::DisplayListBuildingRect());
3382 if (data) {
3383 dirtyRect = data->mDirtyRect.Intersect(visibleRect);
3384 hasOverrideDirtyRect = true;
3385 }
3386 }
3387 }
3388
3389 bool usingFilter = effects->HasFilters();
3390 bool usingMask = nsSVGIntegrationUtils::UsingMaskOrClipPathForFrame(this);
3391 bool usingSVGEffects = usingFilter || usingMask;
3392
3393 nsRect visibleRectOutsideSVGEffects = visibleRect;
3394 nsDisplayList hoistedScrollInfoItemsStorage;
3395 if (usingSVGEffects) {
3396 dirtyRect =
3397 nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(this, dirtyRect);
3398 visibleRect = nsSVGIntegrationUtils::GetRequiredSourceForInvalidArea(
3399 this, visibleRect);
3400 aBuilder->EnterSVGEffectsContents(this, &hoistedScrollInfoItemsStorage);
3401 }
3402
3403 bool useStickyPosition =
3404 disp->mPosition == StylePositionProperty::Sticky &&
3405 IsScrollFrameActive(
3406 aBuilder,
3407 nsLayoutUtils::GetNearestScrollableFrame(
3408 GetParent(), nsLayoutUtils::SCROLLABLE_SAME_DOC |
3409 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN));
3410 bool useFixedPosition = disp->mPosition == StylePositionProperty::Fixed &&
3411 (nsLayoutUtils::IsFixedPosFrameInDisplayPort(this) ||
3412 BuilderHasScrolledClip(aBuilder));
3413
3414 nsDisplayListBuilder::AutoBuildingDisplayList buildingDisplayList(
3415 aBuilder, this, visibleRect, dirtyRect, isTransformed);
3416
3417 // Depending on the effects that are applied to this frame, we can create
3418 // multiple container display items and wrap them around our contents.
3419 // This enum lists all the potential container display items, in the order
3420 // outside to inside.
3421 enum class ContainerItemType : uint8_t {
3422 None = 0,
3423 OwnLayerIfNeeded,
3424 BlendMode,
3425 FixedPosition,
3426 OwnLayerForTransformWithRoundedClip,
3427 Perspective,
3428 Transform,
3429 SeparatorTransforms,
3430 Opacity,
3431 Filter,
3432 BlendContainer
3433 };
3434
3435 nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
3436
3437 auto cssClip = GetClipPropClipRect(disp, effects, GetSize());
3438 auto ApplyClipProp = [&](DisplayListClipState::AutoSaveRestore& aClipState) {
3439 if (!cssClip) {
3440 return;
3441 }
3442 nsPoint offset = aBuilder->GetCurrentFrameOffsetToReferenceFrame();
3443 aBuilder->IntersectDirtyRect(*cssClip);
3444 aBuilder->IntersectVisibleRect(*cssClip);
3445 aClipState.ClipContentDescendants(*cssClip + offset);
3446 };
3447
3448 // The CSS clip property is effectively inside the transform, but outside the
3449 // filters. So if we're not transformed we can apply it just here for
3450 // simplicity, instead of on each of the places that handle clipCapturedBy.
3451 DisplayListClipState::AutoSaveRestore untransformedCssClip(aBuilder);
3452 if (!isTransformed) {
3453 ApplyClipProp(untransformedCssClip);
3454 }
3455
3456 // If there is a current clip, then depending on the container items we
3457 // create, different things can happen to it. Some container items simply
3458 // propagate the clip to their children and aren't clipped themselves.
3459 // But other container items, especially those that establish a different
3460 // geometry for their contents (e.g. transforms), capture the clip on
3461 // themselves and unset the clip for their contents. If we create more than
3462 // one of those container items, the clip will be captured on the outermost
3463 // one and the inner container items will be unclipped.
3464 ContainerItemType clipCapturedBy = ContainerItemType::None;
3465 if (useFixedPosition) {
3466 clipCapturedBy = ContainerItemType::FixedPosition;
3467 } else if (isTransformed) {
3468 const DisplayItemClipChain* currentClip =
3469 aBuilder->ClipState().GetCurrentCombinedClipChain(aBuilder);
3470 if ((hasPerspective || extend3DContext) &&
3471 (currentClip && currentClip->HasRoundedCorners())) {
3472 // If we're creating an nsDisplayTransform item that is going to combine
3473 // its transform with its children (preserve-3d or perspective), then we
3474 // can't have an intermediate surface. Mask layers force an intermediate
3475 // surface, so if we're going to need both then create a separate
3476 // wrapping layer for the mask.
3477 clipCapturedBy = ContainerItemType::OwnLayerForTransformWithRoundedClip;
3478 } else if (hasPerspective) {
3479 clipCapturedBy = ContainerItemType::Perspective;
3480 } else {
3481 clipCapturedBy = ContainerItemType::Transform;
3482 }
3483 } else if (usingFilter) {
3484 clipCapturedBy = ContainerItemType::Filter;
3485 }
3486
3487 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
3488 if (clipCapturedBy != ContainerItemType::None) {
3489 clipState.Clear();
3490 }
3491
3492 DisplayListClipState::AutoSaveRestore transformedCssClip(aBuilder);
3493 if (isTransformed) {
3494 // FIXME(emilio, bug 1525159): In the case we have a both a transform _and_
3495 // filters, this clips the input to the filters as well, which is not
3496 // correct (clipping by the `clip` property is supposed to happen after
3497 // applying the filter effects, per [1].
3498 //
3499 // This is not a regression though, since we used to do that anyway before
3500 // bug 1514384, and even without the transform we get it wrong.
3501 //
3502 // [1]: https://drafts.fxtf.org/css-masking/#placement
3503 ApplyClipProp(transformedCssClip);
3504 }
3505
3506 mozilla::UniquePtr<HitTestInfo> hitTestInfo;
3507
3508 nsDisplayListCollection set(aBuilder);
3509 Maybe<nsRect> clipForMask;
3510 bool insertBackdropRoot;
3511 {
3512 DisplayListClipState::AutoSaveRestore nestedClipState(aBuilder);
3513 nsDisplayListBuilder::AutoInTransformSetter inTransformSetter(aBuilder,
3514 inTransform);
3515 nsDisplayListBuilder::AutoEnterFilter filterASRSetter(aBuilder,
3516 usingFilter);
3517 nsDisplayListBuilder::AutoInEventsAndPluginsOnly inEventsAndPluginsSetter(
3518 aBuilder, opacityItemForEventsAndPluginsOnly);
3519
3520 CheckForApzAwareEventHandlers(aBuilder, this);
3521
3522 if (usingMask) {
3523 clipForMask = ComputeClipForMaskItem(aBuilder, this);
3524 if (clipForMask) {
3525 aBuilder->IntersectDirtyRect(*clipForMask);
3526 aBuilder->IntersectVisibleRect(*clipForMask);
3527 nestedClipState.ClipContentDescendants(
3528 *clipForMask + aBuilder->GetCurrentFrameOffsetToReferenceFrame());
3529 }
3530 }
3531
3532 // extend3DContext also guarantees that applyAbsPosClipping and
3533 // usingSVGEffects are false We only modify the preserve-3d rect if we are
3534 // the top of a preserve-3d heirarchy
3535 if (extend3DContext) {
3536 // Mark these first so MarkAbsoluteFramesForDisplayList knows if we are
3537 // going to be forced to descend into frames.
3538 aBuilder->MarkPreserve3DFramesForDisplayList(this);
3539 }
3540
3541 aBuilder->AdjustWindowDraggingRegion(this);
3542
3543 if (gfxVars::UseWebRender()) {
3544 aBuilder->BuildCompositorHitTestInfoIfNeeded(this, set.BorderBackground(),
3545 true);
3546 } else {
3547 CompositorHitTestInfo info = aBuilder->BuildCompositorHitTestInfo()
3548 ? GetCompositorHitTestInfo(aBuilder)
3549 : CompositorHitTestInvisibleToHit;
3550
3551 if (info != CompositorHitTestInvisibleToHit) {
3552 // Frame has hit test flags set, initialize the hit test info structure.
3553 hitTestInfo = mozilla::MakeUnique<HitTestInfo>(aBuilder, this, info);
3554
3555 // Let child frames know the current hit test area and hit test flags.
3556 aBuilder->SetCompositorHitTestInfo(hitTestInfo->mArea,
3557 hitTestInfo->mFlags);
3558 }
3559 }
3560
3561 MarkAbsoluteFramesForDisplayList(aBuilder);
3562 aBuilder->Check();
3563 BuildDisplayList(aBuilder, set);
3564 aBuilder->Check();
3565 aBuilder->DisplayCaret(this, set.Outlines());
3566
3567 insertBackdropRoot = aBuilder->ContainsBackdropFilter() &&
3568 FormsBackdropRoot(disp, effects, StyleSVGReset());
3569
3570 // Blend modes are a real pain for retained display lists. We build a blend
3571 // container item if the built list contains any blend mode items within
3572 // the current stacking context. This can change without an invalidation
3573 // to the stacking context frame, or the blend mode frame (e.g. by moving
3574 // an intermediate frame).
3575 // When we gain/remove a blend container item, we need to mark this frame
3576 // as invalid and have the full display list for merging to track
3577 // the change correctly.
3578 // It seems really hard to track this in advance, as the bookkeeping
3579 // required to note which stacking contexts have blend descendants
3580 // is complex and likely to be buggy.
3581 // Instead we're doing the sad thing, detecting it afterwards, and just
3582 // repeating display list building if it changed.
3583 // We have to repeat building for the entire display list (or at least
3584 // the outer stacking context), since we need to mark this frame as invalid
3585 // to remove any existing content that isn't wrapped in the blend container,
3586 // and then we need to build content infront/behind the blend container
3587 // to get correct positioning during merging.
3588 if ((insertBackdropRoot || aBuilder->ContainsBlendMode()) &&
3589 aBuilder->IsRetainingDisplayList()) {
3590 if (aBuilder->IsPartialUpdate()) {
3591 aBuilder->SetPartialBuildFailed(true);
3592 } else {
3593 aBuilder->SetDisablePartialUpdates(true);
3594 }
3595 }
3596 }
3597
3598 // If a child contains a backdrop filter, but this stacking context does not
3599 // form a backdrop root, we need to propogate up the tree until we find an
3600 // ancestor that does form a backdrop root.
3601 if (!insertBackdropRoot && aBuilder->ContainsBackdropFilter()) {
3602 autoRestoreBackdropFilter.DelegateUp(true);
3603 }
3604
3605 if (aBuilder->IsBackgroundOnly()) {
3606 set.BlockBorderBackgrounds()->DeleteAll(aBuilder);
3607 set.Floats()->DeleteAll(aBuilder);
3608 set.Content()->DeleteAll(aBuilder);
3609 set.PositionedDescendants()->DeleteAll(aBuilder);
3610 set.Outlines()->DeleteAll(aBuilder);
3611 }
3612
3613 if (hasOverrideDirtyRect &&
3614 StaticPrefs::layout_display_list_show_rebuild_area()) {
3615 nsDisplaySolidColor* color = MakeDisplayItem<nsDisplaySolidColor>(
3616 aBuilder, this,
3617 dirtyRect + aBuilder->GetCurrentFrameOffsetToReferenceFrame(),
3618 NS_RGBA(255, 0, 0, 64), false);
3619 if (color) {
3620 color->SetOverrideZIndex(INT32_MAX);
3621 set.PositionedDescendants()->AppendToTop(color);
3622 }
3623 }
3624
3625 nsIContent* content = GetContent();
3626 if (!content) {
3627 content = PresContext()->Document()->GetRootElement();
3628 }
3629
3630 nsDisplayList resultList;
3631 set.SerializeWithCorrectZOrder(&resultList, content);
3632
3633 #ifdef DEBUG
3634 DisplayDebugBorders(aBuilder, this, set);
3635 #endif
3636
3637 // Get the ASR to use for the container items that we create here.
3638 const ActiveScrolledRoot* containerItemASR = contASRTracker.GetContainerASR();
3639
3640 ContainerTracker ct;
3641
3642 /* If adding both a nsDisplayBlendContainer and a nsDisplayBlendMode to the
3643 * same list, the nsDisplayBlendContainer should be added first. This only
3644 * happens when the element creating this stacking context has mix-blend-mode
3645 * and also contains a child which has mix-blend-mode.
3646 * The nsDisplayBlendContainer must be added to the list first, so it does not
3647 * isolate the containing element blending as well.
3648 */
3649 if (aBuilder->ContainsBlendMode()) {
3650 DisplayListClipState::AutoSaveRestore blendContainerClipState(aBuilder);
3651 resultList.AppendToTop(nsDisplayBlendContainer::CreateForMixBlendMode(
3652 aBuilder, this, &resultList, containerItemASR));
3653 ct.TrackContainer(resultList.GetTop());
3654 }
3655
3656 if (insertBackdropRoot) {
3657 DisplayListClipState::AutoSaveRestore backdropRootContainerClipState(
3658 aBuilder);
3659 resultList.AppendNewToTop<nsDisplayBackdropRootContainer>(
3660 aBuilder, this, &resultList, containerItemASR);
3661 ct.TrackContainer(resultList.GetTop());
3662 }
3663
3664 if (usingBackdropFilter) {
3665 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
3666 nsRect backdropRect =
3667 GetRectRelativeToSelf() + aBuilder->ToReferenceFrame(this);
3668 resultList.AppendNewToTop<nsDisplayBackdropFilters>(
3669 aBuilder, this, &resultList, backdropRect);
3670 ct.TrackContainer(resultList.GetTop());
3671 }
3672
3673 /* If there are any SVG effects, wrap the list up in an SVG effects item
3674 * (which also handles CSS group opacity). Note that we create an SVG effects
3675 * item even if resultList is empty, since a filter can produce graphical
3676 * output even if the element being filtered wouldn't otherwise do so.
3677 */
3678 if (usingSVGEffects) {
3679 MOZ_ASSERT(usingFilter || usingMask,
3680 "Beside filter & mask/clip-path, what else effect do we have?");
3681
3682 if (clipCapturedBy == ContainerItemType::Filter) {
3683 clipState.Restore();
3684 }
3685 // Revert to the post-filter dirty rect.
3686 aBuilder->SetVisibleRect(visibleRectOutsideSVGEffects);
3687
3688 // Skip all filter effects while generating glyph mask.
3689 if (usingFilter && !aBuilder->IsForGenerateGlyphMask()) {
3690 /* List now emptied, so add the new list to the top. */
3691 resultList.AppendNewToTop<nsDisplayFilters>(aBuilder, this, &resultList);
3692 ct.TrackContainer(resultList.GetTop());
3693 }
3694
3695 if (usingMask) {
3696 DisplayListClipState::AutoSaveRestore maskClipState(aBuilder);
3697 // The mask should move with aBuilder->CurrentActiveScrolledRoot(), so
3698 // that's the ASR we prefer to use for the mask item. However, we can
3699 // only do this if the mask if clipped with respect to that ASR, because
3700 // an item always needs to have finite bounds with respect to its ASR.
3701 // If we weren't able to compute a clip for the mask, we fall back to
3702 // using containerItemASR, which is the lowest common ancestor clip of
3703 // the mask's contents. That's not entirely correct, but it satisfies
3704 // the base requirement of the ASR system (that items have finite bounds
3705 // wrt. their ASR).
3706 const ActiveScrolledRoot* maskASR =
3707 clipForMask.isSome() ? aBuilder->CurrentActiveScrolledRoot()
3708 : containerItemASR;
3709 /* List now emptied, so add the new list to the top. */
3710 resultList.AppendNewToTop<nsDisplayMasksAndClipPaths>(
3711 aBuilder, this, &resultList, maskASR);
3712 ct.TrackContainer(resultList.GetTop());
3713 }
3714
3715 // TODO(miko): We could probably create a wraplist here and avoid creating
3716 // it later in |BuildDisplayListForChild()|.
3717 ct.ResetCreatedContainer();
3718
3719 // Also add the hoisted scroll info items. We need those for APZ scrolling
3720 // because nsDisplayMasksAndClipPaths items can't build active layers.
3721 aBuilder->ExitSVGEffectsContents();
3722 resultList.AppendToTop(&hoistedScrollInfoItemsStorage);
3723 }
3724
3725 /* If the list is non-empty and there is CSS group opacity without SVG
3726 * effects, wrap it up in an opacity item.
3727 */
3728 if (useOpacity) {
3729 // Don't clip nsDisplayOpacity items. We clip their descendants instead.
3730 // The clip we would set on an element with opacity would clip
3731 // all descendant content, but some should not be clipped.
3732 DisplayListClipState::AutoSaveRestore opacityClipState(aBuilder);
3733 const bool needsActiveOpacityLayer =
3734 nsDisplayOpacity::NeedsActiveLayer(aBuilder, this);
3735
3736 resultList.AppendNewToTop<nsDisplayOpacity>(
3737 aBuilder, this, &resultList, containerItemASR,
3738 opacityItemForEventsAndPluginsOnly, needsActiveOpacityLayer);
3739 ct.TrackContainer(resultList.GetTop());
3740 }
3741
3742 /* If we're going to apply a transformation and don't have preserve-3d set,
3743 * wrap everything in an nsDisplayTransform. If there's nothing in the list,
3744 * don't add anything.
3745 *
3746 * For the preserve-3d case we want to individually wrap every child in the
3747 * list with a separate nsDisplayTransform instead. When the child is already
3748 * an nsDisplayTransform, we can skip this step, as the computed transform
3749 * will already include our own.
3750 *
3751 * We also traverse into sublists created by nsDisplayWrapList, so that we
3752 * find all the correct children.
3753 */
3754 if (isTransformed && extend3DContext) {
3755 // Install dummy nsDisplayTransform as a leaf containing
3756 // descendants not participating this 3D rendering context.
3757 nsDisplayList nonparticipants;
3758 nsDisplayList participants;
3759 int index = 1;
3760
3761 nsDisplayItem* separator = nullptr;
3762
3763 while (nsDisplayItem* item = resultList.RemoveBottom()) {
3764 if (ItemParticipatesIn3DContext(this, item) &&
3765 !item->GetClip().HasClip()) {
3766 // The frame of this item participates the same 3D context.
3767 WrapSeparatorTransform(aBuilder, this, &nonparticipants, &participants,
3768 index++, &separator);
3769
3770 participants.AppendToTop(item);
3771 } else {
3772 // The frame of the item doesn't participate the current
3773 // context, or has no transform.
3774 //
3775 // For items participating but not transformed, they are add
3776 // to nonparticipants to get a separator layer for handling
3777 // clips, if there is, on an intermediate surface.
3778 // \see ContainerLayer::DefaultComputeEffectiveTransforms().
3779 nonparticipants.AppendToTop(item);
3780 }
3781 }
3782 WrapSeparatorTransform(aBuilder, this, &nonparticipants, &participants,
3783 index++, &separator);
3784
3785 if (separator) {
3786 ct.TrackContainer(separator);
3787 }
3788
3789 resultList.AppendToTop(&participants);
3790 }
3791
3792 if (isTransformed) {
3793 transformedCssClip.Restore();
3794 if (clipCapturedBy == ContainerItemType::Transform) {
3795 // Restore clip state now so nsDisplayTransform is clipped properly.
3796 clipState.Restore();
3797 }
3798 // Revert to the dirtyrect coming in from the parent, without our transform
3799 // taken into account.
3800 aBuilder->SetVisibleRect(visibleRectOutsideTransform);
3801 // Revert to the outer reference frame and offset because all display
3802 // items we create from now on are outside the transform.
3803 nsPoint toOuterReferenceFrame;
3804 const nsIFrame* outerReferenceFrame = this;
3805 if (this != aBuilder->RootReferenceFrame()) {
3806 outerReferenceFrame =
3807 aBuilder->FindReferenceFrameFor(GetParent(), &toOuterReferenceFrame);
3808 }
3809 buildingDisplayList.SetReferenceFrameAndCurrentOffset(
3810 outerReferenceFrame, GetOffsetToCrossDoc(outerReferenceFrame));
3811
3812 // We would like to block async animations for ancestors of ones not
3813 // prerendered in the preserve-3d tree. Now that we've finished processing
3814 // all descendants, update allowAsyncAnimation to take their prerender
3815 // state into account
3816 // FIXME: We don't block async animations for previous siblings because
3817 // their prerender decisions have been made. We may have to figure out a
3818 // better way to rollback their prerender decisions.
3819 // Alternatively we could not block animations for later siblings, and only
3820 // block them for ancestors of a blocked one.
3821 if ((extend3DContext || combines3DTransformWithAncestors) &&
3822 allowAsyncAnimation) {
3823 // aBuilder->GetPreserves3DAllowAsyncAnimation() means the inner or
3824 // previous silbing frames are allowed/disallowed for async animations.
3825 allowAsyncAnimation = aBuilder->GetPreserves3DAllowAsyncAnimation();
3826 }
3827
3828 nsDisplayTransform* transformItem = MakeDisplayItem<nsDisplayTransform>(
3829 aBuilder, this, &resultList, visibleRect, allowAsyncAnimation);
3830 if (transformItem) {
3831 resultList.AppendToTop(transformItem);
3832 ct.TrackContainer(transformItem);
3833 }
3834
3835 if (hasPerspective) {
3836 if (clipCapturedBy == ContainerItemType::Perspective) {
3837 clipState.Restore();
3838 }
3839 resultList.AppendNewToTop<nsDisplayPerspective>(aBuilder, this,
3840 &resultList);
3841 ct.TrackContainer(resultList.GetTop());
3842 }
3843 }
3844
3845 if (clipCapturedBy ==
3846 ContainerItemType::OwnLayerForTransformWithRoundedClip) {
3847 clipState.Restore();
3848 resultList.AppendNewToTopWithIndex<nsDisplayOwnLayer>(
3849 aBuilder, this,
3850 /* aIndex = */ nsDisplayOwnLayer::OwnLayerForTransformWithRoundedClip,
3851 &resultList, aBuilder->CurrentActiveScrolledRoot(),
3852 nsDisplayOwnLayerFlags::None, ScrollbarData{},
3853 /* aForceActive = */ false, false);
3854 ct.TrackContainer(resultList.GetTop());
3855 }
3856
3857 /* If we have sticky positioning, wrap it in a sticky position item.
3858 */
3859 if (useFixedPosition) {
3860 if (clipCapturedBy == ContainerItemType::FixedPosition) {
3861 clipState.Restore();
3862 }
3863 // The ASR for the fixed item should be the ASR of our containing block,
3864 // which has been set as the builder's current ASR, unless this frame is
3865 // invisible and we hadn't saved display item data for it. In that case,
3866 // we need to take the containerItemASR since we might have fixed children.
3867 // For WebRender, we want to the know what |containerItemASR| is for the
3868 // case where the fixed-pos item is not a "real" fixed-pos item (e.g. it's
3869 // nested inside a scrolling transform), so we stash that on the display
3870 // item as well.
3871 const ActiveScrolledRoot* fixedASR = ActiveScrolledRoot::PickAncestor(
3872 containerItemASR, aBuilder->CurrentActiveScrolledRoot());
3873 resultList.AppendNewToTop<nsDisplayFixedPosition>(
3874 aBuilder, this, &resultList, fixedASR, containerItemASR);
3875 ct.TrackContainer(resultList.GetTop());
3876 } else if (useStickyPosition) {
3877 // For position:sticky, the clip needs to be applied both to the sticky
3878 // container item and to the contents. The container item needs the clip
3879 // because a scrolled clip needs to move independently from the sticky
3880 // contents, and the contents need the clip so that they have finite
3881 // clipped bounds with respect to the container item's ASR. The latter is
3882 // a little tricky in the case where the sticky item has both fixed and
3883 // non-fixed descendants, because that means that the sticky container
3884 // item's ASR is the ASR of the fixed descendant.
3885 // For WebRender display list building, though, we still want to know the
3886 // the ASR that the sticky container item would normally have, so we stash
3887 // that on the display item as the "container ASR" (i.e. the normal ASR of
3888 // the container item, excluding the special behaviour induced by fixed
3889 // descendants).
3890 const ActiveScrolledRoot* stickyASR = ActiveScrolledRoot::PickAncestor(
3891 containerItemASR, aBuilder->CurrentActiveScrolledRoot());
3892 resultList.AppendNewToTop<nsDisplayStickyPosition>(
3893 aBuilder, this, &resultList, stickyASR,
3894 aBuilder->CurrentActiveScrolledRoot(),
3895 clipState.IsClippedToDisplayPort());
3896 ct.TrackContainer(resultList.GetTop());
3897
3898 // If the sticky element is inside a filter, annotate the scroll frame that
3899 // scrolls the filter as having out-of-flow content inside a filter (this
3900 // inhibits paint skipping).
3901 if (aBuilder->GetFilterASR() && aBuilder->GetFilterASR() == stickyASR) {
3902 aBuilder->GetFilterASR()
3903 ->mScrollableFrame->SetHasOutOfFlowContentInsideFilter();
3904 }
3905 }
3906
3907 /* If there's blending, wrap up the list in a blend-mode item. Note
3908 * that opacity can be applied before blending as the blend color is
3909 * not affected by foreground opacity (only background alpha).
3910 */
3911
3912 if (useBlendMode) {
3913 DisplayListClipState::AutoSaveRestore blendModeClipState(aBuilder);
3914 resultList.AppendNewToTop<nsDisplayBlendMode>(aBuilder, this, &resultList,
3915 effects->mMixBlendMode,
3916 containerItemASR, false);
3917 ct.TrackContainer(resultList.GetTop());
3918 }
3919
3920 bool createdOwnLayer = false;
3921 CreateOwnLayerIfNeeded(aBuilder, &resultList,
3922 nsDisplayOwnLayer::OwnLayerForStackingContext,
3923 &createdOwnLayer);
3924 if (createdOwnLayer) {
3925 ct.TrackContainer(resultList.GetTop());
3926 }
3927
3928 if (aCreatedContainerItem) {
3929 *aCreatedContainerItem = ct.mCreatedContainer;
3930 }
3931
3932 if (hitTestInfo) {
3933 // WebRender support is not yet implemented.
3934 MOZ_ASSERT(!gfxVars::UseWebRender());
3935 AddHitTestInfo(aBuilder, &resultList, ct.mContainer, this,
3936 std::move(hitTestInfo));
3937 }
3938
3939 aList->AppendToTop(&resultList);
3940 }
3941
WrapInWrapList(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,const ActiveScrolledRoot * aContainerASR,bool aBuiltContainerItem=false)3942 static nsDisplayItem* WrapInWrapList(nsDisplayListBuilder* aBuilder,
3943 nsIFrame* aFrame, nsDisplayList* aList,
3944 const ActiveScrolledRoot* aContainerASR,
3945 bool aBuiltContainerItem = false) {
3946 nsDisplayItem* item = aList->GetBottom();
3947 if (!item) {
3948 return nullptr;
3949 }
3950
3951 // We need a wrap list if there are multiple items, or if the single
3952 // item has a different frame. This can change in a partial build depending
3953 // on which items we build, so we need to ensure that we don't transition
3954 // to/from a wrap list without invalidating correctly.
3955 bool needsWrapList =
3956 item->GetAbove() || item->Frame() != aFrame || item->GetChildren();
3957
3958 // If we have an explicit container item (that can't change without an
3959 // invalidation) or we're doing a full build and don't need a wrap list, then
3960 // we can skip adding one.
3961 if (aBuiltContainerItem || (!aBuilder->IsPartialUpdate() && !needsWrapList)) {
3962 aList->RemoveBottom();
3963 return item;
3964 }
3965
3966 // If we're doing a partial build and we didn't need a wrap list
3967 // previously then we can try to work from there.
3968 if (aBuilder->IsPartialUpdate() &&
3969 !aFrame->HasDisplayItem(uint32_t(DisplayItemType::TYPE_CONTAINER))) {
3970 // If we now need a wrap list, we must previously have had no display items
3971 // or a single one belonging to this frame. Mark the item itself as
3972 // discarded so that RetainedDisplayListBuilder uses the ones we just built.
3973 // We don't want to mark the frame as modified as that would invalidate
3974 // positioned descendants that might be outside of this list, and might not
3975 // have been rebuilt this time.
3976 if (needsWrapList) {
3977 DiscardOldItems(aFrame);
3978 } else {
3979 aList->RemoveBottom();
3980 return item;
3981 }
3982 }
3983
3984 // The last case we could try to handle is when we previously had a wrap list,
3985 // but no longer need it. Unfortunately we can't differentiate this case from
3986 // a partial build where other children exist but we just didn't build them
3987 // this time.
3988 // TODO:RetainedDisplayListBuilder's merge phase has the full list and
3989 // could strip them out.
3990
3991 return MakeDisplayItem<nsDisplayContainer>(aBuilder, aFrame, aContainerASR,
3992 aList);
3993 }
3994
3995 /**
3996 * Check if a frame should be visited for building display list.
3997 */
DescendIntoChild(nsDisplayListBuilder * aBuilder,const nsIFrame * aChild,const nsRect & aVisible,const nsRect & aDirty)3998 static bool DescendIntoChild(nsDisplayListBuilder* aBuilder,
3999 const nsIFrame* aChild, const nsRect& aVisible,
4000 const nsRect& aDirty) {
4001 if (aChild->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) {
4002 return true;
4003 }
4004
4005 // If the child is a scrollframe that we want to ignore, then we need
4006 // to descend into it because its scrolled child may intersect the dirty
4007 // area even if the scrollframe itself doesn't.
4008 if (aChild == aBuilder->GetIgnoreScrollFrame()) {
4009 return true;
4010 }
4011
4012 // There are cases where the "ignore scroll frame" on the builder is not set
4013 // correctly, and so we additionally want to catch cases where the child is
4014 // a root scrollframe and we are ignoring scrolling on the viewport.
4015 if (aChild == aBuilder->GetPresShellIgnoreScrollFrame()) {
4016 return true;
4017 }
4018
4019 const nsRect overflow = aChild->GetVisualOverflowRect();
4020
4021 if (aDirty.Intersects(overflow)) {
4022 return true;
4023 }
4024
4025 if (aChild->ForceDescendIntoIfVisible() && aVisible.Intersects(overflow)) {
4026 return true;
4027 }
4028
4029 if (aChild->IsFrameOfType(nsIFrame::eTablePart)) {
4030 // Relative positioning and transforms can cause table parts to move, but we
4031 // will still paint the backgrounds for their ancestor parts under them at
4032 // their 'normal' position. That means that we must consider the overflow
4033 // rects at both positions.
4034
4035 // We convert the overflow rect into the nsTableFrame's coordinate
4036 // space, applying the normal position offset at each step. Then we
4037 // compare that against the builder's cached dirty rect in table
4038 // coordinate space.
4039 const nsIFrame* f = aChild;
4040 nsRect normalPositionOverflowRelativeToTable = overflow;
4041
4042 while (f->IsFrameOfType(nsIFrame::eTablePart)) {
4043 normalPositionOverflowRelativeToTable += f->GetNormalPosition();
4044 f = f->GetParent();
4045 }
4046
4047 nsDisplayTableBackgroundSet* tableBGs = aBuilder->GetTableBackgroundSet();
4048 if (tableBGs && tableBGs->GetDirtyRect().Intersects(
4049 normalPositionOverflowRelativeToTable)) {
4050 return true;
4051 }
4052 }
4053
4054 return false;
4055 }
4056
BuildDisplayListForSimpleChild(nsDisplayListBuilder * aBuilder,nsIFrame * aChild,const nsDisplayListSet & aLists)4057 void nsIFrame::BuildDisplayListForSimpleChild(nsDisplayListBuilder* aBuilder,
4058 nsIFrame* aChild,
4059 const nsDisplayListSet& aLists) {
4060 // This is the shortcut for frames been handled along the common
4061 // path, the most common one of THE COMMON CASE mentioned later.
4062 MOZ_ASSERT(aChild->Type() != LayoutFrameType::Placeholder);
4063 MOZ_ASSERT(!aBuilder->GetSelectedFramesOnly() &&
4064 !aBuilder->GetIncludeAllOutOfFlows(),
4065 "It should be held for painting to window");
4066 MOZ_ASSERT(aChild->GetStateBits() & NS_FRAME_SIMPLE_DISPLAYLIST);
4067
4068 const nsPoint offset = aChild->GetOffsetTo(this);
4069 const nsRect visible = aBuilder->GetVisibleRect() - offset;
4070 const nsRect dirty = aBuilder->GetDirtyRect() - offset;
4071
4072 if (!DescendIntoChild(aBuilder, aChild, visible, dirty)) {
4073 return;
4074 }
4075
4076 // Child cannot be transformed since it is not a stacking context.
4077 nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(
4078 aBuilder, aChild, visible, dirty, false);
4079
4080 CheckForApzAwareEventHandlers(aBuilder, aChild);
4081
4082 aBuilder->BuildCompositorHitTestInfoIfNeeded(
4083 aChild, aLists.BorderBackground(),
4084 buildingForChild.IsAnimatedGeometryRoot());
4085
4086 aChild->MarkAbsoluteFramesForDisplayList(aBuilder);
4087 aBuilder->AdjustWindowDraggingRegion(aChild);
4088 aBuilder->Check();
4089 aChild->BuildDisplayList(aBuilder, aLists);
4090 aBuilder->Check();
4091 aBuilder->DisplayCaret(aChild, aLists.Outlines());
4092 #ifdef DEBUG
4093 DisplayDebugBorders(aBuilder, aChild, aLists);
4094 #endif
4095 }
4096
ShouldSkipFrame(nsDisplayListBuilder * aBuilder,const nsIFrame * aFrame)4097 static bool ShouldSkipFrame(nsDisplayListBuilder* aBuilder,
4098 const nsIFrame* aFrame) {
4099 // If painting is restricted to just the background of the top level frame,
4100 // then we have nothing to do here.
4101 if (aBuilder->IsBackgroundOnly()) {
4102 return true;
4103 }
4104
4105 if (aBuilder->IsForGenerateGlyphMask() &&
4106 (!aFrame->IsTextFrame() && aFrame->IsLeaf())) {
4107 return true;
4108 }
4109
4110 // The placeholder frame should have the same content as the OOF frame.
4111 if (aBuilder->GetSelectedFramesOnly() &&
4112 (aFrame->IsLeaf() && !aFrame->IsSelected())) {
4113 return true;
4114 }
4115
4116 static const nsFrameState skipFlags =
4117 (NS_FRAME_TOO_DEEP_IN_FRAME_TREE | NS_FRAME_IS_NONDISPLAY);
4118
4119 return (aFrame->GetStateBits() & skipFlags);
4120 }
4121
BuildDisplayListForChild(nsDisplayListBuilder * aBuilder,nsIFrame * aChild,const nsDisplayListSet & aLists,uint32_t aFlags)4122 void nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder,
4123 nsIFrame* aChild,
4124 const nsDisplayListSet& aLists,
4125 uint32_t aFlags) {
4126 AutoCheckBuilder check(aBuilder);
4127
4128 if (ShouldSkipFrame(aBuilder, aChild)) {
4129 return;
4130 }
4131
4132 nsIFrame* child = aChild;
4133 auto* placeholder = child->IsPlaceholderFrame()
4134 ? static_cast<nsPlaceholderFrame*>(child)
4135 : nullptr;
4136 nsIFrame* childOrOutOfFlow =
4137 placeholder ? placeholder->GetOutOfFlowFrame() : child;
4138
4139 nsIFrame* parent = childOrOutOfFlow->GetParent();
4140 const bool shouldApplyOverflowClip =
4141 nsFrame::ShouldApplyOverflowClipping(parent, parent->StyleDisplay());
4142
4143 const bool isPaintingToWindow = aBuilder->IsPaintingToWindow();
4144 const bool doingShortcut =
4145 isPaintingToWindow &&
4146 (child->GetStateBits() & NS_FRAME_SIMPLE_DISPLAYLIST) &&
4147 // Animations may change the stacking context state.
4148 // ShouldApplyOverflowClipping is affected by the parent style, which does
4149 // not invalidate the NS_FRAME_SIMPLE_DISPLAYLIST bit.
4150 !(shouldApplyOverflowClip || child->MayHaveTransformAnimation() ||
4151 child->MayHaveOpacityAnimation());
4152
4153 if (aBuilder->IsForPainting()) {
4154 aBuilder->ClearWillChangeBudgetStatus(child);
4155 }
4156
4157 if (StaticPrefs::layout_css_scroll_anchoring_highlight()) {
4158 if (child->FirstContinuation()->IsScrollAnchor()) {
4159 nsRect bounds = child->GetContentRectRelativeToSelf() +
4160 aBuilder->ToReferenceFrame(child);
4161 nsDisplaySolidColor* color = MakeDisplayItem<nsDisplaySolidColor>(
4162 aBuilder, child, bounds, NS_RGBA(255, 0, 255, 64));
4163 if (color) {
4164 color->SetOverrideZIndex(INT32_MAX);
4165 aLists.PositionedDescendants()->AppendToTop(color);
4166 }
4167 }
4168 }
4169
4170 if (doingShortcut) {
4171 BuildDisplayListForSimpleChild(aBuilder, child, aLists);
4172 return;
4173 }
4174
4175 // dirty rect in child-relative coordinates
4176 NS_ASSERTION(aBuilder->GetCurrentFrame() == this, "Wrong coord space!");
4177 const nsPoint offset = child->GetOffsetTo(this);
4178 nsRect visible = aBuilder->GetVisibleRect() - offset;
4179 nsRect dirty = aBuilder->GetDirtyRect() - offset;
4180
4181 nsDisplayListBuilder::OutOfFlowDisplayData* savedOutOfFlowData = nullptr;
4182 if (placeholder) {
4183 if (placeholder->GetStateBits() & PLACEHOLDER_FOR_TOPLAYER) {
4184 // If the out-of-flow frame is in the top layer, the viewport frame
4185 // will paint it. Skip it here. Note that, only out-of-flow frames
4186 // with this property should be skipped, because non-HTML elements
4187 // may stop their children from being out-of-flow. Those frames
4188 // should still be handled in the normal in-flow path.
4189 return;
4190 }
4191
4192 child = childOrOutOfFlow;
4193 if (aBuilder->IsForPainting()) {
4194 aBuilder->ClearWillChangeBudgetStatus(child);
4195 }
4196
4197 // If 'child' is a pushed float then it's owned by a block that's not an
4198 // ancestor of the placeholder, and it will be painted by that block and
4199 // should not be painted through the placeholder. Also recheck
4200 // NS_FRAME_TOO_DEEP_IN_FRAME_TREE and NS_FRAME_IS_NONDISPLAY.
4201 static const nsFrameState skipFlags =
4202 (NS_FRAME_IS_PUSHED_FLOAT | NS_FRAME_TOO_DEEP_IN_FRAME_TREE |
4203 NS_FRAME_IS_NONDISPLAY);
4204 if (child->HasAnyStateBits(skipFlags) || nsLayoutUtils::IsPopup(child)) {
4205 return;
4206 }
4207
4208 MOZ_ASSERT(child->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW));
4209 savedOutOfFlowData = nsDisplayListBuilder::GetOutOfFlowData(child);
4210
4211 if (aBuilder->GetIncludeAllOutOfFlows()) {
4212 visible = child->GetVisualOverflowRect();
4213 dirty = child->GetVisualOverflowRect();
4214 } else if (savedOutOfFlowData) {
4215 visible =
4216 savedOutOfFlowData->GetVisibleRectForFrame(aBuilder, child, &dirty);
4217 } else {
4218 // The out-of-flow frame did not intersect the dirty area. We may still
4219 // need to traverse into it, since it may contain placeholders we need
4220 // to enter to reach other out-of-flow frames that are visible.
4221 visible.SetEmpty();
4222 dirty.SetEmpty();
4223 }
4224 }
4225
4226 NS_ASSERTION(!child->IsPlaceholderFrame(),
4227 "Should have dealt with placeholders already");
4228
4229 if (!DescendIntoChild(aBuilder, child, visible, dirty)) {
4230 return;
4231 }
4232
4233 const bool isSVG = child->GetStateBits() & NS_FRAME_SVG_LAYOUT;
4234
4235 // This flag is raised if the control flow strays off the common path.
4236 // The common path is the most common one of THE COMMON CASE mentioned later.
4237 bool awayFromCommonPath = !isPaintingToWindow;
4238
4239 // true if this is a real or pseudo stacking context
4240 bool pseudoStackingContext =
4241 (aFlags & DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT) != 0;
4242
4243 if (!pseudoStackingContext && !isSVG && (aFlags & DISPLAY_CHILD_INLINE) &&
4244 !child->IsFrameOfType(eLineParticipant)) {
4245 // child is a non-inline frame in an inline context, i.e.,
4246 // it acts like inline-block or inline-table. Therefore it is a
4247 // pseudo-stacking-context.
4248 pseudoStackingContext = true;
4249 }
4250
4251 const nsStyleDisplay* ourDisp = StyleDisplay();
4252 // REVIEW: Taken from nsBoxFrame::Paint
4253 // Don't paint our children if the theme object is a leaf.
4254 if (IsThemed(ourDisp) &&
4255 !PresContext()->Theme()->WidgetIsContainer(ourDisp->mAppearance))
4256 return;
4257
4258 // Since we're now sure that we're adding this frame to the display list
4259 // (which means we're painting it, modulo occlusion), mark it as visible
4260 // within the displayport.
4261 if (isPaintingToWindow && child->TrackingVisibility()) {
4262 child->PresShell()->EnsureFrameInApproximatelyVisibleList(child);
4263 awayFromCommonPath = true;
4264 }
4265
4266 child->SetBuiltDisplayList(true);
4267
4268 // Child is composited if it's transformed, partially transparent, or has
4269 // SVG effects or a blend mode..
4270 const nsStyleDisplay* disp = child->StyleDisplay();
4271 const nsStyleEffects* effects = child->StyleEffects();
4272 const nsStylePosition* pos = child->StylePosition();
4273
4274 const bool isPositioned = disp->IsAbsPosContainingBlock(child);
4275
4276 const bool isStackingContext =
4277 (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT) ||
4278 child->IsStackingContext(disp, pos, effects, isPositioned);
4279
4280 if (pseudoStackingContext || isStackingContext || isPositioned ||
4281 placeholder || (!isSVG && disp->IsFloating(child)) ||
4282 (isSVG && effects->mClip.IsRect() && IsSVGContentWithCSSClip(child))) {
4283 pseudoStackingContext = true;
4284 awayFromCommonPath = true;
4285 }
4286
4287 NS_ASSERTION(!isStackingContext || pseudoStackingContext,
4288 "Stacking contexts must also be pseudo-stacking-contexts");
4289
4290 nsDisplayListBuilder::AutoBuildingDisplayList buildingForChild(
4291 aBuilder, child, visible, dirty);
4292 DisplayListClipState::AutoClipMultiple clipState(aBuilder);
4293 nsDisplayListBuilder::AutoCurrentActiveScrolledRootSetter asrSetter(aBuilder);
4294 CheckForApzAwareEventHandlers(aBuilder, child);
4295
4296 if (savedOutOfFlowData) {
4297 aBuilder->SetBuildingInvisibleItems(false);
4298
4299 clipState.SetClipChainForContainingBlockDescendants(
4300 savedOutOfFlowData->mContainingBlockClipChain);
4301 asrSetter.SetCurrentActiveScrolledRoot(
4302 savedOutOfFlowData->mContainingBlockActiveScrolledRoot);
4303 MOZ_ASSERT(awayFromCommonPath,
4304 "It is impossible when savedOutOfFlowData is true");
4305 } else if (HasAnyStateBits(NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO) &&
4306 placeholder) {
4307 NS_ASSERTION(visible.IsEmpty(), "should have empty visible rect");
4308 // Every item we build from now until we descent into an out of flow that
4309 // does have saved out of flow data should be invisible. This state gets
4310 // restored when AutoBuildingDisplayList gets out of scope.
4311 aBuilder->SetBuildingInvisibleItems(true);
4312
4313 // If we have nested out-of-flow frames and the outer one isn't visible
4314 // then we won't have stored clip data for it. We can just clear the clip
4315 // instead since we know we won't render anything, and the inner out-of-flow
4316 // frame will setup the correct clip for itself.
4317 clipState.SetClipChainForContainingBlockDescendants(nullptr);
4318 }
4319
4320 // Setup clipping for the parent's overflow:-moz-hidden-unscrollable,
4321 // or overflow:hidden on elements that don't support scrolling (and therefore
4322 // don't create nsHTML/XULScrollFrame). This clipping needs to not clip
4323 // anything directly rendered by the parent, only the rendering of its
4324 // children.
4325 // Don't use overflowClip to restrict the dirty rect, since some of the
4326 // descendants may not be clipped by it. Even if we end up with unnecessary
4327 // display items, they'll be pruned during ComputeVisibility.
4328 //
4329 // FIXME(emilio): Why can't we handle this more similarly to `clip` (on the
4330 // parent, rather than on the children)? Would ClipContentDescendants do what
4331 // we want?
4332 if (shouldApplyOverflowClip) {
4333 ApplyOverflowClipping(aBuilder, parent, clipState);
4334 awayFromCommonPath = true;
4335 }
4336
4337 nsDisplayList list;
4338 nsDisplayList extraPositionedDescendants;
4339 const ActiveScrolledRoot* wrapListASR;
4340 bool builtContainerItem = false;
4341 if (isStackingContext) {
4342 // True stacking context.
4343 // For stacking contexts, BuildDisplayListForStackingContext handles
4344 // clipping and MarkAbsoluteFramesForDisplayList.
4345 nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
4346 child->BuildDisplayListForStackingContext(aBuilder, &list,
4347 &builtContainerItem);
4348 wrapListASR = contASRTracker.GetContainerASR();
4349 if (aBuilder->GetCaretFrame() == child) {
4350 builtContainerItem = false;
4351 }
4352 } else {
4353 Maybe<nsRect> clipPropClip =
4354 child->GetClipPropClipRect(disp, effects, child->GetSize());
4355 if (clipPropClip) {
4356 aBuilder->IntersectVisibleRect(*clipPropClip);
4357 aBuilder->IntersectDirtyRect(*clipPropClip);
4358 clipState.ClipContentDescendants(*clipPropClip +
4359 aBuilder->ToReferenceFrame(child));
4360 awayFromCommonPath = true;
4361 }
4362
4363 child->MarkAbsoluteFramesForDisplayList(aBuilder);
4364
4365 const bool differentAGR = buildingForChild.IsAnimatedGeometryRoot();
4366
4367 if (!awayFromCommonPath &&
4368 // Some SVG frames might change opacity without invalidating the frame,
4369 // so exclude them from the fast-path.
4370 !child->IsFrameOfType(nsIFrame::eSVG)) {
4371 // The shortcut is available for the child for next time.
4372 child->AddStateBits(NS_FRAME_SIMPLE_DISPLAYLIST);
4373 }
4374
4375 if (!pseudoStackingContext) {
4376 // THIS IS THE COMMON CASE.
4377 // Not a pseudo or real stacking context. Do the simple thing and
4378 // return early.
4379
4380 aBuilder->BuildCompositorHitTestInfoIfNeeded(
4381 child, aLists.BorderBackground(), differentAGR);
4382
4383 aBuilder->AdjustWindowDraggingRegion(child);
4384 aBuilder->Check();
4385 child->BuildDisplayList(aBuilder, aLists);
4386 aBuilder->Check();
4387 aBuilder->DisplayCaret(child, aLists.Outlines());
4388 #ifdef DEBUG
4389 DisplayDebugBorders(aBuilder, child, aLists);
4390 #endif
4391 return;
4392 }
4393
4394 // A pseudo-stacking context (e.g., a positioned element with z-index auto).
4395 // We allow positioned descendants of the child to escape to our parent
4396 // stacking context's positioned descendant list, because they might be
4397 // z-index:non-auto
4398 nsDisplayListCollection pseudoStack(aBuilder);
4399
4400 // If this frame has z-index != 0, then the display item might get sorted
4401 // into a different place in the list, and we can't rely on the previous
4402 // hit test info to still be behind us. Force a new hit test info for this
4403 // item, and for the item after it, so that we always have the right hit
4404 // test info.
4405 bool mayBeSorted = isPositioned && (ZIndex() != 0);
4406
4407 aBuilder->BuildCompositorHitTestInfoIfNeeded(
4408 child, pseudoStack.BorderBackground(), differentAGR || mayBeSorted);
4409
4410 aBuilder->AdjustWindowDraggingRegion(child);
4411 nsDisplayListBuilder::AutoContainerASRTracker contASRTracker(aBuilder);
4412 aBuilder->Check();
4413 child->BuildDisplayList(aBuilder, pseudoStack);
4414 aBuilder->Check();
4415 if (aBuilder->DisplayCaret(child, pseudoStack.Outlines())) {
4416 builtContainerItem = false;
4417 }
4418
4419 // If we forced a new hit-test info because this frame is going to be
4420 // sorted, then clear the 'previous' data on the builder so that the next
4421 // item also gets a new hit test info. That way we're guaranteeing hit-test
4422 // info before and after each item that might get moved to a different spot.
4423 if (mayBeSorted) {
4424 aBuilder->SetCompositorHitTestInfo(
4425 nsRect(), CompositorHitTestFlags::eVisibleToHitTest);
4426 }
4427
4428 wrapListASR = contASRTracker.GetContainerASR();
4429
4430 list.AppendToTop(pseudoStack.BorderBackground());
4431 list.AppendToTop(pseudoStack.BlockBorderBackgrounds());
4432 list.AppendToTop(pseudoStack.Floats());
4433 list.AppendToTop(pseudoStack.Content());
4434 list.AppendToTop(pseudoStack.Outlines());
4435 extraPositionedDescendants.AppendToTop(pseudoStack.PositionedDescendants());
4436 #ifdef DEBUG
4437 DisplayDebugBorders(aBuilder, child, aLists);
4438 #endif
4439 }
4440
4441 buildingForChild.RestoreBuildingInvisibleItemsValue();
4442
4443 if (isPositioned || isStackingContext ||
4444 (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) {
4445 // Genuine stacking contexts, and positioned pseudo-stacking-contexts,
4446 // go in this level.
4447 if (!list.IsEmpty()) {
4448 nsDisplayItem* item = WrapInWrapList(aBuilder, child, &list, wrapListASR,
4449 builtContainerItem);
4450 if (isSVG) {
4451 aLists.Content()->AppendToTop(item);
4452 } else {
4453 aLists.PositionedDescendants()->AppendToTop(item);
4454 }
4455 }
4456 } else if (!isSVG && disp->IsFloating(child)) {
4457 if (!list.IsEmpty()) {
4458 aLists.Floats()->AppendToTop(
4459 WrapInWrapList(aBuilder, child, &list, wrapListASR));
4460 }
4461 } else {
4462 aLists.Content()->AppendToTop(&list);
4463 }
4464 // We delay placing the positioned descendants of positioned frames to here,
4465 // because in the absence of z-index this is the correct order for them.
4466 // This doesn't affect correctness because the positioned descendants list
4467 // is sorted by z-order and content in BuildDisplayListForStackingContext,
4468 // but it means that sort routine needs to do less work.
4469 aLists.PositionedDescendants()->AppendToTop(&extraPositionedDescendants);
4470 }
4471
MarkAbsoluteFramesForDisplayList(nsDisplayListBuilder * aBuilder)4472 void nsIFrame::MarkAbsoluteFramesForDisplayList(
4473 nsDisplayListBuilder* aBuilder) {
4474 if (IsAbsoluteContainer()) {
4475 aBuilder->MarkFramesForDisplayList(
4476 this, GetAbsoluteContainingBlock()->GetChildList());
4477 }
4478 }
4479
GetContentForEvent(WidgetEvent * aEvent,nsIContent ** aContent)4480 nsresult nsIFrame::GetContentForEvent(WidgetEvent* aEvent,
4481 nsIContent** aContent) {
4482 nsIFrame* f = nsLayoutUtils::GetNonGeneratedAncestor(this);
4483 *aContent = f->GetContent();
4484 NS_IF_ADDREF(*aContent);
4485 return NS_OK;
4486 }
4487
FireDOMEvent(const nsAString & aDOMEventName,nsIContent * aContent)4488 void nsFrame::FireDOMEvent(const nsAString& aDOMEventName,
4489 nsIContent* aContent) {
4490 nsIContent* target = aContent ? aContent : GetContent();
4491
4492 if (target) {
4493 RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(
4494 target, aDOMEventName, CanBubble::eYes, ChromeOnlyDispatch::eNo);
4495 DebugOnly<nsresult> rv = asyncDispatcher->PostDOMEvent();
4496 NS_ASSERTION(NS_SUCCEEDED(rv), "AsyncEventDispatcher failed to dispatch");
4497 }
4498 }
4499
HandleEvent(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus)4500 nsresult nsIFrame::HandleEvent(nsPresContext* aPresContext,
4501 WidgetGUIEvent* aEvent,
4502 nsEventStatus* aEventStatus) {
4503 if (aEvent->mMessage == eMouseMove) {
4504 // XXX If the second argument of HandleDrag() is WidgetMouseEvent,
4505 // the implementation becomes simpler.
4506 return HandleDrag(aPresContext, aEvent, aEventStatus);
4507 }
4508
4509 if ((aEvent->mClass == eMouseEventClass &&
4510 aEvent->AsMouseEvent()->mButton == MouseButton::eLeft) ||
4511 aEvent->mClass == eTouchEventClass) {
4512 if (aEvent->mMessage == eMouseDown || aEvent->mMessage == eTouchStart) {
4513 HandlePress(aPresContext, aEvent, aEventStatus);
4514 } else if (aEvent->mMessage == eMouseUp || aEvent->mMessage == eTouchEnd) {
4515 HandleRelease(aPresContext, aEvent, aEventStatus);
4516 }
4517 }
4518 return NS_OK;
4519 }
4520
GetDataForTableSelection(const nsFrameSelection * aFrameSelection,mozilla::PresShell * aPresShell,WidgetMouseEvent * aMouseEvent,nsIContent ** aParentContent,int32_t * aContentOffset,TableSelectionMode * aTarget)4521 nsresult nsIFrame::GetDataForTableSelection(
4522 const nsFrameSelection* aFrameSelection, mozilla::PresShell* aPresShell,
4523 WidgetMouseEvent* aMouseEvent, nsIContent** aParentContent,
4524 int32_t* aContentOffset, TableSelectionMode* aTarget) {
4525 if (!aFrameSelection || !aPresShell || !aMouseEvent || !aParentContent ||
4526 !aContentOffset || !aTarget)
4527 return NS_ERROR_NULL_POINTER;
4528
4529 *aParentContent = nullptr;
4530 *aContentOffset = 0;
4531 *aTarget = TableSelectionMode::None;
4532
4533 int16_t displaySelection = aPresShell->GetSelectionFlags();
4534
4535 bool selectingTableCells = aFrameSelection->IsInTableSelectionMode();
4536
4537 // DISPLAY_ALL means we're in an editor.
4538 // If already in cell selection mode,
4539 // continue selecting with mouse drag or end on mouse up,
4540 // or when using shift key to extend block of cells
4541 // (Mouse down does normal selection unless Ctrl/Cmd is pressed)
4542 bool doTableSelection =
4543 displaySelection == nsISelectionDisplay::DISPLAY_ALL &&
4544 selectingTableCells &&
4545 (aMouseEvent->mMessage == eMouseMove ||
4546 (aMouseEvent->mMessage == eMouseUp &&
4547 aMouseEvent->mButton == MouseButton::eLeft) ||
4548 aMouseEvent->IsShift());
4549
4550 if (!doTableSelection) {
4551 // In Browser, special 'table selection' key must be pressed for table
4552 // selection or when just Shift is pressed and we're already in table/cell
4553 // selection mode
4554 #ifdef XP_MACOSX
4555 doTableSelection = aMouseEvent->IsMeta() ||
4556 (aMouseEvent->IsShift() && selectingTableCells);
4557 #else
4558 doTableSelection = aMouseEvent->IsControl() ||
4559 (aMouseEvent->IsShift() && selectingTableCells);
4560 #endif
4561 }
4562 if (!doTableSelection) return NS_OK;
4563
4564 // Get the cell frame or table frame (or parent) of the current content node
4565 nsIFrame* frame = this;
4566 bool foundCell = false;
4567 bool foundTable = false;
4568
4569 // Get the limiting node to stop parent frame search
4570 nsIContent* limiter = aFrameSelection->GetLimiter();
4571
4572 // If our content node is an ancestor of the limiting node,
4573 // we should stop the search right now.
4574 if (limiter && limiter->IsInclusiveDescendantOf(GetContent())) return NS_OK;
4575
4576 // We don't initiate row/col selection from here now,
4577 // but we may in future
4578 // bool selectColumn = false;
4579 // bool selectRow = false;
4580
4581 while (frame) {
4582 // Check for a table cell by querying to a known CellFrame interface
4583 nsITableCellLayout* cellElement = do_QueryFrame(frame);
4584 if (cellElement) {
4585 foundCell = true;
4586 // TODO: If we want to use proximity to top or left border
4587 // for row and column selection, this is the place to do it
4588 break;
4589 } else {
4590 // If not a cell, check for table
4591 // This will happen when starting frame is the table or child of a table,
4592 // such as a row (we were inbetween cells or in table border)
4593 nsTableWrapperFrame* tableFrame = do_QueryFrame(frame);
4594 if (tableFrame) {
4595 foundTable = true;
4596 // TODO: How can we select row when along left table edge
4597 // or select column when along top edge?
4598 break;
4599 } else {
4600 frame = frame->GetParent();
4601 // Stop if we have hit the selection's limiting content node
4602 if (frame && frame->GetContent() == limiter) break;
4603 }
4604 }
4605 }
4606 // We aren't in a cell or table
4607 if (!foundCell && !foundTable) return NS_OK;
4608
4609 nsIContent* tableOrCellContent = frame->GetContent();
4610 if (!tableOrCellContent) return NS_ERROR_FAILURE;
4611
4612 nsCOMPtr<nsIContent> parentContent = tableOrCellContent->GetParent();
4613 if (!parentContent) return NS_ERROR_FAILURE;
4614
4615 int32_t offset = parentContent->ComputeIndexOf(tableOrCellContent);
4616 // Not likely?
4617 if (offset < 0) return NS_ERROR_FAILURE;
4618
4619 // Everything is OK -- set the return values
4620 parentContent.forget(aParentContent);
4621
4622 *aContentOffset = offset;
4623
4624 #if 0
4625 if (selectRow)
4626 *aTarget = TableSelectionMode::Row;
4627 else if (selectColumn)
4628 *aTarget = TableSelectionMode::Column;
4629 else
4630 #endif
4631 if (foundCell) {
4632 *aTarget = TableSelectionMode::Cell;
4633 } else if (foundTable) {
4634 *aTarget = TableSelectionMode::Table;
4635 }
4636
4637 return NS_OK;
4638 }
4639
IsEditingHost(const nsIFrame * aFrame)4640 static bool IsEditingHost(const nsIFrame* aFrame) {
4641 auto* element = nsGenericHTMLElement::FromNodeOrNull(aFrame->GetContent());
4642 return element && element->IsEditableRoot();
4643 }
4644
UsedUserSelect(const nsIFrame * aFrame)4645 static StyleUserSelect UsedUserSelect(const nsIFrame* aFrame) {
4646 if (aFrame->HasAnyStateBits(NS_FRAME_GENERATED_CONTENT)) {
4647 return StyleUserSelect::None;
4648 }
4649
4650 // Per https://drafts.csswg.org/css-ui-4/#content-selection:
4651 //
4652 // The computed value is the specified value, except:
4653 //
4654 // 1 - on editable elements where the computed value is always 'contain'
4655 // regardless of the specified value.
4656 // 2 - when the specified value is auto, which computes to one of the other
4657 // values [...]
4658 //
4659 // See https://github.com/w3c/csswg-drafts/issues/3344 to see why we do this
4660 // at used-value time instead of at computed-value time.
4661 //
4662 // Also, we check for auto first to allow explicitly overriding the value for
4663 // the editing host.
4664 auto style = aFrame->StyleUIReset()->mUserSelect;
4665 if (style != StyleUserSelect::Auto) {
4666 return style;
4667 }
4668
4669 if (aFrame->IsTextInputFrame() || IsEditingHost(aFrame)) {
4670 // We don't implement 'contain' itself, but we make 'text' behave as
4671 // 'contain' for contenteditable and <input> / <textarea> elements anyway so
4672 // this is ok.
4673 return StyleUserSelect::Text;
4674 }
4675
4676 auto* parent = nsLayoutUtils::GetParentOrPlaceholderFor(aFrame);
4677 return parent ? UsedUserSelect(parent) : StyleUserSelect::Text;
4678 }
4679
IsSelectable(StyleUserSelect * aSelectStyle) const4680 bool nsIFrame::IsSelectable(StyleUserSelect* aSelectStyle) const {
4681 auto style = UsedUserSelect(this);
4682 if (aSelectStyle) {
4683 *aSelectStyle = style;
4684 }
4685 return style != StyleUserSelect::None;
4686 }
4687
ShouldHaveLineIfEmpty() const4688 bool nsIFrame::ShouldHaveLineIfEmpty() const {
4689 if (Style()->IsPseudoOrAnonBox()) {
4690 return false;
4691 }
4692 return IsEditingHost(this);
4693 }
4694
4695 /**
4696 * Handles the Mouse Press Event for the frame
4697 */
4698 NS_IMETHODIMP
HandlePress(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus)4699 nsIFrame::HandlePress(nsPresContext* aPresContext, WidgetGUIEvent* aEvent,
4700 nsEventStatus* aEventStatus) {
4701 NS_ENSURE_ARG_POINTER(aEventStatus);
4702 if (nsEventStatus_eConsumeNoDefault == *aEventStatus) {
4703 return NS_OK;
4704 }
4705
4706 NS_ENSURE_ARG_POINTER(aEvent);
4707 if (aEvent->mClass == eTouchEventClass) {
4708 return NS_OK;
4709 }
4710
4711 // We often get out of sync state issues with mousedown events that
4712 // get interrupted by alerts/dialogs.
4713 // Check with the ESM to see if we should process this one
4714 if (!aPresContext->EventStateManager()->EventStatusOK(aEvent)) return NS_OK;
4715
4716 mozilla::PresShell* presShell = aPresContext->GetPresShell();
4717 if (!presShell) {
4718 return NS_ERROR_FAILURE;
4719 }
4720
4721 // if we are in Navigator and the click is in a draggable node, we don't want
4722 // to start selection because we don't want to interfere with a potential
4723 // drag of said node and steal all its glory.
4724 int16_t isEditor = presShell->GetSelectionFlags();
4725 // weaaak. only the editor can display frame selection not just text and
4726 // images
4727 isEditor = isEditor == nsISelectionDisplay::DISPLAY_ALL;
4728
4729 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
4730
4731 if (!mouseEvent->IsAlt()) {
4732 for (nsIContent* content = mContent; content;
4733 content = content->GetFlattenedTreeParent()) {
4734 if (nsContentUtils::ContentIsDraggable(content) &&
4735 !content->IsEditable()) {
4736 // coordinate stuff is the fix for bug #55921
4737 if ((mRect - GetPosition())
4738 .Contains(nsLayoutUtils::GetEventCoordinatesRelativeTo(
4739 mouseEvent, RelativeTo{this}))) {
4740 return NS_OK;
4741 }
4742 }
4743 }
4744 }
4745
4746 // check whether style allows selection
4747 // if not, don't tell selection the mouse event even occurred.
4748 StyleUserSelect selectStyle;
4749 // check for select: none
4750 if (!IsSelectable(&selectStyle)) {
4751 return NS_OK;
4752 }
4753
4754 bool useFrameSelection = (selectStyle == StyleUserSelect::Text);
4755
4756 // If the mouse is dragged outside the nearest enclosing scrollable area
4757 // while making a selection, the area will be scrolled. To do this, capture
4758 // the mouse on the nearest scrollable frame. If there isn't a scrollable
4759 // frame, or something else is already capturing the mouse, there's no
4760 // reason to capture.
4761 if (!PresShell::GetCapturingContent()) {
4762 nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetNearestScrollableFrame(
4763 this, nsLayoutUtils::SCROLLABLE_SAME_DOC |
4764 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
4765 if (scrollFrame) {
4766 nsIFrame* capturingFrame = do_QueryFrame(scrollFrame);
4767 PresShell::SetCapturingContent(capturingFrame->GetContent(),
4768 CaptureFlags::IgnoreAllowedState);
4769 }
4770 }
4771
4772 // XXX This is screwy; it really should use the selection frame, not the
4773 // event frame
4774 const nsFrameSelection* frameselection = nullptr;
4775 if (useFrameSelection)
4776 frameselection = GetConstFrameSelection();
4777 else
4778 frameselection = presShell->ConstFrameSelection();
4779
4780 if (!frameselection || frameselection->GetDisplaySelection() ==
4781 nsISelectionController::SELECTION_OFF)
4782 return NS_OK; // nothing to do we cannot affect selection from here
4783
4784 #ifdef XP_MACOSX
4785 if (mouseEvent->IsControl())
4786 return NS_OK; // short circuit. hard coded for mac due to time restraints.
4787 bool control = mouseEvent->IsMeta();
4788 #else
4789 bool control = mouseEvent->IsControl();
4790 #endif
4791
4792 RefPtr<nsFrameSelection> fc = const_cast<nsFrameSelection*>(frameselection);
4793 if (mouseEvent->mClickCount > 1) {
4794 // These methods aren't const but can't actually delete anything,
4795 // so no need for AutoWeakFrame.
4796 fc->SetDragState(true);
4797 return HandleMultiplePress(aPresContext, mouseEvent, aEventStatus, control);
4798 }
4799
4800 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent,
4801 RelativeTo{this});
4802 ContentOffsets offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN);
4803
4804 if (!offsets.content) return NS_ERROR_FAILURE;
4805
4806 // Let Ctrl/Cmd+mouse down do table selection instead of drag initiation
4807 nsCOMPtr<nsIContent> parentContent;
4808 int32_t contentOffset;
4809 TableSelectionMode target;
4810 nsresult rv;
4811 rv = GetDataForTableSelection(frameselection, presShell, mouseEvent,
4812 getter_AddRefs(parentContent), &contentOffset,
4813 &target);
4814 if (NS_SUCCEEDED(rv) && parentContent) {
4815 fc->SetDragState(true);
4816 return fc->HandleTableSelection(parentContent, contentOffset, target,
4817 mouseEvent);
4818 }
4819
4820 fc->SetDelayedCaretData(0);
4821
4822 // Check if any part of this frame is selected, and if the
4823 // user clicked inside the selected region. If so, we delay
4824 // starting a new selection since the user may be trying to
4825 // drag the selected region to some other app.
4826
4827 if (GetContent() && GetContent()->IsMaybeSelected()) {
4828 bool inSelection = false;
4829 UniquePtr<SelectionDetails> details = frameselection->LookUpSelection(
4830 offsets.content, 0, offsets.EndOffset(), false);
4831
4832 //
4833 // If there are any details, check to see if the user clicked
4834 // within any selected region of the frame.
4835 //
4836
4837 for (SelectionDetails* curDetail = details.get(); curDetail;
4838 curDetail = curDetail->mNext.get()) {
4839 //
4840 // If the user clicked inside a selection, then just
4841 // return without doing anything. We will handle placing
4842 // the caret later on when the mouse is released. We ignore
4843 // the spellcheck, find and url formatting selections.
4844 //
4845 if (curDetail->mSelectionType != SelectionType::eSpellCheck &&
4846 curDetail->mSelectionType != SelectionType::eFind &&
4847 curDetail->mSelectionType != SelectionType::eURLSecondary &&
4848 curDetail->mSelectionType != SelectionType::eURLStrikeout &&
4849 curDetail->mStart <= offsets.StartOffset() &&
4850 offsets.EndOffset() <= curDetail->mEnd) {
4851 inSelection = true;
4852 }
4853 }
4854
4855 if (inSelection) {
4856 fc->SetDragState(false);
4857 fc->SetDelayedCaretData(mouseEvent);
4858 return NS_OK;
4859 }
4860 }
4861
4862 fc->SetDragState(true);
4863
4864 // Do not touch any nsFrame members after this point without adding
4865 // weakFrame checks.
4866 const nsFrameSelection::FocusMode focusMode = [&]() {
4867 // If "Shift" and "Ctrl" are both pressed, "Shift" is given precedence. This
4868 // mimics the old behaviour.
4869 if (mouseEvent->IsShift()) {
4870 return nsFrameSelection::FocusMode::kExtendSelection;
4871 }
4872
4873 if (control) {
4874 return nsFrameSelection::FocusMode::kMultiRangeSelection;
4875 }
4876
4877 return nsFrameSelection::FocusMode::kCollapseToNewPoint;
4878 }();
4879
4880 rv = fc->HandleClick(offsets.content, offsets.StartOffset(),
4881 offsets.EndOffset(), focusMode, offsets.associate);
4882
4883 if (NS_FAILED(rv)) return rv;
4884
4885 if (offsets.offset != offsets.secondaryOffset) fc->MaintainSelection();
4886
4887 if (isEditor && !mouseEvent->IsShift() &&
4888 (offsets.EndOffset() - offsets.StartOffset()) == 1) {
4889 // A single node is selected and we aren't extending an existing
4890 // selection, which means the user clicked directly on an object (either
4891 // -moz-user-select: all or a non-text node without children).
4892 // Therefore, disable selection extension during mouse moves.
4893 // XXX This is a bit hacky; shouldn't editor be able to deal with this?
4894 fc->SetDragState(false);
4895 }
4896
4897 return rv;
4898 }
4899
4900 /*
4901 * SelectByTypeAtPoint
4902 *
4903 * Search for selectable content at point and attempt to select
4904 * based on the start and end selection behaviours.
4905 *
4906 * @param aPresContext Presentation context
4907 * @param aPoint Point at which selection will occur. Coordinates
4908 * should be relaitve to this frame.
4909 * @param aBeginAmountType, aEndAmountType Selection behavior, see
4910 * nsIFrame for definitions.
4911 * @param aSelectFlags Selection flags defined in nsFame.h.
4912 * @return success or failure at finding suitable content to select.
4913 */
SelectByTypeAtPoint(nsPresContext * aPresContext,const nsPoint & aPoint,nsSelectionAmount aBeginAmountType,nsSelectionAmount aEndAmountType,uint32_t aSelectFlags)4914 nsresult nsIFrame::SelectByTypeAtPoint(nsPresContext* aPresContext,
4915 const nsPoint& aPoint,
4916 nsSelectionAmount aBeginAmountType,
4917 nsSelectionAmount aEndAmountType,
4918 uint32_t aSelectFlags) {
4919 NS_ENSURE_ARG_POINTER(aPresContext);
4920
4921 // No point in selecting if selection is turned off
4922 if (DetermineDisplaySelection() == nsISelectionController::SELECTION_OFF) {
4923 return NS_OK;
4924 }
4925
4926 ContentOffsets offsets = GetContentOffsetsFromPoint(aPoint, SKIP_HIDDEN);
4927 if (!offsets.content) return NS_ERROR_FAILURE;
4928
4929 int32_t offset;
4930 nsIFrame* theFrame = nsFrameSelection::GetFrameForNodeOffset(
4931 offsets.content, offsets.offset, offsets.associate, &offset);
4932 if (!theFrame) return NS_ERROR_FAILURE;
4933
4934 nsFrame* frame = static_cast<nsFrame*>(theFrame);
4935 return frame->PeekBackwardAndForward(aBeginAmountType, aEndAmountType, offset,
4936 aBeginAmountType != eSelectWord,
4937 aSelectFlags);
4938 }
4939
4940 /**
4941 * Multiple Mouse Press -- line or paragraph selection -- for the frame.
4942 * Wouldn't it be nice if this didn't have to be hardwired into Frame code?
4943 */
4944 NS_IMETHODIMP
HandleMultiplePress(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus,bool aControlHeld)4945 nsIFrame::HandleMultiplePress(nsPresContext* aPresContext,
4946 WidgetGUIEvent* aEvent,
4947 nsEventStatus* aEventStatus, bool aControlHeld) {
4948 NS_ENSURE_ARG_POINTER(aEvent);
4949 NS_ENSURE_ARG_POINTER(aEventStatus);
4950
4951 if (nsEventStatus_eConsumeNoDefault == *aEventStatus ||
4952 DetermineDisplaySelection() == nsISelectionController::SELECTION_OFF) {
4953 return NS_OK;
4954 }
4955
4956 // Find out whether we're doing line or paragraph selection.
4957 // If browser.triple_click_selects_paragraph is true, triple-click selects
4958 // paragraph. Otherwise, triple-click selects line, and quadruple-click
4959 // selects paragraph (on platforms that support quadruple-click).
4960 nsSelectionAmount beginAmount, endAmount;
4961 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
4962 if (!mouseEvent) {
4963 return NS_OK;
4964 }
4965
4966 if (mouseEvent->mClickCount == 4) {
4967 beginAmount = endAmount = eSelectParagraph;
4968 } else if (mouseEvent->mClickCount == 3) {
4969 if (Preferences::GetBool("browser.triple_click_selects_paragraph")) {
4970 beginAmount = endAmount = eSelectParagraph;
4971 } else {
4972 beginAmount = eSelectBeginLine;
4973 endAmount = eSelectEndLine;
4974 }
4975 } else if (mouseEvent->mClickCount == 2) {
4976 // We only want inline frames; PeekBackwardAndForward dislikes blocks
4977 beginAmount = endAmount = eSelectWord;
4978 } else {
4979 return NS_OK;
4980 }
4981
4982 nsPoint relPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(
4983 mouseEvent, RelativeTo{this});
4984 return SelectByTypeAtPoint(aPresContext, relPoint, beginAmount, endAmount,
4985 (aControlHeld ? SELECT_ACCUMULATE : 0));
4986 }
4987
PeekBackwardAndForward(nsSelectionAmount aAmountBack,nsSelectionAmount aAmountForward,int32_t aStartPos,bool aJumpLines,uint32_t aSelectFlags)4988 nsresult nsFrame::PeekBackwardAndForward(nsSelectionAmount aAmountBack,
4989 nsSelectionAmount aAmountForward,
4990 int32_t aStartPos, bool aJumpLines,
4991 uint32_t aSelectFlags) {
4992 nsIFrame* baseFrame = this;
4993 int32_t baseOffset = aStartPos;
4994 nsresult rv;
4995
4996 if (aAmountBack == eSelectWord) {
4997 // To avoid selecting the previous word when at start of word,
4998 // first move one character forward.
4999 nsPeekOffsetStruct pos(eSelectCharacter, eDirNext, aStartPos, nsPoint(0, 0),
5000 aJumpLines,
5001 true, // limit on scrolled views
5002 false, false, false);
5003 rv = PeekOffset(&pos);
5004 if (NS_SUCCEEDED(rv)) {
5005 baseFrame = pos.mResultFrame;
5006 baseOffset = pos.mContentOffset;
5007 }
5008 }
5009
5010 // Use peek offset one way then the other:
5011 nsPeekOffsetStruct startpos(aAmountBack, eDirPrevious, baseOffset,
5012 nsPoint(0, 0), aJumpLines,
5013 true, // limit on scrolled views
5014 false, false, false);
5015 rv = baseFrame->PeekOffset(&startpos);
5016 if (NS_FAILED(rv)) return rv;
5017
5018 nsPeekOffsetStruct endpos(aAmountForward, eDirNext, aStartPos, nsPoint(0, 0),
5019 aJumpLines,
5020 true, // limit on scrolled views
5021 false, false, false);
5022 rv = PeekOffset(&endpos);
5023 if (NS_FAILED(rv)) return rv;
5024
5025 // Keep frameSelection alive.
5026 RefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
5027
5028 const nsFrameSelection::FocusMode focusMode =
5029 (aSelectFlags & SELECT_ACCUMULATE)
5030 ? nsFrameSelection::FocusMode::kMultiRangeSelection
5031 : nsFrameSelection::FocusMode::kCollapseToNewPoint;
5032 rv = frameSelection->HandleClick(
5033 startpos.mResultContent, startpos.mContentOffset, startpos.mContentOffset,
5034 focusMode, CARET_ASSOCIATE_AFTER);
5035 if (NS_FAILED(rv)) return rv;
5036
5037 rv = frameSelection->HandleClick(
5038 endpos.mResultContent, endpos.mContentOffset, endpos.mContentOffset,
5039 nsFrameSelection::FocusMode::kExtendSelection, CARET_ASSOCIATE_BEFORE);
5040 if (NS_FAILED(rv)) return rv;
5041
5042 // maintain selection
5043 return frameSelection->MaintainSelection(aAmountBack);
5044 }
5045
HandleDrag(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus)5046 NS_IMETHODIMP nsIFrame::HandleDrag(nsPresContext* aPresContext,
5047 WidgetGUIEvent* aEvent,
5048 nsEventStatus* aEventStatus) {
5049 MOZ_ASSERT(aEvent->mClass == eMouseEventClass,
5050 "HandleDrag can only handle mouse event");
5051
5052 RefPtr<nsFrameSelection> frameselection = GetFrameSelection();
5053 if (!frameselection) {
5054 return NS_OK;
5055 }
5056
5057 bool mouseDown = frameselection->GetDragState();
5058 if (!mouseDown) {
5059 return NS_OK;
5060 }
5061
5062 nsIFrame* scrollbar =
5063 nsLayoutUtils::GetClosestFrameOfType(this, LayoutFrameType::Scrollbar);
5064 if (!scrollbar) {
5065 // XXX Do we really need to exclude non-selectable content here?
5066 // GetContentOffsetsFromPoint can handle it just fine, although some
5067 // other stuff might not like it.
5068 // NOTE: DetermineDisplaySelection() returns SELECTION_OFF for
5069 // non-selectable frames.
5070 if (DetermineDisplaySelection() == nsISelectionController::SELECTION_OFF) {
5071 return NS_OK;
5072 }
5073 }
5074
5075 frameselection->StopAutoScrollTimer();
5076
5077 // Check if we are dragging in a table cell
5078 nsCOMPtr<nsIContent> parentContent;
5079 int32_t contentOffset;
5080 TableSelectionMode target;
5081 WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
5082 mozilla::PresShell* presShell = aPresContext->PresShell();
5083 nsresult result;
5084 result = GetDataForTableSelection(frameselection, presShell, mouseEvent,
5085 getter_AddRefs(parentContent),
5086 &contentOffset, &target);
5087
5088 AutoWeakFrame weakThis = this;
5089 if (NS_SUCCEEDED(result) && parentContent) {
5090 result = frameselection->HandleTableSelection(parentContent, contentOffset,
5091 target, mouseEvent);
5092 if (NS_WARN_IF(NS_FAILED(result))) {
5093 return result;
5094 }
5095 } else {
5096 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent,
5097 RelativeTo{this});
5098 frameselection->HandleDrag(this, pt);
5099 }
5100
5101 // The frameselection object notifies selection listeners synchronously above
5102 // which might have killed us.
5103 if (!weakThis.IsAlive()) {
5104 return NS_OK;
5105 }
5106
5107 // get the nearest scrollframe
5108 nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetNearestScrollableFrame(
5109 this, nsLayoutUtils::SCROLLABLE_SAME_DOC |
5110 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
5111
5112 if (scrollFrame) {
5113 nsIFrame* capturingFrame = scrollFrame->GetScrolledFrame();
5114 if (capturingFrame) {
5115 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(
5116 mouseEvent, RelativeTo{capturingFrame});
5117 frameselection->StartAutoScrollTimer(capturingFrame, pt, 30);
5118 }
5119 }
5120
5121 return NS_OK;
5122 }
5123
5124 /**
5125 * This static method handles part of the nsFrame::HandleRelease in a way
5126 * which doesn't rely on the nsFrame object to stay alive.
5127 */
HandleFrameSelection(nsFrameSelection * aFrameSelection,nsIFrame::ContentOffsets & aOffsets,bool aHandleTableSel,int32_t aContentOffsetForTableSel,TableSelectionMode aTargetForTableSel,nsIContent * aParentContentForTableSel,WidgetGUIEvent * aEvent,const nsEventStatus * aEventStatus)5128 static nsresult HandleFrameSelection(nsFrameSelection* aFrameSelection,
5129 nsIFrame::ContentOffsets& aOffsets,
5130 bool aHandleTableSel,
5131 int32_t aContentOffsetForTableSel,
5132 TableSelectionMode aTargetForTableSel,
5133 nsIContent* aParentContentForTableSel,
5134 WidgetGUIEvent* aEvent,
5135 const nsEventStatus* aEventStatus) {
5136 if (!aFrameSelection) {
5137 return NS_OK;
5138 }
5139
5140 nsresult rv = NS_OK;
5141
5142 if (nsEventStatus_eConsumeNoDefault != *aEventStatus) {
5143 if (!aHandleTableSel) {
5144 if (!aOffsets.content || !aFrameSelection->HasDelayedCaretData()) {
5145 return NS_ERROR_FAILURE;
5146 }
5147
5148 // We are doing this to simulate what we would have done on HandlePress.
5149 // We didn't do it there to give the user an opportunity to drag
5150 // the text, but since they didn't drag, we want to place the
5151 // caret.
5152 // However, we'll use the mouse position from the release, since:
5153 // * it's easier
5154 // * that's the normal click position to use (although really, in
5155 // the normal case, small movements that don't count as a drag
5156 // can do selection)
5157 aFrameSelection->SetDragState(true);
5158
5159 const nsFrameSelection::FocusMode focusMode =
5160 aFrameSelection->IsShiftDownInDelayedCaretData()
5161 ? nsFrameSelection::FocusMode::kExtendSelection
5162 : nsFrameSelection::FocusMode::kCollapseToNewPoint;
5163 rv = aFrameSelection->HandleClick(
5164 aOffsets.content, aOffsets.StartOffset(), aOffsets.EndOffset(),
5165 focusMode, aOffsets.associate);
5166 if (NS_FAILED(rv)) {
5167 return rv;
5168 }
5169 } else if (aParentContentForTableSel) {
5170 aFrameSelection->SetDragState(false);
5171 rv = aFrameSelection->HandleTableSelection(
5172 aParentContentForTableSel, aContentOffsetForTableSel,
5173 aTargetForTableSel, aEvent->AsMouseEvent());
5174 if (NS_FAILED(rv)) {
5175 return rv;
5176 }
5177 }
5178 aFrameSelection->SetDelayedCaretData(0);
5179 }
5180
5181 aFrameSelection->SetDragState(false);
5182 aFrameSelection->StopAutoScrollTimer();
5183
5184 return NS_OK;
5185 }
5186
HandleRelease(nsPresContext * aPresContext,WidgetGUIEvent * aEvent,nsEventStatus * aEventStatus)5187 NS_IMETHODIMP nsIFrame::HandleRelease(nsPresContext* aPresContext,
5188 WidgetGUIEvent* aEvent,
5189 nsEventStatus* aEventStatus) {
5190 if (aEvent->mClass != eMouseEventClass) {
5191 return NS_OK;
5192 }
5193
5194 nsIFrame* activeFrame = GetActiveSelectionFrame(aPresContext, this);
5195
5196 nsCOMPtr<nsIContent> captureContent = PresShell::GetCapturingContent();
5197
5198 // We can unconditionally stop capturing because
5199 // we should never be capturing when the mouse button is up
5200 PresShell::ReleaseCapturingContent();
5201
5202 bool selectionOff =
5203 (DetermineDisplaySelection() == nsISelectionController::SELECTION_OFF);
5204
5205 RefPtr<nsFrameSelection> frameselection;
5206 ContentOffsets offsets;
5207 nsCOMPtr<nsIContent> parentContent;
5208 int32_t contentOffsetForTableSel = 0;
5209 TableSelectionMode targetForTableSel = TableSelectionMode::None;
5210 bool handleTableSelection = true;
5211
5212 if (!selectionOff) {
5213 frameselection = GetFrameSelection();
5214 if (nsEventStatus_eConsumeNoDefault != *aEventStatus && frameselection) {
5215 // Check if the frameselection recorded the mouse going down.
5216 // If not, the user must have clicked in a part of the selection.
5217 // Place the caret before continuing!
5218
5219 if (frameselection->MouseDownRecorded()) {
5220 nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(
5221 aEvent, RelativeTo{this});
5222 offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN);
5223 handleTableSelection = false;
5224 } else {
5225 GetDataForTableSelection(frameselection, PresShell(),
5226 aEvent->AsMouseEvent(),
5227 getter_AddRefs(parentContent),
5228 &contentOffsetForTableSel, &targetForTableSel);
5229 }
5230 }
5231 }
5232
5233 // We might be capturing in some other document and the event just happened to
5234 // trickle down here. Make sure that document's frame selection is notified.
5235 // Note, this may cause the current nsFrame object to be deleted, bug 336592.
5236 RefPtr<nsFrameSelection> frameSelection;
5237 if (activeFrame != this &&
5238 static_cast<nsFrame*>(activeFrame)->DetermineDisplaySelection() !=
5239 nsISelectionController::SELECTION_OFF) {
5240 frameSelection = activeFrame->GetFrameSelection();
5241 }
5242
5243 // Also check the selection of the capturing content which might be in a
5244 // different document.
5245 if (!frameSelection && captureContent) {
5246 Document* doc = captureContent->GetUncomposedDoc();
5247 if (doc) {
5248 mozilla::PresShell* capturingPresShell = doc->GetPresShell();
5249 if (capturingPresShell &&
5250 capturingPresShell != PresContext()->GetPresShell()) {
5251 frameSelection = capturingPresShell->FrameSelection();
5252 }
5253 }
5254 }
5255
5256 if (frameSelection) {
5257 frameSelection->SetDragState(false);
5258 frameSelection->StopAutoScrollTimer();
5259 nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetNearestScrollableFrame(
5260 this, nsLayoutUtils::SCROLLABLE_SAME_DOC |
5261 nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
5262 if (scrollFrame) {
5263 // Perform any additional scrolling needed to maintain CSS snap point
5264 // requirements when autoscrolling is over.
5265 scrollFrame->ScrollSnap();
5266 }
5267 }
5268
5269 // Do not call any methods of the current object after this point!!!
5270 // The object is perhaps dead!
5271
5272 return selectionOff ? NS_OK
5273 : HandleFrameSelection(
5274 frameselection, offsets, handleTableSelection,
5275 contentOffsetForTableSel, targetForTableSel,
5276 parentContent, aEvent, aEventStatus);
5277 }
5278
5279 struct MOZ_STACK_CLASS FrameContentRange {
FrameContentRangeFrameContentRange5280 FrameContentRange(nsIContent* aContent, int32_t aStart, int32_t aEnd)
5281 : content(aContent), start(aStart), end(aEnd) {}
5282 nsCOMPtr<nsIContent> content;
5283 int32_t start;
5284 int32_t end;
5285 };
5286
5287 // Retrieve the content offsets of a frame
GetRangeForFrame(nsIFrame * aFrame)5288 static FrameContentRange GetRangeForFrame(nsIFrame* aFrame) {
5289 nsIContent* content = aFrame->GetContent();
5290 if (!content) {
5291 NS_WARNING("Frame has no content");
5292 return FrameContentRange(nullptr, -1, -1);
5293 }
5294
5295 LayoutFrameType type = aFrame->Type();
5296 if (type == LayoutFrameType::Text) {
5297 int32_t offset, offsetEnd;
5298 aFrame->GetOffsets(offset, offsetEnd);
5299 return FrameContentRange(content, offset, offsetEnd);
5300 }
5301
5302 if (type == LayoutFrameType::Br) {
5303 nsIContent* parent = content->GetParent();
5304 int32_t beginOffset = parent->ComputeIndexOf(content);
5305 return FrameContentRange(parent, beginOffset, beginOffset);
5306 }
5307
5308 while (content->IsRootOfNativeAnonymousSubtree()) {
5309 content = content->GetParent();
5310 }
5311
5312 nsIContent* parent = content->GetParent();
5313 if (aFrame->IsBlockFrameOrSubclass() || !parent) {
5314 return FrameContentRange(content, 0, content->GetChildCount());
5315 }
5316
5317 // TODO(emilio): Revise this in presence of Shadow DOM / display: contents,
5318 // it's likely that we don't want to just walk the light tree, and we need to
5319 // change the representation of FrameContentRange.
5320 int32_t index = parent->ComputeIndexOf(content);
5321 MOZ_ASSERT(index >= 0);
5322 return FrameContentRange(parent, index, index + 1);
5323 }
5324
5325 // The FrameTarget represents the closest frame to a point that can be selected
5326 // The frame is the frame represented, frameEdge says whether one end of the
5327 // frame is the result (in which case different handling is needed), and
5328 // afterFrame says which end is repersented if frameEdge is true
5329 struct FrameTarget {
FrameTargetFrameTarget5330 FrameTarget(nsIFrame* aFrame, bool aFrameEdge, bool aAfterFrame)
5331 : frame(aFrame), frameEdge(aFrameEdge), afterFrame(aAfterFrame) {}
5332
NullFrameTarget5333 static FrameTarget Null() { return FrameTarget(nullptr, false, false); }
5334
IsNullFrameTarget5335 bool IsNull() { return !frame; }
5336 nsIFrame* frame;
5337 bool frameEdge;
5338 bool afterFrame;
5339 };
5340
5341 // See function implementation for information
5342 static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame,
5343 const nsPoint& aPoint,
5344 uint32_t aFlags);
5345
SelfIsSelectable(nsIFrame * aFrame,uint32_t aFlags)5346 static bool SelfIsSelectable(nsIFrame* aFrame, uint32_t aFlags) {
5347 if ((aFlags & nsIFrame::SKIP_HIDDEN) &&
5348 !aFrame->StyleVisibility()->IsVisible()) {
5349 return false;
5350 }
5351 return !aFrame->IsGeneratedContentFrame() &&
5352 aFrame->StyleUIReset()->mUserSelect != StyleUserSelect::None;
5353 }
5354
SelectionDescendToKids(nsIFrame * aFrame)5355 static bool SelectionDescendToKids(nsIFrame* aFrame) {
5356 StyleUserSelect style = aFrame->StyleUIReset()->mUserSelect;
5357 nsIFrame* parent = aFrame->GetParent();
5358 // If we are only near (not directly over) then don't traverse
5359 // frames with independent selection (e.g. text and list controls)
5360 // unless we're already inside such a frame (see bug 268497). Note that this
5361 // prevents any of the users of this method from entering form controls.
5362 // XXX We might want some way to allow using the up-arrow to go into a form
5363 // control, but the focus didn't work right anyway; it'd probably be enough
5364 // if the left and right arrows could enter textboxes (which I don't believe
5365 // they can at the moment)
5366 return !aFrame->IsGeneratedContentFrame() && style != StyleUserSelect::All &&
5367 style != StyleUserSelect::None &&
5368 ((parent->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION) ||
5369 !(aFrame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION));
5370 }
5371
GetSelectionClosestFrameForChild(nsIFrame * aChild,const nsPoint & aPoint,uint32_t aFlags)5372 static FrameTarget GetSelectionClosestFrameForChild(nsIFrame* aChild,
5373 const nsPoint& aPoint,
5374 uint32_t aFlags) {
5375 nsIFrame* parent = aChild->GetParent();
5376 if (SelectionDescendToKids(aChild)) {
5377 nsPoint pt = aPoint - aChild->GetOffsetTo(parent);
5378 return GetSelectionClosestFrame(aChild, pt, aFlags);
5379 }
5380 return FrameTarget(aChild, false, false);
5381 }
5382
5383 // When the cursor needs to be at the beginning of a block, it shouldn't be
5384 // before the first child. A click on a block whose first child is a block
5385 // should put the cursor in the child. The cursor shouldn't be between the
5386 // blocks, because that's not where it's expected.
5387 // Note that this method is guaranteed to succeed.
DrillDownToSelectionFrame(nsIFrame * aFrame,bool aEndFrame,uint32_t aFlags)5388 static FrameTarget DrillDownToSelectionFrame(nsIFrame* aFrame, bool aEndFrame,
5389 uint32_t aFlags) {
5390 if (SelectionDescendToKids(aFrame)) {
5391 nsIFrame* result = nullptr;
5392 nsIFrame* frame = aFrame->PrincipalChildList().FirstChild();
5393 if (!aEndFrame) {
5394 while (frame && (!SelfIsSelectable(frame, aFlags) || frame->IsEmpty()))
5395 frame = frame->GetNextSibling();
5396 if (frame) result = frame;
5397 } else {
5398 // Because the frame tree is singly linked, to find the last frame,
5399 // we have to iterate through all the frames
5400 // XXX I have a feeling this could be slow for long blocks, although
5401 // I can't find any slowdowns
5402 while (frame) {
5403 if (!frame->IsEmpty() && SelfIsSelectable(frame, aFlags))
5404 result = frame;
5405 frame = frame->GetNextSibling();
5406 }
5407 }
5408 if (result) return DrillDownToSelectionFrame(result, aEndFrame, aFlags);
5409 }
5410 // If the current frame has no targetable children, target the current frame
5411 return FrameTarget(aFrame, true, aEndFrame);
5412 }
5413
5414 // This method finds the closest valid FrameTarget on a given line; if there is
5415 // no valid FrameTarget on the line, it returns a null FrameTarget
GetSelectionClosestFrameForLine(nsBlockFrame * aParent,nsBlockFrame::LineIterator aLine,const nsPoint & aPoint,uint32_t aFlags)5416 static FrameTarget GetSelectionClosestFrameForLine(
5417 nsBlockFrame* aParent, nsBlockFrame::LineIterator aLine,
5418 const nsPoint& aPoint, uint32_t aFlags) {
5419 // Account for end of lines (any iterator from the block is valid)
5420 if (aLine == aParent->LinesEnd())
5421 return DrillDownToSelectionFrame(aParent, true, aFlags);
5422 nsIFrame* frame = aLine->mFirstChild;
5423 nsIFrame* closestFromIStart = nullptr;
5424 nsIFrame* closestFromIEnd = nullptr;
5425 nscoord closestIStart = aLine->IStart(), closestIEnd = aLine->IEnd();
5426 WritingMode wm = aLine->mWritingMode;
5427 LogicalPoint pt(wm, aPoint, aLine->mContainerSize);
5428 bool canSkipBr = false;
5429 bool lastFrameWasEditable = false;
5430 for (int32_t n = aLine->GetChildCount(); n;
5431 --n, frame = frame->GetNextSibling()) {
5432 // Skip brFrames. Can only skip if the line contains at least
5433 // one selectable and non-empty frame before. Also, avoid skipping brs if
5434 // the previous thing had a different editableness than us, since then we
5435 // may end up not being able to select after it if the br is the last thing
5436 // on the line.
5437 if (!SelfIsSelectable(frame, aFlags) || frame->IsEmpty() ||
5438 (canSkipBr && frame->IsBrFrame() &&
5439 lastFrameWasEditable == frame->GetContent()->IsEditable())) {
5440 continue;
5441 }
5442 canSkipBr = true;
5443 lastFrameWasEditable =
5444 frame->GetContent() && frame->GetContent()->IsEditable();
5445 LogicalRect frameRect =
5446 LogicalRect(wm, frame->GetRect(), aLine->mContainerSize);
5447 if (pt.I(wm) >= frameRect.IStart(wm)) {
5448 if (pt.I(wm) < frameRect.IEnd(wm)) {
5449 return GetSelectionClosestFrameForChild(frame, aPoint, aFlags);
5450 }
5451 if (frameRect.IEnd(wm) >= closestIStart) {
5452 closestFromIStart = frame;
5453 closestIStart = frameRect.IEnd(wm);
5454 }
5455 } else {
5456 if (frameRect.IStart(wm) <= closestIEnd) {
5457 closestFromIEnd = frame;
5458 closestIEnd = frameRect.IStart(wm);
5459 }
5460 }
5461 }
5462 if (!closestFromIStart && !closestFromIEnd) {
5463 // We should only get here if there are no selectable frames on a line
5464 // XXX Do we need more elaborate handling here?
5465 return FrameTarget::Null();
5466 }
5467 if (closestFromIStart &&
5468 (!closestFromIEnd ||
5469 (abs(pt.I(wm) - closestIStart) <= abs(pt.I(wm) - closestIEnd)))) {
5470 return GetSelectionClosestFrameForChild(closestFromIStart, aPoint, aFlags);
5471 }
5472 return GetSelectionClosestFrameForChild(closestFromIEnd, aPoint, aFlags);
5473 }
5474
5475 // This method is for the special handling we do for block frames; they're
5476 // special because they represent paragraphs and because they are organized
5477 // into lines, which have bounds that are not stored elsewhere in the
5478 // frame tree. Returns a null FrameTarget for frames which are not
5479 // blocks or blocks with no lines except editable one.
GetSelectionClosestFrameForBlock(nsIFrame * aFrame,const nsPoint & aPoint,uint32_t aFlags)5480 static FrameTarget GetSelectionClosestFrameForBlock(nsIFrame* aFrame,
5481 const nsPoint& aPoint,
5482 uint32_t aFlags) {
5483 nsBlockFrame* bf = do_QueryFrame(aFrame);
5484 if (!bf) return FrameTarget::Null();
5485
5486 // This code searches for the correct line
5487 nsBlockFrame::LineIterator end = bf->LinesEnd();
5488 nsBlockFrame::LineIterator curLine = bf->LinesBegin();
5489 nsBlockFrame::LineIterator closestLine = end;
5490
5491 if (curLine != end) {
5492 // Convert aPoint into a LogicalPoint in the writing-mode of this block
5493 WritingMode wm = curLine->mWritingMode;
5494 LogicalPoint pt(wm, aPoint, curLine->mContainerSize);
5495 do {
5496 // Check to see if our point lies within the line's block-direction bounds
5497 nscoord BCoord = pt.B(wm) - curLine->BStart();
5498 nscoord BSize = curLine->BSize();
5499 if (BCoord >= 0 && BCoord < BSize) {
5500 closestLine = curLine;
5501 break; // We found the line; stop looking
5502 }
5503 if (BCoord < 0) break;
5504 ++curLine;
5505 } while (curLine != end);
5506
5507 if (closestLine == end) {
5508 nsBlockFrame::LineIterator prevLine = curLine.prev();
5509 nsBlockFrame::LineIterator nextLine = curLine;
5510 // Avoid empty lines
5511 while (nextLine != end && nextLine->IsEmpty()) ++nextLine;
5512 while (prevLine != end && prevLine->IsEmpty()) --prevLine;
5513
5514 // This hidden pref dictates whether a point above or below all lines
5515 // comes up with a line or the beginning or end of the frame; 0 on
5516 // Windows, 1 on other platforms by default at the writing of this code
5517 int32_t dragOutOfFrame =
5518 Preferences::GetInt("browser.drag_out_of_frame_style");
5519
5520 if (prevLine == end) {
5521 if (dragOutOfFrame == 1 || nextLine == end)
5522 return DrillDownToSelectionFrame(aFrame, false, aFlags);
5523 closestLine = nextLine;
5524 } else if (nextLine == end) {
5525 if (dragOutOfFrame == 1)
5526 return DrillDownToSelectionFrame(aFrame, true, aFlags);
5527 closestLine = prevLine;
5528 } else { // Figure out which line is closer
5529 if (pt.B(wm) - prevLine->BEnd() < nextLine->BStart() - pt.B(wm))
5530 closestLine = prevLine;
5531 else
5532 closestLine = nextLine;
5533 }
5534 }
5535 }
5536
5537 do {
5538 FrameTarget target =
5539 GetSelectionClosestFrameForLine(bf, closestLine, aPoint, aFlags);
5540 if (!target.IsNull()) return target;
5541 ++closestLine;
5542 } while (closestLine != end);
5543
5544 // Fall back to just targeting the last targetable place
5545 return DrillDownToSelectionFrame(aFrame, true, aFlags);
5546 }
5547
5548 // GetSelectionClosestFrame is the helper function that calculates the closest
5549 // frame to the given point.
5550 // It doesn't completely account for offset styles, so needs to be used in
5551 // restricted environments.
5552 // Cannot handle overlapping frames correctly, so it should receive the output
5553 // of GetFrameForPoint
5554 // Guaranteed to return a valid FrameTarget
GetSelectionClosestFrame(nsIFrame * aFrame,const nsPoint & aPoint,uint32_t aFlags)5555 static FrameTarget GetSelectionClosestFrame(nsIFrame* aFrame,
5556 const nsPoint& aPoint,
5557 uint32_t aFlags) {
5558 {
5559 // Handle blocks; if the frame isn't a block, the method fails
5560 FrameTarget target =
5561 GetSelectionClosestFrameForBlock(aFrame, aPoint, aFlags);
5562 if (!target.IsNull()) return target;
5563 }
5564
5565 if (nsIFrame* kid = aFrame->PrincipalChildList().FirstChild()) {
5566 // Go through all the child frames to find the closest one
5567 nsIFrame::FrameWithDistance closest = {nullptr, nscoord_MAX, nscoord_MAX};
5568 for (; kid; kid = kid->GetNextSibling()) {
5569 if (!SelfIsSelectable(kid, aFlags) || kid->IsEmpty()) continue;
5570
5571 kid->FindCloserFrameForSelection(aPoint, &closest);
5572 }
5573 if (closest.mFrame) {
5574 if (nsSVGUtils::IsInSVGTextSubtree(closest.mFrame))
5575 return FrameTarget(closest.mFrame, false, false);
5576 return GetSelectionClosestFrameForChild(closest.mFrame, aPoint, aFlags);
5577 }
5578 }
5579
5580 // Use frame edge for grid, flex, table, and non-editable image frames.
5581 const bool useFrameEdge =
5582 aFrame->IsFlexOrGridContainer() || aFrame->IsTableFrame() ||
5583 (static_cast<nsImageFrame*>(do_QueryFrame(aFrame)) &&
5584 !aFrame->GetContent()->IsEditable());
5585 return FrameTarget(aFrame, useFrameEdge, false);
5586 }
5587
OffsetsForSingleFrame(nsIFrame * aFrame,const nsPoint & aPoint)5588 static nsIFrame::ContentOffsets OffsetsForSingleFrame(nsIFrame* aFrame,
5589 const nsPoint& aPoint) {
5590 nsIFrame::ContentOffsets offsets;
5591 FrameContentRange range = GetRangeForFrame(aFrame);
5592 offsets.content = range.content;
5593 // If there are continuations (meaning it's not one rectangle), this is the
5594 // best this function can do
5595 if (aFrame->GetNextContinuation() || aFrame->GetPrevContinuation()) {
5596 offsets.offset = range.start;
5597 offsets.secondaryOffset = range.end;
5598 offsets.associate = CARET_ASSOCIATE_AFTER;
5599 return offsets;
5600 }
5601
5602 // Figure out whether the offsets should be over, after, or before the frame
5603 nsRect rect(nsPoint(0, 0), aFrame->GetSize());
5604
5605 bool isBlock = !aFrame->StyleDisplay()->IsInlineFlow();
5606 bool isRtl = (aFrame->StyleVisibility()->mDirection == StyleDirection::Rtl);
5607 if ((isBlock && rect.y < aPoint.y) ||
5608 (!isBlock && ((isRtl && rect.x + rect.width / 2 > aPoint.x) ||
5609 (!isRtl && rect.x + rect.width / 2 < aPoint.x)))) {
5610 offsets.offset = range.end;
5611 if (rect.Contains(aPoint))
5612 offsets.secondaryOffset = range.start;
5613 else
5614 offsets.secondaryOffset = range.end;
5615 } else {
5616 offsets.offset = range.start;
5617 if (rect.Contains(aPoint))
5618 offsets.secondaryOffset = range.end;
5619 else
5620 offsets.secondaryOffset = range.start;
5621 }
5622 offsets.associate = offsets.offset == range.start ? CARET_ASSOCIATE_AFTER
5623 : CARET_ASSOCIATE_BEFORE;
5624 return offsets;
5625 }
5626
AdjustFrameForSelectionStyles(nsIFrame * aFrame)5627 static nsIFrame* AdjustFrameForSelectionStyles(nsIFrame* aFrame) {
5628 nsIFrame* adjustedFrame = aFrame;
5629 for (nsIFrame* frame = aFrame; frame; frame = frame->GetParent()) {
5630 // These are the conditions that make all children not able to handle
5631 // a cursor.
5632 StyleUserSelect userSelect = frame->StyleUIReset()->mUserSelect;
5633 if (userSelect != StyleUserSelect::Auto &&
5634 userSelect != StyleUserSelect::All) {
5635 break;
5636 }
5637 if (userSelect == StyleUserSelect::All ||
5638 frame->IsGeneratedContentFrame()) {
5639 adjustedFrame = frame;
5640 }
5641 }
5642 return adjustedFrame;
5643 }
5644
GetContentOffsetsFromPoint(const nsPoint & aPoint,uint32_t aFlags)5645 nsIFrame::ContentOffsets nsIFrame::GetContentOffsetsFromPoint(
5646 const nsPoint& aPoint, uint32_t aFlags) {
5647 nsIFrame* adjustedFrame;
5648 if (aFlags & IGNORE_SELECTION_STYLE) {
5649 adjustedFrame = this;
5650 } else {
5651 // This section of code deals with special selection styles. Note that
5652 // -moz-all exists, even though it doesn't need to be explicitly handled.
5653 //
5654 // The offset is forced not to end up in generated content; content offsets
5655 // cannot represent content outside of the document's content tree.
5656
5657 adjustedFrame = AdjustFrameForSelectionStyles(this);
5658
5659 // -moz-user-select: all needs special handling, because clicking on it
5660 // should lead to the whole frame being selected
5661 if (adjustedFrame->StyleUIReset()->mUserSelect == StyleUserSelect::All) {
5662 nsPoint adjustedPoint = aPoint + this->GetOffsetTo(adjustedFrame);
5663 return OffsetsForSingleFrame(adjustedFrame, adjustedPoint);
5664 }
5665
5666 // For other cases, try to find a closest frame starting from the parent of
5667 // the unselectable frame
5668 if (adjustedFrame != this) adjustedFrame = adjustedFrame->GetParent();
5669 }
5670
5671 nsPoint adjustedPoint = aPoint + this->GetOffsetTo(adjustedFrame);
5672
5673 FrameTarget closest =
5674 GetSelectionClosestFrame(adjustedFrame, adjustedPoint, aFlags);
5675
5676 // If the correct offset is at one end of a frame, use offset-based
5677 // calculation method
5678 if (closest.frameEdge) {
5679 ContentOffsets offsets;
5680 FrameContentRange range = GetRangeForFrame(closest.frame);
5681 offsets.content = range.content;
5682 if (closest.afterFrame)
5683 offsets.offset = range.end;
5684 else
5685 offsets.offset = range.start;
5686 offsets.secondaryOffset = offsets.offset;
5687 offsets.associate = offsets.offset == range.start ? CARET_ASSOCIATE_AFTER
5688 : CARET_ASSOCIATE_BEFORE;
5689 return offsets;
5690 }
5691
5692 nsPoint pt;
5693 if (closest.frame != this) {
5694 if (nsSVGUtils::IsInSVGTextSubtree(closest.frame)) {
5695 pt = nsLayoutUtils::TransformAncestorPointToFrame(
5696 RelativeTo{closest.frame}, aPoint, RelativeTo{this});
5697 } else {
5698 pt = aPoint - closest.frame->GetOffsetTo(this);
5699 }
5700 } else {
5701 pt = aPoint;
5702 }
5703 return static_cast<nsFrame*>(closest.frame)
5704 ->CalcContentOffsetsFromFramePoint(pt);
5705
5706 // XXX should I add some kind of offset standardization?
5707 // consider <b>xxxxx</b><i>zzzzz</i>; should any click between the last
5708 // x and first z put the cursor in the same logical position in addition
5709 // to the same visual position?
5710 }
5711
CalcContentOffsetsFromFramePoint(const nsPoint & aPoint)5712 nsIFrame::ContentOffsets nsFrame::CalcContentOffsetsFromFramePoint(
5713 const nsPoint& aPoint) {
5714 return OffsetsForSingleFrame(this, aPoint);
5715 }
5716
AssociateImage(const StyleImage & aImage)5717 bool nsIFrame::AssociateImage(const StyleImage& aImage) {
5718 imgRequestProxy* req = aImage.GetImageRequest();
5719 if (!req) {
5720 return false;
5721 }
5722
5723 mozilla::css::ImageLoader* loader =
5724 PresContext()->Document()->StyleImageLoader();
5725
5726 loader->AssociateRequestToFrame(req, this, 0);
5727 return true;
5728 }
5729
DisassociateImage(const StyleImage & aImage)5730 void nsIFrame::DisassociateImage(const StyleImage& aImage) {
5731 imgRequestProxy* req = aImage.GetImageRequest();
5732 if (!req) {
5733 return;
5734 }
5735
5736 mozilla::css::ImageLoader* loader =
5737 PresContext()->Document()->StyleImageLoader();
5738
5739 loader->DisassociateRequestFromFrame(req, this);
5740 }
5741
GetCursor(const nsPoint &)5742 Maybe<nsIFrame::Cursor> nsIFrame::GetCursor(const nsPoint&) {
5743 StyleCursorKind kind = StyleUI()->mCursor.keyword;
5744 if (kind == StyleCursorKind::Auto) {
5745 // If this is editable, I-beam cursor is better for most elements.
5746 kind = (mContent && mContent->IsEditable()) ? StyleCursorKind::Text
5747 : StyleCursorKind::Default;
5748 }
5749 if (kind == StyleCursorKind::Text && GetWritingMode().IsVertical()) {
5750 // Per CSS UI spec, UA may treat value 'text' as
5751 // 'vertical-text' for vertical text.
5752 kind = StyleCursorKind::VerticalText;
5753 }
5754
5755 return Some(Cursor{kind, AllowCustomCursorImage::Yes});
5756 }
5757
5758 // Resize and incremental reflow
5759
5760 /* virtual */
MarkIntrinsicISizesDirty()5761 void nsIFrame::MarkIntrinsicISizesDirty() {
5762 // This version is meant only for what used to be box-to-block adaptors.
5763 // It should not be called by other derived classes.
5764 if (::IsXULBoxWrapped(this)) {
5765 nsBoxLayoutMetrics* metrics = BoxMetrics();
5766
5767 XULSizeNeedsRecalc(metrics->mPrefSize);
5768 XULSizeNeedsRecalc(metrics->mMinSize);
5769 XULSizeNeedsRecalc(metrics->mMaxSize);
5770 XULSizeNeedsRecalc(metrics->mBlockPrefSize);
5771 XULSizeNeedsRecalc(metrics->mBlockMinSize);
5772 XULCoordNeedsRecalc(metrics->mFlex);
5773 XULCoordNeedsRecalc(metrics->mAscent);
5774 }
5775
5776 // If we're a flex item, clear our flex-item-specific cached measurements
5777 // (which likely depended on our now-stale intrinsic isize).
5778 auto* parentFrame = GetParent();
5779 if (parentFrame && parentFrame->IsFlexContainerFrame()) {
5780 nsFlexContainerFrame::MarkCachedFlexMeasurementsDirty(this);
5781 }
5782
5783 if (GetStateBits() & NS_FRAME_FONT_INFLATION_FLOW_ROOT) {
5784 nsFontInflationData::MarkFontInflationDataTextDirty(this);
5785 }
5786 }
5787
MarkSubtreeDirty()5788 void nsIFrame::MarkSubtreeDirty() {
5789 if (HasAnyStateBits(NS_FRAME_IS_DIRTY)) {
5790 return;
5791 }
5792 // Unconditionally mark given frame dirty.
5793 AddStateBits(NS_FRAME_IS_DIRTY);
5794
5795 // Mark all descendants dirty, unless:
5796 // - Already dirty.
5797 // - TableColGroup
5798 // - XULBox
5799 AutoTArray<nsIFrame*, 32> stack;
5800 for (const auto& childLists : ChildLists()) {
5801 for (nsIFrame* kid : childLists.mList) {
5802 stack.AppendElement(kid);
5803 }
5804 }
5805 while (!stack.IsEmpty()) {
5806 nsIFrame* f = stack.PopLastElement();
5807 if (f->HasAnyStateBits(NS_FRAME_IS_DIRTY) || f->IsTableColGroupFrame() ||
5808 f->IsXULBoxFrame()) {
5809 continue;
5810 }
5811
5812 f->AddStateBits(NS_FRAME_IS_DIRTY);
5813
5814 for (const auto& childLists : f->ChildLists()) {
5815 for (nsIFrame* kid : childLists.mList) {
5816 stack.AppendElement(kid);
5817 }
5818 }
5819 }
5820 }
5821
5822 /* virtual */
GetMinISize(gfxContext * aRenderingContext)5823 nscoord nsIFrame::GetMinISize(gfxContext* aRenderingContext) {
5824 nscoord result = 0;
5825 DISPLAY_MIN_INLINE_SIZE(this, result);
5826 return result;
5827 }
5828
5829 /* virtual */
GetPrefISize(gfxContext * aRenderingContext)5830 nscoord nsIFrame::GetPrefISize(gfxContext* aRenderingContext) {
5831 nscoord result = 0;
5832 DISPLAY_PREF_INLINE_SIZE(this, result);
5833 return result;
5834 }
5835
5836 /* virtual */
AddInlineMinISize(gfxContext * aRenderingContext,nsIFrame::InlineMinISizeData * aData)5837 void nsIFrame::AddInlineMinISize(gfxContext* aRenderingContext,
5838 nsIFrame::InlineMinISizeData* aData) {
5839 nscoord isize = nsLayoutUtils::IntrinsicForContainer(
5840 aRenderingContext, this, nsLayoutUtils::MIN_ISIZE);
5841 aData->DefaultAddInlineMinISize(this, isize);
5842 }
5843
5844 /* virtual */
AddInlinePrefISize(gfxContext * aRenderingContext,nsIFrame::InlinePrefISizeData * aData)5845 void nsIFrame::AddInlinePrefISize(gfxContext* aRenderingContext,
5846 nsIFrame::InlinePrefISizeData* aData) {
5847 nscoord isize = nsLayoutUtils::IntrinsicForContainer(
5848 aRenderingContext, this, nsLayoutUtils::PREF_ISIZE);
5849 aData->DefaultAddInlinePrefISize(isize);
5850 }
5851
DefaultAddInlineMinISize(nsIFrame * aFrame,nscoord aISize,bool aAllowBreak)5852 void nsIFrame::InlineMinISizeData::DefaultAddInlineMinISize(nsIFrame* aFrame,
5853 nscoord aISize,
5854 bool aAllowBreak) {
5855 auto parent = aFrame->GetParent();
5856 MOZ_ASSERT(parent, "Must have a parent if we get here!");
5857 const bool mayBreak = aAllowBreak && !aFrame->CanContinueTextRun() &&
5858 !parent->Style()->ShouldSuppressLineBreak() &&
5859 parent->StyleText()->WhiteSpaceCanWrap(parent);
5860 if (mayBreak) {
5861 OptionallyBreak();
5862 }
5863 mTrailingWhitespace = 0;
5864 mSkipWhitespace = false;
5865 mCurrentLine += aISize;
5866 mAtStartOfLine = false;
5867 if (mayBreak) {
5868 OptionallyBreak();
5869 }
5870 }
5871
DefaultAddInlinePrefISize(nscoord aISize)5872 void nsIFrame::InlinePrefISizeData::DefaultAddInlinePrefISize(nscoord aISize) {
5873 mCurrentLine = NSCoordSaturatingAdd(mCurrentLine, aISize);
5874 mTrailingWhitespace = 0;
5875 mSkipWhitespace = false;
5876 mLineIsEmpty = false;
5877 }
5878
ForceBreak()5879 void nsIFrame::InlineMinISizeData::ForceBreak() {
5880 mCurrentLine -= mTrailingWhitespace;
5881 mPrevLines = std::max(mPrevLines, mCurrentLine);
5882 mCurrentLine = mTrailingWhitespace = 0;
5883
5884 for (uint32_t i = 0, i_end = mFloats.Length(); i != i_end; ++i) {
5885 nscoord float_min = mFloats[i].Width();
5886 if (float_min > mPrevLines) mPrevLines = float_min;
5887 }
5888 mFloats.Clear();
5889 mSkipWhitespace = true;
5890 }
5891
OptionallyBreak(nscoord aHyphenWidth)5892 void nsIFrame::InlineMinISizeData::OptionallyBreak(nscoord aHyphenWidth) {
5893 // If we can fit more content into a smaller width by staying on this
5894 // line (because we're still at a negative offset due to negative
5895 // text-indent or negative margin), don't break. Otherwise, do the
5896 // same as ForceBreak. it doesn't really matter when we accumulate
5897 // floats.
5898 if (mCurrentLine + aHyphenWidth < 0 || mAtStartOfLine) return;
5899 mCurrentLine += aHyphenWidth;
5900 ForceBreak();
5901 }
5902
ForceBreak(StyleClear aBreakType)5903 void nsIFrame::InlinePrefISizeData::ForceBreak(StyleClear aBreakType) {
5904 MOZ_ASSERT(aBreakType == StyleClear::None || aBreakType == StyleClear::Both ||
5905 aBreakType == StyleClear::Left ||
5906 aBreakType == StyleClear::Right,
5907 "Must be a physical break type");
5908
5909 // If this force break is not clearing any float, we can leave all the
5910 // floats to the next force break.
5911 if (mFloats.Length() != 0 && aBreakType != StyleClear::None) {
5912 // preferred widths accumulated for floats that have already
5913 // been cleared past
5914 nscoord floats_done = 0,
5915 // preferred widths accumulated for floats that have not yet
5916 // been cleared past
5917 floats_cur_left = 0, floats_cur_right = 0;
5918
5919 for (uint32_t i = 0, i_end = mFloats.Length(); i != i_end; ++i) {
5920 const FloatInfo& floatInfo = mFloats[i];
5921 const nsStyleDisplay* floatDisp = floatInfo.Frame()->StyleDisplay();
5922 StyleClear breakType = floatDisp->mBreakType;
5923 if (breakType == StyleClear::Left || breakType == StyleClear::Right ||
5924 breakType == StyleClear::Both) {
5925 nscoord floats_cur =
5926 NSCoordSaturatingAdd(floats_cur_left, floats_cur_right);
5927 if (floats_cur > floats_done) {
5928 floats_done = floats_cur;
5929 }
5930 if (breakType != StyleClear::Right) {
5931 floats_cur_left = 0;
5932 }
5933 if (breakType != StyleClear::Left) {
5934 floats_cur_right = 0;
5935 }
5936 }
5937
5938 StyleFloat floatStyle = floatDisp->mFloat;
5939 nscoord& floats_cur =
5940 floatStyle == StyleFloat::Left ? floats_cur_left : floats_cur_right;
5941 nscoord floatWidth = floatInfo.Width();
5942 // Negative-width floats don't change the available space so they
5943 // shouldn't change our intrinsic line width either.
5944 floats_cur = NSCoordSaturatingAdd(floats_cur, std::max(0, floatWidth));
5945 }
5946
5947 nscoord floats_cur =
5948 NSCoordSaturatingAdd(floats_cur_left, floats_cur_right);
5949 if (floats_cur > floats_done) floats_done = floats_cur;
5950
5951 mCurrentLine = NSCoordSaturatingAdd(mCurrentLine, floats_done);
5952
5953 if (aBreakType == StyleClear::Both) {
5954 mFloats.Clear();
5955 } else {
5956 // If the break type does not clear all floats, it means there may
5957 // be some floats whose isize should contribute to the intrinsic
5958 // isize of the next line. The code here scans the current mFloats
5959 // and keeps floats which are not cleared by this break. Note that
5960 // floats may be cleared directly or indirectly. See below.
5961 nsTArray<FloatInfo> newFloats;
5962 MOZ_ASSERT(
5963 aBreakType == StyleClear::Left || aBreakType == StyleClear::Right,
5964 "Other values should have been handled in other branches");
5965 StyleFloat clearFloatType =
5966 aBreakType == StyleClear::Left ? StyleFloat::Left : StyleFloat::Right;
5967 // Iterate the array in reverse so that we can stop when there are
5968 // no longer any floats we need to keep. See below.
5969 for (FloatInfo& floatInfo : Reversed(mFloats)) {
5970 const nsStyleDisplay* floatDisp = floatInfo.Frame()->StyleDisplay();
5971 if (floatDisp->mFloat != clearFloatType) {
5972 newFloats.AppendElement(floatInfo);
5973 } else {
5974 // This is a float on the side that this break directly clears
5975 // which means we're not keeping it in mFloats. However, if
5976 // this float clears floats on the opposite side (via a value
5977 // of either 'both' or one of 'left'/'right'), any remaining
5978 // (earlier) floats on that side would be indirectly cleared
5979 // as well. Thus, we should break out of this loop and stop
5980 // considering earlier floats to be kept in mFloats.
5981 StyleClear floatBreakType = floatDisp->mBreakType;
5982 if (floatBreakType != aBreakType &&
5983 floatBreakType != StyleClear::None) {
5984 break;
5985 }
5986 }
5987 }
5988 newFloats.Reverse();
5989 mFloats = std::move(newFloats);
5990 }
5991 }
5992
5993 mCurrentLine =
5994 NSCoordSaturatingSubtract(mCurrentLine, mTrailingWhitespace, nscoord_MAX);
5995 mPrevLines = std::max(mPrevLines, mCurrentLine);
5996 mCurrentLine = mTrailingWhitespace = 0;
5997 mSkipWhitespace = true;
5998 mLineIsEmpty = true;
5999 }
6000
ResolveMargin(const LengthPercentageOrAuto & aStyle,nscoord aPercentageBasis)6001 static nscoord ResolveMargin(const LengthPercentageOrAuto& aStyle,
6002 nscoord aPercentageBasis) {
6003 if (aStyle.IsAuto()) {
6004 return nscoord(0);
6005 }
6006 return nsLayoutUtils::ResolveToLength<false>(aStyle.AsLengthPercentage(),
6007 aPercentageBasis);
6008 }
6009
ResolvePadding(const LengthPercentage & aStyle,nscoord aPercentageBasis)6010 static nscoord ResolvePadding(const LengthPercentage& aStyle,
6011 nscoord aPercentageBasis) {
6012 return nsLayoutUtils::ResolveToLength<true>(aStyle, aPercentageBasis);
6013 }
6014
IntrinsicSizeOffsets(nsIFrame * aFrame,nscoord aPercentageBasis,bool aForISize)6015 static nsIFrame::IntrinsicSizeOffsetData IntrinsicSizeOffsets(
6016 nsIFrame* aFrame, nscoord aPercentageBasis, bool aForISize) {
6017 nsIFrame::IntrinsicSizeOffsetData result;
6018 WritingMode wm = aFrame->GetWritingMode();
6019 const auto& margin = aFrame->StyleMargin()->mMargin;
6020 bool verticalAxis = aForISize == wm.IsVertical();
6021 if (verticalAxis) {
6022 result.margin += ResolveMargin(margin.Get(eSideTop), aPercentageBasis);
6023 result.margin += ResolveMargin(margin.Get(eSideBottom), aPercentageBasis);
6024 } else {
6025 result.margin += ResolveMargin(margin.Get(eSideLeft), aPercentageBasis);
6026 result.margin += ResolveMargin(margin.Get(eSideRight), aPercentageBasis);
6027 }
6028
6029 const auto& padding = aFrame->StylePadding()->mPadding;
6030 if (verticalAxis) {
6031 result.padding += ResolvePadding(padding.Get(eSideTop), aPercentageBasis);
6032 result.padding +=
6033 ResolvePadding(padding.Get(eSideBottom), aPercentageBasis);
6034 } else {
6035 result.padding += ResolvePadding(padding.Get(eSideLeft), aPercentageBasis);
6036 result.padding += ResolvePadding(padding.Get(eSideRight), aPercentageBasis);
6037 }
6038
6039 const nsStyleBorder* styleBorder = aFrame->StyleBorder();
6040 if (verticalAxis) {
6041 result.border += styleBorder->GetComputedBorderWidth(eSideTop);
6042 result.border += styleBorder->GetComputedBorderWidth(eSideBottom);
6043 } else {
6044 result.border += styleBorder->GetComputedBorderWidth(eSideLeft);
6045 result.border += styleBorder->GetComputedBorderWidth(eSideRight);
6046 }
6047
6048 const nsStyleDisplay* disp = aFrame->StyleDisplay();
6049 if (aFrame->IsThemed(disp)) {
6050 nsPresContext* presContext = aFrame->PresContext();
6051
6052 LayoutDeviceIntMargin border = presContext->Theme()->GetWidgetBorder(
6053 presContext->DeviceContext(), aFrame, disp->mAppearance);
6054 result.border = presContext->DevPixelsToAppUnits(
6055 verticalAxis ? border.TopBottom() : border.LeftRight());
6056
6057 LayoutDeviceIntMargin padding;
6058 if (presContext->Theme()->GetWidgetPadding(presContext->DeviceContext(),
6059 aFrame, disp->mAppearance,
6060 &padding)) {
6061 result.padding = presContext->DevPixelsToAppUnits(
6062 verticalAxis ? padding.TopBottom() : padding.LeftRight());
6063 }
6064 }
6065 return result;
6066 }
6067
IntrinsicISizeOffsets(nscoord aPercentageBasis)6068 /* virtual */ nsIFrame::IntrinsicSizeOffsetData nsIFrame::IntrinsicISizeOffsets(
6069 nscoord aPercentageBasis) {
6070 return IntrinsicSizeOffsets(this, aPercentageBasis, true);
6071 }
6072
IntrinsicBSizeOffsets(nscoord aPercentageBasis)6073 nsIFrame::IntrinsicSizeOffsetData nsIFrame::IntrinsicBSizeOffsets(
6074 nscoord aPercentageBasis) {
6075 return IntrinsicSizeOffsets(this, aPercentageBasis, false);
6076 }
6077
6078 /* virtual */
GetIntrinsicSize()6079 IntrinsicSize nsIFrame::GetIntrinsicSize() {
6080 return IntrinsicSize(); // default is width/height set to eStyleUnit_None
6081 }
6082
6083 /* virtual */
GetIntrinsicRatio()6084 AspectRatio nsIFrame::GetIntrinsicRatio() { return AspectRatio(); }
6085
6086 /* virtual */
ComputeSize(gfxContext * aRenderingContext,WritingMode aWM,const LogicalSize & aCBSize,nscoord aAvailableISize,const LogicalSize & aMargin,const LogicalSize & aBorder,const LogicalSize & aPadding,ComputeSizeFlags aFlags)6087 LogicalSize nsIFrame::ComputeSize(gfxContext* aRenderingContext,
6088 WritingMode aWM, const LogicalSize& aCBSize,
6089 nscoord aAvailableISize,
6090 const LogicalSize& aMargin,
6091 const LogicalSize& aBorder,
6092 const LogicalSize& aPadding,
6093 ComputeSizeFlags aFlags) {
6094 MOZ_ASSERT(!GetIntrinsicRatio(),
6095 "Please override this method and call "
6096 "nsFrame::ComputeSizeWithIntrinsicDimensions instead.");
6097 LogicalSize result =
6098 ComputeAutoSize(aRenderingContext, aWM, aCBSize, aAvailableISize, aMargin,
6099 aBorder, aPadding, aFlags);
6100 const nsStylePosition* stylePos = StylePosition();
6101
6102 LogicalSize boxSizingAdjust(aWM);
6103 if (stylePos->mBoxSizing == StyleBoxSizing::Border) {
6104 boxSizingAdjust = aBorder + aPadding;
6105 }
6106 nscoord boxSizingToMarginEdgeISize = aMargin.ISize(aWM) + aBorder.ISize(aWM) +
6107 aPadding.ISize(aWM) -
6108 boxSizingAdjust.ISize(aWM);
6109
6110 const auto* inlineStyleCoord = &stylePos->ISize(aWM);
6111 const auto* blockStyleCoord = &stylePos->BSize(aWM);
6112
6113 auto parentFrame = GetParent();
6114 auto alignCB = parentFrame;
6115 bool isGridItem = parentFrame && parentFrame->IsGridContainerFrame() &&
6116 !HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
6117 if (parentFrame && parentFrame->IsTableWrapperFrame() && IsTableFrame()) {
6118 // An inner table frame is sized as a grid item if its table wrapper is,
6119 // because they actually have the same CB (the wrapper's CB).
6120 // @see ReflowInput::InitCBReflowInput
6121 auto tableWrapper = GetParent();
6122 auto grandParent = tableWrapper->GetParent();
6123 isGridItem = (grandParent->IsGridContainerFrame() &&
6124 !(tableWrapper->GetStateBits() & NS_FRAME_OUT_OF_FLOW));
6125 if (isGridItem) {
6126 // When resolving justify/align-self below, we want to use the grid
6127 // container's justify/align-items value and WritingMode.
6128 alignCB = grandParent;
6129 }
6130 }
6131 bool isFlexItem =
6132 parentFrame && parentFrame->IsFlexContainerFrame() &&
6133 !parentFrame->HasAnyStateBits(NS_STATE_FLEX_IS_EMULATING_LEGACY_BOX) &&
6134 !HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
6135 // This variable only gets set (and used) if isFlexItem is true. It
6136 // indicates which axis (in this frame's own WM) corresponds to its
6137 // flex container's main axis.
6138 LogicalAxis flexMainAxis =
6139 eLogicalAxisInline; // (init to make valgrind happy)
6140 if (isFlexItem) {
6141 // Flex items use their "flex-basis" property in place of their main-size
6142 // property for sizing purposes, *unless* they have "flex-basis:auto", in
6143 // which case they use their main-size property after all.
6144 flexMainAxis = nsFlexContainerFrame::IsItemInlineAxisMainAxis(this)
6145 ? eLogicalAxisInline
6146 : eLogicalAxisBlock;
6147
6148 // NOTE: The logic here should match the similar chunk for updating
6149 // mainAxisCoord in nsFrame::ComputeSizeWithIntrinsicDimensions() (aside
6150 // from using a different dummy value in the IsUsedFlexBasisContent() case).
6151 const auto* flexBasis = &stylePos->mFlexBasis;
6152 auto& mainAxisCoord =
6153 (flexMainAxis == eLogicalAxisInline ? inlineStyleCoord
6154 : blockStyleCoord);
6155
6156 // NOTE: If we're a table-wrapper frame, we skip this clause and just stick
6157 // with 'main-size:auto' behavior (which -- unlike 'content'
6158 // i.e. 'max-content' -- will give us the ability to honor percent sizes on
6159 // our table-box child when resolving the flex base size). The flexbox spec
6160 // doesn't call for this special case, but webcompat & regression-avoidance
6161 // seems to require it, for the time being... Tables sure are special.
6162 if (nsFlexContainerFrame::IsUsedFlexBasisContent(*flexBasis,
6163 *mainAxisCoord) &&
6164 MOZ_LIKELY(!IsTableWrapperFrame())) {
6165 static const StyleSize maxContStyleCoord(
6166 StyleSize::ExtremumLength(StyleExtremumLength::MaxContent));
6167 mainAxisCoord = &maxContStyleCoord;
6168 // (Note: if our main axis is the block axis, then this 'max-content'
6169 // value will be treated like 'auto', via the IsAutoBSize() call below.)
6170 } else if (flexBasis->IsSize() && !flexBasis->IsAuto()) {
6171 // For all other non-'auto' flex-basis values, we just swap in the
6172 // flex-basis itself for the main-size property.
6173 mainAxisCoord = &flexBasis->AsSize();
6174 } // else: flex-basis is 'auto', which is deferring to some explicit value
6175 // in mainAxisCoord. So we proceed w/o touching mainAxisCoord.
6176 }
6177
6178 const bool isOrthogonal = aWM.IsOrthogonalTo(alignCB->GetWritingMode());
6179 // Compute inline-axis size
6180 if (!inlineStyleCoord->IsAuto()) {
6181 result.ISize(aWM) = ComputeISizeValue(
6182 aRenderingContext, aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM),
6183 boxSizingToMarginEdgeISize, *inlineStyleCoord, aFlags);
6184 } else if (MOZ_UNLIKELY(isGridItem) && !IS_TRUE_OVERFLOW_CONTAINER(this)) {
6185 // 'auto' inline-size for grid-level box - fill the CB for 'stretch' /
6186 // 'normal' and clamp it to the CB if requested:
6187 bool stretch = false;
6188 if (!(aFlags & nsIFrame::eShrinkWrap) &&
6189 !StyleMargin()->HasInlineAxisAuto(aWM) &&
6190 !alignCB->IsMasonry(isOrthogonal ? eLogicalAxisBlock
6191 : eLogicalAxisInline)) {
6192 auto inlineAxisAlignment =
6193 isOrthogonal ? StylePosition()->UsedAlignSelf(alignCB->Style())._0
6194 : StylePosition()->UsedJustifySelf(alignCB->Style())._0;
6195 stretch = inlineAxisAlignment == StyleAlignFlags::NORMAL ||
6196 inlineAxisAlignment == StyleAlignFlags::STRETCH;
6197 }
6198 if (stretch || (aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize)) {
6199 auto iSizeToFillCB =
6200 std::max(nscoord(0), aCBSize.ISize(aWM) - aPadding.ISize(aWM) -
6201 aBorder.ISize(aWM) - aMargin.ISize(aWM));
6202 if (stretch || result.ISize(aWM) > iSizeToFillCB) {
6203 result.ISize(aWM) = iSizeToFillCB;
6204 }
6205 }
6206 }
6207
6208 // Flex items ignore their min & max sizing properties in their
6209 // flex container's main-axis. (Those properties get applied later in
6210 // the flexbox algorithm.)
6211 const auto& maxISizeCoord = stylePos->MaxISize(aWM);
6212 nscoord maxISize = NS_UNCONSTRAINEDSIZE;
6213 if (!maxISizeCoord.IsNone() &&
6214 !(isFlexItem && flexMainAxis == eLogicalAxisInline)) {
6215 maxISize = ComputeISizeValue(
6216 aRenderingContext, aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM),
6217 boxSizingToMarginEdgeISize, maxISizeCoord, aFlags);
6218 result.ISize(aWM) = std::min(maxISize, result.ISize(aWM));
6219 }
6220
6221 const auto& minISizeCoord = stylePos->MinISize(aWM);
6222 nscoord minISize;
6223 if (!minISizeCoord.IsAuto() &&
6224 !(isFlexItem && flexMainAxis == eLogicalAxisInline)) {
6225 minISize = ComputeISizeValue(
6226 aRenderingContext, aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM),
6227 boxSizingToMarginEdgeISize, minISizeCoord, aFlags);
6228 } else if (MOZ_UNLIKELY(aFlags & eIApplyAutoMinSize)) {
6229 // This implements "Implied Minimum Size of Grid Items".
6230 // https://drafts.csswg.org/css-grid/#min-size-auto
6231 minISize = std::min(maxISize, GetMinISize(aRenderingContext));
6232 if (inlineStyleCoord->IsLengthPercentage()) {
6233 minISize = std::min(minISize, result.ISize(aWM));
6234 } else if (aFlags & eIClampMarginBoxMinSize) {
6235 // "if the grid item spans only grid tracks that have a fixed max track
6236 // sizing function, its automatic minimum size in that dimension is
6237 // further clamped to less than or equal to the size necessary to fit
6238 // its margin box within the resulting grid area (flooring at zero)"
6239 // https://drafts.csswg.org/css-grid/#min-size-auto
6240 auto maxMinISize =
6241 std::max(nscoord(0), aCBSize.ISize(aWM) - aPadding.ISize(aWM) -
6242 aBorder.ISize(aWM) - aMargin.ISize(aWM));
6243 minISize = std::min(minISize, maxMinISize);
6244 }
6245 } else {
6246 // Treat "min-width: auto" as 0.
6247 // NOTE: Technically, "auto" is supposed to behave like "min-content" on
6248 // flex items. However, we don't need to worry about that here, because
6249 // flex items' min-sizes are intentionally ignored until the flex
6250 // container explicitly considers them during space distribution.
6251 minISize = 0;
6252 }
6253 result.ISize(aWM) = std::max(minISize, result.ISize(aWM));
6254
6255 // Compute block-axis size
6256 // (but not if we have auto bsize or if we received the "eUseAutoBSize"
6257 // flag -- then, we'll just stick with the bsize that we already calculated
6258 // in the initial ComputeAutoSize() call.)
6259 if (!(aFlags & nsIFrame::eUseAutoBSize)) {
6260 if (!nsLayoutUtils::IsAutoBSize(*blockStyleCoord, aCBSize.BSize(aWM))) {
6261 result.BSize(aWM) = nsLayoutUtils::ComputeBSizeValue(
6262 aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
6263 blockStyleCoord->AsLengthPercentage());
6264 } else if (MOZ_UNLIKELY(isGridItem) && blockStyleCoord->IsAuto() &&
6265 !IS_TRUE_OVERFLOW_CONTAINER(this) &&
6266 !alignCB->IsMasonry(isOrthogonal ? eLogicalAxisInline
6267 : eLogicalAxisBlock)) {
6268 auto cbSize = aCBSize.BSize(aWM);
6269 if (cbSize != NS_UNCONSTRAINEDSIZE) {
6270 // 'auto' block-size for grid-level box - fill the CB for 'stretch' /
6271 // 'normal' and clamp it to the CB if requested:
6272 bool stretch = false;
6273 if (!StyleMargin()->HasBlockAxisAuto(aWM)) {
6274 auto blockAxisAlignment =
6275 isOrthogonal
6276 ? StylePosition()->UsedJustifySelf(alignCB->Style())._0
6277 : StylePosition()->UsedAlignSelf(alignCB->Style())._0;
6278 stretch = blockAxisAlignment == StyleAlignFlags::NORMAL ||
6279 blockAxisAlignment == StyleAlignFlags::STRETCH;
6280 }
6281 if (stretch || (aFlags & ComputeSizeFlags::eBClampMarginBoxMinSize)) {
6282 auto bSizeToFillCB =
6283 std::max(nscoord(0), cbSize - aPadding.BSize(aWM) -
6284 aBorder.BSize(aWM) - aMargin.BSize(aWM));
6285 if (stretch || (result.BSize(aWM) != NS_UNCONSTRAINEDSIZE &&
6286 result.BSize(aWM) > bSizeToFillCB)) {
6287 result.BSize(aWM) = bSizeToFillCB;
6288 }
6289 }
6290 }
6291 }
6292 }
6293
6294 const auto& maxBSizeCoord = stylePos->MaxBSize(aWM);
6295
6296 if (result.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
6297 if (!nsLayoutUtils::IsAutoBSize(maxBSizeCoord, aCBSize.BSize(aWM)) &&
6298 !(isFlexItem && flexMainAxis == eLogicalAxisBlock)) {
6299 nscoord maxBSize = nsLayoutUtils::ComputeBSizeValue(
6300 aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
6301 maxBSizeCoord.AsLengthPercentage());
6302 result.BSize(aWM) = std::min(maxBSize, result.BSize(aWM));
6303 }
6304
6305 const auto& minBSizeCoord = stylePos->MinBSize(aWM);
6306
6307 if (!nsLayoutUtils::IsAutoBSize(minBSizeCoord, aCBSize.BSize(aWM)) &&
6308 !(isFlexItem && flexMainAxis == eLogicalAxisBlock)) {
6309 nscoord minBSize = nsLayoutUtils::ComputeBSizeValue(
6310 aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
6311 minBSizeCoord.AsLengthPercentage());
6312 result.BSize(aWM) = std::max(minBSize, result.BSize(aWM));
6313 }
6314 }
6315
6316 const nsStyleDisplay* disp = StyleDisplay();
6317 if (IsThemed(disp)) {
6318 LayoutDeviceIntSize widget;
6319 bool canOverride = true;
6320 nsPresContext* presContext = PresContext();
6321 presContext->Theme()->GetMinimumWidgetSize(
6322 presContext, this, disp->mAppearance, &widget, &canOverride);
6323
6324 // Convert themed widget's physical dimensions to logical coords
6325 LogicalSize size(aWM,
6326 nsSize(presContext->DevPixelsToAppUnits(widget.width),
6327 presContext->DevPixelsToAppUnits(widget.height)));
6328
6329 // GMWS() returns border-box; we need content-box
6330 size.ISize(aWM) -= aBorder.ISize(aWM) + aPadding.ISize(aWM);
6331 size.BSize(aWM) -= aBorder.BSize(aWM) + aPadding.BSize(aWM);
6332
6333 if (size.BSize(aWM) > result.BSize(aWM) || !canOverride) {
6334 result.BSize(aWM) = size.BSize(aWM);
6335 }
6336 if (size.ISize(aWM) > result.ISize(aWM) || !canOverride) {
6337 result.ISize(aWM) = size.ISize(aWM);
6338 }
6339 }
6340
6341 result.ISize(aWM) = std::max(0, result.ISize(aWM));
6342 result.BSize(aWM) = std::max(0, result.BSize(aWM));
6343
6344 return result;
6345 }
6346
ComputeSizeWithIntrinsicDimensions(gfxContext * aRenderingContext,WritingMode aWM,const IntrinsicSize & aIntrinsicSize,const AspectRatio & aIntrinsicRatio,const LogicalSize & aCBSize,const LogicalSize & aMargin,const LogicalSize & aBorder,const LogicalSize & aPadding,ComputeSizeFlags aFlags)6347 LogicalSize nsContainerFrame::ComputeSizeWithIntrinsicDimensions(
6348 gfxContext* aRenderingContext, WritingMode aWM,
6349 const IntrinsicSize& aIntrinsicSize, const AspectRatio& aIntrinsicRatio,
6350 const LogicalSize& aCBSize, const LogicalSize& aMargin,
6351 const LogicalSize& aBorder, const LogicalSize& aPadding,
6352 ComputeSizeFlags aFlags) {
6353 auto logicalRatio =
6354 aWM.IsVertical() ? aIntrinsicRatio.Inverted() : aIntrinsicRatio;
6355 const nsStylePosition* stylePos = StylePosition();
6356 const auto* inlineStyleCoord = &stylePos->ISize(aWM);
6357 const auto* blockStyleCoord = &stylePos->BSize(aWM);
6358 auto* parentFrame = GetParent();
6359 const bool isGridItem = parentFrame && parentFrame->IsGridContainerFrame() &&
6360 !HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
6361 const bool isFlexItem =
6362 parentFrame && parentFrame->IsFlexContainerFrame() &&
6363 !parentFrame->HasAnyStateBits(NS_STATE_FLEX_IS_EMULATING_LEGACY_BOX) &&
6364 !HasAnyStateBits(NS_FRAME_OUT_OF_FLOW);
6365 // This variable only gets set (and used) if isFlexItem is true. It
6366 // indicates which axis (in this frame's own WM) corresponds to its
6367 // flex container's main axis.
6368 LogicalAxis flexMainAxis =
6369 eLogicalAxisInline; // (init to make valgrind happy)
6370 Maybe<StyleSize> imposedMainSizeStyleCoord;
6371
6372 // If this is a flex item, and we're measuring its cross size after flexing
6373 // to resolve its main size, then we need to use the resolved main size
6374 // that the container provides to us *instead of* the main-size coordinate
6375 // from our style struct. (Otherwise, we'll be using an irrelevant value in
6376 // the aspect-ratio calculations below.)
6377 if (isFlexItem) {
6378 flexMainAxis = nsFlexContainerFrame::IsItemInlineAxisMainAxis(this)
6379 ? eLogicalAxisInline
6380 : eLogicalAxisBlock;
6381
6382 // If FlexItemMainSizeOverride frame-property is set, then that means the
6383 // flex container is imposing a main-size on this flex item for it to use
6384 // as its size in the container's main axis.
6385 bool didImposeMainSize;
6386 nscoord imposedMainSize =
6387 GetProperty(nsIFrame::FlexItemMainSizeOverride(), &didImposeMainSize);
6388 if (didImposeMainSize) {
6389 imposedMainSizeStyleCoord = Some(StyleSize::LengthPercentage(
6390 LengthPercentage::FromAppUnits(imposedMainSize)));
6391 if (flexMainAxis == eLogicalAxisInline) {
6392 inlineStyleCoord = imposedMainSizeStyleCoord.ptr();
6393 } else {
6394 blockStyleCoord = imposedMainSizeStyleCoord.ptr();
6395 }
6396
6397 } else {
6398 // Flex items use their "flex-basis" property in place of their main-size
6399 // property (e.g. "width") for sizing purposes, *unless* they have
6400 // "flex-basis:auto", in which case they use their main-size property
6401 // after all.
6402 // NOTE: The logic here should match the similar chunk for updating
6403 // mainAxisCoord in nsFrame::ComputeSize() (aside from using a different
6404 // dummy value in the IsUsedFlexBasisContent() case).
6405 const auto* flexBasis = &stylePos->mFlexBasis;
6406 auto& mainAxisCoord =
6407 (flexMainAxis == eLogicalAxisInline ? inlineStyleCoord
6408 : blockStyleCoord);
6409
6410 if (nsFlexContainerFrame::IsUsedFlexBasisContent(*flexBasis,
6411 *mainAxisCoord)) {
6412 // If we get here, we're resolving the flex base size for a flex item,
6413 // and we fall into the flexbox spec section 9.2 step 3, substep C (if
6414 // we have a definite cross size) or E (if not). And specifically:
6415 //
6416 // * If we have a definite cross size, we're supposed to resolve our
6417 // main-size based on that and our intrinsic ratio.
6418 // * Otherwise, we're supposed to produce our max-content size.
6419 //
6420 // Conveniently, we can handle both of those scenarios (regardless of
6421 // which substep we fall into) by using the 'auto' keyword for our
6422 // main-axis coordinate here. (This makes sense, because the spec is
6423 // effectively trying to produce the 'auto' sizing behavior).
6424 static const StyleSize autoSize(StyleSize::Auto());
6425 mainAxisCoord = &autoSize;
6426 } else if (flexBasis->IsSize() && !flexBasis->IsAuto()) {
6427 // For all other non-'auto' flex-basis values, we just swap in the
6428 // flex-basis itself for the main-size property.
6429 mainAxisCoord = &flexBasis->AsSize();
6430 } // else: flex-basis is 'auto', which is deferring to some explicit
6431 // value in mainAxisCoord. So we proceed w/o touching mainAxisCoord.
6432 }
6433 }
6434
6435 // Handle intrinsic sizes and their interaction with
6436 // {min-,max-,}{width,height} according to the rules in
6437 // http://www.w3.org/TR/CSS21/visudet.html#min-max-widths
6438
6439 // Note: throughout the following section of the function, I avoid
6440 // a * (b / c) because of its reduced accuracy relative to a * b / c
6441 // or (a * b) / c (which are equivalent).
6442
6443 const bool isAutoISize = inlineStyleCoord->IsAuto();
6444 const bool isAutoBSize =
6445 nsLayoutUtils::IsAutoBSize(*blockStyleCoord, aCBSize.BSize(aWM));
6446
6447 LogicalSize boxSizingAdjust(aWM);
6448 if (stylePos->mBoxSizing == StyleBoxSizing::Border) {
6449 boxSizingAdjust = aBorder + aPadding;
6450 }
6451 nscoord boxSizingToMarginEdgeISize = aMargin.ISize(aWM) + aBorder.ISize(aWM) +
6452 aPadding.ISize(aWM) -
6453 boxSizingAdjust.ISize(aWM);
6454
6455 nscoord iSize, minISize, maxISize, bSize, minBSize, maxBSize;
6456 enum class Stretch {
6457 // stretch to fill the CB (preserving intrinsic ratio) in the relevant axis
6458 StretchPreservingRatio, // XXX not used yet
6459 // stretch to fill the CB in the relevant axis
6460 Stretch,
6461 // no stretching in the relevant axis
6462 NoStretch,
6463 };
6464 // just to avoid having to type these out everywhere:
6465 const auto eStretchPreservingRatio = Stretch::StretchPreservingRatio;
6466 const auto eStretch = Stretch::Stretch;
6467 const auto eNoStretch = Stretch::NoStretch;
6468
6469 Stretch stretchI = eNoStretch; // stretch behavior in the inline axis
6470 Stretch stretchB = eNoStretch; // stretch behavior in the block axis
6471
6472 const bool isOrthogonal = aWM.IsOrthogonalTo(parentFrame->GetWritingMode());
6473 const bool isVertical = aWM.IsVertical();
6474 const auto& isizeCoord =
6475 isVertical ? aIntrinsicSize.height : aIntrinsicSize.width;
6476 const bool hasIntrinsicISize = isizeCoord.isSome();
6477 nscoord intrinsicISize = std::max(0, isizeCoord.valueOr(0));
6478
6479 const auto& bsizeCoord =
6480 isVertical ? aIntrinsicSize.width : aIntrinsicSize.height;
6481 const bool hasIntrinsicBSize = bsizeCoord.isSome();
6482 nscoord intrinsicBSize = std::max(0, bsizeCoord.valueOr(0));
6483
6484 if (!isAutoISize) {
6485 iSize = ComputeISizeValue(
6486 aRenderingContext, aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM),
6487 boxSizingToMarginEdgeISize, *inlineStyleCoord, aFlags);
6488 } else if (MOZ_UNLIKELY(isGridItem) &&
6489 !parentFrame->IsMasonry(isOrthogonal ? eLogicalAxisBlock
6490 : eLogicalAxisInline)) {
6491 MOZ_ASSERT(!IS_TRUE_OVERFLOW_CONTAINER(this));
6492 // 'auto' inline-size for grid-level box - apply 'stretch' as needed:
6493 auto cbSize = aCBSize.ISize(aWM);
6494 if (cbSize != NS_UNCONSTRAINEDSIZE) {
6495 if (!StyleMargin()->HasInlineAxisAuto(aWM)) {
6496 auto inlineAxisAlignment =
6497 isOrthogonal ? stylePos->UsedAlignSelf(GetParent()->Style())._0
6498 : stylePos->UsedJustifySelf(GetParent()->Style())._0;
6499 // Note: 'normal' means 'start' for elements with an intrinsic size
6500 // or ratio in the relevant dimension, otherwise 'stretch'.
6501 // https://drafts.csswg.org/css-grid/#grid-item-sizing
6502 if ((inlineAxisAlignment == StyleAlignFlags::NORMAL &&
6503 !hasIntrinsicISize && !logicalRatio) ||
6504 inlineAxisAlignment == StyleAlignFlags::STRETCH) {
6505 stretchI = eStretch;
6506 }
6507 }
6508 if (stretchI != eNoStretch ||
6509 (aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize)) {
6510 iSize =
6511 std::max(nscoord(0), cbSize - aPadding.ISize(aWM) -
6512 aBorder.ISize(aWM) - aMargin.ISize(aWM));
6513 }
6514 } else {
6515 // Reset this flag to avoid applying the clamping below.
6516 aFlags =
6517 ComputeSizeFlags(aFlags & ~ComputeSizeFlags::eIClampMarginBoxMinSize);
6518 }
6519 }
6520
6521 const auto& maxISizeCoord = stylePos->MaxISize(aWM);
6522
6523 if (!maxISizeCoord.IsNone() &&
6524 !(isFlexItem && flexMainAxis == eLogicalAxisInline)) {
6525 maxISize = ComputeISizeValue(
6526 aRenderingContext, aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM),
6527 boxSizingToMarginEdgeISize, maxISizeCoord, aFlags);
6528 } else {
6529 maxISize = nscoord_MAX;
6530 }
6531
6532 // NOTE: Flex items ignore their min & max sizing properties in their
6533 // flex container's main-axis. (Those properties get applied later in
6534 // the flexbox algorithm.)
6535
6536 const auto& minISizeCoord = stylePos->MinISize(aWM);
6537
6538 if (!minISizeCoord.IsAuto() &&
6539 !(isFlexItem && flexMainAxis == eLogicalAxisInline)) {
6540 minISize = ComputeISizeValue(
6541 aRenderingContext, aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM),
6542 boxSizingToMarginEdgeISize, minISizeCoord, aFlags);
6543 } else {
6544 // Treat "min-width: auto" as 0.
6545 // NOTE: Technically, "auto" is supposed to behave like "min-content" on
6546 // flex items. However, we don't need to worry about that here, because
6547 // flex items' min-sizes are intentionally ignored until the flex
6548 // container explicitly considers them during space distribution.
6549 minISize = 0;
6550 }
6551
6552 if (!isAutoBSize) {
6553 bSize = nsLayoutUtils::ComputeBSizeValue(
6554 aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
6555 blockStyleCoord->AsLengthPercentage());
6556 } else if (MOZ_UNLIKELY(isGridItem) &&
6557 !parentFrame->IsMasonry(isOrthogonal ? eLogicalAxisInline
6558 : eLogicalAxisBlock)) {
6559 MOZ_ASSERT(!IS_TRUE_OVERFLOW_CONTAINER(this));
6560 // 'auto' block-size for grid-level box - apply 'stretch' as needed:
6561 auto cbSize = aCBSize.BSize(aWM);
6562 if (cbSize != NS_UNCONSTRAINEDSIZE) {
6563 if (!StyleMargin()->HasBlockAxisAuto(aWM)) {
6564 auto blockAxisAlignment =
6565 !isOrthogonal ? stylePos->UsedAlignSelf(GetParent()->Style())._0
6566 : stylePos->UsedJustifySelf(GetParent()->Style())._0;
6567 // Note: 'normal' means 'start' for elements with an intrinsic size
6568 // or ratio in the relevant dimension, otherwise 'stretch'.
6569 // https://drafts.csswg.org/css-grid/#grid-item-sizing
6570 if ((blockAxisAlignment == StyleAlignFlags::NORMAL &&
6571 !hasIntrinsicBSize && !logicalRatio) ||
6572 blockAxisAlignment == StyleAlignFlags::STRETCH) {
6573 stretchB = eStretch;
6574 }
6575 }
6576 if (stretchB != eNoStretch ||
6577 (aFlags & ComputeSizeFlags::eBClampMarginBoxMinSize)) {
6578 bSize =
6579 std::max(nscoord(0), cbSize - aPadding.BSize(aWM) -
6580 aBorder.BSize(aWM) - aMargin.BSize(aWM));
6581 }
6582 } else {
6583 // Reset this flag to avoid applying the clamping below.
6584 aFlags =
6585 ComputeSizeFlags(aFlags & ~ComputeSizeFlags::eBClampMarginBoxMinSize);
6586 }
6587 }
6588
6589 const auto& maxBSizeCoord = stylePos->MaxBSize(aWM);
6590
6591 if (!nsLayoutUtils::IsAutoBSize(maxBSizeCoord, aCBSize.BSize(aWM)) &&
6592 !(isFlexItem && flexMainAxis == eLogicalAxisBlock)) {
6593 maxBSize = nsLayoutUtils::ComputeBSizeValue(
6594 aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
6595 maxBSizeCoord.AsLengthPercentage());
6596 } else {
6597 maxBSize = nscoord_MAX;
6598 }
6599
6600 const auto& minBSizeCoord = stylePos->MinBSize(aWM);
6601
6602 if (!nsLayoutUtils::IsAutoBSize(minBSizeCoord, aCBSize.BSize(aWM)) &&
6603 !(isFlexItem && flexMainAxis == eLogicalAxisBlock)) {
6604 minBSize = nsLayoutUtils::ComputeBSizeValue(
6605 aCBSize.BSize(aWM), boxSizingAdjust.BSize(aWM),
6606 minBSizeCoord.AsLengthPercentage());
6607 } else {
6608 minBSize = 0;
6609 }
6610
6611 NS_ASSERTION(aCBSize.ISize(aWM) != NS_UNCONSTRAINEDSIZE,
6612 "Our containing block must not have unconstrained inline-size!");
6613
6614 // Now calculate the used values for iSize and bSize:
6615
6616 if (isAutoISize) {
6617 if (isAutoBSize) {
6618 // 'auto' iSize, 'auto' bSize
6619
6620 // Get tentative values - CSS 2.1 sections 10.3.2 and 10.6.2:
6621
6622 nscoord tentISize, tentBSize;
6623
6624 if (hasIntrinsicISize) {
6625 tentISize = intrinsicISize;
6626 } else if (hasIntrinsicBSize && logicalRatio) {
6627 tentISize = logicalRatio.ApplyTo(intrinsicBSize);
6628 } else if (logicalRatio) {
6629 tentISize =
6630 aCBSize.ISize(aWM) - boxSizingToMarginEdgeISize; // XXX scrollbar?
6631 if (tentISize < 0) tentISize = 0;
6632 } else {
6633 tentISize = nsPresContext::CSSPixelsToAppUnits(300);
6634 }
6635
6636 // If we need to clamp the inline size to fit the CB, we use the 'stretch'
6637 // or 'normal' codepath. We use the ratio-preserving 'normal' codepath
6638 // unless we have 'stretch' in the other axis.
6639 if ((aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize) &&
6640 stretchI != eStretch && tentISize > iSize) {
6641 stretchI = (stretchB == eStretch ? eStretch : eStretchPreservingRatio);
6642 }
6643
6644 if (hasIntrinsicBSize) {
6645 tentBSize = intrinsicBSize;
6646 } else if (logicalRatio) {
6647 tentBSize = logicalRatio.Inverted().ApplyTo(tentISize);
6648 } else {
6649 tentBSize = nsPresContext::CSSPixelsToAppUnits(150);
6650 }
6651
6652 // (ditto the comment about clamping the inline size above)
6653 if ((aFlags & ComputeSizeFlags::eBClampMarginBoxMinSize) &&
6654 stretchB != eStretch && tentBSize > bSize) {
6655 stretchB = (stretchI == eStretch ? eStretch : eStretchPreservingRatio);
6656 }
6657
6658 if (logicalRatio) {
6659 if (stretchI == eStretch) {
6660 tentISize = iSize; // * / 'stretch'
6661 if (stretchB == eStretch) {
6662 tentBSize = bSize; // 'stretch' / 'stretch'
6663 } else if (stretchB == eStretchPreservingRatio) {
6664 // 'normal' / 'stretch'
6665 tentBSize = logicalRatio.Inverted().ApplyTo(iSize);
6666 }
6667 } else if (stretchB == eStretch) {
6668 tentBSize = bSize; // 'stretch' / * (except 'stretch')
6669 if (stretchI == eStretchPreservingRatio) {
6670 // 'stretch' / 'normal'
6671 tentISize = logicalRatio.ApplyTo(bSize);
6672 }
6673 } else if (stretchI == eStretchPreservingRatio) {
6674 tentISize = iSize; // * (except 'stretch') / 'normal'
6675 tentBSize = logicalRatio.Inverted().ApplyTo(iSize);
6676 if (stretchB == eStretchPreservingRatio && tentBSize > bSize) {
6677 // Stretch within the CB size with preserved intrinsic ratio.
6678 tentBSize = bSize; // 'normal' / 'normal'
6679 tentISize = logicalRatio.ApplyTo(bSize);
6680 }
6681 } else if (stretchB == eStretchPreservingRatio) {
6682 tentBSize = bSize; // 'normal' / * (except 'normal' and 'stretch')
6683 tentISize = logicalRatio.ApplyTo(bSize);
6684 }
6685 }
6686
6687 // ComputeAutoSizeWithIntrinsicDimensions preserves the ratio when
6688 // applying the min/max-size. We don't want that when we have 'stretch'
6689 // in either axis because tentISize/tentBSize is likely not according to
6690 // ratio now.
6691 if (logicalRatio && stretchI != eStretch && stretchB != eStretch) {
6692 nsSize autoSize = nsLayoutUtils::ComputeAutoSizeWithIntrinsicDimensions(
6693 minISize, minBSize, maxISize, maxBSize, tentISize, tentBSize);
6694 // The nsSize that ComputeAutoSizeWithIntrinsicDimensions returns will
6695 // actually contain logical values if the parameters passed to it were
6696 // logical coordinates, so we do NOT perform a physical-to-logical
6697 // conversion here, but just assign the fields directly to our result.
6698 iSize = autoSize.width;
6699 bSize = autoSize.height;
6700 } else {
6701 // Not honoring an intrinsic ratio: clamp the dimensions independently.
6702 iSize = NS_CSS_MINMAX(tentISize, minISize, maxISize);
6703 bSize = NS_CSS_MINMAX(tentBSize, minBSize, maxBSize);
6704 }
6705 } else {
6706 // 'auto' iSize, non-'auto' bSize
6707 bSize = NS_CSS_MINMAX(bSize, minBSize, maxBSize);
6708 if (stretchI != eStretch) {
6709 if (logicalRatio) {
6710 iSize = logicalRatio.ApplyTo(bSize);
6711 } else if (hasIntrinsicISize) {
6712 if (!((aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize) &&
6713 intrinsicISize > iSize)) {
6714 iSize = intrinsicISize;
6715 } // else - leave iSize as is to fill the CB
6716 } else {
6717 iSize = nsPresContext::CSSPixelsToAppUnits(300);
6718 }
6719 } // else - leave iSize as is to fill the CB
6720 iSize = NS_CSS_MINMAX(iSize, minISize, maxISize);
6721 }
6722 } else {
6723 if (isAutoBSize) {
6724 // non-'auto' iSize, 'auto' bSize
6725 iSize = NS_CSS_MINMAX(iSize, minISize, maxISize);
6726 if (stretchB != eStretch) {
6727 if (logicalRatio) {
6728 bSize = logicalRatio.Inverted().ApplyTo(iSize);
6729 } else if (hasIntrinsicBSize) {
6730 if (!((aFlags & ComputeSizeFlags::eBClampMarginBoxMinSize) &&
6731 intrinsicBSize > bSize)) {
6732 bSize = intrinsicBSize;
6733 } // else - leave bSize as is to fill the CB
6734 } else {
6735 bSize = nsPresContext::CSSPixelsToAppUnits(150);
6736 }
6737 } // else - leave bSize as is to fill the CB
6738 bSize = NS_CSS_MINMAX(bSize, minBSize, maxBSize);
6739
6740 } else {
6741 // non-'auto' iSize, non-'auto' bSize
6742 iSize = NS_CSS_MINMAX(iSize, minISize, maxISize);
6743 bSize = NS_CSS_MINMAX(bSize, minBSize, maxBSize);
6744 }
6745 }
6746
6747 return LogicalSize(aWM, iSize, bSize);
6748 }
6749
ComputeTightBounds(DrawTarget * aDrawTarget) const6750 nsRect nsIFrame::ComputeTightBounds(DrawTarget* aDrawTarget) const {
6751 return GetVisualOverflowRect();
6752 }
6753
ComputeSimpleTightBounds(DrawTarget * aDrawTarget) const6754 nsRect nsContainerFrame::ComputeSimpleTightBounds(
6755 DrawTarget* aDrawTarget) const {
6756 if (StyleOutline()->ShouldPaintOutline() || StyleBorder()->HasBorder() ||
6757 !StyleBackground()->IsTransparent(this) ||
6758 StyleDisplay()->HasAppearance()) {
6759 // Not necessarily tight, due to clipping, negative
6760 // outline-offset, and lots of other issues, but that's OK
6761 return GetVisualOverflowRect();
6762 }
6763
6764 nsRect r(0, 0, 0, 0);
6765 for (const auto& childLists : ChildLists()) {
6766 for (nsIFrame* child : childLists.mList) {
6767 r.UnionRect(
6768 r, child->ComputeTightBounds(aDrawTarget) + child->GetPosition());
6769 }
6770 }
6771 return r;
6772 }
6773
6774 /* virtual */
GetPrefWidthTightBounds(gfxContext * aContext,nscoord * aX,nscoord * aXMost)6775 nsresult nsIFrame::GetPrefWidthTightBounds(gfxContext* aContext, nscoord* aX,
6776 nscoord* aXMost) {
6777 return NS_ERROR_NOT_IMPLEMENTED;
6778 }
6779
6780 /* virtual */
ComputeAutoSize(gfxContext * aRenderingContext,WritingMode aWM,const mozilla::LogicalSize & aCBSize,nscoord aAvailableISize,const mozilla::LogicalSize & aMargin,const mozilla::LogicalSize & aBorder,const mozilla::LogicalSize & aPadding,ComputeSizeFlags aFlags)6781 LogicalSize nsIFrame::ComputeAutoSize(
6782 gfxContext* aRenderingContext, WritingMode aWM,
6783 const mozilla::LogicalSize& aCBSize, nscoord aAvailableISize,
6784 const mozilla::LogicalSize& aMargin, const mozilla::LogicalSize& aBorder,
6785 const mozilla::LogicalSize& aPadding, ComputeSizeFlags aFlags) {
6786 // Use basic shrink-wrapping as a default implementation.
6787 LogicalSize result(aWM, 0xdeadbeef, NS_UNCONSTRAINEDSIZE);
6788
6789 // don't bother setting it if the result won't be used
6790 if (StylePosition()->ISize(aWM).IsAuto()) {
6791 nscoord availBased = aAvailableISize - aMargin.ISize(aWM) -
6792 aBorder.ISize(aWM) - aPadding.ISize(aWM);
6793 result.ISize(aWM) = ShrinkWidthToFit(aRenderingContext, availBased, aFlags);
6794 }
6795 return result;
6796 }
6797
ShrinkWidthToFit(gfxContext * aRenderingContext,nscoord aISizeInCB,ComputeSizeFlags aFlags)6798 nscoord nsIFrame::ShrinkWidthToFit(gfxContext* aRenderingContext,
6799 nscoord aISizeInCB,
6800 ComputeSizeFlags aFlags) {
6801 // If we're a container for font size inflation, then shrink
6802 // wrapping inside of us should not apply font size inflation.
6803 AutoMaybeDisableFontInflation an(this);
6804
6805 nscoord result;
6806 nscoord minISize = GetMinISize(aRenderingContext);
6807 if (minISize > aISizeInCB) {
6808 const bool clamp = aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize;
6809 result = MOZ_UNLIKELY(clamp) ? aISizeInCB : minISize;
6810 } else {
6811 nscoord prefISize = GetPrefISize(aRenderingContext);
6812 if (prefISize > aISizeInCB) {
6813 result = aISizeInCB;
6814 } else {
6815 result = prefISize;
6816 }
6817 }
6818 return result;
6819 }
6820
ComputeISizeValue(gfxContext * aRenderingContext,nscoord aContainingBlockISize,nscoord aContentEdgeToBoxSizing,nscoord aBoxSizingToMarginEdge,StyleExtremumLength aSize,ComputeSizeFlags aFlags)6821 nscoord nsIFrame::ComputeISizeValue(gfxContext* aRenderingContext,
6822 nscoord aContainingBlockISize,
6823 nscoord aContentEdgeToBoxSizing,
6824 nscoord aBoxSizingToMarginEdge,
6825 StyleExtremumLength aSize,
6826 ComputeSizeFlags aFlags) {
6827 // If 'this' is a container for font size inflation, then shrink
6828 // wrapping inside of it should not apply font size inflation.
6829 AutoMaybeDisableFontInflation an(this);
6830 nscoord result;
6831 switch (aSize) {
6832 case StyleExtremumLength::MaxContent:
6833 result = GetPrefISize(aRenderingContext);
6834 NS_ASSERTION(result >= 0, "inline-size less than zero");
6835 return result;
6836 case StyleExtremumLength::MinContent:
6837 result = GetMinISize(aRenderingContext);
6838 NS_ASSERTION(result >= 0, "inline-size less than zero");
6839 if (MOZ_UNLIKELY(aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize)) {
6840 auto available = aContainingBlockISize -
6841 (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing);
6842 result = std::min(available, result);
6843 }
6844 return result;
6845 case StyleExtremumLength::MozFitContent: {
6846 nscoord pref = GetPrefISize(aRenderingContext),
6847 min = GetMinISize(aRenderingContext),
6848 fill = aContainingBlockISize -
6849 (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing);
6850 if (MOZ_UNLIKELY(aFlags & ComputeSizeFlags::eIClampMarginBoxMinSize)) {
6851 min = std::min(min, fill);
6852 }
6853 result = std::max(min, std::min(pref, fill));
6854 NS_ASSERTION(result >= 0, "inline-size less than zero");
6855 return result;
6856 }
6857 case StyleExtremumLength::MozAvailable:
6858 return aContainingBlockISize -
6859 (aBoxSizingToMarginEdge + aContentEdgeToBoxSizing);
6860 }
6861 MOZ_ASSERT_UNREACHABLE("Unknown extremum length?");
6862 return 0;
6863 }
6864
ComputeISizeValue(gfxContext * aRenderingContext,nscoord aContainingBlockISize,nscoord aContentEdgeToBoxSizing,nscoord aBoxSizingToMarginEdge,const LengthPercentage & aCoord,ComputeSizeFlags aFlags)6865 nscoord nsIFrame::ComputeISizeValue(gfxContext* aRenderingContext,
6866 nscoord aContainingBlockISize,
6867 nscoord aContentEdgeToBoxSizing,
6868 nscoord aBoxSizingToMarginEdge,
6869 const LengthPercentage& aCoord,
6870 ComputeSizeFlags aFlags) {
6871 MOZ_ASSERT(aRenderingContext, "non-null rendering context expected");
6872 LAYOUT_WARN_IF_FALSE(
6873 aContainingBlockISize != NS_UNCONSTRAINEDSIZE,
6874 "have unconstrained inline-size; this should only result from "
6875 "very large sizes, not attempts at intrinsic inline-size "
6876 "calculation");
6877 MOZ_ASSERT(aContainingBlockISize >= 0, "inline-size less than zero");
6878
6879 nscoord result = aCoord.Resolve(aContainingBlockISize);
6880 // The result of a calc() expression might be less than 0; we
6881 // should clamp at runtime (below). (Percentages and coords that
6882 // are less than 0 have already been dropped by the parser.)
6883 result -= aContentEdgeToBoxSizing;
6884 return std::max(0, result);
6885 }
6886
DidReflow(nsPresContext * aPresContext,const ReflowInput * aReflowInput)6887 void nsFrame::DidReflow(nsPresContext* aPresContext,
6888 const ReflowInput* aReflowInput) {
6889 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, ("nsFrame::DidReflow"));
6890
6891 SVGObserverUtils::InvalidateDirectRenderingObservers(
6892 this, SVGObserverUtils::INVALIDATE_REFLOW);
6893
6894 RemoveStateBits(NS_FRAME_IN_REFLOW | NS_FRAME_FIRST_REFLOW |
6895 NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
6896
6897 // Clear state that was used in ReflowInput::InitResizeFlags (see
6898 // comment there for why we can't clear it there).
6899 SetHasBSizeChange(false);
6900
6901 // Notify the percent bsize observer if there is a percent bsize.
6902 // The observer may be able to initiate another reflow with a computed
6903 // bsize. This happens in the case where a table cell has no computed
6904 // bsize but can fabricate one when the cell bsize is known.
6905 if (aReflowInput && aReflowInput->mPercentBSizeObserver && !GetPrevInFlow()) {
6906 const auto& bsize =
6907 aReflowInput->mStylePosition->BSize(aReflowInput->GetWritingMode());
6908 if (bsize.HasPercent()) {
6909 aReflowInput->mPercentBSizeObserver->NotifyPercentBSize(*aReflowInput);
6910 }
6911 }
6912
6913 aPresContext->ReflowedFrame();
6914 }
6915
FinishReflowWithAbsoluteFrames(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus,bool aConstrainBSize)6916 void nsFrame::FinishReflowWithAbsoluteFrames(nsPresContext* aPresContext,
6917 ReflowOutput& aDesiredSize,
6918 const ReflowInput& aReflowInput,
6919 nsReflowStatus& aStatus,
6920 bool aConstrainBSize) {
6921 ReflowAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput, aStatus,
6922 aConstrainBSize);
6923
6924 FinishAndStoreOverflow(&aDesiredSize, aReflowInput.mStyleDisplay);
6925 }
6926
ReflowAbsoluteFrames(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus,bool aConstrainBSize)6927 void nsIFrame::ReflowAbsoluteFrames(nsPresContext* aPresContext,
6928 ReflowOutput& aDesiredSize,
6929 const ReflowInput& aReflowInput,
6930 nsReflowStatus& aStatus,
6931 bool aConstrainBSize) {
6932 if (HasAbsolutelyPositionedChildren()) {
6933 nsAbsoluteContainingBlock* absoluteContainer = GetAbsoluteContainingBlock();
6934
6935 // Let the absolutely positioned container reflow any absolutely positioned
6936 // child frames that need to be reflowed
6937
6938 // The containing block for the abs pos kids is formed by our padding edge.
6939 nsMargin usedBorder = GetUsedBorder();
6940 nscoord containingBlockWidth =
6941 std::max(0, aDesiredSize.Width() - usedBorder.LeftRight());
6942 nscoord containingBlockHeight =
6943 std::max(0, aDesiredSize.Height() - usedBorder.TopBottom());
6944 nsContainerFrame* container = do_QueryFrame(this);
6945 NS_ASSERTION(container,
6946 "Abs-pos children only supported on container frames for now");
6947
6948 nsRect containingBlock(0, 0, containingBlockWidth, containingBlockHeight);
6949 AbsPosReflowFlags flags =
6950 AbsPosReflowFlags::CBWidthAndHeightChanged; // XXX could be optimized
6951 if (aConstrainBSize) {
6952 flags |= AbsPosReflowFlags::ConstrainHeight;
6953 }
6954 absoluteContainer->Reflow(container, aPresContext, aReflowInput, aStatus,
6955 containingBlock, flags,
6956 &aDesiredSize.mOverflowAreas);
6957 }
6958 }
6959
PushDirtyBitToAbsoluteFrames()6960 void nsContainerFrame::PushDirtyBitToAbsoluteFrames() {
6961 if (!(GetStateBits() & NS_FRAME_IS_DIRTY)) {
6962 return; // No dirty bit to push.
6963 }
6964 if (!HasAbsolutelyPositionedChildren()) {
6965 return; // No absolute children to push to.
6966 }
6967 GetAbsoluteContainingBlock()->MarkAllFramesDirty();
6968 }
6969
6970 /* virtual */
CanContinueTextRun() const6971 bool nsIFrame::CanContinueTextRun() const {
6972 // By default, a frame will *not* allow a text run to be continued
6973 // through it.
6974 return false;
6975 }
6976
Reflow(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)6977 void nsIFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
6978 const ReflowInput& aReflowInput,
6979 nsReflowStatus& aStatus) {
6980 MarkInReflow();
6981 DO_GLOBAL_REFLOW_COUNT("nsFrame");
6982 MOZ_ASSERT(aStatus.IsEmpty(), "Caller should pass a fresh reflow status!");
6983 aDesiredSize.ClearSize();
6984 NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
6985 }
6986
IsContentDisabled() const6987 bool nsIFrame::IsContentDisabled() const {
6988 // FIXME(emilio): Doing this via CSS means callers must ensure the style is up
6989 // to date, and they don't!
6990 if (StyleUI()->mUserInput == StyleUserInput::None) {
6991 return true;
6992 }
6993
6994 auto* element = nsGenericHTMLElement::FromNodeOrNull(GetContent());
6995 return element && element->IsDisabled();
6996 }
6997
CharacterDataChanged(const CharacterDataChangeInfo &)6998 nsresult nsIFrame::CharacterDataChanged(const CharacterDataChangeInfo&) {
6999 MOZ_ASSERT_UNREACHABLE("should only be called for text frames");
7000 return NS_OK;
7001 }
7002
AttributeChanged(int32_t aNameSpaceID,nsAtom * aAttribute,int32_t aModType)7003 nsresult nsIFrame::AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute,
7004 int32_t aModType) {
7005 return NS_OK;
7006 }
7007
7008 // Flow member functions
7009
GetPrevContinuation() const7010 nsIFrame* nsIFrame::GetPrevContinuation() const { return nullptr; }
7011
SetPrevContinuation(nsIFrame * aPrevContinuation)7012 void nsIFrame::SetPrevContinuation(nsIFrame* aPrevContinuation) {
7013 MOZ_ASSERT(false, "not splittable");
7014 }
7015
GetNextContinuation() const7016 nsIFrame* nsIFrame::GetNextContinuation() const { return nullptr; }
7017
SetNextContinuation(nsIFrame *)7018 void nsIFrame::SetNextContinuation(nsIFrame*) {
7019 MOZ_ASSERT(false, "not splittable");
7020 }
7021
GetPrevInFlow() const7022 nsIFrame* nsIFrame::GetPrevInFlow() const { return nullptr; }
7023
SetPrevInFlow(nsIFrame * aPrevInFlow)7024 void nsIFrame::SetPrevInFlow(nsIFrame* aPrevInFlow) {
7025 MOZ_ASSERT(false, "not splittable");
7026 }
7027
GetNextInFlow() const7028 nsIFrame* nsIFrame::GetNextInFlow() const { return nullptr; }
7029
SetNextInFlow(nsIFrame *)7030 void nsIFrame::SetNextInFlow(nsIFrame*) { MOZ_ASSERT(false, "not splittable"); }
7031
GetTailContinuation()7032 nsIFrame* nsIFrame::GetTailContinuation() {
7033 nsIFrame* frame = this;
7034 while (frame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
7035 frame = frame->GetPrevContinuation();
7036 NS_ASSERTION(frame, "first continuation can't be overflow container");
7037 }
7038 for (nsIFrame* next = frame->GetNextContinuation();
7039 next && !(next->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER);
7040 next = frame->GetNextContinuation()) {
7041 frame = next;
7042 }
7043
7044 MOZ_ASSERT(frame, "illegal state in continuation chain.");
7045 return frame;
7046 }
7047
7048 // Associated view object
SetView(nsView * aView)7049 void nsIFrame::SetView(nsView* aView) {
7050 if (aView) {
7051 aView->SetFrame(this);
7052
7053 #ifdef DEBUG
7054 LayoutFrameType frameType = Type();
7055 NS_ASSERTION(frameType == LayoutFrameType::SubDocument ||
7056 frameType == LayoutFrameType::ListControl ||
7057 frameType == LayoutFrameType::Object ||
7058 frameType == LayoutFrameType::Viewport ||
7059 frameType == LayoutFrameType::MenuPopup,
7060 "Only specific frame types can have an nsView");
7061 #endif
7062
7063 // Store the view on the frame.
7064 SetViewInternal(aView);
7065
7066 // Set the frame state bit that says the frame has a view
7067 AddStateBits(NS_FRAME_HAS_VIEW);
7068
7069 // Let all of the ancestors know they have a descendant with a view.
7070 for (nsIFrame* f = GetParent();
7071 f && !(f->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW);
7072 f = f->GetParent())
7073 f->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
7074 } else {
7075 MOZ_ASSERT_UNREACHABLE("Destroying a view while the frame is alive?");
7076 RemoveStateBits(NS_FRAME_HAS_VIEW);
7077 SetViewInternal(nullptr);
7078 }
7079 }
7080
7081 // Find the first geometric parent that has a view
GetAncestorWithView() const7082 nsIFrame* nsIFrame::GetAncestorWithView() const {
7083 for (nsIFrame* f = GetParent(); nullptr != f; f = f->GetParent()) {
7084 if (f->HasView()) {
7085 return f;
7086 }
7087 }
7088 return nullptr;
7089 }
7090
7091 template <nsPoint (nsIFrame::*PositionGetter)() const>
OffsetCalculator(const nsIFrame * aThis,const nsIFrame * aOther)7092 static nsPoint OffsetCalculator(const nsIFrame* aThis, const nsIFrame* aOther) {
7093 MOZ_ASSERT(aOther, "Must have frame for destination coordinate system!");
7094
7095 NS_ASSERTION(aThis->PresContext() == aOther->PresContext(),
7096 "GetOffsetTo called on frames in different documents");
7097
7098 nsPoint offset(0, 0);
7099 const nsIFrame* f;
7100 for (f = aThis; f != aOther && f; f = f->GetParent()) {
7101 offset += (f->*PositionGetter)();
7102 }
7103
7104 if (f != aOther) {
7105 // Looks like aOther wasn't an ancestor of |this|. So now we have
7106 // the root-frame-relative position of |this| in |offset|. Convert back
7107 // to the coordinates of aOther
7108 while (aOther) {
7109 offset -= (aOther->*PositionGetter)();
7110 aOther = aOther->GetParent();
7111 }
7112 }
7113
7114 return offset;
7115 }
7116
GetOffsetTo(const nsIFrame * aOther) const7117 nsPoint nsIFrame::GetOffsetTo(const nsIFrame* aOther) const {
7118 return OffsetCalculator<&nsIFrame::GetPosition>(this, aOther);
7119 }
7120
GetOffsetToIgnoringScrolling(const nsIFrame * aOther) const7121 nsPoint nsIFrame::GetOffsetToIgnoringScrolling(const nsIFrame* aOther) const {
7122 return OffsetCalculator<&nsIFrame::GetPositionIgnoringScrolling>(this,
7123 aOther);
7124 }
7125
GetOffsetToCrossDoc(const nsIFrame * aOther) const7126 nsPoint nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther) const {
7127 return GetOffsetToCrossDoc(aOther, PresContext()->AppUnitsPerDevPixel());
7128 }
7129
GetOffsetToCrossDoc(const nsIFrame * aOther,const int32_t aAPD) const7130 nsPoint nsIFrame::GetOffsetToCrossDoc(const nsIFrame* aOther,
7131 const int32_t aAPD) const {
7132 MOZ_ASSERT(aOther, "Must have frame for destination coordinate system!");
7133 NS_ASSERTION(PresContext()->GetRootPresContext() ==
7134 aOther->PresContext()->GetRootPresContext(),
7135 "trying to get the offset between frames in different document "
7136 "hierarchies?");
7137 if (PresContext()->GetRootPresContext() !=
7138 aOther->PresContext()->GetRootPresContext()) {
7139 // crash right away, we are almost certainly going to crash anyway.
7140 MOZ_CRASH(
7141 "trying to get the offset between frames in different "
7142 "document hierarchies?");
7143 }
7144
7145 const nsIFrame* root = nullptr;
7146 // offset will hold the final offset
7147 // docOffset holds the currently accumulated offset at the current APD, it
7148 // will be converted and added to offset when the current APD changes.
7149 nsPoint offset(0, 0), docOffset(0, 0);
7150 const nsIFrame* f = this;
7151 int32_t currAPD = PresContext()->AppUnitsPerDevPixel();
7152 while (f && f != aOther) {
7153 docOffset += f->GetPosition();
7154 nsIFrame* parent = f->GetParent();
7155 if (parent) {
7156 f = parent;
7157 } else {
7158 nsPoint newOffset(0, 0);
7159 root = f;
7160 f = nsLayoutUtils::GetCrossDocParentFrame(f, &newOffset);
7161 int32_t newAPD = f ? f->PresContext()->AppUnitsPerDevPixel() : 0;
7162 if (!f || newAPD != currAPD) {
7163 // Convert docOffset to the right APD and add it to offset.
7164 offset += docOffset.ScaleToOtherAppUnits(currAPD, aAPD);
7165 docOffset.x = docOffset.y = 0;
7166 }
7167 currAPD = newAPD;
7168 docOffset += newOffset;
7169 }
7170 }
7171 if (f == aOther) {
7172 offset += docOffset.ScaleToOtherAppUnits(currAPD, aAPD);
7173 } else {
7174 // Looks like aOther wasn't an ancestor of |this|. So now we have
7175 // the root-document-relative position of |this| in |offset|. Subtract the
7176 // root-document-relative position of |aOther| from |offset|.
7177 // This call won't try to recurse again because root is an ancestor of
7178 // aOther.
7179 nsPoint negOffset = aOther->GetOffsetToCrossDoc(root, aAPD);
7180 offset -= negOffset;
7181 }
7182
7183 return offset;
7184 }
7185
GetScreenRect() const7186 CSSIntRect nsIFrame::GetScreenRect() const {
7187 return CSSIntRect::FromAppUnitsToNearest(GetScreenRectInAppUnits());
7188 }
7189
GetScreenRectInAppUnits() const7190 nsRect nsIFrame::GetScreenRectInAppUnits() const {
7191 nsPresContext* presContext = PresContext();
7192 nsIFrame* rootFrame = presContext->PresShell()->GetRootFrame();
7193 nsPoint rootScreenPos(0, 0);
7194 nsPoint rootFrameOffsetInParent(0, 0);
7195 nsIFrame* rootFrameParent = nsLayoutUtils::GetCrossDocParentFrame(
7196 rootFrame, &rootFrameOffsetInParent);
7197 if (rootFrameParent) {
7198 nsRect parentScreenRectAppUnits =
7199 rootFrameParent->GetScreenRectInAppUnits();
7200 nsPresContext* parentPresContext = rootFrameParent->PresContext();
7201 double parentScale = double(presContext->AppUnitsPerDevPixel()) /
7202 parentPresContext->AppUnitsPerDevPixel();
7203 nsPoint rootPt =
7204 parentScreenRectAppUnits.TopLeft() + rootFrameOffsetInParent;
7205 rootScreenPos.x = NS_round(parentScale * rootPt.x);
7206 rootScreenPos.y = NS_round(parentScale * rootPt.y);
7207 } else {
7208 nsCOMPtr<nsIWidget> rootWidget;
7209 presContext->PresShell()->GetViewManager()->GetRootWidget(
7210 getter_AddRefs(rootWidget));
7211 if (rootWidget) {
7212 LayoutDeviceIntPoint rootDevPx = rootWidget->WidgetToScreenOffset();
7213 rootScreenPos.x = presContext->DevPixelsToAppUnits(rootDevPx.x);
7214 rootScreenPos.y = presContext->DevPixelsToAppUnits(rootDevPx.y);
7215 }
7216 }
7217
7218 return nsRect(rootScreenPos + GetOffsetTo(rootFrame), GetSize());
7219 }
7220
7221 // Returns the offset from this frame to the closest geometric parent that
7222 // has a view. Also returns the containing view or null in case of error
GetOffsetFromView(nsPoint & aOffset,nsView ** aView) const7223 void nsIFrame::GetOffsetFromView(nsPoint& aOffset, nsView** aView) const {
7224 MOZ_ASSERT(nullptr != aView, "null OUT parameter pointer");
7225 nsIFrame* frame = const_cast<nsIFrame*>(this);
7226
7227 *aView = nullptr;
7228 aOffset.MoveTo(0, 0);
7229 do {
7230 aOffset += frame->GetPosition();
7231 frame = frame->GetParent();
7232 } while (frame && !frame->HasView());
7233
7234 if (frame) {
7235 *aView = frame->GetView();
7236 }
7237 }
7238
GetNearestWidget() const7239 nsIWidget* nsIFrame::GetNearestWidget() const {
7240 return GetClosestView()->GetNearestWidget(nullptr);
7241 }
7242
GetNearestWidget(nsPoint & aOffset) const7243 nsIWidget* nsIFrame::GetNearestWidget(nsPoint& aOffset) const {
7244 nsPoint offsetToView;
7245 nsPoint offsetToWidget;
7246 nsIWidget* widget =
7247 GetClosestView(&offsetToView)->GetNearestWidget(&offsetToWidget);
7248 aOffset = offsetToView + offsetToWidget;
7249 return widget;
7250 }
7251
GetTransformMatrix(ViewportType aViewportType,RelativeTo aStopAtAncestor,nsIFrame ** aOutAncestor,uint32_t aFlags) const7252 Matrix4x4Flagged nsIFrame::GetTransformMatrix(ViewportType aViewportType,
7253 RelativeTo aStopAtAncestor,
7254 nsIFrame** aOutAncestor,
7255 uint32_t aFlags) const {
7256 MOZ_ASSERT(aOutAncestor, "Need a place to put the ancestor!");
7257
7258 /* If we're transformed, we want to hand back the combination
7259 * transform/translate matrix that will apply our current transform, then
7260 * shift us to our parent.
7261 */
7262 bool isTransformed = IsTransformed();
7263 const nsIFrame* zoomedContentRoot = nullptr;
7264 if (aStopAtAncestor.mViewportType == ViewportType::Visual) {
7265 zoomedContentRoot = ViewportUtils::IsZoomedContentRoot(this);
7266 if (zoomedContentRoot) {
7267 MOZ_ASSERT(aViewportType != ViewportType::Visual);
7268 }
7269 }
7270
7271 if (isTransformed || zoomedContentRoot) {
7272 Matrix4x4 result;
7273 int32_t scaleFactor =
7274 ((aFlags & IN_CSS_UNITS) ? AppUnitsPerCSSPixel()
7275 : PresContext()->AppUnitsPerDevPixel());
7276
7277 /* Compute the delta to the parent, which we need because we are converting
7278 * coordinates to our parent.
7279 */
7280 if (isTransformed) {
7281 NS_ASSERTION(nsLayoutUtils::GetCrossDocParentFrame(this),
7282 "Cannot transform the viewport frame!");
7283
7284 result = result * nsDisplayTransform::GetResultingTransformMatrix(
7285 this, nsPoint(0, 0), scaleFactor,
7286 nsDisplayTransform::INCLUDE_PERSPECTIVE |
7287 nsDisplayTransform::OFFSET_BY_ORIGIN);
7288 }
7289
7290 // The offset from a zoomed content root to its parent (e.g. from
7291 // a canvas frame to a scroll frame) is in layout coordinates, so
7292 // apply it before applying any layout-to-visual transform.
7293 *aOutAncestor = nsLayoutUtils::GetCrossDocParentFrame(this);
7294 nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
7295 /* Combine the raw transform with a translation to our parent. */
7296 result.PostTranslate(NSAppUnitsToFloatPixels(delta.x, scaleFactor),
7297 NSAppUnitsToFloatPixels(delta.y, scaleFactor), 0.0f);
7298
7299 if (zoomedContentRoot) {
7300 Matrix4x4 layoutToVisual;
7301 ScrollableLayerGuid::ViewID targetScrollId =
7302 nsLayoutUtils::FindOrCreateIDFor(zoomedContentRoot->GetContent());
7303 if (aFlags & nsIFrame::IN_CSS_UNITS) {
7304 layoutToVisual =
7305 ViewportUtils::GetVisualToLayoutTransform(targetScrollId)
7306 .Inverse()
7307 .ToUnknownMatrix();
7308 } else {
7309 layoutToVisual =
7310 ViewportUtils::GetVisualToLayoutTransform<LayoutDevicePixel>(
7311 targetScrollId)
7312 .Inverse()
7313 .ToUnknownMatrix();
7314 }
7315 result = result * layoutToVisual;
7316 }
7317
7318 return result;
7319 }
7320
7321 if (nsLayoutUtils::IsPopup(this) && IsListControlFrame()) {
7322 nsPresContext* presContext = PresContext();
7323 nsIFrame* docRootFrame = presContext->PresShell()->GetRootFrame();
7324
7325 // Compute a matrix that transforms from the popup widget to the toplevel
7326 // widget. We use the widgets because they're the simplest and most
7327 // accurate approach --- this should work no matter how the widget position
7328 // was chosen.
7329 nsIWidget* widget = GetView()->GetWidget();
7330 nsPresContext* rootPresContext = PresContext()->GetRootPresContext();
7331 // Maybe the widget hasn't been created yet? Popups without widgets are
7332 // treated as regular frames. That should work since they'll be rendered
7333 // as part of the page if they're rendered at all.
7334 if (widget && rootPresContext) {
7335 nsIWidget* toplevel = rootPresContext->GetNearestWidget();
7336 if (toplevel) {
7337 LayoutDeviceIntRect screenBounds = widget->GetClientBounds();
7338 LayoutDeviceIntRect toplevelScreenBounds = toplevel->GetClientBounds();
7339 LayoutDeviceIntPoint translation =
7340 screenBounds.TopLeft() - toplevelScreenBounds.TopLeft();
7341
7342 Matrix4x4 transformToTop;
7343 transformToTop._41 = translation.x;
7344 transformToTop._42 = translation.y;
7345
7346 *aOutAncestor = docRootFrame;
7347 Matrix4x4 docRootTransformToTop =
7348 nsLayoutUtils::GetTransformToAncestor(RelativeTo{docRootFrame},
7349 RelativeTo{nullptr})
7350 .GetMatrix();
7351 if (docRootTransformToTop.IsSingular()) {
7352 NS_WARNING(
7353 "Containing document is invisible, we can't compute a valid "
7354 "transform");
7355 } else {
7356 docRootTransformToTop.Invert();
7357 return transformToTop * docRootTransformToTop;
7358 }
7359 }
7360 }
7361 }
7362
7363 *aOutAncestor = nsLayoutUtils::GetCrossDocParentFrame(this);
7364
7365 /* Otherwise, we're not transformed. In that case, we'll walk up the frame
7366 * tree until we either hit the root frame or something that may be
7367 * transformed. We'll then change coordinates into that frame, since we're
7368 * guaranteed that nothing in-between can be transformed. First, however,
7369 * we have to check to see if we have a parent. If not, we'll set the
7370 * outparam to null (indicating that there's nothing left) and will hand back
7371 * the identity matrix.
7372 */
7373 if (!*aOutAncestor) return Matrix4x4();
7374
7375 /* Keep iterating while the frame can't possibly be transformed. */
7376 const nsIFrame* current = this;
7377 auto shouldStopAt = [](const nsIFrame* aCurrent, nsIFrame* aAncestor,
7378 uint32_t aFlags) {
7379 return aAncestor->IsTransformed() || nsLayoutUtils::IsPopup(aAncestor) ||
7380 ViewportUtils::IsZoomedContentRoot(aAncestor) ||
7381 ((aFlags & STOP_AT_STACKING_CONTEXT_AND_DISPLAY_PORT) &&
7382 (aAncestor->IsStackingContext() ||
7383 nsLayoutUtils::FrameHasDisplayPort(aAncestor, aCurrent)));
7384 };
7385 while (*aOutAncestor != aStopAtAncestor.mFrame &&
7386 !shouldStopAt(current, *aOutAncestor, aFlags)) {
7387 /* If no parent, stop iterating. Otherwise, update the ancestor. */
7388 nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(*aOutAncestor);
7389 if (!parent) break;
7390
7391 current = *aOutAncestor;
7392 *aOutAncestor = parent;
7393 }
7394
7395 NS_ASSERTION(*aOutAncestor, "Somehow ended up with a null ancestor...?");
7396
7397 /* Translate from this frame to our ancestor, if it exists. That's the
7398 * entire transform, so we're done.
7399 */
7400 nsPoint delta = GetOffsetToCrossDoc(*aOutAncestor);
7401 int32_t scaleFactor =
7402 ((aFlags & IN_CSS_UNITS) ? AppUnitsPerCSSPixel()
7403 : PresContext()->AppUnitsPerDevPixel());
7404 return Matrix4x4::Translation(NSAppUnitsToFloatPixels(delta.x, scaleFactor),
7405 NSAppUnitsToFloatPixels(delta.y, scaleFactor),
7406 0.0f);
7407 }
7408
InvalidateRenderingObservers(nsIFrame * aDisplayRoot,nsIFrame * aFrame,bool aFrameChanged=true)7409 static void InvalidateRenderingObservers(nsIFrame* aDisplayRoot,
7410 nsIFrame* aFrame,
7411 bool aFrameChanged = true) {
7412 MOZ_ASSERT(aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame));
7413 SVGObserverUtils::InvalidateDirectRenderingObservers(aFrame);
7414 nsIFrame* parent = aFrame;
7415 while (parent != aDisplayRoot &&
7416 (parent = nsLayoutUtils::GetCrossDocParentFrame(parent)) &&
7417 !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
7418 SVGObserverUtils::InvalidateDirectRenderingObservers(parent);
7419 }
7420
7421 if (!aFrameChanged) {
7422 return;
7423 }
7424
7425 aFrame->MarkNeedsDisplayItemRebuild();
7426 }
7427
SchedulePaintInternal(nsIFrame * aDisplayRoot,nsIFrame * aFrame,nsIFrame::PaintType aType=nsIFrame::PAINT_DEFAULT)7428 static void SchedulePaintInternal(
7429 nsIFrame* aDisplayRoot, nsIFrame* aFrame,
7430 nsIFrame::PaintType aType = nsIFrame::PAINT_DEFAULT) {
7431 MOZ_ASSERT(aDisplayRoot == nsLayoutUtils::GetDisplayRootFrame(aFrame));
7432 nsPresContext* pres = aDisplayRoot->PresContext()->GetRootPresContext();
7433
7434 // No need to schedule a paint for an external document since they aren't
7435 // painted directly.
7436 if (!pres || (pres->Document() && pres->Document()->IsResourceDoc())) {
7437 return;
7438 }
7439 if (!pres->GetContainerWeak()) {
7440 NS_WARNING("Shouldn't call SchedulePaint in a detached pres context");
7441 return;
7442 }
7443
7444 pres->PresShell()->ScheduleViewManagerFlush(
7445 aType == nsIFrame::PAINT_DELAYED_COMPRESS ? PaintType::DelayedCompress
7446 : PaintType::Default);
7447
7448 if (aType == nsIFrame::PAINT_DELAYED_COMPRESS) {
7449 return;
7450 }
7451
7452 if (aType == nsIFrame::PAINT_DEFAULT) {
7453 aDisplayRoot->AddStateBits(NS_FRAME_UPDATE_LAYER_TREE);
7454 }
7455 }
7456
InvalidateFrameInternal(nsIFrame * aFrame,bool aHasDisplayItem,bool aRebuildDisplayItems)7457 static void InvalidateFrameInternal(nsIFrame* aFrame, bool aHasDisplayItem,
7458 bool aRebuildDisplayItems) {
7459 if (aHasDisplayItem) {
7460 aFrame->AddStateBits(NS_FRAME_NEEDS_PAINT);
7461 }
7462
7463 if (aRebuildDisplayItems) {
7464 aFrame->MarkNeedsDisplayItemRebuild();
7465 }
7466 SVGObserverUtils::InvalidateDirectRenderingObservers(aFrame);
7467 bool needsSchedulePaint = false;
7468 if (nsLayoutUtils::IsPopup(aFrame)) {
7469 needsSchedulePaint = true;
7470 } else {
7471 nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(aFrame);
7472 while (parent &&
7473 !parent->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
7474 if (aHasDisplayItem && !parent->HasAnyStateBits(NS_FRAME_IS_NONDISPLAY)) {
7475 parent->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
7476 }
7477 SVGObserverUtils::InvalidateDirectRenderingObservers(parent);
7478
7479 // If we're inside a popup, then we need to make sure that we
7480 // call schedule paint so that the NS_FRAME_UPDATE_LAYER_TREE
7481 // flag gets added to the popup display root frame.
7482 if (nsLayoutUtils::IsPopup(parent)) {
7483 needsSchedulePaint = true;
7484 break;
7485 }
7486 parent = nsLayoutUtils::GetCrossDocParentFrame(parent);
7487 }
7488 if (!parent) {
7489 needsSchedulePaint = true;
7490 }
7491 }
7492 if (!aHasDisplayItem) {
7493 return;
7494 }
7495 if (needsSchedulePaint) {
7496 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(aFrame);
7497 SchedulePaintInternal(displayRoot, aFrame);
7498 }
7499 if (aFrame->HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
7500 aFrame->RemoveProperty(nsIFrame::InvalidationRect());
7501 aFrame->RemoveStateBits(NS_FRAME_HAS_INVALID_RECT);
7502 }
7503 }
7504
InvalidateFrameSubtree(bool aRebuildDisplayItems)7505 void nsIFrame::InvalidateFrameSubtree(bool aRebuildDisplayItems /* = true */) {
7506 InvalidateFrame(0, aRebuildDisplayItems);
7507
7508 if (HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT)) {
7509 return;
7510 }
7511
7512 AddStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT);
7513
7514 for (const auto& childList : CrossDocChildLists()) {
7515 for (nsIFrame* child : childList.mList) {
7516 // Don't explicitly rebuild display items for our descendants,
7517 // since we should be marked and it implicitly includes all
7518 // descendants.
7519 child->InvalidateFrameSubtree(false);
7520 }
7521 }
7522 }
7523
ClearInvalidationStateBits()7524 void nsIFrame::ClearInvalidationStateBits() {
7525 if (HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT)) {
7526 for (const auto& childList : CrossDocChildLists()) {
7527 for (nsIFrame* child : childList.mList) {
7528 child->ClearInvalidationStateBits();
7529 }
7530 }
7531 }
7532
7533 RemoveStateBits(NS_FRAME_NEEDS_PAINT | NS_FRAME_DESCENDANT_NEEDS_PAINT |
7534 NS_FRAME_ALL_DESCENDANTS_NEED_PAINT);
7535 }
7536
InvalidateFrame(uint32_t aDisplayItemKey,bool aRebuildDisplayItems)7537 void nsIFrame::InvalidateFrame(uint32_t aDisplayItemKey,
7538 bool aRebuildDisplayItems /* = true */) {
7539 bool hasDisplayItem =
7540 !aDisplayItemKey ||
7541 FrameLayerBuilder::HasRetainedDataFor(this, aDisplayItemKey);
7542 InvalidateFrameInternal(this, hasDisplayItem, aRebuildDisplayItems);
7543 }
7544
InvalidateFrameWithRect(const nsRect & aRect,uint32_t aDisplayItemKey,bool aRebuildDisplayItems)7545 void nsIFrame::InvalidateFrameWithRect(const nsRect& aRect,
7546 uint32_t aDisplayItemKey,
7547 bool aRebuildDisplayItems /* = true */) {
7548 if (aRect.IsEmpty()) {
7549 return;
7550 }
7551 bool hasDisplayItem =
7552 !aDisplayItemKey ||
7553 FrameLayerBuilder::HasRetainedDataFor(this, aDisplayItemKey);
7554 bool alreadyInvalid = false;
7555 if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) {
7556 InvalidateFrameInternal(this, hasDisplayItem, aRebuildDisplayItems);
7557 } else {
7558 alreadyInvalid = true;
7559 }
7560
7561 if (!hasDisplayItem) {
7562 return;
7563 }
7564
7565 nsRect* rect;
7566 if (HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
7567 rect = GetProperty(InvalidationRect());
7568 MOZ_ASSERT(rect);
7569 } else {
7570 if (alreadyInvalid) {
7571 return;
7572 }
7573 rect = new nsRect();
7574 AddProperty(InvalidationRect(), rect);
7575 AddStateBits(NS_FRAME_HAS_INVALID_RECT);
7576 }
7577
7578 *rect = rect->Union(aRect);
7579 }
7580
7581 /*static*/
7582 uint8_t nsIFrame::sLayerIsPrerenderedDataKey;
7583
DoesLayerHaveOutOfDateFrameMetrics(Layer * aLayer)7584 static bool DoesLayerHaveOutOfDateFrameMetrics(Layer* aLayer) {
7585 for (uint32_t i = 0; i < aLayer->GetScrollMetadataCount(); i++) {
7586 const FrameMetrics& metrics = aLayer->GetFrameMetrics(i);
7587 if (!metrics.IsScrollable()) {
7588 continue;
7589 }
7590 nsIScrollableFrame* scrollableFrame =
7591 nsLayoutUtils::FindScrollableFrameFor(metrics.GetScrollId());
7592 if (!scrollableFrame) {
7593 // This shouldn't happen, so let's do the safe thing and trigger a full
7594 // paint if it does.
7595 return true;
7596 }
7597 nsPoint scrollPosition = scrollableFrame->GetScrollPosition();
7598 if (metrics.GetScrollOffset() != CSSPoint::FromAppUnits(scrollPosition)) {
7599 return true;
7600 }
7601 }
7602 return false;
7603 }
7604
DoesLayerOrAncestorsHaveOutOfDateFrameMetrics(Layer * aLayer)7605 static bool DoesLayerOrAncestorsHaveOutOfDateFrameMetrics(Layer* aLayer) {
7606 for (Layer* layer = aLayer; layer; layer = layer->GetParent()) {
7607 if (DoesLayerHaveOutOfDateFrameMetrics(layer)) {
7608 return true;
7609 }
7610 }
7611 return false;
7612 }
7613
TryUpdateTransformOnly(Layer ** aLayerResult)7614 bool nsIFrame::TryUpdateTransformOnly(Layer** aLayerResult) {
7615 // If we move a transformed layer when we have a merged display
7616 // list, then it can end up intersecting other items for which
7617 // we don't have a defined ordering.
7618 // We could allow this if the display list is in the canonical
7619 // ordering (correctly sorted for all intersections), but we
7620 // don't have a way to check that yet.
7621 if (nsLayoutUtils::AreRetainedDisplayListsEnabled()) {
7622 return false;
7623 }
7624
7625 Layer* layer = FrameLayerBuilder::GetDedicatedLayer(
7626 this, DisplayItemType::TYPE_TRANSFORM);
7627 if (!layer || !layer->HasUserData(LayerIsPrerenderedDataKey())) {
7628 // If this layer isn't prerendered or we clip composites to our OS
7629 // window, then we can't correctly optimize to an empty
7630 // transaction in general.
7631 return false;
7632 }
7633
7634 if (DoesLayerOrAncestorsHaveOutOfDateFrameMetrics(layer)) {
7635 // At least one scroll frame that can affect the position of this layer
7636 // has changed its scroll offset since the last paint. Schedule a full
7637 // paint to make sure that this layer's transform and all the frame
7638 // metrics that affect it are in sync.
7639 return false;
7640 }
7641
7642 gfx::Matrix4x4Flagged transform3d;
7643 if (!nsLayoutUtils::GetLayerTransformForFrame(this, &transform3d)) {
7644 // We're not able to compute a layer transform that we know would
7645 // be used at the next layers transaction, so we can't only update
7646 // the transform and will need to schedule an invalidating paint.
7647 return false;
7648 }
7649 gfx::Matrix transform;
7650 gfx::Matrix previousTransform;
7651 // FIXME/bug 796690 and 796705: in general, changes to 3D
7652 // transforms, or transform changes to properties other than
7653 // translation, may lead us to choose a different rendering
7654 // resolution for our layer. So if the transform is 3D or has a
7655 // non-translation change, bail and schedule an invalidating paint.
7656 // (We can often do better than this, for example for scale-down
7657 // changes.)
7658 static const gfx::Float kError = 0.0001f;
7659 if (!transform3d.Is2D(&transform) ||
7660 !layer->GetBaseTransform().Is2D(&previousTransform) ||
7661 !gfx::FuzzyEqual(transform._11, previousTransform._11, kError) ||
7662 !gfx::FuzzyEqual(transform._22, previousTransform._22, kError) ||
7663 !gfx::FuzzyEqual(transform._21, previousTransform._21, kError) ||
7664 !gfx::FuzzyEqual(transform._12, previousTransform._12, kError)) {
7665 return false;
7666 }
7667 layer->SetBaseTransformForNextTransaction(transform3d.GetMatrix());
7668 *aLayerResult = layer;
7669 return true;
7670 }
7671
IsInvalid(nsRect & aRect)7672 bool nsIFrame::IsInvalid(nsRect& aRect) {
7673 if (!HasAnyStateBits(NS_FRAME_NEEDS_PAINT)) {
7674 return false;
7675 }
7676
7677 if (HasAnyStateBits(NS_FRAME_HAS_INVALID_RECT)) {
7678 nsRect* rect = GetProperty(InvalidationRect());
7679 NS_ASSERTION(
7680 rect, "Must have an invalid rect if NS_FRAME_HAS_INVALID_RECT is set!");
7681 aRect = *rect;
7682 } else {
7683 aRect.SetEmpty();
7684 }
7685 return true;
7686 }
7687
SchedulePaint(PaintType aType,bool aFrameChanged)7688 void nsIFrame::SchedulePaint(PaintType aType, bool aFrameChanged) {
7689 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
7690 InvalidateRenderingObservers(displayRoot, this, aFrameChanged);
7691 SchedulePaintInternal(displayRoot, this, aType);
7692 }
7693
SchedulePaintWithoutInvalidatingObservers(PaintType aType)7694 void nsIFrame::SchedulePaintWithoutInvalidatingObservers(PaintType aType) {
7695 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
7696 SchedulePaintInternal(displayRoot, this, aType);
7697 }
7698
InvalidateLayer(DisplayItemType aDisplayItemKey,const nsIntRect * aDamageRect,const nsRect * aFrameDamageRect,uint32_t aFlags)7699 Layer* nsIFrame::InvalidateLayer(DisplayItemType aDisplayItemKey,
7700 const nsIntRect* aDamageRect,
7701 const nsRect* aFrameDamageRect,
7702 uint32_t aFlags /* = 0 */) {
7703 NS_ASSERTION(aDisplayItemKey > DisplayItemType::TYPE_ZERO, "Need a key");
7704
7705 Layer* layer = FrameLayerBuilder::GetDedicatedLayer(this, aDisplayItemKey);
7706
7707 nsIFrame* displayRoot = nsLayoutUtils::GetDisplayRootFrame(this);
7708 InvalidateRenderingObservers(displayRoot, this, false);
7709
7710 // Check if frame supports WebRender's async update
7711 if ((aFlags & UPDATE_IS_ASYNC) &&
7712 WebRenderUserData::SupportsAsyncUpdate(this)) {
7713 // WebRender does not use layer, then return nullptr.
7714 return nullptr;
7715 }
7716
7717 // If the layer is being updated asynchronously, and it's being forwarded
7718 // to a compositor, then we don't need to invalidate.
7719 if ((aFlags & UPDATE_IS_ASYNC) && layer && layer->SupportsAsyncUpdate()) {
7720 return layer;
7721 }
7722
7723 if (!layer) {
7724 if (aFrameDamageRect && aFrameDamageRect->IsEmpty()) {
7725 return nullptr;
7726 }
7727
7728 // Plugins can transition from not rendering anything to rendering,
7729 // and still only call this. So always invalidate, with specifying
7730 // the display item type just in case.
7731 //
7732 // In the bug 930056, dialer app startup but not shown on the
7733 // screen because sometimes we don't have any retainned data
7734 // for remote type displayitem and thus Repaint event is not
7735 // triggered. So, always invalidate here as well.
7736 DisplayItemType displayItemKey = aDisplayItemKey;
7737 if (aDisplayItemKey == DisplayItemType::TYPE_PLUGIN ||
7738 aDisplayItemKey == DisplayItemType::TYPE_REMOTE) {
7739 displayItemKey = DisplayItemType::TYPE_ZERO;
7740 }
7741
7742 if (aFrameDamageRect) {
7743 InvalidateFrameWithRect(*aFrameDamageRect,
7744 static_cast<uint32_t>(displayItemKey));
7745 } else {
7746 InvalidateFrame(static_cast<uint32_t>(displayItemKey));
7747 }
7748
7749 return nullptr;
7750 }
7751
7752 if (aDamageRect && aDamageRect->IsEmpty()) {
7753 return layer;
7754 }
7755
7756 if (aDamageRect) {
7757 layer->AddInvalidRect(*aDamageRect);
7758 } else {
7759 layer->SetInvalidRectToVisibleRegion();
7760 }
7761
7762 SchedulePaintInternal(displayRoot, this, PAINT_COMPOSITE_ONLY);
7763 return layer;
7764 }
7765
ComputeEffectsRect(nsIFrame * aFrame,const nsRect & aOverflowRect,const nsSize & aNewSize)7766 static nsRect ComputeEffectsRect(nsIFrame* aFrame, const nsRect& aOverflowRect,
7767 const nsSize& aNewSize) {
7768 nsRect r = aOverflowRect;
7769
7770 if (aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
7771 // For SVG frames, we only need to account for filters.
7772 // TODO: We could also take account of clipPath and mask to reduce the
7773 // visual overflow, but that's not essential.
7774 if (aFrame->StyleEffects()->HasFilters()) {
7775 SetOrUpdateRectValuedProperty(aFrame, nsIFrame::PreEffectsBBoxProperty(),
7776 r);
7777 r = nsSVGUtils::GetPostFilterVisualOverflowRect(aFrame, aOverflowRect);
7778 }
7779 return r;
7780 }
7781
7782 // box-shadow
7783 r.UnionRect(r, nsLayoutUtils::GetBoxShadowRectForFrame(aFrame, aNewSize));
7784
7785 // border-image-outset.
7786 // We need to include border-image-outset because it can cause the
7787 // border image to be drawn beyond the border box.
7788
7789 // (1) It's important we not check whether there's a border-image
7790 // since the style hint for a change in border image doesn't cause
7791 // reflow, and that's probably more important than optimizing the
7792 // overflow areas for the silly case of border-image-outset without
7793 // border-image
7794 // (2) It's important that we not check whether the border-image
7795 // is actually loaded, since that would require us to reflow when
7796 // the image loads.
7797 const nsStyleBorder* styleBorder = aFrame->StyleBorder();
7798 nsMargin outsetMargin = styleBorder->GetImageOutset();
7799
7800 if (outsetMargin != nsMargin(0, 0, 0, 0)) {
7801 nsRect outsetRect(nsPoint(0, 0), aNewSize);
7802 outsetRect.Inflate(outsetMargin);
7803 r.UnionRect(r, outsetRect);
7804 }
7805
7806 // Note that we don't remove the outlineInnerRect if a frame loses outline
7807 // style. That would require an extra property lookup for every frame,
7808 // or a new frame state bit to track whether a property had been stored,
7809 // or something like that. It's not worth doing that here. At most it's
7810 // only one heap-allocated rect per frame and it will be cleaned up when
7811 // the frame dies.
7812
7813 if (nsSVGIntegrationUtils::UsingOverflowAffectingEffects(aFrame)) {
7814 SetOrUpdateRectValuedProperty(aFrame, nsIFrame::PreEffectsBBoxProperty(),
7815 r);
7816 r = nsSVGIntegrationUtils::ComputePostEffectsVisualOverflowRect(aFrame, r);
7817 }
7818
7819 return r;
7820 }
7821
MovePositionBy(const nsPoint & aTranslation)7822 void nsIFrame::MovePositionBy(const nsPoint& aTranslation) {
7823 nsPoint position = GetNormalPosition() + aTranslation;
7824
7825 const nsMargin* computedOffsets = nullptr;
7826 if (IsRelativelyPositioned()) {
7827 computedOffsets = GetProperty(nsIFrame::ComputedOffsetProperty());
7828 }
7829 ReflowInput::ApplyRelativePositioning(
7830 this, computedOffsets ? *computedOffsets : nsMargin(), &position);
7831 SetPosition(position);
7832 }
7833
GetNormalRect() const7834 nsRect nsIFrame::GetNormalRect() const {
7835 // It might be faster to first check
7836 // StyleDisplay()->IsRelativelyPositionedStyle().
7837 nsPoint* normalPosition = GetProperty(NormalPositionProperty());
7838 if (normalPosition) {
7839 return nsRect(*normalPosition, GetSize());
7840 }
7841 return GetRect();
7842 }
7843
GetPositionIgnoringScrolling() const7844 nsPoint nsIFrame::GetPositionIgnoringScrolling() const {
7845 return GetParent() ? GetParent()->GetPositionOfChildIgnoringScrolling(this)
7846 : GetPosition();
7847 }
7848
GetOverflowRect(nsOverflowType aType) const7849 nsRect nsIFrame::GetOverflowRect(nsOverflowType aType) const {
7850 MOZ_ASSERT(aType == eVisualOverflow || aType == eScrollableOverflow,
7851 "unexpected type");
7852
7853 // Note that in some cases the overflow area might not have been
7854 // updated (yet) to reflect any outline set on the frame or the area
7855 // of child frames. That's OK because any reflow that updates these
7856 // areas will invalidate the appropriate area, so any (mis)uses of
7857 // this method will be fixed up.
7858
7859 if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
7860 // there is an overflow rect, and it's not stored as deltas but as
7861 // a separately-allocated rect
7862 return GetOverflowAreasProperty()->Overflow(aType);
7863 }
7864
7865 if (aType == eVisualOverflow && mOverflow.mType != NS_FRAME_OVERFLOW_NONE) {
7866 return GetVisualOverflowFromDeltas();
7867 }
7868
7869 return nsRect(nsPoint(0, 0), GetSize());
7870 }
7871
GetOverflowAreas() const7872 nsOverflowAreas nsIFrame::GetOverflowAreas() const {
7873 if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
7874 // there is an overflow rect, and it's not stored as deltas but as
7875 // a separately-allocated rect
7876 return *GetOverflowAreasProperty();
7877 }
7878
7879 return nsOverflowAreas(GetVisualOverflowFromDeltas(),
7880 nsRect(nsPoint(0, 0), GetSize()));
7881 }
7882
GetOverflowAreasRelativeToSelf() const7883 nsOverflowAreas nsIFrame::GetOverflowAreasRelativeToSelf() const {
7884 if (IsTransformed()) {
7885 nsOverflowAreas* preTransformOverflows =
7886 GetProperty(PreTransformOverflowAreasProperty());
7887 if (preTransformOverflows) {
7888 return nsOverflowAreas(preTransformOverflows->VisualOverflow(),
7889 preTransformOverflows->ScrollableOverflow());
7890 }
7891 }
7892 return nsOverflowAreas(GetVisualOverflowRect(), GetScrollableOverflowRect());
7893 }
7894
GetScrollableOverflowRectRelativeToParent() const7895 nsRect nsIFrame::GetScrollableOverflowRectRelativeToParent() const {
7896 return GetScrollableOverflowRect() + mRect.TopLeft();
7897 }
7898
GetVisualOverflowRectRelativeToParent() const7899 nsRect nsIFrame::GetVisualOverflowRectRelativeToParent() const {
7900 return GetVisualOverflowRect() + mRect.TopLeft();
7901 }
7902
GetScrollableOverflowRectRelativeToSelf() const7903 nsRect nsIFrame::GetScrollableOverflowRectRelativeToSelf() const {
7904 if (IsTransformed()) {
7905 nsOverflowAreas* preTransformOverflows =
7906 GetProperty(PreTransformOverflowAreasProperty());
7907 if (preTransformOverflows)
7908 return preTransformOverflows->ScrollableOverflow();
7909 }
7910 return GetScrollableOverflowRect();
7911 }
7912
GetVisualOverflowRectRelativeToSelf() const7913 nsRect nsIFrame::GetVisualOverflowRectRelativeToSelf() const {
7914 if (IsTransformed()) {
7915 nsOverflowAreas* preTransformOverflows =
7916 GetProperty(PreTransformOverflowAreasProperty());
7917 if (preTransformOverflows) return preTransformOverflows->VisualOverflow();
7918 }
7919 return GetVisualOverflowRect();
7920 }
7921
GetPreEffectsVisualOverflowRect() const7922 nsRect nsIFrame::GetPreEffectsVisualOverflowRect() const {
7923 nsRect* r = GetProperty(nsIFrame::PreEffectsBBoxProperty());
7924 return r ? *r : GetVisualOverflowRectRelativeToSelf();
7925 }
7926
UpdateOverflow()7927 bool nsIFrame::UpdateOverflow() {
7928 MOZ_ASSERT(FrameMaintainsOverflow(),
7929 "Non-display SVG do not maintain visual overflow rects");
7930
7931 nsRect rect(nsPoint(0, 0), GetSize());
7932 nsOverflowAreas overflowAreas(rect, rect);
7933
7934 if (!ComputeCustomOverflow(overflowAreas)) {
7935 // If updating overflow wasn't supported by this frame, then it should
7936 // have scheduled any necessary reflows. We can return false to say nothing
7937 // changed, and wait for reflow to correct it.
7938 return false;
7939 }
7940
7941 UnionChildOverflow(overflowAreas);
7942
7943 if (FinishAndStoreOverflow(overflowAreas, GetSize())) {
7944 nsView* view = GetView();
7945 if (view) {
7946 ReflowChildFlags flags = GetXULLayoutFlags();
7947 if (!(flags & ReflowChildFlags::NoSizeView)) {
7948 // Make sure the frame's view is properly sized.
7949 nsViewManager* vm = view->GetViewManager();
7950 vm->ResizeView(view, overflowAreas.VisualOverflow(), true);
7951 }
7952 }
7953
7954 return true;
7955 }
7956
7957 // Frames that combine their 3d transform with their ancestors
7958 // only compute a pre-transform overflow rect, and then contribute
7959 // to the normal overflow rect of the preserve-3d root. Always return
7960 // true here so that we propagate changes up to the root for final
7961 // calculation.
7962 return Combines3DTransformWithAncestors();
7963 }
7964
7965 /* virtual */
ComputeCustomOverflow(nsOverflowAreas & aOverflowAreas)7966 bool nsIFrame::ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) {
7967 return true;
7968 }
7969
7970 /* virtual */
UnionChildOverflow(nsOverflowAreas & aOverflowAreas)7971 void nsIFrame::UnionChildOverflow(nsOverflowAreas& aOverflowAreas) {
7972 if (!DoesClipChildren() &&
7973 !(IsXULCollapsed() && (IsXULBoxFrame() || ::IsXULBoxWrapped(this)))) {
7974 nsLayoutUtils::UnionChildOverflow(this, aOverflowAreas);
7975 }
7976 }
7977
7978 // Define the MAX_FRAME_DEPTH to be the ContentSink's MAX_REFLOW_DEPTH plus
7979 // 4 for the frames above the document's frames:
7980 // the Viewport, GFXScroll, ScrollPort, and Canvas
7981 #define MAX_FRAME_DEPTH (MAX_REFLOW_DEPTH + 4)
7982
IsFrameTreeTooDeep(const ReflowInput & aReflowInput,ReflowOutput & aMetrics,nsReflowStatus & aStatus)7983 bool nsContainerFrame::IsFrameTreeTooDeep(const ReflowInput& aReflowInput,
7984 ReflowOutput& aMetrics,
7985 nsReflowStatus& aStatus) {
7986 if (aReflowInput.mReflowDepth > MAX_FRAME_DEPTH) {
7987 NS_WARNING("frame tree too deep; setting zero size and returning");
7988 AddStateBits(NS_FRAME_TOO_DEEP_IN_FRAME_TREE);
7989 ClearOverflowRects();
7990 aMetrics.ClearSize();
7991 aMetrics.SetBlockStartAscent(0);
7992 aMetrics.mCarriedOutBEndMargin.Zero();
7993 aMetrics.mOverflowAreas.Clear();
7994
7995 aStatus.Reset();
7996 if (GetNextInFlow()) {
7997 // Reflow depth might vary between reflows, so we might have
7998 // successfully reflowed and split this frame before. If so, we
7999 // shouldn't delete its continuations.
8000 aStatus.SetIncomplete();
8001 }
8002
8003 return true;
8004 }
8005 RemoveStateBits(NS_FRAME_TOO_DEEP_IN_FRAME_TREE);
8006 return false;
8007 }
8008
IsBlockWrapper() const8009 bool nsIFrame::IsBlockWrapper() const {
8010 auto pseudoType = Style()->GetPseudoType();
8011 return pseudoType == PseudoStyleType::mozBlockInsideInlineWrapper ||
8012 pseudoType == PseudoStyleType::buttonContent ||
8013 pseudoType == PseudoStyleType::cellContent ||
8014 pseudoType == PseudoStyleType::columnSpanWrapper;
8015 }
8016
IsBlockFrameOrSubclass() const8017 bool nsIFrame::IsBlockFrameOrSubclass() const {
8018 const nsBlockFrame* thisAsBlock = do_QueryFrame(this);
8019 return !!thisAsBlock;
8020 }
8021
GetNearestBlockContainer(nsIFrame * frame)8022 static nsIFrame* GetNearestBlockContainer(nsIFrame* frame) {
8023 // The block wrappers we use to wrap blocks inside inlines aren't
8024 // described in the CSS spec. We need to make them not be containing
8025 // blocks.
8026 // Since the parent of such a block is either a normal block or
8027 // another such pseudo, this shouldn't cause anything bad to happen.
8028 // Also the anonymous blocks inside table cells are not containing blocks.
8029 //
8030 // If we ever start skipping table row groups from being containing blocks,
8031 // you need to remove the StickyScrollContainer hack referencing bug 1421660.
8032 while (frame->IsFrameOfType(nsIFrame::eLineParticipant) ||
8033 frame->IsBlockWrapper() ||
8034 // Table rows are not containing blocks either
8035 frame->IsTableRowFrame()) {
8036 frame = frame->GetParent();
8037 NS_ASSERTION(
8038 frame,
8039 "How come we got to the root frame without seeing a containing block?");
8040 }
8041 return frame;
8042 }
8043
GetContainingBlock(uint32_t aFlags,const nsStyleDisplay * aStyleDisplay) const8044 nsIFrame* nsIFrame::GetContainingBlock(
8045 uint32_t aFlags, const nsStyleDisplay* aStyleDisplay) const {
8046 MOZ_ASSERT(aStyleDisplay == StyleDisplay());
8047 if (!GetParent()) {
8048 return nullptr;
8049 }
8050 // MathML frames might have absolute positioning style, but they would
8051 // still be in-flow. So we have to check to make sure that the frame
8052 // is really out-of-flow too.
8053 nsIFrame* f;
8054 if (IsAbsolutelyPositioned(aStyleDisplay) &&
8055 (GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
8056 f = GetParent(); // the parent is always the containing block
8057 } else {
8058 f = GetNearestBlockContainer(GetParent());
8059 }
8060
8061 if (aFlags & SKIP_SCROLLED_FRAME && f &&
8062 f->Style()->GetPseudoType() == PseudoStyleType::scrolledContent) {
8063 f = f->GetParent();
8064 }
8065 return f;
8066 }
8067
8068 #ifdef DEBUG_FRAME_DUMP
8069
ContentIndexInContainer(const nsIFrame * aFrame)8070 int32_t nsFrame::ContentIndexInContainer(const nsIFrame* aFrame) {
8071 int32_t result = -1;
8072
8073 nsIContent* content = aFrame->GetContent();
8074 if (content) {
8075 nsIContent* parentContent = content->GetParent();
8076 if (parentContent) {
8077 result = parentContent->ComputeIndexOf(content);
8078 }
8079 }
8080
8081 return result;
8082 }
8083
8084 /**
8085 * List a frame tree to stderr. Meant to be called from gdb.
8086 */
DebugListFrameTree(nsIFrame * aFrame)8087 void DebugListFrameTree(nsIFrame* aFrame) { ((nsFrame*)aFrame)->List(stderr); }
8088
ListTag() const8089 nsAutoCString nsIFrame::ListTag() const {
8090 nsAutoString tmp;
8091 GetFrameName(tmp);
8092
8093 nsAutoCString tag;
8094 tag += NS_ConvertUTF16toUTF8(tmp);
8095 tag += nsPrintfCString("@%p", static_cast<const void*>(this));
8096 return tag;
8097 }
8098
ConvertToString(const LogicalRect & aRect,const WritingMode aWM,ListFlags aFlags)8099 std::string nsIFrame::ConvertToString(const LogicalRect& aRect,
8100 const WritingMode aWM, ListFlags aFlags) {
8101 if (aFlags.contains(ListFlag::DisplayInCSSPixels)) {
8102 // Abuse CSSRect to store all LogicalRect's dimensions in CSS pixels.
8103 return ToString(mozilla::CSSRect(CSSPixel::FromAppUnits(aRect.IStart(aWM)),
8104 CSSPixel::FromAppUnits(aRect.BStart(aWM)),
8105 CSSPixel::FromAppUnits(aRect.ISize(aWM)),
8106 CSSPixel::FromAppUnits(aRect.BSize(aWM))));
8107 }
8108 return ToString(aRect);
8109 }
8110
ConvertToString(const LogicalSize & aSize,const WritingMode aWM,ListFlags aFlags)8111 std::string nsIFrame::ConvertToString(const LogicalSize& aSize,
8112 const WritingMode aWM, ListFlags aFlags) {
8113 if (aFlags.contains(ListFlag::DisplayInCSSPixels)) {
8114 // Abuse CSSSize to store all LogicalSize's dimensions in CSS pixels.
8115 return ToString(CSSSize(CSSPixel::FromAppUnits(aSize.ISize(aWM)),
8116 CSSPixel::FromAppUnits(aSize.BSize(aWM))));
8117 }
8118 return ToString(aSize);
8119 }
8120
8121 // Debugging
ListGeneric(nsACString & aTo,const char * aPrefix,ListFlags aFlags) const8122 void nsIFrame::ListGeneric(nsACString& aTo, const char* aPrefix,
8123 ListFlags aFlags) const {
8124 aTo += aPrefix;
8125 aTo += ListTag();
8126 if (HasView()) {
8127 aTo += nsPrintfCString(" [view=%p]", static_cast<void*>(GetView()));
8128 }
8129 if (GetParent()) {
8130 aTo += nsPrintfCString(" parent=%p", static_cast<void*>(GetParent()));
8131 }
8132 if (GetNextSibling()) {
8133 aTo += nsPrintfCString(" next=%p", static_cast<void*>(GetNextSibling()));
8134 }
8135 if (GetPrevContinuation()) {
8136 bool fluid = GetPrevInFlow() == GetPrevContinuation();
8137 aTo += nsPrintfCString(" prev-%s=%p", fluid ? "in-flow" : "continuation",
8138 static_cast<void*>(GetPrevContinuation()));
8139 }
8140 if (GetNextContinuation()) {
8141 bool fluid = GetNextInFlow() == GetNextContinuation();
8142 aTo += nsPrintfCString(" next-%s=%p", fluid ? "in-flow" : "continuation",
8143 static_cast<void*>(GetNextContinuation()));
8144 }
8145 void* IBsibling = GetProperty(IBSplitSibling());
8146 if (IBsibling) {
8147 aTo += nsPrintfCString(" IBSplitSibling=%p", IBsibling);
8148 }
8149 void* IBprevsibling = GetProperty(IBSplitPrevSibling());
8150 if (IBprevsibling) {
8151 aTo += nsPrintfCString(" IBSplitPrevSibling=%p", IBprevsibling);
8152 }
8153 if (nsLayoutUtils::FontSizeInflationEnabled(PresContext())) {
8154 if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT)) {
8155 aTo += nsPrintfCString(" FFR");
8156 if (nsFontInflationData* data =
8157 nsFontInflationData::FindFontInflationDataFor(this)) {
8158 aTo += nsPrintfCString(
8159 ",enabled=%s,UIS=%s", data->InflationEnabled() ? "yes" : "no",
8160 ConvertToString(data->UsableISize(), aFlags).c_str());
8161 }
8162 }
8163 if (HasAnyStateBits(NS_FRAME_FONT_INFLATION_CONTAINER)) {
8164 aTo += nsPrintfCString(" FIC");
8165 }
8166 aTo += nsPrintfCString(" FI=%f", nsLayoutUtils::FontSizeInflationFor(this));
8167 }
8168 aTo += nsPrintfCString(" %s", ConvertToString(mRect, aFlags).c_str());
8169
8170 mozilla::WritingMode wm = GetWritingMode();
8171 if (wm.IsVertical() || wm.IsBidiRTL()) {
8172 aTo +=
8173 nsPrintfCString(" wm=%s logical-size=(%s)", ToString(wm).c_str(),
8174 ConvertToString(GetLogicalSize(), wm, aFlags).c_str());
8175 }
8176
8177 nsIFrame* parent = GetParent();
8178 if (parent) {
8179 WritingMode pWM = parent->GetWritingMode();
8180 if (pWM.IsVertical() || pWM.IsBidiRTL()) {
8181 nsSize containerSize = parent->mRect.Size();
8182 LogicalRect lr(pWM, mRect, containerSize);
8183 aTo += nsPrintfCString(" parent-wm=%s cs=(%s) logical-rect=%s",
8184 ToString(pWM).c_str(),
8185 ConvertToString(containerSize, aFlags).c_str(),
8186 ConvertToString(lr, pWM, aFlags).c_str());
8187 }
8188 }
8189 nsIFrame* f = const_cast<nsIFrame*>(this);
8190 if (f->HasOverflowAreas()) {
8191 nsRect vo = f->GetVisualOverflowRect();
8192 if (!vo.IsEqualEdges(mRect)) {
8193 aTo += nsPrintfCString(" vis-overflow=%s",
8194 ConvertToString(vo, aFlags).c_str());
8195 }
8196 nsRect so = f->GetScrollableOverflowRect();
8197 if (!so.IsEqualEdges(mRect)) {
8198 aTo += nsPrintfCString(" scr-overflow=%s",
8199 ConvertToString(so, aFlags).c_str());
8200 }
8201 }
8202 bool hasNormalPosition;
8203 nsPoint normalPosition = GetNormalPosition(&hasNormalPosition);
8204 if (hasNormalPosition) {
8205 aTo += nsPrintfCString(" normal-position=%s",
8206 ConvertToString(normalPosition, aFlags).c_str());
8207 }
8208 if (0 != mState) {
8209 aTo += nsPrintfCString(" [state=%016llx]", (unsigned long long)mState);
8210 }
8211 if (HasProperty(BidiDataProperty())) {
8212 FrameBidiData bidi = GetBidiData();
8213 aTo += nsPrintfCString(" bidi(%d,%d,%d)", bidi.baseLevel,
8214 bidi.embeddingLevel, bidi.precedingControl);
8215 }
8216 if (IsTransformed()) {
8217 aTo += nsPrintfCString(" transformed");
8218 }
8219 if (ChildrenHavePerspective()) {
8220 aTo += nsPrintfCString(" perspective");
8221 }
8222 if (Extend3DContext()) {
8223 aTo += nsPrintfCString(" extend-3d");
8224 }
8225 if (Combines3DTransformWithAncestors()) {
8226 aTo += nsPrintfCString(" combines-3d-transform-with-ancestors");
8227 }
8228 if (mContent) {
8229 aTo += nsPrintfCString(" [content=%p]", static_cast<void*>(mContent));
8230 }
8231 aTo += nsPrintfCString(" [cs=%p", static_cast<void*>(mComputedStyle));
8232 if (mComputedStyle) {
8233 auto pseudoType = mComputedStyle->GetPseudoType();
8234 aTo += ToString(pseudoType).c_str();
8235 }
8236 aTo += "]";
8237 }
8238
List(FILE * out,const char * aPrefix,ListFlags aFlags) const8239 void nsIFrame::List(FILE* out, const char* aPrefix, ListFlags aFlags) const {
8240 nsCString str;
8241 ListGeneric(str, aPrefix, aFlags);
8242 fprintf_stderr(out, "%s\n", str.get());
8243 }
8244
ListMatchedRules(FILE * out,const char * aPrefix) const8245 void nsIFrame::ListMatchedRules(FILE* out, const char* aPrefix) const {
8246 nsTArray<const RawServoStyleRule*> rawRuleList;
8247 Servo_ComputedValues_GetStyleRuleList(mComputedStyle, &rawRuleList);
8248 for (const RawServoStyleRule* rawRule : rawRuleList) {
8249 nsString ruleText;
8250 Servo_StyleRule_GetCssText(rawRule, &ruleText);
8251 fprintf_stderr(out, "%s%s\n", aPrefix,
8252 NS_ConvertUTF16toUTF8(ruleText).get());
8253 }
8254 }
8255
ListWithMatchedRules(FILE * out,const char * aPrefix) const8256 void nsIFrame::ListWithMatchedRules(FILE* out, const char* aPrefix) const {
8257 fprintf_stderr(out, "%s%s\n", aPrefix, ListTag().get());
8258
8259 nsCString rulePrefix;
8260 rulePrefix += aPrefix;
8261 rulePrefix += " ";
8262 ListMatchedRules(out, rulePrefix.get());
8263 }
8264
GetFrameName(nsAString & aResult) const8265 nsresult nsFrame::GetFrameName(nsAString& aResult) const {
8266 return MakeFrameName(NS_LITERAL_STRING("Frame"), aResult);
8267 }
8268
MakeFrameName(const nsAString & aType,nsAString & aResult) const8269 nsresult nsFrame::MakeFrameName(const nsAString& aType,
8270 nsAString& aResult) const {
8271 aResult = aType;
8272 if (mContent && !mContent->IsText()) {
8273 nsAutoString buf;
8274 mContent->NodeInfo()->NameAtom()->ToString(buf);
8275 if (IsSubDocumentFrame()) {
8276 nsAutoString src;
8277 mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::src, src);
8278 buf.AppendLiteral(" src=");
8279 buf.Append(src);
8280 }
8281 aResult.Append('(');
8282 aResult.Append(buf);
8283 aResult.Append(')');
8284 }
8285 aResult.Append('(');
8286 aResult.AppendInt(ContentIndexInContainer(this));
8287 aResult.Append(')');
8288 return NS_OK;
8289 }
8290
DumpFrameTree() const8291 void nsIFrame::DumpFrameTree() const {
8292 PresShell()->GetRootFrame()->List(stderr);
8293 }
8294
DumpFrameTreeInCSSPixels() const8295 void nsIFrame::DumpFrameTreeInCSSPixels() const {
8296 PresShell()->GetRootFrame()->List(stderr, "", ListFlag::DisplayInCSSPixels);
8297 }
8298
DumpFrameTreeLimited() const8299 void nsIFrame::DumpFrameTreeLimited() const { List(stderr); }
DumpFrameTreeLimitedInCSSPixels() const8300 void nsIFrame::DumpFrameTreeLimitedInCSSPixels() const {
8301 List(stderr, "", ListFlag::DisplayInCSSPixels);
8302 }
8303
8304 #endif
8305
IsVisibleForPainting()8306 bool nsIFrame::IsVisibleForPainting() { return StyleVisibility()->IsVisible(); }
8307
IsVisibleOrCollapsedForPainting()8308 bool nsIFrame::IsVisibleOrCollapsedForPainting() {
8309 return StyleVisibility()->IsVisibleOrCollapsed();
8310 }
8311
8312 /* virtual */
IsEmpty()8313 bool nsIFrame::IsEmpty() { return false; }
8314
CachedIsEmpty()8315 bool nsIFrame::CachedIsEmpty() {
8316 MOZ_ASSERT(!(GetStateBits() & NS_FRAME_IS_DIRTY),
8317 "Must only be called on reflowed lines");
8318 return IsEmpty();
8319 }
8320
8321 /* virtual */
IsSelfEmpty()8322 bool nsIFrame::IsSelfEmpty() { return false; }
8323
GetSelectionController(nsPresContext * aPresContext,nsISelectionController ** aSelCon)8324 nsresult nsIFrame::GetSelectionController(nsPresContext* aPresContext,
8325 nsISelectionController** aSelCon) {
8326 if (!aPresContext || !aSelCon) return NS_ERROR_INVALID_ARG;
8327
8328 nsIFrame* frame = this;
8329 while (frame && (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION)) {
8330 nsITextControlFrame* tcf = do_QueryFrame(frame);
8331 if (tcf) {
8332 return tcf->GetOwnedSelectionController(aSelCon);
8333 }
8334 frame = frame->GetParent();
8335 }
8336
8337 *aSelCon = do_AddRef(aPresContext->PresShell()).take();
8338 return NS_OK;
8339 }
8340
GetFrameSelection()8341 already_AddRefed<nsFrameSelection> nsIFrame::GetFrameSelection() {
8342 RefPtr<nsFrameSelection> fs =
8343 const_cast<nsFrameSelection*>(GetConstFrameSelection());
8344 return fs.forget();
8345 }
8346
GetConstFrameSelection() const8347 const nsFrameSelection* nsIFrame::GetConstFrameSelection() const {
8348 nsIFrame* frame = const_cast<nsIFrame*>(this);
8349 while (frame && (frame->GetStateBits() & NS_FRAME_INDEPENDENT_SELECTION)) {
8350 nsITextControlFrame* tcf = do_QueryFrame(frame);
8351 if (tcf) {
8352 return tcf->GetOwnedFrameSelection();
8353 }
8354 frame = frame->GetParent();
8355 }
8356
8357 return PresShell()->ConstFrameSelection();
8358 }
8359
IsFrameSelected() const8360 bool nsIFrame::IsFrameSelected() const {
8361 NS_ASSERTION(!GetContent() || GetContent()->IsMaybeSelected(),
8362 "use the public IsSelected() instead");
8363 return GetContent()->IsSelected(0, GetContent()->GetChildCount());
8364 }
8365
GetPointFromOffset(int32_t inOffset,nsPoint * outPoint)8366 nsresult nsIFrame::GetPointFromOffset(int32_t inOffset, nsPoint* outPoint) {
8367 MOZ_ASSERT(outPoint != nullptr, "Null parameter");
8368 nsRect contentRect = GetContentRectRelativeToSelf();
8369 nsPoint pt = contentRect.TopLeft();
8370 if (mContent) {
8371 nsIContent* newContent = mContent->GetParent();
8372 if (newContent) {
8373 int32_t newOffset = newContent->ComputeIndexOf(mContent);
8374
8375 // Find the direction of the frame from the EmbeddingLevelProperty,
8376 // which is the resolved bidi level set in
8377 // nsBidiPresUtils::ResolveParagraph (odd levels = right-to-left).
8378 // If the embedding level isn't set, just use the CSS direction
8379 // property.
8380 bool hasBidiData;
8381 FrameBidiData bidiData = GetProperty(BidiDataProperty(), &hasBidiData);
8382 bool isRTL = hasBidiData
8383 ? IS_LEVEL_RTL(bidiData.embeddingLevel)
8384 : StyleVisibility()->mDirection == StyleDirection::Rtl;
8385 if ((!isRTL && inOffset > newOffset) ||
8386 (isRTL && inOffset <= newOffset)) {
8387 pt = contentRect.TopRight();
8388 }
8389 }
8390 }
8391 *outPoint = pt;
8392 return NS_OK;
8393 }
8394
GetCharacterRectsInRange(int32_t aInOffset,int32_t aLength,nsTArray<nsRect> & aOutRect)8395 nsresult nsIFrame::GetCharacterRectsInRange(int32_t aInOffset, int32_t aLength,
8396 nsTArray<nsRect>& aOutRect) {
8397 /* no text */
8398 return NS_ERROR_FAILURE;
8399 }
8400
GetChildFrameContainingOffset(int32_t inContentOffset,bool inHint,int32_t * outFrameContentOffset,nsIFrame ** outChildFrame)8401 nsresult nsIFrame::GetChildFrameContainingOffset(int32_t inContentOffset,
8402 bool inHint,
8403 int32_t* outFrameContentOffset,
8404 nsIFrame** outChildFrame) {
8405 MOZ_ASSERT(outChildFrame && outFrameContentOffset, "Null parameter");
8406 *outFrameContentOffset = (int32_t)inHint;
8407 // the best frame to reflect any given offset would be a visible frame if
8408 // possible i.e. we are looking for a valid frame to place the blinking caret
8409 nsRect rect = GetRect();
8410 if (!rect.width || !rect.height) {
8411 // if we have a 0 width or height then lets look for another frame that
8412 // possibly has the same content. If we have no frames in flow then just
8413 // let us return 'this' frame
8414 nsIFrame* nextFlow = GetNextInFlow();
8415 if (nextFlow)
8416 return nextFlow->GetChildFrameContainingOffset(
8417 inContentOffset, inHint, outFrameContentOffset, outChildFrame);
8418 }
8419 *outChildFrame = this;
8420 return NS_OK;
8421 }
8422
8423 //
8424 // What I've pieced together about this routine:
8425 // Starting with a block frame (from which a line frame can be gotten)
8426 // and a line number, drill down and get the first/last selectable
8427 // frame on that line, depending on aPos->mDirection.
8428 // aOutSideLimit != 0 means ignore aLineStart, instead work from
8429 // the end (if > 0) or beginning (if < 0).
8430 //
GetNextPrevLineFromeBlockFrame(nsPresContext * aPresContext,nsPeekOffsetStruct * aPos,nsIFrame * aBlockFrame,int32_t aLineStart,int8_t aOutSideLimit)8431 nsresult nsFrame::GetNextPrevLineFromeBlockFrame(nsPresContext* aPresContext,
8432 nsPeekOffsetStruct* aPos,
8433 nsIFrame* aBlockFrame,
8434 int32_t aLineStart,
8435 int8_t aOutSideLimit) {
8436 // magic numbers aLineStart will be -1 for end of block 0 will be start of
8437 // block
8438 if (!aBlockFrame || !aPos) return NS_ERROR_NULL_POINTER;
8439
8440 aPos->mResultFrame = nullptr;
8441 aPos->mResultContent = nullptr;
8442 aPos->mAttach = aPos->mDirection == eDirNext ? CARET_ASSOCIATE_AFTER
8443 : CARET_ASSOCIATE_BEFORE;
8444
8445 const nsAutoLineIterator it = aBlockFrame->GetLineIterator();
8446 if (!it) {
8447 return NS_ERROR_FAILURE;
8448 }
8449 int32_t searchingLine = aLineStart;
8450 int32_t countLines = it->GetNumLines();
8451 if (aOutSideLimit > 0) // start at end
8452 searchingLine = countLines;
8453 else if (aOutSideLimit < 0) // start at beginning
8454 searchingLine = -1; //"next" will be 0
8455 else if ((aPos->mDirection == eDirPrevious && searchingLine == 0) ||
8456 (aPos->mDirection == eDirNext &&
8457 searchingLine >= (countLines - 1))) {
8458 // we need to jump to new block frame.
8459 return NS_ERROR_FAILURE;
8460 }
8461 int32_t lineFrameCount;
8462 nsIFrame* resultFrame = nullptr;
8463 nsIFrame* farStoppingFrame = nullptr; // we keep searching until we find a
8464 // "this" frame then we go to next line
8465 nsIFrame* nearStoppingFrame = nullptr; // if we are backing up from edge,
8466 // stop here
8467 nsIFrame* firstFrame;
8468 nsIFrame* lastFrame;
8469 nsRect rect;
8470 bool isBeforeFirstFrame, isAfterLastFrame;
8471 bool found = false;
8472
8473 nsresult result = NS_OK;
8474 while (!found) {
8475 if (aPos->mDirection == eDirPrevious)
8476 searchingLine--;
8477 else
8478 searchingLine++;
8479 if ((aPos->mDirection == eDirPrevious && searchingLine < 0) ||
8480 (aPos->mDirection == eDirNext && searchingLine >= countLines)) {
8481 // we need to jump to new block frame.
8482 return NS_ERROR_FAILURE;
8483 }
8484 result = it->GetLine(searchingLine, &firstFrame, &lineFrameCount, rect);
8485 if (!lineFrameCount) continue;
8486 if (NS_SUCCEEDED(result)) {
8487 lastFrame = firstFrame;
8488 for (; lineFrameCount > 1; lineFrameCount--) {
8489 // result = lastFrame->GetNextSibling(&lastFrame, searchingLine);
8490 result = it->GetNextSiblingOnLine(lastFrame, searchingLine);
8491 if (NS_FAILED(result) || !lastFrame) {
8492 NS_ERROR("GetLine promised more frames than could be found");
8493 return NS_ERROR_FAILURE;
8494 }
8495 }
8496 GetLastLeaf(aPresContext, &lastFrame);
8497
8498 if (aPos->mDirection == eDirNext) {
8499 nearStoppingFrame = firstFrame;
8500 farStoppingFrame = lastFrame;
8501 } else {
8502 nearStoppingFrame = lastFrame;
8503 farStoppingFrame = firstFrame;
8504 }
8505 nsPoint offset;
8506 nsView* view; // used for call of get offset from view
8507 aBlockFrame->GetOffsetFromView(offset, &view);
8508 nsPoint newDesiredPos =
8509 aPos->mDesiredCaretPos -
8510 offset; // get desired position into blockframe coords
8511 result = it->FindFrameAt(searchingLine, newDesiredPos, &resultFrame,
8512 &isBeforeFirstFrame, &isAfterLastFrame);
8513 if (NS_FAILED(result)) continue;
8514 }
8515
8516 if (NS_SUCCEEDED(result) && resultFrame) {
8517 // check to see if this is ANOTHER blockframe inside the other one if so
8518 // then call into its lines
8519 nsAutoLineIterator newIt = resultFrame->GetLineIterator();
8520 if (newIt) {
8521 aPos->mResultFrame = resultFrame;
8522 return NS_OK;
8523 }
8524 // resultFrame is not a block frame
8525 result = NS_ERROR_FAILURE;
8526
8527 nsCOMPtr<nsIFrameEnumerator> frameTraversal;
8528 result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
8529 aPresContext, resultFrame, ePostOrder,
8530 false, // aVisual
8531 aPos->mScrollViewStop,
8532 false, // aFollowOOFs
8533 false // aSkipPopupChecks
8534 );
8535 if (NS_FAILED(result)) return result;
8536
8537 auto FoundValidFrame = [aPos](const ContentOffsets& aOffsets,
8538 const nsIFrame* aFrame) {
8539 if (!aOffsets.content) {
8540 return false;
8541 }
8542 if (!aFrame->IsSelectable(nullptr)) {
8543 return false;
8544 }
8545 if (aPos->mForceEditableRegion && !aOffsets.content->IsEditable()) {
8546 return false;
8547 }
8548 return true;
8549 };
8550
8551 nsIFrame* storeOldResultFrame = resultFrame;
8552 while (!found) {
8553 nsPoint point;
8554 nsRect tempRect = resultFrame->GetRect();
8555 nsPoint offset;
8556 nsView* view; // used for call of get offset from view
8557 resultFrame->GetOffsetFromView(offset, &view);
8558 if (!view) {
8559 return NS_ERROR_FAILURE;
8560 }
8561 if (resultFrame->GetWritingMode().IsVertical()) {
8562 point.y = aPos->mDesiredCaretPos.y;
8563 point.x = tempRect.width + offset.x;
8564 } else {
8565 point.y = tempRect.height + offset.y;
8566 point.x = aPos->mDesiredCaretPos.x;
8567 }
8568
8569 // special check. if we allow non-text selection then we can allow a hit
8570 // location to fall before a table. otherwise there is no way to get and
8571 // click signal to fall before a table (it being a line iterator itself)
8572 mozilla::PresShell* presShell = aPresContext->GetPresShell();
8573 if (!presShell) {
8574 return NS_ERROR_FAILURE;
8575 }
8576 int16_t isEditor = presShell->GetSelectionFlags();
8577 isEditor = isEditor == nsISelectionDisplay::DISPLAY_ALL;
8578 if (isEditor) {
8579 if (resultFrame->IsTableWrapperFrame()) {
8580 if (((point.x - offset.x + tempRect.x) < 0) ||
8581 ((point.x - offset.x + tempRect.x) >
8582 tempRect.width)) // off left/right side
8583 {
8584 nsIContent* content = resultFrame->GetContent();
8585 if (content) {
8586 nsIContent* parent = content->GetParent();
8587 if (parent) {
8588 aPos->mResultContent = parent;
8589 aPos->mContentOffset = parent->ComputeIndexOf(content);
8590 aPos->mAttach = CARET_ASSOCIATE_BEFORE;
8591 if ((point.x - offset.x + tempRect.x) > tempRect.width) {
8592 aPos->mContentOffset++; // go to end of this frame
8593 aPos->mAttach = CARET_ASSOCIATE_AFTER;
8594 }
8595 // result frame is the result frames parent.
8596 aPos->mResultFrame = resultFrame->GetParent();
8597 return NS_POSITION_BEFORE_TABLE;
8598 }
8599 }
8600 }
8601 }
8602 }
8603
8604 if (!resultFrame->HasView()) {
8605 nsView* view;
8606 nsPoint offset;
8607 resultFrame->GetOffsetFromView(offset, &view);
8608 ContentOffsets offsets =
8609 resultFrame->GetContentOffsetsFromPoint(point - offset);
8610 aPos->mResultContent = offsets.content;
8611 aPos->mContentOffset = offsets.offset;
8612 aPos->mAttach = offsets.associate;
8613 if (FoundValidFrame(offsets, resultFrame)) {
8614 found = true;
8615 break;
8616 }
8617 }
8618
8619 if (aPos->mDirection == eDirPrevious &&
8620 (resultFrame == farStoppingFrame))
8621 break;
8622 if (aPos->mDirection == eDirNext && (resultFrame == nearStoppingFrame))
8623 break;
8624 // always try previous on THAT line if that fails go the other way
8625 frameTraversal->Prev();
8626 resultFrame = frameTraversal->CurrentItem();
8627 if (!resultFrame) return NS_ERROR_FAILURE;
8628 }
8629
8630 if (!found) {
8631 resultFrame = storeOldResultFrame;
8632
8633 result = NS_NewFrameTraversal(getter_AddRefs(frameTraversal),
8634 aPresContext, resultFrame, eLeaf,
8635 false, // aVisual
8636 aPos->mScrollViewStop,
8637 false, // aFollowOOFs
8638 false // aSkipPopupChecks
8639 );
8640 }
8641 while (!found) {
8642 nsPoint point = aPos->mDesiredCaretPos;
8643 nsView* view;
8644 nsPoint offset;
8645 resultFrame->GetOffsetFromView(offset, &view);
8646 ContentOffsets offsets =
8647 resultFrame->GetContentOffsetsFromPoint(point - offset);
8648 aPos->mResultContent = offsets.content;
8649 aPos->mContentOffset = offsets.offset;
8650 aPos->mAttach = offsets.associate;
8651 if (FoundValidFrame(offsets, resultFrame)) {
8652 found = true;
8653 if (resultFrame == farStoppingFrame)
8654 aPos->mAttach = CARET_ASSOCIATE_BEFORE;
8655 else
8656 aPos->mAttach = CARET_ASSOCIATE_AFTER;
8657 break;
8658 }
8659 if (aPos->mDirection == eDirPrevious &&
8660 (resultFrame == nearStoppingFrame))
8661 break;
8662 if (aPos->mDirection == eDirNext && (resultFrame == farStoppingFrame))
8663 break;
8664 // previous didnt work now we try "next"
8665 frameTraversal->Next();
8666 nsIFrame* tempFrame = frameTraversal->CurrentItem();
8667 if (!tempFrame) break;
8668 resultFrame = tempFrame;
8669 }
8670 aPos->mResultFrame = resultFrame;
8671 } else {
8672 // we need to jump to new block frame.
8673 aPos->mAmount = eSelectLine;
8674 aPos->mStartOffset = 0;
8675 aPos->mAttach = aPos->mDirection == eDirNext ? CARET_ASSOCIATE_BEFORE
8676 : CARET_ASSOCIATE_AFTER;
8677 if (aPos->mDirection == eDirPrevious)
8678 aPos->mStartOffset = -1; // start from end
8679 return aBlockFrame->PeekOffset(aPos);
8680 }
8681 }
8682 return NS_OK;
8683 }
8684
GetExtremeCaretPosition(bool aStart)8685 nsIFrame::CaretPosition nsIFrame::GetExtremeCaretPosition(bool aStart) {
8686 CaretPosition result;
8687
8688 FrameTarget targetFrame = DrillDownToSelectionFrame(this, !aStart, 0);
8689 FrameContentRange range = GetRangeForFrame(targetFrame.frame);
8690 result.mResultContent = range.content;
8691 result.mContentOffset = aStart ? range.start : range.end;
8692 return result;
8693 }
8694
8695 // If this is a preformatted text frame, see if it ends with a newline
FindLineBreakInText(nsIFrame * aFrame,nsDirection aDirection)8696 static nsContentAndOffset FindLineBreakInText(nsIFrame* aFrame,
8697 nsDirection aDirection) {
8698 nsContentAndOffset result;
8699
8700 if (aFrame->IsGeneratedContentFrame() ||
8701 !aFrame->HasSignificantTerminalNewline()) {
8702 return result;
8703 }
8704
8705 int32_t startOffset, endOffset;
8706 aFrame->GetOffsets(startOffset, endOffset);
8707 result.mContent = aFrame->GetContent();
8708 result.mOffset = endOffset - (aDirection == eDirPrevious ? 0 : 1);
8709 return result;
8710 }
8711
8712 // Find the first (or last) descendant of the given frame
8713 // which is either a block-level frame or a BRFrame, or some other kind of break
8714 // which stops the line.
FindLineBreakingFrame(nsIFrame * aFrame,nsDirection aDirection)8715 static nsContentAndOffset FindLineBreakingFrame(nsIFrame* aFrame,
8716 nsDirection aDirection) {
8717 nsContentAndOffset result;
8718
8719 if (aFrame->IsGeneratedContentFrame()) {
8720 return result;
8721 }
8722
8723 // Treat form controls as inline leaves
8724 // XXX we really need a way to determine whether a frame is inline-level
8725 if (static_cast<nsIFormControlFrame*>(do_QueryFrame(aFrame))) {
8726 return result;
8727 }
8728
8729 // Check the frame itself
8730 // Fall through block-in-inline split frames because their mContent is
8731 // the content of the inline frames they were created from. The
8732 // first/last child of such frames is the real block frame we're
8733 // looking for.
8734 if ((aFrame->IsBlockOutside() &&
8735 !(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) ||
8736 aFrame->IsBrFrame()) {
8737 nsIContent* content = aFrame->GetContent();
8738 result.mContent = content->GetParent();
8739 // In some cases (bug 310589, bug 370174) we end up here with a null
8740 // content. This probably shouldn't ever happen, but since it sometimes
8741 // does, we want to avoid crashing here.
8742 NS_ASSERTION(result.mContent, "Unexpected orphan content");
8743 if (result.mContent)
8744 result.mOffset = result.mContent->ComputeIndexOf(content) +
8745 (aDirection == eDirPrevious ? 1 : 0);
8746 return result;
8747 }
8748
8749 result = FindLineBreakInText(aFrame, aDirection);
8750 if (result.mContent) {
8751 return result;
8752 }
8753
8754 // Iterate over children and call ourselves recursively
8755 if (aDirection == eDirPrevious) {
8756 nsIFrame* child =
8757 aFrame->GetChildList(nsIFrame::kPrincipalList).LastChild();
8758 while (child && !result.mContent) {
8759 result = FindLineBreakingFrame(child, aDirection);
8760 child = child->GetPrevSibling();
8761 }
8762 } else { // eDirNext
8763 nsIFrame* child = aFrame->PrincipalChildList().FirstChild();
8764 while (child && !result.mContent) {
8765 result = FindLineBreakingFrame(child, aDirection);
8766 child = child->GetNextSibling();
8767 }
8768 }
8769 return result;
8770 }
8771
PeekOffsetParagraph(nsPeekOffsetStruct * aPos)8772 nsresult nsIFrame::PeekOffsetParagraph(nsPeekOffsetStruct* aPos) {
8773 nsIFrame* frame = this;
8774 nsContentAndOffset blockFrameOrBR;
8775 blockFrameOrBR.mContent = nullptr;
8776 bool reachedBlockAncestor = frame->IsBlockOutside();
8777
8778 auto traverse = [&aPos](nsIFrame* current) {
8779 return aPos->mDirection == eDirPrevious ? current->GetPrevSibling()
8780 : current->GetNextSibling();
8781 };
8782
8783 // Go through containing frames until reaching a block frame.
8784 // In each step, search the previous (or next) siblings for the closest
8785 // "stop frame" (a block frame or a BRFrame).
8786 // If found, set it to be the selection boundary and abort.
8787 while (!reachedBlockAncestor) {
8788 nsIFrame* parent = frame->GetParent();
8789 // Treat a frame associated with the root content as if it were a block
8790 // frame.
8791 if (!frame->mContent || !frame->mContent->GetParent()) {
8792 reachedBlockAncestor = true;
8793 break;
8794 }
8795
8796 if (aPos->mDirection == eDirNext) {
8797 // Try to find our own line-break before looking at our siblings.
8798 blockFrameOrBR = FindLineBreakInText(frame, eDirNext);
8799 }
8800
8801 nsIFrame* sibling = traverse(frame);
8802 while (sibling && !blockFrameOrBR.mContent) {
8803 blockFrameOrBR = FindLineBreakingFrame(sibling, aPos->mDirection);
8804 sibling = traverse(sibling);
8805 }
8806 if (blockFrameOrBR.mContent) {
8807 aPos->mResultContent = blockFrameOrBR.mContent;
8808 aPos->mContentOffset = blockFrameOrBR.mOffset;
8809 break;
8810 }
8811 frame = parent;
8812 reachedBlockAncestor = frame && frame->IsBlockOutside();
8813 }
8814
8815 if (reachedBlockAncestor) { // no "stop frame" found
8816 aPos->mResultContent = frame->GetContent();
8817 if (aPos->mDirection == eDirPrevious) {
8818 aPos->mContentOffset = 0;
8819 } else if (aPos->mResultContent) {
8820 aPos->mContentOffset = aPos->mResultContent->GetChildCount();
8821 }
8822 }
8823 return NS_OK;
8824 }
8825
8826 // Determine movement direction relative to frame
IsMovingInFrameDirection(nsIFrame * frame,nsDirection aDirection,bool aVisual)8827 static bool IsMovingInFrameDirection(nsIFrame* frame, nsDirection aDirection,
8828 bool aVisual) {
8829 bool isReverseDirection = aVisual && IsReversedDirectionFrame(frame);
8830 return aDirection == (isReverseDirection ? eDirPrevious : eDirNext);
8831 }
8832
PeekOffset(nsPeekOffsetStruct * aPos)8833 nsresult nsIFrame::PeekOffset(nsPeekOffsetStruct* aPos) {
8834 if (!aPos) return NS_ERROR_NULL_POINTER;
8835 nsresult result = NS_ERROR_FAILURE;
8836
8837 if (mState & NS_FRAME_IS_DIRTY) return NS_ERROR_UNEXPECTED;
8838
8839 // Translate content offset to be relative to frame
8840 FrameContentRange range = GetRangeForFrame(this);
8841 int32_t offset = aPos->mStartOffset - range.start;
8842 nsIFrame* current = this;
8843
8844 switch (aPos->mAmount) {
8845 case eSelectCharacter:
8846 case eSelectCluster: {
8847 bool eatingNonRenderableWS = false;
8848 nsIFrame::FrameSearchResult peekSearchState = CONTINUE;
8849 bool jumpedLine = false;
8850 bool movedOverNonSelectableText = false;
8851
8852 while (peekSearchState != FOUND) {
8853 bool movingInFrameDirection =
8854 IsMovingInFrameDirection(current, aPos->mDirection, aPos->mVisual);
8855
8856 if (eatingNonRenderableWS) {
8857 peekSearchState =
8858 current->PeekOffsetNoAmount(movingInFrameDirection, &offset);
8859 } else {
8860 PeekOffsetCharacterOptions options;
8861 options.mRespectClusters = aPos->mAmount == eSelectCluster;
8862 peekSearchState = current->PeekOffsetCharacter(movingInFrameDirection,
8863 &offset, options);
8864 }
8865
8866 movedOverNonSelectableText |=
8867 (peekSearchState == CONTINUE_UNSELECTABLE);
8868
8869 if (peekSearchState != FOUND) {
8870 bool movedOverNonSelectable = false;
8871 result = current->GetFrameFromDirection(
8872 *aPos, ¤t, &offset, &jumpedLine, &movedOverNonSelectable);
8873 if (NS_FAILED(result)) return result;
8874
8875 // If we jumped lines, it's as if we found a character, but we still
8876 // need to eat non-renderable content on the new line.
8877 if (jumpedLine) eatingNonRenderableWS = true;
8878
8879 // Remember if we moved over non-selectable text when finding another
8880 // frame.
8881 movedOverNonSelectableText |= movedOverNonSelectable;
8882 }
8883
8884 // Found frame, but because we moved over non selectable text we want
8885 // the offset to be at the frame edge. Note that if we are extending the
8886 // selection, this doesn't matter.
8887 if (peekSearchState == FOUND && movedOverNonSelectableText &&
8888 !aPos->mExtend) {
8889 int32_t start, end;
8890 current->GetOffsets(start, end);
8891 offset = aPos->mDirection == eDirNext ? 0 : end - start;
8892 }
8893 }
8894
8895 // Set outputs
8896 range = GetRangeForFrame(current);
8897 aPos->mResultFrame = current;
8898 aPos->mResultContent = range.content;
8899 // Output offset is relative to content, not frame
8900 aPos->mContentOffset = offset < 0 ? range.end : range.start + offset;
8901 // If we're dealing with a text frame and moving backward positions us at
8902 // the end of that line, decrease the offset by one to make sure that
8903 // we're placed before the linefeed character on the previous line.
8904 if (offset < 0 && jumpedLine && aPos->mDirection == eDirPrevious &&
8905 current->HasSignificantTerminalNewline()) {
8906 --aPos->mContentOffset;
8907 }
8908
8909 break;
8910 }
8911 case eSelectWordNoSpace:
8912 // eSelectWordNoSpace means that we should not be eating any whitespace
8913 // when moving to the adjacent word. This means that we should set aPos->
8914 // mWordMovementType to eEndWord if we're moving forwards, and to
8915 // eStartWord if we're moving backwards.
8916 if (aPos->mDirection == eDirPrevious) {
8917 aPos->mWordMovementType = eStartWord;
8918 } else {
8919 aPos->mWordMovementType = eEndWord;
8920 }
8921 // Intentionally fall through the eSelectWord case.
8922 [[fallthrough]];
8923 case eSelectWord: {
8924 // wordSelectEatSpace means "are we looking for a boundary between
8925 // whitespace and non-whitespace (in the direction we're moving in)". It
8926 // is true when moving forward and looking for a beginning of a word, or
8927 // when moving backwards and looking for an end of a word.
8928 bool wordSelectEatSpace;
8929 if (aPos->mWordMovementType != eDefaultBehavior) {
8930 // aPos->mWordMovementType possible values:
8931 // eEndWord: eat the space if we're moving backwards
8932 // eStartWord: eat the space if we're moving forwards
8933 wordSelectEatSpace = ((aPos->mWordMovementType == eEndWord) ==
8934 (aPos->mDirection == eDirPrevious));
8935 } else {
8936 // Use the hidden preference which is based on operating system
8937 // behavior. This pref only affects whether moving forward by word
8938 // should go to the end of this word or start of the next word. When
8939 // going backwards, the start of the word is always used, on every
8940 // operating system.
8941 wordSelectEatSpace =
8942 aPos->mDirection == eDirNext &&
8943 Preferences::GetBool("layout.word_select.eat_space_to_next_word");
8944 }
8945
8946 // mSawBeforeType means "we already saw characters of the type
8947 // before the boundary we're looking for". Examples:
8948 // 1. If we're moving forward, looking for a word beginning (i.e. a
8949 // boundary between whitespace and non-whitespace), then
8950 // eatingWS==true means "we already saw some whitespace".
8951 // 2. If we're moving backward, looking for a word beginning (i.e. a
8952 // boundary between non-whitespace and whitespace), then
8953 // eatingWS==true means "we already saw some non-whitespace".
8954 PeekWordState state;
8955 int32_t offsetAdjustment = 0;
8956 bool done = false;
8957 while (!done) {
8958 bool movingInFrameDirection =
8959 IsMovingInFrameDirection(current, aPos->mDirection, aPos->mVisual);
8960
8961 done =
8962 current->PeekOffsetWord(movingInFrameDirection, wordSelectEatSpace,
8963 aPos->mIsKeyboardSelect, &offset, &state,
8964 aPos->mTrimSpaces) == FOUND;
8965
8966 if (!done) {
8967 nsIFrame* nextFrame;
8968 int32_t nextFrameOffset;
8969 bool jumpedLine, movedOverNonSelectableText;
8970 result = current->GetFrameFromDirection(*aPos, &nextFrame,
8971 &nextFrameOffset, &jumpedLine,
8972 &movedOverNonSelectableText);
8973 // We can't jump lines if we're looking for whitespace following
8974 // non-whitespace, and we already encountered non-whitespace.
8975 if (NS_FAILED(result) ||
8976 (jumpedLine && !wordSelectEatSpace && state.mSawBeforeType)) {
8977 done = true;
8978 // If we've crossed the line boundary, check to make sure that we
8979 // have not consumed a trailing newline as whitesapce if it's
8980 // significant.
8981 if (jumpedLine && wordSelectEatSpace &&
8982 current->HasSignificantTerminalNewline()) {
8983 offsetAdjustment = -1;
8984 }
8985 } else {
8986 if (jumpedLine) {
8987 state.mContext.Truncate();
8988 }
8989 current = nextFrame;
8990 offset = nextFrameOffset;
8991 // Jumping a line is equivalent to encountering whitespace
8992 if (wordSelectEatSpace && jumpedLine) state.SetSawBeforeType();
8993 }
8994 }
8995 }
8996
8997 // Set outputs
8998 range = GetRangeForFrame(current);
8999 aPos->mResultFrame = current;
9000 aPos->mResultContent = range.content;
9001 // Output offset is relative to content, not frame
9002 aPos->mContentOffset =
9003 (offset < 0 ? range.end : range.start + offset) + offsetAdjustment;
9004 break;
9005 }
9006 case eSelectLine: {
9007 nsAutoLineIterator iter;
9008 nsIFrame* blockFrame = this;
9009
9010 while (NS_FAILED(result)) {
9011 int32_t thisLine = nsFrame::GetLineNumber(
9012 blockFrame, aPos->mScrollViewStop, &blockFrame);
9013 if (thisLine < 0) return NS_ERROR_FAILURE;
9014 iter = blockFrame->GetLineIterator();
9015 NS_ASSERTION(iter, "GetLineNumber() succeeded but no block frame?");
9016
9017 int edgeCase = 0; // no edge case. this should look at thisLine
9018
9019 bool doneLooping = false; // tells us when no more block frames hit.
9020 // this part will find a frame or a block frame. if it's a block frame
9021 // it will "drill down" to find a viable frame or it will return an
9022 // error.
9023 nsIFrame* lastFrame = this;
9024 do {
9025 result = nsFrame::GetNextPrevLineFromeBlockFrame(
9026 PresContext(), aPos, blockFrame, thisLine,
9027 edgeCase); // start from thisLine
9028
9029 // we came back to same spot! keep going
9030 if (NS_SUCCEEDED(result) &&
9031 (!aPos->mResultFrame || aPos->mResultFrame == lastFrame)) {
9032 aPos->mResultFrame = nullptr;
9033 if (aPos->mDirection == eDirPrevious)
9034 thisLine--;
9035 else
9036 thisLine++;
9037 } else // if failure or success with different frame.
9038 doneLooping = true; // do not continue with while loop
9039
9040 lastFrame = aPos->mResultFrame; // set last frame
9041
9042 // make sure block element is not the same as the one we had before
9043 if (NS_SUCCEEDED(result) && aPos->mResultFrame &&
9044 blockFrame != aPos->mResultFrame) {
9045 /* SPECIAL CHECK FOR TABLE NAVIGATION
9046 tables need to navigate also and the frame that supports it is
9047 nsTableRowGroupFrame which is INSIDE nsTableWrapperFrame.
9048 If we have stumbled onto an nsTableWrapperFrame we need to drill
9049 into nsTableRowGroup if we hit a header or footer that's ok just
9050 go into them.
9051 */
9052 bool searchTableBool = false;
9053 if (aPos->mResultFrame->IsTableWrapperFrame() ||
9054 aPos->mResultFrame->IsTableCellFrame()) {
9055 nsIFrame* frame =
9056 aPos->mResultFrame->PrincipalChildList().FirstChild();
9057 // got the table frame now
9058 // ok time to drill down to find iterator
9059 while (frame) {
9060 iter = frame->GetLineIterator();
9061 if (iter) {
9062 aPos->mResultFrame = frame;
9063 searchTableBool = true;
9064 result = NS_OK;
9065 break; // while(frame)
9066 }
9067 result = NS_ERROR_FAILURE;
9068 frame = frame->PrincipalChildList().FirstChild();
9069 }
9070 }
9071
9072 if (!searchTableBool) {
9073 iter = aPos->mResultFrame->GetLineIterator();
9074 result = iter ? NS_OK : NS_ERROR_FAILURE;
9075 }
9076
9077 // we've struck another block element!
9078 if (NS_SUCCEEDED(result) && iter) {
9079 doneLooping = false;
9080 if (aPos->mDirection == eDirPrevious)
9081 edgeCase = 1; // far edge, search from end backwards
9082 else
9083 edgeCase = -1; // near edge search from beginning onwards
9084 thisLine = 0; // this line means nothing now.
9085 // everything else means something so keep looking "inside" the
9086 // block
9087 blockFrame = aPos->mResultFrame;
9088 } else {
9089 // THIS is to mean that everything is ok to the containing while
9090 // loop
9091 result = NS_OK;
9092 break;
9093 }
9094 }
9095 } while (!doneLooping);
9096 }
9097 return result;
9098 }
9099
9100 case eSelectParagraph:
9101 return PeekOffsetParagraph(aPos);
9102
9103 case eSelectBeginLine:
9104 case eSelectEndLine: {
9105 // Adjusted so that the caret can't get confused when content changes
9106 nsIFrame* blockFrame = AdjustFrameForSelectionStyles(this);
9107 int32_t thisLine = nsFrame::GetLineNumber(
9108 blockFrame, aPos->mScrollViewStop, &blockFrame);
9109 if (thisLine < 0) return NS_ERROR_FAILURE;
9110 nsAutoLineIterator it = blockFrame->GetLineIterator();
9111 NS_ASSERTION(it, "GetLineNumber() succeeded but no block frame?");
9112
9113 int32_t lineFrameCount;
9114 nsIFrame* firstFrame;
9115 nsRect usedRect;
9116 nsIFrame* baseFrame = nullptr;
9117 bool endOfLine = (eSelectEndLine == aPos->mAmount);
9118
9119 if (aPos->mVisual && PresContext()->BidiEnabled()) {
9120 bool lineIsRTL = it->GetDirection();
9121 bool isReordered;
9122 nsIFrame* lastFrame;
9123 result =
9124 it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame);
9125 baseFrame = endOfLine ? lastFrame : firstFrame;
9126 if (baseFrame) {
9127 bool frameIsRTL =
9128 (nsBidiPresUtils::FrameDirection(baseFrame) == NSBIDI_RTL);
9129 // If the direction of the frame on the edge is opposite to
9130 // that of the line, we'll need to drill down to its opposite
9131 // end, so reverse endOfLine.
9132 if (frameIsRTL != lineIsRTL) {
9133 endOfLine = !endOfLine;
9134 }
9135 }
9136 } else {
9137 it->GetLine(thisLine, &firstFrame, &lineFrameCount, usedRect);
9138
9139 nsIFrame* frame = firstFrame;
9140 bool lastFrameWasEditable = false;
9141 for (int32_t count = lineFrameCount; count;
9142 --count, frame = frame->GetNextSibling()) {
9143 if (frame->IsGeneratedContentFrame()) {
9144 continue;
9145 }
9146 // When jumping to the end of the line with the "end" key,
9147 // try to skip over brFrames
9148 if (endOfLine && lineFrameCount > 1 && frame->IsBrFrame() &&
9149 lastFrameWasEditable == frame->GetContent()->IsEditable()) {
9150 continue;
9151 }
9152 lastFrameWasEditable =
9153 frame->GetContent() && frame->GetContent()->IsEditable();
9154 baseFrame = frame;
9155 if (!endOfLine) {
9156 break;
9157 }
9158 }
9159 }
9160 if (!baseFrame) return NS_ERROR_FAILURE;
9161 FrameTarget targetFrame =
9162 DrillDownToSelectionFrame(baseFrame, endOfLine, 0);
9163 FrameContentRange range = GetRangeForFrame(targetFrame.frame);
9164 aPos->mResultContent = range.content;
9165 aPos->mContentOffset = endOfLine ? range.end : range.start;
9166 if (endOfLine && targetFrame.frame->HasSignificantTerminalNewline()) {
9167 // Do not position the caret after the terminating newline if we're
9168 // trying to move to the end of line (see bug 596506)
9169 --aPos->mContentOffset;
9170 }
9171 aPos->mResultFrame = targetFrame.frame;
9172 aPos->mAttach = aPos->mContentOffset == range.start
9173 ? CARET_ASSOCIATE_AFTER
9174 : CARET_ASSOCIATE_BEFORE;
9175 if (!range.content) return NS_ERROR_FAILURE;
9176 return NS_OK;
9177 }
9178
9179 default: {
9180 NS_ASSERTION(false, "Invalid amount");
9181 return NS_ERROR_FAILURE;
9182 }
9183 }
9184 return NS_OK;
9185 }
9186
PeekOffsetNoAmount(bool aForward,int32_t * aOffset)9187 nsIFrame::FrameSearchResult nsIFrame::PeekOffsetNoAmount(bool aForward,
9188 int32_t* aOffset) {
9189 NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range");
9190 // Sure, we can stop right here.
9191 return FOUND;
9192 }
9193
PeekOffsetCharacter(bool aForward,int32_t * aOffset,PeekOffsetCharacterOptions aOptions)9194 nsIFrame::FrameSearchResult nsIFrame::PeekOffsetCharacter(
9195 bool aForward, int32_t* aOffset, PeekOffsetCharacterOptions aOptions) {
9196 NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range");
9197 int32_t startOffset = *aOffset;
9198 // A negative offset means "end of frame", which in our case means offset 1.
9199 if (startOffset < 0) startOffset = 1;
9200 if (aForward == (startOffset == 0)) {
9201 // We're before the frame and moving forward, or after it and moving
9202 // backwards: skip to the other side and we're done.
9203 *aOffset = 1 - startOffset;
9204 return FOUND;
9205 }
9206 return CONTINUE;
9207 }
9208
PeekOffsetWord(bool aForward,bool aWordSelectEatSpace,bool aIsKeyboardSelect,int32_t * aOffset,PeekWordState * aState,bool)9209 nsIFrame::FrameSearchResult nsIFrame::PeekOffsetWord(
9210 bool aForward, bool aWordSelectEatSpace, bool aIsKeyboardSelect,
9211 int32_t* aOffset, PeekWordState* aState, bool /*aTrimSpaces*/) {
9212 NS_ASSERTION(aOffset && *aOffset <= 1, "aOffset out of range");
9213 int32_t startOffset = *aOffset;
9214 // This isn't text, so truncate the context
9215 aState->mContext.Truncate();
9216 if (startOffset < 0) startOffset = 1;
9217 if (aForward == (startOffset == 0)) {
9218 // We're before the frame and moving forward, or after it and moving
9219 // backwards. If we're looking for non-whitespace, we found it (without
9220 // skipping this frame).
9221 if (!aState->mAtStart) {
9222 if (aState->mLastCharWasPunctuation) {
9223 // We're not punctuation, so this is a punctuation boundary.
9224 if (BreakWordBetweenPunctuation(aState, aForward, false, false,
9225 aIsKeyboardSelect))
9226 return FOUND;
9227 } else {
9228 // This is not a punctuation boundary.
9229 if (aWordSelectEatSpace && aState->mSawBeforeType) return FOUND;
9230 }
9231 }
9232 // Otherwise skip to the other side and note that we encountered
9233 // non-whitespace.
9234 *aOffset = 1 - startOffset;
9235 aState->Update(false, // not punctuation
9236 false // not whitespace
9237 );
9238 if (!aWordSelectEatSpace) aState->SetSawBeforeType();
9239 }
9240 return CONTINUE;
9241 }
9242
9243 // static
BreakWordBetweenPunctuation(const PeekWordState * aState,bool aForward,bool aPunctAfter,bool aWhitespaceAfter,bool aIsKeyboardSelect)9244 bool nsIFrame::BreakWordBetweenPunctuation(const PeekWordState* aState,
9245 bool aForward, bool aPunctAfter,
9246 bool aWhitespaceAfter,
9247 bool aIsKeyboardSelect) {
9248 NS_ASSERTION(aPunctAfter != aState->mLastCharWasPunctuation,
9249 "Call this only at punctuation boundaries");
9250 if (aState->mLastCharWasWhitespace) {
9251 // We always stop between whitespace and punctuation
9252 return true;
9253 }
9254 if (!Preferences::GetBool("layout.word_select.stop_at_punctuation")) {
9255 // When this pref is false, we never stop at a punctuation boundary unless
9256 // it's followed by whitespace (in the relevant direction).
9257 return aWhitespaceAfter;
9258 }
9259 if (!aIsKeyboardSelect) {
9260 // mouse caret movement (e.g. word selection) always stops at every
9261 // punctuation boundary
9262 return true;
9263 }
9264 bool afterPunct = aForward ? aState->mLastCharWasPunctuation : aPunctAfter;
9265 if (!afterPunct) {
9266 // keyboard caret movement only stops after punctuation (in content order)
9267 return false;
9268 }
9269 // Stop only if we've seen some non-punctuation since the last whitespace;
9270 // don't stop after punctuation that follows whitespace.
9271 return aState->mSeenNonPunctuationSinceWhitespace;
9272 }
9273
CheckVisibility(nsPresContext *,int32_t,int32_t,bool,bool *,bool *)9274 nsresult nsIFrame::CheckVisibility(nsPresContext*, int32_t, int32_t, bool,
9275 bool*, bool*) {
9276 return NS_ERROR_NOT_IMPLEMENTED;
9277 }
9278
GetLineNumber(nsIFrame * aFrame,bool aLockScroll,nsIFrame ** aContainingBlock)9279 int32_t nsFrame::GetLineNumber(nsIFrame* aFrame, bool aLockScroll,
9280 nsIFrame** aContainingBlock) {
9281 NS_ASSERTION(aFrame, "null aFrame");
9282 nsIFrame* blockFrame = aFrame;
9283 nsIFrame* thisBlock;
9284 nsAutoLineIterator it;
9285 nsresult result = NS_ERROR_FAILURE;
9286 while (NS_FAILED(result) && blockFrame) {
9287 thisBlock = blockFrame;
9288 if (thisBlock->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
9289 // if we are searching for a frame that is not in flow we will not find
9290 // it. we must instead look for its placeholder
9291 if (thisBlock->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) {
9292 // abspos continuations don't have placeholders, get the fif
9293 thisBlock = thisBlock->FirstInFlow();
9294 }
9295 thisBlock = thisBlock->GetPlaceholderFrame();
9296 if (!thisBlock) return -1;
9297 }
9298 blockFrame = thisBlock->GetParent();
9299 result = NS_OK;
9300 if (blockFrame) {
9301 if (aLockScroll && blockFrame->IsScrollFrame()) return -1;
9302 it = blockFrame->GetLineIterator();
9303 if (!it) result = NS_ERROR_FAILURE;
9304 }
9305 }
9306 if (!blockFrame || !it) return -1;
9307
9308 if (aContainingBlock) *aContainingBlock = blockFrame;
9309 return it->FindLineContaining(thisBlock);
9310 }
9311
GetFrameFromDirection(nsDirection aDirection,bool aVisual,bool aJumpLines,bool aScrollViewStop,bool aForceEditableRegion,nsIFrame ** aOutFrame,int32_t * aOutOffset,bool * aOutJumpedLine,bool * aOutMovedOverNonSelectableText)9312 nsresult nsIFrame::GetFrameFromDirection(
9313 nsDirection aDirection, bool aVisual, bool aJumpLines, bool aScrollViewStop,
9314 bool aForceEditableRegion, nsIFrame** aOutFrame, int32_t* aOutOffset,
9315 bool* aOutJumpedLine, bool* aOutMovedOverNonSelectableText) {
9316 nsresult result;
9317
9318 if (!aOutFrame || !aOutOffset || !aOutJumpedLine)
9319 return NS_ERROR_NULL_POINTER;
9320
9321 nsPresContext* presContext = PresContext();
9322 *aOutFrame = nullptr;
9323 *aOutOffset = 0;
9324 *aOutJumpedLine = false;
9325 *aOutMovedOverNonSelectableText = false;
9326
9327 // Find the prev/next selectable frame
9328 bool selectable = false;
9329 nsIFrame* traversedFrame = this;
9330 while (!selectable) {
9331 nsIFrame* blockFrame;
9332
9333 int32_t thisLine =
9334 nsFrame::GetLineNumber(traversedFrame, aScrollViewStop, &blockFrame);
9335 if (thisLine < 0) return NS_ERROR_FAILURE;
9336
9337 nsAutoLineIterator it = blockFrame->GetLineIterator();
9338 NS_ASSERTION(it, "GetLineNumber() succeeded but no block frame?");
9339
9340 bool atLineEdge;
9341 nsIFrame* firstFrame;
9342 nsIFrame* lastFrame;
9343 if (aVisual && presContext->BidiEnabled()) {
9344 bool lineIsRTL = it->GetDirection();
9345 bool isReordered;
9346 result =
9347 it->CheckLineOrder(thisLine, &isReordered, &firstFrame, &lastFrame);
9348 nsIFrame** framePtr =
9349 aDirection == eDirPrevious ? &firstFrame : &lastFrame;
9350 if (*framePtr) {
9351 bool frameIsRTL =
9352 (nsBidiPresUtils::FrameDirection(*framePtr) == NSBIDI_RTL);
9353 if ((frameIsRTL == lineIsRTL) == (aDirection == eDirPrevious)) {
9354 nsFrame::GetFirstLeaf(presContext, framePtr);
9355 } else {
9356 nsFrame::GetLastLeaf(presContext, framePtr);
9357 }
9358 atLineEdge = *framePtr == traversedFrame;
9359 } else {
9360 atLineEdge = true;
9361 }
9362 } else {
9363 nsRect nonUsedRect;
9364 int32_t lineFrameCount;
9365 result = it->GetLine(thisLine, &firstFrame, &lineFrameCount, nonUsedRect);
9366 if (NS_FAILED(result)) return result;
9367
9368 if (aDirection == eDirPrevious) {
9369 nsFrame::GetFirstLeaf(presContext, &firstFrame);
9370 atLineEdge = firstFrame == traversedFrame;
9371 } else { // eDirNext
9372 lastFrame = firstFrame;
9373 for (; lineFrameCount > 1; lineFrameCount--) {
9374 result = it->GetNextSiblingOnLine(lastFrame, thisLine);
9375 if (NS_FAILED(result) || !lastFrame) {
9376 NS_ERROR("should not be reached nsFrame");
9377 return NS_ERROR_FAILURE;
9378 }
9379 }
9380 nsFrame::GetLastLeaf(presContext, &lastFrame);
9381 atLineEdge = lastFrame == traversedFrame;
9382 }
9383 }
9384
9385 if (atLineEdge) {
9386 *aOutJumpedLine = true;
9387 if (!aJumpLines)
9388 return NS_ERROR_FAILURE; // we are done. cannot jump lines
9389 }
9390
9391 nsCOMPtr<nsIFrameEnumerator> frameTraversal;
9392 result = NS_NewFrameTraversal(
9393 getter_AddRefs(frameTraversal), presContext, traversedFrame, eLeaf,
9394 aVisual && presContext->BidiEnabled(), aScrollViewStop,
9395 true, // aFollowOOFs
9396 false // aSkipPopupChecks
9397 );
9398 if (NS_FAILED(result)) return result;
9399
9400 if (aDirection == eDirNext)
9401 frameTraversal->Next();
9402 else
9403 frameTraversal->Prev();
9404
9405 traversedFrame = frameTraversal->CurrentItem();
9406 if (!traversedFrame) {
9407 return NS_ERROR_FAILURE;
9408 }
9409
9410 auto IsSelectable = [aForceEditableRegion](const nsIFrame* aFrame) {
9411 if (!aFrame->IsSelectable(nullptr)) {
9412 return false;
9413 }
9414 return !aForceEditableRegion || aFrame->GetContent()->IsEditable();
9415 };
9416
9417 // Skip brFrames, but only we can select something before hitting the end of
9418 // the line or a non-selectable region.
9419 if (atLineEdge && aDirection == eDirPrevious &&
9420 traversedFrame->IsBrFrame()) {
9421 bool canSkipBr = false;
9422 for (nsIFrame* current = traversedFrame->GetPrevSibling(); current;
9423 current = current->GetPrevSibling()) {
9424 if (!current->IsBlockOutside() && IsSelectable(current)) {
9425 canSkipBr = true;
9426 break;
9427 }
9428 }
9429 if (canSkipBr) {
9430 continue;
9431 }
9432 }
9433
9434 selectable = IsSelectable(traversedFrame);
9435 if (!selectable) {
9436 *aOutMovedOverNonSelectableText = true;
9437 }
9438 } // while (!selectable)
9439
9440 *aOutOffset = (aDirection == eDirNext) ? 0 : -1;
9441
9442 if (aVisual && IsReversedDirectionFrame(traversedFrame)) {
9443 // The new frame is reverse-direction, go to the other end
9444 *aOutOffset = -1 - *aOutOffset;
9445 }
9446 *aOutFrame = traversedFrame;
9447 return NS_OK;
9448 }
9449
GetFrameFromDirection(const nsPeekOffsetStruct & aPos,nsIFrame ** aOutFrame,int32_t * aOutOffset,bool * aOutJumpedLine,bool * aOutMovedOverNonSelectableText)9450 nsresult nsIFrame::GetFrameFromDirection(const nsPeekOffsetStruct& aPos,
9451 nsIFrame** aOutFrame,
9452 int32_t* aOutOffset,
9453 bool* aOutJumpedLine,
9454 bool* aOutMovedOverNonSelectableText) {
9455 return GetFrameFromDirection(aPos.mDirection, aPos.mVisual, aPos.mJumpLines,
9456 aPos.mScrollViewStop, aPos.mForceEditableRegion,
9457 aOutFrame, aOutOffset, aOutJumpedLine,
9458 aOutMovedOverNonSelectableText);
9459 }
9460
GetClosestView(nsPoint * aOffset) const9461 nsView* nsIFrame::GetClosestView(nsPoint* aOffset) const {
9462 nsPoint offset(0, 0);
9463 for (const nsIFrame* f = this; f; f = f->GetParent()) {
9464 if (f->HasView()) {
9465 if (aOffset) *aOffset = offset;
9466 return f->GetView();
9467 }
9468 offset += f->GetPosition();
9469 }
9470
9471 MOZ_ASSERT_UNREACHABLE("No view on any parent? How did that happen?");
9472 return nullptr;
9473 }
9474
9475 /* virtual */
ChildIsDirty(nsIFrame * aChild)9476 void nsIFrame::ChildIsDirty(nsIFrame* aChild) {
9477 MOZ_ASSERT_UNREACHABLE(
9478 "should never be called on a frame that doesn't "
9479 "inherit from nsContainerFrame");
9480 }
9481
9482 #ifdef ACCESSIBILITY
AccessibleType()9483 a11y::AccType nsIFrame::AccessibleType() {
9484 if (IsTableCaption() && !GetRect().IsEmpty()) {
9485 return a11y::eHTMLCaptionType;
9486 }
9487 return a11y::eNoType;
9488 }
9489 #endif
9490
ClearOverflowRects()9491 bool nsIFrame::ClearOverflowRects() {
9492 if (mOverflow.mType == NS_FRAME_OVERFLOW_NONE) {
9493 return false;
9494 }
9495 if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
9496 RemoveProperty(OverflowAreasProperty());
9497 }
9498 mOverflow.mType = NS_FRAME_OVERFLOW_NONE;
9499 return true;
9500 }
9501
9502 /** Set the overflowArea rect, storing it as deltas or a separate rect
9503 * depending on its size in relation to the primary frame rect.
9504 */
SetOverflowAreas(const nsOverflowAreas & aOverflowAreas)9505 bool nsIFrame::SetOverflowAreas(const nsOverflowAreas& aOverflowAreas) {
9506 if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
9507 nsOverflowAreas* overflow = GetOverflowAreasProperty();
9508 bool changed = *overflow != aOverflowAreas;
9509 *overflow = aOverflowAreas;
9510
9511 // Don't bother with converting to the deltas form if we already
9512 // have a property.
9513 return changed;
9514 }
9515
9516 const nsRect& vis = aOverflowAreas.VisualOverflow();
9517 uint32_t l = -vis.x, // left edge: positive delta is leftwards
9518 t = -vis.y, // top: positive is upwards
9519 r = vis.XMost() - mRect.width, // right: positive is rightwards
9520 b = vis.YMost() - mRect.height; // bottom: positive is downwards
9521 if (aOverflowAreas.ScrollableOverflow().IsEqualEdges(
9522 nsRect(nsPoint(0, 0), GetSize())) &&
9523 l <= NS_FRAME_OVERFLOW_DELTA_MAX && t <= NS_FRAME_OVERFLOW_DELTA_MAX &&
9524 r <= NS_FRAME_OVERFLOW_DELTA_MAX && b <= NS_FRAME_OVERFLOW_DELTA_MAX &&
9525 // we have to check these against zero because we *never* want to
9526 // set a frame as having no overflow in this function. This is
9527 // because FinishAndStoreOverflow calls this function prior to
9528 // SetRect based on whether the overflow areas match aNewSize.
9529 // In the case where the overflow areas exactly match mRect but
9530 // do not match aNewSize, we need to store overflow in a property
9531 // so that our eventual SetRect/SetSize will know that it has to
9532 // reset our overflow areas.
9533 (l | t | r | b) != 0) {
9534 VisualDeltas oldDeltas = mOverflow.mVisualDeltas;
9535 // It's a "small" overflow area so we store the deltas for each edge
9536 // directly in the frame, rather than allocating a separate rect.
9537 // If they're all zero, that's fine; we're setting things to
9538 // no-overflow.
9539 mOverflow.mVisualDeltas.mLeft = l;
9540 mOverflow.mVisualDeltas.mTop = t;
9541 mOverflow.mVisualDeltas.mRight = r;
9542 mOverflow.mVisualDeltas.mBottom = b;
9543 // There was no scrollable overflow before, and there isn't now.
9544 return oldDeltas != mOverflow.mVisualDeltas;
9545 } else {
9546 bool changed = !aOverflowAreas.ScrollableOverflow().IsEqualEdges(
9547 nsRect(nsPoint(0, 0), GetSize())) ||
9548 !aOverflowAreas.VisualOverflow().IsEqualEdges(
9549 GetVisualOverflowFromDeltas());
9550
9551 // it's a large overflow area that we need to store as a property
9552 mOverflow.mType = NS_FRAME_OVERFLOW_LARGE;
9553 AddProperty(OverflowAreasProperty(), new nsOverflowAreas(aOverflowAreas));
9554 return changed;
9555 }
9556 }
9557
9558 /**
9559 * Compute the union of the border boxes of aFrame and its descendants,
9560 * in aFrame's coordinate space (if aApplyTransform is false) or its
9561 * post-transform coordinate space (if aApplyTransform is true).
9562 */
UnionBorderBoxes(nsIFrame * aFrame,bool aApplyTransform,bool & aOutValid,const nsSize * aSizeOverride=nullptr,const nsOverflowAreas * aOverflowOverride=nullptr)9563 static nsRect UnionBorderBoxes(
9564 nsIFrame* aFrame, bool aApplyTransform, bool& aOutValid,
9565 const nsSize* aSizeOverride = nullptr,
9566 const nsOverflowAreas* aOverflowOverride = nullptr) {
9567 const nsRect bounds(nsPoint(0, 0),
9568 aSizeOverride ? *aSizeOverride : aFrame->GetSize());
9569
9570 // The SVG container frames besides SVGTextFrame do not maintain
9571 // an accurate mRect. It will make the outline be larger than
9572 // we expect, we need to make them narrow to their children's outline.
9573 // aOutValid is set to false if the returned nsRect is not valid
9574 // and should not be included in the outline rectangle.
9575 aOutValid = !(aFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) ||
9576 !aFrame->IsFrameOfType(nsIFrame::eSVGContainer) ||
9577 aFrame->IsSVGTextFrame();
9578
9579 nsRect u;
9580
9581 if (!aFrame->FrameMaintainsOverflow()) {
9582 return u;
9583 }
9584
9585 // Start from our border-box, transformed. See comment below about
9586 // transform of children.
9587 bool doTransform = aApplyTransform && aFrame->IsTransformed();
9588 TransformReferenceBox boundsRefBox(nullptr, bounds);
9589 if (doTransform) {
9590 u = nsDisplayTransform::TransformRect(bounds, aFrame, boundsRefBox);
9591 } else {
9592 u = bounds;
9593 }
9594
9595 // Only iterate through the children if the overflow areas suggest
9596 // that we might need to, and if the frame doesn't clip its overflow
9597 // anyway.
9598 if (aOverflowOverride) {
9599 if (!doTransform &&
9600 bounds.IsEqualEdges(aOverflowOverride->VisualOverflow()) &&
9601 bounds.IsEqualEdges(aOverflowOverride->ScrollableOverflow())) {
9602 return u;
9603 }
9604 } else {
9605 if (!doTransform && bounds.IsEqualEdges(aFrame->GetVisualOverflowRect()) &&
9606 bounds.IsEqualEdges(aFrame->GetScrollableOverflowRect())) {
9607 return u;
9608 }
9609 }
9610 const nsStyleDisplay* disp = aFrame->StyleDisplay();
9611 LayoutFrameType fType = aFrame->Type();
9612 if (nsFrame::ShouldApplyOverflowClipping(aFrame, disp) ||
9613 fType == LayoutFrameType::Scroll ||
9614 fType == LayoutFrameType::ListControl ||
9615 fType == LayoutFrameType::SVGOuterSVG) {
9616 return u;
9617 }
9618
9619 const nsStyleEffects* effects = aFrame->StyleEffects();
9620 Maybe<nsRect> clipPropClipRect =
9621 aFrame->GetClipPropClipRect(disp, effects, bounds.Size());
9622
9623 // Iterate over all children except pop-up, absolutely-positioned,
9624 // float, and overflow ones.
9625 const nsIFrame::ChildListIDs skip = {
9626 nsIFrame::kPopupList, nsIFrame::kSelectPopupList,
9627 nsIFrame::kAbsoluteList, nsIFrame::kFixedList,
9628 nsIFrame::kFloatList, nsIFrame::kOverflowList};
9629 for (const auto& [list, listID] : aFrame->ChildLists()) {
9630 if (skip.contains(listID)) {
9631 continue;
9632 }
9633
9634 for (nsIFrame* child : list) {
9635 if (child->IsPlaceholderFrame()) {
9636 continue;
9637 }
9638
9639 // Note that passing |true| for aApplyTransform when
9640 // child->Combines3DTransformWithAncestors() is incorrect if our
9641 // aApplyTransform is false... but the opposite would be as
9642 // well. This is because elements within a preserve-3d scene
9643 // are always transformed up to the top of the scene. This
9644 // means we don't have a mechanism for getting a transform up to
9645 // an intermediate point within the scene. We choose to
9646 // over-transform rather than under-transform because this is
9647 // consistent with other overflow areas.
9648 bool validRect = true;
9649 nsRect childRect =
9650 UnionBorderBoxes(child, true, validRect) + child->GetPosition();
9651
9652 if (!validRect) {
9653 continue;
9654 }
9655
9656 if (clipPropClipRect) {
9657 // Intersect with the clip before transforming.
9658 childRect.IntersectRect(childRect, *clipPropClipRect);
9659 }
9660
9661 // Note that we transform each child separately according to
9662 // aFrame's transform, and then union, which gives a different
9663 // (smaller) result from unioning and then transforming the
9664 // union. This doesn't match the way we handle overflow areas
9665 // with 2-D transforms, though it does match the way we handle
9666 // overflow areas in preserve-3d 3-D scenes.
9667 if (doTransform && !child->Combines3DTransformWithAncestors()) {
9668 childRect =
9669 nsDisplayTransform::TransformRect(childRect, aFrame, boundsRefBox);
9670 }
9671
9672 // If a SVGContainer has a non-SVGContainer child, we assign
9673 // its child's outline to this SVGContainer directly.
9674 if (!aOutValid && validRect) {
9675 u = childRect;
9676 aOutValid = true;
9677 } else {
9678 u.UnionRectEdges(u, childRect);
9679 }
9680 }
9681 }
9682
9683 return u;
9684 }
9685
ComputeAndIncludeOutlineArea(nsIFrame * aFrame,nsOverflowAreas & aOverflowAreas,const nsSize & aNewSize)9686 static void ComputeAndIncludeOutlineArea(nsIFrame* aFrame,
9687 nsOverflowAreas& aOverflowAreas,
9688 const nsSize& aNewSize) {
9689 const nsStyleOutline* outline = aFrame->StyleOutline();
9690 if (!outline->ShouldPaintOutline()) {
9691 return;
9692 }
9693
9694 // When the outline property is set on a :-moz-block-inside-inline-wrapper
9695 // pseudo-element, it inherited that outline from the inline that was broken
9696 // because it contained a block. In that case, we don't want a really wide
9697 // outline if the block inside the inline is narrow, so union the actual
9698 // contents of the anonymous blocks.
9699 nsIFrame* frameForArea = aFrame;
9700 do {
9701 PseudoStyleType pseudoType = frameForArea->Style()->GetPseudoType();
9702 if (pseudoType != PseudoStyleType::mozBlockInsideInlineWrapper) break;
9703 // If we're done, we really want it and all its later siblings.
9704 frameForArea = frameForArea->PrincipalChildList().FirstChild();
9705 NS_ASSERTION(frameForArea, "anonymous block with no children?");
9706 } while (frameForArea);
9707
9708 // Find the union of the border boxes of all descendants, or in
9709 // the block-in-inline case, all descendants we care about.
9710 //
9711 // Note that the interesting perspective-related cases are taken
9712 // care of by the code that handles those issues for overflow
9713 // calling FinishAndStoreOverflow again, which in turn calls this
9714 // function again. We still need to deal with preserve-3d a bit.
9715 nsRect innerRect;
9716 bool validRect;
9717 if (frameForArea == aFrame) {
9718 innerRect =
9719 UnionBorderBoxes(aFrame, false, validRect, &aNewSize, &aOverflowAreas);
9720 } else {
9721 for (; frameForArea; frameForArea = frameForArea->GetNextSibling()) {
9722 nsRect r(UnionBorderBoxes(frameForArea, true, validRect));
9723
9724 // Adjust for offsets transforms up to aFrame's pre-transform
9725 // (i.e., normal) coordinate space; see comments in
9726 // UnionBorderBoxes for some of the subtlety here.
9727 for (nsIFrame *f = frameForArea, *parent = f->GetParent();
9728 /* see middle of loop */; f = parent, parent = f->GetParent()) {
9729 r += f->GetPosition();
9730 if (parent == aFrame) {
9731 break;
9732 }
9733 if (parent->IsTransformed() && !f->Combines3DTransformWithAncestors()) {
9734 TransformReferenceBox refBox(parent);
9735 r = nsDisplayTransform::TransformRect(r, parent, refBox);
9736 }
9737 }
9738
9739 innerRect.UnionRect(innerRect, r);
9740 }
9741 }
9742
9743 // Keep this code in sync with GetOutlineInnerRect in nsCSSRendering.cpp.
9744 SetOrUpdateRectValuedProperty(aFrame, nsIFrame::OutlineInnerRectProperty(),
9745 innerRect);
9746 const nscoord offset = outline->mOutlineOffset.ToAppUnits();
9747 nsRect outerRect(innerRect);
9748 bool useOutlineAuto = false;
9749 if (StaticPrefs::layout_css_outline_style_auto_enabled()) {
9750 useOutlineAuto = outline->mOutlineStyle.IsAuto();
9751 if (MOZ_UNLIKELY(useOutlineAuto)) {
9752 nsPresContext* presContext = aFrame->PresContext();
9753 nsITheme* theme = presContext->Theme();
9754 if (theme->ThemeSupportsWidget(presContext, aFrame,
9755 StyleAppearance::FocusOutline)) {
9756 outerRect.Inflate(offset);
9757 theme->GetWidgetOverflow(presContext->DeviceContext(), aFrame,
9758 StyleAppearance::FocusOutline, &outerRect);
9759 } else {
9760 useOutlineAuto = false;
9761 }
9762 }
9763 }
9764 if (MOZ_LIKELY(!useOutlineAuto)) {
9765 nscoord width = outline->GetOutlineWidth();
9766 outerRect.Inflate(width + offset);
9767 }
9768
9769 nsRect& vo = aOverflowAreas.VisualOverflow();
9770 vo.UnionRectEdges(vo, innerRect.Union(outerRect));
9771 }
9772
FinishAndStoreOverflow(nsOverflowAreas & aOverflowAreas,nsSize aNewSize,nsSize * aOldSize,const nsStyleDisplay * aStyleDisplay)9773 bool nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas,
9774 nsSize aNewSize, nsSize* aOldSize,
9775 const nsStyleDisplay* aStyleDisplay) {
9776 MOZ_ASSERT(FrameMaintainsOverflow(),
9777 "Don't call - overflow rects not maintained on these SVG frames");
9778
9779 const nsStyleDisplay* disp = StyleDisplayWithOptionalParam(aStyleDisplay);
9780 bool hasTransform = IsTransformed(disp);
9781
9782 nsRect bounds(nsPoint(0, 0), aNewSize);
9783 // Store the passed in overflow area if we are a preserve-3d frame or we have
9784 // a transform, and it's not just the frame bounds.
9785 if (hasTransform || Combines3DTransformWithAncestors(disp)) {
9786 if (!aOverflowAreas.VisualOverflow().IsEqualEdges(bounds) ||
9787 !aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds)) {
9788 nsOverflowAreas* initial =
9789 GetProperty(nsIFrame::InitialOverflowProperty());
9790 if (!initial) {
9791 AddProperty(nsIFrame::InitialOverflowProperty(),
9792 new nsOverflowAreas(aOverflowAreas));
9793 } else if (initial != &aOverflowAreas) {
9794 *initial = aOverflowAreas;
9795 }
9796 } else {
9797 RemoveProperty(nsIFrame::InitialOverflowProperty());
9798 }
9799 #ifdef DEBUG
9800 SetProperty(nsIFrame::DebugInitialOverflowPropertyApplied(), true);
9801 #endif
9802 } else {
9803 #ifdef DEBUG
9804 RemoveProperty(nsIFrame::DebugInitialOverflowPropertyApplied());
9805 #endif
9806 }
9807
9808 nsSize oldSize = mRect.Size();
9809 bool sizeChanged = ((aOldSize ? *aOldSize : oldSize) != aNewSize);
9810
9811 // Our frame size may not have been computed and set yet, but code under
9812 // functions such as ComputeEffectsRect (which we're about to call) use the
9813 // values that are stored in our frame rect to compute their results. We
9814 // need the results from those functions to be based on the frame size that
9815 // we *will* have, so we temporarily set our frame size here before calling
9816 // those functions.
9817 //
9818 // XXX Someone should document here why we revert the frame size before we
9819 // return rather than just leaving it set.
9820 //
9821 // We pass false here to avoid invalidating display items for this temporary
9822 // change. We sometimes reflow frames multiple times, with the final size
9823 // being the same as the initial. The single call to SetSize after reflow is
9824 // done will take care of invalidating display items if the size has actually
9825 // changed.
9826 SetSize(aNewSize, false);
9827
9828 const bool applyOverflowClipping =
9829 nsFrame::ShouldApplyOverflowClipping(this, disp);
9830
9831 if (ChildrenHavePerspective(disp) && sizeChanged) {
9832 RecomputePerspectiveChildrenOverflow(this);
9833
9834 if (!applyOverflowClipping) {
9835 aOverflowAreas.SetAllTo(bounds);
9836 DebugOnly<bool> ok = ComputeCustomOverflow(aOverflowAreas);
9837
9838 // ComputeCustomOverflow() should not return false, when
9839 // FrameMaintainsOverflow() returns true.
9840 MOZ_ASSERT(ok, "FrameMaintainsOverflow() != ComputeCustomOverflow()");
9841
9842 UnionChildOverflow(aOverflowAreas);
9843 }
9844 }
9845
9846 // This is now called FinishAndStoreOverflow() instead of
9847 // StoreOverflow() because frame-generic ways of adding overflow
9848 // can happen here, e.g. CSS2 outline and native theme.
9849 // If the overflow area width or height is nscoord_MAX, then a
9850 // saturating union may have encounted an overflow, so the overflow may not
9851 // contain the frame border-box. Don't warn in that case.
9852 // Don't warn for SVG either, since SVG doesn't need the overflow area
9853 // to contain the frame bounds.
9854 NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
9855 DebugOnly<nsRect*> r = &aOverflowAreas.Overflow(otype);
9856 NS_ASSERTION(aNewSize.width == 0 || aNewSize.height == 0 ||
9857 r->width == nscoord_MAX || r->height == nscoord_MAX ||
9858 (mState & NS_FRAME_SVG_LAYOUT) ||
9859 r->Contains(nsRect(nsPoint(0, 0), aNewSize)),
9860 "Computed overflow area must contain frame bounds");
9861 }
9862
9863 // If we clip our children, clear accumulated overflow area. The
9864 // children are actually clipped to the padding-box, but since the
9865 // overflow area should include the entire border-box, just set it to
9866 // the border-box here.
9867 NS_ASSERTION((disp->mOverflowY == StyleOverflow::MozHiddenUnscrollable) ==
9868 (disp->mOverflowX == StyleOverflow::MozHiddenUnscrollable),
9869 "If one overflow is clip, the other should be too");
9870 if (applyOverflowClipping) {
9871 // The contents are actually clipped to the padding area
9872 aOverflowAreas.SetAllTo(bounds);
9873 }
9874
9875 // Overflow area must always include the frame's top-left and bottom-right,
9876 // even if the frame rect is empty (so we can scroll to those positions).
9877 // Pending a real fix for bug 426879, don't do this for inline frames
9878 // with zero width.
9879 // Do not do this for SVG either, since it will usually massively increase
9880 // the area unnecessarily.
9881 if ((aNewSize.width != 0 || !IsInlineFrame()) &&
9882 !(GetStateBits() & NS_FRAME_SVG_LAYOUT)) {
9883 NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
9884 nsRect& o = aOverflowAreas.Overflow(otype);
9885 o.UnionRectEdges(o, bounds);
9886 }
9887 }
9888
9889 // Note that StyleOverflow::MozHiddenUnscrollable doesn't clip the frame
9890 // background, so we add theme background overflow here so it's not clipped.
9891 if (!::IsXULBoxWrapped(this) && IsThemed(disp)) {
9892 nsRect r(bounds);
9893 nsPresContext* presContext = PresContext();
9894 if (presContext->Theme()->GetWidgetOverflow(presContext->DeviceContext(),
9895 this, disp->mAppearance, &r)) {
9896 nsRect& vo = aOverflowAreas.VisualOverflow();
9897 vo.UnionRectEdges(vo, r);
9898 }
9899 }
9900
9901 ComputeAndIncludeOutlineArea(this, aOverflowAreas, aNewSize);
9902
9903 // Nothing in here should affect scrollable overflow.
9904 aOverflowAreas.VisualOverflow() =
9905 ComputeEffectsRect(this, aOverflowAreas.VisualOverflow(), aNewSize);
9906
9907 // Absolute position clipping
9908 const nsStyleEffects* effects = StyleEffects();
9909 Maybe<nsRect> clipPropClipRect = GetClipPropClipRect(disp, effects, aNewSize);
9910 if (clipPropClipRect) {
9911 NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
9912 nsRect& o = aOverflowAreas.Overflow(otype);
9913 o.IntersectRect(o, *clipPropClipRect);
9914 }
9915 }
9916
9917 /* If we're transformed, transform the overflow rect by the current
9918 * transformation. */
9919 if (hasTransform) {
9920 SetProperty(nsIFrame::PreTransformOverflowAreasProperty(),
9921 new nsOverflowAreas(aOverflowAreas));
9922
9923 if (Combines3DTransformWithAncestors(disp)) {
9924 /* If we're a preserve-3d leaf frame, then our pre-transform overflow
9925 * should be correct. Our post-transform overflow is empty though, because
9926 * we only contribute to the overflow area of the preserve-3d root frame.
9927 * If we're an intermediate frame then the pre-transform overflow should
9928 * contain all our non-preserve-3d children, which is what we want. Again
9929 * we have no post-transform overflow.
9930 */
9931 aOverflowAreas.SetAllTo(nsRect());
9932 } else {
9933 TransformReferenceBox refBox(this);
9934 NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
9935 nsRect& o = aOverflowAreas.Overflow(otype);
9936 o = nsDisplayTransform::TransformRect(o, this, refBox);
9937 }
9938
9939 /* If we're the root of the 3d context, then we want to include the
9940 * overflow areas of all the participants. This won't have happened yet as
9941 * the code above set their overflow area to empty. Manually collect these
9942 * overflow areas now.
9943 */
9944 if (Extend3DContext(disp, effects)) {
9945 ComputePreserve3DChildrenOverflow(aOverflowAreas);
9946 }
9947 }
9948 } else {
9949 RemoveProperty(nsIFrame::PreTransformOverflowAreasProperty());
9950 }
9951
9952 /* Revert the size change in case some caller is depending on this. */
9953 SetSize(oldSize, false);
9954
9955 bool anyOverflowChanged;
9956 if (aOverflowAreas != nsOverflowAreas(bounds, bounds)) {
9957 anyOverflowChanged = SetOverflowAreas(aOverflowAreas);
9958 } else {
9959 anyOverflowChanged = ClearOverflowRects();
9960 }
9961
9962 if (anyOverflowChanged) {
9963 SVGObserverUtils::InvalidateDirectRenderingObservers(this);
9964 if (IsBlockFrameOrSubclass() &&
9965 TextOverflow::CanHaveOverflowMarkers(this)) {
9966 DiscardDisplayItems(this, [](nsDisplayItemBase* aItem) {
9967 return aItem->GetType() == DisplayItemType::TYPE_TEXT_OVERFLOW;
9968 });
9969 SchedulePaint(PAINT_DEFAULT);
9970 }
9971 }
9972 return anyOverflowChanged;
9973 }
9974
RecomputePerspectiveChildrenOverflow(const nsIFrame * aStartFrame)9975 void nsIFrame::RecomputePerspectiveChildrenOverflow(
9976 const nsIFrame* aStartFrame) {
9977 for (const auto& childList : ChildLists()) {
9978 for (nsIFrame* child : childList.mList) {
9979 if (!child->FrameMaintainsOverflow()) {
9980 continue; // frame does not maintain overflow rects
9981 }
9982 if (child->HasPerspective()) {
9983 nsOverflowAreas* overflow =
9984 child->GetProperty(nsIFrame::InitialOverflowProperty());
9985 nsRect bounds(nsPoint(0, 0), child->GetSize());
9986 if (overflow) {
9987 nsOverflowAreas overflowCopy = *overflow;
9988 child->FinishAndStoreOverflow(overflowCopy, bounds.Size());
9989 } else {
9990 nsOverflowAreas boundsOverflow;
9991 boundsOverflow.SetAllTo(bounds);
9992 child->FinishAndStoreOverflow(boundsOverflow, bounds.Size());
9993 }
9994 } else if (child->GetContainingBlock(SKIP_SCROLLED_FRAME) ==
9995 aStartFrame) {
9996 // If a frame is using perspective, then the size used to compute
9997 // perspective-origin is the size of the frame belonging to its parent
9998 // style. We must find any descendant frames using our size
9999 // (by recursing into frames that have the same containing block)
10000 // to update their overflow rects too.
10001 child->RecomputePerspectiveChildrenOverflow(aStartFrame);
10002 }
10003 }
10004 }
10005 }
10006
ComputePreserve3DChildrenOverflow(nsOverflowAreas & aOverflowAreas)10007 void nsIFrame::ComputePreserve3DChildrenOverflow(
10008 nsOverflowAreas& aOverflowAreas) {
10009 // Find all descendants that participate in the 3d context, and include their
10010 // overflow. These descendants have an empty overflow, so won't have been
10011 // included in the normal overflow calculation. Any children that don't
10012 // participate have normal overflow, so will have been included already.
10013
10014 nsRect childVisual;
10015 nsRect childScrollable;
10016 for (const auto& childList : ChildLists()) {
10017 for (nsIFrame* child : childList.mList) {
10018 // If this child participates in the 3d context, then take the
10019 // pre-transform region (which contains all descendants that aren't
10020 // participating in the 3d context) and transform it into the 3d context
10021 // root coordinate space.
10022 const nsStyleDisplay* childDisp = child->StyleDisplay();
10023 if (child->Combines3DTransformWithAncestors(childDisp)) {
10024 nsOverflowAreas childOverflow = child->GetOverflowAreasRelativeToSelf();
10025 TransformReferenceBox refBox(child);
10026 NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
10027 nsRect& o = childOverflow.Overflow(otype);
10028 o = nsDisplayTransform::TransformRect(o, child, refBox);
10029 }
10030
10031 aOverflowAreas.UnionWith(childOverflow);
10032
10033 // If this child also extends the 3d context, then recurse into it
10034 // looking for more participants.
10035 if (child->Extend3DContext(childDisp, child->StyleEffects())) {
10036 child->ComputePreserve3DChildrenOverflow(aOverflowAreas);
10037 }
10038 }
10039 }
10040 }
10041 }
10042
ZIndex() const10043 int32_t nsIFrame::ZIndex() const {
10044 if (!IsAbsPosContainingBlock() && !IsFlexOrGridItem()) {
10045 return 0;
10046 }
10047
10048 const nsStylePosition* position = StylePosition();
10049 if (position->mZIndex.IsInteger()) {
10050 return position->mZIndex.AsInteger();
10051 }
10052 MOZ_ASSERT(position->mZIndex.IsAuto());
10053 // sort the auto and 0 elements together
10054 return 0;
10055 }
10056
IsScrollAnchor(ScrollAnchorContainer ** aOutContainer)10057 bool nsIFrame::IsScrollAnchor(ScrollAnchorContainer** aOutContainer) {
10058 if (!mInScrollAnchorChain) {
10059 return false;
10060 }
10061
10062 nsIFrame* f = this;
10063
10064 // FIXME(emilio, bug 1629280): We should find a non-null anchor if we have the
10065 // flag set, but bug 1629280 makes it so that we cannot really assert it /
10066 // make this just a `while (true)`, and uncomment the below assertion.
10067 while (auto* container = ScrollAnchorContainer::FindFor(f)) {
10068 // MOZ_ASSERT(f->IsInScrollAnchorChain());
10069 if (nsIFrame* anchor = container->AnchorNode()) {
10070 if (anchor != this) {
10071 return false;
10072 }
10073 if (aOutContainer) {
10074 *aOutContainer = container;
10075 }
10076 return true;
10077 }
10078
10079 f = container->Frame();
10080 }
10081
10082 return false;
10083 }
10084
IsInScrollAnchorChain() const10085 bool nsIFrame::IsInScrollAnchorChain() const { return mInScrollAnchorChain; }
10086
SetInScrollAnchorChain(bool aInChain)10087 void nsIFrame::SetInScrollAnchorChain(bool aInChain) {
10088 mInScrollAnchorChain = aInChain;
10089 }
10090
GetDepthInFrameTree() const10091 uint32_t nsIFrame::GetDepthInFrameTree() const {
10092 uint32_t result = 0;
10093 for (nsContainerFrame* ancestor = GetParent(); ancestor;
10094 ancestor = ancestor->GetParent()) {
10095 result++;
10096 }
10097 return result;
10098 }
10099
ConsiderChildOverflow(nsOverflowAreas & aOverflowAreas,nsIFrame * aChildFrame)10100 void nsContainerFrame::ConsiderChildOverflow(nsOverflowAreas& aOverflowAreas,
10101 nsIFrame* aChildFrame) {
10102 if (StyleDisplay()->IsContainLayout() &&
10103 IsFrameOfType(eSupportsContainLayoutAndPaint)) {
10104 // If we have layout containment and are not a non-atomic, inline-level
10105 // principal box, we should only consider our child's visual (ink) overflow,
10106 // leaving the scrollable regions of the parent unaffected.
10107 // Note: scrollable overflow is a subset of visual overflow,
10108 // so this has the same affect as unioning the child's visual and
10109 // scrollable overflow with the parent's visual overflow.
10110 nsRect childVisual = aChildFrame->GetVisualOverflowRect();
10111 nsOverflowAreas combined = nsOverflowAreas(childVisual, nsRect());
10112 aOverflowAreas.UnionWith(combined + aChildFrame->GetPosition());
10113 } else {
10114 aOverflowAreas.UnionWith(aChildFrame->GetOverflowAreas() +
10115 aChildFrame->GetPosition());
10116 }
10117 }
10118
ShouldAvoidBreakInside(const ReflowInput & aReflowInput) const10119 bool nsContainerFrame::ShouldAvoidBreakInside(
10120 const ReflowInput& aReflowInput) const {
10121 const auto* disp = StyleDisplay();
10122 return !aReflowInput.mFlags.mIsTopOfPage &&
10123 StyleBreakWithin::Avoid == disp->mBreakInside &&
10124 !(HasAnyStateBits(NS_FRAME_OUT_OF_FLOW) &&
10125 IsAbsolutelyPositioned(disp)) &&
10126 !GetPrevInFlow();
10127 }
10128
10129 /**
10130 * This function takes a frame that is part of a block-in-inline split,
10131 * and _if_ that frame is an anonymous block created by an ib split it
10132 * returns the block's preceding inline. This is needed because the
10133 * split inline's style is the parent of the anonymous block's style.
10134 *
10135 * If aFrame is not an anonymous block, null is returned.
10136 */
GetIBSplitSiblingForAnonymousBlock(const nsIFrame * aFrame)10137 static nsIFrame* GetIBSplitSiblingForAnonymousBlock(const nsIFrame* aFrame) {
10138 MOZ_ASSERT(aFrame, "Must have a non-null frame!");
10139 NS_ASSERTION(aFrame->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT,
10140 "GetIBSplitSibling should only be called on ib-split frames");
10141
10142 if (aFrame->Style()->GetPseudoType() !=
10143 PseudoStyleType::mozBlockInsideInlineWrapper) {
10144 // it's not an anonymous block
10145 return nullptr;
10146 }
10147
10148 // Find the first continuation of the frame. (Ugh. This ends up
10149 // being O(N^2) when it is called O(N) times.)
10150 aFrame = aFrame->FirstContinuation();
10151
10152 /*
10153 * Now look up the nsGkAtoms::IBSplitPrevSibling
10154 * property.
10155 */
10156 nsIFrame* ibSplitSibling =
10157 aFrame->GetProperty(nsIFrame::IBSplitPrevSibling());
10158 NS_ASSERTION(ibSplitSibling, "Broken frame tree?");
10159 return ibSplitSibling;
10160 }
10161
10162 /**
10163 * Get the parent, corrected for the mangled frame tree resulting from
10164 * having a block within an inline. The result only differs from the
10165 * result of |GetParent| when |GetParent| returns an anonymous block
10166 * that was created for an element that was 'display: inline' because
10167 * that element contained a block.
10168 *
10169 * Also skip anonymous scrolled-content parents; inherit directly from the
10170 * outer scroll frame.
10171 *
10172 * Also skip NAC parents if the child frame is NAC.
10173 */
GetCorrectedParent(const nsIFrame * aFrame)10174 static nsIFrame* GetCorrectedParent(const nsIFrame* aFrame) {
10175 nsIFrame* parent = aFrame->GetParent();
10176 if (!parent) {
10177 return nullptr;
10178 }
10179
10180 // For a table caption we want the _inner_ table frame (unless it's anonymous)
10181 // as the style parent.
10182 if (aFrame->IsTableCaption()) {
10183 nsIFrame* innerTable = parent->PrincipalChildList().FirstChild();
10184 if (!innerTable->Style()->IsAnonBox()) {
10185 return innerTable;
10186 }
10187 }
10188
10189 // Table wrappers are always anon boxes; if we're in here for an outer
10190 // table, that actually means its the _inner_ table that wants to
10191 // know its parent. So get the pseudo of the inner in that case.
10192 auto pseudo = aFrame->Style()->GetPseudoType();
10193 if (pseudo == PseudoStyleType::tableWrapper) {
10194 pseudo =
10195 aFrame->PrincipalChildList().FirstChild()->Style()->GetPseudoType();
10196 }
10197
10198 // Prevent a NAC pseudo-element from inheriting from its NAC parent, and
10199 // inherit from the NAC generator element instead.
10200 if (pseudo != PseudoStyleType::NotPseudo) {
10201 MOZ_ASSERT(aFrame->GetContent());
10202 Element* element = Element::FromNode(aFrame->GetContent());
10203 // Make sure to avoid doing the fixup for non-element-backed pseudos like
10204 // ::first-line and such.
10205 if (element && !element->IsRootOfNativeAnonymousSubtree() &&
10206 element->GetPseudoElementType() == aFrame->Style()->GetPseudoType()) {
10207 while (parent->GetContent() &&
10208 !parent->GetContent()->IsRootOfNativeAnonymousSubtree()) {
10209 parent = parent->GetInFlowParent();
10210 }
10211 parent = parent->GetInFlowParent();
10212 }
10213 }
10214
10215 return nsFrame::CorrectStyleParentFrame(parent, pseudo);
10216 }
10217
10218 /* static */
CorrectStyleParentFrame(nsIFrame * aProspectiveParent,PseudoStyleType aChildPseudo)10219 nsIFrame* nsFrame::CorrectStyleParentFrame(nsIFrame* aProspectiveParent,
10220 PseudoStyleType aChildPseudo) {
10221 MOZ_ASSERT(aProspectiveParent, "Must have a prospective parent");
10222
10223 if (aChildPseudo != PseudoStyleType::NotPseudo) {
10224 // Non-inheriting anon boxes have no style parent frame at all.
10225 if (PseudoStyle::IsNonInheritingAnonBox(aChildPseudo)) {
10226 return nullptr;
10227 }
10228
10229 // Other anon boxes are parented to their actual parent already, except
10230 // for non-elements. Those should not be treated as an anon box.
10231 if (PseudoStyle::IsAnonBox(aChildPseudo) &&
10232 !nsCSSAnonBoxes::IsNonElement(aChildPseudo)) {
10233 NS_ASSERTION(aChildPseudo != PseudoStyleType::mozBlockInsideInlineWrapper,
10234 "Should have dealt with kids that have "
10235 "NS_FRAME_PART_OF_IBSPLIT elsewhere");
10236 return aProspectiveParent;
10237 }
10238 }
10239
10240 // Otherwise, walk up out of all anon boxes. For placeholder frames, walk out
10241 // of all pseudo-elements as well. Otherwise ReparentComputedStyle could
10242 // cause style data to be out of sync with the frame tree.
10243 nsIFrame* parent = aProspectiveParent;
10244 do {
10245 if (parent->GetStateBits() & NS_FRAME_PART_OF_IBSPLIT) {
10246 nsIFrame* sibling = GetIBSplitSiblingForAnonymousBlock(parent);
10247
10248 if (sibling) {
10249 // |parent| was a block in an {ib} split; use the inline as
10250 // |the style parent.
10251 parent = sibling;
10252 }
10253 }
10254
10255 if (!parent->Style()->IsPseudoOrAnonBox()) {
10256 return parent;
10257 }
10258
10259 if (!parent->Style()->IsAnonBox() && aChildPseudo != PseudoStyleType::MAX) {
10260 // nsPlaceholderFrame passes in PseudoStyleType::MAX for
10261 // aChildPseudo (even though that's not a valid pseudo-type) just to
10262 // trigger this behavior of walking up to the nearest non-pseudo
10263 // ancestor.
10264 return parent;
10265 }
10266
10267 parent = parent->GetInFlowParent();
10268 } while (parent);
10269
10270 if (aProspectiveParent->Style()->GetPseudoType() ==
10271 PseudoStyleType::viewportScroll) {
10272 // aProspectiveParent is the scrollframe for a viewport
10273 // and the kids are the anonymous scrollbars
10274 return aProspectiveParent;
10275 }
10276
10277 // We can get here if the root element is absolutely positioned.
10278 // We can't test for this very accurately, but it can only happen
10279 // when the prospective parent is a canvas frame.
10280 NS_ASSERTION(aProspectiveParent->IsCanvasFrame(),
10281 "Should have found a parent before this");
10282 return nullptr;
10283 }
10284
DoGetParentComputedStyle(nsIFrame ** aProviderFrame) const10285 ComputedStyle* nsIFrame::DoGetParentComputedStyle(
10286 nsIFrame** aProviderFrame) const {
10287 *aProviderFrame = nullptr;
10288
10289 // Handle display:contents and the root frame, when there's no parent frame
10290 // to inherit from.
10291 if (MOZ_LIKELY(mContent)) {
10292 Element* parentElement = mContent->GetFlattenedTreeParentElement();
10293 if (MOZ_LIKELY(parentElement)) {
10294 auto pseudo = Style()->GetPseudoType();
10295 if (pseudo == PseudoStyleType::NotPseudo || !mContent->IsElement() ||
10296 (!PseudoStyle::IsAnonBox(pseudo) &&
10297 // Ensure that we don't return the display:contents style
10298 // of the parent content for pseudos that have the same content
10299 // as their primary frame (like -moz-list-bullets do):
10300 IsPrimaryFrame()) ||
10301 /* if next is true then it's really a request for the table frame's
10302 parent context, see nsTable[Outer]Frame::GetParentComputedStyle. */
10303 pseudo == PseudoStyleType::tableWrapper) {
10304 // In some edge cases involving display: contents, we may end up here
10305 // for something that's pending to be reframed. In this case we return
10306 // the wrong style from here (because we've already lost track of it!),
10307 // but it's not a big deal as we're going to be reframed anyway.
10308 if (MOZ_LIKELY(parentElement->HasServoData()) &&
10309 Servo_Element_IsDisplayContents(parentElement)) {
10310 RefPtr<ComputedStyle> style =
10311 ServoStyleSet::ResolveServoStyle(*parentElement);
10312 // NOTE(emilio): we return a weak reference because the element also
10313 // holds the style context alive. This is a bit silly (we could've
10314 // returned a weak ref directly), but it's probably not worth
10315 // optimizing, given this function has just one caller which is rare,
10316 // and this path is rare itself.
10317 return style;
10318 }
10319 }
10320 } else {
10321 if (Style()->GetPseudoType() == PseudoStyleType::NotPseudo) {
10322 // We're a frame for the root. We have no style parent.
10323 return nullptr;
10324 }
10325 }
10326 }
10327
10328 if (!(mState & NS_FRAME_OUT_OF_FLOW)) {
10329 /*
10330 * If this frame is an anonymous block created when an inline with a block
10331 * inside it got split, then the parent style is on its preceding inline. We
10332 * can get to it using GetIBSplitSiblingForAnonymousBlock.
10333 */
10334 if (mState & NS_FRAME_PART_OF_IBSPLIT) {
10335 nsIFrame* ibSplitSibling = GetIBSplitSiblingForAnonymousBlock(this);
10336 if (ibSplitSibling) {
10337 return (*aProviderFrame = ibSplitSibling)->Style();
10338 }
10339 }
10340
10341 // If this frame is one of the blocks that split an inline, we must
10342 // return the "special" inline parent, i.e., the parent that this
10343 // frame would have if we didn't mangle the frame structure.
10344 *aProviderFrame = GetCorrectedParent(this);
10345 return *aProviderFrame ? (*aProviderFrame)->Style() : nullptr;
10346 }
10347
10348 // We're an out-of-flow frame. For out-of-flow frames, we must
10349 // resolve underneath the placeholder's parent. The placeholder is
10350 // reached from the first-in-flow.
10351 nsPlaceholderFrame* placeholder = FirstInFlow()->GetPlaceholderFrame();
10352 if (!placeholder) {
10353 MOZ_ASSERT_UNREACHABLE("no placeholder frame for out-of-flow frame");
10354 *aProviderFrame = GetCorrectedParent(this);
10355 return *aProviderFrame ? (*aProviderFrame)->Style() : nullptr;
10356 }
10357 return placeholder->GetParentComputedStyleForOutOfFlow(aProviderFrame);
10358 }
10359
GetLastLeaf(nsPresContext * aPresContext,nsIFrame ** aFrame)10360 void nsFrame::GetLastLeaf(nsPresContext* aPresContext, nsIFrame** aFrame) {
10361 if (!aFrame || !*aFrame) return;
10362 nsIFrame* child = *aFrame;
10363 // if we are a block frame then go for the last line of 'this'
10364 while (1) {
10365 child = child->PrincipalChildList().FirstChild();
10366 if (!child) return; // nothing to do
10367 nsIFrame* siblingFrame;
10368 nsIContent* content;
10369 // ignore anonymous elements, e.g. mozTableAdd* mozTableRemove*
10370 // see bug 278197 comment #12 #13 for details
10371 while ((siblingFrame = child->GetNextSibling()) &&
10372 (content = siblingFrame->GetContent()) &&
10373 !content->IsRootOfNativeAnonymousSubtree())
10374 child = siblingFrame;
10375 *aFrame = child;
10376 }
10377 }
10378
GetFirstLeaf(nsPresContext * aPresContext,nsIFrame ** aFrame)10379 void nsFrame::GetFirstLeaf(nsPresContext* aPresContext, nsIFrame** aFrame) {
10380 if (!aFrame || !*aFrame) return;
10381 nsIFrame* child = *aFrame;
10382 while (1) {
10383 child = child->PrincipalChildList().FirstChild();
10384 if (!child) return; // nothing to do
10385 *aFrame = child;
10386 }
10387 }
10388
10389 /* virtual */
IsFocusable(int32_t * aTabIndex,bool aWithMouse)10390 bool nsIFrame::IsFocusable(int32_t* aTabIndex, bool aWithMouse) {
10391 int32_t tabIndex = -1;
10392 if (aTabIndex) {
10393 *aTabIndex = -1; // Default for early return is not focusable
10394 }
10395 bool isFocusable = false;
10396
10397 if (mContent && mContent->IsElement() && IsVisibleConsideringAncestors() &&
10398 Style()->GetPseudoType() != PseudoStyleType::anonymousFlexItem &&
10399 Style()->GetPseudoType() != PseudoStyleType::anonymousGridItem) {
10400 const nsStyleUI* ui = StyleUI();
10401 if (ui->mUserFocus != StyleUserFocus::Ignore &&
10402 ui->mUserFocus != StyleUserFocus::None) {
10403 // Pass in default tabindex of -1 for nonfocusable and 0 for focusable
10404 tabIndex = 0;
10405 }
10406 isFocusable = mContent->IsFocusable(&tabIndex, aWithMouse);
10407 if (!isFocusable && !aWithMouse && IsScrollFrame() &&
10408 mContent->IsHTMLElement() &&
10409 !mContent->IsRootOfNativeAnonymousSubtree() && mContent->GetParent() &&
10410 !mContent->AsElement()->HasAttr(kNameSpaceID_None,
10411 nsGkAtoms::tabindex)) {
10412 // Elements with scrollable view are focusable with script & tabbable
10413 // Otherwise you couldn't scroll them with keyboard, which is
10414 // an accessibility issue (e.g. Section 508 rules)
10415 // However, we don't make them to be focusable with the mouse,
10416 // because the extra focus outlines are considered unnecessarily ugly.
10417 // When clicked on, the selection position within the element
10418 // will be enough to make them keyboard scrollable.
10419 nsIScrollableFrame* scrollFrame = do_QueryFrame(this);
10420 if (scrollFrame && !scrollFrame->IsForTextControlWithNoScrollbars() &&
10421 !scrollFrame->GetScrollStyles().IsHiddenInBothDirections() &&
10422 !scrollFrame->GetScrollRange().IsEqualEdges(nsRect(0, 0, 0, 0))) {
10423 // Scroll bars will be used for overflow
10424 isFocusable = true;
10425 tabIndex = 0;
10426 }
10427 }
10428 }
10429
10430 if (aTabIndex) {
10431 *aTabIndex = tabIndex;
10432 }
10433 return isFocusable;
10434 }
10435
10436 /**
10437 * @return true if this text frame ends with a newline character which is
10438 * treated as preformatted. It should return false if this is not a text frame.
10439 */
HasSignificantTerminalNewline() const10440 bool nsIFrame::HasSignificantTerminalNewline() const { return false; }
10441
ConvertSVGDominantBaselineToVerticalAlign(StyleDominantBaseline aDominantBaseline)10442 static StyleVerticalAlignKeyword ConvertSVGDominantBaselineToVerticalAlign(
10443 StyleDominantBaseline aDominantBaseline) {
10444 // Most of these are approximate mappings.
10445 switch (aDominantBaseline) {
10446 case StyleDominantBaseline::Hanging:
10447 case StyleDominantBaseline::TextBeforeEdge:
10448 return StyleVerticalAlignKeyword::TextTop;
10449 case StyleDominantBaseline::TextAfterEdge:
10450 case StyleDominantBaseline::Ideographic:
10451 return StyleVerticalAlignKeyword::TextBottom;
10452 case StyleDominantBaseline::Central:
10453 case StyleDominantBaseline::Middle:
10454 case StyleDominantBaseline::Mathematical:
10455 return StyleVerticalAlignKeyword::Middle;
10456 case StyleDominantBaseline::Auto:
10457 case StyleDominantBaseline::Alphabetic:
10458 return StyleVerticalAlignKeyword::Baseline;
10459 default:
10460 MOZ_ASSERT_UNREACHABLE("unexpected aDominantBaseline value");
10461 return StyleVerticalAlignKeyword::Baseline;
10462 }
10463 }
10464
VerticalAlignEnum() const10465 Maybe<StyleVerticalAlignKeyword> nsIFrame::VerticalAlignEnum() const {
10466 if (nsSVGUtils::IsInSVGTextSubtree(this)) {
10467 StyleDominantBaseline dominantBaseline = StyleSVG()->mDominantBaseline;
10468 return Some(ConvertSVGDominantBaselineToVerticalAlign(dominantBaseline));
10469 }
10470
10471 const auto& verticalAlign = StyleDisplay()->mVerticalAlign;
10472 if (verticalAlign.IsKeyword()) {
10473 return Some(verticalAlign.AsKeyword());
10474 }
10475
10476 return Nothing();
10477 }
10478
10479 NS_IMETHODIMP
RefreshSizeCache(nsBoxLayoutState & aState)10480 nsIFrame::RefreshSizeCache(nsBoxLayoutState& aState) {
10481 // XXXbz this comment needs some rewriting to make sense in the
10482 // post-reflow-branch world.
10483
10484 // Ok we need to compute our minimum, preferred, and maximum sizes.
10485 // 1) Maximum size. This is easy. Its infinite unless it is overloaded by CSS.
10486 // 2) Preferred size. This is a little harder. This is the size the
10487 // block would be if it were laid out on an infinite canvas. So we can
10488 // get this by reflowing the block with and INTRINSIC width and height. We
10489 // can also do a nice optimization for incremental reflow. If the reflow is
10490 // incremental then we can pass a flag to have the block compute the
10491 // preferred width for us! Preferred height can just be the minimum height;
10492 // 3) Minimum size. This is a toughy. We can pass the block a flag asking for
10493 // the max element size. That would give us the width. Unfortunately you
10494 // can only ask for a maxElementSize during an incremental reflow. So on
10495 // other reflows we will just have to use 0. The min height on the other
10496 // hand is fairly easy we need to get the largest line height. This can be
10497 // done with the line iterator.
10498
10499 // if we do have a rendering context
10500 gfxContext* rendContext = aState.GetRenderingContext();
10501 if (rendContext) {
10502 nsPresContext* presContext = aState.PresContext();
10503
10504 // If we don't have any HTML constraints and it's a resize, then nothing in
10505 // the block could have changed, so no refresh is necessary.
10506 nsBoxLayoutMetrics* metrics = BoxMetrics();
10507 if (!XULNeedsRecalc(metrics->mBlockPrefSize)) {
10508 return NS_OK;
10509 }
10510
10511 // the rect we plan to size to.
10512 nsRect rect = GetRect();
10513
10514 nsMargin bp(0, 0, 0, 0);
10515 GetXULBorderAndPadding(bp);
10516
10517 {
10518 // If we're a container for font size inflation, then shrink
10519 // wrapping inside of us should not apply font size inflation.
10520 AutoMaybeDisableFontInflation an(this);
10521
10522 metrics->mBlockPrefSize.width =
10523 GetPrefISize(rendContext) + bp.LeftRight();
10524 metrics->mBlockMinSize.width = GetMinISize(rendContext) + bp.LeftRight();
10525 }
10526
10527 // do the nasty.
10528 const WritingMode wm = aState.OuterReflowInput()
10529 ? aState.OuterReflowInput()->GetWritingMode()
10530 : GetWritingMode();
10531 ReflowOutput desiredSize(wm);
10532 BoxReflow(aState, presContext, desiredSize, rendContext, rect.x, rect.y,
10533 metrics->mBlockPrefSize.width, NS_UNCONSTRAINEDSIZE);
10534
10535 metrics->mBlockMinSize.height = 0;
10536 // ok we need the max ascent of the items on the line. So to do this
10537 // ask the block for its line iterator. Get the max ascent.
10538 nsAutoLineIterator lines = GetLineIterator();
10539 if (lines) {
10540 metrics->mBlockMinSize.height = 0;
10541 int count = 0;
10542 nsIFrame* firstFrame = nullptr;
10543 int32_t framesOnLine;
10544 nsRect lineBounds;
10545
10546 do {
10547 lines->GetLine(count, &firstFrame, &framesOnLine, lineBounds);
10548
10549 if (lineBounds.height > metrics->mBlockMinSize.height)
10550 metrics->mBlockMinSize.height = lineBounds.height;
10551
10552 count++;
10553 } while (firstFrame);
10554 } else {
10555 metrics->mBlockMinSize.height = desiredSize.Height();
10556 }
10557
10558 metrics->mBlockPrefSize.height = metrics->mBlockMinSize.height;
10559
10560 if (desiredSize.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE) {
10561 if (!nsLayoutUtils::GetFirstLineBaseline(wm, this,
10562 &metrics->mBlockAscent))
10563 metrics->mBlockAscent = GetLogicalBaseline(wm);
10564 } else {
10565 metrics->mBlockAscent = desiredSize.BlockStartAscent();
10566 }
10567
10568 #ifdef DEBUG_adaptor
10569 printf("min=(%d,%d), pref=(%d,%d), ascent=%d\n",
10570 metrics->mBlockMinSize.width, metrics->mBlockMinSize.height,
10571 metrics->mBlockPrefSize.width, metrics->mBlockPrefSize.height,
10572 metrics->mBlockAscent);
10573 #endif
10574 }
10575
10576 return NS_OK;
10577 }
10578
GetXULPrefSize(nsBoxLayoutState & aState)10579 nsSize nsIFrame::GetXULPrefSize(nsBoxLayoutState& aState) {
10580 nsSize size(0, 0);
10581 DISPLAY_PREF_SIZE(this, size);
10582 // If the size is cached, and there are no HTML constraints that we might
10583 // be depending on, then we just return the cached size.
10584 nsBoxLayoutMetrics* metrics = BoxMetrics();
10585 if (!XULNeedsRecalc(metrics->mPrefSize)) {
10586 size = metrics->mPrefSize;
10587 return size;
10588 }
10589
10590 if (IsXULCollapsed()) return size;
10591
10592 // get our size in CSS.
10593 bool widthSet, heightSet;
10594 bool completelyRedefined =
10595 nsIFrame::AddXULPrefSize(this, size, widthSet, heightSet);
10596
10597 // Refresh our caches with new sizes.
10598 if (!completelyRedefined) {
10599 RefreshSizeCache(aState);
10600 nsSize blockSize = metrics->mBlockPrefSize;
10601
10602 // notice we don't need to add our borders or padding
10603 // in. That's because the block did it for us.
10604 if (!widthSet) size.width = blockSize.width;
10605 if (!heightSet) size.height = blockSize.height;
10606 }
10607
10608 metrics->mPrefSize = size;
10609 return size;
10610 }
10611
GetXULMinSize(nsBoxLayoutState & aState)10612 nsSize nsIFrame::GetXULMinSize(nsBoxLayoutState& aState) {
10613 nsSize size(0, 0);
10614 DISPLAY_MIN_SIZE(this, size);
10615 // Don't use the cache if we have HTMLReflowInput constraints --- they might
10616 // have changed
10617 nsBoxLayoutMetrics* metrics = BoxMetrics();
10618 if (!XULNeedsRecalc(metrics->mMinSize)) {
10619 size = metrics->mMinSize;
10620 return size;
10621 }
10622
10623 if (IsXULCollapsed()) return size;
10624
10625 // get our size in CSS.
10626 bool widthSet, heightSet;
10627 bool completelyRedefined =
10628 nsIFrame::AddXULMinSize(this, size, widthSet, heightSet);
10629
10630 // Refresh our caches with new sizes.
10631 if (!completelyRedefined) {
10632 RefreshSizeCache(aState);
10633 nsSize blockSize = metrics->mBlockMinSize;
10634
10635 if (!widthSet) size.width = blockSize.width;
10636 if (!heightSet) size.height = blockSize.height;
10637 }
10638
10639 metrics->mMinSize = size;
10640 return size;
10641 }
10642
GetXULMaxSize(nsBoxLayoutState & aState)10643 nsSize nsIFrame::GetXULMaxSize(nsBoxLayoutState& aState) {
10644 nsSize size(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
10645 DISPLAY_MAX_SIZE(this, size);
10646 // Don't use the cache if we have HTMLReflowInput constraints --- they might
10647 // have changed
10648 nsBoxLayoutMetrics* metrics = BoxMetrics();
10649 if (!XULNeedsRecalc(metrics->mMaxSize)) {
10650 size = metrics->mMaxSize;
10651 return size;
10652 }
10653
10654 if (IsXULCollapsed()) return size;
10655
10656 size = nsIFrame::GetUncachedXULMaxSize(aState);
10657 metrics->mMaxSize = size;
10658
10659 return size;
10660 }
10661
GetXULFlex()10662 nscoord nsIFrame::GetXULFlex() {
10663 nsBoxLayoutMetrics* metrics = BoxMetrics();
10664 if (XULNeedsRecalc(metrics->mFlex)) {
10665 nsIFrame::AddXULFlex(this, metrics->mFlex);
10666 }
10667
10668 return metrics->mFlex;
10669 }
10670
GetXULBoxAscent(nsBoxLayoutState & aState)10671 nscoord nsIFrame::GetXULBoxAscent(nsBoxLayoutState& aState) {
10672 nsBoxLayoutMetrics* metrics = BoxMetrics();
10673 if (!XULNeedsRecalc(metrics->mAscent)) {
10674 return metrics->mAscent;
10675 }
10676
10677 if (IsXULCollapsed()) {
10678 metrics->mAscent = 0;
10679 } else {
10680 // Refresh our caches with new sizes.
10681 RefreshSizeCache(aState);
10682 metrics->mAscent = metrics->mBlockAscent;
10683 }
10684
10685 return metrics->mAscent;
10686 }
10687
DoXULLayout(nsBoxLayoutState & aState)10688 nsresult nsIFrame::DoXULLayout(nsBoxLayoutState& aState) {
10689 nsRect ourRect(mRect);
10690
10691 gfxContext* rendContext = aState.GetRenderingContext();
10692 nsPresContext* presContext = aState.PresContext();
10693 WritingMode ourWM = GetWritingMode();
10694 const WritingMode outerWM = aState.OuterReflowInput()
10695 ? aState.OuterReflowInput()->GetWritingMode()
10696 : ourWM;
10697 ReflowOutput desiredSize(outerWM);
10698 LogicalSize ourSize = GetLogicalSize(outerWM);
10699
10700 if (rendContext) {
10701 BoxReflow(aState, presContext, desiredSize, rendContext, ourRect.x,
10702 ourRect.y, ourRect.width, ourRect.height);
10703
10704 if (IsXULCollapsed()) {
10705 SetSize(nsSize(0, 0));
10706 } else {
10707 // if our child needs to be bigger. This might happend with
10708 // wrapping text. There is no way to predict its height until we
10709 // reflow it. Now that we know the height reshuffle upward.
10710 if (desiredSize.ISize(outerWM) > ourSize.ISize(outerWM) ||
10711 desiredSize.BSize(outerWM) > ourSize.BSize(outerWM)) {
10712 #ifdef DEBUG_GROW
10713 XULDumpBox(stdout);
10714 printf(" GREW from (%d,%d) -> (%d,%d)\n", ourSize.ISize(outerWM),
10715 ourSize.BSize(outerWM), desiredSize.ISize(outerWM),
10716 desiredSize.BSize(outerWM));
10717 #endif
10718
10719 if (desiredSize.ISize(outerWM) > ourSize.ISize(outerWM)) {
10720 ourSize.ISize(outerWM) = desiredSize.ISize(outerWM);
10721 }
10722
10723 if (desiredSize.BSize(outerWM) > ourSize.BSize(outerWM)) {
10724 ourSize.BSize(outerWM) = desiredSize.BSize(outerWM);
10725 }
10726 }
10727
10728 // ensure our size is what we think is should be. Someone could have
10729 // reset the frame to be smaller or something dumb like that.
10730 SetSize(ourSize.ConvertTo(ourWM, outerWM));
10731 }
10732 }
10733
10734 // Should we do this if IsXULCollapsed() is true?
10735 LogicalSize size(GetLogicalSize(outerWM));
10736 desiredSize.ISize(outerWM) = size.ISize(outerWM);
10737 desiredSize.BSize(outerWM) = size.BSize(outerWM);
10738 desiredSize.UnionOverflowAreasWithDesiredBounds();
10739
10740 if (HasAbsolutelyPositionedChildren()) {
10741 // Set up a |reflowInput| to pass into ReflowAbsoluteFrames
10742 ReflowInput reflowInput(aState.PresContext(), this,
10743 aState.GetRenderingContext(),
10744 LogicalSize(ourWM, ISize(), NS_UNCONSTRAINEDSIZE),
10745 ReflowInput::DUMMY_PARENT_REFLOW_INPUT);
10746
10747 AddStateBits(NS_FRAME_IN_REFLOW);
10748 // Set up a |reflowStatus| to pass into ReflowAbsoluteFrames
10749 // (just a dummy value; hopefully that's OK)
10750 nsReflowStatus reflowStatus;
10751 ReflowAbsoluteFrames(aState.PresContext(), desiredSize, reflowInput,
10752 reflowStatus);
10753 RemoveStateBits(NS_FRAME_IN_REFLOW);
10754 }
10755
10756 nsSize oldSize(ourRect.Size());
10757 FinishAndStoreOverflow(desiredSize.mOverflowAreas,
10758 size.GetPhysicalSize(outerWM), &oldSize);
10759
10760 SyncXULLayout(aState);
10761
10762 return NS_OK;
10763 }
10764
BoxReflow(nsBoxLayoutState & aState,nsPresContext * aPresContext,ReflowOutput & aDesiredSize,gfxContext * aRenderingContext,nscoord aX,nscoord aY,nscoord aWidth,nscoord aHeight,bool aMoveFrame)10765 void nsIFrame::BoxReflow(nsBoxLayoutState& aState, nsPresContext* aPresContext,
10766 ReflowOutput& aDesiredSize,
10767 gfxContext* aRenderingContext, nscoord aX, nscoord aY,
10768 nscoord aWidth, nscoord aHeight, bool aMoveFrame) {
10769 DO_GLOBAL_REFLOW_COUNT("nsBoxToBlockAdaptor");
10770
10771 #ifdef DEBUG_REFLOW
10772 nsAdaptorAddIndents();
10773 printf("Reflowing: ");
10774 mFrame->ListTag(stdout);
10775 printf("\n");
10776 gIndent2++;
10777 #endif
10778
10779 nsBoxLayoutMetrics* metrics = BoxMetrics();
10780 if (MOZ_UNLIKELY(!metrics)) {
10781 // Can't proceed without BoxMetrics. This should only happen if something
10782 // is seriously broken, e.g. if we try to do XUL layout on a non-XUL frame.
10783 // (If this is a content process, we'll abort even in release builds,
10784 // because XUL layout mixup is extra surprising in content, and aborts are
10785 // less catastrophic in content vs. in chrome.)
10786 MOZ_RELEASE_ASSERT(!XRE_IsContentProcess(),
10787 "Starting XUL BoxReflow w/o BoxMetrics (in content)?");
10788 MOZ_ASSERT_UNREACHABLE("Starting XUL BoxReflow w/o BoxMetrics?");
10789 return;
10790 }
10791
10792 nsReflowStatus status;
10793 WritingMode wm = aDesiredSize.GetWritingMode();
10794
10795 bool needsReflow = NS_SUBTREE_DIRTY(this);
10796
10797 // if we don't need a reflow then
10798 // lets see if we are already that size. Yes? then don't even reflow. We are
10799 // done.
10800 if (!needsReflow) {
10801 if (aWidth != NS_UNCONSTRAINEDSIZE && aHeight != NS_UNCONSTRAINEDSIZE) {
10802 // if the new calculated size has a 0 width or a 0 height
10803 if ((metrics->mLastSize.width == 0 || metrics->mLastSize.height == 0) &&
10804 (aWidth == 0 || aHeight == 0)) {
10805 needsReflow = false;
10806 aDesiredSize.Width() = aWidth;
10807 aDesiredSize.Height() = aHeight;
10808 SetSize(aDesiredSize.Size(wm).ConvertTo(GetWritingMode(), wm));
10809 } else {
10810 aDesiredSize.Width() = metrics->mLastSize.width;
10811 aDesiredSize.Height() = metrics->mLastSize.height;
10812
10813 // remove the margin. The rect of our child does not include it but our
10814 // calculated size does. don't reflow if we are already the right size
10815 if (metrics->mLastSize.width == aWidth &&
10816 metrics->mLastSize.height == aHeight)
10817 needsReflow = false;
10818 else
10819 needsReflow = true;
10820 }
10821 } else {
10822 // if the width or height are intrinsic alway reflow because
10823 // we don't know what it should be.
10824 needsReflow = true;
10825 }
10826 }
10827
10828 // ok now reflow the child into the spacers calculated space
10829 if (needsReflow) {
10830 aDesiredSize.ClearSize();
10831
10832 // create a reflow input to tell our child to flow at the given size.
10833
10834 // Construct a bogus parent reflow input so that there's a usable
10835 // containing block reflow input.
10836 nsMargin margin(0, 0, 0, 0);
10837 GetXULMargin(margin);
10838
10839 nsSize parentSize(aWidth, aHeight);
10840 if (parentSize.height != NS_UNCONSTRAINEDSIZE)
10841 parentSize.height += margin.TopBottom();
10842 if (parentSize.width != NS_UNCONSTRAINEDSIZE)
10843 parentSize.width += margin.LeftRight();
10844
10845 nsIFrame* parentFrame = GetParent();
10846 WritingMode parentWM = parentFrame->GetWritingMode();
10847 ReflowInput parentReflowInput(aPresContext, parentFrame, aRenderingContext,
10848 LogicalSize(parentWM, parentSize),
10849 ReflowInput::DUMMY_PARENT_REFLOW_INPUT);
10850
10851 // This may not do very much useful, but it's probably worth trying.
10852 if (parentSize.width != NS_UNCONSTRAINEDSIZE)
10853 parentReflowInput.SetComputedWidth(std::max(parentSize.width, 0));
10854 if (parentSize.height != NS_UNCONSTRAINEDSIZE)
10855 parentReflowInput.SetComputedHeight(std::max(parentSize.height, 0));
10856 parentReflowInput.ComputedPhysicalMargin().SizeTo(0, 0, 0, 0);
10857 // XXX use box methods
10858 parentFrame->GetXULPadding(parentReflowInput.ComputedPhysicalPadding());
10859 parentFrame->GetXULBorder(
10860 parentReflowInput.ComputedPhysicalBorderPadding());
10861 parentReflowInput.ComputedPhysicalBorderPadding() +=
10862 parentReflowInput.ComputedPhysicalPadding();
10863
10864 // Construct the parent chain manually since constructing it normally
10865 // messes up dimensions.
10866 const ReflowInput* outerReflowInput = aState.OuterReflowInput();
10867 NS_ASSERTION(!outerReflowInput || outerReflowInput->mFrame != this,
10868 "in and out of XUL on a single frame?");
10869 const ReflowInput* parentRI;
10870 if (outerReflowInput && outerReflowInput->mFrame == parentFrame) {
10871 // We're a frame (such as a text control frame) that jumps into
10872 // box reflow and then straight out of it on the child frame.
10873 // This means we actually have a real parent reflow input.
10874 // nsLayoutUtils::InflationMinFontSizeFor used to need this to be
10875 // linked up correctly for text control frames, so do so here).
10876 parentRI = outerReflowInput;
10877 } else {
10878 parentRI = &parentReflowInput;
10879 }
10880
10881 // XXX Is it OK that this reflow input has only one ancestor?
10882 // (It used to have a bogus parent, skipping all the boxes).
10883 WritingMode wm = GetWritingMode();
10884 LogicalSize logicalSize(wm, nsSize(aWidth, aHeight));
10885 logicalSize.BSize(wm) = NS_UNCONSTRAINEDSIZE;
10886 ReflowInput reflowInput(aPresContext, *parentRI, this, logicalSize,
10887 Nothing(), ReflowInput::DUMMY_PARENT_REFLOW_INPUT);
10888
10889 // XXX_jwir3: This is somewhat fishy. If this is actually changing the value
10890 // here (which it might be), then we should make sure that it's
10891 // correct the first time around, rather than changing it later.
10892 reflowInput.mCBReflowInput = parentRI;
10893
10894 reflowInput.mReflowDepth = aState.GetReflowDepth();
10895
10896 // mComputedWidth and mComputedHeight are content-box, not
10897 // border-box
10898 if (aWidth != NS_UNCONSTRAINEDSIZE) {
10899 nscoord computedWidth =
10900 aWidth - reflowInput.ComputedPhysicalBorderPadding().LeftRight();
10901 computedWidth = std::max(computedWidth, 0);
10902 reflowInput.SetComputedWidth(computedWidth);
10903 }
10904
10905 // Most child frames of box frames (e.g. subdocument or scroll frames)
10906 // need to be constrained to the provided size and overflow as necessary.
10907 // The one exception are block frames, because we need to know their
10908 // natural height excluding any overflow area which may be caused by
10909 // various CSS effects such as shadow or outline.
10910 if (!IsBlockFrameOrSubclass()) {
10911 if (aHeight != NS_UNCONSTRAINEDSIZE) {
10912 nscoord computedHeight =
10913 aHeight - reflowInput.ComputedPhysicalBorderPadding().TopBottom();
10914 computedHeight = std::max(computedHeight, 0);
10915 reflowInput.SetComputedHeight(computedHeight);
10916 } else {
10917 reflowInput.SetComputedHeight(
10918 ComputeSize(aRenderingContext, wm, logicalSize,
10919 logicalSize.ISize(wm),
10920 reflowInput.ComputedLogicalMargin().Size(wm),
10921 reflowInput.ComputedLogicalBorderPadding().Size(wm) -
10922 reflowInput.ComputedLogicalPadding().Size(wm),
10923 reflowInput.ComputedLogicalPadding().Size(wm),
10924 ComputeSizeFlags::eDefault)
10925 .Height(wm));
10926 }
10927 }
10928
10929 // Box layout calls SetRect before XULLayout, whereas non-box layout
10930 // calls SetRect after Reflow.
10931 // XXX Perhaps we should be doing this by twiddling the rect back to
10932 // mLastSize before calling Reflow and then switching it back, but
10933 // However, mLastSize can also be the size passed to BoxReflow by
10934 // RefreshSizeCache, so that doesn't really make sense.
10935 if (metrics->mLastSize.width != aWidth) {
10936 reflowInput.SetHResize(true);
10937
10938 // When font size inflation is enabled, a horizontal resize
10939 // requires a full reflow. See ReflowInput::InitResizeFlags
10940 // for more details.
10941 if (nsLayoutUtils::FontSizeInflationEnabled(aPresContext)) {
10942 this->MarkSubtreeDirty();
10943 }
10944 }
10945 if (metrics->mLastSize.height != aHeight) {
10946 reflowInput.SetVResize(true);
10947 }
10948
10949 #ifdef DEBUG_REFLOW
10950 nsAdaptorAddIndents();
10951 printf("Size=(%d,%d)\n", reflowInput.ComputedWidth(),
10952 reflowInput.ComputedHeight());
10953 nsAdaptorAddIndents();
10954 nsAdaptorPrintReason(reflowInput);
10955 printf("\n");
10956 #endif
10957
10958 // place the child and reflow
10959
10960 Reflow(aPresContext, aDesiredSize, reflowInput, status);
10961
10962 NS_ASSERTION(status.IsComplete(), "bad status");
10963
10964 ReflowChildFlags layoutFlags = aState.LayoutFlags();
10965 nsContainerFrame::FinishReflowChild(
10966 this, aPresContext, aDesiredSize, &reflowInput, aX, aY,
10967 layoutFlags | ReflowChildFlags::NoMoveFrame);
10968
10969 // Save the ascent. (bug 103925)
10970 if (IsXULCollapsed()) {
10971 metrics->mAscent = 0;
10972 } else {
10973 if (aDesiredSize.BlockStartAscent() == ReflowOutput::ASK_FOR_BASELINE) {
10974 if (!nsLayoutUtils::GetFirstLineBaseline(wm, this, &metrics->mAscent))
10975 metrics->mAscent = GetLogicalBaseline(wm);
10976 } else
10977 metrics->mAscent = aDesiredSize.BlockStartAscent();
10978 }
10979
10980 } else {
10981 aDesiredSize.SetBlockStartAscent(metrics->mBlockAscent);
10982 }
10983
10984 #ifdef DEBUG_REFLOW
10985 if (aHeight != NS_UNCONSTRAINEDSIZE && aDesiredSize.Height() != aHeight) {
10986 nsAdaptorAddIndents();
10987 printf("*****got taller!*****\n");
10988 }
10989 if (aWidth != NS_UNCONSTRAINEDSIZE && aDesiredSize.Width() != aWidth) {
10990 nsAdaptorAddIndents();
10991 printf("*****got wider!******\n");
10992 }
10993 #endif
10994
10995 metrics->mLastSize.width = aDesiredSize.Width();
10996 metrics->mLastSize.height = aDesiredSize.Height();
10997
10998 #ifdef DEBUG_REFLOW
10999 gIndent2--;
11000 #endif
11001 }
11002
BoxMetrics() const11003 nsBoxLayoutMetrics* nsIFrame::BoxMetrics() const {
11004 nsBoxLayoutMetrics* metrics = GetProperty(BoxMetricsProperty());
11005 NS_ASSERTION(
11006 metrics,
11007 "A box layout method was called but InitBoxMetrics was never called");
11008 return metrics;
11009 }
11010
UpdateStyleOfChildAnonBox(nsIFrame * aChildFrame,ServoRestyleState & aRestyleState)11011 void nsIFrame::UpdateStyleOfChildAnonBox(nsIFrame* aChildFrame,
11012 ServoRestyleState& aRestyleState) {
11013 #ifdef DEBUG
11014 nsIFrame* parent = aChildFrame->GetInFlowParent();
11015 if (aChildFrame->IsTableFrame()) {
11016 parent = parent->GetParent();
11017 }
11018 if (parent->IsLineFrame()) {
11019 parent = parent->GetParent();
11020 }
11021 MOZ_ASSERT(nsLayoutUtils::FirstContinuationOrIBSplitSibling(parent) == this,
11022 "This should only be used for children!");
11023 #endif // DEBUG
11024 MOZ_ASSERT(!GetContent() || !aChildFrame->GetContent() ||
11025 aChildFrame->GetContent() == GetContent(),
11026 "What content node is it a frame for?");
11027 MOZ_ASSERT(!aChildFrame->GetPrevContinuation(),
11028 "Only first continuations should end up here");
11029
11030 // We could force the caller to pass in the pseudo, since some callers know it
11031 // statically... But this API is a bit nicer.
11032 auto pseudo = aChildFrame->Style()->GetPseudoType();
11033 MOZ_ASSERT(PseudoStyle::IsAnonBox(pseudo), "Child is not an anon box?");
11034 MOZ_ASSERT(!PseudoStyle::IsNonInheritingAnonBox(pseudo),
11035 "Why did the caller bother calling us?");
11036
11037 // Anon boxes inherit from their parent; that's us.
11038 RefPtr<ComputedStyle> newContext =
11039 aRestyleState.StyleSet().ResolveInheritingAnonymousBoxStyle(pseudo,
11040 Style());
11041
11042 nsChangeHint childHint =
11043 UpdateStyleOfOwnedChildFrame(aChildFrame, newContext, aRestyleState);
11044
11045 // Now that we've updated the style on aChildFrame, check whether it itself
11046 // has anon boxes to deal with.
11047 ServoRestyleState childrenState(*aChildFrame, aRestyleState, childHint,
11048 ServoRestyleState::Type::InFlow);
11049 aChildFrame->UpdateStyleOfOwnedAnonBoxes(childrenState);
11050
11051 // Assuming anon boxes don't have ::backdrop associated with them... if that
11052 // ever changes, we'd need to handle that here, like we do in
11053 // RestyleManager::ProcessPostTraversal
11054
11055 // We do need to handle block pseudo-elements here, though. Especially list
11056 // bullets.
11057 if (nsBlockFrame* block = do_QueryFrame(aChildFrame)) {
11058 block->UpdatePseudoElementStyles(childrenState);
11059 }
11060 }
11061
11062 /* static */
UpdateStyleOfOwnedChildFrame(nsIFrame * aChildFrame,ComputedStyle * aNewComputedStyle,ServoRestyleState & aRestyleState,const Maybe<ComputedStyle * > & aContinuationComputedStyle)11063 nsChangeHint nsIFrame::UpdateStyleOfOwnedChildFrame(
11064 nsIFrame* aChildFrame, ComputedStyle* aNewComputedStyle,
11065 ServoRestyleState& aRestyleState,
11066 const Maybe<ComputedStyle*>& aContinuationComputedStyle) {
11067 MOZ_ASSERT(!aChildFrame->GetAdditionalComputedStyle(0),
11068 "We don't handle additional styles here");
11069
11070 // Figure out whether we have an actual change. It's important that we do
11071 // this, for several reasons:
11072 //
11073 // 1) Even if all the child's changes are due to properties it inherits from
11074 // us, it's possible that no one ever asked us for those style structs and
11075 // hence changes to them aren't reflected in the changes handled at all.
11076 //
11077 // 2) Content can change stylesheets that change the styles of pseudos, and
11078 // extensions can add/remove stylesheets that change the styles of
11079 // anonymous boxes directly.
11080 uint32_t equalStructs; // Not used, actually.
11081 nsChangeHint childHint = aChildFrame->Style()->CalcStyleDifference(
11082 *aNewComputedStyle, &equalStructs);
11083
11084 // If aChildFrame is out of flow, then aRestyleState's "changes handled by the
11085 // parent" doesn't apply to it, because it may have some other parent in the
11086 // frame tree.
11087 if (!aChildFrame->HasAnyStateBits(NS_FRAME_OUT_OF_FLOW)) {
11088 childHint = NS_RemoveSubsumedHints(
11089 childHint, aRestyleState.ChangesHandledFor(aChildFrame));
11090 }
11091 if (childHint) {
11092 if (childHint & nsChangeHint_ReconstructFrame) {
11093 // If we generate a reconstruct here, remove any non-reconstruct hints we
11094 // may have already generated for this content.
11095 aRestyleState.ChangeList().PopChangesForContent(
11096 aChildFrame->GetContent());
11097 }
11098 aRestyleState.ChangeList().AppendChange(
11099 aChildFrame, aChildFrame->GetContent(), childHint);
11100 }
11101
11102 aChildFrame->SetComputedStyle(aNewComputedStyle);
11103 ComputedStyle* continuationStyle = aContinuationComputedStyle
11104 ? *aContinuationComputedStyle
11105 : aNewComputedStyle;
11106 for (nsIFrame* kid = aChildFrame->GetNextContinuation(); kid;
11107 kid = kid->GetNextContinuation()) {
11108 MOZ_ASSERT(!kid->GetAdditionalComputedStyle(0));
11109 kid->SetComputedStyle(continuationStyle);
11110 }
11111
11112 return childHint;
11113 }
11114
11115 /* static */
AddInPopupStateBitToDescendants(nsIFrame * aFrame)11116 void nsIFrame::AddInPopupStateBitToDescendants(nsIFrame* aFrame) {
11117 if (!aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP) &&
11118 aFrame->TrackingVisibility()) {
11119 // Assume all frames in popups are visible.
11120 aFrame->IncApproximateVisibleCount();
11121 }
11122
11123 aFrame->AddStateBits(NS_FRAME_IN_POPUP);
11124
11125 for (const auto& childList : aFrame->CrossDocChildLists()) {
11126 for (nsIFrame* child : childList.mList) {
11127 AddInPopupStateBitToDescendants(child);
11128 }
11129 }
11130 }
11131
11132 /* static */
RemoveInPopupStateBitFromDescendants(nsIFrame * aFrame)11133 void nsIFrame::RemoveInPopupStateBitFromDescendants(nsIFrame* aFrame) {
11134 if (!aFrame->HasAnyStateBits(NS_FRAME_IN_POPUP) ||
11135 nsLayoutUtils::IsPopup(aFrame)) {
11136 return;
11137 }
11138
11139 aFrame->RemoveStateBits(NS_FRAME_IN_POPUP);
11140
11141 if (aFrame->TrackingVisibility()) {
11142 // We assume all frames in popups are visible, so this decrement balances
11143 // out the increment in AddInPopupStateBitToDescendants above.
11144 aFrame->DecApproximateVisibleCount();
11145 }
11146 for (const auto& childList : aFrame->CrossDocChildLists()) {
11147 for (nsIFrame* child : childList.mList) {
11148 RemoveInPopupStateBitFromDescendants(child);
11149 }
11150 }
11151 }
11152
SetParent(nsContainerFrame * aParent)11153 void nsIFrame::SetParent(nsContainerFrame* aParent) {
11154 // If our parent is a wrapper anon box, our new parent should be too. We
11155 // _can_ change parent if our parent is a wrapper anon box, because some
11156 // wrapper anon boxes can have continuations.
11157 MOZ_ASSERT_IF(ParentIsWrapperAnonBox(),
11158 aParent->Style()->IsInheritingAnonBox());
11159
11160 // Note that the current mParent may already be destroyed at this point.
11161 mParent = aParent;
11162 MOZ_DIAGNOSTIC_ASSERT(!mParent || PresShell() == mParent->PresShell());
11163 if (::IsXULBoxWrapped(this)) {
11164 ::InitBoxMetrics(this, true);
11165 } else {
11166 // We could call Properties().Delete(BoxMetricsProperty()); here but
11167 // that's kind of slow and re-parenting in such a way that we were
11168 // IsXULBoxWrapped() before but not now should be very rare, so we'll just
11169 // keep this unused frame property until this frame dies instead.
11170 }
11171
11172 if (GetStateBits() & (NS_FRAME_HAS_VIEW | NS_FRAME_HAS_CHILD_WITH_VIEW)) {
11173 for (nsIFrame* f = aParent;
11174 f && !(f->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW);
11175 f = f->GetParent()) {
11176 f->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
11177 }
11178 }
11179
11180 if (HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
11181 for (nsIFrame* f = aParent; f; f = f->GetParent()) {
11182 if (f->HasAnyStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE)) {
11183 break;
11184 }
11185 f->AddStateBits(NS_FRAME_CONTAINS_RELATIVE_BSIZE);
11186 }
11187 }
11188
11189 if (HasAnyStateBits(NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)) {
11190 for (nsIFrame* f = aParent; f; f = f->GetParent()) {
11191 if (f->HasAnyStateBits(
11192 NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE)) {
11193 break;
11194 }
11195 f->AddStateBits(NS_FRAME_DESCENDANT_INTRINSIC_ISIZE_DEPENDS_ON_BSIZE);
11196 }
11197 }
11198
11199 if (HasInvalidFrameInSubtree()) {
11200 for (nsIFrame* f = aParent;
11201 f && !f->HasAnyStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT |
11202 NS_FRAME_IS_NONDISPLAY);
11203 f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
11204 f->AddStateBits(NS_FRAME_DESCENDANT_NEEDS_PAINT);
11205 }
11206 }
11207
11208 if (aParent->HasAnyStateBits(NS_FRAME_IN_POPUP)) {
11209 AddInPopupStateBitToDescendants(this);
11210 } else {
11211 RemoveInPopupStateBitFromDescendants(this);
11212 }
11213
11214 // If our new parent only has invalid children, then we just invalidate
11215 // ourselves too. This is probably faster than clearing the flag all
11216 // the way up the frame tree.
11217 if (aParent->HasAnyStateBits(NS_FRAME_ALL_DESCENDANTS_NEED_PAINT)) {
11218 InvalidateFrame();
11219 } else {
11220 SchedulePaint();
11221 }
11222 }
11223
CreateOwnLayerIfNeeded(nsDisplayListBuilder * aBuilder,nsDisplayList * aList,uint16_t aType,bool * aCreatedContainerItem)11224 void nsIFrame::CreateOwnLayerIfNeeded(nsDisplayListBuilder* aBuilder,
11225 nsDisplayList* aList, uint16_t aType,
11226 bool* aCreatedContainerItem) {
11227 if (GetContent() && GetContent()->IsXULElement() &&
11228 GetContent()->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::layer)) {
11229 aList->AppendNewToTopWithIndex<nsDisplayOwnLayer>(
11230 aBuilder, this, /* aIndex = */ aType, aList,
11231 aBuilder->CurrentActiveScrolledRoot(), nsDisplayOwnLayerFlags::None,
11232 ScrollbarData{}, true, false);
11233 if (aCreatedContainerItem) {
11234 *aCreatedContainerItem = true;
11235 }
11236 }
11237 }
11238
IsStackingContext(const nsStyleDisplay * aStyleDisplay,const nsStylePosition * aStylePosition,const nsStyleEffects * aStyleEffects,bool aIsPositioned)11239 bool nsIFrame::IsStackingContext(const nsStyleDisplay* aStyleDisplay,
11240 const nsStylePosition* aStylePosition,
11241 const nsStyleEffects* aStyleEffects,
11242 bool aIsPositioned) {
11243 return HasOpacity(aStyleDisplay, aStyleEffects, nullptr) ||
11244 IsTransformed(aStyleDisplay) ||
11245 ((aStyleDisplay->IsContainPaint() ||
11246 aStyleDisplay->IsContainLayout()) &&
11247 IsFrameOfType(eSupportsContainLayoutAndPaint)) ||
11248 // strictly speaking, 'perspective' doesn't require visual atomicity,
11249 // but the spec says it acts like the rest of these
11250 ChildrenHavePerspective(aStyleDisplay) ||
11251 aStyleEffects->mMixBlendMode != StyleBlend::Normal ||
11252 nsSVGIntegrationUtils::UsingEffectsForFrame(this) ||
11253 (aIsPositioned && (aStyleDisplay->IsPositionForcingStackingContext() ||
11254 aStylePosition->mZIndex.IsInteger())) ||
11255 (aStyleDisplay->mWillChange.bits &
11256 StyleWillChangeBits::STACKING_CONTEXT) ||
11257 aStyleDisplay->mIsolation != StyleIsolation::Auto ||
11258 aStyleEffects->HasBackdropFilters();
11259 }
11260
IsStackingContext()11261 bool nsIFrame::IsStackingContext() {
11262 const nsStyleDisplay* disp = StyleDisplay();
11263 const bool isPositioned = disp->IsAbsPosContainingBlock(this);
11264 return IsStackingContext(disp, StylePosition(), StyleEffects(), isPositioned);
11265 }
11266
IsFrameScrolledOutOfView(const nsIFrame * aTarget,const nsRect & aTargetRect,const nsIFrame * aParent)11267 static bool IsFrameScrolledOutOfView(const nsIFrame* aTarget,
11268 const nsRect& aTargetRect,
11269 const nsIFrame* aParent) {
11270 // The ancestor frame we are checking if it clips out aTargetRect relative to
11271 // aTarget.
11272 nsIFrame* clipParent = nullptr;
11273
11274 // find the first scrollable frame or root frame if we are in a fixed pos
11275 // subtree
11276 for (nsIFrame* f = const_cast<nsIFrame*>(aParent); f;
11277 f = nsLayoutUtils::GetCrossDocParentFrame(f)) {
11278 nsIScrollableFrame* scrollableFrame = do_QueryFrame(f);
11279 if (scrollableFrame) {
11280 clipParent = f;
11281 break;
11282 }
11283 if (f->StyleDisplay()->mPosition == StylePositionProperty::Fixed &&
11284 nsLayoutUtils::IsReallyFixedPos(f)) {
11285 clipParent = f->GetParent();
11286 break;
11287 }
11288 }
11289
11290 if (!clipParent) {
11291 // Even if we couldn't find the nearest scrollable frame, it might mean we
11292 // are in an out-of-process iframe, try to see if |aTarget| frame is
11293 // scrolled out of view in an scrollable frame in a cross-process ancestor
11294 // document.
11295 return nsLayoutUtils::FrameIsScrolledOutOfViewInCrossProcess(aTarget);
11296 }
11297
11298 nsRect clipRect = clipParent->GetVisualOverflowRectRelativeToSelf();
11299 // We consider that the target is scrolled out if the scrollable (or root)
11300 // frame is empty.
11301 if (clipRect.IsEmpty()) {
11302 return true;
11303 }
11304
11305 nsRect transformedRect = nsLayoutUtils::TransformFrameRectToAncestor(
11306 aTarget, aTargetRect, clipParent);
11307
11308 if (transformedRect.IsEmpty()) {
11309 // If the transformed rect is empty it represents a line or a point that we
11310 // should check is outside the the scrollable rect.
11311 if (transformedRect.x > clipRect.XMost() ||
11312 transformedRect.y > clipRect.YMost() ||
11313 clipRect.x > transformedRect.XMost() ||
11314 clipRect.y > transformedRect.YMost()) {
11315 return true;
11316 }
11317 } else if (!transformedRect.Intersects(clipRect)) {
11318 return true;
11319 }
11320
11321 nsIFrame* parent = clipParent->GetParent();
11322 if (!parent) {
11323 return false;
11324 }
11325
11326 return IsFrameScrolledOutOfView(aTarget, aTargetRect, parent);
11327 }
11328
IsScrolledOutOfView() const11329 bool nsIFrame::IsScrolledOutOfView() const {
11330 nsRect rect = GetVisualOverflowRectRelativeToSelf();
11331 return IsFrameScrolledOutOfView(this, rect, this);
11332 }
11333
ComputeWidgetTransform()11334 gfx::Matrix nsIFrame::ComputeWidgetTransform() {
11335 const nsStyleUIReset* uiReset = StyleUIReset();
11336 if (uiReset->mMozWindowTransform.IsNone()) {
11337 return gfx::Matrix();
11338 }
11339
11340 TransformReferenceBox refBox(nullptr, nsRect(nsPoint(), GetSize()));
11341
11342 nsPresContext* presContext = PresContext();
11343 int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
11344 gfx::Matrix4x4 matrix = nsStyleTransformMatrix::ReadTransforms(
11345 uiReset->mMozWindowTransform, refBox, float(appUnitsPerDevPixel));
11346
11347 // Apply the -moz-window-transform-origin translation to the matrix.
11348 const StyleTransformOrigin& origin = uiReset->mWindowTransformOrigin;
11349 Point transformOrigin = nsStyleTransformMatrix::Convert2DPosition(
11350 origin.horizontal, origin.vertical, refBox, appUnitsPerDevPixel);
11351 matrix.ChangeBasis(Point3D(transformOrigin.x, transformOrigin.y, 0));
11352
11353 gfx::Matrix result2d;
11354 if (!matrix.CanDraw2D(&result2d)) {
11355 // FIXME: It would be preferable to reject non-2D transforms at parse time.
11356 NS_WARNING(
11357 "-moz-window-transform does not describe a 2D transform, "
11358 "but only 2d transforms are supported");
11359 return gfx::Matrix();
11360 }
11361
11362 return result2d;
11363 }
11364
DoUpdateStyleOfOwnedAnonBoxes(ServoRestyleState & aRestyleState)11365 void nsIFrame::DoUpdateStyleOfOwnedAnonBoxes(ServoRestyleState& aRestyleState) {
11366 // As a special case, we check for {ib}-split block frames here, rather
11367 // than have an nsInlineFrame::AppendDirectlyOwnedAnonBoxes implementation
11368 // that returns them.
11369 //
11370 // (If we did handle them in AppendDirectlyOwnedAnonBoxes, we would have to
11371 // return *all* of the in-flow {ib}-split block frames, not just the first
11372 // one. For restyling, we really just need the first in flow, and the other
11373 // user of the AppendOwnedAnonBoxes API, AllChildIterator, doesn't need to
11374 // know about them at all, since these block frames never create NAC. So we
11375 // avoid any unncessary hashtable lookups for the {ib}-split frames by calling
11376 // UpdateStyleOfOwnedAnonBoxesForIBSplit directly here.)
11377 if (IsInlineFrame()) {
11378 if ((GetStateBits() & NS_FRAME_PART_OF_IBSPLIT)) {
11379 static_cast<nsInlineFrame*>(this)->UpdateStyleOfOwnedAnonBoxesForIBSplit(
11380 aRestyleState);
11381 }
11382 return;
11383 }
11384
11385 AutoTArray<OwnedAnonBox, 4> frames;
11386 AppendDirectlyOwnedAnonBoxes(frames);
11387 for (OwnedAnonBox& box : frames) {
11388 if (box.mUpdateStyleFn) {
11389 box.mUpdateStyleFn(this, box.mAnonBoxFrame, aRestyleState);
11390 } else {
11391 UpdateStyleOfChildAnonBox(box.mAnonBoxFrame, aRestyleState);
11392 }
11393 }
11394 }
11395
11396 /* virtual */
AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox> & aResult)11397 void nsIFrame::AppendDirectlyOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) {
11398 MOZ_ASSERT(!(GetStateBits() & NS_FRAME_OWNS_ANON_BOXES));
11399 MOZ_ASSERT(false, "Why did this get called?");
11400 }
11401
DoAppendOwnedAnonBoxes(nsTArray<OwnedAnonBox> & aResult)11402 void nsIFrame::DoAppendOwnedAnonBoxes(nsTArray<OwnedAnonBox>& aResult) {
11403 size_t i = aResult.Length();
11404 AppendDirectlyOwnedAnonBoxes(aResult);
11405
11406 // After appending the directly owned anonymous boxes of this frame to
11407 // aResult above, we need to check each of them to see if they own
11408 // any anonymous boxes themselves. Note that we keep progressing
11409 // through aResult, looking for additional entries in aResult from these
11410 // subsequent AppendDirectlyOwnedAnonBoxes calls. (Thus we can't
11411 // use a ranged for loop here.)
11412
11413 while (i < aResult.Length()) {
11414 nsIFrame* f = aResult[i].mAnonBoxFrame;
11415 if (f->GetStateBits() & NS_FRAME_OWNS_ANON_BOXES) {
11416 f->AppendDirectlyOwnedAnonBoxes(aResult);
11417 }
11418 ++i;
11419 }
11420 }
11421
CaretPosition()11422 nsIFrame::CaretPosition::CaretPosition() : mContentOffset(0) {}
11423
11424 nsIFrame::CaretPosition::~CaretPosition() = default;
11425
HasCSSAnimations()11426 bool nsFrame::HasCSSAnimations() {
11427 auto collection =
11428 AnimationCollection<CSSAnimation>::GetAnimationCollection(this);
11429 return collection && collection->mAnimations.Length() > 0;
11430 }
11431
HasCSSTransitions()11432 bool nsFrame::HasCSSTransitions() {
11433 auto collection =
11434 AnimationCollection<CSSTransition>::GetAnimationCollection(this);
11435 return collection && collection->mAnimations.Length() > 0;
11436 }
11437
AddSizeOfExcludingThisForTree(nsWindowSizes & aSizes) const11438 void nsIFrame::AddSizeOfExcludingThisForTree(nsWindowSizes& aSizes) const {
11439 aSizes.mLayoutFramePropertiesSize +=
11440 mProperties.SizeOfExcludingThis(aSizes.mState.mMallocSizeOf);
11441
11442 // We don't do this for Gecko because this stuff is stored in the nsPresArena
11443 // and so measured elsewhere.
11444 if (!aSizes.mState.HaveSeenPtr(mComputedStyle)) {
11445 mComputedStyle->AddSizeOfIncludingThis(aSizes,
11446 &aSizes.mLayoutComputedValuesNonDom);
11447 }
11448
11449 // And our additional styles.
11450 int32_t index = 0;
11451 while (auto* extra = GetAdditionalComputedStyle(index++)) {
11452 if (!aSizes.mState.HaveSeenPtr(extra)) {
11453 extra->AddSizeOfIncludingThis(aSizes,
11454 &aSizes.mLayoutComputedValuesNonDom);
11455 }
11456 }
11457
11458 for (const auto& childList : ChildLists()) {
11459 for (const nsIFrame* f : childList.mList) {
11460 f->AddSizeOfExcludingThisForTree(aSizes);
11461 }
11462 }
11463 }
11464
GetCompositorHitTestArea(nsDisplayListBuilder * aBuilder)11465 nsRect nsIFrame::GetCompositorHitTestArea(nsDisplayListBuilder* aBuilder) {
11466 nsRect area;
11467
11468 nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetScrollableFrameFor(this);
11469 if (scrollFrame) {
11470 // If the frame is content of a scrollframe, then we need to pick up the
11471 // area corresponding to the overflow rect as well. Otherwise the parts of
11472 // the overflow that are not occupied by descendants get skipped and the
11473 // APZ code sends touch events to the content underneath instead.
11474 // See https://bugzilla.mozilla.org/show_bug.cgi?id=1127773#c15.
11475 area = GetScrollableOverflowRect();
11476 } else {
11477 area = nsRect(nsPoint(0, 0), GetSize());
11478 }
11479
11480 if (!area.IsEmpty()) {
11481 return area + aBuilder->ToReferenceFrame(this);
11482 }
11483
11484 return area;
11485 }
11486
GetCompositorHitTestInfo(nsDisplayListBuilder * aBuilder)11487 CompositorHitTestInfo nsIFrame::GetCompositorHitTestInfo(
11488 nsDisplayListBuilder* aBuilder) {
11489 CompositorHitTestInfo result = CompositorHitTestInvisibleToHit;
11490
11491 if (aBuilder->IsInsidePointerEventsNoneDoc()) {
11492 // Somewhere up the parent document chain is a subdocument with pointer-
11493 // events:none set on it.
11494 return result;
11495 }
11496 if (!GetParent()) {
11497 MOZ_ASSERT(IsViewportFrame());
11498 // Viewport frames are never event targets, other frames, like canvas
11499 // frames, are the event targets for any regions viewport frames may cover.
11500 return result;
11501 }
11502 const StylePointerEvents pointerEvents =
11503 StyleUI()->GetEffectivePointerEvents(this);
11504 if (pointerEvents == StylePointerEvents::None) {
11505 return result;
11506 }
11507 if (!StyleVisibility()->IsVisible()) {
11508 return result;
11509 }
11510
11511 // Anything that didn't match the above conditions is visible to hit-testing.
11512 result = CompositorHitTestFlags::eVisibleToHitTest;
11513 if (nsSVGIntegrationUtils::UsingMaskOrClipPathForFrame(this)) {
11514 // If WebRender is enabled, simple clip-paths can be converted into WR
11515 // clips that WR knows how to hit-test against, so we don't need to mark
11516 // it as an irregular area.
11517 if (!gfxVars::UseWebRender() ||
11518 !nsSVGIntegrationUtils::UsingSimpleClipPathForFrame(this)) {
11519 result += CompositorHitTestFlags::eIrregularArea;
11520 }
11521 }
11522
11523 if (aBuilder->IsBuildingNonLayerizedScrollbar()) {
11524 // Scrollbars may be painted into a layer below the actual layer they will
11525 // scroll, and therefore wheel events may be dispatched to the outer frame
11526 // instead of the intended scrollframe. To address this, we force a d-t-c
11527 // region on scrollbar frames that won't be placed in their own layer. See
11528 // bug 1213324 for details.
11529 result += CompositorHitTestFlags::eInactiveScrollframe;
11530 } else if (aBuilder->GetAncestorHasApzAwareEventHandler()) {
11531 result += CompositorHitTestFlags::eApzAwareListeners;
11532 } else if (IsObjectFrame()) {
11533 // If the frame is a plugin frame and wants to handle wheel events as
11534 // default action, we should add the frame to dispatch-to-content region.
11535 nsPluginFrame* pluginFrame = do_QueryFrame(this);
11536 if (pluginFrame && pluginFrame->WantsToHandleWheelEventAsDefaultAction()) {
11537 result += CompositorHitTestFlags::eApzAwareListeners;
11538 }
11539 }
11540
11541 if (aBuilder->IsTouchEventPrefEnabledDoc()) {
11542 // Inherit the touch-action flags from the parent, if there is one. We do
11543 // this because of how the touch-action on a frame combines the touch-action
11544 // from ancestor DOM elements. Refer to the documentation in
11545 // TouchActionHelper.cpp for details; this code is meant to be equivalent to
11546 // that code, but woven into the top-down recursive display list building
11547 // process.
11548 CompositorHitTestInfo inheritedTouchAction =
11549 aBuilder->GetHitTestInfo() & CompositorHitTestTouchActionMask;
11550
11551 nsIFrame* touchActionFrame = this;
11552 if (nsIScrollableFrame* scrollFrame =
11553 nsLayoutUtils::GetScrollableFrameFor(this)) {
11554 ScrollStyles ss = scrollFrame->GetScrollStyles();
11555 if (ss.mVertical != StyleOverflow::Hidden ||
11556 ss.mHorizontal != StyleOverflow::Hidden) {
11557 touchActionFrame = do_QueryFrame(scrollFrame);
11558 // On scrollframes, stop inheriting the pan-x and pan-y flags; instead,
11559 // reset them back to zero to allow panning on the scrollframe unless we
11560 // encounter an element that disables it that's inside the scrollframe.
11561 // This is equivalent to the |considerPanning| variable in
11562 // TouchActionHelper.cpp, but for a top-down traversal.
11563 CompositorHitTestInfo panMask(
11564 CompositorHitTestFlags::eTouchActionPanXDisabled,
11565 CompositorHitTestFlags::eTouchActionPanYDisabled);
11566 inheritedTouchAction -= panMask;
11567 }
11568 }
11569
11570 result += inheritedTouchAction;
11571
11572 const StyleTouchAction touchAction =
11573 nsLayoutUtils::GetTouchActionFromFrame(touchActionFrame);
11574 // The CSS allows the syntax auto | none | [pan-x || pan-y] | manipulation
11575 // so we can eliminate some combinations of things.
11576 if (touchAction == StyleTouchAction::AUTO) {
11577 // nothing to do
11578 } else if (touchAction & StyleTouchAction::MANIPULATION) {
11579 result += CompositorHitTestFlags::eTouchActionDoubleTapZoomDisabled;
11580 } else {
11581 // This path handles the cases none | [pan-x || pan-y] and so both
11582 // double-tap and pinch zoom are disabled in here.
11583 result += CompositorHitTestFlags::eTouchActionPinchZoomDisabled;
11584 result += CompositorHitTestFlags::eTouchActionDoubleTapZoomDisabled;
11585
11586 if (!(touchAction & StyleTouchAction::PAN_X)) {
11587 result += CompositorHitTestFlags::eTouchActionPanXDisabled;
11588 }
11589 if (!(touchAction & StyleTouchAction::PAN_Y)) {
11590 result += CompositorHitTestFlags::eTouchActionPanYDisabled;
11591 }
11592 if (touchAction & StyleTouchAction::NONE) {
11593 // all the touch-action disabling flags will already have been set above
11594 MOZ_ASSERT(result.contains(CompositorHitTestTouchActionMask));
11595 }
11596 }
11597 }
11598
11599 const Maybe<ScrollDirection> scrollDirection =
11600 aBuilder->GetCurrentScrollbarDirection();
11601 if (scrollDirection.isSome()) {
11602 if (GetContent()->IsXULElement(nsGkAtoms::thumb)) {
11603 const bool thumbGetsLayer = aBuilder->GetCurrentScrollbarTarget() !=
11604 layers::ScrollableLayerGuid::NULL_SCROLL_ID;
11605 if (thumbGetsLayer) {
11606 result += CompositorHitTestFlags::eScrollbarThumb;
11607 } else {
11608 result += CompositorHitTestFlags::eInactiveScrollframe;
11609 }
11610 }
11611
11612 if (*scrollDirection == ScrollDirection::eVertical) {
11613 result += CompositorHitTestFlags::eScrollbarVertical;
11614 }
11615
11616 // includes the ScrollbarFrame, SliderFrame, anything else that
11617 // might be inside the xul:scrollbar
11618 result += CompositorHitTestFlags::eScrollbar;
11619 }
11620
11621 return result;
11622 }
11623
11624 // Returns true if we can guarantee there is no visible descendants.
HasNoVisibleDescendants(const nsIFrame * aFrame)11625 static bool HasNoVisibleDescendants(const nsIFrame* aFrame) {
11626 for (const auto& childList : aFrame->ChildLists()) {
11627 for (nsIFrame* f : childList.mList) {
11628 if (nsPlaceholderFrame::GetRealFrameFor(f)
11629 ->IsVisibleOrMayHaveVisibleDescendants()) {
11630 return false;
11631 }
11632 }
11633 }
11634 return true;
11635 }
11636
UpdateVisibleDescendantsState()11637 void nsIFrame::UpdateVisibleDescendantsState() {
11638 if (StyleVisibility()->IsVisible()) {
11639 // Notify invisible ancestors that a visible descendant exists now.
11640 nsIFrame* ancestor;
11641 for (ancestor = GetInFlowParent();
11642 ancestor && !ancestor->StyleVisibility()->IsVisible();
11643 ancestor = ancestor->GetInFlowParent()) {
11644 ancestor->mAllDescendantsAreInvisible = false;
11645 }
11646 } else {
11647 mAllDescendantsAreInvisible = HasNoVisibleDescendants(this);
11648 }
11649 }
11650
11651 // Box layout debugging
11652 #ifdef DEBUG_REFLOW
11653 int32_t gIndent2 = 0;
11654
nsAdaptorAddIndents()11655 void nsAdaptorAddIndents() {
11656 for (int32_t i = 0; i < gIndent2; i++) {
11657 printf(" ");
11658 }
11659 }
11660
nsAdaptorPrintReason(ReflowInput & aReflowInput)11661 void nsAdaptorPrintReason(ReflowInput& aReflowInput) {
11662 char* reflowReasonString;
11663
11664 switch (aReflowInput.reason) {
11665 case eReflowReason_Initial:
11666 reflowReasonString = "initial";
11667 break;
11668
11669 case eReflowReason_Resize:
11670 reflowReasonString = "resize";
11671 break;
11672 case eReflowReason_Dirty:
11673 reflowReasonString = "dirty";
11674 break;
11675 case eReflowReason_StyleChange:
11676 reflowReasonString = "stylechange";
11677 break;
11678 case eReflowReason_Incremental: {
11679 switch (aReflowInput.reflowCommand->Type()) {
11680 case eReflowType_StyleChanged:
11681 reflowReasonString = "incremental (StyleChanged)";
11682 break;
11683 case eReflowType_ReflowDirty:
11684 reflowReasonString = "incremental (ReflowDirty)";
11685 break;
11686 default:
11687 reflowReasonString = "incremental (Unknown)";
11688 }
11689 } break;
11690 default:
11691 reflowReasonString = "unknown";
11692 break;
11693 }
11694
11695 printf("%s", reflowReasonString);
11696 }
11697
11698 #endif
11699
11700 #ifdef DEBUG
GetTagName(nsFrame * aFrame,nsIContent * aContent,int aResultSize,char * aResult)11701 static void GetTagName(nsFrame* aFrame, nsIContent* aContent, int aResultSize,
11702 char* aResult) {
11703 if (aContent) {
11704 snprintf(aResult, aResultSize, "%s@%p",
11705 nsAtomCString(aContent->NodeInfo()->NameAtom()).get(), aFrame);
11706 } else {
11707 snprintf(aResult, aResultSize, "@%p", aFrame);
11708 }
11709 }
11710
Trace(const char * aMethod,bool aEnter)11711 void nsFrame::Trace(const char* aMethod, bool aEnter) {
11712 if (NS_FRAME_LOG_TEST(sFrameLogModule, NS_FRAME_TRACE_CALLS)) {
11713 char tagbuf[40];
11714 GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
11715 printf_stderr("%s: %s %s", tagbuf, aEnter ? "enter" : "exit", aMethod);
11716 }
11717 }
11718
Trace(const char * aMethod,bool aEnter,const nsReflowStatus & aStatus)11719 void nsFrame::Trace(const char* aMethod, bool aEnter,
11720 const nsReflowStatus& aStatus) {
11721 if (NS_FRAME_LOG_TEST(sFrameLogModule, NS_FRAME_TRACE_CALLS)) {
11722 char tagbuf[40];
11723 GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
11724 printf_stderr("%s: %s %s, status=%scomplete%s", tagbuf,
11725 aEnter ? "enter" : "exit", aMethod,
11726 aStatus.IsIncomplete() ? "not" : "",
11727 (aStatus.NextInFlowNeedsReflow()) ? "+reflow" : "");
11728 }
11729 }
11730
TraceMsg(const char * aFormatString,...)11731 void nsFrame::TraceMsg(const char* aFormatString, ...) {
11732 if (NS_FRAME_LOG_TEST(sFrameLogModule, NS_FRAME_TRACE_CALLS)) {
11733 // Format arguments into a buffer
11734 char argbuf[200];
11735 va_list ap;
11736 va_start(ap, aFormatString);
11737 VsprintfLiteral(argbuf, aFormatString, ap);
11738 va_end(ap);
11739
11740 char tagbuf[40];
11741 GetTagName(this, mContent, sizeof(tagbuf), tagbuf);
11742 printf_stderr("%s: %s", tagbuf, argbuf);
11743 }
11744 }
11745
VerifyDirtyBitSet(const nsFrameList & aFrameList)11746 void nsFrame::VerifyDirtyBitSet(const nsFrameList& aFrameList) {
11747 for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
11748 NS_ASSERTION(e.get()->GetStateBits() & NS_FRAME_IS_DIRTY,
11749 "dirty bit not set");
11750 }
11751 }
11752
11753 // Start Display Reflow
DR_cookie(nsPresContext * aPresContext,nsIFrame * aFrame,const ReflowInput & aReflowInput,ReflowOutput & aMetrics,nsReflowStatus & aStatus)11754 DR_cookie::DR_cookie(nsPresContext* aPresContext, nsIFrame* aFrame,
11755 const ReflowInput& aReflowInput, ReflowOutput& aMetrics,
11756 nsReflowStatus& aStatus)
11757 : mPresContext(aPresContext),
11758 mFrame(aFrame),
11759 mReflowInput(aReflowInput),
11760 mMetrics(aMetrics),
11761 mStatus(aStatus) {
11762 MOZ_COUNT_CTOR(DR_cookie);
11763 mValue = nsFrame::DisplayReflowEnter(aPresContext, mFrame, mReflowInput);
11764 }
11765
~DR_cookie()11766 DR_cookie::~DR_cookie() {
11767 MOZ_COUNT_DTOR(DR_cookie);
11768 nsFrame::DisplayReflowExit(mPresContext, mFrame, mMetrics, mStatus, mValue);
11769 }
11770
DR_layout_cookie(nsIFrame * aFrame)11771 DR_layout_cookie::DR_layout_cookie(nsIFrame* aFrame) : mFrame(aFrame) {
11772 MOZ_COUNT_CTOR(DR_layout_cookie);
11773 mValue = nsFrame::DisplayLayoutEnter(mFrame);
11774 }
11775
~DR_layout_cookie()11776 DR_layout_cookie::~DR_layout_cookie() {
11777 MOZ_COUNT_DTOR(DR_layout_cookie);
11778 nsFrame::DisplayLayoutExit(mFrame, mValue);
11779 }
11780
DR_intrinsic_inline_size_cookie(nsIFrame * aFrame,const char * aType,nscoord & aResult)11781 DR_intrinsic_inline_size_cookie::DR_intrinsic_inline_size_cookie(
11782 nsIFrame* aFrame, const char* aType, nscoord& aResult)
11783 : mFrame(aFrame), mType(aType), mResult(aResult) {
11784 MOZ_COUNT_CTOR(DR_intrinsic_inline_size_cookie);
11785 mValue = nsFrame::DisplayIntrinsicISizeEnter(mFrame, mType);
11786 }
11787
~DR_intrinsic_inline_size_cookie()11788 DR_intrinsic_inline_size_cookie::~DR_intrinsic_inline_size_cookie() {
11789 MOZ_COUNT_DTOR(DR_intrinsic_inline_size_cookie);
11790 nsFrame::DisplayIntrinsicISizeExit(mFrame, mType, mResult, mValue);
11791 }
11792
DR_intrinsic_size_cookie(nsIFrame * aFrame,const char * aType,nsSize & aResult)11793 DR_intrinsic_size_cookie::DR_intrinsic_size_cookie(nsIFrame* aFrame,
11794 const char* aType,
11795 nsSize& aResult)
11796 : mFrame(aFrame), mType(aType), mResult(aResult) {
11797 MOZ_COUNT_CTOR(DR_intrinsic_size_cookie);
11798 mValue = nsFrame::DisplayIntrinsicSizeEnter(mFrame, mType);
11799 }
11800
~DR_intrinsic_size_cookie()11801 DR_intrinsic_size_cookie::~DR_intrinsic_size_cookie() {
11802 MOZ_COUNT_DTOR(DR_intrinsic_size_cookie);
11803 nsFrame::DisplayIntrinsicSizeExit(mFrame, mType, mResult, mValue);
11804 }
11805
DR_init_constraints_cookie(nsIFrame * aFrame,ReflowInput * aState,nscoord aCBWidth,nscoord aCBHeight,const nsMargin * aMargin,const nsMargin * aPadding)11806 DR_init_constraints_cookie::DR_init_constraints_cookie(
11807 nsIFrame* aFrame, ReflowInput* aState, nscoord aCBWidth, nscoord aCBHeight,
11808 const nsMargin* aMargin, const nsMargin* aPadding)
11809 : mFrame(aFrame), mState(aState) {
11810 MOZ_COUNT_CTOR(DR_init_constraints_cookie);
11811 mValue = ReflowInput::DisplayInitConstraintsEnter(
11812 mFrame, mState, aCBWidth, aCBHeight, aMargin, aPadding);
11813 }
11814
~DR_init_constraints_cookie()11815 DR_init_constraints_cookie::~DR_init_constraints_cookie() {
11816 MOZ_COUNT_DTOR(DR_init_constraints_cookie);
11817 ReflowInput::DisplayInitConstraintsExit(mFrame, mState, mValue);
11818 }
11819
DR_init_offsets_cookie(nsIFrame * aFrame,SizeComputationInput * aState,nscoord aPercentBasis,WritingMode aCBWritingMode,const nsMargin * aMargin,const nsMargin * aPadding)11820 DR_init_offsets_cookie::DR_init_offsets_cookie(nsIFrame* aFrame,
11821 SizeComputationInput* aState,
11822 nscoord aPercentBasis,
11823 WritingMode aCBWritingMode,
11824 const nsMargin* aMargin,
11825 const nsMargin* aPadding)
11826 : mFrame(aFrame), mState(aState) {
11827 MOZ_COUNT_CTOR(DR_init_offsets_cookie);
11828 mValue = SizeComputationInput::DisplayInitOffsetsEnter(
11829 mFrame, mState, aPercentBasis, aCBWritingMode, aMargin, aPadding);
11830 }
11831
~DR_init_offsets_cookie()11832 DR_init_offsets_cookie::~DR_init_offsets_cookie() {
11833 MOZ_COUNT_DTOR(DR_init_offsets_cookie);
11834 SizeComputationInput::DisplayInitOffsetsExit(mFrame, mState, mValue);
11835 }
11836
DR_init_type_cookie(nsIFrame * aFrame,ReflowInput * aState)11837 DR_init_type_cookie::DR_init_type_cookie(nsIFrame* aFrame, ReflowInput* aState)
11838 : mFrame(aFrame), mState(aState) {
11839 MOZ_COUNT_CTOR(DR_init_type_cookie);
11840 mValue = ReflowInput::DisplayInitFrameTypeEnter(mFrame, mState);
11841 }
11842
~DR_init_type_cookie()11843 DR_init_type_cookie::~DR_init_type_cookie() {
11844 MOZ_COUNT_DTOR(DR_init_type_cookie);
11845 ReflowInput::DisplayInitFrameTypeExit(mFrame, mState, mValue);
11846 }
11847
11848 struct DR_Rule;
11849
11850 struct DR_FrameTypeInfo {
11851 DR_FrameTypeInfo(LayoutFrameType aFrameType, const char* aFrameNameAbbrev,
11852 const char* aFrameName);
11853 ~DR_FrameTypeInfo();
11854
11855 LayoutFrameType mType;
11856 char mNameAbbrev[16];
11857 char mName[32];
11858 nsTArray<DR_Rule*> mRules;
11859
11860 private:
11861 DR_FrameTypeInfo& operator=(const DR_FrameTypeInfo&) = delete;
11862 };
11863
11864 struct DR_FrameTreeNode;
11865 struct DR_Rule;
11866
11867 struct DR_State {
11868 DR_State();
11869 ~DR_State();
11870 void Init();
11871 void AddFrameTypeInfo(LayoutFrameType aFrameType,
11872 const char* aFrameNameAbbrev, const char* aFrameName);
11873 DR_FrameTypeInfo* GetFrameTypeInfo(LayoutFrameType aFrameType);
11874 DR_FrameTypeInfo* GetFrameTypeInfo(char* aFrameName);
11875 void InitFrameTypeTable();
11876 DR_FrameTreeNode* CreateTreeNode(nsIFrame* aFrame,
11877 const ReflowInput* aReflowInput);
11878 void FindMatchingRule(DR_FrameTreeNode& aNode);
11879 bool RuleMatches(DR_Rule& aRule, DR_FrameTreeNode& aNode);
11880 bool GetToken(FILE* aFile, char* aBuf, size_t aBufSize);
11881 DR_Rule* ParseRule(FILE* aFile);
11882 void ParseRulesFile();
11883 void AddRule(nsTArray<DR_Rule*>& aRules, DR_Rule& aRule);
11884 bool IsWhiteSpace(int c);
11885 bool GetNumber(char* aBuf, int32_t& aNumber);
11886 void PrettyUC(nscoord aSize, char* aBuf, int aBufSize);
11887 void PrintMargin(const char* tag, const nsMargin* aMargin);
11888 void DisplayFrameTypeInfo(nsIFrame* aFrame, int32_t aIndent);
11889 void DeleteTreeNode(DR_FrameTreeNode& aNode);
11890
11891 bool mInited;
11892 bool mActive;
11893 int32_t mCount;
11894 int32_t mAssert;
11895 int32_t mIndent;
11896 bool mIndentUndisplayedFrames;
11897 bool mDisplayPixelErrors;
11898 nsTArray<DR_Rule*> mWildRules;
11899 nsTArray<DR_FrameTypeInfo> mFrameTypeTable;
11900 // reflow specific state
11901 nsTArray<DR_FrameTreeNode*> mFrameTreeLeaves;
11902 };
11903
11904 static DR_State* DR_state; // the one and only DR_State
11905
11906 struct DR_RulePart {
DR_RulePartDR_RulePart11907 explicit DR_RulePart(LayoutFrameType aFrameType)
11908 : mFrameType(aFrameType), mNext(0) {}
11909
11910 void Destroy();
11911
11912 LayoutFrameType mFrameType;
11913 DR_RulePart* mNext;
11914 };
11915
Destroy()11916 void DR_RulePart::Destroy() {
11917 if (mNext) {
11918 mNext->Destroy();
11919 }
11920 delete this;
11921 }
11922
11923 struct DR_Rule {
DR_RuleDR_Rule11924 DR_Rule() : mLength(0), mTarget(nullptr), mDisplay(false) {
11925 MOZ_COUNT_CTOR(DR_Rule);
11926 }
~DR_RuleDR_Rule11927 ~DR_Rule() {
11928 if (mTarget) mTarget->Destroy();
11929 MOZ_COUNT_DTOR(DR_Rule);
11930 }
11931 void AddPart(LayoutFrameType aFrameType);
11932
11933 uint32_t mLength;
11934 DR_RulePart* mTarget;
11935 bool mDisplay;
11936 };
11937
AddPart(LayoutFrameType aFrameType)11938 void DR_Rule::AddPart(LayoutFrameType aFrameType) {
11939 DR_RulePart* newPart = new DR_RulePart(aFrameType);
11940 newPart->mNext = mTarget;
11941 mTarget = newPart;
11942 mLength++;
11943 }
11944
~DR_FrameTypeInfo()11945 DR_FrameTypeInfo::~DR_FrameTypeInfo() {
11946 int32_t numElements;
11947 numElements = mRules.Length();
11948 for (int32_t i = numElements - 1; i >= 0; i--) {
11949 delete mRules.ElementAt(i);
11950 }
11951 }
11952
DR_FrameTypeInfo(LayoutFrameType aFrameType,const char * aFrameNameAbbrev,const char * aFrameName)11953 DR_FrameTypeInfo::DR_FrameTypeInfo(LayoutFrameType aFrameType,
11954 const char* aFrameNameAbbrev,
11955 const char* aFrameName) {
11956 mType = aFrameType;
11957 PL_strncpyz(mNameAbbrev, aFrameNameAbbrev, sizeof(mNameAbbrev));
11958 PL_strncpyz(mName, aFrameName, sizeof(mName));
11959 }
11960
11961 struct DR_FrameTreeNode {
DR_FrameTreeNodeDR_FrameTreeNode11962 DR_FrameTreeNode(nsIFrame* aFrame, DR_FrameTreeNode* aParent)
11963 : mFrame(aFrame), mParent(aParent), mDisplay(0), mIndent(0) {
11964 MOZ_COUNT_CTOR(DR_FrameTreeNode);
11965 }
11966
11967 MOZ_COUNTED_DTOR(DR_FrameTreeNode)
11968
11969 nsIFrame* mFrame;
11970 DR_FrameTreeNode* mParent;
11971 bool mDisplay;
11972 uint32_t mIndent;
11973 };
11974
11975 // DR_State implementation
11976
DR_State()11977 DR_State::DR_State()
11978 : mInited(false),
11979 mActive(false),
11980 mCount(0),
11981 mAssert(-1),
11982 mIndent(0),
11983 mIndentUndisplayedFrames(false),
11984 mDisplayPixelErrors(false) {
11985 MOZ_COUNT_CTOR(DR_State);
11986 }
11987
Init()11988 void DR_State::Init() {
11989 char* env = PR_GetEnv("GECKO_DISPLAY_REFLOW_ASSERT");
11990 int32_t num;
11991 if (env) {
11992 if (GetNumber(env, num))
11993 mAssert = num;
11994 else
11995 printf("GECKO_DISPLAY_REFLOW_ASSERT - invalid value = %s", env);
11996 }
11997
11998 env = PR_GetEnv("GECKO_DISPLAY_REFLOW_INDENT_START");
11999 if (env) {
12000 if (GetNumber(env, num))
12001 mIndent = num;
12002 else
12003 printf("GECKO_DISPLAY_REFLOW_INDENT_START - invalid value = %s", env);
12004 }
12005
12006 env = PR_GetEnv("GECKO_DISPLAY_REFLOW_INDENT_UNDISPLAYED_FRAMES");
12007 if (env) {
12008 if (GetNumber(env, num))
12009 mIndentUndisplayedFrames = num;
12010 else
12011 printf(
12012 "GECKO_DISPLAY_REFLOW_INDENT_UNDISPLAYED_FRAMES - invalid value = %s",
12013 env);
12014 }
12015
12016 env = PR_GetEnv("GECKO_DISPLAY_REFLOW_FLAG_PIXEL_ERRORS");
12017 if (env) {
12018 if (GetNumber(env, num))
12019 mDisplayPixelErrors = num;
12020 else
12021 printf("GECKO_DISPLAY_REFLOW_FLAG_PIXEL_ERRORS - invalid value = %s",
12022 env);
12023 }
12024
12025 InitFrameTypeTable();
12026 ParseRulesFile();
12027 mInited = true;
12028 }
12029
~DR_State()12030 DR_State::~DR_State() {
12031 MOZ_COUNT_DTOR(DR_State);
12032 int32_t numElements, i;
12033 numElements = mWildRules.Length();
12034 for (i = numElements - 1; i >= 0; i--) {
12035 delete mWildRules.ElementAt(i);
12036 }
12037 numElements = mFrameTreeLeaves.Length();
12038 for (i = numElements - 1; i >= 0; i--) {
12039 delete mFrameTreeLeaves.ElementAt(i);
12040 }
12041 }
12042
GetNumber(char * aBuf,int32_t & aNumber)12043 bool DR_State::GetNumber(char* aBuf, int32_t& aNumber) {
12044 if (sscanf(aBuf, "%d", &aNumber) > 0)
12045 return true;
12046 else
12047 return false;
12048 }
12049
IsWhiteSpace(int c)12050 bool DR_State::IsWhiteSpace(int c) {
12051 return (c == ' ') || (c == '\t') || (c == '\n') || (c == '\r');
12052 }
12053
GetToken(FILE * aFile,char * aBuf,size_t aBufSize)12054 bool DR_State::GetToken(FILE* aFile, char* aBuf, size_t aBufSize) {
12055 bool haveToken = false;
12056 aBuf[0] = 0;
12057 // get the 1st non whitespace char
12058 int c = -1;
12059 for (c = getc(aFile); (c > 0) && IsWhiteSpace(c); c = getc(aFile)) {
12060 }
12061
12062 if (c > 0) {
12063 haveToken = true;
12064 aBuf[0] = c;
12065 // get everything up to the next whitespace char
12066 size_t cX;
12067 for (cX = 1; cX + 1 < aBufSize; cX++) {
12068 c = getc(aFile);
12069 if (c < 0) { // EOF
12070 ungetc(' ', aFile);
12071 break;
12072 } else {
12073 if (IsWhiteSpace(c)) {
12074 break;
12075 } else {
12076 aBuf[cX] = c;
12077 }
12078 }
12079 }
12080 aBuf[cX] = 0;
12081 }
12082 return haveToken;
12083 }
12084
ParseRule(FILE * aFile)12085 DR_Rule* DR_State::ParseRule(FILE* aFile) {
12086 char buf[128];
12087 int32_t doDisplay;
12088 DR_Rule* rule = nullptr;
12089 while (GetToken(aFile, buf, sizeof(buf))) {
12090 if (GetNumber(buf, doDisplay)) {
12091 if (rule) {
12092 rule->mDisplay = !!doDisplay;
12093 break;
12094 } else {
12095 printf("unexpected token - %s \n", buf);
12096 }
12097 } else {
12098 if (!rule) {
12099 rule = new DR_Rule;
12100 }
12101 if (strcmp(buf, "*") == 0) {
12102 rule->AddPart(LayoutFrameType::None);
12103 } else {
12104 DR_FrameTypeInfo* info = GetFrameTypeInfo(buf);
12105 if (info) {
12106 rule->AddPart(info->mType);
12107 } else {
12108 printf("invalid frame type - %s \n", buf);
12109 }
12110 }
12111 }
12112 }
12113 return rule;
12114 }
12115
AddRule(nsTArray<DR_Rule * > & aRules,DR_Rule & aRule)12116 void DR_State::AddRule(nsTArray<DR_Rule*>& aRules, DR_Rule& aRule) {
12117 int32_t numRules = aRules.Length();
12118 for (int32_t ruleX = 0; ruleX < numRules; ruleX++) {
12119 DR_Rule* rule = aRules.ElementAt(ruleX);
12120 NS_ASSERTION(rule, "program error");
12121 if (aRule.mLength > rule->mLength) {
12122 aRules.InsertElementAt(ruleX, &aRule);
12123 return;
12124 }
12125 }
12126 aRules.AppendElement(&aRule);
12127 }
12128
ShouldLogReflow(const char * processes)12129 static Maybe<bool> ShouldLogReflow(const char* processes) {
12130 switch (processes[0]) {
12131 case 'A':
12132 case 'a':
12133 return Some(true);
12134 case 'P':
12135 case 'p':
12136 return Some(XRE_IsParentProcess());
12137 case 'C':
12138 case 'c':
12139 return Some(XRE_IsContentProcess());
12140 default:
12141 return Nothing{};
12142 }
12143 }
12144
ParseRulesFile()12145 void DR_State::ParseRulesFile() {
12146 char* processes = PR_GetEnv("GECKO_DISPLAY_REFLOW_PROCESSES");
12147 if (processes) {
12148 Maybe<bool> enableLog = ShouldLogReflow(processes);
12149 if (enableLog.isNothing()) {
12150 MOZ_CRASH("GECKO_DISPLAY_REFLOW_PROCESSES: [a]ll [p]arent [c]ontent");
12151 } else if (enableLog.value()) {
12152 DR_Rule* rule = new DR_Rule;
12153 rule->AddPart(LayoutFrameType::None);
12154 rule->mDisplay = true;
12155 AddRule(mWildRules, *rule);
12156 mActive = true;
12157 }
12158 return;
12159 }
12160
12161 char* path = PR_GetEnv("GECKO_DISPLAY_REFLOW_RULES_FILE");
12162 if (path) {
12163 FILE* inFile = fopen(path, "r");
12164 if (!inFile) {
12165 MOZ_CRASH(
12166 "Failed to open the specified rules file; Try `--setpref "
12167 "security.sandbox.content.level=2` if the sandbox is at cause");
12168 }
12169 for (DR_Rule* rule = ParseRule(inFile); rule; rule = ParseRule(inFile)) {
12170 if (rule->mTarget) {
12171 LayoutFrameType fType = rule->mTarget->mFrameType;
12172 if (fType != LayoutFrameType::None) {
12173 DR_FrameTypeInfo* info = GetFrameTypeInfo(fType);
12174 AddRule(info->mRules, *rule);
12175 } else {
12176 AddRule(mWildRules, *rule);
12177 }
12178 mActive = true;
12179 }
12180 }
12181
12182 fclose(inFile);
12183 }
12184 }
12185
AddFrameTypeInfo(LayoutFrameType aFrameType,const char * aFrameNameAbbrev,const char * aFrameName)12186 void DR_State::AddFrameTypeInfo(LayoutFrameType aFrameType,
12187 const char* aFrameNameAbbrev,
12188 const char* aFrameName) {
12189 mFrameTypeTable.EmplaceBack(aFrameType, aFrameNameAbbrev, aFrameName);
12190 }
12191
GetFrameTypeInfo(LayoutFrameType aFrameType)12192 DR_FrameTypeInfo* DR_State::GetFrameTypeInfo(LayoutFrameType aFrameType) {
12193 int32_t numEntries = mFrameTypeTable.Length();
12194 NS_ASSERTION(numEntries != 0, "empty FrameTypeTable");
12195 for (int32_t i = 0; i < numEntries; i++) {
12196 DR_FrameTypeInfo& info = mFrameTypeTable.ElementAt(i);
12197 if (info.mType == aFrameType) {
12198 return &info;
12199 }
12200 }
12201 return &mFrameTypeTable.ElementAt(numEntries -
12202 1); // return unknown frame type
12203 }
12204
GetFrameTypeInfo(char * aFrameName)12205 DR_FrameTypeInfo* DR_State::GetFrameTypeInfo(char* aFrameName) {
12206 int32_t numEntries = mFrameTypeTable.Length();
12207 NS_ASSERTION(numEntries != 0, "empty FrameTypeTable");
12208 for (int32_t i = 0; i < numEntries; i++) {
12209 DR_FrameTypeInfo& info = mFrameTypeTable.ElementAt(i);
12210 if ((strcmp(aFrameName, info.mName) == 0) ||
12211 (strcmp(aFrameName, info.mNameAbbrev) == 0)) {
12212 return &info;
12213 }
12214 }
12215 return &mFrameTypeTable.ElementAt(numEntries -
12216 1); // return unknown frame type
12217 }
12218
InitFrameTypeTable()12219 void DR_State::InitFrameTypeTable() {
12220 AddFrameTypeInfo(LayoutFrameType::Block, "block", "block");
12221 AddFrameTypeInfo(LayoutFrameType::Br, "br", "br");
12222 AddFrameTypeInfo(LayoutFrameType::Bullet, "bullet", "bullet");
12223 AddFrameTypeInfo(LayoutFrameType::ColorControl, "color", "colorControl");
12224 AddFrameTypeInfo(LayoutFrameType::GfxButtonControl, "button",
12225 "gfxButtonControl");
12226 AddFrameTypeInfo(LayoutFrameType::HTMLButtonControl, "HTMLbutton",
12227 "HTMLButtonControl");
12228 AddFrameTypeInfo(LayoutFrameType::HTMLCanvas, "HTMLCanvas", "HTMLCanvas");
12229 AddFrameTypeInfo(LayoutFrameType::SubDocument, "subdoc", "subDocument");
12230 AddFrameTypeInfo(LayoutFrameType::Image, "img", "image");
12231 AddFrameTypeInfo(LayoutFrameType::Inline, "inline", "inline");
12232 AddFrameTypeInfo(LayoutFrameType::Letter, "letter", "letter");
12233 AddFrameTypeInfo(LayoutFrameType::Line, "line", "line");
12234 AddFrameTypeInfo(LayoutFrameType::ListControl, "select", "select");
12235 AddFrameTypeInfo(LayoutFrameType::Object, "obj", "object");
12236 AddFrameTypeInfo(LayoutFrameType::Page, "page", "page");
12237 AddFrameTypeInfo(LayoutFrameType::Placeholder, "place", "placeholder");
12238 AddFrameTypeInfo(LayoutFrameType::Canvas, "canvas", "canvas");
12239 AddFrameTypeInfo(LayoutFrameType::Root, "root", "root");
12240 AddFrameTypeInfo(LayoutFrameType::Scroll, "scroll", "scroll");
12241 AddFrameTypeInfo(LayoutFrameType::TableCell, "cell", "tableCell");
12242 AddFrameTypeInfo(LayoutFrameType::TableCol, "col", "tableCol");
12243 AddFrameTypeInfo(LayoutFrameType::TableColGroup, "colG", "tableColGroup");
12244 AddFrameTypeInfo(LayoutFrameType::Table, "tbl", "table");
12245 AddFrameTypeInfo(LayoutFrameType::TableWrapper, "tblW", "tableWrapper");
12246 AddFrameTypeInfo(LayoutFrameType::TableRowGroup, "rowG", "tableRowGroup");
12247 AddFrameTypeInfo(LayoutFrameType::TableRow, "row", "tableRow");
12248 AddFrameTypeInfo(LayoutFrameType::TextInput, "textCtl", "textInput");
12249 AddFrameTypeInfo(LayoutFrameType::Text, "text", "text");
12250 AddFrameTypeInfo(LayoutFrameType::Viewport, "VP", "viewport");
12251 # ifdef MOZ_XUL
12252 AddFrameTypeInfo(LayoutFrameType::XULLabel, "XULLabel", "XULLabel");
12253 AddFrameTypeInfo(LayoutFrameType::Box, "Box", "Box");
12254 AddFrameTypeInfo(LayoutFrameType::Slider, "Slider", "Slider");
12255 AddFrameTypeInfo(LayoutFrameType::PopupSet, "PopupSet", "PopupSet");
12256 # endif
12257 AddFrameTypeInfo(LayoutFrameType::None, "unknown", "unknown");
12258 }
12259
DisplayFrameTypeInfo(nsIFrame * aFrame,int32_t aIndent)12260 void DR_State::DisplayFrameTypeInfo(nsIFrame* aFrame, int32_t aIndent) {
12261 DR_FrameTypeInfo* frameTypeInfo = GetFrameTypeInfo(aFrame->Type());
12262 if (frameTypeInfo) {
12263 for (int32_t i = 0; i < aIndent; i++) {
12264 printf(" ");
12265 }
12266 if (!strcmp(frameTypeInfo->mNameAbbrev, "unknown")) {
12267 if (aFrame) {
12268 nsAutoString name;
12269 aFrame->GetFrameName(name);
12270 printf("%s %p ", NS_LossyConvertUTF16toASCII(name).get(),
12271 (void*)aFrame);
12272 } else {
12273 printf("%s %p ", frameTypeInfo->mNameAbbrev, (void*)aFrame);
12274 }
12275 } else {
12276 printf("%s %p ", frameTypeInfo->mNameAbbrev, (void*)aFrame);
12277 }
12278 }
12279 }
12280
RuleMatches(DR_Rule & aRule,DR_FrameTreeNode & aNode)12281 bool DR_State::RuleMatches(DR_Rule& aRule, DR_FrameTreeNode& aNode) {
12282 NS_ASSERTION(aRule.mTarget, "program error");
12283
12284 DR_RulePart* rulePart;
12285 DR_FrameTreeNode* parentNode;
12286 for (rulePart = aRule.mTarget->mNext, parentNode = aNode.mParent;
12287 rulePart && parentNode;
12288 rulePart = rulePart->mNext, parentNode = parentNode->mParent) {
12289 if (rulePart->mFrameType != LayoutFrameType::None) {
12290 if (parentNode->mFrame) {
12291 if (rulePart->mFrameType != parentNode->mFrame->Type()) {
12292 return false;
12293 }
12294 } else
12295 NS_ASSERTION(false, "program error");
12296 }
12297 // else wild card match
12298 }
12299 return true;
12300 }
12301
FindMatchingRule(DR_FrameTreeNode & aNode)12302 void DR_State::FindMatchingRule(DR_FrameTreeNode& aNode) {
12303 if (!aNode.mFrame) {
12304 NS_ASSERTION(false, "invalid DR_FrameTreeNode \n");
12305 return;
12306 }
12307
12308 bool matchingRule = false;
12309
12310 DR_FrameTypeInfo* info = GetFrameTypeInfo(aNode.mFrame->Type());
12311 NS_ASSERTION(info, "program error");
12312 int32_t numRules = info->mRules.Length();
12313 for (int32_t ruleX = 0; ruleX < numRules; ruleX++) {
12314 DR_Rule* rule = info->mRules.ElementAt(ruleX);
12315 if (rule && RuleMatches(*rule, aNode)) {
12316 aNode.mDisplay = rule->mDisplay;
12317 matchingRule = true;
12318 break;
12319 }
12320 }
12321 if (!matchingRule) {
12322 int32_t numWildRules = mWildRules.Length();
12323 for (int32_t ruleX = 0; ruleX < numWildRules; ruleX++) {
12324 DR_Rule* rule = mWildRules.ElementAt(ruleX);
12325 if (rule && RuleMatches(*rule, aNode)) {
12326 aNode.mDisplay = rule->mDisplay;
12327 break;
12328 }
12329 }
12330 }
12331 }
12332
CreateTreeNode(nsIFrame * aFrame,const ReflowInput * aReflowInput)12333 DR_FrameTreeNode* DR_State::CreateTreeNode(nsIFrame* aFrame,
12334 const ReflowInput* aReflowInput) {
12335 // find the frame of the parent reflow input (usually just the parent of
12336 // aFrame)
12337 nsIFrame* parentFrame;
12338 if (aReflowInput) {
12339 const ReflowInput* parentRI = aReflowInput->mParentReflowInput;
12340 parentFrame = (parentRI) ? parentRI->mFrame : nullptr;
12341 } else {
12342 parentFrame = aFrame->GetParent();
12343 }
12344
12345 // find the parent tree node leaf
12346 DR_FrameTreeNode* parentNode = nullptr;
12347
12348 DR_FrameTreeNode* lastLeaf = nullptr;
12349 if (mFrameTreeLeaves.Length())
12350 lastLeaf = mFrameTreeLeaves.ElementAt(mFrameTreeLeaves.Length() - 1);
12351 if (lastLeaf) {
12352 for (parentNode = lastLeaf;
12353 parentNode && (parentNode->mFrame != parentFrame);
12354 parentNode = parentNode->mParent) {
12355 }
12356 }
12357 DR_FrameTreeNode* newNode = new DR_FrameTreeNode(aFrame, parentNode);
12358 FindMatchingRule(*newNode);
12359
12360 newNode->mIndent = mIndent;
12361 if (newNode->mDisplay || mIndentUndisplayedFrames) {
12362 ++mIndent;
12363 }
12364
12365 if (lastLeaf && (lastLeaf == parentNode)) {
12366 mFrameTreeLeaves.RemoveLastElement();
12367 }
12368 mFrameTreeLeaves.AppendElement(newNode);
12369 mCount++;
12370
12371 return newNode;
12372 }
12373
PrettyUC(nscoord aSize,char * aBuf,int aBufSize)12374 void DR_State::PrettyUC(nscoord aSize, char* aBuf, int aBufSize) {
12375 if (NS_UNCONSTRAINEDSIZE == aSize) {
12376 strcpy(aBuf, "UC");
12377 } else {
12378 if ((nscoord)0xdeadbeefU == aSize) {
12379 strcpy(aBuf, "deadbeef");
12380 } else {
12381 snprintf(aBuf, aBufSize, "%d", aSize);
12382 }
12383 }
12384 }
12385
PrintMargin(const char * tag,const nsMargin * aMargin)12386 void DR_State::PrintMargin(const char* tag, const nsMargin* aMargin) {
12387 if (aMargin) {
12388 char t[16], r[16], b[16], l[16];
12389 PrettyUC(aMargin->top, t, 16);
12390 PrettyUC(aMargin->right, r, 16);
12391 PrettyUC(aMargin->bottom, b, 16);
12392 PrettyUC(aMargin->left, l, 16);
12393 printf(" %s=%s,%s,%s,%s", tag, t, r, b, l);
12394 } else {
12395 // use %p here for consistency with other null-pointer printouts
12396 printf(" %s=%p", tag, (void*)aMargin);
12397 }
12398 }
12399
DeleteTreeNode(DR_FrameTreeNode & aNode)12400 void DR_State::DeleteTreeNode(DR_FrameTreeNode& aNode) {
12401 mFrameTreeLeaves.RemoveElement(&aNode);
12402 int32_t numLeaves = mFrameTreeLeaves.Length();
12403 if ((0 == numLeaves) ||
12404 (aNode.mParent != mFrameTreeLeaves.ElementAt(numLeaves - 1))) {
12405 mFrameTreeLeaves.AppendElement(aNode.mParent);
12406 }
12407
12408 if (aNode.mDisplay || mIndentUndisplayedFrames) {
12409 --mIndent;
12410 }
12411 // delete the tree node
12412 delete &aNode;
12413 }
12414
CheckPixelError(nscoord aSize,int32_t aPixelToTwips)12415 static void CheckPixelError(nscoord aSize, int32_t aPixelToTwips) {
12416 if (NS_UNCONSTRAINEDSIZE != aSize) {
12417 if ((aSize % aPixelToTwips) > 0) {
12418 printf("VALUE %d is not a whole pixel \n", aSize);
12419 }
12420 }
12421 }
12422
DisplayReflowEnterPrint(nsPresContext * aPresContext,nsIFrame * aFrame,const ReflowInput & aReflowInput,DR_FrameTreeNode & aTreeNode,bool aChanged)12423 static void DisplayReflowEnterPrint(nsPresContext* aPresContext,
12424 nsIFrame* aFrame,
12425 const ReflowInput& aReflowInput,
12426 DR_FrameTreeNode& aTreeNode,
12427 bool aChanged) {
12428 if (aTreeNode.mDisplay) {
12429 DR_state->DisplayFrameTypeInfo(aFrame, aTreeNode.mIndent);
12430
12431 char width[16];
12432 char height[16];
12433
12434 DR_state->PrettyUC(aReflowInput.AvailableWidth(), width, 16);
12435 DR_state->PrettyUC(aReflowInput.AvailableHeight(), height, 16);
12436 printf("Reflow a=%s,%s ", width, height);
12437
12438 DR_state->PrettyUC(aReflowInput.ComputedWidth(), width, 16);
12439 DR_state->PrettyUC(aReflowInput.ComputedHeight(), height, 16);
12440 printf("c=%s,%s ", width, height);
12441
12442 if (aFrame->GetStateBits() & NS_FRAME_IS_DIRTY) printf("dirty ");
12443
12444 if (aFrame->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN)
12445 printf("dirty-children ");
12446
12447 if (aReflowInput.mFlags.mSpecialBSizeReflow) printf("special-bsize ");
12448
12449 if (aReflowInput.IsHResize()) printf("h-resize ");
12450
12451 if (aReflowInput.IsVResize()) printf("v-resize ");
12452
12453 nsIFrame* inFlow = aFrame->GetPrevInFlow();
12454 if (inFlow) {
12455 printf("pif=%p ", (void*)inFlow);
12456 }
12457 inFlow = aFrame->GetNextInFlow();
12458 if (inFlow) {
12459 printf("nif=%p ", (void*)inFlow);
12460 }
12461 if (aChanged)
12462 printf("CHANGED \n");
12463 else
12464 printf("cnt=%d \n", DR_state->mCount);
12465 if (DR_state->mDisplayPixelErrors) {
12466 int32_t d2a = aPresContext->AppUnitsPerDevPixel();
12467 CheckPixelError(aReflowInput.AvailableWidth(), d2a);
12468 CheckPixelError(aReflowInput.AvailableHeight(), d2a);
12469 CheckPixelError(aReflowInput.ComputedWidth(), d2a);
12470 CheckPixelError(aReflowInput.ComputedHeight(), d2a);
12471 }
12472 }
12473 }
12474
DisplayReflowEnter(nsPresContext * aPresContext,nsIFrame * aFrame,const ReflowInput & aReflowInput)12475 void* nsFrame::DisplayReflowEnter(nsPresContext* aPresContext, nsIFrame* aFrame,
12476 const ReflowInput& aReflowInput) {
12477 if (!DR_state->mInited) DR_state->Init();
12478 if (!DR_state->mActive) return nullptr;
12479
12480 NS_ASSERTION(aFrame, "invalid call");
12481
12482 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, &aReflowInput);
12483 if (treeNode) {
12484 DisplayReflowEnterPrint(aPresContext, aFrame, aReflowInput, *treeNode,
12485 false);
12486 }
12487 return treeNode;
12488 }
12489
DisplayLayoutEnter(nsIFrame * aFrame)12490 void* nsFrame::DisplayLayoutEnter(nsIFrame* aFrame) {
12491 if (!DR_state->mInited) DR_state->Init();
12492 if (!DR_state->mActive) return nullptr;
12493
12494 NS_ASSERTION(aFrame, "invalid call");
12495
12496 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr);
12497 if (treeNode && treeNode->mDisplay) {
12498 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12499 printf("XULLayout\n");
12500 }
12501 return treeNode;
12502 }
12503
DisplayIntrinsicISizeEnter(nsIFrame * aFrame,const char * aType)12504 void* nsFrame::DisplayIntrinsicISizeEnter(nsIFrame* aFrame, const char* aType) {
12505 if (!DR_state->mInited) DR_state->Init();
12506 if (!DR_state->mActive) return nullptr;
12507
12508 NS_ASSERTION(aFrame, "invalid call");
12509
12510 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr);
12511 if (treeNode && treeNode->mDisplay) {
12512 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12513 printf("Get%sISize\n", aType);
12514 }
12515 return treeNode;
12516 }
12517
DisplayIntrinsicSizeEnter(nsIFrame * aFrame,const char * aType)12518 void* nsFrame::DisplayIntrinsicSizeEnter(nsIFrame* aFrame, const char* aType) {
12519 if (!DR_state->mInited) DR_state->Init();
12520 if (!DR_state->mActive) return nullptr;
12521
12522 NS_ASSERTION(aFrame, "invalid call");
12523
12524 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr);
12525 if (treeNode && treeNode->mDisplay) {
12526 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12527 printf("Get%sSize\n", aType);
12528 }
12529 return treeNode;
12530 }
12531
DisplayReflowExit(nsPresContext * aPresContext,nsIFrame * aFrame,ReflowOutput & aMetrics,const nsReflowStatus & aStatus,void * aFrameTreeNode)12532 void nsFrame::DisplayReflowExit(nsPresContext* aPresContext, nsIFrame* aFrame,
12533 ReflowOutput& aMetrics,
12534 const nsReflowStatus& aStatus,
12535 void* aFrameTreeNode) {
12536 if (!DR_state->mActive) return;
12537
12538 NS_ASSERTION(aFrame, "DisplayReflowExit - invalid call");
12539 if (!aFrameTreeNode) return;
12540
12541 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
12542 if (treeNode->mDisplay) {
12543 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12544
12545 char width[16];
12546 char height[16];
12547 char x[16];
12548 char y[16];
12549 DR_state->PrettyUC(aMetrics.Width(), width, 16);
12550 DR_state->PrettyUC(aMetrics.Height(), height, 16);
12551 printf("Reflow d=%s,%s", width, height);
12552
12553 if (!aStatus.IsEmpty()) {
12554 printf(" status=%s", ToString(aStatus).c_str());
12555 }
12556 if (aFrame->HasOverflowAreas()) {
12557 DR_state->PrettyUC(aMetrics.VisualOverflow().x, x, 16);
12558 DR_state->PrettyUC(aMetrics.VisualOverflow().y, y, 16);
12559 DR_state->PrettyUC(aMetrics.VisualOverflow().width, width, 16);
12560 DR_state->PrettyUC(aMetrics.VisualOverflow().height, height, 16);
12561 printf(" vis-o=(%s,%s) %s x %s", x, y, width, height);
12562
12563 nsRect storedOverflow = aFrame->GetVisualOverflowRect();
12564 DR_state->PrettyUC(storedOverflow.x, x, 16);
12565 DR_state->PrettyUC(storedOverflow.y, y, 16);
12566 DR_state->PrettyUC(storedOverflow.width, width, 16);
12567 DR_state->PrettyUC(storedOverflow.height, height, 16);
12568 printf(" vis-sto=(%s,%s) %s x %s", x, y, width, height);
12569
12570 DR_state->PrettyUC(aMetrics.ScrollableOverflow().x, x, 16);
12571 DR_state->PrettyUC(aMetrics.ScrollableOverflow().y, y, 16);
12572 DR_state->PrettyUC(aMetrics.ScrollableOverflow().width, width, 16);
12573 DR_state->PrettyUC(aMetrics.ScrollableOverflow().height, height, 16);
12574 printf(" scr-o=(%s,%s) %s x %s", x, y, width, height);
12575
12576 storedOverflow = aFrame->GetScrollableOverflowRect();
12577 DR_state->PrettyUC(storedOverflow.x, x, 16);
12578 DR_state->PrettyUC(storedOverflow.y, y, 16);
12579 DR_state->PrettyUC(storedOverflow.width, width, 16);
12580 DR_state->PrettyUC(storedOverflow.height, height, 16);
12581 printf(" scr-sto=(%s,%s) %s x %s", x, y, width, height);
12582 }
12583 printf("\n");
12584 if (DR_state->mDisplayPixelErrors) {
12585 int32_t d2a = aPresContext->AppUnitsPerDevPixel();
12586 CheckPixelError(aMetrics.Width(), d2a);
12587 CheckPixelError(aMetrics.Height(), d2a);
12588 }
12589 }
12590 DR_state->DeleteTreeNode(*treeNode);
12591 }
12592
DisplayLayoutExit(nsIFrame * aFrame,void * aFrameTreeNode)12593 void nsFrame::DisplayLayoutExit(nsIFrame* aFrame, void* aFrameTreeNode) {
12594 if (!DR_state->mActive) return;
12595
12596 NS_ASSERTION(aFrame, "non-null frame required");
12597 if (!aFrameTreeNode) return;
12598
12599 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
12600 if (treeNode->mDisplay) {
12601 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12602 nsRect rect = aFrame->GetRect();
12603 printf("XULLayout=%d,%d,%d,%d\n", rect.x, rect.y, rect.width, rect.height);
12604 }
12605 DR_state->DeleteTreeNode(*treeNode);
12606 }
12607
DisplayIntrinsicISizeExit(nsIFrame * aFrame,const char * aType,nscoord aResult,void * aFrameTreeNode)12608 void nsFrame::DisplayIntrinsicISizeExit(nsIFrame* aFrame, const char* aType,
12609 nscoord aResult, void* aFrameTreeNode) {
12610 if (!DR_state->mActive) return;
12611
12612 NS_ASSERTION(aFrame, "non-null frame required");
12613 if (!aFrameTreeNode) return;
12614
12615 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
12616 if (treeNode->mDisplay) {
12617 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12618 char iSize[16];
12619 DR_state->PrettyUC(aResult, iSize, 16);
12620 printf("Get%sISize=%s\n", aType, iSize);
12621 }
12622 DR_state->DeleteTreeNode(*treeNode);
12623 }
12624
DisplayIntrinsicSizeExit(nsIFrame * aFrame,const char * aType,nsSize aResult,void * aFrameTreeNode)12625 void nsFrame::DisplayIntrinsicSizeExit(nsIFrame* aFrame, const char* aType,
12626 nsSize aResult, void* aFrameTreeNode) {
12627 if (!DR_state->mActive) return;
12628
12629 NS_ASSERTION(aFrame, "non-null frame required");
12630 if (!aFrameTreeNode) return;
12631
12632 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aFrameTreeNode;
12633 if (treeNode->mDisplay) {
12634 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12635
12636 char width[16];
12637 char height[16];
12638 DR_state->PrettyUC(aResult.width, width, 16);
12639 DR_state->PrettyUC(aResult.height, height, 16);
12640 printf("Get%sSize=%s,%s\n", aType, width, height);
12641 }
12642 DR_state->DeleteTreeNode(*treeNode);
12643 }
12644
12645 /* static */
DisplayReflowStartup()12646 void nsFrame::DisplayReflowStartup() { DR_state = new DR_State(); }
12647
12648 /* static */
DisplayReflowShutdown()12649 void nsFrame::DisplayReflowShutdown() {
12650 delete DR_state;
12651 DR_state = nullptr;
12652 }
12653
Change() const12654 void DR_cookie::Change() const {
12655 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)mValue;
12656 if (treeNode && treeNode->mDisplay) {
12657 DisplayReflowEnterPrint(mPresContext, mFrame, mReflowInput, *treeNode,
12658 true);
12659 }
12660 }
12661
12662 /* static */
DisplayInitConstraintsEnter(nsIFrame * aFrame,ReflowInput * aState,nscoord aContainingBlockWidth,nscoord aContainingBlockHeight,const nsMargin * aBorder,const nsMargin * aPadding)12663 void* ReflowInput::DisplayInitConstraintsEnter(nsIFrame* aFrame,
12664 ReflowInput* aState,
12665 nscoord aContainingBlockWidth,
12666 nscoord aContainingBlockHeight,
12667 const nsMargin* aBorder,
12668 const nsMargin* aPadding) {
12669 MOZ_ASSERT(aFrame, "non-null frame required");
12670 MOZ_ASSERT(aState, "non-null state required");
12671
12672 if (!DR_state->mInited) DR_state->Init();
12673 if (!DR_state->mActive) return nullptr;
12674
12675 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, aState);
12676 if (treeNode && treeNode->mDisplay) {
12677 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12678
12679 printf("InitConstraints parent=%p", (void*)aState->mParentReflowInput);
12680
12681 char width[16];
12682 char height[16];
12683
12684 DR_state->PrettyUC(aContainingBlockWidth, width, 16);
12685 DR_state->PrettyUC(aContainingBlockHeight, height, 16);
12686 printf(" cb=%s,%s", width, height);
12687
12688 DR_state->PrettyUC(aState->AvailableWidth(), width, 16);
12689 DR_state->PrettyUC(aState->AvailableHeight(), height, 16);
12690 printf(" as=%s,%s", width, height);
12691
12692 DR_state->PrintMargin("b", aBorder);
12693 DR_state->PrintMargin("p", aPadding);
12694 putchar('\n');
12695 }
12696 return treeNode;
12697 }
12698
12699 /* static */
DisplayInitConstraintsExit(nsIFrame * aFrame,ReflowInput * aState,void * aValue)12700 void ReflowInput::DisplayInitConstraintsExit(nsIFrame* aFrame,
12701 ReflowInput* aState,
12702 void* aValue) {
12703 MOZ_ASSERT(aFrame, "non-null frame required");
12704 MOZ_ASSERT(aState, "non-null state required");
12705
12706 if (!DR_state->mActive) return;
12707 if (!aValue) return;
12708
12709 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue;
12710 if (treeNode->mDisplay) {
12711 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12712 char cmiw[16], cw[16], cmxw[16], cmih[16], ch[16], cmxh[16];
12713 DR_state->PrettyUC(aState->ComputedMinWidth(), cmiw, 16);
12714 DR_state->PrettyUC(aState->ComputedWidth(), cw, 16);
12715 DR_state->PrettyUC(aState->ComputedMaxWidth(), cmxw, 16);
12716 DR_state->PrettyUC(aState->ComputedMinHeight(), cmih, 16);
12717 DR_state->PrettyUC(aState->ComputedHeight(), ch, 16);
12718 DR_state->PrettyUC(aState->ComputedMaxHeight(), cmxh, 16);
12719 printf("InitConstraints= cw=(%s <= %s <= %s) ch=(%s <= %s <= %s)", cmiw, cw,
12720 cmxw, cmih, ch, cmxh);
12721 DR_state->PrintMargin("co", &aState->ComputedPhysicalOffsets());
12722 putchar('\n');
12723 }
12724 DR_state->DeleteTreeNode(*treeNode);
12725 }
12726
12727 /* static */
DisplayInitOffsetsEnter(nsIFrame * aFrame,SizeComputationInput * aState,nscoord aPercentBasis,WritingMode aCBWritingMode,const nsMargin * aBorder,const nsMargin * aPadding)12728 void* SizeComputationInput::DisplayInitOffsetsEnter(
12729 nsIFrame* aFrame, SizeComputationInput* aState, nscoord aPercentBasis,
12730 WritingMode aCBWritingMode, const nsMargin* aBorder,
12731 const nsMargin* aPadding) {
12732 MOZ_ASSERT(aFrame, "non-null frame required");
12733 MOZ_ASSERT(aState, "non-null state required");
12734
12735 if (!DR_state->mInited) DR_state->Init();
12736 if (!DR_state->mActive) return nullptr;
12737
12738 // aState is not necessarily a ReflowInput
12739 DR_FrameTreeNode* treeNode = DR_state->CreateTreeNode(aFrame, nullptr);
12740 if (treeNode && treeNode->mDisplay) {
12741 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12742
12743 char pctBasisStr[16];
12744 DR_state->PrettyUC(aPercentBasis, pctBasisStr, 16);
12745 printf("InitOffsets pct_basis=%s", pctBasisStr);
12746
12747 DR_state->PrintMargin("b", aBorder);
12748 DR_state->PrintMargin("p", aPadding);
12749 putchar('\n');
12750 }
12751 return treeNode;
12752 }
12753
12754 /* static */
DisplayInitOffsetsExit(nsIFrame * aFrame,SizeComputationInput * aState,void * aValue)12755 void SizeComputationInput::DisplayInitOffsetsExit(nsIFrame* aFrame,
12756 SizeComputationInput* aState,
12757 void* aValue) {
12758 MOZ_ASSERT(aFrame, "non-null frame required");
12759 MOZ_ASSERT(aState, "non-null state required");
12760
12761 if (!DR_state->mActive) return;
12762 if (!aValue) return;
12763
12764 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue;
12765 if (treeNode->mDisplay) {
12766 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12767 printf("InitOffsets=");
12768 DR_state->PrintMargin("m", &aState->ComputedPhysicalMargin());
12769 DR_state->PrintMargin("p", &aState->ComputedPhysicalPadding());
12770 DR_state->PrintMargin("p+b", &aState->ComputedPhysicalBorderPadding());
12771 putchar('\n');
12772 }
12773 DR_state->DeleteTreeNode(*treeNode);
12774 }
12775
12776 /* static */
DisplayInitFrameTypeEnter(nsIFrame * aFrame,ReflowInput * aState)12777 void* ReflowInput::DisplayInitFrameTypeEnter(nsIFrame* aFrame,
12778 ReflowInput* aState) {
12779 MOZ_ASSERT(aFrame, "non-null frame required");
12780 MOZ_ASSERT(aState, "non-null state required");
12781
12782 if (!DR_state->mInited) DR_state->Init();
12783 if (!DR_state->mActive) return nullptr;
12784
12785 // we don't print anything here
12786 return DR_state->CreateTreeNode(aFrame, aState);
12787 }
12788
12789 /* static */
DisplayInitFrameTypeExit(nsIFrame * aFrame,ReflowInput * aState,void * aValue)12790 void ReflowInput::DisplayInitFrameTypeExit(nsIFrame* aFrame,
12791 ReflowInput* aState, void* aValue) {
12792 MOZ_ASSERT(aFrame, "non-null frame required");
12793 MOZ_ASSERT(aState, "non-null state required");
12794
12795 if (!DR_state->mActive) return;
12796 if (!aValue) return;
12797
12798 DR_FrameTreeNode* treeNode = (DR_FrameTreeNode*)aValue;
12799 if (treeNode->mDisplay) {
12800 DR_state->DisplayFrameTypeInfo(aFrame, treeNode->mIndent);
12801 printf("InitFrameType");
12802
12803 if (aFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) printf(" out-of-flow");
12804 if (aFrame->GetPrevInFlow()) printf(" prev-in-flow");
12805 if (aFrame->IsAbsolutelyPositioned()) printf(" abspos");
12806 if (aFrame->IsFloating()) printf(" float");
12807
12808 {
12809 nsAutoString result;
12810 aFrame->Style()->GetComputedPropertyValue(eCSSProperty_display, result);
12811 printf(" display=%s", NS_ConvertUTF16toUTF8(result).get());
12812 }
12813
12814 // This array must exactly match the NS_CSS_FRAME_TYPE constants.
12815 const char* const cssFrameTypes[] = {
12816 "unknown", "inline", "block", "floating", "absolute", "internal-table"};
12817 nsCSSFrameType bareType = NS_FRAME_GET_TYPE(aState->mFrameType);
12818 bool repNoBlock = NS_FRAME_IS_REPLACED_NOBLOCK(aState->mFrameType);
12819 bool repBlock = NS_FRAME_IS_REPLACED_CONTAINS_BLOCK(aState->mFrameType);
12820
12821 if (bareType >= ArrayLength(cssFrameTypes)) {
12822 printf(" result=type %u", bareType);
12823 } else {
12824 printf(" result=%s", cssFrameTypes[bareType]);
12825 }
12826 printf("%s%s\n", repNoBlock ? " +rep" : "", repBlock ? " +repBlk" : "");
12827 }
12828 DR_state->DeleteTreeNode(*treeNode);
12829 }
12830
12831 // End Display Reflow
12832
12833 // Validation of SideIsVertical.
12834 # define CASE(side, result) \
12835 static_assert(SideIsVertical(side) == result, "SideIsVertical is wrong")
12836 CASE(eSideTop, false);
12837 CASE(eSideRight, true);
12838 CASE(eSideBottom, false);
12839 CASE(eSideLeft, true);
12840 # undef CASE
12841
12842 // Validation of HalfCornerIsX.
12843 # define CASE(corner, result) \
12844 static_assert(HalfCornerIsX(corner) == result, "HalfCornerIsX is wrong")
12845 CASE(eCornerTopLeftX, true);
12846 CASE(eCornerTopLeftY, false);
12847 CASE(eCornerTopRightX, true);
12848 CASE(eCornerTopRightY, false);
12849 CASE(eCornerBottomRightX, true);
12850 CASE(eCornerBottomRightY, false);
12851 CASE(eCornerBottomLeftX, true);
12852 CASE(eCornerBottomLeftY, false);
12853 # undef CASE
12854
12855 // Validation of HalfToFullCorner.
12856 # define CASE(corner, result) \
12857 static_assert(HalfToFullCorner(corner) == result, \
12858 "HalfToFullCorner is " \
12859 "wrong")
12860 CASE(eCornerTopLeftX, eCornerTopLeft);
12861 CASE(eCornerTopLeftY, eCornerTopLeft);
12862 CASE(eCornerTopRightX, eCornerTopRight);
12863 CASE(eCornerTopRightY, eCornerTopRight);
12864 CASE(eCornerBottomRightX, eCornerBottomRight);
12865 CASE(eCornerBottomRightY, eCornerBottomRight);
12866 CASE(eCornerBottomLeftX, eCornerBottomLeft);
12867 CASE(eCornerBottomLeftY, eCornerBottomLeft);
12868 # undef CASE
12869
12870 // Validation of FullToHalfCorner.
12871 # define CASE(corner, vert, result) \
12872 static_assert(FullToHalfCorner(corner, vert) == result, \
12873 "FullToHalfCorner is wrong")
12874 CASE(eCornerTopLeft, false, eCornerTopLeftX);
12875 CASE(eCornerTopLeft, true, eCornerTopLeftY);
12876 CASE(eCornerTopRight, false, eCornerTopRightX);
12877 CASE(eCornerTopRight, true, eCornerTopRightY);
12878 CASE(eCornerBottomRight, false, eCornerBottomRightX);
12879 CASE(eCornerBottomRight, true, eCornerBottomRightY);
12880 CASE(eCornerBottomLeft, false, eCornerBottomLeftX);
12881 CASE(eCornerBottomLeft, true, eCornerBottomLeftY);
12882 # undef CASE
12883
12884 // Validation of SideToFullCorner.
12885 # define CASE(side, second, result) \
12886 static_assert(SideToFullCorner(side, second) == result, \
12887 "SideToFullCorner is wrong")
12888 CASE(eSideTop, false, eCornerTopLeft);
12889 CASE(eSideTop, true, eCornerTopRight);
12890
12891 CASE(eSideRight, false, eCornerTopRight);
12892 CASE(eSideRight, true, eCornerBottomRight);
12893
12894 CASE(eSideBottom, false, eCornerBottomRight);
12895 CASE(eSideBottom, true, eCornerBottomLeft);
12896
12897 CASE(eSideLeft, false, eCornerBottomLeft);
12898 CASE(eSideLeft, true, eCornerTopLeft);
12899 # undef CASE
12900
12901 // Validation of SideToHalfCorner.
12902 # define CASE(side, second, parallel, result) \
12903 static_assert(SideToHalfCorner(side, second, parallel) == result, \
12904 "SideToHalfCorner is wrong")
12905 CASE(eSideTop, false, true, eCornerTopLeftX);
12906 CASE(eSideTop, false, false, eCornerTopLeftY);
12907 CASE(eSideTop, true, true, eCornerTopRightX);
12908 CASE(eSideTop, true, false, eCornerTopRightY);
12909
12910 CASE(eSideRight, false, false, eCornerTopRightX);
12911 CASE(eSideRight, false, true, eCornerTopRightY);
12912 CASE(eSideRight, true, false, eCornerBottomRightX);
12913 CASE(eSideRight, true, true, eCornerBottomRightY);
12914
12915 CASE(eSideBottom, false, true, eCornerBottomRightX);
12916 CASE(eSideBottom, false, false, eCornerBottomRightY);
12917 CASE(eSideBottom, true, true, eCornerBottomLeftX);
12918 CASE(eSideBottom, true, false, eCornerBottomLeftY);
12919
12920 CASE(eSideLeft, false, false, eCornerBottomLeftX);
12921 CASE(eSideLeft, false, true, eCornerBottomLeftY);
12922 CASE(eSideLeft, true, false, eCornerTopLeftX);
12923 CASE(eSideLeft, true, true, eCornerTopLeftY);
12924 # undef CASE
12925
12926 #endif
12927