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/. */
9 // Netscape Communications
11 // See documentation in associated header file
16 // Boxes layout a bit differently than html. html does a bottom up layout. Where
17 // boxes do a top down.
19 // 1) First thing a box does it goes out and askes each child for its min, max,
20 // and preferred sizes.
22 // 2) It then adds them up to determine its size.
24 // 3) If the box was asked to layout it self intrinically it will layout its
25 // children at their preferred size otherwise it will layout the child at
26 // the size it was told to. It will squeeze or stretch its children if
29 // However there is a catch. Some html components like block frames can not
30 // determine their preferred size. this is their size if they were laid out
31 // intrinsically. So the box will flow the child to determine this can cache the
34 // Boxes and Incremental Reflow
35 // ----------------------------
36 // Boxes layout out top down by adding up their children's min, max, and
37 // preferred sizes. Only problem is if a incremental reflow occurs. The
38 // preferred size of a child deep in the hierarchy could change. And this could
39 // change any number of syblings around the box. Basically any children in the
40 // reflow chain must have their caches cleared so when asked for there current
41 // size they can relayout themselves.
43 #include "nsBoxFrame.h"
46 #include "mozilla/gfx/2D.h"
47 #include "nsBoxLayoutState.h"
48 #include "mozilla/dom/Touch.h"
49 #include "mozilla/Move.h"
50 #include "mozilla/ComputedStyle.h"
51 #include "nsPlaceholderFrame.h"
52 #include "nsPresContext.h"
54 #include "nsNameSpaceManager.h"
55 #include "nsGkAtoms.h"
56 #include "nsIContent.h"
57 #include "nsHTMLParts.h"
58 #include "nsViewManager.h"
60 #include "nsIPresShell.h"
61 #include "nsCSSRendering.h"
62 #include "nsIServiceManager.h"
63 #include "nsBoxLayout.h"
64 #include "nsSprocketLayout.h"
65 #include "nsIScrollableFrame.h"
66 #include "nsWidgetsCID.h"
67 #include "nsCSSAnonBoxes.h"
68 #include "nsContainerFrame.h"
70 #include "nsTransform2D.h"
71 #include "mozilla/EventStateManager.h"
72 #include "nsDisplayList.h"
73 #include "mozilla/Preferences.h"
74 #include "nsStyleConsts.h"
75 #include "nsLayoutUtils.h"
76 #include "nsSliderFrame.h"
79 // Needed for Print Preview
82 #include "mozilla/TouchEvents.h"
84 using namespace mozilla
;
85 using namespace mozilla::dom
;
86 using namespace mozilla::gfx
;
88 nsIFrame
* NS_NewBoxFrame(nsIPresShell
* aPresShell
, ComputedStyle
* aStyle
,
89 bool aIsRoot
, nsBoxLayout
* aLayoutManager
) {
90 return new (aPresShell
)
91 nsBoxFrame(aStyle
, aPresShell
->GetPresContext(), nsBoxFrame::kClassID
,
92 aIsRoot
, aLayoutManager
);
95 nsIFrame
* NS_NewBoxFrame(nsIPresShell
* aPresShell
, ComputedStyle
* aStyle
) {
96 return new (aPresShell
) nsBoxFrame(aStyle
, aPresShell
->GetPresContext());
99 NS_IMPL_FRAMEARENA_HELPERS(nsBoxFrame
)
102 NS_QUERYFRAME_HEAD(nsBoxFrame
)
103 NS_QUERYFRAME_ENTRY(nsBoxFrame
)
104 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame
)
107 nsBoxFrame::nsBoxFrame(ComputedStyle
* aStyle
, nsPresContext
* aPresContext
,
108 ClassID aID
, bool aIsRoot
, nsBoxLayout
* aLayoutManager
)
109 : nsContainerFrame(aStyle
, aPresContext
, aID
), mFlex(0), mAscent(0) {
110 AddStateBits(NS_STATE_IS_HORIZONTAL
| NS_STATE_AUTO_STRETCH
);
112 if (aIsRoot
) AddStateBits(NS_STATE_IS_ROOT
);
114 mValign
= vAlign_Top
;
115 mHalign
= hAlign_Left
;
117 // if no layout manager specified us the static sprocket layout
118 nsCOMPtr
<nsBoxLayout
> layout
= aLayoutManager
;
120 if (layout
== nullptr) {
121 NS_NewSprocketLayout(layout
);
124 SetXULLayoutManager(layout
);
127 nsBoxFrame::~nsBoxFrame() {}
129 void nsBoxFrame::SetInitialChildList(ChildListID aListID
,
130 nsFrameList
& aChildList
) {
131 nsContainerFrame::SetInitialChildList(aListID
, aChildList
);
132 if (aListID
== kPrincipalList
) {
133 // initialize our list of infos.
134 nsBoxLayoutState
state(PresContext());
137 mLayoutManager
->ChildrenSet(this, state
, mFrames
.FirstChild());
142 void nsBoxFrame::DidSetComputedStyle(ComputedStyle
* aOldComputedStyle
) {
143 nsContainerFrame::DidSetComputedStyle(aOldComputedStyle
);
145 // The values that CacheAttributes() computes depend on our style,
146 // so we need to recompute them here...
151 * Initialize us. This is a good time to get the alignment of the box
153 void nsBoxFrame::Init(nsIContent
* aContent
, nsContainerFrame
* aParent
,
154 nsIFrame
* aPrevInFlow
) {
155 nsContainerFrame::Init(aContent
, aParent
, aPrevInFlow
);
157 if (GetStateBits() & NS_FRAME_FONT_INFLATION_CONTAINER
) {
158 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT
);
161 MarkIntrinsicISizesDirty();
165 UpdateMouseThrough();
167 // register access key
168 RegUnregAccessKey(true);
171 void nsBoxFrame::UpdateMouseThrough() {
172 static Element::AttrValuesArray strings
[] = {nsGkAtoms::never
,
173 nsGkAtoms::always
, nullptr};
174 switch (mContent
->AsElement()->FindAttrValueIn(
175 kNameSpaceID_None
, nsGkAtoms::mousethrough
, strings
, eCaseMatters
)) {
177 AddStateBits(NS_FRAME_MOUSE_THROUGH_NEVER
);
180 AddStateBits(NS_FRAME_MOUSE_THROUGH_ALWAYS
);
183 RemoveStateBits(NS_FRAME_MOUSE_THROUGH_ALWAYS
);
184 RemoveStateBits(NS_FRAME_MOUSE_THROUGH_NEVER
);
190 void nsBoxFrame::CacheAttributes() {
197 mValign
= vAlign_Top
;
198 mHalign
= hAlign_Left
;
201 GetInitialOrientation(orient
);
203 AddStateBits(NS_STATE_IS_HORIZONTAL
);
205 RemoveStateBits(NS_STATE_IS_HORIZONTAL
);
208 GetInitialDirection(normal
);
210 AddStateBits(NS_STATE_IS_DIRECTION_NORMAL
);
212 RemoveStateBits(NS_STATE_IS_DIRECTION_NORMAL
);
214 GetInitialVAlignment(mValign
);
215 GetInitialHAlignment(mHalign
);
217 bool equalSize
= false;
218 GetInitialEqualSize(equalSize
);
220 AddStateBits(NS_STATE_EQUAL_SIZE
);
222 RemoveStateBits(NS_STATE_EQUAL_SIZE
);
224 bool autostretch
= !!(mState
& NS_STATE_AUTO_STRETCH
);
225 GetInitialAutoStretch(autostretch
);
227 AddStateBits(NS_STATE_AUTO_STRETCH
);
229 RemoveStateBits(NS_STATE_AUTO_STRETCH
);
232 bool nsBoxFrame::GetInitialHAlignment(nsBoxFrame::Halignment
& aHalign
) {
233 if (!GetContent() || !GetContent()->IsElement()) return false;
235 Element
* element
= GetContent()->AsElement();
236 // XXXdwh Everything inside this if statement is deprecated code.
237 static Element::AttrValuesArray alignStrings
[] = {nsGkAtoms::left
,
238 nsGkAtoms::right
, nullptr};
239 static const Halignment alignValues
[] = {hAlign_Left
, hAlign_Right
};
240 int32_t index
= element
->FindAttrValueIn(kNameSpaceID_None
, nsGkAtoms::align
,
241 alignStrings
, eCaseMatters
);
243 aHalign
= alignValues
[index
];
247 // Now that the deprecated stuff is out of the way, we move on to check the
248 // appropriate attribute. For horizontal boxes, we are checking the PACK
249 // attribute. For vertical boxes we are checking the ALIGN attribute.
250 nsAtom
* attrName
= IsXULHorizontal() ? nsGkAtoms::pack
: nsGkAtoms::align
;
251 static Element::AttrValuesArray strings
[] = {
252 nsGkAtoms::_empty
, nsGkAtoms::start
, nsGkAtoms::center
, nsGkAtoms::end
,
254 static const Halignment values
[] = {hAlign_Left
/*not used*/, hAlign_Left
,
255 hAlign_Center
, hAlign_Right
};
256 index
= element
->FindAttrValueIn(kNameSpaceID_None
, attrName
, strings
,
259 if (index
== Element::ATTR_VALUE_NO_MATCH
) {
260 // The attr was present but had a nonsensical value. Revert to the default.
264 aHalign
= values
[index
];
268 // Now that we've checked for the attribute it's time to check CSS. For
269 // horizontal boxes we're checking PACK. For vertical boxes we are checking
271 const nsStyleXUL
* boxInfo
= StyleXUL();
272 if (IsXULHorizontal()) {
273 switch (boxInfo
->mBoxPack
) {
274 case StyleBoxPack::Start
:
275 aHalign
= nsBoxFrame::hAlign_Left
;
277 case StyleBoxPack::Center
:
278 aHalign
= nsBoxFrame::hAlign_Center
;
280 case StyleBoxPack::End
:
281 aHalign
= nsBoxFrame::hAlign_Right
;
283 default: // Nonsensical value. Just bail.
287 switch (boxInfo
->mBoxAlign
) {
288 case StyleBoxAlign::Start
:
289 aHalign
= nsBoxFrame::hAlign_Left
;
291 case StyleBoxAlign::Center
:
292 aHalign
= nsBoxFrame::hAlign_Center
;
294 case StyleBoxAlign::End
:
295 aHalign
= nsBoxFrame::hAlign_Right
;
297 default: // Nonsensical value. Just bail.
305 bool nsBoxFrame::GetInitialVAlignment(nsBoxFrame::Valignment
& aValign
) {
306 if (!GetContent() || !GetContent()->IsElement()) return false;
308 Element
* element
= GetContent()->AsElement();
310 static Element::AttrValuesArray valignStrings
[] = {
311 nsGkAtoms::top
, nsGkAtoms::baseline
, nsGkAtoms::middle
, nsGkAtoms::bottom
,
313 static const Valignment valignValues
[] = {vAlign_Top
, vAlign_BaseLine
,
314 vAlign_Middle
, vAlign_Bottom
};
315 int32_t index
= element
->FindAttrValueIn(kNameSpaceID_None
, nsGkAtoms::valign
,
316 valignStrings
, eCaseMatters
);
318 aValign
= valignValues
[index
];
322 // Now that the deprecated stuff is out of the way, we move on to check the
323 // appropriate attribute. For horizontal boxes, we are checking the ALIGN
324 // attribute. For vertical boxes we are checking the PACK attribute.
325 nsAtom
* attrName
= IsXULHorizontal() ? nsGkAtoms::align
: nsGkAtoms::pack
;
326 static Element::AttrValuesArray strings
[] = {
327 nsGkAtoms::_empty
, nsGkAtoms::start
, nsGkAtoms::center
,
328 nsGkAtoms::baseline
, nsGkAtoms::end
, nullptr};
329 static const Valignment values
[] = {vAlign_Top
/*not used*/, vAlign_Top
,
330 vAlign_Middle
, vAlign_BaseLine
,
332 index
= element
->FindAttrValueIn(kNameSpaceID_None
, attrName
, strings
,
334 if (index
== Element::ATTR_VALUE_NO_MATCH
) {
335 // The attr was present but had a nonsensical value. Revert to the default.
339 aValign
= values
[index
];
343 // Now that we've checked for the attribute it's time to check CSS. For
344 // horizontal boxes we're checking ALIGN. For vertical boxes we are checking
346 const nsStyleXUL
* boxInfo
= StyleXUL();
347 if (IsXULHorizontal()) {
348 switch (boxInfo
->mBoxAlign
) {
349 case StyleBoxAlign::Start
:
350 aValign
= nsBoxFrame::vAlign_Top
;
352 case StyleBoxAlign::Center
:
353 aValign
= nsBoxFrame::vAlign_Middle
;
355 case StyleBoxAlign::Baseline
:
356 aValign
= nsBoxFrame::vAlign_BaseLine
;
358 case StyleBoxAlign::End
:
359 aValign
= nsBoxFrame::vAlign_Bottom
;
361 default: // Nonsensical value. Just bail.
365 switch (boxInfo
->mBoxPack
) {
366 case StyleBoxPack::Start
:
367 aValign
= nsBoxFrame::vAlign_Top
;
369 case StyleBoxPack::Center
:
370 aValign
= nsBoxFrame::vAlign_Middle
;
372 case StyleBoxPack::End
:
373 aValign
= nsBoxFrame::vAlign_Bottom
;
375 default: // Nonsensical value. Just bail.
383 void nsBoxFrame::GetInitialOrientation(bool& aIsHorizontal
) {
384 // see if we are a vertical or horizontal box.
385 if (!GetContent()) return;
387 // Check the style system first.
388 const nsStyleXUL
* boxInfo
= StyleXUL();
389 if (boxInfo
->mBoxOrient
== StyleBoxOrient::Horizontal
) {
390 aIsHorizontal
= true;
392 aIsHorizontal
= false;
395 // Now see if we have an attribute. The attribute overrides
396 // the style system value.
397 if (!GetContent()->IsElement()) return;
399 static Element::AttrValuesArray strings
[] = {nsGkAtoms::vertical
,
400 nsGkAtoms::horizontal
, nullptr};
401 int32_t index
= GetContent()->AsElement()->FindAttrValueIn(
402 kNameSpaceID_None
, nsGkAtoms::orient
, strings
, eCaseMatters
);
404 aIsHorizontal
= index
== 1;
408 void nsBoxFrame::GetInitialDirection(bool& aIsNormal
) {
409 if (!GetContent()) return;
411 if (IsXULHorizontal()) {
412 // For horizontal boxes only, we initialize our value based off the CSS
413 // 'direction' property. This means that BiDI users will end up with
414 // horizontally inverted chrome.
415 aIsNormal
= (StyleVisibility()->mDirection
==
416 NS_STYLE_DIRECTION_LTR
); // If text runs RTL then so do we.
418 aIsNormal
= true; // Assume a normal direction in the vertical case.
420 // Now check the style system to see if we should invert aIsNormal.
421 const nsStyleXUL
* boxInfo
= StyleXUL();
422 if (boxInfo
->mBoxDirection
== StyleBoxDirection::Reverse
) {
423 aIsNormal
= !aIsNormal
; // Invert our direction.
426 if (!GetContent()->IsElement()) {
430 Element
* element
= GetContent()->AsElement();
432 // Now see if we have an attribute. The attribute overrides
433 // the style system value.
434 if (IsXULHorizontal()) {
435 static Element::AttrValuesArray strings
[] = {
436 nsGkAtoms::reverse
, nsGkAtoms::ltr
, nsGkAtoms::rtl
, nullptr};
437 int32_t index
= element
->FindAttrValueIn(kNameSpaceID_None
, nsGkAtoms::dir
,
438 strings
, eCaseMatters
);
440 bool values
[] = {!aIsNormal
, true, false};
441 aIsNormal
= values
[index
];
443 } else if (element
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::dir
,
444 nsGkAtoms::reverse
, eCaseMatters
)) {
445 aIsNormal
= !aIsNormal
;
449 /* Returns true if it was set.
451 bool nsBoxFrame::GetInitialEqualSize(bool& aEqualSize
) {
452 // see if we are a vertical or horizontal box.
453 if (!GetContent() || !GetContent()->IsElement()) return false;
455 if (GetContent()->AsElement()->AttrValueIs(kNameSpaceID_None
,
456 nsGkAtoms::equalsize
,
457 nsGkAtoms::always
, eCaseMatters
)) {
465 /* Returns true if it was set.
467 bool nsBoxFrame::GetInitialAutoStretch(bool& aStretch
) {
468 if (!GetContent()) return false;
470 // Check the align attribute.
471 if (GetContent()->IsElement()) {
472 static Element::AttrValuesArray strings
[] = {nsGkAtoms::_empty
,
473 nsGkAtoms::stretch
, nullptr};
474 int32_t index
= GetContent()->AsElement()->FindAttrValueIn(
475 kNameSpaceID_None
, nsGkAtoms::align
, strings
, eCaseMatters
);
476 if (index
!= Element::ATTR_MISSING
&& index
!= 0) {
477 aStretch
= index
== 1;
482 // Check the CSS box-align property.
483 const nsStyleXUL
* boxInfo
= StyleXUL();
484 aStretch
= (boxInfo
->mBoxAlign
== StyleBoxAlign::Stretch
);
489 void nsBoxFrame::DidReflow(nsPresContext
* aPresContext
,
490 const ReflowInput
* aReflowInput
) {
491 nsFrameState preserveBits
=
492 mState
& (NS_FRAME_IS_DIRTY
| NS_FRAME_HAS_DIRTY_CHILDREN
);
493 nsFrame::DidReflow(aPresContext
, aReflowInput
);
494 AddStateBits(preserveBits
);
497 bool nsBoxFrame::HonorPrintBackgroundSettings() {
498 return !mContent
->IsInNativeAnonymousSubtree() &&
499 nsContainerFrame::HonorPrintBackgroundSettings();
502 #ifdef DO_NOISY_REFLOW
503 static int myCounter
= 0;
504 static void printSize(char* aDesc
, nscoord aSize
) {
505 printf(" %s: ", aDesc
);
506 if (aSize
== NS_UNCONSTRAINEDSIZE
) {
515 nscoord
nsBoxFrame::GetMinISize(gfxContext
* aRenderingContext
) {
517 DISPLAY_MIN_INLINE_SIZE(this, result
);
519 nsBoxLayoutState
state(PresContext(), aRenderingContext
);
520 nsSize minSize
= GetXULMinSize(state
);
522 // GetXULMinSize returns border-box width, and we want to return content
523 // width. Since Reflow uses the reflow state's border and padding, we
524 // actually just want to subtract what GetXULMinSize added, which is the
525 // result of GetXULBorderAndPadding.
527 GetXULBorderAndPadding(bp
);
529 result
= minSize
.width
- bp
.LeftRight();
530 result
= std::max(result
, 0);
536 nscoord
nsBoxFrame::GetPrefISize(gfxContext
* aRenderingContext
) {
538 DISPLAY_PREF_INLINE_SIZE(this, result
);
540 nsBoxLayoutState
state(PresContext(), aRenderingContext
);
541 nsSize prefSize
= GetXULPrefSize(state
);
543 // GetXULPrefSize returns border-box width, and we want to return content
544 // width. Since Reflow uses the reflow state's border and padding, we
545 // actually just want to subtract what GetXULPrefSize added, which is the
546 // result of GetXULBorderAndPadding.
548 GetXULBorderAndPadding(bp
);
550 result
= prefSize
.width
- bp
.LeftRight();
551 result
= std::max(result
, 0);
556 void nsBoxFrame::Reflow(nsPresContext
* aPresContext
, ReflowOutput
& aDesiredSize
,
557 const ReflowInput
& aReflowInput
,
558 nsReflowStatus
& aStatus
) {
560 // If you make changes to this method, please keep nsLeafBoxFrame::Reflow
561 // in sync, if the changes are applicable there.
563 DO_GLOBAL_REFLOW_COUNT("nsBoxFrame");
564 DISPLAY_REFLOW(aPresContext
, this, aReflowInput
, aDesiredSize
, aStatus
);
565 MOZ_ASSERT(aStatus
.IsEmpty(), "Caller should pass a fresh reflow status!");
568 aReflowInput
.ComputedWidth() >= 0 && aReflowInput
.ComputedHeight() >= 0,
569 "Computed Size < 0");
571 #ifdef DO_NOISY_REFLOW
573 "\n-------------Starting BoxFrame Reflow ----------------------------\n");
574 printf("%p ** nsBF::Reflow %d ", this, myCounter
++);
576 printSize("AW", aReflowInput
.AvailableWidth());
577 printSize("AH", aReflowInput
.AvailableHeight());
578 printSize("CW", aReflowInput
.ComputedWidth());
579 printSize("CH", aReflowInput
.ComputedHeight());
585 // create the layout state
586 nsBoxLayoutState
state(aPresContext
, aReflowInput
.mRenderingContext
,
587 &aReflowInput
, aReflowInput
.mReflowDepth
);
589 WritingMode wm
= aReflowInput
.GetWritingMode();
590 LogicalSize computedSize
= aReflowInput
.ComputedSize();
592 LogicalMargin m
= aReflowInput
.ComputedLogicalBorderPadding();
593 // GetXULBorderAndPadding(m);
595 LogicalSize
prefSize(wm
);
597 // if we are told to layout intrinsic then get our preferred size.
598 NS_ASSERTION(computedSize
.ISize(wm
) != NS_INTRINSICSIZE
,
599 "computed inline size should always be computed");
600 if (computedSize
.BSize(wm
) == NS_INTRINSICSIZE
) {
601 nsSize physicalPrefSize
= GetXULPrefSize(state
);
602 nsSize minSize
= GetXULMinSize(state
);
603 nsSize maxSize
= GetXULMaxSize(state
);
604 // XXXbz isn't GetXULPrefSize supposed to bounds-check for us?
605 physicalPrefSize
= BoundsCheck(minSize
, physicalPrefSize
, maxSize
);
606 prefSize
= LogicalSize(wm
, physicalPrefSize
);
609 // get our desiredSize
610 computedSize
.ISize(wm
) += m
.IStart(wm
) + m
.IEnd(wm
);
612 if (aReflowInput
.ComputedBSize() == NS_INTRINSICSIZE
) {
613 computedSize
.BSize(wm
) = prefSize
.BSize(wm
);
614 // prefSize is border-box but min/max constraints are content-box.
615 nscoord blockDirBorderPadding
=
616 aReflowInput
.ComputedLogicalBorderPadding().BStartEnd(wm
);
617 nscoord contentBSize
= computedSize
.BSize(wm
) - blockDirBorderPadding
;
618 // Note: contentHeight might be negative, but that's OK because min-height
619 // is never negative.
620 computedSize
.BSize(wm
) =
621 aReflowInput
.ApplyMinMaxHeight(contentBSize
) + blockDirBorderPadding
;
623 computedSize
.BSize(wm
) += m
.BStart(wm
) + m
.BEnd(wm
);
626 nsSize physicalSize
= computedSize
.GetPhysicalSize(wm
);
627 nsRect
r(mRect
.x
, mRect
.y
, physicalSize
.width
, physicalSize
.height
);
629 SetXULBounds(state
, r
);
631 // layout our children
634 // ok our child could have gotten bigger. So lets get its bounds
637 LogicalSize boxSize
= GetLogicalSize(wm
);
638 nscoord ascent
= boxSize
.BSize(wm
);
640 // getting the ascent could be a lot of work. Don't get it if
641 // we are the root. The viewport doesn't care about it.
642 if (!(mState
& NS_STATE_IS_ROOT
)) {
643 ascent
= GetXULBoxAscent(state
);
646 aDesiredSize
.SetSize(wm
, boxSize
);
647 aDesiredSize
.SetBlockStartAscent(ascent
);
649 aDesiredSize
.mOverflowAreas
= GetOverflowAreas();
651 #ifdef DO_NOISY_REFLOW
653 printf("%p ** nsBF(done) W:%d H:%d ", this, aDesiredSize
.Width(),
654 aDesiredSize
.Height());
656 if (maxElementSize
) {
657 printf("MW:%d\n", *maxElementWidth
);
664 ReflowAbsoluteFrames(aPresContext
, aDesiredSize
, aReflowInput
, aStatus
);
666 NS_FRAME_SET_TRUNCATION(aStatus
, aReflowInput
, aDesiredSize
);
669 nsSize
nsBoxFrame::GetXULPrefSize(nsBoxLayoutState
& aBoxLayoutState
) {
670 NS_ASSERTION(aBoxLayoutState
.GetRenderingContext(),
671 "must have rendering context");
674 DISPLAY_PREF_SIZE(this, size
);
675 if (!DoesNeedRecalc(mPrefSize
)) {
680 if (IsXULCollapsed()) return size
;
682 // if the size was not completely redefined in CSS then ask our children
683 bool widthSet
, heightSet
;
684 if (!nsIFrame::AddXULPrefSize(this, size
, widthSet
, heightSet
)) {
685 if (mLayoutManager
) {
686 nsSize layoutSize
= mLayoutManager
->GetXULPrefSize(this, aBoxLayoutState
);
687 if (!widthSet
) size
.width
= layoutSize
.width
;
688 if (!heightSet
) size
.height
= layoutSize
.height
;
690 size
= nsBox::GetXULPrefSize(aBoxLayoutState
);
694 nsSize minSize
= GetXULMinSize(aBoxLayoutState
);
695 nsSize maxSize
= GetXULMaxSize(aBoxLayoutState
);
696 mPrefSize
= BoundsCheck(minSize
, size
, maxSize
);
701 nscoord
nsBoxFrame::GetXULBoxAscent(nsBoxLayoutState
& aBoxLayoutState
) {
702 if (!DoesNeedRecalc(mAscent
)) return mAscent
;
704 if (IsXULCollapsed()) return 0;
707 mAscent
= mLayoutManager
->GetAscent(this, aBoxLayoutState
);
709 mAscent
= nsBox::GetXULBoxAscent(aBoxLayoutState
);
714 nsSize
nsBoxFrame::GetXULMinSize(nsBoxLayoutState
& aBoxLayoutState
) {
715 NS_ASSERTION(aBoxLayoutState
.GetRenderingContext(),
716 "must have rendering context");
719 DISPLAY_MIN_SIZE(this, size
);
720 if (!DoesNeedRecalc(mMinSize
)) {
725 if (IsXULCollapsed()) return size
;
727 // if the size was not completely redefined in CSS then ask our children
728 bool widthSet
, heightSet
;
729 if (!nsIFrame::AddXULMinSize(aBoxLayoutState
, this, size
, widthSet
,
731 if (mLayoutManager
) {
732 nsSize layoutSize
= mLayoutManager
->GetXULMinSize(this, aBoxLayoutState
);
733 if (!widthSet
) size
.width
= layoutSize
.width
;
734 if (!heightSet
) size
.height
= layoutSize
.height
;
736 size
= nsBox::GetXULMinSize(aBoxLayoutState
);
745 nsSize
nsBoxFrame::GetXULMaxSize(nsBoxLayoutState
& aBoxLayoutState
) {
746 NS_ASSERTION(aBoxLayoutState
.GetRenderingContext(),
747 "must have rendering context");
749 nsSize
size(NS_INTRINSICSIZE
, NS_INTRINSICSIZE
);
750 DISPLAY_MAX_SIZE(this, size
);
751 if (!DoesNeedRecalc(mMaxSize
)) {
756 if (IsXULCollapsed()) return size
;
758 // if the size was not completely redefined in CSS then ask our children
759 bool widthSet
, heightSet
;
760 if (!nsIFrame::AddXULMaxSize(this, size
, widthSet
, heightSet
)) {
761 if (mLayoutManager
) {
762 nsSize layoutSize
= mLayoutManager
->GetXULMaxSize(this, aBoxLayoutState
);
763 if (!widthSet
) size
.width
= layoutSize
.width
;
764 if (!heightSet
) size
.height
= layoutSize
.height
;
766 size
= nsBox::GetXULMaxSize(aBoxLayoutState
);
775 nscoord
nsBoxFrame::GetXULFlex() {
776 if (!DoesNeedRecalc(mFlex
)) return mFlex
;
778 mFlex
= nsBox::GetXULFlex();
784 * If subclassing please subclass this method not layout.
785 * layout will call this method.
788 nsBoxFrame::DoXULLayout(nsBoxLayoutState
& aState
) {
789 uint32_t oldFlags
= aState
.LayoutFlags();
790 aState
.SetLayoutFlags(0);
793 if (mLayoutManager
) {
794 CoordNeedsRecalc(mAscent
);
795 rv
= mLayoutManager
->XULLayout(this, aState
);
798 aState
.SetLayoutFlags(oldFlags
);
800 if (HasAbsolutelyPositionedChildren()) {
801 // Set up a |reflowInput| to pass into ReflowAbsoluteFrames
802 WritingMode wm
= GetWritingMode();
803 ReflowInput
reflowInput(
804 aState
.PresContext(), this, aState
.GetRenderingContext(),
805 LogicalSize(wm
, GetLogicalSize().ISize(wm
), NS_UNCONSTRAINEDSIZE
));
807 // Set up a |desiredSize| to pass into ReflowAbsoluteFrames
808 ReflowOutput
desiredSize(reflowInput
);
809 desiredSize
.Width() = mRect
.width
;
810 desiredSize
.Height() = mRect
.height
;
812 // get the ascent (cribbed from ::Reflow)
813 nscoord ascent
= mRect
.height
;
815 // getting the ascent could be a lot of work. Don't get it if
816 // we are the root. The viewport doesn't care about it.
817 if (!(mState
& NS_STATE_IS_ROOT
)) {
818 ascent
= GetXULBoxAscent(aState
);
820 desiredSize
.SetBlockStartAscent(ascent
);
821 desiredSize
.mOverflowAreas
= GetOverflowAreas();
823 AddStateBits(NS_FRAME_IN_REFLOW
);
824 // Set up a |reflowStatus| to pass into ReflowAbsoluteFrames
825 // (just a dummy value; hopefully that's OK)
826 nsReflowStatus reflowStatus
;
827 ReflowAbsoluteFrames(aState
.PresContext(), desiredSize
, reflowInput
,
829 RemoveStateBits(NS_FRAME_IN_REFLOW
);
835 void nsBoxFrame::DestroyFrom(nsIFrame
* aDestructRoot
,
836 PostDestroyData
& aPostDestroyData
) {
837 // unregister access key
838 RegUnregAccessKey(false);
840 // clean up the container box's layout manager and child boxes
841 SetXULLayoutManager(nullptr);
843 nsContainerFrame::DestroyFrom(aDestructRoot
, aPostDestroyData
);
847 void nsBoxFrame::MarkIntrinsicISizesDirty() {
848 SizeNeedsRecalc(mPrefSize
);
849 SizeNeedsRecalc(mMinSize
);
850 SizeNeedsRecalc(mMaxSize
);
851 CoordNeedsRecalc(mFlex
);
852 CoordNeedsRecalc(mAscent
);
854 if (mLayoutManager
) {
855 nsBoxLayoutState
state(PresContext());
856 mLayoutManager
->IntrinsicISizesDirty(this, state
);
859 // Don't call base class method, since everything it does is within an
860 // IsXULBoxWrapped check.
863 void nsBoxFrame::RemoveFrame(ChildListID aListID
, nsIFrame
* aOldFrame
) {
864 MOZ_ASSERT(aListID
== kPrincipalList
, "We don't support out-of-flow kids");
866 nsPresContext
* presContext
= PresContext();
867 nsBoxLayoutState
state(presContext
);
869 // remove the child frame
870 mFrames
.RemoveFrame(aOldFrame
);
872 // notify the layout manager
873 if (mLayoutManager
) mLayoutManager
->ChildrenRemoved(this, state
, aOldFrame
);
875 // destroy the child frame
876 aOldFrame
->Destroy();
878 // mark us dirty and generate a reflow command
879 PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange
,
880 NS_FRAME_HAS_DIRTY_CHILDREN
);
883 void nsBoxFrame::InsertFrames(ChildListID aListID
, nsIFrame
* aPrevFrame
,
884 nsFrameList
& aFrameList
) {
885 NS_ASSERTION(!aPrevFrame
|| aPrevFrame
->GetParent() == this,
886 "inserting after sibling frame with different parent");
887 NS_ASSERTION(!aPrevFrame
|| mFrames
.ContainsFrame(aPrevFrame
),
888 "inserting after sibling frame not in our child list");
889 MOZ_ASSERT(aListID
== kPrincipalList
, "We don't support out-of-flow kids");
891 nsBoxLayoutState
state(PresContext());
893 // insert the child frames
894 const nsFrameList::Slice
& newFrames
=
895 mFrames
.InsertFrames(this, aPrevFrame
, aFrameList
);
897 // notify the layout manager
899 mLayoutManager
->ChildrenInserted(this, state
, aPrevFrame
, newFrames
);
901 // Make sure to check box order _after_ notifying the layout
902 // manager; otherwise the slice we give the layout manager will
903 // just be bogus. If the layout manager cares about the order, we
907 PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange
,
908 NS_FRAME_HAS_DIRTY_CHILDREN
);
911 void nsBoxFrame::AppendFrames(ChildListID aListID
, nsFrameList
& aFrameList
) {
912 MOZ_ASSERT(aListID
== kPrincipalList
, "We don't support out-of-flow kids");
914 nsBoxLayoutState
state(PresContext());
916 // append the new frames
917 const nsFrameList::Slice
& newFrames
= mFrames
.AppendFrames(this, aFrameList
);
919 // notify the layout manager
920 if (mLayoutManager
) mLayoutManager
->ChildrenAppended(this, state
, newFrames
);
922 // Make sure to check box order _after_ notifying the layout
923 // manager; otherwise the slice we give the layout manager will
924 // just be bogus. If the layout manager cares about the order, we
928 // XXXbz why is this NS_FRAME_FIRST_REFLOW check here?
929 if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW
)) {
930 PresShell()->FrameNeedsReflow(this, nsIPresShell::eTreeChange
,
931 NS_FRAME_HAS_DIRTY_CHILDREN
);
936 nsContainerFrame
* nsBoxFrame::GetContentInsertionFrame() {
937 if (GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK
)
938 return PrincipalChildList().FirstChild()->GetContentInsertionFrame();
939 return nsContainerFrame::GetContentInsertionFrame();
942 nsresult
nsBoxFrame::AttributeChanged(int32_t aNameSpaceID
, nsAtom
* aAttribute
,
945 nsContainerFrame::AttributeChanged(aNameSpaceID
, aAttribute
, aModType
);
947 // Ignore 'width', 'height', 'screenX', 'screenY' and 'sizemode' on a
949 if (mContent
->IsAnyOfXULElements(nsGkAtoms::window
, nsGkAtoms::page
,
950 nsGkAtoms::dialog
, nsGkAtoms::wizard
) &&
951 (nsGkAtoms::width
== aAttribute
|| nsGkAtoms::height
== aAttribute
||
952 nsGkAtoms::screenX
== aAttribute
|| nsGkAtoms::screenY
== aAttribute
||
953 nsGkAtoms::sizemode
== aAttribute
)) {
957 if (aAttribute
== nsGkAtoms::width
|| aAttribute
== nsGkAtoms::height
||
958 aAttribute
== nsGkAtoms::align
|| aAttribute
== nsGkAtoms::valign
||
959 aAttribute
== nsGkAtoms::left
|| aAttribute
== nsGkAtoms::top
||
960 aAttribute
== nsGkAtoms::right
|| aAttribute
== nsGkAtoms::bottom
||
961 aAttribute
== nsGkAtoms::start
|| aAttribute
== nsGkAtoms::end
||
962 aAttribute
== nsGkAtoms::minwidth
|| aAttribute
== nsGkAtoms::maxwidth
||
963 aAttribute
== nsGkAtoms::minheight
||
964 aAttribute
== nsGkAtoms::maxheight
|| aAttribute
== nsGkAtoms::flex
||
965 aAttribute
== nsGkAtoms::orient
|| aAttribute
== nsGkAtoms::pack
||
966 aAttribute
== nsGkAtoms::dir
|| aAttribute
== nsGkAtoms::mousethrough
||
967 aAttribute
== nsGkAtoms::equalsize
) {
968 if (aAttribute
== nsGkAtoms::align
|| aAttribute
== nsGkAtoms::valign
||
969 aAttribute
== nsGkAtoms::orient
|| aAttribute
== nsGkAtoms::pack
||
970 aAttribute
== nsGkAtoms::dir
) {
971 mValign
= nsBoxFrame::vAlign_Top
;
972 mHalign
= nsBoxFrame::hAlign_Left
;
975 GetInitialOrientation(orient
);
977 AddStateBits(NS_STATE_IS_HORIZONTAL
);
979 RemoveStateBits(NS_STATE_IS_HORIZONTAL
);
982 GetInitialDirection(normal
);
984 AddStateBits(NS_STATE_IS_DIRECTION_NORMAL
);
986 RemoveStateBits(NS_STATE_IS_DIRECTION_NORMAL
);
988 GetInitialVAlignment(mValign
);
989 GetInitialHAlignment(mHalign
);
991 bool equalSize
= false;
992 GetInitialEqualSize(equalSize
);
994 AddStateBits(NS_STATE_EQUAL_SIZE
);
996 RemoveStateBits(NS_STATE_EQUAL_SIZE
);
998 bool autostretch
= !!(mState
& NS_STATE_AUTO_STRETCH
);
999 GetInitialAutoStretch(autostretch
);
1001 AddStateBits(NS_STATE_AUTO_STRETCH
);
1003 RemoveStateBits(NS_STATE_AUTO_STRETCH
);
1004 } else if (aAttribute
== nsGkAtoms::left
|| aAttribute
== nsGkAtoms::top
||
1005 aAttribute
== nsGkAtoms::right
||
1006 aAttribute
== nsGkAtoms::bottom
||
1007 aAttribute
== nsGkAtoms::start
|| aAttribute
== nsGkAtoms::end
) {
1008 RemoveStateBits(NS_STATE_STACK_NOT_POSITIONED
);
1009 } else if (aAttribute
== nsGkAtoms::mousethrough
) {
1010 UpdateMouseThrough();
1013 PresShell()->FrameNeedsReflow(this, nsIPresShell::eStyleChange
,
1015 } else if (aAttribute
== nsGkAtoms::ordinal
) {
1016 nsIFrame
* parent
= GetParentXULBox(this);
1017 // If our parent is not a box, there's not much we can do... but in that
1018 // case our ordinal doesn't matter anyway, so that's ok.
1019 // Also don't bother with popup frames since they are kept on the
1020 // kPopupList and XULRelayoutChildAtOrdinal() only handles
1021 // principal children.
1022 if (parent
&& !(GetStateBits() & NS_FRAME_OUT_OF_FLOW
) &&
1023 StyleDisplay()->mDisplay
!= mozilla::StyleDisplay::MozPopup
) {
1024 parent
->XULRelayoutChildAtOrdinal(this);
1025 // XXXldb Should this instead be a tree change on the child or parent?
1026 PresShell()->FrameNeedsReflow(parent
, nsIPresShell::eStyleChange
,
1030 // If the accesskey changed, register for the new value
1031 // The old value has been unregistered in nsXULElement::SetAttr
1032 else if (aAttribute
== nsGkAtoms::accesskey
) {
1033 RegUnregAccessKey(true);
1034 } else if (aAttribute
== nsGkAtoms::rows
&&
1035 mContent
->IsXULElement(nsGkAtoms::tree
)) {
1036 // Reflow ourselves and all our children if "rows" changes, since
1037 // nsTreeBodyFrame's layout reads this from its parent (this frame).
1038 PresShell()->FrameNeedsReflow(this, nsIPresShell::eStyleChange
,
1045 void nsBoxFrame::BuildDisplayList(nsDisplayListBuilder
* aBuilder
,
1046 const nsDisplayListSet
& aLists
) {
1047 bool forceLayer
= false;
1049 if (GetContent()->IsXULElement()) {
1050 // forcelayer is only supported on XUL elements with box layout
1051 if (GetContent()->AsElement()->HasAttr(kNameSpaceID_None
,
1052 nsGkAtoms::layer
)) {
1055 // Check for frames that are marked as a part of the region used
1056 // in calculating glass margins on Windows.
1057 const nsStyleDisplay
* styles
= StyleDisplay();
1058 if (styles
&& styles
->mAppearance
== StyleAppearance::MozWinExcludeGlass
) {
1059 aBuilder
->AddWindowExcludeGlassRegion(
1060 this, nsRect(aBuilder
->ToReferenceFrame(this), GetSize()));
1064 nsDisplayListCollection
tempLists(aBuilder
);
1065 const nsDisplayListSet
& destination
= forceLayer
? tempLists
: aLists
;
1067 DisplayBorderBackgroundOutline(aBuilder
, destination
);
1069 Maybe
<nsDisplayListBuilder::AutoContainerASRTracker
> contASRTracker
;
1071 contASRTracker
.emplace(aBuilder
);
1074 BuildDisplayListForChildren(aBuilder
, destination
);
1076 // see if we have to draw a selection frame around this container
1077 DisplaySelectionOverlay(aBuilder
, destination
.Content());
1080 // This is a bit of a hack. Collect up all descendant display items
1081 // and merge them into a single Content() list. This can cause us
1082 // to violate CSS stacking order, but forceLayer is a magic
1083 // XUL-only extension anyway.
1084 nsDisplayList masterList
;
1085 masterList
.AppendToTop(tempLists
.BorderBackground());
1086 masterList
.AppendToTop(tempLists
.BlockBorderBackgrounds());
1087 masterList
.AppendToTop(tempLists
.Floats());
1088 masterList
.AppendToTop(tempLists
.Content());
1089 masterList
.AppendToTop(tempLists
.PositionedDescendants());
1090 masterList
.AppendToTop(tempLists
.Outlines());
1092 const ActiveScrolledRoot
* ownLayerASR
= contASRTracker
->GetContainerASR();
1094 DisplayListClipState::AutoSaveRestore
ownLayerClipState(aBuilder
);
1096 // Wrap the list to make it its own layer
1097 aLists
.Content()->AppendToTop(MakeDisplayItem
<nsDisplayOwnLayer
>(
1098 aBuilder
, this, &masterList
, ownLayerASR
, nsDisplayOwnLayerFlags::eNone
,
1099 mozilla::layers::ScrollbarData
{}, true, true));
1103 void nsBoxFrame::BuildDisplayListForChildren(nsDisplayListBuilder
* aBuilder
,
1104 const nsDisplayListSet
& aLists
) {
1105 nsIFrame
* kid
= mFrames
.FirstChild();
1106 // Put each child's background onto the BlockBorderBackgrounds list
1107 // to emulate the existing two-layer XUL painting scheme.
1108 nsDisplayListSet
set(aLists
, aLists
.BlockBorderBackgrounds());
1109 // The children should be in the right order
1111 BuildDisplayListForChild(aBuilder
, kid
, set
);
1112 kid
= kid
->GetNextSibling();
1116 #ifdef DEBUG_FRAME_DUMP
1117 nsresult
nsBoxFrame::GetFrameName(nsAString
& aResult
) const {
1118 return MakeFrameName(NS_LITERAL_STRING("Box"), aResult
);
1122 // If you make changes to this function, check its counterparts
1123 // in nsTextBoxFrame and nsXULLabelFrame
1124 void nsBoxFrame::RegUnregAccessKey(bool aDoReg
) {
1125 MOZ_ASSERT(mContent
);
1127 // only support accesskeys for the following elements
1128 if (!mContent
->IsAnyOfXULElements(nsGkAtoms::button
, nsGkAtoms::toolbarbutton
,
1129 nsGkAtoms::checkbox
, nsGkAtoms::textbox
,
1130 nsGkAtoms::tab
, nsGkAtoms::radio
)) {
1134 nsAutoString accessKey
;
1135 mContent
->AsElement()->GetAttr(kNameSpaceID_None
, nsGkAtoms::accesskey
,
1138 if (accessKey
.IsEmpty()) return;
1140 // With a valid PresContext we can get the ESM
1141 // and register the access key
1142 EventStateManager
* esm
= PresContext()->EventStateManager();
1144 uint32_t key
= accessKey
.First();
1146 esm
->RegisterAccessKey(mContent
->AsElement(), key
);
1148 esm
->UnregisterAccessKey(mContent
->AsElement(), key
);
1151 void nsBoxFrame::AppendDirectlyOwnedAnonBoxes(nsTArray
<OwnedAnonBox
>& aResult
) {
1152 if (GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK
) {
1153 aResult
.AppendElement(OwnedAnonBox(PrincipalChildList().FirstChild()));
1157 // Helper less-than-or-equal function, used in CheckBoxOrder() as a
1158 // template-parameter for the sorting functions.
1159 static bool IsBoxOrdinalLEQ(nsIFrame
* aFrame1
, nsIFrame
* aFrame2
) {
1160 // If we've got a placeholder frame, use its out-of-flow frame's ordinal val.
1161 nsIFrame
* aRealFrame1
= nsPlaceholderFrame::GetRealFrameFor(aFrame1
);
1162 nsIFrame
* aRealFrame2
= nsPlaceholderFrame::GetRealFrameFor(aFrame2
);
1163 return aRealFrame1
->GetXULOrdinal() <= aRealFrame2
->GetXULOrdinal();
1166 void nsBoxFrame::CheckBoxOrder() {
1167 if (!nsIFrame::IsFrameListSorted
<IsBoxOrdinalLEQ
>(mFrames
)) {
1168 nsIFrame::SortFrameList
<IsBoxOrdinalLEQ
>(mFrames
);
1172 nsresult
nsBoxFrame::LayoutChildAt(nsBoxLayoutState
& aState
, nsIFrame
* aBox
,
1173 const nsRect
& aRect
) {
1174 // get the current rect
1175 nsRect
oldRect(aBox
->GetRect());
1176 aBox
->SetXULBounds(aState
, aRect
);
1178 bool layout
= NS_SUBTREE_DIRTY(aBox
);
1181 (oldRect
.width
!= aRect
.width
|| oldRect
.height
!= aRect
.height
)) {
1182 return aBox
->XULLayout(aState
);
1188 nsresult
nsBoxFrame::XULRelayoutChildAtOrdinal(nsIFrame
* aChild
) {
1189 uint32_t ord
= aChild
->GetXULOrdinal();
1191 nsIFrame
* child
= mFrames
.FirstChild();
1192 nsIFrame
* newPrevSib
= nullptr;
1195 if (ord
< child
->GetXULOrdinal()) {
1199 if (child
!= aChild
) {
1203 child
= GetNextXULBox(child
);
1206 if (aChild
->GetPrevSibling() == newPrevSib
) {
1207 // This box is not moving.
1211 // Take |aChild| out of its old position in the child list.
1212 mFrames
.RemoveFrame(aChild
);
1214 // Insert it after |newPrevSib| or at the start if it's null.
1215 mFrames
.InsertFrame(nullptr, newPrevSib
, aChild
);
1221 * This wrapper class lets us redirect mouse hits from descendant frames
1222 * of a menu to the menu itself, if they didn't specify 'allowevents'.
1224 * The wrapper simply turns a hit on a descendant element
1225 * into a hit on the menu itself, unless there is an element between the target
1226 * and the menu with the "allowevents" attribute.
1228 * This is used by nsMenuFrame and nsTreeColFrame.
1230 * Note that turning a hit on a descendant element into nullptr, so events
1231 * could fall through to the menu background, might be an appealing
1232 * simplification but it would mean slightly strange behaviour in some cases,
1233 * because grabber wrappers can be created for many individual lists and items,
1234 * so the exact fallthrough behaviour would be complex. E.g. an element with
1235 * "allowevents" on top of the Content() list could receive the event even if it
1236 * was covered by a PositionedDescenants() element without "allowevents". It is
1237 * best to never convert a non-null hit into null.
1239 // REVIEW: This is roughly of what nsMenuFrame::GetFrameForPoint used to do.
1240 // I've made 'allowevents' affect child elements because that seems the only
1241 // reasonable thing to do.
1242 class nsDisplayXULEventRedirector final
: public nsDisplayWrapList
{
1244 nsDisplayXULEventRedirector(nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
1245 nsDisplayItem
* aItem
, nsIFrame
* aTargetFrame
)
1246 : nsDisplayWrapList(aBuilder
, aFrame
, aItem
),
1247 mTargetFrame(aTargetFrame
) {}
1248 nsDisplayXULEventRedirector(nsDisplayListBuilder
* aBuilder
, nsIFrame
* aFrame
,
1249 nsDisplayList
* aList
, nsIFrame
* aTargetFrame
)
1250 : nsDisplayWrapList(aBuilder
, aFrame
, aList
),
1251 mTargetFrame(aTargetFrame
) {}
1252 virtual void HitTest(nsDisplayListBuilder
* aBuilder
, const nsRect
& aRect
,
1253 HitTestState
* aState
,
1254 nsTArray
<nsIFrame
*>* aOutFrames
) override
;
1255 virtual bool ShouldFlattenAway(nsDisplayListBuilder
* aBuilder
) override
{
1258 NS_DISPLAY_DECL_NAME("XULEventRedirector", TYPE_XUL_EVENT_REDIRECTOR
)
1260 nsIFrame
* mTargetFrame
;
1263 void nsDisplayXULEventRedirector::HitTest(nsDisplayListBuilder
* aBuilder
,
1264 const nsRect
& aRect
,
1265 HitTestState
* aState
,
1266 nsTArray
<nsIFrame
*>* aOutFrames
) {
1267 nsTArray
<nsIFrame
*> outFrames
;
1268 mList
.HitTest(aBuilder
, aRect
, aState
, &outFrames
);
1270 bool topMostAdded
= false;
1271 uint32_t localLength
= outFrames
.Length();
1273 for (uint32_t i
= 0; i
< localLength
; i
++) {
1274 for (nsIContent
* content
= outFrames
.ElementAt(i
)->GetContent();
1275 content
&& content
!= mTargetFrame
->GetContent();
1276 content
= content
->GetParent()) {
1277 if (!content
->IsElement() ||
1278 !content
->AsElement()->AttrValueIs(kNameSpaceID_None
,
1279 nsGkAtoms::allowevents
,
1280 nsGkAtoms::_true
, eCaseMatters
)) {
1284 // Events are allowed on 'frame', so let it go.
1285 aOutFrames
->AppendElement(outFrames
.ElementAt(i
));
1286 topMostAdded
= true;
1289 // If there was no hit on the topmost frame or its ancestors,
1290 // add the target frame itself as the first candidate (see bug 562554).
1291 if (!topMostAdded
) {
1292 topMostAdded
= true;
1293 aOutFrames
->AppendElement(mTargetFrame
);
1298 class nsXULEventRedirectorWrapper final
: public nsDisplayWrapper
{
1300 explicit nsXULEventRedirectorWrapper(nsIFrame
* aTargetFrame
)
1301 : mTargetFrame(aTargetFrame
) {}
1302 virtual nsDisplayItem
* WrapList(nsDisplayListBuilder
* aBuilder
,
1304 nsDisplayList
* aList
) override
{
1305 return MakeDisplayItem
<nsDisplayXULEventRedirector
>(aBuilder
, aFrame
, aList
,
1308 virtual nsDisplayItem
* WrapItem(nsDisplayListBuilder
* aBuilder
,
1309 nsDisplayItem
* aItem
) override
{
1310 return MakeDisplayItem
<nsDisplayXULEventRedirector
>(
1311 aBuilder
, aItem
->Frame(), aItem
, mTargetFrame
);
1315 nsIFrame
* mTargetFrame
;
1318 void nsBoxFrame::WrapListsInRedirector(nsDisplayListBuilder
* aBuilder
,
1319 const nsDisplayListSet
& aIn
,
1320 const nsDisplayListSet
& aOut
) {
1321 nsXULEventRedirectorWrapper
wrapper(this);
1322 wrapper
.WrapLists(aBuilder
, this, aIn
, aOut
);
1325 bool nsBoxFrame::GetEventPoint(WidgetGUIEvent
* aEvent
, nsPoint
& aPoint
) {
1326 LayoutDeviceIntPoint refPoint
;
1327 bool res
= GetEventPoint(aEvent
, refPoint
);
1328 aPoint
= nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent
, refPoint
, this);
1332 bool nsBoxFrame::GetEventPoint(WidgetGUIEvent
* aEvent
,
1333 LayoutDeviceIntPoint
& aPoint
) {
1334 NS_ENSURE_TRUE(aEvent
, false);
1336 WidgetTouchEvent
* touchEvent
= aEvent
->AsTouchEvent();
1338 // return false if there is more than one touch on the page, or if
1339 // we can't find a touch point
1340 if (touchEvent
->mTouches
.Length() != 1) {
1344 dom::Touch
* touch
= touchEvent
->mTouches
.SafeElementAt(0);
1348 aPoint
= touch
->mRefPoint
;
1350 aPoint
= aEvent
->mRefPoint
;