diff options
author | Jeremy Andrews <athenian200@outlook.com> | 2021-10-11 18:04:47 -0500 |
---|---|---|
committer | Moonchild <moonchild@palemoon.org> | 2022-04-01 15:00:11 +0200 |
commit | c56023163bd63cf7dcbb8fb87a05876b11669bcd (patch) | |
tree | ad04a6d81f1e2b5864208adf8e12b639b9925ff8 | |
parent | 1caa678592ac5c372975bdaa24ce01b470349b1f (diff) | |
download | uxp-c56023163bd63cf7dcbb8fb87a05876b11669bcd.tar.gz |
Issue #1832 - Account for shadow boundaries in nsFrameManager.
Resolves #1832
-rw-r--r-- | dom/base/nsINode.h | 17 | ||||
-rw-r--r-- | layout/base/nsFrameManager.cpp | 161 | ||||
-rw-r--r-- | layout/base/nsFrameManager.h | 4 |
3 files changed, 131 insertions, 51 deletions
diff --git a/dom/base/nsINode.h b/dom/base/nsINode.h index 1599a0094f..1d580540f8 100644 --- a/dom/base/nsINode.h +++ b/dom/base/nsINode.h @@ -1652,6 +1652,9 @@ private: ParserHasNotified, // Sets if the node is apz aware or we have apz aware listeners. MayBeApzAware, + // Set if this node has at some point (and may still have) + // display:none or display:contents children. + NodeMayHaveChildrenWithLayoutBoxesDisabled, // Guard value BooleanFlagCount }; @@ -1790,6 +1793,20 @@ public: { return GetBoolFlag(MayBeApzAware); } + + void SetMayHaveChildrenWithLayoutBoxesDisabled() + { + SetBoolFlag(NodeMayHaveChildrenWithLayoutBoxesDisabled); + } + void UnsetMayHaveChildrenWithLayoutBoxesDisabled() + { + ClearBoolFlag(NodeMayHaveChildrenWithLayoutBoxesDisabled); + } + bool MayHaveChildrenWithLayoutBoxesDisabled() const + { + return GetBoolFlag(NodeMayHaveChildrenWithLayoutBoxesDisabled); + } + protected: void SetParentIsContent(bool aValue) { SetBoolFlag(ParentIsContent, aValue); } void SetIsInDocument() { SetBoolFlag(IsInDocument); } diff --git a/layout/base/nsFrameManager.cpp b/layout/base/nsFrameManager.cpp index be8cd7147d..d6a6cb46b5 100644 --- a/layout/base/nsFrameManager.cpp +++ b/layout/base/nsFrameManager.cpp @@ -187,6 +187,17 @@ nsFrameManager::SetStyleContextInMap(UndisplayedMap* aMap, "undisplayed content must have a parent, unless it's the root " "element"); #endif + + // We set this bit as an optimization so that we can can know when a content + // node may have |display:none| or |display:contents| children. This allows + // other parts of the code to avoid checking for such children in + // mUndisplayedMap and mDisplayContentsMap if the bit isn't present on a node + // that it's handling. + +if (parent) { + parent->SetMayHaveChildrenWithLayoutBoxesDisabled(); +} + aMap->AddNodeFor(parent, aContent, aStyleContext); } @@ -212,7 +223,10 @@ nsFrameManager::ChangeStyleContextInMap(UndisplayedMap* aMap, printf("ChangeStyleContextInMap(%d): p=%p \n", i++, (void *)aContent); #endif - for (UndisplayedNode* node = aMap->GetFirstNode(aContent->GetParent()); + nsIContent* parent = aContent->GetParentElementCrossingShadowRoot(); + MOZ_ASSERT(parent || !aContent->GetParent(), "no non-elements"); + + for (UndisplayedNode* node = aMap->GetFirstNode(parent); node; node = node->getNext()) { if (node->mContent == aContent) { node->mStyle = aStyleContext; @@ -235,9 +249,20 @@ nsFrameManager::ClearUndisplayedContentIn(nsIContent* aContent, if (!mUndisplayedMap) { return; } + + if (aParentContent && + !aParentContent->MayHaveChildrenWithLayoutBoxesDisabled()) { + MOZ_ASSERT(!mUndisplayedMap->GetFirstNode(aParentContent), + "MayHaveChildrenWithLayoutBoxesDisabled bit out of sync - " + "may fail to removenode from mUndisplayedMap"); + return; + } - for (UndisplayedNode* node = mUndisplayedMap->GetFirstNode(aParentContent); - node; node = node->getNext()) { + UndisplayedNode* node = mUndisplayedMap->GetFirstNode(aParentContent); + + const bool haveOneDisplayNoneChild = node && !node->getNext(); + + for (; node; node = node->getNext()) { if (node->mContent == aContent) { mUndisplayedMap->RemoveNodeFor(aParentContent, node); @@ -247,6 +272,22 @@ nsFrameManager::ClearUndisplayedContentIn(nsIContent* aContent, // make sure that there are no more entries for the same content MOZ_ASSERT(!GetUndisplayedContent(aContent), "Found more undisplayed content data after removal"); + + if (haveOneDisplayNoneChild) { + // There are no more children of aParentContent in mUndisplayedMap. + MOZ_ASSERT(!mUndisplayedMap->GetFirstNode(aParentContent), + "Bad UnsetMayHaveChildrenWithLayoutBoxesDisabled call"); + // If we also know that none of its children are in mDisplayContentsMap + // then we can call UnsetMayHaveChildrenWithLayoutBoxesDisabled. We + // don't want to check mDisplayContentsMap though, since that involves + // a hash table lookup in relatively hot code. Still, we know there are + // no chlildren in mDisplayContentsMap if the map is empty, so we do + // check for that. + if (aParentContent && !mDisplayContentsMap) { + aParentContent->UnsetMayHaveChildrenWithLayoutBoxesDisabled(); + } + } + return; } } @@ -257,16 +298,44 @@ nsFrameManager::ClearUndisplayedContentIn(nsIContent* aContent, } void -nsFrameManager::ClearAllUndisplayedContentIn(nsIContent* aParentContent) +nsFrameManager::ClearAllMapsFor(nsIContent* aParentContent) { -#ifdef DEBUG_UNDISPLAYED_MAP +#if defined(DEBUG_UNDISPLAYED_MAP) || defined(DEBUG_DISPLAY_CONTENTS_MAP) static int i = 0; - printf("ClearAllUndisplayedContentIn(%d): parent=%p \n", i++, (void*)aParentContent); + printf("ClearAllMapsFor(%d): parent=%p \n", i++, (void*)aParentContent); #endif - if (mUndisplayedMap) { - mUndisplayedMap->RemoveNodesFor(aParentContent); + if (!aParentContent || + aParentContent->MayHaveChildrenWithLayoutBoxesDisabled()) { + if (mUndisplayedMap) { + mUndisplayedMap->RemoveNodesFor(aParentContent); + } + if (mDisplayContentsMap) { + nsAutoPtr<LinkedList<UndisplayedNode>> list = + mDisplayContentsMap->UnlinkNodesFor(aParentContent); + if (list) { + while (UndisplayedNode* node = list->popFirst()) { + ClearAllMapsFor(node->mContent); + delete node; + } + } + } + if (aParentContent) { + aParentContent->UnsetMayHaveChildrenWithLayoutBoxesDisabled(); + } } +#ifdef DEBUG + else { + if (mUndisplayedMap) { + MOZ_ASSERT(!mUndisplayedMap->GetFirstNode(aParentContent), + "We failed to remove a node from mUndisplayedMap"); + } + if (mDisplayContentsMap) { + MOZ_ASSERT(!mDisplayContentsMap->GetFirstNode(aParentContent), + "We failed to remove a node from mDisplayContentsMap"); + } + } +#endif // Need to look at aParentContent's content list due to XBL insertions. // Nodes in aParentContent's content list do not have aParentContent as a @@ -274,8 +343,10 @@ nsFrameManager::ClearAllUndisplayedContentIn(nsIContent* aParentContent) // the flattened content list and just ignore any nodes we don't care about. FlattenedChildIterator iter(aParentContent); for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) { - if (child->GetParent() != aParentContent) { - ClearUndisplayedContentIn(child, child->GetParent()); + auto parent = child->GetParent(); + if (parent != aParentContent) { + ClearUndisplayedContentIn(child, parent); + ClearDisplayContentsIn(child, parent); } } } @@ -311,8 +382,19 @@ nsFrameManager::ClearDisplayContentsIn(nsIContent* aContent, return; } - for (UndisplayedNode* node = mDisplayContentsMap->GetFirstNode(aParentContent); - node; node = node->getNext()) { + if (aParentContent && + !aParentContent->MayHaveChildrenWithLayoutBoxesDisabled()) { + MOZ_ASSERT(!mDisplayContentsMap->GetFirstNode(aParentContent), + "MayHaveChildrenWithLayoutBoxesDisabled bit out of sync - " + "may fail to remove node from mDisplayContentsMap"); + return; + } + + UndisplayedNode* node = mDisplayContentsMap->GetFirstNode(aParentContent); + + const bool haveOneDisplayContentsChild = node && !node->getNext(); + + for (; node; node = node->getNext()) { if (node->mContent == aContent) { mDisplayContentsMap->RemoveNodeFor(aParentContent, node); @@ -322,8 +404,23 @@ nsFrameManager::ClearDisplayContentsIn(nsIContent* aContent, // make sure that there are no more entries for the same content MOZ_ASSERT(!GetDisplayContentsStyleFor(aContent), "Found more entries for aContent after removal"); - ClearAllDisplayContentsIn(aContent); - ClearAllUndisplayedContentIn(aContent); + ClearAllMapsFor(aContent); + + if (haveOneDisplayContentsChild) { + // There are no more children of aParentContent in mDisplayContentsMap. + MOZ_ASSERT(!mDisplayContentsMap->GetFirstNode(aParentContent), + "Bad UnsetMayHaveChildrenWithLayoutBoxesDisabled call"); + // If we also know that none of its children are in mUndisplayedMap + // then we can call UnsetMayHaveChildrenWithLayoutBoxesDisabled. We + // don't want to check mUndisplayedMap though since that involves a + // hash table lookup in relatively hot code. Still, we know there are + // no children in mUndisplayedMap if the map is empty, so we do + // check for that. + if (aParentContent && !mUndisplayedMap) { + aParentContent->UnsetMayHaveChildrenWithLayoutBoxesDisabled(); + } + } + return; } } @@ -332,39 +429,6 @@ nsFrameManager::ClearDisplayContentsIn(nsIContent* aContent, #endif } -void -nsFrameManager::ClearAllDisplayContentsIn(nsIContent* aParentContent) -{ -#ifdef DEBUG_DISPLAY_CONTENTS_MAP - static int i = 0; - printf("ClearAllDisplayContentsIn(%d): parent=%p \n", i++, (void*)aParentContent); -#endif - - if (mDisplayContentsMap) { - nsAutoPtr<LinkedList<UndisplayedNode>> list = - mDisplayContentsMap->UnlinkNodesFor(aParentContent); - if (list) { - while (UndisplayedNode* node = list->popFirst()) { - ClearAllDisplayContentsIn(node->mContent); - ClearAllUndisplayedContentIn(node->mContent); - delete node; - } - } - } - - // Need to look at aParentContent's content list due to XBL insertions. - // Nodes in aParentContent's content list do not have aParentContent as a - // parent, but are treated as children of aParentContent. We iterate over - // the flattened content list and just ignore any nodes we don't care about. - FlattenedChildIterator iter(aParentContent); - for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) { - if (child->GetParent() != aParentContent) { - ClearDisplayContentsIn(child, child->GetParent()); - ClearUndisplayedContentIn(child, child->GetParent()); - } - } -} - //---------------------------------------------------------------------- void nsFrameManager::AppendFrames(nsContainerFrame* aParentFrame, @@ -441,8 +505,7 @@ nsFrameManager::NotifyDestroyingFrame(nsIFrame* aFrame) { nsIContent* content = aFrame->GetContent(); if (content && content->GetPrimaryFrame() == aFrame) { - ClearAllUndisplayedContentIn(content); - ClearAllDisplayContentsIn(content); + ClearAllMapsFor(content); } } diff --git a/layout/base/nsFrameManager.h b/layout/base/nsFrameManager.h index ae7477d3d3..7537888605 100644 --- a/layout/base/nsFrameManager.h +++ b/layout/base/nsFrameManager.h @@ -99,7 +99,6 @@ public: void ClearUndisplayedContentIn(nsIContent* aContent, nsIContent* aParentContent); - void ClearAllUndisplayedContentIn(nsIContent* aParentContent); // display:contents related methods: /** @@ -139,7 +138,6 @@ public: */ void ClearDisplayContentsIn(nsIContent* aContent, nsIContent* aParentContent); - void ClearAllDisplayContentsIn(nsIContent* aParentContent); // Functions for manipulating the frame model void AppendFrames(nsContainerFrame* aParentFrame, @@ -184,6 +182,8 @@ public: void RestoreFrameStateFor(nsIFrame* aFrame, nsILayoutHistoryState* aState); protected: + void ClearAllMapsFor(nsIContent* aParentContent); + static nsStyleContext* GetStyleContextInMap(UndisplayedMap* aMap, const nsIContent* aContent); static mozilla::UndisplayedNode* |