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 "nsMeterFrame.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/HTMLMeterElement.h" michael@0: #include "nsContentList.h" michael@0: #include "nsStyleSet.h" michael@0: #include "nsThemeConstants.h" michael@0: #include michael@0: michael@0: using mozilla::dom::Element; michael@0: using mozilla::dom::HTMLMeterElement; michael@0: michael@0: nsIFrame* michael@0: NS_NewMeterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) michael@0: { michael@0: return new (aPresShell) nsMeterFrame(aContext); michael@0: } michael@0: michael@0: NS_IMPL_FRAMEARENA_HELPERS(nsMeterFrame) michael@0: michael@0: nsMeterFrame::nsMeterFrame(nsStyleContext* aContext) michael@0: : nsContainerFrame(aContext) michael@0: , mBarDiv(nullptr) michael@0: { michael@0: } michael@0: michael@0: nsMeterFrame::~nsMeterFrame() michael@0: { michael@0: } michael@0: michael@0: void michael@0: nsMeterFrame::DestroyFrom(nsIFrame* aDestructRoot) michael@0: { michael@0: NS_ASSERTION(!GetPrevContinuation(), michael@0: "nsMeterFrame 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: nsMeterFrame::CreateAnonymousContent(nsTArray& aElements) michael@0: { michael@0: // Get the NodeInfoManager and tag necessary to create the meter bar div. michael@0: nsCOMPtr doc = mContent->GetDocument(); michael@0: michael@0: // Create the div. michael@0: mBarDiv = doc->CreateHTMLElement(nsGkAtoms::div); michael@0: michael@0: // Associate ::-moz-meter-bar pseudo-element to the anonymous child. michael@0: nsCSSPseudoElements::Type pseudoType = nsCSSPseudoElements::ePseudo_mozMeterBar; 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: nsMeterFrame::AppendAnonymousContentTo(nsBaseContentList& aElements, michael@0: uint32_t aFilter) michael@0: { michael@0: aElements.MaybeAppendElement(mBarDiv); michael@0: } michael@0: michael@0: NS_QUERYFRAME_HEAD(nsMeterFrame) michael@0: NS_QUERYFRAME_ENTRY(nsMeterFrame) michael@0: NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator) michael@0: NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) michael@0: michael@0: michael@0: nsresult nsMeterFrame::Reflow(nsPresContext* aPresContext, michael@0: nsHTMLReflowMetrics& aDesiredSize, michael@0: const nsHTMLReflowState& aReflowState, michael@0: nsReflowStatus& aStatus) michael@0: { michael@0: DO_GLOBAL_REFLOW_COUNT("nsMeterFrame"); michael@0: DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); michael@0: michael@0: NS_ASSERTION(mBarDiv, "Meter bar div must exist!"); michael@0: NS_ASSERTION(!GetPrevContinuation(), michael@0: "nsMeterFrame 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 meter 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: nsMeterFrame::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: // NOTE: Introduce a new function getPosition in the content part ? michael@0: HTMLMeterElement* meterElement = static_cast(mContent); michael@0: michael@0: double max = meterElement->Max(); michael@0: double min = meterElement->Min(); michael@0: double value = meterElement->Value(); michael@0: michael@0: double position = max - min; michael@0: position = position != 0 ? (value - min) / position : 1; michael@0: michael@0: size = NSToCoordRound(size * position); 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 position is *always* constrained. 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: michael@0: xoffset += reflowState.ComputedPhysicalMargin().left; michael@0: yoffset += reflowState.ComputedPhysicalMargin().top; michael@0: michael@0: nsHTMLReflowMetrics barDesiredSize(reflowState); 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: nsMeterFrame::AttributeChanged(int32_t aNameSpaceID, michael@0: nsIAtom* aAttribute, michael@0: int32_t aModType) michael@0: { michael@0: NS_ASSERTION(mBarDiv, "Meter bar div must exist!"); michael@0: michael@0: if (aNameSpaceID == kNameSpaceID_None && michael@0: (aAttribute == nsGkAtoms::value || michael@0: aAttribute == nsGkAtoms::max || michael@0: aAttribute == nsGkAtoms::min )) { michael@0: nsIFrame* barFrame = mBarDiv->GetPrimaryFrame(); michael@0: NS_ASSERTION(barFrame, "The meter frame should have a child with a frame!"); michael@0: PresContext()->PresShell()->FrameNeedsReflow(barFrame, michael@0: nsIPresShell::eResize, michael@0: NS_FRAME_IS_DIRTY); michael@0: InvalidateFrame(); michael@0: } michael@0: michael@0: return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, michael@0: aModType); michael@0: } michael@0: michael@0: nsSize michael@0: nsMeterFrame::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: nsRefPtr fontMet; michael@0: NS_ENSURE_SUCCESS(nsLayoutUtils::GetFontMetricsForFrame(this, michael@0: getter_AddRefs(fontMet)), michael@0: nsSize(0, 0)); michael@0: michael@0: nsSize autoSize; michael@0: autoSize.height = autoSize.width = fontMet->Font().size; // 1em michael@0: michael@0: if (StyleDisplay()->mOrient == NS_STYLE_ORIENT_VERTICAL) { michael@0: autoSize.height *= 5; // 5em michael@0: } else { michael@0: autoSize.width *= 5; // 5em michael@0: } michael@0: michael@0: return autoSize; michael@0: } michael@0: michael@0: nscoord michael@0: nsMeterFrame::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 *= 5; // 5em michael@0: } michael@0: michael@0: return minWidth; michael@0: } michael@0: michael@0: nscoord michael@0: nsMeterFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) michael@0: { michael@0: return GetMinWidth(aRenderingContext); michael@0: } michael@0: michael@0: bool michael@0: nsMeterFrame::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_METERBAR && michael@0: mBarDiv->GetPrimaryFrame()->StyleDisplay()->mAppearance == NS_THEME_METERBAR_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: nsMeterFrame::GetPseudoElement(nsCSSPseudoElements::Type aType) michael@0: { michael@0: if (aType == nsCSSPseudoElements::ePseudo_mozMeterBar) { michael@0: return mBarDiv; michael@0: } michael@0: michael@0: return nsContainerFrame::GetPseudoElement(aType); michael@0: }