summaryrefslogtreecommitdiff
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
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
-rw-r--r--devtools/client/inspector/computed/test/browser_computed_pseudo-element_01.js4
-rw-r--r--dom/animation/EffectCompositor.cpp22
-rw-r--r--dom/animation/KeyframeEffectReadOnly.cpp12
-rw-r--r--dom/base/ChildIterator.cpp73
-rw-r--r--dom/base/nsDOMWindowUtils.cpp6
-rw-r--r--dom/base/nsGkAtomList.h2
-rw-r--r--dom/xml/nsXMLElement.cpp23
-rw-r--r--dom/xml/nsXMLElement.h3
-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
16 files changed, 188 insertions, 241 deletions
diff --git a/devtools/client/inspector/computed/test/browser_computed_pseudo-element_01.js b/devtools/client/inspector/computed/test/browser_computed_pseudo-element_01.js
index 9ca5451a56..7c18bfe7bf 100644
--- a/devtools/client/inspector/computed/test/browser_computed_pseudo-element_01.js
+++ b/devtools/client/inspector/computed/test/browser_computed_pseudo-element_01.js
@@ -33,7 +33,7 @@ function* testTopLeft(inspector, view) {
let afterElement = children.nodes[children.nodes.length - 1];
yield selectNode(afterElement, inspector);
top = getComputedViewPropertyValue(view, "top");
- is(top, "50%", "The computed view shows the correct top");
+ is(top, "96px", "The computed view shows the correct top");
left = getComputedViewPropertyValue(view, "left");
- is(left, "50%", "The computed view shows the correct left");
+ is(left, "96px", "The computed view shows the correct left");
}
diff --git a/dom/animation/EffectCompositor.cpp b/dom/animation/EffectCompositor.cpp
index c88cabe90b..49da3c0332 100644
--- a/dom/animation/EffectCompositor.cpp
+++ b/dom/animation/EffectCompositor.cpp
@@ -356,24 +356,16 @@ EffectCompositor::GetElementToRestyle(dom::Element* aElement,
return aElement;
}
- nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
- if (!primaryFrame) {
- return nullptr;
- }
- nsIFrame* pseudoFrame;
if (aPseudoType == CSSPseudoElementType::before) {
- pseudoFrame = nsLayoutUtils::GetBeforeFrame(primaryFrame);
- } else if (aPseudoType == CSSPseudoElementType::after) {
- pseudoFrame = nsLayoutUtils::GetAfterFrame(primaryFrame);
- } else {
- NS_NOTREACHED("Should not try to get the element to restyle for a pseudo "
- "other that :before or :after");
- return nullptr;
+ return nsLayoutUtils::GetBeforePseudo(aElement);
}
- if (!pseudoFrame) {
- return nullptr;
+ if (aPseudoType == CSSPseudoElementType::after) {
+ return nsLayoutUtils::GetAfterPseudo(aElement);
}
- return pseudoFrame->GetContent()->AsElement();
+
+ NS_NOTREACHED("Should not try to get the element to restyle for a pseudo "
+ "other that :before or :after");
+ return nullptr;
}
bool
diff --git a/dom/animation/KeyframeEffectReadOnly.cpp b/dom/animation/KeyframeEffectReadOnly.cpp
index 639e0b2b0d..164ee02925 100644
--- a/dom/animation/KeyframeEffectReadOnly.cpp
+++ b/dom/animation/KeyframeEffectReadOnly.cpp
@@ -1105,19 +1105,17 @@ KeyframeEffectReadOnly::GetAnimationFrame() const
return nullptr;
}
- nsIFrame* frame = mTarget->mElement->GetPrimaryFrame();
- if (!frame) {
- return nullptr;
- }
-
+ nsIFrame* frame;
if (mTarget->mPseudoType == CSSPseudoElementType::before) {
- frame = nsLayoutUtils::GetBeforeFrame(frame);
+ frame = nsLayoutUtils::GetBeforeFrame(mTarget->mElement);
} else if (mTarget->mPseudoType == CSSPseudoElementType::after) {
- frame = nsLayoutUtils::GetAfterFrame(frame);
+ frame = nsLayoutUtils::GetAfterFrame(mTarget->mElement);
} else {
+ frame = mTarget->mElement->GetPrimaryFrame();
MOZ_ASSERT(mTarget->mPseudoType == CSSPseudoElementType::NotPseudo,
"unknown mTarget->mPseudoType");
}
+
if (!frame) {
return nullptr;
}
diff --git a/dom/base/ChildIterator.cpp b/dom/base/ChildIterator.cpp
index fb07e9a210..23b919516b 100644
--- a/dom/base/ChildIterator.cpp
+++ b/dom/base/ChildIterator.cpp
@@ -317,11 +317,9 @@ AllChildrenIterator::Get() const
{
switch (mPhase) {
case eAtBeforeKid: {
- nsIFrame* frame = mOriginalContent->GetPrimaryFrame();
- MOZ_ASSERT(frame, "No frame at eAtBeforeKid phase");
- nsIFrame* beforeFrame = nsLayoutUtils::GetBeforeFrame(frame);
- MOZ_ASSERT(beforeFrame, "No content before frame at eAtBeforeKid phase");
- return beforeFrame->GetContent();
+ Element* before = nsLayoutUtils::GetBeforePseudo(mOriginalContent);
+ MOZ_ASSERT(before, "No content before frame at eAtBeforeKid phase");
+ return before;
}
case eAtExplicitKids:
@@ -331,11 +329,9 @@ AllChildrenIterator::Get() const
return mAnonKids[mAnonKidsIdx];
case eAtAfterKid: {
- nsIFrame* frame = mOriginalContent->GetPrimaryFrame();
- MOZ_ASSERT(frame, "No frame at eAtAfterKid phase");
- nsIFrame* afterFrame = nsLayoutUtils::GetAfterFrame(frame);
- MOZ_ASSERT(afterFrame, "No content before frame at eAtBeforeKid phase");
- return afterFrame->GetContent();
+ Element* after = nsLayoutUtils::GetAfterPseudo(mOriginalContent);
+ MOZ_ASSERT(after, "No content after frame at eAtAfterKid phase");
+ return after;
}
default:
@@ -349,15 +345,10 @@ AllChildrenIterator::Seek(nsIContent* aChildToFind)
{
if (mPhase == eAtBegin || mPhase == eAtBeforeKid) {
mPhase = eAtExplicitKids;
- nsIFrame* frame = mOriginalContent->GetPrimaryFrame();
- if (frame) {
- nsIFrame* beforeFrame = nsLayoutUtils::GetBeforeFrame(frame);
- if (beforeFrame) {
- if (beforeFrame->GetContent() == aChildToFind) {
- mPhase = eAtBeforeKid;
- return true;
- }
- }
+ Element* beforePseudo = nsLayoutUtils::GetBeforePseudo(mOriginalContent);
+ if (beforePseudo && beforePseudo == aChildToFind) {
+ mPhase = eAtBeforeKid;
+ return true;
}
}
@@ -404,13 +395,10 @@ AllChildrenIterator::GetNextChild()
{
if (mPhase == eAtBegin) {
mPhase = eAtExplicitKids;
- nsIFrame* frame = mOriginalContent->GetPrimaryFrame();
- if (frame) {
- nsIFrame* beforeFrame = nsLayoutUtils::GetBeforeFrame(frame);
- if (beforeFrame) {
- mPhase = eAtBeforeKid;
- return beforeFrame->GetContent();
- }
+ Element* beforeContent = nsLayoutUtils::GetBeforePseudo(mOriginalContent);
+ if (beforeContent) {
+ mPhase = eAtBeforeKid;
+ return beforeContent;
}
}
@@ -446,13 +434,10 @@ AllChildrenIterator::GetNextChild()
return mAnonKids[mAnonKidsIdx];
}
- nsIFrame* frame = mOriginalContent->GetPrimaryFrame();
- if (frame) {
- nsIFrame* afterFrame = nsLayoutUtils::GetAfterFrame(frame);
- if (afterFrame) {
- mPhase = eAtAfterKid;
- return afterFrame->GetContent();
- }
+ Element* afterContent = nsLayoutUtils::GetAfterPseudo(mOriginalContent);
+ if (afterContent) {
+ mPhase = eAtAfterKid;
+ return afterContent;
}
}
@@ -466,13 +451,10 @@ AllChildrenIterator::GetPreviousChild()
if (mPhase == eAtEnd) {
MOZ_ASSERT(mAnonKidsIdx == mAnonKids.Length());
mPhase = eAtAnonKids;
- nsIFrame* frame = mOriginalContent->GetPrimaryFrame();
- if (frame) {
- nsIFrame* afterFrame = nsLayoutUtils::GetAfterFrame(frame);
- if (afterFrame) {
- mPhase = eAtAfterKid;
- return afterFrame->GetContent();
- }
+ Element* afterContent = nsLayoutUtils::GetAfterPseudo(mOriginalContent);
+ if (afterContent) {
+ mPhase = eAtAfterKid;
+ return afterContent;
}
}
@@ -501,13 +483,10 @@ AllChildrenIterator::GetPreviousChild()
return kid;
}
- nsIFrame* frame = mOriginalContent->GetPrimaryFrame();
- if (frame) {
- nsIFrame* beforeFrame = nsLayoutUtils::GetBeforeFrame(frame);
- if (beforeFrame) {
- mPhase = eAtBeforeKid;
- return beforeFrame->GetContent();
- }
+ Element* beforeContent = nsLayoutUtils::GetBeforePseudo(mOriginalContent);
+ if (beforeContent) {
+ mPhase = eAtBeforeKid;
+ return beforeContent;
}
}
diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp
index 2ab5937ace..3f5b54c942 100644
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -3657,11 +3657,11 @@ nsDOMWindowUtils::GetOMTAStyle(nsIDOMElement* aElement,
RefPtr<nsROCSSPrimitiveValue> cssValue = nullptr;
nsIFrame* frame = element->GetPrimaryFrame();
- if (frame && !aPseudoElement.IsEmpty()) {
+ if (!aPseudoElement.IsEmpty()) {
if (aPseudoElement.EqualsLiteral("::before")) {
- frame = nsLayoutUtils::GetBeforeFrame(frame);
+ frame = nsLayoutUtils::GetBeforeFrame(element);
} else if (aPseudoElement.EqualsLiteral("::after")) {
- frame = nsLayoutUtils::GetAfterFrame(frame);
+ frame = nsLayoutUtils::GetAfterFrame(element);
} else {
return NS_ERROR_INVALID_ARG;
}
diff --git a/dom/base/nsGkAtomList.h b/dom/base/nsGkAtomList.h
index c7c1197b1a..2f0e30387c 100644
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -2144,12 +2144,14 @@ GK_ATOM(ongamepaddisconnected, "ongamepaddisconnected")
#endif
// Content property names
+GK_ATOM(afterPseudoProperty, "afterPseudoProperty") // nsXMLElement*
GK_ATOM(animationsProperty, "AnimationsProperty") // FrameAnimations*
GK_ATOM(animationsOfBeforeProperty, "AnimationsOfBeforeProperty") // FrameAnimations*
GK_ATOM(animationsOfAfterProperty, "AnimationsOfAfterProperty") // FrameAnimations*
GK_ATOM(animationEffectsProperty, "AnimationEffectsProperty") // EffectSet*
GK_ATOM(animationEffectsForBeforeProperty, "AnimationsEffectsForBeforeProperty") // EffectSet*
GK_ATOM(animationEffectsForAfterProperty, "AnimationsEffectsForAfterProperty") // EffectSet*
+GK_ATOM(beforePseudoProperty, "beforePseudoProperty") // nsXMLElement*
GK_ATOM(cssPseudoElementBeforeProperty, "CSSPseudoElementBeforeProperty") // CSSPseudoElement*
GK_ATOM(cssPseudoElementAfterProperty, "CSSPseudoElementAfterProperty") // CSSPseudoElement*
GK_ATOM(transitionsProperty, "TransitionsProperty") // FrameTransitions*
diff --git a/dom/xml/nsXMLElement.cpp b/dom/xml/nsXMLElement.cpp
index 45163a1c42..ad0314f4ca 100644
--- a/dom/xml/nsXMLElement.cpp
+++ b/dom/xml/nsXMLElement.cpp
@@ -9,6 +9,7 @@
#include "mozilla/dom/ElementInlines.h"
#include "nsContentUtils.h" // nsAutoScriptBlocker
+using namespace mozilla;
using namespace mozilla::dom;
nsresult
@@ -29,4 +30,26 @@ nsXMLElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
return ElementBinding::Wrap(aCx, this, aGivenProto);
}
+void
+nsXMLElement::UnbindFromTree(bool aDeep, bool aNullParent)
+{
+ CSSPseudoElementType pseudoType = GetPseudoElementType();
+ bool isBefore = pseudoType == CSSPseudoElementType::before;
+ nsIAtom* property = isBefore
+ ? nsGkAtoms::beforePseudoProperty : nsGkAtoms::afterPseudoProperty;
+
+ switch (pseudoType) {
+ case CSSPseudoElementType::before:
+ case CSSPseudoElementType::after: {
+ MOZ_ASSERT(GetParent());
+ MOZ_ASSERT(GetParent()->IsElement());
+ GetParent()->DeleteProperty(property);
+ break;
+ }
+ default:
+ break;
+ }
+ Element::UnbindFromTree(aDeep, aNullParent);
+}
+
NS_IMPL_ELEMENT_CLONE(nsXMLElement)
diff --git a/dom/xml/nsXMLElement.h b/dom/xml/nsXMLElement.h
index d16b82f399..85f79970cd 100644
--- a/dom/xml/nsXMLElement.h
+++ b/dom/xml/nsXMLElement.h
@@ -35,6 +35,9 @@ public:
virtual nsIDOMNode* AsDOMNode() override { return this; }
+ virtual void UnbindFromTree(bool aDeep = true,
+ bool aNullParent = true) override;
+
protected:
virtual ~nsXMLElement() {}
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