Bug 1708422: part 17) Rename `words` to `normalizedWords` in `mozInlineSpellChecker...
[gecko.git] / layout / xul / nsBox.cpp
blob5cc1af536b53f0d5a7d7ed70b19c9f95a557736f
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "mozilla/Attributes.h"
8 #include "mozilla/StaticPtr.h"
9 #include "nsIFrame.h"
11 #include "nsBoxLayoutState.h"
12 #include "nsBoxFrame.h"
13 #include "nsDOMAttributeMap.h"
14 #include "nsPresContext.h"
15 #include "nsCOMPtr.h"
16 #include "nsIContent.h"
17 #include "nsContainerFrame.h"
18 #include "nsNameSpaceManager.h"
19 #include "nsGkAtoms.h"
20 #include "nsITheme.h"
21 #include "nsBoxLayout.h"
22 #include "nsLayoutUtils.h"
23 #include "FrameLayerBuilder.h"
24 #include "mozilla/dom/Attr.h"
25 #include "mozilla/dom/Element.h"
26 #include <algorithm>
28 using namespace mozilla;
30 nsresult nsIFrame::BeginXULLayout(nsBoxLayoutState& aState) {
31 // mark ourselves as dirty so no child under us
32 // can post an incremental layout.
33 // XXXldb Is this still needed?
34 AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
36 if (HasAnyStateBits(NS_FRAME_IS_DIRTY)) {
37 // If the parent is dirty, all the children are dirty (ReflowInput
38 // does this too).
39 nsIFrame* box;
40 for (box = GetChildXULBox(this); box; box = GetNextXULBox(box))
41 box->MarkSubtreeDirty();
44 // Another copy-over from ReflowInput.
45 // Since we are in reflow, we don't need to store these properties anymore.
46 RemoveProperty(UsedBorderProperty());
47 RemoveProperty(UsedPaddingProperty());
48 RemoveProperty(UsedMarginProperty());
50 return NS_OK;
53 nsresult nsIFrame::EndXULLayout(nsBoxLayoutState& aState) {
54 return SyncXULLayout(aState);
57 nsresult nsIFrame::GetXULClientRect(nsRect& aClientRect) {
58 aClientRect = mRect;
59 aClientRect.MoveTo(0, 0);
61 nsMargin borderPadding;
62 GetXULBorderAndPadding(borderPadding);
64 aClientRect.Deflate(borderPadding);
66 if (aClientRect.width < 0) aClientRect.width = 0;
68 if (aClientRect.height < 0) aClientRect.height = 0;
70 return NS_OK;
73 void nsIFrame::SetXULBounds(nsBoxLayoutState& aState, const nsRect& aRect,
74 bool aRemoveOverflowAreas) {
75 nsRect rect(mRect);
77 ReflowChildFlags flags = GetXULLayoutFlags() | aState.LayoutFlags();
79 if ((flags & ReflowChildFlags::NoMoveFrame) ==
80 ReflowChildFlags::NoMoveFrame) {
81 SetSize(aRect.Size());
82 } else {
83 SetRect(aRect);
86 // Nuke the overflow area. The caller is responsible for restoring
87 // it if necessary.
88 if (aRemoveOverflowAreas) {
89 // remove the previously stored overflow area
90 ClearOverflowRects();
93 if (!(flags & ReflowChildFlags::NoMoveView)) {
94 nsContainerFrame::PositionFrameView(this);
95 if ((rect.x != aRect.x) || (rect.y != aRect.y))
96 nsContainerFrame::PositionChildViews(this);
100 nsresult nsIFrame::GetXULBorderAndPadding(nsMargin& aBorderAndPadding) {
101 aBorderAndPadding.SizeTo(0, 0, 0, 0);
102 nsresult rv = GetXULBorder(aBorderAndPadding);
103 if (NS_FAILED(rv)) return rv;
105 nsMargin padding;
106 rv = GetXULPadding(padding);
107 if (NS_FAILED(rv)) return rv;
109 aBorderAndPadding += padding;
111 return rv;
114 nsresult nsIFrame::GetXULBorder(nsMargin& aBorder) {
115 aBorder.SizeTo(0, 0, 0, 0);
117 StyleAppearance appearance = StyleDisplay()->EffectiveAppearance();
118 if (appearance != StyleAppearance::None) {
119 // Go to the theme for the border.
120 nsPresContext* pc = PresContext();
121 nsITheme* theme = pc->Theme();
122 if (theme->ThemeSupportsWidget(pc, this, appearance)) {
123 LayoutDeviceIntMargin margin =
124 theme->GetWidgetBorder(pc->DeviceContext(), this, appearance);
125 aBorder =
126 LayoutDevicePixel::ToAppUnits(margin, pc->AppUnitsPerDevPixel());
127 return NS_OK;
131 aBorder = StyleBorder()->GetComputedBorder();
133 return NS_OK;
136 nsresult nsIFrame::GetXULPadding(nsMargin& aBorderAndPadding) {
137 StyleAppearance appearance = StyleDisplay()->EffectiveAppearance();
138 if (appearance != StyleAppearance::None) {
139 // Go to the theme for the padding.
140 nsPresContext* pc = PresContext();
141 nsITheme* theme = pc->Theme();
142 if (theme->ThemeSupportsWidget(pc, this, appearance)) {
143 LayoutDeviceIntMargin padding;
144 bool useThemePadding = theme->GetWidgetPadding(pc->DeviceContext(), this,
145 appearance, &padding);
146 if (useThemePadding) {
147 aBorderAndPadding =
148 LayoutDevicePixel::ToAppUnits(padding, pc->AppUnitsPerDevPixel());
149 return NS_OK;
154 aBorderAndPadding.SizeTo(0, 0, 0, 0);
155 StylePadding()->GetPadding(aBorderAndPadding);
157 return NS_OK;
160 nsresult nsIFrame::GetXULMargin(nsMargin& aMargin) {
161 aMargin.SizeTo(0, 0, 0, 0);
162 StyleMargin()->GetMargin(aMargin);
164 return NS_OK;
167 void nsIFrame::XULSizeNeedsRecalc(nsSize& aSize) {
168 aSize.width = -1;
169 aSize.height = -1;
172 void nsIFrame::XULCoordNeedsRecalc(nscoord& aCoord) { aCoord = -1; }
174 bool nsIFrame::XULNeedsRecalc(const nsSize& aSize) {
175 return (aSize.width == -1 || aSize.height == -1);
178 bool nsIFrame::XULNeedsRecalc(nscoord aCoord) { return (aCoord == -1); }
180 nsSize nsIFrame::GetUncachedXULPrefSize(nsBoxLayoutState& aBoxLayoutState) {
181 NS_ASSERTION(aBoxLayoutState.GetRenderingContext(),
182 "must have rendering context");
184 nsSize pref(0, 0);
185 DISPLAY_PREF_SIZE(this, pref);
187 if (IsXULCollapsed()) {
188 return pref;
191 AddXULBorderAndPadding(pref);
192 bool widthSet, heightSet;
193 nsIFrame::AddXULPrefSize(this, pref, widthSet, heightSet);
195 nsSize minSize = GetXULMinSize(aBoxLayoutState);
196 nsSize maxSize = GetXULMaxSize(aBoxLayoutState);
197 return XULBoundsCheck(minSize, pref, maxSize);
200 nsSize nsIFrame::GetUncachedXULMinSize(nsBoxLayoutState& aBoxLayoutState) {
201 NS_ASSERTION(aBoxLayoutState.GetRenderingContext(),
202 "must have rendering context");
204 nsSize min(0, 0);
205 DISPLAY_MIN_SIZE(this, min);
207 if (IsXULCollapsed()) {
208 return min;
211 AddXULBorderAndPadding(min);
212 bool widthSet, heightSet;
213 nsIFrame::AddXULMinSize(this, min, widthSet, heightSet);
214 return min;
217 nsSize nsIFrame::GetXULMinSizeForScrollArea(nsBoxLayoutState& aBoxLayoutState) {
218 return nsSize(0, 0);
221 nsSize nsIFrame::GetUncachedXULMaxSize(nsBoxLayoutState& aBoxLayoutState) {
222 NS_ASSERTION(aBoxLayoutState.GetRenderingContext(),
223 "must have rendering context");
225 nsSize maxSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
226 DISPLAY_MAX_SIZE(this, maxSize);
228 if (IsXULCollapsed()) {
229 return maxSize;
232 AddXULBorderAndPadding(maxSize);
233 bool widthSet, heightSet;
234 nsIFrame::AddXULMaxSize(this, maxSize, widthSet, heightSet);
235 return maxSize;
238 bool nsIFrame::IsXULCollapsed() {
239 return StyleVisibility()->mVisible == StyleVisibility::Collapse;
242 nsresult nsIFrame::XULLayout(nsBoxLayoutState& aState) {
243 NS_ASSERTION(aState.GetRenderingContext(), "must have rendering context");
245 nsIFrame* box = static_cast<nsIFrame*>(this);
246 DISPLAY_LAYOUT(box);
248 box->BeginXULLayout(aState);
250 box->DoXULLayout(aState);
252 box->EndXULLayout(aState);
254 return NS_OK;
257 bool nsIFrame::DoesClipChildrenInBothAxes() {
258 const nsStyleDisplay* display = StyleDisplay();
259 return display->mOverflowX == StyleOverflow::Clip &&
260 display->mOverflowY == StyleOverflow::Clip;
263 nsresult nsIFrame::SyncXULLayout(nsBoxLayoutState& aBoxLayoutState) {
265 if (IsXULCollapsed()) {
266 CollapseChild(aBoxLayoutState, this, true);
267 return NS_OK;
271 if (HasAnyStateBits(NS_FRAME_IS_DIRTY)) {
272 XULRedraw(aBoxLayoutState);
275 RemoveStateBits(NS_FRAME_HAS_DIRTY_CHILDREN | NS_FRAME_IS_DIRTY |
276 NS_FRAME_FIRST_REFLOW | NS_FRAME_IN_REFLOW);
278 nsPresContext* presContext = aBoxLayoutState.PresContext();
280 ReflowChildFlags flags = GetXULLayoutFlags() | aBoxLayoutState.LayoutFlags();
282 nsRect inkOverflow;
284 if (XULComputesOwnOverflowArea()) {
285 inkOverflow = InkOverflowRect();
286 } else {
287 nsRect rect(nsPoint(0, 0), GetSize());
288 OverflowAreas overflowAreas(rect, rect);
289 if (!DoesClipChildrenInBothAxes() && !IsXULCollapsed()) {
290 // See if our child frames caused us to overflow after being laid
291 // out. If so, store the overflow area. This normally can't happen
292 // in XUL, but it can happen with the CSS 'outline' property and
293 // possibly with other exotic stuff (e.g. relatively positioned
294 // frames in HTML inside XUL).
295 nsLayoutUtils::UnionChildOverflow(this, overflowAreas);
298 FinishAndStoreOverflow(overflowAreas, GetSize());
299 inkOverflow = overflowAreas.InkOverflow();
302 nsView* view = GetView();
303 if (view) {
304 // Make sure the frame's view is properly sized and positioned and has
305 // things like opacity correct
306 nsContainerFrame::SyncFrameViewAfterReflow(presContext, this, view,
307 inkOverflow, flags);
310 return NS_OK;
313 nsresult nsIFrame::XULRedraw(nsBoxLayoutState& aState) {
314 if (aState.PaintingDisabled()) return NS_OK;
316 // nsStackLayout, at least, expects us to repaint descendants even
317 // if a damage rect is provided
318 InvalidateFrameSubtree();
320 return NS_OK;
323 bool nsIFrame::AddXULPrefSize(nsIFrame* aBox, nsSize& aSize, bool& aWidthSet,
324 bool& aHeightSet) {
325 aWidthSet = false;
326 aHeightSet = false;
328 // add in the css min, max, pref
329 const nsStylePosition* position = aBox->StylePosition();
331 // see if the width or height was specifically set
332 // XXX Handle eStyleUnit_Enumerated?
333 // (Handling the eStyleUnit_Enumerated types requires
334 // GetXULPrefSize/GetXULMinSize methods that don't consider
335 // (min-/max-/)(width/height) properties.)
336 const auto& width = position->mWidth;
337 if (width.ConvertsToLength()) {
338 aSize.width = std::max(0, width.ToLength());
339 aWidthSet = true;
342 const auto& height = position->mHeight;
343 if (height.ConvertsToLength()) {
344 aSize.height = std::max(0, height.ToLength());
345 aHeightSet = true;
348 nsIContent* content = aBox->GetContent();
349 // ignore 'height' and 'width' attributes if the actual element is not XUL
350 // For example, we might be magic XUL frames whose primary content is an HTML
351 // <select>
352 if (content && content->IsXULElement()) {
353 nsAutoString value;
354 nsresult error;
356 content->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::width, value);
357 if (!value.IsEmpty()) {
358 value.Trim("%");
360 aSize.width = nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
361 aWidthSet = true;
364 content->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::height, value);
365 if (!value.IsEmpty()) {
366 value.Trim("%");
368 aSize.height =
369 nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
370 aHeightSet = true;
374 return (aWidthSet && aHeightSet);
377 bool nsIFrame::AddXULMinSize(nsIFrame* aBox, nsSize& aSize, bool& aWidthSet,
378 bool& aHeightSet) {
379 aWidthSet = false;
380 aHeightSet = false;
382 bool canOverride = true;
384 nsPresContext* pc = aBox->PresContext();
386 // See if a native theme wants to supply a minimum size.
387 const nsStyleDisplay* display = aBox->StyleDisplay();
388 if (display->HasAppearance()) {
389 nsITheme* theme = pc->Theme();
390 StyleAppearance appearance = display->EffectiveAppearance();
391 if (theme->ThemeSupportsWidget(pc, aBox, appearance)) {
392 LayoutDeviceIntSize size;
393 theme->GetMinimumWidgetSize(pc, aBox, appearance, &size, &canOverride);
394 if (size.width) {
395 aSize.width = pc->DevPixelsToAppUnits(size.width);
396 aWidthSet = true;
398 if (size.height) {
399 aSize.height = pc->DevPixelsToAppUnits(size.height);
400 aHeightSet = true;
402 } else {
403 switch (appearance) {
404 case StyleAppearance::ScrollbarVertical:
405 case StyleAppearance::ScrollbarHorizontal: {
406 ComputedStyle* style = nsLayoutUtils::StyleForScrollbar(aBox);
407 auto sizes = theme->GetScrollbarSizes(
408 pc, style->StyleUIReset()->mScrollbarWidth,
409 nsITheme::Overlay::No);
410 if (appearance == StyleAppearance::ScrollbarVertical) {
411 aSize.width = pc->DevPixelsToAppUnits(sizes.mVertical);
412 aWidthSet = true;
413 } else {
414 aSize.height = pc->DevPixelsToAppUnits(sizes.mHorizontal);
415 aHeightSet = true;
417 break;
419 default:
420 break;
425 // add in the css min, max, pref
426 const nsStylePosition* position = aBox->StylePosition();
427 const auto& minWidth = position->mMinWidth;
428 if (minWidth.ConvertsToLength()) {
429 nscoord min = minWidth.ToLength();
430 if (!aWidthSet || (min > aSize.width && canOverride)) {
431 aSize.width = min;
432 aWidthSet = true;
434 } else if (minWidth.ConvertsToPercentage()) {
435 NS_ASSERTION(minWidth.ToPercentage() == 0.0f,
436 "Non-zero percentage values not currently supported");
437 aSize.width = 0;
438 aWidthSet = true; // FIXME: should we really do this for
439 // nonzero values?
441 // XXX Handle ExtremumLength?
442 // (Handling them requires GetXULPrefSize/GetXULMinSize methods that don't
443 // consider (min-/max-/)(width/height) properties.
444 // calc() with percentage is treated like '0' (unset)
446 const auto& minHeight = position->mMinHeight;
447 if (minHeight.ConvertsToLength()) {
448 nscoord min = minHeight.ToLength();
449 if (!aHeightSet || (min > aSize.height && canOverride)) {
450 aSize.height = min;
451 aHeightSet = true;
453 } else if (minHeight.ConvertsToPercentage()) {
454 NS_ASSERTION(position->mMinHeight.ToPercentage() == 0.0f,
455 "Non-zero percentage values not currently supported");
456 aSize.height = 0;
457 aHeightSet = true; // FIXME: should we really do this for
458 // nonzero values?
460 // calc() with percentage is treated like '0' (unset)
462 nsIContent* content = aBox->GetContent();
463 if (content && content->IsXULElement()) {
464 nsAutoString value;
465 nsresult error;
467 content->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::minwidth,
468 value);
469 if (!value.IsEmpty()) {
470 value.Trim("%");
472 nscoord val = nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
473 if (val > aSize.width) aSize.width = val;
474 aWidthSet = true;
477 content->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::minheight,
478 value);
479 if (!value.IsEmpty()) {
480 value.Trim("%");
482 nscoord val = nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
483 if (val > aSize.height) aSize.height = val;
485 aHeightSet = true;
489 return (aWidthSet && aHeightSet);
492 bool nsIFrame::AddXULMaxSize(nsIFrame* aBox, nsSize& aSize, bool& aWidthSet,
493 bool& aHeightSet) {
494 aWidthSet = false;
495 aHeightSet = false;
497 // add in the css min, max, pref
498 const nsStylePosition* position = aBox->StylePosition();
500 // and max
501 // see if the width or height was specifically set
502 // XXX Handle eStyleUnit_Enumerated?
503 // (Handling the eStyleUnit_Enumerated types requires
504 // GetXULPrefSize/GetXULMinSize methods that don't consider
505 // (min-/max-/)(width/height) properties.)
506 const auto& maxWidth = position->mMaxWidth;
507 if (maxWidth.ConvertsToLength()) {
508 aSize.width = maxWidth.ToLength();
509 aWidthSet = true;
511 // percentages and calc() with percentages are treated like 'none'
513 const auto& maxHeight = position->mMaxHeight;
514 if (maxHeight.ConvertsToLength()) {
515 aSize.height = maxHeight.ToLength();
516 aHeightSet = true;
518 // percentages and calc() with percentages are treated like 'none'
520 nsIContent* content = aBox->GetContent();
521 if (content && content->IsXULElement()) {
522 nsAutoString value;
523 nsresult error;
525 content->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::maxwidth,
526 value);
527 if (!value.IsEmpty()) {
528 value.Trim("%");
530 nscoord val = nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
531 aSize.width = val;
532 aWidthSet = true;
535 content->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::maxheight,
536 value);
537 if (!value.IsEmpty()) {
538 value.Trim("%");
540 nscoord val = nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
541 aSize.height = val;
543 aHeightSet = true;
547 return (aWidthSet || aHeightSet);
550 bool nsIFrame::AddXULFlex(nsIFrame* aBox, nscoord& aFlex) {
551 bool flexSet = false;
553 // get the flexibility
554 aFlex = aBox->StyleXUL()->mBoxFlex;
556 // attribute value overrides CSS
557 nsIContent* content = aBox->GetContent();
558 if (content && content->IsXULElement()) {
559 nsresult error;
560 nsAutoString value;
562 content->AsElement()->GetAttr(kNameSpaceID_None, nsGkAtoms::flex, value);
563 if (!value.IsEmpty()) {
564 value.Trim("%");
565 aFlex = value.ToInteger(&error);
566 flexSet = true;
570 if (aFlex < 0) aFlex = 0;
571 if (aFlex >= nscoord_MAX) aFlex = nscoord_MAX - 1;
573 return flexSet || aFlex > 0;
576 void nsIFrame::AddXULBorderAndPadding(nsSize& aSize) {
577 AddXULBorderAndPadding(this, aSize);
580 void nsIFrame::AddXULBorderAndPadding(nsIFrame* aBox, nsSize& aSize) {
581 nsMargin borderPadding(0, 0, 0, 0);
582 aBox->GetXULBorderAndPadding(borderPadding);
583 AddXULMargin(aSize, borderPadding);
586 void nsIFrame::AddXULMargin(nsIFrame* aChild, nsSize& aSize) {
587 nsMargin margin(0, 0, 0, 0);
588 aChild->GetXULMargin(margin);
589 AddXULMargin(aSize, margin);
592 void nsIFrame::AddXULMargin(nsSize& aSize, const nsMargin& aMargin) {
593 if (aSize.width != NS_UNCONSTRAINEDSIZE)
594 aSize.width += aMargin.left + aMargin.right;
596 if (aSize.height != NS_UNCONSTRAINEDSIZE)
597 aSize.height += aMargin.top + aMargin.bottom;
600 nscoord nsIFrame::XULBoundsCheck(nscoord aMin, nscoord aPref, nscoord aMax) {
601 if (aPref > aMax) aPref = aMax;
603 if (aPref < aMin) aPref = aMin;
605 return aPref;
608 nsSize nsIFrame::XULBoundsCheckMinMax(const nsSize& aMinSize,
609 const nsSize& aMaxSize) {
610 return nsSize(std::max(aMaxSize.width, aMinSize.width),
611 std::max(aMaxSize.height, aMinSize.height));
614 nsSize nsIFrame::XULBoundsCheck(const nsSize& aMinSize, const nsSize& aPrefSize,
615 const nsSize& aMaxSize) {
616 return nsSize(
617 XULBoundsCheck(aMinSize.width, aPrefSize.width, aMaxSize.width),
618 XULBoundsCheck(aMinSize.height, aPrefSize.height, aMaxSize.height));
621 /*static*/
622 nsIFrame* nsIFrame::GetChildXULBox(const nsIFrame* aFrame) {
623 // box layout ends at box-wrapped frames, so don't allow these frames
624 // to report child boxes.
625 return aFrame->IsXULBoxFrame() ? aFrame->PrincipalChildList().FirstChild()
626 : nullptr;
629 /*static*/
630 nsIFrame* nsIFrame::GetNextXULBox(const nsIFrame* aFrame) {
631 return aFrame->GetParent() && aFrame->GetParent()->IsXULBoxFrame()
632 ? aFrame->GetNextSibling()
633 : nullptr;
636 /*static*/
637 nsIFrame* nsIFrame::GetParentXULBox(const nsIFrame* aFrame) {
638 return aFrame->GetParent() && aFrame->GetParent()->IsXULBoxFrame()
639 ? aFrame->GetParent()
640 : nullptr;