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