diff -r 000000000000 -r 6474c204b198 layout/xul/nsBox.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/layout/xul/nsBox.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,972 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsBoxLayoutState.h" +#include "nsBox.h" +#include "nsBoxFrame.h" +#include "nsPresContext.h" +#include "nsCOMPtr.h" +#include "nsIContent.h" +#include "nsContainerFrame.h" +#include "nsNameSpaceManager.h" +#include "nsGkAtoms.h" +#include "nsFrameManager.h" +#include "nsIDOMNode.h" +#include "nsIDOMMozNamedAttrMap.h" +#include "nsIDOMAttr.h" +#include "nsITheme.h" +#include "nsIServiceManager.h" +#include "nsBoxLayout.h" +#include "FrameLayerBuilder.h" +#include + +using namespace mozilla; + +#ifdef DEBUG_LAYOUT +int32_t gIndent = 0; +#endif + +#ifdef DEBUG_LAYOUT +void +nsBoxAddIndents() +{ + for(int32_t i=0; i < gIndent; i++) + { + printf(" "); + } +} +#endif + +#ifdef DEBUG_LAYOUT +void +nsBox::AppendAttribute(const nsAutoString& aAttribute, const nsAutoString& aValue, nsAutoString& aResult) +{ + aResult.Append(aAttribute); + aResult.AppendLiteral("='"); + aResult.Append(aValue); + aResult.AppendLiteral("' "); +} + +void +nsBox::ListBox(nsAutoString& aResult) +{ + nsAutoString name; + GetBoxName(name); + + char addr[100]; + sprintf(addr, "[@%p] ", static_cast(this)); + + aResult.AppendASCII(addr); + aResult.Append(name); + aResult.AppendLiteral(" "); + + nsIContent* content = GetContent(); + + // add on all the set attributes + if (content) { + nsCOMPtr node(do_QueryInterface(content)); + nsCOMPtr namedMap; + + node->GetAttributes(getter_AddRefs(namedMap)); + uint32_t length; + namedMap->GetLength(&length); + + nsCOMPtr attribute; + for (uint32_t i = 0; i < length; ++i) + { + namedMap->Item(i, getter_AddRefs(attribute)); + attribute->GetName(name); + nsAutoString value; + attribute->GetValue(value); + AppendAttribute(name, value, aResult); + } + } +} + +nsresult +nsBox::DumpBox(FILE* aFile) +{ + nsAutoString s; + ListBox(s); + fprintf(aFile, "%s", NS_LossyConvertUTF16toASCII(s).get()); + return NS_OK; +} + +void +nsBox::PropagateDebug(nsBoxLayoutState& aState) +{ + // propagate debug information + if (mState & NS_STATE_DEBUG_WAS_SET) { + if (mState & NS_STATE_SET_TO_DEBUG) + SetDebug(aState, true); + else + SetDebug(aState, false); + } else if (mState & NS_STATE_IS_ROOT) { + SetDebug(aState, gDebug); + } +} +#endif + +#ifdef DEBUG_LAYOUT +void +nsBox::GetBoxName(nsAutoString& aName) +{ + aName.AssignLiteral("Box"); +} +#endif + +nsresult +nsBox::BeginLayout(nsBoxLayoutState& aState) +{ +#ifdef DEBUG_LAYOUT + + nsBoxAddIndents(); + printf("Layout: "); + DumpBox(stdout); + printf("\n"); + gIndent++; +#endif + + // mark ourselves as dirty so no child under us + // can post an incremental layout. + // XXXldb Is this still needed? + mState |= NS_FRAME_HAS_DIRTY_CHILDREN; + + if (GetStateBits() & NS_FRAME_IS_DIRTY) + { + // If the parent is dirty, all the children are dirty (nsHTMLReflowState + // does this too). + nsIFrame* box; + for (box = GetChildBox(); box; box = box->GetNextBox()) + box->AddStateBits(NS_FRAME_IS_DIRTY); + } + + // Another copy-over from nsHTMLReflowState. + // Since we are in reflow, we don't need to store these properties anymore. + FrameProperties props = Properties(); + props.Delete(UsedBorderProperty()); + props.Delete(UsedPaddingProperty()); + props.Delete(UsedMarginProperty()); + +#ifdef DEBUG_LAYOUT + PropagateDebug(aState); +#endif + + return NS_OK; +} + +NS_IMETHODIMP +nsBox::DoLayout(nsBoxLayoutState& aState) +{ + return NS_OK; +} + +nsresult +nsBox::EndLayout(nsBoxLayoutState& aState) +{ + + #ifdef DEBUG_LAYOUT + --gIndent; + #endif + + return SyncLayout(aState); +} + +bool nsBox::gGotTheme = false; +nsITheme* nsBox::gTheme = nullptr; + +nsBox::nsBox() +{ + MOZ_COUNT_CTOR(nsBox); + //mX = 0; + //mY = 0; + if (!gGotTheme) { + gGotTheme = true; + CallGetService("@mozilla.org/chrome/chrome-native-theme;1", &gTheme); + } +} + +nsBox::~nsBox() +{ + // NOTE: This currently doesn't get called for |nsBoxToBlockAdaptor| + // objects, so don't rely on putting anything here. + MOZ_COUNT_DTOR(nsBox); +} + +/* static */ void +nsBox::Shutdown() +{ + gGotTheme = false; + NS_IF_RELEASE(gTheme); +} + +nsresult +nsBox::RelayoutChildAtOrdinal(nsBoxLayoutState& aState, nsIFrame* aChild) +{ + return NS_OK; +} + +nsresult +nsIFrame::GetClientRect(nsRect& aClientRect) +{ + aClientRect = mRect; + aClientRect.MoveTo(0,0); + + nsMargin borderPadding; + GetBorderAndPadding(borderPadding); + + aClientRect.Deflate(borderPadding); + + if (aClientRect.width < 0) + aClientRect.width = 0; + + if (aClientRect.height < 0) + aClientRect.height = 0; + + // NS_ASSERTION(aClientRect.width >=0 && aClientRect.height >= 0, "Content Size < 0"); + + return NS_OK; +} + +void +nsBox::SetBounds(nsBoxLayoutState& aState, const nsRect& aRect, bool aRemoveOverflowAreas) +{ + NS_BOX_ASSERTION(this, aRect.width >=0 && aRect.height >= 0, "SetBounds Size < 0"); + + nsRect rect(mRect); + + uint32_t flags = 0; + GetLayoutFlags(flags); + + uint32_t stateFlags = aState.LayoutFlags(); + + flags |= stateFlags; + + if ((flags & NS_FRAME_NO_MOVE_FRAME) == NS_FRAME_NO_MOVE_FRAME) + SetSize(aRect.Size()); + else + SetRect(aRect); + + // Nuke the overflow area. The caller is responsible for restoring + // it if necessary. + if (aRemoveOverflowAreas) { + // remove the previously stored overflow area + ClearOverflowRects(); + } + + if (!(flags & NS_FRAME_NO_MOVE_VIEW)) + { + nsContainerFrame::PositionFrameView(this); + if ((rect.x != aRect.x) || (rect.y != aRect.y)) + nsContainerFrame::PositionChildViews(this); + } + + + /* + // only if the origin changed + if ((rect.x != aRect.x) || (rect.y != aRect.y)) { + if (frame->HasView()) { + nsContainerFrame::PositionFrameView(presContext, frame, + frame->GetView()); + } else { + nsContainerFrame::PositionChildViews(presContext, frame); + } + } + */ +} + +void +nsBox::GetLayoutFlags(uint32_t& aFlags) +{ + aFlags = 0; +} + + +nsresult +nsIFrame::GetBorderAndPadding(nsMargin& aBorderAndPadding) +{ + aBorderAndPadding.SizeTo(0, 0, 0, 0); + nsresult rv = GetBorder(aBorderAndPadding); + if (NS_FAILED(rv)) + return rv; + + nsMargin padding; + rv = GetPadding(padding); + if (NS_FAILED(rv)) + return rv; + + aBorderAndPadding += padding; + + return rv; +} + +nsresult +nsBox::GetBorder(nsMargin& aMargin) +{ + aMargin.SizeTo(0,0,0,0); + + const nsStyleDisplay* disp = StyleDisplay(); + if (disp->mAppearance && gTheme) { + // Go to the theme for the border. + nsPresContext *context = PresContext(); + if (gTheme->ThemeSupportsWidget(context, this, disp->mAppearance)) { + nsIntMargin margin(0, 0, 0, 0); + gTheme->GetWidgetBorder(context->DeviceContext(), this, + disp->mAppearance, &margin); + aMargin.top = context->DevPixelsToAppUnits(margin.top); + aMargin.right = context->DevPixelsToAppUnits(margin.right); + aMargin.bottom = context->DevPixelsToAppUnits(margin.bottom); + aMargin.left = context->DevPixelsToAppUnits(margin.left); + return NS_OK; + } + } + + aMargin = StyleBorder()->GetComputedBorder(); + + return NS_OK; +} + +nsresult +nsBox::GetPadding(nsMargin& aMargin) +{ + const nsStyleDisplay *disp = StyleDisplay(); + if (disp->mAppearance && gTheme) { + // Go to the theme for the padding. + nsPresContext *context = PresContext(); + if (gTheme->ThemeSupportsWidget(context, this, disp->mAppearance)) { + nsIntMargin margin(0, 0, 0, 0); + bool useThemePadding; + + useThemePadding = gTheme->GetWidgetPadding(context->DeviceContext(), + this, disp->mAppearance, + &margin); + if (useThemePadding) { + aMargin.top = context->DevPixelsToAppUnits(margin.top); + aMargin.right = context->DevPixelsToAppUnits(margin.right); + aMargin.bottom = context->DevPixelsToAppUnits(margin.bottom); + aMargin.left = context->DevPixelsToAppUnits(margin.left); + return NS_OK; + } + } + } + + aMargin.SizeTo(0,0,0,0); + StylePadding()->GetPadding(aMargin); + + return NS_OK; +} + +nsresult +nsBox::GetMargin(nsMargin& aMargin) +{ + aMargin.SizeTo(0,0,0,0); + StyleMargin()->GetMargin(aMargin); + + return NS_OK; +} + +void +nsBox::SizeNeedsRecalc(nsSize& aSize) +{ + aSize.width = -1; + aSize.height = -1; +} + +void +nsBox::CoordNeedsRecalc(nscoord& aFlex) +{ + aFlex = -1; +} + +bool +nsBox::DoesNeedRecalc(const nsSize& aSize) +{ + return (aSize.width == -1 || aSize.height == -1); +} + +bool +nsBox::DoesNeedRecalc(nscoord aCoord) +{ + return (aCoord == -1); +} + +nsSize +nsBox::GetPrefSize(nsBoxLayoutState& aState) +{ + NS_ASSERTION(aState.GetRenderingContext(), "must have rendering context"); + + nsSize pref(0,0); + DISPLAY_PREF_SIZE(this, pref); + + if (IsCollapsed()) + return pref; + + AddBorderAndPadding(pref); + bool widthSet, heightSet; + nsIFrame::AddCSSPrefSize(this, pref, widthSet, heightSet); + + nsSize minSize = GetMinSize(aState); + nsSize maxSize = GetMaxSize(aState); + return BoundsCheck(minSize, pref, maxSize); +} + +nsSize +nsBox::GetMinSize(nsBoxLayoutState& aState) +{ + NS_ASSERTION(aState.GetRenderingContext(), "must have rendering context"); + + nsSize min(0,0); + DISPLAY_MIN_SIZE(this, min); + + if (IsCollapsed()) + return min; + + AddBorderAndPadding(min); + bool widthSet, heightSet; + nsIFrame::AddCSSMinSize(aState, this, min, widthSet, heightSet); + return min; +} + +nsSize +nsBox::GetMinSizeForScrollArea(nsBoxLayoutState& aBoxLayoutState) +{ + return nsSize(0, 0); +} + +nsSize +nsBox::GetMaxSize(nsBoxLayoutState& aState) +{ + NS_ASSERTION(aState.GetRenderingContext(), "must have rendering context"); + + nsSize maxSize(NS_INTRINSICSIZE, NS_INTRINSICSIZE); + DISPLAY_MAX_SIZE(this, maxSize); + + if (IsCollapsed()) + return maxSize; + + AddBorderAndPadding(maxSize); + bool widthSet, heightSet; + nsIFrame::AddCSSMaxSize(this, maxSize, widthSet, heightSet); + return maxSize; +} + +nscoord +nsBox::GetFlex(nsBoxLayoutState& aState) +{ + nscoord flex = 0; + + nsIFrame::AddCSSFlex(aState, this, flex); + + return flex; +} + +uint32_t +nsIFrame::GetOrdinal() +{ + uint32_t ordinal = StyleXUL()->mBoxOrdinal; + + // When present, attribute value overrides CSS. + nsIContent* content = GetContent(); + if (content && content->IsXUL()) { + nsresult error; + nsAutoString value; + + content->GetAttr(kNameSpaceID_None, nsGkAtoms::ordinal, value); + if (!value.IsEmpty()) { + ordinal = value.ToInteger(&error); + } + } + + return ordinal; +} + +nscoord +nsBox::GetBoxAscent(nsBoxLayoutState& aState) +{ + if (IsCollapsed()) + return 0; + + return GetPrefSize(aState).height; +} + +bool +nsBox::IsCollapsed() +{ + return StyleVisibility()->mVisible == NS_STYLE_VISIBILITY_COLLAPSE; +} + +nsresult +nsIFrame::Layout(nsBoxLayoutState& aState) +{ + NS_ASSERTION(aState.GetRenderingContext(), "must have rendering context"); + + nsBox *box = static_cast(this); + DISPLAY_LAYOUT(box); + + box->BeginLayout(aState); + + box->DoLayout(aState); + + box->EndLayout(aState); + + return NS_OK; +} + +bool +nsBox::DoesClipChildren() +{ + const nsStyleDisplay* display = StyleDisplay(); + NS_ASSERTION((display->mOverflowY == NS_STYLE_OVERFLOW_CLIP) == + (display->mOverflowX == NS_STYLE_OVERFLOW_CLIP), + "If one overflow is clip, the other should be too"); + return display->mOverflowX == NS_STYLE_OVERFLOW_CLIP; +} + +nsresult +nsBox::SyncLayout(nsBoxLayoutState& aState) +{ + /* + if (IsCollapsed()) { + CollapseChild(aState, this, true); + return NS_OK; + } + */ + + + if (GetStateBits() & NS_FRAME_IS_DIRTY) + Redraw(aState); + + RemoveStateBits(NS_FRAME_HAS_DIRTY_CHILDREN | NS_FRAME_IS_DIRTY + | NS_FRAME_FIRST_REFLOW | NS_FRAME_IN_REFLOW); + + nsPresContext* presContext = aState.PresContext(); + + uint32_t flags = 0; + GetLayoutFlags(flags); + + uint32_t stateFlags = aState.LayoutFlags(); + + flags |= stateFlags; + + nsRect visualOverflow; + + if (ComputesOwnOverflowArea()) { + visualOverflow = GetVisualOverflowRect(); + } + else { + nsRect rect(nsPoint(0, 0), GetSize()); + nsOverflowAreas overflowAreas(rect, rect); + if (!DoesClipChildren() && !IsCollapsed()) { + // See if our child frames caused us to overflow after being laid + // out. If so, store the overflow area. This normally can't happen + // in XUL, but it can happen with the CSS 'outline' property and + // possibly with other exotic stuff (e.g. relatively positioned + // frames in HTML inside XUL). + nsLayoutUtils::UnionChildOverflow(this, overflowAreas); + } + + FinishAndStoreOverflow(overflowAreas, GetSize()); + visualOverflow = overflowAreas.VisualOverflow(); + } + + nsView* view = GetView(); + if (view) { + // Make sure the frame's view is properly sized and positioned and has + // things like opacity correct + nsContainerFrame::SyncFrameViewAfterReflow(presContext, this, view, + visualOverflow, flags); + } + + return NS_OK; +} + +nsresult +nsIFrame::Redraw(nsBoxLayoutState& aState) +{ + if (aState.PaintingDisabled()) + return NS_OK; + + // nsStackLayout, at least, expects us to repaint descendants even + // if a damage rect is provided + InvalidateFrameSubtree(); + + return NS_OK; +} + +bool +nsIFrame::AddCSSPrefSize(nsIFrame* aBox, nsSize& aSize, bool &aWidthSet, bool &aHeightSet) +{ + aWidthSet = false; + aHeightSet = false; + + // add in the css min, max, pref + const nsStylePosition* position = aBox->StylePosition(); + + // see if the width or height was specifically set + // XXX Handle eStyleUnit_Enumerated? + // (Handling the eStyleUnit_Enumerated types requires + // GetPrefSize/GetMinSize methods that don't consider + // (min-/max-/)(width/height) properties.) + const nsStyleCoord &width = position->mWidth; + if (width.GetUnit() == eStyleUnit_Coord) { + aSize.width = width.GetCoordValue(); + aWidthSet = true; + } else if (width.IsCalcUnit()) { + if (!width.CalcHasPercent()) { + // pass 0 for percentage basis since we know there are no %s + aSize.width = nsRuleNode::ComputeComputedCalc(width, 0); + if (aSize.width < 0) + aSize.width = 0; + aWidthSet = true; + } + } + + const nsStyleCoord &height = position->mHeight; + if (height.GetUnit() == eStyleUnit_Coord) { + aSize.height = height.GetCoordValue(); + aHeightSet = true; + } else if (height.IsCalcUnit()) { + if (!height.CalcHasPercent()) { + // pass 0 for percentage basis since we know there are no %s + aSize.height = nsRuleNode::ComputeComputedCalc(height, 0); + if (aSize.height < 0) + aSize.height = 0; + aHeightSet = true; + } + } + + nsIContent* content = aBox->GetContent(); + // ignore 'height' and 'width' attributes if the actual element is not XUL + // For example, we might be magic XUL frames whose primary content is an HTML + //