diff --git a/dom/xbl/nsXBLService.cpp b/dom/xbl/nsXBLService.cpp index c4cb0ec4bdda..8b4aa4aaa47d 100644 --- a/dom/xbl/nsXBLService.cpp +++ b/dom/xbl/nsXBLService.cpp @@ -420,17 +420,7 @@ public: return; } if (ServoStyleSet* servoSet = presShell->StyleSet()->GetAsServo()) { - // In general the element is always styled by the time we're applying XBL - // bindings, because we need to style the element to know what the binding - // URI is. However, programmatic consumers of the XBL service (like the - // XML pretty printer) _can_ apply bindings without having styled the bound - // element. We could assert against this and require the callers manually - // resolve the style first, but it's easy enough to just handle here. - if (MOZ_UNLIKELY(!mElement->HasServoData())) { - servoSet->StyleNewSubtree(mElement); - } else { - servoSet->StyleNewChildren(mElement); - } + servoSet->StyleNewlyBoundElement(mElement); } } diff --git a/layout/style/ServoStyleSet.cpp b/layout/style/ServoStyleSet.cpp index b7324b5df4e9..aedc4a8adc8f 100644 --- a/layout/style/ServoStyleSet.cpp +++ b/layout/style/ServoStyleSet.cpp @@ -374,9 +374,12 @@ ServoStyleSet::PrepareAndTraverseSubtree( aRestyleBehavior == TraversalRestyleBehavior::ForReconstruct; bool forAnimationOnly = aRestyleBehavior == TraversalRestyleBehavior::ForAnimationOnly; + bool forNewlyBoundElement = + aRestyleBehavior == TraversalRestyleBehavior::ForNewlyBoundElement; bool postTraversalRequired = Servo_TraverseSubtree( aRoot, mRawSet.get(), &snapshots, aRootBehavior, aRestyleBehavior); - MOZ_ASSERT(!(isInitial || forReconstruct) || !postTraversalRequired); + MOZ_ASSERT(!(isInitial || forReconstruct || forNewlyBoundElement) || + !postTraversalRequired); // Don't need to trigger a second traversal if this restyle only needs // animation-only restyle. @@ -955,6 +958,34 @@ ServoStyleSet::StyleNewChildren(Element* aParent) // or some of its other children might have pending restyles. } +void +ServoStyleSet::StyleNewlyBoundElement(Element* aElement) +{ + PreTraverse(); + + // In general the element is always styled by the time we're applying XBL + // bindings, because we need to style the element to know what the binding + // URI is. However, programmatic consumers of the XBL service (like the + // XML pretty printer) _can_ apply bindings without having styled the bound + // element. We could assert against this and require the callers manually + // resolve the style first, but it's easy enough to just handle here. + // + // Also, when applying XBL bindings to elements within a display:none or + // unstyled subtree (for example, when elements are wrapped to be + // exposed to JS), we need to tell the traversal that it is OK to + // skip restyling, rather than panic when trying to unwrap the styles + // it expects to have just computed. + + TraversalRootBehavior rootBehavior = + MOZ_UNLIKELY(!aElement->HasServoData()) + ? TraversalRootBehavior::Normal + : TraversalRootBehavior::UnstyledChildrenOnly; + + PrepareAndTraverseSubtree(aElement, + rootBehavior, + TraversalRestyleBehavior::ForNewlyBoundElement); +} + void ServoStyleSet::StyleSubtreeForReconstruct(Element* aRoot) { diff --git a/layout/style/ServoStyleSet.h b/layout/style/ServoStyleSet.h index 94067332f8f2..a599050d1ace 100644 --- a/layout/style/ServoStyleSet.h +++ b/layout/style/ServoStyleSet.h @@ -305,6 +305,14 @@ public: */ void StyleNewChildren(dom::Element* aParent); + /** + * Eagerly styles the children of an element that has just had an XBL + * binding applied to it. Some XBL consumers attach bindings to elements + * that have not been styled yet, and in such cases, this will do the + * equivalent of StyleNewSubtree instead. + */ + void StyleNewlyBoundElement(dom::Element* aElement); + /** * Like StyleNewSubtree, but in response to a request to reconstruct frames * for the given subtree, and so works on elements that already have diff --git a/layout/style/ServoTypes.h b/layout/style/ServoTypes.h index 7d35128d7d9c..d6d7e50dde86 100644 --- a/layout/style/ServoTypes.h +++ b/layout/style/ServoTypes.h @@ -63,6 +63,10 @@ enum class TraversalRootBehavior { enum class TraversalRestyleBehavior { // Normal processing. Normal, + // Normal processing, but tolerant to calls to restyle elements in unstyled + // or display:none subtrees (which can occur when styling elements with + // newly applied XBL bindings). + ForNewlyBoundElement, // Traverses in a mode that doesn't generate any change hints, which is what's // required when handling frame reconstruction. The change hints in this case // are unneeded, since the old frames have already been destroyed.