diff options
author | Matt A. Tobin <email@mattatobin.com> | 2020-04-17 05:10:25 -0400 |
---|---|---|
committer | Matt A. Tobin <email@mattatobin.com> | 2020-04-17 05:10:25 -0400 |
commit | 7614fdb51b177e6975fce5bf9a7facef170e61aa (patch) | |
tree | 598e187ce71ae82b300a3a6b6b2f199aa2f3c43d /layout/base | |
parent | 5f297c5f57583b0f9d27d714beb285919f42d655 (diff) | |
download | UXP-7614fdb51b177e6975fce5bf9a7facef170e61aa.tar UXP-7614fdb51b177e6975fce5bf9a7facef170e61aa.tar.gz UXP-7614fdb51b177e6975fce5bf9a7facef170e61aa.tar.lz UXP-7614fdb51b177e6975fce5bf9a7facef170e61aa.tar.xz UXP-7614fdb51b177e6975fce5bf9a7facef170e61aa.zip |
Bug 1355351 - Make pseudo-elements return the correct style via getComputedStyle
* Add a node property to access the ::before and ::after pseudo-elements
* Look for the frame for ::before and ::after pseudos
* Clean up pseudo-element props
* Simplify nsLayoutUtils callers, and make child iterators notice display: contents pseudos
Tag #1375
Diffstat (limited to 'layout/base')
-rw-r--r-- | layout/base/RestyleManager.cpp | 4 | ||||
-rw-r--r-- | layout/base/ServoRestyleManager.cpp | 13 | ||||
-rw-r--r-- | layout/base/nsCSSFrameConstructor.cpp | 61 | ||||
-rw-r--r-- | layout/base/nsLayoutUtils.cpp | 96 | ||||
-rw-r--r-- | layout/base/nsLayoutUtils.h | 44 |
5 files changed, 68 insertions, 150 deletions
diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp index 4d08dee06..8db3d9302 100644 --- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -3626,14 +3626,14 @@ ElementRestyler::MustReframeForPseudo(CSSPseudoElementType aPseudoType, // Check for a ::before pseudo style and the absence of a ::before content, // but only if aFrame is null or is the first continuation/ib-split. if ((aFrame && !nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(aFrame)) || - nsLayoutUtils::GetBeforeFrameForContent(aGenConParentFrame, aContent)) { + nsLayoutUtils::GetBeforeFrame(aContent)) { return false; } } else { // Similarly for ::after, but check for being the last continuation/ // ib-split. if ((aFrame && nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame)) || - nsLayoutUtils::GetAfterFrameForContent(aGenConParentFrame, aContent)) { + nsLayoutUtils::GetAfterFrame(aContent)) { return false; } } diff --git a/layout/base/ServoRestyleManager.cpp b/layout/base/ServoRestyleManager.cpp index 9624c678b..0659ab857 100644 --- a/layout/base/ServoRestyleManager.cpp +++ b/layout/base/ServoRestyleManager.cpp @@ -277,24 +277,17 @@ ServoRestyleManager::FrameForPseudoElement(const nsIContent* aContent, nsIAtom* aPseudoTagOrNull) { MOZ_ASSERT_IF(aPseudoTagOrNull, aContent->IsElement()); - nsIFrame* primaryFrame = aContent->GetPrimaryFrame(); if (!aPseudoTagOrNull) { - return primaryFrame; - } - - if (!primaryFrame) { - return nullptr; + return aContent->GetPrimaryFrame(); } - // NOTE: we probably need to special-case display: contents here. Gecko's - // RestyleManager passes the primary frame of the parent instead. if (aPseudoTagOrNull == nsCSSPseudoElements::before) { - return nsLayoutUtils::GetBeforeFrameForContent(primaryFrame, aContent); + return nsLayoutUtils::GetBeforeFrame(aContent); } if (aPseudoTagOrNull == nsCSSPseudoElements::after) { - return nsLayoutUtils::GetAfterFrameForContent(primaryFrame, aContent); + return nsLayoutUtils::GetAfterFrame(aContent); } MOZ_CRASH("Unkown pseudo-element given to " diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index b9778d36d..866fa148b 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -1832,11 +1832,7 @@ nsCSSFrameConstructor::CreateGeneratedContentItem(nsFrameConstructorState& aStat aPseudoElement == CSSPseudoElementType::after, "unexpected aPseudoElement"); - // XXXbz is this ever true? - if (!aParentContent->IsElement()) { - NS_ERROR("Bogus generated content parent"); - return; - } + MOZ_ASSERT(aParentContent->IsElement()); StyleSetHandle styleSet = mPresShell->StyleSet(); @@ -1864,6 +1860,13 @@ nsCSSFrameConstructor::CreateGeneratedContentItem(nsFrameConstructorState& aStat nsresult rv = NS_NewXMLElement(getter_AddRefs(container), nodeInfo.forget()); if (NS_FAILED(rv)) return; + + // Cleared when the pseudo is unbound from the tree, so no need to store a + // strong reference, nor a destructor. + nsIAtom* property = isBefore + ? nsGkAtoms::beforePseudoProperty : nsGkAtoms::afterPseudoProperty; + aParentContent->SetProperty(property, container.get()); + container->SetIsNativeAnonymousRoot(); container->SetPseudoElementType(aPseudoElement); @@ -6079,6 +6082,9 @@ AddGenConPseudoToFrame(nsIFrame* aOwnerFrame, nsIContent* aContent) NS_ASSERTION(nsLayoutUtils::IsFirstContinuationOrIBSplitSibling(aOwnerFrame), "property should only be set on first continuation/ib-sibling"); + // FIXME(emilio): Remove this property, and use the frame of the generated + // content itself to tear the content down? It should be quite simpler. + nsIFrame::ContentArray* value = aOwnerFrame->GetProperty(nsIFrame::GenConProperty()); if (!value) { @@ -6340,7 +6346,7 @@ AdjustAppendParentForAfterContent(nsFrameManager* aFrameManager, // frames to find the first one that is either a ::after frame for an // ancestor of aChild or a frame that is for a node later in the // document than aChild and return that in aAfterFrame. - if (aParentFrame->GetGenConPseudos() || + if (aParentFrame->GetProperty(nsIFrame::GenConProperty()) || nsLayoutUtils::HasPseudoStyle(aContainer, aParentFrame->StyleContext(), CSSPseudoElementType::after, aParentFrame->PresContext()) || @@ -6669,9 +6675,8 @@ nsCSSFrameConstructor::FindFrameForContentSibling(nsIContent* aContent, nsIFrame* sibling = aContent->GetPrimaryFrame(); if (!sibling && GetDisplayContentsStyleFor(aContent)) { // A display:contents node - check if it has a ::before / ::after frame... - sibling = aPrevSibling ? - nsLayoutUtils::GetAfterFrameForContent(aParentFrame, aContent) : - nsLayoutUtils::GetBeforeFrameForContent(aParentFrame, aContent); + sibling = aPrevSibling ? nsLayoutUtils::GetAfterFrame(aContent) + : nsLayoutUtils::GetBeforeFrame(aContent); if (!sibling) { // ... then recurse into children ... const bool forward = !aPrevSibling; @@ -6682,9 +6687,8 @@ nsCSSFrameConstructor::FindFrameForContentSibling(nsIContent* aContent, } if (!sibling) { // ... then ::after / ::before on the opposite end. - sibling = aPrevSibling ? - nsLayoutUtils::GetBeforeFrameForContent(aParentFrame, aContent) : - nsLayoutUtils::GetAfterFrameForContent(aParentFrame, aContent); + sibling = aPrevSibling ? nsLayoutUtils::GetAfterFrame(aContent) + : nsLayoutUtils::GetBeforeFrame(aContent); } if (!sibling) { return nullptr; @@ -8291,26 +8295,23 @@ nsCSSFrameConstructor::ContentRemoved(nsIContent* aContainer, MOZ_ASSERT(!childFrame || !GetDisplayContentsStyleFor(aChild), "display:contents nodes shouldn't have a frame"); if (!childFrame && GetDisplayContentsStyleFor(aChild)) { - nsIFrame* ancestorFrame = nullptr; nsIContent* ancestor = aContainer; - for (; ancestor; ancestor = ancestor->GetParent()) { - ancestorFrame = ancestor->GetPrimaryFrame(); - if (ancestorFrame) { - break; - } + while (!ancestor->GetPrimaryFrame()) { + // FIXME(emilio): Should this use the flattened tree parent instead? + ancestor = ancestor->GetParent(); + MOZ_ASSERT(ancestor, "we can't have a display: contents subtree root!"); } - if (ancestorFrame) { - nsIFrame* contentInsertion = ancestorFrame->GetContentInsertionFrame(); - if (ancestorFrame->GetGenConPseudos() || - (contentInsertion && contentInsertion->GetGenConPseudos())) { - *aDidReconstruct = true; - LAYOUT_PHASE_TEMP_EXIT(); - // XXXmats Can we recreate frames only for the ::after/::before content? - // XXX Perhaps even only those that belong to the aChild sub-tree? - RecreateFramesForContent(ancestor, false, aFlags, aDestroyedFramesFor); - LAYOUT_PHASE_TEMP_REENTER(); - return; - } + + nsIFrame* ancestorFrame = ancestor->GetPrimaryFrame(); + if (ancestorFrame->GetProperty(nsIFrame::GenConProperty())) { + *aDidReconstruct = true; + LAYOUT_PHASE_TEMP_EXIT(); + + // XXXmats Can we recreate frames only for the ::after/::before content? + // XXX Perhaps even only those that belong to the aChild sub-tree? + RecreateFramesForContent(ancestor, false, aFlags, aDestroyedFramesFor); + LAYOUT_PHASE_TEMP_REENTER(); + return; } FlattenedChildIterator iter(aChild); diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 21d20c69f..de3482246 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -1486,90 +1486,38 @@ nsLayoutUtils::GetChildListNameFor(nsIFrame* aChildFrame) return id; } -/*static*/ nsIFrame* -nsLayoutUtils::GetBeforeFrameForContent(nsIFrame* aFrame, - const nsIContent* aContent) -{ - // We need to call GetGenConPseudos() on the first continuation/ib-split. - // Find it, for symmetry with GetAfterFrameForContent. - nsContainerFrame* genConParentFrame = - FirstContinuationOrIBSplitSibling(aFrame)->GetContentInsertionFrame(); - if (!genConParentFrame) { - return nullptr; - } - nsTArray<nsIContent*>* prop = genConParentFrame->GetGenConPseudos(); - if (prop) { - const nsTArray<nsIContent*>& pseudos(*prop); - for (uint32_t i = 0; i < pseudos.Length(); ++i) { - if (pseudos[i]->GetParent() == aContent && - pseudos[i]->NodeInfo()->NameAtom() == nsGkAtoms::mozgeneratedcontentbefore) { - return pseudos[i]->GetPrimaryFrame(); - } - } - } - // If the first child frame is a pseudo-frame, then try that. - // Note that the frame we create for the generated content is also a - // pseudo-frame and so don't drill down in that case. - nsIFrame* childFrame = genConParentFrame->PrincipalChildList().FirstChild(); - if (childFrame && - childFrame->IsPseudoFrame(aContent) && - !childFrame->IsGeneratedContentFrame()) { - return GetBeforeFrameForContent(childFrame, aContent); - } - return nullptr; +static Element* +GetPseudo(const nsIContent* aContent, nsIAtom* aPseudoProperty) +{ + MOZ_ASSERT(aPseudoProperty == nsGkAtoms::beforePseudoProperty || + aPseudoProperty == nsGkAtoms::afterPseudoProperty); + return static_cast<Element*>(aContent->GetProperty(aPseudoProperty)); } -/*static*/ nsIFrame* -nsLayoutUtils::GetBeforeFrame(nsIFrame* aFrame) +/*static*/ Element* +nsLayoutUtils::GetBeforePseudo(const nsIContent* aContent) { - return GetBeforeFrameForContent(aFrame, aFrame->GetContent()); + return GetPseudo(aContent, nsGkAtoms::beforePseudoProperty); } /*static*/ nsIFrame* -nsLayoutUtils::GetAfterFrameForContent(nsIFrame* aFrame, - const nsIContent* aContent) -{ - // We need to call GetGenConPseudos() on the first continuation, - // but callers are likely to pass the last. - nsContainerFrame* genConParentFrame = - FirstContinuationOrIBSplitSibling(aFrame)->GetContentInsertionFrame(); - if (!genConParentFrame) { - return nullptr; - } - nsTArray<nsIContent*>* prop = genConParentFrame->GetGenConPseudos(); - if (prop) { - const nsTArray<nsIContent*>& pseudos(*prop); - for (uint32_t i = 0; i < pseudos.Length(); ++i) { - if (pseudos[i]->GetParent() == aContent && - pseudos[i]->NodeInfo()->NameAtom() == nsGkAtoms::mozgeneratedcontentafter) { - return pseudos[i]->GetPrimaryFrame(); - } - } - } - // If the last child frame is a pseudo-frame, then try that. - // Note that the frame we create for the generated content is also a - // pseudo-frame and so don't drill down in that case. - genConParentFrame = aFrame->GetContentInsertionFrame(); - if (!genConParentFrame) { - return nullptr; - } - nsIFrame* lastParentContinuation = - LastContinuationWithChild(static_cast<nsContainerFrame*>( - LastContinuationOrIBSplitSibling(genConParentFrame))); - nsIFrame* childFrame = - lastParentContinuation->GetChildList(nsIFrame::kPrincipalList).LastChild(); - if (childFrame && - childFrame->IsPseudoFrame(aContent) && - !childFrame->IsGeneratedContentFrame()) { - return GetAfterFrameForContent(childFrame->FirstContinuation(), aContent); - } - return nullptr; +nsLayoutUtils::GetBeforeFrame(const nsIContent* aContent) +{ + Element* pseudo = GetBeforePseudo(aContent); + return pseudo ? pseudo->GetPrimaryFrame() : nullptr; +} + +/*static*/ Element* +nsLayoutUtils::GetAfterPseudo(const nsIContent* aContent) +{ + return GetPseudo(aContent, nsGkAtoms::afterPseudoProperty); } /*static*/ nsIFrame* -nsLayoutUtils::GetAfterFrame(nsIFrame* aFrame) +nsLayoutUtils::GetAfterFrame(const nsIContent* aContent) { - return GetAfterFrameForContent(aFrame, aFrame->GetContent()); + Element* pseudo = GetAfterPseudo(aContent); + return pseudo ? pseudo->GetPrimaryFrame() : nullptr; } // static diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index bba1f3265..9757cb799 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -276,50 +276,26 @@ public: static mozilla::layout::FrameChildListID GetChildListNameFor(nsIFrame* aChildFrame); /** - * GetBeforeFrameForContent returns the ::before frame for aContent, if - * one exists. This is typically O(1). The frame passed in must be - * the first-in-flow. - * - * @param aGenConParentFrame an ancestor of the ::before frame - * @param aContent the content whose ::before is wanted - * @return the ::before frame or nullptr if there isn't one + * Returns the ::before pseudo-element for aContent, if any. */ - static nsIFrame* GetBeforeFrameForContent(nsIFrame* aGenConParentFrame, - const nsIContent* aContent); + static mozilla::dom::Element* GetBeforePseudo(const nsIContent* aContent); /** - * GetBeforeFrame returns the outermost ::before frame of the given frame, if - * one exists. This is typically O(1). The frame passed in must be - * the first-in-flow. - * - * @param aFrame the frame whose ::before is wanted - * @return the :before frame or nullptr if there isn't one + * Returns the frame corresponding to the ::before pseudo-element for + * aContent, if any. */ - static nsIFrame* GetBeforeFrame(nsIFrame* aFrame); + static nsIFrame* GetBeforeFrame(const nsIContent* aContent); /** - * GetAfterFrameForContent returns the ::after frame for aContent, if one - * exists. This will walk the in-flow chain of aGenConParentFrame to the - * last-in-flow if needed. This function is typically O(N) in the number - * of child frames, following in-flows, etc. - * - * @param aGenConParentFrame an ancestor of the ::after frame - * @param aContent the content whose ::after is wanted - * @return the ::after frame or nullptr if there isn't one + * Returns the ::after pseudo-element for aContent, if any. */ - static nsIFrame* GetAfterFrameForContent(nsIFrame* aGenConParentFrame, - const nsIContent* aContent); + static mozilla::dom::Element* GetAfterPseudo(const nsIContent* aContent); /** - * GetAfterFrame returns the outermost ::after frame of the given frame, if one - * exists. This will walk the in-flow chain to the last-in-flow if - * needed. This function is typically O(N) in the number of child - * frames, following in-flows, etc. - * - * @param aFrame the frame whose ::after is wanted - * @return the :after frame or nullptr if there isn't one + * Returns the frame corresponding to the ::after pseudo-element for aContent, + * if any. */ - static nsIFrame* GetAfterFrame(nsIFrame* aFrame); + static nsIFrame* GetAfterFrame(const nsIContent* aContent); /** * Given a frame, search up the frame tree until we find an |