Tue, 06 Jan 2015 21:39:09 +0100
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);
1005 }
1007 row->mMax = GET_HEIGHT(size, aIsHorizontal);
1008 return row->mMax;
1009 }
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++)
1018 {
1019 if (aIsHorizontal)
1020 child = GetCellAt(i,aIndex);
1021 else
1022 child = GetCellAt(aIndex,i);
1024 // ignore collapsed children
1025 if (!child->IsCollapsed())
1026 {
1027 nsSize min = child->GetMinSize(aState);
1028 nsSize childSize = nsBox::BoundsCheckMinMax(min, child->GetMaxSize(aState));
1029 nsSprocketLayout::AddLargestSize(size, childSize, aIsHorizontal);
1030 }
1031 }
1033 row->mMax = GET_HEIGHT(size, aIsHorizontal) + top + bottom;
1035 return row->mMax;
1036 }
1038 bool
1039 nsGrid::IsGrid(nsIFrame* aBox)
1040 {
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;
1051 }
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)
1060 {
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)
1122 {
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;
1136 }
1137 } else
1138 break;
1139 }
1141 parent = parentsParent;
1142 }
1144 // get the row flex.
1145 row->mFlex = box->GetFlex(aState);
1146 nsIFrame::AddCSSFlex(aState, box, row->mFlex);
1147 }
1149 return row->mFlex;
1150 }
1152 void
1153 nsGrid::SetLargestSize(nsSize& aSize, nscoord aHeight, bool aIsHorizontal)
1154 {
1155 if (aIsHorizontal) {
1156 if (aSize.height < aHeight)
1157 aSize.height = aHeight;
1158 } else {
1159 if (aSize.width < aHeight)
1160 aSize.width = aHeight;
1161 }
1162 }
1164 void
1165 nsGrid::SetSmallestSize(nsSize& aSize, nscoord aHeight, bool aIsHorizontal)
1166 {
1167 if (aIsHorizontal) {
1168 if (aSize.height > aHeight)
1169 aSize.height = aHeight;
1170 } else {
1171 if (aSize.width < aHeight)
1172 aSize.width = aHeight;
1173 }
1174 }
1176 int32_t
1177 nsGrid::GetRowCount(int32_t aIsHorizontal)
1178 {
1179 RebuildIfNeeded();
1181 if (aIsHorizontal)
1182 return mRowCount;
1183 else
1184 return mColumnCount;
1185 }
1187 int32_t
1188 nsGrid::GetColumnCount(int32_t aIsHorizontal)
1189 {
1190 return GetRowCount(!aIsHorizontal);
1191 }
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)
1198 {
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);
1206 }
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)
1213 {
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);
1220 }
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)
1228 {
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;
1235 }
1237 return aChild;
1238 }
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)
1246 {
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();
1269 }
1271 return aChild;
1272 }
1276 #ifdef DEBUG_grid
1277 void
1278 nsGrid::PrintCellMap()
1279 {
1281 printf("-----Columns------\n");
1282 for (int x=0; x < mColumnCount; x++)
1283 {
1285 nsGridRow* column = GetColumnAt(x);
1286 printf("%d(pf=%d, mn=%d, mx=%d) ", x, column->mPref, column->mMin, column->mMax);
1287 }
1289 printf("\n-----Rows------\n");
1290 for (x=0; x < mRowCount; x++)
1291 {
1292 nsGridRow* column = GetRowAt(x);
1293 printf("%d(pf=%d, mn=%d, mx=%d) ", x, column->mPref, column->mMin, column->mMax);
1294 }
1296 printf("\n");
1298 }
1299 #endif