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 Communicator client 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 : * Author: Eric D Vaughan <evaughan@netscape.com>
24 : * Pierre Phaneuf <pp@ludusdesign.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "nsBoxLayoutState.h"
41 : #include "nsBox.h"
42 : #include "nsBoxFrame.h"
43 : #include "nsPresContext.h"
44 : #include "nsCOMPtr.h"
45 : #include "nsIContent.h"
46 : #include "nsContainerFrame.h"
47 : #include "nsINameSpaceManager.h"
48 : #include "nsGkAtoms.h"
49 : #include "nsFrameManager.h"
50 : #include "nsIDOMNode.h"
51 : #include "nsIDOMNamedNodeMap.h"
52 : #include "nsIDOMAttr.h"
53 : #include "nsIDocument.h"
54 : #include "nsITheme.h"
55 : #include "nsIServiceManager.h"
56 : #include "nsBoxLayout.h"
57 : #include "FrameLayerBuilder.h"
58 :
59 : using namespace mozilla;
60 :
61 : #ifdef DEBUG_LAYOUT
62 : PRInt32 gIndent = 0;
63 : #endif
64 :
65 : #ifdef DEBUG_LAYOUT
66 : void
67 : nsBoxAddIndents()
68 : {
69 : for(PRInt32 i=0; i < gIndent; i++)
70 : {
71 : printf(" ");
72 : }
73 : }
74 : #endif
75 :
76 : #ifdef DEBUG_LAYOUT
77 : void
78 : nsBox::AppendAttribute(const nsAutoString& aAttribute, const nsAutoString& aValue, nsAutoString& aResult)
79 : {
80 : aResult.Append(aAttribute);
81 : aResult.AppendLiteral("='");
82 : aResult.Append(aValue);
83 : aResult.AppendLiteral("' ");
84 : }
85 :
86 : void
87 : nsBox::ListBox(nsAutoString& aResult)
88 : {
89 : nsAutoString name;
90 : GetBoxName(name);
91 :
92 : char addr[100];
93 : sprintf(addr, "[@%p] ", static_cast<void*>(this));
94 :
95 : aResult.AppendASCII(addr);
96 : aResult.Append(name);
97 : aResult.AppendLiteral(" ");
98 :
99 : nsIContent* content = GetContent();
100 :
101 : // add on all the set attributes
102 : if (content) {
103 : nsCOMPtr<nsIDOMNode> node(do_QueryInterface(content));
104 : nsCOMPtr<nsIDOMNamedNodeMap> namedMap;
105 :
106 : node->GetAttributes(getter_AddRefs(namedMap));
107 : PRUint32 length;
108 : namedMap->GetLength(&length);
109 :
110 : nsCOMPtr<nsIDOMNode> attribute;
111 : for (PRUint32 i = 0; i < length; ++i)
112 : {
113 : namedMap->Item(i, getter_AddRefs(attribute));
114 : nsCOMPtr<nsIDOMAttr> attr(do_QueryInterface(attribute));
115 : attr->GetName(name);
116 : nsAutoString value;
117 : attr->GetValue(value);
118 : AppendAttribute(name, value, aResult);
119 : }
120 : }
121 : }
122 :
123 : NS_IMETHODIMP
124 : nsBox::DumpBox(FILE* aFile)
125 : {
126 : nsAutoString s;
127 : ListBox(s);
128 : fprintf(aFile, "%s", NS_LossyConvertUTF16toASCII(s).get());
129 : return NS_OK;
130 : }
131 :
132 : void
133 : nsBox::PropagateDebug(nsBoxLayoutState& aState)
134 : {
135 : // propagate debug information
136 : if (mState & NS_STATE_DEBUG_WAS_SET) {
137 : if (mState & NS_STATE_SET_TO_DEBUG)
138 : SetDebug(aState, true);
139 : else
140 : SetDebug(aState, false);
141 : } else if (mState & NS_STATE_IS_ROOT) {
142 : SetDebug(aState, gDebug);
143 : }
144 : }
145 : #endif
146 :
147 : #ifdef DEBUG_LAYOUT
148 : void
149 : nsBox::GetBoxName(nsAutoString& aName)
150 : {
151 : aName.AssignLiteral("Box");
152 : }
153 : #endif
154 :
155 : nsresult
156 0 : nsBox::BeginLayout(nsBoxLayoutState& aState)
157 : {
158 : #ifdef DEBUG_LAYOUT
159 :
160 : nsBoxAddIndents();
161 : printf("Layout: ");
162 : DumpBox(stdout);
163 : printf("\n");
164 : gIndent++;
165 : #endif
166 :
167 : // mark ourselves as dirty so no child under us
168 : // can post an incremental layout.
169 : // XXXldb Is this still needed?
170 0 : mState |= NS_FRAME_HAS_DIRTY_CHILDREN;
171 :
172 0 : if (GetStateBits() & NS_FRAME_IS_DIRTY)
173 : {
174 : // If the parent is dirty, all the children are dirty (nsHTMLReflowState
175 : // does this too).
176 : nsIFrame* box;
177 0 : for (box = GetChildBox(); box; box = box->GetNextBox())
178 0 : box->AddStateBits(NS_FRAME_IS_DIRTY);
179 : }
180 :
181 : // Another copy-over from nsHTMLReflowState.
182 : // Since we are in reflow, we don't need to store these properties anymore.
183 0 : FrameProperties props = Properties();
184 0 : props.Delete(UsedBorderProperty());
185 0 : props.Delete(UsedPaddingProperty());
186 0 : props.Delete(UsedMarginProperty());
187 :
188 : #ifdef DEBUG_LAYOUT
189 : PropagateDebug(aState);
190 : #endif
191 :
192 0 : return NS_OK;
193 : }
194 :
195 : NS_IMETHODIMP
196 0 : nsBox::DoLayout(nsBoxLayoutState& aState)
197 : {
198 0 : return NS_OK;
199 : }
200 :
201 : nsresult
202 0 : nsBox::EndLayout(nsBoxLayoutState& aState)
203 : {
204 :
205 : #ifdef DEBUG_LAYOUT
206 : --gIndent;
207 : #endif
208 :
209 0 : return SyncLayout(aState);
210 : }
211 :
212 : bool nsBox::gGotTheme = false;
213 : nsITheme* nsBox::gTheme = nsnull;
214 :
215 0 : nsBox::nsBox()
216 : {
217 0 : MOZ_COUNT_CTOR(nsBox);
218 : //mX = 0;
219 : //mY = 0;
220 0 : if (!gGotTheme) {
221 0 : gGotTheme = true;
222 0 : CallGetService("@mozilla.org/chrome/chrome-native-theme;1", &gTheme);
223 : }
224 0 : }
225 :
226 0 : nsBox::~nsBox()
227 : {
228 : // NOTE: This currently doesn't get called for |nsBoxToBlockAdaptor|
229 : // objects, so don't rely on putting anything here.
230 0 : MOZ_COUNT_DTOR(nsBox);
231 0 : }
232 :
233 : /* static */ void
234 1403 : nsBox::Shutdown()
235 : {
236 1403 : gGotTheme = false;
237 1403 : NS_IF_RELEASE(gTheme);
238 1403 : }
239 :
240 : NS_IMETHODIMP
241 0 : nsBox::RelayoutChildAtOrdinal(nsBoxLayoutState& aState, nsIBox* aChild)
242 : {
243 0 : return NS_OK;
244 : }
245 :
246 : nsresult
247 0 : nsIFrame::GetClientRect(nsRect& aClientRect)
248 : {
249 0 : aClientRect = mRect;
250 0 : aClientRect.MoveTo(0,0);
251 :
252 0 : nsMargin borderPadding;
253 0 : GetBorderAndPadding(borderPadding);
254 :
255 0 : aClientRect.Deflate(borderPadding);
256 :
257 0 : if (aClientRect.width < 0)
258 0 : aClientRect.width = 0;
259 :
260 0 : if (aClientRect.height < 0)
261 0 : aClientRect.height = 0;
262 :
263 : // NS_ASSERTION(aClientRect.width >=0 && aClientRect.height >= 0, "Content Size < 0");
264 :
265 0 : return NS_OK;
266 : }
267 :
268 : void
269 0 : nsBox::SetBounds(nsBoxLayoutState& aState, const nsRect& aRect, bool aRemoveOverflowAreas)
270 : {
271 : NS_BOX_ASSERTION(this, aRect.width >=0 && aRect.height >= 0, "SetBounds Size < 0");
272 :
273 0 : nsRect rect(mRect);
274 :
275 0 : PRUint32 flags = 0;
276 0 : GetLayoutFlags(flags);
277 :
278 0 : PRUint32 stateFlags = aState.LayoutFlags();
279 :
280 0 : flags |= stateFlags;
281 :
282 0 : if ((flags & NS_FRAME_NO_MOVE_FRAME) == NS_FRAME_NO_MOVE_FRAME)
283 0 : SetSize(nsSize(aRect.width, aRect.height));
284 : else
285 0 : SetRect(aRect);
286 :
287 : // Nuke the overflow area. The caller is responsible for restoring
288 : // it if necessary.
289 0 : if (aRemoveOverflowAreas) {
290 : // remove the previously stored overflow area
291 0 : ClearOverflowRects();
292 : }
293 :
294 0 : if (!(flags & NS_FRAME_NO_MOVE_VIEW))
295 : {
296 0 : nsContainerFrame::PositionFrameView(this);
297 0 : if ((rect.x != aRect.x) || (rect.y != aRect.y))
298 0 : nsContainerFrame::PositionChildViews(this);
299 : }
300 :
301 :
302 : /*
303 : // only if the origin changed
304 : if ((rect.x != aRect.x) || (rect.y != aRect.y)) {
305 : if (frame->HasView()) {
306 : nsContainerFrame::PositionFrameView(presContext, frame,
307 : frame->GetView());
308 : } else {
309 : nsContainerFrame::PositionChildViews(presContext, frame);
310 : }
311 : }
312 : */
313 0 : }
314 :
315 : void
316 0 : nsBox::GetLayoutFlags(PRUint32& aFlags)
317 : {
318 0 : aFlags = 0;
319 0 : }
320 :
321 :
322 : NS_IMETHODIMP
323 0 : nsIFrame::GetBorderAndPadding(nsMargin& aBorderAndPadding)
324 : {
325 0 : aBorderAndPadding.SizeTo(0, 0, 0, 0);
326 0 : nsresult rv = GetBorder(aBorderAndPadding);
327 0 : if (NS_FAILED(rv))
328 0 : return rv;
329 :
330 0 : nsMargin padding;
331 0 : rv = GetPadding(padding);
332 0 : if (NS_FAILED(rv))
333 0 : return rv;
334 :
335 0 : aBorderAndPadding += padding;
336 :
337 0 : return rv;
338 : }
339 :
340 : NS_IMETHODIMP
341 0 : nsBox::GetBorder(nsMargin& aMargin)
342 : {
343 0 : aMargin.SizeTo(0,0,0,0);
344 :
345 0 : const nsStyleDisplay* disp = GetStyleDisplay();
346 0 : if (disp->mAppearance && gTheme) {
347 : // Go to the theme for the border.
348 0 : nsPresContext *context = PresContext();
349 0 : if (gTheme->ThemeSupportsWidget(context, this, disp->mAppearance)) {
350 0 : nsIntMargin margin(0, 0, 0, 0);
351 : gTheme->GetWidgetBorder(context->DeviceContext(), this,
352 0 : disp->mAppearance, &margin);
353 0 : aMargin.top = context->DevPixelsToAppUnits(margin.top);
354 0 : aMargin.right = context->DevPixelsToAppUnits(margin.right);
355 0 : aMargin.bottom = context->DevPixelsToAppUnits(margin.bottom);
356 0 : aMargin.left = context->DevPixelsToAppUnits(margin.left);
357 0 : return NS_OK;
358 : }
359 : }
360 :
361 0 : aMargin = GetStyleBorder()->GetActualBorder();
362 :
363 0 : return NS_OK;
364 : }
365 :
366 : NS_IMETHODIMP
367 0 : nsBox::GetPadding(nsMargin& aMargin)
368 : {
369 0 : const nsStyleDisplay *disp = GetStyleDisplay();
370 0 : if (disp->mAppearance && gTheme) {
371 : // Go to the theme for the padding.
372 0 : nsPresContext *context = PresContext();
373 0 : if (gTheme->ThemeSupportsWidget(context, this, disp->mAppearance)) {
374 0 : nsIntMargin margin(0, 0, 0, 0);
375 : bool useThemePadding;
376 :
377 : useThemePadding = gTheme->GetWidgetPadding(context->DeviceContext(),
378 : this, disp->mAppearance,
379 0 : &margin);
380 0 : if (useThemePadding) {
381 0 : aMargin.top = context->DevPixelsToAppUnits(margin.top);
382 0 : aMargin.right = context->DevPixelsToAppUnits(margin.right);
383 0 : aMargin.bottom = context->DevPixelsToAppUnits(margin.bottom);
384 0 : aMargin.left = context->DevPixelsToAppUnits(margin.left);
385 0 : return NS_OK;
386 : }
387 : }
388 : }
389 :
390 0 : aMargin.SizeTo(0,0,0,0);
391 0 : GetStylePadding()->GetPadding(aMargin);
392 :
393 0 : return NS_OK;
394 : }
395 :
396 : NS_IMETHODIMP
397 0 : nsBox::GetMargin(nsMargin& aMargin)
398 : {
399 0 : aMargin.SizeTo(0,0,0,0);
400 0 : GetStyleMargin()->GetMargin(aMargin);
401 :
402 0 : return NS_OK;
403 : }
404 :
405 : void
406 0 : nsBox::SizeNeedsRecalc(nsSize& aSize)
407 : {
408 0 : aSize.width = -1;
409 0 : aSize.height = -1;
410 0 : }
411 :
412 : void
413 0 : nsBox::CoordNeedsRecalc(nscoord& aFlex)
414 : {
415 0 : aFlex = -1;
416 0 : }
417 :
418 : bool
419 0 : nsBox::DoesNeedRecalc(const nsSize& aSize)
420 : {
421 0 : return (aSize.width == -1 || aSize.height == -1);
422 : }
423 :
424 : bool
425 0 : nsBox::DoesNeedRecalc(nscoord aCoord)
426 : {
427 0 : return (aCoord == -1);
428 : }
429 :
430 : nsSize
431 0 : nsBox::GetPrefSize(nsBoxLayoutState& aState)
432 : {
433 0 : NS_ASSERTION(aState.GetRenderingContext(), "must have rendering context");
434 :
435 0 : nsSize pref(0,0);
436 0 : DISPLAY_PREF_SIZE(this, pref);
437 :
438 0 : if (IsCollapsed())
439 0 : return pref;
440 :
441 0 : AddBorderAndPadding(pref);
442 : bool widthSet, heightSet;
443 0 : nsIBox::AddCSSPrefSize(this, pref, widthSet, heightSet);
444 :
445 0 : nsSize minSize = GetMinSize(aState);
446 0 : nsSize maxSize = GetMaxSize(aState);
447 0 : return BoundsCheck(minSize, pref, maxSize);
448 : }
449 :
450 : nsSize
451 0 : nsBox::GetMinSize(nsBoxLayoutState& aState)
452 : {
453 0 : NS_ASSERTION(aState.GetRenderingContext(), "must have rendering context");
454 :
455 0 : nsSize min(0,0);
456 0 : DISPLAY_MIN_SIZE(this, min);
457 :
458 0 : if (IsCollapsed())
459 : return min;
460 :
461 0 : AddBorderAndPadding(min);
462 : bool widthSet, heightSet;
463 0 : nsIBox::AddCSSMinSize(aState, this, min, widthSet, heightSet);
464 : return min;
465 : }
466 :
467 : nsSize
468 0 : nsBox::GetMinSizeForScrollArea(nsBoxLayoutState& aBoxLayoutState)
469 : {
470 0 : return nsSize(0, 0);
471 : }
472 :
473 : nsSize
474 0 : nsBox::GetMaxSize(nsBoxLayoutState& aState)
475 : {
476 0 : NS_ASSERTION(aState.GetRenderingContext(), "must have rendering context");
477 :
478 0 : nsSize maxSize(NS_INTRINSICSIZE, NS_INTRINSICSIZE);
479 0 : DISPLAY_MAX_SIZE(this, maxSize);
480 :
481 0 : if (IsCollapsed())
482 : return maxSize;
483 :
484 0 : AddBorderAndPadding(maxSize);
485 : bool widthSet, heightSet;
486 0 : nsIBox::AddCSSMaxSize(this, maxSize, widthSet, heightSet);
487 : return maxSize;
488 : }
489 :
490 : nscoord
491 0 : nsBox::GetFlex(nsBoxLayoutState& aState)
492 : {
493 0 : nscoord flex = 0;
494 :
495 0 : nsIBox::AddCSSFlex(aState, this, flex);
496 :
497 0 : return flex;
498 : }
499 :
500 : PRUint32
501 0 : nsIFrame::GetOrdinal(nsBoxLayoutState& aState)
502 : {
503 0 : PRUint32 ordinal = GetStyleXUL()->mBoxOrdinal;
504 :
505 : // When present, attribute value overrides CSS.
506 0 : nsIContent* content = GetContent();
507 0 : if (content && content->IsXUL()) {
508 : PRInt32 error;
509 0 : nsAutoString value;
510 :
511 0 : content->GetAttr(kNameSpaceID_None, nsGkAtoms::ordinal, value);
512 0 : if (!value.IsEmpty()) {
513 0 : ordinal = value.ToInteger(&error);
514 : }
515 : }
516 :
517 0 : return ordinal;
518 : }
519 :
520 : nscoord
521 0 : nsBox::GetBoxAscent(nsBoxLayoutState& aState)
522 : {
523 0 : if (IsCollapsed())
524 0 : return 0;
525 :
526 0 : return GetPrefSize(aState).height;
527 : }
528 :
529 : bool
530 0 : nsBox::IsCollapsed()
531 : {
532 0 : return GetStyleVisibility()->mVisible == NS_STYLE_VISIBILITY_COLLAPSE;
533 : }
534 :
535 : nsresult
536 0 : nsIFrame::Layout(nsBoxLayoutState& aState)
537 : {
538 0 : NS_ASSERTION(aState.GetRenderingContext(), "must have rendering context");
539 :
540 0 : nsPresContext *presContext = aState.PresContext();
541 : AutoRestore<nsIFrame*> restoreCurrentInflationContainer(presContext->
542 0 : mCurrentInflationContainer);
543 : AutoRestore<nscoord> restoreCurrentInflationContainerWidth(presContext->
544 0 : mCurrentInflationContainerWidth);
545 0 : if (nsLayoutUtils::IsContainerForFontSizeInflation(mParent) &&
546 0 : mParent->IsBoxFrame()) {
547 0 : presContext->mCurrentInflationContainer = mParent;
548 : presContext->mCurrentInflationContainerWidth =
549 0 : mParent->GetContentRect().width;
550 : }
551 :
552 0 : nsBox *box = static_cast<nsBox*>(this);
553 0 : DISPLAY_LAYOUT(box);
554 :
555 0 : box->BeginLayout(aState);
556 :
557 0 : box->DoLayout(aState);
558 :
559 0 : box->EndLayout(aState);
560 :
561 0 : return NS_OK;
562 : }
563 :
564 : bool
565 0 : nsBox::DoesClipChildren()
566 : {
567 0 : const nsStyleDisplay* display = GetStyleDisplay();
568 0 : NS_ASSERTION((display->mOverflowY == NS_STYLE_OVERFLOW_CLIP) ==
569 : (display->mOverflowX == NS_STYLE_OVERFLOW_CLIP),
570 : "If one overflow is clip, the other should be too");
571 0 : return display->mOverflowX == NS_STYLE_OVERFLOW_CLIP;
572 : }
573 :
574 : nsresult
575 0 : nsBox::SyncLayout(nsBoxLayoutState& aState)
576 : {
577 : /*
578 : if (IsCollapsed()) {
579 : CollapseChild(aState, this, true);
580 : return NS_OK;
581 : }
582 : */
583 :
584 :
585 0 : if (GetStateBits() & NS_FRAME_IS_DIRTY)
586 0 : Redraw(aState);
587 :
588 : RemoveStateBits(NS_FRAME_HAS_DIRTY_CHILDREN | NS_FRAME_IS_DIRTY
589 0 : | NS_FRAME_FIRST_REFLOW | NS_FRAME_IN_REFLOW);
590 :
591 0 : nsPresContext* presContext = aState.PresContext();
592 :
593 0 : PRUint32 flags = 0;
594 0 : GetLayoutFlags(flags);
595 :
596 0 : PRUint32 stateFlags = aState.LayoutFlags();
597 :
598 0 : flags |= stateFlags;
599 :
600 0 : nsRect visualOverflow;
601 :
602 0 : if (ComputesOwnOverflowArea()) {
603 0 : visualOverflow = GetVisualOverflowRect();
604 : }
605 : else {
606 0 : nsRect rect(nsPoint(0, 0), GetSize());
607 0 : nsOverflowAreas overflowAreas(rect, rect);
608 0 : if (!DoesClipChildren() && !IsCollapsed()) {
609 : // See if our child frames caused us to overflow after being laid
610 : // out. If so, store the overflow area. This normally can't happen
611 : // in XUL, but it can happen with the CSS 'outline' property and
612 : // possibly with other exotic stuff (e.g. relatively positioned
613 : // frames in HTML inside XUL).
614 0 : nsLayoutUtils::UnionChildOverflow(this, overflowAreas);
615 : }
616 :
617 0 : FinishAndStoreOverflow(overflowAreas, GetSize());
618 0 : visualOverflow = overflowAreas.VisualOverflow();
619 : }
620 :
621 0 : nsIView* view = GetView();
622 0 : if (view) {
623 : // Make sure the frame's view is properly sized and positioned and has
624 : // things like opacity correct
625 : nsContainerFrame::SyncFrameViewAfterReflow(presContext, this, view,
626 0 : visualOverflow, flags);
627 : }
628 :
629 0 : return NS_OK;
630 : }
631 :
632 : nsresult
633 0 : nsIFrame::Redraw(nsBoxLayoutState& aState,
634 : const nsRect* aDamageRect)
635 : {
636 0 : if (aState.PaintingDisabled())
637 0 : return NS_OK;
638 :
639 0 : nsRect damageRect(0,0,0,0);
640 0 : if (aDamageRect)
641 0 : damageRect = *aDamageRect;
642 : else
643 0 : damageRect = GetVisualOverflowRect();
644 :
645 0 : Invalidate(damageRect);
646 : // nsStackLayout, at least, expects us to repaint descendants even
647 : // if a damage rect is provided
648 0 : FrameLayerBuilder::InvalidateThebesLayersInSubtree(this);
649 :
650 0 : return NS_OK;
651 : }
652 :
653 : bool
654 0 : nsIBox::AddCSSPrefSize(nsIBox* aBox, nsSize& aSize, bool &aWidthSet, bool &aHeightSet)
655 : {
656 0 : aWidthSet = false;
657 0 : aHeightSet = false;
658 :
659 : // add in the css min, max, pref
660 0 : const nsStylePosition* position = aBox->GetStylePosition();
661 :
662 : // see if the width or height was specifically set
663 : // XXX Handle eStyleUnit_Enumerated?
664 : // (Handling the eStyleUnit_Enumerated types requires
665 : // GetPrefSize/GetMinSize methods that don't consider
666 : // (min-/max-/)(width/height) properties.)
667 0 : const nsStyleCoord &width = position->mWidth;
668 0 : if (width.GetUnit() == eStyleUnit_Coord) {
669 0 : aSize.width = width.GetCoordValue();
670 0 : aWidthSet = true;
671 0 : } else if (width.IsCalcUnit()) {
672 0 : if (!width.CalcHasPercent()) {
673 : // pass 0 for percentage basis since we know there are no %s
674 0 : aSize.width = nsRuleNode::ComputeComputedCalc(width, 0);
675 0 : if (aSize.width < 0)
676 0 : aSize.width = 0;
677 0 : aWidthSet = true;
678 : }
679 : }
680 :
681 0 : const nsStyleCoord &height = position->mHeight;
682 0 : if (height.GetUnit() == eStyleUnit_Coord) {
683 0 : aSize.height = height.GetCoordValue();
684 0 : aHeightSet = true;
685 0 : } else if (height.IsCalcUnit()) {
686 0 : if (!height.CalcHasPercent()) {
687 : // pass 0 for percentage basis since we know there are no %s
688 0 : aSize.height = nsRuleNode::ComputeComputedCalc(height, 0);
689 0 : if (aSize.height < 0)
690 0 : aSize.height = 0;
691 0 : aHeightSet = true;
692 : }
693 : }
694 :
695 0 : nsIContent* content = aBox->GetContent();
696 : // ignore 'height' and 'width' attributes if the actual element is not XUL
697 : // For example, we might be magic XUL frames whose primary content is an HTML
698 : // <select>
699 0 : if (content && content->IsXUL()) {
700 0 : nsAutoString value;
701 : PRInt32 error;
702 :
703 0 : content->GetAttr(kNameSpaceID_None, nsGkAtoms::width, value);
704 0 : if (!value.IsEmpty()) {
705 0 : value.Trim("%");
706 :
707 : aSize.width =
708 0 : nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
709 0 : aWidthSet = true;
710 : }
711 :
712 0 : content->GetAttr(kNameSpaceID_None, nsGkAtoms::height, value);
713 0 : if (!value.IsEmpty()) {
714 0 : value.Trim("%");
715 :
716 : aSize.height =
717 0 : nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
718 0 : aHeightSet = true;
719 : }
720 : }
721 :
722 0 : return (aWidthSet && aHeightSet);
723 : }
724 :
725 :
726 : bool
727 0 : nsIBox::AddCSSMinSize(nsBoxLayoutState& aState, nsIBox* aBox, nsSize& aSize,
728 : bool &aWidthSet, bool &aHeightSet)
729 : {
730 0 : aWidthSet = false;
731 0 : aHeightSet = false;
732 :
733 0 : bool canOverride = true;
734 :
735 : // See if a native theme wants to supply a minimum size.
736 0 : const nsStyleDisplay* display = aBox->GetStyleDisplay();
737 0 : if (display->mAppearance) {
738 0 : nsITheme *theme = aState.PresContext()->GetTheme();
739 0 : if (theme && theme->ThemeSupportsWidget(aState.PresContext(), aBox, display->mAppearance)) {
740 0 : nsIntSize size;
741 0 : nsRenderingContext* rendContext = aState.GetRenderingContext();
742 0 : if (rendContext) {
743 : theme->GetMinimumWidgetSize(rendContext, aBox,
744 0 : display->mAppearance, &size, &canOverride);
745 0 : if (size.width) {
746 0 : aSize.width = aState.PresContext()->DevPixelsToAppUnits(size.width);
747 0 : aWidthSet = true;
748 : }
749 0 : if (size.height) {
750 0 : aSize.height = aState.PresContext()->DevPixelsToAppUnits(size.height);
751 0 : aHeightSet = true;
752 : }
753 : }
754 : }
755 : }
756 :
757 : // add in the css min, max, pref
758 0 : const nsStylePosition* position = aBox->GetStylePosition();
759 :
760 : // same for min size. Unfortunately min size is always set to 0. So for now
761 : // we will assume 0 (as a coord) means not set.
762 0 : const nsStyleCoord &minWidth = position->mMinWidth;
763 0 : if ((minWidth.GetUnit() == eStyleUnit_Coord &&
764 0 : minWidth.GetCoordValue() != 0) ||
765 0 : (minWidth.IsCalcUnit() && !minWidth.CalcHasPercent())) {
766 0 : nscoord min = nsRuleNode::ComputeCoordPercentCalc(minWidth, 0);
767 0 : if (!aWidthSet || (min > aSize.width && canOverride)) {
768 0 : aSize.width = min;
769 0 : aWidthSet = true;
770 : }
771 0 : } else if (minWidth.GetUnit() == eStyleUnit_Percent) {
772 0 : NS_ASSERTION(minWidth.GetPercentValue() == 0.0f,
773 : "Non-zero percentage values not currently supported");
774 0 : aSize.width = 0;
775 0 : aWidthSet = true; // FIXME: should we really do this for
776 : // nonzero values?
777 : }
778 : // XXX Handle eStyleUnit_Enumerated?
779 : // (Handling the eStyleUnit_Enumerated types requires
780 : // GetPrefSize/GetMinSize methods that don't consider
781 : // (min-/max-/)(width/height) properties.
782 : // calc() with percentage is treated like '0' (unset)
783 :
784 0 : const nsStyleCoord &minHeight = position->mMinHeight;
785 0 : if ((minHeight.GetUnit() == eStyleUnit_Coord &&
786 0 : minHeight.GetCoordValue() != 0) ||
787 0 : (minHeight.IsCalcUnit() && !minHeight.CalcHasPercent())) {
788 0 : nscoord min = nsRuleNode::ComputeCoordPercentCalc(minHeight, 0);
789 0 : if (!aHeightSet || (min > aSize.height && canOverride)) {
790 0 : aSize.height = min;
791 0 : aHeightSet = true;
792 : }
793 0 : } else if (minHeight.GetUnit() == eStyleUnit_Percent) {
794 0 : NS_ASSERTION(position->mMinHeight.GetPercentValue() == 0.0f,
795 : "Non-zero percentage values not currently supported");
796 0 : aSize.height = 0;
797 0 : aHeightSet = true; // FIXME: should we really do this for
798 : // nonzero values?
799 : }
800 : // calc() with percentage is treated like '0' (unset)
801 :
802 0 : nsIContent* content = aBox->GetContent();
803 0 : if (content && content->IsXUL()) {
804 0 : nsAutoString value;
805 : PRInt32 error;
806 :
807 0 : content->GetAttr(kNameSpaceID_None, nsGkAtoms::minwidth, value);
808 0 : if (!value.IsEmpty())
809 : {
810 0 : value.Trim("%");
811 :
812 : nscoord val =
813 0 : nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
814 0 : if (val > aSize.width)
815 0 : aSize.width = val;
816 0 : aWidthSet = true;
817 : }
818 :
819 0 : content->GetAttr(kNameSpaceID_None, nsGkAtoms::minheight, value);
820 0 : if (!value.IsEmpty())
821 : {
822 0 : value.Trim("%");
823 :
824 : nscoord val =
825 0 : nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
826 0 : if (val > aSize.height)
827 0 : aSize.height = val;
828 :
829 0 : aHeightSet = true;
830 : }
831 : }
832 :
833 0 : return (aWidthSet && aHeightSet);
834 : }
835 :
836 : bool
837 0 : nsIBox::AddCSSMaxSize(nsIBox* aBox, nsSize& aSize, bool &aWidthSet, bool &aHeightSet)
838 : {
839 0 : aWidthSet = false;
840 0 : aHeightSet = false;
841 :
842 : // add in the css min, max, pref
843 0 : const nsStylePosition* position = aBox->GetStylePosition();
844 :
845 : // and max
846 : // see if the width or height was specifically set
847 : // XXX Handle eStyleUnit_Enumerated?
848 : // (Handling the eStyleUnit_Enumerated types requires
849 : // GetPrefSize/GetMinSize methods that don't consider
850 : // (min-/max-/)(width/height) properties.)
851 0 : const nsStyleCoord maxWidth = position->mMaxWidth;
852 0 : if (maxWidth.ConvertsToLength()) {
853 0 : aSize.width = nsRuleNode::ComputeCoordPercentCalc(maxWidth, 0);
854 0 : aWidthSet = true;
855 : }
856 : // percentages and calc() with percentages are treated like 'none'
857 :
858 0 : const nsStyleCoord &maxHeight = position->mMaxHeight;
859 0 : if (maxHeight.ConvertsToLength()) {
860 0 : aSize.height = nsRuleNode::ComputeCoordPercentCalc(maxHeight, 0);
861 0 : aHeightSet = true;
862 : }
863 : // percentages and calc() with percentages are treated like 'none'
864 :
865 0 : nsIContent* content = aBox->GetContent();
866 0 : if (content && content->IsXUL()) {
867 0 : nsAutoString value;
868 : PRInt32 error;
869 :
870 0 : content->GetAttr(kNameSpaceID_None, nsGkAtoms::maxwidth, value);
871 0 : if (!value.IsEmpty()) {
872 0 : value.Trim("%");
873 :
874 : nscoord val =
875 0 : nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
876 0 : aSize.width = val;
877 0 : aWidthSet = true;
878 : }
879 :
880 0 : content->GetAttr(kNameSpaceID_None, nsGkAtoms::maxheight, value);
881 0 : if (!value.IsEmpty()) {
882 0 : value.Trim("%");
883 :
884 : nscoord val =
885 0 : nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
886 0 : aSize.height = val;
887 :
888 0 : aHeightSet = true;
889 : }
890 : }
891 :
892 0 : return (aWidthSet || aHeightSet);
893 : }
894 :
895 : bool
896 0 : nsIBox::AddCSSFlex(nsBoxLayoutState& aState, nsIBox* aBox, nscoord& aFlex)
897 : {
898 0 : bool flexSet = false;
899 :
900 : // get the flexibility
901 0 : aFlex = aBox->GetStyleXUL()->mBoxFlex;
902 :
903 : // attribute value overrides CSS
904 0 : nsIContent* content = aBox->GetContent();
905 0 : if (content && content->IsXUL()) {
906 : PRInt32 error;
907 0 : nsAutoString value;
908 :
909 0 : content->GetAttr(kNameSpaceID_None, nsGkAtoms::flex, value);
910 0 : if (!value.IsEmpty()) {
911 0 : value.Trim("%");
912 0 : aFlex = value.ToInteger(&error);
913 0 : flexSet = true;
914 : }
915 : }
916 :
917 0 : if (aFlex < 0)
918 0 : aFlex = 0;
919 0 : if (aFlex >= nscoord_MAX)
920 0 : aFlex = nscoord_MAX - 1;
921 :
922 0 : return flexSet || aFlex > 0;
923 : }
924 :
925 : void
926 0 : nsBox::AddBorderAndPadding(nsSize& aSize)
927 : {
928 0 : AddBorderAndPadding(this, aSize);
929 0 : }
930 :
931 : void
932 0 : nsBox::AddBorderAndPadding(nsIBox* aBox, nsSize& aSize)
933 : {
934 0 : nsMargin borderPadding(0,0,0,0);
935 0 : aBox->GetBorderAndPadding(borderPadding);
936 0 : AddMargin(aSize, borderPadding);
937 0 : }
938 :
939 : void
940 0 : nsBox::AddMargin(nsIBox* aChild, nsSize& aSize)
941 : {
942 0 : nsMargin margin(0,0,0,0);
943 0 : aChild->GetMargin(margin);
944 0 : AddMargin(aSize, margin);
945 0 : }
946 :
947 : void
948 0 : nsBox::AddMargin(nsSize& aSize, const nsMargin& aMargin)
949 : {
950 0 : if (aSize.width != NS_INTRINSICSIZE)
951 0 : aSize.width += aMargin.left + aMargin.right;
952 :
953 0 : if (aSize.height != NS_INTRINSICSIZE)
954 0 : aSize.height += aMargin.top + aMargin.bottom;
955 0 : }
956 :
957 : nscoord
958 0 : nsBox::BoundsCheck(nscoord aMin, nscoord aPref, nscoord aMax)
959 : {
960 0 : if (aPref > aMax)
961 0 : aPref = aMax;
962 :
963 0 : if (aPref < aMin)
964 0 : aPref = aMin;
965 :
966 0 : return aPref;
967 : }
968 :
969 : nsSize
970 0 : nsBox::BoundsCheckMinMax(const nsSize& aMinSize, const nsSize& aMaxSize)
971 : {
972 0 : return nsSize(NS_MAX(aMaxSize.width, aMinSize.width),
973 0 : NS_MAX(aMaxSize.height, aMinSize.height));
974 : }
975 :
976 : nsSize
977 0 : nsBox::BoundsCheck(const nsSize& aMinSize, const nsSize& aPrefSize, const nsSize& aMaxSize)
978 : {
979 : return nsSize(BoundsCheck(aMinSize.width, aPrefSize.width, aMaxSize.width),
980 0 : BoundsCheck(aMinSize.height, aPrefSize.height, aMaxSize.height));
981 : }
982 :
983 : #ifdef DEBUG_LAYOUT
984 : nsresult
985 : nsBox::SetDebug(nsBoxLayoutState& aState, bool aDebug)
986 : {
987 : return NS_OK;
988 : }
989 :
990 : NS_IMETHODIMP
991 : nsBox::GetDebugBoxAt( const nsPoint& aPoint,
992 : nsIBox** aBox)
993 : {
994 : nsRect thisRect(nsPoint(0,0), GetSize());
995 : if (!thisRect.Contains(aPoint))
996 : return NS_ERROR_FAILURE;
997 :
998 : nsIBox* child = GetChildBox();
999 : nsIBox* hit = nsnull;
1000 :
1001 : *aBox = nsnull;
1002 : while (nsnull != child) {
1003 : nsresult rv = child->GetDebugBoxAt(aPoint - child->GetOffsetTo(this), &hit);
1004 :
1005 : if (NS_SUCCEEDED(rv) && hit) {
1006 : *aBox = hit;
1007 : }
1008 : child = child->GetNextBox();
1009 : }
1010 :
1011 : // found a child
1012 : if (*aBox) {
1013 : return NS_OK;
1014 : }
1015 :
1016 : return NS_ERROR_FAILURE;
1017 : }
1018 :
1019 :
1020 : NS_IMETHODIMP
1021 : nsBox::GetDebug(bool& aDebug)
1022 : {
1023 : aDebug = false;
1024 : return NS_OK;
1025 : }
1026 :
1027 : #endif
|