Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / layout / xul / nsBoxFrame.cpp
blob0e5070f01be88cfcf8e31f867008bd33a80f0bf7
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=2 sw=2 et 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 boxes do a top down.
17 // 1) First thing a box does it goes out and askes each child for its min, max, and preferred sizes.
18 // 2) It then adds them up to determine its size.
19 // 3) If the box was asked to layout it self intrinically it will layout its children at their preferred size
20 // otherwise it will layout the child at the size it was told to. It will squeeze or stretch its children if
21 // Necessary.
23 // However there is a catch. Some html components like block frames can not determine their preferred size.
24 // this is their size if they were laid out intrinsically. So the box will flow the child to determine this can
25 // cache the value.
27 // Boxes and Incremental Reflow
28 // ----------------------------
29 // Boxes layout out top down by adding up their children's min, max, and preferred sizes. Only problem is if a incremental
30 // reflow occurs. The preferred size of a child deep in the hierarchy could change. And this could change
31 // any number of syblings around the box. Basically any children in the reflow chain must have their caches cleared
32 // so when asked for there current size they can relayout themselves.
34 #include "nsBoxFrame.h"
36 #include "gfxUtils.h"
37 #include "mozilla/gfx/2D.h"
38 #include "nsBoxLayoutState.h"
39 #include "mozilla/dom/Touch.h"
40 #include "mozilla/Move.h"
41 #include "nsStyleContext.h"
42 #include "nsPlaceholderFrame.h"
43 #include "nsPresContext.h"
44 #include "nsCOMPtr.h"
45 #include "nsNameSpaceManager.h"
46 #include "nsGkAtoms.h"
47 #include "nsIContent.h"
48 #include "nsHTMLParts.h"
49 #include "nsViewManager.h"
50 #include "nsView.h"
51 #include "nsIPresShell.h"
52 #include "nsCSSRendering.h"
53 #include "nsIServiceManager.h"
54 #include "nsBoxLayout.h"
55 #include "nsSprocketLayout.h"
56 #include "nsIScrollableFrame.h"
57 #include "nsWidgetsCID.h"
58 #include "nsCSSAnonBoxes.h"
59 #include "nsContainerFrame.h"
60 #include "nsIDOMElement.h"
61 #include "nsITheme.h"
62 #include "nsTransform2D.h"
63 #include "mozilla/EventStateManager.h"
64 #include "nsIDOMEvent.h"
65 #include "nsDisplayList.h"
66 #include "mozilla/Preferences.h"
67 #include "nsThemeConstants.h"
68 #include "nsLayoutUtils.h"
69 #include <algorithm>
71 // Needed for Print Preview
72 #include "nsIURI.h"
74 #include "mozilla/TouchEvents.h"
76 using namespace mozilla;
77 using namespace mozilla::dom;
78 using namespace mozilla::gfx;
80 //define DEBUG_REDRAW
82 #define DEBUG_SPRING_SIZE 8
83 #define DEBUG_BORDER_SIZE 2
84 #define COIL_SIZE 8
86 //#define TEST_SANITY
88 #ifdef DEBUG_rods
89 //#define DO_NOISY_REFLOW
90 #endif
92 #ifdef DEBUG_LAYOUT
93 bool nsBoxFrame::gDebug = false;
94 nsIFrame* nsBoxFrame::mDebugChild = nullptr;
95 #endif
97 nsIFrame*
98 NS_NewBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot, nsBoxLayout* aLayoutManager)
100 return new (aPresShell) nsBoxFrame(aContext, aIsRoot, aLayoutManager);
103 nsIFrame*
104 NS_NewBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
106 return new (aPresShell) nsBoxFrame(aContext);
109 NS_IMPL_FRAMEARENA_HELPERS(nsBoxFrame)
111 #ifdef DEBUG
112 NS_QUERYFRAME_HEAD(nsBoxFrame)
113 NS_QUERYFRAME_ENTRY(nsBoxFrame)
114 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
115 #endif
117 nsBoxFrame::nsBoxFrame(nsStyleContext* aContext,
118 bool aIsRoot,
119 nsBoxLayout* aLayoutManager) :
120 nsContainerFrame(aContext)
122 mState |= NS_STATE_IS_HORIZONTAL;
123 mState |= NS_STATE_AUTO_STRETCH;
125 if (aIsRoot)
126 mState |= NS_STATE_IS_ROOT;
128 mValign = vAlign_Top;
129 mHalign = hAlign_Left;
131 // if no layout manager specified us the static sprocket layout
132 nsCOMPtr<nsBoxLayout> layout = aLayoutManager;
134 if (layout == nullptr) {
135 NS_NewSprocketLayout(PresContext()->PresShell(), layout);
138 SetLayoutManager(layout);
141 nsBoxFrame::~nsBoxFrame()
145 void
146 nsBoxFrame::SetInitialChildList(ChildListID aListID,
147 nsFrameList& aChildList)
149 nsContainerFrame::SetInitialChildList(aListID, aChildList);
150 // initialize our list of infos.
151 nsBoxLayoutState state(PresContext());
152 CheckBoxOrder();
153 if (mLayoutManager)
154 mLayoutManager->ChildrenSet(this, state, mFrames.FirstChild());
157 /* virtual */ void
158 nsBoxFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
160 nsContainerFrame::DidSetStyleContext(aOldStyleContext);
162 // The values that CacheAttributes() computes depend on our style,
163 // so we need to recompute them here...
164 CacheAttributes();
168 * Initialize us. This is a good time to get the alignment of the box
170 void
171 nsBoxFrame::Init(nsIContent* aContent,
172 nsContainerFrame* aParent,
173 nsIFrame* aPrevInFlow)
175 nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
177 if (GetStateBits() & NS_FRAME_FONT_INFLATION_CONTAINER) {
178 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
181 MarkIntrinsicISizesDirty();
183 CacheAttributes();
185 #ifdef DEBUG_LAYOUT
186 // if we are root and this
187 if (mState & NS_STATE_IS_ROOT)
188 GetDebugPref(GetPresContext());
189 #endif
191 UpdateMouseThrough();
193 // register access key
194 RegUnregAccessKey(true);
197 void nsBoxFrame::UpdateMouseThrough()
199 if (mContent) {
200 static nsIContent::AttrValuesArray strings[] =
201 {&nsGkAtoms::never, &nsGkAtoms::always, nullptr};
202 switch (mContent->FindAttrValueIn(kNameSpaceID_None,
203 nsGkAtoms::mousethrough, strings, eCaseMatters)) {
204 case 0: AddStateBits(NS_FRAME_MOUSE_THROUGH_NEVER); break;
205 case 1: AddStateBits(NS_FRAME_MOUSE_THROUGH_ALWAYS); break;
206 case 2: {
207 RemoveStateBits(NS_FRAME_MOUSE_THROUGH_ALWAYS);
208 RemoveStateBits(NS_FRAME_MOUSE_THROUGH_NEVER);
209 break;
215 void
216 nsBoxFrame::CacheAttributes()
219 printf("Caching: ");
220 DumpBox(stdout);
221 printf("\n");
224 mValign = vAlign_Top;
225 mHalign = hAlign_Left;
227 bool orient = false;
228 GetInitialOrientation(orient);
229 if (orient)
230 mState |= NS_STATE_IS_HORIZONTAL;
231 else
232 mState &= ~NS_STATE_IS_HORIZONTAL;
234 bool normal = true;
235 GetInitialDirection(normal);
236 if (normal)
237 mState |= NS_STATE_IS_DIRECTION_NORMAL;
238 else
239 mState &= ~NS_STATE_IS_DIRECTION_NORMAL;
241 GetInitialVAlignment(mValign);
242 GetInitialHAlignment(mHalign);
244 bool equalSize = false;
245 GetInitialEqualSize(equalSize);
246 if (equalSize)
247 mState |= NS_STATE_EQUAL_SIZE;
248 else
249 mState &= ~NS_STATE_EQUAL_SIZE;
251 bool autostretch = !!(mState & NS_STATE_AUTO_STRETCH);
252 GetInitialAutoStretch(autostretch);
253 if (autostretch)
254 mState |= NS_STATE_AUTO_STRETCH;
255 else
256 mState &= ~NS_STATE_AUTO_STRETCH;
259 #ifdef DEBUG_LAYOUT
260 bool debug = mState & NS_STATE_SET_TO_DEBUG;
261 bool debugSet = GetInitialDebug(debug);
262 if (debugSet) {
263 mState |= NS_STATE_DEBUG_WAS_SET;
264 if (debug)
265 mState |= NS_STATE_SET_TO_DEBUG;
266 else
267 mState &= ~NS_STATE_SET_TO_DEBUG;
268 } else {
269 mState &= ~NS_STATE_DEBUG_WAS_SET;
271 #endif
274 #ifdef DEBUG_LAYOUT
275 bool
276 nsBoxFrame::GetInitialDebug(bool& aDebug)
278 if (!GetContent())
279 return false;
281 static nsIContent::AttrValuesArray strings[] =
282 {&nsGkAtoms::_false, &nsGkAtoms::_true, nullptr};
283 int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None,
284 nsGkAtoms::debug, strings, eCaseMatters);
285 if (index >= 0) {
286 aDebug = index == 1;
287 return true;
290 return false;
292 #endif
294 bool
295 nsBoxFrame::GetInitialHAlignment(nsBoxFrame::Halignment& aHalign)
297 if (!GetContent())
298 return false;
300 // XXXdwh Everything inside this if statement is deprecated code.
301 static nsIContent::AttrValuesArray alignStrings[] =
302 {&nsGkAtoms::left, &nsGkAtoms::right, nullptr};
303 static const Halignment alignValues[] = {hAlign_Left, hAlign_Right};
304 int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::align,
305 alignStrings, eCaseMatters);
306 if (index >= 0) {
307 aHalign = alignValues[index];
308 return true;
311 // Now that the deprecated stuff is out of the way, we move on to check the appropriate
312 // attribute. For horizontal boxes, we are checking the PACK attribute. For vertical boxes
313 // we are checking the ALIGN attribute.
314 nsIAtom* attrName = IsHorizontal() ? nsGkAtoms::pack : nsGkAtoms::align;
315 static nsIContent::AttrValuesArray strings[] =
316 {&nsGkAtoms::_empty, &nsGkAtoms::start, &nsGkAtoms::center, &nsGkAtoms::end, nullptr};
317 static const Halignment values[] =
318 {hAlign_Left/*not used*/, hAlign_Left, hAlign_Center, hAlign_Right};
319 index = GetContent()->FindAttrValueIn(kNameSpaceID_None, attrName,
320 strings, eCaseMatters);
322 if (index == nsIContent::ATTR_VALUE_NO_MATCH) {
323 // The attr was present but had a nonsensical value. Revert to the default.
324 return false;
326 if (index > 0) {
327 aHalign = values[index];
328 return true;
331 // Now that we've checked for the attribute it's time to check CSS. For
332 // horizontal boxes we're checking PACK. For vertical boxes we are checking
333 // ALIGN.
334 const nsStyleXUL* boxInfo = StyleXUL();
335 if (IsHorizontal()) {
336 switch (boxInfo->mBoxPack) {
337 case NS_STYLE_BOX_PACK_START:
338 aHalign = nsBoxFrame::hAlign_Left;
339 return true;
340 case NS_STYLE_BOX_PACK_CENTER:
341 aHalign = nsBoxFrame::hAlign_Center;
342 return true;
343 case NS_STYLE_BOX_PACK_END:
344 aHalign = nsBoxFrame::hAlign_Right;
345 return true;
346 default: // Nonsensical value. Just bail.
347 return false;
350 else {
351 switch (boxInfo->mBoxAlign) {
352 case NS_STYLE_BOX_ALIGN_START:
353 aHalign = nsBoxFrame::hAlign_Left;
354 return true;
355 case NS_STYLE_BOX_ALIGN_CENTER:
356 aHalign = nsBoxFrame::hAlign_Center;
357 return true;
358 case NS_STYLE_BOX_ALIGN_END:
359 aHalign = nsBoxFrame::hAlign_Right;
360 return true;
361 default: // Nonsensical value. Just bail.
362 return false;
366 return false;
369 bool
370 nsBoxFrame::GetInitialVAlignment(nsBoxFrame::Valignment& aValign)
372 if (!GetContent())
373 return false;
375 static nsIContent::AttrValuesArray valignStrings[] =
376 {&nsGkAtoms::top, &nsGkAtoms::baseline, &nsGkAtoms::middle, &nsGkAtoms::bottom, nullptr};
377 static const Valignment valignValues[] =
378 {vAlign_Top, vAlign_BaseLine, vAlign_Middle, vAlign_Bottom};
379 int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::valign,
380 valignStrings, eCaseMatters);
381 if (index >= 0) {
382 aValign = valignValues[index];
383 return true;
386 // Now that the deprecated stuff is out of the way, we move on to check the appropriate
387 // attribute. For horizontal boxes, we are checking the ALIGN attribute. For vertical boxes
388 // we are checking the PACK attribute.
389 nsIAtom* attrName = IsHorizontal() ? nsGkAtoms::align : nsGkAtoms::pack;
390 static nsIContent::AttrValuesArray strings[] =
391 {&nsGkAtoms::_empty, &nsGkAtoms::start, &nsGkAtoms::center,
392 &nsGkAtoms::baseline, &nsGkAtoms::end, nullptr};
393 static const Valignment values[] =
394 {vAlign_Top/*not used*/, vAlign_Top, vAlign_Middle, vAlign_BaseLine, vAlign_Bottom};
395 index = GetContent()->FindAttrValueIn(kNameSpaceID_None, attrName,
396 strings, eCaseMatters);
397 if (index == nsIContent::ATTR_VALUE_NO_MATCH) {
398 // The attr was present but had a nonsensical value. Revert to the default.
399 return false;
401 if (index > 0) {
402 aValign = values[index];
403 return true;
406 // Now that we've checked for the attribute it's time to check CSS. For
407 // horizontal boxes we're checking ALIGN. For vertical boxes we are checking
408 // PACK.
409 const nsStyleXUL* boxInfo = StyleXUL();
410 if (IsHorizontal()) {
411 switch (boxInfo->mBoxAlign) {
412 case NS_STYLE_BOX_ALIGN_START:
413 aValign = nsBoxFrame::vAlign_Top;
414 return true;
415 case NS_STYLE_BOX_ALIGN_CENTER:
416 aValign = nsBoxFrame::vAlign_Middle;
417 return true;
418 case NS_STYLE_BOX_ALIGN_BASELINE:
419 aValign = nsBoxFrame::vAlign_BaseLine;
420 return true;
421 case NS_STYLE_BOX_ALIGN_END:
422 aValign = nsBoxFrame::vAlign_Bottom;
423 return true;
424 default: // Nonsensical value. Just bail.
425 return false;
428 else {
429 switch (boxInfo->mBoxPack) {
430 case NS_STYLE_BOX_PACK_START:
431 aValign = nsBoxFrame::vAlign_Top;
432 return true;
433 case NS_STYLE_BOX_PACK_CENTER:
434 aValign = nsBoxFrame::vAlign_Middle;
435 return true;
436 case NS_STYLE_BOX_PACK_END:
437 aValign = nsBoxFrame::vAlign_Bottom;
438 return true;
439 default: // Nonsensical value. Just bail.
440 return false;
444 return false;
447 void
448 nsBoxFrame::GetInitialOrientation(bool& aIsHorizontal)
450 // see if we are a vertical or horizontal box.
451 if (!GetContent())
452 return;
454 // Check the style system first.
455 const nsStyleXUL* boxInfo = StyleXUL();
456 if (boxInfo->mBoxOrient == NS_STYLE_BOX_ORIENT_HORIZONTAL)
457 aIsHorizontal = true;
458 else
459 aIsHorizontal = false;
461 // Now see if we have an attribute. The attribute overrides
462 // the style system value.
463 static nsIContent::AttrValuesArray strings[] =
464 {&nsGkAtoms::vertical, &nsGkAtoms::horizontal, nullptr};
465 int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::orient,
466 strings, eCaseMatters);
467 if (index >= 0) {
468 aIsHorizontal = index == 1;
472 void
473 nsBoxFrame::GetInitialDirection(bool& aIsNormal)
475 if (!GetContent())
476 return;
478 if (IsHorizontal()) {
479 // For horizontal boxes only, we initialize our value based off the CSS 'direction' property.
480 // This means that BiDI users will end up with horizontally inverted chrome.
481 aIsNormal = (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR); // If text runs RTL then so do we.
483 else
484 aIsNormal = true; // Assume a normal direction in the vertical case.
486 // Now check the style system to see if we should invert aIsNormal.
487 const nsStyleXUL* boxInfo = StyleXUL();
488 if (boxInfo->mBoxDirection == NS_STYLE_BOX_DIRECTION_REVERSE)
489 aIsNormal = !aIsNormal; // Invert our direction.
491 // Now see if we have an attribute. The attribute overrides
492 // the style system value.
493 if (IsHorizontal()) {
494 static nsIContent::AttrValuesArray strings[] =
495 {&nsGkAtoms::reverse, &nsGkAtoms::ltr, &nsGkAtoms::rtl, nullptr};
496 int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::dir,
497 strings, eCaseMatters);
498 if (index >= 0) {
499 bool values[] = {!aIsNormal, true, false};
500 aIsNormal = values[index];
502 } else if (GetContent()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir,
503 nsGkAtoms::reverse, eCaseMatters)) {
504 aIsNormal = !aIsNormal;
508 /* Returns true if it was set.
510 bool
511 nsBoxFrame::GetInitialEqualSize(bool& aEqualSize)
513 // see if we are a vertical or horizontal box.
514 if (!GetContent())
515 return false;
517 if (GetContent()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::equalsize,
518 nsGkAtoms::always, eCaseMatters)) {
519 aEqualSize = true;
520 return true;
523 return false;
526 /* Returns true if it was set.
528 bool
529 nsBoxFrame::GetInitialAutoStretch(bool& aStretch)
531 if (!GetContent())
532 return false;
534 // Check the align attribute.
535 static nsIContent::AttrValuesArray strings[] =
536 {&nsGkAtoms::_empty, &nsGkAtoms::stretch, nullptr};
537 int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::align,
538 strings, eCaseMatters);
539 if (index != nsIContent::ATTR_MISSING && index != 0) {
540 aStretch = index == 1;
541 return true;
544 // Check the CSS box-align property.
545 const nsStyleXUL* boxInfo = StyleXUL();
546 aStretch = (boxInfo->mBoxAlign == NS_STYLE_BOX_ALIGN_STRETCH);
548 return true;
551 void
552 nsBoxFrame::DidReflow(nsPresContext* aPresContext,
553 const nsHTMLReflowState* aReflowState,
554 nsDidReflowStatus aStatus)
556 nsFrameState preserveBits =
557 mState & (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
558 nsFrame::DidReflow(aPresContext, aReflowState, aStatus);
559 mState |= preserveBits;
562 bool
563 nsBoxFrame::HonorPrintBackgroundSettings()
565 return (!mContent || !mContent->IsInNativeAnonymousSubtree()) &&
566 nsContainerFrame::HonorPrintBackgroundSettings();
569 #ifdef DO_NOISY_REFLOW
570 static int myCounter = 0;
571 static void printSize(char * aDesc, nscoord aSize)
573 printf(" %s: ", aDesc);
574 if (aSize == NS_UNCONSTRAINEDSIZE) {
575 printf("UC");
576 } else {
577 printf("%d", aSize);
580 #endif
582 /* virtual */ nscoord
583 nsBoxFrame::GetMinISize(nsRenderingContext *aRenderingContext)
585 nscoord result;
586 DISPLAY_MIN_WIDTH(this, result);
588 nsBoxLayoutState state(PresContext(), aRenderingContext);
589 nsSize minSize = GetMinSize(state);
591 // GetMinSize returns border-box width, and we want to return content
592 // width. Since Reflow uses the reflow state's border and padding, we
593 // actually just want to subtract what GetMinSize added, which is the
594 // result of GetBorderAndPadding.
595 nsMargin bp;
596 GetBorderAndPadding(bp);
598 result = minSize.width - bp.LeftRight();
599 result = std::max(result, 0);
601 return result;
604 /* virtual */ nscoord
605 nsBoxFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
607 nscoord result;
608 DISPLAY_PREF_WIDTH(this, result);
610 nsBoxLayoutState state(PresContext(), aRenderingContext);
611 nsSize prefSize = GetPrefSize(state);
613 // GetPrefSize returns border-box width, and we want to return content
614 // width. Since Reflow uses the reflow state's border and padding, we
615 // actually just want to subtract what GetPrefSize added, which is the
616 // result of GetBorderAndPadding.
617 nsMargin bp;
618 GetBorderAndPadding(bp);
620 result = prefSize.width - bp.LeftRight();
621 result = std::max(result, 0);
623 return result;
626 void
627 nsBoxFrame::Reflow(nsPresContext* aPresContext,
628 nsHTMLReflowMetrics& aDesiredSize,
629 const nsHTMLReflowState& aReflowState,
630 nsReflowStatus& aStatus)
632 // If you make changes to this method, please keep nsLeafBoxFrame::Reflow
633 // in sync, if the changes are applicable there.
635 DO_GLOBAL_REFLOW_COUNT("nsBoxFrame");
636 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
638 NS_ASSERTION(aReflowState.ComputedWidth() >=0 &&
639 aReflowState.ComputedHeight() >= 0, "Computed Size < 0");
641 #ifdef DO_NOISY_REFLOW
642 printf("\n-------------Starting BoxFrame Reflow ----------------------------\n");
643 printf("%p ** nsBF::Reflow %d ", this, myCounter++);
645 printSize("AW", aReflowState.AvailableWidth());
646 printSize("AH", aReflowState.AvailableHeight());
647 printSize("CW", aReflowState.ComputedWidth());
648 printSize("CH", aReflowState.ComputedHeight());
650 printf(" *\n");
652 #endif
654 aStatus = NS_FRAME_COMPLETE;
656 // create the layout state
657 nsBoxLayoutState state(aPresContext, aReflowState.rendContext,
658 &aReflowState, aReflowState.mReflowDepth);
660 WritingMode wm = aReflowState.GetWritingMode();
661 LogicalSize computedSize(wm, aReflowState.ComputedISize(),
662 aReflowState.ComputedBSize());
664 LogicalMargin m = aReflowState.ComputedLogicalBorderPadding();
665 // GetBorderAndPadding(m);
667 LogicalSize prefSize(wm);
669 // if we are told to layout intrinsic then get our preferred size.
670 NS_ASSERTION(computedSize.ISize(wm) != NS_INTRINSICSIZE,
671 "computed inline size should always be computed");
672 if (computedSize.BSize(wm) == NS_INTRINSICSIZE) {
673 nsSize physicalPrefSize = GetPrefSize(state);
674 nsSize minSize = GetMinSize(state);
675 nsSize maxSize = GetMaxSize(state);
676 // XXXbz isn't GetPrefSize supposed to bounds-check for us?
677 physicalPrefSize = BoundsCheck(minSize, physicalPrefSize, maxSize);
678 prefSize = LogicalSize(wm, physicalPrefSize);
681 // get our desiredSize
682 computedSize.ISize(wm) += m.IStart(wm) + m.IEnd(wm);
684 if (aReflowState.ComputedBSize() == NS_INTRINSICSIZE) {
685 computedSize.BSize(wm) = prefSize.BSize(wm);
686 // prefSize is border-box but min/max constraints are content-box.
687 nscoord blockDirBorderPadding =
688 aReflowState.ComputedLogicalBorderPadding().BStartEnd(wm);
689 nscoord contentBSize = computedSize.BSize(wm) - blockDirBorderPadding;
690 // Note: contentHeight might be negative, but that's OK because min-height
691 // is never negative.
692 computedSize.BSize(wm) = aReflowState.ApplyMinMaxHeight(contentBSize) +
693 blockDirBorderPadding;
694 } else {
695 computedSize.BSize(wm) += m.BStart(wm) + m.BEnd(wm);
698 nsSize physicalSize = computedSize.GetPhysicalSize(wm);
699 nsRect r(mRect.x, mRect.y, physicalSize.width, physicalSize.height);
701 SetBounds(state, r);
703 // layout our children
704 Layout(state);
706 // ok our child could have gotten bigger. So lets get its bounds
708 // get the ascent
709 LogicalSize boxSize = GetLogicalSize(wm);
710 nscoord ascent = boxSize.BSize(wm);
712 // getting the ascent could be a lot of work. Don't get it if
713 // we are the root. The viewport doesn't care about it.
714 if (!(mState & NS_STATE_IS_ROOT)) {
715 ascent = GetBoxAscent(state);
718 aDesiredSize.SetSize(wm, boxSize);
719 aDesiredSize.SetBlockStartAscent(ascent);
721 aDesiredSize.mOverflowAreas = GetOverflowAreas();
723 #ifdef DO_NOISY_REFLOW
725 printf("%p ** nsBF(done) W:%d H:%d ", this, aDesiredSize.Width(), aDesiredSize.Height());
727 if (maxElementSize) {
728 printf("MW:%d\n", *maxElementWidth);
729 } else {
730 printf("MW:?\n");
734 #endif
736 ReflowAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus);
738 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
741 nsSize
742 nsBoxFrame::GetPrefSize(nsBoxLayoutState& aBoxLayoutState)
744 NS_ASSERTION(aBoxLayoutState.GetRenderingContext(),
745 "must have rendering context");
747 nsSize size(0,0);
748 DISPLAY_PREF_SIZE(this, size);
749 if (!DoesNeedRecalc(mPrefSize)) {
750 return mPrefSize;
753 #ifdef DEBUG_LAYOUT
754 PropagateDebug(aBoxLayoutState);
755 #endif
757 if (IsCollapsed())
758 return size;
760 // if the size was not completely redefined in CSS then ask our children
761 bool widthSet, heightSet;
762 if (!nsIFrame::AddCSSPrefSize(this, size, widthSet, heightSet))
764 if (mLayoutManager) {
765 nsSize layoutSize = mLayoutManager->GetPrefSize(this, aBoxLayoutState);
766 if (!widthSet)
767 size.width = layoutSize.width;
768 if (!heightSet)
769 size.height = layoutSize.height;
771 else {
772 size = nsBox::GetPrefSize(aBoxLayoutState);
776 nsSize minSize = GetMinSize(aBoxLayoutState);
777 nsSize maxSize = GetMaxSize(aBoxLayoutState);
778 mPrefSize = BoundsCheck(minSize, size, maxSize);
780 return mPrefSize;
783 nscoord
784 nsBoxFrame::GetBoxAscent(nsBoxLayoutState& aBoxLayoutState)
786 if (!DoesNeedRecalc(mAscent))
787 return mAscent;
789 #ifdef DEBUG_LAYOUT
790 PropagateDebug(aBoxLayoutState);
791 #endif
793 if (IsCollapsed())
794 return 0;
796 if (mLayoutManager)
797 mAscent = mLayoutManager->GetAscent(this, aBoxLayoutState);
798 else
799 mAscent = nsBox::GetBoxAscent(aBoxLayoutState);
801 return mAscent;
804 nsSize
805 nsBoxFrame::GetMinSize(nsBoxLayoutState& aBoxLayoutState)
807 NS_ASSERTION(aBoxLayoutState.GetRenderingContext(),
808 "must have rendering context");
810 nsSize size(0,0);
811 DISPLAY_MIN_SIZE(this, size);
812 if (!DoesNeedRecalc(mMinSize)) {
813 return mMinSize;
816 #ifdef DEBUG_LAYOUT
817 PropagateDebug(aBoxLayoutState);
818 #endif
820 if (IsCollapsed())
821 return size;
823 // if the size was not completely redefined in CSS then ask our children
824 bool widthSet, heightSet;
825 if (!nsIFrame::AddCSSMinSize(aBoxLayoutState, this, size, widthSet, heightSet))
827 if (mLayoutManager) {
828 nsSize layoutSize = mLayoutManager->GetMinSize(this, aBoxLayoutState);
829 if (!widthSet)
830 size.width = layoutSize.width;
831 if (!heightSet)
832 size.height = layoutSize.height;
834 else {
835 size = nsBox::GetMinSize(aBoxLayoutState);
839 mMinSize = size;
841 return size;
844 nsSize
845 nsBoxFrame::GetMaxSize(nsBoxLayoutState& aBoxLayoutState)
847 NS_ASSERTION(aBoxLayoutState.GetRenderingContext(),
848 "must have rendering context");
850 nsSize size(NS_INTRINSICSIZE, NS_INTRINSICSIZE);
851 DISPLAY_MAX_SIZE(this, size);
852 if (!DoesNeedRecalc(mMaxSize)) {
853 return mMaxSize;
856 #ifdef DEBUG_LAYOUT
857 PropagateDebug(aBoxLayoutState);
858 #endif
860 if (IsCollapsed())
861 return size;
863 // if the size was not completely redefined in CSS then ask our children
864 bool widthSet, heightSet;
865 if (!nsIFrame::AddCSSMaxSize(this, size, widthSet, heightSet))
867 if (mLayoutManager) {
868 nsSize layoutSize = mLayoutManager->GetMaxSize(this, aBoxLayoutState);
869 if (!widthSet)
870 size.width = layoutSize.width;
871 if (!heightSet)
872 size.height = layoutSize.height;
874 else {
875 size = nsBox::GetMaxSize(aBoxLayoutState);
879 mMaxSize = size;
881 return size;
884 nscoord
885 nsBoxFrame::GetFlex(nsBoxLayoutState& aBoxLayoutState)
887 if (!DoesNeedRecalc(mFlex))
888 return mFlex;
890 mFlex = nsBox::GetFlex(aBoxLayoutState);
892 return mFlex;
896 * If subclassing please subclass this method not layout.
897 * layout will call this method.
899 NS_IMETHODIMP
900 nsBoxFrame::DoLayout(nsBoxLayoutState& aState)
902 uint32_t oldFlags = aState.LayoutFlags();
903 aState.SetLayoutFlags(0);
905 nsresult rv = NS_OK;
906 if (mLayoutManager) {
907 CoordNeedsRecalc(mAscent);
908 rv = mLayoutManager->Layout(this, aState);
911 aState.SetLayoutFlags(oldFlags);
913 if (HasAbsolutelyPositionedChildren()) {
914 // Set up a |reflowState| to pass into ReflowAbsoluteFrames
915 WritingMode wm = GetWritingMode();
916 nsHTMLReflowState reflowState(aState.PresContext(), this,
917 aState.GetRenderingContext(),
918 LogicalSize(wm, GetLogicalSize().ISize(wm),
919 NS_UNCONSTRAINEDSIZE));
921 // Set up a |desiredSize| to pass into ReflowAbsoluteFrames
922 nsHTMLReflowMetrics desiredSize(reflowState);
923 desiredSize.Width() = mRect.width;
924 desiredSize.Height() = mRect.height;
926 // get the ascent (cribbed from ::Reflow)
927 nscoord ascent = mRect.height;
929 // getting the ascent could be a lot of work. Don't get it if
930 // we are the root. The viewport doesn't care about it.
931 if (!(mState & NS_STATE_IS_ROOT)) {
932 ascent = GetBoxAscent(aState);
934 desiredSize.SetBlockStartAscent(ascent);
935 desiredSize.mOverflowAreas = GetOverflowAreas();
937 AddStateBits(NS_FRAME_IN_REFLOW);
938 // Set up a |reflowStatus| to pass into ReflowAbsoluteFrames
939 // (just a dummy value; hopefully that's OK)
940 nsReflowStatus reflowStatus = NS_FRAME_COMPLETE;
941 ReflowAbsoluteFrames(aState.PresContext(), desiredSize,
942 reflowState, reflowStatus);
943 RemoveStateBits(NS_FRAME_IN_REFLOW);
946 return rv;
949 void
950 nsBoxFrame::DestroyFrom(nsIFrame* aDestructRoot)
952 // unregister access key
953 RegUnregAccessKey(false);
955 // clean up the container box's layout manager and child boxes
956 SetLayoutManager(nullptr);
958 nsContainerFrame::DestroyFrom(aDestructRoot);
961 #ifdef DEBUG_LAYOUT
962 nsresult
963 nsBoxFrame::SetDebug(nsBoxLayoutState& aState, bool aDebug)
965 // see if our state matches the given debug state
966 bool debugSet = mState & NS_STATE_CURRENTLY_IN_DEBUG;
967 bool debugChanged = (!aDebug && debugSet) || (aDebug && !debugSet);
969 // if it doesn't then tell each child below us the new debug state
970 if (debugChanged)
972 if (aDebug) {
973 mState |= NS_STATE_CURRENTLY_IN_DEBUG;
974 } else {
975 mState &= ~NS_STATE_CURRENTLY_IN_DEBUG;
978 SetDebugOnChildList(aState, mFirstChild, aDebug);
980 MarkIntrinsicISizesDirty();
983 return NS_OK;
985 #endif
987 /* virtual */ void
988 nsBoxFrame::MarkIntrinsicISizesDirty()
990 SizeNeedsRecalc(mPrefSize);
991 SizeNeedsRecalc(mMinSize);
992 SizeNeedsRecalc(mMaxSize);
993 CoordNeedsRecalc(mFlex);
994 CoordNeedsRecalc(mAscent);
996 if (mLayoutManager) {
997 nsBoxLayoutState state(PresContext());
998 mLayoutManager->IntrinsicISizesDirty(this, state);
1001 // Don't call base class method, since everything it does is within an
1002 // IsBoxWrapped check.
1005 void
1006 nsBoxFrame::RemoveFrame(ChildListID aListID,
1007 nsIFrame* aOldFrame)
1009 NS_PRECONDITION(aListID == kPrincipalList, "We don't support out-of-flow kids");
1010 nsPresContext* presContext = PresContext();
1011 nsBoxLayoutState state(presContext);
1013 // remove the child frame
1014 mFrames.RemoveFrame(aOldFrame);
1016 // notify the layout manager
1017 if (mLayoutManager)
1018 mLayoutManager->ChildrenRemoved(this, state, aOldFrame);
1020 // destroy the child frame
1021 aOldFrame->Destroy();
1023 // mark us dirty and generate a reflow command
1024 PresContext()->PresShell()->
1025 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
1026 NS_FRAME_HAS_DIRTY_CHILDREN);
1029 void
1030 nsBoxFrame::InsertFrames(ChildListID aListID,
1031 nsIFrame* aPrevFrame,
1032 nsFrameList& aFrameList)
1034 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
1035 "inserting after sibling frame with different parent");
1036 NS_ASSERTION(!aPrevFrame || mFrames.ContainsFrame(aPrevFrame),
1037 "inserting after sibling frame not in our child list");
1038 NS_PRECONDITION(aListID == kPrincipalList, "We don't support out-of-flow kids");
1039 nsBoxLayoutState state(PresContext());
1041 // insert the child frames
1042 const nsFrameList::Slice& newFrames =
1043 mFrames.InsertFrames(this, aPrevFrame, aFrameList);
1045 // notify the layout manager
1046 if (mLayoutManager)
1047 mLayoutManager->ChildrenInserted(this, state, aPrevFrame, newFrames);
1049 // Make sure to check box order _after_ notifying the layout
1050 // manager; otherwise the slice we give the layout manager will
1051 // just be bogus. If the layout manager cares about the order, we
1052 // just lose.
1053 CheckBoxOrder();
1055 #ifdef DEBUG_LAYOUT
1056 // if we are in debug make sure our children are in debug as well.
1057 if (mState & NS_STATE_CURRENTLY_IN_DEBUG)
1058 SetDebugOnChildList(state, mFrames.FirstChild(), true);
1059 #endif
1061 PresContext()->PresShell()->
1062 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
1063 NS_FRAME_HAS_DIRTY_CHILDREN);
1067 void
1068 nsBoxFrame::AppendFrames(ChildListID aListID,
1069 nsFrameList& aFrameList)
1071 NS_PRECONDITION(aListID == kPrincipalList, "We don't support out-of-flow kids");
1072 nsBoxLayoutState state(PresContext());
1074 // append the new frames
1075 const nsFrameList::Slice& newFrames = mFrames.AppendFrames(this, aFrameList);
1077 // notify the layout manager
1078 if (mLayoutManager)
1079 mLayoutManager->ChildrenAppended(this, state, newFrames);
1081 // Make sure to check box order _after_ notifying the layout
1082 // manager; otherwise the slice we give the layout manager will
1083 // just be bogus. If the layout manager cares about the order, we
1084 // just lose.
1085 CheckBoxOrder();
1087 #ifdef DEBUG_LAYOUT
1088 // if we are in debug make sure our children are in debug as well.
1089 if (mState & NS_STATE_CURRENTLY_IN_DEBUG)
1090 SetDebugOnChildList(state, mFrames.FirstChild(), true);
1091 #endif
1093 // XXXbz why is this NS_FRAME_FIRST_REFLOW check here?
1094 if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
1095 PresContext()->PresShell()->
1096 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
1097 NS_FRAME_HAS_DIRTY_CHILDREN);
1101 /* virtual */ nsContainerFrame*
1102 nsBoxFrame::GetContentInsertionFrame()
1104 if (GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK)
1105 return GetFirstPrincipalChild()->GetContentInsertionFrame();
1106 return nsContainerFrame::GetContentInsertionFrame();
1109 nsresult
1110 nsBoxFrame::AttributeChanged(int32_t aNameSpaceID,
1111 nsIAtom* aAttribute,
1112 int32_t aModType)
1114 nsresult rv = nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
1115 aModType);
1117 // Ignore 'width', 'height', 'screenX', 'screenY' and 'sizemode' on a
1118 // <window>.
1119 nsIAtom *tag = mContent->Tag();
1120 if ((tag == nsGkAtoms::window ||
1121 tag == nsGkAtoms::page ||
1122 tag == nsGkAtoms::dialog ||
1123 tag == nsGkAtoms::wizard) &&
1124 (nsGkAtoms::width == aAttribute ||
1125 nsGkAtoms::height == aAttribute ||
1126 nsGkAtoms::screenX == aAttribute ||
1127 nsGkAtoms::screenY == aAttribute ||
1128 nsGkAtoms::sizemode == aAttribute)) {
1129 return rv;
1132 if (aAttribute == nsGkAtoms::width ||
1133 aAttribute == nsGkAtoms::height ||
1134 aAttribute == nsGkAtoms::align ||
1135 aAttribute == nsGkAtoms::valign ||
1136 aAttribute == nsGkAtoms::left ||
1137 aAttribute == nsGkAtoms::top ||
1138 aAttribute == nsGkAtoms::right ||
1139 aAttribute == nsGkAtoms::bottom ||
1140 aAttribute == nsGkAtoms::start ||
1141 aAttribute == nsGkAtoms::end ||
1142 aAttribute == nsGkAtoms::minwidth ||
1143 aAttribute == nsGkAtoms::maxwidth ||
1144 aAttribute == nsGkAtoms::minheight ||
1145 aAttribute == nsGkAtoms::maxheight ||
1146 aAttribute == nsGkAtoms::flex ||
1147 aAttribute == nsGkAtoms::orient ||
1148 aAttribute == nsGkAtoms::pack ||
1149 aAttribute == nsGkAtoms::dir ||
1150 aAttribute == nsGkAtoms::mousethrough ||
1151 aAttribute == nsGkAtoms::equalsize) {
1153 if (aAttribute == nsGkAtoms::align ||
1154 aAttribute == nsGkAtoms::valign ||
1155 aAttribute == nsGkAtoms::orient ||
1156 aAttribute == nsGkAtoms::pack ||
1157 #ifdef DEBUG_LAYOUT
1158 aAttribute == nsGkAtoms::debug ||
1159 #endif
1160 aAttribute == nsGkAtoms::dir) {
1162 mValign = nsBoxFrame::vAlign_Top;
1163 mHalign = nsBoxFrame::hAlign_Left;
1165 bool orient = true;
1166 GetInitialOrientation(orient);
1167 if (orient)
1168 mState |= NS_STATE_IS_HORIZONTAL;
1169 else
1170 mState &= ~NS_STATE_IS_HORIZONTAL;
1172 bool normal = true;
1173 GetInitialDirection(normal);
1174 if (normal)
1175 mState |= NS_STATE_IS_DIRECTION_NORMAL;
1176 else
1177 mState &= ~NS_STATE_IS_DIRECTION_NORMAL;
1179 GetInitialVAlignment(mValign);
1180 GetInitialHAlignment(mHalign);
1182 bool equalSize = false;
1183 GetInitialEqualSize(equalSize);
1184 if (equalSize)
1185 mState |= NS_STATE_EQUAL_SIZE;
1186 else
1187 mState &= ~NS_STATE_EQUAL_SIZE;
1189 #ifdef DEBUG_LAYOUT
1190 bool debug = mState & NS_STATE_SET_TO_DEBUG;
1191 bool debugSet = GetInitialDebug(debug);
1192 if (debugSet) {
1193 mState |= NS_STATE_DEBUG_WAS_SET;
1195 if (debug)
1196 mState |= NS_STATE_SET_TO_DEBUG;
1197 else
1198 mState &= ~NS_STATE_SET_TO_DEBUG;
1199 } else {
1200 mState &= ~NS_STATE_DEBUG_WAS_SET;
1202 #endif
1204 bool autostretch = !!(mState & NS_STATE_AUTO_STRETCH);
1205 GetInitialAutoStretch(autostretch);
1206 if (autostretch)
1207 mState |= NS_STATE_AUTO_STRETCH;
1208 else
1209 mState &= ~NS_STATE_AUTO_STRETCH;
1211 else if (aAttribute == nsGkAtoms::left ||
1212 aAttribute == nsGkAtoms::top ||
1213 aAttribute == nsGkAtoms::right ||
1214 aAttribute == nsGkAtoms::bottom ||
1215 aAttribute == nsGkAtoms::start ||
1216 aAttribute == nsGkAtoms::end) {
1217 mState &= ~NS_STATE_STACK_NOT_POSITIONED;
1219 else if (aAttribute == nsGkAtoms::mousethrough) {
1220 UpdateMouseThrough();
1223 PresContext()->PresShell()->
1224 FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
1226 else if (aAttribute == nsGkAtoms::ordinal) {
1227 nsBoxLayoutState state(PresContext());
1228 nsIFrame* parent = GetParentBox(this);
1229 // If our parent is not a box, there's not much we can do... but in that
1230 // case our ordinal doesn't matter anyway, so that's ok.
1231 // Also don't bother with popup frames since they are kept on the
1232 // kPopupList and RelayoutChildAtOrdinal() only handles
1233 // principal children.
1234 if (parent && !(GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
1235 StyleDisplay()->mDisplay != NS_STYLE_DISPLAY_POPUP) {
1236 parent->RelayoutChildAtOrdinal(state, this);
1237 // XXXldb Should this instead be a tree change on the child or parent?
1238 PresContext()->PresShell()->
1239 FrameNeedsReflow(parent, nsIPresShell::eStyleChange,
1240 NS_FRAME_IS_DIRTY);
1243 // If the accesskey changed, register for the new value
1244 // The old value has been unregistered in nsXULElement::SetAttr
1245 else if (aAttribute == nsGkAtoms::accesskey) {
1246 RegUnregAccessKey(true);
1248 else if (aAttribute == nsGkAtoms::rows &&
1249 tag == nsGkAtoms::tree) {
1250 // Reflow ourselves and all our children if "rows" changes, since
1251 // nsTreeBodyFrame's layout reads this from its parent (this frame).
1252 PresContext()->PresShell()->
1253 FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
1256 return rv;
1259 #ifdef DEBUG_LAYOUT
1260 void
1261 nsBoxFrame::GetDebugPref(nsPresContext* aPresContext)
1263 gDebug = Preferences::GetBool("xul.debug.box");
1266 class nsDisplayXULDebug : public nsDisplayItem {
1267 public:
1268 nsDisplayXULDebug(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) :
1269 nsDisplayItem(aBuilder, aFrame) {
1270 MOZ_COUNT_CTOR(nsDisplayXULDebug);
1272 #ifdef NS_BUILD_REFCNT_LOGGING
1273 virtual ~nsDisplayXULDebug() {
1274 MOZ_COUNT_DTOR(nsDisplayXULDebug);
1276 #endif
1278 virtual void HitTest(nsDisplayListBuilder* aBuilder, nsRect aRect,
1279 HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {
1280 nsPoint rectCenter(aRect.x + aRect.width / 2, aRect.y + aRect.height / 2);
1281 static_cast<nsBoxFrame*>(mFrame)->
1282 DisplayDebugInfoFor(this, rectCenter - ToReferenceFrame());
1283 aOutFrames->AppendElement(this);
1285 virtual void Paint(nsDisplayListBuilder* aBuilder
1286 nsRenderingContext* aCtx);
1287 NS_DISPLAY_DECL_NAME("XULDebug", TYPE_XUL_DEBUG)
1290 void
1291 nsDisplayXULDebug::Paint(nsDisplayListBuilder* aBuilder,
1292 nsRenderingContext* aCtx)
1294 static_cast<nsBoxFrame*>(mFrame)->
1295 PaintXULDebugOverlay(*aCtx->GetDrawTarget(), ToReferenceFrame());
1298 static void
1299 PaintXULDebugBackground(nsIFrame* aFrame, nsRenderingContext* aCtx,
1300 const nsRect& aDirtyRect, nsPoint aPt)
1302 static_cast<nsBoxFrame*>(aFrame)->PaintXULDebugBackground(*aCtx, aPt);
1304 #endif
1306 void
1307 nsBoxFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
1308 const nsRect& aDirtyRect,
1309 const nsDisplayListSet& aLists)
1311 bool forceLayer = false;
1312 uint32_t flags = 0;
1313 mozilla::layers::FrameMetrics::ViewID scrollTargetId =
1314 mozilla::layers::FrameMetrics::NULL_SCROLL_ID;
1316 if (GetContent()->IsXUL()) {
1317 // forcelayer is only supported on XUL elements with box layout
1318 if (GetContent()->HasAttr(kNameSpaceID_None, nsGkAtoms::layer)) {
1319 forceLayer = true;
1320 } else {
1321 nsIFrame* parent = GetParentBox(this);
1322 if (parent && parent->GetType() == nsGkAtoms::sliderFrame) {
1323 aBuilder->GetScrollbarInfo(&scrollTargetId, &flags);
1324 forceLayer = (scrollTargetId != layers::FrameMetrics::NULL_SCROLL_ID);
1325 nsLayoutUtils::SetScrollbarThumbLayerization(this, forceLayer);
1328 // Check for frames that are marked as a part of the region used
1329 // in calculating glass margins on Windows.
1330 const nsStyleDisplay* styles = StyleDisplay();
1331 if (styles && styles->mAppearance == NS_THEME_WIN_EXCLUDE_GLASS) {
1332 aBuilder->AddWindowExcludeGlassRegion(
1333 nsRect(aBuilder->ToReferenceFrame(this), GetSize()));
1336 aBuilder->AdjustWindowDraggingRegion(this);
1339 nsDisplayListCollection tempLists;
1340 const nsDisplayListSet& destination = forceLayer ? tempLists : aLists;
1342 DisplayBorderBackgroundOutline(aBuilder, destination);
1344 #ifdef DEBUG_LAYOUT
1345 if (mState & NS_STATE_CURRENTLY_IN_DEBUG) {
1346 destination.BorderBackground()->AppendNewToTop(new (aBuilder)
1347 nsDisplayGeneric(aBuilder, this, PaintXULDebugBackground,
1348 "XULDebugBackground"));
1349 destination.Outlines()->AppendNewToTop(new (aBuilder)
1350 nsDisplayXULDebug(aBuilder, this));
1352 #endif
1354 BuildDisplayListForChildren(aBuilder, aDirtyRect, destination);
1356 // see if we have to draw a selection frame around this container
1357 DisplaySelectionOverlay(aBuilder, destination.Content());
1359 if (forceLayer) {
1360 // This is a bit of a hack. Collect up all descendant display items
1361 // and merge them into a single Content() list. This can cause us
1362 // to violate CSS stacking order, but forceLayer is a magic
1363 // XUL-only extension anyway.
1364 nsDisplayList masterList;
1365 masterList.AppendToTop(tempLists.BorderBackground());
1366 masterList.AppendToTop(tempLists.BlockBorderBackgrounds());
1367 masterList.AppendToTop(tempLists.Floats());
1368 masterList.AppendToTop(tempLists.Content());
1369 masterList.AppendToTop(tempLists.PositionedDescendants());
1370 masterList.AppendToTop(tempLists.Outlines());
1372 // Wrap the list to make it its own layer
1373 aLists.Content()->AppendNewToTop(new (aBuilder)
1374 nsDisplayOwnLayer(aBuilder, this, &masterList, flags, scrollTargetId));
1378 void
1379 nsBoxFrame::BuildDisplayListForChildren(nsDisplayListBuilder* aBuilder,
1380 const nsRect& aDirtyRect,
1381 const nsDisplayListSet& aLists)
1383 nsIFrame* kid = mFrames.FirstChild();
1384 // Put each child's background onto the BlockBorderBackgrounds list
1385 // to emulate the existing two-layer XUL painting scheme.
1386 nsDisplayListSet set(aLists, aLists.BlockBorderBackgrounds());
1387 // The children should be in the right order
1388 while (kid) {
1389 BuildDisplayListForChild(aBuilder, kid, aDirtyRect, set);
1390 kid = kid->GetNextSibling();
1394 // REVIEW: PaintChildren did a few things none of which are a big deal
1395 // anymore:
1396 // * Paint some debugging rects for this frame.
1397 // This is done by nsDisplayXULDebugBackground, which goes in the
1398 // BorderBackground() layer so it isn't clipped by OVERFLOW_CLIP.
1399 // * Apply OVERFLOW_CLIP to the children.
1400 // This is now in nsFrame::BuildDisplayListForStackingContext/Child.
1401 // * Actually paint the children.
1402 // Moved to BuildDisplayList.
1403 // * Paint per-kid debug information.
1404 // This is done by nsDisplayXULDebug, which is in the Outlines()
1405 // layer so it goes on top. This means it is not clipped by OVERFLOW_CLIP,
1406 // whereas it did used to respect OVERFLOW_CLIP, but too bad.
1407 #ifdef DEBUG_LAYOUT
1408 void
1409 nsBoxFrame::PaintXULDebugBackground(nsRenderingContext& aRenderingContext,
1410 nsPoint aPt)
1412 nsMargin border;
1413 GetBorder(border);
1415 nsMargin debugBorder;
1416 nsMargin debugMargin;
1417 nsMargin debugPadding;
1419 bool isHorizontal = IsHorizontal();
1421 GetDebugBorder(debugBorder);
1422 PixelMarginToTwips(GetPresContext(), debugBorder);
1424 GetDebugMargin(debugMargin);
1425 PixelMarginToTwips(GetPresContext(), debugMargin);
1427 GetDebugPadding(debugPadding);
1428 PixelMarginToTwips(GetPresContext(), debugPadding);
1430 nsRect inner(mRect);
1431 inner.MoveTo(aPt);
1432 inner.Deflate(debugMargin);
1433 inner.Deflate(border);
1434 //nsRect borderRect(inner);
1436 int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
1438 ColorPattern color(ToDeviceColor(isHorizontal ? Color(0.f, 0.f, 1.f, 1.f) :
1439 Color(1.f, 0.f, 0.f, 1.f));
1441 DrawTarget* drawTarget = aRenderingContext.GetDrawTarget();
1443 //left
1444 nsRect r(inner);
1445 r.width = debugBorder.left;
1446 drawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), color);
1448 // top
1449 r = inner;
1450 r.height = debugBorder.top;
1451 drawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), color);
1453 //right
1454 r = inner;
1455 r.x = r.x + r.width - debugBorder.right;
1456 r.width = debugBorder.right;
1457 drawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), color);
1459 //bottom
1460 r = inner;
1461 r.y = r.y + r.height - debugBorder.bottom;
1462 r.height = debugBorder.bottom;
1463 drawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), color);
1465 // if we have dirty children or we are dirty
1466 // place a green border around us.
1467 if (NS_SUBTREE_DIRTY(this)) {
1468 nsRect dirty(inner);
1469 ColorPattern green(ToDeviceColor(Color0.f, 1.f, 0.f, 1.f)));
1470 drawTarget->StrokeRect(NSRectToRect(dirty, appUnitsPerDevPixel), green);
1474 void
1475 nsBoxFrame::PaintXULDebugOverlay(DrawTarget& aDrawTarget, nsPoint aPt)
1476 nsMargin border;
1477 GetBorder(border);
1479 nsMargin debugMargin;
1480 GetDebugMargin(debugMargin);
1481 PixelMarginToTwips(GetPresContext(), debugMargin);
1483 nsRect inner(mRect);
1484 inner.MoveTo(aPt);
1485 inner.Deflate(debugMargin);
1486 inner.Deflate(border);
1488 nscoord onePixel = GetPresContext()->IntScaledPixelsToTwips(1);
1490 kid = nsBox::GetChildBox(this);
1491 while (nullptr != kid) {
1492 bool isHorizontal = IsHorizontal();
1494 nscoord x, y, borderSize, spacerSize;
1496 nsRect cr(kid->mRect);
1497 nsMargin margin;
1498 kid->GetMargin(margin);
1499 cr.Inflate(margin);
1501 if (isHorizontal)
1503 cr.y = inner.y;
1504 x = cr.x;
1505 y = cr.y + onePixel;
1506 spacerSize = debugBorder.top - onePixel*4;
1507 } else {
1508 cr.x = inner.x;
1509 x = cr.y;
1510 y = cr.x + onePixel;
1511 spacerSize = debugBorder.left - onePixel*4;
1514 nsBoxLayoutState state(GetPresContext());
1515 nscoord flex = kid->GetFlex(state);
1517 if (!kid->IsCollapsed()) {
1518 if (isHorizontal)
1519 borderSize = cr.width;
1520 else
1521 borderSize = cr.height;
1523 DrawSpacer(GetPresContext(), aDrawTarget, isHorizontal, flex, x, y, borderSize, spacerSize);
1526 kid = GetNextBox(kid);
1529 #endif
1531 #ifdef DEBUG_LAYOUT
1532 void
1533 nsBoxFrame::GetBoxName(nsAutoString& aName)
1535 GetFrameName(aName);
1537 #endif
1539 #ifdef DEBUG_FRAME_DUMP
1540 nsresult
1541 nsBoxFrame::GetFrameName(nsAString& aResult) const
1543 return MakeFrameName(NS_LITERAL_STRING("Box"), aResult);
1545 #endif
1547 nsIAtom*
1548 nsBoxFrame::GetType() const
1550 return nsGkAtoms::boxFrame;
1553 #ifdef DEBUG_LAYOUT
1554 nsresult
1555 nsBoxFrame::GetDebug(bool& aDebug)
1557 aDebug = (mState & NS_STATE_CURRENTLY_IN_DEBUG);
1558 return NS_OK;
1560 #endif
1562 // REVIEW: nsBoxFrame::GetFrameForPoint is a problem because of 'mousethrough'
1563 // attribute support. Here's how it works:
1564 // * For each child frame F, we determine the target frame T(F) by recursively
1565 // invoking GetFrameForPoint on the child
1566 // * Let F' be the last child frame such that T(F') doesn't have mousethrough.
1567 // If F' exists, return T(F')
1568 // * Otherwise let F'' be the first child frame such that T(F'') is non-null.
1569 // If F'' exists, return T(F'')
1570 // * Otherwise return this frame, if this frame contains the point
1571 // * Otherwise return null
1572 // It's not clear how this should work for more complex z-ordering situations.
1573 // The basic principle seems to be that if a frame F has a descendant
1574 // 'mousethrough' frame that includes the target position, then F
1575 // will not receive events (unless it overrides GetFrameForPoint).
1576 // A 'mousethrough' frame will only receive an event if, after applying that rule,
1577 // all eligible frames are 'mousethrough'; the bottom-most inner-most 'mousethrough'
1578 // frame is then chosen (the first eligible frame reached in a
1579 // traversal of the frame tree --- pre/post is irrelevant since ancestors
1580 // of the mousethrough frames can't be eligible).
1581 // IMHO this is very bogus and adds a great deal of complexity for something
1582 // that is very rarely used. So I'm redefining 'mousethrough' to the following:
1583 // a frame with mousethrough is transparent to mouse events. This is compatible
1584 // with the way 'mousethrough' is used in Seamonkey's navigator.xul and
1585 // Firefox's browser.xul. The only other place it's used is in the 'expander'
1586 // XBL binding, which in our tree is only used by Thunderbird SMIME Advanced
1587 // Preferences, and I can't figure out what that does, so I'll have to test it.
1588 // If it's broken I'll probably just change the binding to use it more sensibly.
1589 // This new behaviour is implemented in nsDisplayList::HitTest.
1590 // REVIEW: This debug-box stuff is annoying. I'm just going to put debug boxes
1591 // in the outline layer and avoid GetDebugBoxAt.
1593 // REVIEW: GetCursor had debug-only event dumping code. I have replaced it
1594 // with instrumentation in nsDisplayXULDebug.
1596 #ifdef DEBUG_LAYOUT
1597 void
1598 nsBoxFrame::DrawLine(DrawTarget& aDrawTarget, bool aHorizontal, nscoord x1, nscoord y1, nscoord x2, nscoord y2)
1600 nsPoint p1(x1, y1);
1601 nsPoint p2(x2, y2);
1602 if (!aHorizontal) {
1603 Swap(p1.x, p1.y);
1604 Swap(p2.x, p2.y);
1606 ColorPattern white(ToDeviceColor(Color(1.f, 1.f, 1.f, 1.f)));
1607 StrokeLineWithSnapping(p1, p2, PresContext()->AppUnitsPerDevPixel(),
1608 aDrawTarget, color);
1611 void
1612 nsBoxFrame::FillRect(DrawTarget& aDrawTarget, bool aHorizontal, nscoord x, nscoord y, nscoord width, nscoord height)
1614 Rect rect = NSRectToSnappedRect(aHorizontal ? nsRect(x, y, width, height) :
1615 nsRect(y, x, height, width),
1616 PresContext()->AppUnitsPerDevPixel(),
1617 aDrawTarget);
1618 ColorPattern white(ToDeviceColor(Color(1.f, 1.f, 1.f, 1.f)));
1619 aDrawTarget.FillRect(rect, white);
1622 void
1623 nsBoxFrame::DrawSpacer(nsPresContext* aPresContext, DrawTarget& aDrawTarget,
1624 bool aHorizontal, int32_t flex, nscoord x, nscoord y,
1625 nscoord size, nscoord spacerSize)
1627 nscoord onePixel = aPresContext->IntScaledPixelsToTwips(1);
1629 // if we do draw the coils
1630 int distance = 0;
1631 int center = 0;
1632 int offset = 0;
1633 int coilSize = COIL_SIZE*onePixel;
1634 int halfSpacer = spacerSize/2;
1636 distance = size;
1637 center = y + halfSpacer;
1638 offset = x;
1640 int coils = distance/coilSize;
1642 int halfCoilSize = coilSize/2;
1644 if (flex == 0) {
1645 DrawLine(aDrawTarget, aHorizontal, x,y + spacerSize/2, x + size, y + spacerSize/2);
1646 } else {
1647 for (int i=0; i < coils; i++)
1649 DrawLine(aDrawTarget, aHorizontal, offset, center+halfSpacer, offset+halfCoilSize, center-halfSpacer);
1650 DrawLine(aDrawTarget, aHorizontal, offset+halfCoilSize, center-halfSpacer, offset+coilSize, center+halfSpacer);
1652 offset += coilSize;
1656 FillRect(aDrawTarget, aHorizontal, x + size - spacerSize/2, y, spacerSize/2, spacerSize);
1657 FillRect(aDrawTarget, aHorizontal, x, y, spacerSize/2, spacerSize);
1660 void
1661 nsBoxFrame::GetDebugBorder(nsMargin& aInset)
1663 aInset.SizeTo(2,2,2,2);
1665 if (IsHorizontal())
1666 aInset.top = 10;
1667 else
1668 aInset.left = 10;
1671 void
1672 nsBoxFrame::GetDebugMargin(nsMargin& aInset)
1674 aInset.SizeTo(2,2,2,2);
1677 void
1678 nsBoxFrame::GetDebugPadding(nsMargin& aPadding)
1680 aPadding.SizeTo(2,2,2,2);
1683 void
1684 nsBoxFrame::PixelMarginToTwips(nsPresContext* aPresContext, nsMargin& aMarginPixels)
1686 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
1687 aMarginPixels.left *= onePixel;
1688 aMarginPixels.right *= onePixel;
1689 aMarginPixels.top *= onePixel;
1690 aMarginPixels.bottom *= onePixel;
1693 void
1694 nsBoxFrame::GetValue(nsPresContext* aPresContext, const nsSize& a, const nsSize& b, char* ch)
1696 float p2t = aPresContext->ScaledPixelsToTwips();
1698 char width[100];
1699 char height[100];
1701 if (a.width == NS_INTRINSICSIZE)
1702 sprintf(width,"%s","INF");
1703 else
1704 sprintf(width,"%d", nscoord(a.width/*/p2t*/));
1706 if (a.height == NS_INTRINSICSIZE)
1707 sprintf(height,"%s","INF");
1708 else
1709 sprintf(height,"%d", nscoord(a.height/*/p2t*/));
1712 sprintf(ch, "(%s%s, %s%s)", width, (b.width != NS_INTRINSICSIZE ? "[SET]" : ""),
1713 height, (b.height != NS_INTRINSICSIZE ? "[SET]" : ""));
1717 void
1718 nsBoxFrame::GetValue(nsPresContext* aPresContext, int32_t a, int32_t b, char* ch)
1720 if (a == NS_INTRINSICSIZE)
1721 sprintf(ch, "%d[SET]", b);
1722 else
1723 sprintf(ch, "%d", a);
1726 nsresult
1727 nsBoxFrame::DisplayDebugInfoFor(nsIFrame* aBox,
1728 nsPoint& aPoint)
1730 nsBoxLayoutState state(GetPresContext());
1732 nscoord x = aPoint.x;
1733 nscoord y = aPoint.y;
1735 // get the area inside our border but not our debug margins.
1736 nsRect insideBorder(aBox->mRect);
1737 insideBorder.MoveTo(0,0):
1738 nsMargin border(0,0,0,0);
1739 aBox->GetBorderAndPadding(border);
1740 insideBorder.Deflate(border);
1742 bool isHorizontal = IsHorizontal();
1744 if (!insideBorder.Contains(nsPoint(x,y)))
1745 return NS_ERROR_FAILURE;
1747 //printf("%%%%%% inside box %%%%%%%\n");
1749 int count = 0;
1750 nsIFrame* child = nsBox::GetChildBox(aBox);
1752 nsMargin m;
1753 nsMargin m2;
1754 GetDebugBorder(m);
1755 PixelMarginToTwips(aPresContext, m);
1757 GetDebugMargin(m2);
1758 PixelMarginToTwips(aPresContext, m2);
1760 m += m2;
1762 if ((isHorizontal && y < insideBorder.y + m.top) ||
1763 (!isHorizontal && x < insideBorder.x + m.left)) {
1764 //printf("**** inside debug border *******\n");
1765 while (child)
1767 const nsRect& r = child->mRect;
1769 // if we are not in the child. But in the spacer above the child.
1770 if ((isHorizontal && x >= r.x && x < r.x + r.width) ||
1771 (!isHorizontal && y >= r.y && y < r.y + r.height)) {
1772 aCursor = NS_STYLE_CURSOR_POINTER;
1773 // found it but we already showed it.
1774 if (mDebugChild == child)
1775 return NS_OK;
1777 if (aBox->GetContent()) {
1778 printf("---------------\n");
1779 DumpBox(stdout);
1780 printf("\n");
1783 if (child->GetContent()) {
1784 printf("child #%d: ", count);
1785 child->DumpBox(stdout);
1786 printf("\n");
1789 mDebugChild = child;
1791 nsSize prefSizeCSS(NS_INTRINSICSIZE, NS_INTRINSICSIZE);
1792 nsSize minSizeCSS (NS_INTRINSICSIZE, NS_INTRINSICSIZE);
1793 nsSize maxSizeCSS (NS_INTRINSICSIZE, NS_INTRINSICSIZE);
1794 nscoord flexCSS = NS_INTRINSICSIZE;
1796 bool widthSet, heightSet;
1797 nsIFrame::AddCSSPrefSize(child, prefSizeCSS, widthSet, heightSet);
1798 nsIFrame::AddCSSMinSize (state, child, minSizeCSS, widthSet, heightSet);
1799 nsIFrame::AddCSSMaxSize (child, maxSizeCSS, widthSet, heightSet);
1800 nsIFrame::AddCSSFlex (state, child, flexCSS);
1802 nsSize prefSize = child->GetPrefSize(state);
1803 nsSize minSize = child->GetMinSize(state);
1804 nsSize maxSize = child->GetMaxSize(state);
1805 nscoord flexSize = child->GetFlex(state);
1806 nscoord ascentSize = child->GetBoxAscent(state);
1808 char min[100];
1809 char pref[100];
1810 char max[100];
1811 char calc[100];
1812 char flex[100];
1813 char ascent[100];
1815 nsSize actualSize;
1816 GetFrameSizeWithMargin(child, actualSize);
1817 nsSize actualSizeCSS (NS_INTRINSICSIZE, NS_INTRINSICSIZE);
1819 GetValue(aPresContext, minSize, minSizeCSS, min);
1820 GetValue(aPresContext, prefSize, prefSizeCSS, pref);
1821 GetValue(aPresContext, maxSize, maxSizeCSS, max);
1822 GetValue(aPresContext, actualSize, actualSizeCSS, calc);
1823 GetValue(aPresContext, flexSize, flexCSS, flex);
1824 GetValue(aPresContext, ascentSize, NS_INTRINSICSIZE, ascent);
1827 printf("min%s, pref%s, max%s, actual%s, flex=%s, ascent=%s\n\n",
1828 min,
1829 pref,
1830 max,
1831 calc,
1832 flex,
1833 ascent
1836 return NS_OK;
1839 child = GetNextBox(child);
1840 count++;
1842 } else {
1845 mDebugChild = nullptr;
1847 return NS_OK;
1850 void
1851 nsBoxFrame::SetDebugOnChildList(nsBoxLayoutState& aState, nsIFrame* aChild, bool aDebug)
1853 nsIFrame* child = nsBox::GetChildBox(this);
1854 while (child)
1856 child->SetDebug(aState, aDebug);
1857 child = GetNextBox(child);
1861 nsresult
1862 nsBoxFrame::GetFrameSizeWithMargin(nsIFrame* aBox, nsSize& aSize)
1864 nsRect rect(aBox->GetRect());
1865 nsMargin margin(0,0,0,0);
1866 aBox->GetMargin(margin);
1867 rect.Inflate(margin);
1868 aSize.width = rect.width;
1869 aSize.height = rect.height;
1870 return NS_OK;
1872 #endif
1874 // If you make changes to this function, check its counterparts
1875 // in nsTextBoxFrame and nsXULLabelFrame
1876 void
1877 nsBoxFrame::RegUnregAccessKey(bool aDoReg)
1879 MOZ_ASSERT(mContent);
1881 // find out what type of element this is
1882 nsIAtom *atom = mContent->Tag();
1884 // only support accesskeys for the following elements
1885 if (atom != nsGkAtoms::button &&
1886 atom != nsGkAtoms::toolbarbutton &&
1887 atom != nsGkAtoms::checkbox &&
1888 atom != nsGkAtoms::textbox &&
1889 atom != nsGkAtoms::tab &&
1890 atom != nsGkAtoms::radio) {
1891 return;
1894 nsAutoString accessKey;
1895 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, accessKey);
1897 if (accessKey.IsEmpty())
1898 return;
1900 // With a valid PresContext we can get the ESM
1901 // and register the access key
1902 EventStateManager* esm = PresContext()->EventStateManager();
1904 uint32_t key = accessKey.First();
1905 if (aDoReg)
1906 esm->RegisterAccessKey(mContent, key);
1907 else
1908 esm->UnregisterAccessKey(mContent, key);
1911 bool
1912 nsBoxFrame::SupportsOrdinalsInChildren()
1914 return true;
1917 // Helper less-than-or-equal function, used in CheckBoxOrder() as a
1918 // template-parameter for the sorting functions.
1919 bool
1920 IsBoxOrdinalLEQ(nsIFrame* aFrame1,
1921 nsIFrame* aFrame2)
1923 // If we've got a placeholder frame, use its out-of-flow frame's ordinal val.
1924 nsIFrame* aRealFrame1 = nsPlaceholderFrame::GetRealFrameFor(aFrame1);
1925 nsIFrame* aRealFrame2 = nsPlaceholderFrame::GetRealFrameFor(aFrame2);
1926 return aRealFrame1->GetOrdinal() <= aRealFrame2->GetOrdinal();
1929 void
1930 nsBoxFrame::CheckBoxOrder()
1932 if (SupportsOrdinalsInChildren() &&
1933 !nsIFrame::IsFrameListSorted<IsBoxOrdinalLEQ>(mFrames)) {
1934 nsIFrame::SortFrameList<IsBoxOrdinalLEQ>(mFrames);
1938 nsresult
1939 nsBoxFrame::LayoutChildAt(nsBoxLayoutState& aState, nsIFrame* aBox, const nsRect& aRect)
1941 // get the current rect
1942 nsRect oldRect(aBox->GetRect());
1943 aBox->SetBounds(aState, aRect);
1945 bool layout = NS_SUBTREE_DIRTY(aBox);
1947 if (layout || (oldRect.width != aRect.width || oldRect.height != aRect.height)) {
1948 return aBox->Layout(aState);
1951 return NS_OK;
1954 nsresult
1955 nsBoxFrame::RelayoutChildAtOrdinal(nsBoxLayoutState& aState, nsIFrame* aChild)
1957 if (!SupportsOrdinalsInChildren())
1958 return NS_OK;
1960 uint32_t ord = aChild->GetOrdinal();
1962 nsIFrame* child = mFrames.FirstChild();
1963 nsIFrame* newPrevSib = nullptr;
1965 while (child) {
1966 if (ord < child->GetOrdinal()) {
1967 break;
1970 if (child != aChild) {
1971 newPrevSib = child;
1974 child = GetNextBox(child);
1977 if (aChild->GetPrevSibling() == newPrevSib) {
1978 // This box is not moving.
1979 return NS_OK;
1982 // Take |aChild| out of its old position in the child list.
1983 mFrames.RemoveFrame(aChild);
1985 // Insert it after |newPrevSib| or at the start if it's null.
1986 mFrames.InsertFrame(nullptr, newPrevSib, aChild);
1988 return NS_OK;
1992 * This wrapper class lets us redirect mouse hits from descendant frames
1993 * of a menu to the menu itself, if they didn't specify 'allowevents'.
1995 * The wrapper simply turns a hit on a descendant element
1996 * into a hit on the menu itself, unless there is an element between the target
1997 * and the menu with the "allowevents" attribute.
1999 * This is used by nsMenuFrame and nsTreeColFrame.
2001 * Note that turning a hit on a descendant element into nullptr, so events
2002 * could fall through to the menu background, might be an appealing simplification
2003 * but it would mean slightly strange behaviour in some cases, because grabber
2004 * wrappers can be created for many individual lists and items, so the exact
2005 * fallthrough behaviour would be complex. E.g. an element with "allowevents"
2006 * on top of the Content() list could receive the event even if it was covered
2007 * by a PositionedDescenants() element without "allowevents". It is best to
2008 * never convert a non-null hit into null.
2010 // REVIEW: This is roughly of what nsMenuFrame::GetFrameForPoint used to do.
2011 // I've made 'allowevents' affect child elements because that seems the only
2012 // reasonable thing to do.
2013 class nsDisplayXULEventRedirector : public nsDisplayWrapList {
2014 public:
2015 nsDisplayXULEventRedirector(nsDisplayListBuilder* aBuilder,
2016 nsIFrame* aFrame, nsDisplayItem* aItem,
2017 nsIFrame* aTargetFrame)
2018 : nsDisplayWrapList(aBuilder, aFrame, aItem), mTargetFrame(aTargetFrame) {}
2019 nsDisplayXULEventRedirector(nsDisplayListBuilder* aBuilder,
2020 nsIFrame* aFrame, nsDisplayList* aList,
2021 nsIFrame* aTargetFrame)
2022 : nsDisplayWrapList(aBuilder, aFrame, aList), mTargetFrame(aTargetFrame) {}
2023 virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
2024 HitTestState* aState,
2025 nsTArray<nsIFrame*> *aOutFrames) MOZ_OVERRIDE;
2026 virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE {
2027 return false;
2029 NS_DISPLAY_DECL_NAME("XULEventRedirector", TYPE_XUL_EVENT_REDIRECTOR)
2030 private:
2031 nsIFrame* mTargetFrame;
2034 void nsDisplayXULEventRedirector::HitTest(nsDisplayListBuilder* aBuilder,
2035 const nsRect& aRect, HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
2037 nsTArray<nsIFrame*> outFrames;
2038 mList.HitTest(aBuilder, aRect, aState, &outFrames);
2040 bool topMostAdded = false;
2041 uint32_t localLength = outFrames.Length();
2043 for (uint32_t i = 0; i < localLength; i++) {
2045 for (nsIContent* content = outFrames.ElementAt(i)->GetContent();
2046 content && content != mTargetFrame->GetContent();
2047 content = content->GetParent()) {
2048 if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::allowevents,
2049 nsGkAtoms::_true, eCaseMatters)) {
2050 // Events are allowed on 'frame', so let it go.
2051 aOutFrames->AppendElement(outFrames.ElementAt(i));
2052 topMostAdded = true;
2056 // If there was no hit on the topmost frame or its ancestors,
2057 // add the target frame itself as the first candidate (see bug 562554).
2058 if (!topMostAdded) {
2059 topMostAdded = true;
2060 aOutFrames->AppendElement(mTargetFrame);
2065 class nsXULEventRedirectorWrapper : public nsDisplayWrapper
2067 public:
2068 explicit nsXULEventRedirectorWrapper(nsIFrame* aTargetFrame)
2069 : mTargetFrame(aTargetFrame) {}
2070 virtual nsDisplayItem* WrapList(nsDisplayListBuilder* aBuilder,
2071 nsIFrame* aFrame,
2072 nsDisplayList* aList) MOZ_OVERRIDE {
2073 return new (aBuilder)
2074 nsDisplayXULEventRedirector(aBuilder, aFrame, aList, mTargetFrame);
2076 virtual nsDisplayItem* WrapItem(nsDisplayListBuilder* aBuilder,
2077 nsDisplayItem* aItem) MOZ_OVERRIDE {
2078 return new (aBuilder)
2079 nsDisplayXULEventRedirector(aBuilder, aItem->Frame(), aItem,
2080 mTargetFrame);
2082 private:
2083 nsIFrame* mTargetFrame;
2086 void
2087 nsBoxFrame::WrapListsInRedirector(nsDisplayListBuilder* aBuilder,
2088 const nsDisplayListSet& aIn,
2089 const nsDisplayListSet& aOut)
2091 nsXULEventRedirectorWrapper wrapper(this);
2092 wrapper.WrapLists(aBuilder, this, aIn, aOut);
2095 bool
2096 nsBoxFrame::GetEventPoint(WidgetGUIEvent* aEvent, nsPoint &aPoint) {
2097 nsIntPoint refPoint;
2098 bool res = GetEventPoint(aEvent, refPoint);
2099 aPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, refPoint, this);
2100 return res;
2103 bool
2104 nsBoxFrame::GetEventPoint(WidgetGUIEvent* aEvent, nsIntPoint &aPoint) {
2105 NS_ENSURE_TRUE(aEvent, false);
2107 WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
2108 if (touchEvent) {
2109 // return false if there is more than one touch on the page, or if
2110 // we can't find a touch point
2111 if (touchEvent->touches.Length() != 1) {
2112 return false;
2115 dom::Touch* touch = touchEvent->touches.SafeElementAt(0);
2116 if (!touch) {
2117 return false;
2119 aPoint = touch->mRefPoint;
2120 } else {
2121 aPoint = LayoutDeviceIntPoint::ToUntyped(aEvent->refPoint);
2123 return true;