layout/xul/nsStackLayout.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

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

mercurial