/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 "mozilla/Attributes.h" #include "mozilla/StaticPtr.h" #include "nsIFrame.h" #include "nsBoxLayoutState.h" #include "nsBoxFrame.h" #include "nsDOMAttributeMap.h" #include "nsPresContext.h" #include "nsCOMPtr.h" #include "nsIContent.h" #include "nsContainerFrame.h" #include "nsNameSpaceManager.h" #include "nsGkAtoms.h" #include "nsITheme.h" #include "nsBoxLayout.h" #include "nsLayoutUtils.h" #include "FrameLayerBuilder.h" #include "mozilla/dom/Attr.h" #include "mozilla/dom/Element.h" #include using namespace mozilla; nsresult nsIFrame::BeginXULLayout(nsBoxLayoutState& aState) { // mark ourselves as dirty so no child under us // can post an incremental layout. // XXXldb Is this still needed? AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN); if (HasAnyStateBits(NS_FRAME_IS_DIRTY)) { // If the parent is dirty, all the children are dirty (ReflowInput // does this too). nsIFrame* box; for (box = GetChildXULBox(this); box; box = GetNextXULBox(box)) box->MarkSubtreeDirty(); } // Another copy-over from ReflowInput. // Since we are in reflow, we don't need to store these properties anymore. RemoveProperty(UsedBorderProperty()); RemoveProperty(UsedPaddingProperty()); RemoveProperty(UsedMarginProperty()); return NS_OK; } nsresult nsIFrame::EndXULLayout(nsBoxLayoutState& aState) { return SyncXULLayout(aState); } nsresult nsIFrame::GetXULClientRect(nsRect& aClientRect) { aClientRect = mRect; aClientRect.MoveTo(0, 0); nsMargin borderPadding; GetXULBorderAndPadding(borderPadding); aClientRect.Deflate(borderPadding); if (aClientRect.width < 0) aClientRect.width = 0; if (aClientRect.height < 0) aClientRect.height = 0; return NS_OK; } void nsIFrame::SetXULBounds(nsBoxLayoutState& aState, const nsRect& aRect, bool aRemoveOverflowAreas) { nsRect rect(mRect); ReflowChildFlags flags = GetXULLayoutFlags() | aState.LayoutFlags(); if ((flags & ReflowChildFlags::NoMoveFrame) == ReflowChildFlags::NoMoveFrame) { 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 & ReflowChildFlags::NoMoveView)) { nsContainerFrame::PositionFrameView(this); if ((rect.x != aRect.x) || (rect.y != aRect.y)) nsContainerFrame::PositionChildViews(this); } } nsresult nsIFrame::GetXULBorderAndPadding(nsMargin& aBorderAndPadding) { aBorderAndPadding.SizeTo(0, 0, 0, 0); nsresult rv = GetXULBorder(aBorderAndPadding); if (NS_FAILED(rv)) return rv; nsMargin padding; rv = GetXULPadding(padding); if (NS_FAILED(rv)) return rv; aBorderAndPadding += padding; return rv; } nsresult nsIFrame::GetXULBorder(nsMargin& aBorder) { aBorder.SizeTo(0, 0, 0, 0); StyleAppearance appearance = StyleDisplay()->EffectiveAppearance(); if (appearance != StyleAppearance::None) { // Go to the theme for the border. nsPresContext* pc = PresContext(); nsITheme* theme = pc->Theme(); if (theme->ThemeSupportsWidget(pc, this, appearance)) { LayoutDeviceIntMargin margin = theme->GetWidgetBorder(pc->DeviceContext(), this, appearance); aBorder = LayoutDevicePixel::ToAppUnits(margin, pc->AppUnitsPerDevPixel()); return NS_OK; } } aBorder = StyleBorder()->GetComputedBorder(); return NS_OK; } nsresult nsIFrame::GetXULPadding(nsMargin& aBorderAndPadding) { StyleAppearance appearance = StyleDisplay()->EffectiveAppearance(); if (appearance != StyleAppearance::None) { // Go to the theme for the padding. nsPresContext* pc = PresContext(); nsITheme* theme = pc->Theme(); if (theme->ThemeSupportsWidget(pc, this, appearance)) { LayoutDeviceIntMargin padding; bool useThemePadding = theme->GetWidgetPadding(pc->DeviceContext(), this, appearance, &padding); if (useThemePadding) { aBorderAndPadding = LayoutDevicePixel::ToAppUnits(padding, pc->AppUnitsPerDevPixel()); return NS_OK; } } } aBorderAndPadding.SizeTo(0, 0, 0, 0); StylePadding()->GetPadding(aBorderAndPadding); return NS_OK; } nsresult nsIFrame::GetXULMargin(nsMargin& aMargin) { aMargin.SizeTo(0, 0, 0, 0); StyleMargin()->GetMargin(aMargin); return NS_OK; } void nsIFrame::XULSizeNeedsRecalc(nsSize& aSize) { aSize.width = -1; aSize.height = -1; } void nsIFrame::XULCoordNeedsRecalc(nscoord& aCoord) { aCoord = -1; } bool nsIFrame::XULNeedsRecalc(const nsSize& aSize) { return (aSize.width == -1 || aSize.height == -1); } bool nsIFrame::XULNeedsRecalc(nscoord aCoord) { return (aCoord == -1); } nsSize nsIFrame::GetUncachedXULPrefSize(nsBoxLayoutState& aBoxLayoutState) { NS_ASSERTION(aBoxLayoutState.GetRenderingContext(), "must have rendering context"); nsSize pref(0, 0); DISPLAY_PREF_SIZE(this, pref); if (IsXULCollapsed()) { return pref; } AddXULBorderAndPadding(pref); bool widthSet, heightSet; nsIFrame::AddXULPrefSize(this, pref, widthSet, heightSet); nsSize minSize = GetXULMinSize(aBoxLayoutState); nsSize maxSize = GetXULMaxSize(aBoxLayoutState); return XULBoundsCheck(minSize, pref, maxSize); } nsSize nsIFrame::GetUncachedXULMinSize(nsBoxLayoutState& aBoxLayoutState) { NS_ASSERTION(aBoxLayoutState.GetRenderingContext(), "must have rendering context"); nsSize min(0, 0); DISPLAY_MIN_SIZE(this, min); if (IsXULCollapsed()) { return min; } AddXULBorderAndPadding(min); bool widthSet, heightSet; nsIFrame::AddXULMinSize(this, min, widthSet, heightSet); return min; } nsSize nsIFrame::GetXULMinSizeForScrollArea(nsBoxLayoutState& aBoxLayoutState) { return nsSize(0, 0); } nsSize nsIFrame::GetUncachedXULMaxSize(nsBoxLayoutState& aBoxLayoutState) { NS_ASSERTION(aBoxLayoutState.GetRenderingContext(), "must have rendering context"); nsSize maxSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); DISPLAY_MAX_SIZE(this, maxSize); if (IsXULCollapsed()) { return maxSize; } AddXULBorderAndPadding(maxSize); bool widthSet, heightSet; nsIFrame::AddXULMaxSize(this, maxSize, widthSet, heightSet); return maxSize; } bool nsIFrame::IsXULCollapsed() { return StyleVisibility()->mVisible == StyleVisibility::Collapse; } nsresult nsIFrame::XULLayout(nsBoxLayoutState& aState) { NS_ASSERTION(aState.GetRenderingContext(), "must have rendering context"); nsIFrame* box = static_cast(this); DISPLAY_LAYOUT(box); box->BeginXULLayout(aState); box->DoXULLayout(aState); box->EndXULLayout(aState); return NS_OK; } bool nsIFrame::DoesClipChildrenInBothAxes() { const nsStyleDisplay* display = StyleDisplay(); return display->mOverflowX == StyleOverflow::Clip && display->mOverflowY == StyleOverflow::Clip; } nsresult nsIFrame::SyncXULLayout(nsBoxLayoutState& aBoxLayoutState) { /* if (IsXULCollapsed()) { CollapseChild(aBoxLayoutState, this, true); return NS_OK; } */ if (HasAnyStateBits(NS_FRAME_IS_DIRTY)) { XULRedraw(aBoxLayoutState); } RemoveStateBits(NS_FRAME_HAS_DIRTY_CHILDREN | NS_FRAME_IS_DIRTY | NS_FRAME_FIRST_REFLOW | NS_FRAME_IN_REFLOW); nsPresContext* presContext = aBoxLayoutState.PresContext(); ReflowChildFlags flags = GetXULLayoutFlags() | aBoxLayoutState.LayoutFlags(); nsRect inkOverflow; if (XULComputesOwnOverflowArea()) { inkOverflow = InkOverflowRect(); } else { nsRect rect(nsPoint(0, 0), GetSize()); OverflowAreas overflowAreas(rect, rect); if (!DoesClipChildrenInBothAxes() && !IsXULCollapsed()) { // 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()); inkOverflow = overflowAreas.InkOverflow(); } 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, inkOverflow, flags); } return NS_OK; } nsresult nsIFrame::XULRedraw(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::AddXULPrefSize(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 // GetXULPrefSize/GetXULMinSize methods that don't consider // (min-/max-/)(width/height) properties.) const auto& width = position->mWidth; if (width.ConvertsToLength()) { aSize.width = std::max(0, width.ToLength()); aWidthSet = true; } const auto& height = position->mHeight; if (height.ConvertsToLength()) { aSize.height = std::max(0, height.ToLength()); 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 //