layout/xul/nsBoxFrame.cpp

Wed, 31 Dec 2014 07:16:47 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:16:47 +0100
branch
TOR_BUG_9701
changeset 3
141e0f1194b1
permissions
-rw-r--r--

Revert simplistic fix pending revisit of Mozilla integration attempt.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=2 sw=2 et tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 //
michael@0 8 // Eric Vaughan
michael@0 9 // Netscape Communications
michael@0 10 //
michael@0 11 // See documentation in associated header file
michael@0 12 //
michael@0 13
michael@0 14 // How boxes layout
michael@0 15 // ----------------
michael@0 16 // Boxes layout a bit differently than html. html does a bottom up layout. Where boxes do a top down.
michael@0 17 // 1) First thing a box does it goes out and askes each child for its min, max, and preferred sizes.
michael@0 18 // 2) It then adds them up to determine its size.
michael@0 19 // 3) If the box was asked to layout it self intrinically it will layout its children at their preferred size
michael@0 20 // otherwise it will layout the child at the size it was told to. It will squeeze or stretch its children if
michael@0 21 // Necessary.
michael@0 22 //
michael@0 23 // However there is a catch. Some html components like block frames can not determine their preferred size.
michael@0 24 // this is their size if they were laid out intrinsically. So the box will flow the child to determine this can
michael@0 25 // cache the value.
michael@0 26
michael@0 27 // Boxes and Incremental Reflow
michael@0 28 // ----------------------------
michael@0 29 // Boxes layout out top down by adding up their children's min, max, and preferred sizes. Only problem is if a incremental
michael@0 30 // reflow occurs. The preferred size of a child deep in the hierarchy could change. And this could change
michael@0 31 // any number of syblings around the box. Basically any children in the reflow chain must have their caches cleared
michael@0 32 // so when asked for there current size they can relayout themselves.
michael@0 33
michael@0 34 #include "nsBoxLayoutState.h"
michael@0 35 #include "nsBoxFrame.h"
michael@0 36 #include "mozilla/dom/Touch.h"
michael@0 37 #include "nsStyleContext.h"
michael@0 38 #include "nsPlaceholderFrame.h"
michael@0 39 #include "nsPresContext.h"
michael@0 40 #include "nsCOMPtr.h"
michael@0 41 #include "nsNameSpaceManager.h"
michael@0 42 #include "nsGkAtoms.h"
michael@0 43 #include "nsIContent.h"
michael@0 44 #include "nsHTMLParts.h"
michael@0 45 #include "nsViewManager.h"
michael@0 46 #include "nsView.h"
michael@0 47 #include "nsIPresShell.h"
michael@0 48 #include "nsCSSRendering.h"
michael@0 49 #include "nsIServiceManager.h"
michael@0 50 #include "nsBoxLayout.h"
michael@0 51 #include "nsSprocketLayout.h"
michael@0 52 #include "nsIScrollableFrame.h"
michael@0 53 #include "nsWidgetsCID.h"
michael@0 54 #include "nsCSSAnonBoxes.h"
michael@0 55 #include "nsContainerFrame.h"
michael@0 56 #include "nsIDOMElement.h"
michael@0 57 #include "nsITheme.h"
michael@0 58 #include "nsTransform2D.h"
michael@0 59 #include "mozilla/EventStateManager.h"
michael@0 60 #include "nsIDOMEvent.h"
michael@0 61 #include "nsDisplayList.h"
michael@0 62 #include "mozilla/Preferences.h"
michael@0 63 #include "nsThemeConstants.h"
michael@0 64 #include "nsLayoutUtils.h"
michael@0 65 #include <algorithm>
michael@0 66
michael@0 67 // Needed for Print Preview
michael@0 68 #include "nsIURI.h"
michael@0 69
michael@0 70 #include "mozilla/TouchEvents.h"
michael@0 71
michael@0 72 using namespace mozilla;
michael@0 73 using namespace mozilla::dom;
michael@0 74
michael@0 75 //define DEBUG_REDRAW
michael@0 76
michael@0 77 #define DEBUG_SPRING_SIZE 8
michael@0 78 #define DEBUG_BORDER_SIZE 2
michael@0 79 #define COIL_SIZE 8
michael@0 80
michael@0 81 //#define TEST_SANITY
michael@0 82
michael@0 83 #ifdef DEBUG_rods
michael@0 84 //#define DO_NOISY_REFLOW
michael@0 85 #endif
michael@0 86
michael@0 87 #ifdef DEBUG_LAYOUT
michael@0 88 bool nsBoxFrame::gDebug = false;
michael@0 89 nsIFrame* nsBoxFrame::mDebugChild = nullptr;
michael@0 90 #endif
michael@0 91
michael@0 92 nsIFrame*
michael@0 93 NS_NewBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot, nsBoxLayout* aLayoutManager)
michael@0 94 {
michael@0 95 return new (aPresShell) nsBoxFrame(aPresShell, aContext, aIsRoot, aLayoutManager);
michael@0 96 }
michael@0 97
michael@0 98 nsIFrame*
michael@0 99 NS_NewBoxFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
michael@0 100 {
michael@0 101 return new (aPresShell) nsBoxFrame(aPresShell, aContext);
michael@0 102 }
michael@0 103
michael@0 104 NS_IMPL_FRAMEARENA_HELPERS(nsBoxFrame)
michael@0 105
michael@0 106 #ifdef DEBUG
michael@0 107 NS_QUERYFRAME_HEAD(nsBoxFrame)
michael@0 108 NS_QUERYFRAME_ENTRY(nsBoxFrame)
michael@0 109 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
michael@0 110 #endif
michael@0 111
michael@0 112 nsBoxFrame::nsBoxFrame(nsIPresShell* aPresShell,
michael@0 113 nsStyleContext* aContext,
michael@0 114 bool aIsRoot,
michael@0 115 nsBoxLayout* aLayoutManager) :
michael@0 116 nsContainerFrame(aContext)
michael@0 117 {
michael@0 118 mState |= NS_STATE_IS_HORIZONTAL;
michael@0 119 mState |= NS_STATE_AUTO_STRETCH;
michael@0 120
michael@0 121 if (aIsRoot)
michael@0 122 mState |= NS_STATE_IS_ROOT;
michael@0 123
michael@0 124 mValign = vAlign_Top;
michael@0 125 mHalign = hAlign_Left;
michael@0 126
michael@0 127 // if no layout manager specified us the static sprocket layout
michael@0 128 nsCOMPtr<nsBoxLayout> layout = aLayoutManager;
michael@0 129
michael@0 130 if (layout == nullptr) {
michael@0 131 NS_NewSprocketLayout(aPresShell, layout);
michael@0 132 }
michael@0 133
michael@0 134 SetLayoutManager(layout);
michael@0 135 }
michael@0 136
michael@0 137 nsBoxFrame::~nsBoxFrame()
michael@0 138 {
michael@0 139 }
michael@0 140
michael@0 141 nsresult
michael@0 142 nsBoxFrame::SetInitialChildList(ChildListID aListID,
michael@0 143 nsFrameList& aChildList)
michael@0 144 {
michael@0 145 nsresult r = nsContainerFrame::SetInitialChildList(aListID, aChildList);
michael@0 146 if (r == NS_OK) {
michael@0 147 // initialize our list of infos.
michael@0 148 nsBoxLayoutState state(PresContext());
michael@0 149 CheckBoxOrder();
michael@0 150 if (mLayoutManager)
michael@0 151 mLayoutManager->ChildrenSet(this, state, mFrames.FirstChild());
michael@0 152 } else {
michael@0 153 NS_WARNING("Warning add child failed!!\n");
michael@0 154 }
michael@0 155
michael@0 156 return r;
michael@0 157 }
michael@0 158
michael@0 159 /* virtual */ void
michael@0 160 nsBoxFrame::DidSetStyleContext(nsStyleContext* aOldStyleContext)
michael@0 161 {
michael@0 162 nsContainerFrame::DidSetStyleContext(aOldStyleContext);
michael@0 163
michael@0 164 // The values that CacheAttributes() computes depend on our style,
michael@0 165 // so we need to recompute them here...
michael@0 166 CacheAttributes();
michael@0 167 }
michael@0 168
michael@0 169 /**
michael@0 170 * Initialize us. This is a good time to get the alignment of the box
michael@0 171 */
michael@0 172 void
michael@0 173 nsBoxFrame::Init(nsIContent* aContent,
michael@0 174 nsIFrame* aParent,
michael@0 175 nsIFrame* aPrevInFlow)
michael@0 176 {
michael@0 177 nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
michael@0 178
michael@0 179 if (GetStateBits() & NS_FRAME_FONT_INFLATION_CONTAINER) {
michael@0 180 AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT);
michael@0 181 }
michael@0 182
michael@0 183 MarkIntrinsicWidthsDirty();
michael@0 184
michael@0 185 CacheAttributes();
michael@0 186
michael@0 187 #ifdef DEBUG_LAYOUT
michael@0 188 // if we are root and this
michael@0 189 if (mState & NS_STATE_IS_ROOT)
michael@0 190 GetDebugPref(GetPresContext());
michael@0 191 #endif
michael@0 192
michael@0 193 UpdateMouseThrough();
michael@0 194
michael@0 195 // register access key
michael@0 196 RegUnregAccessKey(true);
michael@0 197 }
michael@0 198
michael@0 199 void nsBoxFrame::UpdateMouseThrough()
michael@0 200 {
michael@0 201 if (mContent) {
michael@0 202 static nsIContent::AttrValuesArray strings[] =
michael@0 203 {&nsGkAtoms::never, &nsGkAtoms::always, nullptr};
michael@0 204 switch (mContent->FindAttrValueIn(kNameSpaceID_None,
michael@0 205 nsGkAtoms::mousethrough, strings, eCaseMatters)) {
michael@0 206 case 0: AddStateBits(NS_FRAME_MOUSE_THROUGH_NEVER); break;
michael@0 207 case 1: AddStateBits(NS_FRAME_MOUSE_THROUGH_ALWAYS); break;
michael@0 208 case 2: {
michael@0 209 RemoveStateBits(NS_FRAME_MOUSE_THROUGH_ALWAYS);
michael@0 210 RemoveStateBits(NS_FRAME_MOUSE_THROUGH_NEVER);
michael@0 211 break;
michael@0 212 }
michael@0 213 }
michael@0 214 }
michael@0 215 }
michael@0 216
michael@0 217 void
michael@0 218 nsBoxFrame::CacheAttributes()
michael@0 219 {
michael@0 220 /*
michael@0 221 printf("Caching: ");
michael@0 222 DumpBox(stdout);
michael@0 223 printf("\n");
michael@0 224 */
michael@0 225
michael@0 226 mValign = vAlign_Top;
michael@0 227 mHalign = hAlign_Left;
michael@0 228
michael@0 229 bool orient = false;
michael@0 230 GetInitialOrientation(orient);
michael@0 231 if (orient)
michael@0 232 mState |= NS_STATE_IS_HORIZONTAL;
michael@0 233 else
michael@0 234 mState &= ~NS_STATE_IS_HORIZONTAL;
michael@0 235
michael@0 236 bool normal = true;
michael@0 237 GetInitialDirection(normal);
michael@0 238 if (normal)
michael@0 239 mState |= NS_STATE_IS_DIRECTION_NORMAL;
michael@0 240 else
michael@0 241 mState &= ~NS_STATE_IS_DIRECTION_NORMAL;
michael@0 242
michael@0 243 GetInitialVAlignment(mValign);
michael@0 244 GetInitialHAlignment(mHalign);
michael@0 245
michael@0 246 bool equalSize = false;
michael@0 247 GetInitialEqualSize(equalSize);
michael@0 248 if (equalSize)
michael@0 249 mState |= NS_STATE_EQUAL_SIZE;
michael@0 250 else
michael@0 251 mState &= ~NS_STATE_EQUAL_SIZE;
michael@0 252
michael@0 253 bool autostretch = !!(mState & NS_STATE_AUTO_STRETCH);
michael@0 254 GetInitialAutoStretch(autostretch);
michael@0 255 if (autostretch)
michael@0 256 mState |= NS_STATE_AUTO_STRETCH;
michael@0 257 else
michael@0 258 mState &= ~NS_STATE_AUTO_STRETCH;
michael@0 259
michael@0 260
michael@0 261 #ifdef DEBUG_LAYOUT
michael@0 262 bool debug = mState & NS_STATE_SET_TO_DEBUG;
michael@0 263 bool debugSet = GetInitialDebug(debug);
michael@0 264 if (debugSet) {
michael@0 265 mState |= NS_STATE_DEBUG_WAS_SET;
michael@0 266 if (debug)
michael@0 267 mState |= NS_STATE_SET_TO_DEBUG;
michael@0 268 else
michael@0 269 mState &= ~NS_STATE_SET_TO_DEBUG;
michael@0 270 } else {
michael@0 271 mState &= ~NS_STATE_DEBUG_WAS_SET;
michael@0 272 }
michael@0 273 #endif
michael@0 274 }
michael@0 275
michael@0 276 #ifdef DEBUG_LAYOUT
michael@0 277 bool
michael@0 278 nsBoxFrame::GetInitialDebug(bool& aDebug)
michael@0 279 {
michael@0 280 if (!GetContent())
michael@0 281 return false;
michael@0 282
michael@0 283 static nsIContent::AttrValuesArray strings[] =
michael@0 284 {&nsGkAtoms::_false, &nsGkAtoms::_true, nullptr};
michael@0 285 int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None,
michael@0 286 nsGkAtoms::debug, strings, eCaseMatters);
michael@0 287 if (index >= 0) {
michael@0 288 aDebug = index == 1;
michael@0 289 return true;
michael@0 290 }
michael@0 291
michael@0 292 return false;
michael@0 293 }
michael@0 294 #endif
michael@0 295
michael@0 296 bool
michael@0 297 nsBoxFrame::GetInitialHAlignment(nsBoxFrame::Halignment& aHalign)
michael@0 298 {
michael@0 299 if (!GetContent())
michael@0 300 return false;
michael@0 301
michael@0 302 // XXXdwh Everything inside this if statement is deprecated code.
michael@0 303 static nsIContent::AttrValuesArray alignStrings[] =
michael@0 304 {&nsGkAtoms::left, &nsGkAtoms::right, nullptr};
michael@0 305 static const Halignment alignValues[] = {hAlign_Left, hAlign_Right};
michael@0 306 int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::align,
michael@0 307 alignStrings, eCaseMatters);
michael@0 308 if (index >= 0) {
michael@0 309 aHalign = alignValues[index];
michael@0 310 return true;
michael@0 311 }
michael@0 312
michael@0 313 // Now that the deprecated stuff is out of the way, we move on to check the appropriate
michael@0 314 // attribute. For horizontal boxes, we are checking the PACK attribute. For vertical boxes
michael@0 315 // we are checking the ALIGN attribute.
michael@0 316 nsIAtom* attrName = IsHorizontal() ? nsGkAtoms::pack : nsGkAtoms::align;
michael@0 317 static nsIContent::AttrValuesArray strings[] =
michael@0 318 {&nsGkAtoms::_empty, &nsGkAtoms::start, &nsGkAtoms::center, &nsGkAtoms::end, nullptr};
michael@0 319 static const Halignment values[] =
michael@0 320 {hAlign_Left/*not used*/, hAlign_Left, hAlign_Center, hAlign_Right};
michael@0 321 index = GetContent()->FindAttrValueIn(kNameSpaceID_None, attrName,
michael@0 322 strings, eCaseMatters);
michael@0 323
michael@0 324 if (index == nsIContent::ATTR_VALUE_NO_MATCH) {
michael@0 325 // The attr was present but had a nonsensical value. Revert to the default.
michael@0 326 return false;
michael@0 327 }
michael@0 328 if (index > 0) {
michael@0 329 aHalign = values[index];
michael@0 330 return true;
michael@0 331 }
michael@0 332
michael@0 333 // Now that we've checked for the attribute it's time to check CSS. For
michael@0 334 // horizontal boxes we're checking PACK. For vertical boxes we are checking
michael@0 335 // ALIGN.
michael@0 336 const nsStyleXUL* boxInfo = StyleXUL();
michael@0 337 if (IsHorizontal()) {
michael@0 338 switch (boxInfo->mBoxPack) {
michael@0 339 case NS_STYLE_BOX_PACK_START:
michael@0 340 aHalign = nsBoxFrame::hAlign_Left;
michael@0 341 return true;
michael@0 342 case NS_STYLE_BOX_PACK_CENTER:
michael@0 343 aHalign = nsBoxFrame::hAlign_Center;
michael@0 344 return true;
michael@0 345 case NS_STYLE_BOX_PACK_END:
michael@0 346 aHalign = nsBoxFrame::hAlign_Right;
michael@0 347 return true;
michael@0 348 default: // Nonsensical value. Just bail.
michael@0 349 return false;
michael@0 350 }
michael@0 351 }
michael@0 352 else {
michael@0 353 switch (boxInfo->mBoxAlign) {
michael@0 354 case NS_STYLE_BOX_ALIGN_START:
michael@0 355 aHalign = nsBoxFrame::hAlign_Left;
michael@0 356 return true;
michael@0 357 case NS_STYLE_BOX_ALIGN_CENTER:
michael@0 358 aHalign = nsBoxFrame::hAlign_Center;
michael@0 359 return true;
michael@0 360 case NS_STYLE_BOX_ALIGN_END:
michael@0 361 aHalign = nsBoxFrame::hAlign_Right;
michael@0 362 return true;
michael@0 363 default: // Nonsensical value. Just bail.
michael@0 364 return false;
michael@0 365 }
michael@0 366 }
michael@0 367
michael@0 368 return false;
michael@0 369 }
michael@0 370
michael@0 371 bool
michael@0 372 nsBoxFrame::GetInitialVAlignment(nsBoxFrame::Valignment& aValign)
michael@0 373 {
michael@0 374 if (!GetContent())
michael@0 375 return false;
michael@0 376
michael@0 377 static nsIContent::AttrValuesArray valignStrings[] =
michael@0 378 {&nsGkAtoms::top, &nsGkAtoms::baseline, &nsGkAtoms::middle, &nsGkAtoms::bottom, nullptr};
michael@0 379 static const Valignment valignValues[] =
michael@0 380 {vAlign_Top, vAlign_BaseLine, vAlign_Middle, vAlign_Bottom};
michael@0 381 int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::valign,
michael@0 382 valignStrings, eCaseMatters);
michael@0 383 if (index >= 0) {
michael@0 384 aValign = valignValues[index];
michael@0 385 return true;
michael@0 386 }
michael@0 387
michael@0 388 // Now that the deprecated stuff is out of the way, we move on to check the appropriate
michael@0 389 // attribute. For horizontal boxes, we are checking the ALIGN attribute. For vertical boxes
michael@0 390 // we are checking the PACK attribute.
michael@0 391 nsIAtom* attrName = IsHorizontal() ? nsGkAtoms::align : nsGkAtoms::pack;
michael@0 392 static nsIContent::AttrValuesArray strings[] =
michael@0 393 {&nsGkAtoms::_empty, &nsGkAtoms::start, &nsGkAtoms::center,
michael@0 394 &nsGkAtoms::baseline, &nsGkAtoms::end, nullptr};
michael@0 395 static const Valignment values[] =
michael@0 396 {vAlign_Top/*not used*/, vAlign_Top, vAlign_Middle, vAlign_BaseLine, vAlign_Bottom};
michael@0 397 index = GetContent()->FindAttrValueIn(kNameSpaceID_None, attrName,
michael@0 398 strings, eCaseMatters);
michael@0 399 if (index == nsIContent::ATTR_VALUE_NO_MATCH) {
michael@0 400 // The attr was present but had a nonsensical value. Revert to the default.
michael@0 401 return false;
michael@0 402 }
michael@0 403 if (index > 0) {
michael@0 404 aValign = values[index];
michael@0 405 return true;
michael@0 406 }
michael@0 407
michael@0 408 // Now that we've checked for the attribute it's time to check CSS. For
michael@0 409 // horizontal boxes we're checking ALIGN. For vertical boxes we are checking
michael@0 410 // PACK.
michael@0 411 const nsStyleXUL* boxInfo = StyleXUL();
michael@0 412 if (IsHorizontal()) {
michael@0 413 switch (boxInfo->mBoxAlign) {
michael@0 414 case NS_STYLE_BOX_ALIGN_START:
michael@0 415 aValign = nsBoxFrame::vAlign_Top;
michael@0 416 return true;
michael@0 417 case NS_STYLE_BOX_ALIGN_CENTER:
michael@0 418 aValign = nsBoxFrame::vAlign_Middle;
michael@0 419 return true;
michael@0 420 case NS_STYLE_BOX_ALIGN_BASELINE:
michael@0 421 aValign = nsBoxFrame::vAlign_BaseLine;
michael@0 422 return true;
michael@0 423 case NS_STYLE_BOX_ALIGN_END:
michael@0 424 aValign = nsBoxFrame::vAlign_Bottom;
michael@0 425 return true;
michael@0 426 default: // Nonsensical value. Just bail.
michael@0 427 return false;
michael@0 428 }
michael@0 429 }
michael@0 430 else {
michael@0 431 switch (boxInfo->mBoxPack) {
michael@0 432 case NS_STYLE_BOX_PACK_START:
michael@0 433 aValign = nsBoxFrame::vAlign_Top;
michael@0 434 return true;
michael@0 435 case NS_STYLE_BOX_PACK_CENTER:
michael@0 436 aValign = nsBoxFrame::vAlign_Middle;
michael@0 437 return true;
michael@0 438 case NS_STYLE_BOX_PACK_END:
michael@0 439 aValign = nsBoxFrame::vAlign_Bottom;
michael@0 440 return true;
michael@0 441 default: // Nonsensical value. Just bail.
michael@0 442 return false;
michael@0 443 }
michael@0 444 }
michael@0 445
michael@0 446 return false;
michael@0 447 }
michael@0 448
michael@0 449 void
michael@0 450 nsBoxFrame::GetInitialOrientation(bool& aIsHorizontal)
michael@0 451 {
michael@0 452 // see if we are a vertical or horizontal box.
michael@0 453 if (!GetContent())
michael@0 454 return;
michael@0 455
michael@0 456 // Check the style system first.
michael@0 457 const nsStyleXUL* boxInfo = StyleXUL();
michael@0 458 if (boxInfo->mBoxOrient == NS_STYLE_BOX_ORIENT_HORIZONTAL)
michael@0 459 aIsHorizontal = true;
michael@0 460 else
michael@0 461 aIsHorizontal = false;
michael@0 462
michael@0 463 // Now see if we have an attribute. The attribute overrides
michael@0 464 // the style system value.
michael@0 465 static nsIContent::AttrValuesArray strings[] =
michael@0 466 {&nsGkAtoms::vertical, &nsGkAtoms::horizontal, nullptr};
michael@0 467 int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::orient,
michael@0 468 strings, eCaseMatters);
michael@0 469 if (index >= 0) {
michael@0 470 aIsHorizontal = index == 1;
michael@0 471 }
michael@0 472 }
michael@0 473
michael@0 474 void
michael@0 475 nsBoxFrame::GetInitialDirection(bool& aIsNormal)
michael@0 476 {
michael@0 477 if (!GetContent())
michael@0 478 return;
michael@0 479
michael@0 480 if (IsHorizontal()) {
michael@0 481 // For horizontal boxes only, we initialize our value based off the CSS 'direction' property.
michael@0 482 // This means that BiDI users will end up with horizontally inverted chrome.
michael@0 483 aIsNormal = (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_LTR); // If text runs RTL then so do we.
michael@0 484 }
michael@0 485 else
michael@0 486 aIsNormal = true; // Assume a normal direction in the vertical case.
michael@0 487
michael@0 488 // Now check the style system to see if we should invert aIsNormal.
michael@0 489 const nsStyleXUL* boxInfo = StyleXUL();
michael@0 490 if (boxInfo->mBoxDirection == NS_STYLE_BOX_DIRECTION_REVERSE)
michael@0 491 aIsNormal = !aIsNormal; // Invert our direction.
michael@0 492
michael@0 493 // Now see if we have an attribute. The attribute overrides
michael@0 494 // the style system value.
michael@0 495 if (IsHorizontal()) {
michael@0 496 static nsIContent::AttrValuesArray strings[] =
michael@0 497 {&nsGkAtoms::reverse, &nsGkAtoms::ltr, &nsGkAtoms::rtl, nullptr};
michael@0 498 int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::dir,
michael@0 499 strings, eCaseMatters);
michael@0 500 if (index >= 0) {
michael@0 501 bool values[] = {!aIsNormal, true, false};
michael@0 502 aIsNormal = values[index];
michael@0 503 }
michael@0 504 } else if (GetContent()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::dir,
michael@0 505 nsGkAtoms::reverse, eCaseMatters)) {
michael@0 506 aIsNormal = !aIsNormal;
michael@0 507 }
michael@0 508 }
michael@0 509
michael@0 510 /* Returns true if it was set.
michael@0 511 */
michael@0 512 bool
michael@0 513 nsBoxFrame::GetInitialEqualSize(bool& aEqualSize)
michael@0 514 {
michael@0 515 // see if we are a vertical or horizontal box.
michael@0 516 if (!GetContent())
michael@0 517 return false;
michael@0 518
michael@0 519 if (GetContent()->AttrValueIs(kNameSpaceID_None, nsGkAtoms::equalsize,
michael@0 520 nsGkAtoms::always, eCaseMatters)) {
michael@0 521 aEqualSize = true;
michael@0 522 return true;
michael@0 523 }
michael@0 524
michael@0 525 return false;
michael@0 526 }
michael@0 527
michael@0 528 /* Returns true if it was set.
michael@0 529 */
michael@0 530 bool
michael@0 531 nsBoxFrame::GetInitialAutoStretch(bool& aStretch)
michael@0 532 {
michael@0 533 if (!GetContent())
michael@0 534 return false;
michael@0 535
michael@0 536 // Check the align attribute.
michael@0 537 static nsIContent::AttrValuesArray strings[] =
michael@0 538 {&nsGkAtoms::_empty, &nsGkAtoms::stretch, nullptr};
michael@0 539 int32_t index = GetContent()->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::align,
michael@0 540 strings, eCaseMatters);
michael@0 541 if (index != nsIContent::ATTR_MISSING && index != 0) {
michael@0 542 aStretch = index == 1;
michael@0 543 return true;
michael@0 544 }
michael@0 545
michael@0 546 // Check the CSS box-align property.
michael@0 547 const nsStyleXUL* boxInfo = StyleXUL();
michael@0 548 aStretch = (boxInfo->mBoxAlign == NS_STYLE_BOX_ALIGN_STRETCH);
michael@0 549
michael@0 550 return true;
michael@0 551 }
michael@0 552
michael@0 553 nsresult
michael@0 554 nsBoxFrame::DidReflow(nsPresContext* aPresContext,
michael@0 555 const nsHTMLReflowState* aReflowState,
michael@0 556 nsDidReflowStatus aStatus)
michael@0 557 {
michael@0 558 nsFrameState preserveBits =
michael@0 559 mState & (NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
michael@0 560 nsresult rv = nsFrame::DidReflow(aPresContext, aReflowState, aStatus);
michael@0 561 mState |= preserveBits;
michael@0 562 return rv;
michael@0 563 }
michael@0 564
michael@0 565 bool
michael@0 566 nsBoxFrame::HonorPrintBackgroundSettings()
michael@0 567 {
michael@0 568 return (!mContent || !mContent->IsInNativeAnonymousSubtree()) &&
michael@0 569 nsContainerFrame::HonorPrintBackgroundSettings();
michael@0 570 }
michael@0 571
michael@0 572 #ifdef DO_NOISY_REFLOW
michael@0 573 static int myCounter = 0;
michael@0 574 static void printSize(char * aDesc, nscoord aSize)
michael@0 575 {
michael@0 576 printf(" %s: ", aDesc);
michael@0 577 if (aSize == NS_UNCONSTRAINEDSIZE) {
michael@0 578 printf("UC");
michael@0 579 } else {
michael@0 580 printf("%d", aSize);
michael@0 581 }
michael@0 582 }
michael@0 583 #endif
michael@0 584
michael@0 585 /* virtual */ nscoord
michael@0 586 nsBoxFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
michael@0 587 {
michael@0 588 nscoord result;
michael@0 589 DISPLAY_MIN_WIDTH(this, result);
michael@0 590
michael@0 591 nsBoxLayoutState state(PresContext(), aRenderingContext);
michael@0 592 nsSize minSize = GetMinSize(state);
michael@0 593
michael@0 594 // GetMinSize returns border-box width, and we want to return content
michael@0 595 // width. Since Reflow uses the reflow state's border and padding, we
michael@0 596 // actually just want to subtract what GetMinSize added, which is the
michael@0 597 // result of GetBorderAndPadding.
michael@0 598 nsMargin bp;
michael@0 599 GetBorderAndPadding(bp);
michael@0 600
michael@0 601 result = minSize.width - bp.LeftRight();
michael@0 602 result = std::max(result, 0);
michael@0 603
michael@0 604 return result;
michael@0 605 }
michael@0 606
michael@0 607 /* virtual */ nscoord
michael@0 608 nsBoxFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
michael@0 609 {
michael@0 610 nscoord result;
michael@0 611 DISPLAY_PREF_WIDTH(this, result);
michael@0 612
michael@0 613 nsBoxLayoutState state(PresContext(), aRenderingContext);
michael@0 614 nsSize prefSize = GetPrefSize(state);
michael@0 615
michael@0 616 // GetPrefSize returns border-box width, and we want to return content
michael@0 617 // width. Since Reflow uses the reflow state's border and padding, we
michael@0 618 // actually just want to subtract what GetPrefSize added, which is the
michael@0 619 // result of GetBorderAndPadding.
michael@0 620 nsMargin bp;
michael@0 621 GetBorderAndPadding(bp);
michael@0 622
michael@0 623 result = prefSize.width - bp.LeftRight();
michael@0 624 result = std::max(result, 0);
michael@0 625
michael@0 626 return result;
michael@0 627 }
michael@0 628
michael@0 629 nsresult
michael@0 630 nsBoxFrame::Reflow(nsPresContext* aPresContext,
michael@0 631 nsHTMLReflowMetrics& aDesiredSize,
michael@0 632 const nsHTMLReflowState& aReflowState,
michael@0 633 nsReflowStatus& aStatus)
michael@0 634 {
michael@0 635 // If you make changes to this method, please keep nsLeafBoxFrame::Reflow
michael@0 636 // in sync, if the changes are applicable there.
michael@0 637
michael@0 638 DO_GLOBAL_REFLOW_COUNT("nsBoxFrame");
michael@0 639 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
michael@0 640
michael@0 641 NS_ASSERTION(aReflowState.ComputedWidth() >=0 &&
michael@0 642 aReflowState.ComputedHeight() >= 0, "Computed Size < 0");
michael@0 643
michael@0 644 #ifdef DO_NOISY_REFLOW
michael@0 645 printf("\n-------------Starting BoxFrame Reflow ----------------------------\n");
michael@0 646 printf("%p ** nsBF::Reflow %d ", this, myCounter++);
michael@0 647
michael@0 648 printSize("AW", aReflowState.AvailableWidth());
michael@0 649 printSize("AH", aReflowState.AvailableHeight());
michael@0 650 printSize("CW", aReflowState.ComputedWidth());
michael@0 651 printSize("CH", aReflowState.ComputedHeight());
michael@0 652
michael@0 653 printf(" *\n");
michael@0 654
michael@0 655 #endif
michael@0 656
michael@0 657 aStatus = NS_FRAME_COMPLETE;
michael@0 658
michael@0 659 // create the layout state
michael@0 660 nsBoxLayoutState state(aPresContext, aReflowState.rendContext,
michael@0 661 &aReflowState, aReflowState.mReflowDepth);
michael@0 662
michael@0 663 nsSize computedSize(aReflowState.ComputedWidth(),aReflowState.ComputedHeight());
michael@0 664
michael@0 665 nsMargin m;
michael@0 666 m = aReflowState.ComputedPhysicalBorderPadding();
michael@0 667 // GetBorderAndPadding(m);
michael@0 668
michael@0 669 nsSize prefSize(0,0);
michael@0 670
michael@0 671 // if we are told to layout intrinsic then get our preferred size.
michael@0 672 NS_ASSERTION(computedSize.width != NS_INTRINSICSIZE,
michael@0 673 "computed width should always be computed");
michael@0 674 if (computedSize.height == NS_INTRINSICSIZE) {
michael@0 675 prefSize = GetPrefSize(state);
michael@0 676 nsSize minSize = GetMinSize(state);
michael@0 677 nsSize maxSize = GetMaxSize(state);
michael@0 678 // XXXbz isn't GetPrefSize supposed to bounds-check for us?
michael@0 679 prefSize = BoundsCheck(minSize, prefSize, maxSize);
michael@0 680 }
michael@0 681
michael@0 682 // get our desiredSize
michael@0 683 computedSize.width += m.left + m.right;
michael@0 684
michael@0 685 if (aReflowState.ComputedHeight() == NS_INTRINSICSIZE) {
michael@0 686 computedSize.height = prefSize.height;
michael@0 687 // prefSize is border-box but min/max constraints are content-box.
michael@0 688 nscoord verticalBorderPadding =
michael@0 689 aReflowState.ComputedPhysicalBorderPadding().TopBottom();
michael@0 690 nscoord contentHeight = computedSize.height - verticalBorderPadding;
michael@0 691 // Note: contentHeight might be negative, but that's OK because min-height
michael@0 692 // is never negative.
michael@0 693 computedSize.height = aReflowState.ApplyMinMaxHeight(contentHeight) +
michael@0 694 verticalBorderPadding;
michael@0 695 } else {
michael@0 696 computedSize.height += m.top + m.bottom;
michael@0 697 }
michael@0 698
michael@0 699 nsRect r(mRect.x, mRect.y, computedSize.width, computedSize.height);
michael@0 700
michael@0 701 SetBounds(state, r);
michael@0 702
michael@0 703 // layout our children
michael@0 704 Layout(state);
michael@0 705
michael@0 706 // ok our child could have gotten bigger. So lets get its bounds
michael@0 707
michael@0 708 // get the ascent
michael@0 709 nscoord ascent = mRect.height;
michael@0 710
michael@0 711 // getting the ascent could be a lot of work. Don't get it if
michael@0 712 // we are the root. The viewport doesn't care about it.
michael@0 713 if (!(mState & NS_STATE_IS_ROOT)) {
michael@0 714 ascent = GetBoxAscent(state);
michael@0 715 }
michael@0 716
michael@0 717 aDesiredSize.Width() = mRect.width;
michael@0 718 aDesiredSize.Height() = mRect.height;
michael@0 719 aDesiredSize.SetTopAscent(ascent);
michael@0 720
michael@0 721 aDesiredSize.mOverflowAreas = GetOverflowAreas();
michael@0 722
michael@0 723 #ifdef DO_NOISY_REFLOW
michael@0 724 {
michael@0 725 printf("%p ** nsBF(done) W:%d H:%d ", this, aDesiredSize.Width(), aDesiredSize.Height());
michael@0 726
michael@0 727 if (maxElementSize) {
michael@0 728 printf("MW:%d\n", *maxElementWidth);
michael@0 729 } else {
michael@0 730 printf("MW:?\n");
michael@0 731 }
michael@0 732
michael@0 733 }
michael@0 734 #endif
michael@0 735
michael@0 736 ReflowAbsoluteFrames(aPresContext, aDesiredSize, aReflowState, aStatus);
michael@0 737
michael@0 738 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
michael@0 739 return NS_OK;
michael@0 740 }
michael@0 741
michael@0 742 nsSize
michael@0 743 nsBoxFrame::GetPrefSize(nsBoxLayoutState& aBoxLayoutState)
michael@0 744 {
michael@0 745 NS_ASSERTION(aBoxLayoutState.GetRenderingContext(),
michael@0 746 "must have rendering context");
michael@0 747
michael@0 748 nsSize size(0,0);
michael@0 749 DISPLAY_PREF_SIZE(this, size);
michael@0 750 if (!DoesNeedRecalc(mPrefSize)) {
michael@0 751 return mPrefSize;
michael@0 752 }
michael@0 753
michael@0 754 #ifdef DEBUG_LAYOUT
michael@0 755 PropagateDebug(aBoxLayoutState);
michael@0 756 #endif
michael@0 757
michael@0 758 if (IsCollapsed())
michael@0 759 return size;
michael@0 760
michael@0 761 // if the size was not completely redefined in CSS then ask our children
michael@0 762 bool widthSet, heightSet;
michael@0 763 if (!nsIFrame::AddCSSPrefSize(this, size, widthSet, heightSet))
michael@0 764 {
michael@0 765 if (mLayoutManager) {
michael@0 766 nsSize layoutSize = mLayoutManager->GetPrefSize(this, aBoxLayoutState);
michael@0 767 if (!widthSet)
michael@0 768 size.width = layoutSize.width;
michael@0 769 if (!heightSet)
michael@0 770 size.height = layoutSize.height;
michael@0 771 }
michael@0 772 else {
michael@0 773 size = nsBox::GetPrefSize(aBoxLayoutState);
michael@0 774 }
michael@0 775 }
michael@0 776
michael@0 777 nsSize minSize = GetMinSize(aBoxLayoutState);
michael@0 778 nsSize maxSize = GetMaxSize(aBoxLayoutState);
michael@0 779 mPrefSize = BoundsCheck(minSize, size, maxSize);
michael@0 780
michael@0 781 return mPrefSize;
michael@0 782 }
michael@0 783
michael@0 784 nscoord
michael@0 785 nsBoxFrame::GetBoxAscent(nsBoxLayoutState& aBoxLayoutState)
michael@0 786 {
michael@0 787 if (!DoesNeedRecalc(mAscent))
michael@0 788 return mAscent;
michael@0 789
michael@0 790 #ifdef DEBUG_LAYOUT
michael@0 791 PropagateDebug(aBoxLayoutState);
michael@0 792 #endif
michael@0 793
michael@0 794 if (IsCollapsed())
michael@0 795 return 0;
michael@0 796
michael@0 797 if (mLayoutManager)
michael@0 798 mAscent = mLayoutManager->GetAscent(this, aBoxLayoutState);
michael@0 799 else
michael@0 800 mAscent = nsBox::GetBoxAscent(aBoxLayoutState);
michael@0 801
michael@0 802 return mAscent;
michael@0 803 }
michael@0 804
michael@0 805 nsSize
michael@0 806 nsBoxFrame::GetMinSize(nsBoxLayoutState& aBoxLayoutState)
michael@0 807 {
michael@0 808 NS_ASSERTION(aBoxLayoutState.GetRenderingContext(),
michael@0 809 "must have rendering context");
michael@0 810
michael@0 811 nsSize size(0,0);
michael@0 812 DISPLAY_MIN_SIZE(this, size);
michael@0 813 if (!DoesNeedRecalc(mMinSize)) {
michael@0 814 return mMinSize;
michael@0 815 }
michael@0 816
michael@0 817 #ifdef DEBUG_LAYOUT
michael@0 818 PropagateDebug(aBoxLayoutState);
michael@0 819 #endif
michael@0 820
michael@0 821 if (IsCollapsed())
michael@0 822 return size;
michael@0 823
michael@0 824 // if the size was not completely redefined in CSS then ask our children
michael@0 825 bool widthSet, heightSet;
michael@0 826 if (!nsIFrame::AddCSSMinSize(aBoxLayoutState, this, size, widthSet, heightSet))
michael@0 827 {
michael@0 828 if (mLayoutManager) {
michael@0 829 nsSize layoutSize = mLayoutManager->GetMinSize(this, aBoxLayoutState);
michael@0 830 if (!widthSet)
michael@0 831 size.width = layoutSize.width;
michael@0 832 if (!heightSet)
michael@0 833 size.height = layoutSize.height;
michael@0 834 }
michael@0 835 else {
michael@0 836 size = nsBox::GetMinSize(aBoxLayoutState);
michael@0 837 }
michael@0 838 }
michael@0 839
michael@0 840 mMinSize = size;
michael@0 841
michael@0 842 return size;
michael@0 843 }
michael@0 844
michael@0 845 nsSize
michael@0 846 nsBoxFrame::GetMaxSize(nsBoxLayoutState& aBoxLayoutState)
michael@0 847 {
michael@0 848 NS_ASSERTION(aBoxLayoutState.GetRenderingContext(),
michael@0 849 "must have rendering context");
michael@0 850
michael@0 851 nsSize size(NS_INTRINSICSIZE, NS_INTRINSICSIZE);
michael@0 852 DISPLAY_MAX_SIZE(this, size);
michael@0 853 if (!DoesNeedRecalc(mMaxSize)) {
michael@0 854 return mMaxSize;
michael@0 855 }
michael@0 856
michael@0 857 #ifdef DEBUG_LAYOUT
michael@0 858 PropagateDebug(aBoxLayoutState);
michael@0 859 #endif
michael@0 860
michael@0 861 if (IsCollapsed())
michael@0 862 return size;
michael@0 863
michael@0 864 // if the size was not completely redefined in CSS then ask our children
michael@0 865 bool widthSet, heightSet;
michael@0 866 if (!nsIFrame::AddCSSMaxSize(this, size, widthSet, heightSet))
michael@0 867 {
michael@0 868 if (mLayoutManager) {
michael@0 869 nsSize layoutSize = mLayoutManager->GetMaxSize(this, aBoxLayoutState);
michael@0 870 if (!widthSet)
michael@0 871 size.width = layoutSize.width;
michael@0 872 if (!heightSet)
michael@0 873 size.height = layoutSize.height;
michael@0 874 }
michael@0 875 else {
michael@0 876 size = nsBox::GetMaxSize(aBoxLayoutState);
michael@0 877 }
michael@0 878 }
michael@0 879
michael@0 880 mMaxSize = size;
michael@0 881
michael@0 882 return size;
michael@0 883 }
michael@0 884
michael@0 885 nscoord
michael@0 886 nsBoxFrame::GetFlex(nsBoxLayoutState& aBoxLayoutState)
michael@0 887 {
michael@0 888 if (!DoesNeedRecalc(mFlex))
michael@0 889 return mFlex;
michael@0 890
michael@0 891 mFlex = nsBox::GetFlex(aBoxLayoutState);
michael@0 892
michael@0 893 return mFlex;
michael@0 894 }
michael@0 895
michael@0 896 /**
michael@0 897 * If subclassing please subclass this method not layout.
michael@0 898 * layout will call this method.
michael@0 899 */
michael@0 900 NS_IMETHODIMP
michael@0 901 nsBoxFrame::DoLayout(nsBoxLayoutState& aState)
michael@0 902 {
michael@0 903 uint32_t oldFlags = aState.LayoutFlags();
michael@0 904 aState.SetLayoutFlags(0);
michael@0 905
michael@0 906 nsresult rv = NS_OK;
michael@0 907 if (mLayoutManager) {
michael@0 908 CoordNeedsRecalc(mAscent);
michael@0 909 rv = mLayoutManager->Layout(this, aState);
michael@0 910 }
michael@0 911
michael@0 912 aState.SetLayoutFlags(oldFlags);
michael@0 913
michael@0 914 if (HasAbsolutelyPositionedChildren()) {
michael@0 915 // Set up a |reflowState| to pass into ReflowAbsoluteFrames
michael@0 916 nsHTMLReflowState reflowState(aState.PresContext(), this,
michael@0 917 aState.GetRenderingContext(),
michael@0 918 nsSize(mRect.width, NS_UNCONSTRAINEDSIZE));
michael@0 919
michael@0 920 // Set up a |desiredSize| to pass into ReflowAbsoluteFrames
michael@0 921 nsHTMLReflowMetrics desiredSize(reflowState);
michael@0 922 desiredSize.Width() = mRect.width;
michael@0 923 desiredSize.Height() = mRect.height;
michael@0 924
michael@0 925 // get the ascent (cribbed from ::Reflow)
michael@0 926 nscoord ascent = mRect.height;
michael@0 927
michael@0 928 // getting the ascent could be a lot of work. Don't get it if
michael@0 929 // we are the root. The viewport doesn't care about it.
michael@0 930 if (!(mState & NS_STATE_IS_ROOT)) {
michael@0 931 ascent = GetBoxAscent(aState);
michael@0 932 }
michael@0 933 desiredSize.SetTopAscent(ascent);
michael@0 934 desiredSize.mOverflowAreas = GetOverflowAreas();
michael@0 935
michael@0 936 AddStateBits(NS_FRAME_IN_REFLOW);
michael@0 937 // Set up a |reflowStatus| to pass into ReflowAbsoluteFrames
michael@0 938 // (just a dummy value; hopefully that's OK)
michael@0 939 nsReflowStatus reflowStatus = NS_FRAME_COMPLETE;
michael@0 940 ReflowAbsoluteFrames(aState.PresContext(), desiredSize,
michael@0 941 reflowState, reflowStatus);
michael@0 942 RemoveStateBits(NS_FRAME_IN_REFLOW);
michael@0 943 }
michael@0 944
michael@0 945 return rv;
michael@0 946 }
michael@0 947
michael@0 948 void
michael@0 949 nsBoxFrame::DestroyFrom(nsIFrame* aDestructRoot)
michael@0 950 {
michael@0 951 // unregister access key
michael@0 952 RegUnregAccessKey(false);
michael@0 953
michael@0 954 // clean up the container box's layout manager and child boxes
michael@0 955 SetLayoutManager(nullptr);
michael@0 956
michael@0 957 nsContainerFrame::DestroyFrom(aDestructRoot);
michael@0 958 }
michael@0 959
michael@0 960 #ifdef DEBUG_LAYOUT
michael@0 961 nsresult
michael@0 962 nsBoxFrame::SetDebug(nsBoxLayoutState& aState, bool aDebug)
michael@0 963 {
michael@0 964 // see if our state matches the given debug state
michael@0 965 bool debugSet = mState & NS_STATE_CURRENTLY_IN_DEBUG;
michael@0 966 bool debugChanged = (!aDebug && debugSet) || (aDebug && !debugSet);
michael@0 967
michael@0 968 // if it doesn't then tell each child below us the new debug state
michael@0 969 if (debugChanged)
michael@0 970 {
michael@0 971 if (aDebug) {
michael@0 972 mState |= NS_STATE_CURRENTLY_IN_DEBUG;
michael@0 973 } else {
michael@0 974 mState &= ~NS_STATE_CURRENTLY_IN_DEBUG;
michael@0 975 }
michael@0 976
michael@0 977 SetDebugOnChildList(aState, mFirstChild, aDebug);
michael@0 978
michael@0 979 MarkIntrinsicWidthsDirty();
michael@0 980 }
michael@0 981
michael@0 982 return NS_OK;
michael@0 983 }
michael@0 984 #endif
michael@0 985
michael@0 986 /* virtual */ void
michael@0 987 nsBoxFrame::MarkIntrinsicWidthsDirty()
michael@0 988 {
michael@0 989 SizeNeedsRecalc(mPrefSize);
michael@0 990 SizeNeedsRecalc(mMinSize);
michael@0 991 SizeNeedsRecalc(mMaxSize);
michael@0 992 CoordNeedsRecalc(mFlex);
michael@0 993 CoordNeedsRecalc(mAscent);
michael@0 994
michael@0 995 if (mLayoutManager) {
michael@0 996 nsBoxLayoutState state(PresContext());
michael@0 997 mLayoutManager->IntrinsicWidthsDirty(this, state);
michael@0 998 }
michael@0 999
michael@0 1000 // Don't call base class method, since everything it does is within an
michael@0 1001 // IsBoxWrapped check.
michael@0 1002 }
michael@0 1003
michael@0 1004 nsresult
michael@0 1005 nsBoxFrame::RemoveFrame(ChildListID aListID,
michael@0 1006 nsIFrame* aOldFrame)
michael@0 1007 {
michael@0 1008 NS_PRECONDITION(aListID == kPrincipalList, "We don't support out-of-flow kids");
michael@0 1009 nsPresContext* presContext = PresContext();
michael@0 1010 nsBoxLayoutState state(presContext);
michael@0 1011
michael@0 1012 // remove the child frame
michael@0 1013 mFrames.RemoveFrame(aOldFrame);
michael@0 1014
michael@0 1015 // notify the layout manager
michael@0 1016 if (mLayoutManager)
michael@0 1017 mLayoutManager->ChildrenRemoved(this, state, aOldFrame);
michael@0 1018
michael@0 1019 // destroy the child frame
michael@0 1020 aOldFrame->Destroy();
michael@0 1021
michael@0 1022 // mark us dirty and generate a reflow command
michael@0 1023 PresContext()->PresShell()->
michael@0 1024 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
michael@0 1025 NS_FRAME_HAS_DIRTY_CHILDREN);
michael@0 1026 return NS_OK;
michael@0 1027 }
michael@0 1028
michael@0 1029 nsresult
michael@0 1030 nsBoxFrame::InsertFrames(ChildListID aListID,
michael@0 1031 nsIFrame* aPrevFrame,
michael@0 1032 nsFrameList& aFrameList)
michael@0 1033 {
michael@0 1034 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
michael@0 1035 "inserting after sibling frame with different parent");
michael@0 1036 NS_ASSERTION(!aPrevFrame || mFrames.ContainsFrame(aPrevFrame),
michael@0 1037 "inserting after sibling frame not in our child list");
michael@0 1038 NS_PRECONDITION(aListID == kPrincipalList, "We don't support out-of-flow kids");
michael@0 1039 nsBoxLayoutState state(PresContext());
michael@0 1040
michael@0 1041 // insert the child frames
michael@0 1042 const nsFrameList::Slice& newFrames =
michael@0 1043 mFrames.InsertFrames(this, aPrevFrame, aFrameList);
michael@0 1044
michael@0 1045 // notify the layout manager
michael@0 1046 if (mLayoutManager)
michael@0 1047 mLayoutManager->ChildrenInserted(this, state, aPrevFrame, newFrames);
michael@0 1048
michael@0 1049 // Make sure to check box order _after_ notifying the layout
michael@0 1050 // manager; otherwise the slice we give the layout manager will
michael@0 1051 // just be bogus. If the layout manager cares about the order, we
michael@0 1052 // just lose.
michael@0 1053 CheckBoxOrder();
michael@0 1054
michael@0 1055 #ifdef DEBUG_LAYOUT
michael@0 1056 // if we are in debug make sure our children are in debug as well.
michael@0 1057 if (mState & NS_STATE_CURRENTLY_IN_DEBUG)
michael@0 1058 SetDebugOnChildList(state, mFrames.FirstChild(), true);
michael@0 1059 #endif
michael@0 1060
michael@0 1061 PresContext()->PresShell()->
michael@0 1062 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
michael@0 1063 NS_FRAME_HAS_DIRTY_CHILDREN);
michael@0 1064 return NS_OK;
michael@0 1065 }
michael@0 1066
michael@0 1067
michael@0 1068 nsresult
michael@0 1069 nsBoxFrame::AppendFrames(ChildListID aListID,
michael@0 1070 nsFrameList& aFrameList)
michael@0 1071 {
michael@0 1072 NS_PRECONDITION(aListID == kPrincipalList, "We don't support out-of-flow kids");
michael@0 1073 nsBoxLayoutState state(PresContext());
michael@0 1074
michael@0 1075 // append the new frames
michael@0 1076 const nsFrameList::Slice& newFrames = mFrames.AppendFrames(this, aFrameList);
michael@0 1077
michael@0 1078 // notify the layout manager
michael@0 1079 if (mLayoutManager)
michael@0 1080 mLayoutManager->ChildrenAppended(this, state, newFrames);
michael@0 1081
michael@0 1082 // Make sure to check box order _after_ notifying the layout
michael@0 1083 // manager; otherwise the slice we give the layout manager will
michael@0 1084 // just be bogus. If the layout manager cares about the order, we
michael@0 1085 // just lose.
michael@0 1086 CheckBoxOrder();
michael@0 1087
michael@0 1088 #ifdef DEBUG_LAYOUT
michael@0 1089 // if we are in debug make sure our children are in debug as well.
michael@0 1090 if (mState & NS_STATE_CURRENTLY_IN_DEBUG)
michael@0 1091 SetDebugOnChildList(state, mFrames.FirstChild(), true);
michael@0 1092 #endif
michael@0 1093
michael@0 1094 // XXXbz why is this NS_FRAME_FIRST_REFLOW check here?
michael@0 1095 if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
michael@0 1096 PresContext()->PresShell()->
michael@0 1097 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
michael@0 1098 NS_FRAME_HAS_DIRTY_CHILDREN);
michael@0 1099 }
michael@0 1100 return NS_OK;
michael@0 1101 }
michael@0 1102
michael@0 1103 /* virtual */ nsIFrame*
michael@0 1104 nsBoxFrame::GetContentInsertionFrame()
michael@0 1105 {
michael@0 1106 if (GetStateBits() & NS_STATE_BOX_WRAPS_KIDS_IN_BLOCK)
michael@0 1107 return GetFirstPrincipalChild()->GetContentInsertionFrame();
michael@0 1108 return nsContainerFrame::GetContentInsertionFrame();
michael@0 1109 }
michael@0 1110
michael@0 1111 nsresult
michael@0 1112 nsBoxFrame::AttributeChanged(int32_t aNameSpaceID,
michael@0 1113 nsIAtom* aAttribute,
michael@0 1114 int32_t aModType)
michael@0 1115 {
michael@0 1116 nsresult rv = nsContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
michael@0 1117 aModType);
michael@0 1118
michael@0 1119 // Ignore 'width', 'height', 'screenX', 'screenY' and 'sizemode' on a
michael@0 1120 // <window>.
michael@0 1121 nsIAtom *tag = mContent->Tag();
michael@0 1122 if ((tag == nsGkAtoms::window ||
michael@0 1123 tag == nsGkAtoms::page ||
michael@0 1124 tag == nsGkAtoms::dialog ||
michael@0 1125 tag == nsGkAtoms::wizard) &&
michael@0 1126 (nsGkAtoms::width == aAttribute ||
michael@0 1127 nsGkAtoms::height == aAttribute ||
michael@0 1128 nsGkAtoms::screenX == aAttribute ||
michael@0 1129 nsGkAtoms::screenY == aAttribute ||
michael@0 1130 nsGkAtoms::sizemode == aAttribute)) {
michael@0 1131 return rv;
michael@0 1132 }
michael@0 1133
michael@0 1134 if (aAttribute == nsGkAtoms::width ||
michael@0 1135 aAttribute == nsGkAtoms::height ||
michael@0 1136 aAttribute == nsGkAtoms::align ||
michael@0 1137 aAttribute == nsGkAtoms::valign ||
michael@0 1138 aAttribute == nsGkAtoms::left ||
michael@0 1139 aAttribute == nsGkAtoms::top ||
michael@0 1140 aAttribute == nsGkAtoms::right ||
michael@0 1141 aAttribute == nsGkAtoms::bottom ||
michael@0 1142 aAttribute == nsGkAtoms::start ||
michael@0 1143 aAttribute == nsGkAtoms::end ||
michael@0 1144 aAttribute == nsGkAtoms::minwidth ||
michael@0 1145 aAttribute == nsGkAtoms::maxwidth ||
michael@0 1146 aAttribute == nsGkAtoms::minheight ||
michael@0 1147 aAttribute == nsGkAtoms::maxheight ||
michael@0 1148 aAttribute == nsGkAtoms::flex ||
michael@0 1149 aAttribute == nsGkAtoms::orient ||
michael@0 1150 aAttribute == nsGkAtoms::pack ||
michael@0 1151 aAttribute == nsGkAtoms::dir ||
michael@0 1152 aAttribute == nsGkAtoms::mousethrough ||
michael@0 1153 aAttribute == nsGkAtoms::equalsize) {
michael@0 1154
michael@0 1155 if (aAttribute == nsGkAtoms::align ||
michael@0 1156 aAttribute == nsGkAtoms::valign ||
michael@0 1157 aAttribute == nsGkAtoms::orient ||
michael@0 1158 aAttribute == nsGkAtoms::pack ||
michael@0 1159 #ifdef DEBUG_LAYOUT
michael@0 1160 aAttribute == nsGkAtoms::debug ||
michael@0 1161 #endif
michael@0 1162 aAttribute == nsGkAtoms::dir) {
michael@0 1163
michael@0 1164 mValign = nsBoxFrame::vAlign_Top;
michael@0 1165 mHalign = nsBoxFrame::hAlign_Left;
michael@0 1166
michael@0 1167 bool orient = true;
michael@0 1168 GetInitialOrientation(orient);
michael@0 1169 if (orient)
michael@0 1170 mState |= NS_STATE_IS_HORIZONTAL;
michael@0 1171 else
michael@0 1172 mState &= ~NS_STATE_IS_HORIZONTAL;
michael@0 1173
michael@0 1174 bool normal = true;
michael@0 1175 GetInitialDirection(normal);
michael@0 1176 if (normal)
michael@0 1177 mState |= NS_STATE_IS_DIRECTION_NORMAL;
michael@0 1178 else
michael@0 1179 mState &= ~NS_STATE_IS_DIRECTION_NORMAL;
michael@0 1180
michael@0 1181 GetInitialVAlignment(mValign);
michael@0 1182 GetInitialHAlignment(mHalign);
michael@0 1183
michael@0 1184 bool equalSize = false;
michael@0 1185 GetInitialEqualSize(equalSize);
michael@0 1186 if (equalSize)
michael@0 1187 mState |= NS_STATE_EQUAL_SIZE;
michael@0 1188 else
michael@0 1189 mState &= ~NS_STATE_EQUAL_SIZE;
michael@0 1190
michael@0 1191 #ifdef DEBUG_LAYOUT
michael@0 1192 bool debug = mState & NS_STATE_SET_TO_DEBUG;
michael@0 1193 bool debugSet = GetInitialDebug(debug);
michael@0 1194 if (debugSet) {
michael@0 1195 mState |= NS_STATE_DEBUG_WAS_SET;
michael@0 1196
michael@0 1197 if (debug)
michael@0 1198 mState |= NS_STATE_SET_TO_DEBUG;
michael@0 1199 else
michael@0 1200 mState &= ~NS_STATE_SET_TO_DEBUG;
michael@0 1201 } else {
michael@0 1202 mState &= ~NS_STATE_DEBUG_WAS_SET;
michael@0 1203 }
michael@0 1204 #endif
michael@0 1205
michael@0 1206 bool autostretch = !!(mState & NS_STATE_AUTO_STRETCH);
michael@0 1207 GetInitialAutoStretch(autostretch);
michael@0 1208 if (autostretch)
michael@0 1209 mState |= NS_STATE_AUTO_STRETCH;
michael@0 1210 else
michael@0 1211 mState &= ~NS_STATE_AUTO_STRETCH;
michael@0 1212 }
michael@0 1213 else if (aAttribute == nsGkAtoms::left ||
michael@0 1214 aAttribute == nsGkAtoms::top ||
michael@0 1215 aAttribute == nsGkAtoms::right ||
michael@0 1216 aAttribute == nsGkAtoms::bottom ||
michael@0 1217 aAttribute == nsGkAtoms::start ||
michael@0 1218 aAttribute == nsGkAtoms::end) {
michael@0 1219 mState &= ~NS_STATE_STACK_NOT_POSITIONED;
michael@0 1220 }
michael@0 1221 else if (aAttribute == nsGkAtoms::mousethrough) {
michael@0 1222 UpdateMouseThrough();
michael@0 1223 }
michael@0 1224
michael@0 1225 PresContext()->PresShell()->
michael@0 1226 FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
michael@0 1227 }
michael@0 1228 else if (aAttribute == nsGkAtoms::ordinal) {
michael@0 1229 nsBoxLayoutState state(PresContext());
michael@0 1230 nsIFrame* parent = GetParentBox();
michael@0 1231 // If our parent is not a box, there's not much we can do... but in that
michael@0 1232 // case our ordinal doesn't matter anyway, so that's ok.
michael@0 1233 // Also don't bother with popup frames since they are kept on the
michael@0 1234 // kPopupList and RelayoutChildAtOrdinal() only handles
michael@0 1235 // principal children.
michael@0 1236 if (parent && !(GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
michael@0 1237 StyleDisplay()->mDisplay != NS_STYLE_DISPLAY_POPUP) {
michael@0 1238 parent->RelayoutChildAtOrdinal(state, this);
michael@0 1239 // XXXldb Should this instead be a tree change on the child or parent?
michael@0 1240 PresContext()->PresShell()->
michael@0 1241 FrameNeedsReflow(parent, nsIPresShell::eStyleChange,
michael@0 1242 NS_FRAME_IS_DIRTY);
michael@0 1243 }
michael@0 1244 }
michael@0 1245 // If the accesskey changed, register for the new value
michael@0 1246 // The old value has been unregistered in nsXULElement::SetAttr
michael@0 1247 else if (aAttribute == nsGkAtoms::accesskey) {
michael@0 1248 RegUnregAccessKey(true);
michael@0 1249 }
michael@0 1250 else if (aAttribute == nsGkAtoms::rows &&
michael@0 1251 tag == nsGkAtoms::tree) {
michael@0 1252 // Reflow ourselves and all our children if "rows" changes, since
michael@0 1253 // nsTreeBodyFrame's layout reads this from its parent (this frame).
michael@0 1254 PresContext()->PresShell()->
michael@0 1255 FrameNeedsReflow(this, nsIPresShell::eStyleChange, NS_FRAME_IS_DIRTY);
michael@0 1256 }
michael@0 1257
michael@0 1258 return rv;
michael@0 1259 }
michael@0 1260
michael@0 1261 #ifdef DEBUG_LAYOUT
michael@0 1262 void
michael@0 1263 nsBoxFrame::GetDebugPref(nsPresContext* aPresContext)
michael@0 1264 {
michael@0 1265 gDebug = Preferences::GetBool("xul.debug.box");
michael@0 1266 }
michael@0 1267
michael@0 1268 class nsDisplayXULDebug : public nsDisplayItem {
michael@0 1269 public:
michael@0 1270 nsDisplayXULDebug(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) :
michael@0 1271 nsDisplayItem(aBuilder, aFrame) {
michael@0 1272 MOZ_COUNT_CTOR(nsDisplayXULDebug);
michael@0 1273 }
michael@0 1274 #ifdef NS_BUILD_REFCNT_LOGGING
michael@0 1275 virtual ~nsDisplayXULDebug() {
michael@0 1276 MOZ_COUNT_DTOR(nsDisplayXULDebug);
michael@0 1277 }
michael@0 1278 #endif
michael@0 1279
michael@0 1280 virtual void HitTest(nsDisplayListBuilder* aBuilder, nsRect aRect,
michael@0 1281 HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {
michael@0 1282 nsPoint rectCenter(aRect.x + aRect.width / 2, aRect.y + aRect.height / 2);
michael@0 1283 static_cast<nsBoxFrame*>(mFrame)->
michael@0 1284 DisplayDebugInfoFor(this, rectCenter - ToReferenceFrame());
michael@0 1285 aOutFrames->AppendElement(this);
michael@0 1286 }
michael@0 1287 virtual void Paint(nsDisplayListBuilder* aBuilder
michael@0 1288 nsRenderingContext* aCtx);
michael@0 1289 NS_DISPLAY_DECL_NAME("XULDebug", TYPE_XUL_DEBUG)
michael@0 1290 };
michael@0 1291
michael@0 1292 void
michael@0 1293 nsDisplayXULDebug::Paint(nsDisplayListBuilder* aBuilder,
michael@0 1294 nsRenderingContext* aCtx)
michael@0 1295 {
michael@0 1296 static_cast<nsBoxFrame*>(mFrame)->
michael@0 1297 PaintXULDebugOverlay(*aCtx, ToReferenceFrame());
michael@0 1298 }
michael@0 1299
michael@0 1300 static void
michael@0 1301 PaintXULDebugBackground(nsIFrame* aFrame, nsRenderingContext* aCtx,
michael@0 1302 const nsRect& aDirtyRect, nsPoint aPt)
michael@0 1303 {
michael@0 1304 static_cast<nsBoxFrame*>(aFrame)->PaintXULDebugBackground(*aCtx, aPt);
michael@0 1305 }
michael@0 1306 #endif
michael@0 1307
michael@0 1308 void
michael@0 1309 nsBoxFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
michael@0 1310 const nsRect& aDirtyRect,
michael@0 1311 const nsDisplayListSet& aLists)
michael@0 1312 {
michael@0 1313 // forcelayer is only supported on XUL elements with box layout
michael@0 1314 bool forceLayer =
michael@0 1315 GetContent()->HasAttr(kNameSpaceID_None, nsGkAtoms::layer) &&
michael@0 1316 GetContent()->IsXUL();
michael@0 1317
michael@0 1318 // Check for frames that are marked as a part of the region used
michael@0 1319 // in calculating glass margins on Windows.
michael@0 1320 if (GetContent()->IsXUL()) {
michael@0 1321 const nsStyleDisplay* styles = StyleDisplay();
michael@0 1322 if (styles && styles->mAppearance == NS_THEME_WIN_EXCLUDE_GLASS) {
michael@0 1323 nsRect rect = nsRect(aBuilder->ToReferenceFrame(this), GetSize());
michael@0 1324 aBuilder->AddExcludedGlassRegion(rect);
michael@0 1325 }
michael@0 1326 }
michael@0 1327
michael@0 1328 nsDisplayListCollection tempLists;
michael@0 1329 const nsDisplayListSet& destination = forceLayer ? tempLists : aLists;
michael@0 1330
michael@0 1331 DisplayBorderBackgroundOutline(aBuilder, destination);
michael@0 1332
michael@0 1333 #ifdef DEBUG_LAYOUT
michael@0 1334 if (mState & NS_STATE_CURRENTLY_IN_DEBUG) {
michael@0 1335 destination.BorderBackground()->AppendNewToTop(new (aBuilder)
michael@0 1336 nsDisplayGeneric(aBuilder, this, PaintXULDebugBackground,
michael@0 1337 "XULDebugBackground"));
michael@0 1338 destination.Outlines()->AppendNewToTop(new (aBuilder)
michael@0 1339 nsDisplayXULDebug(aBuilder, this));
michael@0 1340 }
michael@0 1341 #endif
michael@0 1342
michael@0 1343 BuildDisplayListForChildren(aBuilder, aDirtyRect, destination);
michael@0 1344
michael@0 1345 // see if we have to draw a selection frame around this container
michael@0 1346 DisplaySelectionOverlay(aBuilder, destination.Content());
michael@0 1347
michael@0 1348 if (forceLayer) {
michael@0 1349 // This is a bit of a hack. Collect up all descendant display items
michael@0 1350 // and merge them into a single Content() list. This can cause us
michael@0 1351 // to violate CSS stacking order, but forceLayer is a magic
michael@0 1352 // XUL-only extension anyway.
michael@0 1353 nsDisplayList masterList;
michael@0 1354 masterList.AppendToTop(tempLists.BorderBackground());
michael@0 1355 masterList.AppendToTop(tempLists.BlockBorderBackgrounds());
michael@0 1356 masterList.AppendToTop(tempLists.Floats());
michael@0 1357 masterList.AppendToTop(tempLists.Content());
michael@0 1358 masterList.AppendToTop(tempLists.PositionedDescendants());
michael@0 1359 masterList.AppendToTop(tempLists.Outlines());
michael@0 1360 // Wrap the list to make it its own layer
michael@0 1361 aLists.Content()->AppendNewToTop(new (aBuilder)
michael@0 1362 nsDisplayOwnLayer(aBuilder, this, &masterList));
michael@0 1363 }
michael@0 1364 }
michael@0 1365
michael@0 1366 void
michael@0 1367 nsBoxFrame::BuildDisplayListForChildren(nsDisplayListBuilder* aBuilder,
michael@0 1368 const nsRect& aDirtyRect,
michael@0 1369 const nsDisplayListSet& aLists)
michael@0 1370 {
michael@0 1371 nsIFrame* kid = mFrames.FirstChild();
michael@0 1372 // Put each child's background onto the BlockBorderBackgrounds list
michael@0 1373 // to emulate the existing two-layer XUL painting scheme.
michael@0 1374 nsDisplayListSet set(aLists, aLists.BlockBorderBackgrounds());
michael@0 1375 // The children should be in the right order
michael@0 1376 while (kid) {
michael@0 1377 BuildDisplayListForChild(aBuilder, kid, aDirtyRect, set);
michael@0 1378 kid = kid->GetNextSibling();
michael@0 1379 }
michael@0 1380 }
michael@0 1381
michael@0 1382 // REVIEW: PaintChildren did a few things none of which are a big deal
michael@0 1383 // anymore:
michael@0 1384 // * Paint some debugging rects for this frame.
michael@0 1385 // This is done by nsDisplayXULDebugBackground, which goes in the
michael@0 1386 // BorderBackground() layer so it isn't clipped by OVERFLOW_CLIP.
michael@0 1387 // * Apply OVERFLOW_CLIP to the children.
michael@0 1388 // This is now in nsFrame::BuildDisplayListForStackingContext/Child.
michael@0 1389 // * Actually paint the children.
michael@0 1390 // Moved to BuildDisplayList.
michael@0 1391 // * Paint per-kid debug information.
michael@0 1392 // This is done by nsDisplayXULDebug, which is in the Outlines()
michael@0 1393 // layer so it goes on top. This means it is not clipped by OVERFLOW_CLIP,
michael@0 1394 // whereas it did used to respect OVERFLOW_CLIP, but too bad.
michael@0 1395 #ifdef DEBUG_LAYOUT
michael@0 1396 void
michael@0 1397 nsBoxFrame::PaintXULDebugBackground(nsRenderingContext& aRenderingContext,
michael@0 1398 nsPoint aPt)
michael@0 1399 {
michael@0 1400 nsMargin border;
michael@0 1401 GetBorder(border);
michael@0 1402
michael@0 1403 nsMargin debugBorder;
michael@0 1404 nsMargin debugMargin;
michael@0 1405 nsMargin debugPadding;
michael@0 1406
michael@0 1407 bool isHorizontal = IsHorizontal();
michael@0 1408
michael@0 1409 GetDebugBorder(debugBorder);
michael@0 1410 PixelMarginToTwips(GetPresContext(), debugBorder);
michael@0 1411
michael@0 1412 GetDebugMargin(debugMargin);
michael@0 1413 PixelMarginToTwips(GetPresContext(), debugMargin);
michael@0 1414
michael@0 1415 GetDebugPadding(debugPadding);
michael@0 1416 PixelMarginToTwips(GetPresContext(), debugPadding);
michael@0 1417
michael@0 1418 nsRect inner(mRect);
michael@0 1419 inner.MoveTo(aPt);
michael@0 1420 inner.Deflate(debugMargin);
michael@0 1421 inner.Deflate(border);
michael@0 1422 //nsRect borderRect(inner);
michael@0 1423
michael@0 1424 nscolor color;
michael@0 1425 if (isHorizontal) {
michael@0 1426 color = NS_RGB(0,0,255);
michael@0 1427 } else {
michael@0 1428 color = NS_RGB(255,0,0);
michael@0 1429 }
michael@0 1430
michael@0 1431 aRenderingContext.SetColor(color);
michael@0 1432
michael@0 1433 //left
michael@0 1434 nsRect r(inner);
michael@0 1435 r.width = debugBorder.left;
michael@0 1436 aRenderingContext.FillRect(r);
michael@0 1437
michael@0 1438 // top
michael@0 1439 r = inner;
michael@0 1440 r.height = debugBorder.top;
michael@0 1441 aRenderingContext.FillRect(r);
michael@0 1442
michael@0 1443 //right
michael@0 1444 r = inner;
michael@0 1445 r.x = r.x + r.width - debugBorder.right;
michael@0 1446 r.width = debugBorder.right;
michael@0 1447 aRenderingContext.FillRect(r);
michael@0 1448
michael@0 1449 //bottom
michael@0 1450 r = inner;
michael@0 1451 r.y = r.y + r.height - debugBorder.bottom;
michael@0 1452 r.height = debugBorder.bottom;
michael@0 1453 aRenderingContext.FillRect(r);
michael@0 1454
michael@0 1455
michael@0 1456 // if we have dirty children or we are dirty
michael@0 1457 // place a green border around us.
michael@0 1458 if (NS_SUBTREE_DIRTY(this)) {
michael@0 1459 nsRect dirtyr(inner);
michael@0 1460 aRenderingContext.SetColor(NS_RGB(0,255,0));
michael@0 1461 aRenderingContext.DrawRect(dirtyr);
michael@0 1462 aRenderingContext.SetColor(color);
michael@0 1463 }
michael@0 1464 }
michael@0 1465
michael@0 1466 void
michael@0 1467 nsBoxFrame::PaintXULDebugOverlay(nsRenderingContext& aRenderingContext,
michael@0 1468 nsPoint aPt)
michael@0 1469 nsMargin border;
michael@0 1470 GetBorder(border);
michael@0 1471
michael@0 1472 nsMargin debugMargin;
michael@0 1473 GetDebugMargin(debugMargin);
michael@0 1474 PixelMarginToTwips(GetPresContext(), debugMargin);
michael@0 1475
michael@0 1476 nsRect inner(mRect);
michael@0 1477 inner.MoveTo(aPt);
michael@0 1478 inner.Deflate(debugMargin);
michael@0 1479 inner.Deflate(border);
michael@0 1480
michael@0 1481 nscoord onePixel = GetPresContext()->IntScaledPixelsToTwips(1);
michael@0 1482
michael@0 1483 kid = GetChildBox();
michael@0 1484 while (nullptr != kid) {
michael@0 1485 bool isHorizontal = IsHorizontal();
michael@0 1486
michael@0 1487 nscoord x, y, borderSize, spacerSize;
michael@0 1488
michael@0 1489 nsRect cr(kid->mRect);
michael@0 1490 nsMargin margin;
michael@0 1491 kid->GetMargin(margin);
michael@0 1492 cr.Inflate(margin);
michael@0 1493
michael@0 1494 if (isHorizontal)
michael@0 1495 {
michael@0 1496 cr.y = inner.y;
michael@0 1497 x = cr.x;
michael@0 1498 y = cr.y + onePixel;
michael@0 1499 spacerSize = debugBorder.top - onePixel*4;
michael@0 1500 } else {
michael@0 1501 cr.x = inner.x;
michael@0 1502 x = cr.y;
michael@0 1503 y = cr.x + onePixel;
michael@0 1504 spacerSize = debugBorder.left - onePixel*4;
michael@0 1505 }
michael@0 1506
michael@0 1507 nsBoxLayoutState state(GetPresContext());
michael@0 1508 nscoord flex = kid->GetFlex(state);
michael@0 1509
michael@0 1510 if (!kid->IsCollapsed()) {
michael@0 1511 aRenderingContext.SetColor(NS_RGB(255,255,255));
michael@0 1512
michael@0 1513 if (isHorizontal)
michael@0 1514 borderSize = cr.width;
michael@0 1515 else
michael@0 1516 borderSize = cr.height;
michael@0 1517
michael@0 1518 DrawSpacer(GetPresContext(), aRenderingContext, isHorizontal, flex, x, y, borderSize, spacerSize);
michael@0 1519 }
michael@0 1520
michael@0 1521 kid = kid->GetNextBox();
michael@0 1522 }
michael@0 1523 }
michael@0 1524 #endif
michael@0 1525
michael@0 1526 #ifdef DEBUG_LAYOUT
michael@0 1527 void
michael@0 1528 nsBoxFrame::GetBoxName(nsAutoString& aName)
michael@0 1529 {
michael@0 1530 GetFrameName(aName);
michael@0 1531 }
michael@0 1532 #endif
michael@0 1533
michael@0 1534 #ifdef DEBUG_FRAME_DUMP
michael@0 1535 nsresult
michael@0 1536 nsBoxFrame::GetFrameName(nsAString& aResult) const
michael@0 1537 {
michael@0 1538 return MakeFrameName(NS_LITERAL_STRING("Box"), aResult);
michael@0 1539 }
michael@0 1540 #endif
michael@0 1541
michael@0 1542 nsIAtom*
michael@0 1543 nsBoxFrame::GetType() const
michael@0 1544 {
michael@0 1545 return nsGkAtoms::boxFrame;
michael@0 1546 }
michael@0 1547
michael@0 1548 #ifdef DEBUG_LAYOUT
michael@0 1549 nsresult
michael@0 1550 nsBoxFrame::GetDebug(bool& aDebug)
michael@0 1551 {
michael@0 1552 aDebug = (mState & NS_STATE_CURRENTLY_IN_DEBUG);
michael@0 1553 return NS_OK;
michael@0 1554 }
michael@0 1555 #endif
michael@0 1556
michael@0 1557 // REVIEW: nsBoxFrame::GetFrameForPoint is a problem because of 'mousethrough'
michael@0 1558 // attribute support. Here's how it works:
michael@0 1559 // * For each child frame F, we determine the target frame T(F) by recursively
michael@0 1560 // invoking GetFrameForPoint on the child
michael@0 1561 // * Let F' be the last child frame such that T(F') doesn't have mousethrough.
michael@0 1562 // If F' exists, return T(F')
michael@0 1563 // * Otherwise let F'' be the first child frame such that T(F'') is non-null.
michael@0 1564 // If F'' exists, return T(F'')
michael@0 1565 // * Otherwise return this frame, if this frame contains the point
michael@0 1566 // * Otherwise return null
michael@0 1567 // It's not clear how this should work for more complex z-ordering situations.
michael@0 1568 // The basic principle seems to be that if a frame F has a descendant
michael@0 1569 // 'mousethrough' frame that includes the target position, then F
michael@0 1570 // will not receive events (unless it overrides GetFrameForPoint).
michael@0 1571 // A 'mousethrough' frame will only receive an event if, after applying that rule,
michael@0 1572 // all eligible frames are 'mousethrough'; the bottom-most inner-most 'mousethrough'
michael@0 1573 // frame is then chosen (the first eligible frame reached in a
michael@0 1574 // traversal of the frame tree --- pre/post is irrelevant since ancestors
michael@0 1575 // of the mousethrough frames can't be eligible).
michael@0 1576 // IMHO this is very bogus and adds a great deal of complexity for something
michael@0 1577 // that is very rarely used. So I'm redefining 'mousethrough' to the following:
michael@0 1578 // a frame with mousethrough is transparent to mouse events. This is compatible
michael@0 1579 // with the way 'mousethrough' is used in Seamonkey's navigator.xul and
michael@0 1580 // Firefox's browser.xul. The only other place it's used is in the 'expander'
michael@0 1581 // XBL binding, which in our tree is only used by Thunderbird SMIME Advanced
michael@0 1582 // Preferences, and I can't figure out what that does, so I'll have to test it.
michael@0 1583 // If it's broken I'll probably just change the binding to use it more sensibly.
michael@0 1584 // This new behaviour is implemented in nsDisplayList::HitTest.
michael@0 1585 // REVIEW: This debug-box stuff is annoying. I'm just going to put debug boxes
michael@0 1586 // in the outline layer and avoid GetDebugBoxAt.
michael@0 1587
michael@0 1588 // REVIEW: GetCursor had debug-only event dumping code. I have replaced it
michael@0 1589 // with instrumentation in nsDisplayXULDebug.
michael@0 1590
michael@0 1591 #ifdef DEBUG_LAYOUT
michael@0 1592 void
michael@0 1593 nsBoxFrame::DrawLine(nsRenderingContext& aRenderingContext, bool aHorizontal, nscoord x1, nscoord y1, nscoord x2, nscoord y2)
michael@0 1594 {
michael@0 1595 if (aHorizontal)
michael@0 1596 aRenderingContext.DrawLine(x1,y1,x2,y2);
michael@0 1597 else
michael@0 1598 aRenderingContext.DrawLine(y1,x1,y2,x2);
michael@0 1599 }
michael@0 1600
michael@0 1601 void
michael@0 1602 nsBoxFrame::FillRect(nsRenderingContext& aRenderingContext, bool aHorizontal, nscoord x, nscoord y, nscoord width, nscoord height)
michael@0 1603 {
michael@0 1604 if (aHorizontal)
michael@0 1605 aRenderingContext.FillRect(x,y,width,height);
michael@0 1606 else
michael@0 1607 aRenderingContext.FillRect(y,x,height,width);
michael@0 1608 }
michael@0 1609
michael@0 1610 void
michael@0 1611 nsBoxFrame::DrawSpacer(nsPresContext* aPresContext, nsRenderingContext& aRenderingContext, bool aHorizontal, int32_t flex, nscoord x, nscoord y, nscoord size, nscoord spacerSize)
michael@0 1612 {
michael@0 1613 nscoord onePixel = aPresContext->IntScaledPixelsToTwips(1);
michael@0 1614
michael@0 1615 // if we do draw the coils
michael@0 1616 int distance = 0;
michael@0 1617 int center = 0;
michael@0 1618 int offset = 0;
michael@0 1619 int coilSize = COIL_SIZE*onePixel;
michael@0 1620 int halfSpacer = spacerSize/2;
michael@0 1621
michael@0 1622 distance = size;
michael@0 1623 center = y + halfSpacer;
michael@0 1624 offset = x;
michael@0 1625
michael@0 1626 int coils = distance/coilSize;
michael@0 1627
michael@0 1628 int halfCoilSize = coilSize/2;
michael@0 1629
michael@0 1630 if (flex == 0) {
michael@0 1631 DrawLine(aRenderingContext, aHorizontal, x,y + spacerSize/2, x + size, y + spacerSize/2);
michael@0 1632 } else {
michael@0 1633 for (int i=0; i < coils; i++)
michael@0 1634 {
michael@0 1635 DrawLine(aRenderingContext, aHorizontal, offset, center+halfSpacer, offset+halfCoilSize, center-halfSpacer);
michael@0 1636 DrawLine(aRenderingContext, aHorizontal, offset+halfCoilSize, center-halfSpacer, offset+coilSize, center+halfSpacer);
michael@0 1637
michael@0 1638 offset += coilSize;
michael@0 1639 }
michael@0 1640 }
michael@0 1641
michael@0 1642 FillRect(aRenderingContext, aHorizontal, x + size - spacerSize/2, y, spacerSize/2, spacerSize);
michael@0 1643 FillRect(aRenderingContext, aHorizontal, x, y, spacerSize/2, spacerSize);
michael@0 1644
michael@0 1645 //DrawKnob(aPresContext, aRenderingContext, x + size - spacerSize, y, spacerSize);
michael@0 1646 }
michael@0 1647
michael@0 1648 void
michael@0 1649 nsBoxFrame::GetDebugBorder(nsMargin& aInset)
michael@0 1650 {
michael@0 1651 aInset.SizeTo(2,2,2,2);
michael@0 1652
michael@0 1653 if (IsHorizontal())
michael@0 1654 aInset.top = 10;
michael@0 1655 else
michael@0 1656 aInset.left = 10;
michael@0 1657 }
michael@0 1658
michael@0 1659 void
michael@0 1660 nsBoxFrame::GetDebugMargin(nsMargin& aInset)
michael@0 1661 {
michael@0 1662 aInset.SizeTo(2,2,2,2);
michael@0 1663 }
michael@0 1664
michael@0 1665 void
michael@0 1666 nsBoxFrame::GetDebugPadding(nsMargin& aPadding)
michael@0 1667 {
michael@0 1668 aPadding.SizeTo(2,2,2,2);
michael@0 1669 }
michael@0 1670
michael@0 1671 void
michael@0 1672 nsBoxFrame::PixelMarginToTwips(nsPresContext* aPresContext, nsMargin& aMarginPixels)
michael@0 1673 {
michael@0 1674 nscoord onePixel = nsPresContext::CSSPixelsToAppUnits(1);
michael@0 1675 aMarginPixels.left *= onePixel;
michael@0 1676 aMarginPixels.right *= onePixel;
michael@0 1677 aMarginPixels.top *= onePixel;
michael@0 1678 aMarginPixels.bottom *= onePixel;
michael@0 1679 }
michael@0 1680
michael@0 1681 void
michael@0 1682 nsBoxFrame::GetValue(nsPresContext* aPresContext, const nsSize& a, const nsSize& b, char* ch)
michael@0 1683 {
michael@0 1684 float p2t = aPresContext->ScaledPixelsToTwips();
michael@0 1685
michael@0 1686 char width[100];
michael@0 1687 char height[100];
michael@0 1688
michael@0 1689 if (a.width == NS_INTRINSICSIZE)
michael@0 1690 sprintf(width,"%s","INF");
michael@0 1691 else
michael@0 1692 sprintf(width,"%d", nscoord(a.width/*/p2t*/));
michael@0 1693
michael@0 1694 if (a.height == NS_INTRINSICSIZE)
michael@0 1695 sprintf(height,"%s","INF");
michael@0 1696 else
michael@0 1697 sprintf(height,"%d", nscoord(a.height/*/p2t*/));
michael@0 1698
michael@0 1699
michael@0 1700 sprintf(ch, "(%s%s, %s%s)", width, (b.width != NS_INTRINSICSIZE ? "[SET]" : ""),
michael@0 1701 height, (b.height != NS_INTRINSICSIZE ? "[SET]" : ""));
michael@0 1702
michael@0 1703 }
michael@0 1704
michael@0 1705 void
michael@0 1706 nsBoxFrame::GetValue(nsPresContext* aPresContext, int32_t a, int32_t b, char* ch)
michael@0 1707 {
michael@0 1708 if (a == NS_INTRINSICSIZE)
michael@0 1709 sprintf(ch, "%d[SET]", b);
michael@0 1710 else
michael@0 1711 sprintf(ch, "%d", a);
michael@0 1712 }
michael@0 1713
michael@0 1714 nsresult
michael@0 1715 nsBoxFrame::DisplayDebugInfoFor(nsIFrame* aBox,
michael@0 1716 nsPoint& aPoint)
michael@0 1717 {
michael@0 1718 nsBoxLayoutState state(GetPresContext());
michael@0 1719
michael@0 1720 nscoord x = aPoint.x;
michael@0 1721 nscoord y = aPoint.y;
michael@0 1722
michael@0 1723 // get the area inside our border but not our debug margins.
michael@0 1724 nsRect insideBorder(aBox->mRect);
michael@0 1725 insideBorder.MoveTo(0,0):
michael@0 1726 nsMargin border(0,0,0,0);
michael@0 1727 aBox->GetBorderAndPadding(border);
michael@0 1728 insideBorder.Deflate(border);
michael@0 1729
michael@0 1730 bool isHorizontal = IsHorizontal();
michael@0 1731
michael@0 1732 if (!insideBorder.Contains(nsPoint(x,y)))
michael@0 1733 return NS_ERROR_FAILURE;
michael@0 1734
michael@0 1735 //printf("%%%%%% inside box %%%%%%%\n");
michael@0 1736
michael@0 1737 int count = 0;
michael@0 1738 nsIFrame* child = aBox->GetChildBox();
michael@0 1739
michael@0 1740 nsMargin m;
michael@0 1741 nsMargin m2;
michael@0 1742 GetDebugBorder(m);
michael@0 1743 PixelMarginToTwips(aPresContext, m);
michael@0 1744
michael@0 1745 GetDebugMargin(m2);
michael@0 1746 PixelMarginToTwips(aPresContext, m2);
michael@0 1747
michael@0 1748 m += m2;
michael@0 1749
michael@0 1750 if ((isHorizontal && y < insideBorder.y + m.top) ||
michael@0 1751 (!isHorizontal && x < insideBorder.x + m.left)) {
michael@0 1752 //printf("**** inside debug border *******\n");
michael@0 1753 while (child)
michael@0 1754 {
michael@0 1755 const nsRect& r = child->mRect;
michael@0 1756
michael@0 1757 // if we are not in the child. But in the spacer above the child.
michael@0 1758 if ((isHorizontal && x >= r.x && x < r.x + r.width) ||
michael@0 1759 (!isHorizontal && y >= r.y && y < r.y + r.height)) {
michael@0 1760 aCursor = NS_STYLE_CURSOR_POINTER;
michael@0 1761 // found it but we already showed it.
michael@0 1762 if (mDebugChild == child)
michael@0 1763 return NS_OK;
michael@0 1764
michael@0 1765 if (aBox->GetContent()) {
michael@0 1766 printf("---------------\n");
michael@0 1767 DumpBox(stdout);
michael@0 1768 printf("\n");
michael@0 1769 }
michael@0 1770
michael@0 1771 if (child->GetContent()) {
michael@0 1772 printf("child #%d: ", count);
michael@0 1773 child->DumpBox(stdout);
michael@0 1774 printf("\n");
michael@0 1775 }
michael@0 1776
michael@0 1777 mDebugChild = child;
michael@0 1778
michael@0 1779 nsSize prefSizeCSS(NS_INTRINSICSIZE, NS_INTRINSICSIZE);
michael@0 1780 nsSize minSizeCSS (NS_INTRINSICSIZE, NS_INTRINSICSIZE);
michael@0 1781 nsSize maxSizeCSS (NS_INTRINSICSIZE, NS_INTRINSICSIZE);
michael@0 1782 nscoord flexCSS = NS_INTRINSICSIZE;
michael@0 1783
michael@0 1784 bool widthSet, heightSet;
michael@0 1785 nsIFrame::AddCSSPrefSize(child, prefSizeCSS, widthSet, heightSet);
michael@0 1786 nsIFrame::AddCSSMinSize (state, child, minSizeCSS, widthSet, heightSet);
michael@0 1787 nsIFrame::AddCSSMaxSize (child, maxSizeCSS, widthSet, heightSet);
michael@0 1788 nsIFrame::AddCSSFlex (state, child, flexCSS);
michael@0 1789
michael@0 1790 nsSize prefSize = child->GetPrefSize(state);
michael@0 1791 nsSize minSize = child->GetMinSize(state);
michael@0 1792 nsSize maxSize = child->GetMaxSize(state);
michael@0 1793 nscoord flexSize = child->GetFlex(state);
michael@0 1794 nscoord ascentSize = child->GetBoxAscent(state);
michael@0 1795
michael@0 1796 char min[100];
michael@0 1797 char pref[100];
michael@0 1798 char max[100];
michael@0 1799 char calc[100];
michael@0 1800 char flex[100];
michael@0 1801 char ascent[100];
michael@0 1802
michael@0 1803 nsSize actualSize;
michael@0 1804 GetFrameSizeWithMargin(child, actualSize);
michael@0 1805 nsSize actualSizeCSS (NS_INTRINSICSIZE, NS_INTRINSICSIZE);
michael@0 1806
michael@0 1807 GetValue(aPresContext, minSize, minSizeCSS, min);
michael@0 1808 GetValue(aPresContext, prefSize, prefSizeCSS, pref);
michael@0 1809 GetValue(aPresContext, maxSize, maxSizeCSS, max);
michael@0 1810 GetValue(aPresContext, actualSize, actualSizeCSS, calc);
michael@0 1811 GetValue(aPresContext, flexSize, flexCSS, flex);
michael@0 1812 GetValue(aPresContext, ascentSize, NS_INTRINSICSIZE, ascent);
michael@0 1813
michael@0 1814
michael@0 1815 printf("min%s, pref%s, max%s, actual%s, flex=%s, ascent=%s\n\n",
michael@0 1816 min,
michael@0 1817 pref,
michael@0 1818 max,
michael@0 1819 calc,
michael@0 1820 flex,
michael@0 1821 ascent
michael@0 1822 );
michael@0 1823
michael@0 1824 return NS_OK;
michael@0 1825 }
michael@0 1826
michael@0 1827 child = child->GetNextBox();
michael@0 1828 count++;
michael@0 1829 }
michael@0 1830 } else {
michael@0 1831 }
michael@0 1832
michael@0 1833 mDebugChild = nullptr;
michael@0 1834
michael@0 1835 return NS_OK;
michael@0 1836 }
michael@0 1837
michael@0 1838 void
michael@0 1839 nsBoxFrame::SetDebugOnChildList(nsBoxLayoutState& aState, nsIFrame* aChild, bool aDebug)
michael@0 1840 {
michael@0 1841 nsIFrame* child = GetChildBox();
michael@0 1842 while (child)
michael@0 1843 {
michael@0 1844 child->SetDebug(aState, aDebug);
michael@0 1845 child = child->GetNextBox();
michael@0 1846 }
michael@0 1847 }
michael@0 1848
michael@0 1849 nsresult
michael@0 1850 nsBoxFrame::GetFrameSizeWithMargin(nsIFrame* aBox, nsSize& aSize)
michael@0 1851 {
michael@0 1852 nsRect rect(aBox->GetRect());
michael@0 1853 nsMargin margin(0,0,0,0);
michael@0 1854 aBox->GetMargin(margin);
michael@0 1855 rect.Inflate(margin);
michael@0 1856 aSize.width = rect.width;
michael@0 1857 aSize.height = rect.height;
michael@0 1858 return NS_OK;
michael@0 1859 }
michael@0 1860 #endif
michael@0 1861
michael@0 1862 // If you make changes to this function, check its counterparts
michael@0 1863 // in nsTextBoxFrame and nsXULLabelFrame
michael@0 1864 void
michael@0 1865 nsBoxFrame::RegUnregAccessKey(bool aDoReg)
michael@0 1866 {
michael@0 1867 MOZ_ASSERT(mContent);
michael@0 1868
michael@0 1869 // find out what type of element this is
michael@0 1870 nsIAtom *atom = mContent->Tag();
michael@0 1871
michael@0 1872 // only support accesskeys for the following elements
michael@0 1873 if (atom != nsGkAtoms::button &&
michael@0 1874 atom != nsGkAtoms::toolbarbutton &&
michael@0 1875 atom != nsGkAtoms::checkbox &&
michael@0 1876 atom != nsGkAtoms::textbox &&
michael@0 1877 atom != nsGkAtoms::tab &&
michael@0 1878 atom != nsGkAtoms::radio) {
michael@0 1879 return;
michael@0 1880 }
michael@0 1881
michael@0 1882 nsAutoString accessKey;
michael@0 1883 mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::accesskey, accessKey);
michael@0 1884
michael@0 1885 if (accessKey.IsEmpty())
michael@0 1886 return;
michael@0 1887
michael@0 1888 // With a valid PresContext we can get the ESM
michael@0 1889 // and register the access key
michael@0 1890 EventStateManager* esm = PresContext()->EventStateManager();
michael@0 1891
michael@0 1892 uint32_t key = accessKey.First();
michael@0 1893 if (aDoReg)
michael@0 1894 esm->RegisterAccessKey(mContent, key);
michael@0 1895 else
michael@0 1896 esm->UnregisterAccessKey(mContent, key);
michael@0 1897 }
michael@0 1898
michael@0 1899 bool
michael@0 1900 nsBoxFrame::SupportsOrdinalsInChildren()
michael@0 1901 {
michael@0 1902 return true;
michael@0 1903 }
michael@0 1904
michael@0 1905 // Helper less-than-or-equal function, used in CheckBoxOrder() as a
michael@0 1906 // template-parameter for the sorting functions.
michael@0 1907 bool
michael@0 1908 IsBoxOrdinalLEQ(nsIFrame* aFrame1,
michael@0 1909 nsIFrame* aFrame2)
michael@0 1910 {
michael@0 1911 // If we've got a placeholder frame, use its out-of-flow frame's ordinal val.
michael@0 1912 nsIFrame* aRealFrame1 = nsPlaceholderFrame::GetRealFrameFor(aFrame1);
michael@0 1913 nsIFrame* aRealFrame2 = nsPlaceholderFrame::GetRealFrameFor(aFrame2);
michael@0 1914 return aRealFrame1->GetOrdinal() <= aRealFrame2->GetOrdinal();
michael@0 1915 }
michael@0 1916
michael@0 1917 void
michael@0 1918 nsBoxFrame::CheckBoxOrder()
michael@0 1919 {
michael@0 1920 if (SupportsOrdinalsInChildren() &&
michael@0 1921 !nsIFrame::IsFrameListSorted<IsBoxOrdinalLEQ>(mFrames)) {
michael@0 1922 nsIFrame::SortFrameList<IsBoxOrdinalLEQ>(mFrames);
michael@0 1923 }
michael@0 1924 }
michael@0 1925
michael@0 1926 nsresult
michael@0 1927 nsBoxFrame::LayoutChildAt(nsBoxLayoutState& aState, nsIFrame* aBox, const nsRect& aRect)
michael@0 1928 {
michael@0 1929 // get the current rect
michael@0 1930 nsRect oldRect(aBox->GetRect());
michael@0 1931 aBox->SetBounds(aState, aRect);
michael@0 1932
michael@0 1933 bool layout = NS_SUBTREE_DIRTY(aBox);
michael@0 1934
michael@0 1935 if (layout || (oldRect.width != aRect.width || oldRect.height != aRect.height)) {
michael@0 1936 return aBox->Layout(aState);
michael@0 1937 }
michael@0 1938
michael@0 1939 return NS_OK;
michael@0 1940 }
michael@0 1941
michael@0 1942 nsresult
michael@0 1943 nsBoxFrame::RelayoutChildAtOrdinal(nsBoxLayoutState& aState, nsIFrame* aChild)
michael@0 1944 {
michael@0 1945 if (!SupportsOrdinalsInChildren())
michael@0 1946 return NS_OK;
michael@0 1947
michael@0 1948 uint32_t ord = aChild->GetOrdinal();
michael@0 1949
michael@0 1950 nsIFrame* child = mFrames.FirstChild();
michael@0 1951 nsIFrame* newPrevSib = nullptr;
michael@0 1952
michael@0 1953 while (child) {
michael@0 1954 if (ord < child->GetOrdinal()) {
michael@0 1955 break;
michael@0 1956 }
michael@0 1957
michael@0 1958 if (child != aChild) {
michael@0 1959 newPrevSib = child;
michael@0 1960 }
michael@0 1961
michael@0 1962 child = child->GetNextBox();
michael@0 1963 }
michael@0 1964
michael@0 1965 if (aChild->GetPrevSibling() == newPrevSib) {
michael@0 1966 // This box is not moving.
michael@0 1967 return NS_OK;
michael@0 1968 }
michael@0 1969
michael@0 1970 // Take |aChild| out of its old position in the child list.
michael@0 1971 mFrames.RemoveFrame(aChild);
michael@0 1972
michael@0 1973 // Insert it after |newPrevSib| or at the start if it's null.
michael@0 1974 mFrames.InsertFrame(nullptr, newPrevSib, aChild);
michael@0 1975
michael@0 1976 return NS_OK;
michael@0 1977 }
michael@0 1978
michael@0 1979 /**
michael@0 1980 * This wrapper class lets us redirect mouse hits from descendant frames
michael@0 1981 * of a menu to the menu itself, if they didn't specify 'allowevents'.
michael@0 1982 *
michael@0 1983 * The wrapper simply turns a hit on a descendant element
michael@0 1984 * into a hit on the menu itself, unless there is an element between the target
michael@0 1985 * and the menu with the "allowevents" attribute.
michael@0 1986 *
michael@0 1987 * This is used by nsMenuFrame and nsTreeColFrame.
michael@0 1988 *
michael@0 1989 * Note that turning a hit on a descendant element into nullptr, so events
michael@0 1990 * could fall through to the menu background, might be an appealing simplification
michael@0 1991 * but it would mean slightly strange behaviour in some cases, because grabber
michael@0 1992 * wrappers can be created for many individual lists and items, so the exact
michael@0 1993 * fallthrough behaviour would be complex. E.g. an element with "allowevents"
michael@0 1994 * on top of the Content() list could receive the event even if it was covered
michael@0 1995 * by a PositionedDescenants() element without "allowevents". It is best to
michael@0 1996 * never convert a non-null hit into null.
michael@0 1997 */
michael@0 1998 // REVIEW: This is roughly of what nsMenuFrame::GetFrameForPoint used to do.
michael@0 1999 // I've made 'allowevents' affect child elements because that seems the only
michael@0 2000 // reasonable thing to do.
michael@0 2001 class nsDisplayXULEventRedirector : public nsDisplayWrapList {
michael@0 2002 public:
michael@0 2003 nsDisplayXULEventRedirector(nsDisplayListBuilder* aBuilder,
michael@0 2004 nsIFrame* aFrame, nsDisplayItem* aItem,
michael@0 2005 nsIFrame* aTargetFrame)
michael@0 2006 : nsDisplayWrapList(aBuilder, aFrame, aItem), mTargetFrame(aTargetFrame) {}
michael@0 2007 nsDisplayXULEventRedirector(nsDisplayListBuilder* aBuilder,
michael@0 2008 nsIFrame* aFrame, nsDisplayList* aList,
michael@0 2009 nsIFrame* aTargetFrame)
michael@0 2010 : nsDisplayWrapList(aBuilder, aFrame, aList), mTargetFrame(aTargetFrame) {}
michael@0 2011 virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
michael@0 2012 HitTestState* aState,
michael@0 2013 nsTArray<nsIFrame*> *aOutFrames) MOZ_OVERRIDE;
michael@0 2014 NS_DISPLAY_DECL_NAME("XULEventRedirector", TYPE_XUL_EVENT_REDIRECTOR)
michael@0 2015 private:
michael@0 2016 nsIFrame* mTargetFrame;
michael@0 2017 };
michael@0 2018
michael@0 2019 void nsDisplayXULEventRedirector::HitTest(nsDisplayListBuilder* aBuilder,
michael@0 2020 const nsRect& aRect, HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
michael@0 2021 {
michael@0 2022 nsTArray<nsIFrame*> outFrames;
michael@0 2023 mList.HitTest(aBuilder, aRect, aState, &outFrames);
michael@0 2024
michael@0 2025 bool topMostAdded = false;
michael@0 2026 uint32_t localLength = outFrames.Length();
michael@0 2027
michael@0 2028 for (uint32_t i = 0; i < localLength; i++) {
michael@0 2029
michael@0 2030 for (nsIContent* content = outFrames.ElementAt(i)->GetContent();
michael@0 2031 content && content != mTargetFrame->GetContent();
michael@0 2032 content = content->GetParent()) {
michael@0 2033 if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::allowevents,
michael@0 2034 nsGkAtoms::_true, eCaseMatters)) {
michael@0 2035 // Events are allowed on 'frame', so let it go.
michael@0 2036 aOutFrames->AppendElement(outFrames.ElementAt(i));
michael@0 2037 topMostAdded = true;
michael@0 2038 }
michael@0 2039 }
michael@0 2040
michael@0 2041 // If there was no hit on the topmost frame or its ancestors,
michael@0 2042 // add the target frame itself as the first candidate (see bug 562554).
michael@0 2043 if (!topMostAdded) {
michael@0 2044 topMostAdded = true;
michael@0 2045 aOutFrames->AppendElement(mTargetFrame);
michael@0 2046 }
michael@0 2047 }
michael@0 2048 }
michael@0 2049
michael@0 2050 class nsXULEventRedirectorWrapper : public nsDisplayWrapper
michael@0 2051 {
michael@0 2052 public:
michael@0 2053 nsXULEventRedirectorWrapper(nsIFrame* aTargetFrame)
michael@0 2054 : mTargetFrame(aTargetFrame) {}
michael@0 2055 virtual nsDisplayItem* WrapList(nsDisplayListBuilder* aBuilder,
michael@0 2056 nsIFrame* aFrame,
michael@0 2057 nsDisplayList* aList) MOZ_OVERRIDE {
michael@0 2058 return new (aBuilder)
michael@0 2059 nsDisplayXULEventRedirector(aBuilder, aFrame, aList, mTargetFrame);
michael@0 2060 }
michael@0 2061 virtual nsDisplayItem* WrapItem(nsDisplayListBuilder* aBuilder,
michael@0 2062 nsDisplayItem* aItem) MOZ_OVERRIDE {
michael@0 2063 return new (aBuilder)
michael@0 2064 nsDisplayXULEventRedirector(aBuilder, aItem->Frame(), aItem,
michael@0 2065 mTargetFrame);
michael@0 2066 }
michael@0 2067 private:
michael@0 2068 nsIFrame* mTargetFrame;
michael@0 2069 };
michael@0 2070
michael@0 2071 void
michael@0 2072 nsBoxFrame::WrapListsInRedirector(nsDisplayListBuilder* aBuilder,
michael@0 2073 const nsDisplayListSet& aIn,
michael@0 2074 const nsDisplayListSet& aOut)
michael@0 2075 {
michael@0 2076 nsXULEventRedirectorWrapper wrapper(this);
michael@0 2077 wrapper.WrapLists(aBuilder, this, aIn, aOut);
michael@0 2078 }
michael@0 2079
michael@0 2080 bool
michael@0 2081 nsBoxFrame::GetEventPoint(WidgetGUIEvent* aEvent, nsPoint &aPoint) {
michael@0 2082 nsIntPoint refPoint;
michael@0 2083 bool res = GetEventPoint(aEvent, refPoint);
michael@0 2084 aPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, refPoint, this);
michael@0 2085 return res;
michael@0 2086 }
michael@0 2087
michael@0 2088 bool
michael@0 2089 nsBoxFrame::GetEventPoint(WidgetGUIEvent* aEvent, nsIntPoint &aPoint) {
michael@0 2090 NS_ENSURE_TRUE(aEvent, false);
michael@0 2091
michael@0 2092 WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
michael@0 2093 if (touchEvent) {
michael@0 2094 // return false if there is more than one touch on the page, or if
michael@0 2095 // we can't find a touch point
michael@0 2096 if (touchEvent->touches.Length() != 1) {
michael@0 2097 return false;
michael@0 2098 }
michael@0 2099
michael@0 2100 dom::Touch* touch = touchEvent->touches.SafeElementAt(0);
michael@0 2101 if (!touch) {
michael@0 2102 return false;
michael@0 2103 }
michael@0 2104 aPoint = touch->mRefPoint;
michael@0 2105 } else {
michael@0 2106 aPoint = LayoutDeviceIntPoint::ToUntyped(aEvent->refPoint);
michael@0 2107 }
michael@0 2108 return true;
michael@0 2109 }

mercurial