summaryrefslogtreecommitdiffstats
path: root/layout/base
diff options
context:
space:
mode:
authorMatt A. Tobin <email@mattatobin.com>2020-04-17 07:20:06 -0400
committerMatt A. Tobin <email@mattatobin.com>2020-04-17 07:20:06 -0400
commitee4857f2098163c1355716944753ab1da1b09611 (patch)
tree7312278ab091377dcf5ebca50bdbd2ba53b38d04 /layout/base
parent352fe83985e4a251a96fb5260d923a36c9ed56b4 (diff)
downloadUXP-ee4857f2098163c1355716944753ab1da1b09611.tar
UXP-ee4857f2098163c1355716944753ab1da1b09611.tar.gz
UXP-ee4857f2098163c1355716944753ab1da1b09611.tar.lz
UXP-ee4857f2098163c1355716944753ab1da1b09611.tar.xz
UXP-ee4857f2098163c1355716944753ab1da1b09611.zip
Bug 1413619 - Fix insertion point computation when display: contents pseudos are involved.
Tag #1375
Diffstat (limited to 'layout/base')
-rw-r--r--layout/base/nsCSSFrameConstructor.cpp240
-rw-r--r--layout/base/nsCSSFrameConstructor.h93
-rw-r--r--layout/base/nsFrameManager.cpp3
-rw-r--r--layout/base/nsFrameManager.h6
-rw-r--r--layout/base/nsLayoutUtils.cpp27
-rw-r--r--layout/base/nsLayoutUtils.h17
6 files changed, 160 insertions, 226 deletions
diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp
index 617e9a940..8b3bb2e9d 100644
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -6662,115 +6662,147 @@ nsCSSFrameConstructor::IsValidSibling(nsIFrame* aSibling,
return true;
}
+// FIXME(emilio): If we ever kill IsValidSibling() we can simplify this quite a
+// bit (no need to pass aTargetContent or aTargetContentDisplay, and the
+// adjust() calls can be responsibility of the caller).
+template<nsCSSFrameConstructor::SiblingDirection aDirection>
nsIFrame*
-nsCSSFrameConstructor::FindFrameForContentSibling(nsIContent* aContent,
- nsIContent* aTargetContent,
- StyleDisplay& aTargetContentDisplay,
- nsContainerFrame* aParentFrame,
- bool aPrevSibling)
-{
- nsIFrame* sibling = aContent->GetPrimaryFrame();
- if (!sibling && GetDisplayContentsStyleFor(aContent)) {
- // A display:contents node - check if it has a ::before / ::after frame...
- sibling = aPrevSibling ? nsLayoutUtils::GetAfterFrame(aContent)
- : nsLayoutUtils::GetBeforeFrame(aContent);
- if (!sibling) {
- // ... then recurse into children ...
- const bool forward = !aPrevSibling;
- FlattenedChildIterator iter(aContent, forward);
- sibling = aPrevSibling ?
- FindPreviousSibling(iter, aTargetContent, aTargetContentDisplay, aParentFrame) :
- FindNextSibling(iter, aTargetContent, aTargetContentDisplay, aParentFrame);
-
- // The recursion above has already done all the placeholder and
- // continuation fixups.
+nsCSSFrameConstructor::FindSiblingInternal(
+ FlattenedChildIterator aIter,
+ nsIContent* aTargetContent,
+ StyleDisplay& aTargetContentDisplay)
+{
+ auto adjust = [&](nsIFrame* aPotentialSiblingFrame) -> nsIFrame* {
+ return AdjustSiblingFrame(
+ aPotentialSiblingFrame, aTargetContent, aTargetContentDisplay,
+ aDirection);
+ };
+
+ auto nextDomSibling = [](FlattenedChildIterator& aIter) -> nsIContent* {
+ return aDirection == SiblingDirection::Forward
+ ? aIter.GetNextChild() : aIter.GetPreviousChild();
+ };
+
+ auto getNearPseudo = [](const nsIContent* aContent) -> nsIFrame* {
+ return aDirection == SiblingDirection::Forward
+ ? nsLayoutUtils::GetBeforeFrame(aContent)
+ : nsLayoutUtils::GetAfterFrame(aContent);
+ };
+
+ auto getFarPseudo = [](const nsIContent* aContent) -> nsIFrame* {
+ return aDirection == SiblingDirection::Forward
+ ? nsLayoutUtils::GetAfterFrame(aContent)
+ : nsLayoutUtils::GetBeforeFrame(aContent);
+ };
+
+ while (nsIContent* sibling = nextDomSibling(aIter)) {
+ if (nsIFrame* primaryFrame = sibling->GetPrimaryFrame()) {
+ // XXX the GetContent() == sibling check is needed due to bug 135040.
+ // Remove it once that's fixed.
+ if (primaryFrame->GetContent() == sibling) {
+ if (nsIFrame* frame = adjust(primaryFrame)) {
+ return frame;
+ }
+ }
+ }
+
+ if (GetDisplayContentsStyleFor(sibling)) {
+ if (nsIFrame* frame = adjust(getNearPseudo(sibling))) {
+ return frame;
+ }
+
+ const bool startFromBeginning = aDirection == SiblingDirection::Forward;
+ FlattenedChildIterator iter(sibling, startFromBeginning);
+ nsIFrame* sibling = FindSiblingInternal<aDirection>(
+ iter, aTargetContent, aTargetContentDisplay);
if (sibling) {
return sibling;
}
}
- if (!sibling) {
- // ... then ::after / ::before on the opposite end.
- sibling = aPrevSibling ? nsLayoutUtils::GetAfterFrame(aContent)
- : nsLayoutUtils::GetBeforeFrame(aContent);
- }
- if (!sibling) {
- return nullptr;
- }
- } else if (!sibling || sibling->GetContent() != aContent) {
- // XXX the GetContent() != aContent check is needed due to bug 135040.
- // Remove it once that's fixed.
- return nullptr;
}
- // If the frame is out-of-flow, GetPrimaryFrame() will have returned the
- // out-of-flow frame; we want the placeholder.
- if (sibling->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
- nsIFrame* placeholderFrame = sibling->GetPlaceholderFrame();
- NS_ASSERTION(placeholderFrame, "no placeholder for out-of-flow frame");
- sibling = placeholderFrame;
+ return adjust(getFarPseudo(aIter.Parent()));
+}
+
+nsIFrame*
+nsCSSFrameConstructor::AdjustSiblingFrame(
+ nsIFrame* aSibling,
+ nsIContent* aTargetContent,
+ mozilla::StyleDisplay& aTargetContentDisplay,
+ SiblingDirection aDirection)
+{
+ if (!aSibling) {
+ return nullptr;
}
- // The frame we have now should never be a continuation.
- NS_ASSERTION(!sibling->GetPrevContinuation(), "How did that happen?");
+ if (aSibling->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
+ aSibling = aSibling->GetPlaceholderFrame();
+ MOZ_ASSERT(aSibling);
+ }
- if (aPrevSibling) {
- // The frame may be a ib-split frame (a split inline frame that
- // contains a block). Get the last part of that split.
- if (IsFramePartOfIBSplit(sibling)) {
- sibling = GetLastIBSplitSibling(sibling, true);
+ MOZ_ASSERT(!aSibling->GetPrevContinuation(), "How?");
+ if (aDirection == SiblingDirection::Backward) {
+ // The frame may be a ib-split frame (a split inline frame that contains a
+ // block). Get the last part of that split.
+ if (IsFramePartOfIBSplit(aSibling)) {
+ aSibling = GetLastIBSplitSibling(aSibling, true);
}
// The frame may have a continuation. If so, we want the last
// non-overflow-container continuation as our previous sibling.
- sibling = sibling->GetTailContinuation();
+ aSibling = aSibling->GetTailContinuation();
}
- if (aTargetContent &&
- !IsValidSibling(sibling, aTargetContent, aTargetContentDisplay)) {
- sibling = nullptr;
+ if (!IsValidSibling(aSibling, aTargetContent, aTargetContentDisplay)) {
+ return nullptr;
}
- return sibling;
+ return aSibling;
}
nsIFrame*
-nsCSSFrameConstructor::FindPreviousSibling(FlattenedChildIterator aIter,
- nsIContent* aTargetContent,
- StyleDisplay& aTargetContentDisplay,
- nsContainerFrame* aParentFrame)
+nsCSSFrameConstructor::FindPreviousSibling(const FlattenedChildIterator& aIter,
+ StyleDisplay& aTargetContentDisplay)
{
- // Note: not all content objects are associated with a frame (e.g., if it's
- // `display: none') so keep looking until we find a previous frame.
- while (nsIContent* sibling = aIter.GetPreviousChild()) {
- MOZ_ASSERT(sibling != aTargetContent);
- nsIFrame* prevSibling =
- FindFrameForContentSibling(sibling, aTargetContent, aTargetContentDisplay,
- aParentFrame, true);
- if (prevSibling) {
- // Found a previous sibling, we're done!
- return prevSibling;
- }
- }
+ return FindSibling<SiblingDirection::Backward>(aIter, aTargetContentDisplay);
+}
- return nullptr;
+nsIFrame*
+nsCSSFrameConstructor::FindNextSibling(const FlattenedChildIterator& aIter,
+ StyleDisplay& aTargetContentDisplay)
+{
+ return FindSibling<SiblingDirection::Forward>(aIter, aTargetContentDisplay);
}
+template<nsCSSFrameConstructor::SiblingDirection aDirection>
nsIFrame*
-nsCSSFrameConstructor::FindNextSibling(FlattenedChildIterator aIter,
- nsIContent* aTargetContent,
- StyleDisplay& aTargetContentDisplay,
- nsContainerFrame* aParentFrame)
+nsCSSFrameConstructor::FindSibling(const FlattenedChildIterator& aIter,
+ StyleDisplay& aTargetContentDisplay)
{
- while (nsIContent* sibling = aIter.GetNextChild()) {
- MOZ_ASSERT(sibling != aTargetContent);
- nsIFrame* nextSibling =
- FindFrameForContentSibling(sibling, aTargetContent, aTargetContentDisplay,
- aParentFrame, false);
+ nsIContent* targetContent = aIter.Get();
+ nsIFrame* sibling =
+ FindSiblingInternal<aDirection>(aIter, targetContent, aTargetContentDisplay);
+ if (sibling) {
+ return sibling;
+ }
- if (nextSibling) {
- // We found a next sibling, we're done!
- return nextSibling;
+ // Our siblings (if any) do not have a frame to guide us. The frame for the
+ // target content should be inserted whereever a frame for the container would
+ // be inserted. This is needed when inserting into display: contents nodes.
+ const nsIContent* current = aIter.Parent();
+ while (GetDisplayContentsStyleFor(current)) {
+ const nsIContent* parent = current->GetFlattenedTreeParent();
+ MOZ_ASSERT(parent, "No display: contents on the root");
+
+ FlattenedChildIterator iter(parent);
+ iter.Seek(current);
+ sibling = FindSiblingInternal<aDirection>(
+ iter, targetContent, aTargetContentDisplay);
+ if (sibling) {
+ return sibling;
}
+
+ current = parent;
}
return nullptr;
@@ -6836,8 +6868,7 @@ nsCSSFrameConstructor::GetInsertionPrevSibling(InsertionPoint* aInsertion,
// Note that FindPreviousSibling is passed the iterator by value, so that
// the later usage of the iterator starts from the same place.
StyleDisplay childDisplay = UNSET_DISPLAY;
- nsIFrame* prevSibling =
- FindPreviousSibling(iter, iter.Get(), childDisplay, aInsertion->mParentFrame);
+ nsIFrame* prevSibling = FindPreviousSibling(iter, childDisplay);
// Now, find the geometric parent so that we can handle
// continuations properly. Use the prev sibling if we have it;
@@ -6850,34 +6881,7 @@ nsCSSFrameConstructor::GetInsertionPrevSibling(InsertionPoint* aInsertion,
iter.Seek(aEndSkipChild);
iter.GetPreviousChild();
}
- nsIFrame* nextSibling =
- FindNextSibling(iter, iter.Get(), childDisplay, aInsertion->mParentFrame);
- if (GetDisplayContentsStyleFor(aInsertion->mContainer)) {
- if (!nextSibling) {
- // Our siblings (if any) does not have a frame to guide us.
- // The frame for aChild should be inserted whereever a frame for
- // the container would be inserted. This is needed when inserting
- // into nested display:contents nodes.
- nsIContent* child = aInsertion->mContainer;
- nsIContent* parent = child->GetParent();
- aInsertion->mParentFrame =
- ::GetAdjustedParentFrame(aInsertion->mParentFrame,
- aInsertion->mParentFrame->GetType(),
- parent);
- InsertionPoint fakeInsertion(aInsertion->mParentFrame, parent);
- nsIFrame* result = GetInsertionPrevSibling(&fakeInsertion, child, aIsAppend,
- aIsRangeInsertSafe, nullptr, nullptr);
- MOZ_ASSERT(aInsertion->mParentFrame->GetContent() ==
- fakeInsertion.mParentFrame->GetContent());
- // fakeInsertion.mParentFrame may now be a continuation of the frame
- // we started with in the ctor above.
- aInsertion->mParentFrame = fakeInsertion.mParentFrame;
- return result;
- }
-
- prevSibling = nextSibling->GetPrevSibling();
- }
-
+ nsIFrame* nextSibling = FindNextSibling(iter, childDisplay);
if (nextSibling) {
aInsertion->mParentFrame = nextSibling->GetParent()->GetContentInsertionFrame();
} else {
@@ -8022,22 +8026,6 @@ nsCSSFrameConstructor::ContentRangeInserted(nsIContent* aContainer,
}
}
- if (!prevSibling) {
- // We're inserting the new frames as the first child. See if the
- // parent has a :before pseudo-element
- nsIFrame* firstChild = insertion.mParentFrame->PrincipalChildList().FirstChild();
-
- if (firstChild &&
- nsLayoutUtils::IsGeneratedContentFor(container, firstChild,
- nsCSSPseudoElements::before)) {
- // Insert the new frames after the last continuation of the :before
- prevSibling = firstChild->GetTailContinuation();
- insertion.mParentFrame = prevSibling->GetParent()->GetContentInsertionFrame();
- // Don't change isAppend here; we'll can call AppendFrames as needed, and
- // the change to our prevSibling doesn't affect that.
- }
- }
-
FrameConstructionItemList items;
ParentType parentType = GetParentType(frameType);
FlattenedChildIterator iter(aContainer);
diff --git a/layout/base/nsCSSFrameConstructor.h b/layout/base/nsCSSFrameConstructor.h
index 49107ee52..8c565cb02 100644
--- a/layout/base/nsCSSFrameConstructor.h
+++ b/layout/base/nsCSSFrameConstructor.h
@@ -1962,64 +1962,53 @@ private:
nsIFrame* aPrevSibling,
nsFrameItems& aFrameItems);
- /**
- * Find the right frame to use for aContent when looking for sibling
- * frames for aTargetContent. If aPrevSibling is true, this
- * will look for last continuations, etc, as necessary. This calls
- * IsValidSibling as needed; if that returns false it returns null.
- *
- * @param aContent the content to search for frames
- * @param aTargetContent the content we're finding a sibling frame for
- * @param aTargetContentDisplay the CSS display enum for aTargetContent if
- * already known, UNSET_DISPLAY otherwise. It will be filled in
- * if needed.
- * @param aParentFrame the nearest ancestor frame, used internally for
- * finding ::after / ::before frames
- * @param aPrevSibling true if we're searching in reverse DOM order
- */
- nsIFrame* FindFrameForContentSibling(nsIContent* aContent,
- nsIContent* aTargetContent,
- mozilla::StyleDisplay& aTargetContentDisplay,
- nsContainerFrame* aParentFrame,
- bool aPrevSibling);
+ // The direction in which we should look for siblings.
+ enum class SiblingDirection
+ {
+ Forward,
+ Backward,
+ };
/**
- * Find the frame for the content immediately preceding the one aIter
- * points to, following continuations if necessary. aIter is passed by
- * value on purpose, so as not to modify the caller's iterator.
+ * Find the frame for the content immediately next to the one aIter points to,
+ * in the direction SiblingDirection indicates, following continuations if
+ * necessary.
*
- * @param aIter should be positioned such that aIter.GetPreviousChild()
- * is the first content to search for frames
- * @param aTargetContent the content we're finding a sibling frame for
- * @param aTargetContentDisplay the CSS display enum for aTargetContent if
- * already known, UNSET_DISPLAY otherwise. It will be filled in
- * if needed.
- * @param aParentFrame the nearest ancestor frame, used inernally for
- * finding ::after / ::before frames
- */
- nsIFrame* FindPreviousSibling(mozilla::dom::FlattenedChildIterator aIter,
- nsIContent* aTargetContent,
- mozilla::StyleDisplay& aTargetContentDisplay,
- nsContainerFrame* aParentFrame);
-
- /**
- * Find the frame for the content node immediately following the one aIter
- * points to, following continuations if necessary. aIter is passed by value
- * on purpose, so as not to modify the caller's iterator.
+ * aIter is passed by const reference on purpose, so as not to modify the
+ * caller's iterator.
*
- * @param aIter should be positioned such that aIter.GetNextChild()
+ * @param aIter should be positioned such that aIter.GetPreviousChild()
* is the first content to search for frames
- * @param aTargetContent the content we're finding a sibling frame for
- * @param aTargetContentDisplay the CSS display enum for aTargetContent if
- * already known, UNSET_DISPLAY otherwise. It will be filled in
- * if needed.
- * @param aParentFrame the nearest ancestor frame, used inernally for
- * finding ::after / ::before frames
+ * @param aTargetContentDisplay the CSS display enum for the content aIter
+ * points to if already known, UNSET_DISPLAY otherwise. It will be
+ * filled in if needed.
*/
- nsIFrame* FindNextSibling(mozilla::dom::FlattenedChildIterator aIter,
- nsIContent* aTargetContent,
- mozilla::StyleDisplay& aTargetContentDisplay,
- nsContainerFrame* aParentFrame);
+ template<SiblingDirection>
+ nsIFrame* FindSibling(const mozilla::dom::FlattenedChildIterator& aIter,
+ mozilla::StyleDisplay& aTargetContentDisplay);
+
+ // Helper for the implementation of FindSibling.
+ template<SiblingDirection>
+ nsIFrame* FindSiblingInternal(
+ mozilla::dom::FlattenedChildIterator,
+ nsIContent* aTargetContent,
+ mozilla::StyleDisplay& aTargetContentDisplay);
+
+ // An alias of FindSibling<SiblingDirection::Forward>.
+ nsIFrame* FindNextSibling(const mozilla::dom::FlattenedChildIterator& aIter,
+ mozilla::StyleDisplay& aTargetContentDisplay);
+ // An alias of FindSibling<SiblingDirection::Backwards>.
+ nsIFrame* FindPreviousSibling(const mozilla::dom::FlattenedChildIterator& aIter,
+ mozilla::StyleDisplay& aTargetContentDisplay);
+
+ // Given a potential first-continuation sibling frame for aTargetContent,
+ // verify that it is an actual valid sibling for it, and return the
+ // appropriate continuation the new frame for aTargetContent should be
+ // inserted next to.
+ nsIFrame* AdjustSiblingFrame(nsIFrame* aSibling,
+ nsIContent* aTargetContent,
+ mozilla::StyleDisplay& aTargetContentDisplay,
+ SiblingDirection aDirection);
// Find the right previous sibling for an insertion. This also updates the
// parent frame to point to the correct continuation of the parent frame to
diff --git a/layout/base/nsFrameManager.cpp b/layout/base/nsFrameManager.cpp
index d703537fa..ce5c60bbc 100644
--- a/layout/base/nsFrameManager.cpp
+++ b/layout/base/nsFrameManager.cpp
@@ -132,7 +132,8 @@ nsFrameManager::Destroy()
//----------------------------------------------------------------------
/* static */ nsStyleContext*
-nsFrameManager::GetStyleContextInMap(UndisplayedMap* aMap, nsIContent* aContent)
+nsFrameManager::GetStyleContextInMap(UndisplayedMap* aMap,
+ const nsIContent* aContent)
{
if (!aContent) {
return nullptr;
diff --git a/layout/base/nsFrameManager.h b/layout/base/nsFrameManager.h
index 84d2c93e7..ae7477d3d 100644
--- a/layout/base/nsFrameManager.h
+++ b/layout/base/nsFrameManager.h
@@ -80,7 +80,7 @@ public:
void Destroy();
// Mapping undisplayed content
- nsStyleContext* GetUndisplayedContent(nsIContent* aContent)
+ nsStyleContext* GetUndisplayedContent(const nsIContent* aContent)
{
if (!mUndisplayedMap) {
return nullptr;
@@ -105,7 +105,7 @@ public:
/**
* Return the registered display:contents style context for aContent, if any.
*/
- nsStyleContext* GetDisplayContentsStyleFor(nsIContent* aContent)
+ nsStyleContext* GetDisplayContentsStyleFor(const nsIContent* aContent)
{
if (!mDisplayContentsMap) {
return nullptr;
@@ -185,7 +185,7 @@ public:
nsILayoutHistoryState* aState);
protected:
static nsStyleContext* GetStyleContextInMap(UndisplayedMap* aMap,
- nsIContent* aContent);
+ const nsIContent* aContent);
static mozilla::UndisplayedNode*
GetAllUndisplayedNodesInMapFor(UndisplayedMap* aMap,
nsIContent* aParentContent);
diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp
index 2c4449994..7496a4946 100644
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -1588,33 +1588,6 @@ nsLayoutUtils::GetFloatFromPlaceholder(nsIFrame* aFrame) {
}
// static
-bool
-nsLayoutUtils::IsGeneratedContentFor(nsIContent* aContent,
- nsIFrame* aFrame,
- nsIAtom* aPseudoElement)
-{
- NS_PRECONDITION(aFrame, "Must have a frame");
- NS_PRECONDITION(aPseudoElement, "Must have a pseudo name");
-
- if (!aFrame->IsGeneratedContentFrame()) {
- return false;
- }
- nsIFrame* parent = aFrame->GetParent();
- NS_ASSERTION(parent, "Generated content can't be root frame");
- if (parent->IsGeneratedContentFrame()) {
- // Not the root of the generated content
- return false;
- }
-
- if (aContent && parent->GetContent() != aContent) {
- return false;
- }
-
- return (aFrame->GetContent()->NodeInfo()->NameAtom() == nsGkAtoms::mozgeneratedcontentbefore) ==
- (aPseudoElement == nsCSSPseudoElements::before);
-}
-
-// static
nsIFrame*
nsLayoutUtils::GetCrossDocParentFrame(const nsIFrame* aFrame,
nsPoint* aExtraOffset)
diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h
index 9757cb799..36a43e46b 100644
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -353,23 +353,6 @@ public:
*/
static nsIFrame* GetRealPrimaryFrameFor(const nsIContent* aContent);
- /**
- * IsGeneratedContentFor returns true if aFrame is the outermost
- * frame for generated content of type aPseudoElement for aContent.
- * aFrame *might not* have the aPseudoElement pseudo-style! For example
- * it might be a table wrapper frame and the inner table frame might
- * have the pseudo-style.
- *
- * @param aContent the content node we're looking at. If this is
- * null, then we just assume that aFrame has the right content
- * pointer.
- * @param aFrame the frame we're looking at
- * @param aPseudoElement the pseudo type we're interested in
- * @return whether aFrame is the generated aPseudoElement frame for aContent
- */
- static bool IsGeneratedContentFor(nsIContent* aContent, nsIFrame* aFrame,
- nsIAtom* aPseudoElement);
-
#ifdef DEBUG
// TODO: remove, see bug 598468.
static bool gPreventAssertInCompareTreePosition;