Backout a74bd5095902, Bug 959405 - Please update the Buri Moz-central, 1.3, 1.2 with...
[gecko.git] / layout / forms / nsHTMLButtonControlFrame.cpp
blob5d84d4ca8f59897c760ad8de85ec77e81abd7480
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "nsHTMLButtonControlFrame.h"
8 #include "nsContainerFrame.h"
9 #include "nsIFormControlFrame.h"
10 #include "nsPresContext.h"
11 #include "nsGkAtoms.h"
12 #include "nsButtonFrameRenderer.h"
13 #include "nsCSSAnonBoxes.h"
14 #include "nsFormControlFrame.h"
15 #include "nsINameSpaceManager.h"
16 #include "nsStyleSet.h"
17 #include "nsDisplayList.h"
18 #include <algorithm>
20 using namespace mozilla;
22 nsIFrame*
23 NS_NewHTMLButtonControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
25 return new (aPresShell) nsHTMLButtonControlFrame(aContext);
28 NS_IMPL_FRAMEARENA_HELPERS(nsHTMLButtonControlFrame)
30 nsHTMLButtonControlFrame::nsHTMLButtonControlFrame(nsStyleContext* aContext)
31 : nsContainerFrame(aContext)
35 nsHTMLButtonControlFrame::~nsHTMLButtonControlFrame()
39 void
40 nsHTMLButtonControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
42 nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), false);
43 nsContainerFrame::DestroyFrom(aDestructRoot);
46 void
47 nsHTMLButtonControlFrame::Init(
48 nsIContent* aContent,
49 nsIFrame* aParent,
50 nsIFrame* aPrevInFlow)
52 nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
53 mRenderer.SetFrame(this, PresContext());
56 NS_QUERYFRAME_HEAD(nsHTMLButtonControlFrame)
57 NS_QUERYFRAME_ENTRY(nsIFormControlFrame)
58 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
60 #ifdef ACCESSIBILITY
61 a11y::AccType
62 nsHTMLButtonControlFrame::AccessibleType()
64 return a11y::eHTMLButtonType;
66 #endif
68 nsIAtom*
69 nsHTMLButtonControlFrame::GetType() const
71 return nsGkAtoms::HTMLButtonControlFrame;
74 void
75 nsHTMLButtonControlFrame::SetFocus(bool aOn, bool aRepaint)
79 NS_IMETHODIMP
80 nsHTMLButtonControlFrame::HandleEvent(nsPresContext* aPresContext,
81 WidgetGUIEvent* aEvent,
82 nsEventStatus* aEventStatus)
84 // if disabled do nothing
85 if (mRenderer.isDisabled()) {
86 return NS_OK;
89 // mouse clicks are handled by content
90 // we don't want our children to get any events. So just pass it to frame.
91 return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
95 void
96 nsHTMLButtonControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
97 const nsRect& aDirtyRect,
98 const nsDisplayListSet& aLists)
100 nsDisplayList onTop;
101 if (IsVisibleForPainting(aBuilder)) {
102 mRenderer.DisplayButton(aBuilder, aLists.BorderBackground(), &onTop);
105 nsDisplayListCollection set;
107 // Do not allow the child subtree to receive events.
108 if (!aBuilder->IsForEventDelivery()) {
109 DisplayListClipState::AutoSaveRestore clipState(aBuilder);
111 if (IsInput() || StyleDisplay()->mOverflowX != NS_STYLE_OVERFLOW_VISIBLE) {
112 nsMargin border = StyleBorder()->GetComputedBorder();
113 nsRect rect(aBuilder->ToReferenceFrame(this), GetSize());
114 rect.Deflate(border);
115 nscoord radii[8];
116 bool hasRadii = GetPaddingBoxBorderRadii(radii);
117 clipState.ClipContainingBlockDescendants(rect, hasRadii ? radii : nullptr);
120 BuildDisplayListForChild(aBuilder, mFrames.FirstChild(), aDirtyRect, set,
121 DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT);
122 // That should put the display items in set.Content()
125 // Put the foreground outline and focus rects on top of the children
126 set.Content()->AppendToTop(&onTop);
127 set.MoveTo(aLists);
129 DisplayOutline(aBuilder, aLists);
131 // to draw border when selected in editor
132 DisplaySelectionOverlay(aBuilder, aLists.Content());
135 nscoord
136 nsHTMLButtonControlFrame::GetMinWidth(nsRenderingContext* aRenderingContext)
138 nscoord result;
139 DISPLAY_MIN_WIDTH(this, result);
141 nsIFrame* kid = mFrames.FirstChild();
142 result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
143 kid,
144 nsLayoutUtils::MIN_WIDTH);
146 result += mRenderer.GetAddedButtonBorderAndPadding().LeftRight();
148 return result;
151 nscoord
152 nsHTMLButtonControlFrame::GetPrefWidth(nsRenderingContext* aRenderingContext)
154 nscoord result;
155 DISPLAY_PREF_WIDTH(this, result);
157 nsIFrame* kid = mFrames.FirstChild();
158 result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
159 kid,
160 nsLayoutUtils::PREF_WIDTH);
161 result += mRenderer.GetAddedButtonBorderAndPadding().LeftRight();
162 return result;
165 NS_IMETHODIMP
166 nsHTMLButtonControlFrame::Reflow(nsPresContext* aPresContext,
167 nsHTMLReflowMetrics& aDesiredSize,
168 const nsHTMLReflowState& aReflowState,
169 nsReflowStatus& aStatus)
171 DO_GLOBAL_REFLOW_COUNT("nsHTMLButtonControlFrame");
172 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
174 NS_PRECONDITION(aReflowState.ComputedWidth() != NS_INTRINSICSIZE,
175 "Should have real computed width by now");
177 if (mState & NS_FRAME_FIRST_REFLOW) {
178 nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), true);
181 // Reflow the child
182 nsIFrame* firstKid = mFrames.FirstChild();
184 MOZ_ASSERT(firstKid, "Button should have a child frame for its contents");
185 MOZ_ASSERT(!firstKid->GetNextSibling(),
186 "Button should have exactly one child frame");
187 MOZ_ASSERT(firstKid->StyleContext()->GetPseudo() ==
188 nsCSSAnonBoxes::buttonContent,
189 "Button's child frame has unexpected pseudo type!");
191 // XXXbz Eventually we may want to check-and-bail if
192 // !aReflowState.ShouldReflowAllKids() &&
193 // !NS_SUBTREE_DIRTY(firstKid).
194 // We'd need to cache our ascent for that, of course.
196 // Reflow the contents of the button.
197 // (This populates our aDesiredSize, too.)
198 ReflowButtonContents(aPresContext, aDesiredSize,
199 aReflowState, firstKid);
201 ConsiderChildOverflow(aDesiredSize.mOverflowAreas, firstKid);
203 aStatus = NS_FRAME_COMPLETE;
204 FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize,
205 aReflowState, aStatus);
207 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
208 return NS_OK;
211 // Helper-function that lets us clone the button's reflow state, but with its
212 // ComputedWidth and ComputedHeight reduced by the amount of renderer-specific
213 // focus border and padding that we're using. (This lets us provide a more
214 // appropriate content-box size for descendents' percent sizes to resolve
215 // against.)
216 static nsHTMLReflowState
217 CloneReflowStateWithReducedContentBox(
218 const nsHTMLReflowState& aButtonReflowState,
219 const nsMargin& aFocusPadding)
221 nscoord adjustedWidth =
222 aButtonReflowState.ComputedWidth() - aFocusPadding.LeftRight();
223 adjustedWidth = std::max(0, adjustedWidth);
225 // (Only adjust height if it's an actual length.)
226 nscoord adjustedHeight = aButtonReflowState.ComputedHeight();
227 if (adjustedHeight != NS_INTRINSICSIZE) {
228 adjustedHeight -= aFocusPadding.TopBottom();
229 adjustedHeight = std::max(0, adjustedHeight);
232 nsHTMLReflowState clone(aButtonReflowState);
233 clone.SetComputedWidth(adjustedWidth);
234 clone.SetComputedHeight(adjustedHeight);
236 return clone;
239 void
240 nsHTMLButtonControlFrame::ReflowButtonContents(nsPresContext* aPresContext,
241 nsHTMLReflowMetrics& aButtonDesiredSize,
242 const nsHTMLReflowState& aButtonReflowState,
243 nsIFrame* aFirstKid)
245 // Buttons have some bonus renderer-determined border/padding,
246 // which occupies part of the button's content-box area:
247 const nsMargin focusPadding = mRenderer.GetAddedButtonBorderAndPadding();
249 nsSize availSize(aButtonReflowState.ComputedWidth(), NS_INTRINSICSIZE);
251 // Indent the child inside us by the focus border. We must do this separate
252 // from the regular border.
253 availSize.width -= focusPadding.LeftRight();
255 // See whether out availSize's width is big enough. If it's smaller than our
256 // intrinsic min width, that means that the kid wouldn't really fit; for a
257 // better look in such cases we adjust the available width and our left
258 // offset to allow the kid to spill left into our padding.
259 nscoord xoffset = focusPadding.left +
260 aButtonReflowState.ComputedPhysicalBorderPadding().left;
261 nscoord extrawidth = GetMinWidth(aButtonReflowState.rendContext) -
262 aButtonReflowState.ComputedWidth();
263 if (extrawidth > 0) {
264 nscoord extraleft = extrawidth / 2;
265 nscoord extraright = extrawidth - extraleft;
266 NS_ASSERTION(extraright >=0, "How'd that happen?");
268 // Do not allow the extras to be bigger than the relevant padding
269 extraleft = std::min(extraleft, aButtonReflowState.ComputedPhysicalPadding().left);
270 extraright = std::min(extraright, aButtonReflowState.ComputedPhysicalPadding().right);
271 xoffset -= extraleft;
272 availSize.width += extraleft + extraright;
274 availSize.width = std::max(availSize.width,0);
276 // Give child a clone of the button's reflow state, with height/width reduced
277 // by focusPadding, so that descendants with height:100% don't protrude.
278 nsHTMLReflowState adjustedButtonReflowState =
279 CloneReflowStateWithReducedContentBox(aButtonReflowState, focusPadding);
281 nsHTMLReflowState contentsReflowState(aPresContext,
282 adjustedButtonReflowState,
283 aFirstKid, availSize);
285 nsReflowStatus contentsReflowStatus;
286 nsHTMLReflowMetrics contentsDesiredSize(aButtonReflowState.GetWritingMode());
287 ReflowChild(aFirstKid, aPresContext,
288 contentsDesiredSize, contentsReflowState,
289 xoffset,
290 focusPadding.top + aButtonReflowState.ComputedPhysicalBorderPadding().top,
291 0, contentsReflowStatus);
292 MOZ_ASSERT(NS_FRAME_IS_COMPLETE(contentsReflowStatus),
293 "We gave button-contents frame unconstrained available height, "
294 "so it should be complete");
296 // Compute the button's content-box height:
297 nscoord buttonContentBoxHeight = 0;
298 if (aButtonReflowState.ComputedHeight() != NS_INTRINSICSIZE) {
299 // Button has a fixed height -- that's its content-box height.
300 buttonContentBoxHeight = aButtonReflowState.ComputedHeight();
301 } else {
302 // Button is intrinsically sized -- it should shrinkwrap the
303 // button-contents' height, plus any focus-padding space:
304 buttonContentBoxHeight =
305 contentsDesiredSize.Height() + focusPadding.TopBottom();
307 // Make sure we obey min/max-height in the case when we're doing intrinsic
308 // sizing (we get it for free when we have a non-intrinsic
309 // aButtonReflowState.ComputedHeight()). Note that we do this before
310 // adjusting for borderpadding, since mComputedMaxHeight and
311 // mComputedMinHeight are content heights.
312 buttonContentBoxHeight =
313 NS_CSS_MINMAX(buttonContentBoxHeight,
314 aButtonReflowState.ComputedMinHeight(),
315 aButtonReflowState.ComputedMaxHeight());
318 // Center child vertically in the button
319 // (technically, inside of the button's focus-padding area)
320 nscoord extraSpace =
321 buttonContentBoxHeight - focusPadding.TopBottom() -
322 contentsDesiredSize.Height();
324 nscoord yoffset = std::max(0, extraSpace / 2);
326 // Adjust yoffset to be in terms of the button's frame-rect, instead of
327 // its focus-padding rect:
328 yoffset += focusPadding.top + aButtonReflowState.ComputedPhysicalBorderPadding().top;
330 // Place the child
331 FinishReflowChild(aFirstKid, aPresContext,
332 &contentsReflowState, contentsDesiredSize,
333 xoffset, yoffset, 0);
335 // Make sure we have a useful 'ascent' value for the child
336 if (contentsDesiredSize.TopAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) {
337 contentsDesiredSize.SetTopAscent(aFirstKid->GetBaseline());
340 // OK, we're done with the child frame.
341 // Use what we learned to populate the button frame's reflow metrics.
342 // * Button's height & width are content-box size + border-box contribution:
343 aButtonDesiredSize.Width() = aButtonReflowState.ComputedWidth() +
344 aButtonReflowState.ComputedPhysicalBorderPadding().LeftRight();
346 aButtonDesiredSize.Height() = buttonContentBoxHeight +
347 aButtonReflowState.ComputedPhysicalBorderPadding().TopBottom();
349 // * Button's ascent is its child's ascent, plus the child's y-offset
350 // within our frame:
351 aButtonDesiredSize.SetTopAscent(contentsDesiredSize.TopAscent() + yoffset);
353 aButtonDesiredSize.SetOverflowAreasToDesiredBounds();
356 nsresult nsHTMLButtonControlFrame::SetFormProperty(nsIAtom* aName, const nsAString& aValue)
358 if (nsGkAtoms::value == aName) {
359 return mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::value,
360 aValue, true);
362 return NS_OK;
365 nsStyleContext*
366 nsHTMLButtonControlFrame::GetAdditionalStyleContext(int32_t aIndex) const
368 return mRenderer.GetStyleContext(aIndex);
371 void
372 nsHTMLButtonControlFrame::SetAdditionalStyleContext(int32_t aIndex,
373 nsStyleContext* aStyleContext)
375 mRenderer.SetStyleContext(aIndex, aStyleContext);
378 NS_IMETHODIMP
379 nsHTMLButtonControlFrame::AppendFrames(ChildListID aListID,
380 nsFrameList& aFrameList)
382 NS_NOTREACHED("unsupported operation");
383 return NS_ERROR_UNEXPECTED;
386 NS_IMETHODIMP
387 nsHTMLButtonControlFrame::InsertFrames(ChildListID aListID,
388 nsIFrame* aPrevFrame,
389 nsFrameList& aFrameList)
391 NS_NOTREACHED("unsupported operation");
392 return NS_ERROR_UNEXPECTED;
395 NS_IMETHODIMP
396 nsHTMLButtonControlFrame::RemoveFrame(ChildListID aListID,
397 nsIFrame* aOldFrame)
399 NS_NOTREACHED("unsupported operation");
400 return NS_ERROR_UNEXPECTED;