1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/forms/nsMeterFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,282 @@ 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 "nsMeterFrame.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/HTMLMeterElement.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 mozilla::dom::Element; 1.31 +using mozilla::dom::HTMLMeterElement; 1.32 + 1.33 +nsIFrame* 1.34 +NS_NewMeterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) 1.35 +{ 1.36 + return new (aPresShell) nsMeterFrame(aContext); 1.37 +} 1.38 + 1.39 +NS_IMPL_FRAMEARENA_HELPERS(nsMeterFrame) 1.40 + 1.41 +nsMeterFrame::nsMeterFrame(nsStyleContext* aContext) 1.42 + : nsContainerFrame(aContext) 1.43 + , mBarDiv(nullptr) 1.44 +{ 1.45 +} 1.46 + 1.47 +nsMeterFrame::~nsMeterFrame() 1.48 +{ 1.49 +} 1.50 + 1.51 +void 1.52 +nsMeterFrame::DestroyFrom(nsIFrame* aDestructRoot) 1.53 +{ 1.54 + NS_ASSERTION(!GetPrevContinuation(), 1.55 + "nsMeterFrame should not have continuations; if it does we " 1.56 + "need to call RegUnregAccessKey only for the first."); 1.57 + nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), false); 1.58 + nsContentUtils::DestroyAnonymousContent(&mBarDiv); 1.59 + nsContainerFrame::DestroyFrom(aDestructRoot); 1.60 +} 1.61 + 1.62 +nsresult 1.63 +nsMeterFrame::CreateAnonymousContent(nsTArray<ContentInfo>& aElements) 1.64 +{ 1.65 + // Get the NodeInfoManager and tag necessary to create the meter bar div. 1.66 + nsCOMPtr<nsIDocument> doc = mContent->GetDocument(); 1.67 + 1.68 + // Create the div. 1.69 + mBarDiv = doc->CreateHTMLElement(nsGkAtoms::div); 1.70 + 1.71 + // Associate ::-moz-meter-bar pseudo-element to the anonymous child. 1.72 + nsCSSPseudoElements::Type pseudoType = nsCSSPseudoElements::ePseudo_mozMeterBar; 1.73 + nsRefPtr<nsStyleContext> newStyleContext = PresContext()->StyleSet()-> 1.74 + ResolvePseudoElementStyle(mContent->AsElement(), pseudoType, 1.75 + StyleContext(), mBarDiv->AsElement()); 1.76 + 1.77 + if (!aElements.AppendElement(ContentInfo(mBarDiv, newStyleContext))) { 1.78 + return NS_ERROR_OUT_OF_MEMORY; 1.79 + } 1.80 + 1.81 + return NS_OK; 1.82 +} 1.83 + 1.84 +void 1.85 +nsMeterFrame::AppendAnonymousContentTo(nsBaseContentList& aElements, 1.86 + uint32_t aFilter) 1.87 +{ 1.88 + aElements.MaybeAppendElement(mBarDiv); 1.89 +} 1.90 + 1.91 +NS_QUERYFRAME_HEAD(nsMeterFrame) 1.92 + NS_QUERYFRAME_ENTRY(nsMeterFrame) 1.93 + NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator) 1.94 +NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame) 1.95 + 1.96 + 1.97 +nsresult nsMeterFrame::Reflow(nsPresContext* aPresContext, 1.98 + nsHTMLReflowMetrics& aDesiredSize, 1.99 + const nsHTMLReflowState& aReflowState, 1.100 + nsReflowStatus& aStatus) 1.101 +{ 1.102 + DO_GLOBAL_REFLOW_COUNT("nsMeterFrame"); 1.103 + DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus); 1.104 + 1.105 + NS_ASSERTION(mBarDiv, "Meter bar div must exist!"); 1.106 + NS_ASSERTION(!GetPrevContinuation(), 1.107 + "nsMeterFrame should not have continuations; if it does we " 1.108 + "need to call RegUnregAccessKey only for the first."); 1.109 + 1.110 + if (mState & NS_FRAME_FIRST_REFLOW) { 1.111 + nsFormControlFrame::RegUnRegAccessKey(this, true); 1.112 + } 1.113 + 1.114 + nsIFrame* barFrame = mBarDiv->GetPrimaryFrame(); 1.115 + NS_ASSERTION(barFrame, "The meter frame should have a child with a frame!"); 1.116 + 1.117 + ReflowBarFrame(barFrame, aPresContext, aReflowState, aStatus); 1.118 + 1.119 + aDesiredSize.Width() = aReflowState.ComputedWidth() + 1.120 + aReflowState.ComputedPhysicalBorderPadding().LeftRight(); 1.121 + aDesiredSize.Height() = aReflowState.ComputedHeight() + 1.122 + aReflowState.ComputedPhysicalBorderPadding().TopBottom(); 1.123 + 1.124 + aDesiredSize.SetOverflowAreasToDesiredBounds(); 1.125 + ConsiderChildOverflow(aDesiredSize.mOverflowAreas, barFrame); 1.126 + FinishAndStoreOverflow(&aDesiredSize); 1.127 + 1.128 + aStatus = NS_FRAME_COMPLETE; 1.129 + 1.130 + NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize); 1.131 + 1.132 + return NS_OK; 1.133 +} 1.134 + 1.135 +void 1.136 +nsMeterFrame::ReflowBarFrame(nsIFrame* aBarFrame, 1.137 + nsPresContext* aPresContext, 1.138 + const nsHTMLReflowState& aReflowState, 1.139 + nsReflowStatus& aStatus) 1.140 +{ 1.141 + bool vertical = StyleDisplay()->mOrient == NS_STYLE_ORIENT_VERTICAL; 1.142 + nsHTMLReflowState reflowState(aPresContext, aReflowState, aBarFrame, 1.143 + nsSize(aReflowState.ComputedWidth(), 1.144 + NS_UNCONSTRAINEDSIZE)); 1.145 + nscoord size = vertical ? aReflowState.ComputedHeight() 1.146 + : aReflowState.ComputedWidth(); 1.147 + nscoord xoffset = aReflowState.ComputedPhysicalBorderPadding().left; 1.148 + nscoord yoffset = aReflowState.ComputedPhysicalBorderPadding().top; 1.149 + 1.150 + // NOTE: Introduce a new function getPosition in the content part ? 1.151 + HTMLMeterElement* meterElement = static_cast<HTMLMeterElement*>(mContent); 1.152 + 1.153 + double max = meterElement->Max(); 1.154 + double min = meterElement->Min(); 1.155 + double value = meterElement->Value(); 1.156 + 1.157 + double position = max - min; 1.158 + position = position != 0 ? (value - min) / position : 1; 1.159 + 1.160 + size = NSToCoordRound(size * position); 1.161 + 1.162 + if (!vertical && StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) { 1.163 + xoffset += aReflowState.ComputedWidth() - size; 1.164 + } 1.165 + 1.166 + // The bar position is *always* constrained. 1.167 + if (vertical) { 1.168 + // We want the bar to begin at the bottom. 1.169 + yoffset += aReflowState.ComputedHeight() - size; 1.170 + 1.171 + size -= reflowState.ComputedPhysicalMargin().TopBottom() + 1.172 + reflowState.ComputedPhysicalBorderPadding().TopBottom(); 1.173 + size = std::max(size, 0); 1.174 + reflowState.SetComputedHeight(size); 1.175 + } else { 1.176 + size -= reflowState.ComputedPhysicalMargin().LeftRight() + 1.177 + reflowState.ComputedPhysicalBorderPadding().LeftRight(); 1.178 + size = std::max(size, 0); 1.179 + reflowState.SetComputedWidth(size); 1.180 + } 1.181 + 1.182 + xoffset += reflowState.ComputedPhysicalMargin().left; 1.183 + yoffset += reflowState.ComputedPhysicalMargin().top; 1.184 + 1.185 + nsHTMLReflowMetrics barDesiredSize(reflowState); 1.186 + ReflowChild(aBarFrame, aPresContext, barDesiredSize, reflowState, xoffset, 1.187 + yoffset, 0, aStatus); 1.188 + FinishReflowChild(aBarFrame, aPresContext, barDesiredSize, &reflowState, 1.189 + xoffset, yoffset, 0); 1.190 +} 1.191 + 1.192 +nsresult 1.193 +nsMeterFrame::AttributeChanged(int32_t aNameSpaceID, 1.194 + nsIAtom* aAttribute, 1.195 + int32_t aModType) 1.196 +{ 1.197 + NS_ASSERTION(mBarDiv, "Meter bar div must exist!"); 1.198 + 1.199 + if (aNameSpaceID == kNameSpaceID_None && 1.200 + (aAttribute == nsGkAtoms::value || 1.201 + aAttribute == nsGkAtoms::max || 1.202 + aAttribute == nsGkAtoms::min )) { 1.203 + nsIFrame* barFrame = mBarDiv->GetPrimaryFrame(); 1.204 + NS_ASSERTION(barFrame, "The meter frame should have a child with a frame!"); 1.205 + PresContext()->PresShell()->FrameNeedsReflow(barFrame, 1.206 + nsIPresShell::eResize, 1.207 + NS_FRAME_IS_DIRTY); 1.208 + InvalidateFrame(); 1.209 + } 1.210 + 1.211 + return nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute, 1.212 + aModType); 1.213 +} 1.214 + 1.215 +nsSize 1.216 +nsMeterFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext, 1.217 + nsSize aCBSize, nscoord aAvailableWidth, 1.218 + nsSize aMargin, nsSize aBorder, 1.219 + nsSize aPadding, bool aShrinkWrap) 1.220 +{ 1.221 + nsRefPtr<nsFontMetrics> fontMet; 1.222 + NS_ENSURE_SUCCESS(nsLayoutUtils::GetFontMetricsForFrame(this, 1.223 + getter_AddRefs(fontMet)), 1.224 + nsSize(0, 0)); 1.225 + 1.226 + nsSize autoSize; 1.227 + autoSize.height = autoSize.width = fontMet->Font().size; // 1em 1.228 + 1.229 + if (StyleDisplay()->mOrient == NS_STYLE_ORIENT_VERTICAL) { 1.230 + autoSize.height *= 5; // 5em 1.231 + } else { 1.232 + autoSize.width *= 5; // 5em 1.233 + } 1.234 + 1.235 + return autoSize; 1.236 +} 1.237 + 1.238 +nscoord 1.239 +nsMeterFrame::GetMinWidth(nsRenderingContext *aRenderingContext) 1.240 +{ 1.241 + nsRefPtr<nsFontMetrics> fontMet; 1.242 + NS_ENSURE_SUCCESS( 1.243 + nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet)), 0); 1.244 + 1.245 + nscoord minWidth = fontMet->Font().size; // 1em 1.246 + 1.247 + if (StyleDisplay()->mOrient == NS_STYLE_ORIENT_AUTO || 1.248 + StyleDisplay()->mOrient == NS_STYLE_ORIENT_HORIZONTAL) { 1.249 + // The orientation is horizontal 1.250 + minWidth *= 5; // 5em 1.251 + } 1.252 + 1.253 + return minWidth; 1.254 +} 1.255 + 1.256 +nscoord 1.257 +nsMeterFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) 1.258 +{ 1.259 + return GetMinWidth(aRenderingContext); 1.260 +} 1.261 + 1.262 +bool 1.263 +nsMeterFrame::ShouldUseNativeStyle() const 1.264 +{ 1.265 + // Use the native style if these conditions are satisfied: 1.266 + // - both frames use the native appearance; 1.267 + // - neither frame has author specified rules setting the border or the 1.268 + // background. 1.269 + return StyleDisplay()->mAppearance == NS_THEME_METERBAR && 1.270 + mBarDiv->GetPrimaryFrame()->StyleDisplay()->mAppearance == NS_THEME_METERBAR_CHUNK && 1.271 + !PresContext()->HasAuthorSpecifiedRules(const_cast<nsMeterFrame*>(this), 1.272 + NS_AUTHOR_SPECIFIED_BORDER | NS_AUTHOR_SPECIFIED_BACKGROUND) && 1.273 + !PresContext()->HasAuthorSpecifiedRules(mBarDiv->GetPrimaryFrame(), 1.274 + NS_AUTHOR_SPECIFIED_BORDER | NS_AUTHOR_SPECIFIED_BACKGROUND); 1.275 +} 1.276 + 1.277 +Element* 1.278 +nsMeterFrame::GetPseudoElement(nsCSSPseudoElements::Type aType) 1.279 +{ 1.280 + if (aType == nsCSSPseudoElements::ePseudo_mozMeterBar) { 1.281 + return mBarDiv; 1.282 + } 1.283 + 1.284 + return nsContainerFrame::GetPseudoElement(aType); 1.285 +}