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