Bug 1529208 [wpt PR 15469] - [Code Health] Fix incorrect test name, a=testonly
[gecko.git] / layout / xul / nsBoxFrame.cpp
blob63ed40bae3866b8ca4bc5633da2a16a4318d8cbf
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/. */
7 //
8 // Eric Vaughan
9 // Netscape Communications
11 // See documentation in associated header file
14 // How boxes layout
15 // ----------------
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
27 // Necessary.
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
32 // value.
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"
45 #include "gfxUtils.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"
53 #include "nsCOMPtr.h"
54 #include "nsNameSpaceManager.h"
55 #include "nsGkAtoms.h"
56 #include "nsIContent.h"
57 #include "nsHTMLParts.h"
58 #include "nsViewManager.h"
59 #include "nsView.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"
69 #include "nsITheme.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"
77 #include <algorithm>
79 // Needed for Print Preview
80 #include "nsIURI.h"
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)
101 #ifdef DEBUG
102 NS_QUERYFRAME_HEAD(nsBoxFrame)
103 NS_QUERYFRAME_ENTRY(nsBoxFrame)
104 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
105 #endif
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());
135 CheckBoxOrder();
136 if (mLayoutManager)
137 mLayoutManager->ChildrenSet(this, state, mFrames.FirstChild());
141 /* virtual */
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...
147 CacheAttributes();
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();
163 CacheAttributes();
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)) {
176 case 0:
177 AddStateBits(NS_FRAME_MOUSE_THROUGH_NEVER);
178 break;
179 case 1:
180 AddStateBits(NS_FRAME_MOUSE_THROUGH_ALWAYS);
181 break;
182 case 2: {
183 RemoveStateBits(NS_FRAME_MOUSE_THROUGH_ALWAYS);
184 RemoveStateBits(NS_FRAME_MOUSE_THROUGH_NEVER);
185 break;
190 void nsBoxFrame::CacheAttributes() {
192 printf("Caching: ");
193 XULDumpBox(stdout);
194 printf("\n");
197 mValign = vAlign_Top;
198 mHalign = hAlign_Left;
200 bool orient = false;
201 GetInitialOrientation(orient);
202 if (orient)
203 AddStateBits(NS_STATE_IS_HORIZONTAL);
204 else
205 RemoveStateBits(NS_STATE_IS_HORIZONTAL);
207 bool normal = true;
208 GetInitialDirection(normal);
209 if (normal)
210 AddStateBits(NS_STATE_IS_DIRECTION_NORMAL);
211 else
212 RemoveStateBits(NS_STATE_IS_DIRECTION_NORMAL);
214 GetInitialVAlignment(mValign);
215 GetInitialHAlignment(mHalign);
217 bool equalSize = false;
218 GetInitialEqualSize(equalSize);
219 if (equalSize)
220 AddStateBits(NS_STATE_EQUAL_SIZE);
221 else
222 RemoveStateBits(NS_STATE_EQUAL_SIZE);
224 bool autostretch = !!(mState & NS_STATE_AUTO_STRETCH);
225 GetInitialAutoStretch(autostretch);
226 if (autostretch)
227 AddStateBits(NS_STATE_AUTO_STRETCH);
228 else
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);
242 if (index >= 0) {
243 aHalign = alignValues[index];
244 return true;
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,
253 nullptr};
254 static const Halignment values[] = {hAlign_Left /*not used*/, hAlign_Left,
255 hAlign_Center, hAlign_Right};
256 index = element->FindAttrValueIn(kNameSpaceID_None, attrName, strings,
257 eCaseMatters);
259 if (index == Element::ATTR_VALUE_NO_MATCH) {
260 // The attr was present but had a nonsensical value. Revert to the default.
261 return false;
263 if (index > 0) {
264 aHalign = values[index];
265 return true;
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
270 // ALIGN.
271 const nsStyleXUL* boxInfo = StyleXUL();
272 if (IsXULHorizontal()) {
273 switch (boxInfo->mBoxPack) {
274 case StyleBoxPack::Start:
275 aHalign = nsBoxFrame::hAlign_Left;
276 return true;
277 case StyleBoxPack::Center:
278 aHalign = nsBoxFrame::hAlign_Center;
279 return true;
280 case StyleBoxPack::End:
281 aHalign = nsBoxFrame::hAlign_Right;
282 return true;
283 default: // Nonsensical value. Just bail.
284 return false;
286 } else {
287 switch (boxInfo->mBoxAlign) {
288 case StyleBoxAlign::Start:
289 aHalign = nsBoxFrame::hAlign_Left;
290 return true;
291 case StyleBoxAlign::Center:
292 aHalign = nsBoxFrame::hAlign_Center;
293 return true;
294 case StyleBoxAlign::End:
295 aHalign = nsBoxFrame::hAlign_Right;
296 return true;
297 default: // Nonsensical value. Just bail.
298 return false;
302 return false;
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,
312 nullptr};
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);
317 if (index >= 0) {
318 aValign = valignValues[index];
319 return true;
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,
331 vAlign_Bottom};
332 index = element->FindAttrValueIn(kNameSpaceID_None, attrName, strings,
333 eCaseMatters);
334 if (index == Element::ATTR_VALUE_NO_MATCH) {
335 // The attr was present but had a nonsensical value. Revert to the default.
336 return false;
338 if (index > 0) {
339 aValign = values[index];
340 return true;
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
345 // PACK.
346 const nsStyleXUL* boxInfo = StyleXUL();
347 if (IsXULHorizontal()) {
348 switch (boxInfo->mBoxAlign) {
349 case StyleBoxAlign::Start:
350 aValign = nsBoxFrame::vAlign_Top;
351 return true;
352 case StyleBoxAlign::Center:
353 aValign = nsBoxFrame::vAlign_Middle;
354 return true;
355 case StyleBoxAlign::Baseline:
356 aValign = nsBoxFrame::vAlign_BaseLine;
357 return true;
358 case StyleBoxAlign::End:
359 aValign = nsBoxFrame::vAlign_Bottom;
360 return true;
361 default: // Nonsensical value. Just bail.
362 return false;
364 } else {
365 switch (boxInfo->mBoxPack) {
366 case StyleBoxPack::Start:
367 aValign = nsBoxFrame::vAlign_Top;
368 return true;
369 case StyleBoxPack::Center:
370 aValign = nsBoxFrame::vAlign_Middle;
371 return true;
372 case StyleBoxPack::End:
373 aValign = nsBoxFrame::vAlign_Bottom;
374 return true;
375 default: // Nonsensical value. Just bail.
376 return false;
380 return false;
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;
391 } else {
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);
403 if (index >= 0) {
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.
417 } else
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()) {
427 return;
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);
439 if (index >= 0) {
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)) {
458 aEqualSize = true;
459 return true;
462 return false;
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;
478 return true;
482 // Check the CSS box-align property.
483 const nsStyleXUL* boxInfo = StyleXUL();
484 aStretch = (boxInfo->mBoxAlign == StyleBoxAlign::Stretch);
486 return true;
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) {
507 printf("UC");
508 } else {
509 printf("%d", aSize);
512 #endif
514 /* virtual */
515 nscoord nsBoxFrame::GetMinISize(gfxContext* aRenderingContext) {
516 nscoord result;
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.
526 nsMargin bp;
527 GetXULBorderAndPadding(bp);
529 result = minSize.width - bp.LeftRight();
530 result = std::max(result, 0);
532 return result;
535 /* virtual */
536 nscoord nsBoxFrame::GetPrefISize(gfxContext* aRenderingContext) {
537 nscoord result;
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.
547 nsMargin bp;
548 GetXULBorderAndPadding(bp);
550 result = prefSize.width - bp.LeftRight();
551 result = std::max(result, 0);
553 return result;
556 void nsBoxFrame::Reflow(nsPresContext* aPresContext, ReflowOutput& aDesiredSize,
557 const ReflowInput& aReflowInput,
558 nsReflowStatus& aStatus) {
559 MarkInReflow();
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!");
567 NS_ASSERTION(
568 aReflowInput.ComputedWidth() >= 0 && aReflowInput.ComputedHeight() >= 0,
569 "Computed Size < 0");
571 #ifdef DO_NOISY_REFLOW
572 printf(
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());
581 printf(" *\n");
583 #endif
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;
622 } else {
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
632 XULLayout(state);
634 // ok our child could have gotten bigger. So lets get its bounds
636 // get the ascent
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);
658 } else {
659 printf("MW:?\n");
662 #endif
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");
673 nsSize size(0, 0);
674 DISPLAY_PREF_SIZE(this, size);
675 if (!DoesNeedRecalc(mPrefSize)) {
676 size = mPrefSize;
677 return size;
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;
689 } else {
690 size = nsBox::GetXULPrefSize(aBoxLayoutState);
694 nsSize minSize = GetXULMinSize(aBoxLayoutState);
695 nsSize maxSize = GetXULMaxSize(aBoxLayoutState);
696 mPrefSize = BoundsCheck(minSize, size, maxSize);
698 return mPrefSize;
701 nscoord nsBoxFrame::GetXULBoxAscent(nsBoxLayoutState& aBoxLayoutState) {
702 if (!DoesNeedRecalc(mAscent)) return mAscent;
704 if (IsXULCollapsed()) return 0;
706 if (mLayoutManager)
707 mAscent = mLayoutManager->GetAscent(this, aBoxLayoutState);
708 else
709 mAscent = nsBox::GetXULBoxAscent(aBoxLayoutState);
711 return mAscent;
714 nsSize nsBoxFrame::GetXULMinSize(nsBoxLayoutState& aBoxLayoutState) {
715 NS_ASSERTION(aBoxLayoutState.GetRenderingContext(),
716 "must have rendering context");
718 nsSize size(0, 0);
719 DISPLAY_MIN_SIZE(this, size);
720 if (!DoesNeedRecalc(mMinSize)) {
721 size = mMinSize;
722 return size;
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,
730 heightSet)) {
731 if (mLayoutManager) {
732 nsSize layoutSize = mLayoutManager->GetXULMinSize(this, aBoxLayoutState);
733 if (!widthSet) size.width = layoutSize.width;
734 if (!heightSet) size.height = layoutSize.height;
735 } else {
736 size = nsBox::GetXULMinSize(aBoxLayoutState);
740 mMinSize = size;
742 return size;
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)) {
752 size = mMaxSize;
753 return size;
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;
765 } else {
766 size = nsBox::GetXULMaxSize(aBoxLayoutState);
770 mMaxSize = size;
772 return size;
775 nscoord nsBoxFrame::GetXULFlex() {
776 if (!DoesNeedRecalc(mFlex)) return mFlex;
778 mFlex = nsBox::GetXULFlex();
780 return mFlex;
784 * If subclassing please subclass this method not layout.
785 * layout will call this method.
787 NS_IMETHODIMP
788 nsBoxFrame::DoXULLayout(nsBoxLayoutState& aState) {
789 uint32_t oldFlags = aState.LayoutFlags();
790 aState.SetLayoutFlags(0);
792 nsresult rv = NS_OK;
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,
828 reflowStatus);
829 RemoveStateBits(NS_FRAME_IN_REFLOW);
832 return rv;
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);
846 /* virtual */
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
898 if (mLayoutManager)
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
904 // just lose.
905 CheckBoxOrder();
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
925 // just lose.
926 CheckBoxOrder();
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);
935 /* virtual */
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,
943 int32_t aModType) {
944 nsresult rv =
945 nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
947 // Ignore 'width', 'height', 'screenX', 'screenY' and 'sizemode' on a
948 // <window>.
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)) {
954 return rv;
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;
974 bool orient = true;
975 GetInitialOrientation(orient);
976 if (orient)
977 AddStateBits(NS_STATE_IS_HORIZONTAL);
978 else
979 RemoveStateBits(NS_STATE_IS_HORIZONTAL);
981 bool normal = true;
982 GetInitialDirection(normal);
983 if (normal)
984 AddStateBits(NS_STATE_IS_DIRECTION_NORMAL);
985 else
986 RemoveStateBits(NS_STATE_IS_DIRECTION_NORMAL);
988 GetInitialVAlignment(mValign);
989 GetInitialHAlignment(mHalign);
991 bool equalSize = false;
992 GetInitialEqualSize(equalSize);
993 if (equalSize)
994 AddStateBits(NS_STATE_EQUAL_SIZE);
995 else
996 RemoveStateBits(NS_STATE_EQUAL_SIZE);
998 bool autostretch = !!(mState & NS_STATE_AUTO_STRETCH);
999 GetInitialAutoStretch(autostretch);
1000 if (autostretch)
1001 AddStateBits(NS_STATE_AUTO_STRETCH);
1002 else
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,
1014 NS_FRAME_IS_DIRTY);
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,
1027 NS_FRAME_IS_DIRTY);
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,
1039 NS_FRAME_IS_DIRTY);
1042 return rv;
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)) {
1053 forceLayer = true;
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;
1070 if (forceLayer) {
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());
1079 if (forceLayer) {
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
1110 while (kid) {
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);
1120 #endif
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)) {
1131 return;
1134 nsAutoString accessKey;
1135 mContent->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey,
1136 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();
1145 if (aDoReg)
1146 esm->RegisterAccessKey(mContent->AsElement(), key);
1147 else
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);
1180 if (layout ||
1181 (oldRect.width != aRect.width || oldRect.height != aRect.height)) {
1182 return aBox->XULLayout(aState);
1185 return NS_OK;
1188 nsresult nsBoxFrame::XULRelayoutChildAtOrdinal(nsIFrame* aChild) {
1189 uint32_t ord = aChild->GetXULOrdinal();
1191 nsIFrame* child = mFrames.FirstChild();
1192 nsIFrame* newPrevSib = nullptr;
1194 while (child) {
1195 if (ord < child->GetXULOrdinal()) {
1196 break;
1199 if (child != aChild) {
1200 newPrevSib = child;
1203 child = GetNextXULBox(child);
1206 if (aChild->GetPrevSibling() == newPrevSib) {
1207 // This box is not moving.
1208 return NS_OK;
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);
1217 return NS_OK;
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 {
1243 public:
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 {
1256 return false;
1258 NS_DISPLAY_DECL_NAME("XULEventRedirector", TYPE_XUL_EVENT_REDIRECTOR)
1259 private:
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)) {
1281 continue;
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 {
1299 public:
1300 explicit nsXULEventRedirectorWrapper(nsIFrame* aTargetFrame)
1301 : mTargetFrame(aTargetFrame) {}
1302 virtual nsDisplayItem* WrapList(nsDisplayListBuilder* aBuilder,
1303 nsIFrame* aFrame,
1304 nsDisplayList* aList) override {
1305 return MakeDisplayItem<nsDisplayXULEventRedirector>(aBuilder, aFrame, aList,
1306 mTargetFrame);
1308 virtual nsDisplayItem* WrapItem(nsDisplayListBuilder* aBuilder,
1309 nsDisplayItem* aItem) override {
1310 return MakeDisplayItem<nsDisplayXULEventRedirector>(
1311 aBuilder, aItem->Frame(), aItem, mTargetFrame);
1314 private:
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);
1329 return res;
1332 bool nsBoxFrame::GetEventPoint(WidgetGUIEvent* aEvent,
1333 LayoutDeviceIntPoint& aPoint) {
1334 NS_ENSURE_TRUE(aEvent, false);
1336 WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
1337 if (touchEvent) {
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) {
1341 return false;
1344 dom::Touch* touch = touchEvent->mTouches.SafeElementAt(0);
1345 if (!touch) {
1346 return false;
1348 aPoint = touch->mRefPoint;
1349 } else {
1350 aPoint = aEvent->mRefPoint;
1352 return true;