summaryrefslogtreecommitdiff
path: root/layout
diff options
context:
space:
mode:
authorMatt A. Tobin <email@mattatobin.com>2020-04-17 05:10:25 -0400
committerMatt A. Tobin <email@mattatobin.com>2020-04-17 05:10:25 -0400
commit7614fdb51b177e6975fce5bf9a7facef170e61aa (patch)
tree598e187ce71ae82b300a3a6b6b2f199aa2f3c43d /layout
parent5f297c5f57583b0f9d27d714beb285919f42d655 (diff)
downloaduxp-7614fdb51b177e6975fce5bf9a7facef170e61aa.tar.gz
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')
-rw-r--r--layout/base/RestyleManager.cpp4
-rw-r--r--layout/base/ServoRestyleManager.cpp13
-rw-r--r--layout/base/nsCSSFrameConstructor.cpp61
-rw-r--r--layout/base/nsLayoutUtils.cpp96
-rw-r--r--layout/base/nsLayoutUtils.h44
-rw-r--r--layout/generic/nsFrame.cpp15
-rw-r--r--layout/generic/nsIFrame.h4
-rw-r--r--layout/style/nsComputedDOMStyle.cpp47
8 files changed, 117 insertions, 167 deletions
diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp
index 4d08dee067..8db3d93020 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 9624c678be..0659ab8571 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 b9778d36de..866fa148bf 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 21d20c69fa..de34822460 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 bba1f32652..9757cb7999 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
diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp
index 71f6172bd5..25b685fb28 100644
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -10008,19 +10008,16 @@ nsIFrame::IsPseudoStackingContextFromStyle() {
Element*
nsIFrame::GetPseudoElement(CSSPseudoElementType aType)
{
- nsIFrame* frame = nullptr;
+ if (!mContent) {
+ return nullptr;
+ }
if (aType == CSSPseudoElementType::before) {
- frame = nsLayoutUtils::GetBeforeFrame(this);
- } else if (aType == CSSPseudoElementType::after) {
- frame = nsLayoutUtils::GetAfterFrame(this);
+ return nsLayoutUtils::GetBeforePseudo(mContent);
}
- if (frame) {
- nsIContent* content = frame->GetContent();
- if (content->IsElement()) {
- return content->AsElement();
- }
+ if (aType == CSSPseudoElementType::after) {
+ return nsLayoutUtils::GetAfterPseudo(mContent);
}
return nullptr;
diff --git a/layout/generic/nsIFrame.h b/layout/generic/nsIFrame.h
index 3137aaee27..9372b15a2a 100644
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -1062,10 +1062,6 @@ public:
return GetBidiData().embeddingLevel;
}
- nsTArray<nsIContent*>* GetGenConPseudos() {
- return GetProperty(GenConProperty());
- }
-
/**
* Return the distance between the border edge of the frame and the
* margin edge of the frame. Like GetRect(), returns the dimensions
diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp
index 102e0df18c..20e5651bd4 100644
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -638,6 +638,34 @@ nsComputedDOMStyle::SetFrameStyleContext(nsStyleContext* aContext)
mStyleContext = aContext;
}
+/**
+ * The following function checks whether we need to explicitly resolve the style
+ * again, even though we have a style context coming from the frame.
+ *
+ * This basically checks whether the style is or may be under a ::first-line or
+ * ::first-letter frame, in which case we can't return the frame style, and we
+ * need to resolve it. See bug 505515.
+ */
+static bool
+MustReresolveStyle(const nsStyleContext* aContext)
+{
+ MOZ_ASSERT(aContext);
+
+ if (aContext->HasPseudoElementData()) {
+ if (!aContext->GetPseudo() ||
+ aContext->StyleSource().IsServoComputedValues()) {
+ // TODO(emilio): When ::first-line is supported in Servo, we may want to
+ // fix this to avoid re-resolving pseudo-element styles.
+ return true;
+ }
+
+ return aContext->GetParent() &&
+ aContext->GetParent()->HasPseudoElementData();
+ }
+
+ return false;
+}
+
void
nsComputedDOMStyle::UpdateCurrentStyleSources(bool aNeedsLayoutFlush)
{
@@ -690,9 +718,20 @@ nsComputedDOMStyle::UpdateCurrentStyleSources(bool aNeedsLayoutFlush)
// XXX the !mElement->IsHTMLElement(nsGkAtoms::area)
// check is needed due to bug 135040 (to avoid using
// mPrimaryFrame). Remove it once that's fixed.
- if (!mPseudo && mStyleType == eAll &&
- !mElement->IsHTMLElement(nsGkAtoms::area)) {
- mOuterFrame = mElement->GetPrimaryFrame();
+ if (mStyleType == eAll && !mElement->IsHTMLElement(nsGkAtoms::area)) {
+ mOuterFrame = nullptr;
+
+ if (!mPseudo) {
+ mOuterFrame = mElement->GetPrimaryFrame();
+ } else if (mPseudo == nsCSSPseudoElements::before ||
+ mPseudo == nsCSSPseudoElements::after) {
+ nsIAtom* property = mPseudo == nsCSSPseudoElements::before
+ ? nsGkAtoms::beforePseudoProperty
+ : nsGkAtoms::afterPseudoProperty;
+
+ auto* pseudo = static_cast<Element*>(mElement->GetProperty(property));
+ mOuterFrame = pseudo ? pseudo->GetPrimaryFrame() : nullptr;
+ }
mInnerFrame = mOuterFrame;
if (mOuterFrame) {
nsIAtom* type = mOuterFrame->GetType();
@@ -711,7 +750,7 @@ nsComputedDOMStyle::UpdateCurrentStyleSources(bool aNeedsLayoutFlush)
}
}
- if (!mStyleContext || mStyleContext->HasPseudoElementData()) {
+ if (!mStyleContext || MustReresolveStyle(mStyleContext)) {
#ifdef DEBUG
if (mStyleContext) {
// We want to check that going through this path because of