michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 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: #include "nsProgressFrame.h" michael@0: michael@0: #include "nsIContent.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsNameSpaceManager.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsNodeInfoManager.h" michael@0: #include "nsINodeInfo.h" michael@0: #include "nsContentCreatorFunctions.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsFormControlFrame.h" michael@0: #include "nsFontMetrics.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "mozilla/dom/HTMLProgressElement.h" michael@0: #include "nsContentList.h" michael@0: #include "nsStyleSet.h" michael@0: #include "nsThemeConstants.h" michael@0: #include michael@0: michael@0: using namespace mozilla::dom; michael@0: michael@0: nsIFrame* michael@0: NS_NewProgressFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) michael@0: { michael@0: return new (aPresShell) nsProgressFrame(aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsProgressFrame) michael@0: michael@0: nsProgressFrame::nsProgressFrame(nsStyleContext* aContext) michael@0: : nsContainerFrame(aContext) michael@0: , mBarDiv(nullptr) michael@0: { michael@0: } michael@0: michael@0: nsProgressFrame::~nsProgressFrame() michael@0: { michael@0: } michael@0: michael@0: void michael@0: nsProgressFrame::DestroyFrom(nsIFrame* aDestructRoot) michael@0: { michael@0: NS_ASSERTION(!GetPrevContinuation(), michael@0: "nsProgressFrame should not have continuations; if it does we " michael@0: "need to call RegUnregAccessKey only for the first."); michael@0: nsFormControlFrame::RegUnRegAccessKey(static_cast(this), false); michael@0: nsContentUtils::DestroyAnonymousContent(&mBarDiv); michael@0: nsContainerFrame::DestroyFrom(aDestructRoot); michael@0: } michael@0: michael@0: nsresult michael@0: nsProgressFrame::CreateAnonymousContent(nsTArray& aElements) michael@0: { michael@0: // Create the progress bar div. michael@0: nsCOMPtr doc = mContent->GetDocument(); michael@0: mBarDiv = doc->CreateHTMLElement(nsGkAtoms::div); michael@0: michael@0: // Associate ::-moz-progress-bar pseudo-element to the anonymous child. michael@0: nsCSSPseudoElements::Type pseudoType = nsCSSPseudoElements::ePseudo_mozProgressBar; michael@0: nsRefPtr newStyleContext = PresContext()->StyleSet()-> michael@0: ResolvePseudoElementStyle(mContent->AsElement(), pseudoType, michael@0: StyleContext(), mBarDiv->AsElement()); michael@0: michael@0: if (!aElements.AppendElement(ContentInfo(mBarDiv, newStyleContext))) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsProgressFrame::AppendAnonymousContentTo(nsBaseContentList& aElements, michael@0: uint32_t aFilter) michael@0: { michael@0: aElements.MaybeAppendElement(mBarDiv); michael@0: } michael@0: michael@0: NS_QUERYFRAME_HEAD(nsProgressFrame) michael@0: NS_QUERYFRAME_ENTRY(nsProgressFrame) michael@0: NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator) michael@0: NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) michael@0: michael@0: michael@0: void michael@0: nsProgressFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, michael@0: const nsRect& aDirtyRect, michael@0: const nsDisplayListSet& aLists) michael@0: { michael@0: BuildDisplayListForInline(aBuilder, aDirtyRect, aLists); michael@0: } michael@0: michael@0: nsresult nsProgressFrame::Reflow(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus) michael@0: { michael@0: DO_GLOBAL_REFLOW_COUNT("nsProgressFrame"); michael@0: DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); michael@0: michael@0: NS_ASSERTION(mBarDiv, "Progress bar div must exist!"); michael@0: NS_ASSERTION(!GetPrevContinuation(), michael@0: "nsProgressFrame should not have continuations; if it does we " michael@0: "need to call RegUnregAccessKey only for the first."); michael@0: michael@0: if (mState & NS_FRAME_FIRST_REFLOW) { michael@0: nsFormControlFrame::RegUnRegAccessKey(this, true); michael@0: } michael@0: michael@0: nsIFrame* barFrame = mBarDiv->GetPrimaryFrame(); michael@0: NS_ASSERTION(barFrame, "The progress frame should have a child with a frame!"); michael@0: michael@0: ReflowBarFrame(barFrame, aPresContext, aReflowState, aStatus); michael@0: michael@0: aDesiredSize.Width() = aReflowState.ComputedWidth() + michael@0: aReflowState.ComputedPhysicalBorderPadding().LeftRight(); michael@0: aDesiredSize.Height() = aReflowState.ComputedHeight() + michael@0: aReflowState.ComputedPhysicalBorderPadding().TopBottom(); michael@0: michael@0: aDesiredSize.SetOverflowAreasToDesiredBounds(); michael@0: ConsiderChildOverflow(aDesiredSize.mOverflowAreas, barFrame); michael@0: FinishAndStoreOverflow(&aDesiredSize); michael@0: michael@0: aStatus = NS_FRAME_COMPLETE; michael@0: michael@0: NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsProgressFrame::ReflowBarFrame(nsIFrame* aBarFrame, michael@0: nsPresContext* aPresContext, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus) michael@0: { michael@0: bool vertical = StyleDisplay()->mOrient == NS_STYLE_ORIENT_VERTICAL; michael@0: nsHTMLReflowState reflowState(aPresContext, aReflowState, aBarFrame, michael@0: nsSize(aReflowState.ComputedWidth(), michael@0: NS_UNCONSTRAINEDSIZE)); michael@0: nscoord size = vertical ? aReflowState.ComputedHeight() michael@0: : aReflowState.ComputedWidth(); michael@0: nscoord xoffset = aReflowState.ComputedPhysicalBorderPadding().left; michael@0: nscoord yoffset = aReflowState.ComputedPhysicalBorderPadding().top; michael@0: michael@0: double position = static_cast(mContent)->Position(); michael@0: michael@0: // Force the bar's size to match the current progress. michael@0: // When indeterminate, the progress' size will be 100%. michael@0: if (position >= 0.0) { michael@0: size *= position; michael@0: } michael@0: michael@0: if (!vertical && StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { michael@0: xoffset += aReflowState.ComputedWidth() - size; michael@0: } michael@0: michael@0: // The bar size is fixed in these cases: michael@0: // - the progress position is determined: the bar size is fixed according michael@0: // to it's value. michael@0: // - the progress position is indeterminate and the bar appearance should be michael@0: // shown as native: the bar size is forced to 100%. michael@0: // Otherwise (when the progress is indeterminate and the bar appearance isn't michael@0: // native), the bar size isn't fixed and can be set by the author. michael@0: if (position != -1 || ShouldUseNativeStyle()) { michael@0: if (vertical) { michael@0: // We want the bar to begin at the bottom. michael@0: yoffset += aReflowState.ComputedHeight() - size; michael@0: michael@0: size -= reflowState.ComputedPhysicalMargin().TopBottom() + michael@0: reflowState.ComputedPhysicalBorderPadding().TopBottom(); michael@0: size = std::max(size, 0); michael@0: reflowState.SetComputedHeight(size); michael@0: } else { michael@0: size -= reflowState.ComputedPhysicalMargin().LeftRight() + michael@0: reflowState.ComputedPhysicalBorderPadding().LeftRight(); michael@0: size = std::max(size, 0); michael@0: reflowState.SetComputedWidth(size); michael@0: } michael@0: } else if (vertical) { michael@0: // For vertical progress bars, we need to position the bar specificly when michael@0: // the width isn't constrained (position == -1 and !ShouldUseNativeStyle()) michael@0: // because aReflowState.ComputedHeight() - size == 0. michael@0: yoffset += aReflowState.ComputedHeight() - reflowState.ComputedHeight(); michael@0: } michael@0: michael@0: xoffset += reflowState.ComputedPhysicalMargin().left; michael@0: yoffset += reflowState.ComputedPhysicalMargin().top; michael@0: michael@0: nsHTMLReflowMetrics barDesiredSize(aReflowState); michael@0: ReflowChild(aBarFrame, aPresContext, barDesiredSize, reflowState, xoffset, michael@0: yoffset, 0, aStatus); michael@0: FinishReflowChild(aBarFrame, aPresContext, barDesiredSize, &reflowState, michael@0: xoffset, yoffset, 0); michael@0: } michael@0: michael@0: nsresult michael@0: nsProgressFrame::AttributeChanged(int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: NS_ASSERTION(mBarDiv, "Progress bar div must exist!"); michael@0: michael@0: if (aNameSpaceID == kNameSpaceID_None && michael@0: (aAttribute == nsGkAtoms::value || aAttribute == nsGkAtoms::max)) { michael@0: nsIFrame* barFrame = mBarDiv->GetPrimaryFrame(); michael@0: NS_ASSERTION(barFrame, "The progress frame should have a child with a frame!"); michael@0: PresContext()->PresShell()->FrameNeedsReflow(barFrame, nsIPresShell::eResize, michael@0: NS_FRAME_IS_DIRTY); michael@0: InvalidateFrame(); michael@0: } michael@0: michael@0: return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType); michael@0: } michael@0: michael@0: nsSize michael@0: nsProgressFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, michael@0: nsSize aCBSize, nscoord aAvailableWidth, michael@0: nsSize aMargin, nsSize aBorder, michael@0: nsSize aPadding, bool aShrinkWrap) michael@0: { michael@0: nsSize autoSize; michael@0: autoSize.height = autoSize.width = michael@0: NSToCoordRound(StyleFont()->mFont.size * michael@0: nsLayoutUtils::FontSizeInflationFor(this)); // 1em michael@0: michael@0: if (StyleDisplay()->mOrient == NS_STYLE_ORIENT_VERTICAL) { michael@0: autoSize.height *= 10; // 10em michael@0: } else { michael@0: autoSize.width *= 10; // 10em michael@0: } michael@0: michael@0: return autoSize; michael@0: } michael@0: michael@0: nscoord michael@0: nsProgressFrame::GetMinWidth(nsRenderingContext *aRenderingContext) michael@0: { michael@0: nsRefPtr fontMet; michael@0: NS_ENSURE_SUCCESS( michael@0: nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet)), 0); michael@0: michael@0: nscoord minWidth = fontMet->Font().size; // 1em michael@0: michael@0: if (StyleDisplay()->mOrient == NS_STYLE_ORIENT_AUTO || michael@0: StyleDisplay()->mOrient == NS_STYLE_ORIENT_HORIZONTAL) { michael@0: // The orientation is horizontal michael@0: minWidth *= 10; // 10em michael@0: } michael@0: michael@0: return minWidth; michael@0: } michael@0: michael@0: nscoord michael@0: nsProgressFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) michael@0: { michael@0: return GetMinWidth(aRenderingContext); michael@0: } michael@0: michael@0: bool michael@0: nsProgressFrame::ShouldUseNativeStyle() const michael@0: { michael@0: // Use the native style if these conditions are satisfied: michael@0: // - both frames use the native appearance; michael@0: // - neither frame has author specified rules setting the border or the michael@0: // background. michael@0: return StyleDisplay()->mAppearance == NS_THEME_PROGRESSBAR && michael@0: mBarDiv->GetPrimaryFrame()->StyleDisplay()->mAppearance == NS_THEME_PROGRESSBAR_CHUNK && michael@0: !PresContext()->HasAuthorSpecifiedRules(const_cast(this), michael@0: NS_AUTHOR_SPECIFIED_BORDER | NS_AUTHOR_SPECIFIED_BACKGROUND) && michael@0: !PresContext()->HasAuthorSpecifiedRules(mBarDiv->GetPrimaryFrame(), michael@0: NS_AUTHOR_SPECIFIED_BORDER | NS_AUTHOR_SPECIFIED_BACKGROUND); michael@0: } michael@0: michael@0: Element* michael@0: nsProgressFrame::GetPseudoElement(nsCSSPseudoElements::Type aType) michael@0: { michael@0: if (aType == nsCSSPseudoElements::ePseudo_mozProgressBar) { michael@0: return mBarDiv; michael@0: } michael@0: michael@0: return nsContainerFrame::GetPseudoElement(aType); michael@0: }