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 +