mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-08-21 03:43:16 +09:00
Bug 1413619 - Fix insertion point computation when display: contents pseudos are involved.
Tag #1375
This commit is contained in:
parent
9b5061e795
commit
5b1452e120
10 changed files with 192 additions and 243 deletions
|
|
@ -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)
|
||||
nsCSSFrameConstructor::FindSiblingInternal(
|
||||
FlattenedChildIterator aIter,
|
||||
nsIContent* aTargetContent,
|
||||
StyleDisplay& aTargetContentDisplay)
|
||||
{
|
||||
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);
|
||||
auto adjust = [&](nsIFrame* aPotentialSiblingFrame) -> nsIFrame* {
|
||||
return AdjustSiblingFrame(
|
||||
aPotentialSiblingFrame, aTargetContent, aTargetContentDisplay,
|
||||
aDirection);
|
||||
};
|
||||
|
||||
// The recursion above has already done all the placeholder and
|
||||
// continuation fixups.
|
||||
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 adjust(getFarPseudo(aIter.Parent()));
|
||||
}
|
||||
|
||||
nsIFrame*
|
||||
nsCSSFrameConstructor::AdjustSiblingFrame(
|
||||
nsIFrame* aSibling,
|
||||
nsIContent* aTargetContent,
|
||||
mozilla::StyleDisplay& aTargetContentDisplay,
|
||||
SiblingDirection aDirection)
|
||||
{
|
||||
if (!aSibling) {
|
||||
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;
|
||||
if (aSibling->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
|
||||
aSibling = aSibling->GetPlaceholderFrame();
|
||||
MOZ_ASSERT(aSibling);
|
||||
}
|
||||
|
||||
// The frame we have now should never be a continuation.
|
||||
NS_ASSERTION(!sibling->GetPrevContinuation(), "How did that happen?");
|
||||
|
||||
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 nullptr;
|
||||
return FindSibling<SiblingDirection::Backward>(aIter, aTargetContentDisplay);
|
||||
}
|
||||
|
||||
nsIFrame*
|
||||
nsCSSFrameConstructor::FindNextSibling(FlattenedChildIterator aIter,
|
||||
nsIContent* aTargetContent,
|
||||
StyleDisplay& aTargetContentDisplay,
|
||||
nsContainerFrame* aParentFrame)
|
||||
nsCSSFrameConstructor::FindNextSibling(const FlattenedChildIterator& aIter,
|
||||
StyleDisplay& aTargetContentDisplay)
|
||||
{
|
||||
while (nsIContent* sibling = aIter.GetNextChild()) {
|
||||
MOZ_ASSERT(sibling != aTargetContent);
|
||||
nsIFrame* nextSibling =
|
||||
FindFrameForContentSibling(sibling, aTargetContent, aTargetContentDisplay,
|
||||
aParentFrame, false);
|
||||
return FindSibling<SiblingDirection::Forward>(aIter, aTargetContentDisplay);
|
||||
}
|
||||
|
||||
if (nextSibling) {
|
||||
// We found a next sibling, we're done!
|
||||
return nextSibling;
|
||||
template<nsCSSFrameConstructor::SiblingDirection aDirection>
|
||||
nsIFrame*
|
||||
nsCSSFrameConstructor::FindSibling(const FlattenedChildIterator& aIter,
|
||||
StyleDisplay& aTargetContentDisplay)
|
||||
{
|
||||
nsIContent* targetContent = aIter.Get();
|
||||
nsIFrame* sibling =
|
||||
FindSiblingInternal<aDirection>(aIter, targetContent, aTargetContentDisplay);
|
||||
if (sibling) {
|
||||
return sibling;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue