Bug 1331322 - Allow tagging of pseudo-implementing native anonymous content with the pseudo type at creation time

* Stop using a node bit for HasExplicitBaseURI
* Move MAY_HAVE_CLASS to mBoolFlags
* Add a flag to indicate that a node is native anonymous content
* Allow tagging of pseudo-implementing native anonymous content with the pseudo type at creation time, and eliminate explicit style contexts in nsIAnonymousContentCreator::ContentInfo

Tag #1375
This commit is contained in:
Matt A. Tobin 2020-04-16 16:37:28 -04:00 committed by Roy Tam
commit b723a5c8c4
25 changed files with 267 additions and 128 deletions

View file

@ -1865,6 +1865,7 @@ nsCSSFrameConstructor::CreateGeneratedContentItem(nsFrameConstructorState& aStat
if (NS_FAILED(rv))
return;
container->SetIsNativeAnonymousRoot();
container->SetPseudoElementType(aPseudoElement);
// If the parent is in a shadow tree, make sure we don't
// bind with a document because shadow roots and its descendants
@ -4161,6 +4162,13 @@ ConnectAnonymousTreeDescendants(nsIContent* aParent,
}
}
void SetNativeAnonymousBitOnDescendants(nsIContent *aRoot)
{
for (nsIContent* curr = aRoot; curr; curr = curr->GetNextNode(aRoot)) {
curr->SetFlags(NODE_IS_NATIVE_ANONYMOUS);
}
}
nsresult
nsCSSFrameConstructor::GetAnonymousContent(nsIContent* aParent,
nsIFrame* aParentFrame,
@ -4182,16 +4190,17 @@ nsCSSFrameConstructor::GetAnonymousContent(nsIContent* aParent,
nsIContent* content = aContent[i].mContent;
NS_ASSERTION(content, "null anonymous content?");
ConnectAnonymousTreeDescendants(content, aContent[i].mChildren);
// least-surprise CSS binding until we do the SVG specified
// cascading rules for <svg:use> - bug 265894
if (aParentFrame->GetType() == nsGkAtoms::svgUseFrame) {
content->SetFlags(NODE_IS_ANONYMOUS_ROOT);
} else {
content->SetIsNativeAnonymousRoot();
SetNativeAnonymousBitOnDescendants(content);
}
ConnectAnonymousTreeDescendants(content, aContent[i].mChildren);
bool anonContentIsEditable = content->HasFlag(NODE_IS_EDITABLE);
// If the parent is in a shadow tree, make sure we don't
@ -4216,11 +4225,9 @@ nsCSSFrameConstructor::GetAnonymousContent(nsIContent* aParent,
}
if (ServoStyleSet* styleSet = mPresShell->StyleSet()->GetAsServo()) {
// Eagerly compute styles for the anonymous content tree, but only do so
// if the content doesn't have an explicit style context (if it does, we
// don't need the normal computed values).
// Eagerly compute styles for the anonymous content tree.
for (auto& info : aContent) {
if (!info.mStyleContext) {
if (info.mContent->IsElement()) {
styleSet->StyleNewSubtree(info.mContent);
}
}
@ -4999,22 +5006,35 @@ nsCSSFrameConstructor::ResolveStyleContext(const InsertionPoint& aInsertion,
already_AddRefed<nsStyleContext>
nsCSSFrameConstructor::ResolveStyleContext(nsStyleContext* aParentStyleContext,
nsIContent* aContent,
nsFrameConstructorState* aState)
nsFrameConstructorState* aState,
Element* aOriginatingElementOrNull)
{
StyleSetHandle styleSet = mPresShell->StyleSet();
aContent->OwnerDoc()->FlushPendingLinkUpdates();
RefPtr<nsStyleContext> result;
if (aContent->IsElement()) {
if (aState) {
result = styleSet->ResolveStyleFor(aContent->AsElement(),
aParentStyleContext,
aState->mTreeMatchContext);
auto pseudoType = aContent->AsElement()->GetPseudoElementType();
if (pseudoType == CSSPseudoElementType::NotPseudo) {
MOZ_ASSERT(!aOriginatingElementOrNull);
if (aState) {
result = styleSet->ResolveStyleFor(aContent->AsElement(),
aParentStyleContext,
aState->mTreeMatchContext);
} else {
result = styleSet->ResolveStyleFor(aContent->AsElement(),
aParentStyleContext);
}
} else {
result = styleSet->ResolveStyleFor(aContent->AsElement(),
aParentStyleContext);
MOZ_ASSERT(aOriginatingElementOrNull);
MOZ_ASSERT(aContent->IsInNativeAnonymousSubtree());
result = styleSet->ResolvePseudoElementStyle(aOriginatingElementOrNull,
pseudoType,
aParentStyleContext,
aContent->AsElement());
}
} else {
MOZ_ASSERT(!aOriginatingElementOrNull);
NS_ASSERTION(aContent->IsNodeOfType(nsINode::eTEXT),
"shouldn't waste time creating style contexts for "
"comments and processing instructions");
@ -10643,24 +10663,96 @@ nsCSSFrameConstructor::AddFCItemsForAnonymousContent(
RefPtr<nsStyleContext> styleContext;
TreeMatchContext::AutoParentDisplayBasedStyleFixupSkipper
parentDisplayBasedStyleFixupSkipper(aState.mTreeMatchContext);
if (aAnonymousItems[i].mStyleContext) {
// If we have an explicit style context, that means that the anonymous
// content creator had its own plan for the style, and doesn't need the
// computed style obtained by cascading this content as a normal node.
// This happens when a native anonymous node is used to implement a
// pseudo-element. Allowing Servo to traverse these nodes would be wasted
// work, so assert that we didn't do that.
MOZ_ASSERT_IF(content->IsStyledByServo(), !content->HasServoData());
styleContext = aAnonymousItems[i].mStyleContext.forget();
} else {
// If we don't have an explicit style context, that means we need the
// ordinary computed values. Make sure we eagerly cascaded them when the
// anonymous nodes were created.
MOZ_ASSERT_IF(content->IsStyledByServo() && content->IsElement(),
content->HasServoData());
styleContext = ResolveStyleContext(aFrame, content, &aState);
// Make sure we eagerly performed the servo cascade when the anonymous
// nodes were created.
MOZ_ASSERT_IF(content->IsStyledByServo() && content->IsElement(),
content->AsElement()->HasServoData());
// Determine whether this NAC is pseudo-implementing.
nsIAtom* pseudo = nullptr;
if (content->IsElement()) {
auto pseudoType = content->AsElement()->GetPseudoElementType();
if (pseudoType != CSSPseudoElementType::NotPseudo) {
pseudo = nsCSSPseudoElements::GetPseudoAtom(pseudoType);
}
}
// Determine the appropriate parent style for this NAC, and if the NAC
// implements a pseudo-element, the appropriate originating element
// (that is to say, the element to the left of the ::pseudo-element in
// the selector). This is all rather tricky, and merits some discussion.
//
// First, it's important to note that author stylesheets generally do not
// apply to elements in native-anonymous subtrees. The exceptions to
// this are web-exposed pseudo-elements, where authors can style the
// pseudo-implementing NAC if the originating element is not itself in a NAC
// subtree.
//
// For this reason, it's very important that we avoid using a style parent
// that is inside a NAC subtree together with an originating element that
// is not inside a NAC subtree, since that would allow authors to
// explicitly inherit styles from internal elements, potentially making
// the NAC hierarchy observable. To ensure this, and generally simplify
// things, we always set the originating element to the style parent.
//
// As a consequence of the above, all web-exposed pseudo-elements (which,
// by definition, must have a content-accessible originating element) must
// also inherit style from that same content-accessible element. To avoid
// unintuitive behavior differences between NAC elements that do and don't
// correspond to web-exposed pseudo-elements, we follow this protocol for
// all NAC, pseudo-implementing or not.
//
// However, things get tricky with the <video> element, where we have a
// bunch of XBL-generated anonymous content descending from a native-
// anonymous XULElement. The XBL elements inherit style from their
// flattened tree parent, because that's how XBL works. But then we need
// to figure out what to do when one of those anonymous XBL elements
// (like an <input> element) generates its own (possibly pseudo-element-
// implementing) NAC.
//
// In this case, we inherit style from the XBL-generated NAC-creating
// element, rather than the <video> element. There are a number of good
// reasons for this. First, inheriting from the great-grandparent while
// the parent inherits from the grandparent would be bizarre at best.
// Second, exposing pseudo-elements from elements within our particular
// XBL implementation would allow content styles to (un)intentionally
// alter the video controls, which would be very bad. Third, our UA
// stylesheets have selectors like:
//
// input[type=range][orient=horizontal]::-moz-range-track
//
// and we need to make sure that the originating element is the <input>,
// not the <video>, because that's where the |orient| attribute lives.
//
// The upshot of all of this is that, to find the style parent (and
// originating element, if applicable), we walk up our parent chain to the
// first element that is not itself NAC (distinct from whether it happens
// to be in a NAC subtree).
//
// To implement all this, we need to pass the correct parent style context
// here because SetPrimaryFrame() may not have been called on the content
// yet and thus ResolveStyleContext can't find it otherwise.
//
// We don't need to worry about display:contents here, because such
// elements don't get a frame and thus can't generate NAC. But we do need
// to worry about anonymous boxes, which CorrectStyleParentFrame handles
// for us.
nsIFrame* inheritFrame = aFrame;
while (inheritFrame->GetContent()->IsNativeAnonymous()) {
inheritFrame = inheritFrame->GetParent();
}
if (inheritFrame->GetType() == nsGkAtoms::canvasFrame) {
// CorrectStyleParentFrame returns nullptr if the prospective parent is
// the canvas frame, so avoid calling it in that situation.
} else {
inheritFrame = nsFrame::CorrectStyleParentFrame(inheritFrame, pseudo);
}
Element* originating = pseudo ? inheritFrame->GetContent()->AsElement() : nullptr;
styleContext =
ResolveStyleContext(inheritFrame->StyleContext(), content, &aState, originating);
nsTArray<nsIAnonymousContentCreator::ContentInfo>* anonChildren = nullptr;
if (!aAnonymousItems[i].mChildren.IsEmpty()) {
anonChildren = &aAnonymousItems[i].mChildren;