michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 sw=2 et tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: // michael@0: // Eric Vaughan michael@0: // Netscape Communications michael@0: // michael@0: // See documentation in associated header file michael@0: // michael@0: michael@0: // How boxes layout michael@0: // ---------------- michael@0: // Boxes layout a bit differently than html. html does a bottom up layout. Where boxes do a top down. michael@0: // 1) First thing a box does it goes out and askes each child for its min, max, and preferred sizes. michael@0: // 2) It then adds them up to determine its size. michael@0: // 3) If the box was asked to layout it self intrinically it will layout its children at their preferred size michael@0: // otherwise it will layout the child at the size it was told to. It will squeeze or stretch its children if michael@0: // Necessary. michael@0: // michael@0: // However there is a catch. Some html components like block frames can not determine their preferred size. michael@0: // this is their size if they were laid out intrinsically. So the box will flow the child to determine this can michael@0: // cache the value. michael@0: michael@0: // Boxes and Incremental Reflow michael@0: // ---------------------------- michael@0: // Boxes layout out top down by adding up their children's min, max, and preferred sizes. Only problem is if a incremental michael@0: // reflow occurs. The preferred size of a child deep in the hierarchy could change. And this could change michael@0: // any number of syblings around the box. Basically any children in the reflow chain must have their caches cleared michael@0: // so when asked for there current size they can relayout themselves. michael@0: michael@0: #include "nsBoxLayoutState.h" michael@0: #include "nsBoxFrame.h" michael@0: #include "mozilla/dom/Touch.h" michael@0: #include "nsStyleContext.h" michael@0: #include "nsPlaceholderFrame.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsNameSpaceManager.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsIContent.h" michael@0: #include "nsHTMLParts.h" michael@0: #include "nsViewManager.h" michael@0: #include "nsView.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsCSSRendering.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsBoxLayout.h" michael@0: #include "nsSprocketLayout.h" michael@0: #include "nsIScrollableFrame.h" michael@0: #include "nsWidgetsCID.h" michael@0: #include "nsCSSAnonBoxes.h" michael@0: #include "nsContainerFrame.h" michael@0: #include "nsIDOMElement.h" michael@0: #include "nsITheme.h" michael@0: #include "nsTransform2D.h" michael@0: #include "mozilla/EventStateManager.h" michael@0: #include "nsIDOMEvent.h" michael@0: #include "nsDisplayList.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "nsThemeConstants.h" michael@0: #include "nsLayoutUtils.h" michael@0: #include michael@0: michael@0: // Needed for Print Preview michael@0: #include "nsIURI.h" michael@0: michael@0: #include "mozilla/TouchEvents.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: //define DEBUG_REDRAW michael@0: michael@0: #define DEBUG_SPRING_SIZE 8 michael@0: #define DEBUG_BORDER_SIZE 2 michael@0: #define COIL_SIZE 8 michael@0: michael@0: //#define TEST_SANITY michael@0: michael@0: #ifdef DEBUG_rods michael@0: //#define DO_NOISY_REFLOW michael@0: #endif michael@0: michael@0: #ifdef DEBUG_LAYOUT michael@0: bool nsBoxFrame::gDebug = false; michael@0: nsIFrame* nsBoxFrame::mDebugChild = nullptr; michael@0: #endif michael@0: michael@0: nsIFrame* michael@0: NS_NewBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot, nsBoxLayout* aLayoutManager) michael@0: { michael@0: return new (aPresShell) nsBoxFrame(aPresShell, aContext, aIsRoot, aLayoutManager); michael@0: } michael@0: michael@0: nsIFrame* michael@0: NS_NewBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) michael@0: { michael@0: return new (aPresShell) nsBoxFrame(aPresShell, aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsBoxFrame) michael@0: michael@0: #ifdef DEBUG michael@0: NS_QUERYFRAME_HEAD(nsBoxFrame) michael@0: NS_QUERYFRAME_ENTRY(nsBoxFrame) michael@0: NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) michael@0: #endif michael@0: michael@0: nsBoxFrame::nsBoxFrame(nsIPresShell* aPresShell, michael@0: nsStyleContext* aContext, michael@0: bool aIsRoot, michael@0: nsBoxLayout* aLayoutManager) : michael@0: nsContainerFrame(aContext) michael@0: { michael@0: mState |= NS_STATE_IS_HORIZONTAL; michael@0: mState |= NS_STATE_AUTO_STRETCH; michael@0: michael@0: if (aIsRoot) michael@0: mState |= NS_STATE_IS_ROOT; michael@0: michael@0: mValign = vAlign_Top; michael@0: mHalign = hAlign_Left; michael@0: michael@0: // if no layout manager specified us the static sprocket layout michael@0: nsCOMPtr layout = aLayoutManager; michael@0: michael@0: if (layout == nullptr) { michael@0: NS_NewSprocketLayout(aPresShell, layout); michael@0: } michael@0: michael@0: SetLayoutManager(layout); michael@0: } michael@0: michael@0: nsBoxFrame::~nsBoxFrame() michael@0: { michael@0: } michael@0: michael@0: nsresult michael@0: nsBoxFrame::SetInitialChildList(ChildListID aListID, michael@0: nsFrameList& aChildList) michael@0: { michael@0: nsresult r = nsContainerFrame::SetInitialChildList(aListID, aChildList); michael@0: if (r == NS_OK) { michael@0: // initialize our list of infos. michael@0: nsBoxLayoutState state(PresContext()); michael@0: CheckBoxOrder(); michael@0: if (mLayoutManager) michael@0: mLayoutManager->ChildrenSet(this, state, mFrames.FirstChild()); michael@0: } else { michael@0: NS_WARNING("Warning add child failed!!\n"); michael@0: } michael@0: michael@0: return r; michael@0: } michael@0: michael@0: /* virtual */ void michael@0: nsBoxFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext) michael@0: { michael@0: nsContainerFrame::DidSetStyleContext(aOldStyleContext); michael@0: michael@0: // The values that CacheAttributes() computes depend on our style, michael@0: // so we need to recompute them here... michael@0: CacheAttributes(); michael@0: } michael@0: michael@0: /** michael@0: * Initialize us. This is a good time to get the alignment of the box michael@0: */ michael@0: void michael@0: nsBoxFrame::Init(nsIContent* aContent, michael@0: nsIFrame* aParent, michael@0: nsIFrame* aPrevInFlow) michael@0: { michael@0: nsContainerFrame::Init(aContent, aParent, aPrevInFlow); michael@0: michael@0: if (GetStateBits() & NS_FRAME_FONT_INFLATION_CONTAINER) { michael@0: AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT); michael@0: } michael@0: michael@0: MarkIntrinsicWidthsDirty(); michael@0: michael@0: CacheAttributes(); michael@0: michael@0: #ifdef DEBUG_LAYOUT michael@0: // if we are root and this michael@0: if (mState & NS_STATE_IS_ROOT) michael@0: GetDebugPref(GetPresContext()); michael@0: #endif michael@0: michael@0: UpdateMouseThrough(); michael@0: michael@0: // register access key michael@0: RegUnregAccessKey(true); michael@0: } michael@0: michael@0: void nsBoxFrame::UpdateMouseThrough() michael@0: { michael@0: if (mContent) { michael@0: static nsIContent::AttrValuesArray strings[] = michael@0: {&nsGkAtoms::never, &nsGkAtoms::always, nullptr}; michael@0: switch (mContent->FindAttrValueIn(kNameSpaceID_None, michael@0: nsGkAtoms::mousethrough, strings, eCaseMatters)) { michael@0: case 0: AddStateBits(NS_FRAME_MOUSE_THROUGH_NEVER); break; michael@0: case 1: AddStateBits(NS_FRAME_MOUSE_THROUGH_ALWAYS); break; michael@0: case 2: { michael@0: RemoveStateBits(NS_FRAME_MOUSE_THROUGH_ALWAYS); michael@0: RemoveStateBits(NS_FRAME_MOUSE_THROUGH_NEVER); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsBoxFrame::CacheAttributes() michael@0: { michael@0: /* michael@0: printf("Caching: "); michael@0: DumpBox(stdout); michael@0: printf("\n"); michael@0: */ michael@0: michael@0: mValign = vAlign_Top; michael@0: mHalign = hAlign_Left; michael@0: michael@0: bool orient = false; michael@0: GetInitialOrientation(orient); michael@0: if (orient) michael@0: mState |= NS_STATE_IS_HORIZONTAL; michael@0: else michael@0: mState &= ~NS_STATE_IS_HORIZONTAL; michael@0: michael@0: bool normal = true; michael@0: GetInitialDirection(normal); michael@0: if (normal) michael@0: mState |= NS_STATE_IS_DIRECTION_NORMAL; michael@0: else michael@0: mState &= ~NS_STATE_IS_DIRECTION_NORMAL; michael@0: michael@0: GetInitialVAlignment(mValign); michael@0: GetInitialHAlignment(mHalign); michael@0: michael@0: bool equalSize = false; michael@0: GetInitialEqualSize(equalSize); michael@0: if (equalSize) michael@0: mState |= NS_STATE_EQUAL_SIZE; michael@0: else michael@0: mState &= ~NS_STATE_EQUAL_SIZE; michael@0: michael@0: bool autostretch = !!(mState & NS_STATE_AUTO_STRETCH); michael@0: GetInitialAutoStretch(autostretch); michael@0: if (autostretch) michael@0: mState |= NS_STATE_AUTO_STRETCH; michael@0: else michael@0: mState &= ~NS_STATE_AUTO_STRETCH; michael@0: michael@0: michael@0: #ifdef DEBUG_LAYOUT michael@0: bool debug = mState & NS_STATE_SET_TO_DEBUG; michael@0: bool debugSet = GetInitialDebug(debug); michael@0: if (debugSet) { michael@0: mState |= NS_STATE_DEBUG_WAS_SET; michael@0: if (debug) michael@0: mState |= NS_STATE_SET_TO_DEBUG; michael@0: else michael@0: mState &= ~NS_STATE_SET_TO_DEBUG; michael@0: } else { michael@0: mState &= ~NS_STATE_DEBUG_WAS_SET; michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: #ifdef DEBUG_LAYOUT michael@0: bool michael@0: nsBoxFrame::GetInitialDebug(bool& aDebug) michael@0: { michael@0: if (!GetContent()) michael@0: return false; michael@0: michael@0: static nsIContent::AttrValuesArray strings[] = michael@0: {&nsGkAtoms::_false, &nsGkAtoms::_true, nullptr}; michael@0: int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, michael@0: nsGkAtoms::debug, strings, eCaseMatters); michael@0: if (index >= 0) { michael@0: aDebug = index == 1; michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: #endif michael@0: michael@0: bool michael@0: nsBoxFrame::GetInitialHAlignment(nsBoxFrame::Halignment& aHalign) michael@0: { michael@0: if (!GetContent()) michael@0: return false; michael@0: michael@0: // XXXdwh Everything inside this if statement is deprecated code. michael@0: static nsIContent::AttrValuesArray alignStrings[] = michael@0: {&nsGkAtoms::left, &nsGkAtoms::right, nullptr}; michael@0: static const Halignment alignValues[] = {hAlign_Left, hAlign_Right}; michael@0: int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::align, michael@0: alignStrings, eCaseMatters); michael@0: if (index >= 0) { michael@0: aHalign = alignValues[index]; michael@0: return true; michael@0: } michael@0: michael@0: // Now that the deprecated stuff is out of the way, we move on to check the appropriate michael@0: // attribute. For horizontal boxes, we are checking the PACK attribute. For vertical boxes michael@0: // we are checking the ALIGN attribute. michael@0: nsIAtom* attrName = IsHorizontal() ? nsGkAtoms::pack : nsGkAtoms::align; michael@0: static nsIContent::AttrValuesArray strings[] = michael@0: {&nsGkAtoms::_empty, &nsGkAtoms::start, &nsGkAtoms::center, &nsGkAtoms::end, nullptr}; michael@0: static const Halignment values[] = michael@0: {hAlign_Left/*not used*/, hAlign_Left, hAlign_Center, hAlign_Right}; michael@0: index = GetContent()->FindAttrValueIn(kNameSpaceID_None, attrName, michael@0: strings, eCaseMatters); michael@0: michael@0: if (index == nsIContent::ATTR_VALUE_NO_MATCH) { michael@0: // The attr was present but had a nonsensical value. Revert to the default. michael@0: return false; michael@0: } michael@0: if (index > 0) { michael@0: aHalign = values[index]; michael@0: return true; michael@0: } michael@0: michael@0: // Now that we've checked for the attribute it's time to check CSS. For michael@0: // horizontal boxes we're checking PACK. For vertical boxes we are checking michael@0: // ALIGN. michael@0: const nsStyleXUL* boxInfo = StyleXUL(); michael@0: if (IsHorizontal()) { michael@0: switch (boxInfo->mBoxPack) { michael@0: case NS_STYLE_BOX_PACK_START: michael@0: aHalign = nsBoxFrame::hAlign_Left; michael@0: return true; michael@0: case NS_STYLE_BOX_PACK_CENTER: michael@0: aHalign = nsBoxFrame::hAlign_Center; michael@0: return true; michael@0: case NS_STYLE_BOX_PACK_END: michael@0: aHalign = nsBoxFrame::hAlign_Right; michael@0: return true; michael@0: default: // Nonsensical value. Just bail. michael@0: return false; michael@0: } michael@0: } michael@0: else { michael@0: switch (boxInfo->mBoxAlign) { michael@0: case NS_STYLE_BOX_ALIGN_START: michael@0: aHalign = nsBoxFrame::hAlign_Left; michael@0: return true; michael@0: case NS_STYLE_BOX_ALIGN_CENTER: michael@0: aHalign = nsBoxFrame::hAlign_Center; michael@0: return true; michael@0: case NS_STYLE_BOX_ALIGN_END: michael@0: aHalign = nsBoxFrame::hAlign_Right; michael@0: return true; michael@0: default: // Nonsensical value. Just bail. michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: nsBoxFrame::GetInitialVAlignment(nsBoxFrame::Valignment& aValign) michael@0: { michael@0: if (!GetContent()) michael@0: return false; michael@0: michael@0: static nsIContent::AttrValuesArray valignStrings[] = michael@0: {&nsGkAtoms::top, &nsGkAtoms::baseline, &nsGkAtoms::middle, &nsGkAtoms::bottom, nullptr}; michael@0: static const Valignment valignValues[] = michael@0: {vAlign_Top, vAlign_BaseLine, vAlign_Middle, vAlign_Bottom}; michael@0: int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::valign, michael@0: valignStrings, eCaseMatters); michael@0: if (index >= 0) { michael@0: aValign = valignValues[index]; michael@0: return true; michael@0: } michael@0: michael@0: // Now that the deprecated stuff is out of the way, we move on to check the appropriate michael@0: // attribute. For horizontal boxes, we are checking the ALIGN attribute. For vertical boxes michael@0: // we are checking the PACK attribute. michael@0: nsIAtom* attrName = IsHorizontal() ? nsGkAtoms::align : nsGkAtoms::pack; michael@0: static nsIContent::AttrValuesArray strings[] = michael@0: {&nsGkAtoms::_empty, &nsGkAtoms::start, &nsGkAtoms::center, michael@0: &nsGkAtoms::baseline, &nsGkAtoms::end, nullptr}; michael@0: static const Valignment values[] = michael@0: {vAlign_Top/*not used*/, vAlign_Top, vAlign_Middle, vAlign_BaseLine, vAlign_Bottom}; michael@0: index = GetContent()->FindAttrValueIn(kNameSpaceID_None, attrName, michael@0: strings, eCaseMatters); michael@0: if (index == nsIContent::ATTR_VALUE_NO_MATCH) { michael@0: // The attr was present but had a nonsensical value. Revert to the default. michael@0: return false; michael@0: } michael@0: if (index > 0) { michael@0: aValign = values[index]; michael@0: return true; michael@0: } michael@0: michael@0: // Now that we've checked for the attribute it's time to check CSS. For michael@0: // horizontal boxes we're checking ALIGN. For vertical boxes we are checking michael@0: // PACK. michael@0: const nsStyleXUL* boxInfo = StyleXUL(); michael@0: if (IsHorizontal()) { michael@0: switch (boxInfo->mBoxAlign) { michael@0: case NS_STYLE_BOX_ALIGN_START: michael@0: aValign = nsBoxFrame::vAlign_Top; michael@0: return true; michael@0: case NS_STYLE_BOX_ALIGN_CENTER: michael@0: aValign = nsBoxFrame::vAlign_Middle; michael@0: return true; michael@0: case NS_STYLE_BOX_ALIGN_BASELINE: michael@0: aValign = nsBoxFrame::vAlign_BaseLine; michael@0: return true; michael@0: case NS_STYLE_BOX_ALIGN_END: michael@0: aValign = nsBoxFrame::vAlign_Bottom; michael@0: return true; michael@0: default: // Nonsensical value. Just bail. michael@0: return false; michael@0: } michael@0: } michael@0: else { michael@0: switch (boxInfo->mBoxPack) { michael@0: case NS_STYLE_BOX_PACK_START: michael@0: aValign = nsBoxFrame::vAlign_Top; michael@0: return true; michael@0: case NS_STYLE_BOX_PACK_CENTER: michael@0: aValign = nsBoxFrame::vAlign_Middle; michael@0: return true; michael@0: case NS_STYLE_BOX_PACK_END: michael@0: aValign = nsBoxFrame::vAlign_Bottom; michael@0: return true; michael@0: default: // Nonsensical value. Just bail. michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: nsBoxFrame::GetInitialOrientation(bool& aIsHorizontal) michael@0: { michael@0: // see if we are a vertical or horizontal box. michael@0: if (!GetContent()) michael@0: return; michael@0: michael@0: // Check the style system first. michael@0: const nsStyleXUL* boxInfo = StyleXUL(); michael@0: if (boxInfo->mBoxOrient == NS_STYLE_BOX_ORIENT_HORIZONTAL) michael@0: aIsHorizontal = true; michael@0: else michael@0: aIsHorizontal = false; michael@0: michael@0: // Now see if we have an attribute. The attribute overrides michael@0: // the style system value. michael@0: static nsIContent::AttrValuesArray strings[] = michael@0: {&nsGkAtoms::vertical, &nsGkAtoms::horizontal, nullptr}; michael@0: int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::orient, michael@0: strings, eCaseMatters); michael@0: if (index >= 0) { michael@0: aIsHorizontal = index == 1; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsBoxFrame::GetInitialDirection(bool& aIsNormal) michael@0: { michael@0: if (!GetContent()) michael@0: return; michael@0: michael@0: if (IsHorizontal()) { michael@0: // For horizontal boxes only, we initialize our value based off the CSS 'direction' property. michael@0: // This means that BiDI users will end up with horizontally inverted chrome. michael@0: aIsNormal = (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR); // If text runs RTL then so do we. michael@0: } michael@0: else michael@0: aIsNormal = true; // Assume a normal direction in the vertical case. michael@0: michael@0: // Now check the style system to see if we should invert aIsNormal. michael@0: const nsStyleXUL* boxInfo = StyleXUL(); michael@0: if (boxInfo->mBoxDirection == NS_STYLE_BOX_DIRECTION_REVERSE) michael@0: aIsNormal = !aIsNormal; // Invert our direction. michael@0: michael@0: // Now see if we have an attribute. The attribute overrides michael@0: // the style system value. michael@0: if (IsHorizontal()) { michael@0: static nsIContent::AttrValuesArray strings[] = michael@0: {&nsGkAtoms::reverse, &nsGkAtoms::ltr, &nsGkAtoms::rtl, nullptr}; michael@0: int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::dir, michael@0: strings, eCaseMatters); michael@0: if (index >= 0) { michael@0: bool values[] = {!aIsNormal, true, false}; michael@0: aIsNormal = values[index]; michael@0: } michael@0: } else if (GetContent()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir, michael@0: nsGkAtoms::reverse, eCaseMatters)) { michael@0: aIsNormal = !aIsNormal; michael@0: } michael@0: } michael@0: michael@0: /* Returns true if it was set. michael@0: */ michael@0: bool michael@0: nsBoxFrame::GetInitialEqualSize(bool& aEqualSize) michael@0: { michael@0: // see if we are a vertical or horizontal box. michael@0: if (!GetContent()) michael@0: return false; michael@0: michael@0: if (GetContent()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::equalsize, michael@0: nsGkAtoms::always, eCaseMatters)) { michael@0: aEqualSize = true; michael@0: return true; michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: /* Returns true if it was set. michael@0: */ michael@0: bool michael@0: nsBoxFrame::GetInitialAutoStretch(bool& aStretch) michael@0: { michael@0: if (!GetContent()) michael@0: return false; michael@0: michael@0: // Check the align attribute. michael@0: static nsIContent::AttrValuesArray strings[] = michael@0: {&nsGkAtoms::_empty, &nsGkAtoms::stretch, nullptr}; michael@0: int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::align, michael@0: strings, eCaseMatters); michael@0: if (index != nsIContent::ATTR_MISSING && index != 0) { michael@0: aStretch = index == 1; michael@0: return true; michael@0: } michael@0: michael@0: // Check the CSS box-align property. michael@0: const nsStyleXUL* boxInfo = StyleXUL(); michael@0: aStretch = (boxInfo->mBoxAlign == NS_STYLE_BOX_ALIGN_STRETCH); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: nsresult michael@0: nsBoxFrame::DidReflow(nsPresContext* aPresContext, michael@0: const nsHTMLReflowState* aReflowState, michael@0: nsDidReflowStatus aStatus) michael@0: { michael@0: nsFrameState preserveBits = michael@0: mState & (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN); michael@0: nsresult rv = nsFrame::DidReflow(aPresContext, aReflowState, aStatus); michael@0: mState |= preserveBits; michael@0: return rv; michael@0: } michael@0: michael@0: bool michael@0: nsBoxFrame::HonorPrintBackgroundSettings() michael@0: { michael@0: return (!mContent || !mContent->IsInNativeAnonymousSubtree()) && michael@0: nsContainerFrame::HonorPrintBackgroundSettings(); michael@0: } michael@0: michael@0: #ifdef DO_NOISY_REFLOW michael@0: static int myCounter = 0; michael@0: static void printSize(char * aDesc, nscoord aSize) michael@0: { michael@0: printf(" %s: ", aDesc); michael@0: if (aSize == NS_UNCONSTRAINEDSIZE) { michael@0: printf("UC"); michael@0: } else { michael@0: printf("%d", aSize); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: /* virtual */ nscoord michael@0: nsBoxFrame::GetMinWidth(nsRenderingContext *aRenderingContext) michael@0: { michael@0: nscoord result; michael@0: DISPLAY_MIN_WIDTH(this, result); michael@0: michael@0: nsBoxLayoutState state(PresContext(), aRenderingContext); michael@0: nsSize minSize = GetMinSize(state); michael@0: michael@0: // GetMinSize returns border-box width, and we want to return content michael@0: // width. Since Reflow uses the reflow state's border and padding, we michael@0: // actually just want to subtract what GetMinSize added, which is the michael@0: // result of GetBorderAndPadding. michael@0: nsMargin bp; michael@0: GetBorderAndPadding(bp); michael@0: michael@0: result = minSize.width - bp.LeftRight(); michael@0: result = std::max(result, 0); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: /* virtual */ nscoord michael@0: nsBoxFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) michael@0: { michael@0: nscoord result; michael@0: DISPLAY_PREF_WIDTH(this, result); michael@0: michael@0: nsBoxLayoutState state(PresContext(), aRenderingContext); michael@0: nsSize prefSize = GetPrefSize(state); michael@0: michael@0: // GetPrefSize returns border-box width, and we want to return content michael@0: // width. Since Reflow uses the reflow state's border and padding, we michael@0: // actually just want to subtract what GetPrefSize added, which is the michael@0: // result of GetBorderAndPadding. michael@0: nsMargin bp; michael@0: GetBorderAndPadding(bp); michael@0: michael@0: result = prefSize.width - bp.LeftRight(); michael@0: result = std::max(result, 0); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: nsresult michael@0: nsBoxFrame::Reflow(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus) michael@0: { michael@0: // If you make changes to this method, please keep nsLeafBoxFrame::Reflow michael@0: // in sync, if the changes are applicable there. michael@0: michael@0: DO_GLOBAL_REFLOW_COUNT("nsBoxFrame"); michael@0: DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); michael@0: michael@0: NS_ASSERTION(aReflowState.ComputedWidth() >=0 && michael@0: aReflowState.ComputedHeight() >= 0, "Computed Size < 0"); michael@0: michael@0: #ifdef DO_NOISY_REFLOW michael@0: printf("\n-------------Starting BoxFrame Reflow ----------------------------\n"); michael@0: printf("%p ** nsBF::Reflow %d ", this, myCounter++); michael@0: michael@0: printSize("AW", aReflowState.AvailableWidth()); michael@0: printSize("AH", aReflowState.AvailableHeight()); michael@0: printSize("CW", aReflowState.ComputedWidth()); michael@0: printSize("CH", aReflowState.ComputedHeight()); michael@0: michael@0: printf(" *\n"); michael@0: michael@0: #endif michael@0: michael@0: aStatus = NS_FRAME_COMPLETE; michael@0: michael@0: // create the layout state michael@0: nsBoxLayoutState state(aPresContext, aReflowState.rendContext, michael@0: &aReflowState, aReflowState.mReflowDepth); michael@0: michael@0: nsSize computedSize(aReflowState.ComputedWidth(),aReflowState.ComputedHeight()); michael@0: michael@0: nsMargin m; michael@0: m = aReflowState.ComputedPhysicalBorderPadding(); michael@0: // GetBorderAndPadding(m); michael@0: michael@0: nsSize prefSize(0,0); michael@0: michael@0: // if we are told to layout intrinsic then get our preferred size. michael@0: NS_ASSERTION(computedSize.width != NS_INTRINSICSIZE, michael@0: "computed width should always be computed"); michael@0: if (computedSize.height == NS_INTRINSICSIZE) { michael@0: prefSize = GetPrefSize(state); michael@0: nsSize minSize = GetMinSize(state); michael@0: nsSize maxSize = GetMaxSize(state); michael@0: // XXXbz isn't GetPrefSize supposed to bounds-check for us? michael@0: prefSize = BoundsCheck(minSize, prefSize, maxSize); michael@0: } michael@0: michael@0: // get our desiredSize michael@0: computedSize.width += m.left + m.right; michael@0: michael@0: if (aReflowState.ComputedHeight() == NS_INTRINSICSIZE) { michael@0: computedSize.height = prefSize.height; michael@0: // prefSize is border-box but min/max constraints are content-box. michael@0: nscoord verticalBorderPadding = michael@0: aReflowState.ComputedPhysicalBorderPadding().TopBottom(); michael@0: nscoord contentHeight = computedSize.height - verticalBorderPadding; michael@0: // Note: contentHeight might be negative, but that's OK because min-height michael@0: // is never negative. michael@0: computedSize.height = aReflowState.ApplyMinMaxHeight(contentHeight) + michael@0: verticalBorderPadding; michael@0: } else { michael@0: computedSize.height += m.top + m.bottom; michael@0: } michael@0: michael@0: nsRect r(mRect.x, mRect.y, computedSize.width, computedSize.height); michael@0: michael@0: SetBounds(state, r); michael@0: michael@0: // layout our children michael@0: Layout(state); michael@0: michael@0: // ok our child could have gotten bigger. So lets get its bounds michael@0: michael@0: // get the ascent michael@0: nscoord ascent = mRect.height; michael@0: michael@0: // getting the ascent could be a lot of work. Don't get it if michael@0: // we are the root. The viewport doesn't care about it. michael@0: if (!(mState & NS_STATE_IS_ROOT)) { michael@0: ascent = GetBoxAscent(state); michael@0: } michael@0: michael@0: aDesiredSize.Width() = mRect.width; michael@0: aDesiredSize.Height() = mRect.height; michael@0: aDesiredSize.SetTopAscent(ascent); michael@0: michael@0: aDesiredSize.mOverflowAreas = GetOverflowAreas(); michael@0: michael@0: #ifdef DO_NOISY_REFLOW michael@0: { michael@0: printf("%p ** nsBF(done) W:%d H:%d ", this, aDesiredSize.Width(), aDesiredSize.Height()); michael@0: michael@0: if (maxElementSize) { michael@0: printf("MW:%d\n", *maxElementWidth); michael@0: } else { michael@0: printf("MW:?\n"); michael@0: } michael@0: michael@0: } michael@0: #endif michael@0: michael@0: ReflowAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus); michael@0: michael@0: NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsSize michael@0: nsBoxFrame::GetPrefSize(nsBoxLayoutState& aBoxLayoutState) michael@0: { michael@0: NS_ASSERTION(aBoxLayoutState.GetRenderingContext(), michael@0: "must have rendering context"); michael@0: michael@0: nsSize size(0,0); michael@0: DISPLAY_PREF_SIZE(this, size); michael@0: if (!DoesNeedRecalc(mPrefSize)) { michael@0: return mPrefSize; michael@0: } michael@0: michael@0: #ifdef DEBUG_LAYOUT michael@0: PropagateDebug(aBoxLayoutState); michael@0: #endif michael@0: michael@0: if (IsCollapsed()) michael@0: return size; michael@0: michael@0: // if the size was not completely redefined in CSS then ask our children michael@0: bool widthSet, heightSet; michael@0: if (!nsIFrame::AddCSSPrefSize(this, size, widthSet, heightSet)) michael@0: { michael@0: if (mLayoutManager) { michael@0: nsSize layoutSize = mLayoutManager->GetPrefSize(this, aBoxLayoutState); michael@0: if (!widthSet) michael@0: size.width = layoutSize.width; michael@0: if (!heightSet) michael@0: size.height = layoutSize.height; michael@0: } michael@0: else { michael@0: size = nsBox::GetPrefSize(aBoxLayoutState); michael@0: } michael@0: } michael@0: michael@0: nsSize minSize = GetMinSize(aBoxLayoutState); michael@0: nsSize maxSize = GetMaxSize(aBoxLayoutState); michael@0: mPrefSize = BoundsCheck(minSize, size, maxSize); michael@0: michael@0: return mPrefSize; michael@0: } michael@0: michael@0: nscoord michael@0: nsBoxFrame::GetBoxAscent(nsBoxLayoutState& aBoxLayoutState) michael@0: { michael@0: if (!DoesNeedRecalc(mAscent)) michael@0: return mAscent; michael@0: michael@0: #ifdef DEBUG_LAYOUT michael@0: PropagateDebug(aBoxLayoutState); michael@0: #endif michael@0: michael@0: if (IsCollapsed()) michael@0: return 0; michael@0: michael@0: if (mLayoutManager) michael@0: mAscent = mLayoutManager->GetAscent(this, aBoxLayoutState); michael@0: else michael@0: mAscent = nsBox::GetBoxAscent(aBoxLayoutState); michael@0: michael@0: return mAscent; michael@0: } michael@0: michael@0: nsSize michael@0: nsBoxFrame::GetMinSize(nsBoxLayoutState& aBoxLayoutState) michael@0: { michael@0: NS_ASSERTION(aBoxLayoutState.GetRenderingContext(), michael@0: "must have rendering context"); michael@0: michael@0: nsSize size(0,0); michael@0: DISPLAY_MIN_SIZE(this, size); michael@0: if (!DoesNeedRecalc(mMinSize)) { michael@0: return mMinSize; michael@0: } michael@0: michael@0: #ifdef DEBUG_LAYOUT michael@0: PropagateDebug(aBoxLayoutState); michael@0: #endif michael@0: michael@0: if (IsCollapsed()) michael@0: return size; michael@0: michael@0: // if the size was not completely redefined in CSS then ask our children michael@0: bool widthSet, heightSet; michael@0: if (!nsIFrame::AddCSSMinSize(aBoxLayoutState, this, size, widthSet, heightSet)) michael@0: { michael@0: if (mLayoutManager) { michael@0: nsSize layoutSize = mLayoutManager->GetMinSize(this, aBoxLayoutState); michael@0: if (!widthSet) michael@0: size.width = layoutSize.width; michael@0: if (!heightSet) michael@0: size.height = layoutSize.height; michael@0: } michael@0: else { michael@0: size = nsBox::GetMinSize(aBoxLayoutState); michael@0: } michael@0: } michael@0: michael@0: mMinSize = size; michael@0: michael@0: return size; michael@0: } michael@0: michael@0: nsSize michael@0: nsBoxFrame::GetMaxSize(nsBoxLayoutState& aBoxLayoutState) michael@0: { michael@0: NS_ASSERTION(aBoxLayoutState.GetRenderingContext(), michael@0: "must have rendering context"); michael@0: michael@0: nsSize size(NS_INTRINSICSIZE, NS_INTRINSICSIZE); michael@0: DISPLAY_MAX_SIZE(this, size); michael@0: if (!DoesNeedRecalc(mMaxSize)) { michael@0: return mMaxSize; michael@0: } michael@0: michael@0: #ifdef DEBUG_LAYOUT michael@0: PropagateDebug(aBoxLayoutState); michael@0: #endif michael@0: michael@0: if (IsCollapsed()) michael@0: return size; michael@0: michael@0: // if the size was not completely redefined in CSS then ask our children michael@0: bool widthSet, heightSet; michael@0: if (!nsIFrame::AddCSSMaxSize(this, size, widthSet, heightSet)) michael@0: { michael@0: if (mLayoutManager) { michael@0: nsSize layoutSize = mLayoutManager->GetMaxSize(this, aBoxLayoutState); michael@0: if (!widthSet) michael@0: size.width = layoutSize.width; michael@0: if (!heightSet) michael@0: size.height = layoutSize.height; michael@0: } michael@0: else { michael@0: size = nsBox::GetMaxSize(aBoxLayoutState); michael@0: } michael@0: } michael@0: michael@0: mMaxSize = size; michael@0: michael@0: return size; michael@0: } michael@0: michael@0: nscoord michael@0: nsBoxFrame::GetFlex(nsBoxLayoutState& aBoxLayoutState) michael@0: { michael@0: if (!DoesNeedRecalc(mFlex)) michael@0: return mFlex; michael@0: michael@0: mFlex = nsBox::GetFlex(aBoxLayoutState); michael@0: michael@0: return mFlex; michael@0: } michael@0: michael@0: /** michael@0: * If subclassing please subclass this method not layout. michael@0: * layout will call this method. michael@0: */ michael@0: NS_IMETHODIMP michael@0: nsBoxFrame::DoLayout(nsBoxLayoutState& aState) michael@0: { michael@0: uint32_t oldFlags = aState.LayoutFlags(); michael@0: aState.SetLayoutFlags(0); michael@0: michael@0: nsresult rv = NS_OK; michael@0: if (mLayoutManager) { michael@0: CoordNeedsRecalc(mAscent); michael@0: rv = mLayoutManager->Layout(this, aState); michael@0: } michael@0: michael@0: aState.SetLayoutFlags(oldFlags); michael@0: michael@0: if (HasAbsolutelyPositionedChildren()) { michael@0: // Set up a |reflowState| to pass into ReflowAbsoluteFrames michael@0: nsHTMLReflowState reflowState(aState.PresContext(), this, michael@0: aState.GetRenderingContext(), michael@0: nsSize(mRect.width, NS_UNCONSTRAINEDSIZE)); michael@0: michael@0: // Set up a |desiredSize| to pass into ReflowAbsoluteFrames michael@0: nsHTMLReflowMetrics desiredSize(reflowState); michael@0: desiredSize.Width() = mRect.width; michael@0: desiredSize.Height() = mRect.height; michael@0: michael@0: // get the ascent (cribbed from ::Reflow) michael@0: nscoord ascent = mRect.height; michael@0: michael@0: // getting the ascent could be a lot of work. Don't get it if michael@0: // we are the root. The viewport doesn't care about it. michael@0: if (!(mState & NS_STATE_IS_ROOT)) { michael@0: ascent = GetBoxAscent(aState); michael@0: } michael@0: desiredSize.SetTopAscent(ascent); michael@0: desiredSize.mOverflowAreas = GetOverflowAreas(); michael@0: michael@0: AddStateBits(NS_FRAME_IN_REFLOW); michael@0: // Set up a |reflowStatus| to pass into ReflowAbsoluteFrames michael@0: // (just a dummy value; hopefully that's OK) michael@0: nsReflowStatus reflowStatus = NS_FRAME_COMPLETE; michael@0: ReflowAbsoluteFrames(aState.PresContext(), desiredSize, michael@0: reflowState, reflowStatus); michael@0: RemoveStateBits(NS_FRAME_IN_REFLOW); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: void michael@0: nsBoxFrame::DestroyFrom(nsIFrame* aDestructRoot) michael@0: { michael@0: // unregister access key michael@0: RegUnregAccessKey(false); michael@0: michael@0: // clean up the container box's layout manager and child boxes michael@0: SetLayoutManager(nullptr); michael@0: michael@0: nsContainerFrame::DestroyFrom(aDestructRoot); michael@0: } michael@0: michael@0: #ifdef DEBUG_LAYOUT michael@0: nsresult michael@0: nsBoxFrame::SetDebug(nsBoxLayoutState& aState, bool aDebug) michael@0: { michael@0: // see if our state matches the given debug state michael@0: bool debugSet = mState & NS_STATE_CURRENTLY_IN_DEBUG; michael@0: bool debugChanged = (!aDebug && debugSet) || (aDebug && !debugSet); michael@0: michael@0: // if it doesn't then tell each child below us the new debug state michael@0: if (debugChanged) michael@0: { michael@0: if (aDebug) { michael@0: mState |= NS_STATE_CURRENTLY_IN_DEBUG; michael@0: } else { michael@0: mState &= ~NS_STATE_CURRENTLY_IN_DEBUG; michael@0: } michael@0: michael@0: SetDebugOnChildList(aState, mFirstChild, aDebug); michael@0: michael@0: MarkIntrinsicWidthsDirty(); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: #endif michael@0: michael@0: /* virtual */ void michael@0: nsBoxFrame::MarkIntrinsicWidthsDirty() michael@0: { michael@0: SizeNeedsRecalc(mPrefSize); michael@0: SizeNeedsRecalc(mMinSize); michael@0: SizeNeedsRecalc(mMaxSize); michael@0: CoordNeedsRecalc(mFlex); michael@0: CoordNeedsRecalc(mAscent); michael@0: michael@0: if (mLayoutManager) { michael@0: nsBoxLayoutState state(PresContext()); michael@0: mLayoutManager->IntrinsicWidthsDirty(this, state); michael@0: } michael@0: michael@0: // Don't call base class method, since everything it does is within an michael@0: // IsBoxWrapped check. michael@0: } michael@0: michael@0: nsresult michael@0: nsBoxFrame::RemoveFrame(ChildListID aListID, michael@0: nsIFrame* aOldFrame) michael@0: { michael@0: NS_PRECONDITION(aListID == kPrincipalList, "We don't support out-of-flow kids"); michael@0: nsPresContext* presContext = PresContext(); michael@0: nsBoxLayoutState state(presContext); michael@0: michael@0: // remove the child frame michael@0: mFrames.RemoveFrame(aOldFrame); michael@0: michael@0: // notify the layout manager michael@0: if (mLayoutManager) michael@0: mLayoutManager->ChildrenRemoved(this, state, aOldFrame); michael@0: michael@0: // destroy the child frame michael@0: aOldFrame->Destroy(); michael@0: michael@0: // mark us dirty and generate a reflow command michael@0: PresContext()->PresShell()-> michael@0: FrameNeedsReflow(this, nsIPresShell::eTreeChange, michael@0: NS_FRAME_HAS_DIRTY_CHILDREN); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsBoxFrame::InsertFrames(ChildListID aListID, michael@0: nsIFrame* aPrevFrame, michael@0: nsFrameList& aFrameList) michael@0: { michael@0: NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this, michael@0: "inserting after sibling frame with different parent"); michael@0: NS_ASSERTION(!aPrevFrame || mFrames.ContainsFrame(aPrevFrame), michael@0: "inserting after sibling frame not in our child list"); michael@0: NS_PRECONDITION(aListID == kPrincipalList, "We don't support out-of-flow kids"); michael@0: nsBoxLayoutState state(PresContext()); michael@0: michael@0: // insert the child frames michael@0: const nsFrameList::Slice& newFrames = michael@0: mFrames.InsertFrames(this, aPrevFrame, aFrameList); michael@0: michael@0: // notify the layout manager michael@0: if (mLayoutManager) michael@0: mLayoutManager->ChildrenInserted(this, state, aPrevFrame, newFrames); michael@0: michael@0: // Make sure to check box order _after_ notifying the layout michael@0: // manager; otherwise the slice we give the layout manager will michael@0: // just be bogus. If the layout manager cares about the order, we michael@0: // just lose. michael@0: CheckBoxOrder(); michael@0: michael@0: #ifdef DEBUG_LAYOUT michael@0: // if we are in debug make sure our children are in debug as well. michael@0: if (mState & NS_STATE_CURRENTLY_IN_DEBUG) michael@0: SetDebugOnChildList(state, mFrames.FirstChild(), true); michael@0: #endif michael@0: michael@0: PresContext()->PresShell()-> michael@0: FrameNeedsReflow(this, nsIPresShell::eTreeChange, michael@0: NS_FRAME_HAS_DIRTY_CHILDREN); michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: nsresult michael@0: nsBoxFrame::AppendFrames(ChildListID aListID, michael@0: nsFrameList& aFrameList) michael@0: { michael@0: NS_PRECONDITION(aListID == kPrincipalList, "We don't support out-of-flow kids"); michael@0: nsBoxLayoutState state(PresContext()); michael@0: michael@0: // append the new frames michael@0: const nsFrameList::Slice& newFrames = mFrames.AppendFrames(this, aFrameList); michael@0: michael@0: // notify the layout manager michael@0: if (mLayoutManager) michael@0: mLayoutManager->ChildrenAppended(this, state, newFrames); michael@0: michael@0: // Make sure to check box order _after_ notifying the layout michael@0: // manager; otherwise the slice we give the layout manager will michael@0: // just be bogus. If the layout manager cares about the order, we michael@0: // just lose. michael@0: CheckBoxOrder(); michael@0: michael@0: #ifdef DEBUG_LAYOUT michael@0: // if we are in debug make sure our children are in debug as well. michael@0: if (mState & NS_STATE_CURRENTLY_IN_DEBUG) michael@0: SetDebugOnChildList(state, mFrames.FirstChild(), true); michael@0: #endif michael@0: michael@0: // XXXbz why is this NS_FRAME_FIRST_REFLOW check here? michael@0: if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) { michael@0: PresContext()->PresShell()-> michael@0: FrameNeedsReflow(this, nsIPresShell::eTreeChange, michael@0: NS_FRAME_HAS_DIRTY_CHILDREN); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* virtual */ nsIFrame* michael@0: nsBoxFrame::GetContentInsertionFrame() michael@0: { michael@0: if (GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK) michael@0: return GetFirstPrincipalChild()->GetContentInsertionFrame(); michael@0: return nsContainerFrame::GetContentInsertionFrame(); michael@0: } michael@0: michael@0: nsresult michael@0: nsBoxFrame::AttributeChanged(int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: nsresult rv = nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, michael@0: aModType); michael@0: michael@0: // Ignore 'width', 'height', 'screenX', 'screenY' and 'sizemode' on a michael@0: // . michael@0: nsIAtom *tag = mContent->Tag(); michael@0: if ((tag == nsGkAtoms::window || michael@0: tag == nsGkAtoms::page || michael@0: tag == nsGkAtoms::dialog || michael@0: tag == nsGkAtoms::wizard) && michael@0: (nsGkAtoms::width == aAttribute || michael@0: nsGkAtoms::height == aAttribute || michael@0: nsGkAtoms::screenX == aAttribute || michael@0: nsGkAtoms::screenY == aAttribute || michael@0: nsGkAtoms::sizemode == aAttribute)) { michael@0: return rv; michael@0: } michael@0: michael@0: if (aAttribute == nsGkAtoms::width || michael@0: aAttribute == nsGkAtoms::height || michael@0: aAttribute == nsGkAtoms::align || michael@0: aAttribute == nsGkAtoms::valign || michael@0: aAttribute == nsGkAtoms::left || michael@0: aAttribute == nsGkAtoms::top || michael@0: aAttribute == nsGkAtoms::right || michael@0: aAttribute == nsGkAtoms::bottom || michael@0: aAttribute == nsGkAtoms::start || michael@0: aAttribute == nsGkAtoms::end || michael@0: aAttribute == nsGkAtoms::minwidth || michael@0: aAttribute == nsGkAtoms::maxwidth || michael@0: aAttribute == nsGkAtoms::minheight || michael@0: aAttribute == nsGkAtoms::maxheight || michael@0: aAttribute == nsGkAtoms::flex || michael@0: aAttribute == nsGkAtoms::orient || michael@0: aAttribute == nsGkAtoms::pack || michael@0: aAttribute == nsGkAtoms::dir || michael@0: aAttribute == nsGkAtoms::mousethrough || michael@0: aAttribute == nsGkAtoms::equalsize) { michael@0: michael@0: if (aAttribute == nsGkAtoms::align || michael@0: aAttribute == nsGkAtoms::valign || michael@0: aAttribute == nsGkAtoms::orient || michael@0: aAttribute == nsGkAtoms::pack || michael@0: #ifdef DEBUG_LAYOUT michael@0: aAttribute == nsGkAtoms::debug || michael@0: #endif michael@0: aAttribute == nsGkAtoms::dir) { michael@0: michael@0: mValign = nsBoxFrame::vAlign_Top; michael@0: mHalign = nsBoxFrame::hAlign_Left; michael@0: michael@0: bool orient = true; michael@0: GetInitialOrientation(orient); michael@0: if (orient) michael@0: mState |= NS_STATE_IS_HORIZONTAL; michael@0: else michael@0: mState &= ~NS_STATE_IS_HORIZONTAL; michael@0: michael@0: bool normal = true; michael@0: GetInitialDirection(normal); michael@0: if (normal) michael@0: mState |= NS_STATE_IS_DIRECTION_NORMAL; michael@0: else michael@0: mState &= ~NS_STATE_IS_DIRECTION_NORMAL; michael@0: michael@0: GetInitialVAlignment(mValign); michael@0: GetInitialHAlignment(mHalign); michael@0: michael@0: bool equalSize = false; michael@0: GetInitialEqualSize(equalSize); michael@0: if (equalSize) michael@0: mState |= NS_STATE_EQUAL_SIZE; michael@0: else michael@0: mState &= ~NS_STATE_EQUAL_SIZE; michael@0: michael@0: #ifdef DEBUG_LAYOUT michael@0: bool debug = mState & NS_STATE_SET_TO_DEBUG; michael@0: bool debugSet = GetInitialDebug(debug); michael@0: if (debugSet) { michael@0: mState |= NS_STATE_DEBUG_WAS_SET; michael@0: michael@0: if (debug) michael@0: mState |= NS_STATE_SET_TO_DEBUG; michael@0: else michael@0: mState &= ~NS_STATE_SET_TO_DEBUG; michael@0: } else { michael@0: mState &= ~NS_STATE_DEBUG_WAS_SET; michael@0: } michael@0: #endif michael@0: michael@0: bool autostretch = !!(mState & NS_STATE_AUTO_STRETCH); michael@0: GetInitialAutoStretch(autostretch); michael@0: if (autostretch) michael@0: mState |= NS_STATE_AUTO_STRETCH; michael@0: else michael@0: mState &= ~NS_STATE_AUTO_STRETCH; michael@0: } michael@0: else if (aAttribute == nsGkAtoms::left || michael@0: aAttribute == nsGkAtoms::top || michael@0: aAttribute == nsGkAtoms::right || michael@0: aAttribute == nsGkAtoms::bottom || michael@0: aAttribute == nsGkAtoms::start || michael@0: aAttribute == nsGkAtoms::end) { michael@0: mState &= ~NS_STATE_STACK_NOT_POSITIONED; michael@0: } michael@0: else if (aAttribute == nsGkAtoms::mousethrough) { michael@0: UpdateMouseThrough(); michael@0: } michael@0: michael@0: PresContext()->PresShell()-> michael@0: FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); michael@0: } michael@0: else if (aAttribute == nsGkAtoms::ordinal) { michael@0: nsBoxLayoutState state(PresContext()); michael@0: nsIFrame* parent = GetParentBox(); michael@0: // If our parent is not a box, there's not much we can do... but in that michael@0: // case our ordinal doesn't matter anyway, so that's ok. michael@0: // Also don't bother with popup frames since they are kept on the michael@0: // kPopupList and RelayoutChildAtOrdinal() only handles michael@0: // principal children. michael@0: if (parent && !(GetStateBits() & NS_FRAME_OUT_OF_FLOW) && michael@0: StyleDisplay()->mDisplay != NS_STYLE_DISPLAY_POPUP) { michael@0: parent->RelayoutChildAtOrdinal(state, this); michael@0: // XXXldb Should this instead be a tree change on the child or parent? michael@0: PresContext()->PresShell()-> michael@0: FrameNeedsReflow(parent, nsIPresShell::eStyleChange, michael@0: NS_FRAME_IS_DIRTY); michael@0: } michael@0: } michael@0: // If the accesskey changed, register for the new value michael@0: // The old value has been unregistered in nsXULElement::SetAttr michael@0: else if (aAttribute == nsGkAtoms::accesskey) { michael@0: RegUnregAccessKey(true); michael@0: } michael@0: else if (aAttribute == nsGkAtoms::rows && michael@0: tag == nsGkAtoms::tree) { michael@0: // Reflow ourselves and all our children if "rows" changes, since michael@0: // nsTreeBodyFrame's layout reads this from its parent (this frame). michael@0: PresContext()->PresShell()-> michael@0: FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: #ifdef DEBUG_LAYOUT michael@0: void michael@0: nsBoxFrame::GetDebugPref(nsPresContext* aPresContext) michael@0: { michael@0: gDebug = Preferences::GetBool("xul.debug.box"); michael@0: } michael@0: michael@0: class nsDisplayXULDebug : public nsDisplayItem { michael@0: public: michael@0: nsDisplayXULDebug(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) : michael@0: nsDisplayItem(aBuilder, aFrame) { michael@0: MOZ_COUNT_CTOR(nsDisplayXULDebug); michael@0: } michael@0: #ifdef NS_BUILD_REFCNT_LOGGING michael@0: virtual ~nsDisplayXULDebug() { michael@0: MOZ_COUNT_DTOR(nsDisplayXULDebug); michael@0: } michael@0: #endif michael@0: michael@0: virtual void HitTest(nsDisplayListBuilder* aBuilder, nsRect aRect, michael@0: HitTestState* aState, nsTArray *aOutFrames) { michael@0: nsPoint rectCenter(aRect.x + aRect.width / 2, aRect.y + aRect.height / 2); michael@0: static_cast(mFrame)-> michael@0: DisplayDebugInfoFor(this, rectCenter - ToReferenceFrame()); michael@0: aOutFrames->AppendElement(this); michael@0: } michael@0: virtual void Paint(nsDisplayListBuilder* aBuilder michael@0: nsRenderingContext* aCtx); michael@0: NS_DISPLAY_DECL_NAME("XULDebug", TYPE_XUL_DEBUG) michael@0: }; michael@0: michael@0: void michael@0: nsDisplayXULDebug::Paint(nsDisplayListBuilder* aBuilder, michael@0: nsRenderingContext* aCtx) michael@0: { michael@0: static_cast(mFrame)-> michael@0: PaintXULDebugOverlay(*aCtx, ToReferenceFrame()); michael@0: } michael@0: michael@0: static void michael@0: PaintXULDebugBackground(nsIFrame* aFrame, nsRenderingContext* aCtx, michael@0: const nsRect& aDirtyRect, nsPoint aPt) michael@0: { michael@0: static_cast(aFrame)->PaintXULDebugBackground(*aCtx, aPt); michael@0: } michael@0: #endif michael@0: michael@0: void michael@0: nsBoxFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) michael@0: { michael@0: // forcelayer is only supported on XUL elements with box layout michael@0: bool forceLayer = michael@0: GetContent()->HasAttr(kNameSpaceID_None, nsGkAtoms::layer) && michael@0: GetContent()->IsXUL(); michael@0: michael@0: // Check for frames that are marked as a part of the region used michael@0: // in calculating glass margins on Windows. michael@0: if (GetContent()->IsXUL()) { michael@0: const nsStyleDisplay* styles = StyleDisplay(); michael@0: if (styles && styles->mAppearance == NS_THEME_WIN_EXCLUDE_GLASS) { michael@0: nsRect rect = nsRect(aBuilder->ToReferenceFrame(this), GetSize()); michael@0: aBuilder->AddExcludedGlassRegion(rect); michael@0: } michael@0: } michael@0: michael@0: nsDisplayListCollection tempLists; michael@0: const nsDisplayListSet& destination = forceLayer ? tempLists : aLists; michael@0: michael@0: DisplayBorderBackgroundOutline(aBuilder, destination); michael@0: michael@0: #ifdef DEBUG_LAYOUT michael@0: if (mState & NS_STATE_CURRENTLY_IN_DEBUG) { michael@0: destination.BorderBackground()->AppendNewToTop(new (aBuilder) michael@0: nsDisplayGeneric(aBuilder, this, PaintXULDebugBackground, michael@0: "XULDebugBackground")); michael@0: destination.Outlines()->AppendNewToTop(new (aBuilder) michael@0: nsDisplayXULDebug(aBuilder, this)); michael@0: } michael@0: #endif michael@0: michael@0: BuildDisplayListForChildren(aBuilder, aDirtyRect, destination); michael@0: michael@0: // see if we have to draw a selection frame around this container michael@0: DisplaySelectionOverlay(aBuilder, destination.Content()); michael@0: michael@0: if (forceLayer) { michael@0: // This is a bit of a hack. Collect up all descendant display items michael@0: // and merge them into a single Content() list. This can cause us michael@0: // to violate CSS stacking order, but forceLayer is a magic michael@0: // XUL-only extension anyway. michael@0: nsDisplayList masterList; michael@0: masterList.AppendToTop(tempLists.BorderBackground()); michael@0: masterList.AppendToTop(tempLists.BlockBorderBackgrounds()); michael@0: masterList.AppendToTop(tempLists.Floats()); michael@0: masterList.AppendToTop(tempLists.Content()); michael@0: masterList.AppendToTop(tempLists.PositionedDescendants()); michael@0: masterList.AppendToTop(tempLists.Outlines()); michael@0: // Wrap the list to make it its own layer michael@0: aLists.Content()->AppendNewToTop(new (aBuilder) michael@0: nsDisplayOwnLayer(aBuilder, this, &masterList)); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsBoxFrame::BuildDisplayListForChildren(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) michael@0: { michael@0: nsIFrame* kid = mFrames.FirstChild(); michael@0: // Put each child's background onto the BlockBorderBackgrounds list michael@0: // to emulate the existing two-layer XUL painting scheme. michael@0: nsDisplayListSet set(aLists, aLists.BlockBorderBackgrounds()); michael@0: // The children should be in the right order michael@0: while (kid) { michael@0: BuildDisplayListForChild(aBuilder, kid, aDirtyRect, set); michael@0: kid = kid->GetNextSibling(); michael@0: } michael@0: } michael@0: michael@0: // REVIEW: PaintChildren did a few things none of which are a big deal michael@0: // anymore: michael@0: // * Paint some debugging rects for this frame. michael@0: // This is done by nsDisplayXULDebugBackground, which goes in the michael@0: // BorderBackground() layer so it isn't clipped by OVERFLOW_CLIP. michael@0: // * Apply OVERFLOW_CLIP to the children. michael@0: // This is now in nsFrame::BuildDisplayListForStackingContext/Child. michael@0: // * Actually paint the children. michael@0: // Moved to BuildDisplayList. michael@0: // * Paint per-kid debug information. michael@0: // This is done by nsDisplayXULDebug, which is in the Outlines() michael@0: // layer so it goes on top. This means it is not clipped by OVERFLOW_CLIP, michael@0: // whereas it did used to respect OVERFLOW_CLIP, but too bad. michael@0: #ifdef DEBUG_LAYOUT michael@0: void michael@0: nsBoxFrame::PaintXULDebugBackground(nsRenderingContext& aRenderingContext, michael@0: nsPoint aPt) michael@0: { michael@0: nsMargin border; michael@0: GetBorder(border); michael@0: michael@0: nsMargin debugBorder; michael@0: nsMargin debugMargin; michael@0: nsMargin debugPadding; michael@0: michael@0: bool isHorizontal = IsHorizontal(); michael@0: michael@0: GetDebugBorder(debugBorder); michael@0: PixelMarginToTwips(GetPresContext(), debugBorder); michael@0: michael@0: GetDebugMargin(debugMargin); michael@0: PixelMarginToTwips(GetPresContext(), debugMargin); michael@0: michael@0: GetDebugPadding(debugPadding); michael@0: PixelMarginToTwips(GetPresContext(), debugPadding); michael@0: michael@0: nsRect inner(mRect); michael@0: inner.MoveTo(aPt); michael@0: inner.Deflate(debugMargin); michael@0: inner.Deflate(border); michael@0: //nsRect borderRect(inner); michael@0: michael@0: nscolor color; michael@0: if (isHorizontal) { michael@0: color = NS_RGB(0,0,255); michael@0: } else { michael@0: color = NS_RGB(255,0,0); michael@0: } michael@0: michael@0: aRenderingContext.SetColor(color); michael@0: michael@0: //left michael@0: nsRect r(inner); michael@0: r.width = debugBorder.left; michael@0: aRenderingContext.FillRect(r); michael@0: michael@0: // top michael@0: r = inner; michael@0: r.height = debugBorder.top; michael@0: aRenderingContext.FillRect(r); michael@0: michael@0: //right michael@0: r = inner; michael@0: r.x = r.x + r.width - debugBorder.right; michael@0: r.width = debugBorder.right; michael@0: aRenderingContext.FillRect(r); michael@0: michael@0: //bottom michael@0: r = inner; michael@0: r.y = r.y + r.height - debugBorder.bottom; michael@0: r.height = debugBorder.bottom; michael@0: aRenderingContext.FillRect(r); michael@0: michael@0: michael@0: // if we have dirty children or we are dirty michael@0: // place a green border around us. michael@0: if (NS_SUBTREE_DIRTY(this)) { michael@0: nsRect dirtyr(inner); michael@0: aRenderingContext.SetColor(NS_RGB(0,255,0)); michael@0: aRenderingContext.DrawRect(dirtyr); michael@0: aRenderingContext.SetColor(color); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsBoxFrame::PaintXULDebugOverlay(nsRenderingContext& aRenderingContext, michael@0: nsPoint aPt) michael@0: nsMargin border; michael@0: GetBorder(border); michael@0: michael@0: nsMargin debugMargin; michael@0: GetDebugMargin(debugMargin); michael@0: PixelMarginToTwips(GetPresContext(), debugMargin); michael@0: michael@0: nsRect inner(mRect); michael@0: inner.MoveTo(aPt); michael@0: inner.Deflate(debugMargin); michael@0: inner.Deflate(border); michael@0: michael@0: nscoord onePixel = GetPresContext()->IntScaledPixelsToTwips(1); michael@0: michael@0: kid = GetChildBox(); michael@0: while (nullptr != kid) { michael@0: bool isHorizontal = IsHorizontal(); michael@0: michael@0: nscoord x, y, borderSize, spacerSize; michael@0: michael@0: nsRect cr(kid->mRect); michael@0: nsMargin margin; michael@0: kid->GetMargin(margin); michael@0: cr.Inflate(margin); michael@0: michael@0: if (isHorizontal) michael@0: { michael@0: cr.y = inner.y; michael@0: x = cr.x; michael@0: y = cr.y + onePixel; michael@0: spacerSize = debugBorder.top - onePixel*4; michael@0: } else { michael@0: cr.x = inner.x; michael@0: x = cr.y; michael@0: y = cr.x + onePixel; michael@0: spacerSize = debugBorder.left - onePixel*4; michael@0: } michael@0: michael@0: nsBoxLayoutState state(GetPresContext()); michael@0: nscoord flex = kid->GetFlex(state); michael@0: michael@0: if (!kid->IsCollapsed()) { michael@0: aRenderingContext.SetColor(NS_RGB(255,255,255)); michael@0: michael@0: if (isHorizontal) michael@0: borderSize = cr.width; michael@0: else michael@0: borderSize = cr.height; michael@0: michael@0: DrawSpacer(GetPresContext(), aRenderingContext, isHorizontal, flex, x, y, borderSize, spacerSize); michael@0: } michael@0: michael@0: kid = kid->GetNextBox(); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: #ifdef DEBUG_LAYOUT michael@0: void michael@0: nsBoxFrame::GetBoxName(nsAutoString& aName) michael@0: { michael@0: GetFrameName(aName); michael@0: } michael@0: #endif michael@0: michael@0: #ifdef DEBUG_FRAME_DUMP michael@0: nsresult michael@0: nsBoxFrame::GetFrameName(nsAString& aResult) const michael@0: { michael@0: return MakeFrameName(NS_LITERAL_STRING("Box"), aResult); michael@0: } michael@0: #endif michael@0: michael@0: nsIAtom* michael@0: nsBoxFrame::GetType() const michael@0: { michael@0: return nsGkAtoms::boxFrame; michael@0: } michael@0: michael@0: #ifdef DEBUG_LAYOUT michael@0: nsresult michael@0: nsBoxFrame::GetDebug(bool& aDebug) michael@0: { michael@0: aDebug = (mState & NS_STATE_CURRENTLY_IN_DEBUG); michael@0: return NS_OK; michael@0: } michael@0: #endif michael@0: michael@0: // REVIEW: nsBoxFrame::GetFrameForPoint is a problem because of 'mousethrough' michael@0: // attribute support. Here's how it works: michael@0: // * For each child frame F, we determine the target frame T(F) by recursively michael@0: // invoking GetFrameForPoint on the child michael@0: // * Let F' be the last child frame such that T(F') doesn't have mousethrough. michael@0: // If F' exists, return T(F') michael@0: // * Otherwise let F'' be the first child frame such that T(F'') is non-null. michael@0: // If F'' exists, return T(F'') michael@0: // * Otherwise return this frame, if this frame contains the point michael@0: // * Otherwise return null michael@0: // It's not clear how this should work for more complex z-ordering situations. michael@0: // The basic principle seems to be that if a frame F has a descendant michael@0: // 'mousethrough' frame that includes the target position, then F michael@0: // will not receive events (unless it overrides GetFrameForPoint). michael@0: // A 'mousethrough' frame will only receive an event if, after applying that rule, michael@0: // all eligible frames are 'mousethrough'; the bottom-most inner-most 'mousethrough' michael@0: // frame is then chosen (the first eligible frame reached in a michael@0: // traversal of the frame tree --- pre/post is irrelevant since ancestors michael@0: // of the mousethrough frames can't be eligible). michael@0: // IMHO this is very bogus and adds a great deal of complexity for something michael@0: // that is very rarely used. So I'm redefining 'mousethrough' to the following: michael@0: // a frame with mousethrough is transparent to mouse events. This is compatible michael@0: // with the way 'mousethrough' is used in Seamonkey's navigator.xul and michael@0: // Firefox's browser.xul. The only other place it's used is in the 'expander' michael@0: // XBL binding, which in our tree is only used by Thunderbird SMIME Advanced michael@0: // Preferences, and I can't figure out what that does, so I'll have to test it. michael@0: // If it's broken I'll probably just change the binding to use it more sensibly. michael@0: // This new behaviour is implemented in nsDisplayList::HitTest. michael@0: // REVIEW: This debug-box stuff is annoying. I'm just going to put debug boxes michael@0: // in the outline layer and avoid GetDebugBoxAt. michael@0: michael@0: // REVIEW: GetCursor had debug-only event dumping code. I have replaced it michael@0: // with instrumentation in nsDisplayXULDebug. michael@0: michael@0: #ifdef DEBUG_LAYOUT michael@0: void michael@0: nsBoxFrame::DrawLine(nsRenderingContext& aRenderingContext, bool aHorizontal, nscoord x1, nscoord y1, nscoord x2, nscoord y2) michael@0: { michael@0: if (aHorizontal) michael@0: aRenderingContext.DrawLine(x1,y1,x2,y2); michael@0: else michael@0: aRenderingContext.DrawLine(y1,x1,y2,x2); michael@0: } michael@0: michael@0: void michael@0: nsBoxFrame::FillRect(nsRenderingContext& aRenderingContext, bool aHorizontal, nscoord x, nscoord y, nscoord width, nscoord height) michael@0: { michael@0: if (aHorizontal) michael@0: aRenderingContext.FillRect(x,y,width,height); michael@0: else michael@0: aRenderingContext.FillRect(y,x,height,width); michael@0: } michael@0: michael@0: void michael@0: nsBoxFrame::DrawSpacer(nsPresContext* aPresContext, nsRenderingContext& aRenderingContext, bool aHorizontal, int32_t flex, nscoord x, nscoord y, nscoord size, nscoord spacerSize) michael@0: { michael@0: nscoord onePixel = aPresContext->IntScaledPixelsToTwips(1); michael@0: michael@0: // if we do draw the coils michael@0: int distance = 0; michael@0: int center = 0; michael@0: int offset = 0; michael@0: int coilSize = COIL_SIZE*onePixel; michael@0: int halfSpacer = spacerSize/2; michael@0: michael@0: distance = size; michael@0: center = y + halfSpacer; michael@0: offset = x; michael@0: michael@0: int coils = distance/coilSize; michael@0: michael@0: int halfCoilSize = coilSize/2; michael@0: michael@0: if (flex == 0) { michael@0: DrawLine(aRenderingContext, aHorizontal, x,y + spacerSize/2, x + size, y + spacerSize/2); michael@0: } else { michael@0: for (int i=0; i < coils; i++) michael@0: { michael@0: DrawLine(aRenderingContext, aHorizontal, offset, center+halfSpacer, offset+halfCoilSize, center-halfSpacer); michael@0: DrawLine(aRenderingContext, aHorizontal, offset+halfCoilSize, center-halfSpacer, offset+coilSize, center+halfSpacer); michael@0: michael@0: offset += coilSize; michael@0: } michael@0: } michael@0: michael@0: FillRect(aRenderingContext, aHorizontal, x + size - spacerSize/2, y, spacerSize/2, spacerSize); michael@0: FillRect(aRenderingContext, aHorizontal, x, y, spacerSize/2, spacerSize); michael@0: michael@0: //DrawKnob(aPresContext, aRenderingContext, x + size - spacerSize, y, spacerSize); michael@0: } michael@0: michael@0: void michael@0: nsBoxFrame::GetDebugBorder(nsMargin& aInset) michael@0: { michael@0: aInset.SizeTo(2,2,2,2); michael@0: michael@0: if (IsHorizontal()) michael@0: aInset.top = 10; michael@0: else michael@0: aInset.left = 10; michael@0: } michael@0: michael@0: void michael@0: nsBoxFrame::GetDebugMargin(nsMargin& aInset) michael@0: { michael@0: aInset.SizeTo(2,2,2,2); michael@0: } michael@0: michael@0: void michael@0: nsBoxFrame::GetDebugPadding(nsMargin& aPadding) michael@0: { michael@0: aPadding.SizeTo(2,2,2,2); michael@0: } michael@0: michael@0: void michael@0: nsBoxFrame::PixelMarginToTwips(nsPresContext* aPresContext, nsMargin& aMarginPixels) michael@0: { michael@0: nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1); michael@0: aMarginPixels.left *= onePixel; michael@0: aMarginPixels.right *= onePixel; michael@0: aMarginPixels.top *= onePixel; michael@0: aMarginPixels.bottom *= onePixel; michael@0: } michael@0: michael@0: void michael@0: nsBoxFrame::GetValue(nsPresContext* aPresContext, const nsSize& a, const nsSize& b, char* ch) michael@0: { michael@0: float p2t = aPresContext->ScaledPixelsToTwips(); michael@0: michael@0: char width[100]; michael@0: char height[100]; michael@0: michael@0: if (a.width == NS_INTRINSICSIZE) michael@0: sprintf(width,"%s","INF"); michael@0: else michael@0: sprintf(width,"%d", nscoord(a.width/*/p2t*/)); michael@0: michael@0: if (a.height == NS_INTRINSICSIZE) michael@0: sprintf(height,"%s","INF"); michael@0: else michael@0: sprintf(height,"%d", nscoord(a.height/*/p2t*/)); michael@0: michael@0: michael@0: sprintf(ch, "(%s%s, %s%s)", width, (b.width != NS_INTRINSICSIZE ? "[SET]" : ""), michael@0: height, (b.height != NS_INTRINSICSIZE ? "[SET]" : "")); michael@0: michael@0: } michael@0: michael@0: void michael@0: nsBoxFrame::GetValue(nsPresContext* aPresContext, int32_t a, int32_t b, char* ch) michael@0: { michael@0: if (a == NS_INTRINSICSIZE) michael@0: sprintf(ch, "%d[SET]", b); michael@0: else michael@0: sprintf(ch, "%d", a); michael@0: } michael@0: michael@0: nsresult michael@0: nsBoxFrame::DisplayDebugInfoFor(nsIFrame* aBox, michael@0: nsPoint& aPoint) michael@0: { michael@0: nsBoxLayoutState state(GetPresContext()); michael@0: michael@0: nscoord x = aPoint.x; michael@0: nscoord y = aPoint.y; michael@0: michael@0: // get the area inside our border but not our debug margins. michael@0: nsRect insideBorder(aBox->mRect); michael@0: insideBorder.MoveTo(0,0): michael@0: nsMargin border(0,0,0,0); michael@0: aBox->GetBorderAndPadding(border); michael@0: insideBorder.Deflate(border); michael@0: michael@0: bool isHorizontal = IsHorizontal(); michael@0: michael@0: if (!insideBorder.Contains(nsPoint(x,y))) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: //printf("%%%%%% inside box %%%%%%%\n"); michael@0: michael@0: int count = 0; michael@0: nsIFrame* child = aBox->GetChildBox(); michael@0: michael@0: nsMargin m; michael@0: nsMargin m2; michael@0: GetDebugBorder(m); michael@0: PixelMarginToTwips(aPresContext, m); michael@0: michael@0: GetDebugMargin(m2); michael@0: PixelMarginToTwips(aPresContext, m2); michael@0: michael@0: m += m2; michael@0: michael@0: if ((isHorizontal && y < insideBorder.y + m.top) || michael@0: (!isHorizontal && x < insideBorder.x + m.left)) { michael@0: //printf("**** inside debug border *******\n"); michael@0: while (child) michael@0: { michael@0: const nsRect& r = child->mRect; michael@0: michael@0: // if we are not in the child. But in the spacer above the child. michael@0: if ((isHorizontal && x >= r.x && x < r.x + r.width) || michael@0: (!isHorizontal && y >= r.y && y < r.y + r.height)) { michael@0: aCursor = NS_STYLE_CURSOR_POINTER; michael@0: // found it but we already showed it. michael@0: if (mDebugChild == child) michael@0: return NS_OK; michael@0: michael@0: if (aBox->GetContent()) { michael@0: printf("---------------\n"); michael@0: DumpBox(stdout); michael@0: printf("\n"); michael@0: } michael@0: michael@0: if (child->GetContent()) { michael@0: printf("child #%d: ", count); michael@0: child->DumpBox(stdout); michael@0: printf("\n"); michael@0: } michael@0: michael@0: mDebugChild = child; michael@0: michael@0: nsSize prefSizeCSS(NS_INTRINSICSIZE, NS_INTRINSICSIZE); michael@0: nsSize minSizeCSS (NS_INTRINSICSIZE, NS_INTRINSICSIZE); michael@0: nsSize maxSizeCSS (NS_INTRINSICSIZE, NS_INTRINSICSIZE); michael@0: nscoord flexCSS = NS_INTRINSICSIZE; michael@0: michael@0: bool widthSet, heightSet; michael@0: nsIFrame::AddCSSPrefSize(child, prefSizeCSS, widthSet, heightSet); michael@0: nsIFrame::AddCSSMinSize (state, child, minSizeCSS, widthSet, heightSet); michael@0: nsIFrame::AddCSSMaxSize (child, maxSizeCSS, widthSet, heightSet); michael@0: nsIFrame::AddCSSFlex (state, child, flexCSS); michael@0: michael@0: nsSize prefSize = child->GetPrefSize(state); michael@0: nsSize minSize = child->GetMinSize(state); michael@0: nsSize maxSize = child->GetMaxSize(state); michael@0: nscoord flexSize = child->GetFlex(state); michael@0: nscoord ascentSize = child->GetBoxAscent(state); michael@0: michael@0: char min[100]; michael@0: char pref[100]; michael@0: char max[100]; michael@0: char calc[100]; michael@0: char flex[100]; michael@0: char ascent[100]; michael@0: michael@0: nsSize actualSize; michael@0: GetFrameSizeWithMargin(child, actualSize); michael@0: nsSize actualSizeCSS (NS_INTRINSICSIZE, NS_INTRINSICSIZE); michael@0: michael@0: GetValue(aPresContext, minSize, minSizeCSS, min); michael@0: GetValue(aPresContext, prefSize, prefSizeCSS, pref); michael@0: GetValue(aPresContext, maxSize, maxSizeCSS, max); michael@0: GetValue(aPresContext, actualSize, actualSizeCSS, calc); michael@0: GetValue(aPresContext, flexSize, flexCSS, flex); michael@0: GetValue(aPresContext, ascentSize, NS_INTRINSICSIZE, ascent); michael@0: michael@0: michael@0: printf("min%s, pref%s, max%s, actual%s, flex=%s, ascent=%s\n\n", michael@0: min, michael@0: pref, michael@0: max, michael@0: calc, michael@0: flex, michael@0: ascent michael@0: ); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: child = child->GetNextBox(); michael@0: count++; michael@0: } michael@0: } else { michael@0: } michael@0: michael@0: mDebugChild = nullptr; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsBoxFrame::SetDebugOnChildList(nsBoxLayoutState& aState, nsIFrame* aChild, bool aDebug) michael@0: { michael@0: nsIFrame* child = GetChildBox(); michael@0: while (child) michael@0: { michael@0: child->SetDebug(aState, aDebug); michael@0: child = child->GetNextBox(); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsBoxFrame::GetFrameSizeWithMargin(nsIFrame* aBox, nsSize& aSize) michael@0: { michael@0: nsRect rect(aBox->GetRect()); michael@0: nsMargin margin(0,0,0,0); michael@0: aBox->GetMargin(margin); michael@0: rect.Inflate(margin); michael@0: aSize.width = rect.width; michael@0: aSize.height = rect.height; michael@0: return NS_OK; michael@0: } michael@0: #endif michael@0: michael@0: // If you make changes to this function, check its counterparts michael@0: // in nsTextBoxFrame and nsXULLabelFrame michael@0: void michael@0: nsBoxFrame::RegUnregAccessKey(bool aDoReg) michael@0: { michael@0: MOZ_ASSERT(mContent); michael@0: michael@0: // find out what type of element this is michael@0: nsIAtom *atom = mContent->Tag(); michael@0: michael@0: // only support accesskeys for the following elements michael@0: if (atom != nsGkAtoms::button && michael@0: atom != nsGkAtoms::toolbarbutton && michael@0: atom != nsGkAtoms::checkbox && michael@0: atom != nsGkAtoms::textbox && michael@0: atom != nsGkAtoms::tab && michael@0: atom != nsGkAtoms::radio) { michael@0: return; michael@0: } michael@0: michael@0: nsAutoString accessKey; michael@0: mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, accessKey); michael@0: michael@0: if (accessKey.IsEmpty()) michael@0: return; michael@0: michael@0: // With a valid PresContext we can get the ESM michael@0: // and register the access key michael@0: EventStateManager* esm = PresContext()->EventStateManager(); michael@0: michael@0: uint32_t key = accessKey.First(); michael@0: if (aDoReg) michael@0: esm->RegisterAccessKey(mContent, key); michael@0: else michael@0: esm->UnregisterAccessKey(mContent, key); michael@0: } michael@0: michael@0: bool michael@0: nsBoxFrame::SupportsOrdinalsInChildren() michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: // Helper less-than-or-equal function, used in CheckBoxOrder() as a michael@0: // template-parameter for the sorting functions. michael@0: bool michael@0: IsBoxOrdinalLEQ(nsIFrame* aFrame1, michael@0: nsIFrame* aFrame2) michael@0: { michael@0: // If we've got a placeholder frame, use its out-of-flow frame's ordinal val. michael@0: nsIFrame* aRealFrame1 = nsPlaceholderFrame::GetRealFrameFor(aFrame1); michael@0: nsIFrame* aRealFrame2 = nsPlaceholderFrame::GetRealFrameFor(aFrame2); michael@0: return aRealFrame1->GetOrdinal() <= aRealFrame2->GetOrdinal(); michael@0: } michael@0: michael@0: void michael@0: nsBoxFrame::CheckBoxOrder() michael@0: { michael@0: if (SupportsOrdinalsInChildren() && michael@0: !nsIFrame::IsFrameListSorted(mFrames)) { michael@0: nsIFrame::SortFrameList(mFrames); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsBoxFrame::LayoutChildAt(nsBoxLayoutState& aState, nsIFrame* aBox, const nsRect& aRect) michael@0: { michael@0: // get the current rect michael@0: nsRect oldRect(aBox->GetRect()); michael@0: aBox->SetBounds(aState, aRect); michael@0: michael@0: bool layout = NS_SUBTREE_DIRTY(aBox); michael@0: michael@0: if (layout || (oldRect.width != aRect.width || oldRect.height != aRect.height)) { michael@0: return aBox->Layout(aState); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsBoxFrame::RelayoutChildAtOrdinal(nsBoxLayoutState& aState, nsIFrame* aChild) michael@0: { michael@0: if (!SupportsOrdinalsInChildren()) michael@0: return NS_OK; michael@0: michael@0: uint32_t ord = aChild->GetOrdinal(); michael@0: michael@0: nsIFrame* child = mFrames.FirstChild(); michael@0: nsIFrame* newPrevSib = nullptr; michael@0: michael@0: while (child) { michael@0: if (ord < child->GetOrdinal()) { michael@0: break; michael@0: } michael@0: michael@0: if (child != aChild) { michael@0: newPrevSib = child; michael@0: } michael@0: michael@0: child = child->GetNextBox(); michael@0: } michael@0: michael@0: if (aChild->GetPrevSibling() == newPrevSib) { michael@0: // This box is not moving. michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Take |aChild| out of its old position in the child list. michael@0: mFrames.RemoveFrame(aChild); michael@0: michael@0: // Insert it after |newPrevSib| or at the start if it's null. michael@0: mFrames.InsertFrame(nullptr, newPrevSib, aChild); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: /** michael@0: * This wrapper class lets us redirect mouse hits from descendant frames michael@0: * of a menu to the menu itself, if they didn't specify 'allowevents'. michael@0: * michael@0: * The wrapper simply turns a hit on a descendant element michael@0: * into a hit on the menu itself, unless there is an element between the target michael@0: * and the menu with the "allowevents" attribute. michael@0: * michael@0: * This is used by nsMenuFrame and nsTreeColFrame. michael@0: * michael@0: * Note that turning a hit on a descendant element into nullptr, so events michael@0: * could fall through to the menu background, might be an appealing simplification michael@0: * but it would mean slightly strange behaviour in some cases, because grabber michael@0: * wrappers can be created for many individual lists and items, so the exact michael@0: * fallthrough behaviour would be complex. E.g. an element with "allowevents" michael@0: * on top of the Content() list could receive the event even if it was covered michael@0: * by a PositionedDescenants() element without "allowevents". It is best to michael@0: * never convert a non-null hit into null. michael@0: */ michael@0: // REVIEW: This is roughly of what nsMenuFrame::GetFrameForPoint used to do. michael@0: // I've made 'allowevents' affect child elements because that seems the only michael@0: // reasonable thing to do. michael@0: class nsDisplayXULEventRedirector : public nsDisplayWrapList { michael@0: public: michael@0: nsDisplayXULEventRedirector(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame, nsDisplayItem* aItem, michael@0: nsIFrame* aTargetFrame) michael@0: : nsDisplayWrapList(aBuilder, aFrame, aItem), mTargetFrame(aTargetFrame) {} michael@0: nsDisplayXULEventRedirector(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame, nsDisplayList* aList, michael@0: nsIFrame* aTargetFrame) michael@0: : nsDisplayWrapList(aBuilder, aFrame, aList), mTargetFrame(aTargetFrame) {} michael@0: virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect, michael@0: HitTestState* aState, michael@0: nsTArray *aOutFrames) MOZ_OVERRIDE; michael@0: NS_DISPLAY_DECL_NAME("XULEventRedirector", TYPE_XUL_EVENT_REDIRECTOR) michael@0: private: michael@0: nsIFrame* mTargetFrame; michael@0: }; michael@0: michael@0: void nsDisplayXULEventRedirector::HitTest(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aRect, HitTestState* aState, nsTArray *aOutFrames) michael@0: { michael@0: nsTArray outFrames; michael@0: mList.HitTest(aBuilder, aRect, aState, &outFrames); michael@0: michael@0: bool topMostAdded = false; michael@0: uint32_t localLength = outFrames.Length(); michael@0: michael@0: for (uint32_t i = 0; i < localLength; i++) { michael@0: michael@0: for (nsIContent* content = outFrames.ElementAt(i)->GetContent(); michael@0: content && content != mTargetFrame->GetContent(); michael@0: content = content->GetParent()) { michael@0: if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::allowevents, michael@0: nsGkAtoms::_true, eCaseMatters)) { michael@0: // Events are allowed on 'frame', so let it go. michael@0: aOutFrames->AppendElement(outFrames.ElementAt(i)); michael@0: topMostAdded = true; michael@0: } michael@0: } michael@0: michael@0: // If there was no hit on the topmost frame or its ancestors, michael@0: // add the target frame itself as the first candidate (see bug 562554). michael@0: if (!topMostAdded) { michael@0: topMostAdded = true; michael@0: aOutFrames->AppendElement(mTargetFrame); michael@0: } michael@0: } michael@0: } michael@0: michael@0: class nsXULEventRedirectorWrapper : public nsDisplayWrapper michael@0: { michael@0: public: michael@0: nsXULEventRedirectorWrapper(nsIFrame* aTargetFrame) michael@0: : mTargetFrame(aTargetFrame) {} michael@0: virtual nsDisplayItem* WrapList(nsDisplayListBuilder* aBuilder, michael@0: nsIFrame* aFrame, michael@0: nsDisplayList* aList) MOZ_OVERRIDE { michael@0: return new (aBuilder) michael@0: nsDisplayXULEventRedirector(aBuilder, aFrame, aList, mTargetFrame); michael@0: } michael@0: virtual nsDisplayItem* WrapItem(nsDisplayListBuilder* aBuilder, michael@0: nsDisplayItem* aItem) MOZ_OVERRIDE { michael@0: return new (aBuilder) michael@0: nsDisplayXULEventRedirector(aBuilder, aItem->Frame(), aItem, michael@0: mTargetFrame); michael@0: } michael@0: private: michael@0: nsIFrame* mTargetFrame; michael@0: }; michael@0: michael@0: void michael@0: nsBoxFrame::WrapListsInRedirector(nsDisplayListBuilder* aBuilder, michael@0: const nsDisplayListSet& aIn, michael@0: const nsDisplayListSet& aOut) michael@0: { michael@0: nsXULEventRedirectorWrapper wrapper(this); michael@0: wrapper.WrapLists(aBuilder, this, aIn, aOut); michael@0: } michael@0: michael@0: bool michael@0: nsBoxFrame::GetEventPoint(WidgetGUIEvent* aEvent, nsPoint &aPoint) { michael@0: nsIntPoint refPoint; michael@0: bool res = GetEventPoint(aEvent, refPoint); michael@0: aPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, refPoint, this); michael@0: return res; michael@0: } michael@0: michael@0: bool michael@0: nsBoxFrame::GetEventPoint(WidgetGUIEvent* aEvent, nsIntPoint &aPoint) { michael@0: NS_ENSURE_TRUE(aEvent, false); michael@0: michael@0: WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent(); michael@0: if (touchEvent) { michael@0: // return false if there is more than one touch on the page, or if michael@0: // we can't find a touch point michael@0: if (touchEvent->touches.Length() != 1) { michael@0: return false; michael@0: } michael@0: michael@0: dom::Touch* touch = touchEvent->touches.SafeElementAt(0); michael@0: if (!touch) { michael@0: return false; michael@0: } michael@0: aPoint = touch->mRefPoint; michael@0: } else { michael@0: aPoint = LayoutDeviceIntPoint::ToUntyped(aEvent->refPoint); michael@0: } michael@0: return true; michael@0: }