layout/xul/nsStackLayout.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

     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/. */
     6 //
     7 // Eric Vaughan
     8 // Netscape Communications
     9 //
    10 // See documentation in associated header file
    11 //
    13 #include "nsStackLayout.h"
    14 #include "nsCOMPtr.h"
    15 #include "nsBoxLayoutState.h"
    16 #include "nsBox.h"
    17 #include "nsBoxFrame.h"
    18 #include "nsGkAtoms.h"
    19 #include "nsIContent.h"
    20 #include "nsNameSpaceManager.h"
    22 using namespace mozilla;
    24 nsBoxLayout* nsStackLayout::gInstance = nullptr;
    26 #define SPECIFIED_LEFT (1 << NS_SIDE_LEFT)
    27 #define SPECIFIED_RIGHT (1 << NS_SIDE_RIGHT)
    28 #define SPECIFIED_TOP (1 << NS_SIDE_TOP)
    29 #define SPECIFIED_BOTTOM (1 << NS_SIDE_BOTTOM)
    31 nsresult
    32 NS_NewStackLayout( nsIPresShell* aPresShell, nsCOMPtr<nsBoxLayout>& aNewLayout)
    33 {
    34   if (!nsStackLayout::gInstance) {
    35     nsStackLayout::gInstance = new nsStackLayout();
    36     NS_IF_ADDREF(nsStackLayout::gInstance);
    37   }
    38   // we have not instance variables so just return our static one.
    39   aNewLayout = nsStackLayout::gInstance;
    40   return NS_OK;
    41 } 
    43 /*static*/ void
    44 nsStackLayout::Shutdown()
    45 {
    46   NS_IF_RELEASE(gInstance);
    47 }
    49 nsStackLayout::nsStackLayout()
    50 {
    51 }
    53 /*
    54  * Sizing: we are as wide as the widest child plus its left offset
    55  * we are tall as the tallest child plus its top offset.
    56  *
    57  * Only children which have -moz-stack-sizing set to stretch-to-fit
    58  * (the default) will be included in the size computations.
    59  */
    61 nsSize
    62 nsStackLayout::GetPrefSize(nsIFrame* aBox, nsBoxLayoutState& aState)
    63 {
    64   nsSize prefSize (0, 0);
    66   nsIFrame* child = aBox->GetChildBox();
    67   while (child) {
    68     if (child->StyleXUL()->mStretchStack) {
    69       nsSize pref = child->GetPrefSize(aState);
    71       AddMargin(child, pref);
    72       nsMargin offset;
    73       GetOffset(aState, child, offset);
    74       pref.width += offset.LeftRight();
    75       pref.height += offset.TopBottom();
    76       AddLargestSize(prefSize, pref);
    77     }
    79     child = child->GetNextBox();
    80   }
    82   AddBorderAndPadding(aBox, prefSize);
    84   return prefSize;
    85 }
    87 nsSize
    88 nsStackLayout::GetMinSize(nsIFrame* aBox, nsBoxLayoutState& aState)
    89 {
    90   nsSize minSize (0, 0);
    92   nsIFrame* child = aBox->GetChildBox();
    93   while (child) {
    94     if (child->StyleXUL()->mStretchStack) {
    95       nsSize min = child->GetMinSize(aState);
    97       AddMargin(child, min);
    98       nsMargin offset;
    99       GetOffset(aState, child, offset);
   100       min.width += offset.LeftRight();
   101       min.height += offset.TopBottom();
   102       AddLargestSize(minSize, min);
   103     }
   105     child = child->GetNextBox();
   106   }
   108   AddBorderAndPadding(aBox, minSize);
   110   return minSize;
   111 }
   113 nsSize
   114 nsStackLayout::GetMaxSize(nsIFrame* aBox, nsBoxLayoutState& aState)
   115 {
   116   nsSize maxSize (NS_INTRINSICSIZE, NS_INTRINSICSIZE);
   118   nsIFrame* child = aBox->GetChildBox();
   119   while (child) {
   120     if (child->StyleXUL()->mStretchStack) {
   121       nsSize min = child->GetMinSize(aState);
   122       nsSize max = child->GetMaxSize(aState);
   124       max = nsBox::BoundsCheckMinMax(min, max);
   126       AddMargin(child, max);
   127       nsMargin offset;
   128       GetOffset(aState, child, offset);
   129       max.width += offset.LeftRight();
   130       max.height += offset.TopBottom();
   131       AddSmallestSize(maxSize, max);
   132     }
   134     child = child->GetNextBox();
   135   }
   137   AddBorderAndPadding(aBox, maxSize);
   139   return maxSize;
   140 }
   143 nscoord
   144 nsStackLayout::GetAscent(nsIFrame* aBox, nsBoxLayoutState& aState)
   145 {
   146   nscoord vAscent = 0;
   148   nsIFrame* child = aBox->GetChildBox();
   149   while (child) {  
   150     nscoord ascent = child->GetBoxAscent(aState);
   151     nsMargin margin;
   152     child->GetMargin(margin);
   153     ascent += margin.top;
   154     if (ascent > vAscent)
   155       vAscent = ascent;
   157     child = child->GetNextBox();
   158   }
   160   return vAscent;
   161 }
   163 uint8_t
   164 nsStackLayout::GetOffset(nsBoxLayoutState& aState, nsIFrame* aChild, nsMargin& aOffset)
   165 {
   166   aOffset = nsMargin(0, 0, 0, 0);
   168   // get the left, right, top and bottom offsets
   170   // As an optimization, we cache the fact that we are not positioned to avoid
   171   // wasting time fetching attributes.
   172   if (aChild->IsBoxFrame() &&
   173       (aChild->GetStateBits() & NS_STATE_STACK_NOT_POSITIONED))
   174     return 0;
   176   uint8_t offsetSpecified = 0;
   177   nsIContent* content = aChild->GetContent();
   178   if (content) {
   179     bool ltr = aChild->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR;
   180     nsAutoString value;
   181     nsresult error;
   183     content->GetAttr(kNameSpaceID_None, nsGkAtoms::start, value);
   184     if (!value.IsEmpty()) {
   185       value.Trim("%");
   186       if (ltr) {
   187         aOffset.left =
   188           nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
   189         offsetSpecified |= SPECIFIED_LEFT;
   190       } else {
   191         aOffset.right =
   192           nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
   193         offsetSpecified |= SPECIFIED_RIGHT;
   194       }
   195     }
   197     content->GetAttr(kNameSpaceID_None, nsGkAtoms::end, value);
   198     if (!value.IsEmpty()) {
   199       value.Trim("%");
   200       if (ltr) {
   201         aOffset.right =
   202           nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
   203         offsetSpecified |= SPECIFIED_RIGHT;
   204       } else {
   205         aOffset.left =
   206           nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
   207         offsetSpecified |= SPECIFIED_LEFT;
   208       }
   209     }
   211     content->GetAttr(kNameSpaceID_None, nsGkAtoms::left, value);
   212     if (!value.IsEmpty()) {
   213       value.Trim("%");
   214       aOffset.left =
   215         nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
   216       offsetSpecified |= SPECIFIED_LEFT;
   217     }
   219     content->GetAttr(kNameSpaceID_None, nsGkAtoms::right, value);
   220     if (!value.IsEmpty()) {
   221       value.Trim("%");
   222       aOffset.right =
   223         nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
   224       offsetSpecified |= SPECIFIED_RIGHT;
   225     }
   227     content->GetAttr(kNameSpaceID_None, nsGkAtoms::top, value);
   228     if (!value.IsEmpty()) {
   229       value.Trim("%");
   230       aOffset.top =
   231         nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
   232       offsetSpecified |= SPECIFIED_TOP;
   233     }
   235     content->GetAttr(kNameSpaceID_None, nsGkAtoms::bottom, value);
   236     if (!value.IsEmpty()) {
   237       value.Trim("%");
   238       aOffset.bottom =
   239         nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
   240       offsetSpecified |= SPECIFIED_BOTTOM;
   241     }
   242   }
   244   if (!offsetSpecified && aChild->IsBoxFrame()) {
   245     // If no offset was specified at all, then we cache this fact to avoid requerying
   246     // CSS or the content model.
   247     aChild->AddStateBits(NS_STATE_STACK_NOT_POSITIONED);
   248   }
   250   return offsetSpecified;
   251 }
   254 NS_IMETHODIMP
   255 nsStackLayout::Layout(nsIFrame* aBox, nsBoxLayoutState& aState)
   256 {
   257   nsRect clientRect;
   258   aBox->GetClientRect(clientRect);
   260   bool grow;
   262   do {
   263     nsIFrame* child = aBox->GetChildBox();
   264     grow = false;
   266     while (child) 
   267     {  
   268       nsMargin margin;
   269       child->GetMargin(margin);
   270       nsRect childRect(clientRect);
   271       childRect.Deflate(margin);
   273       if (childRect.width < 0)
   274         childRect.width = 0;
   276       if (childRect.height < 0)
   277         childRect.height = 0;
   279       nsRect oldRect(child->GetRect());
   280       bool sizeChanged = !oldRect.IsEqualEdges(childRect);
   282       // only lay out dirty children or children whose sizes have changed
   283       if (sizeChanged || NS_SUBTREE_DIRTY(child)) {
   284           // add in the child's margin
   285           nsMargin margin;
   286           child->GetMargin(margin);
   288           // obtain our offset from the top left border of the stack's content box.
   289           nsMargin offset;
   290           uint8_t offsetSpecified = GetOffset(aState, child, offset);
   292           // Set the position and size based on which offsets have been specified:
   293           //   left only - offset from left edge, preferred width
   294           //   right only - offset from right edge, preferred width
   295           //   left and right - offset from left and right edges, width in between this
   296           //   neither - no offset, full width of stack
   297           // Vertical direction is similar.
   298           //
   299           // Margins on the child are also included in the edge offsets
   300           if (offsetSpecified) {
   301             if (offsetSpecified & SPECIFIED_LEFT) {
   302               childRect.x = clientRect.x + offset.left + margin.left;
   303               if (offsetSpecified & SPECIFIED_RIGHT) {
   304                 nsSize min = child->GetMinSize(aState);
   305                 nsSize max = child->GetMaxSize(aState);
   306                 nscoord width = clientRect.width - offset.LeftRight() - margin.LeftRight();
   307                 childRect.width = clamped(width, min.width, max.width);
   308               }
   309               else {
   310                 childRect.width = child->GetPrefSize(aState).width;
   311               }
   312             }
   313             else if (offsetSpecified & SPECIFIED_RIGHT) {
   314               childRect.width = child->GetPrefSize(aState).width;
   315               childRect.x = clientRect.XMost() - offset.right - margin.right - childRect.width;
   316             }
   318             if (offsetSpecified & SPECIFIED_TOP) {
   319               childRect.y = clientRect.y + offset.top + margin.top;
   320               if (offsetSpecified & SPECIFIED_BOTTOM) {
   321                 nsSize min = child->GetMinSize(aState);
   322                 nsSize max = child->GetMaxSize(aState);
   323                 nscoord height = clientRect.height - offset.TopBottom() - margin.TopBottom();
   324                 childRect.height = clamped(height, min.height, max.height);
   325               }
   326               else {
   327                 childRect.height = child->GetPrefSize(aState).height;
   328               }
   329             }
   330             else if (offsetSpecified & SPECIFIED_BOTTOM) {
   331               childRect.height = child->GetPrefSize(aState).height;
   332               childRect.y = clientRect.YMost() - offset.bottom - margin.bottom - childRect.height;
   333             }
   334           }
   336           // Now place the child.
   337           child->SetBounds(aState, childRect);
   339           // Flow the child.
   340           child->Layout(aState);
   342           // Get the child's new rect.
   343           childRect = child->GetRect();
   344           childRect.Inflate(margin);
   346           if (child->StyleXUL()->mStretchStack) {
   347             // Did the child push back on us and get bigger?
   348             if (offset.LeftRight() + childRect.width > clientRect.width) {
   349               clientRect.width = childRect.width + offset.LeftRight();
   350               grow = true;
   351             }
   353             if (offset.TopBottom() + childRect.height > clientRect.height) {
   354               clientRect.height = childRect.height + offset.TopBottom();
   355               grow = true;
   356             }
   357           }
   358        }
   360        child = child->GetNextBox();
   361      }
   362    } while (grow);
   364    // if some HTML inside us got bigger we need to force ourselves to
   365    // get bigger
   366    nsRect bounds(aBox->GetRect());
   367    nsMargin bp;
   368    aBox->GetBorderAndPadding(bp);
   369    clientRect.Inflate(bp);
   371    if (clientRect.width > bounds.width || clientRect.height > bounds.height)
   372    {
   373      if (clientRect.width > bounds.width)
   374        bounds.width = clientRect.width;
   375      if (clientRect.height > bounds.height)
   376        bounds.height = clientRect.height;
   378      aBox->SetBounds(aState, bounds);
   379    }
   381    return NS_OK;
   382 }

mercurial