layout/tables/nsTableOuterFrame.cpp

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

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 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5 #include "nsTableOuterFrame.h"
michael@0 6 #include "nsTableFrame.h"
michael@0 7 #include "nsTableCellFrame.h"
michael@0 8 #include "nsStyleContext.h"
michael@0 9 #include "nsStyleConsts.h"
michael@0 10 #include "nsPresContext.h"
michael@0 11 #include "nsCSSRendering.h"
michael@0 12 #include "nsIContent.h"
michael@0 13 #include "prinrval.h"
michael@0 14 #include "nsGkAtoms.h"
michael@0 15 #include "nsHTMLParts.h"
michael@0 16 #include "nsIPresShell.h"
michael@0 17 #include "nsIServiceManager.h"
michael@0 18 #include "nsIDOMNode.h"
michael@0 19 #include "nsDisplayList.h"
michael@0 20 #include "nsLayoutUtils.h"
michael@0 21 #include <algorithm>
michael@0 22
michael@0 23 using namespace mozilla;
michael@0 24 using namespace mozilla::layout;
michael@0 25
michael@0 26 /* ----------- nsTableCaptionFrame ---------- */
michael@0 27
michael@0 28 #define NS_TABLE_FRAME_CAPTION_LIST_INDEX 1
michael@0 29 #define NO_SIDE 100
michael@0 30
michael@0 31 // caption frame
michael@0 32 nsTableCaptionFrame::nsTableCaptionFrame(nsStyleContext* aContext):
michael@0 33 nsBlockFrame(aContext)
michael@0 34 {
michael@0 35 // shrink wrap
michael@0 36 SetFlags(NS_BLOCK_FLOAT_MGR);
michael@0 37 }
michael@0 38
michael@0 39 nsTableCaptionFrame::~nsTableCaptionFrame()
michael@0 40 {
michael@0 41 }
michael@0 42
michael@0 43 nsIAtom*
michael@0 44 nsTableCaptionFrame::GetType() const
michael@0 45 {
michael@0 46 return nsGkAtoms::tableCaptionFrame;
michael@0 47 }
michael@0 48
michael@0 49 /* virtual */ nscoord
michael@0 50 nsTableOuterFrame::GetBaseline() const
michael@0 51 {
michael@0 52 nsIFrame* kid = mFrames.FirstChild();
michael@0 53 if (!kid) {
michael@0 54 NS_NOTREACHED("no inner table");
michael@0 55 return nsContainerFrame::GetBaseline();
michael@0 56 }
michael@0 57
michael@0 58 return kid->GetBaseline() + kid->GetPosition().y;
michael@0 59 }
michael@0 60
michael@0 61 /* virtual */ nsSize
michael@0 62 nsTableCaptionFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext,
michael@0 63 nsSize aCBSize, nscoord aAvailableWidth,
michael@0 64 nsSize aMargin, nsSize aBorder,
michael@0 65 nsSize aPadding, bool aShrinkWrap)
michael@0 66 {
michael@0 67 nsSize result = nsBlockFrame::ComputeAutoSize(aRenderingContext, aCBSize,
michael@0 68 aAvailableWidth, aMargin, aBorder, aPadding, aShrinkWrap);
michael@0 69
michael@0 70 // If we're a container for font size inflation, then shrink
michael@0 71 // wrapping inside of us should not apply font size inflation.
michael@0 72 AutoMaybeDisableFontInflation an(this);
michael@0 73
michael@0 74 uint8_t captionSide = StyleTableBorder()->mCaptionSide;
michael@0 75 if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
michael@0 76 captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
michael@0 77 result.width = GetMinWidth(aRenderingContext);
michael@0 78 } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
michael@0 79 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
michael@0 80 // The outer frame constrains our available width to the width of
michael@0 81 // the table. Grow if our min-width is bigger than that, but not
michael@0 82 // larger than the containing block width. (It would really be nice
michael@0 83 // to transmit that information another way, so we could grow up to
michael@0 84 // the table's available width, but that's harder.)
michael@0 85 nscoord min = GetMinWidth(aRenderingContext);
michael@0 86 if (min > aCBSize.width)
michael@0 87 min = aCBSize.width;
michael@0 88 if (min > result.width)
michael@0 89 result.width = min;
michael@0 90 }
michael@0 91 return result;
michael@0 92 }
michael@0 93
michael@0 94 nsIFrame*
michael@0 95 nsTableCaptionFrame::GetParentStyleContextFrame() const
michael@0 96 {
michael@0 97 NS_PRECONDITION(mContent->GetParent(),
michael@0 98 "How could we not have a parent here?");
michael@0 99
michael@0 100 // The caption's style context parent is the inner frame, unless
michael@0 101 // it's anonymous.
michael@0 102 nsIFrame* outerFrame = GetParent();
michael@0 103 if (outerFrame && outerFrame->GetType() == nsGkAtoms::tableOuterFrame) {
michael@0 104 nsIFrame* innerFrame = outerFrame->GetFirstPrincipalChild();
michael@0 105 if (innerFrame) {
michael@0 106 return nsFrame::CorrectStyleParentFrame(innerFrame,
michael@0 107 StyleContext()->GetPseudo());
michael@0 108 }
michael@0 109 }
michael@0 110
michael@0 111 NS_NOTREACHED("Where is our inner table frame?");
michael@0 112 return nsBlockFrame::GetParentStyleContextFrame();
michael@0 113 }
michael@0 114
michael@0 115 #ifdef ACCESSIBILITY
michael@0 116 a11y::AccType
michael@0 117 nsTableCaptionFrame::AccessibleType()
michael@0 118 {
michael@0 119 if (!GetRect().IsEmpty()) {
michael@0 120 return a11y::eHTMLCaptionType;
michael@0 121 }
michael@0 122
michael@0 123 return a11y::eNoType;
michael@0 124 }
michael@0 125 #endif
michael@0 126
michael@0 127 #ifdef DEBUG_FRAME_DUMP
michael@0 128 nsresult
michael@0 129 nsTableCaptionFrame::GetFrameName(nsAString& aResult) const
michael@0 130 {
michael@0 131 return MakeFrameName(NS_LITERAL_STRING("Caption"), aResult);
michael@0 132 }
michael@0 133 #endif
michael@0 134
michael@0 135 nsIFrame*
michael@0 136 NS_NewTableCaptionFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
michael@0 137 {
michael@0 138 return new (aPresShell) nsTableCaptionFrame(aContext);
michael@0 139 }
michael@0 140
michael@0 141 NS_IMPL_FRAMEARENA_HELPERS(nsTableCaptionFrame)
michael@0 142
michael@0 143 /* ----------- nsTableOuterFrame ---------- */
michael@0 144
michael@0 145 nsTableOuterFrame::nsTableOuterFrame(nsStyleContext* aContext):
michael@0 146 nsContainerFrame(aContext)
michael@0 147 {
michael@0 148 }
michael@0 149
michael@0 150 nsTableOuterFrame::~nsTableOuterFrame()
michael@0 151 {
michael@0 152 }
michael@0 153
michael@0 154 NS_QUERYFRAME_HEAD(nsTableOuterFrame)
michael@0 155 NS_QUERYFRAME_ENTRY(nsTableOuterFrame)
michael@0 156 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
michael@0 157
michael@0 158 #ifdef ACCESSIBILITY
michael@0 159 a11y::AccType
michael@0 160 nsTableOuterFrame::AccessibleType()
michael@0 161 {
michael@0 162 return a11y::eHTMLTableType;
michael@0 163 }
michael@0 164 #endif
michael@0 165
michael@0 166 void
michael@0 167 nsTableOuterFrame::DestroyFrom(nsIFrame* aDestructRoot)
michael@0 168 {
michael@0 169 DestroyAbsoluteFrames(aDestructRoot);
michael@0 170 mCaptionFrames.DestroyFramesFrom(aDestructRoot);
michael@0 171 nsContainerFrame::DestroyFrom(aDestructRoot);
michael@0 172 }
michael@0 173
michael@0 174 const nsFrameList&
michael@0 175 nsTableOuterFrame::GetChildList(ChildListID aListID) const
michael@0 176 {
michael@0 177 if (aListID == kCaptionList) {
michael@0 178 return mCaptionFrames;
michael@0 179 }
michael@0 180
michael@0 181 return nsContainerFrame::GetChildList(aListID);
michael@0 182 }
michael@0 183
michael@0 184 void
michael@0 185 nsTableOuterFrame::GetChildLists(nsTArray<ChildList>* aLists) const
michael@0 186 {
michael@0 187 nsContainerFrame::GetChildLists(aLists);
michael@0 188 mCaptionFrames.AppendIfNonempty(aLists, kCaptionList);
michael@0 189 }
michael@0 190
michael@0 191 nsresult
michael@0 192 nsTableOuterFrame::SetInitialChildList(ChildListID aListID,
michael@0 193 nsFrameList& aChildList)
michael@0 194 {
michael@0 195 if (kCaptionList == aListID) {
michael@0 196 // the frame constructor already checked for table-caption display type
michael@0 197 mCaptionFrames.SetFrames(aChildList);
michael@0 198 }
michael@0 199 else {
michael@0 200 NS_ASSERTION(aListID == kPrincipalList, "wrong childlist");
michael@0 201 NS_ASSERTION(mFrames.IsEmpty(), "Frame leak!");
michael@0 202 NS_ASSERTION(aChildList.FirstChild() &&
michael@0 203 nsGkAtoms::tableFrame == aChildList.FirstChild()->GetType(),
michael@0 204 "expected a table frame");
michael@0 205 mFrames.SetFrames(aChildList);
michael@0 206 }
michael@0 207
michael@0 208 return NS_OK;
michael@0 209 }
michael@0 210
michael@0 211 nsresult
michael@0 212 nsTableOuterFrame::AppendFrames(ChildListID aListID,
michael@0 213 nsFrameList& aFrameList)
michael@0 214 {
michael@0 215 nsresult rv;
michael@0 216
michael@0 217 // We only have two child frames: the inner table and a caption frame.
michael@0 218 // The inner frame is provided when we're initialized, and it cannot change
michael@0 219 if (kCaptionList == aListID) {
michael@0 220 NS_ASSERTION(aFrameList.IsEmpty() ||
michael@0 221 aFrameList.FirstChild()->GetType() == nsGkAtoms::tableCaptionFrame,
michael@0 222 "appending non-caption frame to captionList");
michael@0 223 mCaptionFrames.AppendFrames(this, aFrameList);
michael@0 224 rv = NS_OK;
michael@0 225
michael@0 226 // Reflow the new caption frame. It's already marked dirty, so
michael@0 227 // just tell the pres shell.
michael@0 228 PresContext()->PresShell()->
michael@0 229 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
michael@0 230 NS_FRAME_HAS_DIRTY_CHILDREN);
michael@0 231 }
michael@0 232 else {
michael@0 233 NS_PRECONDITION(false, "unexpected child list");
michael@0 234 rv = NS_ERROR_UNEXPECTED;
michael@0 235 }
michael@0 236
michael@0 237 return rv;
michael@0 238 }
michael@0 239
michael@0 240 nsresult
michael@0 241 nsTableOuterFrame::InsertFrames(ChildListID aListID,
michael@0 242 nsIFrame* aPrevFrame,
michael@0 243 nsFrameList& aFrameList)
michael@0 244 {
michael@0 245 if (kCaptionList == aListID) {
michael@0 246 NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
michael@0 247 "inserting after sibling frame with different parent");
michael@0 248 NS_ASSERTION(aFrameList.IsEmpty() ||
michael@0 249 aFrameList.FirstChild()->GetType() == nsGkAtoms::tableCaptionFrame,
michael@0 250 "inserting non-caption frame into captionList");
michael@0 251 mCaptionFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
michael@0 252
michael@0 253 // Reflow the new caption frame. It's already marked dirty, so
michael@0 254 // just tell the pres shell.
michael@0 255 PresContext()->PresShell()->
michael@0 256 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
michael@0 257 NS_FRAME_HAS_DIRTY_CHILDREN);
michael@0 258 return NS_OK;
michael@0 259 }
michael@0 260 else {
michael@0 261 NS_PRECONDITION(!aPrevFrame, "invalid previous frame");
michael@0 262 return AppendFrames(aListID, aFrameList);
michael@0 263 }
michael@0 264 }
michael@0 265
michael@0 266 nsresult
michael@0 267 nsTableOuterFrame::RemoveFrame(ChildListID aListID,
michael@0 268 nsIFrame* aOldFrame)
michael@0 269 {
michael@0 270 // We only have two child frames: the inner table and one caption frame.
michael@0 271 // The inner frame can't be removed so this should be the caption
michael@0 272 NS_PRECONDITION(kCaptionList == aListID, "can't remove inner frame");
michael@0 273
michael@0 274 if (HasSideCaption()) {
michael@0 275 // The old caption width had an effect on the inner table width so
michael@0 276 // we're going to need to reflow it. Mark it dirty
michael@0 277 InnerTableFrame()->AddStateBits(NS_FRAME_IS_DIRTY);
michael@0 278 }
michael@0 279
michael@0 280 // Remove the frame and destroy it
michael@0 281 mCaptionFrames.DestroyFrame(aOldFrame);
michael@0 282
michael@0 283 PresContext()->PresShell()->
michael@0 284 FrameNeedsReflow(this, nsIPresShell::eTreeChange,
michael@0 285 NS_FRAME_HAS_DIRTY_CHILDREN); // also means child removed
michael@0 286
michael@0 287 return NS_OK;
michael@0 288 }
michael@0 289
michael@0 290 void
michael@0 291 nsTableOuterFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
michael@0 292 const nsRect& aDirtyRect,
michael@0 293 const nsDisplayListSet& aLists)
michael@0 294 {
michael@0 295 // No border, background or outline are painted because they all belong
michael@0 296 // to the inner table.
michael@0 297
michael@0 298 // If there's no caption, take a short cut to avoid having to create
michael@0 299 // the special display list set and then sort it.
michael@0 300 if (mCaptionFrames.IsEmpty()) {
michael@0 301 BuildDisplayListForInnerTable(aBuilder, aDirtyRect, aLists);
michael@0 302 return;
michael@0 303 }
michael@0 304
michael@0 305 nsDisplayListCollection set;
michael@0 306 BuildDisplayListForInnerTable(aBuilder, aDirtyRect, set);
michael@0 307
michael@0 308 nsDisplayListSet captionSet(set, set.BlockBorderBackgrounds());
michael@0 309 BuildDisplayListForChild(aBuilder, mCaptionFrames.FirstChild(),
michael@0 310 aDirtyRect, captionSet);
michael@0 311
michael@0 312 // Now we have to sort everything by content order, since the caption
michael@0 313 // may be somewhere inside the table
michael@0 314 set.SortAllByContentOrder(aBuilder, GetContent());
michael@0 315 set.MoveTo(aLists);
michael@0 316 }
michael@0 317
michael@0 318 void
michael@0 319 nsTableOuterFrame::BuildDisplayListForInnerTable(nsDisplayListBuilder* aBuilder,
michael@0 320 const nsRect& aDirtyRect,
michael@0 321 const nsDisplayListSet& aLists)
michael@0 322 {
michael@0 323 // Just paint the regular children, but the children's background is our
michael@0 324 // true background (there should only be one, the real table)
michael@0 325 nsIFrame* kid = mFrames.FirstChild();
michael@0 326 // The children should be in content order
michael@0 327 while (kid) {
michael@0 328 BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
michael@0 329 kid = kid->GetNextSibling();
michael@0 330 }
michael@0 331 }
michael@0 332
michael@0 333 nsIFrame*
michael@0 334 nsTableOuterFrame::GetParentStyleContextFrame() const
michael@0 335 {
michael@0 336 // The table outer frame and the (inner) table frame split the style
michael@0 337 // data by giving the table frame the style context associated with
michael@0 338 // the table content node and creating a style context for the outer
michael@0 339 // frame that is a *child* of the table frame's style context,
michael@0 340 // matching the ::-moz-table-outer pseudo-element. html.css has a
michael@0 341 // rule that causes that pseudo-element (and thus the outer table)
michael@0 342 // to inherit *some* style properties from the table frame. The
michael@0 343 // children of the table inherit directly from the inner table, and
michael@0 344 // the outer table's style context is a leaf.
michael@0 345
michael@0 346 return InnerTableFrame();
michael@0 347 }
michael@0 348
michael@0 349 // INCREMENTAL REFLOW HELPER FUNCTIONS
michael@0 350
michael@0 351 void
michael@0 352 nsTableOuterFrame::InitChildReflowState(nsPresContext& aPresContext,
michael@0 353 nsHTMLReflowState& aReflowState)
michael@0 354
michael@0 355 {
michael@0 356 nsMargin collapseBorder;
michael@0 357 nsMargin collapsePadding(0,0,0,0);
michael@0 358 nsMargin* pCollapseBorder = nullptr;
michael@0 359 nsMargin* pCollapsePadding = nullptr;
michael@0 360 if (aReflowState.frame == InnerTableFrame() &&
michael@0 361 InnerTableFrame()->IsBorderCollapse()) {
michael@0 362 collapseBorder = InnerTableFrame()->GetIncludedOuterBCBorder();
michael@0 363 pCollapseBorder = &collapseBorder;
michael@0 364 pCollapsePadding = &collapsePadding;
michael@0 365 }
michael@0 366 aReflowState.Init(&aPresContext, -1, -1, pCollapseBorder, pCollapsePadding);
michael@0 367 }
michael@0 368
michael@0 369 // get the margin and padding data. nsHTMLReflowState doesn't handle the
michael@0 370 // case of auto margins
michael@0 371 void
michael@0 372 nsTableOuterFrame::GetChildMargin(nsPresContext* aPresContext,
michael@0 373 const nsHTMLReflowState& aOuterRS,
michael@0 374 nsIFrame* aChildFrame,
michael@0 375 nscoord aAvailWidth,
michael@0 376 nsMargin& aMargin)
michael@0 377 {
michael@0 378 // construct a reflow state to compute margin and padding. Auto margins
michael@0 379 // will not be computed at this time.
michael@0 380
michael@0 381 // create and init the child reflow state
michael@0 382 // XXX We really shouldn't construct a reflow state to do this.
michael@0 383 nsHTMLReflowState childRS(aPresContext, aOuterRS, aChildFrame,
michael@0 384 nsSize(aAvailWidth, aOuterRS.AvailableHeight()),
michael@0 385 -1, -1, nsHTMLReflowState::CALLER_WILL_INIT);
michael@0 386 InitChildReflowState(*aPresContext, childRS);
michael@0 387
michael@0 388 aMargin = childRS.ComputedPhysicalMargin();
michael@0 389 }
michael@0 390
michael@0 391 static nsSize
michael@0 392 GetContainingBlockSize(const nsHTMLReflowState& aOuterRS)
michael@0 393 {
michael@0 394 nsSize size(0,0);
michael@0 395 const nsHTMLReflowState* containRS =
michael@0 396 aOuterRS.mCBReflowState;
michael@0 397
michael@0 398 if (containRS) {
michael@0 399 size.width = containRS->ComputedWidth();
michael@0 400 if (NS_UNCONSTRAINEDSIZE == size.width) {
michael@0 401 size.width = 0;
michael@0 402 }
michael@0 403 size.height = containRS->ComputedHeight();
michael@0 404 if (NS_UNCONSTRAINEDSIZE == size.height) {
michael@0 405 size.height = 0;
michael@0 406 }
michael@0 407 }
michael@0 408 return size;
michael@0 409 }
michael@0 410
michael@0 411 /* virtual */ nscoord
michael@0 412 nsTableOuterFrame::GetMinWidth(nsRenderingContext *aRenderingContext)
michael@0 413 {
michael@0 414 nscoord width = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
michael@0 415 InnerTableFrame(), nsLayoutUtils::MIN_WIDTH);
michael@0 416 DISPLAY_MIN_WIDTH(this, width);
michael@0 417 if (mCaptionFrames.NotEmpty()) {
michael@0 418 nscoord capWidth =
michael@0 419 nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
michael@0 420 mCaptionFrames.FirstChild(),
michael@0 421 nsLayoutUtils::MIN_WIDTH);
michael@0 422 if (HasSideCaption()) {
michael@0 423 width += capWidth;
michael@0 424 } else {
michael@0 425 if (capWidth > width) {
michael@0 426 width = capWidth;
michael@0 427 }
michael@0 428 }
michael@0 429 }
michael@0 430 return width;
michael@0 431 }
michael@0 432
michael@0 433 /* virtual */ nscoord
michael@0 434 nsTableOuterFrame::GetPrefWidth(nsRenderingContext *aRenderingContext)
michael@0 435 {
michael@0 436 nscoord maxWidth;
michael@0 437 DISPLAY_PREF_WIDTH(this, maxWidth);
michael@0 438
michael@0 439 maxWidth = nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
michael@0 440 InnerTableFrame(), nsLayoutUtils::PREF_WIDTH);
michael@0 441 if (mCaptionFrames.NotEmpty()) {
michael@0 442 uint8_t captionSide = GetCaptionSide();
michael@0 443 switch(captionSide) {
michael@0 444 case NS_STYLE_CAPTION_SIDE_LEFT:
michael@0 445 case NS_STYLE_CAPTION_SIDE_RIGHT:
michael@0 446 {
michael@0 447 nscoord capMin =
michael@0 448 nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
michael@0 449 mCaptionFrames.FirstChild(),
michael@0 450 nsLayoutUtils::MIN_WIDTH);
michael@0 451 maxWidth += capMin;
michael@0 452 }
michael@0 453 break;
michael@0 454 default:
michael@0 455 {
michael@0 456 nsLayoutUtils::IntrinsicWidthType iwt;
michael@0 457 if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
michael@0 458 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
michael@0 459 // Don't let the caption's pref width expand the table's pref
michael@0 460 // width.
michael@0 461 iwt = nsLayoutUtils::MIN_WIDTH;
michael@0 462 } else {
michael@0 463 NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
michael@0 464 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE,
michael@0 465 "unexpected caption side");
michael@0 466 iwt = nsLayoutUtils::PREF_WIDTH;
michael@0 467 }
michael@0 468 nscoord capPref =
michael@0 469 nsLayoutUtils::IntrinsicForContainer(aRenderingContext,
michael@0 470 mCaptionFrames.FirstChild(),
michael@0 471 iwt);
michael@0 472 maxWidth = std::max(maxWidth, capPref);
michael@0 473 }
michael@0 474 break;
michael@0 475 }
michael@0 476 }
michael@0 477 return maxWidth;
michael@0 478 }
michael@0 479
michael@0 480 // Compute the margin-box width of aChildFrame given the inputs. If
michael@0 481 // aMarginResult is non-null, fill it with the part of the margin-width
michael@0 482 // that was contributed by the margin.
michael@0 483 static nscoord
michael@0 484 ChildShrinkWrapWidth(nsRenderingContext *aRenderingContext,
michael@0 485 nsIFrame *aChildFrame,
michael@0 486 nsSize aCBSize, nscoord aAvailableWidth,
michael@0 487 nscoord *aMarginResult = nullptr)
michael@0 488 {
michael@0 489 AutoMaybeDisableFontInflation an(aChildFrame);
michael@0 490
michael@0 491 nsCSSOffsetState offsets(aChildFrame, aRenderingContext, aCBSize.width);
michael@0 492 nsSize size = aChildFrame->ComputeSize(aRenderingContext, aCBSize,
michael@0 493 aAvailableWidth,
michael@0 494 nsSize(offsets.ComputedPhysicalMargin().LeftRight(),
michael@0 495 offsets.ComputedPhysicalMargin().TopBottom()),
michael@0 496 nsSize(offsets.ComputedPhysicalBorderPadding().LeftRight() -
michael@0 497 offsets.ComputedPhysicalPadding().LeftRight(),
michael@0 498 offsets.ComputedPhysicalBorderPadding().TopBottom() -
michael@0 499 offsets.ComputedPhysicalPadding().TopBottom()),
michael@0 500 nsSize(offsets.ComputedPhysicalPadding().LeftRight(),
michael@0 501 offsets.ComputedPhysicalPadding().TopBottom()),
michael@0 502 true);
michael@0 503 if (aMarginResult)
michael@0 504 *aMarginResult = offsets.ComputedPhysicalMargin().LeftRight();
michael@0 505 return size.width + offsets.ComputedPhysicalMargin().LeftRight() +
michael@0 506 offsets.ComputedPhysicalBorderPadding().LeftRight();
michael@0 507 }
michael@0 508
michael@0 509 /* virtual */ nsSize
michael@0 510 nsTableOuterFrame::ComputeAutoSize(nsRenderingContext *aRenderingContext,
michael@0 511 nsSize aCBSize, nscoord aAvailableWidth,
michael@0 512 nsSize aMargin, nsSize aBorder,
michael@0 513 nsSize aPadding, bool aShrinkWrap)
michael@0 514 {
michael@0 515 nscoord kidAvailableWidth = aAvailableWidth - aMargin.width;
michael@0 516 NS_ASSERTION(aBorder == nsSize(0, 0) &&
michael@0 517 aPadding == nsSize(0, 0),
michael@0 518 "Table outer frames cannot hae borders or paddings");
michael@0 519
michael@0 520 // When we're shrink-wrapping, our auto size needs to wrap around the
michael@0 521 // actual size of the table, which (if it is specified as a percent)
michael@0 522 // could be something that is not reflected in our GetMinWidth and
michael@0 523 // GetPrefWidth. See bug 349457 for an example.
michael@0 524
michael@0 525 // Match the availableWidth logic in Reflow.
michael@0 526 uint8_t captionSide = GetCaptionSide();
michael@0 527 nscoord width;
michael@0 528 if (captionSide == NO_SIDE) {
michael@0 529 width = ChildShrinkWrapWidth(aRenderingContext, InnerTableFrame(),
michael@0 530 aCBSize, kidAvailableWidth);
michael@0 531 } else if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
michael@0 532 captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
michael@0 533 nscoord capWidth = ChildShrinkWrapWidth(aRenderingContext,
michael@0 534 mCaptionFrames.FirstChild(),
michael@0 535 aCBSize, kidAvailableWidth);
michael@0 536 width = capWidth + ChildShrinkWrapWidth(aRenderingContext,
michael@0 537 InnerTableFrame(), aCBSize,
michael@0 538 kidAvailableWidth - capWidth);
michael@0 539 } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
michael@0 540 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
michael@0 541 nscoord margin;
michael@0 542 width = ChildShrinkWrapWidth(aRenderingContext, InnerTableFrame(),
michael@0 543 aCBSize, kidAvailableWidth, &margin);
michael@0 544 nscoord capWidth = ChildShrinkWrapWidth(aRenderingContext,
michael@0 545 mCaptionFrames.FirstChild(), aCBSize,
michael@0 546 width - margin);
michael@0 547 if (capWidth > width)
michael@0 548 width = capWidth;
michael@0 549 } else {
michael@0 550 NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
michael@0 551 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE,
michael@0 552 "unexpected caption-side");
michael@0 553 width = ChildShrinkWrapWidth(aRenderingContext, InnerTableFrame(),
michael@0 554 aCBSize, kidAvailableWidth);
michael@0 555 nscoord capWidth = ChildShrinkWrapWidth(aRenderingContext,
michael@0 556 mCaptionFrames.FirstChild(),
michael@0 557 aCBSize, kidAvailableWidth);
michael@0 558 if (capWidth > width)
michael@0 559 width = capWidth;
michael@0 560 }
michael@0 561
michael@0 562 return nsSize(width, NS_UNCONSTRAINEDSIZE);
michael@0 563 }
michael@0 564
michael@0 565 uint8_t
michael@0 566 nsTableOuterFrame::GetCaptionSide()
michael@0 567 {
michael@0 568 if (mCaptionFrames.NotEmpty()) {
michael@0 569 return mCaptionFrames.FirstChild()->StyleTableBorder()->mCaptionSide;
michael@0 570 }
michael@0 571 else {
michael@0 572 return NO_SIDE; // no caption
michael@0 573 }
michael@0 574 }
michael@0 575
michael@0 576 uint8_t
michael@0 577 nsTableOuterFrame::GetCaptionVerticalAlign()
michael@0 578 {
michael@0 579 const nsStyleCoord& va =
michael@0 580 mCaptionFrames.FirstChild()->StyleTextReset()->mVerticalAlign;
michael@0 581 return (va.GetUnit() == eStyleUnit_Enumerated)
michael@0 582 ? va.GetIntValue()
michael@0 583 : NS_STYLE_VERTICAL_ALIGN_TOP;
michael@0 584 }
michael@0 585
michael@0 586 void
michael@0 587 nsTableOuterFrame::SetDesiredSize(uint8_t aCaptionSide,
michael@0 588 const nsMargin& aInnerMargin,
michael@0 589 const nsMargin& aCaptionMargin,
michael@0 590 nscoord& aWidth,
michael@0 591 nscoord& aHeight)
michael@0 592 {
michael@0 593 aWidth = aHeight = 0;
michael@0 594
michael@0 595 nsRect innerRect = InnerTableFrame()->GetRect();
michael@0 596 nscoord innerWidth = innerRect.width;
michael@0 597
michael@0 598 nsRect captionRect(0,0,0,0);
michael@0 599 nscoord captionWidth = 0;
michael@0 600 if (mCaptionFrames.NotEmpty()) {
michael@0 601 captionRect = mCaptionFrames.FirstChild()->GetRect();
michael@0 602 captionWidth = captionRect.width;
michael@0 603 }
michael@0 604 switch(aCaptionSide) {
michael@0 605 case NS_STYLE_CAPTION_SIDE_LEFT:
michael@0 606 aWidth = std::max(aInnerMargin.left, aCaptionMargin.left + captionWidth + aCaptionMargin.right) +
michael@0 607 innerWidth + aInnerMargin.right;
michael@0 608 break;
michael@0 609 case NS_STYLE_CAPTION_SIDE_RIGHT:
michael@0 610 aWidth = std::max(aInnerMargin.right, aCaptionMargin.left + captionWidth + aCaptionMargin.right) +
michael@0 611 innerWidth + aInnerMargin.left;
michael@0 612 break;
michael@0 613 default:
michael@0 614 aWidth = aInnerMargin.left + innerWidth + aInnerMargin.right;
michael@0 615 aWidth = std::max(aWidth, captionRect.XMost() + aCaptionMargin.right);
michael@0 616 }
michael@0 617 aHeight = innerRect.YMost() + aInnerMargin.bottom;
michael@0 618 if (NS_STYLE_CAPTION_SIDE_BOTTOM != aCaptionSide) {
michael@0 619 aHeight = std::max(aHeight, captionRect.YMost() + aCaptionMargin.bottom);
michael@0 620 }
michael@0 621 else {
michael@0 622 aHeight = std::max(aHeight, captionRect.YMost() + aCaptionMargin.bottom +
michael@0 623 aInnerMargin.bottom);
michael@0 624 }
michael@0 625
michael@0 626 }
michael@0 627
michael@0 628 nsresult
michael@0 629 nsTableOuterFrame::GetCaptionOrigin(uint32_t aCaptionSide,
michael@0 630 const nsSize& aContainBlockSize,
michael@0 631 const nsSize& aInnerSize,
michael@0 632 const nsMargin& aInnerMargin,
michael@0 633 const nsSize& aCaptionSize,
michael@0 634 nsMargin& aCaptionMargin,
michael@0 635 nsPoint& aOrigin)
michael@0 636 {
michael@0 637 aOrigin.x = aOrigin.y = 0;
michael@0 638 if ((NS_UNCONSTRAINEDSIZE == aInnerSize.width) || (NS_UNCONSTRAINEDSIZE == aInnerSize.height) ||
michael@0 639 (NS_UNCONSTRAINEDSIZE == aCaptionSize.width) || (NS_UNCONSTRAINEDSIZE == aCaptionSize.height)) {
michael@0 640 return NS_OK;
michael@0 641 }
michael@0 642 if (mCaptionFrames.IsEmpty()) return NS_OK;
michael@0 643
michael@0 644 NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.left, "The computed caption margin is auto?");
michael@0 645 NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.top, "The computed caption margin is auto?");
michael@0 646 NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.bottom, "The computed caption margin is auto?");
michael@0 647
michael@0 648 // horizontal computation
michael@0 649 switch(aCaptionSide) {
michael@0 650 case NS_STYLE_CAPTION_SIDE_BOTTOM:
michael@0 651 case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: {
michael@0 652 // FIXME: Position relative to right edge for RTL. (Based on table
michael@0 653 // direction or table parent direction?)
michael@0 654 aOrigin.x = aCaptionMargin.left;
michael@0 655 if (aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
michael@0 656 // We placed the caption using only the table's width as available
michael@0 657 // width, and we should position it this way as well.
michael@0 658 aOrigin.x += aInnerMargin.left;
michael@0 659 }
michael@0 660 } break;
michael@0 661 case NS_STYLE_CAPTION_SIDE_LEFT: {
michael@0 662 aOrigin.x = aCaptionMargin.left;
michael@0 663 } break;
michael@0 664 case NS_STYLE_CAPTION_SIDE_RIGHT: {
michael@0 665 aOrigin.x = aInnerMargin.left + aInnerSize.width + aCaptionMargin.left;
michael@0 666 } break;
michael@0 667 default: { // top
michael@0 668 NS_ASSERTION(aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP ||
michael@0 669 aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE,
michael@0 670 "unexpected caption side");
michael@0 671 // FIXME: Position relative to right edge for RTL. (Based on table
michael@0 672 // direction or table parent direction?)
michael@0 673 aOrigin.x = aCaptionMargin.left;
michael@0 674 if (aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP) {
michael@0 675 // We placed the caption using only the table's width as available
michael@0 676 // width, and we should position it this way as well.
michael@0 677 aOrigin.x += aInnerMargin.left;
michael@0 678 }
michael@0 679
michael@0 680 } break;
michael@0 681 }
michael@0 682 // vertical computation
michael@0 683 switch (aCaptionSide) {
michael@0 684 case NS_STYLE_CAPTION_SIDE_RIGHT:
michael@0 685 case NS_STYLE_CAPTION_SIDE_LEFT:
michael@0 686 aOrigin.y = aInnerMargin.top;
michael@0 687 switch (GetCaptionVerticalAlign()) {
michael@0 688 case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
michael@0 689 aOrigin.y = std::max(0, aInnerMargin.top + ((aInnerSize.height - aCaptionSize.height) / 2));
michael@0 690 break;
michael@0 691 case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
michael@0 692 aOrigin.y = std::max(0, aInnerMargin.top + aInnerSize.height - aCaptionSize.height);
michael@0 693 break;
michael@0 694 default:
michael@0 695 break;
michael@0 696 }
michael@0 697 break;
michael@0 698 case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE:
michael@0 699 case NS_STYLE_CAPTION_SIDE_BOTTOM: {
michael@0 700 aOrigin.y = aInnerMargin.top + aInnerSize.height + aCaptionMargin.top;
michael@0 701 } break;
michael@0 702 case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE:
michael@0 703 case NS_STYLE_CAPTION_SIDE_TOP: {
michael@0 704 aOrigin.y = aInnerMargin.top + aCaptionMargin.top;
michael@0 705 } break;
michael@0 706 default:
michael@0 707 NS_NOTREACHED("Unknown caption alignment type");
michael@0 708 break;
michael@0 709 }
michael@0 710 return NS_OK;
michael@0 711 }
michael@0 712
michael@0 713 nsresult
michael@0 714 nsTableOuterFrame::GetInnerOrigin(uint32_t aCaptionSide,
michael@0 715 const nsSize& aContainBlockSize,
michael@0 716 const nsSize& aCaptionSize,
michael@0 717 const nsMargin& aCaptionMargin,
michael@0 718 const nsSize& aInnerSize,
michael@0 719 nsMargin& aInnerMargin,
michael@0 720 nsPoint& aOrigin)
michael@0 721 {
michael@0 722
michael@0 723 NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.left, "The computed caption margin is auto?");
michael@0 724 NS_ASSERTION(NS_AUTOMARGIN != aCaptionMargin.right, "The computed caption margin is auto?");
michael@0 725 NS_ASSERTION(NS_AUTOMARGIN != aInnerMargin.left, "The computed inner margin is auto?");
michael@0 726 NS_ASSERTION(NS_AUTOMARGIN != aInnerMargin.right, "The computed inner margin is auto?");
michael@0 727 NS_ASSERTION(NS_AUTOMARGIN != aInnerMargin.top, "The computed inner margin is auto?");
michael@0 728 NS_ASSERTION(NS_AUTOMARGIN != aInnerMargin.bottom, "The computed inner margin is auto?");
michael@0 729
michael@0 730 aOrigin.x = aOrigin.y = 0;
michael@0 731 if ((NS_UNCONSTRAINEDSIZE == aInnerSize.width) || (NS_UNCONSTRAINEDSIZE == aInnerSize.height) ||
michael@0 732 (NS_UNCONSTRAINEDSIZE == aCaptionSize.width) || (NS_UNCONSTRAINEDSIZE == aCaptionSize.height)) {
michael@0 733 return NS_OK;
michael@0 734 }
michael@0 735
michael@0 736 nscoord minCapWidth = aCaptionSize.width;
michael@0 737
michael@0 738 minCapWidth += aCaptionMargin.left;
michael@0 739 minCapWidth += aCaptionMargin.right;
michael@0 740
michael@0 741 // horizontal computation
michael@0 742 switch (aCaptionSide) {
michael@0 743 case NS_STYLE_CAPTION_SIDE_LEFT: {
michael@0 744 if (aInnerMargin.left < minCapWidth) {
michael@0 745 // shift the inner table to get some place for the caption
michael@0 746 aInnerMargin.right += aInnerMargin.left - minCapWidth;
michael@0 747 aInnerMargin.right = std::max(0, aInnerMargin.right);
michael@0 748 aInnerMargin.left = minCapWidth;
michael@0 749 }
michael@0 750 aOrigin.x = aInnerMargin.left;
michael@0 751 } break;
michael@0 752 default: {
michael@0 753 NS_ASSERTION(aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP ||
michael@0 754 aCaptionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
michael@0 755 aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM ||
michael@0 756 aCaptionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE ||
michael@0 757 aCaptionSide == NS_STYLE_CAPTION_SIDE_RIGHT ||
michael@0 758 aCaptionSide == NO_SIDE,
michael@0 759 "unexpected caption side");
michael@0 760 aOrigin.x = aInnerMargin.left;
michael@0 761 } break;
michael@0 762 }
michael@0 763
michael@0 764 // vertical computation
michael@0 765 switch (aCaptionSide) {
michael@0 766 case NS_STYLE_CAPTION_SIDE_BOTTOM:
michael@0 767 case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: {
michael@0 768 aOrigin.y = aInnerMargin.top;
michael@0 769 } break;
michael@0 770 case NS_STYLE_CAPTION_SIDE_LEFT:
michael@0 771 case NS_STYLE_CAPTION_SIDE_RIGHT: {
michael@0 772 aOrigin.y = aInnerMargin.top;
michael@0 773 switch (GetCaptionVerticalAlign()) {
michael@0 774 case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
michael@0 775 aOrigin.y = std::max(aInnerMargin.top, (aCaptionSize.height - aInnerSize.height) / 2);
michael@0 776 break;
michael@0 777 case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
michael@0 778 aOrigin.y = std::max(aInnerMargin.top, aCaptionSize.height - aInnerSize.height);
michael@0 779 break;
michael@0 780 default:
michael@0 781 break;
michael@0 782 }
michael@0 783 } break;
michael@0 784 case NO_SIDE:
michael@0 785 case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE:
michael@0 786 case NS_STYLE_CAPTION_SIDE_TOP: {
michael@0 787 aOrigin.y = aInnerMargin.top + aCaptionMargin.top + aCaptionSize.height +
michael@0 788 aCaptionMargin.bottom;
michael@0 789 } break;
michael@0 790 default:
michael@0 791 NS_NOTREACHED("Unknown caption alignment type");
michael@0 792 break;
michael@0 793 }
michael@0 794 return NS_OK;
michael@0 795 }
michael@0 796
michael@0 797 void
michael@0 798 nsTableOuterFrame::OuterBeginReflowChild(nsPresContext* aPresContext,
michael@0 799 nsIFrame* aChildFrame,
michael@0 800 const nsHTMLReflowState& aOuterRS,
michael@0 801 void* aChildRSSpace,
michael@0 802 nscoord aAvailWidth)
michael@0 803 {
michael@0 804 // work around pixel rounding errors, round down to ensure we don't exceed the avail height in
michael@0 805 nscoord availHeight = aOuterRS.AvailableHeight();
michael@0 806 if (NS_UNCONSTRAINEDSIZE != availHeight) {
michael@0 807 if (mCaptionFrames.FirstChild() == aChildFrame) {
michael@0 808 availHeight = NS_UNCONSTRAINEDSIZE;
michael@0 809 } else {
michael@0 810 nsMargin margin;
michael@0 811 GetChildMargin(aPresContext, aOuterRS, aChildFrame,
michael@0 812 aOuterRS.AvailableWidth(), margin);
michael@0 813
michael@0 814 NS_ASSERTION(NS_UNCONSTRAINEDSIZE != margin.top, "No unconstrainedsize arithmetic, please");
michael@0 815 availHeight -= margin.top;
michael@0 816
michael@0 817 NS_ASSERTION(NS_UNCONSTRAINEDSIZE != margin.bottom, "No unconstrainedsize arithmetic, please");
michael@0 818 availHeight -= margin.bottom;
michael@0 819 }
michael@0 820 }
michael@0 821 nsSize availSize(aAvailWidth, availHeight);
michael@0 822 // create and init the child reflow state, using placement new on
michael@0 823 // stack space allocated by the caller, so that the caller can destroy
michael@0 824 // it
michael@0 825 nsHTMLReflowState &childRS = * new (aChildRSSpace)
michael@0 826 nsHTMLReflowState(aPresContext, aOuterRS, aChildFrame, availSize,
michael@0 827 -1, -1, nsHTMLReflowState::CALLER_WILL_INIT);
michael@0 828 InitChildReflowState(*aPresContext, childRS);
michael@0 829
michael@0 830 // see if we need to reset top-of-page due to a caption
michael@0 831 if (childRS.mFlags.mIsTopOfPage &&
michael@0 832 mCaptionFrames.FirstChild() == aChildFrame) {
michael@0 833 uint8_t captionSide = GetCaptionSide();
michael@0 834 if (captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM ||
michael@0 835 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE) {
michael@0 836 childRS.mFlags.mIsTopOfPage = false;
michael@0 837 }
michael@0 838 }
michael@0 839 }
michael@0 840
michael@0 841 nsresult
michael@0 842 nsTableOuterFrame::OuterDoReflowChild(nsPresContext* aPresContext,
michael@0 843 nsIFrame* aChildFrame,
michael@0 844 const nsHTMLReflowState& aChildRS,
michael@0 845 nsHTMLReflowMetrics& aMetrics,
michael@0 846 nsReflowStatus& aStatus)
michael@0 847 {
michael@0 848
michael@0 849 // use the current position as a best guess for placement
michael@0 850 nsPoint childPt = aChildFrame->GetPosition();
michael@0 851 uint32_t flags = NS_FRAME_NO_MOVE_FRAME;
michael@0 852
michael@0 853 // We don't want to delete our next-in-flow's child if it's an inner table
michael@0 854 // frame, because outer table frames always assume that their inner table
michael@0 855 // frames don't go away. If an outer table frame is removed because it is
michael@0 856 // a next-in-flow of an already complete outer table frame, then it will
michael@0 857 // take care of removing it's inner table frame.
michael@0 858 if (aChildFrame == InnerTableFrame()) {
michael@0 859 flags |= NS_FRAME_NO_DELETE_NEXT_IN_FLOW_CHILD;
michael@0 860 }
michael@0 861
michael@0 862 return ReflowChild(aChildFrame, aPresContext, aMetrics, aChildRS,
michael@0 863 childPt.x, childPt.y, flags, aStatus);
michael@0 864 }
michael@0 865
michael@0 866 void
michael@0 867 nsTableOuterFrame::UpdateReflowMetrics(uint8_t aCaptionSide,
michael@0 868 nsHTMLReflowMetrics& aMet,
michael@0 869 const nsMargin& aInnerMargin,
michael@0 870 const nsMargin& aCaptionMargin)
michael@0 871 {
michael@0 872 SetDesiredSize(aCaptionSide, aInnerMargin, aCaptionMargin,
michael@0 873 aMet.Width(), aMet.Height());
michael@0 874
michael@0 875 aMet.SetOverflowAreasToDesiredBounds();
michael@0 876 ConsiderChildOverflow(aMet.mOverflowAreas, InnerTableFrame());
michael@0 877 if (mCaptionFrames.NotEmpty()) {
michael@0 878 ConsiderChildOverflow(aMet.mOverflowAreas, mCaptionFrames.FirstChild());
michael@0 879 }
michael@0 880 }
michael@0 881
michael@0 882 nsresult nsTableOuterFrame::Reflow(nsPresContext* aPresContext,
michael@0 883 nsHTMLReflowMetrics& aDesiredSize,
michael@0 884 const nsHTMLReflowState& aOuterRS,
michael@0 885 nsReflowStatus& aStatus)
michael@0 886 {
michael@0 887 DO_GLOBAL_REFLOW_COUNT("nsTableOuterFrame");
michael@0 888 DISPLAY_REFLOW(aPresContext, this, aOuterRS, aDesiredSize, aStatus);
michael@0 889
michael@0 890 nsresult rv = NS_OK;
michael@0 891 uint8_t captionSide = GetCaptionSide();
michael@0 892
michael@0 893 // Initialize out parameters
michael@0 894 aDesiredSize.Width() = aDesiredSize.Height() = 0;
michael@0 895 aStatus = NS_FRAME_COMPLETE;
michael@0 896
michael@0 897 if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
michael@0 898 // Set up our kids. They're already present, on an overflow list,
michael@0 899 // or there are none so we'll create them now
michael@0 900 MoveOverflowToChildList();
michael@0 901 }
michael@0 902
michael@0 903 // Use longs to get more-aligned space.
michael@0 904 #define LONGS_IN_HTMLRS \
michael@0 905 ((sizeof(nsHTMLReflowState) + sizeof(long) - 1) / sizeof(long))
michael@0 906 long captionRSSpace[LONGS_IN_HTMLRS];
michael@0 907 nsHTMLReflowState *captionRS =
michael@0 908 static_cast<nsHTMLReflowState*>((void*)captionRSSpace);
michael@0 909 long innerRSSpace[LONGS_IN_HTMLRS];
michael@0 910 nsHTMLReflowState *innerRS =
michael@0 911 static_cast<nsHTMLReflowState*>((void*) innerRSSpace);
michael@0 912
michael@0 913 nsRect origInnerRect = InnerTableFrame()->GetRect();
michael@0 914 nsRect origInnerVisualOverflow = InnerTableFrame()->GetVisualOverflowRect();
michael@0 915 bool innerFirstReflow =
michael@0 916 (InnerTableFrame()->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
michael@0 917 nsRect origCaptionRect;
michael@0 918 nsRect origCaptionVisualOverflow;
michael@0 919 bool captionFirstReflow;
michael@0 920 if (mCaptionFrames.NotEmpty()) {
michael@0 921 origCaptionRect = mCaptionFrames.FirstChild()->GetRect();
michael@0 922 origCaptionVisualOverflow =
michael@0 923 mCaptionFrames.FirstChild()->GetVisualOverflowRect();
michael@0 924 captionFirstReflow =
michael@0 925 (mCaptionFrames.FirstChild()->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
michael@0 926 }
michael@0 927
michael@0 928 // ComputeAutoSize has to match this logic.
michael@0 929 if (captionSide == NO_SIDE) {
michael@0 930 // We don't have a caption.
michael@0 931 OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS,
michael@0 932 innerRSSpace, aOuterRS.ComputedWidth());
michael@0 933 } else if (captionSide == NS_STYLE_CAPTION_SIDE_LEFT ||
michael@0 934 captionSide == NS_STYLE_CAPTION_SIDE_RIGHT) {
michael@0 935 // nsTableCaptionFrame::ComputeAutoSize takes care of making side
michael@0 936 // captions small. Compute the caption's size first, and tell the
michael@0 937 // table to fit in what's left.
michael@0 938 OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRS,
michael@0 939 captionRSSpace, aOuterRS.ComputedWidth());
michael@0 940 nscoord innerAvailWidth = aOuterRS.ComputedWidth() -
michael@0 941 (captionRS->ComputedWidth() + captionRS->ComputedPhysicalMargin().LeftRight() +
michael@0 942 captionRS->ComputedPhysicalBorderPadding().LeftRight());
michael@0 943 OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS,
michael@0 944 innerRSSpace, innerAvailWidth);
michael@0 945
michael@0 946 } else if (captionSide == NS_STYLE_CAPTION_SIDE_TOP ||
michael@0 947 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM) {
michael@0 948 // Compute the table's size first, and then prevent the caption from
michael@0 949 // being wider unless it has to be.
michael@0 950 //
michael@0 951 // Note that CSS 2.1 (but not 2.0) says:
michael@0 952 // The width of the anonymous box is the border-edge width of the
michael@0 953 // table box inside it
michael@0 954 // We don't actually make our anonymous box that width (if we did,
michael@0 955 // it would break 'auto' margins), but this effectively does that.
michael@0 956 OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS,
michael@0 957 innerRSSpace, aOuterRS.ComputedWidth());
michael@0 958 // It's good that CSS 2.1 says not to include margins, since we
michael@0 959 // can't, since they already been converted so they exactly
michael@0 960 // fill the available width (ignoring the margin on one side if
michael@0 961 // neither are auto). (We take advantage of that later when we call
michael@0 962 // GetCaptionOrigin, though.)
michael@0 963 nscoord innerBorderWidth = innerRS->ComputedWidth() +
michael@0 964 innerRS->ComputedPhysicalBorderPadding().LeftRight();
michael@0 965 OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRS,
michael@0 966 captionRSSpace, innerBorderWidth);
michael@0 967 } else {
michael@0 968 NS_ASSERTION(captionSide == NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE ||
michael@0 969 captionSide == NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE,
michael@0 970 "unexpected caption-side");
michael@0 971 // Size the table and the caption independently.
michael@0 972 OuterBeginReflowChild(aPresContext, mCaptionFrames.FirstChild(), aOuterRS,
michael@0 973 captionRSSpace, aOuterRS.ComputedWidth());
michael@0 974 OuterBeginReflowChild(aPresContext, InnerTableFrame(), aOuterRS,
michael@0 975 innerRSSpace, aOuterRS.ComputedWidth());
michael@0 976 }
michael@0 977
michael@0 978 // First reflow the caption.
michael@0 979 nsHTMLReflowMetrics captionMet(captionRS->GetWritingMode());
michael@0 980 nsSize captionSize;
michael@0 981 nsMargin captionMargin;
michael@0 982 if (mCaptionFrames.NotEmpty()) {
michael@0 983 nsReflowStatus capStatus; // don't let the caption cause incomplete
michael@0 984 rv = OuterDoReflowChild(aPresContext, mCaptionFrames.FirstChild(),
michael@0 985 *captionRS, captionMet, capStatus);
michael@0 986 if (NS_FAILED(rv)) return rv;
michael@0 987 captionSize.width = captionMet.Width();
michael@0 988 captionSize.height = captionMet.Height();
michael@0 989 captionMargin = captionRS->ComputedPhysicalMargin();
michael@0 990 // Now that we know the height of the caption, reduce the available height
michael@0 991 // for the table frame if we are height constrained and the caption is above
michael@0 992 // or below the inner table.
michael@0 993 if (NS_UNCONSTRAINEDSIZE != aOuterRS.AvailableHeight()) {
michael@0 994 nscoord captionHeight = 0;
michael@0 995 switch (captionSide) {
michael@0 996 case NS_STYLE_CAPTION_SIDE_TOP:
michael@0 997 case NS_STYLE_CAPTION_SIDE_BOTTOM:
michael@0 998 case NS_STYLE_CAPTION_SIDE_TOP_OUTSIDE:
michael@0 999 case NS_STYLE_CAPTION_SIDE_BOTTOM_OUTSIDE: {
michael@0 1000 captionHeight = captionSize.height + captionMargin.TopBottom();
michael@0 1001 break;
michael@0 1002 }
michael@0 1003 }
michael@0 1004 innerRS->AvailableHeight() =
michael@0 1005 std::max(0, innerRS->AvailableHeight() - captionHeight);
michael@0 1006 }
michael@0 1007 } else {
michael@0 1008 captionSize.SizeTo(0,0);
michael@0 1009 captionMargin.SizeTo(0,0,0,0);
michael@0 1010 }
michael@0 1011
michael@0 1012 // Then, now that we know how much to reduce the width of the inner
michael@0 1013 // table to account for side captions, reflow the inner table.
michael@0 1014 nsHTMLReflowMetrics innerMet(innerRS->GetWritingMode());
michael@0 1015 rv = OuterDoReflowChild(aPresContext, InnerTableFrame(), *innerRS,
michael@0 1016 innerMet, aStatus);
michael@0 1017 if (NS_FAILED(rv)) return rv;
michael@0 1018 nsSize innerSize;
michael@0 1019 innerSize.width = innerMet.Width();
michael@0 1020 innerSize.height = innerMet.Height();
michael@0 1021 nsMargin innerMargin = innerRS->ComputedPhysicalMargin();
michael@0 1022
michael@0 1023 nsSize containSize = GetContainingBlockSize(aOuterRS);
michael@0 1024
michael@0 1025 // Now that we've reflowed both we can place them.
michael@0 1026 // XXXldb Most of the input variables here are now uninitialized!
michael@0 1027
michael@0 1028 // XXX Need to recompute inner table's auto margins for the case of side
michael@0 1029 // captions. (Caption's are broken too, but that should be fixed earlier.)
michael@0 1030
michael@0 1031 if (mCaptionFrames.NotEmpty()) {
michael@0 1032 nsPoint captionOrigin;
michael@0 1033 GetCaptionOrigin(captionSide, containSize, innerSize,
michael@0 1034 innerMargin, captionSize, captionMargin, captionOrigin);
michael@0 1035 FinishReflowChild(mCaptionFrames.FirstChild(), aPresContext, captionMet,
michael@0 1036 captionRS, captionOrigin.x, captionOrigin.y, 0);
michael@0 1037 captionRS->~nsHTMLReflowState();
michael@0 1038 }
michael@0 1039 // XXX If the height is constrained then we need to check whether
michael@0 1040 // everything still fits...
michael@0 1041
michael@0 1042 nsPoint innerOrigin;
michael@0 1043 GetInnerOrigin(captionSide, containSize, captionSize,
michael@0 1044 captionMargin, innerSize, innerMargin, innerOrigin);
michael@0 1045 FinishReflowChild(InnerTableFrame(), aPresContext, innerMet, innerRS,
michael@0 1046 innerOrigin.x, innerOrigin.y, 0);
michael@0 1047 innerRS->~nsHTMLReflowState();
michael@0 1048
michael@0 1049 nsTableFrame::InvalidateTableFrame(InnerTableFrame(), origInnerRect,
michael@0 1050 origInnerVisualOverflow, innerFirstReflow);
michael@0 1051 if (mCaptionFrames.NotEmpty()) {
michael@0 1052 nsTableFrame::InvalidateTableFrame(mCaptionFrames.FirstChild(), origCaptionRect,
michael@0 1053 origCaptionVisualOverflow,
michael@0 1054 captionFirstReflow);
michael@0 1055 }
michael@0 1056
michael@0 1057 UpdateReflowMetrics(captionSide, aDesiredSize, innerMargin, captionMargin);
michael@0 1058
michael@0 1059 if (GetPrevInFlow()) {
michael@0 1060 ReflowOverflowContainerChildren(aPresContext, aOuterRS,
michael@0 1061 aDesiredSize.mOverflowAreas, 0,
michael@0 1062 aStatus);
michael@0 1063 }
michael@0 1064
michael@0 1065 FinishReflowWithAbsoluteFrames(aPresContext, aDesiredSize, aOuterRS, aStatus);
michael@0 1066
michael@0 1067 // Return our desired rect
michael@0 1068
michael@0 1069 NS_FRAME_SET_TRUNCATION(aStatus, aOuterRS, aDesiredSize);
michael@0 1070 return rv;
michael@0 1071 }
michael@0 1072
michael@0 1073 nsIAtom*
michael@0 1074 nsTableOuterFrame::GetType() const
michael@0 1075 {
michael@0 1076 return nsGkAtoms::tableOuterFrame;
michael@0 1077 }
michael@0 1078
michael@0 1079 /* ----- global methods ----- */
michael@0 1080
michael@0 1081 nsIContent*
michael@0 1082 nsTableOuterFrame::GetCellAt(uint32_t aRowIdx, uint32_t aColIdx) const
michael@0 1083 {
michael@0 1084 nsTableCellMap* cellMap = InnerTableFrame()->GetCellMap();
michael@0 1085 if (!cellMap) {
michael@0 1086 return nullptr;
michael@0 1087 }
michael@0 1088
michael@0 1089 nsTableCellFrame* cell = cellMap->GetCellInfoAt(aRowIdx, aColIdx);
michael@0 1090 if (!cell) {
michael@0 1091 return nullptr;
michael@0 1092 }
michael@0 1093
michael@0 1094 return cell->GetContent();
michael@0 1095 }
michael@0 1096
michael@0 1097
michael@0 1098 nsIFrame*
michael@0 1099 NS_NewTableOuterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
michael@0 1100 {
michael@0 1101 return new (aPresShell) nsTableOuterFrame(aContext);
michael@0 1102 }
michael@0 1103
michael@0 1104 NS_IMPL_FRAMEARENA_HELPERS(nsTableOuterFrame)
michael@0 1105
michael@0 1106 #ifdef DEBUG_FRAME_DUMP
michael@0 1107 nsresult
michael@0 1108 nsTableOuterFrame::GetFrameName(nsAString& aResult) const
michael@0 1109 {
michael@0 1110 return MakeFrameName(NS_LITERAL_STRING("TableOuter"), aResult);
michael@0 1111 }
michael@0 1112 #endif
michael@0 1113

mercurial