Files
palemoon27/layout/generic/nsLineBox.cpp
T
roytam1 55e2238bc1 import changes from `dev' branch of rmottola/Arctic-Fox:
- bug 1212906 - don't handle windows messages while waiting for a sync a11y ipc message r=billm (c9297e58f4)
- Bug 1183431 - Tests for hypothetical box computation (to determine static position of abspos element) where orthogonal writing modes are involved. r=dholbert (87b22c5cf5)
- Bug 1221112: When checking for ::before/::after while sorting flex items by DOM order, dig past placeholder to out-of-flow frame. r=mats (b2e252fa86)
- Bug 1169420: add crashtests. (no review) (f7484702a2)
- Bug 1183431 - Ensure hypothetical box has a writing mode with the same block direction as the absolute containing block. r=dholbert (9106e022d1)
- Partial - No bug - Various crashtest manifest cleanups. (f2d0568f2d)
- Bug 1221874 - Crashtest for overzealous writing-mode check (currently asserts). r=smontagu (ef6685f606)
- Bug 1221874 - Ignore the sideways-orientation flag when logical-coordinate classes are checking writing-mode compatibility. r=smontagu (9617a761d6)
- Bug 1193488 - patch 2 - Update the writing-mode representation printed by debugging code such as DumpFrameTree to reflect the new property values. r=dholbert (b6b3fdda58)
- Bug 1208593 - Make the WritingMode::DebugString() method available even in non-DEBUG compilation (needed by --enable-dump-painting builds, for example). r=mchang (d6b46fde86)
- Bug 1122918 - Part 1 - Add parsing for logical inline-start/end keywords to the 'float' and 'clear' properties. r=heycam (20f9dc4d11)
- Bug 1122918 - Part 2 - Provide accessors that return logical 'float' and 'clear' values resolved to their physical equivalents. r=heycam (9380f050d6)
- Bug 1108055 - Part 1: Add ComputedTimingProperties dictionary. r=smaug (ac88430cfe)
- Bug 1108055 - Part 2: Refine ComputedTiming. r=birtles (3c25124b5f)
- Bug 1108055 - Part 3: Implement GetComputedTiming method. r=birtles (d9fe931cc5)
- Bug 1108055 - Part 4: Add ComputedTiming mochitests. r=birtles (1687824926)
- Bug 1195180 part 1 - Add Animation::NeedsTicks; r=heycam (9ac3bbda87)
- Bug 1195180 part 2 - Make DocumentTimeline inherit from nsARefreshObserver; r=heycam (292f9d154d)
- Bug 1195180 part 3 - Add flag to record if DocumentTimeline is observing the refresh driver; r=heycam (3d90d3f149)
- Bug 1195180 part 4 - Unregister from refresh observer when there are no animations needing ticks; r=heycam (ad76c3ddd5)
- Bug 1195180 part 5 - Synchronize refresh driver observing with presshell creation/teardown; r=heycam (94b7d9176f)
- Bug 1195180 part 6 - Lazily remove animations from timelines; r=heycam (bb3beaa264)
- Bug 1195180 part 7 - Store animations in an array; r=heycam (199756e7d6)
- Bug 1195180 part 8 - Tick animations from their timeline; r=heycam (12942bdc45)
- Bug 1195180 part 9 - Add test for seeking finished -> paused; r=heycam (0b42afcd26)
- Bug 1195180 part 10 - Remove refresh driver observing machinery from CommonAnimationManager; r=heycam (bf233d32ac)
- Bug 1208938 part 1 - Rename AnimationCollection::mNeedsRefreshes to mStyleChanging; r=heycam (679bdf0770)
- Bug 1208938 part 2 - Remove Animation::HasEndEventToQueue; r=heycam (c75bdb64b7)
- Bug 1208938 part 3 - Update pending finishing handling; r=heycam (c9f4c1aace)
- Bug 1196379 - Fix incorrect assertion checking for sorted arrays in nsDocumentRuleResultCacheKey::Matches. r=dbaron (a12db544ef)
- Bug 978833 patch 3 - Make css::Declaration implement nsIStyleRule. r=heycam (3b79410f06)
- Bug 978833 patch 4 - Add pointer back from css::Declaration to css::Rule. r=heycam (2f86331573)
- Bug 978833 patch 5 - Rename ImportantRule to ImportantStyleData. r=heycam (e8471fc026)
- Bug 978833 patch 6 - Move ImportantStyleData from StyleRule.{h,cpp} to Declaration.{h,cpp} r=heycam (612875a128)
- Bug 978833 patch 7 - Fuse allocation of ImportantStyleData with Declaration. r=heycam (a8e9e99a35)
- Bug 978833 patch 8 - Call SetImmutable for declarations of @page and keyframe rules. r=heycam (6b04b5a81e)
- Bug 978833 patch 9 - Move keyframe !important data assertion to where it will continue to happen. r=heycam (862c6df232)
- Bug 978833 patch 10 - Raise consistent exceptions so that the exception doesn't depend on rule destruction timing. r=bzbarsky (8bd58857e9)
- Bug 978833 patch 11 - Add missing #includes in preparation for nsRuleWalker.h #include change in following patch. r=heycam (25a6357983)
- Bug 1180118 - Part 6: Return eRestyle_SomeDescendants from HasAttributeDependentStyle where appropriate. r=bzbarsky (21d88cc938)
- Bug 1202512 - Part 2: Make SelectorMatchesTree take a flags argument. r=bzbarsky (b660a417f8)
- Bug 978833 patch 12 - Use the css::Declaration instead of the css::StyleRule as the matching rule. r=heycam (dd6a7914a6)
- Bug 978833 patch 13 - Remove important rule creation from css::StyleRule. r=heycam (4766c9a635)
- Bug 978833 patch 14 - Pass mozilla::css::Rule instead of nsIStyleRule to nsIDocument/nsIDocumentObserver style rule methods. r=heycam (a632b828b9)
- Bug 978833 patch 15 - Make css::Rule no longer inherit from nsIStyleRule. r=heycam (a975922671)
- Bug 978833 patch 16 - Always call Declaration::SetImmutable when we match a rule. r=heycam (0f0c89aede)
- Bug 978833 patch 17 - Remove Rule::mWasMatched. r=heycam (d0bb9bfef1)
- Bug 978833 patch 18 - Eliminate StyleRule::RuleMatched and call Declaration::SetImmutable directly for style rules (like for @page and keyframe rules).  r=heycam (70d5e45a7c)
- Bug 978833 patch 19 - Fix ResolvedStyleCache to use Declaration rather than nsCSSKeyframeRule as keys. r=heycam (223cf85e8b)
- Bug 978833 patch 20 - Rev IIDs that I should have revised in Bug 978833 patch 14. (c6421cbca8)
- Bug 1214936 - Make the ArrayBuffer constructor throw if invoked without 'new'. (r=evilpie) (ec76f43c26)
- Bug 1198826 - Rename obj parameter to buffer to improve readability, r=terrence (3b1d280a76)
- pointer style (1f9d6a7d22)
- Bug 1218111 - Fix property enumeration order of unboxed objects with expando properties. r=bhackett (a7fdc9482d)
- Bug 980945 - Throw when invoking a typed array constructor without new. r=efaust (4f80026991)
- Bug 1192572 - Add id to each opcode section in SpiderMonkey Bytecode documentation. r=Waldo (bfd9d4c165)
- Bug 1219363 - Fix sort of indexed properties to not include properties already in the Vector. r=jorendorff (77edd4d779)
- Bug 1217593 - Fix compilation of LComputeThis now this can be undefined r=shu (d227ddf666)
- Bug 1212343 - Propagate OOM from SafepointWriter r=nbp (d5a79b7d61)
- remove core files (e008661015)
- Bug 1215937 - Remove use of expression closure from js/xpconnect/. r=bholley (e928296111)
- Bug 1196975 - part1: shimwaiver. r=billm (40bc720f37)
- Bug 1196975 - part2: shimwaiver applications. r=mossop (60cf9a9342)
- Bug 1196975 - part3: GetTabForContentWindowNoShim. r=billm (05d2af3c9a)
- Bug 1196975 - part4: waiveInterposition. r=billm (31ecb441b1)
- Bug 1196975 - part5: waiveInterposition for childloader. r=mossop (fe30dddc85)
- Bug 1196975 - part6: SDK Worker should not support CPOWs. r=mossop (7e59947069)
- Bug 1129662: sdk/page-worker should use a remote page. r=krizsa (ef63bafbe4)
- Bug 1216157 - Check for OOM when generating Ion RegExp stubs. r=jonco (690229c6d7)
- Bug 1221436 patch 5 - Stop generating new style rules in DeclarationChanged (and rename it to SetDeclaration), since we no longer need a new style rule for nsIStyleRule identity rules. r=heycam (98d860d06e)
- Bug 1220516 - Devirtualize CSSStyleSheet::GetOriginalURI. r=bzbarsky (6669f8ef70)
- Fix formatting of some macros; no bug. (fe337a3e91)
- Bug 1165538 part 1 - Use delegated constructor for nsStyleFont to simplify code and remove the need of nsStyleFont::Init. r=heycam (521e59852a)
- Bug 1165538 part 2 - Add -moz-min-font-size-ratio internal property. r=heycam (a16ef2d609)
- Bug 1165538 part 3 - Apply -moz-min-font-size-ratio to rt elements and add test. r=heycam (363684724b)
- Bug 1035091 part 1: change CSS parser and loader APIs to distinguish UA, user, and author sheets instead of just UA vs everyone else. r=heycam (01decf75df)
- Bug 1221436 patch 1 - Have a pointer back from the css::Declaration for style attributes to the nsHTMLCSSStyleSheet. r=heycam (308bf9701e)
- Bug 1221436 patch 2 - For style attributes, only store a css::Declaration instead of a css::StyleRule. r=heycam (f068adb5e9)
- Bug 1167588 - Mark Rule::mParentRule as MOZ_NON_OWNING_REF. r=dbaron (bdb2bfe4a7)
- Bug 1221436 patch 3 - Remove Rule::SetHTMLCSSStyleSheet and related code, now unused. r=heycam (d4fd6fca7c)
- Bug 1221436 patch 4 - Use the same pointer to store the owning rule and the nsHTMLCSSStyleSheet on css::Declaration, since we never need both. r=heycam (1a949ec7dc)
- Bug 1221436 patch 6 - Remove mechanism for replacing style rules. r=heycam (870539dcca)
- Bug 1221436 patch 7 - Remove ReplaceStyleRule/ReplaceRuleInGroup mechanism. r=heycam (a8a58186c1)
- Bug 1186768 patch 1 - Avoid setting different font-size conditions due to MathML font size adjustments. r=heycam (5b48d02031)
- Bug 1146101 - Test. r=dbaron a=abillings (b214ab1529)
- Bug 1161366 - Correctly set referrer when loading fonts with |new FontFace()|. r=dbaron (52a59ad6a4)
- Bug 1206105 - Use CheckedInt for an+b selector matching. r=bzbarsky (739875148c)
- Bug 1163446 - Drop reference to FontFace once a FontFaceBufferSource is loaded. r=jdaggett (9fd3a219c6)
- Bug 1164813 - Make nsAnimationManager update cached in-effect status even when there is no frame; r=dbaron (6f94c96764)
- Bug 1186768 patch 2 - Crashtest. r=heycam (a566cfef9b)
- Bug 1186768 patch 3 - Compute affectedByScriptMinSize sooner to avoid asserting or doing extra work. r=heycam (30dac96a78)
- Bug 1038663 (part 1) - Make nsStyleText::mWordSpacing an nsStyleCoord. r=heycam. (42a6a2398a)
- Bug 1038663 (part 2, attempt 2) - Move GetFirstFontMetrics() up. r=heycam. (5992325377)
- Bug 1038663 (part 3, attempt 2) - Factor out space width computation. r=heycam. (92281609f0)
- Bug 1038663 (part 4, attempt 2) - Inline StyleToCoord(). r=heycam. (f40476c105)
- Bug 1038663 (part 5, attempt 2) - Change GetSpacingFlags(). r=heycam (e1fe5ecdf0)
- Bug 1038663 (part 6, attempt 2) - Allow percentage values for 'word-spacing'. r=heycam. (433fee69f6)
- Bug 1038663 (part 7, attempt 3) - Add test for percentage values for 'word-spacing'. r=heycam. (72df555579)
- Bug 1216747 - Record dependency on writing mode in nsRuleNode::ComputeStylePosition. r=heycam (8604ceff20)
- Bug 1216747 followup - Use the correct value when calling SetWritingModeDependency. (b39a82cbf7)
- Bug 1216747 - Cleanup after test fix. r=heycam (e99ec5989a)
- Bug 1170781 - Patch 1: Implement CSS 'contain: paint'. r=dholbert (3b2f65288f)
- Bug 1170781 - Patch 2: Test CSS 'contain: paint'. r=dholbert (ee83c7c0fd)
2022-11-16 08:06:33 +08:00

995 lines
25 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
// vim:cindent:ts=2:et:sw=2:
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* representation of one line within a block frame, a CSS line box */
#include "nsLineBox.h"
#include "mozilla/ArenaObjectID.h"
#include "mozilla/Assertions.h"
#include "mozilla/Likely.h"
#include "mozilla/WritingModes.h"
#include "nsBidiPresUtils.h"
#include "nsFrame.h"
#include "nsIFrameInlines.h"
#include "nsPresArena.h"
#include "nsPrintfCString.h"
#include "prprf.h"
#ifdef DEBUG
static int32_t ctorCount;
int32_t nsLineBox::GetCtorCount() { return ctorCount; }
#endif
#ifndef _MSC_VER
// static nsLineBox constant; initialized in the header file.
const uint32_t nsLineBox::kMinChildCountForHashtable;
#endif
using namespace mozilla;
nsLineBox::nsLineBox(nsIFrame* aFrame, int32_t aCount, bool aIsBlock)
: mFirstChild(aFrame)
, mContainerSize(-1, -1)
, mBounds(WritingMode()) // mBounds will be initialized with the correct
// writing mode when it is set
// NOTE: memory is already zeroed since we allocate with AllocateByObjectID.
{
MOZ_COUNT_CTOR(nsLineBox);
#ifdef DEBUG
++ctorCount;
NS_ASSERTION(!aIsBlock || aCount == 1, "Blocks must have exactly one child");
nsIFrame* f = aFrame;
for (int32_t n = aCount; n > 0; f = f->GetNextSibling(), --n) {
NS_ASSERTION(aIsBlock == f->IsBlockOutside(),
"wrong kind of child frame");
}
#endif
static_assert(NS_STYLE_CLEAR_MAX <= 15,
"FlagBits needs more bits to store the full range of "
"break type ('clear') values");
#if NS_STYLE_CLEAR_NONE > 0
mFlags.mBreakType = NS_STYLE_CLEAR_NONE;
#endif
mChildCount = aCount;
MarkDirty();
mFlags.mBlock = aIsBlock;
}
nsLineBox::~nsLineBox()
{
MOZ_COUNT_DTOR(nsLineBox);
if (MOZ_UNLIKELY(mFlags.mHasHashedFrames)) {
delete mFrames;
}
Cleanup();
}
nsLineBox*
NS_NewLineBox(nsIPresShell* aPresShell, nsIFrame* aFrame, bool aIsBlock)
{
return new (aPresShell) nsLineBox(aFrame, 1, aIsBlock);
}
nsLineBox*
NS_NewLineBox(nsIPresShell* aPresShell, nsLineBox* aFromLine,
nsIFrame* aFrame, int32_t aCount)
{
nsLineBox* newLine = new (aPresShell) nsLineBox(aFrame, aCount, false);
newLine->NoteFramesMovedFrom(aFromLine);
newLine->mContainerSize = aFromLine->mContainerSize;
return newLine;
}
void
nsLineBox::StealHashTableFrom(nsLineBox* aFromLine, uint32_t aFromLineNewCount)
{
MOZ_ASSERT(!mFlags.mHasHashedFrames);
MOZ_ASSERT(GetChildCount() >= int32_t(aFromLineNewCount));
mFrames = aFromLine->mFrames;
mFlags.mHasHashedFrames = 1;
aFromLine->mFlags.mHasHashedFrames = 0;
aFromLine->mChildCount = aFromLineNewCount;
// remove aFromLine's frames that aren't on this line
nsIFrame* f = aFromLine->mFirstChild;
for (uint32_t i = 0; i < aFromLineNewCount; f = f->GetNextSibling(), ++i) {
mFrames->RemoveEntry(f);
}
}
void
nsLineBox::NoteFramesMovedFrom(nsLineBox* aFromLine)
{
uint32_t fromCount = aFromLine->GetChildCount();
uint32_t toCount = GetChildCount();
MOZ_ASSERT(toCount <= fromCount, "moved more frames than aFromLine has");
uint32_t fromNewCount = fromCount - toCount;
if (MOZ_LIKELY(!aFromLine->mFlags.mHasHashedFrames)) {
aFromLine->mChildCount = fromNewCount;
MOZ_ASSERT(toCount < kMinChildCountForHashtable);
} else if (fromNewCount < kMinChildCountForHashtable) {
// aFromLine has a hash table but will not have it after moving the frames
// so this line can steal the hash table if it needs it.
if (toCount >= kMinChildCountForHashtable) {
StealHashTableFrom(aFromLine, fromNewCount);
} else {
delete aFromLine->mFrames;
aFromLine->mFlags.mHasHashedFrames = 0;
aFromLine->mChildCount = fromNewCount;
}
} else {
// aFromLine still needs a hash table.
if (toCount < kMinChildCountForHashtable) {
// remove the moved frames from it
nsIFrame* f = mFirstChild;
for (uint32_t i = 0; i < toCount; f = f->GetNextSibling(), ++i) {
aFromLine->mFrames->RemoveEntry(f);
}
} else if (toCount <= fromNewCount) {
// This line needs a hash table, allocate a hash table for it since that
// means fewer hash ops.
nsIFrame* f = mFirstChild;
for (uint32_t i = 0; i < toCount; f = f->GetNextSibling(), ++i) {
aFromLine->mFrames->RemoveEntry(f); // toCount RemoveEntry
}
SwitchToHashtable(); // toCount PutEntry
} else {
// This line needs a hash table, but it's fewer hash ops to steal
// aFromLine's hash table and allocate a new hash table for that line.
StealHashTableFrom(aFromLine, fromNewCount); // fromNewCount RemoveEntry
aFromLine->SwitchToHashtable(); // fromNewCount PutEntry
}
}
}
void*
nsLineBox::operator new(size_t sz, nsIPresShell* aPresShell) CPP_THROW_NEW
{
return aPresShell->AllocateByObjectID(eArenaObjectID_nsLineBox, sz);
}
void
nsLineBox::Destroy(nsIPresShell* aPresShell)
{
this->nsLineBox::~nsLineBox();
aPresShell->FreeByObjectID(eArenaObjectID_nsLineBox, this);
}
void
nsLineBox::Cleanup()
{
if (mData) {
if (IsBlock()) {
delete mBlockData;
}
else {
delete mInlineData;
}
mData = nullptr;
}
}
#ifdef DEBUG_FRAME_DUMP
static void
ListFloats(FILE* out, const char* aPrefix, const nsFloatCacheList& aFloats)
{
nsFloatCache* fc = aFloats.Head();
while (fc) {
nsCString str(aPrefix);
nsIFrame* frame = fc->mFloat;
str += nsPrintfCString("floatframe@%p ", static_cast<void*>(frame));
if (frame) {
nsAutoString frameName;
frame->GetFrameName(frameName);
str += NS_ConvertUTF16toUTF8(frameName).get();
}
else {
str += "\n###!!! NULL out-of-flow frame";
}
fprintf_stderr(out, "%s\n", str.get());
fc = fc->Next();
}
}
const char *
BreakTypeToString(uint8_t aBreakType)
{
switch (aBreakType) {
case NS_STYLE_CLEAR_NONE: return "nobr";
case NS_STYLE_CLEAR_LEFT: return "leftbr";
case NS_STYLE_CLEAR_RIGHT: return "rightbr";
case NS_STYLE_CLEAR_BOTH: return "leftbr+rightbr";
case NS_STYLE_CLEAR_LINE: return "linebr";
default:
break;
}
return "unknown";
}
char*
nsLineBox::StateToString(char* aBuf, int32_t aBufSize) const
{
PR_snprintf(aBuf, aBufSize, "%s,%s,%s,%s,%s,before:%s,after:%s[0x%x]",
IsBlock() ? "block" : "inline",
IsDirty() ? "dirty" : "clean",
IsPreviousMarginDirty() ? "prevmargindirty" : "prevmarginclean",
IsImpactedByFloat() ? "impacted" : "not impacted",
IsLineWrapped() ? "wrapped" : "not wrapped",
BreakTypeToString(GetBreakTypeBefore()),
BreakTypeToString(GetBreakTypeAfter()),
mAllFlags);
return aBuf;
}
void
nsLineBox::List(FILE* out, int32_t aIndent, uint32_t aFlags) const
{
nsCString str;
while (aIndent-- > 0) {
str += " ";
}
List(out, str.get(), aFlags);
}
void
nsLineBox::List(FILE* out, const char* aPrefix, uint32_t aFlags) const
{
nsCString str(aPrefix);
char cbuf[100];
str += nsPrintfCString("line %p: count=%d state=%s ",
static_cast<const void*>(this), GetChildCount(),
StateToString(cbuf, sizeof(cbuf)));
if (IsBlock() && !GetCarriedOutBEndMargin().IsZero()) {
str += nsPrintfCString("bm=%d ", GetCarriedOutBEndMargin().get());
}
nsRect bounds = GetPhysicalBounds();
str += nsPrintfCString("{%d,%d,%d,%d} ",
bounds.x, bounds.y, bounds.width, bounds.height);
if (mWritingMode.IsVertical() || !mWritingMode.IsBidiLTR()) {
str += nsPrintfCString("{%s: %d,%d,%d,%d; cs=%d,%d} ",
mWritingMode.DebugString(),
IStart(), BStart(), ISize(), BSize(),
mContainerSize.width, mContainerSize.height);
}
if (mData &&
(!mData->mOverflowAreas.VisualOverflow().IsEqualEdges(bounds) ||
!mData->mOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds))) {
str += nsPrintfCString("vis-overflow=%d,%d,%d,%d scr-overflow=%d,%d,%d,%d ",
mData->mOverflowAreas.VisualOverflow().x,
mData->mOverflowAreas.VisualOverflow().y,
mData->mOverflowAreas.VisualOverflow().width,
mData->mOverflowAreas.VisualOverflow().height,
mData->mOverflowAreas.ScrollableOverflow().x,
mData->mOverflowAreas.ScrollableOverflow().y,
mData->mOverflowAreas.ScrollableOverflow().width,
mData->mOverflowAreas.ScrollableOverflow().height);
}
fprintf_stderr(out, "%s<\n", str.get());
nsIFrame* frame = mFirstChild;
int32_t n = GetChildCount();
nsCString pfx(aPrefix);
pfx += " ";
while (--n >= 0) {
frame->List(out, pfx.get(), aFlags);
frame = frame->GetNextSibling();
}
if (HasFloats()) {
fprintf_stderr(out, "%s> floats <\n", aPrefix);
ListFloats(out, pfx.get(), mInlineData->mFloats);
}
fprintf_stderr(out, "%s>\n", aPrefix);
}
#endif
#ifdef DEBUG
nsIFrame*
nsLineBox::LastChild() const
{
nsIFrame* frame = mFirstChild;
int32_t n = GetChildCount() - 1;
while (--n >= 0) {
frame = frame->GetNextSibling();
}
return frame;
}
#endif
int32_t
nsLineBox::IndexOf(nsIFrame* aFrame) const
{
int32_t i, n = GetChildCount();
nsIFrame* frame = mFirstChild;
for (i = 0; i < n; i++) {
if (frame == aFrame) {
return i;
}
frame = frame->GetNextSibling();
}
return -1;
}
bool
nsLineBox::IsEmpty() const
{
if (IsBlock())
return mFirstChild->IsEmpty();
int32_t n;
nsIFrame *kid;
for (n = GetChildCount(), kid = mFirstChild;
n > 0;
--n, kid = kid->GetNextSibling())
{
if (!kid->IsEmpty())
return false;
}
if (HasBullet()) {
return false;
}
return true;
}
bool
nsLineBox::CachedIsEmpty()
{
if (mFlags.mDirty) {
return IsEmpty();
}
if (mFlags.mEmptyCacheValid) {
return mFlags.mEmptyCacheState;
}
bool result;
if (IsBlock()) {
result = mFirstChild->CachedIsEmpty();
} else {
int32_t n;
nsIFrame *kid;
result = true;
for (n = GetChildCount(), kid = mFirstChild;
n > 0;
--n, kid = kid->GetNextSibling())
{
if (!kid->CachedIsEmpty()) {
result = false;
break;
}
}
if (HasBullet()) {
result = false;
}
}
mFlags.mEmptyCacheValid = true;
mFlags.mEmptyCacheState = result;
return result;
}
void
nsLineBox::DeleteLineList(nsPresContext* aPresContext, nsLineList& aLines,
nsIFrame* aDestructRoot, nsFrameList* aFrames)
{
nsIPresShell* shell = aPresContext->PresShell();
// Keep our line list and frame list up to date as we
// remove frames, in case something wants to traverse the
// frame tree while we're destroying.
while (!aLines.empty()) {
nsLineBox* line = aLines.front();
if (MOZ_UNLIKELY(line->mFlags.mHasHashedFrames)) {
line->SwitchToCounter(); // Avoid expensive has table removals.
}
while (line->GetChildCount() > 0) {
nsIFrame* child = aFrames->RemoveFirstChild();
MOZ_ASSERT(child == line->mFirstChild, "Lines out of sync");
line->mFirstChild = aFrames->FirstChild();
line->NoteFrameRemoved(child);
child->DestroyFrom(aDestructRoot);
}
aLines.pop_front();
line->Destroy(shell);
}
}
bool
nsLineBox::RFindLineContaining(nsIFrame* aFrame,
const nsLineList::iterator& aBegin,
nsLineList::iterator& aEnd,
nsIFrame* aLastFrameBeforeEnd,
int32_t* aFrameIndexInLine)
{
NS_PRECONDITION(aFrame, "null ptr");
nsIFrame* curFrame = aLastFrameBeforeEnd;
while (aBegin != aEnd) {
--aEnd;
NS_ASSERTION(aEnd->LastChild() == curFrame, "Unexpected curFrame");
if (MOZ_UNLIKELY(aEnd->mFlags.mHasHashedFrames) &&
!aEnd->Contains(aFrame)) {
if (aEnd->mFirstChild) {
curFrame = aEnd->mFirstChild->GetPrevSibling();
}
continue;
}
// i is the index of curFrame in aEnd
int32_t i = aEnd->GetChildCount() - 1;
while (i >= 0) {
if (curFrame == aFrame) {
*aFrameIndexInLine = i;
return true;
}
--i;
curFrame = curFrame->GetPrevSibling();
}
MOZ_ASSERT(!aEnd->mFlags.mHasHashedFrames, "Contains lied to us!");
}
*aFrameIndexInLine = -1;
return false;
}
nsCollapsingMargin
nsLineBox::GetCarriedOutBEndMargin() const
{
NS_ASSERTION(IsBlock(),
"GetCarriedOutBEndMargin called on non-block line.");
return (IsBlock() && mBlockData)
? mBlockData->mCarriedOutBEndMargin
: nsCollapsingMargin();
}
bool
nsLineBox::SetCarriedOutBEndMargin(nsCollapsingMargin aValue)
{
bool changed = false;
if (IsBlock()) {
if (!aValue.IsZero()) {
if (!mBlockData) {
mBlockData = new ExtraBlockData(GetPhysicalBounds());
}
changed = aValue != mBlockData->mCarriedOutBEndMargin;
mBlockData->mCarriedOutBEndMargin = aValue;
}
else if (mBlockData) {
changed = aValue != mBlockData->mCarriedOutBEndMargin;
mBlockData->mCarriedOutBEndMargin = aValue;
MaybeFreeData();
}
}
return changed;
}
void
nsLineBox::MaybeFreeData()
{
nsRect bounds = GetPhysicalBounds();
if (mData && mData->mOverflowAreas == nsOverflowAreas(bounds, bounds)) {
if (IsInline()) {
if (mInlineData->mFloats.IsEmpty()) {
delete mInlineData;
mInlineData = nullptr;
}
}
else if (mBlockData->mCarriedOutBEndMargin.IsZero()) {
delete mBlockData;
mBlockData = nullptr;
}
}
}
// XXX get rid of this???
nsFloatCache*
nsLineBox::GetFirstFloat()
{
MOZ_ASSERT(IsInline(), "block line can't have floats");
return mInlineData ? mInlineData->mFloats.Head() : nullptr;
}
// XXX this might be too eager to free memory
void
nsLineBox::FreeFloats(nsFloatCacheFreeList& aFreeList)
{
MOZ_ASSERT(IsInline(), "block line can't have floats");
if (IsInline() && mInlineData) {
if (mInlineData->mFloats.NotEmpty()) {
aFreeList.Append(mInlineData->mFloats);
}
MaybeFreeData();
}
}
void
nsLineBox::AppendFloats(nsFloatCacheFreeList& aFreeList)
{
MOZ_ASSERT(IsInline(), "block line can't have floats");
if (IsInline()) {
if (aFreeList.NotEmpty()) {
if (!mInlineData) {
mInlineData = new ExtraInlineData(GetPhysicalBounds());
}
mInlineData->mFloats.Append(aFreeList);
}
}
}
bool
nsLineBox::RemoveFloat(nsIFrame* aFrame)
{
MOZ_ASSERT(IsInline(), "block line can't have floats");
if (IsInline() && mInlineData) {
nsFloatCache* fc = mInlineData->mFloats.Find(aFrame);
if (fc) {
// Note: the placeholder is part of the line's child list
// and will be removed later.
mInlineData->mFloats.Remove(fc);
delete fc;
MaybeFreeData();
return true;
}
}
return false;
}
void
nsLineBox::SetOverflowAreas(const nsOverflowAreas& aOverflowAreas)
{
NS_FOR_FRAME_OVERFLOW_TYPES(otype) {
NS_ASSERTION(aOverflowAreas.Overflow(otype).width >= 0,
"illegal width for combined area");
NS_ASSERTION(aOverflowAreas.Overflow(otype).height >= 0,
"illegal height for combined area");
}
nsRect bounds = GetPhysicalBounds();
if (!aOverflowAreas.VisualOverflow().IsEqualInterior(bounds) ||
!aOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds)) {
if (!mData) {
if (IsInline()) {
mInlineData = new ExtraInlineData(bounds);
}
else {
mBlockData = new ExtraBlockData(bounds);
}
}
mData->mOverflowAreas = aOverflowAreas;
}
else if (mData) {
// Store away new value so that MaybeFreeData compares against
// the right value.
mData->mOverflowAreas = aOverflowAreas;
MaybeFreeData();
}
}
//----------------------------------------------------------------------
static nsLineBox* gDummyLines[1];
nsLineIterator::nsLineIterator()
{
mLines = gDummyLines;
mNumLines = 0;
mIndex = 0;
mRightToLeft = false;
}
nsLineIterator::~nsLineIterator()
{
if (mLines != gDummyLines) {
delete [] mLines;
}
}
/* virtual */ void
nsLineIterator::DisposeLineIterator()
{
delete this;
}
nsresult
nsLineIterator::Init(nsLineList& aLines, bool aRightToLeft)
{
mRightToLeft = aRightToLeft;
// Count the lines
int32_t numLines = aLines.size();
if (0 == numLines) {
// Use gDummyLines so that we don't need null pointer checks in
// the accessor methods
mLines = gDummyLines;
return NS_OK;
}
// Make a linear array of the lines
mLines = new nsLineBox*[numLines];
if (!mLines) {
// Use gDummyLines so that we don't need null pointer checks in
// the accessor methods
mLines = gDummyLines;
return NS_ERROR_OUT_OF_MEMORY;
}
nsLineBox** lp = mLines;
for (nsLineList::iterator line = aLines.begin(), line_end = aLines.end() ;
line != line_end;
++line)
{
*lp++ = line;
}
mNumLines = numLines;
return NS_OK;
}
int32_t
nsLineIterator::GetNumLines()
{
return mNumLines;
}
bool
nsLineIterator::GetDirection()
{
return mRightToLeft;
}
NS_IMETHODIMP
nsLineIterator::GetLine(int32_t aLineNumber,
nsIFrame** aFirstFrameOnLine,
int32_t* aNumFramesOnLine,
nsRect& aLineBounds)
{
NS_ENSURE_ARG_POINTER(aFirstFrameOnLine);
NS_ENSURE_ARG_POINTER(aNumFramesOnLine);
if ((aLineNumber < 0) || (aLineNumber >= mNumLines)) {
*aFirstFrameOnLine = nullptr;
*aNumFramesOnLine = 0;
aLineBounds.SetRect(0, 0, 0, 0);
return NS_OK;
}
nsLineBox* line = mLines[aLineNumber];
*aFirstFrameOnLine = line->mFirstChild;
*aNumFramesOnLine = line->GetChildCount();
aLineBounds = line->GetPhysicalBounds();
return NS_OK;
}
int32_t
nsLineIterator::FindLineContaining(nsIFrame* aFrame, int32_t aStartLine)
{
NS_PRECONDITION(aStartLine <= mNumLines, "Bogus line numbers");
int32_t lineNumber = aStartLine;
while (lineNumber != mNumLines) {
nsLineBox* line = mLines[lineNumber];
if (line->Contains(aFrame)) {
return lineNumber;
}
++lineNumber;
}
return -1;
}
NS_IMETHODIMP
nsLineIterator::CheckLineOrder(int32_t aLine,
bool *aIsReordered,
nsIFrame **aFirstVisual,
nsIFrame **aLastVisual)
{
NS_ASSERTION (aLine >= 0 && aLine < mNumLines, "aLine out of range!");
nsLineBox* line = mLines[aLine];
if (!line->mFirstChild) { // empty line
*aIsReordered = false;
*aFirstVisual = nullptr;
*aLastVisual = nullptr;
return NS_OK;
}
nsIFrame* leftmostFrame;
nsIFrame* rightmostFrame;
*aIsReordered = nsBidiPresUtils::CheckLineOrder(line->mFirstChild, line->GetChildCount(), &leftmostFrame, &rightmostFrame);
// map leftmost/rightmost to first/last according to paragraph direction
*aFirstVisual = mRightToLeft ? rightmostFrame : leftmostFrame;
*aLastVisual = mRightToLeft ? leftmostFrame : rightmostFrame;
return NS_OK;
}
NS_IMETHODIMP
nsLineIterator::FindFrameAt(int32_t aLineNumber,
nsPoint aPos,
nsIFrame** aFrameFound,
bool* aPosIsBeforeFirstFrame,
bool* aPosIsAfterLastFrame)
{
NS_PRECONDITION(aFrameFound && aPosIsBeforeFirstFrame && aPosIsAfterLastFrame,
"null OUT ptr");
if (!aFrameFound || !aPosIsBeforeFirstFrame || !aPosIsAfterLastFrame) {
return NS_ERROR_NULL_POINTER;
}
if ((aLineNumber < 0) || (aLineNumber >= mNumLines)) {
return NS_ERROR_INVALID_ARG;
}
nsLineBox* line = mLines[aLineNumber];
if (!line) {
*aFrameFound = nullptr;
*aPosIsBeforeFirstFrame = true;
*aPosIsAfterLastFrame = false;
return NS_OK;
}
if (line->ISize() == 0 && line->BSize() == 0)
return NS_ERROR_FAILURE;
nsIFrame* frame = line->mFirstChild;
nsIFrame* closestFromStart = nullptr;
nsIFrame* closestFromEnd = nullptr;
WritingMode wm = line->mWritingMode;
nsSize containerSize = line->mContainerSize;
LogicalPoint pos(wm, aPos, containerSize);
int32_t n = line->GetChildCount();
while (n--) {
LogicalRect rect = frame->GetLogicalRect(wm, containerSize);
if (rect.ISize(wm) > 0) {
// If pos.I() is inside this frame - this is it
if (rect.IStart(wm) <= pos.I(wm) && rect.IEnd(wm) > pos.I(wm)) {
closestFromStart = closestFromEnd = frame;
break;
}
if (rect.IStart(wm) < pos.I(wm)) {
if (!closestFromStart ||
rect.IEnd(wm) > closestFromStart->
GetLogicalRect(wm, containerSize).IEnd(wm))
closestFromStart = frame;
}
else {
if (!closestFromEnd ||
rect.IStart(wm) < closestFromEnd->
GetLogicalRect(wm, containerSize).IStart(wm))
closestFromEnd = frame;
}
}
frame = frame->GetNextSibling();
}
if (!closestFromStart && !closestFromEnd) {
// All frames were zero-width. Just take the first one.
closestFromStart = closestFromEnd = line->mFirstChild;
}
*aPosIsBeforeFirstFrame = mRightToLeft ? !closestFromEnd : !closestFromStart;
*aPosIsAfterLastFrame = mRightToLeft ? !closestFromStart : !closestFromEnd;
if (closestFromStart == closestFromEnd) {
*aFrameFound = closestFromStart;
}
else if (!closestFromStart) {
*aFrameFound = closestFromEnd;
}
else if (!closestFromEnd) {
*aFrameFound = closestFromStart;
}
else { // we're between two frames
nscoord delta =
closestFromEnd->GetLogicalRect(wm, containerSize).IStart(wm) -
closestFromStart->GetLogicalRect(wm, containerSize).IEnd(wm);
if (pos.I(wm) < closestFromStart->
GetLogicalRect(wm, containerSize).IEnd(wm) + delta/2) {
*aFrameFound = closestFromStart;
} else {
*aFrameFound = closestFromEnd;
}
}
return NS_OK;
}
NS_IMETHODIMP
nsLineIterator::GetNextSiblingOnLine(nsIFrame*& aFrame, int32_t aLineNumber)
{
aFrame = aFrame->GetNextSibling();
return NS_OK;
}
//----------------------------------------------------------------------
#ifdef NS_BUILD_REFCNT_LOGGING
nsFloatCacheList::nsFloatCacheList() :
mHead(nullptr)
{
MOZ_COUNT_CTOR(nsFloatCacheList);
}
#endif
nsFloatCacheList::~nsFloatCacheList()
{
DeleteAll();
MOZ_COUNT_DTOR(nsFloatCacheList);
}
void
nsFloatCacheList::DeleteAll()
{
nsFloatCache* c = mHead;
while (c) {
nsFloatCache* next = c->Next();
delete c;
c = next;
}
mHead = nullptr;
}
nsFloatCache*
nsFloatCacheList::Tail() const
{
nsFloatCache* fc = mHead;
while (fc) {
if (!fc->mNext) {
break;
}
fc = fc->mNext;
}
return fc;
}
void
nsFloatCacheList::Append(nsFloatCacheFreeList& aList)
{
NS_PRECONDITION(aList.NotEmpty(), "Appending empty list will fail");
nsFloatCache* tail = Tail();
if (tail) {
NS_ASSERTION(!tail->mNext, "Bogus!");
tail->mNext = aList.mHead;
}
else {
NS_ASSERTION(!mHead, "Bogus!");
mHead = aList.mHead;
}
aList.mHead = nullptr;
aList.mTail = nullptr;
}
nsFloatCache*
nsFloatCacheList::Find(nsIFrame* aOutOfFlowFrame)
{
nsFloatCache* fc = mHead;
while (fc) {
if (fc->mFloat == aOutOfFlowFrame) {
break;
}
fc = fc->Next();
}
return fc;
}
nsFloatCache*
nsFloatCacheList::RemoveAndReturnPrev(nsFloatCache* aElement)
{
nsFloatCache* fc = mHead;
nsFloatCache* prev = nullptr;
while (fc) {
if (fc == aElement) {
if (prev) {
prev->mNext = fc->mNext;
} else {
mHead = fc->mNext;
}
return prev;
}
prev = fc;
fc = fc->mNext;
}
return nullptr;
}
//----------------------------------------------------------------------
#ifdef NS_BUILD_REFCNT_LOGGING
nsFloatCacheFreeList::nsFloatCacheFreeList() :
mTail(nullptr)
{
MOZ_COUNT_CTOR(nsFloatCacheFreeList);
}
nsFloatCacheFreeList::~nsFloatCacheFreeList()
{
MOZ_COUNT_DTOR(nsFloatCacheFreeList);
}
#endif
void
nsFloatCacheFreeList::Append(nsFloatCacheList& aList)
{
NS_PRECONDITION(aList.NotEmpty(), "Appending empty list will fail");
if (mTail) {
NS_ASSERTION(!mTail->mNext, "Bogus");
mTail->mNext = aList.mHead;
}
else {
NS_ASSERTION(!mHead, "Bogus");
mHead = aList.mHead;
}
mTail = aList.Tail();
aList.mHead = nullptr;
}
void
nsFloatCacheFreeList::Remove(nsFloatCache* aElement)
{
nsFloatCache* prev = nsFloatCacheList::RemoveAndReturnPrev(aElement);
if (mTail == aElement) {
mTail = prev;
}
}
void
nsFloatCacheFreeList::DeleteAll()
{
nsFloatCacheList::DeleteAll();
mTail = nullptr;
}
nsFloatCache*
nsFloatCacheFreeList::Alloc(nsIFrame* aFloat)
{
NS_PRECONDITION(aFloat->GetStateBits() & NS_FRAME_OUT_OF_FLOW,
"This is a float cache, why isn't the frame out-of-flow?");
nsFloatCache* fc = mHead;
if (mHead) {
if (mHead == mTail) {
mHead = mTail = nullptr;
}
else {
mHead = fc->mNext;
}
fc->mNext = nullptr;
}
else {
fc = new nsFloatCache();
}
fc->mFloat = aFloat;
return fc;
}
void
nsFloatCacheFreeList::Append(nsFloatCache* aFloat)
{
NS_ASSERTION(!aFloat->mNext, "Bogus!");
aFloat->mNext = nullptr;
if (mTail) {
NS_ASSERTION(!mTail->mNext, "Bogus!");
mTail->mNext = aFloat;
mTail = aFloat;
}
else {
NS_ASSERTION(!mHead, "Bogus!");
mHead = mTail = aFloat;
}
}
//----------------------------------------------------------------------
nsFloatCache::nsFloatCache()
: mFloat(nullptr),
mNext(nullptr)
{
MOZ_COUNT_CTOR(nsFloatCache);
}
#ifdef NS_BUILD_REFCNT_LOGGING
nsFloatCache::~nsFloatCache()
{
MOZ_COUNT_DTOR(nsFloatCache);
}
#endif