1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either of the GNU General Public License Version 2 or later (the "GPL"),
26 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : #include "nsHTMLButtonControlFrame.h"
39 :
40 : #include "nsCOMPtr.h"
41 : #include "nsContainerFrame.h"
42 : #include "nsIFormControlFrame.h"
43 : #include "nsHTMLParts.h"
44 : #include "nsIFormControl.h"
45 :
46 : #include "nsPresContext.h"
47 : #include "nsIPresShell.h"
48 : #include "nsStyleContext.h"
49 : #include "nsLeafFrame.h"
50 : #include "nsCSSRendering.h"
51 : #include "nsISupports.h"
52 : #include "nsGkAtoms.h"
53 : #include "nsCSSAnonBoxes.h"
54 : #include "nsStyleConsts.h"
55 : #include "nsIComponentManager.h"
56 : #include "nsIDocument.h"
57 : #include "nsButtonFrameRenderer.h"
58 : #include "nsFormControlFrame.h"
59 : #include "nsFrameManager.h"
60 : #include "nsINameSpaceManager.h"
61 : #include "nsIServiceManager.h"
62 : #include "nsIDOMHTMLButtonElement.h"
63 : #include "nsIDOMHTMLInputElement.h"
64 : #include "nsStyleSet.h"
65 : #ifdef ACCESSIBILITY
66 : #include "nsAccessibilityService.h"
67 : #endif
68 : #include "nsDisplayList.h"
69 :
70 : nsIFrame*
71 0 : NS_NewHTMLButtonControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
72 : {
73 0 : return new (aPresShell) nsHTMLButtonControlFrame(aContext);
74 : }
75 :
76 0 : NS_IMPL_FRAMEARENA_HELPERS(nsHTMLButtonControlFrame)
77 :
78 0 : nsHTMLButtonControlFrame::nsHTMLButtonControlFrame(nsStyleContext* aContext)
79 0 : : nsContainerFrame(aContext)
80 : {
81 0 : }
82 :
83 0 : nsHTMLButtonControlFrame::~nsHTMLButtonControlFrame()
84 : {
85 0 : }
86 :
87 : void
88 0 : nsHTMLButtonControlFrame::DestroyFrom(nsIFrame* aDestructRoot)
89 : {
90 0 : nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), false);
91 0 : DestroyAbsoluteFrames(aDestructRoot);
92 0 : nsContainerFrame::DestroyFrom(aDestructRoot);
93 0 : }
94 :
95 : NS_IMETHODIMP
96 0 : nsHTMLButtonControlFrame::Init(
97 : nsIContent* aContent,
98 : nsIFrame* aParent,
99 : nsIFrame* aPrevInFlow)
100 : {
101 0 : nsresult rv = nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
102 0 : if (NS_SUCCEEDED(rv)) {
103 0 : mRenderer.SetFrame(this, PresContext());
104 : }
105 0 : return rv;
106 : }
107 :
108 0 : NS_QUERYFRAME_HEAD(nsHTMLButtonControlFrame)
109 0 : NS_QUERYFRAME_ENTRY(nsIFormControlFrame)
110 0 : NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
111 :
112 : #ifdef ACCESSIBILITY
113 : already_AddRefed<nsAccessible>
114 0 : nsHTMLButtonControlFrame::CreateAccessible()
115 : {
116 0 : nsAccessibilityService* accService = nsIPresShell::AccService();
117 0 : if (accService) {
118 : return accService->CreateHTMLButtonAccessible(mContent,
119 0 : PresContext()->PresShell());
120 : }
121 :
122 0 : return nsnull;
123 : }
124 : #endif
125 :
126 : nsIAtom*
127 0 : nsHTMLButtonControlFrame::GetType() const
128 : {
129 0 : return nsGkAtoms::HTMLButtonControlFrame;
130 : }
131 :
132 : void
133 0 : nsHTMLButtonControlFrame::SetFocus(bool aOn, bool aRepaint)
134 : {
135 0 : }
136 :
137 : NS_IMETHODIMP
138 0 : nsHTMLButtonControlFrame::HandleEvent(nsPresContext* aPresContext,
139 : nsGUIEvent* aEvent,
140 : nsEventStatus* aEventStatus)
141 : {
142 : // if disabled do nothing
143 0 : if (mRenderer.isDisabled()) {
144 0 : return NS_OK;
145 : }
146 :
147 : // mouse clicks are handled by content
148 : // we don't want our children to get any events. So just pass it to frame.
149 0 : return nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
150 : }
151 :
152 :
153 : NS_IMETHODIMP
154 0 : nsHTMLButtonControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
155 : const nsRect& aDirtyRect,
156 : const nsDisplayListSet& aLists)
157 : {
158 0 : nsDisplayList onTop;
159 0 : if (IsVisibleForPainting(aBuilder)) {
160 0 : nsresult rv = mRenderer.DisplayButton(aBuilder, aLists.BorderBackground(), &onTop);
161 0 : NS_ENSURE_SUCCESS(rv, rv);
162 : }
163 :
164 0 : nsDisplayListCollection set;
165 : // Do not allow the child subtree to receive events.
166 0 : if (!aBuilder->IsForEventDelivery()) {
167 : nsresult rv =
168 : BuildDisplayListForChild(aBuilder, mFrames.FirstChild(), aDirtyRect, set,
169 0 : DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT);
170 0 : NS_ENSURE_SUCCESS(rv, rv);
171 : // That should put the display items in set.Content()
172 : }
173 :
174 : // Put the foreground outline and focus rects on top of the children
175 0 : set.Content()->AppendToTop(&onTop);
176 :
177 : // clips to our padding box for <input>s but not <button>s, unless
178 : // they have non-visible overflow..
179 0 : if (IsInput() || GetStyleDisplay()->mOverflowX != NS_STYLE_OVERFLOW_VISIBLE) {
180 0 : nsMargin border = GetStyleBorder()->GetActualBorder();
181 0 : nsRect rect(aBuilder->ToReferenceFrame(this), GetSize());
182 0 : rect.Deflate(border);
183 : nscoord radii[8];
184 0 : GetPaddingBoxBorderRadii(radii);
185 :
186 0 : nsresult rv = OverflowClip(aBuilder, set, aLists, rect, radii);
187 0 : NS_ENSURE_SUCCESS(rv, rv);
188 : } else {
189 0 : set.MoveTo(aLists);
190 : }
191 :
192 0 : nsresult rv = DisplayOutline(aBuilder, aLists);
193 0 : NS_ENSURE_SUCCESS(rv, rv);
194 :
195 : // to draw border when selected in editor
196 0 : return DisplaySelectionOverlay(aBuilder, aLists.Content());
197 : }
198 :
199 : nscoord
200 0 : nsHTMLButtonControlFrame::GetMinWidth(nsRenderingContext* aRenderingContext)
201 : {
202 : nscoord result;
203 0 : DISPLAY_MIN_WIDTH(this, result);
204 :
205 0 : nsIFrame* kid = mFrames.FirstChild();
206 : result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
207 : kid,
208 0 : nsLayoutUtils::MIN_WIDTH);
209 :
210 0 : result += mRenderer.GetAddedButtonBorderAndPadding().LeftRight();
211 :
212 0 : return result;
213 : }
214 :
215 : nscoord
216 0 : nsHTMLButtonControlFrame::GetPrefWidth(nsRenderingContext* aRenderingContext)
217 : {
218 : nscoord result;
219 0 : DISPLAY_PREF_WIDTH(this, result);
220 :
221 0 : nsIFrame* kid = mFrames.FirstChild();
222 : result = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
223 : kid,
224 0 : nsLayoutUtils::PREF_WIDTH);
225 0 : result += mRenderer.GetAddedButtonBorderAndPadding().LeftRight();
226 0 : return result;
227 : }
228 :
229 : NS_IMETHODIMP
230 0 : nsHTMLButtonControlFrame::Reflow(nsPresContext* aPresContext,
231 : nsHTMLReflowMetrics& aDesiredSize,
232 : const nsHTMLReflowState& aReflowState,
233 : nsReflowStatus& aStatus)
234 : {
235 0 : DO_GLOBAL_REFLOW_COUNT("nsHTMLButtonControlFrame");
236 0 : DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
237 :
238 0 : NS_PRECONDITION(aReflowState.ComputedWidth() != NS_INTRINSICSIZE,
239 : "Should have real computed width by now");
240 :
241 0 : if (mState & NS_FRAME_FIRST_REFLOW) {
242 0 : nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), true);
243 : }
244 :
245 : // Reflow the child
246 0 : nsIFrame* firstKid = mFrames.FirstChild();
247 :
248 : // XXXbz Eventually we may want to check-and-bail if
249 : // !aReflowState.ShouldReflowAllKids() &&
250 : // !NS_SUBTREE_DIRTY(firstKid).
251 : // We'd need to cache our ascent for that, of course.
252 :
253 0 : nsMargin focusPadding = mRenderer.GetAddedButtonBorderAndPadding();
254 :
255 : // Reflow the contents of the button.
256 : ReflowButtonContents(aPresContext, aDesiredSize, aReflowState, firstKid,
257 0 : focusPadding, aStatus);
258 :
259 0 : aDesiredSize.width = aReflowState.ComputedWidth();
260 :
261 : // If computed use the computed value.
262 0 : if (aReflowState.ComputedHeight() != NS_INTRINSICSIZE)
263 0 : aDesiredSize.height = aReflowState.ComputedHeight();
264 : else
265 0 : aDesiredSize.height += focusPadding.TopBottom();
266 :
267 0 : aDesiredSize.width += aReflowState.mComputedBorderPadding.LeftRight();
268 0 : aDesiredSize.height += aReflowState.mComputedBorderPadding.TopBottom();
269 :
270 : // Make sure we obey min/max-height. Note that we do this after adjusting
271 : // for borderpadding, since buttons have border-box sizing...
272 :
273 : // XXXbz unless someone overrides that, of course! We should really consider
274 : // exposing nsHTMLReflowState::AdjustComputed* or something.
275 : aDesiredSize.height = NS_CSS_MINMAX(aDesiredSize.height,
276 : aReflowState.mComputedMinHeight,
277 0 : aReflowState.mComputedMaxHeight);
278 :
279 : aDesiredSize.ascent +=
280 0 : aReflowState.mComputedBorderPadding.top + focusPadding.top;
281 :
282 0 : aDesiredSize.SetOverflowAreasToDesiredBounds();
283 0 : ConsiderChildOverflow(aDesiredSize.mOverflowAreas, firstKid);
284 0 : FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus);
285 :
286 0 : aStatus = NS_FRAME_COMPLETE;
287 :
288 0 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
289 0 : return NS_OK;
290 : }
291 :
292 : void
293 0 : nsHTMLButtonControlFrame::ReflowButtonContents(nsPresContext* aPresContext,
294 : nsHTMLReflowMetrics& aDesiredSize,
295 : const nsHTMLReflowState& aReflowState,
296 : nsIFrame* aFirstKid,
297 : nsMargin aFocusPadding,
298 : nsReflowStatus& aStatus)
299 : {
300 0 : nsSize availSize(aReflowState.ComputedWidth(), NS_INTRINSICSIZE);
301 :
302 : // Indent the child inside us by the focus border. We must do this separate
303 : // from the regular border.
304 0 : availSize.width -= aFocusPadding.LeftRight();
305 :
306 : // See whether out availSize's width is big enough. If it's smaller than our
307 : // intrinsic min width, that means that the kid wouldn't really fit; for a
308 : // better look in such cases we adjust the available width and our left
309 : // offset to allow the kid to spill left into our padding.
310 0 : nscoord xoffset = aFocusPadding.left + aReflowState.mComputedBorderPadding.left;
311 0 : nscoord extrawidth = GetMinWidth(aReflowState.rendContext) -
312 0 : aReflowState.ComputedWidth();
313 0 : if (extrawidth > 0) {
314 0 : nscoord extraleft = extrawidth / 2;
315 0 : nscoord extraright = extrawidth - extraleft;
316 0 : NS_ASSERTION(extraright >=0, "How'd that happen?");
317 :
318 : // Do not allow the extras to be bigger than the relevant padding
319 0 : extraleft = NS_MIN(extraleft, aReflowState.mComputedPadding.left);
320 0 : extraright = NS_MIN(extraright, aReflowState.mComputedPadding.right);
321 0 : xoffset -= extraleft;
322 0 : availSize.width += extraleft + extraright;
323 : }
324 0 : availSize.width = NS_MAX(availSize.width,0);
325 :
326 : nsHTMLReflowState reflowState(aPresContext, aReflowState, aFirstKid,
327 0 : availSize);
328 :
329 : ReflowChild(aFirstKid, aPresContext, aDesiredSize, reflowState,
330 : xoffset,
331 : aFocusPadding.top + aReflowState.mComputedBorderPadding.top,
332 0 : 0, aStatus);
333 :
334 : // calculate the min internal height so the contents gets centered correctly.
335 : // XXXbz this assumes border-box sizing.
336 : nscoord minInternalHeight = aReflowState.mComputedMinHeight -
337 0 : aReflowState.mComputedBorderPadding.TopBottom();
338 0 : minInternalHeight = NS_MAX(minInternalHeight, 0);
339 :
340 : // center child vertically
341 0 : nscoord yoff = 0;
342 0 : if (aReflowState.ComputedHeight() != NS_INTRINSICSIZE) {
343 0 : yoff = (aReflowState.ComputedHeight() - aDesiredSize.height)/2;
344 0 : if (yoff < 0) {
345 0 : yoff = 0;
346 : }
347 0 : } else if (aDesiredSize.height < minInternalHeight) {
348 0 : yoff = (minInternalHeight - aDesiredSize.height) / 2;
349 : }
350 :
351 : // Place the child
352 : FinishReflowChild(aFirstKid, aPresContext, &reflowState, aDesiredSize,
353 : xoffset,
354 0 : yoff + aFocusPadding.top + aReflowState.mComputedBorderPadding.top, 0);
355 :
356 0 : if (aDesiredSize.ascent == nsHTMLReflowMetrics::ASK_FOR_BASELINE)
357 0 : aDesiredSize.ascent = aFirstKid->GetBaseline();
358 :
359 : // Adjust the baseline by our offset (since we moved the child's
360 : // baseline by that much).
361 0 : aDesiredSize.ascent += yoff;
362 0 : }
363 :
364 : PRIntn
365 0 : nsHTMLButtonControlFrame::GetSkipSides() const
366 : {
367 0 : return 0;
368 : }
369 :
370 0 : nsresult nsHTMLButtonControlFrame::SetFormProperty(nsIAtom* aName, const nsAString& aValue)
371 : {
372 0 : if (nsGkAtoms::value == aName) {
373 : return mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::value,
374 0 : aValue, true);
375 : }
376 0 : return NS_OK;
377 : }
378 :
379 0 : nsresult nsHTMLButtonControlFrame::GetFormProperty(nsIAtom* aName, nsAString& aValue) const
380 : {
381 0 : if (nsGkAtoms::value == aName)
382 0 : mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::value, aValue);
383 :
384 0 : return NS_OK;
385 : }
386 :
387 : nsStyleContext*
388 0 : nsHTMLButtonControlFrame::GetAdditionalStyleContext(PRInt32 aIndex) const
389 : {
390 0 : return mRenderer.GetStyleContext(aIndex);
391 : }
392 :
393 : void
394 0 : nsHTMLButtonControlFrame::SetAdditionalStyleContext(PRInt32 aIndex,
395 : nsStyleContext* aStyleContext)
396 : {
397 0 : mRenderer.SetStyleContext(aIndex, aStyleContext);
398 0 : }
399 :
400 : NS_IMETHODIMP
401 0 : nsHTMLButtonControlFrame::AppendFrames(ChildListID aListID,
402 : nsFrameList& aFrameList)
403 : {
404 0 : NS_NOTREACHED("unsupported operation");
405 0 : return NS_ERROR_UNEXPECTED;
406 : }
407 :
408 : NS_IMETHODIMP
409 0 : nsHTMLButtonControlFrame::InsertFrames(ChildListID aListID,
410 : nsIFrame* aPrevFrame,
411 : nsFrameList& aFrameList)
412 : {
413 0 : NS_NOTREACHED("unsupported operation");
414 0 : return NS_ERROR_UNEXPECTED;
415 : }
416 :
417 : NS_IMETHODIMP
418 0 : nsHTMLButtonControlFrame::RemoveFrame(ChildListID aListID,
419 : nsIFrame* aOldFrame)
420 : {
421 0 : NS_NOTREACHED("unsupported operation");
422 0 : return NS_ERROR_UNEXPECTED;
423 : }
|