Coverage Report

Created: 2018-09-25 14:53

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