LCOV - code coverage report
Current view: top level - layout/xul - nsStackLayout.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 139 210 66.2 %
Date: 2017-07-14 16:53:18 Functions: 8 9 88.9 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : //
       7             : // Eric Vaughan
       8             : // Netscape Communications
       9             : //
      10             : // See documentation in associated header file
      11             : //
      12             : 
      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"
      21             : 
      22             : using namespace mozilla;
      23             : 
      24             : nsBoxLayout* nsStackLayout::gInstance = nullptr;
      25             : 
      26             : #define SPECIFIED_LEFT (1 << eSideLeft)
      27             : #define SPECIFIED_RIGHT (1 << eSideRight)
      28             : #define SPECIFIED_TOP (1 << eSideTop)
      29             : #define SPECIFIED_BOTTOM (1 << eSideBottom)
      30             : 
      31             : nsresult
      32          16 : NS_NewStackLayout(nsCOMPtr<nsBoxLayout>& aNewLayout)
      33             : {
      34          16 :   if (!nsStackLayout::gInstance) {
      35           1 :     nsStackLayout::gInstance = new nsStackLayout();
      36           1 :     NS_IF_ADDREF(nsStackLayout::gInstance);
      37             :   }
      38             :   // we have not instance variables so just return our static one.
      39          16 :   aNewLayout = nsStackLayout::gInstance;
      40          16 :   return NS_OK;
      41             : }
      42             : 
      43             : /*static*/ void
      44           0 : nsStackLayout::Shutdown()
      45             : {
      46           0 :   NS_IF_RELEASE(gInstance);
      47           0 : }
      48             : 
      49           1 : nsStackLayout::nsStackLayout()
      50             : {
      51           1 : }
      52             : 
      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             :  */
      60             : 
      61             : nsSize
      62          43 : nsStackLayout::GetXULPrefSize(nsIFrame* aBox, nsBoxLayoutState& aState)
      63             : {
      64          43 :   nsSize prefSize (0, 0);
      65             : 
      66          43 :   nsIFrame* child = nsBox::GetChildXULBox(aBox);
      67         145 :   while (child) {
      68          51 :     auto stackSizing = child->StyleXUL()->mStackSizing;
      69          51 :     if (stackSizing != StyleStackSizing::Ignore) {
      70          51 :       nsSize pref = child->GetXULPrefSize(aState);
      71             : 
      72          51 :       AddMargin(child, pref);
      73          51 :       nsMargin offset;
      74          51 :       GetOffset(child, offset);
      75          51 :       pref.width += offset.LeftRight();
      76          51 :       pref.height += offset.TopBottom();
      77             : 
      78          51 :       if (pref.width > prefSize.width &&
      79             :           stackSizing != StyleStackSizing::IgnoreHorizontal) {
      80          45 :         prefSize.width = pref.width;
      81             :       }
      82          51 :       if (pref.height > prefSize.height &&
      83             :           stackSizing != StyleStackSizing::IgnoreVertical) {
      84          38 :         prefSize.height = pref.height;
      85             :       }
      86             :     }
      87             : 
      88          51 :     child = nsBox::GetNextXULBox(child);
      89             :   }
      90             : 
      91          43 :   AddBorderAndPadding(aBox, prefSize);
      92             : 
      93          43 :   return prefSize;
      94             : }
      95             : 
      96             : nsSize
      97          43 : nsStackLayout::GetXULMinSize(nsIFrame* aBox, nsBoxLayoutState& aState)
      98             : {
      99          43 :   nsSize minSize (0, 0);
     100             : 
     101          43 :   nsIFrame* child = nsBox::GetChildXULBox(aBox);
     102         145 :   while (child) {
     103          51 :     auto stackSizing = child->StyleXUL()->mStackSizing;
     104          51 :     if (stackSizing != StyleStackSizing::Ignore) {
     105          51 :       nsSize min = child->GetXULMinSize(aState);
     106             : 
     107          51 :       AddMargin(child, min);
     108          51 :       nsMargin offset;
     109          51 :       GetOffset(child, offset);
     110          51 :       min.width += offset.LeftRight();
     111          51 :       min.height += offset.TopBottom();
     112             : 
     113          51 :       if (min.width > minSize.width &&
     114             :           stackSizing != StyleStackSizing::IgnoreHorizontal) {
     115          42 :         minSize.width = min.width;
     116             :       }
     117          51 :       if (min.height > minSize.height &&
     118             :           stackSizing != StyleStackSizing::IgnoreVertical) {
     119          34 :         minSize.height = min.height;
     120             :       }
     121             :     }
     122             : 
     123          51 :     child = nsBox::GetNextXULBox(child);
     124             :   }
     125             : 
     126          43 :   AddBorderAndPadding(aBox, minSize);
     127             : 
     128          43 :   return minSize;
     129             : }
     130             : 
     131             : nsSize
     132          43 : nsStackLayout::GetXULMaxSize(nsIFrame* aBox, nsBoxLayoutState& aState)
     133             : {
     134          43 :   nsSize maxSize (NS_INTRINSICSIZE, NS_INTRINSICSIZE);
     135             : 
     136          43 :   nsIFrame* child = nsBox::GetChildXULBox(aBox);
     137         145 :   while (child) {
     138          51 :     auto stackSizing = child->StyleXUL()->mStackSizing;
     139          51 :     if (stackSizing != StyleStackSizing::Ignore) {
     140          51 :       nsSize min = child->GetXULMinSize(aState);
     141          51 :       nsSize max = child->GetXULMaxSize(aState);
     142             : 
     143          51 :       max = nsBox::BoundsCheckMinMax(min, max);
     144             : 
     145          51 :       AddMargin(child, max);
     146          51 :       nsMargin offset;
     147          51 :       GetOffset(child, offset);
     148          51 :       max.width += offset.LeftRight();
     149          51 :       max.height += offset.TopBottom();
     150             : 
     151          51 :       if (max.width < maxSize.width &&
     152             :           stackSizing != StyleStackSizing::IgnoreHorizontal) {
     153           6 :         maxSize.width = max.width;
     154             :       }
     155          51 :       if (max.height < maxSize.height &&
     156             :           stackSizing != StyleStackSizing::IgnoreVertical) {
     157           0 :         maxSize.height = max.height;
     158             :       }
     159             :     }
     160             : 
     161          51 :     child = nsBox::GetNextXULBox(child);
     162             :   }
     163             : 
     164          43 :   AddBorderAndPadding(aBox, maxSize);
     165             : 
     166          43 :   return maxSize;
     167             : }
     168             : 
     169             : 
     170             : nscoord
     171          72 : nsStackLayout::GetAscent(nsIFrame* aBox, nsBoxLayoutState& aState)
     172             : {
     173          72 :   nscoord vAscent = 0;
     174             : 
     175          72 :   nsIFrame* child = nsBox::GetChildXULBox(aBox);
     176         238 :   while (child) {
     177          83 :     nscoord ascent = child->GetXULBoxAscent(aState);
     178          83 :     nsMargin margin;
     179          83 :     child->GetXULMargin(margin);
     180          83 :     ascent += margin.top;
     181          83 :     if (ascent > vAscent)
     182          67 :       vAscent = ascent;
     183             : 
     184          83 :     child = nsBox::GetNextXULBox(child);
     185             :   }
     186             : 
     187          72 :   return vAscent;
     188             : }
     189             : 
     190             : uint8_t
     191         234 : nsStackLayout::GetOffset(nsIFrame* aChild, nsMargin& aOffset)
     192             : {
     193         234 :   aOffset = nsMargin(0, 0, 0, 0);
     194             : 
     195             :   // get the left, right, top and bottom offsets
     196             : 
     197             :   // As an optimization, we cache the fact that we are not positioned to avoid
     198             :   // wasting time fetching attributes.
     199         462 :   if (aChild->IsXULBoxFrame() &&
     200         228 :       (aChild->GetStateBits() & NS_STATE_STACK_NOT_POSITIONED))
     201         215 :     return 0;
     202             : 
     203          19 :   uint8_t offsetSpecified = 0;
     204          19 :   nsIContent* content = aChild->GetContent();
     205          19 :   if (content) {
     206          19 :     bool ltr = aChild->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR;
     207          38 :     nsAutoString value;
     208             :     nsresult error;
     209             : 
     210          19 :     content->GetAttr(kNameSpaceID_None, nsGkAtoms::start, value);
     211          19 :     if (!value.IsEmpty()) {
     212           0 :       value.Trim("%");
     213           0 :       if (ltr) {
     214           0 :         aOffset.left =
     215           0 :           nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
     216           0 :         offsetSpecified |= SPECIFIED_LEFT;
     217             :       } else {
     218           0 :         aOffset.right =
     219           0 :           nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
     220           0 :         offsetSpecified |= SPECIFIED_RIGHT;
     221             :       }
     222             :     }
     223             : 
     224          19 :     content->GetAttr(kNameSpaceID_None, nsGkAtoms::end, value);
     225          19 :     if (!value.IsEmpty()) {
     226           0 :       value.Trim("%");
     227           0 :       if (ltr) {
     228           0 :         aOffset.right =
     229           0 :           nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
     230           0 :         offsetSpecified |= SPECIFIED_RIGHT;
     231             :       } else {
     232           0 :         aOffset.left =
     233           0 :           nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
     234           0 :         offsetSpecified |= SPECIFIED_LEFT;
     235             :       }
     236             :     }
     237             : 
     238          19 :     content->GetAttr(kNameSpaceID_None, nsGkAtoms::left, value);
     239          19 :     if (!value.IsEmpty()) {
     240           0 :       value.Trim("%");
     241           0 :       aOffset.left =
     242           0 :         nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
     243           0 :       offsetSpecified |= SPECIFIED_LEFT;
     244             :     }
     245             : 
     246          19 :     content->GetAttr(kNameSpaceID_None, nsGkAtoms::right, value);
     247          19 :     if (!value.IsEmpty()) {
     248           0 :       value.Trim("%");
     249           0 :       aOffset.right =
     250           0 :         nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
     251           0 :       offsetSpecified |= SPECIFIED_RIGHT;
     252             :     }
     253             : 
     254          19 :     content->GetAttr(kNameSpaceID_None, nsGkAtoms::top, value);
     255          19 :     if (!value.IsEmpty()) {
     256           0 :       value.Trim("%");
     257           0 :       aOffset.top =
     258           0 :         nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
     259           0 :       offsetSpecified |= SPECIFIED_TOP;
     260             :     }
     261             : 
     262          19 :     content->GetAttr(kNameSpaceID_None, nsGkAtoms::bottom, value);
     263          19 :     if (!value.IsEmpty()) {
     264           0 :       value.Trim("%");
     265           0 :       aOffset.bottom =
     266           0 :         nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
     267           0 :       offsetSpecified |= SPECIFIED_BOTTOM;
     268             :     }
     269             :   }
     270             : 
     271          19 :   if (!offsetSpecified && aChild->IsXULBoxFrame()) {
     272             :     // If no offset was specified at all, then we cache this fact to avoid requerying
     273             :     // CSS or the content model.
     274          13 :     aChild->AddStateBits(NS_STATE_STACK_NOT_POSITIONED);
     275             :   }
     276             : 
     277          19 :   return offsetSpecified;
     278             : }
     279             : 
     280             : 
     281             : NS_IMETHODIMP
     282          79 : nsStackLayout::XULLayout(nsIFrame* aBox, nsBoxLayoutState& aState)
     283             : {
     284         158 :   nsRect clientRect;
     285          79 :   aBox->GetXULClientRect(clientRect);
     286             : 
     287             :   bool grow;
     288             : 
     289          79 :   do {
     290          79 :     nsIFrame* child = nsBox::GetChildXULBox(aBox);
     291          79 :     grow = false;
     292             : 
     293         255 :     while (child)
     294             :     {
     295          88 :       nsMargin margin;
     296          88 :       child->GetXULMargin(margin);
     297         176 :       nsRect childRect(clientRect);
     298          88 :       childRect.Deflate(margin);
     299             : 
     300          88 :       if (childRect.width < 0)
     301           0 :         childRect.width = 0;
     302             : 
     303          88 :       if (childRect.height < 0)
     304           0 :         childRect.height = 0;
     305             : 
     306         176 :       nsRect oldRect(child->GetRect());
     307          88 :       bool sizeChanged = !oldRect.IsEqualEdges(childRect);
     308             : 
     309             :       // only lay out dirty children or children whose sizes have changed
     310          88 :       if (sizeChanged || NS_SUBTREE_DIRTY(child)) {
     311             :           // add in the child's margin
     312          81 :           nsMargin margin;
     313          81 :           child->GetXULMargin(margin);
     314             : 
     315             :           // obtain our offset from the top left border of the stack's content box.
     316          81 :           nsMargin offset;
     317          81 :           uint8_t offsetSpecified = GetOffset(child, offset);
     318             : 
     319             :           // Set the position and size based on which offsets have been specified:
     320             :           //   left only - offset from left edge, preferred width
     321             :           //   right only - offset from right edge, preferred width
     322             :           //   left and right - offset from left and right edges, width in between this
     323             :           //   neither - no offset, full width of stack
     324             :           // Vertical direction is similar.
     325             :           //
     326             :           // Margins on the child are also included in the edge offsets
     327          81 :           if (offsetSpecified) {
     328           0 :             nsSize min = child->GetXULMinSize(aState);
     329           0 :             nsSize max = child->GetXULMaxSize(aState);
     330           0 :             if (offsetSpecified & SPECIFIED_LEFT) {
     331           0 :               childRect.x = clientRect.x + offset.left + margin.left;
     332           0 :               if (offsetSpecified & SPECIFIED_RIGHT) {
     333           0 :                 nscoord width = clientRect.width - offset.LeftRight() - margin.LeftRight();
     334           0 :                 childRect.width = clamped(width, min.width, max.width);
     335             :               }
     336             :               else {
     337           0 :                 nscoord width = child->GetXULPrefSize(aState).width;
     338           0 :                 childRect.width = clamped(width, min.width, max.width);
     339             :               }
     340             :             }
     341           0 :             else if (offsetSpecified & SPECIFIED_RIGHT) {
     342           0 :               nscoord width = child->GetXULPrefSize(aState).width;
     343           0 :               childRect.width = clamped(width, min.width, max.width);
     344           0 :               childRect.x = clientRect.XMost() - offset.right - margin.right - childRect.width;
     345             :             }
     346             : 
     347           0 :             if (offsetSpecified & SPECIFIED_TOP) {
     348           0 :               childRect.y = clientRect.y + offset.top + margin.top;
     349           0 :               if (offsetSpecified & SPECIFIED_BOTTOM) {
     350           0 :                 nscoord height = clientRect.height - offset.TopBottom() - margin.TopBottom();
     351           0 :                 childRect.height = clamped(height, min.height, max.height);
     352             :               }
     353             :               else {
     354           0 :                 nscoord height = child->GetXULPrefSize(aState).height;
     355           0 :                 childRect.height = clamped(height, min.height, max.height);
     356             :               }
     357             :             }
     358           0 :             else if (offsetSpecified & SPECIFIED_BOTTOM) {
     359           0 :               nscoord height = child->GetXULPrefSize(aState).height;
     360           0 :               childRect.height = clamped(height, min.height, max.height);
     361           0 :               childRect.y = clientRect.YMost() - offset.bottom - margin.bottom - childRect.height;
     362             :             }
     363             :           }
     364             : 
     365             :           // Now place the child.
     366          81 :           child->SetXULBounds(aState, childRect);
     367             : 
     368             :           // Flow the child.
     369          81 :           child->XULLayout(aState);
     370             : 
     371             :           // Get the child's new rect.
     372          81 :           childRect = child->GetRect();
     373          81 :           childRect.Inflate(margin);
     374             : 
     375          81 :           auto stackSizing = child->StyleXUL()->mStackSizing;
     376          81 :           if (stackSizing != StyleStackSizing::Ignore) {
     377             :             // Did the child push back on us and get bigger?
     378          81 :             if (offset.LeftRight() + childRect.width > clientRect.width &&
     379             :                 stackSizing != StyleStackSizing::IgnoreHorizontal) {
     380           0 :               clientRect.width = childRect.width + offset.LeftRight();
     381           0 :               grow = true;
     382             :             }
     383             : 
     384          81 :             if (offset.TopBottom() + childRect.height > clientRect.height &&
     385             :                 stackSizing != StyleStackSizing::IgnoreVertical) {
     386           0 :               clientRect.height = childRect.height + offset.TopBottom();
     387           0 :               grow = true;
     388             :             }
     389             :           }
     390             :        }
     391             : 
     392          88 :        child = nsBox::GetNextXULBox(child);
     393             :      }
     394             :    } while (grow);
     395             : 
     396             :    // if some HTML inside us got bigger we need to force ourselves to
     397             :    // get bigger
     398         158 :    nsRect bounds(aBox->GetRect());
     399          79 :    nsMargin bp;
     400          79 :    aBox->GetXULBorderAndPadding(bp);
     401          79 :    clientRect.Inflate(bp);
     402             : 
     403          79 :    if (clientRect.width > bounds.width || clientRect.height > bounds.height)
     404             :    {
     405           0 :      if (clientRect.width > bounds.width)
     406           0 :        bounds.width = clientRect.width;
     407           0 :      if (clientRect.height > bounds.height)
     408           0 :        bounds.height = clientRect.height;
     409             : 
     410           0 :      aBox->SetXULBounds(aState, bounds);
     411             :    }
     412             : 
     413         158 :    return NS_OK;
     414             : }
     415             : 

Generated by: LCOV version 1.13