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