layout/xul/nsStackLayout.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/xul/nsStackLayout.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,383 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +//
    1.10 +// Eric Vaughan
    1.11 +// Netscape Communications
    1.12 +//
    1.13 +// See documentation in associated header file
    1.14 +//
    1.15 +
    1.16 +#include "nsStackLayout.h"
    1.17 +#include "nsCOMPtr.h"
    1.18 +#include "nsBoxLayoutState.h"
    1.19 +#include "nsBox.h"
    1.20 +#include "nsBoxFrame.h"
    1.21 +#include "nsGkAtoms.h"
    1.22 +#include "nsIContent.h"
    1.23 +#include "nsNameSpaceManager.h"
    1.24 +
    1.25 +using namespace mozilla;
    1.26 +
    1.27 +nsBoxLayout* nsStackLayout::gInstance = nullptr;
    1.28 +
    1.29 +#define SPECIFIED_LEFT (1 << NS_SIDE_LEFT)
    1.30 +#define SPECIFIED_RIGHT (1 << NS_SIDE_RIGHT)
    1.31 +#define SPECIFIED_TOP (1 << NS_SIDE_TOP)
    1.32 +#define SPECIFIED_BOTTOM (1 << NS_SIDE_BOTTOM)
    1.33 +
    1.34 +nsresult
    1.35 +NS_NewStackLayout( nsIPresShell* aPresShell, nsCOMPtr<nsBoxLayout>& aNewLayout)
    1.36 +{
    1.37 +  if (!nsStackLayout::gInstance) {
    1.38 +    nsStackLayout::gInstance = new nsStackLayout();
    1.39 +    NS_IF_ADDREF(nsStackLayout::gInstance);
    1.40 +  }
    1.41 +  // we have not instance variables so just return our static one.
    1.42 +  aNewLayout = nsStackLayout::gInstance;
    1.43 +  return NS_OK;
    1.44 +} 
    1.45 +
    1.46 +/*static*/ void
    1.47 +nsStackLayout::Shutdown()
    1.48 +{
    1.49 +  NS_IF_RELEASE(gInstance);
    1.50 +}
    1.51 +
    1.52 +nsStackLayout::nsStackLayout()
    1.53 +{
    1.54 +}
    1.55 +
    1.56 +/*
    1.57 + * Sizing: we are as wide as the widest child plus its left offset
    1.58 + * we are tall as the tallest child plus its top offset.
    1.59 + *
    1.60 + * Only children which have -moz-stack-sizing set to stretch-to-fit
    1.61 + * (the default) will be included in the size computations.
    1.62 + */
    1.63 +
    1.64 +nsSize
    1.65 +nsStackLayout::GetPrefSize(nsIFrame* aBox, nsBoxLayoutState& aState)
    1.66 +{
    1.67 +  nsSize prefSize (0, 0);
    1.68 +
    1.69 +  nsIFrame* child = aBox->GetChildBox();
    1.70 +  while (child) {
    1.71 +    if (child->StyleXUL()->mStretchStack) {
    1.72 +      nsSize pref = child->GetPrefSize(aState);
    1.73 +
    1.74 +      AddMargin(child, pref);
    1.75 +      nsMargin offset;
    1.76 +      GetOffset(aState, child, offset);
    1.77 +      pref.width += offset.LeftRight();
    1.78 +      pref.height += offset.TopBottom();
    1.79 +      AddLargestSize(prefSize, pref);
    1.80 +    }
    1.81 +
    1.82 +    child = child->GetNextBox();
    1.83 +  }
    1.84 +
    1.85 +  AddBorderAndPadding(aBox, prefSize);
    1.86 +
    1.87 +  return prefSize;
    1.88 +}
    1.89 +
    1.90 +nsSize
    1.91 +nsStackLayout::GetMinSize(nsIFrame* aBox, nsBoxLayoutState& aState)
    1.92 +{
    1.93 +  nsSize minSize (0, 0);
    1.94 +
    1.95 +  nsIFrame* child = aBox->GetChildBox();
    1.96 +  while (child) {
    1.97 +    if (child->StyleXUL()->mStretchStack) {
    1.98 +      nsSize min = child->GetMinSize(aState);
    1.99 +
   1.100 +      AddMargin(child, min);
   1.101 +      nsMargin offset;
   1.102 +      GetOffset(aState, child, offset);
   1.103 +      min.width += offset.LeftRight();
   1.104 +      min.height += offset.TopBottom();
   1.105 +      AddLargestSize(minSize, min);
   1.106 +    }
   1.107 +
   1.108 +    child = child->GetNextBox();
   1.109 +  }
   1.110 +
   1.111 +  AddBorderAndPadding(aBox, minSize);
   1.112 +
   1.113 +  return minSize;
   1.114 +}
   1.115 +
   1.116 +nsSize
   1.117 +nsStackLayout::GetMaxSize(nsIFrame* aBox, nsBoxLayoutState& aState)
   1.118 +{
   1.119 +  nsSize maxSize (NS_INTRINSICSIZE, NS_INTRINSICSIZE);
   1.120 +
   1.121 +  nsIFrame* child = aBox->GetChildBox();
   1.122 +  while (child) {
   1.123 +    if (child->StyleXUL()->mStretchStack) {
   1.124 +      nsSize min = child->GetMinSize(aState);
   1.125 +      nsSize max = child->GetMaxSize(aState);
   1.126 +
   1.127 +      max = nsBox::BoundsCheckMinMax(min, max);
   1.128 +
   1.129 +      AddMargin(child, max);
   1.130 +      nsMargin offset;
   1.131 +      GetOffset(aState, child, offset);
   1.132 +      max.width += offset.LeftRight();
   1.133 +      max.height += offset.TopBottom();
   1.134 +      AddSmallestSize(maxSize, max);
   1.135 +    }
   1.136 +
   1.137 +    child = child->GetNextBox();
   1.138 +  }
   1.139 +
   1.140 +  AddBorderAndPadding(aBox, maxSize);
   1.141 +
   1.142 +  return maxSize;
   1.143 +}
   1.144 +
   1.145 +
   1.146 +nscoord
   1.147 +nsStackLayout::GetAscent(nsIFrame* aBox, nsBoxLayoutState& aState)
   1.148 +{
   1.149 +  nscoord vAscent = 0;
   1.150 +
   1.151 +  nsIFrame* child = aBox->GetChildBox();
   1.152 +  while (child) {  
   1.153 +    nscoord ascent = child->GetBoxAscent(aState);
   1.154 +    nsMargin margin;
   1.155 +    child->GetMargin(margin);
   1.156 +    ascent += margin.top;
   1.157 +    if (ascent > vAscent)
   1.158 +      vAscent = ascent;
   1.159 +
   1.160 +    child = child->GetNextBox();
   1.161 +  }
   1.162 +
   1.163 +  return vAscent;
   1.164 +}
   1.165 +
   1.166 +uint8_t
   1.167 +nsStackLayout::GetOffset(nsBoxLayoutState& aState, nsIFrame* aChild, nsMargin& aOffset)
   1.168 +{
   1.169 +  aOffset = nsMargin(0, 0, 0, 0);
   1.170 +
   1.171 +  // get the left, right, top and bottom offsets
   1.172 +
   1.173 +  // As an optimization, we cache the fact that we are not positioned to avoid
   1.174 +  // wasting time fetching attributes.
   1.175 +  if (aChild->IsBoxFrame() &&
   1.176 +      (aChild->GetStateBits() & NS_STATE_STACK_NOT_POSITIONED))
   1.177 +    return 0;
   1.178 +
   1.179 +  uint8_t offsetSpecified = 0;
   1.180 +  nsIContent* content = aChild->GetContent();
   1.181 +  if (content) {
   1.182 +    bool ltr = aChild->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR;
   1.183 +    nsAutoString value;
   1.184 +    nsresult error;
   1.185 +
   1.186 +    content->GetAttr(kNameSpaceID_None, nsGkAtoms::start, value);
   1.187 +    if (!value.IsEmpty()) {
   1.188 +      value.Trim("%");
   1.189 +      if (ltr) {
   1.190 +        aOffset.left =
   1.191 +          nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
   1.192 +        offsetSpecified |= SPECIFIED_LEFT;
   1.193 +      } else {
   1.194 +        aOffset.right =
   1.195 +          nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
   1.196 +        offsetSpecified |= SPECIFIED_RIGHT;
   1.197 +      }
   1.198 +    }
   1.199 +
   1.200 +    content->GetAttr(kNameSpaceID_None, nsGkAtoms::end, value);
   1.201 +    if (!value.IsEmpty()) {
   1.202 +      value.Trim("%");
   1.203 +      if (ltr) {
   1.204 +        aOffset.right =
   1.205 +          nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
   1.206 +        offsetSpecified |= SPECIFIED_RIGHT;
   1.207 +      } else {
   1.208 +        aOffset.left =
   1.209 +          nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
   1.210 +        offsetSpecified |= SPECIFIED_LEFT;
   1.211 +      }
   1.212 +    }
   1.213 +
   1.214 +    content->GetAttr(kNameSpaceID_None, nsGkAtoms::left, value);
   1.215 +    if (!value.IsEmpty()) {
   1.216 +      value.Trim("%");
   1.217 +      aOffset.left =
   1.218 +        nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
   1.219 +      offsetSpecified |= SPECIFIED_LEFT;
   1.220 +    }
   1.221 +
   1.222 +    content->GetAttr(kNameSpaceID_None, nsGkAtoms::right, value);
   1.223 +    if (!value.IsEmpty()) {
   1.224 +      value.Trim("%");
   1.225 +      aOffset.right =
   1.226 +        nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
   1.227 +      offsetSpecified |= SPECIFIED_RIGHT;
   1.228 +    }
   1.229 +
   1.230 +    content->GetAttr(kNameSpaceID_None, nsGkAtoms::top, value);
   1.231 +    if (!value.IsEmpty()) {
   1.232 +      value.Trim("%");
   1.233 +      aOffset.top =
   1.234 +        nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
   1.235 +      offsetSpecified |= SPECIFIED_TOP;
   1.236 +    }
   1.237 +
   1.238 +    content->GetAttr(kNameSpaceID_None, nsGkAtoms::bottom, value);
   1.239 +    if (!value.IsEmpty()) {
   1.240 +      value.Trim("%");
   1.241 +      aOffset.bottom =
   1.242 +        nsPresContext::CSSPixelsToAppUnits(value.ToInteger(&error));
   1.243 +      offsetSpecified |= SPECIFIED_BOTTOM;
   1.244 +    }
   1.245 +  }
   1.246 +
   1.247 +  if (!offsetSpecified && aChild->IsBoxFrame()) {
   1.248 +    // If no offset was specified at all, then we cache this fact to avoid requerying
   1.249 +    // CSS or the content model.
   1.250 +    aChild->AddStateBits(NS_STATE_STACK_NOT_POSITIONED);
   1.251 +  }
   1.252 +
   1.253 +  return offsetSpecified;
   1.254 +}
   1.255 +
   1.256 +
   1.257 +NS_IMETHODIMP
   1.258 +nsStackLayout::Layout(nsIFrame* aBox, nsBoxLayoutState& aState)
   1.259 +{
   1.260 +  nsRect clientRect;
   1.261 +  aBox->GetClientRect(clientRect);
   1.262 +
   1.263 +  bool grow;
   1.264 +
   1.265 +  do {
   1.266 +    nsIFrame* child = aBox->GetChildBox();
   1.267 +    grow = false;
   1.268 +
   1.269 +    while (child) 
   1.270 +    {  
   1.271 +      nsMargin margin;
   1.272 +      child->GetMargin(margin);
   1.273 +      nsRect childRect(clientRect);
   1.274 +      childRect.Deflate(margin);
   1.275 +
   1.276 +      if (childRect.width < 0)
   1.277 +        childRect.width = 0;
   1.278 +
   1.279 +      if (childRect.height < 0)
   1.280 +        childRect.height = 0;
   1.281 +
   1.282 +      nsRect oldRect(child->GetRect());
   1.283 +      bool sizeChanged = !oldRect.IsEqualEdges(childRect);
   1.284 +
   1.285 +      // only lay out dirty children or children whose sizes have changed
   1.286 +      if (sizeChanged || NS_SUBTREE_DIRTY(child)) {
   1.287 +          // add in the child's margin
   1.288 +          nsMargin margin;
   1.289 +          child->GetMargin(margin);
   1.290 +
   1.291 +          // obtain our offset from the top left border of the stack's content box.
   1.292 +          nsMargin offset;
   1.293 +          uint8_t offsetSpecified = GetOffset(aState, child, offset);
   1.294 +
   1.295 +          // Set the position and size based on which offsets have been specified:
   1.296 +          //   left only - offset from left edge, preferred width
   1.297 +          //   right only - offset from right edge, preferred width
   1.298 +          //   left and right - offset from left and right edges, width in between this
   1.299 +          //   neither - no offset, full width of stack
   1.300 +          // Vertical direction is similar.
   1.301 +          //
   1.302 +          // Margins on the child are also included in the edge offsets
   1.303 +          if (offsetSpecified) {
   1.304 +            if (offsetSpecified & SPECIFIED_LEFT) {
   1.305 +              childRect.x = clientRect.x + offset.left + margin.left;
   1.306 +              if (offsetSpecified & SPECIFIED_RIGHT) {
   1.307 +                nsSize min = child->GetMinSize(aState);
   1.308 +                nsSize max = child->GetMaxSize(aState);
   1.309 +                nscoord width = clientRect.width - offset.LeftRight() - margin.LeftRight();
   1.310 +                childRect.width = clamped(width, min.width, max.width);
   1.311 +              }
   1.312 +              else {
   1.313 +                childRect.width = child->GetPrefSize(aState).width;
   1.314 +              }
   1.315 +            }
   1.316 +            else if (offsetSpecified & SPECIFIED_RIGHT) {
   1.317 +              childRect.width = child->GetPrefSize(aState).width;
   1.318 +              childRect.x = clientRect.XMost() - offset.right - margin.right - childRect.width;
   1.319 +            }
   1.320 +
   1.321 +            if (offsetSpecified & SPECIFIED_TOP) {
   1.322 +              childRect.y = clientRect.y + offset.top + margin.top;
   1.323 +              if (offsetSpecified & SPECIFIED_BOTTOM) {
   1.324 +                nsSize min = child->GetMinSize(aState);
   1.325 +                nsSize max = child->GetMaxSize(aState);
   1.326 +                nscoord height = clientRect.height - offset.TopBottom() - margin.TopBottom();
   1.327 +                childRect.height = clamped(height, min.height, max.height);
   1.328 +              }
   1.329 +              else {
   1.330 +                childRect.height = child->GetPrefSize(aState).height;
   1.331 +              }
   1.332 +            }
   1.333 +            else if (offsetSpecified & SPECIFIED_BOTTOM) {
   1.334 +              childRect.height = child->GetPrefSize(aState).height;
   1.335 +              childRect.y = clientRect.YMost() - offset.bottom - margin.bottom - childRect.height;
   1.336 +            }
   1.337 +          }
   1.338 +
   1.339 +          // Now place the child.
   1.340 +          child->SetBounds(aState, childRect);
   1.341 +
   1.342 +          // Flow the child.
   1.343 +          child->Layout(aState);
   1.344 +
   1.345 +          // Get the child's new rect.
   1.346 +          childRect = child->GetRect();
   1.347 +          childRect.Inflate(margin);
   1.348 +
   1.349 +          if (child->StyleXUL()->mStretchStack) {
   1.350 +            // Did the child push back on us and get bigger?
   1.351 +            if (offset.LeftRight() + childRect.width > clientRect.width) {
   1.352 +              clientRect.width = childRect.width + offset.LeftRight();
   1.353 +              grow = true;
   1.354 +            }
   1.355 +
   1.356 +            if (offset.TopBottom() + childRect.height > clientRect.height) {
   1.357 +              clientRect.height = childRect.height + offset.TopBottom();
   1.358 +              grow = true;
   1.359 +            }
   1.360 +          }
   1.361 +       }
   1.362 +
   1.363 +       child = child->GetNextBox();
   1.364 +     }
   1.365 +   } while (grow);
   1.366 +   
   1.367 +   // if some HTML inside us got bigger we need to force ourselves to
   1.368 +   // get bigger
   1.369 +   nsRect bounds(aBox->GetRect());
   1.370 +   nsMargin bp;
   1.371 +   aBox->GetBorderAndPadding(bp);
   1.372 +   clientRect.Inflate(bp);
   1.373 +
   1.374 +   if (clientRect.width > bounds.width || clientRect.height > bounds.height)
   1.375 +   {
   1.376 +     if (clientRect.width > bounds.width)
   1.377 +       bounds.width = clientRect.width;
   1.378 +     if (clientRect.height > bounds.height)
   1.379 +       bounds.height = clientRect.height;
   1.380 +
   1.381 +     aBox->SetBounds(aState, bounds);
   1.382 +   }
   1.383 +
   1.384 +   return NS_OK;
   1.385 +}
   1.386 +

mercurial