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 : // YY need to pass isMultiple before create called
39 :
40 : //#include "nsFormControlFrame.h"
41 : #include "nsContainerFrame.h"
42 : #include "nsLegendFrame.h"
43 : #include "nsIDOMNode.h"
44 : #include "nsIDOMHTMLFieldSetElement.h"
45 : #include "nsIDOMHTMLLegendElement.h"
46 : #include "nsCSSRendering.h"
47 : //#include "nsIDOMHTMLCollection.h"
48 : #include "nsIContent.h"
49 : #include "nsIFrame.h"
50 : #include "nsISupports.h"
51 : #include "nsIAtom.h"
52 : #include "nsPresContext.h"
53 : #include "nsFrameManager.h"
54 : #include "nsHTMLParts.h"
55 : #include "nsGkAtoms.h"
56 : #include "nsStyleConsts.h"
57 : #include "nsFont.h"
58 : #include "nsCOMPtr.h"
59 : #ifdef ACCESSIBILITY
60 : #include "nsAccessibilityService.h"
61 : #endif
62 : #include "nsIServiceManager.h"
63 : #include "nsDisplayList.h"
64 : #include "nsRenderingContext.h"
65 :
66 : using namespace mozilla;
67 : using namespace mozilla::layout;
68 :
69 : class nsLegendFrame;
70 :
71 0 : class nsFieldSetFrame : public nsContainerFrame {
72 : public:
73 : NS_DECL_FRAMEARENA_HELPERS
74 :
75 : nsFieldSetFrame(nsStyleContext* aContext);
76 :
77 : NS_IMETHOD SetInitialChildList(ChildListID aListID,
78 : nsFrameList& aChildList);
79 :
80 : NS_HIDDEN_(nscoord)
81 : GetIntrinsicWidth(nsRenderingContext* aRenderingContext,
82 : nsLayoutUtils::IntrinsicWidthType);
83 : virtual nscoord GetMinWidth(nsRenderingContext* aRenderingContext);
84 : virtual nscoord GetPrefWidth(nsRenderingContext* aRenderingContext);
85 : virtual nsSize ComputeSize(nsRenderingContext *aRenderingContext,
86 : nsSize aCBSize, nscoord aAvailableWidth,
87 : nsSize aMargin, nsSize aBorder, nsSize aPadding,
88 : bool aShrinkWrap);
89 : virtual nscoord GetBaseline() const;
90 : virtual void DestroyFrom(nsIFrame* aDestructRoot);
91 :
92 : NS_IMETHOD Reflow(nsPresContext* aPresContext,
93 : nsHTMLReflowMetrics& aDesiredSize,
94 : const nsHTMLReflowState& aReflowState,
95 : nsReflowStatus& aStatus);
96 :
97 : NS_IMETHOD BuildDisplayList(nsDisplayListBuilder* aBuilder,
98 : const nsRect& aDirtyRect,
99 : const nsDisplayListSet& aLists);
100 :
101 : void PaintBorderBackground(nsRenderingContext& aRenderingContext,
102 : nsPoint aPt, const nsRect& aDirtyRect, PRUint32 aBGFlags);
103 :
104 : NS_IMETHOD AppendFrames(ChildListID aListID,
105 : nsFrameList& aFrameList);
106 : NS_IMETHOD InsertFrames(ChildListID aListID,
107 : nsIFrame* aPrevFrame,
108 : nsFrameList& aFrameList);
109 : NS_IMETHOD RemoveFrame(ChildListID aListID,
110 : nsIFrame* aOldFrame);
111 :
112 : virtual nsIAtom* GetType() const;
113 :
114 : #ifdef ACCESSIBILITY
115 : virtual already_AddRefed<nsAccessible> CreateAccessible();
116 : #endif
117 :
118 : #ifdef DEBUG
119 0 : NS_IMETHOD GetFrameName(nsAString& aResult) const {
120 0 : return MakeFrameName(NS_LITERAL_STRING("FieldSet"), aResult);
121 : }
122 : #endif
123 :
124 : protected:
125 :
126 : virtual PRIntn GetSkipSides() const;
127 : void ReparentFrameList(const nsFrameList& aFrameList);
128 :
129 : nsIFrame* mLegendFrame;
130 : nsIFrame* mContentFrame;
131 : nsRect mLegendRect;
132 : nscoord mLegendSpace;
133 : };
134 :
135 : nsIFrame*
136 0 : NS_NewFieldSetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
137 : {
138 0 : return new (aPresShell) nsFieldSetFrame(aContext);
139 : }
140 :
141 0 : NS_IMPL_FRAMEARENA_HELPERS(nsFieldSetFrame)
142 :
143 0 : nsFieldSetFrame::nsFieldSetFrame(nsStyleContext* aContext)
144 0 : : nsContainerFrame(aContext)
145 : {
146 0 : mContentFrame = nsnull;
147 0 : mLegendFrame = nsnull;
148 0 : mLegendSpace = 0;
149 0 : }
150 :
151 : void
152 0 : nsFieldSetFrame::DestroyFrom(nsIFrame* aDestructRoot)
153 : {
154 0 : DestroyAbsoluteFrames(aDestructRoot);
155 0 : nsContainerFrame::DestroyFrom(aDestructRoot);
156 0 : }
157 :
158 : nsIAtom*
159 0 : nsFieldSetFrame::GetType() const
160 : {
161 0 : return nsGkAtoms::fieldSetFrame;
162 : }
163 :
164 : NS_IMETHODIMP
165 0 : nsFieldSetFrame::SetInitialChildList(ChildListID aListID,
166 : nsFrameList& aChildList)
167 : {
168 : // Get the content and legend frames.
169 0 : if (!aChildList.OnlyChild()) {
170 0 : NS_ASSERTION(aChildList.GetLength() == 2, "Unexpected child list");
171 0 : mContentFrame = aChildList.LastChild();
172 0 : mLegendFrame = aChildList.FirstChild();
173 : } else {
174 0 : mContentFrame = aChildList.FirstChild();
175 0 : mLegendFrame = nsnull;
176 : }
177 :
178 : // Queue up the frames for the content frame
179 0 : return nsContainerFrame::SetInitialChildList(kPrincipalList, aChildList);
180 : }
181 :
182 : class nsDisplayFieldSetBorderBackground : public nsDisplayItem {
183 : public:
184 0 : nsDisplayFieldSetBorderBackground(nsDisplayListBuilder* aBuilder,
185 : nsFieldSetFrame* aFrame)
186 0 : : nsDisplayItem(aBuilder, aFrame) {
187 0 : MOZ_COUNT_CTOR(nsDisplayFieldSetBorderBackground);
188 0 : }
189 : #ifdef NS_BUILD_REFCNT_LOGGING
190 0 : virtual ~nsDisplayFieldSetBorderBackground() {
191 0 : MOZ_COUNT_DTOR(nsDisplayFieldSetBorderBackground);
192 0 : }
193 : #endif
194 :
195 : virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
196 : HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames);
197 : virtual void Paint(nsDisplayListBuilder* aBuilder,
198 : nsRenderingContext* aCtx);
199 0 : NS_DISPLAY_DECL_NAME("FieldSetBorderBackground", TYPE_FIELDSET_BORDER_BACKGROUND)
200 : };
201 :
202 0 : void nsDisplayFieldSetBorderBackground::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
203 : HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
204 : {
205 : // aPt is guaranteed to be in this item's bounds. We do the hit test based on the
206 : // frame bounds even though our background doesn't cover the whole frame.
207 : // It's not clear whether this is correct.
208 0 : aOutFrames->AppendElement(mFrame);
209 0 : }
210 :
211 : void
212 0 : nsDisplayFieldSetBorderBackground::Paint(nsDisplayListBuilder* aBuilder,
213 : nsRenderingContext* aCtx)
214 : {
215 : static_cast<nsFieldSetFrame*>(mFrame)->
216 0 : PaintBorderBackground(*aCtx, ToReferenceFrame(),
217 0 : mVisibleRect, aBuilder->GetBackgroundPaintFlags());
218 0 : }
219 :
220 : NS_IMETHODIMP
221 0 : nsFieldSetFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
222 : const nsRect& aDirtyRect,
223 : const nsDisplayListSet& aLists) {
224 : // Paint our background and border in a special way.
225 : // REVIEW: We don't really need to check frame emptiness here; if it's empty,
226 : // the background/border display item won't do anything, and if it isn't empty,
227 : // we need to paint the outline
228 0 : if (IsVisibleForPainting(aBuilder)) {
229 0 : if (GetStyleBorder()->mBoxShadow) {
230 : nsresult rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
231 0 : nsDisplayBoxShadowOuter(aBuilder, this));
232 0 : NS_ENSURE_SUCCESS(rv, rv);
233 : }
234 :
235 : // don't bother checking to see if we really have a border or background.
236 : // we usually will have a border.
237 : nsresult rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
238 0 : nsDisplayFieldSetBorderBackground(aBuilder, this));
239 0 : NS_ENSURE_SUCCESS(rv, rv);
240 :
241 0 : rv = DisplayOutlineUnconditional(aBuilder, aLists);
242 0 : NS_ENSURE_SUCCESS(rv, rv);
243 :
244 0 : DO_GLOBAL_REFLOW_COUNT_DSP("nsFieldSetFrame");
245 : }
246 :
247 0 : nsDisplayListCollection contentDisplayItems;
248 0 : if (mContentFrame) {
249 : // Collect mContentFrame's display items into their own collection. We need
250 : // to be calling BuildDisplayList on mContentFrame before mLegendFrame in
251 : // case it contains out-of-flow frames whose placeholders are under
252 : // mLegendFrame. However, we want mContentFrame's display items to be
253 : // after mLegendFrame's display items in z-order, so we need to save them
254 : // and append them later.
255 : nsresult rv = BuildDisplayListForChild(aBuilder, mContentFrame, aDirtyRect,
256 0 : contentDisplayItems);
257 0 : NS_ENSURE_SUCCESS(rv, rv);
258 : }
259 0 : if (mLegendFrame) {
260 : // The legend's background goes on our BlockBorderBackgrounds list because
261 : // it's a block child.
262 0 : nsDisplayListSet set(aLists, aLists.BlockBorderBackgrounds());
263 0 : nsresult rv = BuildDisplayListForChild(aBuilder, mLegendFrame, aDirtyRect, set);
264 0 : NS_ENSURE_SUCCESS(rv, rv);
265 : }
266 : // Put mContentFrame's display items on the master list. Note that
267 : // this moves mContentFrame's border/background display items to our
268 : // BorderBackground() list, which isn't really correct, but it's OK because
269 : // mContentFrame is anonymous and can't have its own border and background.
270 0 : contentDisplayItems.MoveTo(aLists);
271 0 : return NS_OK;
272 : }
273 :
274 : void
275 0 : nsFieldSetFrame::PaintBorderBackground(nsRenderingContext& aRenderingContext,
276 : nsPoint aPt, const nsRect& aDirtyRect, PRUint32 aBGFlags)
277 : {
278 0 : PRIntn skipSides = GetSkipSides();
279 0 : const nsStyleBorder* borderStyle = GetStyleBorder();
280 :
281 0 : nscoord topBorder = borderStyle->GetActualBorderWidth(NS_SIDE_TOP);
282 0 : nscoord yoff = 0;
283 0 : nsPresContext* presContext = PresContext();
284 :
285 : // if the border is smaller than the legend. Move the border down
286 : // to be centered on the legend.
287 : // FIXME: This means border-radius clamping is incorrect; we should
288 : // override nsIFrame::GetBorderRadii.
289 0 : if (topBorder < mLegendRect.height)
290 0 : yoff = (mLegendRect.height - topBorder)/2;
291 :
292 0 : nsRect rect(aPt.x, aPt.y + yoff, mRect.width, mRect.height - yoff);
293 :
294 : nsCSSRendering::PaintBackground(presContext, aRenderingContext, this,
295 0 : aDirtyRect, rect, aBGFlags);
296 :
297 : nsCSSRendering::PaintBoxShadowInner(presContext, aRenderingContext,
298 0 : this, rect, aDirtyRect);
299 :
300 0 : if (mLegendFrame) {
301 :
302 : // Use the rect of the legend frame, not mLegendRect, so we draw our
303 : // border under the legend's left and right margins.
304 0 : nsRect legendRect = mLegendFrame->GetRect() + aPt;
305 :
306 : // we should probably use PaintBorderEdges to do this but for now just use clipping
307 : // to achieve the same effect.
308 :
309 : // draw left side
310 0 : nsRect clipRect(rect);
311 0 : clipRect.width = legendRect.x - rect.x;
312 0 : clipRect.height = topBorder;
313 :
314 0 : aRenderingContext.PushState();
315 0 : aRenderingContext.IntersectClip(clipRect);
316 : nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
317 0 : aDirtyRect, rect, mStyleContext, skipSides);
318 :
319 0 : aRenderingContext.PopState();
320 :
321 :
322 : // draw right side
323 0 : clipRect = rect;
324 0 : clipRect.x = legendRect.XMost();
325 0 : clipRect.width = rect.XMost() - legendRect.XMost();
326 0 : clipRect.height = topBorder;
327 :
328 0 : aRenderingContext.PushState();
329 0 : aRenderingContext.IntersectClip(clipRect);
330 : nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
331 0 : aDirtyRect, rect, mStyleContext, skipSides);
332 :
333 0 : aRenderingContext.PopState();
334 :
335 :
336 : // draw bottom
337 0 : clipRect = rect;
338 0 : clipRect.y += topBorder;
339 0 : clipRect.height = mRect.height - (yoff + topBorder);
340 :
341 0 : aRenderingContext.PushState();
342 0 : aRenderingContext.IntersectClip(clipRect);
343 : nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
344 0 : aDirtyRect, rect, mStyleContext, skipSides);
345 :
346 0 : aRenderingContext.PopState();
347 : } else {
348 :
349 : nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
350 : aDirtyRect,
351 0 : nsRect(aPt, mRect.Size()),
352 0 : mStyleContext, skipSides);
353 : }
354 0 : }
355 :
356 : nscoord
357 0 : nsFieldSetFrame::GetIntrinsicWidth(nsRenderingContext* aRenderingContext,
358 : nsLayoutUtils::IntrinsicWidthType aType)
359 : {
360 0 : nscoord legendWidth = 0;
361 0 : nscoord contentWidth = 0;
362 0 : if (mLegendFrame) {
363 : legendWidth =
364 : nsLayoutUtils::IntrinsicForContainer(aRenderingContext, mLegendFrame,
365 0 : aType);
366 : }
367 :
368 0 : if (mContentFrame) {
369 : contentWidth =
370 : nsLayoutUtils::IntrinsicForContainer(aRenderingContext, mContentFrame,
371 0 : aType);
372 : }
373 :
374 0 : return NS_MAX(legendWidth, contentWidth);
375 : }
376 :
377 :
378 : nscoord
379 0 : nsFieldSetFrame::GetMinWidth(nsRenderingContext* aRenderingContext)
380 : {
381 0 : nscoord result = 0;
382 0 : DISPLAY_MIN_WIDTH(this, result);
383 :
384 0 : result = GetIntrinsicWidth(aRenderingContext, nsLayoutUtils::MIN_WIDTH);
385 0 : return result;
386 : }
387 :
388 : nscoord
389 0 : nsFieldSetFrame::GetPrefWidth(nsRenderingContext* aRenderingContext)
390 : {
391 0 : nscoord result = 0;
392 0 : DISPLAY_PREF_WIDTH(this, result);
393 :
394 0 : result = GetIntrinsicWidth(aRenderingContext, nsLayoutUtils::PREF_WIDTH);
395 0 : return result;
396 : }
397 :
398 : /* virtual */ nsSize
399 0 : nsFieldSetFrame::ComputeSize(nsRenderingContext *aRenderingContext,
400 : nsSize aCBSize, nscoord aAvailableWidth,
401 : nsSize aMargin, nsSize aBorder, nsSize aPadding,
402 : bool aShrinkWrap)
403 : {
404 : nsSize result =
405 : nsContainerFrame::ComputeSize(aRenderingContext, aCBSize, aAvailableWidth,
406 0 : aMargin, aBorder, aPadding, aShrinkWrap);
407 :
408 : // Fieldsets never shrink below their min width.
409 :
410 : // If we're a container for font size inflation, then shrink
411 : // wrapping inside of us should not apply font size inflation.
412 0 : AutoMaybeNullInflationContainer an(this);
413 :
414 0 : nscoord minWidth = GetMinWidth(aRenderingContext);
415 0 : if (minWidth > result.width)
416 0 : result.width = minWidth;
417 :
418 : return result;
419 : }
420 :
421 : NS_IMETHODIMP
422 0 : nsFieldSetFrame::Reflow(nsPresContext* aPresContext,
423 : nsHTMLReflowMetrics& aDesiredSize,
424 : const nsHTMLReflowState& aReflowState,
425 : nsReflowStatus& aStatus)
426 : {
427 0 : DO_GLOBAL_REFLOW_COUNT("nsFieldSetFrame");
428 0 : DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
429 :
430 0 : NS_PRECONDITION(aReflowState.ComputedWidth() != NS_INTRINSICSIZE,
431 : "Should have a precomputed width!");
432 :
433 : // Initialize OUT parameter
434 0 : aStatus = NS_FRAME_COMPLETE;
435 :
436 : //------------ Handle Incremental Reflow -----------------
437 : bool reflowContent;
438 : bool reflowLegend;
439 :
440 0 : if (aReflowState.ShouldReflowAllKids()) {
441 0 : reflowContent = mContentFrame != nsnull;
442 0 : reflowLegend = mLegendFrame != nsnull;
443 : } else {
444 0 : reflowContent = mContentFrame && NS_SUBTREE_DIRTY(mContentFrame);
445 0 : reflowLegend = mLegendFrame && NS_SUBTREE_DIRTY(mLegendFrame);
446 : }
447 :
448 : // We don't allow fieldsets to break vertically. If we did, we'd
449 : // need logic here to push and pull overflow frames.
450 0 : nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE);
451 0 : NS_ASSERTION(!mContentFrame ||
452 : nsLayoutUtils::IntrinsicForContainer(aReflowState.rendContext,
453 : mContentFrame,
454 : nsLayoutUtils::MIN_WIDTH) <=
455 : availSize.width,
456 : "Bogus availSize.width; should be bigger");
457 0 : NS_ASSERTION(!mLegendFrame ||
458 : nsLayoutUtils::IntrinsicForContainer(aReflowState.rendContext,
459 : mLegendFrame,
460 : nsLayoutUtils::MIN_WIDTH) <=
461 : availSize.width,
462 : "Bogus availSize.width; should be bigger");
463 :
464 : // get our border and padding
465 0 : const nsMargin &borderPadding = aReflowState.mComputedBorderPadding;
466 0 : nsMargin border = borderPadding - aReflowState.mComputedPadding;
467 :
468 : // Figure out how big the legend is if there is one.
469 : // get the legend's margin
470 0 : nsMargin legendMargin(0,0,0,0);
471 : // reflow the legend only if needed
472 0 : if (reflowLegend) {
473 : nsHTMLReflowState legendReflowState(aPresContext, aReflowState,
474 0 : mLegendFrame, availSize);
475 :
476 0 : nsHTMLReflowMetrics legendDesiredSize;
477 :
478 : ReflowChild(mLegendFrame, aPresContext, legendDesiredSize, legendReflowState,
479 0 : 0, 0, NS_FRAME_NO_MOVE_FRAME, aStatus);
480 : #ifdef NOISY_REFLOW
481 : printf(" returned (%d, %d)\n", legendDesiredSize.width, legendDesiredSize.height);
482 : #endif
483 : // figure out the legend's rectangle
484 0 : legendMargin = mLegendFrame->GetUsedMargin();
485 0 : mLegendRect.width = legendDesiredSize.width + legendMargin.left + legendMargin.right;
486 0 : mLegendRect.height = legendDesiredSize.height + legendMargin.top + legendMargin.bottom;
487 0 : mLegendRect.x = borderPadding.left;
488 0 : mLegendRect.y = 0;
489 :
490 0 : nscoord oldSpace = mLegendSpace;
491 0 : mLegendSpace = 0;
492 0 : if (mLegendRect.height > border.top) {
493 : // center the border on the legend
494 0 : mLegendSpace = mLegendRect.height - border.top;
495 : } else {
496 0 : mLegendRect.y = (border.top - mLegendRect.height)/2;
497 : }
498 :
499 : // if the legend space changes then we need to reflow the
500 : // content area as well.
501 0 : if (mLegendSpace != oldSpace && mContentFrame) {
502 0 : reflowContent = true;
503 : }
504 :
505 : FinishReflowChild(mLegendFrame, aPresContext, &legendReflowState,
506 0 : legendDesiredSize, 0, 0, NS_FRAME_NO_MOVE_FRAME);
507 0 : } else if (!mLegendFrame) {
508 0 : mLegendRect.SetEmpty();
509 0 : mLegendSpace = 0;
510 : } else {
511 : // mLegendSpace and mLegendRect haven't changed, but we need
512 : // the used margin when placing the legend.
513 0 : legendMargin = mLegendFrame->GetUsedMargin();
514 : }
515 :
516 : // reflow the content frame only if needed
517 0 : if (reflowContent) {
518 : nsHTMLReflowState kidReflowState(aPresContext, aReflowState, mContentFrame,
519 0 : availSize);
520 : // Our child is "height:100%" but we actually want its height to be reduced
521 : // by the amount of content-height the legend is eating up, unless our
522 : // height is unconstrained (in which case the child's will be too).
523 0 : if (aReflowState.ComputedHeight() != NS_UNCONSTRAINEDSIZE) {
524 0 : kidReflowState.SetComputedHeight(NS_MAX(0, aReflowState.ComputedHeight() - mLegendSpace));
525 : }
526 :
527 : kidReflowState.mComputedMinHeight =
528 0 : NS_MAX(0, aReflowState.mComputedMinHeight - mLegendSpace);
529 :
530 0 : if (aReflowState.mComputedMaxHeight != NS_UNCONSTRAINEDSIZE) {
531 : kidReflowState.mComputedMaxHeight =
532 0 : NS_MAX(0, aReflowState.mComputedMaxHeight - mLegendSpace);
533 : }
534 :
535 0 : nsHTMLReflowMetrics kidDesiredSize(aDesiredSize.mFlags);
536 : // Reflow the frame
537 0 : NS_ASSERTION(kidReflowState.mComputedMargin == nsMargin(0,0,0,0),
538 : "Margins on anonymous fieldset child not supported!");
539 0 : nsPoint pt(borderPadding.left, borderPadding.top + mLegendSpace);
540 : ReflowChild(mContentFrame, aPresContext, kidDesiredSize, kidReflowState,
541 0 : pt.x, pt.y, 0, aStatus);
542 :
543 : FinishReflowChild(mContentFrame, aPresContext, &kidReflowState,
544 0 : kidDesiredSize, pt.x, pt.y, 0);
545 0 : NS_FRAME_TRACE_REFLOW_OUT("FieldSet::Reflow", aStatus);
546 : }
547 :
548 0 : nsRect contentRect(0,0,0,0);
549 0 : if (mContentFrame) {
550 : // We don't support margins on mContentFrame, so our "content rect" is just
551 : // its rect.
552 0 : contentRect = mContentFrame->GetRect();
553 : }
554 :
555 : // use the computed width if the inner content does not fill it
556 0 : if (aReflowState.ComputedWidth() > contentRect.width) {
557 0 : contentRect.width = aReflowState.ComputedWidth();
558 : }
559 :
560 0 : if (mLegendFrame) {
561 : // if the content rect is larger then the legend we can align the legend
562 0 : if (contentRect.width > mLegendRect.width) {
563 0 : PRInt32 align = static_cast<nsLegendFrame*>(mLegendFrame)->GetAlign();
564 :
565 0 : switch(align) {
566 : case NS_STYLE_TEXT_ALIGN_RIGHT:
567 0 : mLegendRect.x = contentRect.width - mLegendRect.width + borderPadding.left;
568 0 : break;
569 : case NS_STYLE_TEXT_ALIGN_CENTER:
570 : // Note: rounding removed; there doesn't seem to be any need
571 0 : mLegendRect.x = contentRect.width / 2 - mLegendRect.width / 2 + borderPadding.left;
572 0 : break;
573 : }
574 :
575 : } else {
576 : // otherwise make place for the legend
577 0 : contentRect.width = mLegendRect.width;
578 : }
579 : // place the legend
580 0 : nsRect actualLegendRect(mLegendRect);
581 0 : actualLegendRect.Deflate(legendMargin);
582 :
583 0 : nsPoint curOrigin = mLegendFrame->GetPosition();
584 :
585 : // only if the origin changed
586 0 : if ((curOrigin.x != mLegendRect.x) || (curOrigin.y != mLegendRect.y)) {
587 0 : mLegendFrame->SetPosition(nsPoint(actualLegendRect.x , actualLegendRect.y));
588 0 : nsContainerFrame::PositionFrameView(mLegendFrame);
589 :
590 : // We need to recursively process the legend frame's
591 : // children since we're moving the frame after Reflow.
592 0 : nsContainerFrame::PositionChildViews(mLegendFrame);
593 : }
594 : }
595 :
596 : // Return our size and our result
597 0 : if (aReflowState.ComputedHeight() == NS_INTRINSICSIZE) {
598 : aDesiredSize.height = mLegendSpace +
599 0 : borderPadding.TopBottom() +
600 0 : contentRect.height;
601 : } else {
602 0 : nscoord min = borderPadding.TopBottom() + mLegendRect.height;
603 : aDesiredSize.height =
604 0 : aReflowState.ComputedHeight() + borderPadding.TopBottom();
605 0 : if (aDesiredSize.height < min)
606 0 : aDesiredSize.height = min;
607 : }
608 0 : aDesiredSize.width = contentRect.width + borderPadding.LeftRight();
609 0 : aDesiredSize.SetOverflowAreasToDesiredBounds();
610 0 : if (mLegendFrame)
611 0 : ConsiderChildOverflow(aDesiredSize.mOverflowAreas, mLegendFrame);
612 0 : if (mContentFrame)
613 0 : ConsiderChildOverflow(aDesiredSize.mOverflowAreas, mContentFrame);
614 0 : FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus);
615 :
616 0 : Invalidate(aDesiredSize.VisualOverflow());
617 :
618 0 : NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
619 0 : return NS_OK;
620 : }
621 :
622 : PRIntn
623 0 : nsFieldSetFrame::GetSkipSides() const
624 : {
625 0 : return 0;
626 : }
627 :
628 : NS_IMETHODIMP
629 0 : nsFieldSetFrame::AppendFrames(ChildListID aListID,
630 : nsFrameList& aFrameList)
631 : {
632 : // aFrameList is not allowed to contain "the legend" for this fieldset
633 0 : ReparentFrameList(aFrameList);
634 0 : return mContentFrame->AppendFrames(aListID, aFrameList);
635 : }
636 :
637 : NS_IMETHODIMP
638 0 : nsFieldSetFrame::InsertFrames(ChildListID aListID,
639 : nsIFrame* aPrevFrame,
640 : nsFrameList& aFrameList)
641 : {
642 0 : NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this ||
643 : aPrevFrame->GetParent() == mContentFrame,
644 : "inserting after sibling frame with different parent");
645 :
646 : // aFrameList is not allowed to contain "the legend" for this fieldset
647 0 : ReparentFrameList(aFrameList);
648 0 : if (NS_UNLIKELY(aPrevFrame == mLegendFrame)) {
649 0 : aPrevFrame = nsnull;
650 : }
651 0 : return mContentFrame->InsertFrames(aListID, aPrevFrame, aFrameList);
652 : }
653 :
654 : NS_IMETHODIMP
655 0 : nsFieldSetFrame::RemoveFrame(ChildListID aListID,
656 : nsIFrame* aOldFrame)
657 : {
658 : // For reference, see bug 70648, bug 276104 and bug 236071.
659 0 : NS_ASSERTION(aOldFrame != mLegendFrame, "Cannot remove mLegendFrame here");
660 0 : return mContentFrame->RemoveFrame(aListID, aOldFrame);
661 : }
662 :
663 : #ifdef ACCESSIBILITY
664 : already_AddRefed<nsAccessible>
665 0 : nsFieldSetFrame::CreateAccessible()
666 : {
667 0 : nsAccessibilityService* accService = nsIPresShell::AccService();
668 0 : if (accService) {
669 : return accService->CreateHTMLGroupboxAccessible(mContent,
670 0 : PresContext()->PresShell());
671 : }
672 :
673 0 : return nsnull;
674 : }
675 : #endif
676 :
677 : void
678 0 : nsFieldSetFrame::ReparentFrameList(const nsFrameList& aFrameList)
679 : {
680 0 : nsFrameManager* frameManager = PresContext()->FrameManager();
681 0 : for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
682 0 : NS_ASSERTION(mLegendFrame || e.get()->GetType() != nsGkAtoms::legendFrame,
683 : "The fieldset's legend is not allowed in this list");
684 0 : e.get()->SetParent(mContentFrame);
685 0 : frameManager->ReparentStyleContext(e.get());
686 : }
687 0 : }
688 :
689 : nscoord
690 0 : nsFieldSetFrame::GetBaseline() const
691 : {
692 : // We know mContentFrame is a block, so calling GetBaseline() on it will do
693 : // the right thing (that being to return the baseline of the last line).
694 0 : NS_ASSERTION(nsLayoutUtils::GetAsBlock(mContentFrame),
695 : "Unexpected mContentFrame");
696 0 : return mContentFrame->GetPosition().y + mContentFrame->GetBaseline();
697 : }
|