layout/xul/grid/nsGrid.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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 "nsGrid.h"
    14 #include "nsGridRowGroupLayout.h"
    15 #include "nsBox.h"
    16 #include "nsIScrollableFrame.h"
    17 #include "nsSprocketLayout.h"
    18 #include "nsGridLayout2.h"
    19 #include "nsGridRow.h"
    20 #include "nsGridCell.h"
    21 #include "nsHTMLReflowState.h"
    23 /*
    24 The grid control expands the idea of boxes from 1 dimension to 2 dimensions. 
    25 It works by allowing the XUL to define a collection of rows and columns and then 
    26 stacking them on top of each other. Here is and example.
    28 Example 1:
    30 <grid>
    31    <columns>
    32       <column/>
    33       <column/>
    34    </columns>
    36    <rows>
    37       <row/>
    38       <row/>
    39    </rows>
    40 </grid>
    42 example 2:
    44 <grid>
    45    <columns>
    46       <column flex="1"/>
    47       <column flex="1"/>
    48    </columns>
    50    <rows>
    51       <row>
    52          <text value="hello"/>
    53          <text value="there"/>
    54       </row>
    55    </rows>
    56 </grid>
    58 example 3:
    60 <grid>
    62 <rows>
    63       <row>
    64          <text value="hello"/>
    65          <text value="there"/>
    66       </row>
    67    </rows>
    69    <columns>
    70       <column>
    71          <text value="Hey I'm in the column and I'm on top!"/>
    72       </column>
    73       <column/>
    74    </columns>
    76 </grid>
    78 Usually the columns are first and the rows are second, so the rows will be drawn on top of the columns. 
    79 You can reverse this by defining the rows first.
    80 Other tags are then placed in the <row> or <column> tags causing the grid to accommodate everyone.  
    81 It does this by creating 3 things: A cellmap, a row list, and a column list. The cellmap is a 2 
    82 dimensional array of nsGridCells. Each cell contains 2 boxes.  One cell from the column list 
    83 and one from the row list. When a cell is asked for its size it returns that smallest size it can 
    84 be to accommodate the 2 cells. Row lists and Column lists use the same data structure: nsGridRow. 
    85 Essentially a row and column are the same except a row goes alone the x axis and a column the y. 
    86 To make things easier and save code everything is written in terms of the x dimension. A flag is 
    87 passed in called "isHorizontal" that can flip the calculations to the y axis.
    89 Usually the number of cells in a row match the number of columns, but not always. 
    90 It is possible to define 5 columns for a grid but have 10 cells in one of the rows. 
    91 In this case 5 extra columns will be added to the column list to handle the situation. 
    92 These are called extraColumns/Rows.
    93 */
    95 nsGrid::nsGrid():mBox(nullptr),
    96                  mRows(nullptr),
    97                  mColumns(nullptr), 
    98                  mRowsBox(nullptr),
    99                  mColumnsBox(nullptr),
   100                  mNeedsRebuild(true),
   101                  mRowCount(0),
   102                  mColumnCount(0),
   103                  mExtraRowCount(0),
   104                  mExtraColumnCount(0),
   105                  mCellMap(nullptr),
   106                  mMarkingDirty(false)
   107 {
   108     MOZ_COUNT_CTOR(nsGrid);
   109 }
   111 nsGrid::~nsGrid()
   112 {
   113     FreeMap();
   114     MOZ_COUNT_DTOR(nsGrid);
   115 }
   117 /*
   118  * This is called whenever something major happens in the grid. And example 
   119  * might be when many cells or row are added. It sets a flag signaling that 
   120  * all the grids caches information should be recalculated.
   121  */
   122 void
   123 nsGrid::NeedsRebuild(nsBoxLayoutState& aState)
   124 {
   125   if (mNeedsRebuild)
   126     return;
   128   // iterate through columns and rows and dirty them
   129   mNeedsRebuild = true;
   131   // find the new row and column box. They could have 
   132   // been changed.
   133   mRowsBox = nullptr;
   134   mColumnsBox = nullptr;
   135   FindRowsAndColumns(&mRowsBox, &mColumnsBox);
   137   // tell all the rows and columns they are dirty
   138   DirtyRows(mRowsBox, aState);
   139   DirtyRows(mColumnsBox, aState);
   140 }
   144 /**
   145  * If we are marked for rebuild. Then build everything
   146  */
   147 void
   148 nsGrid::RebuildIfNeeded()
   149 {
   150   if (!mNeedsRebuild)
   151     return;
   153   mNeedsRebuild = false;
   155   // find the row and columns frames
   156   FindRowsAndColumns(&mRowsBox, &mColumnsBox);
   158   // count the rows and columns
   159   int32_t computedRowCount = 0;
   160   int32_t computedColumnCount = 0;
   161   int32_t rowCount = 0;
   162   int32_t columnCount = 0;
   164   CountRowsColumns(mRowsBox, rowCount, computedColumnCount);
   165   CountRowsColumns(mColumnsBox, columnCount, computedRowCount);
   167   // computedRowCount are the actual number of rows as determined by the 
   168   // columns children.
   169   // computedColumnCount are the number of columns as determined by the number
   170   // of rows children.
   171   // We can use this information to see how many extra columns or rows we need.
   172   // This can happen if there are are more children in a row that number of columns
   173   // defined. Example:
   174   //
   175   // <columns>
   176   //   <column/>
   177   // </columns>
   178   //
   179   // <rows>
   180   //   <row>
   181   //     <button/><button/>
   182   //   </row>
   183   // </rows>
   184   //
   185   // computedColumnCount = 2 // for the 2 buttons in the row tag
   186   // computedRowCount = 0 // there is nothing in the  column tag
   187   // mColumnCount = 1 // one column defined
   188   // mRowCount = 1 // one row defined
   189   // 
   190   // So in this case we need to make 1 extra column.
   191   //
   193   // Make sure to update mExtraColumnCount no matter what, since it might
   194   // happen that we now have as many columns as are defined, and we wouldn't
   195   // want to have a positive mExtraColumnCount hanging about in that case!
   196   mExtraColumnCount = computedColumnCount - columnCount;
   197   if (computedColumnCount > columnCount) {
   198      columnCount = computedColumnCount;
   199   }
   201   // Same for rows.
   202   mExtraRowCount = computedRowCount - rowCount;
   203   if (computedRowCount > rowCount) {
   204      rowCount = computedRowCount;
   205   }
   207   // build and poplulate row and columns arrays
   208   BuildRows(mRowsBox, rowCount, &mRows, true);
   209   BuildRows(mColumnsBox, columnCount, &mColumns, false);
   211   // build and populate the cell map
   212   mCellMap = BuildCellMap(rowCount, columnCount);
   214   mRowCount = rowCount;
   215   mColumnCount = columnCount;
   217   // populate the cell map from column and row children
   218   PopulateCellMap(mRows, mColumns, mRowCount, mColumnCount, true);
   219   PopulateCellMap(mColumns, mRows, mColumnCount, mRowCount, false);
   220 }
   222 void
   223 nsGrid::FreeMap()
   224 {
   225   if (mRows) 
   226     delete[] mRows;
   228   if (mColumns)
   229     delete[] mColumns;
   231   if (mCellMap)
   232     delete[] mCellMap;
   234   mRows = nullptr;
   235   mColumns = nullptr;
   236   mCellMap = nullptr;
   237   mColumnCount = 0;
   238   mRowCount = 0;
   239   mExtraColumnCount = 0;
   240   mExtraRowCount = 0;
   241   mRowsBox = nullptr;
   242   mColumnsBox = nullptr;
   243 }
   245 /**
   246  * finds the first <rows> and <columns> tags in the <grid> tag
   247  */
   248 void
   249 nsGrid::FindRowsAndColumns(nsIFrame** aRows, nsIFrame** aColumns)
   250 {
   251   *aRows = nullptr;
   252   *aColumns = nullptr;
   254   // find the boxes that contain our rows and columns
   255   nsIFrame* child = nullptr;
   256   // if we have <grid></grid> then mBox will be null (bug 125689)
   257   if (mBox)
   258     child = mBox->GetChildBox();
   260   while(child)
   261   {
   262     nsIFrame* oldBox = child;
   263     nsIScrollableFrame *scrollFrame = do_QueryFrame(child);
   264     if (scrollFrame) {
   265        nsIFrame* scrolledFrame = scrollFrame->GetScrolledFrame();
   266        NS_ASSERTION(scrolledFrame,"Error no scroll frame!!");
   267        child = do_QueryFrame(scrolledFrame);
   268     }
   270     nsCOMPtr<nsIGridPart> monument = GetPartFromBox(child);
   271     if (monument)
   272     {
   273       nsGridRowGroupLayout* rowGroup = monument->CastToRowGroupLayout();
   274       if (rowGroup) {
   275          bool isHorizontal = !nsSprocketLayout::IsHorizontal(child);
   276          if (isHorizontal)
   277            *aRows = child;
   278          else
   279            *aColumns = child;
   281          if (*aRows && *aColumns)
   282            return;
   283       }
   284     }
   286     if (scrollFrame) {
   287       child = oldBox;
   288     }
   290     child = child->GetNextBox();
   291   }
   292 }
   294 /**
   295  * Count the number of rows and columns in the given box. aRowCount well become the actual number
   296  * rows defined in the xul. aComputedColumnCount will become the number of columns by counting the number
   297  * of cells in each row.
   298  */
   299 void
   300 nsGrid::CountRowsColumns(nsIFrame* aRowBox, int32_t& aRowCount, int32_t& aComputedColumnCount)
   301 {
   302   aRowCount = 0;
   303   aComputedColumnCount = 0;
   304   // get the rowboxes layout manager. Then ask it to do the work for us
   305   if (aRowBox) {
   306     nsCOMPtr<nsIGridPart> monument = GetPartFromBox(aRowBox);
   307     if (monument) 
   308        monument->CountRowsColumns(aRowBox, aRowCount, aComputedColumnCount);
   309   }
   310 }
   313 /**
   314  * Given the number of rows create nsGridRow objects for them and full them out.
   315  */
   316 void
   317 nsGrid::BuildRows(nsIFrame* aBox, int32_t aRowCount, nsGridRow** aRows, bool aIsHorizontal)
   318 {
   319   // if no rows then return null
   320   if (aRowCount == 0) {
   322     // make sure we free up the memory.
   323     if (*aRows)
   324       delete[] (*aRows);
   326     *aRows = nullptr;
   327     return;
   328   }
   330   // create the array
   331   nsGridRow* row;
   333   // only create new rows if we have to. Reuse old rows.
   334   if (aIsHorizontal)
   335   { 
   336     if (aRowCount > mRowCount) {
   337        delete[] mRows;
   338        row = new nsGridRow[aRowCount];
   339     } else {
   340       for (int32_t i=0; i < mRowCount; i++)
   341         mRows[i].Init(nullptr, false);
   343       row = mRows;
   344     }
   345   } else {
   346     if (aRowCount > mColumnCount) {
   347        delete[] mColumns;
   348        row = new nsGridRow[aRowCount];
   349     } else {
   350        for (int32_t i=0; i < mColumnCount; i++)
   351          mColumns[i].Init(nullptr, false);
   353        row = mColumns;
   354     }
   355   }
   357   // populate it if we can. If not it will contain only dynamic columns
   358   if (aBox)
   359   {
   360     nsCOMPtr<nsIGridPart> monument = GetPartFromBox(aBox);
   361     if (monument) {
   362        monument->BuildRows(aBox, row);
   363     }
   364   }
   366   *aRows = row;
   367 }
   370 /**
   371  * Given the number of rows and columns. Build a cellmap
   372  */
   373 nsGridCell*
   374 nsGrid::BuildCellMap(int32_t aRows, int32_t aColumns)
   375 {
   376   int32_t size = aRows*aColumns;
   377   int32_t oldsize = mRowCount*mColumnCount;
   378   if (size == 0) {
   379     delete[] mCellMap;
   380   }
   381   else {
   382     if (size > oldsize) {
   383       delete[] mCellMap;
   384       return new nsGridCell[size];
   385     } else {
   386       // clear out cellmap
   387       for (int32_t i=0; i < oldsize; i++)
   388       {
   389         mCellMap[i].SetBoxInRow(nullptr);
   390         mCellMap[i].SetBoxInColumn(nullptr);
   391       }
   392       return mCellMap;
   393     }
   394   }
   395   return nullptr;
   396 }
   398 /** 
   399  * Run through all the cells in the rows and columns and populate then with 2 cells. One from the row and one
   400  * from the column
   401  */
   402 void
   403 nsGrid::PopulateCellMap(nsGridRow* aRows, nsGridRow* aColumns, int32_t aRowCount, int32_t aColumnCount, bool aIsHorizontal)
   404 {
   405   if (!aRows)
   406     return;
   408    // look through the columns
   409   int32_t j = 0;
   411   for(int32_t i=0; i < aRowCount; i++) 
   412   {
   413      nsIFrame* child = nullptr;
   414      nsGridRow* row = &aRows[i];
   416      // skip bogus rows. They have no cells
   417      if (row->mIsBogus) 
   418        continue;
   420      child = row->mBox;
   421      if (child) {
   422        child = child->GetChildBox();
   424        j = 0;
   426        while(child && j < aColumnCount)
   427        {
   428          // skip bogus column. They have no cells
   429          nsGridRow* column = &aColumns[j];
   430          if (column->mIsBogus) 
   431          {
   432            j++;
   433            continue;
   434          }
   436          if (aIsHorizontal)
   437            GetCellAt(j,i)->SetBoxInRow(child);
   438          else
   439            GetCellAt(i,j)->SetBoxInColumn(child);
   441          child = child->GetNextBox();
   443          j++;
   444        }
   445      }
   446   }
   447 }
   449 /**
   450  * Run through the rows in the given box and mark them dirty so they 
   451  * will get recalculated and get a layout.
   452  */
   453 void 
   454 nsGrid::DirtyRows(nsIFrame* aRowBox, nsBoxLayoutState& aState)
   455 {
   456   // make sure we prevent others from dirtying things.
   457   mMarkingDirty = true;
   459   // if the box is a grid part have it recursively hand it.
   460   if (aRowBox) {
   461     nsCOMPtr<nsIGridPart> part = GetPartFromBox(aRowBox);
   462     if (part) 
   463        part->DirtyRows(aRowBox, aState);
   464   }
   466   mMarkingDirty = false;
   467 }
   469 nsGridRow*
   470 nsGrid::GetColumnAt(int32_t aIndex, bool aIsHorizontal)
   471 {
   472   return GetRowAt(aIndex, !aIsHorizontal);
   473 }
   475 nsGridRow*
   476 nsGrid::GetRowAt(int32_t aIndex, bool aIsHorizontal)
   477 {
   478   RebuildIfNeeded();
   480   if (aIsHorizontal) {
   481     NS_ASSERTION(aIndex < mRowCount && aIndex >= 0, "Index out of range");
   482     return &mRows[aIndex];
   483   } else {
   484     NS_ASSERTION(aIndex < mColumnCount && aIndex >= 0, "Index out of range");
   485     return &mColumns[aIndex];
   486   }
   487 }
   489 nsGridCell*
   490 nsGrid::GetCellAt(int32_t aX, int32_t aY)
   491 {
   492   RebuildIfNeeded();
   494   NS_ASSERTION(aY < mRowCount && aY >= 0, "Index out of range");
   495   NS_ASSERTION(aX < mColumnCount && aX >= 0, "Index out of range");
   496   return &mCellMap[aY*mColumnCount+aX];
   497 }
   499 int32_t
   500 nsGrid::GetExtraColumnCount(bool aIsHorizontal)
   501 {
   502   return GetExtraRowCount(!aIsHorizontal);
   503 }
   505 int32_t
   506 nsGrid::GetExtraRowCount(bool aIsHorizontal)
   507 {
   508   RebuildIfNeeded();
   510   if (aIsHorizontal)
   511     return mExtraRowCount;
   512   else
   513     return mExtraColumnCount;
   514 }
   517 /**
   518  * These methods return the preferred, min, max sizes for a given row index.
   519  * aIsHorizontal if aIsHorizontal is true. If you pass false you will get the inverse.
   520  * As if you called GetPrefColumnSize(aState, index, aPref)
   521  */
   522 nsSize
   523 nsGrid::GetPrefRowSize(nsBoxLayoutState& aState, int32_t aRowIndex, bool aIsHorizontal)
   524 { 
   525   nsSize size(0,0);
   526   if (!(aRowIndex >=0 && aRowIndex < GetRowCount(aIsHorizontal)))
   527     return size;
   529   nscoord height = GetPrefRowHeight(aState, aRowIndex, aIsHorizontal);
   530   SetLargestSize(size, height, aIsHorizontal);
   532   return size;
   533 }
   535 nsSize
   536 nsGrid::GetMinRowSize(nsBoxLayoutState& aState, int32_t aRowIndex, bool aIsHorizontal)
   537 { 
   538   nsSize size(0,0);
   539   if (!(aRowIndex >=0 && aRowIndex < GetRowCount(aIsHorizontal)))
   540     return size;
   542   nscoord height = GetMinRowHeight(aState, aRowIndex, aIsHorizontal);
   543   SetLargestSize(size, height, aIsHorizontal);
   545   return size;
   546 }
   548 nsSize
   549 nsGrid::GetMaxRowSize(nsBoxLayoutState& aState, int32_t aRowIndex, bool aIsHorizontal)
   550 { 
   551   nsSize size(NS_INTRINSICSIZE,NS_INTRINSICSIZE);
   552   if (!(aRowIndex >=0 && aRowIndex < GetRowCount(aIsHorizontal)))
   553     return size;
   555   nscoord height = GetMaxRowHeight(aState, aRowIndex, aIsHorizontal);
   556   SetSmallestSize(size, height, aIsHorizontal);
   558   return size;
   559 }
   561 // static
   562 nsIGridPart*
   563 nsGrid::GetPartFromBox(nsIFrame* aBox)
   564 {
   565   if (!aBox)
   566     return nullptr;
   568   nsBoxLayout* layout = aBox->GetLayoutManager();
   569   return layout ? layout->AsGridPart() : nullptr;
   570 }
   572 nsMargin
   573 nsGrid::GetBoxTotalMargin(nsIFrame* aBox, bool aIsHorizontal)
   574 {
   575   nsMargin margin(0,0,0,0);
   576   // walk the boxes parent chain getting the border/padding/margin of our parent rows
   578   // first get the layour manager
   579   nsIGridPart* part = GetPartFromBox(aBox);
   580   if (part)
   581     margin = part->GetTotalMargin(aBox, aIsHorizontal);
   583   return margin;
   584 }
   586 /**
   587  * The first and last rows can be affected by <rows> tags with borders or margin
   588  * gets first and last rows and their indexes.
   589  * If it fails because there are no rows then:
   590  * FirstRow is nullptr
   591  * LastRow is nullptr
   592  * aFirstIndex = -1
   593  * aLastIndex = -1
   594  */
   595 void
   596 nsGrid::GetFirstAndLastRow(nsBoxLayoutState& aState, 
   597                           int32_t& aFirstIndex, 
   598                           int32_t& aLastIndex, 
   599                           nsGridRow*& aFirstRow,
   600                           nsGridRow*& aLastRow,
   601                           bool aIsHorizontal)
   602 {
   603   aFirstRow = nullptr;
   604   aLastRow = nullptr;
   605   aFirstIndex = -1;
   606   aLastIndex = -1;
   608   int32_t count = GetRowCount(aIsHorizontal);
   610   if (count == 0)
   611     return;
   614   // We could have collapsed columns either before or after our index.
   615   // they should not count. So if we are the 5th row and the first 4 are
   616   // collaped we become the first row. Or if we are the 9th row and
   617   // 10 up to the last row are collapsed we then become the last.
   619   // see if we are first
   620   int32_t i;
   621   for (i=0; i < count; i++)
   622   {
   623      nsGridRow* row = GetRowAt(i,aIsHorizontal);
   624      if (!row->IsCollapsed()) {
   625        aFirstIndex = i;
   626        aFirstRow = row;
   627        break;
   628      }
   629   }
   631   // see if we are last
   632   for (i=count-1; i >= 0; i--)
   633   {
   634      nsGridRow* row = GetRowAt(i,aIsHorizontal);
   635      if (!row->IsCollapsed()) {
   636        aLastIndex = i;
   637        aLastRow = row;
   638        break;
   639      }
   641   }
   642 }
   644 /**
   645  * A row can have a top and bottom offset. Usually this is just the top and bottom border/padding.
   646  * However if the row is the first or last it could be affected by the fact a column or columns could
   647  * have a top or bottom margin. 
   648  */
   649 void
   650 nsGrid::GetRowOffsets(nsBoxLayoutState& aState, int32_t aIndex, nscoord& aTop, nscoord& aBottom, bool aIsHorizontal)
   651 {
   653   RebuildIfNeeded();
   655   nsGridRow* row = GetRowAt(aIndex, aIsHorizontal);
   657   if (row->IsOffsetSet()) 
   658   {
   659     aTop    = row->mTop;
   660     aBottom = row->mBottom;
   661     return;
   662   }
   664   // first get the rows top and bottom border and padding
   665   nsIFrame* box = row->GetBox();
   667   // add up all the padding
   668   nsMargin margin(0,0,0,0);
   669   nsMargin border(0,0,0,0);
   670   nsMargin padding(0,0,0,0);
   671   nsMargin totalBorderPadding(0,0,0,0);
   672   nsMargin totalMargin(0,0,0,0);
   674   // if there is a box and it's not bogus take its
   675   // borders padding into account
   676   if (box && !row->mIsBogus)
   677   {
   678     if (!box->IsCollapsed())
   679     {
   680        // get real border and padding. GetBorderAndPadding
   681        // is redefined on nsGridRowLeafFrame. If we called it here
   682        // we would be in finite recurson.
   683        box->GetBorder(border);
   684        box->GetPadding(padding);
   686        totalBorderPadding += border;
   687        totalBorderPadding += padding;
   688      }
   690      // if we are the first or last row
   691      // take into account <rows> tags around us
   692      // that could have borders or margins.
   693      // fortunately they only affect the first
   694      // and last row inside the <rows> tag
   696      totalMargin = GetBoxTotalMargin(box, aIsHorizontal);
   697   }
   699   if (aIsHorizontal) {
   700     row->mTop = totalBorderPadding.top;
   701     row->mBottom = totalBorderPadding.bottom;
   702     row->mTopMargin = totalMargin.top;
   703     row->mBottomMargin = totalMargin.bottom;
   704   } else {
   705     row->mTop = totalBorderPadding.left;
   706     row->mBottom = totalBorderPadding.right;
   707     row->mTopMargin = totalMargin.left;
   708     row->mBottomMargin = totalMargin.right;
   709   }
   711   // if we are the first or last row take into account the top and bottom borders
   712   // of each columns. 
   714   // If we are the first row then get the largest top border/padding in 
   715   // our columns. If that's larger than the rows top border/padding use it.
   717   // If we are the last row then get the largest bottom border/padding in 
   718   // our columns. If that's larger than the rows bottom border/padding use it.
   719   int32_t firstIndex = 0;
   720   int32_t lastIndex = 0;
   721   nsGridRow* firstRow = nullptr;
   722   nsGridRow* lastRow = nullptr;
   723   GetFirstAndLastRow(aState, firstIndex, lastIndex, firstRow, lastRow, aIsHorizontal);
   725   if (aIndex == firstIndex || aIndex == lastIndex) {
   726     nscoord maxTop = 0;
   727     nscoord maxBottom = 0;
   729     // run through the columns. Look at each column
   730     // pick the largest top border or bottom border
   731     int32_t count = GetColumnCount(aIsHorizontal); 
   733     for (int32_t i=0; i < count; i++)
   734     {  
   735       nsMargin totalChildBorderPadding(0,0,0,0);
   737       nsGridRow* column = GetColumnAt(i,aIsHorizontal);
   738       nsIFrame* box = column->GetBox();
   740       if (box) 
   741       {
   742         // ignore collapsed children
   743         if (!box->IsCollapsed())
   744         {
   745            // include the margin of the columns. To the row
   746            // at this point border/padding and margins all added
   747            // up to more needed space.
   748            margin = GetBoxTotalMargin(box, !aIsHorizontal);
   749            // get real border and padding. GetBorderAndPadding
   750            // is redefined on nsGridRowLeafFrame. If we called it here
   751            // we would be in finite recurson.
   752            box->GetBorder(border);
   753            box->GetPadding(padding);
   754            totalChildBorderPadding += border;
   755            totalChildBorderPadding += padding;
   756            totalChildBorderPadding += margin;
   757         }
   759         nscoord top;
   760         nscoord bottom;
   762         // pick the largest top margin
   763         if (aIndex == firstIndex) {
   764           if (aIsHorizontal) {
   765             top = totalChildBorderPadding.top;
   766           } else {
   767             top = totalChildBorderPadding.left;
   768           }
   769           if (top > maxTop)
   770             maxTop = top;
   771         } 
   773         // pick the largest bottom margin
   774         if (aIndex == lastIndex) {
   775           if (aIsHorizontal) {
   776             bottom = totalChildBorderPadding.bottom;
   777           } else {
   778             bottom = totalChildBorderPadding.right;
   779           }
   780           if (bottom > maxBottom)
   781              maxBottom = bottom;
   782         }
   784       }
   786       // If the biggest top border/padding the columns is larger than this rows top border/padding
   787       // the use it.
   788       if (aIndex == firstIndex) {
   789         if (maxTop > (row->mTop + row->mTopMargin))
   790           row->mTop = maxTop - row->mTopMargin;
   791       }
   793       // If the biggest bottom border/padding the columns is larger than this rows bottom border/padding
   794       // the use it.
   795       if (aIndex == lastIndex) {
   796         if (maxBottom > (row->mBottom + row->mBottomMargin))
   797           row->mBottom = maxBottom - row->mBottomMargin;
   798       }
   799     }
   800   }
   802   aTop    = row->mTop;
   803   aBottom = row->mBottom;
   804 }
   806 /**
   807  * These methods return the preferred, min, max coord for a given row index if
   808  * aIsHorizontal is true. If you pass false you will get the inverse.
   809  * As if you called GetPrefColumnHeight(aState, index, aPref).
   810  */
   811 nscoord
   812 nsGrid::GetPrefRowHeight(nsBoxLayoutState& aState, int32_t aIndex, bool aIsHorizontal)
   813 {
   814   RebuildIfNeeded();
   816   nsGridRow* row = GetRowAt(aIndex, aIsHorizontal);
   818   if (row->IsCollapsed())
   819     return 0;
   821   if (row->IsPrefSet()) 
   822     return row->mPref;
   824   nsIFrame* box = row->mBox;
   826   // set in CSS?
   827   if (box) 
   828   {
   829     bool widthSet, heightSet;
   830     nsSize cssSize(-1, -1);
   831     nsIFrame::AddCSSPrefSize(box, cssSize, widthSet, heightSet);
   833     row->mPref = GET_HEIGHT(cssSize, aIsHorizontal);
   835     // yep do nothing.
   836     if (row->mPref != -1)
   837       return row->mPref;
   838   }
   840   // get the offsets so they are cached.
   841   nscoord top;
   842   nscoord bottom;
   843   GetRowOffsets(aState, aIndex, top, bottom, aIsHorizontal);
   845   // is the row bogus? If so then just ask it for its size
   846   // it should not be affected by cells in the grid. 
   847   if (row->mIsBogus)
   848   {
   849      nsSize size(0,0);
   850      if (box) 
   851      {
   852        size = box->GetPrefSize(aState);
   853        nsBox::AddMargin(box, size);
   854        nsGridLayout2::AddOffset(aState, box, size);
   855      }
   857      row->mPref = GET_HEIGHT(size, aIsHorizontal);
   858      return row->mPref;
   859   }
   861   nsSize size(0,0);
   863   nsGridCell* child;
   865   int32_t count = GetColumnCount(aIsHorizontal); 
   867   for (int32_t i=0; i < count; i++)
   868   {  
   869     if (aIsHorizontal)
   870      child = GetCellAt(i,aIndex);
   871     else
   872      child = GetCellAt(aIndex,i);
   874     // ignore collapsed children
   875     if (!child->IsCollapsed())
   876     {
   877       nsSize childSize = child->GetPrefSize(aState);
   879       nsSprocketLayout::AddLargestSize(size, childSize, aIsHorizontal);
   880     }
   881   }
   883   row->mPref = GET_HEIGHT(size, aIsHorizontal) + top + bottom;
   885   return row->mPref;
   886 }
   888 nscoord
   889 nsGrid::GetMinRowHeight(nsBoxLayoutState& aState, int32_t aIndex, bool aIsHorizontal)
   890 {
   891   RebuildIfNeeded();
   893   nsGridRow* row = GetRowAt(aIndex, aIsHorizontal);
   895   if (row->IsCollapsed())
   896     return 0;
   898   if (row->IsMinSet()) 
   899     return row->mMin;
   901   nsIFrame* box = row->mBox;
   903   // set in CSS?
   904   if (box) {
   905     bool widthSet, heightSet;
   906     nsSize cssSize(-1, -1);
   907     nsIFrame::AddCSSMinSize(aState, box, cssSize, widthSet, heightSet);
   909     row->mMin = GET_HEIGHT(cssSize, aIsHorizontal);
   911     // yep do nothing.
   912     if (row->mMin != -1)
   913       return row->mMin;
   914   }
   916   // get the offsets so they are cached.
   917   nscoord top;
   918   nscoord bottom;
   919   GetRowOffsets(aState, aIndex, top, bottom, aIsHorizontal);
   921   // is the row bogus? If so then just ask it for its size
   922   // it should not be affected by cells in the grid. 
   923   if (row->mIsBogus)
   924   {
   925      nsSize size(0,0);
   926      if (box) {
   927        size = box->GetPrefSize(aState);
   928        nsBox::AddMargin(box, size);
   929        nsGridLayout2::AddOffset(aState, box, size);
   930      }
   932      row->mMin = GET_HEIGHT(size, aIsHorizontal) + top + bottom;
   933      return row->mMin;
   934   }
   936   nsSize size(0,0);
   938   nsGridCell* child;
   940   int32_t count = GetColumnCount(aIsHorizontal); 
   942   for (int32_t i=0; i < count; i++)
   943   {  
   944     if (aIsHorizontal)
   945      child = GetCellAt(i,aIndex);
   946     else
   947      child = GetCellAt(aIndex,i);
   949     // ignore collapsed children
   950     if (!child->IsCollapsed())
   951     {
   952       nsSize childSize = child->GetMinSize(aState);
   954       nsSprocketLayout::AddLargestSize(size, childSize, aIsHorizontal);
   955     }
   956   }
   958   row->mMin = GET_HEIGHT(size, aIsHorizontal);
   960   return row->mMin;
   961 }
   963 nscoord
   964 nsGrid::GetMaxRowHeight(nsBoxLayoutState& aState, int32_t aIndex, bool aIsHorizontal)
   965 {
   966   RebuildIfNeeded();
   968   nsGridRow* row = GetRowAt(aIndex, aIsHorizontal);
   970   if (row->IsCollapsed())
   971     return 0;
   973   if (row->IsMaxSet()) 
   974     return row->mMax;
   976   nsIFrame* box = row->mBox;
   978   // set in CSS?
   979   if (box) {
   980     bool widthSet, heightSet;
   981     nsSize cssSize(-1, -1);
   982     nsIFrame::AddCSSMaxSize(box, cssSize, widthSet, heightSet);
   984     row->mMax = GET_HEIGHT(cssSize, aIsHorizontal);
   986     // yep do nothing.
   987     if (row->mMax != -1)
   988       return row->mMax;
   989   }
   991   // get the offsets so they are cached.
   992   nscoord top;
   993   nscoord bottom;
   994   GetRowOffsets(aState, aIndex, top, bottom, aIsHorizontal);
   996   // is the row bogus? If so then just ask it for its size
   997   // it should not be affected by cells in the grid. 
   998   if (row->mIsBogus)
   999   {
  1000      nsSize size(NS_INTRINSICSIZE,NS_INTRINSICSIZE);
  1001      if (box) {
  1002        size = box->GetPrefSize(aState);
  1003        nsBox::AddMargin(box, size);
  1004        nsGridLayout2::AddOffset(aState, box, size);
  1007      row->mMax = GET_HEIGHT(size, aIsHorizontal);
  1008      return row->mMax;
  1011   nsSize size(NS_INTRINSICSIZE,NS_INTRINSICSIZE);
  1013   nsGridCell* child;
  1015   int32_t count = GetColumnCount(aIsHorizontal); 
  1017   for (int32_t i=0; i < count; i++)
  1019     if (aIsHorizontal)
  1020      child = GetCellAt(i,aIndex);
  1021     else
  1022      child = GetCellAt(aIndex,i);
  1024     // ignore collapsed children
  1025     if (!child->IsCollapsed())
  1027       nsSize min = child->GetMinSize(aState);
  1028       nsSize childSize = nsBox::BoundsCheckMinMax(min, child->GetMaxSize(aState));
  1029       nsSprocketLayout::AddLargestSize(size, childSize, aIsHorizontal);
  1033   row->mMax = GET_HEIGHT(size, aIsHorizontal) + top + bottom;
  1035   return row->mMax;
  1038 bool
  1039 nsGrid::IsGrid(nsIFrame* aBox)
  1041   nsIGridPart* part = GetPartFromBox(aBox);
  1042   if (!part)
  1043     return false;
  1045   nsGridLayout2* grid = part->CastToGridLayout();
  1047   if (grid)
  1048     return true;
  1050   return false;
  1053 /**
  1054  * This get the flexibilty of the row at aIndex. It's not trivial. There are a few
  1055  * things we need to look at. Specifically we need to see if any <rows> or <columns>
  1056  * tags are around us. Their flexibilty will affect ours.
  1057  */
  1058 nscoord
  1059 nsGrid::GetRowFlex(nsBoxLayoutState& aState, int32_t aIndex, bool aIsHorizontal)
  1061   RebuildIfNeeded();
  1063   nsGridRow* row = GetRowAt(aIndex, aIsHorizontal);
  1065   if (row->IsFlexSet()) 
  1066     return row->mFlex;
  1068   nsIFrame* box = row->mBox;
  1069   row->mFlex = 0;
  1071   if (box) {
  1073     // We need our flex but a inflexible row could be around us. If so
  1074     // neither are we. However if its the row tag just inside the grid it won't 
  1075     // affect us. We need to do this for this case:
  1076     // <grid> 
  1077     //   <rows> 
  1078     //     <rows> // this is not flexible. So our children should not be flexible
  1079     //        <row flex="1"/>
  1080     //        <row flex="1"/>
  1081     //     </rows>
  1082     //        <row/>
  1083     //   </rows>
  1084     // </grid>
  1085     //
  1086     // or..
  1087     //
  1088     // <grid> 
  1089     //  <rows>
  1090     //   <rows> // this is not flexible. So our children should not be flexible
  1091     //     <rows flex="1"> 
  1092     //        <row flex="1"/>
  1093     //        <row flex="1"/>
  1094     //     </rows>
  1095     //        <row/>
  1096     //   </rows>
  1097     //  </row>
  1098     // </grid>
  1101     // So here is how it looks
  1102     //
  1103     // <grid>     
  1104     //   <rows>   // parentsParent
  1105     //     <rows> // parent
  1106     //        <row flex="1"/> 
  1107     //        <row flex="1"/>
  1108     //     </rows>
  1109     //        <row/>
  1110     //   </rows>
  1111     // </grid>
  1113     // so the answer is simple: 1) Walk our parent chain. 2) If we find
  1114     // someone who is not flexible and they aren't the rows immediately in
  1115     // the grid. 3) Then we are not flexible
  1117     box = GetScrollBox(box);
  1118     nsIFrame* parent = box->GetParentBox();
  1119     nsIFrame* parentsParent=nullptr;
  1121     while(parent)
  1123       parent = GetScrollBox(parent);
  1124       parentsParent = parent->GetParentBox();
  1126       // if our parents parent is not a grid
  1127       // the get its flex. If its 0 then we are
  1128       // not flexible.
  1129       if (parentsParent) {
  1130         if (!IsGrid(parentsParent)) {
  1131           nscoord flex = parent->GetFlex(aState);
  1132           nsIFrame::AddCSSFlex(aState, parent, flex);
  1133           if (flex == 0) {
  1134             row->mFlex = 0;
  1135             return row->mFlex;
  1137         } else 
  1138           break;
  1141       parent = parentsParent;
  1144     // get the row flex.
  1145     row->mFlex = box->GetFlex(aState);
  1146     nsIFrame::AddCSSFlex(aState, box, row->mFlex);
  1149   return row->mFlex;
  1152 void
  1153 nsGrid::SetLargestSize(nsSize& aSize, nscoord aHeight, bool aIsHorizontal)
  1155   if (aIsHorizontal) {
  1156     if (aSize.height < aHeight)
  1157       aSize.height = aHeight;
  1158   } else {
  1159     if (aSize.width < aHeight)
  1160       aSize.width = aHeight;
  1164 void
  1165 nsGrid::SetSmallestSize(nsSize& aSize, nscoord aHeight, bool aIsHorizontal)
  1167   if (aIsHorizontal) {
  1168     if (aSize.height > aHeight)
  1169       aSize.height = aHeight;
  1170   } else {
  1171     if (aSize.width < aHeight)
  1172       aSize.width = aHeight;
  1176 int32_t 
  1177 nsGrid::GetRowCount(int32_t aIsHorizontal)
  1179   RebuildIfNeeded();
  1181   if (aIsHorizontal)
  1182     return mRowCount;
  1183   else
  1184     return mColumnCount;
  1187 int32_t 
  1188 nsGrid::GetColumnCount(int32_t aIsHorizontal)
  1190   return GetRowCount(!aIsHorizontal);
  1193 /*
  1194  * A cell in the given row or columns at the given index has had a child added or removed
  1195  */
  1196 void 
  1197 nsGrid::CellAddedOrRemoved(nsBoxLayoutState& aState, int32_t aIndex, bool aIsHorizontal)
  1199   // TBD see if the cell will fit in our current row. If it will
  1200   // just add it in. 
  1201   // but for now rebuild everything.
  1202   if (mMarkingDirty)
  1203     return;
  1205   NeedsRebuild(aState);
  1208 /**
  1209  * A row or columns at the given index had been added or removed
  1210  */
  1211 void 
  1212 nsGrid::RowAddedOrRemoved(nsBoxLayoutState& aState, int32_t aIndex, bool aIsHorizontal)
  1214   // TBD see if we have extra room in the table and just add the new row in
  1215   // for now rebuild the world
  1216   if (mMarkingDirty)
  1217     return;
  1219   NeedsRebuild(aState);
  1222 /*
  1223  * Scrollframes are tranparent. If this is given a scrollframe is will return the
  1224  * frame inside. If there is no scrollframe it does nothing.
  1225  */
  1226 nsIFrame*
  1227 nsGrid::GetScrolledBox(nsIFrame* aChild)
  1229   // first see if it is a scrollframe. If so walk down into it and get the scrolled child
  1230       nsIScrollableFrame *scrollFrame = do_QueryFrame(aChild);
  1231       if (scrollFrame) {
  1232          nsIFrame* scrolledFrame = scrollFrame->GetScrolledFrame();
  1233          NS_ASSERTION(scrolledFrame,"Error no scroll frame!!");
  1234          return scrolledFrame;
  1237       return aChild;
  1240 /*
  1241  * Scrollframes are tranparent. If this is given a child in a scrollframe is will return the
  1242  * scrollframe ourside it. If there is no scrollframe it does nothing.
  1243  */
  1244 nsIFrame*
  1245 nsGrid::GetScrollBox(nsIFrame* aChild)
  1247   if (!aChild)
  1248     return nullptr;
  1250   // get parent
  1251   nsIFrame* parent = aChild->GetParentBox();
  1253   // walk up until we find a scrollframe or a part
  1254   // if it's a scrollframe return it.
  1255   // if it's a parent then the child passed does not
  1256   // have a scroll frame immediately wrapped around it.
  1257   while (parent) {
  1258     nsIScrollableFrame *scrollFrame = do_QueryFrame(parent);
  1259     // scrollframe? Yep return it.
  1260     if (scrollFrame)
  1261       return parent;
  1263     nsCOMPtr<nsIGridPart> parentGridRow = GetPartFromBox(parent);
  1264     // if a part then just return the child
  1265     if (parentGridRow) 
  1266       break;
  1268     parent = parent->GetParentBox();
  1271   return aChild;
  1276 #ifdef DEBUG_grid
  1277 void
  1278 nsGrid::PrintCellMap()
  1281   printf("-----Columns------\n");
  1282   for (int x=0; x < mColumnCount; x++) 
  1285     nsGridRow* column = GetColumnAt(x);
  1286     printf("%d(pf=%d, mn=%d, mx=%d) ", x, column->mPref, column->mMin, column->mMax);
  1289   printf("\n-----Rows------\n");
  1290   for (x=0; x < mRowCount; x++) 
  1292     nsGridRow* column = GetRowAt(x);
  1293     printf("%d(pf=%d, mn=%d, mx=%d) ", x, column->mPref, column->mMin, column->mMax);
  1296   printf("\n");
  1299 #endif

mercurial