1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/forms/nsProgressFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,289 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "nsProgressFrame.h" 1.10 + 1.11 +#include "nsIContent.h" 1.12 +#include "nsPresContext.h" 1.13 +#include "nsGkAtoms.h" 1.14 +#include "nsNameSpaceManager.h" 1.15 +#include "nsIDocument.h" 1.16 +#include "nsIPresShell.h" 1.17 +#include "nsNodeInfoManager.h" 1.18 +#include "nsINodeInfo.h" 1.19 +#include "nsContentCreatorFunctions.h" 1.20 +#include "nsContentUtils.h" 1.21 +#include "nsFormControlFrame.h" 1.22 +#include "nsFontMetrics.h" 1.23 +#include "mozilla/dom/Element.h" 1.24 +#include "mozilla/dom/HTMLProgressElement.h" 1.25 +#include "nsContentList.h" 1.26 +#include "nsStyleSet.h" 1.27 +#include "nsThemeConstants.h" 1.28 +#include <algorithm> 1.29 + 1.30 +using namespace mozilla::dom; 1.31 + 1.32 +nsIFrame* 1.33 +NS_NewProgressFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) 1.34 +{ 1.35 + return new (aPresShell) nsProgressFrame(aContext); 1.36 +} 1.37 + 1.38 +NS_IMPL_FRAMEARENA_HELPERS(nsProgressFrame) 1.39 + 1.40 +nsProgressFrame::nsProgressFrame(nsStyleContext* aContext) 1.41 + : nsContainerFrame(aContext) 1.42 + , mBarDiv(nullptr) 1.43 +{ 1.44 +} 1.45 + 1.46 +nsProgressFrame::~nsProgressFrame() 1.47 +{ 1.48 +} 1.49 + 1.50 +void 1.51 +nsProgressFrame::DestroyFrom(nsIFrame* aDestructRoot) 1.52 +{ 1.53 + NS_ASSERTION(!GetPrevContinuation(), 1.54 + "nsProgressFrame should not have continuations; if it does we " 1.55 + "need to call RegUnregAccessKey only for the first."); 1.56 + nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), false); 1.57 + nsContentUtils::DestroyAnonymousContent(&mBarDiv); 1.58 + nsContainerFrame::DestroyFrom(aDestructRoot); 1.59 +} 1.60 + 1.61 +nsresult 1.62 +nsProgressFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) 1.63 +{ 1.64 + // Create the progress bar div. 1.65 + nsCOMPtr<nsIDocument> doc = mContent->GetDocument(); 1.66 + mBarDiv = doc->CreateHTMLElement(nsGkAtoms::div); 1.67 + 1.68 + // Associate ::-moz-progress-bar pseudo-element to the anonymous child. 1.69 + nsCSSPseudoElements::Type pseudoType = nsCSSPseudoElements::ePseudo_mozProgressBar; 1.70 + nsRefPtr<nsStyleContext> newStyleContext = PresContext()->StyleSet()-> 1.71 + ResolvePseudoElementStyle(mContent->AsElement(), pseudoType, 1.72 + StyleContext(), mBarDiv->AsElement()); 1.73 + 1.74 + if (!aElements.AppendElement(ContentInfo(mBarDiv, newStyleContext))) { 1.75 + return NS_ERROR_OUT_OF_MEMORY; 1.76 + } 1.77 + 1.78 + return NS_OK; 1.79 +} 1.80 + 1.81 +void 1.82 +nsProgressFrame::AppendAnonymousContentTo(nsBaseContentList& aElements, 1.83 + uint32_t aFilter) 1.84 +{ 1.85 + aElements.MaybeAppendElement(mBarDiv); 1.86 +} 1.87 + 1.88 +NS_QUERYFRAME_HEAD(nsProgressFrame) 1.89 + NS_QUERYFRAME_ENTRY(nsProgressFrame) 1.90 + NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator) 1.91 +NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) 1.92 + 1.93 + 1.94 +void 1.95 +nsProgressFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.96 + const nsRect& aDirtyRect, 1.97 + const nsDisplayListSet& aLists) 1.98 +{ 1.99 + BuildDisplayListForInline(aBuilder, aDirtyRect, aLists); 1.100 +} 1.101 + 1.102 +nsresult nsProgressFrame::Reflow(nsPresContext* aPresContext, 1.103 + nsHTMLReflowMetrics& aDesiredSize, 1.104 + const nsHTMLReflowState& aReflowState, 1.105 + nsReflowStatus& aStatus) 1.106 +{ 1.107 + DO_GLOBAL_REFLOW_COUNT("nsProgressFrame"); 1.108 + DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); 1.109 + 1.110 + NS_ASSERTION(mBarDiv, "Progress bar div must exist!"); 1.111 + NS_ASSERTION(!GetPrevContinuation(), 1.112 + "nsProgressFrame should not have continuations; if it does we " 1.113 + "need to call RegUnregAccessKey only for the first."); 1.114 + 1.115 + if (mState & NS_FRAME_FIRST_REFLOW) { 1.116 + nsFormControlFrame::RegUnRegAccessKey(this, true); 1.117 + } 1.118 + 1.119 + nsIFrame* barFrame = mBarDiv->GetPrimaryFrame(); 1.120 + NS_ASSERTION(barFrame, "The progress frame should have a child with a frame!"); 1.121 + 1.122 + ReflowBarFrame(barFrame, aPresContext, aReflowState, aStatus); 1.123 + 1.124 + aDesiredSize.Width() = aReflowState.ComputedWidth() + 1.125 + aReflowState.ComputedPhysicalBorderPadding().LeftRight(); 1.126 + aDesiredSize.Height() = aReflowState.ComputedHeight() + 1.127 + aReflowState.ComputedPhysicalBorderPadding().TopBottom(); 1.128 + 1.129 + aDesiredSize.SetOverflowAreasToDesiredBounds(); 1.130 + ConsiderChildOverflow(aDesiredSize.mOverflowAreas, barFrame); 1.131 + FinishAndStoreOverflow(&aDesiredSize); 1.132 + 1.133 + aStatus = NS_FRAME_COMPLETE; 1.134 + 1.135 + NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); 1.136 + 1.137 + return NS_OK; 1.138 +} 1.139 + 1.140 +void 1.141 +nsProgressFrame::ReflowBarFrame(nsIFrame* aBarFrame, 1.142 + nsPresContext* aPresContext, 1.143 + const nsHTMLReflowState& aReflowState, 1.144 + nsReflowStatus& aStatus) 1.145 +{ 1.146 + bool vertical = StyleDisplay()->mOrient == NS_STYLE_ORIENT_VERTICAL; 1.147 + nsHTMLReflowState reflowState(aPresContext, aReflowState, aBarFrame, 1.148 + nsSize(aReflowState.ComputedWidth(), 1.149 + NS_UNCONSTRAINEDSIZE)); 1.150 + nscoord size = vertical ? aReflowState.ComputedHeight() 1.151 + : aReflowState.ComputedWidth(); 1.152 + nscoord xoffset = aReflowState.ComputedPhysicalBorderPadding().left; 1.153 + nscoord yoffset = aReflowState.ComputedPhysicalBorderPadding().top; 1.154 + 1.155 + double position = static_cast<HTMLProgressElement*>(mContent)->Position(); 1.156 + 1.157 + // Force the bar's size to match the current progress. 1.158 + // When indeterminate, the progress' size will be 100%. 1.159 + if (position >= 0.0) { 1.160 + size *= position; 1.161 + } 1.162 + 1.163 + if (!vertical && StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { 1.164 + xoffset += aReflowState.ComputedWidth() - size; 1.165 + } 1.166 + 1.167 + // The bar size is fixed in these cases: 1.168 + // - the progress position is determined: the bar size is fixed according 1.169 + // to it's value. 1.170 + // - the progress position is indeterminate and the bar appearance should be 1.171 + // shown as native: the bar size is forced to 100%. 1.172 + // Otherwise (when the progress is indeterminate and the bar appearance isn't 1.173 + // native), the bar size isn't fixed and can be set by the author. 1.174 + if (position != -1 || ShouldUseNativeStyle()) { 1.175 + if (vertical) { 1.176 + // We want the bar to begin at the bottom. 1.177 + yoffset += aReflowState.ComputedHeight() - size; 1.178 + 1.179 + size -= reflowState.ComputedPhysicalMargin().TopBottom() + 1.180 + reflowState.ComputedPhysicalBorderPadding().TopBottom(); 1.181 + size = std::max(size, 0); 1.182 + reflowState.SetComputedHeight(size); 1.183 + } else { 1.184 + size -= reflowState.ComputedPhysicalMargin().LeftRight() + 1.185 + reflowState.ComputedPhysicalBorderPadding().LeftRight(); 1.186 + size = std::max(size, 0); 1.187 + reflowState.SetComputedWidth(size); 1.188 + } 1.189 + } else if (vertical) { 1.190 + // For vertical progress bars, we need to position the bar specificly when 1.191 + // the width isn't constrained (position == -1 and !ShouldUseNativeStyle()) 1.192 + // because aReflowState.ComputedHeight() - size == 0. 1.193 + yoffset += aReflowState.ComputedHeight() - reflowState.ComputedHeight(); 1.194 + } 1.195 + 1.196 + xoffset += reflowState.ComputedPhysicalMargin().left; 1.197 + yoffset += reflowState.ComputedPhysicalMargin().top; 1.198 + 1.199 + nsHTMLReflowMetrics barDesiredSize(aReflowState); 1.200 + ReflowChild(aBarFrame, aPresContext, barDesiredSize, reflowState, xoffset, 1.201 + yoffset, 0, aStatus); 1.202 + FinishReflowChild(aBarFrame, aPresContext, barDesiredSize, &reflowState, 1.203 + xoffset, yoffset, 0); 1.204 +} 1.205 + 1.206 +nsresult 1.207 +nsProgressFrame::AttributeChanged(int32_t aNameSpaceID, 1.208 + nsIAtom* aAttribute, 1.209 + int32_t aModType) 1.210 +{ 1.211 + NS_ASSERTION(mBarDiv, "Progress bar div must exist!"); 1.212 + 1.213 + if (aNameSpaceID == kNameSpaceID_None && 1.214 + (aAttribute == nsGkAtoms::value || aAttribute == nsGkAtoms::max)) { 1.215 + nsIFrame* barFrame = mBarDiv->GetPrimaryFrame(); 1.216 + NS_ASSERTION(barFrame, "The progress frame should have a child with a frame!"); 1.217 + PresContext()->PresShell()->FrameNeedsReflow(barFrame, nsIPresShell::eResize, 1.218 + NS_FRAME_IS_DIRTY); 1.219 + InvalidateFrame(); 1.220 + } 1.221 + 1.222 + return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType); 1.223 +} 1.224 + 1.225 +nsSize 1.226 +nsProgressFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, 1.227 + nsSize aCBSize, nscoord aAvailableWidth, 1.228 + nsSize aMargin, nsSize aBorder, 1.229 + nsSize aPadding, bool aShrinkWrap) 1.230 +{ 1.231 + nsSize autoSize; 1.232 + autoSize.height = autoSize.width = 1.233 + NSToCoordRound(StyleFont()->mFont.size * 1.234 + nsLayoutUtils::FontSizeInflationFor(this)); // 1em 1.235 + 1.236 + if (StyleDisplay()->mOrient == NS_STYLE_ORIENT_VERTICAL) { 1.237 + autoSize.height *= 10; // 10em 1.238 + } else { 1.239 + autoSize.width *= 10; // 10em 1.240 + } 1.241 + 1.242 + return autoSize; 1.243 +} 1.244 + 1.245 +nscoord 1.246 +nsProgressFrame::GetMinWidth(nsRenderingContext *aRenderingContext) 1.247 +{ 1.248 + nsRefPtr<nsFontMetrics> fontMet; 1.249 + NS_ENSURE_SUCCESS( 1.250 + nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet)), 0); 1.251 + 1.252 + nscoord minWidth = fontMet->Font().size; // 1em 1.253 + 1.254 + if (StyleDisplay()->mOrient == NS_STYLE_ORIENT_AUTO || 1.255 + StyleDisplay()->mOrient == NS_STYLE_ORIENT_HORIZONTAL) { 1.256 + // The orientation is horizontal 1.257 + minWidth *= 10; // 10em 1.258 + } 1.259 + 1.260 + return minWidth; 1.261 +} 1.262 + 1.263 +nscoord 1.264 +nsProgressFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) 1.265 +{ 1.266 + return GetMinWidth(aRenderingContext); 1.267 +} 1.268 + 1.269 +bool 1.270 +nsProgressFrame::ShouldUseNativeStyle() const 1.271 +{ 1.272 + // Use the native style if these conditions are satisfied: 1.273 + // - both frames use the native appearance; 1.274 + // - neither frame has author specified rules setting the border or the 1.275 + // background. 1.276 + return StyleDisplay()->mAppearance == NS_THEME_PROGRESSBAR && 1.277 + mBarDiv->GetPrimaryFrame()->StyleDisplay()->mAppearance == NS_THEME_PROGRESSBAR_CHUNK && 1.278 + !PresContext()->HasAuthorSpecifiedRules(const_cast<nsProgressFrame*>(this), 1.279 + NS_AUTHOR_SPECIFIED_BORDER | NS_AUTHOR_SPECIFIED_BACKGROUND) && 1.280 + !PresContext()->HasAuthorSpecifiedRules(mBarDiv->GetPrimaryFrame(), 1.281 + NS_AUTHOR_SPECIFIED_BORDER | NS_AUTHOR_SPECIFIED_BACKGROUND); 1.282 +} 1.283 + 1.284 +Element* 1.285 +nsProgressFrame::GetPseudoElement(nsCSSPseudoElements::Type aType) 1.286 +{ 1.287 + if (aType == nsCSSPseudoElements::ePseudo_mozProgressBar) { 1.288 + return mBarDiv; 1.289 + } 1.290 + 1.291 + return nsContainerFrame::GetPseudoElement(aType); 1.292 +}