1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/layout/generic/nsBlockFrame.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,7280 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +// vim:cindent:ts=2:et:sw=2: 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* 1.11 + * rendering object for CSS display:block, inline-block, and list-item 1.12 + * boxes, also used for various anonymous boxes 1.13 + */ 1.14 + 1.15 +#include "nsBlockFrame.h" 1.16 + 1.17 +#include "mozilla/DebugOnly.h" 1.18 + 1.19 +#include "nsCOMPtr.h" 1.20 +#include "nsAbsoluteContainingBlock.h" 1.21 +#include "nsBlockReflowContext.h" 1.22 +#include "nsBlockReflowState.h" 1.23 +#include "nsBulletFrame.h" 1.24 +#include "nsLineBox.h" 1.25 +#include "nsLineLayout.h" 1.26 +#include "nsPlaceholderFrame.h" 1.27 +#include "nsStyleConsts.h" 1.28 +#include "nsFrameManager.h" 1.29 +#include "nsPresContext.h" 1.30 +#include "nsIPresShell.h" 1.31 +#include "nsStyleContext.h" 1.32 +#include "nsHTMLParts.h" 1.33 +#include "nsGkAtoms.h" 1.34 +#include "nsGenericHTMLElement.h" 1.35 +#include "nsAttrValueInlines.h" 1.36 +#include "prprf.h" 1.37 +#include "nsFloatManager.h" 1.38 +#include "prenv.h" 1.39 +#include "plstr.h" 1.40 +#include "nsError.h" 1.41 +#include "nsAutoPtr.h" 1.42 +#include "nsIScrollableFrame.h" 1.43 +#include <algorithm> 1.44 +#ifdef ACCESSIBILITY 1.45 +#include "nsIDOMHTMLDocument.h" 1.46 +#endif 1.47 +#include "nsLayoutUtils.h" 1.48 +#include "nsDisplayList.h" 1.49 +#include "nsCSSAnonBoxes.h" 1.50 +#include "nsCSSFrameConstructor.h" 1.51 +#include "nsRenderingContext.h" 1.52 +#include "TextOverflow.h" 1.53 +#include "nsIFrameInlines.h" 1.54 + 1.55 +#include "nsBidiPresUtils.h" 1.56 + 1.57 +static const int MIN_LINES_NEEDING_CURSOR = 20; 1.58 + 1.59 +static const char16_t kDiscCharacter = 0x2022; 1.60 +static const char16_t kCircleCharacter = 0x25e6; 1.61 +static const char16_t kSquareCharacter = 0x25aa; 1.62 + 1.63 +#define DISABLE_FLOAT_BREAKING_IN_COLUMNS 1.64 + 1.65 +using namespace mozilla; 1.66 +using namespace mozilla::css; 1.67 +using namespace mozilla::layout; 1.68 + 1.69 +#ifdef DEBUG 1.70 +#include "nsBlockDebugFlags.h" 1.71 + 1.72 +bool nsBlockFrame::gLamePaintMetrics; 1.73 +bool nsBlockFrame::gLameReflowMetrics; 1.74 +bool nsBlockFrame::gNoisy; 1.75 +bool nsBlockFrame::gNoisyDamageRepair; 1.76 +bool nsBlockFrame::gNoisyIntrinsic; 1.77 +bool nsBlockFrame::gNoisyReflow; 1.78 +bool nsBlockFrame::gReallyNoisyReflow; 1.79 +bool nsBlockFrame::gNoisyFloatManager; 1.80 +bool nsBlockFrame::gVerifyLines; 1.81 +bool nsBlockFrame::gDisableResizeOpt; 1.82 + 1.83 +int32_t nsBlockFrame::gNoiseIndent; 1.84 + 1.85 +struct BlockDebugFlags { 1.86 + const char* name; 1.87 + bool* on; 1.88 +}; 1.89 + 1.90 +static const BlockDebugFlags gFlags[] = { 1.91 + { "reflow", &nsBlockFrame::gNoisyReflow }, 1.92 + { "really-noisy-reflow", &nsBlockFrame::gReallyNoisyReflow }, 1.93 + { "intrinsic", &nsBlockFrame::gNoisyIntrinsic }, 1.94 + { "float-manager", &nsBlockFrame::gNoisyFloatManager }, 1.95 + { "verify-lines", &nsBlockFrame::gVerifyLines }, 1.96 + { "damage-repair", &nsBlockFrame::gNoisyDamageRepair }, 1.97 + { "lame-paint-metrics", &nsBlockFrame::gLamePaintMetrics }, 1.98 + { "lame-reflow-metrics", &nsBlockFrame::gLameReflowMetrics }, 1.99 + { "disable-resize-opt", &nsBlockFrame::gDisableResizeOpt }, 1.100 +}; 1.101 +#define NUM_DEBUG_FLAGS (sizeof(gFlags) / sizeof(gFlags[0])) 1.102 + 1.103 +static void 1.104 +ShowDebugFlags() 1.105 +{ 1.106 + printf("Here are the available GECKO_BLOCK_DEBUG_FLAGS:\n"); 1.107 + const BlockDebugFlags* bdf = gFlags; 1.108 + const BlockDebugFlags* end = gFlags + NUM_DEBUG_FLAGS; 1.109 + for (; bdf < end; bdf++) { 1.110 + printf(" %s\n", bdf->name); 1.111 + } 1.112 + printf("Note: GECKO_BLOCK_DEBUG_FLAGS is a comma separated list of flag\n"); 1.113 + printf("names (no whitespace)\n"); 1.114 +} 1.115 + 1.116 +void 1.117 +nsBlockFrame::InitDebugFlags() 1.118 +{ 1.119 + static bool firstTime = true; 1.120 + if (firstTime) { 1.121 + firstTime = false; 1.122 + char* flags = PR_GetEnv("GECKO_BLOCK_DEBUG_FLAGS"); 1.123 + if (flags) { 1.124 + bool error = false; 1.125 + for (;;) { 1.126 + char* cm = PL_strchr(flags, ','); 1.127 + if (cm) *cm = '\0'; 1.128 + 1.129 + bool found = false; 1.130 + const BlockDebugFlags* bdf = gFlags; 1.131 + const BlockDebugFlags* end = gFlags + NUM_DEBUG_FLAGS; 1.132 + for (; bdf < end; bdf++) { 1.133 + if (PL_strcasecmp(bdf->name, flags) == 0) { 1.134 + *(bdf->on) = true; 1.135 + printf("nsBlockFrame: setting %s debug flag on\n", bdf->name); 1.136 + gNoisy = true; 1.137 + found = true; 1.138 + break; 1.139 + } 1.140 + } 1.141 + if (!found) { 1.142 + error = true; 1.143 + } 1.144 + 1.145 + if (!cm) break; 1.146 + *cm = ','; 1.147 + flags = cm + 1; 1.148 + } 1.149 + if (error) { 1.150 + ShowDebugFlags(); 1.151 + } 1.152 + } 1.153 + } 1.154 +} 1.155 + 1.156 +#endif 1.157 + 1.158 +// add in a sanity check for absurdly deep frame trees. See bug 42138 1.159 +// can't just use IsFrameTreeTooDeep() because that method has side effects we don't want 1.160 +#define MAX_DEPTH_FOR_LIST_RENUMBERING 200 // 200 open displayable tags is pretty unrealistic 1.161 + 1.162 +//---------------------------------------------------------------------- 1.163 + 1.164 +// Debugging support code 1.165 + 1.166 +#ifdef DEBUG 1.167 +const char* nsBlockFrame::kReflowCommandType[] = { 1.168 + "ContentChanged", 1.169 + "StyleChanged", 1.170 + "ReflowDirty", 1.171 + "Timeout", 1.172 + "UserDefined", 1.173 +}; 1.174 +#endif 1.175 + 1.176 +#ifdef REALLY_NOISY_FIRST_LINE 1.177 +static void 1.178 +DumpStyleGeneaology(nsIFrame* aFrame, const char* gap) 1.179 +{ 1.180 + fputs(gap, stdout); 1.181 + nsFrame::ListTag(stdout, aFrame); 1.182 + printf(": "); 1.183 + nsStyleContext* sc = aFrame->StyleContext(); 1.184 + while (nullptr != sc) { 1.185 + nsStyleContext* psc; 1.186 + printf("%p ", sc); 1.187 + psc = sc->GetParent(); 1.188 + sc = psc; 1.189 + } 1.190 + printf("\n"); 1.191 +} 1.192 +#endif 1.193 + 1.194 +#ifdef REFLOW_STATUS_COVERAGE 1.195 +static void 1.196 +RecordReflowStatus(bool aChildIsBlock, nsReflowStatus aFrameReflowStatus) 1.197 +{ 1.198 + static uint32_t record[2]; 1.199 + 1.200 + // 0: child-is-block 1.201 + // 1: child-is-inline 1.202 + int index = 0; 1.203 + if (!aChildIsBlock) index |= 1; 1.204 + 1.205 + // Compute new status 1.206 + uint32_t newS = record[index]; 1.207 + if (NS_INLINE_IS_BREAK(aFrameReflowStatus)) { 1.208 + if (NS_INLINE_IS_BREAK_BEFORE(aFrameReflowStatus)) { 1.209 + newS |= 1; 1.210 + } 1.211 + else if (NS_FRAME_IS_NOT_COMPLETE(aFrameReflowStatus)) { 1.212 + newS |= 2; 1.213 + } 1.214 + else { 1.215 + newS |= 4; 1.216 + } 1.217 + } 1.218 + else if (NS_FRAME_IS_NOT_COMPLETE(aFrameReflowStatus)) { 1.219 + newS |= 8; 1.220 + } 1.221 + else { 1.222 + newS |= 16; 1.223 + } 1.224 + 1.225 + // Log updates to the status that yield different values 1.226 + if (record[index] != newS) { 1.227 + record[index] = newS; 1.228 + printf("record(%d): %02x %02x\n", index, record[0], record[1]); 1.229 + } 1.230 +} 1.231 +#endif 1.232 + 1.233 +// Destructor function for the overflowLines frame property 1.234 +static void 1.235 +DestroyOverflowLines(void* aPropertyValue) 1.236 +{ 1.237 + NS_ERROR("Overflow lines should never be destroyed by the FramePropertyTable"); 1.238 +} 1.239 + 1.240 +NS_DECLARE_FRAME_PROPERTY(OverflowLinesProperty, DestroyOverflowLines) 1.241 +NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowOutOfFlowsProperty) 1.242 +NS_DECLARE_FRAME_PROPERTY_FRAMELIST(PushedFloatProperty) 1.243 +NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OutsideBulletProperty) 1.244 +NS_DECLARE_FRAME_PROPERTY(InsideBulletProperty, nullptr) 1.245 +NS_DECLARE_FRAME_PROPERTY(BottomEdgeOfChildrenProperty, nullptr) 1.246 + 1.247 +//---------------------------------------------------------------------- 1.248 + 1.249 +nsIFrame* 1.250 +NS_NewBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, nsFrameState aFlags) 1.251 +{ 1.252 + nsBlockFrame* it = new (aPresShell) nsBlockFrame(aContext); 1.253 + it->SetFlags(aFlags); 1.254 + return it; 1.255 +} 1.256 + 1.257 +NS_IMPL_FRAMEARENA_HELPERS(nsBlockFrame) 1.258 + 1.259 +nsBlockFrame::~nsBlockFrame() 1.260 +{ 1.261 +} 1.262 + 1.263 +void 1.264 +nsBlockFrame::DestroyFrom(nsIFrame* aDestructRoot) 1.265 +{ 1.266 + ClearLineCursor(); 1.267 + DestroyAbsoluteFrames(aDestructRoot); 1.268 + mFloats.DestroyFramesFrom(aDestructRoot); 1.269 + nsPresContext* presContext = PresContext(); 1.270 + nsIPresShell* shell = presContext->PresShell(); 1.271 + nsLineBox::DeleteLineList(presContext, mLines, aDestructRoot, 1.272 + &mFrames); 1.273 + 1.274 + FramePropertyTable* props = presContext->PropertyTable(); 1.275 + 1.276 + if (HasPushedFloats()) { 1.277 + SafelyDestroyFrameListProp(aDestructRoot, shell, props, 1.278 + PushedFloatProperty()); 1.279 + RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS); 1.280 + } 1.281 + 1.282 + // destroy overflow lines now 1.283 + FrameLines* overflowLines = RemoveOverflowLines(); 1.284 + if (overflowLines) { 1.285 + nsLineBox::DeleteLineList(presContext, overflowLines->mLines, 1.286 + aDestructRoot, &overflowLines->mFrames); 1.287 + delete overflowLines; 1.288 + } 1.289 + 1.290 + if (GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) { 1.291 + SafelyDestroyFrameListProp(aDestructRoot, shell, props, 1.292 + OverflowOutOfFlowsProperty()); 1.293 + RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS); 1.294 + } 1.295 + 1.296 + if (HasOutsideBullet()) { 1.297 + SafelyDestroyFrameListProp(aDestructRoot, shell, props, 1.298 + OutsideBulletProperty()); 1.299 + RemoveStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET); 1.300 + } 1.301 + 1.302 + nsBlockFrameSuper::DestroyFrom(aDestructRoot); 1.303 +} 1.304 + 1.305 +/* virtual */ nsILineIterator* 1.306 +nsBlockFrame::GetLineIterator() 1.307 +{ 1.308 + nsLineIterator* it = new nsLineIterator; 1.309 + if (!it) 1.310 + return nullptr; 1.311 + 1.312 + const nsStyleVisibility* visibility = StyleVisibility(); 1.313 + nsresult rv = it->Init(mLines, visibility->mDirection == NS_STYLE_DIRECTION_RTL); 1.314 + if (NS_FAILED(rv)) { 1.315 + delete it; 1.316 + return nullptr; 1.317 + } 1.318 + return it; 1.319 +} 1.320 + 1.321 +NS_QUERYFRAME_HEAD(nsBlockFrame) 1.322 + NS_QUERYFRAME_ENTRY(nsBlockFrame) 1.323 +NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrameSuper) 1.324 + 1.325 +nsSplittableType 1.326 +nsBlockFrame::GetSplittableType() const 1.327 +{ 1.328 + return NS_FRAME_SPLITTABLE_NON_RECTANGULAR; 1.329 +} 1.330 + 1.331 +#ifdef DEBUG_FRAME_DUMP 1.332 +void 1.333 +nsBlockFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const 1.334 +{ 1.335 + nsCString str; 1.336 + ListGeneric(str, aPrefix, aFlags); 1.337 + 1.338 + fprintf_stderr(out, "%s<\n", str.get()); 1.339 + 1.340 + nsCString pfx(aPrefix); 1.341 + pfx += " "; 1.342 + 1.343 + // Output the lines 1.344 + if (!mLines.empty()) { 1.345 + const_line_iterator line = begin_lines(), line_end = end_lines(); 1.346 + for ( ; line != line_end; ++line) { 1.347 + line->List(out, pfx.get(), aFlags); 1.348 + } 1.349 + } 1.350 + 1.351 + // Output the overflow lines. 1.352 + const FrameLines* overflowLines = GetOverflowLines(); 1.353 + if (overflowLines && !overflowLines->mLines.empty()) { 1.354 + fprintf_stderr(out, "%sOverflow-lines %p/%p <\n", pfx.get(), overflowLines, &overflowLines->mFrames); 1.355 + nsCString nestedPfx(pfx); 1.356 + nestedPfx += " "; 1.357 + const_line_iterator line = overflowLines->mLines.begin(), 1.358 + line_end = overflowLines->mLines.end(); 1.359 + for ( ; line != line_end; ++line) { 1.360 + line->List(out, nestedPfx.get(), aFlags); 1.361 + } 1.362 + fprintf_stderr(out, "%s>\n", pfx.get()); 1.363 + } 1.364 + 1.365 + // skip the principal list - we printed the lines above 1.366 + // skip the overflow list - we printed the overflow lines above 1.367 + ChildListIterator lists(this); 1.368 + ChildListIDs skip(kPrincipalList | kOverflowList); 1.369 + for (; !lists.IsDone(); lists.Next()) { 1.370 + if (skip.Contains(lists.CurrentID())) { 1.371 + continue; 1.372 + } 1.373 + fprintf_stderr(out, "%s%s %p <\n", pfx.get(), 1.374 + mozilla::layout::ChildListName(lists.CurrentID()), 1.375 + &GetChildList(lists.CurrentID())); 1.376 + nsCString nestedPfx(pfx); 1.377 + nestedPfx += " "; 1.378 + nsFrameList::Enumerator childFrames(lists.CurrentList()); 1.379 + for (; !childFrames.AtEnd(); childFrames.Next()) { 1.380 + nsIFrame* kid = childFrames.get(); 1.381 + kid->List(out, nestedPfx.get(), aFlags); 1.382 + } 1.383 + fprintf_stderr(out, "%s>\n", pfx.get()); 1.384 + } 1.385 + 1.386 + fprintf_stderr(out, "%s>\n", aPrefix); 1.387 +} 1.388 + 1.389 +nsresult 1.390 +nsBlockFrame::GetFrameName(nsAString& aResult) const 1.391 +{ 1.392 + return MakeFrameName(NS_LITERAL_STRING("Block"), aResult); 1.393 +} 1.394 +#endif 1.395 + 1.396 +#ifdef DEBUG 1.397 +nsFrameState 1.398 +nsBlockFrame::GetDebugStateBits() const 1.399 +{ 1.400 + // We don't want to include our cursor flag in the bits the 1.401 + // regression tester looks at 1.402 + return nsBlockFrameSuper::GetDebugStateBits() & ~NS_BLOCK_HAS_LINE_CURSOR; 1.403 +} 1.404 +#endif 1.405 + 1.406 +nsIAtom* 1.407 +nsBlockFrame::GetType() const 1.408 +{ 1.409 + return nsGkAtoms::blockFrame; 1.410 +} 1.411 + 1.412 +void 1.413 +nsBlockFrame::InvalidateFrame(uint32_t aDisplayItemKey) 1.414 +{ 1.415 + if (IsSVGText()) { 1.416 + NS_ASSERTION(GetParent()->GetType() == nsGkAtoms::svgTextFrame, 1.417 + "unexpected block frame in SVG text"); 1.418 + GetParent()->InvalidateFrame(); 1.419 + return; 1.420 + } 1.421 + nsBlockFrameSuper::InvalidateFrame(aDisplayItemKey); 1.422 +} 1.423 + 1.424 +void 1.425 +nsBlockFrame::InvalidateFrameWithRect(const nsRect& aRect, uint32_t aDisplayItemKey) 1.426 +{ 1.427 + if (IsSVGText()) { 1.428 + NS_ASSERTION(GetParent()->GetType() == nsGkAtoms::svgTextFrame, 1.429 + "unexpected block frame in SVG text"); 1.430 + GetParent()->InvalidateFrame(); 1.431 + return; 1.432 + } 1.433 + nsBlockFrameSuper::InvalidateFrameWithRect(aRect, aDisplayItemKey); 1.434 +} 1.435 + 1.436 +nscoord 1.437 +nsBlockFrame::GetBaseline() const 1.438 +{ 1.439 + nscoord result; 1.440 + if (nsLayoutUtils::GetLastLineBaseline(this, &result)) 1.441 + return result; 1.442 + return nsFrame::GetBaseline(); 1.443 +} 1.444 + 1.445 +nscoord 1.446 +nsBlockFrame::GetCaretBaseline() const 1.447 +{ 1.448 + nsRect contentRect = GetContentRect(); 1.449 + nsMargin bp = GetUsedBorderAndPadding(); 1.450 + 1.451 + if (!mLines.empty()) { 1.452 + const_line_iterator line = begin_lines(); 1.453 + const nsLineBox* firstLine = line; 1.454 + if (firstLine->GetChildCount()) { 1.455 + return bp.top + firstLine->mFirstChild->GetCaretBaseline(); 1.456 + } 1.457 + } 1.458 + nsRefPtr<nsFontMetrics> fm; 1.459 + float inflation = nsLayoutUtils::FontSizeInflationFor(this); 1.460 + nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm), inflation); 1.461 + nscoord lineHeight = 1.462 + nsHTMLReflowState::CalcLineHeight(GetContent(), StyleContext(), 1.463 + contentRect.height, inflation); 1.464 + return nsLayoutUtils::GetCenteredFontBaseline(fm, lineHeight) + bp.top; 1.465 +} 1.466 + 1.467 +///////////////////////////////////////////////////////////////////////////// 1.468 +// Child frame enumeration 1.469 + 1.470 +const nsFrameList& 1.471 +nsBlockFrame::GetChildList(ChildListID aListID) const 1.472 +{ 1.473 + switch (aListID) { 1.474 + case kPrincipalList: 1.475 + return mFrames; 1.476 + case kOverflowList: { 1.477 + FrameLines* overflowLines = GetOverflowLines(); 1.478 + return overflowLines ? overflowLines->mFrames : nsFrameList::EmptyList(); 1.479 + } 1.480 + case kFloatList: 1.481 + return mFloats; 1.482 + case kOverflowOutOfFlowList: { 1.483 + const nsFrameList* list = GetOverflowOutOfFlows(); 1.484 + return list ? *list : nsFrameList::EmptyList(); 1.485 + } 1.486 + case kPushedFloatsList: { 1.487 + const nsFrameList* list = GetPushedFloats(); 1.488 + return list ? *list : nsFrameList::EmptyList(); 1.489 + } 1.490 + case kBulletList: { 1.491 + const nsFrameList* list = GetOutsideBulletList(); 1.492 + return list ? *list : nsFrameList::EmptyList(); 1.493 + } 1.494 + default: 1.495 + return nsContainerFrame::GetChildList(aListID); 1.496 + } 1.497 +} 1.498 + 1.499 +void 1.500 +nsBlockFrame::GetChildLists(nsTArray<ChildList>* aLists) const 1.501 +{ 1.502 + nsContainerFrame::GetChildLists(aLists); 1.503 + FrameLines* overflowLines = GetOverflowLines(); 1.504 + if (overflowLines) { 1.505 + overflowLines->mFrames.AppendIfNonempty(aLists, kOverflowList); 1.506 + } 1.507 + const nsFrameList* list = GetOverflowOutOfFlows(); 1.508 + if (list) { 1.509 + list->AppendIfNonempty(aLists, kOverflowOutOfFlowList); 1.510 + } 1.511 + mFloats.AppendIfNonempty(aLists, kFloatList); 1.512 + list = GetOutsideBulletList(); 1.513 + if (list) { 1.514 + list->AppendIfNonempty(aLists, kBulletList); 1.515 + } 1.516 + list = GetPushedFloats(); 1.517 + if (list) { 1.518 + list->AppendIfNonempty(aLists, kPushedFloatsList); 1.519 + } 1.520 +} 1.521 + 1.522 +/* virtual */ bool 1.523 +nsBlockFrame::IsFloatContainingBlock() const 1.524 +{ 1.525 + return true; 1.526 +} 1.527 + 1.528 +static void 1.529 +ReparentFrame(nsIFrame* aFrame, nsIFrame* aOldParent, nsIFrame* aNewParent) 1.530 +{ 1.531 + NS_ASSERTION(aOldParent == aFrame->GetParent(), 1.532 + "Parent not consistent with expectations"); 1.533 + 1.534 + aFrame->SetParent(aNewParent); 1.535 + 1.536 + // When pushing and pulling frames we need to check for whether any 1.537 + // views need to be reparented 1.538 + nsContainerFrame::ReparentFrameView(aFrame, aOldParent, aNewParent); 1.539 +} 1.540 + 1.541 +static void 1.542 +ReparentFrames(nsFrameList& aFrameList, nsIFrame* aOldParent, 1.543 + nsIFrame* aNewParent) 1.544 +{ 1.545 + for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) { 1.546 + ReparentFrame(e.get(), aOldParent, aNewParent); 1.547 + } 1.548 +} 1.549 + 1.550 +/** 1.551 + * Remove the first line from aFromLines and adjust the associated frame list 1.552 + * aFromFrames accordingly. The removed line is assigned to *aOutLine and 1.553 + * a frame list with its frames is assigned to *aOutFrames, i.e. the frames 1.554 + * that were extracted from the head of aFromFrames. 1.555 + * aFromLines must contain at least one line, the line may be empty. 1.556 + * @return true if aFromLines becomes empty 1.557 + */ 1.558 +static bool 1.559 +RemoveFirstLine(nsLineList& aFromLines, nsFrameList& aFromFrames, 1.560 + nsLineBox** aOutLine, nsFrameList* aOutFrames) 1.561 +{ 1.562 + nsLineList_iterator removedLine = aFromLines.begin(); 1.563 + *aOutLine = removedLine; 1.564 + nsLineList_iterator next = aFromLines.erase(removedLine); 1.565 + bool isLastLine = next == aFromLines.end(); 1.566 + nsIFrame* lastFrame = isLastLine ? aFromFrames.LastChild() 1.567 + : next->mFirstChild->GetPrevSibling(); 1.568 + nsFrameList::FrameLinkEnumerator linkToBreak(aFromFrames, lastFrame); 1.569 + *aOutFrames = aFromFrames.ExtractHead(linkToBreak); 1.570 + return isLastLine; 1.571 +} 1.572 + 1.573 +////////////////////////////////////////////////////////////////////// 1.574 +// Reflow methods 1.575 + 1.576 +/* virtual */ void 1.577 +nsBlockFrame::MarkIntrinsicWidthsDirty() 1.578 +{ 1.579 + nsBlockFrame* dirtyBlock = static_cast<nsBlockFrame*>(FirstContinuation()); 1.580 + dirtyBlock->mMinWidth = NS_INTRINSIC_WIDTH_UNKNOWN; 1.581 + dirtyBlock->mPrefWidth = NS_INTRINSIC_WIDTH_UNKNOWN; 1.582 + if (!(GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION)) { 1.583 + for (nsIFrame* frame = dirtyBlock; frame; 1.584 + frame = frame->GetNextContinuation()) { 1.585 + frame->AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION); 1.586 + } 1.587 + } 1.588 + 1.589 + nsBlockFrameSuper::MarkIntrinsicWidthsDirty(); 1.590 +} 1.591 + 1.592 +void 1.593 +nsBlockFrame::CheckIntrinsicCacheAgainstShrinkWrapState() 1.594 +{ 1.595 + nsPresContext *presContext = PresContext(); 1.596 + if (!nsLayoutUtils::FontSizeInflationEnabled(presContext)) { 1.597 + return; 1.598 + } 1.599 + bool inflationEnabled = 1.600 + !presContext->mInflationDisabledForShrinkWrap; 1.601 + if (inflationEnabled != 1.602 + !!(GetStateBits() & NS_BLOCK_FRAME_INTRINSICS_INFLATED)) { 1.603 + mMinWidth = NS_INTRINSIC_WIDTH_UNKNOWN; 1.604 + mPrefWidth = NS_INTRINSIC_WIDTH_UNKNOWN; 1.605 + if (inflationEnabled) { 1.606 + AddStateBits(NS_BLOCK_FRAME_INTRINSICS_INFLATED); 1.607 + } else { 1.608 + RemoveStateBits(NS_BLOCK_FRAME_INTRINSICS_INFLATED); 1.609 + } 1.610 + } 1.611 +} 1.612 + 1.613 +/* virtual */ nscoord 1.614 +nsBlockFrame::GetMinWidth(nsRenderingContext *aRenderingContext) 1.615 +{ 1.616 + nsIFrame* firstInFlow = FirstContinuation(); 1.617 + if (firstInFlow != this) 1.618 + return firstInFlow->GetMinWidth(aRenderingContext); 1.619 + 1.620 + DISPLAY_MIN_WIDTH(this, mMinWidth); 1.621 + 1.622 + CheckIntrinsicCacheAgainstShrinkWrapState(); 1.623 + 1.624 + if (mMinWidth != NS_INTRINSIC_WIDTH_UNKNOWN) 1.625 + return mMinWidth; 1.626 + 1.627 +#ifdef DEBUG 1.628 + if (gNoisyIntrinsic) { 1.629 + IndentBy(stdout, gNoiseIndent); 1.630 + ListTag(stdout); 1.631 + printf(": GetMinWidth\n"); 1.632 + } 1.633 + AutoNoisyIndenter indenter(gNoisyIntrinsic); 1.634 +#endif 1.635 + 1.636 + for (nsBlockFrame* curFrame = this; curFrame; 1.637 + curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) { 1.638 + curFrame->LazyMarkLinesDirty(); 1.639 + } 1.640 + 1.641 + if (GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION) 1.642 + ResolveBidi(); 1.643 + InlineMinWidthData data; 1.644 + for (nsBlockFrame* curFrame = this; curFrame; 1.645 + curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) { 1.646 + for (line_iterator line = curFrame->begin_lines(), line_end = curFrame->end_lines(); 1.647 + line != line_end; ++line) 1.648 + { 1.649 +#ifdef DEBUG 1.650 + if (gNoisyIntrinsic) { 1.651 + IndentBy(stdout, gNoiseIndent); 1.652 + printf("line (%s%s)\n", 1.653 + line->IsBlock() ? "block" : "inline", 1.654 + line->IsEmpty() ? ", empty" : ""); 1.655 + } 1.656 + AutoNoisyIndenter lineindent(gNoisyIntrinsic); 1.657 +#endif 1.658 + if (line->IsBlock()) { 1.659 + data.ForceBreak(aRenderingContext); 1.660 + data.currentLine = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, 1.661 + line->mFirstChild, nsLayoutUtils::MIN_WIDTH); 1.662 + data.ForceBreak(aRenderingContext); 1.663 + } else { 1.664 + if (!curFrame->GetPrevContinuation() && 1.665 + line == curFrame->begin_lines()) { 1.666 + // Only add text-indent if it has no percentages; using a 1.667 + // percentage basis of 0 unconditionally would give strange 1.668 + // behavior for calc(10%-3px). 1.669 + const nsStyleCoord &indent = StyleText()->mTextIndent; 1.670 + if (indent.ConvertsToLength()) 1.671 + data.currentLine += nsRuleNode::ComputeCoordPercentCalc(indent, 0); 1.672 + } 1.673 + // XXX Bug NNNNNN Should probably handle percentage text-indent. 1.674 + 1.675 + data.line = &line; 1.676 + data.lineContainer = curFrame; 1.677 + nsIFrame *kid = line->mFirstChild; 1.678 + for (int32_t i = 0, i_end = line->GetChildCount(); i != i_end; 1.679 + ++i, kid = kid->GetNextSibling()) { 1.680 + kid->AddInlineMinWidth(aRenderingContext, &data); 1.681 + } 1.682 + } 1.683 +#ifdef DEBUG 1.684 + if (gNoisyIntrinsic) { 1.685 + IndentBy(stdout, gNoiseIndent); 1.686 + printf("min: [prevLines=%d currentLine=%d]\n", 1.687 + data.prevLines, data.currentLine); 1.688 + } 1.689 +#endif 1.690 + } 1.691 + } 1.692 + data.ForceBreak(aRenderingContext); 1.693 + 1.694 + mMinWidth = data.prevLines; 1.695 + return mMinWidth; 1.696 +} 1.697 + 1.698 +/* virtual */ nscoord 1.699 +nsBlockFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) 1.700 +{ 1.701 + nsIFrame* firstInFlow = FirstContinuation(); 1.702 + if (firstInFlow != this) 1.703 + return firstInFlow->GetPrefWidth(aRenderingContext); 1.704 + 1.705 + DISPLAY_PREF_WIDTH(this, mPrefWidth); 1.706 + 1.707 + CheckIntrinsicCacheAgainstShrinkWrapState(); 1.708 + 1.709 + if (mPrefWidth != NS_INTRINSIC_WIDTH_UNKNOWN) 1.710 + return mPrefWidth; 1.711 + 1.712 +#ifdef DEBUG 1.713 + if (gNoisyIntrinsic) { 1.714 + IndentBy(stdout, gNoiseIndent); 1.715 + ListTag(stdout); 1.716 + printf(": GetPrefWidth\n"); 1.717 + } 1.718 + AutoNoisyIndenter indenter(gNoisyIntrinsic); 1.719 +#endif 1.720 + 1.721 + for (nsBlockFrame* curFrame = this; curFrame; 1.722 + curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) { 1.723 + curFrame->LazyMarkLinesDirty(); 1.724 + } 1.725 + 1.726 + if (GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION) 1.727 + ResolveBidi(); 1.728 + InlinePrefWidthData data; 1.729 + for (nsBlockFrame* curFrame = this; curFrame; 1.730 + curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) { 1.731 + for (line_iterator line = curFrame->begin_lines(), line_end = curFrame->end_lines(); 1.732 + line != line_end; ++line) 1.733 + { 1.734 +#ifdef DEBUG 1.735 + if (gNoisyIntrinsic) { 1.736 + IndentBy(stdout, gNoiseIndent); 1.737 + printf("line (%s%s)\n", 1.738 + line->IsBlock() ? "block" : "inline", 1.739 + line->IsEmpty() ? ", empty" : ""); 1.740 + } 1.741 + AutoNoisyIndenter lineindent(gNoisyIntrinsic); 1.742 +#endif 1.743 + if (line->IsBlock()) { 1.744 + data.ForceBreak(aRenderingContext); 1.745 + data.currentLine = nsLayoutUtils::IntrinsicForContainer(aRenderingContext, 1.746 + line->mFirstChild, nsLayoutUtils::PREF_WIDTH); 1.747 + data.ForceBreak(aRenderingContext); 1.748 + } else { 1.749 + if (!curFrame->GetPrevContinuation() && 1.750 + line == curFrame->begin_lines()) { 1.751 + // Only add text-indent if it has no percentages; using a 1.752 + // percentage basis of 0 unconditionally would give strange 1.753 + // behavior for calc(10%-3px). 1.754 + const nsStyleCoord &indent = StyleText()->mTextIndent; 1.755 + if (indent.ConvertsToLength()) 1.756 + data.currentLine += nsRuleNode::ComputeCoordPercentCalc(indent, 0); 1.757 + } 1.758 + // XXX Bug NNNNNN Should probably handle percentage text-indent. 1.759 + 1.760 + data.line = &line; 1.761 + data.lineContainer = curFrame; 1.762 + nsIFrame *kid = line->mFirstChild; 1.763 + for (int32_t i = 0, i_end = line->GetChildCount(); i != i_end; 1.764 + ++i, kid = kid->GetNextSibling()) { 1.765 + kid->AddInlinePrefWidth(aRenderingContext, &data); 1.766 + } 1.767 + } 1.768 +#ifdef DEBUG 1.769 + if (gNoisyIntrinsic) { 1.770 + IndentBy(stdout, gNoiseIndent); 1.771 + printf("pref: [prevLines=%d currentLine=%d]\n", 1.772 + data.prevLines, data.currentLine); 1.773 + } 1.774 +#endif 1.775 + } 1.776 + } 1.777 + data.ForceBreak(aRenderingContext); 1.778 + 1.779 + mPrefWidth = data.prevLines; 1.780 + return mPrefWidth; 1.781 +} 1.782 + 1.783 +nsRect 1.784 +nsBlockFrame::ComputeTightBounds(gfxContext* aContext) const 1.785 +{ 1.786 + // be conservative 1.787 + if (StyleContext()->HasTextDecorationLines()) { 1.788 + return GetVisualOverflowRect(); 1.789 + } 1.790 + return ComputeSimpleTightBounds(aContext); 1.791 +} 1.792 + 1.793 +/* virtual */ nsresult 1.794 +nsBlockFrame::GetPrefWidthTightBounds(nsRenderingContext* aRenderingContext, 1.795 + nscoord* aX, 1.796 + nscoord* aXMost) 1.797 +{ 1.798 + nsIFrame* firstInFlow = FirstContinuation(); 1.799 + if (firstInFlow != this) { 1.800 + return firstInFlow->GetPrefWidthTightBounds(aRenderingContext, aX, aXMost); 1.801 + } 1.802 + 1.803 + *aX = 0; 1.804 + *aXMost = 0; 1.805 + 1.806 + nsresult rv; 1.807 + InlinePrefWidthData data; 1.808 + for (nsBlockFrame* curFrame = this; curFrame; 1.809 + curFrame = static_cast<nsBlockFrame*>(curFrame->GetNextContinuation())) { 1.810 + for (line_iterator line = curFrame->begin_lines(), line_end = curFrame->end_lines(); 1.811 + line != line_end; ++line) 1.812 + { 1.813 + nscoord childX, childXMost; 1.814 + if (line->IsBlock()) { 1.815 + data.ForceBreak(aRenderingContext); 1.816 + rv = line->mFirstChild->GetPrefWidthTightBounds(aRenderingContext, 1.817 + &childX, &childXMost); 1.818 + NS_ENSURE_SUCCESS(rv, rv); 1.819 + *aX = std::min(*aX, childX); 1.820 + *aXMost = std::max(*aXMost, childXMost); 1.821 + } else { 1.822 + if (!curFrame->GetPrevContinuation() && 1.823 + line == curFrame->begin_lines()) { 1.824 + // Only add text-indent if it has no percentages; using a 1.825 + // percentage basis of 0 unconditionally would give strange 1.826 + // behavior for calc(10%-3px). 1.827 + const nsStyleCoord &indent = StyleText()->mTextIndent; 1.828 + if (indent.ConvertsToLength()) { 1.829 + data.currentLine += nsRuleNode::ComputeCoordPercentCalc(indent, 0); 1.830 + } 1.831 + } 1.832 + // XXX Bug NNNNNN Should probably handle percentage text-indent. 1.833 + 1.834 + data.line = &line; 1.835 + data.lineContainer = curFrame; 1.836 + nsIFrame *kid = line->mFirstChild; 1.837 + for (int32_t i = 0, i_end = line->GetChildCount(); i != i_end; 1.838 + ++i, kid = kid->GetNextSibling()) { 1.839 + rv = kid->GetPrefWidthTightBounds(aRenderingContext, &childX, 1.840 + &childXMost); 1.841 + NS_ENSURE_SUCCESS(rv, rv); 1.842 + *aX = std::min(*aX, data.currentLine + childX); 1.843 + *aXMost = std::max(*aXMost, data.currentLine + childXMost); 1.844 + kid->AddInlinePrefWidth(aRenderingContext, &data); 1.845 + } 1.846 + } 1.847 + } 1.848 + } 1.849 + data.ForceBreak(aRenderingContext); 1.850 + 1.851 + return NS_OK; 1.852 +} 1.853 + 1.854 +static bool 1.855 +AvailableSpaceShrunk(const nsRect& aOldAvailableSpace, 1.856 + const nsRect& aNewAvailableSpace) 1.857 +{ 1.858 + if (aNewAvailableSpace.width == 0) { 1.859 + // Positions are not significant if the width is zero. 1.860 + return aOldAvailableSpace.width != 0; 1.861 + } 1.862 + NS_ASSERTION(aOldAvailableSpace.x <= aNewAvailableSpace.x && 1.863 + aOldAvailableSpace.XMost() >= aNewAvailableSpace.XMost(), 1.864 + "available space should never grow"); 1.865 + return aOldAvailableSpace.width != aNewAvailableSpace.width; 1.866 +} 1.867 + 1.868 +static nsSize 1.869 +CalculateContainingBlockSizeForAbsolutes(const nsHTMLReflowState& aReflowState, 1.870 + nsSize aFrameSize) 1.871 +{ 1.872 + // The issue here is that for a 'height' of 'auto' the reflow state 1.873 + // code won't know how to calculate the containing block height 1.874 + // because it's calculated bottom up. So we use our own computed 1.875 + // size as the dimensions. 1.876 + nsIFrame* frame = aReflowState.frame; 1.877 + 1.878 + nsSize cbSize(aFrameSize); 1.879 + // Containing block is relative to the padding edge 1.880 + const nsMargin& border = 1.881 + aReflowState.ComputedPhysicalBorderPadding() - aReflowState.ComputedPhysicalPadding(); 1.882 + cbSize.width -= border.LeftRight(); 1.883 + cbSize.height -= border.TopBottom(); 1.884 + 1.885 + if (frame->GetParent()->GetContent() == frame->GetContent() && 1.886 + frame->GetParent()->GetType() != nsGkAtoms::canvasFrame) { 1.887 + // We are a wrapped frame for the content (and the wrapper is not the 1.888 + // canvas frame, whose size is not meaningful here). 1.889 + // Use the container's dimensions, if they have been precomputed. 1.890 + // XXX This is a hack! We really should be waiting until the outermost 1.891 + // frame is fully reflowed and using the resulting dimensions, even 1.892 + // if they're intrinsic. 1.893 + // In fact we should be attaching absolute children to the outermost 1.894 + // frame and not always sticking them in block frames. 1.895 + 1.896 + // First, find the reflow state for the outermost frame for this 1.897 + // content, except for fieldsets where the inner anonymous frame has 1.898 + // the correct padding area with the legend taken into account. 1.899 + const nsHTMLReflowState* aLastRS = &aReflowState; 1.900 + const nsHTMLReflowState* lastButOneRS = &aReflowState; 1.901 + while (aLastRS->parentReflowState && 1.902 + aLastRS->parentReflowState->frame->GetContent() == frame->GetContent() && 1.903 + aLastRS->parentReflowState->frame->GetType() != nsGkAtoms::fieldSetFrame) { 1.904 + lastButOneRS = aLastRS; 1.905 + aLastRS = aLastRS->parentReflowState; 1.906 + } 1.907 + if (aLastRS != &aReflowState) { 1.908 + // Scrollbars need to be specifically excluded, if present, because they are outside the 1.909 + // padding-edge. We need better APIs for getting the various boxes from a frame. 1.910 + nsIScrollableFrame* scrollFrame = do_QueryFrame(aLastRS->frame); 1.911 + nsMargin scrollbars(0,0,0,0); 1.912 + if (scrollFrame) { 1.913 + scrollbars = 1.914 + scrollFrame->GetDesiredScrollbarSizes(aLastRS->frame->PresContext(), 1.915 + aLastRS->rendContext); 1.916 + if (!lastButOneRS->mFlags.mAssumingHScrollbar) { 1.917 + scrollbars.top = scrollbars.bottom = 0; 1.918 + } 1.919 + if (!lastButOneRS->mFlags.mAssumingVScrollbar) { 1.920 + scrollbars.left = scrollbars.right = 0; 1.921 + } 1.922 + } 1.923 + // We found a reflow state for the outermost wrapping frame, so use 1.924 + // its computed metrics if available 1.925 + if (aLastRS->ComputedWidth() != NS_UNCONSTRAINEDSIZE) { 1.926 + cbSize.width = std::max(0, 1.927 + aLastRS->ComputedWidth() + aLastRS->ComputedPhysicalPadding().LeftRight() - scrollbars.LeftRight()); 1.928 + } 1.929 + if (aLastRS->ComputedHeight() != NS_UNCONSTRAINEDSIZE) { 1.930 + cbSize.height = std::max(0, 1.931 + aLastRS->ComputedHeight() + aLastRS->ComputedPhysicalPadding().TopBottom() - scrollbars.TopBottom()); 1.932 + } 1.933 + } 1.934 + } 1.935 + 1.936 + return cbSize; 1.937 +} 1.938 + 1.939 +nsresult 1.940 +nsBlockFrame::Reflow(nsPresContext* aPresContext, 1.941 + nsHTMLReflowMetrics& aMetrics, 1.942 + const nsHTMLReflowState& aReflowState, 1.943 + nsReflowStatus& aStatus) 1.944 +{ 1.945 + DO_GLOBAL_REFLOW_COUNT("nsBlockFrame"); 1.946 + DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus); 1.947 +#ifdef DEBUG 1.948 + if (gNoisyReflow) { 1.949 + IndentBy(stdout, gNoiseIndent); 1.950 + ListTag(stdout); 1.951 + printf(": begin reflow availSize=%d,%d computedSize=%d,%d\n", 1.952 + aReflowState.AvailableWidth(), aReflowState.AvailableHeight(), 1.953 + aReflowState.ComputedWidth(), aReflowState.ComputedHeight()); 1.954 + } 1.955 + AutoNoisyIndenter indent(gNoisy); 1.956 + PRTime start = 0; // Initialize these variablies to silence the compiler. 1.957 + int32_t ctc = 0; // We only use these if they are set (gLameReflowMetrics). 1.958 + if (gLameReflowMetrics) { 1.959 + start = PR_Now(); 1.960 + ctc = nsLineBox::GetCtorCount(); 1.961 + } 1.962 +#endif 1.963 + 1.964 + const nsHTMLReflowState *reflowState = &aReflowState; 1.965 + nscoord consumedHeight = GetConsumedHeight(); 1.966 + nscoord effectiveComputedHeight = GetEffectiveComputedHeight(aReflowState, 1.967 + consumedHeight); 1.968 + Maybe<nsHTMLReflowState> mutableReflowState; 1.969 + // If we have non-auto height, we're clipping our kids and we fit, 1.970 + // make sure our kids fit too. 1.971 + if (aReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE && 1.972 + aReflowState.ComputedHeight() != NS_AUTOHEIGHT && 1.973 + ShouldApplyOverflowClipping(this, aReflowState.mStyleDisplay)) { 1.974 + LogicalMargin blockDirExtras = aReflowState.ComputedLogicalBorderPadding(); 1.975 + WritingMode wm = aReflowState.GetWritingMode(); 1.976 + if (GetLogicalSkipSides() & (LOGICAL_SIDE_B_START)) { 1.977 + blockDirExtras.BStart(wm) = 0; 1.978 + } else { 1.979 + // Bottom margin never causes us to create continuations, so we 1.980 + // don't need to worry about whether it fits in its entirety. 1.981 + blockDirExtras.BStart(wm) += 1.982 + aReflowState.ComputedLogicalMargin().BStart(wm); 1.983 + } 1.984 + 1.985 + if (effectiveComputedHeight + blockDirExtras.BStartEnd(wm) <= 1.986 + aReflowState.AvailableBSize()) { 1.987 + mutableReflowState.construct(aReflowState); 1.988 + mutableReflowState.ref().AvailableBSize() = NS_UNCONSTRAINEDSIZE; 1.989 + reflowState = mutableReflowState.addr(); 1.990 + } 1.991 + } 1.992 + 1.993 + // See comment below about oldSize. Use *only* for the 1.994 + // abs-pos-containing-block-size-change optimization! 1.995 + nsSize oldSize = GetSize(); 1.996 + 1.997 + // Should we create a float manager? 1.998 + nsAutoFloatManager autoFloatManager(const_cast<nsHTMLReflowState&>(*reflowState)); 1.999 + 1.1000 + // XXXldb If we start storing the float manager in the frame rather 1.1001 + // than keeping it around only during reflow then we should create it 1.1002 + // only when there are actually floats to manage. Otherwise things 1.1003 + // like tables will gain significant bloat. 1.1004 + bool needFloatManager = nsBlockFrame::BlockNeedsFloatManager(this); 1.1005 + if (needFloatManager) 1.1006 + autoFloatManager.CreateFloatManager(aPresContext); 1.1007 + 1.1008 + // OK, some lines may be reflowed. Blow away any saved line cursor 1.1009 + // because we may invalidate the nondecreasing 1.1010 + // overflowArea.VisualOverflow().y/yMost invariant, and we may even 1.1011 + // delete the line with the line cursor. 1.1012 + ClearLineCursor(); 1.1013 + 1.1014 + if (IsFrameTreeTooDeep(*reflowState, aMetrics, aStatus)) { 1.1015 + return NS_OK; 1.1016 + } 1.1017 + 1.1018 + bool topMarginRoot, bottomMarginRoot; 1.1019 + IsMarginRoot(&topMarginRoot, &bottomMarginRoot); 1.1020 + 1.1021 + // Cache the consumed height in the block reflow state so that we don't have 1.1022 + // to continually recompute it. 1.1023 + nsBlockReflowState state(*reflowState, aPresContext, this, 1.1024 + topMarginRoot, bottomMarginRoot, needFloatManager, 1.1025 + consumedHeight); 1.1026 + 1.1027 + if (GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION) 1.1028 + static_cast<nsBlockFrame*>(FirstContinuation())->ResolveBidi(); 1.1029 + 1.1030 + if (RenumberLists(aPresContext)) { 1.1031 + AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN); 1.1032 + } 1.1033 + 1.1034 + nsresult rv = NS_OK; 1.1035 + 1.1036 + // ALWAYS drain overflow. We never want to leave the previnflow's 1.1037 + // overflow lines hanging around; block reflow depends on the 1.1038 + // overflow line lists being cleared out between reflow passes. 1.1039 + DrainOverflowLines(); 1.1040 + 1.1041 + // Handle paginated overflow (see nsContainerFrame.h) 1.1042 + nsOverflowAreas ocBounds; 1.1043 + nsReflowStatus ocStatus = NS_FRAME_COMPLETE; 1.1044 + if (GetPrevInFlow()) { 1.1045 + ReflowOverflowContainerChildren(aPresContext, *reflowState, ocBounds, 0, 1.1046 + ocStatus); 1.1047 + } 1.1048 + 1.1049 + // Now that we're done cleaning up our overflow container lists, we can 1.1050 + // give |state| its nsOverflowContinuationTracker. 1.1051 + nsOverflowContinuationTracker tracker(this, false); 1.1052 + state.mOverflowTracker = &tracker; 1.1053 + 1.1054 + // Drain & handle pushed floats 1.1055 + DrainPushedFloats(state); 1.1056 + nsOverflowAreas fcBounds; 1.1057 + nsReflowStatus fcStatus = NS_FRAME_COMPLETE; 1.1058 + ReflowPushedFloats(state, fcBounds, fcStatus); 1.1059 + 1.1060 + // If we're not dirty (which means we'll mark everything dirty later) 1.1061 + // and our width has changed, mark the lines dirty that we need to 1.1062 + // mark dirty for a resize reflow. 1.1063 + if (!(GetStateBits() & NS_FRAME_IS_DIRTY) && reflowState->mFlags.mHResize) { 1.1064 + PrepareResizeReflow(state); 1.1065 + } 1.1066 + 1.1067 + LazyMarkLinesDirty(); 1.1068 + 1.1069 + mState &= ~NS_FRAME_FIRST_REFLOW; 1.1070 + 1.1071 + // Now reflow... 1.1072 + rv = ReflowDirtyLines(state); 1.1073 + 1.1074 + // If we have a next-in-flow, and that next-in-flow has pushed floats from 1.1075 + // this frame from a previous iteration of reflow, then we should not return 1.1076 + // a status of NS_FRAME_COMPLETE, since we actually have overflow, it's just 1.1077 + // already been handled. 1.1078 + 1.1079 + // NOTE: This really shouldn't happen, since we _should_ pull back our floats 1.1080 + // and reflow them, but just in case it does, this is a safety precaution so 1.1081 + // we don't end up with a placeholder pointing to frames that have already 1.1082 + // been deleted as part of removing our next-in-flow. 1.1083 + if (NS_FRAME_IS_COMPLETE(state.mReflowStatus)) { 1.1084 + nsBlockFrame* nif = static_cast<nsBlockFrame*>(GetNextInFlow()); 1.1085 + while (nif) { 1.1086 + if (nif->HasPushedFloatsFromPrevContinuation()) { 1.1087 + NS_MergeReflowStatusInto(&state.mReflowStatus, NS_FRAME_NOT_COMPLETE); 1.1088 + } 1.1089 + 1.1090 + nif = static_cast<nsBlockFrame*>(nif->GetNextInFlow()); 1.1091 + } 1.1092 + } 1.1093 + 1.1094 + NS_ASSERTION(NS_SUCCEEDED(rv), "reflow dirty lines failed"); 1.1095 + if (NS_FAILED(rv)) return rv; 1.1096 + 1.1097 + NS_MergeReflowStatusInto(&state.mReflowStatus, ocStatus); 1.1098 + NS_MergeReflowStatusInto(&state.mReflowStatus, fcStatus); 1.1099 + 1.1100 + // If we end in a BR with clear and affected floats continue, 1.1101 + // we need to continue, too. 1.1102 + if (NS_UNCONSTRAINEDSIZE != reflowState->AvailableHeight() && 1.1103 + NS_FRAME_IS_COMPLETE(state.mReflowStatus) && 1.1104 + state.mFloatManager->ClearContinues(FindTrailingClear())) { 1.1105 + NS_FRAME_SET_INCOMPLETE(state.mReflowStatus); 1.1106 + } 1.1107 + 1.1108 + if (!NS_FRAME_IS_FULLY_COMPLETE(state.mReflowStatus)) { 1.1109 + if (HasOverflowLines() || HasPushedFloats()) { 1.1110 + state.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW; 1.1111 + } 1.1112 + 1.1113 +#ifdef DEBUG_kipp 1.1114 + ListTag(stdout); printf(": block is not fully complete\n"); 1.1115 +#endif 1.1116 + } 1.1117 + 1.1118 + // Place the "marker" (bullet) frame if it is placed next to a block 1.1119 + // child. 1.1120 + // 1.1121 + // According to the CSS2 spec, section 12.6.1, the "marker" box 1.1122 + // participates in the height calculation of the list-item box's 1.1123 + // first line box. 1.1124 + // 1.1125 + // There are exactly two places a bullet can be placed: near the 1.1126 + // first or second line. It's only placed on the second line in a 1.1127 + // rare case: an empty first line followed by a second line that 1.1128 + // contains a block (example: <LI>\n<P>... ). This is where 1.1129 + // the second case can happen. 1.1130 + if (HasOutsideBullet() && !mLines.empty() && 1.1131 + (mLines.front()->IsBlock() || 1.1132 + (0 == mLines.front()->BSize() && 1.1133 + mLines.front() != mLines.back() && 1.1134 + mLines.begin().next()->IsBlock()))) { 1.1135 + // Reflow the bullet 1.1136 + nsHTMLReflowMetrics metrics(aReflowState); 1.1137 + // XXX Use the entire line when we fix bug 25888. 1.1138 + nsLayoutUtils::LinePosition position; 1.1139 + bool havePosition = nsLayoutUtils::GetFirstLinePosition(this, &position); 1.1140 + nscoord lineTop = havePosition ? position.mTop 1.1141 + : reflowState->ComputedPhysicalBorderPadding().top; 1.1142 + nsIFrame* bullet = GetOutsideBullet(); 1.1143 + ReflowBullet(bullet, state, metrics, lineTop); 1.1144 + NS_ASSERTION(!BulletIsEmpty() || metrics.Height() == 0, 1.1145 + "empty bullet took up space"); 1.1146 + 1.1147 + if (havePosition && !BulletIsEmpty()) { 1.1148 + // We have some lines to align the bullet with. 1.1149 + 1.1150 + // Doing the alignment using the baseline will also cater for 1.1151 + // bullets that are placed next to a child block (bug 92896) 1.1152 + 1.1153 + // Tall bullets won't look particularly nice here... 1.1154 + nsRect bbox = bullet->GetRect(); 1.1155 + bbox.y = position.mBaseline - metrics.TopAscent(); 1.1156 + bullet->SetRect(bbox); 1.1157 + } 1.1158 + // Otherwise just leave the bullet where it is, up against our top padding. 1.1159 + } 1.1160 + 1.1161 + CheckFloats(state); 1.1162 + 1.1163 + // Compute our final size 1.1164 + nscoord bottomEdgeOfChildren; 1.1165 + ComputeFinalSize(*reflowState, state, aMetrics, &bottomEdgeOfChildren); 1.1166 + nsRect areaBounds = nsRect(0, 0, aMetrics.Width(), aMetrics.Height()); 1.1167 + ComputeOverflowAreas(areaBounds, reflowState->mStyleDisplay, 1.1168 + bottomEdgeOfChildren, aMetrics.mOverflowAreas); 1.1169 + // Factor overflow container child bounds into the overflow area 1.1170 + aMetrics.mOverflowAreas.UnionWith(ocBounds); 1.1171 + // Factor pushed float child bounds into the overflow area 1.1172 + aMetrics.mOverflowAreas.UnionWith(fcBounds); 1.1173 + 1.1174 + // Let the absolutely positioned container reflow any absolutely positioned 1.1175 + // child frames that need to be reflowed, e.g., elements with a percentage 1.1176 + // based width/height 1.1177 + // We want to do this under either of two conditions: 1.1178 + // 1. If we didn't do the incremental reflow above. 1.1179 + // 2. If our size changed. 1.1180 + // Even though it's the padding edge that's the containing block, we 1.1181 + // can use our rect (the border edge) since if the border style 1.1182 + // changed, the reflow would have been targeted at us so we'd satisfy 1.1183 + // condition 1. 1.1184 + // XXX checking oldSize is bogus, there are various reasons we might have 1.1185 + // reflowed but our size might not have been changed to what we 1.1186 + // asked for (e.g., we ended up being pushed to a new page) 1.1187 + // When WillReflowAgainForClearance is true, we will reflow again without 1.1188 + // resetting the size. Because of this, we must not reflow our abs-pos children 1.1189 + // in that situation --- what we think is our "new size" 1.1190 + // will not be our real new size. This also happens to be more efficient. 1.1191 + if (HasAbsolutelyPositionedChildren()) { 1.1192 + nsAbsoluteContainingBlock* absoluteContainer = GetAbsoluteContainingBlock(); 1.1193 + bool haveInterrupt = aPresContext->HasPendingInterrupt(); 1.1194 + if (reflowState->WillReflowAgainForClearance() || 1.1195 + haveInterrupt) { 1.1196 + // Make sure that when we reflow again we'll actually reflow all the abs 1.1197 + // pos frames that might conceivably depend on our size (or all of them, 1.1198 + // if we're dirty right now and interrupted; in that case we also need 1.1199 + // to mark them all with NS_FRAME_IS_DIRTY). Sadly, we can't do much 1.1200 + // better than that, because we don't really know what our size will be, 1.1201 + // and it might in fact not change on the followup reflow! 1.1202 + if (haveInterrupt && (GetStateBits() & NS_FRAME_IS_DIRTY)) { 1.1203 + absoluteContainer->MarkAllFramesDirty(); 1.1204 + } else { 1.1205 + absoluteContainer->MarkSizeDependentFramesDirty(); 1.1206 + } 1.1207 + } else { 1.1208 + nsSize containingBlockSize = 1.1209 + CalculateContainingBlockSizeForAbsolutes(*reflowState, 1.1210 + nsSize(aMetrics.Width(), 1.1211 + aMetrics.Height())); 1.1212 + 1.1213 + // Mark frames that depend on changes we just made to this frame as dirty: 1.1214 + // Now we can assume that the padding edge hasn't moved. 1.1215 + // We need to reflow the absolutes if one of them depends on 1.1216 + // its placeholder position, or the containing block size in a 1.1217 + // direction in which the containing block size might have 1.1218 + // changed. 1.1219 + bool cbWidthChanged = aMetrics.Width() != oldSize.width; 1.1220 + bool isRoot = !GetContent()->GetParent(); 1.1221 + // If isRoot and we have auto height, then we are the initial 1.1222 + // containing block and the containing block height is the 1.1223 + // viewport height, which can't change during incremental 1.1224 + // reflow. 1.1225 + bool cbHeightChanged = 1.1226 + !(isRoot && NS_UNCONSTRAINEDSIZE == reflowState->ComputedHeight()) && 1.1227 + aMetrics.Height() != oldSize.height; 1.1228 + 1.1229 + nsRect containingBlock(nsPoint(0, 0), containingBlockSize); 1.1230 + absoluteContainer->Reflow(this, aPresContext, *reflowState, 1.1231 + state.mReflowStatus, 1.1232 + containingBlock, true, 1.1233 + cbWidthChanged, cbHeightChanged, 1.1234 + &aMetrics.mOverflowAreas); 1.1235 + 1.1236 + //XXXfr Why isn't this rv (and others in this file) checked/returned? 1.1237 + } 1.1238 + } 1.1239 + 1.1240 + FinishAndStoreOverflow(&aMetrics); 1.1241 + 1.1242 + // Clear the float manager pointer in the block reflow state so we 1.1243 + // don't waste time translating the coordinate system back on a dead 1.1244 + // float manager. 1.1245 + if (needFloatManager) 1.1246 + state.mFloatManager = nullptr; 1.1247 + 1.1248 + aStatus = state.mReflowStatus; 1.1249 + 1.1250 +#ifdef DEBUG 1.1251 + // Between when we drain pushed floats and when we complete reflow, 1.1252 + // we're allowed to have multiple continuations of the same float on 1.1253 + // our floats list, since a first-in-flow might get pushed to a later 1.1254 + // continuation of its containing block. But it's not permitted 1.1255 + // outside that time. 1.1256 + nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats); 1.1257 + 1.1258 + if (gNoisyReflow) { 1.1259 + IndentBy(stdout, gNoiseIndent); 1.1260 + ListTag(stdout); 1.1261 + printf(": status=%x (%scomplete) metrics=%d,%d carriedMargin=%d", 1.1262 + aStatus, NS_FRAME_IS_COMPLETE(aStatus) ? "" : "not ", 1.1263 + aMetrics.Width(), aMetrics.Height(), 1.1264 + aMetrics.mCarriedOutBottomMargin.get()); 1.1265 + if (HasOverflowAreas()) { 1.1266 + printf(" overflow-vis={%d,%d,%d,%d}", 1.1267 + aMetrics.VisualOverflow().x, 1.1268 + aMetrics.VisualOverflow().y, 1.1269 + aMetrics.VisualOverflow().width, 1.1270 + aMetrics.VisualOverflow().height); 1.1271 + printf(" overflow-scr={%d,%d,%d,%d}", 1.1272 + aMetrics.ScrollableOverflow().x, 1.1273 + aMetrics.ScrollableOverflow().y, 1.1274 + aMetrics.ScrollableOverflow().width, 1.1275 + aMetrics.ScrollableOverflow().height); 1.1276 + } 1.1277 + printf("\n"); 1.1278 + } 1.1279 + 1.1280 + if (gLameReflowMetrics) { 1.1281 + PRTime end = PR_Now(); 1.1282 + 1.1283 + int32_t ectc = nsLineBox::GetCtorCount(); 1.1284 + int32_t numLines = mLines.size(); 1.1285 + if (!numLines) numLines = 1; 1.1286 + PRTime delta, perLineDelta, lines; 1.1287 + lines = int64_t(numLines); 1.1288 + delta = end - start; 1.1289 + perLineDelta = delta / lines; 1.1290 + 1.1291 + ListTag(stdout); 1.1292 + char buf[400]; 1.1293 + PR_snprintf(buf, sizeof(buf), 1.1294 + ": %lld elapsed (%lld per line) (%d lines; %d new lines)", 1.1295 + delta, perLineDelta, numLines, ectc - ctc); 1.1296 + printf("%s\n", buf); 1.1297 + } 1.1298 +#endif 1.1299 + 1.1300 + NS_FRAME_SET_TRUNCATION(aStatus, (*reflowState), aMetrics); 1.1301 + return rv; 1.1302 +} 1.1303 + 1.1304 +bool 1.1305 +nsBlockFrame::CheckForCollapsedBottomMarginFromClearanceLine() 1.1306 +{ 1.1307 + line_iterator begin = begin_lines(); 1.1308 + line_iterator line = end_lines(); 1.1309 + 1.1310 + while (true) { 1.1311 + if (begin == line) { 1.1312 + return false; 1.1313 + } 1.1314 + --line; 1.1315 + if (line->BSize() != 0 || !line->CachedIsEmpty()) { 1.1316 + return false; 1.1317 + } 1.1318 + if (line->HasClearance()) { 1.1319 + return true; 1.1320 + } 1.1321 + } 1.1322 + // not reached 1.1323 +} 1.1324 + 1.1325 +void 1.1326 +nsBlockFrame::ComputeFinalSize(const nsHTMLReflowState& aReflowState, 1.1327 + nsBlockReflowState& aState, 1.1328 + nsHTMLReflowMetrics& aMetrics, 1.1329 + nscoord* aBottomEdgeOfChildren) 1.1330 +{ 1.1331 + const nsMargin& borderPadding = aState.BorderPadding(); 1.1332 +#ifdef NOISY_FINAL_SIZE 1.1333 + ListTag(stdout); 1.1334 + printf(": mY=%d mIsBottomMarginRoot=%s mPrevBottomMargin=%d bp=%d,%d\n", 1.1335 + aState.mY, aState.GetFlag(BRS_ISBOTTOMMARGINROOT) ? "yes" : "no", 1.1336 + aState.mPrevBottomMargin, 1.1337 + borderPadding.top, borderPadding.bottom); 1.1338 +#endif 1.1339 + 1.1340 + // Compute final width 1.1341 + aMetrics.Width() = 1.1342 + NSCoordSaturatingAdd(NSCoordSaturatingAdd(borderPadding.left, 1.1343 + aReflowState.ComputedWidth()), 1.1344 + borderPadding.right); 1.1345 + 1.1346 + // Return bottom margin information 1.1347 + // rbs says he hit this assertion occasionally (see bug 86947), so 1.1348 + // just set the margin to zero and we'll figure out why later 1.1349 + //NS_ASSERTION(aMetrics.mCarriedOutBottomMargin.IsZero(), 1.1350 + // "someone else set the margin"); 1.1351 + nscoord nonCarriedOutVerticalMargin = 0; 1.1352 + if (!aState.GetFlag(BRS_ISBOTTOMMARGINROOT)) { 1.1353 + // Apply rule from CSS 2.1 section 8.3.1. If we have some empty 1.1354 + // line with clearance and a non-zero top margin and all 1.1355 + // subsequent lines are empty, then we do not allow our children's 1.1356 + // carried out bottom margin to be carried out of us and collapse 1.1357 + // with our own bottom margin. 1.1358 + if (CheckForCollapsedBottomMarginFromClearanceLine()) { 1.1359 + // Convert the children's carried out margin to something that 1.1360 + // we will include in our height 1.1361 + nonCarriedOutVerticalMargin = aState.mPrevBottomMargin.get(); 1.1362 + aState.mPrevBottomMargin.Zero(); 1.1363 + } 1.1364 + aMetrics.mCarriedOutBottomMargin = aState.mPrevBottomMargin; 1.1365 + } else { 1.1366 + aMetrics.mCarriedOutBottomMargin.Zero(); 1.1367 + } 1.1368 + 1.1369 + nscoord bottomEdgeOfChildren = aState.mY + nonCarriedOutVerticalMargin; 1.1370 + // Shrink wrap our height around our contents. 1.1371 + if (aState.GetFlag(BRS_ISBOTTOMMARGINROOT) || 1.1372 + NS_UNCONSTRAINEDSIZE != aReflowState.ComputedHeight()) { 1.1373 + // When we are a bottom-margin root make sure that our last 1.1374 + // childs bottom margin is fully applied. We also do this when 1.1375 + // we have a computed height, since in that case the carried out 1.1376 + // margin is not going to be applied anywhere, so we should note it 1.1377 + // here to be included in the overflow area. 1.1378 + // Apply the margin only if there's space for it. 1.1379 + if (bottomEdgeOfChildren < aState.mReflowState.AvailableHeight()) 1.1380 + { 1.1381 + // Truncate bottom margin if it doesn't fit to our available height. 1.1382 + bottomEdgeOfChildren = 1.1383 + std::min(bottomEdgeOfChildren + aState.mPrevBottomMargin.get(), 1.1384 + aState.mReflowState.AvailableHeight()); 1.1385 + } 1.1386 + } 1.1387 + if (aState.GetFlag(BRS_FLOAT_MGR)) { 1.1388 + // Include the float manager's state to properly account for the 1.1389 + // bottom margin of any floated elements; e.g., inside a table cell. 1.1390 + nscoord floatHeight = 1.1391 + aState.ClearFloats(bottomEdgeOfChildren, NS_STYLE_CLEAR_BOTH, 1.1392 + nullptr, nsFloatManager::DONT_CLEAR_PUSHED_FLOATS); 1.1393 + bottomEdgeOfChildren = std::max(bottomEdgeOfChildren, floatHeight); 1.1394 + } 1.1395 + 1.1396 + if (NS_UNCONSTRAINEDSIZE != aReflowState.ComputedHeight() 1.1397 + && (mParent->GetType() != nsGkAtoms::columnSetFrame || 1.1398 + aReflowState.parentReflowState->AvailableHeight() == NS_UNCONSTRAINEDSIZE)) { 1.1399 + ComputeFinalHeight(aReflowState, &aState.mReflowStatus, 1.1400 + aState.mY + nonCarriedOutVerticalMargin, 1.1401 + borderPadding, aMetrics, aState.mConsumedHeight); 1.1402 + if (!NS_FRAME_IS_COMPLETE(aState.mReflowStatus)) { 1.1403 + // Use the current height; continuations will take up the rest. 1.1404 + // Do extend the height to at least consume the available 1.1405 + // height, otherwise our left/right borders (for example) won't 1.1406 + // extend all the way to the break. 1.1407 + aMetrics.Height() = std::max(aReflowState.AvailableHeight(), 1.1408 + aState.mY + nonCarriedOutVerticalMargin); 1.1409 + // ... but don't take up more height than is available 1.1410 + nscoord effectiveComputedHeight = 1.1411 + GetEffectiveComputedHeight(aReflowState, aState.GetConsumedHeight()); 1.1412 + aMetrics.Height() = std::min(aMetrics.Height(), 1.1413 + borderPadding.top + effectiveComputedHeight); 1.1414 + // XXX It's pretty wrong that our bottom border still gets drawn on 1.1415 + // on its own on the last-in-flow, even if we ran out of height 1.1416 + // here. We need GetSkipSides to check whether we ran out of content 1.1417 + // height in the current frame, not whether it's last-in-flow. 1.1418 + } 1.1419 + 1.1420 + // Don't carry out a bottom margin when our height is fixed. 1.1421 + aMetrics.mCarriedOutBottomMargin.Zero(); 1.1422 + } 1.1423 + else if (NS_FRAME_IS_COMPLETE(aState.mReflowStatus)) { 1.1424 + nscoord contentHeight = bottomEdgeOfChildren - borderPadding.top; 1.1425 + nscoord autoHeight = aReflowState.ApplyMinMaxHeight(contentHeight); 1.1426 + if (autoHeight != contentHeight) { 1.1427 + // Our min-height or max-height made our height change. Don't carry out 1.1428 + // our kids' bottom margins. 1.1429 + aMetrics.mCarriedOutBottomMargin.Zero(); 1.1430 + } 1.1431 + autoHeight += borderPadding.top + borderPadding.bottom; 1.1432 + aMetrics.Height() = autoHeight; 1.1433 + } 1.1434 + else { 1.1435 + NS_ASSERTION(aReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE, 1.1436 + "Shouldn't be incomplete if availableHeight is UNCONSTRAINED."); 1.1437 + aMetrics.Height() = std::max(aState.mY, aReflowState.AvailableHeight()); 1.1438 + if (aReflowState.AvailableHeight() == NS_UNCONSTRAINEDSIZE) 1.1439 + // This should never happen, but it does. See bug 414255 1.1440 + aMetrics.Height() = aState.mY; 1.1441 + } 1.1442 + 1.1443 + if (IS_TRUE_OVERFLOW_CONTAINER(this) && 1.1444 + NS_FRAME_IS_NOT_COMPLETE(aState.mReflowStatus)) { 1.1445 + // Overflow containers can only be overflow complete. 1.1446 + // Note that auto height overflow containers have no normal children 1.1447 + NS_ASSERTION(aMetrics.Height() == 0, "overflow containers must be zero-height"); 1.1448 + NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState.mReflowStatus); 1.1449 + } 1.1450 + 1.1451 + // Screen out negative heights --- can happen due to integer overflows :-( 1.1452 + aMetrics.Height() = std::max(0, aMetrics.Height()); 1.1453 + *aBottomEdgeOfChildren = bottomEdgeOfChildren; 1.1454 + 1.1455 + FrameProperties properties = Properties(); 1.1456 + if (bottomEdgeOfChildren != aMetrics.Height() - borderPadding.bottom) { 1.1457 + properties.Set(BottomEdgeOfChildrenProperty(), 1.1458 + NS_INT32_TO_PTR(bottomEdgeOfChildren)); 1.1459 + } else { 1.1460 + properties.Delete(BottomEdgeOfChildrenProperty()); 1.1461 + } 1.1462 + 1.1463 +#ifdef DEBUG_blocks 1.1464 + if (CRAZY_SIZE(aMetrics.Width()) || CRAZY_SIZE(aMetrics.Height())) { 1.1465 + ListTag(stdout); 1.1466 + printf(": WARNING: desired:%d,%d\n", aMetrics.Width(), aMetrics.Height()); 1.1467 + } 1.1468 +#endif 1.1469 +} 1.1470 + 1.1471 +static void 1.1472 +ConsiderBottomEdgeOfChildren(nscoord aBottomEdgeOfChildren, 1.1473 + nsOverflowAreas& aOverflowAreas) 1.1474 +{ 1.1475 + // Factor in the bottom edge of the children. Child frames will be added 1.1476 + // to the overflow area as we iterate through the lines, but their margins 1.1477 + // won't, so we need to account for bottom margins here. 1.1478 + // REVIEW: For now, we do this for both visual and scrollable area, 1.1479 + // although when we make scrollable overflow area not be a subset of 1.1480 + // visual, we can change this. 1.1481 + NS_FOR_FRAME_OVERFLOW_TYPES(otype) { 1.1482 + nsRect& o = aOverflowAreas.Overflow(otype); 1.1483 + o.height = std::max(o.YMost(), aBottomEdgeOfChildren) - o.y; 1.1484 + } 1.1485 +} 1.1486 + 1.1487 +void 1.1488 +nsBlockFrame::ComputeOverflowAreas(const nsRect& aBounds, 1.1489 + const nsStyleDisplay* aDisplay, 1.1490 + nscoord aBottomEdgeOfChildren, 1.1491 + nsOverflowAreas& aOverflowAreas) 1.1492 +{ 1.1493 + // Compute the overflow areas of our children 1.1494 + // XXX_perf: This can be done incrementally. It is currently one of 1.1495 + // the things that makes incremental reflow O(N^2). 1.1496 + nsOverflowAreas areas(aBounds, aBounds); 1.1497 + if (!ShouldApplyOverflowClipping(this, aDisplay)) { 1.1498 + for (line_iterator line = begin_lines(), line_end = end_lines(); 1.1499 + line != line_end; 1.1500 + ++line) { 1.1501 + areas.UnionWith(line->GetOverflowAreas()); 1.1502 + } 1.1503 + 1.1504 + // Factor an outside bullet in; normally the bullet will be factored into 1.1505 + // the line-box's overflow areas. However, if the line is a block 1.1506 + // line then it won't; if there are no lines, it won't. So just 1.1507 + // factor it in anyway (it can't hurt if it was already done). 1.1508 + // XXXldb Can we just fix GetOverflowArea instead? 1.1509 + nsIFrame* outsideBullet = GetOutsideBullet(); 1.1510 + if (outsideBullet) { 1.1511 + areas.UnionAllWith(outsideBullet->GetRect()); 1.1512 + } 1.1513 + 1.1514 + ConsiderBottomEdgeOfChildren(aBottomEdgeOfChildren, areas); 1.1515 + } 1.1516 + 1.1517 +#ifdef NOISY_COMBINED_AREA 1.1518 + ListTag(stdout); 1.1519 + printf(": ca=%d,%d,%d,%d\n", area.x, area.y, area.width, area.height); 1.1520 +#endif 1.1521 + 1.1522 + aOverflowAreas = areas; 1.1523 +} 1.1524 + 1.1525 +bool 1.1526 +nsBlockFrame::UpdateOverflow() 1.1527 +{ 1.1528 + nsRect rect(nsPoint(0, 0), GetSize()); 1.1529 + nsOverflowAreas overflowAreas(rect, rect); 1.1530 + 1.1531 + // We need to update the overflow areas of lines manually, as they 1.1532 + // get cached and re-used otherwise. Lines aren't exposed as normal 1.1533 + // frame children, so calling UnionChildOverflow alone will end up 1.1534 + // using the old cached values. 1.1535 + for (line_iterator line = begin_lines(), line_end = end_lines(); 1.1536 + line != line_end; 1.1537 + ++line) { 1.1538 + nsRect bounds = line->GetPhysicalBounds(); 1.1539 + nsOverflowAreas lineAreas(bounds, bounds); 1.1540 + 1.1541 + int32_t n = line->GetChildCount(); 1.1542 + for (nsIFrame* lineFrame = line->mFirstChild; 1.1543 + n > 0; lineFrame = lineFrame->GetNextSibling(), --n) { 1.1544 + ConsiderChildOverflow(lineAreas, lineFrame); 1.1545 + } 1.1546 + 1.1547 + // Consider the overflow areas of the floats attached to the line as well 1.1548 + if (line->HasFloats()) { 1.1549 + for (nsFloatCache* fc = line->GetFirstFloat(); fc; fc = fc->Next()) { 1.1550 + ConsiderChildOverflow(lineAreas, fc->mFloat); 1.1551 + } 1.1552 + } 1.1553 + 1.1554 + line->SetOverflowAreas(lineAreas); 1.1555 + overflowAreas.UnionWith(lineAreas); 1.1556 + } 1.1557 + 1.1558 + // Line cursor invariants depend on the overflow areas of the lines, so 1.1559 + // we must clear the line cursor since those areas may have changed. 1.1560 + ClearLineCursor(); 1.1561 + 1.1562 + // Union with child frames, skipping the principal and float lists 1.1563 + // since we already handled those using the line boxes. 1.1564 + nsLayoutUtils::UnionChildOverflow(this, overflowAreas, 1.1565 + kPrincipalList | kFloatList); 1.1566 + 1.1567 + bool found; 1.1568 + nscoord bottomEdgeOfChildren = NS_PTR_TO_INT32( 1.1569 + Properties().Get(BottomEdgeOfChildrenProperty(), &found)); 1.1570 + if (found) { 1.1571 + ConsiderBottomEdgeOfChildren(bottomEdgeOfChildren, overflowAreas); 1.1572 + } 1.1573 + 1.1574 + return FinishAndStoreOverflow(overflowAreas, GetSize()); 1.1575 +} 1.1576 + 1.1577 +void 1.1578 +nsBlockFrame::LazyMarkLinesDirty() 1.1579 +{ 1.1580 + if (GetStateBits() & NS_BLOCK_LOOK_FOR_DIRTY_FRAMES) { 1.1581 + for (line_iterator line = begin_lines(), line_end = end_lines(); 1.1582 + line != line_end; ++line) { 1.1583 + int32_t n = line->GetChildCount(); 1.1584 + for (nsIFrame* lineFrame = line->mFirstChild; 1.1585 + n > 0; lineFrame = lineFrame->GetNextSibling(), --n) { 1.1586 + if (NS_SUBTREE_DIRTY(lineFrame)) { 1.1587 + // NOTE: MarkLineDirty does more than just marking the line dirty. 1.1588 + MarkLineDirty(line, &mLines); 1.1589 + break; 1.1590 + } 1.1591 + } 1.1592 + } 1.1593 + RemoveStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES); 1.1594 + } 1.1595 +} 1.1596 + 1.1597 +void 1.1598 +nsBlockFrame::MarkLineDirty(line_iterator aLine, const nsLineList* aLineList) 1.1599 +{ 1.1600 + // Mark aLine dirty 1.1601 + aLine->MarkDirty(); 1.1602 + aLine->SetInvalidateTextRuns(true); 1.1603 +#ifdef DEBUG 1.1604 + if (gNoisyReflow) { 1.1605 + IndentBy(stdout, gNoiseIndent); 1.1606 + ListTag(stdout); 1.1607 + printf(": mark line %p dirty\n", static_cast<void*>(aLine.get())); 1.1608 + } 1.1609 +#endif 1.1610 + 1.1611 + // Mark previous line dirty if it's an inline line so that it can 1.1612 + // maybe pullup something from the line just affected. 1.1613 + // XXX We don't need to do this if aPrevLine ends in a break-after... 1.1614 + if (aLine != aLineList->front() && aLine->IsInline() && 1.1615 + aLine.prev()->IsInline()) { 1.1616 + aLine.prev()->MarkDirty(); 1.1617 + aLine.prev()->SetInvalidateTextRuns(true); 1.1618 +#ifdef DEBUG 1.1619 + if (gNoisyReflow) { 1.1620 + IndentBy(stdout, gNoiseIndent); 1.1621 + ListTag(stdout); 1.1622 + printf(": mark prev-line %p dirty\n", 1.1623 + static_cast<void*>(aLine.prev().get())); 1.1624 + } 1.1625 +#endif 1.1626 + } 1.1627 +} 1.1628 + 1.1629 +/** 1.1630 + * Test whether lines are certain to be aligned left so that we can make 1.1631 + * resizing optimizations 1.1632 + */ 1.1633 +static inline bool 1.1634 +IsAlignedLeft(uint8_t aAlignment, 1.1635 + uint8_t aDirection, 1.1636 + uint8_t aUnicodeBidi, 1.1637 + nsIFrame* aFrame) 1.1638 +{ 1.1639 + return aFrame->IsSVGText() || 1.1640 + NS_STYLE_TEXT_ALIGN_LEFT == aAlignment || 1.1641 + (((NS_STYLE_TEXT_ALIGN_DEFAULT == aAlignment && 1.1642 + NS_STYLE_DIRECTION_LTR == aDirection) || 1.1643 + (NS_STYLE_TEXT_ALIGN_END == aAlignment && 1.1644 + NS_STYLE_DIRECTION_RTL == aDirection)) && 1.1645 + !(NS_STYLE_UNICODE_BIDI_PLAINTEXT & aUnicodeBidi)); 1.1646 +} 1.1647 + 1.1648 +void 1.1649 +nsBlockFrame::PrepareResizeReflow(nsBlockReflowState& aState) 1.1650 +{ 1.1651 + // See if we can try and avoid marking all the lines as dirty 1.1652 + bool tryAndSkipLines = 1.1653 + // The left content-edge must be a constant distance from the left 1.1654 + // border-edge. 1.1655 + !StylePadding()->mPadding.GetLeft().HasPercent(); 1.1656 + 1.1657 +#ifdef DEBUG 1.1658 + if (gDisableResizeOpt) { 1.1659 + tryAndSkipLines = false; 1.1660 + } 1.1661 + if (gNoisyReflow) { 1.1662 + if (!tryAndSkipLines) { 1.1663 + IndentBy(stdout, gNoiseIndent); 1.1664 + ListTag(stdout); 1.1665 + printf(": marking all lines dirty: availWidth=%d\n", 1.1666 + aState.mReflowState.AvailableWidth()); 1.1667 + } 1.1668 + } 1.1669 +#endif 1.1670 + 1.1671 + if (tryAndSkipLines) { 1.1672 + nscoord newAvailWidth = aState.mReflowState.ComputedPhysicalBorderPadding().left + 1.1673 + aState.mReflowState.ComputedWidth(); 1.1674 + NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mReflowState.ComputedPhysicalBorderPadding().left && 1.1675 + NS_UNCONSTRAINEDSIZE != aState.mReflowState.ComputedWidth(), 1.1676 + "math on NS_UNCONSTRAINEDSIZE"); 1.1677 + 1.1678 +#ifdef DEBUG 1.1679 + if (gNoisyReflow) { 1.1680 + IndentBy(stdout, gNoiseIndent); 1.1681 + ListTag(stdout); 1.1682 + printf(": trying to avoid marking all lines dirty\n"); 1.1683 + } 1.1684 +#endif 1.1685 + 1.1686 + for (line_iterator line = begin_lines(), line_end = end_lines(); 1.1687 + line != line_end; 1.1688 + ++line) 1.1689 + { 1.1690 + // We let child blocks make their own decisions the same 1.1691 + // way we are here. 1.1692 + bool isLastLine = line == mLines.back() && !GetNextInFlow(); 1.1693 + if (line->IsBlock() || 1.1694 + line->HasFloats() || 1.1695 + (!isLastLine && !line->HasBreakAfter()) || 1.1696 + ((isLastLine || !line->IsLineWrapped())) || 1.1697 + line->ResizeReflowOptimizationDisabled() || 1.1698 + line->IsImpactedByFloat() || 1.1699 + (line->IEnd() > newAvailWidth)) { 1.1700 + line->MarkDirty(); 1.1701 + } 1.1702 + 1.1703 +#ifdef REALLY_NOISY_REFLOW 1.1704 + if (!line->IsBlock()) { 1.1705 + printf("PrepareResizeReflow thinks line %p is %simpacted by floats\n", 1.1706 + line.get(), line->IsImpactedByFloat() ? "" : "not "); 1.1707 + } 1.1708 +#endif 1.1709 +#ifdef DEBUG 1.1710 + if (gNoisyReflow && !line->IsDirty()) { 1.1711 + IndentBy(stdout, gNoiseIndent + 1); 1.1712 + printf("skipped: line=%p next=%p %s %s%s%s breakTypeBefore/After=%d/%d xmost=%d\n", 1.1713 + static_cast<void*>(line.get()), 1.1714 + static_cast<void*>((line.next() != end_lines() ? line.next().get() : nullptr)), 1.1715 + line->IsBlock() ? "block" : "inline", 1.1716 + line->HasBreakAfter() ? "has-break-after " : "", 1.1717 + line->HasFloats() ? "has-floats " : "", 1.1718 + line->IsImpactedByFloat() ? "impacted " : "", 1.1719 + line->GetBreakTypeBefore(), line->GetBreakTypeAfter(), 1.1720 + line->IEnd()); 1.1721 + } 1.1722 +#endif 1.1723 + } 1.1724 + } 1.1725 + else { 1.1726 + // Mark everything dirty 1.1727 + for (line_iterator line = begin_lines(), line_end = end_lines(); 1.1728 + line != line_end; 1.1729 + ++line) 1.1730 + { 1.1731 + line->MarkDirty(); 1.1732 + } 1.1733 + } 1.1734 +} 1.1735 + 1.1736 +//---------------------------------------- 1.1737 + 1.1738 +/** 1.1739 + * Propagate reflow "damage" from from earlier lines to the current 1.1740 + * line. The reflow damage comes from the following sources: 1.1741 + * 1. The regions of float damage remembered during reflow. 1.1742 + * 2. The combination of nonzero |aDeltaY| and any impact by a float, 1.1743 + * either the previous reflow or now. 1.1744 + * 1.1745 + * When entering this function, |aLine| is still at its old position and 1.1746 + * |aDeltaY| indicates how much it will later be slid (assuming it 1.1747 + * doesn't get marked dirty and reflowed entirely). 1.1748 + */ 1.1749 +void 1.1750 +nsBlockFrame::PropagateFloatDamage(nsBlockReflowState& aState, 1.1751 + nsLineBox* aLine, 1.1752 + nscoord aDeltaY) 1.1753 +{ 1.1754 + nsFloatManager *floatManager = aState.mReflowState.mFloatManager; 1.1755 + NS_ASSERTION((aState.mReflowState.parentReflowState && 1.1756 + aState.mReflowState.parentReflowState->mFloatManager == floatManager) || 1.1757 + aState.mReflowState.mBlockDelta == 0, "Bad block delta passed in"); 1.1758 + 1.1759 + // Check to see if there are any floats; if there aren't, there can't 1.1760 + // be any float damage 1.1761 + if (!floatManager->HasAnyFloats()) 1.1762 + return; 1.1763 + 1.1764 + // Check the damage region recorded in the float damage. 1.1765 + if (floatManager->HasFloatDamage()) { 1.1766 + // Need to check mBounds *and* mCombinedArea to find intersections 1.1767 + // with aLine's floats 1.1768 + nscoord lineYA = aLine->BStart() + aDeltaY; 1.1769 + nscoord lineYB = lineYA + aLine->BSize(); 1.1770 + // Scrollable overflow should be sufficient for things that affect 1.1771 + // layout. 1.1772 + nsRect overflow = aLine->GetOverflowArea(eScrollableOverflow); 1.1773 + nscoord lineYCombinedA = overflow.y + aDeltaY; 1.1774 + nscoord lineYCombinedB = lineYCombinedA + overflow.height; 1.1775 + if (floatManager->IntersectsDamage(lineYA, lineYB) || 1.1776 + floatManager->IntersectsDamage(lineYCombinedA, lineYCombinedB)) { 1.1777 + aLine->MarkDirty(); 1.1778 + return; 1.1779 + } 1.1780 + } 1.1781 + 1.1782 + // Check if the line is moving relative to the float manager 1.1783 + if (aDeltaY + aState.mReflowState.mBlockDelta != 0) { 1.1784 + if (aLine->IsBlock()) { 1.1785 + // Unconditionally reflow sliding blocks; we only really need to reflow 1.1786 + // if there's a float impacting this block, but the current float manager 1.1787 + // makes it difficult to check that. Therefore, we let the child block 1.1788 + // decide what it needs to reflow. 1.1789 + aLine->MarkDirty(); 1.1790 + } else { 1.1791 + bool wasImpactedByFloat = aLine->IsImpactedByFloat(); 1.1792 + nsFlowAreaRect floatAvailableSpace = 1.1793 + aState.GetFloatAvailableSpaceForHeight(aLine->BStart() + aDeltaY, 1.1794 + aLine->BSize(), 1.1795 + nullptr); 1.1796 + 1.1797 +#ifdef REALLY_NOISY_REFLOW 1.1798 + printf("nsBlockFrame::PropagateFloatDamage %p was = %d, is=%d\n", 1.1799 + this, wasImpactedByFloat, floatAvailableSpace.mHasFloats); 1.1800 +#endif 1.1801 + 1.1802 + // Mark the line dirty if it was or is affected by a float 1.1803 + // We actually only really need to reflow if the amount of impact 1.1804 + // changes, but that's not straightforward to check 1.1805 + if (wasImpactedByFloat || floatAvailableSpace.mHasFloats) { 1.1806 + aLine->MarkDirty(); 1.1807 + } 1.1808 + } 1.1809 + } 1.1810 +} 1.1811 + 1.1812 +static bool LineHasClear(nsLineBox* aLine) { 1.1813 + return aLine->IsBlock() 1.1814 + ? (aLine->GetBreakTypeBefore() || 1.1815 + (aLine->mFirstChild->GetStateBits() & NS_BLOCK_HAS_CLEAR_CHILDREN) || 1.1816 + !nsBlockFrame::BlockCanIntersectFloats(aLine->mFirstChild)) 1.1817 + : aLine->HasFloatBreakAfter(); 1.1818 +} 1.1819 + 1.1820 + 1.1821 +/** 1.1822 + * Reparent a whole list of floats from aOldParent to this block. The 1.1823 + * floats might be taken from aOldParent's overflow list. They will be 1.1824 + * removed from the list. They end up appended to our mFloats list. 1.1825 + */ 1.1826 +void 1.1827 +nsBlockFrame::ReparentFloats(nsIFrame* aFirstFrame, nsBlockFrame* aOldParent, 1.1828 + bool aReparentSiblings) { 1.1829 + nsFrameList list; 1.1830 + aOldParent->CollectFloats(aFirstFrame, list, aReparentSiblings); 1.1831 + if (list.NotEmpty()) { 1.1832 + for (nsIFrame* f = list.FirstChild(); f; f = f->GetNextSibling()) { 1.1833 + ReparentFrame(f, aOldParent, this); 1.1834 + } 1.1835 + mFloats.AppendFrames(nullptr, list); 1.1836 + } 1.1837 +} 1.1838 + 1.1839 +static void DumpLine(const nsBlockReflowState& aState, nsLineBox* aLine, 1.1840 + nscoord aDeltaY, int32_t aDeltaIndent) { 1.1841 +#ifdef DEBUG 1.1842 + if (nsBlockFrame::gNoisyReflow) { 1.1843 + nsRect ovis(aLine->GetVisualOverflowArea()); 1.1844 + nsRect oscr(aLine->GetScrollableOverflowArea()); 1.1845 + nsBlockFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent + aDeltaIndent); 1.1846 + printf("line=%p mY=%d dirty=%s oldBounds={%d,%d,%d,%d} oldoverflow-vis={%d,%d,%d,%d} oldoverflow-scr={%d,%d,%d,%d} deltaY=%d mPrevBottomMargin=%d childCount=%d\n", 1.1847 + static_cast<void*>(aLine), aState.mY, 1.1848 + aLine->IsDirty() ? "yes" : "no", 1.1849 + aLine->IStart(), aLine->BStart(), 1.1850 + aLine->ISize(), aLine->BSize(), 1.1851 + ovis.x, ovis.y, ovis.width, ovis.height, 1.1852 + oscr.x, oscr.y, oscr.width, oscr.height, 1.1853 + aDeltaY, aState.mPrevBottomMargin.get(), aLine->GetChildCount()); 1.1854 + } 1.1855 +#endif 1.1856 +} 1.1857 + 1.1858 +/** 1.1859 + * Reflow the dirty lines 1.1860 + */ 1.1861 +nsresult 1.1862 +nsBlockFrame::ReflowDirtyLines(nsBlockReflowState& aState) 1.1863 +{ 1.1864 + nsresult rv = NS_OK; 1.1865 + bool keepGoing = true; 1.1866 + bool repositionViews = false; // should we really need this? 1.1867 + bool foundAnyClears = aState.mFloatBreakType != NS_STYLE_CLEAR_NONE; 1.1868 + bool willReflowAgain = false; 1.1869 + 1.1870 +#ifdef DEBUG 1.1871 + if (gNoisyReflow) { 1.1872 + IndentBy(stdout, gNoiseIndent); 1.1873 + ListTag(stdout); 1.1874 + printf(": reflowing dirty lines"); 1.1875 + printf(" computedWidth=%d\n", aState.mReflowState.ComputedWidth()); 1.1876 + } 1.1877 + AutoNoisyIndenter indent(gNoisyReflow); 1.1878 +#endif 1.1879 + 1.1880 + bool selfDirty = (GetStateBits() & NS_FRAME_IS_DIRTY) || 1.1881 + (aState.mReflowState.mFlags.mVResize && 1.1882 + (GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT)); 1.1883 + 1.1884 + // Reflow our last line if our availableHeight has increased 1.1885 + // so that we (and our last child) pull up content as necessary 1.1886 + if (aState.mReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE 1.1887 + && GetNextInFlow() && aState.mReflowState.AvailableHeight() > mRect.height) { 1.1888 + line_iterator lastLine = end_lines(); 1.1889 + if (lastLine != begin_lines()) { 1.1890 + --lastLine; 1.1891 + lastLine->MarkDirty(); 1.1892 + } 1.1893 + } 1.1894 + // the amount by which we will slide the current line if it is not 1.1895 + // dirty 1.1896 + nscoord deltaY = 0; 1.1897 + 1.1898 + // whether we did NOT reflow the previous line and thus we need to 1.1899 + // recompute the carried out margin before the line if we want to 1.1900 + // reflow it or if its previous margin is dirty 1.1901 + bool needToRecoverState = false; 1.1902 + // Float continuations were reflowed in ReflowPushedFloats 1.1903 + bool reflowedFloat = mFloats.NotEmpty() && 1.1904 + (mFloats.FirstChild()->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT); 1.1905 + bool lastLineMovedUp = false; 1.1906 + // We save up information about BR-clearance here 1.1907 + uint8_t inlineFloatBreakType = aState.mFloatBreakType; 1.1908 + 1.1909 + line_iterator line = begin_lines(), line_end = end_lines(); 1.1910 + 1.1911 + // Reflow the lines that are already ours 1.1912 + for ( ; line != line_end; ++line, aState.AdvanceToNextLine()) { 1.1913 + DumpLine(aState, line, deltaY, 0); 1.1914 +#ifdef DEBUG 1.1915 + AutoNoisyIndenter indent2(gNoisyReflow); 1.1916 +#endif 1.1917 + 1.1918 + if (selfDirty) 1.1919 + line->MarkDirty(); 1.1920 + 1.1921 + // This really sucks, but we have to look inside any blocks that have clear 1.1922 + // elements inside them. 1.1923 + // XXX what can we do smarter here? 1.1924 + if (!line->IsDirty() && line->IsBlock() && 1.1925 + (line->mFirstChild->GetStateBits() & NS_BLOCK_HAS_CLEAR_CHILDREN)) { 1.1926 + line->MarkDirty(); 1.1927 + } 1.1928 + 1.1929 + nsIFrame *replacedBlock = nullptr; 1.1930 + if (line->IsBlock() && 1.1931 + !nsBlockFrame::BlockCanIntersectFloats(line->mFirstChild)) { 1.1932 + replacedBlock = line->mFirstChild; 1.1933 + } 1.1934 + 1.1935 + // We have to reflow the line if it's a block whose clearance 1.1936 + // might have changed, so detect that. 1.1937 + if (!line->IsDirty() && 1.1938 + (line->GetBreakTypeBefore() != NS_STYLE_CLEAR_NONE || 1.1939 + replacedBlock)) { 1.1940 + nscoord curY = aState.mY; 1.1941 + // See where we would be after applying any clearance due to 1.1942 + // BRs. 1.1943 + if (inlineFloatBreakType != NS_STYLE_CLEAR_NONE) { 1.1944 + curY = aState.ClearFloats(curY, inlineFloatBreakType); 1.1945 + } 1.1946 + 1.1947 + nscoord newY = 1.1948 + aState.ClearFloats(curY, line->GetBreakTypeBefore(), replacedBlock); 1.1949 + 1.1950 + if (line->HasClearance()) { 1.1951 + // Reflow the line if it might not have clearance anymore. 1.1952 + if (newY == curY 1.1953 + // aState.mY is the clearance point which should be the 1.1954 + // top border-edge of the block frame. If sliding the 1.1955 + // block by deltaY isn't going to put it in the predicted 1.1956 + // position, then we'd better reflow the line. 1.1957 + || newY != line->BStart() + deltaY) { 1.1958 + line->MarkDirty(); 1.1959 + } 1.1960 + } else { 1.1961 + // Reflow the line if the line might have clearance now. 1.1962 + if (curY != newY) { 1.1963 + line->MarkDirty(); 1.1964 + } 1.1965 + } 1.1966 + } 1.1967 + 1.1968 + // We might have to reflow a line that is after a clearing BR. 1.1969 + if (inlineFloatBreakType != NS_STYLE_CLEAR_NONE) { 1.1970 + aState.mY = aState.ClearFloats(aState.mY, inlineFloatBreakType); 1.1971 + if (aState.mY != line->BStart() + deltaY) { 1.1972 + // SlideLine is not going to put the line where the clearance 1.1973 + // put it. Reflow the line to be sure. 1.1974 + line->MarkDirty(); 1.1975 + } 1.1976 + inlineFloatBreakType = NS_STYLE_CLEAR_NONE; 1.1977 + } 1.1978 + 1.1979 + bool previousMarginWasDirty = line->IsPreviousMarginDirty(); 1.1980 + if (previousMarginWasDirty) { 1.1981 + // If the previous margin is dirty, reflow the current line 1.1982 + line->MarkDirty(); 1.1983 + line->ClearPreviousMarginDirty(); 1.1984 + } else if (line->BEnd() + deltaY > aState.mBottomEdge) { 1.1985 + // Lines that aren't dirty but get slid past our height constraint must 1.1986 + // be reflowed. 1.1987 + line->MarkDirty(); 1.1988 + } 1.1989 + 1.1990 + // If we have a constrained height (i.e., breaking columns/pages), 1.1991 + // and the distance to the bottom might have changed, then we need 1.1992 + // to reflow any line that might have floats in it, both because the 1.1993 + // breakpoints within those floats may have changed and because we 1.1994 + // might have to push/pull the floats in their entirety. 1.1995 + // FIXME: What about a deltaY or height change that forces us to 1.1996 + // push lines? Why does that work? 1.1997 + if (!line->IsDirty() && 1.1998 + aState.mReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE && 1.1999 + (deltaY != 0 || aState.mReflowState.mFlags.mVResize || 1.2000 + aState.mReflowState.mFlags.mMustReflowPlaceholders) && 1.2001 + (line->IsBlock() || line->HasFloats() || line->HadFloatPushed())) { 1.2002 + line->MarkDirty(); 1.2003 + } 1.2004 + 1.2005 + if (!line->IsDirty()) { 1.2006 + // See if there's any reflow damage that requires that we mark the 1.2007 + // line dirty. 1.2008 + PropagateFloatDamage(aState, line, deltaY); 1.2009 + } 1.2010 + 1.2011 + // If the container width has changed reset the container width. If the 1.2012 + // line's writing mode is not ltr, or if the line is not left-aligned, also 1.2013 + // mark the line dirty. 1.2014 + if (aState.mContainerWidth != line->mContainerWidth) { 1.2015 + line->mContainerWidth = aState.mContainerWidth; 1.2016 + 1.2017 + bool isLastLine = line == mLines.back() && 1.2018 + !GetNextInFlow() && 1.2019 + NS_STYLE_TEXT_ALIGN_AUTO == StyleText()->mTextAlignLast; 1.2020 + uint8_t align = isLastLine ? 1.2021 + StyleText()->mTextAlign : StyleText()->mTextAlignLast; 1.2022 + 1.2023 + if (line->mWritingMode.IsVertical() || 1.2024 + !line->mWritingMode.IsBidiLTR() || 1.2025 + !IsAlignedLeft(align, 1.2026 + aState.mReflowState.mStyleVisibility->mDirection, 1.2027 + StyleTextReset()->mUnicodeBidi, this)) { 1.2028 + line->MarkDirty(); 1.2029 + } 1.2030 + } 1.2031 + 1.2032 + if (needToRecoverState && line->IsDirty()) { 1.2033 + // We need to reconstruct the bottom margin only if we didn't 1.2034 + // reflow the previous line and we do need to reflow (or repair 1.2035 + // the top position of) the next line. 1.2036 + aState.ReconstructMarginAbove(line); 1.2037 + } 1.2038 + 1.2039 + bool reflowedPrevLine = !needToRecoverState; 1.2040 + if (needToRecoverState) { 1.2041 + needToRecoverState = false; 1.2042 + 1.2043 + // Update aState.mPrevChild as if we had reflowed all of the frames in 1.2044 + // this line. 1.2045 + if (line->IsDirty()) { 1.2046 + NS_ASSERTION(line->mFirstChild->GetPrevSibling() == 1.2047 + line.prev()->LastChild(), "unexpected line frames"); 1.2048 + aState.mPrevChild = line->mFirstChild->GetPrevSibling(); 1.2049 + } 1.2050 + } 1.2051 + 1.2052 + // Now repair the line and update |aState.mY| by calling 1.2053 + // |ReflowLine| or |SlideLine|. 1.2054 + // If we're going to reflow everything again, then no need to reflow 1.2055 + // the dirty line ... unless the line has floats, in which case we'd 1.2056 + // better reflow it now to refresh its float cache, which may contain 1.2057 + // dangling frame pointers! Ugh! This reflow of the line may be 1.2058 + // incorrect because we skipped reflowing previous lines (e.g., floats 1.2059 + // may be placed incorrectly), but that's OK because we'll mark the 1.2060 + // line dirty below under "if (aState.mReflowState.mDiscoveredClearance..." 1.2061 + if (line->IsDirty() && (line->HasFloats() || !willReflowAgain)) { 1.2062 + lastLineMovedUp = true; 1.2063 + 1.2064 + bool maybeReflowingForFirstTime = 1.2065 + line->IStart() == 0 && line->BStart() == 0 && 1.2066 + line->ISize() == 0 && line->BSize() == 0; 1.2067 + 1.2068 + // Compute the dirty lines "before" BEnd, after factoring in 1.2069 + // the running deltaY value - the running value is implicit in 1.2070 + // aState.mY. 1.2071 + nscoord oldY = line->BStart(); 1.2072 + nscoord oldYMost = line->BEnd(); 1.2073 + 1.2074 + NS_ASSERTION(!willReflowAgain || !line->IsBlock(), 1.2075 + "Don't reflow blocks while willReflowAgain is true, reflow of block abs-pos children depends on this"); 1.2076 + 1.2077 + // Reflow the dirty line. If it's an incremental reflow, then force 1.2078 + // it to invalidate the dirty area if necessary 1.2079 + rv = ReflowLine(aState, line, &keepGoing); 1.2080 + NS_ENSURE_SUCCESS(rv, rv); 1.2081 + 1.2082 + if (aState.mReflowState.WillReflowAgainForClearance()) { 1.2083 + line->MarkDirty(); 1.2084 + willReflowAgain = true; 1.2085 + // Note that once we've entered this state, every line that gets here 1.2086 + // (e.g. because it has floats) gets marked dirty and reflowed again. 1.2087 + // in the next pass. This is important, see above. 1.2088 + } 1.2089 + 1.2090 + if (line->HasFloats()) { 1.2091 + reflowedFloat = true; 1.2092 + } 1.2093 + 1.2094 + if (!keepGoing) { 1.2095 + DumpLine(aState, line, deltaY, -1); 1.2096 + if (0 == line->GetChildCount()) { 1.2097 + DeleteLine(aState, line, line_end); 1.2098 + } 1.2099 + break; 1.2100 + } 1.2101 + 1.2102 + // Test to see whether the margin that should be carried out 1.2103 + // to the next line (NL) might have changed. In ReflowBlockFrame 1.2104 + // we call nextLine->MarkPreviousMarginDirty if the block's 1.2105 + // actual carried-out bottom margin changed. So here we only 1.2106 + // need to worry about the following effects: 1.2107 + // 1) the line was just created, and it might now be blocking 1.2108 + // a carried-out bottom margin from previous lines that 1.2109 + // used to reach NL from reaching NL 1.2110 + // 2) the line used to be empty, and is now not empty, 1.2111 + // thus blocking a carried-out bottom margin from previous lines 1.2112 + // that used to reach NL from reaching NL 1.2113 + // 3) the line wasn't empty, but now is, so a carried-out 1.2114 + // bottom margin from previous lines that didn't used to reach NL 1.2115 + // now does 1.2116 + // 4) the line might have changed in a way that affects NL's 1.2117 + // ShouldApplyTopMargin decision. The three things that matter 1.2118 + // are the line's emptiness, its adjacency to the top of the block, 1.2119 + // and whether it has clearance (the latter only matters if the block 1.2120 + // was and is adjacent to the top and empty). 1.2121 + // 1.2122 + // If the line is empty now, we can't reliably tell if the line was empty 1.2123 + // before, so we just assume it was and do nextLine->MarkPreviousMarginDirty. 1.2124 + // This means the checks in 4) are redundant; if the line is empty now 1.2125 + // we don't need to check 4), but if the line is not empty now and we're sure 1.2126 + // it wasn't empty before, any adjacency and clearance changes are irrelevant 1.2127 + // to the result of nextLine->ShouldApplyTopMargin. 1.2128 + if (line.next() != end_lines()) { 1.2129 + bool maybeWasEmpty = oldY == line.next()->BStart(); 1.2130 + bool isEmpty = line->CachedIsEmpty(); 1.2131 + if (maybeReflowingForFirstTime /*1*/ || 1.2132 + (isEmpty || maybeWasEmpty) /*2/3/4*/) { 1.2133 + line.next()->MarkPreviousMarginDirty(); 1.2134 + // since it's marked dirty, nobody will care about |deltaY| 1.2135 + } 1.2136 + } 1.2137 + 1.2138 + // If the line was just reflowed for the first time, then its 1.2139 + // old mBounds cannot be trusted so this deltaY computation is 1.2140 + // bogus. But that's OK because we just did 1.2141 + // MarkPreviousMarginDirty on the next line which will force it 1.2142 + // to be reflowed, so this computation of deltaY will not be 1.2143 + // used. 1.2144 + deltaY = line->BEnd() - oldYMost; 1.2145 + 1.2146 + // Now do an interrupt check. We want to do this only in the case when we 1.2147 + // actually reflow the line, so that if we get back in here we'll get 1.2148 + // further on the reflow before interrupting. 1.2149 + aState.mPresContext->CheckForInterrupt(this); 1.2150 + } else { 1.2151 + aState.mOverflowTracker->Skip(line->mFirstChild, aState.mReflowStatus); 1.2152 + // Nop except for blocks (we don't create overflow container 1.2153 + // continuations for any inlines atm), so only checking mFirstChild 1.2154 + // is enough 1.2155 + 1.2156 + lastLineMovedUp = deltaY < 0; 1.2157 + 1.2158 + if (deltaY != 0) 1.2159 + SlideLine(aState, line, deltaY); 1.2160 + else 1.2161 + repositionViews = true; 1.2162 + 1.2163 + NS_ASSERTION(!line->IsDirty() || !line->HasFloats(), 1.2164 + "Possibly stale float cache here!"); 1.2165 + if (willReflowAgain && line->IsBlock()) { 1.2166 + // If we're going to reflow everything again, and this line is a block, 1.2167 + // then there is no need to recover float state. The line may contain 1.2168 + // other lines with floats, but in that case RecoverStateFrom would only 1.2169 + // add floats to the float manager. We don't need to do that because 1.2170 + // everything's going to get reflowed again "for real". Calling 1.2171 + // RecoverStateFrom in this situation could be lethal because the 1.2172 + // block's descendant lines may have float caches containing dangling 1.2173 + // frame pointers. Ugh! 1.2174 + // If this line is inline, then we need to recover its state now 1.2175 + // to make sure that we don't forget to move its floats by deltaY. 1.2176 + } else { 1.2177 + // XXX EVIL O(N^2) EVIL 1.2178 + aState.RecoverStateFrom(line, deltaY); 1.2179 + } 1.2180 + 1.2181 + // Keep mY up to date in case we're propagating reflow damage 1.2182 + // and also because our final height may depend on it. If the 1.2183 + // line is inlines, then only update mY if the line is not 1.2184 + // empty, because that's what PlaceLine does. (Empty blocks may 1.2185 + // want to update mY, e.g. if they have clearance.) 1.2186 + if (line->IsBlock() || !line->CachedIsEmpty()) { 1.2187 + aState.mY = line->BEnd(); 1.2188 + } 1.2189 + 1.2190 + needToRecoverState = true; 1.2191 + 1.2192 + if (reflowedPrevLine && !line->IsBlock() && 1.2193 + aState.mPresContext->HasPendingInterrupt()) { 1.2194 + // Need to make sure to pull overflows from any prev-in-flows 1.2195 + for (nsIFrame* inlineKid = line->mFirstChild; inlineKid; 1.2196 + inlineKid = inlineKid->GetFirstPrincipalChild()) { 1.2197 + inlineKid->PullOverflowsFromPrevInFlow(); 1.2198 + } 1.2199 + } 1.2200 + } 1.2201 + 1.2202 + // Record if we need to clear floats before reflowing the next 1.2203 + // line. Note that inlineFloatBreakType will be handled and 1.2204 + // cleared before the next line is processed, so there is no 1.2205 + // need to combine break types here. 1.2206 + if (line->HasFloatBreakAfter()) { 1.2207 + inlineFloatBreakType = line->GetBreakTypeAfter(); 1.2208 + } 1.2209 + 1.2210 + if (LineHasClear(line.get())) { 1.2211 + foundAnyClears = true; 1.2212 + } 1.2213 + 1.2214 + DumpLine(aState, line, deltaY, -1); 1.2215 + 1.2216 + if (aState.mPresContext->HasPendingInterrupt()) { 1.2217 + willReflowAgain = true; 1.2218 + // Another option here might be to leave |line| clean if 1.2219 + // !HasPendingInterrupt() before the CheckForInterrupt() call, since in 1.2220 + // that case the line really did reflow as it should have. Not sure 1.2221 + // whether that would be safe, so doing this for now instead. Also not 1.2222 + // sure whether we really want to mark all lines dirty after an 1.2223 + // interrupt, but until we get better at propagating float damage we 1.2224 + // really do need to do it this way; see comments inside MarkLineDirty. 1.2225 + MarkLineDirtyForInterrupt(line); 1.2226 + } 1.2227 + } 1.2228 + 1.2229 + // Handle BR-clearance from the last line of the block 1.2230 + if (inlineFloatBreakType != NS_STYLE_CLEAR_NONE) { 1.2231 + aState.mY = aState.ClearFloats(aState.mY, inlineFloatBreakType); 1.2232 + } 1.2233 + 1.2234 + if (needToRecoverState) { 1.2235 + // Is this expensive? 1.2236 + aState.ReconstructMarginAbove(line); 1.2237 + 1.2238 + // Update aState.mPrevChild as if we had reflowed all of the frames in 1.2239 + // the last line. 1.2240 + NS_ASSERTION(line == line_end || line->mFirstChild->GetPrevSibling() == 1.2241 + line.prev()->LastChild(), "unexpected line frames"); 1.2242 + aState.mPrevChild = 1.2243 + line == line_end ? mFrames.LastChild() : line->mFirstChild->GetPrevSibling(); 1.2244 + } 1.2245 + 1.2246 + // Should we really have to do this? 1.2247 + if (repositionViews) 1.2248 + nsContainerFrame::PlaceFrameView(this); 1.2249 + 1.2250 + // We can skip trying to pull up the next line if our height is constrained 1.2251 + // (so we can report being incomplete) and there is no next in flow or we 1.2252 + // were told not to or we know it will be futile, i.e., 1.2253 + // -- the next in flow is not changing 1.2254 + // -- and we cannot have added more space for its first line to be 1.2255 + // pulled up into, 1.2256 + // -- it's an incremental reflow of a descendant 1.2257 + // -- and we didn't reflow any floats (so the available space 1.2258 + // didn't change) 1.2259 + // -- my chain of next-in-flows either has no first line, or its first 1.2260 + // line isn't dirty. 1.2261 + bool heightConstrained = 1.2262 + aState.mReflowState.AvailableHeight() != NS_UNCONSTRAINEDSIZE; 1.2263 + bool skipPull = willReflowAgain && heightConstrained; 1.2264 + if (!skipPull && heightConstrained && aState.mNextInFlow && 1.2265 + (aState.mReflowState.mFlags.mNextInFlowUntouched && 1.2266 + !lastLineMovedUp && 1.2267 + !(GetStateBits() & NS_FRAME_IS_DIRTY) && 1.2268 + !reflowedFloat)) { 1.2269 + // We'll place lineIter at the last line of this block, so that 1.2270 + // nsBlockInFlowLineIterator::Next() will take us to the first 1.2271 + // line of my next-in-flow-chain. (But first, check that I 1.2272 + // have any lines -- if I don't, just bail out of this 1.2273 + // optimization.) 1.2274 + line_iterator lineIter = this->end_lines(); 1.2275 + if (lineIter != this->begin_lines()) { 1.2276 + lineIter--; // I have lines; step back from dummy iterator to last line. 1.2277 + nsBlockInFlowLineIterator bifLineIter(this, lineIter); 1.2278 + 1.2279 + // Check for next-in-flow-chain's first line. 1.2280 + // (First, see if there is such a line, and second, see if it's clean) 1.2281 + if (!bifLineIter.Next() || 1.2282 + !bifLineIter.GetLine()->IsDirty()) { 1.2283 + skipPull=true; 1.2284 + } 1.2285 + } 1.2286 + } 1.2287 + 1.2288 + if (skipPull && aState.mNextInFlow) { 1.2289 + NS_ASSERTION(heightConstrained, "Height should be constrained here\n"); 1.2290 + if (IS_TRUE_OVERFLOW_CONTAINER(aState.mNextInFlow)) 1.2291 + NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState.mReflowStatus); 1.2292 + else 1.2293 + NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus); 1.2294 + } 1.2295 + 1.2296 + if (!skipPull && aState.mNextInFlow) { 1.2297 + // Pull data from a next-in-flow if there's still room for more 1.2298 + // content here. 1.2299 + while (keepGoing && aState.mNextInFlow) { 1.2300 + // Grab first line from our next-in-flow 1.2301 + nsBlockFrame* nextInFlow = aState.mNextInFlow; 1.2302 + nsLineBox* pulledLine; 1.2303 + nsFrameList pulledFrames; 1.2304 + if (!nextInFlow->mLines.empty()) { 1.2305 + RemoveFirstLine(nextInFlow->mLines, nextInFlow->mFrames, 1.2306 + &pulledLine, &pulledFrames); 1.2307 + } else { 1.2308 + // Grab an overflow line if there are any 1.2309 + FrameLines* overflowLines = nextInFlow->GetOverflowLines(); 1.2310 + if (!overflowLines) { 1.2311 + aState.mNextInFlow = 1.2312 + static_cast<nsBlockFrame*>(nextInFlow->GetNextInFlow()); 1.2313 + continue; 1.2314 + } 1.2315 + bool last = 1.2316 + RemoveFirstLine(overflowLines->mLines, overflowLines->mFrames, 1.2317 + &pulledLine, &pulledFrames); 1.2318 + if (last) { 1.2319 + nextInFlow->DestroyOverflowLines(); 1.2320 + } 1.2321 + } 1.2322 + 1.2323 + if (pulledFrames.IsEmpty()) { 1.2324 + // The line is empty. Try the next one. 1.2325 + NS_ASSERTION(pulledLine->GetChildCount() == 0 && 1.2326 + !pulledLine->mFirstChild, "bad empty line"); 1.2327 + nextInFlow->FreeLineBox(pulledLine); 1.2328 + continue; 1.2329 + } 1.2330 + 1.2331 + if (pulledLine == nextInFlow->GetLineCursor()) { 1.2332 + nextInFlow->ClearLineCursor(); 1.2333 + } 1.2334 + ReparentFrames(pulledFrames, nextInFlow, this); 1.2335 + 1.2336 + NS_ASSERTION(pulledFrames.LastChild() == pulledLine->LastChild(), 1.2337 + "Unexpected last frame"); 1.2338 + NS_ASSERTION(aState.mPrevChild || mLines.empty(), "should have a prevchild here"); 1.2339 + NS_ASSERTION(aState.mPrevChild == mFrames.LastChild(), 1.2340 + "Incorrect aState.mPrevChild before inserting line at end"); 1.2341 + 1.2342 + // Shift pulledLine's frames into our mFrames list. 1.2343 + mFrames.AppendFrames(nullptr, pulledFrames); 1.2344 + 1.2345 + // Add line to our line list, and set its last child as our new prev-child 1.2346 + line = mLines.before_insert(end_lines(), pulledLine); 1.2347 + aState.mPrevChild = mFrames.LastChild(); 1.2348 + 1.2349 + // Reparent floats whose placeholders are in the line. 1.2350 + ReparentFloats(pulledLine->mFirstChild, nextInFlow, true); 1.2351 + 1.2352 + DumpLine(aState, pulledLine, deltaY, 0); 1.2353 +#ifdef DEBUG 1.2354 + AutoNoisyIndenter indent2(gNoisyReflow); 1.2355 +#endif 1.2356 + 1.2357 + if (aState.mPresContext->HasPendingInterrupt()) { 1.2358 + MarkLineDirtyForInterrupt(line); 1.2359 + } else { 1.2360 + // Now reflow it and any lines that it makes during it's reflow 1.2361 + // (we have to loop here because reflowing the line may cause a new 1.2362 + // line to be created; see SplitLine's callers for examples of 1.2363 + // when this happens). 1.2364 + while (line != end_lines()) { 1.2365 + rv = ReflowLine(aState, line, &keepGoing); 1.2366 + NS_ENSURE_SUCCESS(rv, rv); 1.2367 + 1.2368 + if (aState.mReflowState.WillReflowAgainForClearance()) { 1.2369 + line->MarkDirty(); 1.2370 + keepGoing = false; 1.2371 + NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus); 1.2372 + break; 1.2373 + } 1.2374 + 1.2375 + DumpLine(aState, line, deltaY, -1); 1.2376 + if (!keepGoing) { 1.2377 + if (0 == line->GetChildCount()) { 1.2378 + DeleteLine(aState, line, line_end); 1.2379 + } 1.2380 + break; 1.2381 + } 1.2382 + 1.2383 + if (LineHasClear(line.get())) { 1.2384 + foundAnyClears = true; 1.2385 + } 1.2386 + 1.2387 + if (aState.mPresContext->CheckForInterrupt(this)) { 1.2388 + MarkLineDirtyForInterrupt(line); 1.2389 + break; 1.2390 + } 1.2391 + 1.2392 + // If this is an inline frame then its time to stop 1.2393 + ++line; 1.2394 + aState.AdvanceToNextLine(); 1.2395 + } 1.2396 + } 1.2397 + } 1.2398 + 1.2399 + if (NS_FRAME_IS_NOT_COMPLETE(aState.mReflowStatus)) { 1.2400 + aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW; 1.2401 + } //XXXfr shouldn't set this flag when nextinflow has no lines 1.2402 + } 1.2403 + 1.2404 + // Handle an odd-ball case: a list-item with no lines 1.2405 + if (HasOutsideBullet() && mLines.empty()) { 1.2406 + nsHTMLReflowMetrics metrics(aState.mReflowState); 1.2407 + nsIFrame* bullet = GetOutsideBullet(); 1.2408 + ReflowBullet(bullet, aState, metrics, 1.2409 + aState.mReflowState.ComputedPhysicalBorderPadding().top); 1.2410 + NS_ASSERTION(!BulletIsEmpty() || metrics.Height() == 0, 1.2411 + "empty bullet took up space"); 1.2412 + 1.2413 + if (!BulletIsEmpty()) { 1.2414 + // There are no lines so we have to fake up some y motion so that 1.2415 + // we end up with *some* height. 1.2416 + 1.2417 + if (metrics.TopAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) { 1.2418 + nscoord ascent; 1.2419 + if (nsLayoutUtils::GetFirstLineBaseline(bullet, &ascent)) { 1.2420 + metrics.SetTopAscent(ascent); 1.2421 + } else { 1.2422 + metrics.SetTopAscent(metrics.Height()); 1.2423 + } 1.2424 + } 1.2425 + 1.2426 + nsRefPtr<nsFontMetrics> fm; 1.2427 + nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm), 1.2428 + nsLayoutUtils::FontSizeInflationFor(this)); 1.2429 + aState.mReflowState.rendContext->SetFont(fm); // FIXME: needed? 1.2430 + 1.2431 + nscoord minAscent = 1.2432 + nsLayoutUtils::GetCenteredFontBaseline(fm, aState.mMinLineHeight); 1.2433 + nscoord minDescent = aState.mMinLineHeight - minAscent; 1.2434 + 1.2435 + aState.mY += std::max(minAscent, metrics.TopAscent()) + 1.2436 + std::max(minDescent, metrics.Height() - metrics.TopAscent()); 1.2437 + 1.2438 + nscoord offset = minAscent - metrics.TopAscent(); 1.2439 + if (offset > 0) { 1.2440 + bullet->SetRect(bullet->GetRect() + nsPoint(0, offset)); 1.2441 + } 1.2442 + } 1.2443 + } 1.2444 + 1.2445 + if (foundAnyClears) { 1.2446 + AddStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN); 1.2447 + } else { 1.2448 + RemoveStateBits(NS_BLOCK_HAS_CLEAR_CHILDREN); 1.2449 + } 1.2450 + 1.2451 +#ifdef DEBUG 1.2452 + VerifyLines(true); 1.2453 + VerifyOverflowSituation(); 1.2454 + if (gNoisyReflow) { 1.2455 + IndentBy(stdout, gNoiseIndent - 1); 1.2456 + ListTag(stdout); 1.2457 + printf(": done reflowing dirty lines (status=%x)\n", 1.2458 + aState.mReflowStatus); 1.2459 + } 1.2460 +#endif 1.2461 + 1.2462 + return rv; 1.2463 +} 1.2464 + 1.2465 +static void MarkAllDescendantLinesDirty(nsBlockFrame* aBlock) 1.2466 +{ 1.2467 + nsLineList::iterator line = aBlock->begin_lines(); 1.2468 + nsLineList::iterator endLine = aBlock->end_lines(); 1.2469 + while (line != endLine) { 1.2470 + if (line->IsBlock()) { 1.2471 + nsIFrame* f = line->mFirstChild; 1.2472 + nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(f); 1.2473 + if (bf) { 1.2474 + MarkAllDescendantLinesDirty(bf); 1.2475 + } 1.2476 + } 1.2477 + line->MarkDirty(); 1.2478 + ++line; 1.2479 + } 1.2480 +} 1.2481 + 1.2482 +void 1.2483 +nsBlockFrame::MarkLineDirtyForInterrupt(nsLineBox* aLine) 1.2484 +{ 1.2485 + aLine->MarkDirty(); 1.2486 + 1.2487 + // Just checking NS_FRAME_IS_DIRTY is ok, because we've already 1.2488 + // marked the lines that need to be marked dirty based on our 1.2489 + // vertical resize stuff. So we'll definitely reflow all those kids; 1.2490 + // the only question is how they should behave. 1.2491 + if (GetStateBits() & NS_FRAME_IS_DIRTY) { 1.2492 + // Mark all our child frames dirty so we make sure to reflow them 1.2493 + // later. 1.2494 + int32_t n = aLine->GetChildCount(); 1.2495 + for (nsIFrame* f = aLine->mFirstChild; n > 0; 1.2496 + f = f->GetNextSibling(), --n) { 1.2497 + f->AddStateBits(NS_FRAME_IS_DIRTY); 1.2498 + } 1.2499 + // And mark all the floats whose reflows we might be skipping dirty too. 1.2500 + if (aLine->HasFloats()) { 1.2501 + for (nsFloatCache* fc = aLine->GetFirstFloat(); fc; fc = fc->Next()) { 1.2502 + fc->mFloat->AddStateBits(NS_FRAME_IS_DIRTY); 1.2503 + } 1.2504 + } 1.2505 + } else { 1.2506 + // Dirty all the descendant lines of block kids to handle float damage, 1.2507 + // since our nsFloatManager will go away by the next time we're reflowing. 1.2508 + // XXXbz Can we do something more like what PropagateFloatDamage does? 1.2509 + // Would need to sort out the exact business with mBlockDelta for that.... 1.2510 + // This marks way too much dirty. If we ever make this better, revisit 1.2511 + // which lines we mark dirty in the interrupt case in ReflowDirtyLines. 1.2512 + nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(aLine->mFirstChild); 1.2513 + if (bf) { 1.2514 + MarkAllDescendantLinesDirty(bf); 1.2515 + } 1.2516 + } 1.2517 +} 1.2518 + 1.2519 +void 1.2520 +nsBlockFrame::DeleteLine(nsBlockReflowState& aState, 1.2521 + nsLineList::iterator aLine, 1.2522 + nsLineList::iterator aLineEnd) 1.2523 +{ 1.2524 + NS_PRECONDITION(0 == aLine->GetChildCount(), "can't delete !empty line"); 1.2525 + if (0 == aLine->GetChildCount()) { 1.2526 + NS_ASSERTION(aState.mCurrentLine == aLine, 1.2527 + "using function more generally than designed, " 1.2528 + "but perhaps OK now"); 1.2529 + nsLineBox* line = aLine; 1.2530 + aLine = mLines.erase(aLine); 1.2531 + FreeLineBox(line); 1.2532 + // Mark the previous margin of the next line dirty since we need to 1.2533 + // recompute its top position. 1.2534 + if (aLine != aLineEnd) 1.2535 + aLine->MarkPreviousMarginDirty(); 1.2536 + } 1.2537 +} 1.2538 + 1.2539 +/** 1.2540 + * Reflow a line. The line will either contain a single block frame 1.2541 + * or contain 1 or more inline frames. aKeepReflowGoing indicates 1.2542 + * whether or not the caller should continue to reflow more lines. 1.2543 + */ 1.2544 +nsresult 1.2545 +nsBlockFrame::ReflowLine(nsBlockReflowState& aState, 1.2546 + line_iterator aLine, 1.2547 + bool* aKeepReflowGoing) 1.2548 +{ 1.2549 + nsresult rv = NS_OK; 1.2550 + 1.2551 + NS_ABORT_IF_FALSE(aLine->GetChildCount(), "reflowing empty line"); 1.2552 + 1.2553 + // Setup the line-layout for the new line 1.2554 + aState.mCurrentLine = aLine; 1.2555 + aLine->ClearDirty(); 1.2556 + aLine->InvalidateCachedIsEmpty(); 1.2557 + aLine->ClearHadFloatPushed(); 1.2558 + 1.2559 + // Now that we know what kind of line we have, reflow it 1.2560 + if (aLine->IsBlock()) { 1.2561 + rv = ReflowBlockFrame(aState, aLine, aKeepReflowGoing); 1.2562 + } else { 1.2563 + aLine->SetLineWrapped(false); 1.2564 + rv = ReflowInlineFrames(aState, aLine, aKeepReflowGoing); 1.2565 + } 1.2566 + 1.2567 + return rv; 1.2568 +} 1.2569 + 1.2570 +nsIFrame* 1.2571 +nsBlockFrame::PullFrame(nsBlockReflowState& aState, 1.2572 + line_iterator aLine) 1.2573 +{ 1.2574 + // First check our remaining lines. 1.2575 + if (end_lines() != aLine.next()) { 1.2576 + return PullFrameFrom(aLine, this, aLine.next()); 1.2577 + } 1.2578 + 1.2579 + NS_ASSERTION(!GetOverflowLines(), 1.2580 + "Our overflow lines should have been removed at the start of reflow"); 1.2581 + 1.2582 + // Try each next-in-flow. 1.2583 + nsBlockFrame* nextInFlow = aState.mNextInFlow; 1.2584 + while (nextInFlow) { 1.2585 + if (nextInFlow->mLines.empty()) { 1.2586 + nextInFlow->DrainSelfOverflowList(); 1.2587 + } 1.2588 + if (!nextInFlow->mLines.empty()) { 1.2589 + return PullFrameFrom(aLine, nextInFlow, nextInFlow->mLines.begin()); 1.2590 + } 1.2591 + nextInFlow = static_cast<nsBlockFrame*>(nextInFlow->GetNextInFlow()); 1.2592 + aState.mNextInFlow = nextInFlow; 1.2593 + } 1.2594 + 1.2595 + return nullptr; 1.2596 +} 1.2597 + 1.2598 +nsIFrame* 1.2599 +nsBlockFrame::PullFrameFrom(nsLineBox* aLine, 1.2600 + nsBlockFrame* aFromContainer, 1.2601 + nsLineList::iterator aFromLine) 1.2602 +{ 1.2603 + nsLineBox* fromLine = aFromLine; 1.2604 + NS_ABORT_IF_FALSE(fromLine, "bad line to pull from"); 1.2605 + NS_ABORT_IF_FALSE(fromLine->GetChildCount(), "empty line"); 1.2606 + NS_ABORT_IF_FALSE(aLine->GetChildCount(), "empty line"); 1.2607 + 1.2608 + NS_ASSERTION(fromLine->IsBlock() == fromLine->mFirstChild->IsBlockOutside(), 1.2609 + "Disagreement about whether it's a block or not"); 1.2610 + 1.2611 + if (fromLine->IsBlock()) { 1.2612 + // If our line is not empty and the child in aFromLine is a block 1.2613 + // then we cannot pull up the frame into this line. In this case 1.2614 + // we stop pulling. 1.2615 + return nullptr; 1.2616 + } 1.2617 + // Take frame from fromLine 1.2618 + nsIFrame* frame = fromLine->mFirstChild; 1.2619 + nsIFrame* newFirstChild = frame->GetNextSibling(); 1.2620 + 1.2621 + if (aFromContainer != this) { 1.2622 + // The frame is being pulled from a next-in-flow; therefore we 1.2623 + // need to add it to our sibling list. 1.2624 + MOZ_ASSERT(aLine == mLines.back()); 1.2625 + MOZ_ASSERT(aFromLine == aFromContainer->mLines.begin(), 1.2626 + "should only pull from first line"); 1.2627 + aFromContainer->mFrames.RemoveFrame(frame); 1.2628 + 1.2629 + // When pushing and pulling frames we need to check for whether any 1.2630 + // views need to be reparented. 1.2631 + ReparentFrame(frame, aFromContainer, this); 1.2632 + mFrames.AppendFrame(nullptr, frame); 1.2633 + 1.2634 + // The frame might have (or contain) floats that need to be brought 1.2635 + // over too. (pass 'false' since there are no siblings to check) 1.2636 + ReparentFloats(frame, aFromContainer, false); 1.2637 + } else { 1.2638 + MOZ_ASSERT(aLine == aFromLine.prev()); 1.2639 + } 1.2640 + 1.2641 + aLine->NoteFrameAdded(frame); 1.2642 + fromLine->NoteFrameRemoved(frame); 1.2643 + 1.2644 + if (fromLine->GetChildCount() > 0) { 1.2645 + // Mark line dirty now that we pulled a child 1.2646 + fromLine->MarkDirty(); 1.2647 + fromLine->mFirstChild = newFirstChild; 1.2648 + } else { 1.2649 + // Free up the fromLine now that it's empty. 1.2650 + // Its bounds might need to be redrawn, though. 1.2651 + if (aFromLine.next() != aFromContainer->mLines.end()) { 1.2652 + aFromLine.next()->MarkPreviousMarginDirty(); 1.2653 + } 1.2654 + aFromContainer->mLines.erase(aFromLine); 1.2655 + // aFromLine is now invalid 1.2656 + aFromContainer->FreeLineBox(fromLine); 1.2657 + } 1.2658 + 1.2659 +#ifdef DEBUG 1.2660 + VerifyLines(true); 1.2661 + VerifyOverflowSituation(); 1.2662 +#endif 1.2663 + 1.2664 + return frame; 1.2665 +} 1.2666 + 1.2667 +void 1.2668 +nsBlockFrame::SlideLine(nsBlockReflowState& aState, 1.2669 + nsLineBox* aLine, nscoord aDY) 1.2670 +{ 1.2671 + NS_PRECONDITION(aDY != 0, "why slide a line nowhere?"); 1.2672 + 1.2673 + // Adjust line state 1.2674 + aLine->SlideBy(aDY, aState.mContainerWidth); 1.2675 + 1.2676 + // Adjust the frames in the line 1.2677 + nsIFrame* kid = aLine->mFirstChild; 1.2678 + if (!kid) { 1.2679 + return; 1.2680 + } 1.2681 + 1.2682 + if (aLine->IsBlock()) { 1.2683 + if (aDY) { 1.2684 + kid->MovePositionBy(nsPoint(0, aDY)); 1.2685 + } 1.2686 + 1.2687 + // Make sure the frame's view and any child views are updated 1.2688 + nsContainerFrame::PlaceFrameView(kid); 1.2689 + } 1.2690 + else { 1.2691 + // Adjust the Y coordinate of the frames in the line. 1.2692 + // Note: we need to re-position views even if aDY is 0, because 1.2693 + // one of our parent frames may have moved and so the view's position 1.2694 + // relative to its parent may have changed 1.2695 + int32_t n = aLine->GetChildCount(); 1.2696 + while (--n >= 0) { 1.2697 + if (aDY) { 1.2698 + kid->MovePositionBy(nsPoint(0, aDY)); 1.2699 + } 1.2700 + // Make sure the frame's view and any child views are updated 1.2701 + nsContainerFrame::PlaceFrameView(kid); 1.2702 + kid = kid->GetNextSibling(); 1.2703 + } 1.2704 + } 1.2705 +} 1.2706 + 1.2707 +nsresult 1.2708 +nsBlockFrame::AttributeChanged(int32_t aNameSpaceID, 1.2709 + nsIAtom* aAttribute, 1.2710 + int32_t aModType) 1.2711 +{ 1.2712 + nsresult rv = nsBlockFrameSuper::AttributeChanged(aNameSpaceID, 1.2713 + aAttribute, aModType); 1.2714 + 1.2715 + if (NS_FAILED(rv)) { 1.2716 + return rv; 1.2717 + } 1.2718 + if (nsGkAtoms::start == aAttribute || 1.2719 + (nsGkAtoms::reversed == aAttribute && mContent->IsHTML(nsGkAtoms::ol))) { 1.2720 + nsPresContext* presContext = PresContext(); 1.2721 + 1.2722 + // XXX Not sure if this is necessary anymore 1.2723 + if (RenumberLists(presContext)) { 1.2724 + presContext->PresShell()-> 1.2725 + FrameNeedsReflow(this, nsIPresShell::eStyleChange, 1.2726 + NS_FRAME_HAS_DIRTY_CHILDREN); 1.2727 + } 1.2728 + } 1.2729 + else if (nsGkAtoms::value == aAttribute) { 1.2730 + const nsStyleDisplay* styleDisplay = StyleDisplay(); 1.2731 + if (NS_STYLE_DISPLAY_LIST_ITEM == styleDisplay->mDisplay) { 1.2732 + // Search for the closest ancestor that's a block frame. We 1.2733 + // make the assumption that all related list items share a 1.2734 + // common block parent. 1.2735 + // XXXldb I think that's a bad assumption. 1.2736 + nsBlockFrame* blockParent = nsLayoutUtils::FindNearestBlockAncestor(this); 1.2737 + 1.2738 + // Tell the enclosing block frame to renumber list items within 1.2739 + // itself 1.2740 + if (nullptr != blockParent) { 1.2741 + nsPresContext* presContext = PresContext(); 1.2742 + // XXX Not sure if this is necessary anymore 1.2743 + if (blockParent->RenumberLists(presContext)) { 1.2744 + presContext->PresShell()-> 1.2745 + FrameNeedsReflow(blockParent, nsIPresShell::eStyleChange, 1.2746 + NS_FRAME_HAS_DIRTY_CHILDREN); 1.2747 + } 1.2748 + } 1.2749 + } 1.2750 + } 1.2751 + 1.2752 + return rv; 1.2753 +} 1.2754 + 1.2755 +static inline bool 1.2756 +IsNonAutoNonZeroHeight(const nsStyleCoord& aCoord) 1.2757 +{ 1.2758 + if (aCoord.GetUnit() == eStyleUnit_Auto) 1.2759 + return false; 1.2760 + if (aCoord.IsCoordPercentCalcUnit()) { 1.2761 + // If we evaluate the length/percent/calc at a percentage basis of 1.2762 + // both nscoord_MAX and 0, and it's zero both ways, then it's a zero 1.2763 + // length, percent, or combination thereof. Test > 0 so we clamp 1.2764 + // negative calc() results to 0. 1.2765 + return nsRuleNode::ComputeCoordPercentCalc(aCoord, nscoord_MAX) > 0 || 1.2766 + nsRuleNode::ComputeCoordPercentCalc(aCoord, 0) > 0; 1.2767 + } 1.2768 + NS_ABORT_IF_FALSE(false, "unexpected unit for height or min-height"); 1.2769 + return true; 1.2770 +} 1.2771 + 1.2772 +/* virtual */ bool 1.2773 +nsBlockFrame::IsSelfEmpty() 1.2774 +{ 1.2775 + // Blocks which are margin-roots (including inline-blocks) cannot be treated 1.2776 + // as empty for margin-collapsing and other purposes. They're more like 1.2777 + // replaced elements. 1.2778 + if (GetStateBits() & NS_BLOCK_MARGIN_ROOT) 1.2779 + return false; 1.2780 + 1.2781 + const nsStylePosition* position = StylePosition(); 1.2782 + 1.2783 + if (IsNonAutoNonZeroHeight(position->mMinHeight) || 1.2784 + IsNonAutoNonZeroHeight(position->mHeight)) 1.2785 + return false; 1.2786 + 1.2787 + const nsStyleBorder* border = StyleBorder(); 1.2788 + const nsStylePadding* padding = StylePadding(); 1.2789 + if (border->GetComputedBorderWidth(NS_SIDE_TOP) != 0 || 1.2790 + border->GetComputedBorderWidth(NS_SIDE_BOTTOM) != 0 || 1.2791 + !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetTop()) || 1.2792 + !nsLayoutUtils::IsPaddingZero(padding->mPadding.GetBottom())) { 1.2793 + return false; 1.2794 + } 1.2795 + 1.2796 + if (HasOutsideBullet() && !BulletIsEmpty()) { 1.2797 + return false; 1.2798 + } 1.2799 + 1.2800 + return true; 1.2801 +} 1.2802 + 1.2803 +bool 1.2804 +nsBlockFrame::CachedIsEmpty() 1.2805 +{ 1.2806 + if (!IsSelfEmpty()) { 1.2807 + return false; 1.2808 + } 1.2809 + 1.2810 + for (line_iterator line = begin_lines(), line_end = end_lines(); 1.2811 + line != line_end; 1.2812 + ++line) 1.2813 + { 1.2814 + if (!line->CachedIsEmpty()) 1.2815 + return false; 1.2816 + } 1.2817 + 1.2818 + return true; 1.2819 +} 1.2820 + 1.2821 +bool 1.2822 +nsBlockFrame::IsEmpty() 1.2823 +{ 1.2824 + if (!IsSelfEmpty()) { 1.2825 + return false; 1.2826 + } 1.2827 + 1.2828 + for (line_iterator line = begin_lines(), line_end = end_lines(); 1.2829 + line != line_end; 1.2830 + ++line) 1.2831 + { 1.2832 + if (!line->IsEmpty()) 1.2833 + return false; 1.2834 + } 1.2835 + 1.2836 + return true; 1.2837 +} 1.2838 + 1.2839 +bool 1.2840 +nsBlockFrame::ShouldApplyTopMargin(nsBlockReflowState& aState, 1.2841 + nsLineBox* aLine) 1.2842 +{ 1.2843 + if (aState.GetFlag(BRS_APPLYTOPMARGIN)) { 1.2844 + // Apply short-circuit check to avoid searching the line list 1.2845 + return true; 1.2846 + } 1.2847 + 1.2848 + if (!aState.IsAdjacentWithTop()) { 1.2849 + // If we aren't at the top Y coordinate then something of non-zero 1.2850 + // height must have been placed. Therefore the childs top-margin 1.2851 + // applies. 1.2852 + aState.SetFlag(BRS_APPLYTOPMARGIN, true); 1.2853 + return true; 1.2854 + } 1.2855 + 1.2856 + // Determine if this line is "essentially" the first line 1.2857 + line_iterator line = begin_lines(); 1.2858 + if (aState.GetFlag(BRS_HAVELINEADJACENTTOTOP)) { 1.2859 + line = aState.mLineAdjacentToTop; 1.2860 + } 1.2861 + while (line != aLine) { 1.2862 + if (!line->CachedIsEmpty() || line->HasClearance()) { 1.2863 + // A line which precedes aLine is non-empty, or has clearance, 1.2864 + // so therefore the top margin applies. 1.2865 + aState.SetFlag(BRS_APPLYTOPMARGIN, true); 1.2866 + return true; 1.2867 + } 1.2868 + // No need to apply the top margin if the line has floats. We 1.2869 + // should collapse anyway (bug 44419) 1.2870 + ++line; 1.2871 + aState.SetFlag(BRS_HAVELINEADJACENTTOTOP, true); 1.2872 + aState.mLineAdjacentToTop = line; 1.2873 + } 1.2874 + 1.2875 + // The line being reflowed is "essentially" the first line in the 1.2876 + // block. Therefore its top-margin will be collapsed by the 1.2877 + // generational collapsing logic with its parent (us). 1.2878 + return false; 1.2879 +} 1.2880 + 1.2881 +nsresult 1.2882 +nsBlockFrame::ReflowBlockFrame(nsBlockReflowState& aState, 1.2883 + line_iterator aLine, 1.2884 + bool* aKeepReflowGoing) 1.2885 +{ 1.2886 + NS_PRECONDITION(*aKeepReflowGoing, "bad caller"); 1.2887 + 1.2888 + nsresult rv = NS_OK; 1.2889 + 1.2890 + nsIFrame* frame = aLine->mFirstChild; 1.2891 + if (!frame) { 1.2892 + NS_ASSERTION(false, "program error - unexpected empty line"); 1.2893 + return NS_ERROR_NULL_POINTER; 1.2894 + } 1.2895 + 1.2896 + // Prepare the block reflow engine 1.2897 + const nsStyleDisplay* display = frame->StyleDisplay(); 1.2898 + nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState); 1.2899 + 1.2900 + uint8_t breakType = display->mBreakType; 1.2901 + if (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType) { 1.2902 + breakType = nsLayoutUtils::CombineBreakType(breakType, 1.2903 + aState.mFloatBreakType); 1.2904 + aState.mFloatBreakType = NS_STYLE_CLEAR_NONE; 1.2905 + } 1.2906 + 1.2907 + // Clear past floats before the block if the clear style is not none 1.2908 + aLine->SetBreakTypeBefore(breakType); 1.2909 + 1.2910 + // See if we should apply the top margin. If the block frame being 1.2911 + // reflowed is a continuation (non-null prev-in-flow) then we don't 1.2912 + // apply its top margin because it's not significant. Otherwise, dig 1.2913 + // deeper. 1.2914 + bool applyTopMargin = 1.2915 + !frame->GetPrevInFlow() && ShouldApplyTopMargin(aState, aLine); 1.2916 + 1.2917 + if (applyTopMargin) { 1.2918 + // The HasClearance setting is only valid if ShouldApplyTopMargin 1.2919 + // returned false (in which case the top-margin-root set our 1.2920 + // clearance flag). Otherwise clear it now. We'll set it later on 1.2921 + // ourselves if necessary. 1.2922 + aLine->ClearHasClearance(); 1.2923 + } 1.2924 + bool treatWithClearance = aLine->HasClearance(); 1.2925 + 1.2926 + bool mightClearFloats = breakType != NS_STYLE_CLEAR_NONE; 1.2927 + nsIFrame *replacedBlock = nullptr; 1.2928 + if (!nsBlockFrame::BlockCanIntersectFloats(frame)) { 1.2929 + mightClearFloats = true; 1.2930 + replacedBlock = frame; 1.2931 + } 1.2932 + 1.2933 + // If our top margin was counted as part of some parents top-margin 1.2934 + // collapse and we are being speculatively reflowed assuming this 1.2935 + // frame DID NOT need clearance, then we need to check that 1.2936 + // assumption. 1.2937 + if (!treatWithClearance && !applyTopMargin && mightClearFloats && 1.2938 + aState.mReflowState.mDiscoveredClearance) { 1.2939 + nscoord curY = aState.mY + aState.mPrevBottomMargin.get(); 1.2940 + nscoord clearY = aState.ClearFloats(curY, breakType, replacedBlock); 1.2941 + if (clearY != curY) { 1.2942 + // Looks like that assumption was invalid, we do need 1.2943 + // clearance. Tell our ancestor so it can reflow again. It is 1.2944 + // responsible for actually setting our clearance flag before 1.2945 + // the next reflow. 1.2946 + treatWithClearance = true; 1.2947 + // Only record the first frame that requires clearance 1.2948 + if (!*aState.mReflowState.mDiscoveredClearance) { 1.2949 + *aState.mReflowState.mDiscoveredClearance = frame; 1.2950 + } 1.2951 + aState.mPrevChild = frame; 1.2952 + // Exactly what we do now is flexible since we'll definitely be 1.2953 + // reflowed. 1.2954 + return NS_OK; 1.2955 + } 1.2956 + } 1.2957 + if (treatWithClearance) { 1.2958 + applyTopMargin = true; 1.2959 + } 1.2960 + 1.2961 + nsIFrame* clearanceFrame = nullptr; 1.2962 + nscoord startingY = aState.mY; 1.2963 + nsCollapsingMargin incomingMargin = aState.mPrevBottomMargin; 1.2964 + nscoord clearance; 1.2965 + // Save the original position of the frame so that we can reposition 1.2966 + // its view as needed. 1.2967 + nsPoint originalPosition = frame->GetPosition(); 1.2968 + while (true) { 1.2969 + clearance = 0; 1.2970 + nscoord topMargin = 0; 1.2971 + bool mayNeedRetry = false; 1.2972 + bool clearedFloats = false; 1.2973 + if (applyTopMargin) { 1.2974 + // Precompute the blocks top margin value so that we can get the 1.2975 + // correct available space (there might be a float that's 1.2976 + // already been placed below the aState.mPrevBottomMargin 1.2977 + 1.2978 + // Setup a reflowState to get the style computed margin-top 1.2979 + // value. We'll use a reason of `resize' so that we don't fudge 1.2980 + // any incremental reflow state. 1.2981 + 1.2982 + // The availSpace here is irrelevant to our needs - all we want 1.2983 + // out if this setup is the margin-top value which doesn't depend 1.2984 + // on the childs available space. 1.2985 + // XXX building a complete nsHTMLReflowState just to get the margin-top 1.2986 + // seems like a waste. And we do this for almost every block! 1.2987 + nsSize availSpace(aState.mContentArea.width, NS_UNCONSTRAINEDSIZE); 1.2988 + nsHTMLReflowState reflowState(aState.mPresContext, aState.mReflowState, 1.2989 + frame, availSpace); 1.2990 + 1.2991 + if (treatWithClearance) { 1.2992 + aState.mY += aState.mPrevBottomMargin.get(); 1.2993 + aState.mPrevBottomMargin.Zero(); 1.2994 + } 1.2995 + 1.2996 + // Now compute the collapsed margin-top value into aState.mPrevBottomMargin, assuming 1.2997 + // that all child margins collapse down to clearanceFrame. 1.2998 + nsBlockReflowContext::ComputeCollapsedTopMargin(reflowState, 1.2999 + &aState.mPrevBottomMargin, clearanceFrame, &mayNeedRetry); 1.3000 + 1.3001 + // XXX optimization; we could check the collapsing children to see if they are sure 1.3002 + // to require clearance, and so avoid retrying them 1.3003 + 1.3004 + if (clearanceFrame) { 1.3005 + // Don't allow retries on the second pass. The clearance decisions for the 1.3006 + // blocks whose top-margins collapse with ours are now fixed. 1.3007 + mayNeedRetry = false; 1.3008 + } 1.3009 + 1.3010 + if (!treatWithClearance && !clearanceFrame && mightClearFloats) { 1.3011 + // We don't know if we need clearance and this is the first, 1.3012 + // optimistic pass. So determine whether *this block* needs 1.3013 + // clearance. Note that we do not allow the decision for whether 1.3014 + // this block has clearance to change on the second pass; that 1.3015 + // decision is only allowed to be made under the optimistic 1.3016 + // first pass. 1.3017 + nscoord curY = aState.mY + aState.mPrevBottomMargin.get(); 1.3018 + nscoord clearY = aState.ClearFloats(curY, breakType, replacedBlock); 1.3019 + if (clearY != curY) { 1.3020 + // Looks like we need clearance and we didn't know about it already. So 1.3021 + // recompute collapsed margin 1.3022 + treatWithClearance = true; 1.3023 + // Remember this decision, needed for incremental reflow 1.3024 + aLine->SetHasClearance(); 1.3025 + 1.3026 + // Apply incoming margins 1.3027 + aState.mY += aState.mPrevBottomMargin.get(); 1.3028 + aState.mPrevBottomMargin.Zero(); 1.3029 + 1.3030 + // Compute the collapsed margin again, ignoring the incoming margin this time 1.3031 + mayNeedRetry = false; 1.3032 + nsBlockReflowContext::ComputeCollapsedTopMargin(reflowState, 1.3033 + &aState.mPrevBottomMargin, clearanceFrame, &mayNeedRetry); 1.3034 + } 1.3035 + } 1.3036 + 1.3037 + // Temporarily advance the running Y value so that the 1.3038 + // GetAvailableSpace method will return the right available 1.3039 + // space. This undone as soon as the horizontal margins are 1.3040 + // computed. 1.3041 + topMargin = aState.mPrevBottomMargin.get(); 1.3042 + 1.3043 + if (treatWithClearance) { 1.3044 + nscoord currentY = aState.mY; 1.3045 + // advance mY to the clear position. 1.3046 + aState.mY = aState.ClearFloats(aState.mY, breakType, replacedBlock); 1.3047 + 1.3048 + clearedFloats = aState.mY != currentY; 1.3049 + 1.3050 + // Compute clearance. It's the amount we need to add to the top 1.3051 + // border-edge of the frame, after applying collapsed margins 1.3052 + // from the frame and its children, to get it to line up with 1.3053 + // the bottom of the floats. The former is currentY + topMargin, 1.3054 + // the latter is the current aState.mY. 1.3055 + // Note that negative clearance is possible 1.3056 + clearance = aState.mY - (currentY + topMargin); 1.3057 + 1.3058 + // Add clearance to our top margin while we compute available 1.3059 + // space for the frame 1.3060 + topMargin += clearance; 1.3061 + 1.3062 + // Note that aState.mY should stay where it is: at the top 1.3063 + // border-edge of the frame 1.3064 + } else { 1.3065 + // Advance aState.mY to the top border-edge of the frame. 1.3066 + aState.mY += topMargin; 1.3067 + } 1.3068 + } 1.3069 + 1.3070 + // Here aState.mY is the top border-edge of the block. 1.3071 + // Compute the available space for the block 1.3072 + nsFlowAreaRect floatAvailableSpace = aState.GetFloatAvailableSpace(); 1.3073 +#ifdef REALLY_NOISY_REFLOW 1.3074 + printf("setting line %p isImpacted to %s\n", 1.3075 + aLine.get(), floatAvailableSpace.mHasFloats?"true":"false"); 1.3076 +#endif 1.3077 + aLine->SetLineIsImpactedByFloat(floatAvailableSpace.mHasFloats); 1.3078 + nsRect availSpace; 1.3079 + aState.ComputeBlockAvailSpace(frame, display, floatAvailableSpace, 1.3080 + replacedBlock != nullptr, availSpace); 1.3081 + 1.3082 + // The check for 1.3083 + // (!aState.mReflowState.mFlags.mIsTopOfPage || clearedFloats) 1.3084 + // is to some degree out of paranoia: if we reliably eat up top 1.3085 + // margins at the top of the page as we ought to, it wouldn't be 1.3086 + // needed. 1.3087 + if ((!aState.mReflowState.mFlags.mIsTopOfPage || clearedFloats) && 1.3088 + availSpace.height < 0) { 1.3089 + // We know already that this child block won't fit on this 1.3090 + // page/column due to the top margin or the clearance. So we need 1.3091 + // to get out of here now. (If we don't, most blocks will handle 1.3092 + // things fine, and report break-before, but zero-height blocks 1.3093 + // won't, and will thus make their parent overly-large and force 1.3094 + // *it* to be pushed in its entirety.) 1.3095 + // Doing this means that we also don't need to worry about the 1.3096 + // |availSpace.height += topMargin| below interacting with pushed 1.3097 + // floats (which force nscoord_MAX clearance) to cause a 1.3098 + // constrained height to turn into an unconstrained one. 1.3099 + aState.mY = startingY; 1.3100 + aState.mPrevBottomMargin = incomingMargin; 1.3101 + *aKeepReflowGoing = false; 1.3102 + if (ShouldAvoidBreakInside(aState.mReflowState)) { 1.3103 + aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE(); 1.3104 + } else { 1.3105 + PushLines(aState, aLine.prev()); 1.3106 + NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus); 1.3107 + } 1.3108 + return NS_OK; 1.3109 + } 1.3110 + 1.3111 + // Now put the Y coordinate back to the top of the top-margin + 1.3112 + // clearance, and flow the block. 1.3113 + aState.mY -= topMargin; 1.3114 + availSpace.y -= topMargin; 1.3115 + if (NS_UNCONSTRAINEDSIZE != availSpace.height) { 1.3116 + availSpace.height += topMargin; 1.3117 + } 1.3118 + 1.3119 + // Reflow the block into the available space 1.3120 + // construct the html reflow state for the block. ReflowBlock 1.3121 + // will initialize it 1.3122 + nsHTMLReflowState blockHtmlRS(aState.mPresContext, aState.mReflowState, frame, 1.3123 + availSpace.Size()); 1.3124 + blockHtmlRS.mFlags.mHasClearance = aLine->HasClearance(); 1.3125 + 1.3126 + nsFloatManager::SavedState floatManagerState; 1.3127 + if (mayNeedRetry) { 1.3128 + blockHtmlRS.mDiscoveredClearance = &clearanceFrame; 1.3129 + aState.mFloatManager->PushState(&floatManagerState); 1.3130 + } else if (!applyTopMargin) { 1.3131 + blockHtmlRS.mDiscoveredClearance = aState.mReflowState.mDiscoveredClearance; 1.3132 + } 1.3133 + 1.3134 + nsReflowStatus frameReflowStatus = NS_FRAME_COMPLETE; 1.3135 + rv = brc.ReflowBlock(availSpace, applyTopMargin, aState.mPrevBottomMargin, 1.3136 + clearance, aState.IsAdjacentWithTop(), 1.3137 + aLine.get(), blockHtmlRS, frameReflowStatus, aState); 1.3138 + 1.3139 + NS_ENSURE_SUCCESS(rv, rv); 1.3140 + 1.3141 + if (mayNeedRetry && clearanceFrame) { 1.3142 + aState.mFloatManager->PopState(&floatManagerState); 1.3143 + aState.mY = startingY; 1.3144 + aState.mPrevBottomMargin = incomingMargin; 1.3145 + continue; 1.3146 + } 1.3147 + 1.3148 + aState.mPrevChild = frame; 1.3149 + 1.3150 + if (blockHtmlRS.WillReflowAgainForClearance()) { 1.3151 + // If an ancestor of ours is going to reflow for clearance, we 1.3152 + // need to avoid calling PlaceBlock, because it unsets dirty bits 1.3153 + // on the child block (both itself, and through its call to 1.3154 + // nsFrame::DidReflow), and those dirty bits imply dirtiness for 1.3155 + // all of the child block, including the lines it didn't reflow. 1.3156 + NS_ASSERTION(originalPosition == frame->GetPosition(), 1.3157 + "we need to call PositionChildViews"); 1.3158 + return NS_OK; 1.3159 + } 1.3160 + 1.3161 +#if defined(REFLOW_STATUS_COVERAGE) 1.3162 + RecordReflowStatus(true, frameReflowStatus); 1.3163 +#endif 1.3164 + 1.3165 + if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) { 1.3166 + // None of the child block fits. 1.3167 + *aKeepReflowGoing = false; 1.3168 + if (ShouldAvoidBreakInside(aState.mReflowState)) { 1.3169 + aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE(); 1.3170 + } else { 1.3171 + PushLines(aState, aLine.prev()); 1.3172 + NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus); 1.3173 + } 1.3174 + } 1.3175 + else { 1.3176 + // Note: line-break-after a block is a nop 1.3177 + 1.3178 + // Try to place the child block. 1.3179 + // Don't force the block to fit if we have positive clearance, because 1.3180 + // pushing it to the next page would give it more room. 1.3181 + // Don't force the block to fit if it's impacted by a float. If it is, 1.3182 + // then pushing it to the next page would give it more room. Note that 1.3183 + // isImpacted doesn't include impact from the block's own floats. 1.3184 + bool forceFit = aState.IsAdjacentWithTop() && clearance <= 0 && 1.3185 + !floatAvailableSpace.mHasFloats; 1.3186 + nsCollapsingMargin collapsedBottomMargin; 1.3187 + nsOverflowAreas overflowAreas; 1.3188 + *aKeepReflowGoing = brc.PlaceBlock(blockHtmlRS, forceFit, aLine.get(), 1.3189 + collapsedBottomMargin, 1.3190 + overflowAreas, 1.3191 + frameReflowStatus, 1.3192 + aState.mContainerWidth); 1.3193 + if (!NS_FRAME_IS_FULLY_COMPLETE(frameReflowStatus) && 1.3194 + ShouldAvoidBreakInside(aState.mReflowState)) { 1.3195 + *aKeepReflowGoing = false; 1.3196 + } 1.3197 + 1.3198 + if (aLine->SetCarriedOutBottomMargin(collapsedBottomMargin)) { 1.3199 + line_iterator nextLine = aLine; 1.3200 + ++nextLine; 1.3201 + if (nextLine != end_lines()) { 1.3202 + nextLine->MarkPreviousMarginDirty(); 1.3203 + } 1.3204 + } 1.3205 + 1.3206 + aLine->SetOverflowAreas(overflowAreas); 1.3207 + if (*aKeepReflowGoing) { 1.3208 + // Some of the child block fit 1.3209 + 1.3210 + // Advance to new Y position 1.3211 + nscoord newY = aLine->BEnd(); 1.3212 + aState.mY = newY; 1.3213 + 1.3214 + // Continue the block frame now if it didn't completely fit in 1.3215 + // the available space. 1.3216 + if (!NS_FRAME_IS_FULLY_COMPLETE(frameReflowStatus)) { 1.3217 + bool madeContinuation = 1.3218 + CreateContinuationFor(aState, nullptr, frame); 1.3219 + 1.3220 + nsIFrame* nextFrame = frame->GetNextInFlow(); 1.3221 + NS_ASSERTION(nextFrame, "We're supposed to have a next-in-flow by now"); 1.3222 + 1.3223 + if (NS_FRAME_IS_NOT_COMPLETE(frameReflowStatus)) { 1.3224 + // If nextFrame used to be an overflow container, make it a normal block 1.3225 + if (!madeContinuation && 1.3226 + (NS_FRAME_IS_OVERFLOW_CONTAINER & nextFrame->GetStateBits())) { 1.3227 + nsOverflowContinuationTracker::AutoFinish fini(aState.mOverflowTracker, frame); 1.3228 + nsContainerFrame* parent = 1.3229 + static_cast<nsContainerFrame*>(nextFrame->GetParent()); 1.3230 + rv = parent->StealFrame(nextFrame); 1.3231 + NS_ENSURE_SUCCESS(rv, rv); 1.3232 + if (parent != this) 1.3233 + ReparentFrame(nextFrame, parent, this); 1.3234 + mFrames.InsertFrame(nullptr, frame, nextFrame); 1.3235 + madeContinuation = true; // needs to be added to mLines 1.3236 + nextFrame->RemoveStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); 1.3237 + frameReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW; 1.3238 + } 1.3239 + 1.3240 + // Push continuation to a new line, but only if we actually made one. 1.3241 + if (madeContinuation) { 1.3242 + nsLineBox* line = NewLineBox(nextFrame, true); 1.3243 + mLines.after_insert(aLine, line); 1.3244 + } 1.3245 + 1.3246 + PushLines(aState, aLine); 1.3247 + NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus); 1.3248 + 1.3249 + // If we need to reflow the continuation of the block child, 1.3250 + // then we'd better reflow our continuation 1.3251 + if (frameReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) { 1.3252 + aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW; 1.3253 + // We also need to make that continuation's line dirty so 1.3254 + // it gets reflowed when we reflow our next in flow. The 1.3255 + // nif's line must always be either a line of the nif's 1.3256 + // parent block (only if we didn't make a continuation) or 1.3257 + // else one of our own overflow lines. In the latter case 1.3258 + // the line is already marked dirty, so just handle the 1.3259 + // first case. 1.3260 + if (!madeContinuation) { 1.3261 + nsBlockFrame* nifBlock = 1.3262 + nsLayoutUtils::GetAsBlock(nextFrame->GetParent()); 1.3263 + NS_ASSERTION(nifBlock, 1.3264 + "A block's child's next in flow's parent must be a block!"); 1.3265 + for (line_iterator line = nifBlock->begin_lines(), 1.3266 + line_end = nifBlock->end_lines(); line != line_end; ++line) { 1.3267 + if (line->Contains(nextFrame)) { 1.3268 + line->MarkDirty(); 1.3269 + break; 1.3270 + } 1.3271 + } 1.3272 + } 1.3273 + } 1.3274 + *aKeepReflowGoing = false; 1.3275 + 1.3276 + // The bottom margin for a block is only applied on the last 1.3277 + // flow block. Since we just continued the child block frame, 1.3278 + // we know that line->mFirstChild is not the last flow block 1.3279 + // therefore zero out the running margin value. 1.3280 +#ifdef NOISY_VERTICAL_MARGINS 1.3281 + ListTag(stdout); 1.3282 + printf(": reflow incomplete, frame="); 1.3283 + nsFrame::ListTag(stdout, frame); 1.3284 + printf(" prevBottomMargin=%d, setting to zero\n", 1.3285 + aState.mPrevBottomMargin); 1.3286 +#endif 1.3287 + aState.mPrevBottomMargin.Zero(); 1.3288 + } 1.3289 + else { // frame is complete but its overflow is not complete 1.3290 + // Disconnect the next-in-flow and put it in our overflow tracker 1.3291 + if (!madeContinuation && 1.3292 + !(NS_FRAME_IS_OVERFLOW_CONTAINER & nextFrame->GetStateBits())) { 1.3293 + // It already exists, but as a normal next-in-flow, so we need 1.3294 + // to dig it out of the child lists. 1.3295 + nsContainerFrame* parent = static_cast<nsContainerFrame*> 1.3296 + (nextFrame->GetParent()); 1.3297 + rv = parent->StealFrame(nextFrame); 1.3298 + NS_ENSURE_SUCCESS(rv, rv); 1.3299 + } 1.3300 + else if (madeContinuation) { 1.3301 + mFrames.RemoveFrame(nextFrame); 1.3302 + } 1.3303 + 1.3304 + // Put it in our overflow list 1.3305 + aState.mOverflowTracker->Insert(nextFrame, frameReflowStatus); 1.3306 + NS_MergeReflowStatusInto(&aState.mReflowStatus, frameReflowStatus); 1.3307 + 1.3308 +#ifdef NOISY_VERTICAL_MARGINS 1.3309 + ListTag(stdout); 1.3310 + printf(": reflow complete but overflow incomplete for "); 1.3311 + nsFrame::ListTag(stdout, frame); 1.3312 + printf(" prevBottomMargin=%d collapsedBottomMargin=%d\n", 1.3313 + aState.mPrevBottomMargin, collapsedBottomMargin.get()); 1.3314 +#endif 1.3315 + aState.mPrevBottomMargin = collapsedBottomMargin; 1.3316 + } 1.3317 + } 1.3318 + else { // frame is fully complete 1.3319 +#ifdef NOISY_VERTICAL_MARGINS 1.3320 + ListTag(stdout); 1.3321 + printf(": reflow complete for "); 1.3322 + nsFrame::ListTag(stdout, frame); 1.3323 + printf(" prevBottomMargin=%d collapsedBottomMargin=%d\n", 1.3324 + aState.mPrevBottomMargin, collapsedBottomMargin.get()); 1.3325 +#endif 1.3326 + aState.mPrevBottomMargin = collapsedBottomMargin; 1.3327 + } 1.3328 +#ifdef NOISY_VERTICAL_MARGINS 1.3329 + ListTag(stdout); 1.3330 + printf(": frame="); 1.3331 + nsFrame::ListTag(stdout, frame); 1.3332 + printf(" carriedOutBottomMargin=%d collapsedBottomMargin=%d => %d\n", 1.3333 + brc.GetCarriedOutBottomMargin(), collapsedBottomMargin.get(), 1.3334 + aState.mPrevBottomMargin); 1.3335 +#endif 1.3336 + } else { 1.3337 + if ((aLine == mLines.front() && !GetPrevInFlow()) || 1.3338 + ShouldAvoidBreakInside(aState.mReflowState)) { 1.3339 + // If it's our very first line *or* we're not at the top of the page 1.3340 + // and we have page-break-inside:avoid, then we need to be pushed to 1.3341 + // our parent's next-in-flow. 1.3342 + aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE(); 1.3343 + } else { 1.3344 + // Push the line that didn't fit and any lines that follow it 1.3345 + // to our next-in-flow. 1.3346 + PushLines(aState, aLine.prev()); 1.3347 + NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus); 1.3348 + } 1.3349 + } 1.3350 + } 1.3351 + break; // out of the reflow retry loop 1.3352 + } 1.3353 + 1.3354 + // Now that we've got its final position all figured out, position any child 1.3355 + // views it may have. Note that the case when frame has a view got handled 1.3356 + // by FinishReflowChild, but that function didn't have the coordinates needed 1.3357 + // to correctly decide whether to reposition child views. 1.3358 + if (originalPosition != frame->GetPosition() && !frame->HasView()) { 1.3359 + nsContainerFrame::PositionChildViews(frame); 1.3360 + } 1.3361 + 1.3362 +#ifdef DEBUG 1.3363 + VerifyLines(true); 1.3364 +#endif 1.3365 + return rv; 1.3366 +} 1.3367 + 1.3368 +nsresult 1.3369 +nsBlockFrame::ReflowInlineFrames(nsBlockReflowState& aState, 1.3370 + line_iterator aLine, 1.3371 + bool* aKeepReflowGoing) 1.3372 +{ 1.3373 + nsresult rv = NS_OK; 1.3374 + *aKeepReflowGoing = true; 1.3375 + 1.3376 + aLine->SetLineIsImpactedByFloat(false); 1.3377 + 1.3378 + // Setup initial coordinate system for reflowing the inline frames 1.3379 + // into. Apply a previous block frame's bottom margin first. 1.3380 + if (ShouldApplyTopMargin(aState, aLine)) { 1.3381 + aState.mY += aState.mPrevBottomMargin.get(); 1.3382 + } 1.3383 + nsFlowAreaRect floatAvailableSpace = aState.GetFloatAvailableSpace(); 1.3384 + 1.3385 + LineReflowStatus lineReflowStatus; 1.3386 + do { 1.3387 + nscoord availableSpaceHeight = 0; 1.3388 + do { 1.3389 + bool allowPullUp = true; 1.3390 + nsIContent* forceBreakInContent = nullptr; 1.3391 + int32_t forceBreakOffset = -1; 1.3392 + gfxBreakPriority forceBreakPriority = gfxBreakPriority::eNoBreak; 1.3393 + do { 1.3394 + nsFloatManager::SavedState floatManagerState; 1.3395 + aState.mReflowState.mFloatManager->PushState(&floatManagerState); 1.3396 + 1.3397 + // Once upon a time we allocated the first 30 nsLineLayout objects 1.3398 + // on the stack, and then we switched to the heap. At that time 1.3399 + // these objects were large (1100 bytes on a 32 bit system). 1.3400 + // Then the nsLineLayout object was shrunk to 156 bytes by 1.3401 + // removing some internal buffers. Given that it is so much 1.3402 + // smaller, the complexity of 2 different ways of allocating 1.3403 + // no longer makes sense. Now we always allocate on the stack. 1.3404 + nsLineLayout lineLayout(aState.mPresContext, 1.3405 + aState.mReflowState.mFloatManager, 1.3406 + &aState.mReflowState, &aLine); 1.3407 + lineLayout.Init(&aState, aState.mMinLineHeight, aState.mLineNumber); 1.3408 + if (forceBreakInContent) { 1.3409 + lineLayout.ForceBreakAtPosition(forceBreakInContent, forceBreakOffset); 1.3410 + } 1.3411 + rv = DoReflowInlineFrames(aState, lineLayout, aLine, 1.3412 + floatAvailableSpace, availableSpaceHeight, 1.3413 + &floatManagerState, aKeepReflowGoing, 1.3414 + &lineReflowStatus, allowPullUp); 1.3415 + lineLayout.EndLineReflow(); 1.3416 + 1.3417 + if (NS_FAILED(rv)) { 1.3418 + return rv; 1.3419 + } 1.3420 + 1.3421 + if (LINE_REFLOW_REDO_NO_PULL == lineReflowStatus || 1.3422 + LINE_REFLOW_REDO_MORE_FLOATS == lineReflowStatus || 1.3423 + LINE_REFLOW_REDO_NEXT_BAND == lineReflowStatus) { 1.3424 + if (lineLayout.NeedsBackup()) { 1.3425 + NS_ASSERTION(!forceBreakInContent, "Backing up twice; this should never be necessary"); 1.3426 + // If there is no saved break position, then this will set 1.3427 + // set forceBreakInContent to null and we won't back up, which is 1.3428 + // correct. 1.3429 + forceBreakInContent = lineLayout.GetLastOptionalBreakPosition(&forceBreakOffset, &forceBreakPriority); 1.3430 + } else { 1.3431 + forceBreakInContent = nullptr; 1.3432 + } 1.3433 + // restore the float manager state 1.3434 + aState.mReflowState.mFloatManager->PopState(&floatManagerState); 1.3435 + // Clear out float lists 1.3436 + aState.mCurrentLineFloats.DeleteAll(); 1.3437 + aState.mBelowCurrentLineFloats.DeleteAll(); 1.3438 + } 1.3439 + 1.3440 + // Don't allow pullup on a subsequent LINE_REFLOW_REDO_NO_PULL pass 1.3441 + allowPullUp = false; 1.3442 + } while (LINE_REFLOW_REDO_NO_PULL == lineReflowStatus); 1.3443 + } while (LINE_REFLOW_REDO_MORE_FLOATS == lineReflowStatus); 1.3444 + } while (LINE_REFLOW_REDO_NEXT_BAND == lineReflowStatus); 1.3445 + 1.3446 + return rv; 1.3447 +} 1.3448 + 1.3449 +void 1.3450 +nsBlockFrame::PushTruncatedLine(nsBlockReflowState& aState, 1.3451 + line_iterator aLine, 1.3452 + bool* aKeepReflowGoing) 1.3453 +{ 1.3454 + PushLines(aState, aLine.prev()); 1.3455 + *aKeepReflowGoing = false; 1.3456 + NS_FRAME_SET_INCOMPLETE(aState.mReflowStatus); 1.3457 +} 1.3458 + 1.3459 +#ifdef DEBUG 1.3460 +static const char* LineReflowStatusNames[] = { 1.3461 + "LINE_REFLOW_OK", "LINE_REFLOW_STOP", "LINE_REFLOW_REDO_NO_PULL", 1.3462 + "LINE_REFLOW_REDO_MORE_FLOATS", 1.3463 + "LINE_REFLOW_REDO_NEXT_BAND", "LINE_REFLOW_TRUNCATED" 1.3464 +}; 1.3465 +#endif 1.3466 + 1.3467 +nsresult 1.3468 +nsBlockFrame::DoReflowInlineFrames(nsBlockReflowState& aState, 1.3469 + nsLineLayout& aLineLayout, 1.3470 + line_iterator aLine, 1.3471 + nsFlowAreaRect& aFloatAvailableSpace, 1.3472 + nscoord& aAvailableSpaceHeight, 1.3473 + nsFloatManager::SavedState* 1.3474 + aFloatStateBeforeLine, 1.3475 + bool* aKeepReflowGoing, 1.3476 + LineReflowStatus* aLineReflowStatus, 1.3477 + bool aAllowPullUp) 1.3478 +{ 1.3479 + // Forget all of the floats on the line 1.3480 + aLine->FreeFloats(aState.mFloatCacheFreeList); 1.3481 + aState.mFloatOverflowAreas.Clear(); 1.3482 + 1.3483 + // We need to set this flag on the line if any of our reflow passes 1.3484 + // are impacted by floats. 1.3485 + if (aFloatAvailableSpace.mHasFloats) 1.3486 + aLine->SetLineIsImpactedByFloat(true); 1.3487 +#ifdef REALLY_NOISY_REFLOW 1.3488 + printf("nsBlockFrame::DoReflowInlineFrames %p impacted = %d\n", 1.3489 + this, aFloatAvailableSpace.mHasFloats); 1.3490 +#endif 1.3491 + 1.3492 + WritingMode wm = GetWritingMode(aLine->mFirstChild); 1.3493 + LogicalRect lineRect(wm, aFloatAvailableSpace.mRect, aState.mContainerWidth); 1.3494 + 1.3495 + nscoord iStart = lineRect.IStart(wm); 1.3496 + 1.3497 + nscoord availISize = lineRect.ISize(wm); 1.3498 + nscoord availBSize; 1.3499 + if (aState.GetFlag(BRS_UNCONSTRAINEDHEIGHT)) { 1.3500 + availBSize = NS_UNCONSTRAINEDSIZE; 1.3501 + } 1.3502 + else { 1.3503 + /* XXX get the height right! */ 1.3504 + availBSize = lineRect.BSize(wm); 1.3505 + } 1.3506 + 1.3507 + // Make sure to enable resize optimization before we call BeginLineReflow 1.3508 + // because it might get disabled there 1.3509 + aLine->EnableResizeReflowOptimization(); 1.3510 + 1.3511 + aLineLayout.BeginLineReflow(iStart, aState.mY, 1.3512 + availISize, availBSize, 1.3513 + aFloatAvailableSpace.mHasFloats, 1.3514 + false, /*XXX isTopOfPage*/ 1.3515 + wm, aState.mContainerWidth); 1.3516 + 1.3517 + aState.SetFlag(BRS_LINE_LAYOUT_EMPTY, false); 1.3518 + 1.3519 + // XXX Unfortunately we need to know this before reflowing the first 1.3520 + // inline frame in the line. FIX ME. 1.3521 + if ((0 == aLineLayout.GetLineNumber()) && 1.3522 + (NS_BLOCK_HAS_FIRST_LETTER_CHILD & mState) && 1.3523 + (NS_BLOCK_HAS_FIRST_LETTER_STYLE & mState)) { 1.3524 + aLineLayout.SetFirstLetterStyleOK(true); 1.3525 + } 1.3526 + NS_ASSERTION(!((NS_BLOCK_HAS_FIRST_LETTER_CHILD & mState) && 1.3527 + GetPrevContinuation()), 1.3528 + "first letter child bit should only be on first continuation"); 1.3529 + 1.3530 + // Reflow the frames that are already on the line first 1.3531 + nsresult rv = NS_OK; 1.3532 + LineReflowStatus lineReflowStatus = LINE_REFLOW_OK; 1.3533 + int32_t i; 1.3534 + nsIFrame* frame = aLine->mFirstChild; 1.3535 + 1.3536 + if (aFloatAvailableSpace.mHasFloats) { 1.3537 + // There is a soft break opportunity at the start of the line, because 1.3538 + // we can always move this line down below float(s). 1.3539 + if (aLineLayout.NotifyOptionalBreakPosition(frame->GetContent(), 0, true, gfxBreakPriority::eNormalBreak)) { 1.3540 + lineReflowStatus = LINE_REFLOW_REDO_NEXT_BAND; 1.3541 + } 1.3542 + } 1.3543 + 1.3544 + // need to repeatedly call GetChildCount here, because the child 1.3545 + // count can change during the loop! 1.3546 + for (i = 0; LINE_REFLOW_OK == lineReflowStatus && i < aLine->GetChildCount(); 1.3547 + i++, frame = frame->GetNextSibling()) { 1.3548 + rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame, 1.3549 + &lineReflowStatus); 1.3550 + NS_ENSURE_SUCCESS(rv, rv); 1.3551 + if (LINE_REFLOW_OK != lineReflowStatus) { 1.3552 + // It is possible that one or more of next lines are empty 1.3553 + // (because of DeleteNextInFlowChild). If so, delete them now 1.3554 + // in case we are finished. 1.3555 + ++aLine; 1.3556 + while ((aLine != end_lines()) && (0 == aLine->GetChildCount())) { 1.3557 + // XXX Is this still necessary now that DeleteNextInFlowChild 1.3558 + // uses DoRemoveFrame? 1.3559 + nsLineBox *toremove = aLine; 1.3560 + aLine = mLines.erase(aLine); 1.3561 + NS_ASSERTION(nullptr == toremove->mFirstChild, "bad empty line"); 1.3562 + FreeLineBox(toremove); 1.3563 + } 1.3564 + --aLine; 1.3565 + 1.3566 + NS_ASSERTION(lineReflowStatus != LINE_REFLOW_TRUNCATED, 1.3567 + "ReflowInlineFrame should never determine that a line " 1.3568 + "needs to go to the next page/column"); 1.3569 + } 1.3570 + } 1.3571 + 1.3572 + // Don't pull up new frames into lines with continuation placeholders 1.3573 + if (aAllowPullUp) { 1.3574 + // Pull frames and reflow them until we can't 1.3575 + while (LINE_REFLOW_OK == lineReflowStatus) { 1.3576 + frame = PullFrame(aState, aLine); 1.3577 + if (!frame) { 1.3578 + break; 1.3579 + } 1.3580 + 1.3581 + while (LINE_REFLOW_OK == lineReflowStatus) { 1.3582 + int32_t oldCount = aLine->GetChildCount(); 1.3583 + rv = ReflowInlineFrame(aState, aLineLayout, aLine, frame, 1.3584 + &lineReflowStatus); 1.3585 + NS_ENSURE_SUCCESS(rv, rv); 1.3586 + if (aLine->GetChildCount() != oldCount) { 1.3587 + // We just created a continuation for aFrame AND its going 1.3588 + // to end up on this line (e.g. :first-letter 1.3589 + // situation). Therefore we have to loop here before trying 1.3590 + // to pull another frame. 1.3591 + frame = frame->GetNextSibling(); 1.3592 + } 1.3593 + else { 1.3594 + break; 1.3595 + } 1.3596 + } 1.3597 + } 1.3598 + } 1.3599 + 1.3600 + aState.SetFlag(BRS_LINE_LAYOUT_EMPTY, aLineLayout.LineIsEmpty()); 1.3601 + 1.3602 + // We only need to backup if the line isn't going to be reflowed again anyway 1.3603 + bool needsBackup = aLineLayout.NeedsBackup() && 1.3604 + (lineReflowStatus == LINE_REFLOW_STOP || lineReflowStatus == LINE_REFLOW_OK); 1.3605 + if (needsBackup && aLineLayout.HaveForcedBreakPosition()) { 1.3606 + NS_WARNING("We shouldn't be backing up more than once! " 1.3607 + "Someone must have set a break opportunity beyond the available width, " 1.3608 + "even though there were better break opportunities before it"); 1.3609 + needsBackup = false; 1.3610 + } 1.3611 + if (needsBackup) { 1.3612 + // We need to try backing up to before a text run 1.3613 + int32_t offset; 1.3614 + gfxBreakPriority breakPriority; 1.3615 + nsIContent* breakContent = aLineLayout.GetLastOptionalBreakPosition(&offset, &breakPriority); 1.3616 + // XXX It's possible, in fact not unusual, for the break opportunity to already 1.3617 + // be the end of the line. We should detect that and optimize to not 1.3618 + // re-do the line. 1.3619 + if (breakContent) { 1.3620 + // We can back up! 1.3621 + lineReflowStatus = LINE_REFLOW_REDO_NO_PULL; 1.3622 + } 1.3623 + } else { 1.3624 + // In case we reflow this line again, remember that we don't 1.3625 + // need to force any breaking 1.3626 + aLineLayout.ClearOptionalBreakPosition(); 1.3627 + } 1.3628 + 1.3629 + if (LINE_REFLOW_REDO_NEXT_BAND == lineReflowStatus) { 1.3630 + // This happens only when we have a line that is impacted by 1.3631 + // floats and the first element in the line doesn't fit with 1.3632 + // the floats. 1.3633 + // 1.3634 + // What we do is to advance past the first float we find and 1.3635 + // then reflow the line all over again. 1.3636 + NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aFloatAvailableSpace.mRect.height, 1.3637 + "unconstrained height on totally empty line"); 1.3638 + 1.3639 + // See the analogous code for blocks in nsBlockReflowState::ClearFloats. 1.3640 + if (aFloatAvailableSpace.mRect.height > 0) { 1.3641 + NS_ASSERTION(aFloatAvailableSpace.mHasFloats, 1.3642 + "redo line on totally empty line with non-empty band..."); 1.3643 + // We should never hit this case if we've placed floats on the 1.3644 + // line; if we have, then the GetFloatAvailableSpace call is wrong 1.3645 + // and needs to happen after the caller pops the space manager 1.3646 + // state. 1.3647 + aState.mFloatManager->AssertStateMatches(aFloatStateBeforeLine); 1.3648 + aState.mY += aFloatAvailableSpace.mRect.height; 1.3649 + aFloatAvailableSpace = aState.GetFloatAvailableSpace(); 1.3650 + } else { 1.3651 + NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aState.mReflowState.AvailableHeight(), 1.3652 + "We shouldn't be running out of height here"); 1.3653 + if (NS_UNCONSTRAINEDSIZE == aState.mReflowState.AvailableHeight()) { 1.3654 + // just move it down a bit to try to get out of this mess 1.3655 + aState.mY += 1; 1.3656 + // We should never hit this case if we've placed floats on the 1.3657 + // line; if we have, then the GetFloatAvailableSpace call is wrong 1.3658 + // and needs to happen after the caller pops the space manager 1.3659 + // state. 1.3660 + aState.mFloatManager->AssertStateMatches(aFloatStateBeforeLine); 1.3661 + aFloatAvailableSpace = aState.GetFloatAvailableSpace(); 1.3662 + } else { 1.3663 + // There's nowhere to retry placing the line, so we want to push 1.3664 + // it to the next page/column where its contents can fit not 1.3665 + // next to a float. 1.3666 + lineReflowStatus = LINE_REFLOW_TRUNCATED; 1.3667 + PushTruncatedLine(aState, aLine, aKeepReflowGoing); 1.3668 + } 1.3669 + } 1.3670 + 1.3671 + // XXX: a small optimization can be done here when paginating: 1.3672 + // if the new Y coordinate is past the end of the block then 1.3673 + // push the line and return now instead of later on after we are 1.3674 + // past the float. 1.3675 + } 1.3676 + else if (LINE_REFLOW_TRUNCATED != lineReflowStatus && 1.3677 + LINE_REFLOW_REDO_NO_PULL != lineReflowStatus) { 1.3678 + // If we are propagating out a break-before status then there is 1.3679 + // no point in placing the line. 1.3680 + if (!NS_INLINE_IS_BREAK_BEFORE(aState.mReflowStatus)) { 1.3681 + if (!PlaceLine(aState, aLineLayout, aLine, aFloatStateBeforeLine, 1.3682 + aFloatAvailableSpace.mRect, aAvailableSpaceHeight, 1.3683 + aKeepReflowGoing)) { 1.3684 + lineReflowStatus = LINE_REFLOW_REDO_MORE_FLOATS; 1.3685 + // PlaceLine already called GetAvailableSpaceForHeight for us. 1.3686 + } 1.3687 + } 1.3688 + } 1.3689 +#ifdef DEBUG 1.3690 + if (gNoisyReflow) { 1.3691 + printf("Line reflow status = %s\n", LineReflowStatusNames[lineReflowStatus]); 1.3692 + } 1.3693 +#endif 1.3694 + 1.3695 + if (aLineLayout.GetDirtyNextLine()) { 1.3696 + // aLine may have been pushed to the overflow lines. 1.3697 + FrameLines* overflowLines = GetOverflowLines(); 1.3698 + // We can't just compare iterators front() to aLine here, since they may be in 1.3699 + // different lists. 1.3700 + bool pushedToOverflowLines = overflowLines && 1.3701 + overflowLines->mLines.front() == aLine.get(); 1.3702 + if (pushedToOverflowLines) { 1.3703 + // aLine is stale, it's associated with the main line list but it should 1.3704 + // be associated with the overflow line list now 1.3705 + aLine = overflowLines->mLines.begin(); 1.3706 + } 1.3707 + nsBlockInFlowLineIterator iter(this, aLine, pushedToOverflowLines); 1.3708 + if (iter.Next() && iter.GetLine()->IsInline()) { 1.3709 + iter.GetLine()->MarkDirty(); 1.3710 + if (iter.GetContainer() != this) { 1.3711 + aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW; 1.3712 + } 1.3713 + } 1.3714 + } 1.3715 + 1.3716 + *aLineReflowStatus = lineReflowStatus; 1.3717 + 1.3718 + return rv; 1.3719 +} 1.3720 + 1.3721 +/** 1.3722 + * Reflow an inline frame. The reflow status is mapped from the frames 1.3723 + * reflow status to the lines reflow status (not to our reflow status). 1.3724 + * The line reflow status is simple: true means keep placing frames 1.3725 + * on the line; false means don't (the line is done). If the line 1.3726 + * has some sort of breaking affect then aLine's break-type will be set 1.3727 + * to something other than NS_STYLE_CLEAR_NONE. 1.3728 + */ 1.3729 +nsresult 1.3730 +nsBlockFrame::ReflowInlineFrame(nsBlockReflowState& aState, 1.3731 + nsLineLayout& aLineLayout, 1.3732 + line_iterator aLine, 1.3733 + nsIFrame* aFrame, 1.3734 + LineReflowStatus* aLineReflowStatus) 1.3735 +{ 1.3736 + NS_ENSURE_ARG_POINTER(aFrame); 1.3737 + 1.3738 + *aLineReflowStatus = LINE_REFLOW_OK; 1.3739 + 1.3740 +#ifdef NOISY_FIRST_LETTER 1.3741 + ListTag(stdout); 1.3742 + printf(": reflowing "); 1.3743 + nsFrame::ListTag(stdout, aFrame); 1.3744 + printf(" reflowingFirstLetter=%s\n", 1.3745 + aLineLayout.GetFirstLetterStyleOK() ? "on" : "off"); 1.3746 +#endif 1.3747 + 1.3748 + // Reflow the inline frame 1.3749 + nsReflowStatus frameReflowStatus; 1.3750 + bool pushedFrame; 1.3751 + nsresult rv = aLineLayout.ReflowFrame(aFrame, frameReflowStatus, 1.3752 + nullptr, pushedFrame); 1.3753 + NS_ENSURE_SUCCESS(rv, rv); 1.3754 + 1.3755 + if (frameReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) { 1.3756 + aLineLayout.SetDirtyNextLine(); 1.3757 + } 1.3758 + 1.3759 + NS_ENSURE_SUCCESS(rv, rv); 1.3760 +#ifdef REALLY_NOISY_REFLOW_CHILD 1.3761 + nsFrame::ListTag(stdout, aFrame); 1.3762 + printf(": status=%x\n", frameReflowStatus); 1.3763 +#endif 1.3764 + 1.3765 +#if defined(REFLOW_STATUS_COVERAGE) 1.3766 + RecordReflowStatus(false, frameReflowStatus); 1.3767 +#endif 1.3768 + 1.3769 + // Send post-reflow notification 1.3770 + aState.mPrevChild = aFrame; 1.3771 + 1.3772 + /* XXX 1.3773 + This is where we need to add logic to handle some odd behavior. 1.3774 + For one thing, we should usually place at least one thing next 1.3775 + to a left float, even when that float takes up all the width on a line. 1.3776 + see bug 22496 1.3777 + */ 1.3778 + 1.3779 + // Process the child frames reflow status. There are 5 cases: 1.3780 + // complete, not-complete, break-before, break-after-complete, 1.3781 + // break-after-not-complete. There are two situations: we are a 1.3782 + // block or we are an inline. This makes a total of 10 cases 1.3783 + // (fortunately, there is some overlap). 1.3784 + aLine->SetBreakTypeAfter(NS_STYLE_CLEAR_NONE); 1.3785 + if (NS_INLINE_IS_BREAK(frameReflowStatus) || 1.3786 + (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType)) { 1.3787 + // Always abort the line reflow (because a line break is the 1.3788 + // minimal amount of break we do). 1.3789 + *aLineReflowStatus = LINE_REFLOW_STOP; 1.3790 + 1.3791 + // XXX what should aLine's break-type be set to in all these cases? 1.3792 + uint8_t breakType = NS_INLINE_GET_BREAK_TYPE(frameReflowStatus); 1.3793 + NS_ASSERTION((NS_STYLE_CLEAR_NONE != breakType) || 1.3794 + (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType), "bad break type"); 1.3795 + NS_ASSERTION(NS_STYLE_CLEAR_MAX >= breakType, "invalid break type"); 1.3796 + 1.3797 + if (NS_INLINE_IS_BREAK_BEFORE(frameReflowStatus)) { 1.3798 + // Break-before cases. 1.3799 + if (aFrame == aLine->mFirstChild) { 1.3800 + // If we break before the first frame on the line then we must 1.3801 + // be trying to place content where there's no room (e.g. on a 1.3802 + // line with wide floats). Inform the caller to reflow the 1.3803 + // line after skipping past a float. 1.3804 + *aLineReflowStatus = LINE_REFLOW_REDO_NEXT_BAND; 1.3805 + } 1.3806 + else { 1.3807 + // It's not the first child on this line so go ahead and split 1.3808 + // the line. We will see the frame again on the next-line. 1.3809 + SplitLine(aState, aLineLayout, aLine, aFrame, aLineReflowStatus); 1.3810 + 1.3811 + // If we're splitting the line because the frame didn't fit and it 1.3812 + // was pushed, then mark the line as having word wrapped. We need to 1.3813 + // know that if we're shrink wrapping our width 1.3814 + if (pushedFrame) { 1.3815 + aLine->SetLineWrapped(true); 1.3816 + } 1.3817 + } 1.3818 + } 1.3819 + else { 1.3820 + // If a float split and its prev-in-flow was followed by a <BR>, then combine 1.3821 + // the <BR>'s break type with the inline's break type (the inline will be the very 1.3822 + // next frame after the split float). 1.3823 + if (NS_STYLE_CLEAR_NONE != aState.mFloatBreakType) { 1.3824 + breakType = nsLayoutUtils::CombineBreakType(breakType, 1.3825 + aState.mFloatBreakType); 1.3826 + aState.mFloatBreakType = NS_STYLE_CLEAR_NONE; 1.3827 + } 1.3828 + // Break-after cases 1.3829 + if (breakType == NS_STYLE_CLEAR_LINE) { 1.3830 + if (!aLineLayout.GetLineEndsInBR()) { 1.3831 + breakType = NS_STYLE_CLEAR_NONE; 1.3832 + } 1.3833 + } 1.3834 + aLine->SetBreakTypeAfter(breakType); 1.3835 + if (NS_FRAME_IS_COMPLETE(frameReflowStatus)) { 1.3836 + // Split line, but after the frame just reflowed 1.3837 + SplitLine(aState, aLineLayout, aLine, aFrame->GetNextSibling(), aLineReflowStatus); 1.3838 + 1.3839 + if (NS_INLINE_IS_BREAK_AFTER(frameReflowStatus) && 1.3840 + !aLineLayout.GetLineEndsInBR()) { 1.3841 + aLineLayout.SetDirtyNextLine(); 1.3842 + } 1.3843 + } 1.3844 + } 1.3845 + } 1.3846 + 1.3847 + if (!NS_FRAME_IS_FULLY_COMPLETE(frameReflowStatus)) { 1.3848 + // Create a continuation for the incomplete frame. Note that the 1.3849 + // frame may already have a continuation. 1.3850 + CreateContinuationFor(aState, aLine, aFrame); 1.3851 + 1.3852 + // Remember that the line has wrapped 1.3853 + if (!aLineLayout.GetLineEndsInBR()) { 1.3854 + aLine->SetLineWrapped(true); 1.3855 + } 1.3856 + 1.3857 + // If we just ended a first-letter frame or reflowed a placeholder then 1.3858 + // don't split the line and don't stop the line reflow... 1.3859 + // But if we are going to stop anyways we'd better split the line. 1.3860 + if ((!(frameReflowStatus & NS_INLINE_BREAK_FIRST_LETTER_COMPLETE) && 1.3861 + nsGkAtoms::placeholderFrame != aFrame->GetType()) || 1.3862 + *aLineReflowStatus == LINE_REFLOW_STOP) { 1.3863 + // Split line after the current frame 1.3864 + *aLineReflowStatus = LINE_REFLOW_STOP; 1.3865 + SplitLine(aState, aLineLayout, aLine, aFrame->GetNextSibling(), aLineReflowStatus); 1.3866 + } 1.3867 + } 1.3868 + 1.3869 + return NS_OK; 1.3870 +} 1.3871 + 1.3872 +bool 1.3873 +nsBlockFrame::CreateContinuationFor(nsBlockReflowState& aState, 1.3874 + nsLineBox* aLine, 1.3875 + nsIFrame* aFrame) 1.3876 +{ 1.3877 + nsIFrame* newFrame = nullptr; 1.3878 + 1.3879 + if (!aFrame->GetNextInFlow()) { 1.3880 + newFrame = aState.mPresContext->PresShell()->FrameConstructor()-> 1.3881 + CreateContinuingFrame(aState.mPresContext, aFrame, this); 1.3882 + 1.3883 + mFrames.InsertFrame(nullptr, aFrame, newFrame); 1.3884 + 1.3885 + if (aLine) { 1.3886 + aLine->NoteFrameAdded(newFrame); 1.3887 + } 1.3888 + } 1.3889 +#ifdef DEBUG 1.3890 + VerifyLines(false); 1.3891 +#endif 1.3892 + return !!newFrame; 1.3893 +} 1.3894 + 1.3895 +nsresult 1.3896 +nsBlockFrame::SplitFloat(nsBlockReflowState& aState, 1.3897 + nsIFrame* aFloat, 1.3898 + nsReflowStatus aFloatStatus) 1.3899 +{ 1.3900 + nsIFrame* nextInFlow = aFloat->GetNextInFlow(); 1.3901 + if (nextInFlow) { 1.3902 + nsContainerFrame *oldParent = 1.3903 + static_cast<nsContainerFrame*>(nextInFlow->GetParent()); 1.3904 + DebugOnly<nsresult> rv = oldParent->StealFrame(nextInFlow); 1.3905 + NS_ASSERTION(NS_SUCCEEDED(rv), "StealFrame failed"); 1.3906 + if (oldParent != this) { 1.3907 + ReparentFrame(nextInFlow, oldParent, this); 1.3908 + } 1.3909 + } else { 1.3910 + nextInFlow = aState.mPresContext->PresShell()->FrameConstructor()-> 1.3911 + CreateContinuingFrame(aState.mPresContext, aFloat, this); 1.3912 + } 1.3913 + if (NS_FRAME_OVERFLOW_IS_INCOMPLETE(aFloatStatus)) 1.3914 + aFloat->GetNextInFlow()->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER); 1.3915 + 1.3916 + // The containing block is now overflow-incomplete. 1.3917 + NS_FRAME_SET_OVERFLOW_INCOMPLETE(aState.mReflowStatus); 1.3918 + 1.3919 + if (aFloat->StyleDisplay()->mFloats == NS_STYLE_FLOAT_LEFT) { 1.3920 + aState.mFloatManager->SetSplitLeftFloatAcrossBreak(); 1.3921 + } else { 1.3922 + NS_ABORT_IF_FALSE(aFloat->StyleDisplay()->mFloats == 1.3923 + NS_STYLE_FLOAT_RIGHT, "unexpected float side"); 1.3924 + aState.mFloatManager->SetSplitRightFloatAcrossBreak(); 1.3925 + } 1.3926 + 1.3927 + aState.AppendPushedFloat(nextInFlow); 1.3928 + return NS_OK; 1.3929 +} 1.3930 + 1.3931 +static nsFloatCache* 1.3932 +GetLastFloat(nsLineBox* aLine) 1.3933 +{ 1.3934 + nsFloatCache* fc = aLine->GetFirstFloat(); 1.3935 + while (fc && fc->Next()) { 1.3936 + fc = fc->Next(); 1.3937 + } 1.3938 + return fc; 1.3939 +} 1.3940 + 1.3941 +static bool 1.3942 +CheckPlaceholderInLine(nsIFrame* aBlock, nsLineBox* aLine, nsFloatCache* aFC) 1.3943 +{ 1.3944 + if (!aFC) 1.3945 + return true; 1.3946 + NS_ASSERTION(!aFC->mFloat->GetPrevContinuation(), 1.3947 + "float in a line should never be a continuation"); 1.3948 + NS_ASSERTION(!(aFC->mFloat->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT), 1.3949 + "float in a line should never be a pushed float"); 1.3950 + nsIFrame* ph = aBlock->PresContext()->FrameManager()-> 1.3951 + GetPlaceholderFrameFor(aFC->mFloat->FirstInFlow()); 1.3952 + for (nsIFrame* f = ph; f; f = f->GetParent()) { 1.3953 + if (f->GetParent() == aBlock) 1.3954 + return aLine->Contains(f); 1.3955 + } 1.3956 + NS_ASSERTION(false, "aBlock is not an ancestor of aFrame!"); 1.3957 + return true; 1.3958 +} 1.3959 + 1.3960 +void 1.3961 +nsBlockFrame::SplitLine(nsBlockReflowState& aState, 1.3962 + nsLineLayout& aLineLayout, 1.3963 + line_iterator aLine, 1.3964 + nsIFrame* aFrame, 1.3965 + LineReflowStatus* aLineReflowStatus) 1.3966 +{ 1.3967 + NS_ABORT_IF_FALSE(aLine->IsInline(), "illegal SplitLine on block line"); 1.3968 + 1.3969 + int32_t pushCount = aLine->GetChildCount() - aLineLayout.GetCurrentSpanCount(); 1.3970 + NS_ABORT_IF_FALSE(pushCount >= 0, "bad push count"); 1.3971 + 1.3972 +#ifdef DEBUG 1.3973 + if (gNoisyReflow) { 1.3974 + nsFrame::IndentBy(stdout, gNoiseIndent); 1.3975 + printf("split line: from line=%p pushCount=%d aFrame=", 1.3976 + static_cast<void*>(aLine.get()), pushCount); 1.3977 + if (aFrame) { 1.3978 + nsFrame::ListTag(stdout, aFrame); 1.3979 + } 1.3980 + else { 1.3981 + printf("(null)"); 1.3982 + } 1.3983 + printf("\n"); 1.3984 + if (gReallyNoisyReflow) { 1.3985 + aLine->List(stdout, gNoiseIndent+1); 1.3986 + } 1.3987 + } 1.3988 +#endif 1.3989 + 1.3990 + if (0 != pushCount) { 1.3991 + NS_ABORT_IF_FALSE(aLine->GetChildCount() > pushCount, "bad push"); 1.3992 + NS_ABORT_IF_FALSE(nullptr != aFrame, "whoops"); 1.3993 +#ifdef DEBUG 1.3994 + { 1.3995 + nsIFrame *f = aFrame; 1.3996 + int32_t count = pushCount; 1.3997 + while (f && count > 0) { 1.3998 + f = f->GetNextSibling(); 1.3999 + --count; 1.4000 + } 1.4001 + NS_ASSERTION(count == 0, "Not enough frames to push"); 1.4002 + } 1.4003 +#endif 1.4004 + 1.4005 + // Put frames being split out into their own line 1.4006 + nsLineBox* newLine = NewLineBox(aLine, aFrame, pushCount); 1.4007 + mLines.after_insert(aLine, newLine); 1.4008 +#ifdef DEBUG 1.4009 + if (gReallyNoisyReflow) { 1.4010 + newLine->List(stdout, gNoiseIndent+1); 1.4011 + } 1.4012 +#endif 1.4013 + 1.4014 + // Let line layout know that some frames are no longer part of its 1.4015 + // state. 1.4016 + aLineLayout.SplitLineTo(aLine->GetChildCount()); 1.4017 + 1.4018 + // If floats have been placed whose placeholders have been pushed to the new 1.4019 + // line, we need to reflow the old line again. We don't want to look at the 1.4020 + // frames in the new line, because as a large paragraph is laid out the 1.4021 + // we'd get O(N^2) performance. So instead we just check that the last 1.4022 + // float and the last below-current-line float are still in aLine. 1.4023 + if (!CheckPlaceholderInLine(this, aLine, GetLastFloat(aLine)) || 1.4024 + !CheckPlaceholderInLine(this, aLine, aState.mBelowCurrentLineFloats.Tail())) { 1.4025 + *aLineReflowStatus = LINE_REFLOW_REDO_NO_PULL; 1.4026 + } 1.4027 + 1.4028 +#ifdef DEBUG 1.4029 + VerifyLines(true); 1.4030 +#endif 1.4031 + } 1.4032 +} 1.4033 + 1.4034 +bool 1.4035 +nsBlockFrame::IsLastLine(nsBlockReflowState& aState, 1.4036 + line_iterator aLine) 1.4037 +{ 1.4038 + while (++aLine != end_lines()) { 1.4039 + // There is another line 1.4040 + if (0 != aLine->GetChildCount()) { 1.4041 + // If the next line is a block line then this line is the last in a 1.4042 + // group of inline lines. 1.4043 + return aLine->IsBlock(); 1.4044 + } 1.4045 + // The next line is empty, try the next one 1.4046 + } 1.4047 + 1.4048 + // XXX Not sure about this part 1.4049 + // Try our next-in-flows lines to answer the question 1.4050 + nsBlockFrame* nextInFlow = (nsBlockFrame*) GetNextInFlow(); 1.4051 + while (nullptr != nextInFlow) { 1.4052 + for (line_iterator line = nextInFlow->begin_lines(), 1.4053 + line_end = nextInFlow->end_lines(); 1.4054 + line != line_end; 1.4055 + ++line) 1.4056 + { 1.4057 + if (0 != line->GetChildCount()) 1.4058 + return line->IsBlock(); 1.4059 + } 1.4060 + nextInFlow = (nsBlockFrame*) nextInFlow->GetNextInFlow(); 1.4061 + } 1.4062 + 1.4063 + // This is the last line - so don't allow justification 1.4064 + return true; 1.4065 +} 1.4066 + 1.4067 +bool 1.4068 +nsBlockFrame::PlaceLine(nsBlockReflowState& aState, 1.4069 + nsLineLayout& aLineLayout, 1.4070 + line_iterator aLine, 1.4071 + nsFloatManager::SavedState *aFloatStateBeforeLine, 1.4072 + nsRect& aFloatAvailableSpace, 1.4073 + nscoord& aAvailableSpaceHeight, 1.4074 + bool* aKeepReflowGoing) 1.4075 +{ 1.4076 + // Trim extra white-space from the line before placing the frames 1.4077 + aLineLayout.TrimTrailingWhiteSpace(); 1.4078 + 1.4079 + // Vertically align the frames on this line. 1.4080 + // 1.4081 + // According to the CSS2 spec, section 12.6.1, the "marker" box 1.4082 + // participates in the height calculation of the list-item box's 1.4083 + // first line box. 1.4084 + // 1.4085 + // There are exactly two places a bullet can be placed: near the 1.4086 + // first or second line. It's only placed on the second line in a 1.4087 + // rare case: when the first line is empty. 1.4088 + bool addedBullet = false; 1.4089 + if (HasOutsideBullet() && 1.4090 + ((aLine == mLines.front() && 1.4091 + (!aLineLayout.IsZeroBSize() || (aLine == mLines.back()))) || 1.4092 + (mLines.front() != mLines.back() && 1.4093 + 0 == mLines.front()->BSize() && 1.4094 + aLine == mLines.begin().next()))) { 1.4095 + nsHTMLReflowMetrics metrics(aState.mReflowState); 1.4096 + nsIFrame* bullet = GetOutsideBullet(); 1.4097 + ReflowBullet(bullet, aState, metrics, aState.mY); 1.4098 + NS_ASSERTION(!BulletIsEmpty() || metrics.Height() == 0, 1.4099 + "empty bullet took up space"); 1.4100 + aLineLayout.AddBulletFrame(bullet, metrics); 1.4101 + addedBullet = true; 1.4102 + } 1.4103 + aLineLayout.BlockDirAlignLine(); 1.4104 + 1.4105 + // We want to compare to the available space that we would have had in 1.4106 + // the line's height *before* we placed any floats in the line itself. 1.4107 + // Floats that are in the line are handled during line reflow (and may 1.4108 + // result in floats being pushed to below the line or (I HOPE???) in a 1.4109 + // reflow with a forced break position). 1.4110 + nsRect oldFloatAvailableSpace(aFloatAvailableSpace); 1.4111 + // As we redo for floats, we can't reduce the amount of height we're 1.4112 + // checking. 1.4113 + aAvailableSpaceHeight = std::max(aAvailableSpaceHeight, aLine->BSize()); 1.4114 + aFloatAvailableSpace = 1.4115 + aState.GetFloatAvailableSpaceForHeight(aLine->BStart(), 1.4116 + aAvailableSpaceHeight, 1.4117 + aFloatStateBeforeLine).mRect; 1.4118 + NS_ASSERTION(aFloatAvailableSpace.y == oldFloatAvailableSpace.y, "yikes"); 1.4119 + // Restore the height to the position of the next band. 1.4120 + aFloatAvailableSpace.height = oldFloatAvailableSpace.height; 1.4121 + // If the available space between the floats is smaller now that we 1.4122 + // know the height, return false (and cause another pass with 1.4123 + // LINE_REFLOW_REDO_MORE_FLOATS). 1.4124 + if (AvailableSpaceShrunk(oldFloatAvailableSpace, aFloatAvailableSpace)) { 1.4125 + return false; 1.4126 + } 1.4127 + 1.4128 +#ifdef DEBUG 1.4129 + { 1.4130 + static nscoord lastHeight = 0; 1.4131 + if (CRAZY_SIZE(aLine->BStart())) { 1.4132 + lastHeight = aLine->BStart(); 1.4133 + if (abs(aLine->BStart() - lastHeight) > CRAZY_COORD/10) { 1.4134 + nsFrame::ListTag(stdout); 1.4135 + printf(": line=%p y=%d line.bounds.height=%d\n", 1.4136 + static_cast<void*>(aLine.get()), 1.4137 + aLine->BStart(), aLine->BSize()); 1.4138 + } 1.4139 + } 1.4140 + else { 1.4141 + lastHeight = 0; 1.4142 + } 1.4143 + } 1.4144 +#endif 1.4145 + 1.4146 + // Only block frames horizontally align their children because 1.4147 + // inline frames "shrink-wrap" around their children (therefore 1.4148 + // there is no extra horizontal space). 1.4149 + const nsStyleText* styleText = StyleText(); 1.4150 + 1.4151 + /** 1.4152 + * text-align-last defaults to the same value as text-align when 1.4153 + * text-align-last is set to auto (except when text-align is set to justify), 1.4154 + * so in that case we don't need to set isLastLine. 1.4155 + * 1.4156 + * In other words, isLastLine really means isLastLineAndWeCare. 1.4157 + */ 1.4158 + bool isLastLine = 1.4159 + !IsSVGText() && 1.4160 + ((NS_STYLE_TEXT_ALIGN_AUTO != styleText->mTextAlignLast || 1.4161 + NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign) && 1.4162 + (aLineLayout.GetLineEndsInBR() || 1.4163 + IsLastLine(aState, aLine))); 1.4164 + 1.4165 + aLineLayout.InlineDirAlignFrames(aLine, isLastLine); 1.4166 + 1.4167 + // From here on, pfd->mBounds rectangles are incorrect because bidi 1.4168 + // might have moved frames around! 1.4169 + nsOverflowAreas overflowAreas; 1.4170 + aLineLayout.RelativePositionFrames(overflowAreas); 1.4171 + aLine->SetOverflowAreas(overflowAreas); 1.4172 + if (addedBullet) { 1.4173 + aLineLayout.RemoveBulletFrame(GetOutsideBullet()); 1.4174 + } 1.4175 + 1.4176 + // Inline lines do not have margins themselves; however they are 1.4177 + // impacted by prior block margins. If this line ends up having some 1.4178 + // height then we zero out the previous bottom margin value that was 1.4179 + // already applied to the line's starting Y coordinate. Otherwise we 1.4180 + // leave it be so that the previous blocks bottom margin can be 1.4181 + // collapsed with a block that follows. 1.4182 + nscoord newY; 1.4183 + 1.4184 + if (!aLine->CachedIsEmpty()) { 1.4185 + // This line has some height. Therefore the application of the 1.4186 + // previous-bottom-margin should stick. 1.4187 + aState.mPrevBottomMargin.Zero(); 1.4188 + newY = aLine->BEnd(); 1.4189 + } 1.4190 + else { 1.4191 + // Don't let the previous-bottom-margin value affect the newY 1.4192 + // coordinate (it was applied in ReflowInlineFrames speculatively) 1.4193 + // since the line is empty. 1.4194 + // We already called |ShouldApplyTopMargin|, and if we applied it 1.4195 + // then BRS_APPLYTOPMARGIN is set. 1.4196 + nscoord dy = aState.GetFlag(BRS_APPLYTOPMARGIN) 1.4197 + ? -aState.mPrevBottomMargin.get() : 0; 1.4198 + newY = aState.mY + dy; 1.4199 + } 1.4200 + 1.4201 + if (!NS_FRAME_IS_FULLY_COMPLETE(aState.mReflowStatus) && 1.4202 + ShouldAvoidBreakInside(aState.mReflowState)) { 1.4203 + aLine->AppendFloats(aState.mCurrentLineFloats); 1.4204 + aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE(); 1.4205 + return true; 1.4206 + } 1.4207 + 1.4208 + // See if the line fit (our first line always does). 1.4209 + if (mLines.front() != aLine && 1.4210 + newY > aState.mBottomEdge && 1.4211 + aState.mBottomEdge != NS_UNCONSTRAINEDSIZE) { 1.4212 + NS_ASSERTION(aState.mCurrentLine == aLine, "oops"); 1.4213 + if (ShouldAvoidBreakInside(aState.mReflowState)) { 1.4214 + // All our content doesn't fit, start on the next page. 1.4215 + aState.mReflowStatus = NS_INLINE_LINE_BREAK_BEFORE(); 1.4216 + } else { 1.4217 + // Push aLine and all of its children and anything else that 1.4218 + // follows to our next-in-flow. 1.4219 + PushTruncatedLine(aState, aLine, aKeepReflowGoing); 1.4220 + } 1.4221 + return true; 1.4222 + } 1.4223 + 1.4224 + aState.mY = newY; 1.4225 + 1.4226 + // Add the already placed current-line floats to the line 1.4227 + aLine->AppendFloats(aState.mCurrentLineFloats); 1.4228 + 1.4229 + // Any below current line floats to place? 1.4230 + if (aState.mBelowCurrentLineFloats.NotEmpty()) { 1.4231 + // Reflow the below-current-line floats, which places on the line's 1.4232 + // float list. 1.4233 + aState.PlaceBelowCurrentLineFloats(aState.mBelowCurrentLineFloats, aLine); 1.4234 + aLine->AppendFloats(aState.mBelowCurrentLineFloats); 1.4235 + } 1.4236 + 1.4237 + // When a line has floats, factor them into the combined-area 1.4238 + // computations. 1.4239 + if (aLine->HasFloats()) { 1.4240 + // Combine the float combined area (stored in aState) and the 1.4241 + // value computed by the line layout code. 1.4242 + nsOverflowAreas lineOverflowAreas; 1.4243 + NS_FOR_FRAME_OVERFLOW_TYPES(otype) { 1.4244 + nsRect &o = lineOverflowAreas.Overflow(otype); 1.4245 + o = aLine->GetOverflowArea(otype); 1.4246 +#ifdef NOISY_COMBINED_AREA 1.4247 + ListTag(stdout); 1.4248 + printf(": overflow %d lineCA=%d,%d,%d,%d floatCA=%d,%d,%d,%d\n", 1.4249 + otype, 1.4250 + o.x, o.y, o.width, o.height, 1.4251 + aState.mFloatOverflowAreas.Overflow(otype).x, 1.4252 + aState.mFloatOverflowAreas.Overflow(otype).y, 1.4253 + aState.mFloatOverflowAreas.Overflow(otype).width, 1.4254 + aState.mFloatOverflowAreas.Overflow(otype).height); 1.4255 +#endif 1.4256 + o.UnionRect(aState.mFloatOverflowAreas.Overflow(otype), o); 1.4257 + 1.4258 +#ifdef NOISY_COMBINED_AREA 1.4259 + printf(" ==> final lineCA=%d,%d,%d,%d\n", 1.4260 + o.x, o.y, o.width, o.height); 1.4261 +#endif 1.4262 + } 1.4263 + aLine->SetOverflowAreas(lineOverflowAreas); 1.4264 + } 1.4265 + 1.4266 + // Apply break-after clearing if necessary 1.4267 + // This must stay in sync with |ReflowDirtyLines|. 1.4268 + if (aLine->HasFloatBreakAfter()) { 1.4269 + aState.mY = aState.ClearFloats(aState.mY, aLine->GetBreakTypeAfter()); 1.4270 + } 1.4271 + return true; 1.4272 +} 1.4273 + 1.4274 +void 1.4275 +nsBlockFrame::PushLines(nsBlockReflowState& aState, 1.4276 + nsLineList::iterator aLineBefore) 1.4277 +{ 1.4278 + // NOTE: aLineBefore is always a normal line, not an overflow line. 1.4279 + // The following expression will assert otherwise. 1.4280 + DebugOnly<bool> check = aLineBefore == mLines.begin(); 1.4281 + 1.4282 + nsLineList::iterator overBegin(aLineBefore.next()); 1.4283 + 1.4284 + // PushTruncatedPlaceholderLine sometimes pushes the first line. Ugh. 1.4285 + bool firstLine = overBegin == begin_lines(); 1.4286 + 1.4287 + if (overBegin != end_lines()) { 1.4288 + // Remove floats in the lines from mFloats 1.4289 + nsFrameList floats; 1.4290 + CollectFloats(overBegin->mFirstChild, floats, true); 1.4291 + 1.4292 + if (floats.NotEmpty()) { 1.4293 + // Push the floats onto the front of the overflow out-of-flows list 1.4294 + nsAutoOOFFrameList oofs(this); 1.4295 + oofs.mList.InsertFrames(nullptr, nullptr, floats); 1.4296 + } 1.4297 + 1.4298 + // overflow lines can already exist in some cases, in particular, 1.4299 + // when shrinkwrapping and we discover that the shrinkwap causes 1.4300 + // the height of some child block to grow which creates additional 1.4301 + // overflowing content. In such cases we must prepend the new 1.4302 + // overflow to the existing overflow. 1.4303 + FrameLines* overflowLines = RemoveOverflowLines(); 1.4304 + if (!overflowLines) { 1.4305 + // XXXldb use presshell arena! 1.4306 + overflowLines = new FrameLines(); 1.4307 + } 1.4308 + if (overflowLines) { 1.4309 + nsIFrame* lineBeforeLastFrame; 1.4310 + if (firstLine) { 1.4311 + lineBeforeLastFrame = nullptr; // removes all frames 1.4312 + } else { 1.4313 + nsIFrame* f = overBegin->mFirstChild; 1.4314 + lineBeforeLastFrame = f ? f->GetPrevSibling() : mFrames.LastChild(); 1.4315 + NS_ASSERTION(!f || lineBeforeLastFrame == aLineBefore->LastChild(), 1.4316 + "unexpected line frames"); 1.4317 + } 1.4318 + nsFrameList pushedFrames = mFrames.RemoveFramesAfter(lineBeforeLastFrame); 1.4319 + overflowLines->mFrames.InsertFrames(nullptr, nullptr, pushedFrames); 1.4320 + 1.4321 + overflowLines->mLines.splice(overflowLines->mLines.begin(), mLines, 1.4322 + overBegin, end_lines()); 1.4323 + NS_ASSERTION(!overflowLines->mLines.empty(), "should not be empty"); 1.4324 + // this takes ownership but it won't delete it immediately so we 1.4325 + // can keep using it. 1.4326 + SetOverflowLines(overflowLines); 1.4327 + 1.4328 + // Mark all the overflow lines dirty so that they get reflowed when 1.4329 + // they are pulled up by our next-in-flow. 1.4330 + 1.4331 + // XXXldb Can this get called O(N) times making the whole thing O(N^2)? 1.4332 + for (line_iterator line = overflowLines->mLines.begin(), 1.4333 + line_end = overflowLines->mLines.end(); 1.4334 + line != line_end; 1.4335 + ++line) 1.4336 + { 1.4337 + line->MarkDirty(); 1.4338 + line->MarkPreviousMarginDirty(); 1.4339 + line->SetBoundsEmpty(); 1.4340 + if (line->HasFloats()) { 1.4341 + line->FreeFloats(aState.mFloatCacheFreeList); 1.4342 + } 1.4343 + } 1.4344 + } 1.4345 + } 1.4346 + 1.4347 +#ifdef DEBUG 1.4348 + VerifyOverflowSituation(); 1.4349 +#endif 1.4350 +} 1.4351 + 1.4352 +// The overflowLines property is stored as a pointer to a line list, 1.4353 +// which must be deleted. However, the following functions all maintain 1.4354 +// the invariant that the property is never set if the list is empty. 1.4355 + 1.4356 +bool 1.4357 +nsBlockFrame::DrainOverflowLines() 1.4358 +{ 1.4359 +#ifdef DEBUG 1.4360 + VerifyOverflowSituation(); 1.4361 +#endif 1.4362 + 1.4363 + // Steal the prev-in-flow's overflow lines and prepend them. 1.4364 + bool didFindOverflow = false; 1.4365 + nsBlockFrame* prevBlock = static_cast<nsBlockFrame*>(GetPrevInFlow()); 1.4366 + if (prevBlock) { 1.4367 + prevBlock->ClearLineCursor(); 1.4368 + FrameLines* overflowLines = prevBlock->RemoveOverflowLines(); 1.4369 + if (overflowLines) { 1.4370 + // Make all the frames on the overflow line list mine. 1.4371 + ReparentFrames(overflowLines->mFrames, prevBlock, this); 1.4372 + 1.4373 + // Make the overflow out-of-flow frames mine too. 1.4374 + nsAutoOOFFrameList oofs(prevBlock); 1.4375 + if (oofs.mList.NotEmpty()) { 1.4376 + ReparentFrames(oofs.mList, prevBlock, this); 1.4377 + mFloats.InsertFrames(nullptr, nullptr, oofs.mList); 1.4378 + } 1.4379 + 1.4380 + if (!mLines.empty()) { 1.4381 + // Remember to recompute the margins on the first line. This will 1.4382 + // also recompute the correct deltaY if necessary. 1.4383 + mLines.front()->MarkPreviousMarginDirty(); 1.4384 + } 1.4385 + // The overflow lines have already been marked dirty and their previous 1.4386 + // margins marked dirty also. 1.4387 + 1.4388 + // Prepend the overflow frames/lines to our principal list. 1.4389 + mFrames.InsertFrames(nullptr, nullptr, overflowLines->mFrames); 1.4390 + mLines.splice(mLines.begin(), overflowLines->mLines); 1.4391 + NS_ASSERTION(overflowLines->mLines.empty(), "splice should empty list"); 1.4392 + delete overflowLines; 1.4393 + didFindOverflow = true; 1.4394 + } 1.4395 + } 1.4396 + 1.4397 + // Now append our own overflow lines. 1.4398 + return DrainSelfOverflowList() || didFindOverflow; 1.4399 +} 1.4400 + 1.4401 +bool 1.4402 +nsBlockFrame::DrainSelfOverflowList() 1.4403 +{ 1.4404 + nsAutoPtr<FrameLines> ourOverflowLines(RemoveOverflowLines()); 1.4405 + if (!ourOverflowLines) { 1.4406 + return false; 1.4407 + } 1.4408 + 1.4409 + // No need to reparent frames in our own overflow lines/oofs, because they're 1.4410 + // already ours. But we should put overflow floats back in mFloats. 1.4411 + nsAutoOOFFrameList oofs(this); 1.4412 + if (oofs.mList.NotEmpty()) { 1.4413 + // The overflow floats go after our regular floats. 1.4414 + mFloats.AppendFrames(nullptr, oofs.mList); 1.4415 + } 1.4416 + 1.4417 + if (!ourOverflowLines->mLines.empty()) { 1.4418 + mFrames.AppendFrames(nullptr, ourOverflowLines->mFrames); 1.4419 + mLines.splice(mLines.end(), ourOverflowLines->mLines); 1.4420 + } 1.4421 + return true; 1.4422 +} 1.4423 + 1.4424 +/** 1.4425 + * Pushed floats are floats whose placeholders are in a previous 1.4426 + * continuation. They might themselves be next-continuations of a float 1.4427 + * that partially fit in an earlier continuation, or they might be the 1.4428 + * first continuation of a float that couldn't be placed at all. 1.4429 + * 1.4430 + * Pushed floats live permanently at the beginning of a block's float 1.4431 + * list, where they must live *before* any floats whose placeholders are 1.4432 + * in that block. 1.4433 + * 1.4434 + * Temporarily, during reflow, they also live on the pushed floats list, 1.4435 + * which only holds them between (a) when one continuation pushes them to 1.4436 + * its pushed floats list because they don't fit and (b) when the next 1.4437 + * continuation pulls them onto the beginning of its float list. 1.4438 + * 1.4439 + * DrainPushedFloats sets up pushed floats the way we need them at the 1.4440 + * start of reflow; they are then reflowed by ReflowPushedFloats (which 1.4441 + * might push some of them on). Floats with placeholders in this block 1.4442 + * are reflowed by (nsBlockReflowState/nsLineLayout)::AddFloat, which 1.4443 + * also maintains these invariants. 1.4444 + */ 1.4445 +void 1.4446 +nsBlockFrame::DrainPushedFloats(nsBlockReflowState& aState) 1.4447 +{ 1.4448 +#ifdef DEBUG 1.4449 + // Between when we drain pushed floats and when we complete reflow, 1.4450 + // we're allowed to have multiple continuations of the same float on 1.4451 + // our floats list, since a first-in-flow might get pushed to a later 1.4452 + // continuation of its containing block. But it's not permitted 1.4453 + // outside that time. 1.4454 + nsLayoutUtils::AssertNoDuplicateContinuations(this, mFloats); 1.4455 +#endif 1.4456 + 1.4457 + // If we're getting reflowed multiple times without our 1.4458 + // next-continuation being reflowed, we might need to pull back floats 1.4459 + // that we just put in the list to be pushed to our next-in-flow. 1.4460 + // We don't want to pull back any next-in-flows of floats on our own 1.4461 + // float list, and we only need to pull back first-in-flows whose 1.4462 + // placeholders were in earlier blocks (since first-in-flows whose 1.4463 + // placeholders are in this block will get pulled appropriately by 1.4464 + // AddFloat, and will then be more likely to be in the correct order). 1.4465 + // FIXME: What if there's a continuation in our pushed floats list 1.4466 + // whose prev-in-flow is in a previous continuation of this block 1.4467 + // rather than this block? Might we need to pull it back so we don't 1.4468 + // report ourselves complete? 1.4469 + // FIXME: Maybe we should just pull all of them back? 1.4470 + nsPresContext* presContext = PresContext(); 1.4471 + nsFrameList* ourPushedFloats = GetPushedFloats(); 1.4472 + if (ourPushedFloats) { 1.4473 + // When we pull back floats, we want to put them with the pushed 1.4474 + // floats, which must live at the start of our float list, but we 1.4475 + // want them at the end of those pushed floats. 1.4476 + // FIXME: This isn't quite right! What if they're all pushed floats? 1.4477 + nsIFrame *insertionPrevSibling = nullptr; /* beginning of list */ 1.4478 + for (nsIFrame* f = mFloats.FirstChild(); 1.4479 + f && (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT); 1.4480 + f = f->GetNextSibling()) { 1.4481 + insertionPrevSibling = f; 1.4482 + } 1.4483 + 1.4484 + for (nsIFrame *f = ourPushedFloats->LastChild(), *next; f; f = next) { 1.4485 + next = f->GetPrevSibling(); 1.4486 + 1.4487 + if (f->GetPrevContinuation()) { 1.4488 + // FIXME 1.4489 + } else { 1.4490 + nsPlaceholderFrame *placeholder = 1.4491 + presContext->FrameManager()->GetPlaceholderFrameFor(f); 1.4492 + nsIFrame *floatOriginalParent = presContext->PresShell()-> 1.4493 + FrameConstructor()->GetFloatContainingBlock(placeholder); 1.4494 + if (floatOriginalParent != this) { 1.4495 + // This is a first continuation that was pushed from one of our 1.4496 + // previous continuations. Take it out of the pushed floats 1.4497 + // list and put it in our floats list, before any of our 1.4498 + // floats, but after other pushed floats. 1.4499 + ourPushedFloats->RemoveFrame(f); 1.4500 + mFloats.InsertFrame(nullptr, insertionPrevSibling, f); 1.4501 + } 1.4502 + } 1.4503 + } 1.4504 + 1.4505 + if (ourPushedFloats->IsEmpty()) { 1.4506 + RemovePushedFloats()->Delete(presContext->PresShell()); 1.4507 + } 1.4508 + } 1.4509 + 1.4510 + // After our prev-in-flow has completed reflow, it may have a pushed 1.4511 + // floats list, containing floats that we need to own. Take these. 1.4512 + nsBlockFrame* prevBlock = static_cast<nsBlockFrame*>(GetPrevInFlow()); 1.4513 + if (prevBlock) { 1.4514 + AutoFrameListPtr list(presContext, prevBlock->RemovePushedFloats()); 1.4515 + if (list && list->NotEmpty()) { 1.4516 + mFloats.InsertFrames(this, nullptr, *list); 1.4517 + } 1.4518 + } 1.4519 +} 1.4520 + 1.4521 +nsBlockFrame::FrameLines* 1.4522 +nsBlockFrame::GetOverflowLines() const 1.4523 +{ 1.4524 + if (!HasOverflowLines()) { 1.4525 + return nullptr; 1.4526 + } 1.4527 + FrameLines* prop = 1.4528 + static_cast<FrameLines*>(Properties().Get(OverflowLinesProperty())); 1.4529 + NS_ASSERTION(prop && !prop->mLines.empty() && 1.4530 + prop->mLines.front()->GetChildCount() == 0 ? prop->mFrames.IsEmpty() : 1.4531 + prop->mLines.front()->mFirstChild == prop->mFrames.FirstChild(), 1.4532 + "value should always be stored and non-empty when state set"); 1.4533 + return prop; 1.4534 +} 1.4535 + 1.4536 +nsBlockFrame::FrameLines* 1.4537 +nsBlockFrame::RemoveOverflowLines() 1.4538 +{ 1.4539 + if (!HasOverflowLines()) { 1.4540 + return nullptr; 1.4541 + } 1.4542 + FrameLines* prop = 1.4543 + static_cast<FrameLines*>(Properties().Remove(OverflowLinesProperty())); 1.4544 + NS_ASSERTION(prop && !prop->mLines.empty() && 1.4545 + prop->mLines.front()->GetChildCount() == 0 ? prop->mFrames.IsEmpty() : 1.4546 + prop->mLines.front()->mFirstChild == prop->mFrames.FirstChild(), 1.4547 + "value should always be stored and non-empty when state set"); 1.4548 + RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES); 1.4549 + return prop; 1.4550 +} 1.4551 + 1.4552 +void 1.4553 +nsBlockFrame::DestroyOverflowLines() 1.4554 +{ 1.4555 + NS_ASSERTION(HasOverflowLines(), "huh?"); 1.4556 + FrameLines* prop = 1.4557 + static_cast<FrameLines*>(Properties().Remove(OverflowLinesProperty())); 1.4558 + NS_ASSERTION(prop && prop->mLines.empty(), 1.4559 + "value should always be stored but empty when destroying"); 1.4560 + RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_LINES); 1.4561 + delete prop; 1.4562 +} 1.4563 + 1.4564 +// This takes ownership of aOverflowLines. 1.4565 +// XXX We should allocate overflowLines from presShell arena! 1.4566 +void 1.4567 +nsBlockFrame::SetOverflowLines(FrameLines* aOverflowLines) 1.4568 +{ 1.4569 + NS_ASSERTION(aOverflowLines, "null lines"); 1.4570 + NS_ASSERTION(!aOverflowLines->mLines.empty(), "empty lines"); 1.4571 + NS_ASSERTION(aOverflowLines->mLines.front()->mFirstChild == 1.4572 + aOverflowLines->mFrames.FirstChild(), 1.4573 + "invalid overflow lines / frames"); 1.4574 + NS_ASSERTION(!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_LINES), 1.4575 + "Overwriting existing overflow lines"); 1.4576 + 1.4577 + FrameProperties props = Properties(); 1.4578 + // Verify that we won't overwrite an existing overflow list 1.4579 + NS_ASSERTION(!props.Get(OverflowLinesProperty()), "existing overflow list"); 1.4580 + props.Set(OverflowLinesProperty(), aOverflowLines); 1.4581 + AddStateBits(NS_BLOCK_HAS_OVERFLOW_LINES); 1.4582 +} 1.4583 + 1.4584 +nsFrameList* 1.4585 +nsBlockFrame::GetOverflowOutOfFlows() const 1.4586 +{ 1.4587 + if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) { 1.4588 + return nullptr; 1.4589 + } 1.4590 + nsFrameList* result = 1.4591 + GetPropTableFrames(OverflowOutOfFlowsProperty()); 1.4592 + NS_ASSERTION(result, "value should always be non-empty when state set"); 1.4593 + return result; 1.4594 +} 1.4595 + 1.4596 +// This takes ownership of the frames 1.4597 +void 1.4598 +nsBlockFrame::SetOverflowOutOfFlows(const nsFrameList& aList, 1.4599 + nsFrameList* aPropValue) 1.4600 +{ 1.4601 + NS_PRECONDITION(!!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) == 1.4602 + !!aPropValue, "state does not match value"); 1.4603 + 1.4604 + if (aList.IsEmpty()) { 1.4605 + if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) { 1.4606 + return; 1.4607 + } 1.4608 + nsFrameList* list = RemovePropTableFrames(OverflowOutOfFlowsProperty()); 1.4609 + NS_ASSERTION(aPropValue == list, "prop value mismatch"); 1.4610 + list->Clear(); 1.4611 + list->Delete(PresContext()->PresShell()); 1.4612 + RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS); 1.4613 + } 1.4614 + else if (GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) { 1.4615 + NS_ASSERTION(aPropValue == GetPropTableFrames(OverflowOutOfFlowsProperty()), 1.4616 + "prop value mismatch"); 1.4617 + *aPropValue = aList; 1.4618 + } 1.4619 + else { 1.4620 + SetPropTableFrames(new (PresContext()->PresShell()) nsFrameList(aList), 1.4621 + OverflowOutOfFlowsProperty()); 1.4622 + AddStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS); 1.4623 + } 1.4624 +} 1.4625 + 1.4626 +nsBulletFrame* 1.4627 +nsBlockFrame::GetInsideBullet() const 1.4628 +{ 1.4629 + if (!HasInsideBullet()) { 1.4630 + return nullptr; 1.4631 + } 1.4632 + NS_ASSERTION(!HasOutsideBullet(), "invalid bullet state"); 1.4633 + nsBulletFrame* frame = 1.4634 + static_cast<nsBulletFrame*>(Properties().Get(InsideBulletProperty())); 1.4635 + NS_ASSERTION(frame && frame->GetType() == nsGkAtoms::bulletFrame, 1.4636 + "bogus inside bullet frame"); 1.4637 + return frame; 1.4638 +} 1.4639 + 1.4640 +nsBulletFrame* 1.4641 +nsBlockFrame::GetOutsideBullet() const 1.4642 +{ 1.4643 + nsFrameList* list = GetOutsideBulletList(); 1.4644 + return list ? static_cast<nsBulletFrame*>(list->FirstChild()) 1.4645 + : nullptr; 1.4646 +} 1.4647 + 1.4648 +nsFrameList* 1.4649 +nsBlockFrame::GetOutsideBulletList() const 1.4650 +{ 1.4651 + if (!HasOutsideBullet()) { 1.4652 + return nullptr; 1.4653 + } 1.4654 + NS_ASSERTION(!HasInsideBullet(), "invalid bullet state"); 1.4655 + nsFrameList* list = 1.4656 + static_cast<nsFrameList*>(Properties().Get(OutsideBulletProperty())); 1.4657 + NS_ASSERTION(list && list->GetLength() == 1 && 1.4658 + list->FirstChild()->GetType() == nsGkAtoms::bulletFrame, 1.4659 + "bogus outside bullet list"); 1.4660 + return list; 1.4661 +} 1.4662 + 1.4663 +nsFrameList* 1.4664 +nsBlockFrame::GetPushedFloats() const 1.4665 +{ 1.4666 + if (!HasPushedFloats()) { 1.4667 + return nullptr; 1.4668 + } 1.4669 + nsFrameList* result = 1.4670 + static_cast<nsFrameList*>(Properties().Get(PushedFloatProperty())); 1.4671 + NS_ASSERTION(result, "value should always be non-empty when state set"); 1.4672 + return result; 1.4673 +} 1.4674 + 1.4675 +nsFrameList* 1.4676 +nsBlockFrame::EnsurePushedFloats() 1.4677 +{ 1.4678 + nsFrameList *result = GetPushedFloats(); 1.4679 + if (result) 1.4680 + return result; 1.4681 + 1.4682 + result = new (PresContext()->PresShell()) nsFrameList; 1.4683 + Properties().Set(PushedFloatProperty(), result); 1.4684 + AddStateBits(NS_BLOCK_HAS_PUSHED_FLOATS); 1.4685 + 1.4686 + return result; 1.4687 +} 1.4688 + 1.4689 +nsFrameList* 1.4690 +nsBlockFrame::RemovePushedFloats() 1.4691 +{ 1.4692 + if (!HasPushedFloats()) { 1.4693 + return nullptr; 1.4694 + } 1.4695 + nsFrameList *result = 1.4696 + static_cast<nsFrameList*>(Properties().Remove(PushedFloatProperty())); 1.4697 + RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS); 1.4698 + NS_ASSERTION(result, "value should always be non-empty when state set"); 1.4699 + return result; 1.4700 +} 1.4701 + 1.4702 +////////////////////////////////////////////////////////////////////// 1.4703 +// Frame list manipulation routines 1.4704 + 1.4705 +nsresult 1.4706 +nsBlockFrame::AppendFrames(ChildListID aListID, 1.4707 + nsFrameList& aFrameList) 1.4708 +{ 1.4709 + if (aFrameList.IsEmpty()) { 1.4710 + return NS_OK; 1.4711 + } 1.4712 + if (aListID != kPrincipalList) { 1.4713 + if (kAbsoluteList == aListID) { 1.4714 + return nsContainerFrame::AppendFrames(aListID, aFrameList); 1.4715 + } 1.4716 + else if (kFloatList == aListID) { 1.4717 + mFloats.AppendFrames(nullptr, aFrameList); 1.4718 + return NS_OK; 1.4719 + } 1.4720 + else { 1.4721 + NS_ERROR("unexpected child list"); 1.4722 + return NS_ERROR_INVALID_ARG; 1.4723 + } 1.4724 + } 1.4725 + 1.4726 + // Find the proper last-child for where the append should go 1.4727 + nsIFrame* lastKid = mFrames.LastChild(); 1.4728 + NS_ASSERTION((mLines.empty() ? nullptr : mLines.back()->LastChild()) == 1.4729 + lastKid, "out-of-sync mLines / mFrames"); 1.4730 + 1.4731 + // Add frames after the last child 1.4732 +#ifdef NOISY_REFLOW_REASON 1.4733 + ListTag(stdout); 1.4734 + printf(": append "); 1.4735 + nsFrame::ListTag(stdout, aFrameList); 1.4736 + if (lastKid) { 1.4737 + printf(" after "); 1.4738 + nsFrame::ListTag(stdout, lastKid); 1.4739 + } 1.4740 + printf("\n"); 1.4741 +#endif 1.4742 + 1.4743 + AddFrames(aFrameList, lastKid); 1.4744 + PresContext()->PresShell()-> 1.4745 + FrameNeedsReflow(this, nsIPresShell::eTreeChange, 1.4746 + NS_FRAME_HAS_DIRTY_CHILDREN); // XXX sufficient? 1.4747 + return NS_OK; 1.4748 +} 1.4749 + 1.4750 +nsresult 1.4751 +nsBlockFrame::InsertFrames(ChildListID aListID, 1.4752 + nsIFrame* aPrevFrame, 1.4753 + nsFrameList& aFrameList) 1.4754 +{ 1.4755 + NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this, 1.4756 + "inserting after sibling frame with different parent"); 1.4757 + 1.4758 + if (aListID != kPrincipalList) { 1.4759 + if (kAbsoluteList == aListID) { 1.4760 + return nsContainerFrame::InsertFrames(aListID, aPrevFrame, aFrameList); 1.4761 + } 1.4762 + else if (kFloatList == aListID) { 1.4763 + mFloats.InsertFrames(this, aPrevFrame, aFrameList); 1.4764 + return NS_OK; 1.4765 + } 1.4766 + else if (kNoReflowPrincipalList != aListID) { 1.4767 + NS_ERROR("unexpected child list"); 1.4768 + return NS_ERROR_INVALID_ARG; 1.4769 + } 1.4770 + } 1.4771 + 1.4772 +#ifdef NOISY_REFLOW_REASON 1.4773 + ListTag(stdout); 1.4774 + printf(": insert "); 1.4775 + nsFrame::ListTag(stdout, aFrameList); 1.4776 + if (aPrevFrame) { 1.4777 + printf(" after "); 1.4778 + nsFrame::ListTag(stdout, aPrevFrame); 1.4779 + } 1.4780 + printf("\n"); 1.4781 +#endif 1.4782 + 1.4783 + AddFrames(aFrameList, aPrevFrame); 1.4784 + 1.4785 + if (aListID != kNoReflowPrincipalList) 1.4786 + PresContext()->PresShell()-> 1.4787 + FrameNeedsReflow(this, nsIPresShell::eTreeChange, 1.4788 + NS_FRAME_HAS_DIRTY_CHILDREN); // XXX sufficient? 1.4789 + return NS_OK; 1.4790 +} 1.4791 + 1.4792 +static bool 1.4793 +ShouldPutNextSiblingOnNewLine(nsIFrame* aLastFrame) 1.4794 +{ 1.4795 + nsIAtom* type = aLastFrame->GetType(); 1.4796 + if (type == nsGkAtoms::brFrame) { 1.4797 + return true; 1.4798 + } 1.4799 + // XXX the TEXT_OFFSETS_NEED_FIXING check is a wallpaper for bug 822910. 1.4800 + if (type == nsGkAtoms::textFrame && 1.4801 + !(aLastFrame->GetStateBits() & TEXT_OFFSETS_NEED_FIXING)) { 1.4802 + return aLastFrame->HasSignificantTerminalNewline(); 1.4803 + } 1.4804 + return false; 1.4805 +} 1.4806 + 1.4807 +void 1.4808 +nsBlockFrame::AddFrames(nsFrameList& aFrameList, nsIFrame* aPrevSibling) 1.4809 +{ 1.4810 + // Clear our line cursor, since our lines may change. 1.4811 + ClearLineCursor(); 1.4812 + 1.4813 + if (aFrameList.IsEmpty()) { 1.4814 + return; 1.4815 + } 1.4816 + 1.4817 + // If we're inserting at the beginning of our list and we have an 1.4818 + // inside bullet, insert after that bullet. 1.4819 + if (!aPrevSibling && HasInsideBullet()) { 1.4820 + aPrevSibling = GetInsideBullet(); 1.4821 + } 1.4822 + 1.4823 + // Attempt to find the line that contains the previous sibling 1.4824 + FrameLines* overflowLines; 1.4825 + nsLineList* lineList = &mLines; 1.4826 + nsLineList::iterator prevSibLine = lineList->end(); 1.4827 + int32_t prevSiblingIndex = -1; 1.4828 + if (aPrevSibling) { 1.4829 + // XXX_perf This is technically O(N^2) in some cases, but by using 1.4830 + // RFind instead of Find, we make it O(N) in the most common case, 1.4831 + // which is appending content. 1.4832 + 1.4833 + // Find the line that contains the previous sibling 1.4834 + if (!nsLineBox::RFindLineContaining(aPrevSibling, lineList->begin(), 1.4835 + prevSibLine, mFrames.LastChild(), 1.4836 + &prevSiblingIndex)) { 1.4837 + // Not in mLines - try overflow lines. 1.4838 + overflowLines = GetOverflowLines(); 1.4839 + lineList = overflowLines ? &overflowLines->mLines : nullptr; 1.4840 + if (overflowLines) { 1.4841 + prevSibLine = overflowLines->mLines.end(); 1.4842 + prevSiblingIndex = -1; 1.4843 + if (!nsLineBox::RFindLineContaining(aPrevSibling, lineList->begin(), 1.4844 + prevSibLine, 1.4845 + overflowLines->mFrames.LastChild(), 1.4846 + &prevSiblingIndex)) { 1.4847 + lineList = nullptr; 1.4848 + } 1.4849 + } 1.4850 + if (!lineList) { 1.4851 + // Note: defensive code! RFindLineContaining must not return 1.4852 + // false in this case, so if it does... 1.4853 + NS_NOTREACHED("prev sibling not in line list"); 1.4854 + lineList = &mLines; 1.4855 + aPrevSibling = nullptr; 1.4856 + prevSibLine = lineList->end(); 1.4857 + } 1.4858 + } 1.4859 + } 1.4860 + 1.4861 + // Find the frame following aPrevSibling so that we can join up the 1.4862 + // two lists of frames. 1.4863 + if (aPrevSibling) { 1.4864 + // Split line containing aPrevSibling in two if the insertion 1.4865 + // point is somewhere in the middle of the line. 1.4866 + int32_t rem = prevSibLine->GetChildCount() - prevSiblingIndex - 1; 1.4867 + if (rem) { 1.4868 + // Split the line in two where the frame(s) are being inserted. 1.4869 + nsLineBox* line = NewLineBox(prevSibLine, aPrevSibling->GetNextSibling(), rem); 1.4870 + lineList->after_insert(prevSibLine, line); 1.4871 + // Mark prevSibLine dirty and as needing textrun invalidation, since 1.4872 + // we may be breaking up text in the line. Its previous line may also 1.4873 + // need to be invalidated because it may be able to pull some text up. 1.4874 + MarkLineDirty(prevSibLine, lineList); 1.4875 + // The new line will also need its textruns recomputed because of the 1.4876 + // frame changes. 1.4877 + line->MarkDirty(); 1.4878 + line->SetInvalidateTextRuns(true); 1.4879 + } 1.4880 + } 1.4881 + else if (! lineList->empty()) { 1.4882 + lineList->front()->MarkDirty(); 1.4883 + lineList->front()->SetInvalidateTextRuns(true); 1.4884 + } 1.4885 + nsFrameList& frames = lineList == &mLines ? mFrames : overflowLines->mFrames; 1.4886 + const nsFrameList::Slice& newFrames = 1.4887 + frames.InsertFrames(nullptr, aPrevSibling, aFrameList); 1.4888 + 1.4889 + // Walk through the new frames being added and update the line data 1.4890 + // structures to fit. 1.4891 + for (nsFrameList::Enumerator e(newFrames); !e.AtEnd(); e.Next()) { 1.4892 + nsIFrame* newFrame = e.get(); 1.4893 + NS_ASSERTION(!aPrevSibling || aPrevSibling->GetNextSibling() == newFrame, 1.4894 + "Unexpected aPrevSibling"); 1.4895 + NS_ASSERTION(newFrame->GetType() != nsGkAtoms::placeholderFrame || 1.4896 + (!newFrame->IsAbsolutelyPositioned() && 1.4897 + !newFrame->IsFloating()), 1.4898 + "Placeholders should not float or be positioned"); 1.4899 + 1.4900 + bool isBlock = newFrame->IsBlockOutside(); 1.4901 + 1.4902 + // If the frame is a block frame, or if there is no previous line or if the 1.4903 + // previous line is a block line we need to make a new line. We also make 1.4904 + // a new line, as an optimization, in the two cases we know we'll need it: 1.4905 + // if the previous line ended with a <br>, or if it has significant whitespace 1.4906 + // and ended in a newline. 1.4907 + if (isBlock || prevSibLine == lineList->end() || prevSibLine->IsBlock() || 1.4908 + (aPrevSibling && ShouldPutNextSiblingOnNewLine(aPrevSibling))) { 1.4909 + // Create a new line for the frame and add its line to the line 1.4910 + // list. 1.4911 + nsLineBox* line = NewLineBox(newFrame, isBlock); 1.4912 + if (prevSibLine != lineList->end()) { 1.4913 + // Append new line after prevSibLine 1.4914 + lineList->after_insert(prevSibLine, line); 1.4915 + ++prevSibLine; 1.4916 + } 1.4917 + else { 1.4918 + // New line is going before the other lines 1.4919 + lineList->push_front(line); 1.4920 + prevSibLine = lineList->begin(); 1.4921 + } 1.4922 + } 1.4923 + else { 1.4924 + prevSibLine->NoteFrameAdded(newFrame); 1.4925 + // We're adding inline content to prevSibLine, so we need to mark it 1.4926 + // dirty, ensure its textruns are recomputed, and possibly do the same 1.4927 + // to its previous line since that line may be able to pull content up. 1.4928 + MarkLineDirty(prevSibLine, lineList); 1.4929 + } 1.4930 + 1.4931 + aPrevSibling = newFrame; 1.4932 + } 1.4933 + 1.4934 +#ifdef DEBUG 1.4935 + MOZ_ASSERT(aFrameList.IsEmpty()); 1.4936 + VerifyLines(true); 1.4937 +#endif 1.4938 +} 1.4939 + 1.4940 +void 1.4941 +nsBlockFrame::RemoveFloatFromFloatCache(nsIFrame* aFloat) 1.4942 +{ 1.4943 + // Find which line contains the float, so we can update 1.4944 + // the float cache. 1.4945 + line_iterator line = begin_lines(), line_end = end_lines(); 1.4946 + for ( ; line != line_end; ++line) { 1.4947 + if (line->IsInline() && line->RemoveFloat(aFloat)) { 1.4948 + break; 1.4949 + } 1.4950 + } 1.4951 +} 1.4952 + 1.4953 +void 1.4954 +nsBlockFrame::RemoveFloat(nsIFrame* aFloat) 1.4955 +{ 1.4956 +#ifdef DEBUG 1.4957 + // Floats live in mFloats, or in the PushedFloat or OverflowOutOfFlows 1.4958 + // frame list properties. 1.4959 + if (!mFloats.ContainsFrame(aFloat)) { 1.4960 + MOZ_ASSERT((GetOverflowOutOfFlows() && 1.4961 + GetOverflowOutOfFlows()->ContainsFrame(aFloat)) || 1.4962 + (GetPushedFloats() && 1.4963 + GetPushedFloats()->ContainsFrame(aFloat)), 1.4964 + "aFloat is not our child or on an unexpected frame list"); 1.4965 + } 1.4966 +#endif 1.4967 + 1.4968 + if (mFloats.StartRemoveFrame(aFloat)) { 1.4969 + return; 1.4970 + } 1.4971 + 1.4972 + nsFrameList* list = GetPushedFloats(); 1.4973 + if (list && list->ContinueRemoveFrame(aFloat)) { 1.4974 +#if 0 1.4975 + // XXXmats not yet - need to investigate nsBlockReflowState::mPushedFloats 1.4976 + // first so we don't leave it pointing to a deleted list. 1.4977 + if (list->IsEmpty()) { 1.4978 + delete RemovePushedFloats(); 1.4979 + } 1.4980 +#endif 1.4981 + return; 1.4982 + } 1.4983 + 1.4984 + { 1.4985 + nsAutoOOFFrameList oofs(this); 1.4986 + if (oofs.mList.ContinueRemoveFrame(aFloat)) { 1.4987 + return; 1.4988 + } 1.4989 + } 1.4990 +} 1.4991 + 1.4992 +static void MarkSameFloatManagerLinesDirty(nsBlockFrame* aBlock) 1.4993 +{ 1.4994 + nsBlockFrame* blockWithFloatMgr = aBlock; 1.4995 + while (!(blockWithFloatMgr->GetStateBits() & NS_BLOCK_FLOAT_MGR)) { 1.4996 + nsBlockFrame* bf = nsLayoutUtils::GetAsBlock(blockWithFloatMgr->GetParent()); 1.4997 + if (!bf) { 1.4998 + break; 1.4999 + } 1.5000 + blockWithFloatMgr = bf; 1.5001 + } 1.5002 + 1.5003 + // Mark every line at and below the line where the float was 1.5004 + // dirty, and mark their lines dirty too. We could probably do 1.5005 + // something more efficient --- e.g., just dirty the lines that intersect 1.5006 + // the float vertically. 1.5007 + MarkAllDescendantLinesDirty(blockWithFloatMgr); 1.5008 +} 1.5009 + 1.5010 +/** 1.5011 + * Returns true if aFrame is a block that has one or more float children. 1.5012 + */ 1.5013 +static bool BlockHasAnyFloats(nsIFrame* aFrame) 1.5014 +{ 1.5015 + nsBlockFrame* block = nsLayoutUtils::GetAsBlock(aFrame); 1.5016 + if (!block) 1.5017 + return false; 1.5018 + if (block->GetFirstChild(nsIFrame::kFloatList)) 1.5019 + return true; 1.5020 + 1.5021 + nsLineList::iterator line = block->begin_lines(); 1.5022 + nsLineList::iterator endLine = block->end_lines(); 1.5023 + while (line != endLine) { 1.5024 + if (line->IsBlock() && BlockHasAnyFloats(line->mFirstChild)) 1.5025 + return true; 1.5026 + ++line; 1.5027 + } 1.5028 + return false; 1.5029 +} 1.5030 + 1.5031 +nsresult 1.5032 +nsBlockFrame::RemoveFrame(ChildListID aListID, 1.5033 + nsIFrame* aOldFrame) 1.5034 +{ 1.5035 + nsresult rv = NS_OK; 1.5036 + 1.5037 +#ifdef NOISY_REFLOW_REASON 1.5038 + ListTag(stdout); 1.5039 + printf(": remove "); 1.5040 + nsFrame::ListTag(stdout, aOldFrame); 1.5041 + printf("\n"); 1.5042 +#endif 1.5043 + 1.5044 + if (aListID == kPrincipalList) { 1.5045 + bool hasFloats = BlockHasAnyFloats(aOldFrame); 1.5046 + rv = DoRemoveFrame(aOldFrame, REMOVE_FIXED_CONTINUATIONS); 1.5047 + if (hasFloats) { 1.5048 + MarkSameFloatManagerLinesDirty(this); 1.5049 + } 1.5050 + } 1.5051 + else if (kAbsoluteList == aListID) { 1.5052 + nsContainerFrame::RemoveFrame(aListID, aOldFrame); 1.5053 + return NS_OK; 1.5054 + } 1.5055 + else if (kFloatList == aListID) { 1.5056 + // Make sure to mark affected lines dirty for the float frame 1.5057 + // we are removing; this way is a bit messy, but so is the rest of the code. 1.5058 + // See bug 390762. 1.5059 + NS_ASSERTION(!aOldFrame->GetPrevContinuation(), 1.5060 + "RemoveFrame should not be called on pushed floats."); 1.5061 + for (nsIFrame* f = aOldFrame; 1.5062 + f && !(f->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER); 1.5063 + f = f->GetNextContinuation()) { 1.5064 + MarkSameFloatManagerLinesDirty(static_cast<nsBlockFrame*>(f->GetParent())); 1.5065 + } 1.5066 + DoRemoveOutOfFlowFrame(aOldFrame); 1.5067 + } 1.5068 + else if (kNoReflowPrincipalList == aListID) { 1.5069 + // Skip the call to |FrameNeedsReflow| below by returning now. 1.5070 + return DoRemoveFrame(aOldFrame, REMOVE_FIXED_CONTINUATIONS); 1.5071 + } 1.5072 + else { 1.5073 + NS_ERROR("unexpected child list"); 1.5074 + rv = NS_ERROR_INVALID_ARG; 1.5075 + } 1.5076 + 1.5077 + if (NS_SUCCEEDED(rv)) { 1.5078 + PresContext()->PresShell()-> 1.5079 + FrameNeedsReflow(this, nsIPresShell::eTreeChange, 1.5080 + NS_FRAME_HAS_DIRTY_CHILDREN); // XXX sufficient? 1.5081 + } 1.5082 + return rv; 1.5083 +} 1.5084 + 1.5085 +void 1.5086 +nsBlockFrame::DoRemoveOutOfFlowFrame(nsIFrame* aFrame) 1.5087 +{ 1.5088 + // The containing block is always the parent of aFrame. 1.5089 + nsBlockFrame* block = (nsBlockFrame*)aFrame->GetParent(); 1.5090 + 1.5091 + // Remove aFrame from the appropriate list. 1.5092 + if (aFrame->IsAbsolutelyPositioned()) { 1.5093 + // This also deletes the next-in-flows 1.5094 + block->GetAbsoluteContainingBlock()->RemoveFrame(block, 1.5095 + kAbsoluteList, 1.5096 + aFrame); 1.5097 + } 1.5098 + else { 1.5099 + // First remove aFrame's next-in-flows. 1.5100 + nsIFrame* nif = aFrame->GetNextInFlow(); 1.5101 + if (nif) { 1.5102 + static_cast<nsContainerFrame*>(nif->GetParent()) 1.5103 + ->DeleteNextInFlowChild(nif, false); 1.5104 + } 1.5105 + // Now remove aFrame from its child list and Destroy it. 1.5106 + block->RemoveFloatFromFloatCache(aFrame); 1.5107 + block->RemoveFloat(aFrame); 1.5108 + aFrame->Destroy(); 1.5109 + } 1.5110 +} 1.5111 + 1.5112 +/** 1.5113 + * This helps us iterate over the list of all normal + overflow lines 1.5114 + */ 1.5115 +void 1.5116 +nsBlockFrame::TryAllLines(nsLineList::iterator* aIterator, 1.5117 + nsLineList::iterator* aStartIterator, 1.5118 + nsLineList::iterator* aEndIterator, 1.5119 + bool* aInOverflowLines, 1.5120 + FrameLines** aOverflowLines) 1.5121 +{ 1.5122 + if (*aIterator == *aEndIterator) { 1.5123 + if (!*aInOverflowLines) { 1.5124 + // Try the overflow lines 1.5125 + *aInOverflowLines = true; 1.5126 + FrameLines* lines = GetOverflowLines(); 1.5127 + if (lines) { 1.5128 + *aStartIterator = lines->mLines.begin(); 1.5129 + *aIterator = *aStartIterator; 1.5130 + *aEndIterator = lines->mLines.end(); 1.5131 + *aOverflowLines = lines; 1.5132 + } 1.5133 + } 1.5134 + } 1.5135 +} 1.5136 + 1.5137 +nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame, 1.5138 + line_iterator aLine) 1.5139 + : mFrame(aFrame), mLine(aLine), mLineList(&aFrame->mLines) 1.5140 +{ 1.5141 + // This will assert if aLine isn't in mLines of aFrame: 1.5142 + DebugOnly<bool> check = aLine == mFrame->begin_lines(); 1.5143 +} 1.5144 + 1.5145 +nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame, 1.5146 + line_iterator aLine, bool aInOverflow) 1.5147 + : mFrame(aFrame), mLine(aLine), 1.5148 + mLineList(aInOverflow ? &aFrame->GetOverflowLines()->mLines 1.5149 + : &aFrame->mLines) 1.5150 +{ 1.5151 +} 1.5152 + 1.5153 +nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame, 1.5154 + bool* aFoundValidLine) 1.5155 + : mFrame(aFrame), mLineList(&aFrame->mLines) 1.5156 +{ 1.5157 + mLine = aFrame->begin_lines(); 1.5158 + *aFoundValidLine = FindValidLine(); 1.5159 +} 1.5160 + 1.5161 +static nsIFrame* 1.5162 +FindChildContaining(nsBlockFrame* aFrame, nsIFrame* aFindFrame) 1.5163 +{ 1.5164 + NS_ASSERTION(aFrame, "must have frame"); 1.5165 + nsIFrame* child; 1.5166 + while (true) { 1.5167 + nsIFrame* block = aFrame; 1.5168 + do { 1.5169 + child = nsLayoutUtils::FindChildContainingDescendant(block, aFindFrame); 1.5170 + if (child) 1.5171 + break; 1.5172 + block = block->GetNextContinuation(); 1.5173 + } while (block); 1.5174 + if (!child) 1.5175 + return nullptr; 1.5176 + if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) 1.5177 + break; 1.5178 + aFindFrame = aFrame->PresContext()->FrameManager()->GetPlaceholderFrameFor(child); 1.5179 + } 1.5180 + 1.5181 + return child; 1.5182 +} 1.5183 + 1.5184 +nsBlockInFlowLineIterator::nsBlockInFlowLineIterator(nsBlockFrame* aFrame, 1.5185 + nsIFrame* aFindFrame, bool* aFoundValidLine) 1.5186 + : mFrame(aFrame), mLineList(&aFrame->mLines) 1.5187 +{ 1.5188 + *aFoundValidLine = false; 1.5189 + 1.5190 + nsIFrame* child = FindChildContaining(aFrame, aFindFrame); 1.5191 + if (!child) 1.5192 + return; 1.5193 + 1.5194 + // Try to use the cursor if it exists, otherwise fall back to the first line 1.5195 + nsLineBox* cursor = aFrame->GetLineCursor(); 1.5196 + if (!cursor) { 1.5197 + line_iterator iter = aFrame->begin_lines(); 1.5198 + if (iter != aFrame->end_lines()) { 1.5199 + cursor = iter; 1.5200 + } 1.5201 + } 1.5202 + 1.5203 + if (cursor) { 1.5204 + // Perform a simultaneous forward and reverse search starting from the 1.5205 + // line cursor. 1.5206 + nsBlockFrame::line_iterator line = aFrame->line(cursor); 1.5207 + nsBlockFrame::reverse_line_iterator rline = aFrame->rline(cursor); 1.5208 + nsBlockFrame::line_iterator line_end = aFrame->end_lines(); 1.5209 + nsBlockFrame::reverse_line_iterator rline_end = aFrame->rend_lines(); 1.5210 + // rline is positioned on the line containing 'cursor', so it's not 1.5211 + // rline_end. So we can safely increment it (i.e. move it to one line 1.5212 + // earlier) to start searching there. 1.5213 + ++rline; 1.5214 + while (line != line_end || rline != rline_end) { 1.5215 + if (line != line_end) { 1.5216 + if (line->Contains(child)) { 1.5217 + *aFoundValidLine = true; 1.5218 + mLine = line; 1.5219 + return; 1.5220 + } 1.5221 + ++line; 1.5222 + } 1.5223 + if (rline != rline_end) { 1.5224 + if (rline->Contains(child)) { 1.5225 + *aFoundValidLine = true; 1.5226 + mLine = rline; 1.5227 + return; 1.5228 + } 1.5229 + ++rline; 1.5230 + } 1.5231 + } 1.5232 + // Didn't find the line 1.5233 + } 1.5234 + 1.5235 + // If we reach here, it means that we have not been able to find the 1.5236 + // desired frame in our in-flow lines. So we should start looking at 1.5237 + // our overflow lines. In order to do that, we set mLine to the end 1.5238 + // iterator so that FindValidLine starts to look at overflow lines, 1.5239 + // if any. 1.5240 + 1.5241 + mLine = aFrame->end_lines(); 1.5242 + 1.5243 + if (!FindValidLine()) 1.5244 + return; 1.5245 + 1.5246 + do { 1.5247 + if (mLine->Contains(child)) { 1.5248 + *aFoundValidLine = true; 1.5249 + return; 1.5250 + } 1.5251 + } while (Next()); 1.5252 +} 1.5253 + 1.5254 +nsBlockFrame::line_iterator 1.5255 +nsBlockInFlowLineIterator::End() 1.5256 +{ 1.5257 + return mLineList->end(); 1.5258 +} 1.5259 + 1.5260 +bool 1.5261 +nsBlockInFlowLineIterator::IsLastLineInList() 1.5262 +{ 1.5263 + line_iterator end = End(); 1.5264 + return mLine != end && mLine.next() == end; 1.5265 +} 1.5266 + 1.5267 +bool 1.5268 +nsBlockInFlowLineIterator::Next() 1.5269 +{ 1.5270 + ++mLine; 1.5271 + return FindValidLine(); 1.5272 +} 1.5273 + 1.5274 +bool 1.5275 +nsBlockInFlowLineIterator::Prev() 1.5276 +{ 1.5277 + line_iterator begin = mLineList->begin(); 1.5278 + if (mLine != begin) { 1.5279 + --mLine; 1.5280 + return true; 1.5281 + } 1.5282 + bool currentlyInOverflowLines = GetInOverflow(); 1.5283 + while (true) { 1.5284 + if (currentlyInOverflowLines) { 1.5285 + mLineList = &mFrame->mLines; 1.5286 + mLine = mLineList->end(); 1.5287 + if (mLine != mLineList->begin()) { 1.5288 + --mLine; 1.5289 + return true; 1.5290 + } 1.5291 + } else { 1.5292 + mFrame = static_cast<nsBlockFrame*>(mFrame->GetPrevInFlow()); 1.5293 + if (!mFrame) 1.5294 + return false; 1.5295 + nsBlockFrame::FrameLines* overflowLines = mFrame->GetOverflowLines(); 1.5296 + if (overflowLines) { 1.5297 + mLineList = &overflowLines->mLines; 1.5298 + mLine = mLineList->end(); 1.5299 + NS_ASSERTION(mLine != mLineList->begin(), "empty overflow line list?"); 1.5300 + --mLine; 1.5301 + return true; 1.5302 + } 1.5303 + } 1.5304 + currentlyInOverflowLines = !currentlyInOverflowLines; 1.5305 + } 1.5306 +} 1.5307 + 1.5308 +bool 1.5309 +nsBlockInFlowLineIterator::FindValidLine() 1.5310 +{ 1.5311 + line_iterator end = mLineList->end(); 1.5312 + if (mLine != end) 1.5313 + return true; 1.5314 + bool currentlyInOverflowLines = GetInOverflow(); 1.5315 + while (true) { 1.5316 + if (currentlyInOverflowLines) { 1.5317 + mFrame = static_cast<nsBlockFrame*>(mFrame->GetNextInFlow()); 1.5318 + if (!mFrame) 1.5319 + return false; 1.5320 + mLineList = &mFrame->mLines; 1.5321 + mLine = mLineList->begin(); 1.5322 + if (mLine != mLineList->end()) 1.5323 + return true; 1.5324 + } else { 1.5325 + nsBlockFrame::FrameLines* overflowLines = mFrame->GetOverflowLines(); 1.5326 + if (overflowLines) { 1.5327 + mLineList = &overflowLines->mLines; 1.5328 + mLine = mLineList->begin(); 1.5329 + NS_ASSERTION(mLine != mLineList->end(), "empty overflow line list?"); 1.5330 + return true; 1.5331 + } 1.5332 + } 1.5333 + currentlyInOverflowLines = !currentlyInOverflowLines; 1.5334 + } 1.5335 +} 1.5336 + 1.5337 +static nsresult RemoveBlockChild(nsIFrame* aFrame, 1.5338 + bool aRemoveOnlyFluidContinuations) 1.5339 +{ 1.5340 + if (!aFrame) 1.5341 + return NS_OK; 1.5342 + 1.5343 + nsBlockFrame* nextBlock = nsLayoutUtils::GetAsBlock(aFrame->GetParent()); 1.5344 + NS_ASSERTION(nextBlock, 1.5345 + "Our child's continuation's parent is not a block?"); 1.5346 + return nextBlock->DoRemoveFrame(aFrame, 1.5347 + (aRemoveOnlyFluidContinuations ? 0 : nsBlockFrame::REMOVE_FIXED_CONTINUATIONS)); 1.5348 +} 1.5349 + 1.5350 +// This function removes aDeletedFrame and all its continuations. It 1.5351 +// is optimized for deleting a whole series of frames. The easy 1.5352 +// implementation would invoke itself recursively on 1.5353 +// aDeletedFrame->GetNextContinuation, then locate the line containing 1.5354 +// aDeletedFrame and remove aDeletedFrame from that line. But here we 1.5355 +// start by locating aDeletedFrame and then scanning from that point 1.5356 +// on looking for continuations. 1.5357 +nsresult 1.5358 +nsBlockFrame::DoRemoveFrame(nsIFrame* aDeletedFrame, uint32_t aFlags) 1.5359 +{ 1.5360 + // Clear our line cursor, since our lines may change. 1.5361 + ClearLineCursor(); 1.5362 + 1.5363 + if (aDeletedFrame->GetStateBits() & 1.5364 + (NS_FRAME_OUT_OF_FLOW | NS_FRAME_IS_OVERFLOW_CONTAINER)) { 1.5365 + if (!aDeletedFrame->GetPrevInFlow()) { 1.5366 + NS_ASSERTION(aDeletedFrame->GetStateBits() & NS_FRAME_OUT_OF_FLOW, 1.5367 + "Expected out-of-flow frame"); 1.5368 + DoRemoveOutOfFlowFrame(aDeletedFrame); 1.5369 + } 1.5370 + else { 1.5371 + nsContainerFrame::DeleteNextInFlowChild(aDeletedFrame, 1.5372 + (aFlags & FRAMES_ARE_EMPTY) != 0); 1.5373 + } 1.5374 + return NS_OK; 1.5375 + } 1.5376 + 1.5377 + // Find the line that contains deletedFrame 1.5378 + nsLineList::iterator line_start = mLines.begin(), 1.5379 + line_end = mLines.end(); 1.5380 + nsLineList::iterator line = line_start; 1.5381 + FrameLines* overflowLines = nullptr; 1.5382 + bool searchingOverflowList = false; 1.5383 + // Make sure we look in the overflow lines even if the normal line 1.5384 + // list is empty 1.5385 + TryAllLines(&line, &line_start, &line_end, &searchingOverflowList, 1.5386 + &overflowLines); 1.5387 + while (line != line_end) { 1.5388 + if (line->Contains(aDeletedFrame)) { 1.5389 + break; 1.5390 + } 1.5391 + ++line; 1.5392 + TryAllLines(&line, &line_start, &line_end, &searchingOverflowList, 1.5393 + &overflowLines); 1.5394 + } 1.5395 + 1.5396 + if (line == line_end) { 1.5397 + NS_ERROR("can't find deleted frame in lines"); 1.5398 + return NS_ERROR_FAILURE; 1.5399 + } 1.5400 + 1.5401 + if (!(aFlags & FRAMES_ARE_EMPTY)) { 1.5402 + if (line != line_start) { 1.5403 + line.prev()->MarkDirty(); 1.5404 + line.prev()->SetInvalidateTextRuns(true); 1.5405 + } 1.5406 + else if (searchingOverflowList && !mLines.empty()) { 1.5407 + mLines.back()->MarkDirty(); 1.5408 + mLines.back()->SetInvalidateTextRuns(true); 1.5409 + } 1.5410 + } 1.5411 + 1.5412 + while (line != line_end && aDeletedFrame) { 1.5413 + NS_ASSERTION(this == aDeletedFrame->GetParent(), "messed up delete code"); 1.5414 + NS_ASSERTION(line->Contains(aDeletedFrame), "frame not in line"); 1.5415 + 1.5416 + if (!(aFlags & FRAMES_ARE_EMPTY)) { 1.5417 + line->MarkDirty(); 1.5418 + line->SetInvalidateTextRuns(true); 1.5419 + } 1.5420 + 1.5421 + // If the frame being deleted is the last one on the line then 1.5422 + // optimize away the line->Contains(next-in-flow) call below. 1.5423 + bool isLastFrameOnLine = 1 == line->GetChildCount(); 1.5424 + if (!isLastFrameOnLine) { 1.5425 + line_iterator next = line.next(); 1.5426 + nsIFrame* lastFrame = next != line_end ? 1.5427 + next->mFirstChild->GetPrevSibling() : 1.5428 + (searchingOverflowList ? overflowLines->mFrames.LastChild() : 1.5429 + mFrames.LastChild()); 1.5430 + NS_ASSERTION(next == line_end || lastFrame == line->LastChild(), 1.5431 + "unexpected line frames"); 1.5432 + isLastFrameOnLine = lastFrame == aDeletedFrame; 1.5433 + } 1.5434 + 1.5435 + // Remove aDeletedFrame from the line 1.5436 + if (line->mFirstChild == aDeletedFrame) { 1.5437 + // We should be setting this to null if aDeletedFrame 1.5438 + // is the only frame on the line. HOWEVER in that case 1.5439 + // we will be removing the line anyway, see below. 1.5440 + line->mFirstChild = aDeletedFrame->GetNextSibling(); 1.5441 + } 1.5442 + 1.5443 + // Hmm, this won't do anything if we're removing a frame in the first 1.5444 + // overflow line... Hopefully doesn't matter 1.5445 + --line; 1.5446 + if (line != line_end && !line->IsBlock()) { 1.5447 + // Since we just removed a frame that follows some inline 1.5448 + // frames, we need to reflow the previous line. 1.5449 + line->MarkDirty(); 1.5450 + } 1.5451 + ++line; 1.5452 + 1.5453 + // Take aDeletedFrame out of the sibling list. Note that 1.5454 + // prevSibling will only be nullptr when we are deleting the very 1.5455 + // first frame in the main or overflow list. 1.5456 + if (searchingOverflowList) { 1.5457 + overflowLines->mFrames.RemoveFrame(aDeletedFrame); 1.5458 + } else { 1.5459 + mFrames.RemoveFrame(aDeletedFrame); 1.5460 + } 1.5461 + 1.5462 + // Update the child count of the line to be accurate 1.5463 + line->NoteFrameRemoved(aDeletedFrame); 1.5464 + 1.5465 + // Destroy frame; capture its next continuation first in case we need 1.5466 + // to destroy that too. 1.5467 + nsIFrame* deletedNextContinuation = (aFlags & REMOVE_FIXED_CONTINUATIONS) ? 1.5468 + aDeletedFrame->GetNextContinuation() : aDeletedFrame->GetNextInFlow(); 1.5469 +#ifdef NOISY_REMOVE_FRAME 1.5470 + printf("DoRemoveFrame: %s line=%p frame=", 1.5471 + searchingOverflowList?"overflow":"normal", line.get()); 1.5472 + nsFrame::ListTag(stdout, aDeletedFrame); 1.5473 + printf(" prevSibling=%p deletedNextContinuation=%p\n", 1.5474 + aDeletedFrame->GetPrevSibling(), deletedNextContinuation); 1.5475 +#endif 1.5476 + 1.5477 + // If next-in-flow is an overflow container, must remove it first. 1.5478 + if (deletedNextContinuation && 1.5479 + deletedNextContinuation->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) { 1.5480 + static_cast<nsContainerFrame*>(deletedNextContinuation->GetParent()) 1.5481 + ->DeleteNextInFlowChild(deletedNextContinuation, false); 1.5482 + deletedNextContinuation = nullptr; 1.5483 + } 1.5484 + 1.5485 + aDeletedFrame->Destroy(); 1.5486 + aDeletedFrame = deletedNextContinuation; 1.5487 + 1.5488 + bool haveAdvancedToNextLine = false; 1.5489 + // If line is empty, remove it now. 1.5490 + if (0 == line->GetChildCount()) { 1.5491 +#ifdef NOISY_REMOVE_FRAME 1.5492 + printf("DoRemoveFrame: %s line=%p became empty so it will be removed\n", 1.5493 + searchingOverflowList?"overflow":"normal", line.get()); 1.5494 +#endif 1.5495 + nsLineBox *cur = line; 1.5496 + if (!searchingOverflowList) { 1.5497 + line = mLines.erase(line); 1.5498 + // Invalidate the space taken up by the line. 1.5499 + // XXX We need to do this if we're removing a frame as a result of 1.5500 + // a call to RemoveFrame(), but we may not need to do this in all 1.5501 + // cases... 1.5502 +#ifdef NOISY_BLOCK_INVALIDATE 1.5503 + nsRect visOverflow(cur->GetVisualOverflowArea()); 1.5504 + printf("%p invalidate 10 (%d, %d, %d, %d)\n", 1.5505 + this, visOverflow.x, visOverflow.y, 1.5506 + visOverflow.width, visOverflow.height); 1.5507 +#endif 1.5508 + } else { 1.5509 + line = overflowLines->mLines.erase(line); 1.5510 + if (overflowLines->mLines.empty()) { 1.5511 + DestroyOverflowLines(); 1.5512 + overflowLines = nullptr; 1.5513 + // We just invalidated our iterators. Since we were in 1.5514 + // the overflow lines list, which is now empty, set them 1.5515 + // so we're at the end of the regular line list. 1.5516 + line_start = mLines.begin(); 1.5517 + line_end = mLines.end(); 1.5518 + line = line_end; 1.5519 + } 1.5520 + } 1.5521 + FreeLineBox(cur); 1.5522 + 1.5523 + // If we're removing a line, ReflowDirtyLines isn't going to 1.5524 + // know that it needs to slide lines unless something is marked 1.5525 + // dirty. So mark the previous margin of the next line dirty if 1.5526 + // there is one. 1.5527 + if (line != line_end) { 1.5528 + line->MarkPreviousMarginDirty(); 1.5529 + } 1.5530 + haveAdvancedToNextLine = true; 1.5531 + } else { 1.5532 + // Make the line that just lost a frame dirty, and advance to 1.5533 + // the next line. 1.5534 + if (!deletedNextContinuation || isLastFrameOnLine || 1.5535 + !line->Contains(deletedNextContinuation)) { 1.5536 + line->MarkDirty(); 1.5537 + ++line; 1.5538 + haveAdvancedToNextLine = true; 1.5539 + } 1.5540 + } 1.5541 + 1.5542 + if (deletedNextContinuation) { 1.5543 + // See if we should keep looking in the current flow's line list. 1.5544 + if (deletedNextContinuation->GetParent() != this) { 1.5545 + // The deceased frames continuation is not a child of the 1.5546 + // current block. So break out of the loop so that we advance 1.5547 + // to the next parent. 1.5548 + // 1.5549 + // If we have a continuation in a different block then all bets are 1.5550 + // off regarding whether we are deleting frames without actual content, 1.5551 + // so don't propagate FRAMES_ARE_EMPTY any further. 1.5552 + aFlags &= ~FRAMES_ARE_EMPTY; 1.5553 + break; 1.5554 + } 1.5555 + 1.5556 + // If we advanced to the next line then check if we should switch to the 1.5557 + // overflow line list. 1.5558 + if (haveAdvancedToNextLine) { 1.5559 + if (line != line_end && !searchingOverflowList && 1.5560 + !line->Contains(deletedNextContinuation)) { 1.5561 + // We have advanced to the next *normal* line but the next-in-flow 1.5562 + // is not there - force a switch to the overflow line list. 1.5563 + line = line_end; 1.5564 + } 1.5565 + 1.5566 + TryAllLines(&line, &line_start, &line_end, &searchingOverflowList, 1.5567 + &overflowLines); 1.5568 +#ifdef NOISY_REMOVE_FRAME 1.5569 + printf("DoRemoveFrame: now on %s line=%p\n", 1.5570 + searchingOverflowList?"overflow":"normal", line.get()); 1.5571 +#endif 1.5572 + } 1.5573 + } 1.5574 + } 1.5575 + 1.5576 + if (!(aFlags & FRAMES_ARE_EMPTY) && line.next() != line_end) { 1.5577 + line.next()->MarkDirty(); 1.5578 + line.next()->SetInvalidateTextRuns(true); 1.5579 + } 1.5580 + 1.5581 +#ifdef DEBUG 1.5582 + VerifyLines(true); 1.5583 + VerifyOverflowSituation(); 1.5584 +#endif 1.5585 + 1.5586 + // Advance to next flow block if the frame has more continuations 1.5587 + return RemoveBlockChild(aDeletedFrame, !(aFlags & REMOVE_FIXED_CONTINUATIONS)); 1.5588 +} 1.5589 + 1.5590 +static bool 1.5591 +FindBlockLineFor(nsIFrame* aChild, 1.5592 + nsLineList::iterator aBegin, 1.5593 + nsLineList::iterator aEnd, 1.5594 + nsLineList::iterator* aResult) 1.5595 +{ 1.5596 + MOZ_ASSERT(aChild->IsBlockOutside()); 1.5597 + for (nsLineList::iterator line = aBegin; line != aEnd; ++line) { 1.5598 + MOZ_ASSERT(line->GetChildCount() > 0); 1.5599 + if (line->IsBlock() && line->mFirstChild == aChild) { 1.5600 + MOZ_ASSERT(line->GetChildCount() == 1); 1.5601 + *aResult = line; 1.5602 + return true; 1.5603 + } 1.5604 + } 1.5605 + return false; 1.5606 +} 1.5607 + 1.5608 +static bool 1.5609 +FindInlineLineFor(nsIFrame* aChild, 1.5610 + const nsFrameList& aFrameList, 1.5611 + nsLineList::iterator aBegin, 1.5612 + nsLineList::iterator aEnd, 1.5613 + nsLineList::iterator* aResult) 1.5614 +{ 1.5615 + MOZ_ASSERT(!aChild->IsBlockOutside()); 1.5616 + for (nsLineList::iterator line = aBegin; line != aEnd; ++line) { 1.5617 + MOZ_ASSERT(line->GetChildCount() > 0); 1.5618 + if (!line->IsBlock()) { 1.5619 + // Optimize by comparing the line's last child first. 1.5620 + nsLineList::iterator next = line.next(); 1.5621 + if (aChild == (next == aEnd ? aFrameList.LastChild() 1.5622 + : next->mFirstChild->GetPrevSibling()) || 1.5623 + line->Contains(aChild)) { 1.5624 + *aResult = line; 1.5625 + return true; 1.5626 + } 1.5627 + } 1.5628 + } 1.5629 + return false; 1.5630 +} 1.5631 + 1.5632 +static bool 1.5633 +FindLineFor(nsIFrame* aChild, 1.5634 + const nsFrameList& aFrameList, 1.5635 + nsLineList::iterator aBegin, 1.5636 + nsLineList::iterator aEnd, 1.5637 + nsLineList::iterator* aResult) 1.5638 +{ 1.5639 + return aChild->IsBlockOutside() ? 1.5640 + FindBlockLineFor(aChild, aBegin, aEnd, aResult) : 1.5641 + FindInlineLineFor(aChild, aFrameList, aBegin, aEnd, aResult); 1.5642 +} 1.5643 + 1.5644 +nsresult 1.5645 +nsBlockFrame::StealFrame(nsIFrame* aChild, 1.5646 + bool aForceNormal) 1.5647 +{ 1.5648 + MOZ_ASSERT(aChild->GetParent() == this); 1.5649 + 1.5650 + if ((aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW) && 1.5651 + aChild->IsFloating()) { 1.5652 + RemoveFloat(aChild); 1.5653 + return NS_OK; 1.5654 + } 1.5655 + 1.5656 + if ((aChild->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) 1.5657 + && !aForceNormal) { 1.5658 + return nsContainerFrame::StealFrame(aChild); 1.5659 + } 1.5660 + 1.5661 + MOZ_ASSERT(!(aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW)); 1.5662 + 1.5663 + nsLineList::iterator line; 1.5664 + if (FindLineFor(aChild, mFrames, mLines.begin(), mLines.end(), &line)) { 1.5665 + RemoveFrameFromLine(aChild, line, mFrames, mLines); 1.5666 + } else { 1.5667 + FrameLines* overflowLines = GetOverflowLines(); 1.5668 + DebugOnly<bool> found; 1.5669 + found = FindLineFor(aChild, overflowLines->mFrames, 1.5670 + overflowLines->mLines.begin(), 1.5671 + overflowLines->mLines.end(), &line); 1.5672 + MOZ_ASSERT(found); 1.5673 + RemoveFrameFromLine(aChild, line, overflowLines->mFrames, 1.5674 + overflowLines->mLines); 1.5675 + if (overflowLines->mLines.empty()) { 1.5676 + DestroyOverflowLines(); 1.5677 + } 1.5678 + } 1.5679 + 1.5680 + return NS_OK; 1.5681 +} 1.5682 + 1.5683 +void 1.5684 +nsBlockFrame::RemoveFrameFromLine(nsIFrame* aChild, nsLineList::iterator aLine, 1.5685 + nsFrameList& aFrameList, nsLineList& aLineList) 1.5686 +{ 1.5687 + aFrameList.RemoveFrame(aChild); 1.5688 + if (aChild == aLine->mFirstChild) { 1.5689 + aLine->mFirstChild = aChild->GetNextSibling(); 1.5690 + } 1.5691 + aLine->NoteFrameRemoved(aChild); 1.5692 + if (aLine->GetChildCount() > 0) { 1.5693 + aLine->MarkDirty(); 1.5694 + } else { 1.5695 + // The line became empty - destroy it. 1.5696 + nsLineBox* lineBox = aLine; 1.5697 + aLine = aLineList.erase(aLine); 1.5698 + if (aLine != aLineList.end()) { 1.5699 + aLine->MarkPreviousMarginDirty(); 1.5700 + } 1.5701 + FreeLineBox(lineBox); 1.5702 + } 1.5703 +} 1.5704 + 1.5705 +void 1.5706 +nsBlockFrame::DeleteNextInFlowChild(nsIFrame* aNextInFlow, 1.5707 + bool aDeletingEmptyFrames) 1.5708 +{ 1.5709 + NS_PRECONDITION(aNextInFlow->GetPrevInFlow(), "bad next-in-flow"); 1.5710 + 1.5711 + if (aNextInFlow->GetStateBits() & 1.5712 + (NS_FRAME_OUT_OF_FLOW | NS_FRAME_IS_OVERFLOW_CONTAINER)) { 1.5713 + nsContainerFrame::DeleteNextInFlowChild(aNextInFlow, aDeletingEmptyFrames); 1.5714 + } 1.5715 + else { 1.5716 +#ifdef DEBUG 1.5717 + if (aDeletingEmptyFrames) { 1.5718 + nsLayoutUtils::AssertTreeOnlyEmptyNextInFlows(aNextInFlow); 1.5719 + } 1.5720 +#endif 1.5721 + DoRemoveFrame(aNextInFlow, 1.5722 + aDeletingEmptyFrames ? FRAMES_ARE_EMPTY : 0); 1.5723 + } 1.5724 +} 1.5725 + 1.5726 +const nsStyleText* 1.5727 +nsBlockFrame::StyleTextForLineLayout() 1.5728 +{ 1.5729 + // Return the pointer to an unmodified style text 1.5730 + return StyleText(); 1.5731 +} 1.5732 + 1.5733 +//////////////////////////////////////////////////////////////////////// 1.5734 +// Float support 1.5735 + 1.5736 +nsRect 1.5737 +nsBlockFrame::AdjustFloatAvailableSpace(nsBlockReflowState& aState, 1.5738 + const nsRect& aFloatAvailableSpace, 1.5739 + nsIFrame* aFloatFrame) 1.5740 +{ 1.5741 + // Compute the available width. By default, assume the width of the 1.5742 + // containing block. 1.5743 + nscoord availWidth; 1.5744 + const nsStyleDisplay* floatDisplay = aFloatFrame->StyleDisplay(); 1.5745 + 1.5746 + if (NS_STYLE_DISPLAY_TABLE != floatDisplay->mDisplay || 1.5747 + eCompatibility_NavQuirks != aState.mPresContext->CompatibilityMode() ) { 1.5748 + availWidth = aState.mContentArea.width; 1.5749 + } 1.5750 + else { 1.5751 + // This quirk matches the one in nsBlockReflowState::FlowAndPlaceFloat 1.5752 + // give tables only the available space 1.5753 + // if they can shrink we may not be constrained to place 1.5754 + // them in the next line 1.5755 + availWidth = aFloatAvailableSpace.width; 1.5756 + } 1.5757 + 1.5758 + nscoord availHeight = NS_UNCONSTRAINEDSIZE == aState.mContentArea.height 1.5759 + ? NS_UNCONSTRAINEDSIZE 1.5760 + : std::max(0, aState.mContentArea.YMost() - aState.mY); 1.5761 + 1.5762 +#ifdef DISABLE_FLOAT_BREAKING_IN_COLUMNS 1.5763 + if (availHeight != NS_UNCONSTRAINEDSIZE && 1.5764 + nsLayoutUtils::GetClosestFrameOfType(this, nsGkAtoms::columnSetFrame)) { 1.5765 + // Tell the float it has unrestricted height, so it won't break. 1.5766 + // If the float doesn't actually fit in the column it will fail to be 1.5767 + // placed, and either move to the top of the next column or just 1.5768 + // overflow. 1.5769 + availHeight = NS_UNCONSTRAINEDSIZE; 1.5770 + } 1.5771 +#endif 1.5772 + 1.5773 + return nsRect(aState.mContentArea.x, 1.5774 + aState.mContentArea.y, 1.5775 + availWidth, availHeight); 1.5776 +} 1.5777 + 1.5778 +nscoord 1.5779 +nsBlockFrame::ComputeFloatWidth(nsBlockReflowState& aState, 1.5780 + const nsRect& aFloatAvailableSpace, 1.5781 + nsIFrame* aFloat) 1.5782 +{ 1.5783 + NS_PRECONDITION(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW, 1.5784 + "aFloat must be an out-of-flow frame"); 1.5785 + // Reflow the float. 1.5786 + nsRect availSpace = AdjustFloatAvailableSpace(aState, aFloatAvailableSpace, 1.5787 + aFloat); 1.5788 + 1.5789 + nsHTMLReflowState floatRS(aState.mPresContext, aState.mReflowState, aFloat, 1.5790 + availSpace.Size()); 1.5791 + return floatRS.ComputedWidth() + floatRS.ComputedPhysicalBorderPadding().LeftRight() + 1.5792 + floatRS.ComputedPhysicalMargin().LeftRight(); 1.5793 +} 1.5794 + 1.5795 +nsresult 1.5796 +nsBlockFrame::ReflowFloat(nsBlockReflowState& aState, 1.5797 + const nsRect& aAdjustedAvailableSpace, 1.5798 + nsIFrame* aFloat, 1.5799 + nsMargin& aFloatMargin, 1.5800 + nsMargin& aFloatOffsets, 1.5801 + bool aFloatPushedDown, 1.5802 + nsReflowStatus& aReflowStatus) 1.5803 +{ 1.5804 + NS_PRECONDITION(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW, 1.5805 + "aFloat must be an out-of-flow frame"); 1.5806 + // Reflow the float. 1.5807 + aReflowStatus = NS_FRAME_COMPLETE; 1.5808 + 1.5809 +#ifdef NOISY_FLOAT 1.5810 + printf("Reflow Float %p in parent %p, availSpace(%d,%d,%d,%d)\n", 1.5811 + aFloat, this, 1.5812 + aFloatAvailableSpace.x, aFloatAvailableSpace.y, 1.5813 + aFloatAvailableSpace.width, aFloatAvailableSpace.height 1.5814 + ); 1.5815 +#endif 1.5816 + 1.5817 + nsHTMLReflowState floatRS(aState.mPresContext, aState.mReflowState, aFloat, 1.5818 + nsSize(aAdjustedAvailableSpace.width, 1.5819 + aAdjustedAvailableSpace.height)); 1.5820 + 1.5821 + // Normally the mIsTopOfPage state is copied from the parent reflow 1.5822 + // state. However, when reflowing a float, if we've placed other 1.5823 + // floats that force this float *down* or *narrower*, we should unset 1.5824 + // the mIsTopOfPage state. 1.5825 + // FIXME: This is somewhat redundant with the |isAdjacentWithTop| 1.5826 + // variable below, which has the exact same effect. Perhaps it should 1.5827 + // be merged into that, except that the test for narrowing here is not 1.5828 + // about adjacency with the top, so it seems misleading. 1.5829 + if (floatRS.mFlags.mIsTopOfPage && 1.5830 + (aFloatPushedDown || 1.5831 + aAdjustedAvailableSpace.width != aState.mContentArea.width)) { 1.5832 + floatRS.mFlags.mIsTopOfPage = false; 1.5833 + } 1.5834 + 1.5835 + // Setup a block reflow context to reflow the float. 1.5836 + nsBlockReflowContext brc(aState.mPresContext, aState.mReflowState); 1.5837 + 1.5838 + // Reflow the float 1.5839 + bool isAdjacentWithTop = aState.IsAdjacentWithTop(); 1.5840 + 1.5841 + nsIFrame* clearanceFrame = nullptr; 1.5842 + nsresult rv; 1.5843 + do { 1.5844 + nsCollapsingMargin margin; 1.5845 + bool mayNeedRetry = false; 1.5846 + floatRS.mDiscoveredClearance = nullptr; 1.5847 + // Only first in flow gets a top margin. 1.5848 + if (!aFloat->GetPrevInFlow()) { 1.5849 + nsBlockReflowContext::ComputeCollapsedTopMargin(floatRS, &margin, 1.5850 + clearanceFrame, &mayNeedRetry); 1.5851 + 1.5852 + if (mayNeedRetry && !clearanceFrame) { 1.5853 + floatRS.mDiscoveredClearance = &clearanceFrame; 1.5854 + // We don't need to push the float manager state because the the block has its own 1.5855 + // float manager that will be destroyed and recreated 1.5856 + } 1.5857 + } 1.5858 + 1.5859 + rv = brc.ReflowBlock(aAdjustedAvailableSpace, true, margin, 1.5860 + 0, isAdjacentWithTop, 1.5861 + nullptr, floatRS, 1.5862 + aReflowStatus, aState); 1.5863 + } while (NS_SUCCEEDED(rv) && clearanceFrame); 1.5864 + 1.5865 + if (!NS_FRAME_IS_FULLY_COMPLETE(aReflowStatus) && 1.5866 + ShouldAvoidBreakInside(floatRS)) { 1.5867 + aReflowStatus = NS_INLINE_LINE_BREAK_BEFORE(); 1.5868 + } else if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus) && 1.5869 + (NS_UNCONSTRAINEDSIZE == aAdjustedAvailableSpace.height)) { 1.5870 + // An incomplete reflow status means we should split the float 1.5871 + // if the height is constrained (bug 145305). 1.5872 + aReflowStatus = NS_FRAME_COMPLETE; 1.5873 + } 1.5874 + 1.5875 + if (aReflowStatus & NS_FRAME_REFLOW_NEXTINFLOW) { 1.5876 + aState.mReflowStatus |= NS_FRAME_REFLOW_NEXTINFLOW; 1.5877 + } 1.5878 + 1.5879 + if (aFloat->GetType() == nsGkAtoms::letterFrame) { 1.5880 + // We never split floating first letters; an incomplete state for 1.5881 + // such frames simply means that there is more content to be 1.5882 + // reflowed on the line. 1.5883 + if (NS_FRAME_IS_NOT_COMPLETE(aReflowStatus)) 1.5884 + aReflowStatus = NS_FRAME_COMPLETE; 1.5885 + } 1.5886 + 1.5887 + if (NS_FAILED(rv)) { 1.5888 + return rv; 1.5889 + } 1.5890 + 1.5891 + // Capture the margin and offsets information for the caller 1.5892 + aFloatMargin = floatRS.ComputedPhysicalMargin(); // float margins don't collapse 1.5893 + aFloatOffsets = floatRS.ComputedPhysicalOffsets(); 1.5894 + 1.5895 + const nsHTMLReflowMetrics& metrics = brc.GetMetrics(); 1.5896 + 1.5897 + // Set the rect, make sure the view is properly sized and positioned, 1.5898 + // and tell the frame we're done reflowing it 1.5899 + // XXXldb This seems like the wrong place to be doing this -- shouldn't 1.5900 + // we be doing this in nsBlockReflowState::FlowAndPlaceFloat after 1.5901 + // we've positioned the float, and shouldn't we be doing the equivalent 1.5902 + // of |PlaceFrameView| here? 1.5903 + aFloat->SetSize(nsSize(metrics.Width(), metrics.Height())); 1.5904 + if (aFloat->HasView()) { 1.5905 + nsContainerFrame::SyncFrameViewAfterReflow(aState.mPresContext, aFloat, 1.5906 + aFloat->GetView(), 1.5907 + metrics.VisualOverflow(), 1.5908 + NS_FRAME_NO_MOVE_VIEW); 1.5909 + } 1.5910 + // Pass floatRS so the frame hierarchy can be used (redoFloatRS has the same hierarchy) 1.5911 + aFloat->DidReflow(aState.mPresContext, &floatRS, 1.5912 + nsDidReflowStatus::FINISHED); 1.5913 + 1.5914 +#ifdef NOISY_FLOAT 1.5915 + printf("end ReflowFloat %p, sized to %d,%d\n", 1.5916 + aFloat, metrics.Width(), metrics.Height()); 1.5917 +#endif 1.5918 + 1.5919 + return NS_OK; 1.5920 +} 1.5921 + 1.5922 +uint8_t 1.5923 +nsBlockFrame::FindTrailingClear() 1.5924 +{ 1.5925 + // find the break type of the last line 1.5926 + for (nsIFrame* b = this; b; b = b->GetPrevInFlow()) { 1.5927 + nsBlockFrame* block = static_cast<nsBlockFrame*>(b); 1.5928 + line_iterator endLine = block->end_lines(); 1.5929 + if (endLine != block->begin_lines()) { 1.5930 + --endLine; 1.5931 + return endLine->GetBreakTypeAfter(); 1.5932 + } 1.5933 + } 1.5934 + return NS_STYLE_CLEAR_NONE; 1.5935 +} 1.5936 + 1.5937 +void 1.5938 +nsBlockFrame::ReflowPushedFloats(nsBlockReflowState& aState, 1.5939 + nsOverflowAreas& aOverflowAreas, 1.5940 + nsReflowStatus& aStatus) 1.5941 +{ 1.5942 + // Pushed floats live at the start of our float list; see comment 1.5943 + // above nsBlockFrame::DrainPushedFloats. 1.5944 + for (nsIFrame* f = mFloats.FirstChild(), *next; 1.5945 + f && (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT); 1.5946 + f = next) { 1.5947 + // save next sibling now, since reflowing could push the entire 1.5948 + // float, changing its siblings 1.5949 + next = f->GetNextSibling(); 1.5950 + 1.5951 + // When we push a first-continuation float in a non-initial reflow, 1.5952 + // it's possible that we end up with two continuations with the same 1.5953 + // parent. This happens if, on the previous reflow of the block or 1.5954 + // a previous reflow of the line containing the block, the float was 1.5955 + // split between continuations A and B of the parent, but on the 1.5956 + // current reflow, none of the float can fit in A. 1.5957 + // 1.5958 + // When this happens, we might even have the two continuations 1.5959 + // out-of-order due to the management of the pushed floats. In 1.5960 + // particular, if the float's placeholder was in a pushed line that 1.5961 + // we reflowed before it was pushed, and we split the float during 1.5962 + // that reflow, we might have the continuation of the float before 1.5963 + // the float itself. (In the general case, however, it's correct 1.5964 + // for floats in the pushed floats list to come before floats 1.5965 + // anchored in pushed lines; however, in this case it's wrong. We 1.5966 + // should probably find a way to fix it somehow, since it leads to 1.5967 + // incorrect layout in some cases.) 1.5968 + // 1.5969 + // When we have these out-of-order continuations, we might hit the 1.5970 + // next-continuation before the previous-continuation. When that 1.5971 + // happens, just push it. When we reflow the next continuation, 1.5972 + // we'll either pull all of its content back and destroy it (by 1.5973 + // calling DeleteNextInFlowChild), or nsBlockFrame::SplitFloat will 1.5974 + // pull it out of its current position and push it again (and 1.5975 + // potentially repeat this cycle for the next continuation, although 1.5976 + // hopefully then they'll be in the right order). 1.5977 + // 1.5978 + // We should also need this code for the in-order case if the first 1.5979 + // continuation of a float gets moved across more than one 1.5980 + // continuation of the containing block. In this case we'd manage 1.5981 + // to push the second continuation without this check, but not the 1.5982 + // third and later. 1.5983 + nsIFrame *prevContinuation = f->GetPrevContinuation(); 1.5984 + if (prevContinuation && prevContinuation->GetParent() == f->GetParent()) { 1.5985 + mFloats.RemoveFrame(f); 1.5986 + aState.AppendPushedFloat(f); 1.5987 + continue; 1.5988 + } 1.5989 + 1.5990 + // Always call FlowAndPlaceFloat; we might need to place this float 1.5991 + // if didn't belong to this block the last time it was reflowed. 1.5992 + aState.FlowAndPlaceFloat(f); 1.5993 + 1.5994 + ConsiderChildOverflow(aOverflowAreas, f); 1.5995 + } 1.5996 + 1.5997 + // If there are continued floats, then we may need to continue BR clearance 1.5998 + if (0 != aState.ClearFloats(0, NS_STYLE_CLEAR_BOTH)) { 1.5999 + aState.mFloatBreakType = static_cast<nsBlockFrame*>(GetPrevInFlow()) 1.6000 + ->FindTrailingClear(); 1.6001 + } 1.6002 +} 1.6003 + 1.6004 +void 1.6005 +nsBlockFrame::RecoverFloats(nsFloatManager& aFloatManager) 1.6006 +{ 1.6007 + // Recover our own floats 1.6008 + nsIFrame* stop = nullptr; // Stop before we reach pushed floats that 1.6009 + // belong to our next-in-flow 1.6010 + for (nsIFrame* f = mFloats.FirstChild(); f && f != stop; f = f->GetNextSibling()) { 1.6011 + nsRect region = nsFloatManager::GetRegionFor(f); 1.6012 + aFloatManager.AddFloat(f, region); 1.6013 + if (!stop && f->GetNextInFlow()) 1.6014 + stop = f->GetNextInFlow(); 1.6015 + } 1.6016 + 1.6017 + // Recurse into our overflow container children 1.6018 + for (nsIFrame* oc = GetFirstChild(kOverflowContainersList); 1.6019 + oc; oc = oc->GetNextSibling()) { 1.6020 + RecoverFloatsFor(oc, aFloatManager); 1.6021 + } 1.6022 + 1.6023 + // Recurse into our normal children 1.6024 + for (nsBlockFrame::line_iterator line = begin_lines(); line != end_lines(); ++line) { 1.6025 + if (line->IsBlock()) { 1.6026 + RecoverFloatsFor(line->mFirstChild, aFloatManager); 1.6027 + } 1.6028 + } 1.6029 +} 1.6030 + 1.6031 +void 1.6032 +nsBlockFrame::RecoverFloatsFor(nsIFrame* aFrame, 1.6033 + nsFloatManager& aFloatManager) 1.6034 +{ 1.6035 + NS_PRECONDITION(aFrame, "null frame"); 1.6036 + // Only blocks have floats 1.6037 + nsBlockFrame* block = nsLayoutUtils::GetAsBlock(aFrame); 1.6038 + // Don't recover any state inside a block that has its own space manager 1.6039 + // (we don't currently have any blocks like this, though, thanks to our 1.6040 + // use of extra frames for 'overflow') 1.6041 + if (block && !nsBlockFrame::BlockNeedsFloatManager(block)) { 1.6042 + // If the element is relatively positioned, then adjust x and y 1.6043 + // accordingly so that we consider relatively positioned frames 1.6044 + // at their original position. 1.6045 + nsPoint pos = block->GetNormalPosition(); 1.6046 + aFloatManager.Translate(pos.x, pos.y); 1.6047 + block->RecoverFloats(aFloatManager); 1.6048 + aFloatManager.Translate(-pos.x, -pos.y); 1.6049 + } 1.6050 +} 1.6051 + 1.6052 +////////////////////////////////////////////////////////////////////// 1.6053 +// Painting, event handling 1.6054 + 1.6055 +#ifdef DEBUG 1.6056 +static void ComputeVisualOverflowArea(nsLineList& aLines, 1.6057 + nscoord aWidth, nscoord aHeight, 1.6058 + nsRect& aResult) 1.6059 +{ 1.6060 + nscoord xa = 0, ya = 0, xb = aWidth, yb = aHeight; 1.6061 + for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end(); 1.6062 + line != line_end; 1.6063 + ++line) { 1.6064 + // Compute min and max x/y values for the reflowed frame's 1.6065 + // combined areas 1.6066 + nsRect visOverflow(line->GetVisualOverflowArea()); 1.6067 + nscoord x = visOverflow.x; 1.6068 + nscoord y = visOverflow.y; 1.6069 + nscoord xmost = x + visOverflow.width; 1.6070 + nscoord ymost = y + visOverflow.height; 1.6071 + if (x < xa) { 1.6072 + xa = x; 1.6073 + } 1.6074 + if (xmost > xb) { 1.6075 + xb = xmost; 1.6076 + } 1.6077 + if (y < ya) { 1.6078 + ya = y; 1.6079 + } 1.6080 + if (ymost > yb) { 1.6081 + yb = ymost; 1.6082 + } 1.6083 + } 1.6084 + 1.6085 + aResult.x = xa; 1.6086 + aResult.y = ya; 1.6087 + aResult.width = xb - xa; 1.6088 + aResult.height = yb - ya; 1.6089 +} 1.6090 +#endif 1.6091 + 1.6092 +bool 1.6093 +nsBlockFrame::IsVisibleInSelection(nsISelection* aSelection) 1.6094 +{ 1.6095 + if (mContent->IsHTML() && (mContent->Tag() == nsGkAtoms::html || 1.6096 + mContent->Tag() == nsGkAtoms::body)) 1.6097 + return true; 1.6098 + 1.6099 + nsCOMPtr<nsIDOMNode> node(do_QueryInterface(mContent)); 1.6100 + bool visible; 1.6101 + nsresult rv = aSelection->ContainsNode(node, true, &visible); 1.6102 + return NS_SUCCEEDED(rv) && visible; 1.6103 +} 1.6104 + 1.6105 +#ifdef DEBUG 1.6106 +static void DebugOutputDrawLine(int32_t aDepth, nsLineBox* aLine, bool aDrawn) { 1.6107 + if (nsBlockFrame::gNoisyDamageRepair) { 1.6108 + nsFrame::IndentBy(stdout, aDepth+1); 1.6109 + nsRect lineArea = aLine->GetVisualOverflowArea(); 1.6110 + printf("%s line=%p bounds=%d,%d,%d,%d ca=%d,%d,%d,%d\n", 1.6111 + aDrawn ? "draw" : "skip", 1.6112 + static_cast<void*>(aLine), 1.6113 + aLine->IStart(), aLine->BStart(), 1.6114 + aLine->ISize(), aLine->BSize(), 1.6115 + lineArea.x, lineArea.y, 1.6116 + lineArea.width, lineArea.height); 1.6117 + } 1.6118 +} 1.6119 +#endif 1.6120 + 1.6121 +static void 1.6122 +DisplayLine(nsDisplayListBuilder* aBuilder, const nsRect& aLineArea, 1.6123 + const nsRect& aDirtyRect, nsBlockFrame::line_iterator& aLine, 1.6124 + int32_t aDepth, int32_t& aDrawnLines, const nsDisplayListSet& aLists, 1.6125 + nsBlockFrame* aFrame, TextOverflow* aTextOverflow) { 1.6126 + // If the line's combined area (which includes child frames that 1.6127 + // stick outside of the line's bounding box or our bounding box) 1.6128 + // intersects the dirty rect then paint the line. 1.6129 + bool intersect = aLineArea.Intersects(aDirtyRect); 1.6130 +#ifdef DEBUG 1.6131 + if (nsBlockFrame::gLamePaintMetrics) { 1.6132 + aDrawnLines++; 1.6133 + } 1.6134 + DebugOutputDrawLine(aDepth, aLine.get(), intersect); 1.6135 +#endif 1.6136 + // The line might contain a placeholder for a visible out-of-flow, in which 1.6137 + // case we need to descend into it. If there is such a placeholder, we will 1.6138 + // have NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO set. 1.6139 + // In particular, we really want to check ShouldDescendIntoFrame() 1.6140 + // on all the frames on the line, but that might be expensive. So 1.6141 + // we approximate it by checking it on aFrame; if it's true for any 1.6142 + // frame in the line, it's also true for aFrame. 1.6143 + bool lineInline = aLine->IsInline(); 1.6144 + bool lineMayHaveTextOverflow = aTextOverflow && lineInline; 1.6145 + if (!intersect && !aBuilder->ShouldDescendIntoFrame(aFrame) && 1.6146 + !lineMayHaveTextOverflow) 1.6147 + return; 1.6148 + 1.6149 + // Collect our line's display items in a temporary nsDisplayListCollection, 1.6150 + // so that we can apply any "text-overflow" clipping to the entire collection 1.6151 + // without affecting previous lines. 1.6152 + nsDisplayListCollection collection; 1.6153 + 1.6154 + // Block-level child backgrounds go on the blockBorderBackgrounds list ... 1.6155 + // Inline-level child backgrounds go on the regular child content list. 1.6156 + nsDisplayListSet childLists(collection, 1.6157 + lineInline ? collection.Content() : collection.BlockBorderBackgrounds()); 1.6158 + 1.6159 + uint32_t flags = lineInline ? nsIFrame::DISPLAY_CHILD_INLINE : 0; 1.6160 + 1.6161 + nsIFrame* kid = aLine->mFirstChild; 1.6162 + int32_t n = aLine->GetChildCount(); 1.6163 + while (--n >= 0) { 1.6164 + aFrame->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, 1.6165 + childLists, flags); 1.6166 + kid = kid->GetNextSibling(); 1.6167 + } 1.6168 + 1.6169 + if (lineMayHaveTextOverflow) { 1.6170 + aTextOverflow->ProcessLine(collection, aLine.get()); 1.6171 + } 1.6172 + 1.6173 + collection.MoveTo(aLists); 1.6174 +} 1.6175 + 1.6176 +void 1.6177 +nsBlockFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, 1.6178 + const nsRect& aDirtyRect, 1.6179 + const nsDisplayListSet& aLists) 1.6180 +{ 1.6181 + int32_t drawnLines; // Will only be used if set (gLamePaintMetrics). 1.6182 + int32_t depth = 0; 1.6183 +#ifdef DEBUG 1.6184 + if (gNoisyDamageRepair) { 1.6185 + depth = GetDepth(); 1.6186 + nsRect ca; 1.6187 + ::ComputeVisualOverflowArea(mLines, mRect.width, mRect.height, ca); 1.6188 + nsFrame::IndentBy(stdout, depth); 1.6189 + ListTag(stdout); 1.6190 + printf(": bounds=%d,%d,%d,%d dirty(absolute)=%d,%d,%d,%d ca=%d,%d,%d,%d\n", 1.6191 + mRect.x, mRect.y, mRect.width, mRect.height, 1.6192 + aDirtyRect.x, aDirtyRect.y, aDirtyRect.width, aDirtyRect.height, 1.6193 + ca.x, ca.y, ca.width, ca.height); 1.6194 + } 1.6195 + PRTime start = 0; // Initialize these variables to silence the compiler. 1.6196 + if (gLamePaintMetrics) { 1.6197 + start = PR_Now(); 1.6198 + drawnLines = 0; 1.6199 + } 1.6200 +#endif 1.6201 + 1.6202 + DisplayBorderBackgroundOutline(aBuilder, aLists); 1.6203 + 1.6204 + if (GetPrevInFlow()) { 1.6205 + DisplayOverflowContainers(aBuilder, aDirtyRect, aLists); 1.6206 + for (nsIFrame* f = mFloats.FirstChild(); f; f = f->GetNextSibling()) { 1.6207 + if (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT) 1.6208 + BuildDisplayListForChild(aBuilder, f, aDirtyRect, aLists); 1.6209 + } 1.6210 + } 1.6211 + 1.6212 + aBuilder->MarkFramesForDisplayList(this, mFloats, aDirtyRect); 1.6213 + 1.6214 + // Prepare for text-overflow processing. 1.6215 + nsAutoPtr<TextOverflow> textOverflow( 1.6216 + TextOverflow::WillProcessLines(aBuilder, this)); 1.6217 + 1.6218 + // We'll collect our lines' display items here, & then append this to aLists. 1.6219 + nsDisplayListCollection linesDisplayListCollection; 1.6220 + 1.6221 + // Don't use the line cursor if we might have a descendant placeholder ... 1.6222 + // it might skip lines that contain placeholders but don't themselves 1.6223 + // intersect with the dirty area. 1.6224 + // In particular, we really want to check ShouldDescendIntoFrame() 1.6225 + // on all our child frames, but that might be expensive. So we 1.6226 + // approximate it by checking it on |this|; if it's true for any 1.6227 + // frame in our child list, it's also true for |this|. 1.6228 + nsLineBox* cursor = aBuilder->ShouldDescendIntoFrame(this) ? 1.6229 + nullptr : GetFirstLineContaining(aDirtyRect.y); 1.6230 + line_iterator line_end = end_lines(); 1.6231 + 1.6232 + if (cursor) { 1.6233 + for (line_iterator line = mLines.begin(cursor); 1.6234 + line != line_end; 1.6235 + ++line) { 1.6236 + nsRect lineArea = line->GetVisualOverflowArea(); 1.6237 + if (!lineArea.IsEmpty()) { 1.6238 + // Because we have a cursor, the combinedArea.ys are non-decreasing. 1.6239 + // Once we've passed aDirtyRect.YMost(), we can never see it again. 1.6240 + if (lineArea.y >= aDirtyRect.YMost()) { 1.6241 + break; 1.6242 + } 1.6243 + DisplayLine(aBuilder, lineArea, aDirtyRect, line, depth, drawnLines, 1.6244 + linesDisplayListCollection, this, textOverflow); 1.6245 + } 1.6246 + } 1.6247 + } else { 1.6248 + bool nonDecreasingYs = true; 1.6249 + int32_t lineCount = 0; 1.6250 + nscoord lastY = INT32_MIN; 1.6251 + nscoord lastYMost = INT32_MIN; 1.6252 + for (line_iterator line = begin_lines(); 1.6253 + line != line_end; 1.6254 + ++line) { 1.6255 + nsRect lineArea = line->GetVisualOverflowArea(); 1.6256 + DisplayLine(aBuilder, lineArea, aDirtyRect, line, depth, drawnLines, 1.6257 + linesDisplayListCollection, this, textOverflow); 1.6258 + if (!lineArea.IsEmpty()) { 1.6259 + if (lineArea.y < lastY 1.6260 + || lineArea.YMost() < lastYMost) { 1.6261 + nonDecreasingYs = false; 1.6262 + } 1.6263 + lastY = lineArea.y; 1.6264 + lastYMost = lineArea.YMost(); 1.6265 + } 1.6266 + lineCount++; 1.6267 + } 1.6268 + 1.6269 + if (nonDecreasingYs && lineCount >= MIN_LINES_NEEDING_CURSOR) { 1.6270 + SetupLineCursor(); 1.6271 + } 1.6272 + } 1.6273 + 1.6274 + // Pick up the resulting text-overflow markers. We append them to 1.6275 + // PositionedDescendants just before we append the lines' display items, 1.6276 + // so that our text-overflow markers will appear on top of this block's 1.6277 + // normal content but below any of its its' positioned children. 1.6278 + if (textOverflow) { 1.6279 + aLists.PositionedDescendants()->AppendToTop(&textOverflow->GetMarkers()); 1.6280 + } 1.6281 + linesDisplayListCollection.MoveTo(aLists); 1.6282 + 1.6283 + if (HasOutsideBullet()) { 1.6284 + // Display outside bullets manually 1.6285 + nsIFrame* bullet = GetOutsideBullet(); 1.6286 + BuildDisplayListForChild(aBuilder, bullet, aDirtyRect, aLists); 1.6287 + } 1.6288 + 1.6289 +#ifdef DEBUG 1.6290 + if (gLamePaintMetrics) { 1.6291 + PRTime end = PR_Now(); 1.6292 + 1.6293 + int32_t numLines = mLines.size(); 1.6294 + if (!numLines) numLines = 1; 1.6295 + PRTime lines, deltaPerLine, delta; 1.6296 + lines = int64_t(numLines); 1.6297 + delta = end - start; 1.6298 + deltaPerLine = delta / lines; 1.6299 + 1.6300 + ListTag(stdout); 1.6301 + char buf[400]; 1.6302 + PR_snprintf(buf, sizeof(buf), 1.6303 + ": %lld elapsed (%lld per line) lines=%d drawn=%d skip=%d", 1.6304 + delta, deltaPerLine, 1.6305 + numLines, drawnLines, numLines - drawnLines); 1.6306 + printf("%s\n", buf); 1.6307 + } 1.6308 +#endif 1.6309 +} 1.6310 + 1.6311 +#ifdef ACCESSIBILITY 1.6312 +a11y::AccType 1.6313 +nsBlockFrame::AccessibleType() 1.6314 +{ 1.6315 + // block frame may be for <hr> 1.6316 + if (mContent->Tag() == nsGkAtoms::hr) { 1.6317 + return a11y::eHTMLHRType; 1.6318 + } 1.6319 + 1.6320 + if (!HasBullet() || !PresContext()) { 1.6321 + if (!mContent->GetParent()) { 1.6322 + // Don't create accessible objects for the root content node, they are redundant with 1.6323 + // the nsDocAccessible object created with the document node 1.6324 + return a11y::eNoType; 1.6325 + } 1.6326 + 1.6327 + nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = 1.6328 + do_QueryInterface(mContent->GetDocument()); 1.6329 + if (htmlDoc) { 1.6330 + nsCOMPtr<nsIDOMHTMLElement> body; 1.6331 + htmlDoc->GetBody(getter_AddRefs(body)); 1.6332 + if (SameCOMIdentity(body, mContent)) { 1.6333 + // Don't create accessible objects for the body, they are redundant with 1.6334 + // the nsDocAccessible object created with the document node 1.6335 + return a11y::eNoType; 1.6336 + } 1.6337 + } 1.6338 + 1.6339 + // Not a bullet, treat as normal HTML container 1.6340 + return a11y::eHyperTextType; 1.6341 + } 1.6342 + 1.6343 + // Create special list bullet accessible 1.6344 + return a11y::eHTMLLiType; 1.6345 +} 1.6346 +#endif 1.6347 + 1.6348 +void nsBlockFrame::ClearLineCursor() 1.6349 +{ 1.6350 + if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR)) { 1.6351 + return; 1.6352 + } 1.6353 + 1.6354 + Properties().Delete(LineCursorProperty()); 1.6355 + RemoveStateBits(NS_BLOCK_HAS_LINE_CURSOR); 1.6356 +} 1.6357 + 1.6358 +void nsBlockFrame::SetupLineCursor() 1.6359 +{ 1.6360 + if (GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR 1.6361 + || mLines.empty()) { 1.6362 + return; 1.6363 + } 1.6364 + 1.6365 + Properties().Set(LineCursorProperty(), mLines.front()); 1.6366 + AddStateBits(NS_BLOCK_HAS_LINE_CURSOR); 1.6367 +} 1.6368 + 1.6369 +nsLineBox* nsBlockFrame::GetFirstLineContaining(nscoord y) 1.6370 +{ 1.6371 + if (!(GetStateBits() & NS_BLOCK_HAS_LINE_CURSOR)) { 1.6372 + return nullptr; 1.6373 + } 1.6374 + 1.6375 + FrameProperties props = Properties(); 1.6376 + 1.6377 + nsLineBox* property = static_cast<nsLineBox*> 1.6378 + (props.Get(LineCursorProperty())); 1.6379 + line_iterator cursor = mLines.begin(property); 1.6380 + nsRect cursorArea = cursor->GetVisualOverflowArea(); 1.6381 + 1.6382 + while ((cursorArea.IsEmpty() || cursorArea.YMost() > y) 1.6383 + && cursor != mLines.front()) { 1.6384 + cursor = cursor.prev(); 1.6385 + cursorArea = cursor->GetVisualOverflowArea(); 1.6386 + } 1.6387 + while ((cursorArea.IsEmpty() || cursorArea.YMost() <= y) 1.6388 + && cursor != mLines.back()) { 1.6389 + cursor = cursor.next(); 1.6390 + cursorArea = cursor->GetVisualOverflowArea(); 1.6391 + } 1.6392 + 1.6393 + if (cursor.get() != property) { 1.6394 + props.Set(LineCursorProperty(), cursor.get()); 1.6395 + } 1.6396 + 1.6397 + return cursor.get(); 1.6398 +} 1.6399 + 1.6400 +/* virtual */ void 1.6401 +nsBlockFrame::ChildIsDirty(nsIFrame* aChild) 1.6402 +{ 1.6403 + // See if the child is absolutely positioned 1.6404 + if (aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW && 1.6405 + aChild->IsAbsolutelyPositioned()) { 1.6406 + // do nothing 1.6407 + } else if (aChild == GetOutsideBullet()) { 1.6408 + // The bullet lives in the first line, unless the first line has 1.6409 + // height 0 and there is a second line, in which case it lives 1.6410 + // in the second line. 1.6411 + line_iterator bulletLine = begin_lines(); 1.6412 + if (bulletLine != end_lines() && bulletLine->BSize() == 0 && 1.6413 + bulletLine != mLines.back()) { 1.6414 + bulletLine = bulletLine.next(); 1.6415 + } 1.6416 + 1.6417 + if (bulletLine != end_lines()) { 1.6418 + MarkLineDirty(bulletLine, &mLines); 1.6419 + } 1.6420 + // otherwise we have an empty line list, and ReflowDirtyLines 1.6421 + // will handle reflowing the bullet. 1.6422 + } else { 1.6423 + // Note that we should go through our children to mark lines dirty 1.6424 + // before the next reflow. Doing it now could make things O(N^2) 1.6425 + // since finding the right line is O(N). 1.6426 + // We don't need to worry about marking lines on the overflow list 1.6427 + // as dirty; we're guaranteed to reflow them if we take them off the 1.6428 + // overflow list. 1.6429 + // However, we might have gotten a float, in which case we need to 1.6430 + // reflow the line containing its placeholder. So find the 1.6431 + // ancestor-or-self of the placeholder that's a child of the block, 1.6432 + // and mark it as NS_FRAME_HAS_DIRTY_CHILDREN too, so that we mark 1.6433 + // its line dirty when we handle NS_BLOCK_LOOK_FOR_DIRTY_FRAMES. 1.6434 + // We need to take some care to handle the case where a float is in 1.6435 + // a different continuation than its placeholder, including marking 1.6436 + // an extra block with NS_BLOCK_LOOK_FOR_DIRTY_FRAMES. 1.6437 + if (!(aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) { 1.6438 + AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES); 1.6439 + } else { 1.6440 + NS_ASSERTION(aChild->IsFloating(), "should be a float"); 1.6441 + nsIFrame *thisFC = FirstContinuation(); 1.6442 + nsIFrame *placeholderPath = 1.6443 + PresContext()->FrameManager()->GetPlaceholderFrameFor(aChild); 1.6444 + // SVG code sometimes sends FrameNeedsReflow notifications during 1.6445 + // frame destruction, leading to null placeholders, but we're safe 1.6446 + // ignoring those. 1.6447 + if (placeholderPath) { 1.6448 + for (;;) { 1.6449 + nsIFrame *parent = placeholderPath->GetParent(); 1.6450 + if (parent->GetContent() == mContent && 1.6451 + parent->FirstContinuation() == thisFC) { 1.6452 + parent->AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES); 1.6453 + break; 1.6454 + } 1.6455 + placeholderPath = parent; 1.6456 + } 1.6457 + placeholderPath->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN); 1.6458 + } 1.6459 + } 1.6460 + } 1.6461 + 1.6462 + nsBlockFrameSuper::ChildIsDirty(aChild); 1.6463 +} 1.6464 + 1.6465 +void 1.6466 +nsBlockFrame::Init(nsIContent* aContent, 1.6467 + nsIFrame* aParent, 1.6468 + nsIFrame* aPrevInFlow) 1.6469 +{ 1.6470 + if (aPrevInFlow) { 1.6471 + // Copy over the inherited block frame bits from the prev-in-flow. 1.6472 + SetFlags(aPrevInFlow->GetStateBits() & 1.6473 + (NS_BLOCK_FLAGS_MASK & ~NS_BLOCK_FLAGS_NON_INHERITED_MASK)); 1.6474 + } 1.6475 + 1.6476 + nsBlockFrameSuper::Init(aContent, aParent, aPrevInFlow); 1.6477 + 1.6478 + if (!aPrevInFlow || 1.6479 + aPrevInFlow->GetStateBits() & NS_BLOCK_NEEDS_BIDI_RESOLUTION) 1.6480 + AddStateBits(NS_BLOCK_NEEDS_BIDI_RESOLUTION); 1.6481 + 1.6482 + if ((GetStateBits() & 1.6483 + (NS_FRAME_FONT_INFLATION_CONTAINER | NS_BLOCK_FLOAT_MGR)) == 1.6484 + (NS_FRAME_FONT_INFLATION_CONTAINER | NS_BLOCK_FLOAT_MGR)) { 1.6485 + AddStateBits(NS_FRAME_FONT_INFLATION_FLOW_ROOT); 1.6486 + } 1.6487 +} 1.6488 + 1.6489 +nsresult 1.6490 +nsBlockFrame::SetInitialChildList(ChildListID aListID, 1.6491 + nsFrameList& aChildList) 1.6492 +{ 1.6493 + NS_ASSERTION(aListID != kPrincipalList || 1.6494 + (GetStateBits() & (NS_BLOCK_FRAME_HAS_INSIDE_BULLET | 1.6495 + NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET)) == 0, 1.6496 + "how can we have a bullet already?"); 1.6497 + 1.6498 + if (kAbsoluteList == aListID) { 1.6499 + nsContainerFrame::SetInitialChildList(aListID, aChildList); 1.6500 + } 1.6501 + else if (kFloatList == aListID) { 1.6502 + mFloats.SetFrames(aChildList); 1.6503 + } 1.6504 + else { 1.6505 + nsPresContext* presContext = PresContext(); 1.6506 + 1.6507 +#ifdef DEBUG 1.6508 + // The only times a block that is an anonymous box is allowed to have a 1.6509 + // first-letter frame are when it's the block inside a non-anonymous cell, 1.6510 + // the block inside a fieldset, a scrolled content block, or a column 1.6511 + // content block. Note that this means that blocks which are the anonymous 1.6512 + // block in {ib} splits do NOT get first-letter frames. Note that 1.6513 + // NS_BLOCK_HAS_FIRST_LETTER_STYLE gets set on all continuations of the 1.6514 + // block. 1.6515 + nsIAtom *pseudo = StyleContext()->GetPseudo(); 1.6516 + bool haveFirstLetterStyle = 1.6517 + (!pseudo || 1.6518 + (pseudo == nsCSSAnonBoxes::cellContent && 1.6519 + mParent->StyleContext()->GetPseudo() == nullptr) || 1.6520 + pseudo == nsCSSAnonBoxes::fieldsetContent || 1.6521 + pseudo == nsCSSAnonBoxes::scrolledContent || 1.6522 + pseudo == nsCSSAnonBoxes::columnContent || 1.6523 + pseudo == nsCSSAnonBoxes::mozSVGText) && 1.6524 + !IsFrameOfType(eMathML) && 1.6525 + nsRefPtr<nsStyleContext>(GetFirstLetterStyle(presContext)) != nullptr; 1.6526 + NS_ASSERTION(haveFirstLetterStyle == 1.6527 + ((mState & NS_BLOCK_HAS_FIRST_LETTER_STYLE) != 0), 1.6528 + "NS_BLOCK_HAS_FIRST_LETTER_STYLE state out of sync"); 1.6529 +#endif 1.6530 + 1.6531 + AddFrames(aChildList, nullptr); 1.6532 + 1.6533 + // Create a list bullet if this is a list-item. Note that this is 1.6534 + // done here so that RenumberLists will work (it needs the bullets 1.6535 + // to store the bullet numbers). Also note that due to various 1.6536 + // wrapper frames (scrollframes, columns) we want to use the 1.6537 + // outermost (primary, ideally, but it's not set yet when we get 1.6538 + // here) frame of our content for the display check. On the other 1.6539 + // hand, we look at ourselves for the GetPrevInFlow() check, since 1.6540 + // for a columnset we don't want a bullet per column. Note that 1.6541 + // the outermost frame for the content is the primary frame in 1.6542 + // most cases; the ones when it's not (like tables) can't be 1.6543 + // NS_STYLE_DISPLAY_LIST_ITEM). 1.6544 + nsIFrame* possibleListItem = this; 1.6545 + while (1) { 1.6546 + nsIFrame* parent = possibleListItem->GetParent(); 1.6547 + if (parent->GetContent() != GetContent()) { 1.6548 + break; 1.6549 + } 1.6550 + possibleListItem = parent; 1.6551 + } 1.6552 + if (NS_STYLE_DISPLAY_LIST_ITEM == 1.6553 + possibleListItem->StyleDisplay()->mDisplay && 1.6554 + !GetPrevInFlow()) { 1.6555 + // Resolve style for the bullet frame 1.6556 + const nsStyleList* styleList = StyleList(); 1.6557 + nsCSSPseudoElements::Type pseudoType; 1.6558 + switch (styleList->mListStyleType) { 1.6559 + case NS_STYLE_LIST_STYLE_DISC: 1.6560 + case NS_STYLE_LIST_STYLE_CIRCLE: 1.6561 + case NS_STYLE_LIST_STYLE_SQUARE: 1.6562 + pseudoType = nsCSSPseudoElements::ePseudo_mozListBullet; 1.6563 + break; 1.6564 + default: 1.6565 + pseudoType = nsCSSPseudoElements::ePseudo_mozListNumber; 1.6566 + break; 1.6567 + } 1.6568 + 1.6569 + nsIPresShell *shell = presContext->PresShell(); 1.6570 + 1.6571 + nsStyleContext* parentStyle = 1.6572 + CorrectStyleParentFrame(this, 1.6573 + nsCSSPseudoElements::GetPseudoAtom(pseudoType))->StyleContext(); 1.6574 + nsRefPtr<nsStyleContext> kidSC = shell->StyleSet()-> 1.6575 + ResolvePseudoElementStyle(mContent->AsElement(), pseudoType, 1.6576 + parentStyle, nullptr); 1.6577 + 1.6578 + // Create bullet frame 1.6579 + nsBulletFrame* bullet = new (shell) nsBulletFrame(kidSC); 1.6580 + bullet->Init(mContent, this, nullptr); 1.6581 + 1.6582 + // If the list bullet frame should be positioned inside then add 1.6583 + // it to the flow now. 1.6584 + if (NS_STYLE_LIST_STYLE_POSITION_INSIDE == 1.6585 + styleList->mListStylePosition) { 1.6586 + nsFrameList bulletList(bullet, bullet); 1.6587 + AddFrames(bulletList, nullptr); 1.6588 + Properties().Set(InsideBulletProperty(), bullet); 1.6589 + AddStateBits(NS_BLOCK_FRAME_HAS_INSIDE_BULLET); 1.6590 + } else { 1.6591 + nsFrameList* bulletList = new (shell) nsFrameList(bullet, bullet); 1.6592 + Properties().Set(OutsideBulletProperty(), bulletList); 1.6593 + AddStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET); 1.6594 + } 1.6595 + } 1.6596 + } 1.6597 + 1.6598 + return NS_OK; 1.6599 +} 1.6600 + 1.6601 +bool 1.6602 +nsBlockFrame::BulletIsEmpty() const 1.6603 +{ 1.6604 + NS_ASSERTION(mContent->GetPrimaryFrame()->StyleDisplay()->mDisplay == 1.6605 + NS_STYLE_DISPLAY_LIST_ITEM && HasOutsideBullet(), 1.6606 + "should only care when we have an outside bullet"); 1.6607 + const nsStyleList* list = StyleList(); 1.6608 + return list->mListStyleType == NS_STYLE_LIST_STYLE_NONE && 1.6609 + !list->GetListStyleImage(); 1.6610 +} 1.6611 + 1.6612 +void 1.6613 +nsBlockFrame::GetBulletText(nsAString& aText) const 1.6614 +{ 1.6615 + aText.Truncate(); 1.6616 + 1.6617 + const nsStyleList* myList = StyleList(); 1.6618 + if (myList->GetListStyleImage() || 1.6619 + myList->mListStyleType == NS_STYLE_LIST_STYLE_DISC) { 1.6620 + aText.Assign(kDiscCharacter); 1.6621 + } 1.6622 + else if (myList->mListStyleType == NS_STYLE_LIST_STYLE_CIRCLE) { 1.6623 + aText.Assign(kCircleCharacter); 1.6624 + } 1.6625 + else if (myList->mListStyleType == NS_STYLE_LIST_STYLE_SQUARE) { 1.6626 + aText.Assign(kSquareCharacter); 1.6627 + } 1.6628 + else if (myList->mListStyleType != NS_STYLE_LIST_STYLE_NONE) { 1.6629 + nsBulletFrame* bullet = GetBullet(); 1.6630 + if (bullet) { 1.6631 + nsAutoString text; 1.6632 + bullet->GetListItemText(*myList, text); 1.6633 + aText = text; 1.6634 + } 1.6635 + } 1.6636 +} 1.6637 + 1.6638 +// static 1.6639 +bool 1.6640 +nsBlockFrame::FrameStartsCounterScope(nsIFrame* aFrame) 1.6641 +{ 1.6642 + nsIContent* content = aFrame->GetContent(); 1.6643 + if (!content || !content->IsHTML()) 1.6644 + return false; 1.6645 + 1.6646 + nsIAtom *localName = content->NodeInfo()->NameAtom(); 1.6647 + return localName == nsGkAtoms::ol || 1.6648 + localName == nsGkAtoms::ul || 1.6649 + localName == nsGkAtoms::dir || 1.6650 + localName == nsGkAtoms::menu; 1.6651 +} 1.6652 + 1.6653 +bool 1.6654 +nsBlockFrame::RenumberLists(nsPresContext* aPresContext) 1.6655 +{ 1.6656 + if (!FrameStartsCounterScope(this)) { 1.6657 + // If this frame doesn't start a counter scope then we don't need 1.6658 + // to renumber child list items. 1.6659 + return false; 1.6660 + } 1.6661 + 1.6662 + MOZ_ASSERT(mContent->IsHTML(), 1.6663 + "FrameStartsCounterScope should only return true for HTML elements"); 1.6664 + 1.6665 + // Setup initial list ordinal value 1.6666 + // XXX Map html's start property to counter-reset style 1.6667 + int32_t ordinal = 1; 1.6668 + int32_t increment; 1.6669 + if (mContent->Tag() == nsGkAtoms::ol && 1.6670 + mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::reversed)) { 1.6671 + increment = -1; 1.6672 + } else { 1.6673 + increment = 1; 1.6674 + } 1.6675 + 1.6676 + nsGenericHTMLElement *hc = nsGenericHTMLElement::FromContent(mContent); 1.6677 + // Must be non-null, since FrameStartsCounterScope only returns true 1.6678 + // for HTML elements. 1.6679 + MOZ_ASSERT(hc, "How is mContent not HTML?"); 1.6680 + const nsAttrValue* attr = hc->GetParsedAttr(nsGkAtoms::start); 1.6681 + if (attr && attr->Type() == nsAttrValue::eInteger) { 1.6682 + ordinal = attr->GetIntegerValue(); 1.6683 + } else if (increment < 0) { 1.6684 + // <ol reversed> case, or some other case with a negative increment: count 1.6685 + // up the child list 1.6686 + ordinal = 0; 1.6687 + for (nsIContent* kid = mContent->GetFirstChild(); kid; 1.6688 + kid = kid->GetNextSibling()) { 1.6689 + if (kid->IsHTML(nsGkAtoms::li)) { 1.6690 + // FIXME: This isn't right in terms of what CSS says to do for 1.6691 + // overflow of counters (but it only matters when this node has 1.6692 + // more than numeric_limits<int32_t>::max() children). 1.6693 + ordinal -= increment; 1.6694 + } 1.6695 + } 1.6696 + } 1.6697 + 1.6698 + // Get to first-in-flow 1.6699 + nsBlockFrame* block = static_cast<nsBlockFrame*>(FirstInFlow()); 1.6700 + return RenumberListsInBlock(aPresContext, block, &ordinal, 0, increment); 1.6701 +} 1.6702 + 1.6703 +bool 1.6704 +nsBlockFrame::RenumberListsInBlock(nsPresContext* aPresContext, 1.6705 + nsBlockFrame* aBlockFrame, 1.6706 + int32_t* aOrdinal, 1.6707 + int32_t aDepth, 1.6708 + int32_t aIncrement) 1.6709 +{ 1.6710 + // Examine each line in the block 1.6711 + bool foundValidLine; 1.6712 + nsBlockInFlowLineIterator bifLineIter(aBlockFrame, &foundValidLine); 1.6713 + 1.6714 + if (!foundValidLine) 1.6715 + return false; 1.6716 + 1.6717 + bool renumberedABullet = false; 1.6718 + 1.6719 + do { 1.6720 + nsLineList::iterator line = bifLineIter.GetLine(); 1.6721 + nsIFrame* kid = line->mFirstChild; 1.6722 + int32_t n = line->GetChildCount(); 1.6723 + while (--n >= 0) { 1.6724 + bool kidRenumberedABullet = RenumberListsFor(aPresContext, kid, aOrdinal, 1.6725 + aDepth, aIncrement); 1.6726 + if (kidRenumberedABullet) { 1.6727 + line->MarkDirty(); 1.6728 + renumberedABullet = true; 1.6729 + } 1.6730 + kid = kid->GetNextSibling(); 1.6731 + } 1.6732 + } while (bifLineIter.Next()); 1.6733 + 1.6734 + // We need to set NS_FRAME_HAS_DIRTY_CHILDREN bits up the tree between 1.6735 + // the bullet and the caller of RenumberLists. But the caller itself 1.6736 + // has to be responsible for setting the bit itself, since that caller 1.6737 + // might be making a FrameNeedsReflow call, which requires that the 1.6738 + // bit not be set yet. 1.6739 + if (renumberedABullet && aDepth != 0) { 1.6740 + aBlockFrame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN); 1.6741 + } 1.6742 + 1.6743 + return renumberedABullet; 1.6744 +} 1.6745 + 1.6746 +bool 1.6747 +nsBlockFrame::RenumberListsFor(nsPresContext* aPresContext, 1.6748 + nsIFrame* aKid, 1.6749 + int32_t* aOrdinal, 1.6750 + int32_t aDepth, 1.6751 + int32_t aIncrement) 1.6752 +{ 1.6753 + NS_PRECONDITION(aPresContext && aKid && aOrdinal, "null params are immoral!"); 1.6754 + 1.6755 + // add in a sanity check for absurdly deep frame trees. See bug 42138 1.6756 + if (MAX_DEPTH_FOR_LIST_RENUMBERING < aDepth) 1.6757 + return false; 1.6758 + 1.6759 + // if the frame is a placeholder, then get the out of flow frame 1.6760 + nsIFrame* kid = nsPlaceholderFrame::GetRealFrameFor(aKid); 1.6761 + const nsStyleDisplay* display = kid->StyleDisplay(); 1.6762 + 1.6763 + // drill down through any wrappers to the real frame 1.6764 + kid = kid->GetContentInsertionFrame(); 1.6765 + 1.6766 + // possible there is no content insertion frame 1.6767 + if (!kid) 1.6768 + return false; 1.6769 + 1.6770 + bool kidRenumberedABullet = false; 1.6771 + 1.6772 + // If the frame is a list-item and the frame implements our 1.6773 + // block frame API then get its bullet and set the list item 1.6774 + // ordinal. 1.6775 + if (NS_STYLE_DISPLAY_LIST_ITEM == display->mDisplay) { 1.6776 + // Make certain that the frame is a block frame in case 1.6777 + // something foreign has crept in. 1.6778 + nsBlockFrame* listItem = nsLayoutUtils::GetAsBlock(kid); 1.6779 + if (listItem) { 1.6780 + nsBulletFrame* bullet = listItem->GetBullet(); 1.6781 + if (bullet) { 1.6782 + bool changed; 1.6783 + *aOrdinal = bullet->SetListItemOrdinal(*aOrdinal, &changed, aIncrement); 1.6784 + if (changed) { 1.6785 + kidRenumberedABullet = true; 1.6786 + 1.6787 + // The ordinal changed - mark the bullet frame, and any 1.6788 + // intermediate frames between it and the block (are there 1.6789 + // ever any?), dirty. 1.6790 + // The calling code will make the necessary FrameNeedsReflow 1.6791 + // call for the list ancestor. 1.6792 + bullet->AddStateBits(NS_FRAME_IS_DIRTY); 1.6793 + nsIFrame *f = bullet; 1.6794 + do { 1.6795 + nsIFrame *parent = f->GetParent(); 1.6796 + parent->ChildIsDirty(f); 1.6797 + f = parent; 1.6798 + } while (f != listItem); 1.6799 + } 1.6800 + } 1.6801 + 1.6802 + // XXX temporary? if the list-item has child list-items they 1.6803 + // should be numbered too; especially since the list-item is 1.6804 + // itself (ASSUMED!) not to be a counter-resetter. 1.6805 + bool meToo = RenumberListsInBlock(aPresContext, listItem, aOrdinal, 1.6806 + aDepth + 1, aIncrement); 1.6807 + if (meToo) { 1.6808 + kidRenumberedABullet = true; 1.6809 + } 1.6810 + } 1.6811 + } 1.6812 + else if (NS_STYLE_DISPLAY_BLOCK == display->mDisplay) { 1.6813 + if (FrameStartsCounterScope(kid)) { 1.6814 + // Don't bother recursing into a block frame that is a new 1.6815 + // counter scope. Any list-items in there will be handled by 1.6816 + // it. 1.6817 + } 1.6818 + else { 1.6819 + // If the display=block element is a block frame then go ahead 1.6820 + // and recurse into it, as it might have child list-items. 1.6821 + nsBlockFrame* kidBlock = nsLayoutUtils::GetAsBlock(kid); 1.6822 + if (kidBlock) { 1.6823 + kidRenumberedABullet = RenumberListsInBlock(aPresContext, kidBlock, 1.6824 + aOrdinal, aDepth + 1, 1.6825 + aIncrement); 1.6826 + } 1.6827 + } 1.6828 + } 1.6829 + return kidRenumberedABullet; 1.6830 +} 1.6831 + 1.6832 +void 1.6833 +nsBlockFrame::ReflowBullet(nsIFrame* aBulletFrame, 1.6834 + nsBlockReflowState& aState, 1.6835 + nsHTMLReflowMetrics& aMetrics, 1.6836 + nscoord aLineTop) 1.6837 +{ 1.6838 + const nsHTMLReflowState &rs = aState.mReflowState; 1.6839 + 1.6840 + // Reflow the bullet now 1.6841 + nsSize availSize; 1.6842 + // Make up a width since it doesn't really matter (XXX). 1.6843 + availSize.width = aState.mContentArea.width; 1.6844 + availSize.height = NS_UNCONSTRAINEDSIZE; 1.6845 + 1.6846 + // Get the reason right. 1.6847 + // XXXwaterson Should this look just like the logic in 1.6848 + // nsBlockReflowContext::ReflowBlock and nsLineLayout::ReflowFrame? 1.6849 + nsHTMLReflowState reflowState(aState.mPresContext, rs, 1.6850 + aBulletFrame, availSize); 1.6851 + nsReflowStatus status; 1.6852 + aBulletFrame->WillReflow(aState.mPresContext); 1.6853 + aBulletFrame->Reflow(aState.mPresContext, aMetrics, reflowState, status); 1.6854 + 1.6855 + // Get the float available space using our saved state from before we 1.6856 + // started reflowing the block, so that we ignore any floats inside 1.6857 + // the block. 1.6858 + // FIXME: aLineTop isn't actually set correctly by some callers, since 1.6859 + // they reposition the line. 1.6860 + nsRect floatAvailSpace = 1.6861 + aState.GetFloatAvailableSpaceWithState(aLineTop, 1.6862 + &aState.mFloatManagerStateBefore) 1.6863 + .mRect; 1.6864 + // FIXME (bug 25888): need to check the entire region that the first 1.6865 + // line overlaps, not just the top pixel. 1.6866 + 1.6867 + // Place the bullet now. We want to place the bullet relative to the 1.6868 + // border-box of the associated block (using the right/left margin of 1.6869 + // the bullet frame as separation). However, if a line box would be 1.6870 + // displaced by floats that are *outside* the associated block, we 1.6871 + // want to displace it by the same amount. That is, we act as though 1.6872 + // the edge of the floats is the content-edge of the block, and place 1.6873 + // the bullet at a position offset from there by the block's padding, 1.6874 + // the block's border, and the bullet frame's margin. 1.6875 + 1.6876 + // IStart from floatAvailSpace gives us the content/float start edge 1.6877 + // in the current writing mode. Then we subtract out the start 1.6878 + // border/padding and the bullet's width and margin to offset the position. 1.6879 + WritingMode wm = rs.GetWritingMode(); 1.6880 + nscoord containerWidth = floatAvailSpace.XMost(); 1.6881 + LogicalRect logicalFAS(wm, floatAvailSpace, containerWidth); 1.6882 + // Get the bullet's margin, converted to our writing mode so that we can 1.6883 + // combine it with other logical values here. 1.6884 + WritingMode bulletWM = reflowState.GetWritingMode(); 1.6885 + LogicalMargin bulletMargin = 1.6886 + reflowState.ComputedLogicalMargin().ConvertTo(wm, bulletWM); 1.6887 + nscoord iStart = logicalFAS.IStart(wm) - 1.6888 + rs.ComputedLogicalBorderPadding().IStart(wm) - 1.6889 + bulletMargin.IEnd(wm) - 1.6890 + aMetrics.ISize(); 1.6891 + 1.6892 + // Approximate the bullets position; vertical alignment will provide 1.6893 + // the final vertical location. We pass our writing-mode here, because 1.6894 + // it may be different from the bullet frame's mode. 1.6895 + nscoord bStart = logicalFAS.BStart(wm); 1.6896 + aBulletFrame->SetRect(wm, LogicalRect(wm, LogicalPoint(wm, iStart, bStart), 1.6897 + LogicalSize(wm, aMetrics.ISize(), 1.6898 + aMetrics.BSize())), 1.6899 + containerWidth); 1.6900 + aBulletFrame->DidReflow(aState.mPresContext, &aState.mReflowState, 1.6901 + nsDidReflowStatus::FINISHED); 1.6902 +} 1.6903 + 1.6904 +// This is used to scan frames for any float placeholders, add their 1.6905 +// floats to the list represented by aList, and remove the 1.6906 +// floats from whatever list they might be in. We don't search descendants 1.6907 +// that are float containing blocks. Floats that or not children of 'this' 1.6908 +// are ignored (they are not added to aList). 1.6909 +void 1.6910 +nsBlockFrame::DoCollectFloats(nsIFrame* aFrame, nsFrameList& aList, 1.6911 + bool aCollectSiblings) 1.6912 +{ 1.6913 + while (aFrame) { 1.6914 + // Don't descend into float containing blocks. 1.6915 + if (!aFrame->IsFloatContainingBlock()) { 1.6916 + nsIFrame *outOfFlowFrame = 1.6917 + aFrame->GetType() == nsGkAtoms::placeholderFrame ? 1.6918 + nsLayoutUtils::GetFloatFromPlaceholder(aFrame) : nullptr; 1.6919 + if (outOfFlowFrame && outOfFlowFrame->GetParent() == this) { 1.6920 + RemoveFloat(outOfFlowFrame); 1.6921 + aList.AppendFrame(nullptr, outOfFlowFrame); 1.6922 + // FIXME: By not pulling floats whose parent is one of our 1.6923 + // later siblings, are we risking the pushed floats getting 1.6924 + // out-of-order? 1.6925 + // XXXmats nsInlineFrame's lazy reparenting depends on NOT doing that. 1.6926 + } 1.6927 + 1.6928 + DoCollectFloats(aFrame->GetFirstPrincipalChild(), aList, true); 1.6929 + DoCollectFloats(aFrame->GetFirstChild(kOverflowList), aList, true); 1.6930 + } 1.6931 + if (!aCollectSiblings) 1.6932 + break; 1.6933 + aFrame = aFrame->GetNextSibling(); 1.6934 + } 1.6935 +} 1.6936 + 1.6937 +void 1.6938 +nsBlockFrame::CheckFloats(nsBlockReflowState& aState) 1.6939 +{ 1.6940 +#ifdef DEBUG 1.6941 + // If any line is still dirty, that must mean we're going to reflow this 1.6942 + // block again soon (e.g. because we bailed out after noticing that 1.6943 + // clearance was imposed), so don't worry if the floats are out of sync. 1.6944 + bool anyLineDirty = false; 1.6945 + 1.6946 + // Check that the float list is what we would have built 1.6947 + nsAutoTArray<nsIFrame*, 8> lineFloats; 1.6948 + for (line_iterator line = begin_lines(), line_end = end_lines(); 1.6949 + line != line_end; ++line) { 1.6950 + if (line->HasFloats()) { 1.6951 + nsFloatCache* fc = line->GetFirstFloat(); 1.6952 + while (fc) { 1.6953 + lineFloats.AppendElement(fc->mFloat); 1.6954 + fc = fc->Next(); 1.6955 + } 1.6956 + } 1.6957 + if (line->IsDirty()) { 1.6958 + anyLineDirty = true; 1.6959 + } 1.6960 + } 1.6961 + 1.6962 + nsAutoTArray<nsIFrame*, 8> storedFloats; 1.6963 + bool equal = true; 1.6964 + uint32_t i = 0; 1.6965 + for (nsIFrame* f = mFloats.FirstChild(); f; f = f->GetNextSibling()) { 1.6966 + if (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT) 1.6967 + continue; 1.6968 + storedFloats.AppendElement(f); 1.6969 + if (i < lineFloats.Length() && lineFloats.ElementAt(i) != f) { 1.6970 + equal = false; 1.6971 + } 1.6972 + ++i; 1.6973 + } 1.6974 + 1.6975 + if ((!equal || lineFloats.Length() != storedFloats.Length()) && !anyLineDirty) { 1.6976 + NS_WARNING("nsBlockFrame::CheckFloats: Explicit float list is out of sync with float cache"); 1.6977 +#if defined(DEBUG_roc) 1.6978 + nsFrame::RootFrameList(PresContext(), stdout, 0); 1.6979 + for (i = 0; i < lineFloats.Length(); ++i) { 1.6980 + printf("Line float: %p\n", lineFloats.ElementAt(i)); 1.6981 + } 1.6982 + for (i = 0; i < storedFloats.Length(); ++i) { 1.6983 + printf("Stored float: %p\n", storedFloats.ElementAt(i)); 1.6984 + } 1.6985 +#endif 1.6986 + } 1.6987 +#endif 1.6988 + 1.6989 + const nsFrameList* oofs = GetOverflowOutOfFlows(); 1.6990 + if (oofs && oofs->NotEmpty()) { 1.6991 + // Floats that were pushed should be removed from our float 1.6992 + // manager. Otherwise the float manager's YMost or XMost might 1.6993 + // be larger than necessary, causing this block to get an 1.6994 + // incorrect desired height (or width). Some of these floats 1.6995 + // may not actually have been added to the float manager because 1.6996 + // they weren't reflowed before being pushed; that's OK, 1.6997 + // RemoveRegions will ignore them. It is safe to do this here 1.6998 + // because we know from here on the float manager will only be 1.6999 + // used for its XMost and YMost, not to place new floats and 1.7000 + // lines. 1.7001 + aState.mFloatManager->RemoveTrailingRegions(oofs->FirstChild()); 1.7002 + } 1.7003 +} 1.7004 + 1.7005 +void 1.7006 +nsBlockFrame::IsMarginRoot(bool* aTopMarginRoot, bool* aBottomMarginRoot) 1.7007 +{ 1.7008 + if (!(GetStateBits() & NS_BLOCK_MARGIN_ROOT)) { 1.7009 + nsIFrame* parent = GetParent(); 1.7010 + if (!parent || parent->IsFloatContainingBlock()) { 1.7011 + *aTopMarginRoot = false; 1.7012 + *aBottomMarginRoot = false; 1.7013 + return; 1.7014 + } 1.7015 + if (parent->GetType() == nsGkAtoms::columnSetFrame) { 1.7016 + *aTopMarginRoot = GetPrevInFlow() == nullptr; 1.7017 + *aBottomMarginRoot = GetNextInFlow() == nullptr; 1.7018 + return; 1.7019 + } 1.7020 + } 1.7021 + 1.7022 + *aTopMarginRoot = true; 1.7023 + *aBottomMarginRoot = true; 1.7024 +} 1.7025 + 1.7026 +/* static */ 1.7027 +bool 1.7028 +nsBlockFrame::BlockNeedsFloatManager(nsIFrame* aBlock) 1.7029 +{ 1.7030 + NS_PRECONDITION(aBlock, "Must have a frame"); 1.7031 + NS_ASSERTION(nsLayoutUtils::GetAsBlock(aBlock), "aBlock must be a block"); 1.7032 + 1.7033 + nsIFrame* parent = aBlock->GetParent(); 1.7034 + return (aBlock->GetStateBits() & NS_BLOCK_FLOAT_MGR) || 1.7035 + (parent && !parent->IsFloatContainingBlock()); 1.7036 +} 1.7037 + 1.7038 +/* static */ 1.7039 +bool 1.7040 +nsBlockFrame::BlockCanIntersectFloats(nsIFrame* aFrame) 1.7041 +{ 1.7042 + return aFrame->IsFrameOfType(nsIFrame::eBlockFrame) && 1.7043 + !aFrame->IsFrameOfType(nsIFrame::eReplaced) && 1.7044 + !(aFrame->GetStateBits() & NS_BLOCK_FLOAT_MGR); 1.7045 +} 1.7046 + 1.7047 +// Note that this width can vary based on the vertical position. 1.7048 +// However, the cases where it varies are the cases where the width fits 1.7049 +// in the available space given, which means that variation shouldn't 1.7050 +// matter. 1.7051 +/* static */ 1.7052 +nsBlockFrame::ReplacedElementWidthToClear 1.7053 +nsBlockFrame::WidthToClearPastFloats(nsBlockReflowState& aState, 1.7054 + const nsRect& aFloatAvailableSpace, 1.7055 + nsIFrame* aFrame) 1.7056 +{ 1.7057 + nscoord leftOffset, rightOffset; 1.7058 + nsCSSOffsetState offsetState(aFrame, aState.mReflowState.rendContext, 1.7059 + aState.mContentArea.width); 1.7060 + 1.7061 + ReplacedElementWidthToClear result; 1.7062 + aState.ComputeReplacedBlockOffsetsForFloats(aFrame, aFloatAvailableSpace, 1.7063 + leftOffset, rightOffset); 1.7064 + nscoord availWidth = aState.mContentArea.width - leftOffset - rightOffset; 1.7065 + 1.7066 + // We actually don't want the min width here; see bug 427782; we only 1.7067 + // want to displace if the width won't compute to a value small enough 1.7068 + // to fit. 1.7069 + // All we really need here is the result of ComputeSize, and we 1.7070 + // could *almost* get that from an nsCSSOffsetState, except for the 1.7071 + // last argument. 1.7072 + nsSize availSpace(availWidth, NS_UNCONSTRAINEDSIZE); 1.7073 + nsHTMLReflowState reflowState(aState.mPresContext, aState.mReflowState, 1.7074 + aFrame, availSpace); 1.7075 + result.borderBoxWidth = reflowState.ComputedWidth() + 1.7076 + reflowState.ComputedPhysicalBorderPadding().LeftRight(); 1.7077 + // Use the margins from offsetState rather than reflowState so that 1.7078 + // they aren't reduced by ignoring margins in overconstrained cases. 1.7079 + result.marginLeft = offsetState.ComputedPhysicalMargin().left; 1.7080 + result.marginRight = offsetState.ComputedPhysicalMargin().right; 1.7081 + return result; 1.7082 +} 1.7083 + 1.7084 +/* static */ 1.7085 +nsBlockFrame* 1.7086 +nsBlockFrame::GetNearestAncestorBlock(nsIFrame* aCandidate) 1.7087 +{ 1.7088 + nsBlockFrame* block = nullptr; 1.7089 + while(aCandidate) { 1.7090 + block = nsLayoutUtils::GetAsBlock(aCandidate); 1.7091 + if (block) { 1.7092 + // yay, candidate is a block! 1.7093 + return block; 1.7094 + } 1.7095 + // Not a block. Check its parent next. 1.7096 + aCandidate = aCandidate->GetParent(); 1.7097 + } 1.7098 + NS_NOTREACHED("Fell off frame tree looking for ancestor block!"); 1.7099 + return nullptr; 1.7100 +} 1.7101 + 1.7102 +void 1.7103 +nsBlockFrame::ComputeFinalHeight(const nsHTMLReflowState& aReflowState, 1.7104 + nsReflowStatus* aStatus, 1.7105 + nscoord aContentHeight, 1.7106 + const nsMargin& aBorderPadding, 1.7107 + nsHTMLReflowMetrics& aMetrics, 1.7108 + nscoord aConsumed) 1.7109 +{ 1.7110 + 1.7111 + // Figure out how much of the computed height should be 1.7112 + // applied to this frame. 1.7113 + nscoord computedHeightLeftOver = GetEffectiveComputedHeight(aReflowState, 1.7114 + aConsumed); 1.7115 + NS_ASSERTION(!( IS_TRUE_OVERFLOW_CONTAINER(this) 1.7116 + && computedHeightLeftOver ), 1.7117 + "overflow container must not have computedHeightLeftOver"); 1.7118 + 1.7119 + aMetrics.Height() = 1.7120 + NSCoordSaturatingAdd(NSCoordSaturatingAdd(aBorderPadding.top, 1.7121 + computedHeightLeftOver), 1.7122 + aBorderPadding.bottom); 1.7123 + 1.7124 + if (NS_FRAME_IS_NOT_COMPLETE(*aStatus) 1.7125 + && aMetrics.Height() < aReflowState.AvailableHeight()) { 1.7126 + // We ran out of height on this page but we're incomplete 1.7127 + // Set status to complete except for overflow 1.7128 + NS_FRAME_SET_OVERFLOW_INCOMPLETE(*aStatus); 1.7129 + } 1.7130 + 1.7131 + if (NS_FRAME_IS_COMPLETE(*aStatus)) { 1.7132 + if (computedHeightLeftOver > 0 && 1.7133 + NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight() && 1.7134 + aMetrics.Height() > aReflowState.AvailableHeight()) { 1.7135 + if (ShouldAvoidBreakInside(aReflowState)) { 1.7136 + *aStatus = NS_INLINE_LINE_BREAK_BEFORE(); 1.7137 + return; 1.7138 + } 1.7139 + // We don't fit and we consumed some of the computed height, 1.7140 + // so we should consume all the available height and then 1.7141 + // break. If our bottom border/padding straddles the break 1.7142 + // point, then this will increase our height and push the 1.7143 + // border/padding to the next page/column. 1.7144 + aMetrics.Height() = std::max(aReflowState.AvailableHeight(), 1.7145 + aContentHeight); 1.7146 + NS_FRAME_SET_INCOMPLETE(*aStatus); 1.7147 + if (!GetNextInFlow()) 1.7148 + *aStatus |= NS_FRAME_REFLOW_NEXTINFLOW; 1.7149 + } 1.7150 + } 1.7151 +} 1.7152 + 1.7153 +nsresult 1.7154 +nsBlockFrame::ResolveBidi() 1.7155 +{ 1.7156 + NS_ASSERTION(!GetPrevInFlow(), 1.7157 + "ResolveBidi called on non-first continuation"); 1.7158 + 1.7159 + nsPresContext* presContext = PresContext(); 1.7160 + if (!presContext->BidiEnabled()) { 1.7161 + return NS_OK; 1.7162 + } 1.7163 + 1.7164 + return nsBidiPresUtils::Resolve(this); 1.7165 +} 1.7166 + 1.7167 +#ifdef DEBUG 1.7168 +void 1.7169 +nsBlockFrame::VerifyLines(bool aFinalCheckOK) 1.7170 +{ 1.7171 + if (!gVerifyLines) { 1.7172 + return; 1.7173 + } 1.7174 + if (mLines.empty()) { 1.7175 + return; 1.7176 + } 1.7177 + 1.7178 + nsLineBox* cursor = GetLineCursor(); 1.7179 + 1.7180 + // Add up the counts on each line. Also validate that IsFirstLine is 1.7181 + // set properly. 1.7182 + int32_t count = 0; 1.7183 + line_iterator line, line_end; 1.7184 + for (line = begin_lines(), line_end = end_lines(); 1.7185 + line != line_end; 1.7186 + ++line) { 1.7187 + if (line == cursor) { 1.7188 + cursor = nullptr; 1.7189 + } 1.7190 + if (aFinalCheckOK) { 1.7191 + NS_ABORT_IF_FALSE(line->GetChildCount(), "empty line"); 1.7192 + if (line->IsBlock()) { 1.7193 + NS_ASSERTION(1 == line->GetChildCount(), "bad first line"); 1.7194 + } 1.7195 + } 1.7196 + count += line->GetChildCount(); 1.7197 + } 1.7198 + 1.7199 + // Then count the frames 1.7200 + int32_t frameCount = 0; 1.7201 + nsIFrame* frame = mLines.front()->mFirstChild; 1.7202 + while (frame) { 1.7203 + frameCount++; 1.7204 + frame = frame->GetNextSibling(); 1.7205 + } 1.7206 + NS_ASSERTION(count == frameCount, "bad line list"); 1.7207 + 1.7208 + // Next: test that each line has right number of frames on it 1.7209 + for (line = begin_lines(), line_end = end_lines(); 1.7210 + line != line_end; 1.7211 + ) { 1.7212 + count = line->GetChildCount(); 1.7213 + frame = line->mFirstChild; 1.7214 + while (--count >= 0) { 1.7215 + frame = frame->GetNextSibling(); 1.7216 + } 1.7217 + ++line; 1.7218 + if ((line != line_end) && (0 != line->GetChildCount())) { 1.7219 + NS_ASSERTION(frame == line->mFirstChild, "bad line list"); 1.7220 + } 1.7221 + } 1.7222 + 1.7223 + if (cursor) { 1.7224 + FrameLines* overflowLines = GetOverflowLines(); 1.7225 + if (overflowLines) { 1.7226 + line_iterator line = overflowLines->mLines.begin(); 1.7227 + line_iterator line_end = overflowLines->mLines.end(); 1.7228 + for (; line != line_end; ++line) { 1.7229 + if (line == cursor) { 1.7230 + cursor = nullptr; 1.7231 + break; 1.7232 + } 1.7233 + } 1.7234 + } 1.7235 + } 1.7236 + NS_ASSERTION(!cursor, "stale LineCursorProperty"); 1.7237 +} 1.7238 + 1.7239 +void 1.7240 +nsBlockFrame::VerifyOverflowSituation() 1.7241 +{ 1.7242 + nsBlockFrame* flow = static_cast<nsBlockFrame*>(FirstInFlow()); 1.7243 + while (flow) { 1.7244 + FrameLines* overflowLines = flow->GetOverflowLines(); 1.7245 + if (overflowLines) { 1.7246 + NS_ASSERTION(!overflowLines->mLines.empty(), 1.7247 + "should not be empty if present"); 1.7248 + NS_ASSERTION(overflowLines->mLines.front()->mFirstChild, 1.7249 + "bad overflow lines"); 1.7250 + NS_ASSERTION(overflowLines->mLines.front()->mFirstChild == 1.7251 + overflowLines->mFrames.FirstChild(), 1.7252 + "bad overflow frames / lines"); 1.7253 + } 1.7254 + nsLineBox* cursor = flow->GetLineCursor(); 1.7255 + if (cursor) { 1.7256 + line_iterator line = flow->begin_lines(); 1.7257 + line_iterator line_end = flow->end_lines(); 1.7258 + for (; line != line_end && line != cursor; ++line) 1.7259 + ; 1.7260 + if (line == line_end && overflowLines) { 1.7261 + line = overflowLines->mLines.begin(); 1.7262 + line_end = overflowLines->mLines.end(); 1.7263 + for (; line != line_end && line != cursor; ++line) 1.7264 + ; 1.7265 + } 1.7266 + MOZ_ASSERT(line != line_end, "stale LineCursorProperty"); 1.7267 + } 1.7268 + flow = static_cast<nsBlockFrame*>(flow->GetNextInFlow()); 1.7269 + } 1.7270 +} 1.7271 + 1.7272 +int32_t 1.7273 +nsBlockFrame::GetDepth() const 1.7274 +{ 1.7275 + int32_t depth = 0; 1.7276 + nsIFrame* parent = mParent; 1.7277 + while (parent) { 1.7278 + parent = parent->GetParent(); 1.7279 + depth++; 1.7280 + } 1.7281 + return depth; 1.7282 +} 1.7283 +#endif