Wed, 31 Dec 2014 06:55:50 +0100
Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2
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 | } |