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/. */
6 
7 //
8 // Eric Vaughan
9 // Netscape Communications
10 //
11 // See documentation in associated header file
12 //
13 
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.
22 //
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.
26 
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.
33 
34 #include "nsBoxFrame.h"
35 
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 "nsSliderFrame.h"
70 #include <algorithm>
71 
72 // Needed for Print Preview
73 #include "nsIURI.h"
74 
75 #include "mozilla/TouchEvents.h"
76 
77 using namespace mozilla;
78 using namespace mozilla::dom;
79 using namespace mozilla::gfx;
80 
81 //define DEBUG_REDRAW
82 
83 #define DEBUG_SPRING_SIZE 8
84 #define DEBUG_BORDER_SIZE 2
85 #define COIL_SIZE 8
86 
87 //#define TEST_SANITY
88 
89 #ifdef DEBUG_rods
90 //#define DO_NOISY_REFLOW
91 #endif
92 
93 #ifdef DEBUG_LAYOUT
94 bool nsBoxFrame::gDebug = false;
95 nsIFrame* nsBoxFrame::mDebugChild = nullptr;
96 #endif
97 
98 nsIFrame*
NS_NewBoxFrame(nsIPresShell * aPresShell,nsStyleContext * aContext,bool aIsRoot,nsBoxLayout * aLayoutManager)99 NS_NewBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot, nsBoxLayout* aLayoutManager)
100 {
101   return new (aPresShell) nsBoxFrame(aContext, aIsRoot, aLayoutManager);
102 }
103 
104 nsIFrame*
NS_NewBoxFrame(nsIPresShell * aPresShell,nsStyleContext * aContext)105 NS_NewBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
106 {
107   return new (aPresShell) nsBoxFrame(aContext);
108 }
109 
110 NS_IMPL_FRAMEARENA_HELPERS(nsBoxFrame)
111 
112 #ifdef DEBUG
NS_QUERYFRAME_HEAD(nsBoxFrame)113 NS_QUERYFRAME_HEAD(nsBoxFrame)
114   NS_QUERYFRAME_ENTRY(nsBoxFrame)
115 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
116 #endif
117 
118 nsBoxFrame::nsBoxFrame(nsStyleContext* aContext,
119                        bool aIsRoot,
120                        nsBoxLayout* aLayoutManager) :
121   nsContainerFrame(aContext)
122 {
123   mState |= NS_STATE_IS_HORIZONTAL;
124   mState |= NS_STATE_AUTO_STRETCH;
125 
126   if (aIsRoot)
127      mState |= NS_STATE_IS_ROOT;
128 
129   mValign = vAlign_Top;
130   mHalign = hAlign_Left;
131 
132   // if no layout manager specified us the static sprocket layout
133   nsCOMPtr<nsBoxLayout> layout = aLayoutManager;
134 
135   if (layout == nullptr) {
136     NS_NewSprocketLayout(layout);
137   }
138 
139   SetXULLayoutManager(layout);
140 }
141 
~nsBoxFrame()142 nsBoxFrame::~nsBoxFrame()
143 {
144 }
145 
146 void
SetInitialChildList(ChildListID aListID,nsFrameList & aChildList)147 nsBoxFrame::SetInitialChildList(ChildListID     aListID,
148                                 nsFrameList&    aChildList)
149 {
150   nsContainerFrame::SetInitialChildList(aListID, aChildList);
151   if (aListID == kPrincipalList) {
152     // initialize our list of infos.
153     nsBoxLayoutState state(PresContext());
154     CheckBoxOrder();
155     if (mLayoutManager)
156       mLayoutManager->ChildrenSet(this, state, mFrames.FirstChild());
157   }
158 }
159 
160 /* virtual */ void
DidSetStyleContext(nsStyleContext * aOldStyleContext)161 nsBoxFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
162 {
163   nsContainerFrame::DidSetStyleContext(aOldStyleContext);
164 
165   // The values that CacheAttributes() computes depend on our style,
166   // so we need to recompute them here...
167   CacheAttributes();
168 }
169 
170 /**
171  * Initialize us. This is a good time to get the alignment of the box
172  */
173 void
Init(nsIContent * aContent,nsContainerFrame * aParent,nsIFrame * aPrevInFlow)174 nsBoxFrame::Init(nsIContent*       aContent,
175                  nsContainerFrame* aParent,
176                  nsIFrame*         aPrevInFlow)
177 {
178   nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
179 
180   if (GetStateBits() & NS_FRAME_FONT_INFLATION_CONTAINER) {
181     AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
182   }
183 
184   MarkIntrinsicISizesDirty();
185 
186   CacheAttributes();
187 
188 #ifdef DEBUG_LAYOUT
189     // if we are root and this
190   if (mState & NS_STATE_IS_ROOT) {
191     GetDebugPref();
192   }
193 #endif
194 
195   UpdateMouseThrough();
196 
197   // register access key
198   RegUnregAccessKey(true);
199 }
200 
UpdateMouseThrough()201 void nsBoxFrame::UpdateMouseThrough()
202 {
203   if (mContent) {
204     static nsIContent::AttrValuesArray strings[] =
205       {&nsGkAtoms::never, &nsGkAtoms::always, nullptr};
206     switch (mContent->FindAttrValueIn(kNameSpaceID_None,
207               nsGkAtoms::mousethrough, strings, eCaseMatters)) {
208       case 0: AddStateBits(NS_FRAME_MOUSE_THROUGH_NEVER); break;
209       case 1: AddStateBits(NS_FRAME_MOUSE_THROUGH_ALWAYS); break;
210       case 2: {
211         RemoveStateBits(NS_FRAME_MOUSE_THROUGH_ALWAYS);
212         RemoveStateBits(NS_FRAME_MOUSE_THROUGH_NEVER);
213         break;
214       }
215     }
216   }
217 }
218 
219 void
CacheAttributes()220 nsBoxFrame::CacheAttributes()
221 {
222   /*
223   printf("Caching: ");
224   XULDumpBox(stdout);
225   printf("\n");
226    */
227 
228   mValign = vAlign_Top;
229   mHalign = hAlign_Left;
230 
231   bool orient = false;
232   GetInitialOrientation(orient);
233   if (orient)
234     mState |= NS_STATE_IS_HORIZONTAL;
235   else
236     mState &= ~NS_STATE_IS_HORIZONTAL;
237 
238   bool normal = true;
239   GetInitialDirection(normal);
240   if (normal)
241     mState |= NS_STATE_IS_DIRECTION_NORMAL;
242   else
243     mState &= ~NS_STATE_IS_DIRECTION_NORMAL;
244 
245   GetInitialVAlignment(mValign);
246   GetInitialHAlignment(mHalign);
247 
248   bool equalSize = false;
249   GetInitialEqualSize(equalSize);
250   if (equalSize)
251         mState |= NS_STATE_EQUAL_SIZE;
252     else
253         mState &= ~NS_STATE_EQUAL_SIZE;
254 
255   bool autostretch = !!(mState & NS_STATE_AUTO_STRETCH);
256   GetInitialAutoStretch(autostretch);
257   if (autostretch)
258         mState |= NS_STATE_AUTO_STRETCH;
259      else
260         mState &= ~NS_STATE_AUTO_STRETCH;
261 
262 
263 #ifdef DEBUG_LAYOUT
264   bool debug = mState & NS_STATE_SET_TO_DEBUG;
265   bool debugSet = GetInitialDebug(debug);
266   if (debugSet) {
267         mState |= NS_STATE_DEBUG_WAS_SET;
268         if (debug)
269             mState |= NS_STATE_SET_TO_DEBUG;
270         else
271             mState &= ~NS_STATE_SET_TO_DEBUG;
272   } else {
273         mState &= ~NS_STATE_DEBUG_WAS_SET;
274   }
275 #endif
276 }
277 
278 #ifdef DEBUG_LAYOUT
279 bool
GetInitialDebug(bool & aDebug)280 nsBoxFrame::GetInitialDebug(bool& aDebug)
281 {
282   if (!GetContent())
283     return false;
284 
285   static nsIContent::AttrValuesArray strings[] =
286     {&nsGkAtoms::_false, &nsGkAtoms::_true, nullptr};
287   int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None,
288       nsGkAtoms::debug, strings, eCaseMatters);
289   if (index >= 0) {
290     aDebug = index == 1;
291     return true;
292   }
293 
294   return false;
295 }
296 #endif
297 
298 bool
GetInitialHAlignment(nsBoxFrame::Halignment & aHalign)299 nsBoxFrame::GetInitialHAlignment(nsBoxFrame::Halignment& aHalign)
300 {
301   if (!GetContent())
302     return false;
303 
304   // XXXdwh Everything inside this if statement is deprecated code.
305   static nsIContent::AttrValuesArray alignStrings[] =
306     {&nsGkAtoms::left, &nsGkAtoms::right, nullptr};
307   static const Halignment alignValues[] = {hAlign_Left, hAlign_Right};
308   int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::align,
309       alignStrings, eCaseMatters);
310   if (index >= 0) {
311     aHalign = alignValues[index];
312     return true;
313   }
314 
315   // Now that the deprecated stuff is out of the way, we move on to check the appropriate
316   // attribute.  For horizontal boxes, we are checking the PACK attribute.  For vertical boxes
317   // we are checking the ALIGN attribute.
318   nsIAtom* attrName = IsXULHorizontal() ? nsGkAtoms::pack : nsGkAtoms::align;
319   static nsIContent::AttrValuesArray strings[] =
320     {&nsGkAtoms::_empty, &nsGkAtoms::start, &nsGkAtoms::center, &nsGkAtoms::end, nullptr};
321   static const Halignment values[] =
322     {hAlign_Left/*not used*/, hAlign_Left, hAlign_Center, hAlign_Right};
323   index = GetContent()->FindAttrValueIn(kNameSpaceID_None, attrName,
324       strings, eCaseMatters);
325 
326   if (index == nsIContent::ATTR_VALUE_NO_MATCH) {
327     // The attr was present but had a nonsensical value. Revert to the default.
328     return false;
329   }
330   if (index > 0) {
331     aHalign = values[index];
332     return true;
333   }
334 
335   // Now that we've checked for the attribute it's time to check CSS.  For
336   // horizontal boxes we're checking PACK.  For vertical boxes we are checking
337   // ALIGN.
338   const nsStyleXUL* boxInfo = StyleXUL();
339   if (IsXULHorizontal()) {
340     switch (boxInfo->mBoxPack) {
341       case StyleBoxPack::Start:
342         aHalign = nsBoxFrame::hAlign_Left;
343         return true;
344       case StyleBoxPack::Center:
345         aHalign = nsBoxFrame::hAlign_Center;
346         return true;
347       case StyleBoxPack::End:
348         aHalign = nsBoxFrame::hAlign_Right;
349         return true;
350       default: // Nonsensical value. Just bail.
351         return false;
352     }
353   }
354   else {
355     switch (boxInfo->mBoxAlign) {
356       case StyleBoxAlign::Start:
357         aHalign = nsBoxFrame::hAlign_Left;
358         return true;
359       case StyleBoxAlign::Center:
360         aHalign = nsBoxFrame::hAlign_Center;
361         return true;
362       case StyleBoxAlign::End:
363         aHalign = nsBoxFrame::hAlign_Right;
364         return true;
365       default: // Nonsensical value. Just bail.
366         return false;
367     }
368   }
369 
370   return false;
371 }
372 
373 bool
GetInitialVAlignment(nsBoxFrame::Valignment & aValign)374 nsBoxFrame::GetInitialVAlignment(nsBoxFrame::Valignment& aValign)
375 {
376   if (!GetContent())
377     return false;
378 
379   static nsIContent::AttrValuesArray valignStrings[] =
380     {&nsGkAtoms::top, &nsGkAtoms::baseline, &nsGkAtoms::middle, &nsGkAtoms::bottom, nullptr};
381   static const Valignment valignValues[] =
382     {vAlign_Top, vAlign_BaseLine, vAlign_Middle, vAlign_Bottom};
383   int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::valign,
384       valignStrings, eCaseMatters);
385   if (index >= 0) {
386     aValign = valignValues[index];
387     return true;
388   }
389 
390   // Now that the deprecated stuff is out of the way, we move on to check the appropriate
391   // attribute.  For horizontal boxes, we are checking the ALIGN attribute.  For vertical boxes
392   // we are checking the PACK attribute.
393   nsIAtom* attrName = IsXULHorizontal() ? nsGkAtoms::align : nsGkAtoms::pack;
394   static nsIContent::AttrValuesArray strings[] =
395     {&nsGkAtoms::_empty, &nsGkAtoms::start, &nsGkAtoms::center,
396      &nsGkAtoms::baseline, &nsGkAtoms::end, nullptr};
397   static const Valignment values[] =
398     {vAlign_Top/*not used*/, vAlign_Top, vAlign_Middle, vAlign_BaseLine, vAlign_Bottom};
399   index = GetContent()->FindAttrValueIn(kNameSpaceID_None, attrName,
400       strings, eCaseMatters);
401   if (index == nsIContent::ATTR_VALUE_NO_MATCH) {
402     // The attr was present but had a nonsensical value. Revert to the default.
403     return false;
404   }
405   if (index > 0) {
406     aValign = values[index];
407     return true;
408   }
409 
410   // Now that we've checked for the attribute it's time to check CSS.  For
411   // horizontal boxes we're checking ALIGN.  For vertical boxes we are checking
412   // PACK.
413   const nsStyleXUL* boxInfo = StyleXUL();
414   if (IsXULHorizontal()) {
415     switch (boxInfo->mBoxAlign) {
416       case StyleBoxAlign::Start:
417         aValign = nsBoxFrame::vAlign_Top;
418         return true;
419       case StyleBoxAlign::Center:
420         aValign = nsBoxFrame::vAlign_Middle;
421         return true;
422       case StyleBoxAlign::Baseline:
423         aValign = nsBoxFrame::vAlign_BaseLine;
424         return true;
425       case StyleBoxAlign::End:
426         aValign = nsBoxFrame::vAlign_Bottom;
427         return true;
428       default: // Nonsensical value. Just bail.
429         return false;
430     }
431   }
432   else {
433     switch (boxInfo->mBoxPack) {
434       case StyleBoxPack::Start:
435         aValign = nsBoxFrame::vAlign_Top;
436         return true;
437       case StyleBoxPack::Center:
438         aValign = nsBoxFrame::vAlign_Middle;
439         return true;
440       case StyleBoxPack::End:
441         aValign = nsBoxFrame::vAlign_Bottom;
442         return true;
443       default: // Nonsensical value. Just bail.
444         return false;
445     }
446   }
447 
448   return false;
449 }
450 
451 void
GetInitialOrientation(bool & aIsHorizontal)452 nsBoxFrame::GetInitialOrientation(bool& aIsHorizontal)
453 {
454  // see if we are a vertical or horizontal box.
455   if (!GetContent())
456     return;
457 
458   // Check the style system first.
459   const nsStyleXUL* boxInfo = StyleXUL();
460   if (boxInfo->mBoxOrient == StyleBoxOrient::Horizontal) {
461     aIsHorizontal = true;
462   } else {
463     aIsHorizontal = false;
464   }
465 
466   // Now see if we have an attribute.  The attribute overrides
467   // the style system value.
468   static nsIContent::AttrValuesArray strings[] =
469     {&nsGkAtoms::vertical, &nsGkAtoms::horizontal, nullptr};
470   int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::orient,
471       strings, eCaseMatters);
472   if (index >= 0) {
473     aIsHorizontal = index == 1;
474   }
475 }
476 
477 void
GetInitialDirection(bool & aIsNormal)478 nsBoxFrame::GetInitialDirection(bool& aIsNormal)
479 {
480   if (!GetContent())
481     return;
482 
483   if (IsXULHorizontal()) {
484     // For horizontal boxes only, we initialize our value based off the CSS 'direction' property.
485     // This means that BiDI users will end up with horizontally inverted chrome.
486     aIsNormal = (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR); // If text runs RTL then so do we.
487   }
488   else
489     aIsNormal = true; // Assume a normal direction in the vertical case.
490 
491   // Now check the style system to see if we should invert aIsNormal.
492   const nsStyleXUL* boxInfo = StyleXUL();
493   if (boxInfo->mBoxDirection == StyleBoxDirection::Reverse) {
494     aIsNormal = !aIsNormal; // Invert our direction.
495   }
496 
497   // Now see if we have an attribute.  The attribute overrides
498   // the style system value.
499   if (IsXULHorizontal()) {
500     static nsIContent::AttrValuesArray strings[] =
501       {&nsGkAtoms::reverse, &nsGkAtoms::ltr, &nsGkAtoms::rtl, nullptr};
502     int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::dir,
503         strings, eCaseMatters);
504     if (index >= 0) {
505       bool values[] = {!aIsNormal, true, false};
506       aIsNormal = values[index];
507     }
508   } else if (GetContent()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir,
509                                        nsGkAtoms::reverse, eCaseMatters)) {
510     aIsNormal = !aIsNormal;
511   }
512 }
513 
514 /* Returns true if it was set.
515  */
516 bool
GetInitialEqualSize(bool & aEqualSize)517 nsBoxFrame::GetInitialEqualSize(bool& aEqualSize)
518 {
519  // see if we are a vertical or horizontal box.
520   if (!GetContent())
521      return false;
522 
523   if (GetContent()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::equalsize,
524                            nsGkAtoms::always, eCaseMatters)) {
525     aEqualSize = true;
526     return true;
527   }
528 
529   return false;
530 }
531 
532 /* Returns true if it was set.
533  */
534 bool
GetInitialAutoStretch(bool & aStretch)535 nsBoxFrame::GetInitialAutoStretch(bool& aStretch)
536 {
537   if (!GetContent())
538      return false;
539 
540   // Check the align attribute.
541   static nsIContent::AttrValuesArray strings[] =
542     {&nsGkAtoms::_empty, &nsGkAtoms::stretch, nullptr};
543   int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::align,
544       strings, eCaseMatters);
545   if (index != nsIContent::ATTR_MISSING && index != 0) {
546     aStretch = index == 1;
547     return true;
548   }
549 
550   // Check the CSS box-align property.
551   const nsStyleXUL* boxInfo = StyleXUL();
552   aStretch = (boxInfo->mBoxAlign == StyleBoxAlign::Stretch);
553 
554   return true;
555 }
556 
557 void
DidReflow(nsPresContext * aPresContext,const ReflowInput * aReflowInput,nsDidReflowStatus aStatus)558 nsBoxFrame::DidReflow(nsPresContext*           aPresContext,
559                       const ReflowInput*  aReflowInput,
560                       nsDidReflowStatus         aStatus)
561 {
562   nsFrameState preserveBits =
563     mState & (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
564   nsFrame::DidReflow(aPresContext, aReflowInput, aStatus);
565   mState |= preserveBits;
566 }
567 
568 bool
HonorPrintBackgroundSettings()569 nsBoxFrame::HonorPrintBackgroundSettings()
570 {
571   return (!mContent || !mContent->IsInNativeAnonymousSubtree()) &&
572     nsContainerFrame::HonorPrintBackgroundSettings();
573 }
574 
575 #ifdef DO_NOISY_REFLOW
576 static int myCounter = 0;
printSize(char * aDesc,nscoord aSize)577 static void printSize(char * aDesc, nscoord aSize)
578 {
579   printf(" %s: ", aDesc);
580   if (aSize == NS_UNCONSTRAINEDSIZE) {
581     printf("UC");
582   } else {
583     printf("%d", aSize);
584   }
585 }
586 #endif
587 
588 /* virtual */ nscoord
GetMinISize(nsRenderingContext * aRenderingContext)589 nsBoxFrame::GetMinISize(nsRenderingContext *aRenderingContext)
590 {
591   nscoord result;
592   DISPLAY_MIN_WIDTH(this, result);
593 
594   nsBoxLayoutState state(PresContext(), aRenderingContext);
595   nsSize minSize = GetXULMinSize(state);
596 
597   // GetXULMinSize returns border-box width, and we want to return content
598   // width.  Since Reflow uses the reflow state's border and padding, we
599   // actually just want to subtract what GetXULMinSize added, which is the
600   // result of GetXULBorderAndPadding.
601   nsMargin bp;
602   GetXULBorderAndPadding(bp);
603 
604   result = minSize.width - bp.LeftRight();
605   result = std::max(result, 0);
606 
607   return result;
608 }
609 
610 /* virtual */ nscoord
GetPrefISize(nsRenderingContext * aRenderingContext)611 nsBoxFrame::GetPrefISize(nsRenderingContext *aRenderingContext)
612 {
613   nscoord result;
614   DISPLAY_PREF_WIDTH(this, result);
615 
616   nsBoxLayoutState state(PresContext(), aRenderingContext);
617   nsSize prefSize = GetXULPrefSize(state);
618 
619   // GetXULPrefSize returns border-box width, and we want to return content
620   // width.  Since Reflow uses the reflow state's border and padding, we
621   // actually just want to subtract what GetXULPrefSize added, which is the
622   // result of GetXULBorderAndPadding.
623   nsMargin bp;
624   GetXULBorderAndPadding(bp);
625 
626   result = prefSize.width - bp.LeftRight();
627   result = std::max(result, 0);
628 
629   return result;
630 }
631 
632 void
Reflow(nsPresContext * aPresContext,ReflowOutput & aDesiredSize,const ReflowInput & aReflowInput,nsReflowStatus & aStatus)633 nsBoxFrame::Reflow(nsPresContext*          aPresContext,
634                    ReflowOutput&     aDesiredSize,
635                    const ReflowInput& aReflowInput,
636                    nsReflowStatus&          aStatus)
637 {
638   MarkInReflow();
639   // If you make changes to this method, please keep nsLeafBoxFrame::Reflow
640   // in sync, if the changes are applicable there.
641 
642   DO_GLOBAL_REFLOW_COUNT("nsBoxFrame");
643   DISPLAY_REFLOW(aPresContext, this, aReflowInput, aDesiredSize, aStatus);
644 
645   NS_ASSERTION(aReflowInput.ComputedWidth() >=0 &&
646                aReflowInput.ComputedHeight() >= 0, "Computed Size < 0");
647 
648 #ifdef DO_NOISY_REFLOW
649   printf("\n-------------Starting BoxFrame Reflow ----------------------------\n");
650   printf("%p ** nsBF::Reflow %d ", this, myCounter++);
651 
652   printSize("AW", aReflowInput.AvailableWidth());
653   printSize("AH", aReflowInput.AvailableHeight());
654   printSize("CW", aReflowInput.ComputedWidth());
655   printSize("CH", aReflowInput.ComputedHeight());
656 
657   printf(" *\n");
658 
659 #endif
660 
661   aStatus = NS_FRAME_COMPLETE;
662 
663   // create the layout state
664   nsBoxLayoutState state(aPresContext, aReflowInput.mRenderingContext,
665                          &aReflowInput, aReflowInput.mReflowDepth);
666 
667   WritingMode wm = aReflowInput.GetWritingMode();
668   LogicalSize computedSize(wm, aReflowInput.ComputedISize(),
669                            aReflowInput.ComputedBSize());
670 
671   LogicalMargin m = aReflowInput.ComputedLogicalBorderPadding();
672   // GetXULBorderAndPadding(m);
673 
674   LogicalSize prefSize(wm);
675 
676   // if we are told to layout intrinsic then get our preferred size.
677   NS_ASSERTION(computedSize.ISize(wm) != NS_INTRINSICSIZE,
678                "computed inline size should always be computed");
679   if (computedSize.BSize(wm) == NS_INTRINSICSIZE) {
680     nsSize physicalPrefSize = GetXULPrefSize(state);
681     nsSize minSize = GetXULMinSize(state);
682     nsSize maxSize = GetXULMaxSize(state);
683     // XXXbz isn't GetXULPrefSize supposed to bounds-check for us?
684     physicalPrefSize = BoundsCheck(minSize, physicalPrefSize, maxSize);
685     prefSize = LogicalSize(wm, physicalPrefSize);
686   }
687 
688   // get our desiredSize
689   computedSize.ISize(wm) += m.IStart(wm) + m.IEnd(wm);
690 
691   if (aReflowInput.ComputedBSize() == NS_INTRINSICSIZE) {
692     computedSize.BSize(wm) = prefSize.BSize(wm);
693     // prefSize is border-box but min/max constraints are content-box.
694     nscoord blockDirBorderPadding =
695       aReflowInput.ComputedLogicalBorderPadding().BStartEnd(wm);
696     nscoord contentBSize = computedSize.BSize(wm) - blockDirBorderPadding;
697     // Note: contentHeight might be negative, but that's OK because min-height
698     // is never negative.
699     computedSize.BSize(wm) = aReflowInput.ApplyMinMaxHeight(contentBSize) +
700                              blockDirBorderPadding;
701   } else {
702     computedSize.BSize(wm) += m.BStart(wm) + m.BEnd(wm);
703   }
704 
705   nsSize physicalSize = computedSize.GetPhysicalSize(wm);
706   nsRect r(mRect.x, mRect.y, physicalSize.width, physicalSize.height);
707 
708   SetXULBounds(state, r);
709 
710   // layout our children
711   XULLayout(state);
712 
713   // ok our child could have gotten bigger. So lets get its bounds
714 
715   // get the ascent
716   LogicalSize boxSize = GetLogicalSize(wm);
717   nscoord ascent = boxSize.BSize(wm);
718 
719   // getting the ascent could be a lot of work. Don't get it if
720   // we are the root. The viewport doesn't care about it.
721   if (!(mState & NS_STATE_IS_ROOT)) {
722     ascent = GetXULBoxAscent(state);
723   }
724 
725   aDesiredSize.SetSize(wm, boxSize);
726   aDesiredSize.SetBlockStartAscent(ascent);
727 
728   aDesiredSize.mOverflowAreas = GetOverflowAreas();
729 
730 #ifdef DO_NOISY_REFLOW
731   {
732     printf("%p ** nsBF(done) W:%d H:%d  ", this, aDesiredSize.Width(), aDesiredSize.Height());
733 
734     if (maxElementSize) {
735       printf("MW:%d\n", *maxElementWidth);
736     } else {
737       printf("MW:?\n");
738     }
739 
740   }
741 #endif
742 
743   ReflowAbsoluteFrames(aPresContext, aDesiredSize, aReflowInput, aStatus);
744 
745   NS_FRAME_SET_TRUNCATION(aStatus, aReflowInput, aDesiredSize);
746 }
747 
748 nsSize
GetXULPrefSize(nsBoxLayoutState & aBoxLayoutState)749 nsBoxFrame::GetXULPrefSize(nsBoxLayoutState& aBoxLayoutState)
750 {
751   NS_ASSERTION(aBoxLayoutState.GetRenderingContext(),
752                "must have rendering context");
753 
754   nsSize size(0,0);
755   DISPLAY_PREF_SIZE(this, size);
756   if (!DoesNeedRecalc(mPrefSize)) {
757      return mPrefSize;
758   }
759 
760 #ifdef DEBUG_LAYOUT
761   PropagateDebug(aBoxLayoutState);
762 #endif
763 
764   if (IsXULCollapsed())
765     return size;
766 
767   // if the size was not completely redefined in CSS then ask our children
768   bool widthSet, heightSet;
769   if (!nsIFrame::AddXULPrefSize(this, size, widthSet, heightSet))
770   {
771     if (mLayoutManager) {
772       nsSize layoutSize = mLayoutManager->GetXULPrefSize(this, aBoxLayoutState);
773       if (!widthSet)
774         size.width = layoutSize.width;
775       if (!heightSet)
776         size.height = layoutSize.height;
777     }
778     else {
779       size = nsBox::GetXULPrefSize(aBoxLayoutState);
780     }
781   }
782 
783   nsSize minSize = GetXULMinSize(aBoxLayoutState);
784   nsSize maxSize = GetXULMaxSize(aBoxLayoutState);
785   mPrefSize = BoundsCheck(minSize, size, maxSize);
786 
787   return mPrefSize;
788 }
789 
790 nscoord
GetXULBoxAscent(nsBoxLayoutState & aBoxLayoutState)791 nsBoxFrame::GetXULBoxAscent(nsBoxLayoutState& aBoxLayoutState)
792 {
793   if (!DoesNeedRecalc(mAscent))
794      return mAscent;
795 
796 #ifdef DEBUG_LAYOUT
797   PropagateDebug(aBoxLayoutState);
798 #endif
799 
800   if (IsXULCollapsed())
801     return 0;
802 
803   if (mLayoutManager)
804     mAscent = mLayoutManager->GetAscent(this, aBoxLayoutState);
805   else
806     mAscent = nsBox::GetXULBoxAscent(aBoxLayoutState);
807 
808   return mAscent;
809 }
810 
811 nsSize
GetXULMinSize(nsBoxLayoutState & aBoxLayoutState)812 nsBoxFrame::GetXULMinSize(nsBoxLayoutState& aBoxLayoutState)
813 {
814   NS_ASSERTION(aBoxLayoutState.GetRenderingContext(),
815                "must have rendering context");
816 
817   nsSize size(0,0);
818   DISPLAY_MIN_SIZE(this, size);
819   if (!DoesNeedRecalc(mMinSize)) {
820     return mMinSize;
821   }
822 
823 #ifdef DEBUG_LAYOUT
824   PropagateDebug(aBoxLayoutState);
825 #endif
826 
827   if (IsXULCollapsed())
828     return size;
829 
830   // if the size was not completely redefined in CSS then ask our children
831   bool widthSet, heightSet;
832   if (!nsIFrame::AddXULMinSize(aBoxLayoutState, this, size, widthSet, heightSet))
833   {
834     if (mLayoutManager) {
835       nsSize layoutSize = mLayoutManager->GetXULMinSize(this, aBoxLayoutState);
836       if (!widthSet)
837         size.width = layoutSize.width;
838       if (!heightSet)
839         size.height = layoutSize.height;
840     }
841     else {
842       size = nsBox::GetXULMinSize(aBoxLayoutState);
843     }
844   }
845 
846   mMinSize = size;
847 
848   return size;
849 }
850 
851 nsSize
GetXULMaxSize(nsBoxLayoutState & aBoxLayoutState)852 nsBoxFrame::GetXULMaxSize(nsBoxLayoutState& aBoxLayoutState)
853 {
854   NS_ASSERTION(aBoxLayoutState.GetRenderingContext(),
855                "must have rendering context");
856 
857   nsSize size(NS_INTRINSICSIZE, NS_INTRINSICSIZE);
858   DISPLAY_MAX_SIZE(this, size);
859   if (!DoesNeedRecalc(mMaxSize)) {
860     return mMaxSize;
861   }
862 
863 #ifdef DEBUG_LAYOUT
864   PropagateDebug(aBoxLayoutState);
865 #endif
866 
867   if (IsXULCollapsed())
868     return size;
869 
870   // if the size was not completely redefined in CSS then ask our children
871   bool widthSet, heightSet;
872   if (!nsIFrame::AddXULMaxSize(this, size, widthSet, heightSet))
873   {
874     if (mLayoutManager) {
875       nsSize layoutSize = mLayoutManager->GetXULMaxSize(this, aBoxLayoutState);
876       if (!widthSet)
877         size.width = layoutSize.width;
878       if (!heightSet)
879         size.height = layoutSize.height;
880     }
881     else {
882       size = nsBox::GetXULMaxSize(aBoxLayoutState);
883     }
884   }
885 
886   mMaxSize = size;
887 
888   return size;
889 }
890 
891 nscoord
GetXULFlex()892 nsBoxFrame::GetXULFlex()
893 {
894   if (!DoesNeedRecalc(mFlex))
895      return mFlex;
896 
897   mFlex = nsBox::GetXULFlex();
898 
899   return mFlex;
900 }
901 
902 /**
903  * If subclassing please subclass this method not layout.
904  * layout will call this method.
905  */
906 NS_IMETHODIMP
DoXULLayout(nsBoxLayoutState & aState)907 nsBoxFrame::DoXULLayout(nsBoxLayoutState& aState)
908 {
909   uint32_t oldFlags = aState.LayoutFlags();
910   aState.SetLayoutFlags(0);
911 
912   nsresult rv = NS_OK;
913   if (mLayoutManager) {
914     CoordNeedsRecalc(mAscent);
915     rv = mLayoutManager->XULLayout(this, aState);
916   }
917 
918   aState.SetLayoutFlags(oldFlags);
919 
920   if (HasAbsolutelyPositionedChildren()) {
921     // Set up a |reflowInput| to pass into ReflowAbsoluteFrames
922     WritingMode wm = GetWritingMode();
923     ReflowInput reflowInput(aState.PresContext(), this,
924                                   aState.GetRenderingContext(),
925                                   LogicalSize(wm, GetLogicalSize().ISize(wm),
926                                               NS_UNCONSTRAINEDSIZE));
927 
928     // Set up a |desiredSize| to pass into ReflowAbsoluteFrames
929     ReflowOutput desiredSize(reflowInput);
930     desiredSize.Width() = mRect.width;
931     desiredSize.Height() = mRect.height;
932 
933     // get the ascent (cribbed from ::Reflow)
934     nscoord ascent = mRect.height;
935 
936     // getting the ascent could be a lot of work. Don't get it if
937     // we are the root. The viewport doesn't care about it.
938     if (!(mState & NS_STATE_IS_ROOT)) {
939       ascent = GetXULBoxAscent(aState);
940     }
941     desiredSize.SetBlockStartAscent(ascent);
942     desiredSize.mOverflowAreas = GetOverflowAreas();
943 
944     AddStateBits(NS_FRAME_IN_REFLOW);
945     // Set up a |reflowStatus| to pass into ReflowAbsoluteFrames
946     // (just a dummy value; hopefully that's OK)
947     nsReflowStatus reflowStatus = NS_FRAME_COMPLETE;
948     ReflowAbsoluteFrames(aState.PresContext(), desiredSize,
949                          reflowInput, reflowStatus);
950     RemoveStateBits(NS_FRAME_IN_REFLOW);
951   }
952 
953   return rv;
954 }
955 
956 void
DestroyFrom(nsIFrame * aDestructRoot)957 nsBoxFrame::DestroyFrom(nsIFrame* aDestructRoot)
958 {
959   // unregister access key
960   RegUnregAccessKey(false);
961 
962   // clean up the container box's layout manager and child boxes
963   SetXULLayoutManager(nullptr);
964 
965   nsContainerFrame::DestroyFrom(aDestructRoot);
966 }
967 
968 #ifdef DEBUG_LAYOUT
969 nsresult
SetXULDebug(nsBoxLayoutState & aState,bool aDebug)970 nsBoxFrame::SetXULDebug(nsBoxLayoutState& aState, bool aDebug)
971 {
972   // see if our state matches the given debug state
973   bool debugSet = mState & NS_STATE_CURRENTLY_IN_DEBUG;
974   bool debugChanged = (!aDebug && debugSet) || (aDebug && !debugSet);
975 
976   // if it doesn't then tell each child below us the new debug state
977   if (debugChanged)
978   {
979      if (aDebug) {
980          mState |= NS_STATE_CURRENTLY_IN_DEBUG;
981      } else {
982          mState &= ~NS_STATE_CURRENTLY_IN_DEBUG;
983      }
984 
985      SetDebugOnChildList(aState, mFirstChild, aDebug);
986 
987     MarkIntrinsicISizesDirty();
988   }
989 
990   return NS_OK;
991 }
992 #endif
993 
994 /* virtual */ void
MarkIntrinsicISizesDirty()995 nsBoxFrame::MarkIntrinsicISizesDirty()
996 {
997   SizeNeedsRecalc(mPrefSize);
998   SizeNeedsRecalc(mMinSize);
999   SizeNeedsRecalc(mMaxSize);
1000   CoordNeedsRecalc(mFlex);
1001   CoordNeedsRecalc(mAscent);
1002 
1003   if (mLayoutManager) {
1004     nsBoxLayoutState state(PresContext());
1005     mLayoutManager->IntrinsicISizesDirty(this, state);
1006   }
1007 
1008   // Don't call base class method, since everything it does is within an
1009   // IsXULBoxWrapped check.
1010 }
1011 
1012 void
RemoveFrame(ChildListID aListID,nsIFrame * aOldFrame)1013 nsBoxFrame::RemoveFrame(ChildListID     aListID,
1014                         nsIFrame*       aOldFrame)
1015 {
1016   NS_PRECONDITION(aListID == kPrincipalList, "We don't support out-of-flow kids");
1017   nsPresContext* presContext = PresContext();
1018   nsBoxLayoutState state(presContext);
1019 
1020   // remove the child frame
1021   mFrames.RemoveFrame(aOldFrame);
1022 
1023   // notify the layout manager
1024   if (mLayoutManager)
1025     mLayoutManager->ChildrenRemoved(this, state, aOldFrame);
1026 
1027   // destroy the child frame
1028   aOldFrame->Destroy();
1029 
1030   // mark us dirty and generate a reflow command
1031   PresContext()->PresShell()->
1032     FrameNeedsReflow(this, nsIPresShell::eTreeChange,
1033                      NS_FRAME_HAS_DIRTY_CHILDREN);
1034 }
1035 
1036 void
InsertFrames(ChildListID aListID,nsIFrame * aPrevFrame,nsFrameList & aFrameList)1037 nsBoxFrame::InsertFrames(ChildListID     aListID,
1038                          nsIFrame*       aPrevFrame,
1039                          nsFrameList&    aFrameList)
1040 {
1041    NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
1042                 "inserting after sibling frame with different parent");
1043    NS_ASSERTION(!aPrevFrame || mFrames.ContainsFrame(aPrevFrame),
1044                 "inserting after sibling frame not in our child list");
1045    NS_PRECONDITION(aListID == kPrincipalList, "We don't support out-of-flow kids");
1046    nsBoxLayoutState state(PresContext());
1047 
1048    // insert the child frames
1049    const nsFrameList::Slice& newFrames =
1050      mFrames.InsertFrames(this, aPrevFrame, aFrameList);
1051 
1052    // notify the layout manager
1053    if (mLayoutManager)
1054      mLayoutManager->ChildrenInserted(this, state, aPrevFrame, newFrames);
1055 
1056    // Make sure to check box order _after_ notifying the layout
1057    // manager; otherwise the slice we give the layout manager will
1058    // just be bogus.  If the layout manager cares about the order, we
1059    // just lose.
1060    CheckBoxOrder();
1061 
1062 #ifdef DEBUG_LAYOUT
1063    // if we are in debug make sure our children are in debug as well.
1064    if (mState & NS_STATE_CURRENTLY_IN_DEBUG)
1065        SetDebugOnChildList(state, mFrames.FirstChild(), true);
1066 #endif
1067 
1068    PresContext()->PresShell()->
1069      FrameNeedsReflow(this, nsIPresShell::eTreeChange,
1070                       NS_FRAME_HAS_DIRTY_CHILDREN);
1071 }
1072 
1073 
1074 void
AppendFrames(ChildListID aListID,nsFrameList & aFrameList)1075 nsBoxFrame::AppendFrames(ChildListID     aListID,
1076                          nsFrameList&    aFrameList)
1077 {
1078    NS_PRECONDITION(aListID == kPrincipalList, "We don't support out-of-flow kids");
1079    nsBoxLayoutState state(PresContext());
1080 
1081    // append the new frames
1082    const nsFrameList::Slice& newFrames = mFrames.AppendFrames(this, aFrameList);
1083 
1084    // notify the layout manager
1085    if (mLayoutManager)
1086      mLayoutManager->ChildrenAppended(this, state, newFrames);
1087 
1088    // Make sure to check box order _after_ notifying the layout
1089    // manager; otherwise the slice we give the layout manager will
1090    // just be bogus.  If the layout manager cares about the order, we
1091    // just lose.
1092    CheckBoxOrder();
1093 
1094 #ifdef DEBUG_LAYOUT
1095    // if we are in debug make sure our children are in debug as well.
1096    if (mState & NS_STATE_CURRENTLY_IN_DEBUG)
1097        SetDebugOnChildList(state, mFrames.FirstChild(), true);
1098 #endif
1099 
1100    // XXXbz why is this NS_FRAME_FIRST_REFLOW check here?
1101    if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
1102      PresContext()->PresShell()->
1103        FrameNeedsReflow(this, nsIPresShell::eTreeChange,
1104                         NS_FRAME_HAS_DIRTY_CHILDREN);
1105    }
1106 }
1107 
1108 /* virtual */ nsContainerFrame*
GetContentInsertionFrame()1109 nsBoxFrame::GetContentInsertionFrame()
1110 {
1111   if (GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK)
1112     return PrincipalChildList().FirstChild()->GetContentInsertionFrame();
1113   return nsContainerFrame::GetContentInsertionFrame();
1114 }
1115 
1116 nsresult
AttributeChanged(int32_t aNameSpaceID,nsIAtom * aAttribute,int32_t aModType)1117 nsBoxFrame::AttributeChanged(int32_t aNameSpaceID,
1118                              nsIAtom* aAttribute,
1119                              int32_t aModType)
1120 {
1121   nsresult rv = nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
1122                                                    aModType);
1123 
1124   // Ignore 'width', 'height', 'screenX', 'screenY' and 'sizemode' on a
1125   // <window>.
1126   if (mContent->IsAnyOfXULElements(nsGkAtoms::window,
1127                                    nsGkAtoms::page,
1128                                    nsGkAtoms::dialog,
1129                                    nsGkAtoms::wizard) &&
1130       (nsGkAtoms::width == aAttribute ||
1131        nsGkAtoms::height == aAttribute ||
1132        nsGkAtoms::screenX == aAttribute ||
1133        nsGkAtoms::screenY == aAttribute ||
1134        nsGkAtoms::sizemode == aAttribute)) {
1135     return rv;
1136   }
1137 
1138   if (aAttribute == nsGkAtoms::width       ||
1139       aAttribute == nsGkAtoms::height      ||
1140       aAttribute == nsGkAtoms::align       ||
1141       aAttribute == nsGkAtoms::valign      ||
1142       aAttribute == nsGkAtoms::left        ||
1143       aAttribute == nsGkAtoms::top         ||
1144       aAttribute == nsGkAtoms::right        ||
1145       aAttribute == nsGkAtoms::bottom       ||
1146       aAttribute == nsGkAtoms::start        ||
1147       aAttribute == nsGkAtoms::end          ||
1148       aAttribute == nsGkAtoms::minwidth     ||
1149       aAttribute == nsGkAtoms::maxwidth     ||
1150       aAttribute == nsGkAtoms::minheight    ||
1151       aAttribute == nsGkAtoms::maxheight    ||
1152       aAttribute == nsGkAtoms::flex         ||
1153       aAttribute == nsGkAtoms::orient       ||
1154       aAttribute == nsGkAtoms::pack         ||
1155       aAttribute == nsGkAtoms::dir          ||
1156       aAttribute == nsGkAtoms::mousethrough ||
1157       aAttribute == nsGkAtoms::equalsize) {
1158 
1159     if (aAttribute == nsGkAtoms::align  ||
1160         aAttribute == nsGkAtoms::valign ||
1161         aAttribute == nsGkAtoms::orient  ||
1162         aAttribute == nsGkAtoms::pack    ||
1163 #ifdef DEBUG_LAYOUT
1164         aAttribute == nsGkAtoms::debug   ||
1165 #endif
1166         aAttribute == nsGkAtoms::dir) {
1167 
1168       mValign = nsBoxFrame::vAlign_Top;
1169       mHalign = nsBoxFrame::hAlign_Left;
1170 
1171       bool orient = true;
1172       GetInitialOrientation(orient);
1173       if (orient)
1174         mState |= NS_STATE_IS_HORIZONTAL;
1175       else
1176         mState &= ~NS_STATE_IS_HORIZONTAL;
1177 
1178       bool normal = true;
1179       GetInitialDirection(normal);
1180       if (normal)
1181         mState |= NS_STATE_IS_DIRECTION_NORMAL;
1182       else
1183         mState &= ~NS_STATE_IS_DIRECTION_NORMAL;
1184 
1185       GetInitialVAlignment(mValign);
1186       GetInitialHAlignment(mHalign);
1187 
1188       bool equalSize = false;
1189       GetInitialEqualSize(equalSize);
1190       if (equalSize)
1191         mState |= NS_STATE_EQUAL_SIZE;
1192       else
1193         mState &= ~NS_STATE_EQUAL_SIZE;
1194 
1195 #ifdef DEBUG_LAYOUT
1196       bool debug = mState & NS_STATE_SET_TO_DEBUG;
1197       bool debugSet = GetInitialDebug(debug);
1198       if (debugSet) {
1199         mState |= NS_STATE_DEBUG_WAS_SET;
1200 
1201         if (debug)
1202           mState |= NS_STATE_SET_TO_DEBUG;
1203         else
1204           mState &= ~NS_STATE_SET_TO_DEBUG;
1205       } else {
1206         mState &= ~NS_STATE_DEBUG_WAS_SET;
1207       }
1208 #endif
1209 
1210       bool autostretch = !!(mState & NS_STATE_AUTO_STRETCH);
1211       GetInitialAutoStretch(autostretch);
1212       if (autostretch)
1213         mState |= NS_STATE_AUTO_STRETCH;
1214       else
1215         mState &= ~NS_STATE_AUTO_STRETCH;
1216     }
1217     else if (aAttribute == nsGkAtoms::left ||
1218              aAttribute == nsGkAtoms::top ||
1219              aAttribute == nsGkAtoms::right ||
1220              aAttribute == nsGkAtoms::bottom ||
1221              aAttribute == nsGkAtoms::start ||
1222              aAttribute == nsGkAtoms::end) {
1223       mState &= ~NS_STATE_STACK_NOT_POSITIONED;
1224     }
1225     else if (aAttribute == nsGkAtoms::mousethrough) {
1226       UpdateMouseThrough();
1227     }
1228 
1229     PresContext()->PresShell()->
1230       FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
1231   }
1232   else if (aAttribute == nsGkAtoms::ordinal) {
1233     nsIFrame* parent = GetParentXULBox(this);
1234     // If our parent is not a box, there's not much we can do... but in that
1235     // case our ordinal doesn't matter anyway, so that's ok.
1236     // Also don't bother with popup frames since they are kept on the
1237     // kPopupList and XULRelayoutChildAtOrdinal() only handles
1238     // principal children.
1239     if (parent && !(GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
1240         StyleDisplay()->mDisplay != mozilla::StyleDisplay::Popup) {
1241       parent->XULRelayoutChildAtOrdinal(this);
1242       // XXXldb Should this instead be a tree change on the child or parent?
1243       PresContext()->PresShell()->
1244         FrameNeedsReflow(parent, nsIPresShell::eStyleChange,
1245                          NS_FRAME_IS_DIRTY);
1246     }
1247   }
1248   // If the accesskey changed, register for the new value
1249   // The old value has been unregistered in nsXULElement::SetAttr
1250   else if (aAttribute == nsGkAtoms::accesskey) {
1251     RegUnregAccessKey(true);
1252   }
1253   else if (aAttribute == nsGkAtoms::rows &&
1254            mContent->IsXULElement(nsGkAtoms::tree)) {
1255     // Reflow ourselves and all our children if "rows" changes, since
1256     // nsTreeBodyFrame's layout reads this from its parent (this frame).
1257     PresContext()->PresShell()->
1258       FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
1259   }
1260 
1261   return rv;
1262 }
1263 
1264 #ifdef DEBUG_LAYOUT
1265 void
GetDebugPref()1266 nsBoxFrame::GetDebugPref()
1267 {
1268   gDebug = Preferences::GetBool("xul.debug.box");
1269 }
1270 
1271 class nsDisplayXULDebug : public nsDisplayItem {
1272 public:
nsDisplayXULDebug(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame)1273   nsDisplayXULDebug(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) :
1274     nsDisplayItem(aBuilder, aFrame) {
1275     MOZ_COUNT_CTOR(nsDisplayXULDebug);
1276   }
1277 #ifdef NS_BUILD_REFCNT_LOGGING
~nsDisplayXULDebug()1278   virtual ~nsDisplayXULDebug() {
1279     MOZ_COUNT_DTOR(nsDisplayXULDebug);
1280   }
1281 #endif
1282 
HitTest(nsDisplayListBuilder * aBuilder,nsRect aRect,HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames)1283   virtual void HitTest(nsDisplayListBuilder* aBuilder, nsRect aRect,
1284                        HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {
1285     nsPoint rectCenter(aRect.x + aRect.width / 2, aRect.y + aRect.height / 2);
1286     static_cast<nsBoxFrame*>(mFrame)->
1287       DisplayDebugInfoFor(this, rectCenter - ToReferenceFrame());
1288     aOutFrames->AppendElement(this);
1289   }
1290   virtual void Paint(nsDisplayListBuilder* aBuilder
1291                      nsRenderingContext* aCtx);
1292   NS_DISPLAY_DECL_NAME("XULDebug", TYPE_XUL_DEBUG)
1293 };
1294 
1295 void
Paint(nsDisplayListBuilder * aBuilder,nsRenderingContext * aCtx)1296 nsDisplayXULDebug::Paint(nsDisplayListBuilder* aBuilder,
1297                          nsRenderingContext* aCtx)
1298 {
1299   static_cast<nsBoxFrame*>(mFrame)->
1300     PaintXULDebugOverlay(*aCtx->GetDrawTarget(), ToReferenceFrame());
1301 }
1302 
1303 static void
PaintXULDebugBackground(nsIFrame * aFrame,DrawTarget * aDrawTarget,const nsRect & aDirtyRect,nsPoint aPt)1304 PaintXULDebugBackground(nsIFrame* aFrame, DrawTarget* aDrawTarget,
1305                         const nsRect& aDirtyRect, nsPoint aPt)
1306 {
1307   static_cast<nsBoxFrame*>(aFrame)->PaintXULDebugBackground(aDrawTarget, aPt);
1308 }
1309 #endif
1310 
1311 void
BuildDisplayList(nsDisplayListBuilder * aBuilder,const nsRect & aDirtyRect,const nsDisplayListSet & aLists)1312 nsBoxFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
1313                              const nsRect&           aDirtyRect,
1314                              const nsDisplayListSet& aLists)
1315 {
1316   bool forceLayer = false;
1317 
1318   if (GetContent()->IsXULElement()) {
1319     // forcelayer is only supported on XUL elements with box layout
1320     if (GetContent()->HasAttr(kNameSpaceID_None, nsGkAtoms::layer)) {
1321       forceLayer = true;
1322     }
1323     // Check for frames that are marked as a part of the region used
1324     // in calculating glass margins on Windows.
1325     const nsStyleDisplay* styles = StyleDisplay();
1326     if (styles && styles->mAppearance == NS_THEME_WIN_EXCLUDE_GLASS) {
1327       aBuilder->AddWindowExcludeGlassRegion(
1328           nsRect(aBuilder->ToReferenceFrame(this), GetSize()));
1329     }
1330   }
1331 
1332   nsDisplayListCollection tempLists;
1333   const nsDisplayListSet& destination = forceLayer ? tempLists : aLists;
1334 
1335   DisplayBorderBackgroundOutline(aBuilder, destination);
1336 
1337 #ifdef DEBUG_LAYOUT
1338   if (mState & NS_STATE_CURRENTLY_IN_DEBUG) {
1339     destination.BorderBackground()->AppendNewToTop(new (aBuilder)
1340       nsDisplayGeneric(aBuilder, this, PaintXULDebugBackground,
1341                        "XULDebugBackground"));
1342     destination.Outlines()->AppendNewToTop(new (aBuilder)
1343       nsDisplayXULDebug(aBuilder, this));
1344   }
1345 #endif
1346 
1347   BuildDisplayListForChildren(aBuilder, aDirtyRect, destination);
1348 
1349   // see if we have to draw a selection frame around this container
1350   DisplaySelectionOverlay(aBuilder, destination.Content());
1351 
1352   if (forceLayer) {
1353     // This is a bit of a hack. Collect up all descendant display items
1354     // and merge them into a single Content() list. This can cause us
1355     // to violate CSS stacking order, but forceLayer is a magic
1356     // XUL-only extension anyway.
1357     nsDisplayList masterList;
1358     masterList.AppendToTop(tempLists.BorderBackground());
1359     masterList.AppendToTop(tempLists.BlockBorderBackgrounds());
1360     masterList.AppendToTop(tempLists.Floats());
1361     masterList.AppendToTop(tempLists.Content());
1362     masterList.AppendToTop(tempLists.PositionedDescendants());
1363     masterList.AppendToTop(tempLists.Outlines());
1364 
1365     // Wrap the list to make it its own layer
1366     aLists.Content()->AppendNewToTop(new (aBuilder)
1367       nsDisplayOwnLayer(aBuilder, this, &masterList));
1368   }
1369 }
1370 
1371 void
BuildDisplayListForChildren(nsDisplayListBuilder * aBuilder,const nsRect & aDirtyRect,const nsDisplayListSet & aLists)1372 nsBoxFrame::BuildDisplayListForChildren(nsDisplayListBuilder*   aBuilder,
1373                                         const nsRect&           aDirtyRect,
1374                                         const nsDisplayListSet& aLists)
1375 {
1376   nsIFrame* kid = mFrames.FirstChild();
1377   // Put each child's background onto the BlockBorderBackgrounds list
1378   // to emulate the existing two-layer XUL painting scheme.
1379   nsDisplayListSet set(aLists, aLists.BlockBorderBackgrounds());
1380   // The children should be in the right order
1381   while (kid) {
1382     BuildDisplayListForChild(aBuilder, kid, aDirtyRect, set);
1383     kid = kid->GetNextSibling();
1384   }
1385 }
1386 
1387 // REVIEW: PaintChildren did a few things none of which are a big deal
1388 // anymore:
1389 // * Paint some debugging rects for this frame.
1390 // This is done by nsDisplayXULDebugBackground, which goes in the
1391 // BorderBackground() layer so it isn't clipped by OVERFLOW_CLIP.
1392 // * Apply OVERFLOW_CLIP to the children.
1393 // This is now in nsFrame::BuildDisplayListForStackingContext/Child.
1394 // * Actually paint the children.
1395 // Moved to BuildDisplayList.
1396 // * Paint per-kid debug information.
1397 // This is done by nsDisplayXULDebug, which is in the Outlines()
1398 // layer so it goes on top. This means it is not clipped by OVERFLOW_CLIP,
1399 // whereas it did used to respect OVERFLOW_CLIP, but too bad.
1400 #ifdef DEBUG_LAYOUT
1401 void
PaintXULDebugBackground(DrawTarget * aDrawTarget,nsPoint aPt)1402 nsBoxFrame::PaintXULDebugBackground(DrawTarget* aDrawTarget, nsPoint aPt)
1403 {
1404   nsMargin border;
1405   GetXULBorder(border);
1406 
1407   nsMargin debugBorder;
1408   nsMargin debugMargin;
1409   nsMargin debugPadding;
1410 
1411   bool isHorizontal = IsXULHorizontal();
1412 
1413   GetDebugBorder(debugBorder);
1414   PixelMarginToTwips(debugBorder);
1415 
1416   GetDebugMargin(debugMargin);
1417   PixelMarginToTwips(debugMargin);
1418 
1419   GetDebugPadding(debugPadding);
1420   PixelMarginToTwips(debugPadding);
1421 
1422   nsRect inner(mRect);
1423   inner.MoveTo(aPt);
1424   inner.Deflate(debugMargin);
1425   inner.Deflate(border);
1426   //nsRect borderRect(inner);
1427 
1428   int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
1429 
1430   ColorPattern color(ToDeviceColor(isHorizontal ? Color(0.f, 0.f, 1.f, 1.f) :
1431                                                   Color(1.f, 0.f, 0.f, 1.f)));
1432 
1433   //left
1434   nsRect r(inner);
1435   r.width = debugBorder.left;
1436   aDrawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), color);
1437 
1438   // top
1439   r = inner;
1440   r.height = debugBorder.top;
1441   aDrawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), color);
1442 
1443   //right
1444   r = inner;
1445   r.x = r.x + r.width - debugBorder.right;
1446   r.width = debugBorder.right;
1447   aDrawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), color);
1448 
1449   //bottom
1450   r = inner;
1451   r.y = r.y + r.height - debugBorder.bottom;
1452   r.height = debugBorder.bottom;
1453   aDrawTarget->FillRect(NSRectToRect(r, appUnitsPerDevPixel), color);
1454 
1455   // If we have dirty children or we are dirty place a green border around us.
1456   if (NS_SUBTREE_DIRTY(this)) {
1457     nsRect dirty(inner);
1458     ColorPattern green(ToDeviceColor(Color(0.f, 1.f, 0.f, 1.f)));
1459     aDrawTarget->StrokeRect(NSRectToRect(dirty, appUnitsPerDevPixel), green);
1460   }
1461 }
1462 
1463 void
PaintXULDebugOverlay(DrawTarget & aDrawTarget,nsPoint aPt)1464 nsBoxFrame::PaintXULDebugOverlay(DrawTarget& aDrawTarget, nsPoint aPt)
1465 {
1466   nsMargin border;
1467   GetXULBorder(border);
1468 
1469   nsMargin debugMargin;
1470   GetDebugMargin(debugMargin);
1471   PixelMarginToTwips(debugMargin);
1472 
1473   nsRect inner(mRect);
1474   inner.MoveTo(aPt);
1475   inner.Deflate(debugMargin);
1476   inner.Deflate(border);
1477 
1478   nscoord onePixel = GetPresContext()->IntScaledPixelsToTwips(1);
1479 
1480   kid = nsBox::GetChildXULBox(this);
1481   while (nullptr != kid) {
1482     bool isHorizontal = IsXULHorizontal();
1483 
1484     nscoord x, y, borderSize, spacerSize;
1485 
1486     nsRect cr(kid->mRect);
1487     nsMargin margin;
1488     kid->GetXULMargin(margin);
1489     cr.Inflate(margin);
1490 
1491     if (isHorizontal)
1492     {
1493         cr.y = inner.y;
1494         x = cr.x;
1495         y = cr.y + onePixel;
1496         spacerSize = debugBorder.top - onePixel*4;
1497     } else {
1498         cr.x = inner.x;
1499         x = cr.y;
1500         y = cr.x + onePixel;
1501         spacerSize = debugBorder.left - onePixel*4;
1502     }
1503 
1504     nscoord flex = kid->GetXULFlex();
1505 
1506     if (!kid->IsXULCollapsed()) {
1507       if (isHorizontal)
1508           borderSize = cr.width;
1509       else
1510           borderSize = cr.height;
1511 
1512       DrawSpacer(GetPresContext(), aDrawTarget, isHorizontal, flex, x, y, borderSize, spacerSize);
1513     }
1514 
1515     kid = GetNextXULBox(kid);
1516   }
1517 }
1518 #endif
1519 
1520 #ifdef DEBUG_LAYOUT
1521 void
GetBoxName(nsAutoString & aName)1522 nsBoxFrame::GetBoxName(nsAutoString& aName)
1523 {
1524    GetFrameName(aName);
1525 }
1526 #endif
1527 
1528 #ifdef DEBUG_FRAME_DUMP
1529 nsresult
GetFrameName(nsAString & aResult) const1530 nsBoxFrame::GetFrameName(nsAString& aResult) const
1531 {
1532   return MakeFrameName(NS_LITERAL_STRING("Box"), aResult);
1533 }
1534 #endif
1535 
1536 nsIAtom*
GetType() const1537 nsBoxFrame::GetType() const
1538 {
1539   return nsGkAtoms::boxFrame;
1540 }
1541 
1542 #ifdef DEBUG_LAYOUT
1543 nsresult
GetXULDebug(bool & aDebug)1544 nsBoxFrame::GetXULDebug(bool& aDebug)
1545 {
1546   aDebug = (mState & NS_STATE_CURRENTLY_IN_DEBUG);
1547   return NS_OK;
1548 }
1549 #endif
1550 
1551 // REVIEW: nsBoxFrame::GetFrameForPoint is a problem because of 'mousethrough'
1552 // attribute support. Here's how it works:
1553 // * For each child frame F, we determine the target frame T(F) by recursively
1554 // invoking GetFrameForPoint on the child
1555 // * Let F' be the last child frame such that T(F') doesn't have mousethrough.
1556 // If F' exists, return T(F')
1557 // * Otherwise let F'' be the first child frame such that T(F'') is non-null.
1558 // If F'' exists, return T(F'')
1559 // * Otherwise return this frame, if this frame contains the point
1560 // * Otherwise return null
1561 // It's not clear how this should work for more complex z-ordering situations.
1562 // The basic principle seems to be that if a frame F has a descendant
1563 // 'mousethrough' frame that includes the target position, then F
1564 // will not receive events (unless it overrides GetFrameForPoint).
1565 // A 'mousethrough' frame will only receive an event if, after applying that rule,
1566 // all eligible frames are 'mousethrough'; the bottom-most inner-most 'mousethrough'
1567 // frame is then chosen (the first eligible frame reached in a
1568 // traversal of the frame tree --- pre/post is irrelevant since ancestors
1569 // of the mousethrough frames can't be eligible).
1570 // IMHO this is very bogus and adds a great deal of complexity for something
1571 // that is very rarely used. So I'm redefining 'mousethrough' to the following:
1572 // a frame with mousethrough is transparent to mouse events. This is compatible
1573 // with the way 'mousethrough' is used in Seamonkey's navigator.xul and
1574 // Firefox's browser.xul. The only other place it's used is in the 'expander'
1575 // XBL binding, which in our tree is only used by Thunderbird SMIME Advanced
1576 // Preferences, and I can't figure out what that does, so I'll have to test it.
1577 // If it's broken I'll probably just change the binding to use it more sensibly.
1578 // This new behaviour is implemented in nsDisplayList::HitTest.
1579 // REVIEW: This debug-box stuff is annoying. I'm just going to put debug boxes
1580 // in the outline layer and avoid GetDebugBoxAt.
1581 
1582 // REVIEW: GetCursor had debug-only event dumping code. I have replaced it
1583 // with instrumentation in nsDisplayXULDebug.
1584 
1585 #ifdef DEBUG_LAYOUT
1586 void
DrawLine(DrawTarget & aDrawTarget,bool aHorizontal,nscoord x1,nscoord y1,nscoord x2,nscoord y2)1587 nsBoxFrame::DrawLine(DrawTarget& aDrawTarget, bool aHorizontal, nscoord x1, nscoord y1, nscoord x2, nscoord y2)
1588 {
1589     nsPoint p1(x1, y1);
1590     nsPoint p2(x2, y2);
1591     if (!aHorizontal) {
1592       Swap(p1.x, p1.y);
1593       Swap(p2.x, p2.y);
1594     }
1595     ColorPattern white(ToDeviceColor(Color(1.f, 1.f, 1.f, 1.f)));
1596     StrokeLineWithSnapping(p1, p2, PresContext()->AppUnitsPerDevPixel(),
1597                            aDrawTarget, color);
1598 }
1599 
1600 void
FillRect(DrawTarget & aDrawTarget,bool aHorizontal,nscoord x,nscoord y,nscoord width,nscoord height)1601 nsBoxFrame::FillRect(DrawTarget& aDrawTarget, bool aHorizontal, nscoord x, nscoord y, nscoord width, nscoord height)
1602 {
1603     Rect rect = NSRectToSnappedRect(aHorizontal ? nsRect(x, y, width, height) :
1604                                                   nsRect(y, x, height, width),
1605                                     PresContext()->AppUnitsPerDevPixel(),
1606                                     aDrawTarget);
1607     ColorPattern white(ToDeviceColor(Color(1.f, 1.f, 1.f, 1.f)));
1608     aDrawTarget.FillRect(rect, white);
1609 }
1610 
1611 void
DrawSpacer(nsPresContext * aPresContext,DrawTarget & aDrawTarget,bool aHorizontal,int32_t flex,nscoord x,nscoord y,nscoord size,nscoord spacerSize)1612 nsBoxFrame::DrawSpacer(nsPresContext* aPresContext, DrawTarget& aDrawTarget,
1613                        bool aHorizontal, int32_t flex, nscoord x, nscoord y,
1614                        nscoord size, nscoord spacerSize)
1615 {
1616          nscoord onePixel = aPresContext->IntScaledPixelsToTwips(1);
1617 
1618      // if we do draw the coils
1619         int distance = 0;
1620         int center = 0;
1621         int offset = 0;
1622         int coilSize = COIL_SIZE*onePixel;
1623         int halfSpacer = spacerSize/2;
1624 
1625         distance = size;
1626         center = y + halfSpacer;
1627         offset = x;
1628 
1629         int coils = distance/coilSize;
1630 
1631         int halfCoilSize = coilSize/2;
1632 
1633         if (flex == 0) {
1634             DrawLine(aDrawTarget, aHorizontal, x,y + spacerSize/2, x + size, y + spacerSize/2);
1635         } else {
1636             for (int i=0; i < coils; i++)
1637             {
1638                    DrawLine(aDrawTarget, aHorizontal, offset, center+halfSpacer, offset+halfCoilSize, center-halfSpacer);
1639                    DrawLine(aDrawTarget, aHorizontal, offset+halfCoilSize, center-halfSpacer, offset+coilSize, center+halfSpacer);
1640 
1641                    offset += coilSize;
1642             }
1643         }
1644 
1645         FillRect(aDrawTarget, aHorizontal, x + size - spacerSize/2, y, spacerSize/2, spacerSize);
1646         FillRect(aDrawTarget, aHorizontal, x, y, spacerSize/2, spacerSize);
1647 }
1648 
1649 void
GetDebugBorder(nsMargin & aInset)1650 nsBoxFrame::GetDebugBorder(nsMargin& aInset)
1651 {
1652     aInset.SizeTo(2,2,2,2);
1653 
1654     if (IsXULHorizontal())
1655        aInset.top = 10;
1656     else
1657        aInset.left = 10;
1658 }
1659 
1660 void
GetDebugMargin(nsMargin & aInset)1661 nsBoxFrame::GetDebugMargin(nsMargin& aInset)
1662 {
1663     aInset.SizeTo(2,2,2,2);
1664 }
1665 
1666 void
GetDebugPadding(nsMargin & aPadding)1667 nsBoxFrame::GetDebugPadding(nsMargin& aPadding)
1668 {
1669     aPadding.SizeTo(2,2,2,2);
1670 }
1671 
1672 void
PixelMarginToTwips(nsMargin & aMarginPixels)1673 nsBoxFrame::PixelMarginToTwips(nsMargin& aMarginPixels)
1674 {
1675   nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
1676   aMarginPixels.left   *= onePixel;
1677   aMarginPixels.right  *= onePixel;
1678   aMarginPixels.top    *= onePixel;
1679   aMarginPixels.bottom *= onePixel;
1680 }
1681 
1682 void
GetValue(nsPresContext * aPresContext,const nsSize & a,const nsSize & b,char * ch)1683 nsBoxFrame::GetValue(nsPresContext* aPresContext, const nsSize& a, const nsSize& b, char* ch)
1684 {
1685     float p2t = aPresContext->ScaledPixelsToTwips();
1686 
1687     char width[100];
1688     char height[100];
1689 
1690     if (a.width == NS_INTRINSICSIZE)
1691         sprintf(width,"%s","INF");
1692     else
1693         sprintf(width,"%d", nscoord(a.width/*/p2t*/));
1694 
1695     if (a.height == NS_INTRINSICSIZE)
1696         sprintf(height,"%s","INF");
1697     else
1698         sprintf(height,"%d", nscoord(a.height/*/p2t*/));
1699 
1700 
1701     sprintf(ch, "(%s%s, %s%s)", width, (b.width != NS_INTRINSICSIZE ? "[SET]" : ""),
1702                     height, (b.height != NS_INTRINSICSIZE ? "[SET]" : ""));
1703 
1704 }
1705 
1706 void
GetValue(nsPresContext * aPresContext,int32_t a,int32_t b,char * ch)1707 nsBoxFrame::GetValue(nsPresContext* aPresContext, int32_t a, int32_t b, char* ch)
1708 {
1709     if (a == NS_INTRINSICSIZE)
1710       sprintf(ch, "%d[SET]", b);
1711     else
1712       sprintf(ch, "%d", a);
1713 }
1714 
1715 nsresult
DisplayDebugInfoFor(nsIFrame * aBox,nsPoint & aPoint)1716 nsBoxFrame::DisplayDebugInfoFor(nsIFrame*  aBox,
1717                                 nsPoint& aPoint)
1718 {
1719     nsBoxLayoutState state(GetPresContext());
1720 
1721     nscoord x = aPoint.x;
1722     nscoord y = aPoint.y;
1723 
1724     // get the area inside our border but not our debug margins.
1725     nsRect insideBorder(aBox->mRect);
1726     insideBorder.MoveTo(0,0):
1727     nsMargin border(0,0,0,0);
1728     aBox->GetXULBorderAndPadding(border);
1729     insideBorder.Deflate(border);
1730 
1731     bool isHorizontal = IsXULHorizontal();
1732 
1733     if (!insideBorder.Contains(nsPoint(x,y)))
1734         return NS_ERROR_FAILURE;
1735 
1736     //printf("%%%%%% inside box %%%%%%%\n");
1737 
1738     int count = 0;
1739     nsIFrame* child = nsBox::GetChildXULBox(aBox);
1740 
1741     nsMargin m;
1742     nsMargin m2;
1743     GetDebugBorder(m);
1744     PixelMarginToTwips(m);
1745 
1746     GetDebugMargin(m2);
1747     PixelMarginToTwips(m2);
1748 
1749     m += m2;
1750 
1751     if ((isHorizontal && y < insideBorder.y + m.top) ||
1752         (!isHorizontal && x < insideBorder.x + m.left)) {
1753         //printf("**** inside debug border *******\n");
1754         while (child)
1755         {
1756             const nsRect& r = child->mRect;
1757 
1758             // if we are not in the child. But in the spacer above the child.
1759             if ((isHorizontal && x >= r.x && x < r.x + r.width) ||
1760                 (!isHorizontal && y >= r.y && y < r.y + r.height)) {
1761                 aCursor = NS_STYLE_CURSOR_POINTER;
1762                    // found it but we already showed it.
1763                     if (mDebugChild == child)
1764                         return NS_OK;
1765 
1766                     if (aBox->GetContent()) {
1767                       printf("---------------\n");
1768                       XULDumpBox(stdout);
1769                       printf("\n");
1770                     }
1771 
1772                     if (child->GetContent()) {
1773                         printf("child #%d: ", count);
1774                         child->XULDumpBox(stdout);
1775                         printf("\n");
1776                     }
1777 
1778                     mDebugChild = child;
1779 
1780                     nsSize prefSizeCSS(NS_INTRINSICSIZE, NS_INTRINSICSIZE);
1781                     nsSize minSizeCSS (NS_INTRINSICSIZE, NS_INTRINSICSIZE);
1782                     nsSize maxSizeCSS (NS_INTRINSICSIZE, NS_INTRINSICSIZE);
1783                     nscoord flexCSS = NS_INTRINSICSIZE;
1784 
1785                     bool widthSet, heightSet;
1786                     nsIFrame::AddXULPrefSize(child, prefSizeCSS, widthSet, heightSet);
1787                     nsIFrame::AddXULMinSize (state, child, minSizeCSS, widthSet, heightSet);
1788                     nsIFrame::AddXULMaxSize (child, maxSizeCSS, widthSet, heightSet);
1789                     nsIFrame::AddXULFlex    (child, flexCSS);
1790 
1791                     nsSize prefSize = child->GetXULPrefSize(state);
1792                     nsSize minSize = child->GetXULMinSize(state);
1793                     nsSize maxSize = child->GetXULMaxSize(state);
1794                     nscoord flexSize = child->GetXULFlex();
1795                     nscoord ascentSize = child->GetXULBoxAscent(state);
1796 
1797                     char min[100];
1798                     char pref[100];
1799                     char max[100];
1800                     char calc[100];
1801                     char flex[100];
1802                     char ascent[100];
1803 
1804                     nsSize actualSize;
1805                     GetFrameSizeWithMargin(child, actualSize);
1806                     nsSize actualSizeCSS (NS_INTRINSICSIZE, NS_INTRINSICSIZE);
1807 
1808                     GetValue(aPresContext, minSize,  minSizeCSS, min);
1809                     GetValue(aPresContext, prefSize, prefSizeCSS, pref);
1810                     GetValue(aPresContext, maxSize,  maxSizeCSS, max);
1811                     GetValue(aPresContext, actualSize, actualSizeCSS, calc);
1812                     GetValue(aPresContext, flexSize,  flexCSS, flex);
1813                     GetValue(aPresContext, ascentSize,  NS_INTRINSICSIZE, ascent);
1814 
1815 
1816                     printf("min%s, pref%s, max%s, actual%s, flex=%s, ascent=%s\n\n",
1817                         min,
1818                         pref,
1819                         max,
1820                         calc,
1821                         flex,
1822                         ascent
1823                     );
1824 
1825                     return NS_OK;
1826             }
1827 
1828           child = GetNextXULBox(child);
1829           count++;
1830         }
1831     } else {
1832     }
1833 
1834     mDebugChild = nullptr;
1835 
1836     return NS_OK;
1837 }
1838 
1839 void
SetDebugOnChildList(nsBoxLayoutState & aState,nsIFrame * aChild,bool aDebug)1840 nsBoxFrame::SetDebugOnChildList(nsBoxLayoutState& aState, nsIFrame* aChild, bool aDebug)
1841 {
1842     nsIFrame* child = nsBox::GetChildXULBox(this);
1843      while (child)
1844      {
1845         child->SetXULDebug(aState, aDebug);
1846         child = GetNextXULBox(child);
1847      }
1848 }
1849 
1850 nsresult
GetFrameSizeWithMargin(nsIFrame * aBox,nsSize & aSize)1851 nsBoxFrame::GetFrameSizeWithMargin(nsIFrame* aBox, nsSize& aSize)
1852 {
1853   nsRect rect(aBox->GetRect());
1854   nsMargin margin(0,0,0,0);
1855   aBox->GetXULMargin(margin);
1856   rect.Inflate(margin);
1857   aSize.width = rect.width;
1858   aSize.height = rect.height;
1859   return NS_OK;
1860 }
1861 #endif
1862 
1863 // If you make changes to this function, check its counterparts
1864 // in nsTextBoxFrame and nsXULLabelFrame
1865 void
RegUnregAccessKey(bool aDoReg)1866 nsBoxFrame::RegUnregAccessKey(bool aDoReg)
1867 {
1868   MOZ_ASSERT(mContent);
1869 
1870   // only support accesskeys for the following elements
1871   if (!mContent->IsAnyOfXULElements(nsGkAtoms::button,
1872                                     nsGkAtoms::toolbarbutton,
1873                                     nsGkAtoms::checkbox,
1874                                     nsGkAtoms::textbox,
1875                                     nsGkAtoms::tab,
1876                                     nsGkAtoms::radio)) {
1877     return;
1878   }
1879 
1880   nsAutoString accessKey;
1881   mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, accessKey);
1882 
1883   if (accessKey.IsEmpty())
1884     return;
1885 
1886   // With a valid PresContext we can get the ESM
1887   // and register the access key
1888   EventStateManager* esm = PresContext()->EventStateManager();
1889 
1890   uint32_t key = accessKey.First();
1891   if (aDoReg)
1892     esm->RegisterAccessKey(mContent, key);
1893   else
1894     esm->UnregisterAccessKey(mContent, key);
1895 }
1896 
1897 bool
SupportsOrdinalsInChildren()1898 nsBoxFrame::SupportsOrdinalsInChildren()
1899 {
1900   return true;
1901 }
1902 
1903 // Helper less-than-or-equal function, used in CheckBoxOrder() as a
1904 // template-parameter for the sorting functions.
1905 bool
IsBoxOrdinalLEQ(nsIFrame * aFrame1,nsIFrame * aFrame2)1906 IsBoxOrdinalLEQ(nsIFrame* aFrame1,
1907                 nsIFrame* aFrame2)
1908 {
1909   // If we've got a placeholder frame, use its out-of-flow frame's ordinal val.
1910   nsIFrame* aRealFrame1 = nsPlaceholderFrame::GetRealFrameFor(aFrame1);
1911   nsIFrame* aRealFrame2 = nsPlaceholderFrame::GetRealFrameFor(aFrame2);
1912   return aRealFrame1->GetXULOrdinal() <= aRealFrame2->GetXULOrdinal();
1913 }
1914 
1915 void
CheckBoxOrder()1916 nsBoxFrame::CheckBoxOrder()
1917 {
1918   if (SupportsOrdinalsInChildren() &&
1919       !nsIFrame::IsFrameListSorted<IsBoxOrdinalLEQ>(mFrames)) {
1920     nsIFrame::SortFrameList<IsBoxOrdinalLEQ>(mFrames);
1921   }
1922 }
1923 
1924 nsresult
LayoutChildAt(nsBoxLayoutState & aState,nsIFrame * aBox,const nsRect & aRect)1925 nsBoxFrame::LayoutChildAt(nsBoxLayoutState& aState, nsIFrame* aBox, const nsRect& aRect)
1926 {
1927   // get the current rect
1928   nsRect oldRect(aBox->GetRect());
1929   aBox->SetXULBounds(aState, aRect);
1930 
1931   bool layout = NS_SUBTREE_DIRTY(aBox);
1932 
1933   if (layout || (oldRect.width != aRect.width || oldRect.height != aRect.height))  {
1934     return aBox->XULLayout(aState);
1935   }
1936 
1937   return NS_OK;
1938 }
1939 
1940 nsresult
XULRelayoutChildAtOrdinal(nsIFrame * aChild)1941 nsBoxFrame::XULRelayoutChildAtOrdinal(nsIFrame* aChild)
1942 {
1943   if (!SupportsOrdinalsInChildren())
1944     return NS_OK;
1945 
1946   uint32_t ord = aChild->GetXULOrdinal();
1947 
1948   nsIFrame* child = mFrames.FirstChild();
1949   nsIFrame* newPrevSib = nullptr;
1950 
1951   while (child) {
1952     if (ord < child->GetXULOrdinal()) {
1953       break;
1954     }
1955 
1956     if (child != aChild) {
1957       newPrevSib = child;
1958     }
1959 
1960     child = GetNextXULBox(child);
1961   }
1962 
1963   if (aChild->GetPrevSibling() == newPrevSib) {
1964     // This box is not moving.
1965     return NS_OK;
1966   }
1967 
1968   // Take |aChild| out of its old position in the child list.
1969   mFrames.RemoveFrame(aChild);
1970 
1971   // Insert it after |newPrevSib| or at the start if it's null.
1972   mFrames.InsertFrame(nullptr, newPrevSib, aChild);
1973 
1974   return NS_OK;
1975 }
1976 
1977 /**
1978  * This wrapper class lets us redirect mouse hits from descendant frames
1979  * of a menu to the menu itself, if they didn't specify 'allowevents'.
1980  *
1981  * The wrapper simply turns a hit on a descendant element
1982  * into a hit on the menu itself, unless there is an element between the target
1983  * and the menu with the "allowevents" attribute.
1984  *
1985  * This is used by nsMenuFrame and nsTreeColFrame.
1986  *
1987  * Note that turning a hit on a descendant element into nullptr, so events
1988  * could fall through to the menu background, might be an appealing simplification
1989  * but it would mean slightly strange behaviour in some cases, because grabber
1990  * wrappers can be created for many individual lists and items, so the exact
1991  * fallthrough behaviour would be complex. E.g. an element with "allowevents"
1992  * on top of the Content() list could receive the event even if it was covered
1993  * by a PositionedDescenants() element without "allowevents". It is best to
1994  * never convert a non-null hit into null.
1995  */
1996 // REVIEW: This is roughly of what nsMenuFrame::GetFrameForPoint used to do.
1997 // I've made 'allowevents' affect child elements because that seems the only
1998 // reasonable thing to do.
1999 class nsDisplayXULEventRedirector : public nsDisplayWrapList {
2000 public:
nsDisplayXULEventRedirector(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayItem * aItem,nsIFrame * aTargetFrame)2001   nsDisplayXULEventRedirector(nsDisplayListBuilder* aBuilder,
2002                               nsIFrame* aFrame, nsDisplayItem* aItem,
2003                               nsIFrame* aTargetFrame)
2004     : nsDisplayWrapList(aBuilder, aFrame, aItem), mTargetFrame(aTargetFrame) {}
nsDisplayXULEventRedirector(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList,nsIFrame * aTargetFrame)2005   nsDisplayXULEventRedirector(nsDisplayListBuilder* aBuilder,
2006                               nsIFrame* aFrame, nsDisplayList* aList,
2007                               nsIFrame* aTargetFrame)
2008     : nsDisplayWrapList(aBuilder, aFrame, aList), mTargetFrame(aTargetFrame) {}
2009   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
2010                        HitTestState* aState,
2011                        nsTArray<nsIFrame*> *aOutFrames) override;
ShouldFlattenAway(nsDisplayListBuilder * aBuilder)2012   virtual bool ShouldFlattenAway(nsDisplayListBuilder* aBuilder) override {
2013     return false;
2014   }
2015   NS_DISPLAY_DECL_NAME("XULEventRedirector", TYPE_XUL_EVENT_REDIRECTOR)
2016 private:
2017   nsIFrame* mTargetFrame;
2018 };
2019 
HitTest(nsDisplayListBuilder * aBuilder,const nsRect & aRect,HitTestState * aState,nsTArray<nsIFrame * > * aOutFrames)2020 void nsDisplayXULEventRedirector::HitTest(nsDisplayListBuilder* aBuilder,
2021     const nsRect& aRect, HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
2022 {
2023   nsTArray<nsIFrame*> outFrames;
2024   mList.HitTest(aBuilder, aRect, aState, &outFrames);
2025 
2026   bool topMostAdded = false;
2027   uint32_t localLength = outFrames.Length();
2028 
2029   for (uint32_t i = 0; i < localLength; i++) {
2030 
2031     for (nsIContent* content = outFrames.ElementAt(i)->GetContent();
2032          content && content != mTargetFrame->GetContent();
2033          content = content->GetParent()) {
2034       if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::allowevents,
2035                                nsGkAtoms::_true, eCaseMatters)) {
2036         // Events are allowed on 'frame', so let it go.
2037         aOutFrames->AppendElement(outFrames.ElementAt(i));
2038         topMostAdded = true;
2039       }
2040     }
2041 
2042     // If there was no hit on the topmost frame or its ancestors,
2043     // add the target frame itself as the first candidate (see bug 562554).
2044     if (!topMostAdded) {
2045       topMostAdded = true;
2046       aOutFrames->AppendElement(mTargetFrame);
2047     }
2048   }
2049 }
2050 
2051 class nsXULEventRedirectorWrapper : public nsDisplayWrapper
2052 {
2053 public:
nsXULEventRedirectorWrapper(nsIFrame * aTargetFrame)2054   explicit nsXULEventRedirectorWrapper(nsIFrame* aTargetFrame)
2055       : mTargetFrame(aTargetFrame) {}
WrapList(nsDisplayListBuilder * aBuilder,nsIFrame * aFrame,nsDisplayList * aList)2056   virtual nsDisplayItem* WrapList(nsDisplayListBuilder* aBuilder,
2057                                   nsIFrame* aFrame,
2058                                   nsDisplayList* aList) override {
2059     return new (aBuilder)
2060         nsDisplayXULEventRedirector(aBuilder, aFrame, aList, mTargetFrame);
2061   }
WrapItem(nsDisplayListBuilder * aBuilder,nsDisplayItem * aItem)2062   virtual nsDisplayItem* WrapItem(nsDisplayListBuilder* aBuilder,
2063                                   nsDisplayItem* aItem) override {
2064     return new (aBuilder)
2065         nsDisplayXULEventRedirector(aBuilder, aItem->Frame(), aItem,
2066                                     mTargetFrame);
2067   }
2068 private:
2069   nsIFrame* mTargetFrame;
2070 };
2071 
2072 void
WrapListsInRedirector(nsDisplayListBuilder * aBuilder,const nsDisplayListSet & aIn,const nsDisplayListSet & aOut)2073 nsBoxFrame::WrapListsInRedirector(nsDisplayListBuilder*   aBuilder,
2074                                   const nsDisplayListSet& aIn,
2075                                   const nsDisplayListSet& aOut)
2076 {
2077   nsXULEventRedirectorWrapper wrapper(this);
2078   wrapper.WrapLists(aBuilder, this, aIn, aOut);
2079 }
2080 
2081 bool
GetEventPoint(WidgetGUIEvent * aEvent,nsPoint & aPoint)2082 nsBoxFrame::GetEventPoint(WidgetGUIEvent* aEvent, nsPoint &aPoint) {
2083   LayoutDeviceIntPoint refPoint;
2084   bool res = GetEventPoint(aEvent, refPoint);
2085   aPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(
2086     aEvent, refPoint, this);
2087   return res;
2088 }
2089 
2090 bool
GetEventPoint(WidgetGUIEvent * aEvent,LayoutDeviceIntPoint & aPoint)2091 nsBoxFrame::GetEventPoint(WidgetGUIEvent* aEvent, LayoutDeviceIntPoint& aPoint) {
2092   NS_ENSURE_TRUE(aEvent, false);
2093 
2094   WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
2095   if (touchEvent) {
2096     // return false if there is more than one touch on the page, or if
2097     // we can't find a touch point
2098     if (touchEvent->mTouches.Length() != 1) {
2099       return false;
2100     }
2101 
2102     dom::Touch* touch = touchEvent->mTouches.SafeElementAt(0);
2103     if (!touch) {
2104       return false;
2105     }
2106     aPoint = touch->mRefPoint;
2107   } else {
2108     aPoint = aEvent->mRefPoint;
2109   }
2110   return true;
2111 }
2112