/* static */ void
ServoRestyleManager::ClearRestyleStateFromSubtree(Element* aElement)
{
  if (aElement->HasDirtyDescendantsForServo()) {
    StyleChildrenIterator it(aElement);
    for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
      if (n->IsElement()) {
        ClearRestyleStateFromSubtree(n->AsElement());
      }
    }
  }

  Unused << Servo_TakeChangeHint(aElement);
  aElement->UnsetHasDirtyDescendantsForServo();
  aElement->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES);
}
void
ServoRestyleManager::ProcessPostTraversal(Element* aElement,
                                          nsStyleContext* aParentContext,
                                          ServoStyleSet* aStyleSet,
                                          nsStyleChangeList& aChangeList)
{
  nsIFrame* styleFrame = nsLayoutUtils::GetStyleFrame(aElement);

  // Grab the change hint from Servo.
  nsChangeHint changeHint = Servo_TakeChangeHint(aElement);

  // Handle lazy frame construction by posting a reconstruct for any lazily-
  // constructed roots.
  if (aElement->HasFlag(NODE_NEEDS_FRAME)) {
    changeHint |= nsChangeHint_ReconstructFrame;
    // The only time the primary frame is non-null is when image maps do hacky
    // SetPrimaryFrame calls.
    MOZ_ASSERT_IF(styleFrame, styleFrame->IsImageFrame());
    styleFrame = nullptr;
  }

  // Although we shouldn't generate non-ReconstructFrame hints for elements with
  // no frames, we can still get them here if they were explicitly posted by
  // PostRestyleEvent, such as a RepaintFrame hint when a :link changes to be
  // :visited.  Skip processing these hints if there is no frame.
  if ((styleFrame || (changeHint & nsChangeHint_ReconstructFrame)) && changeHint) {
    aChangeList.AppendChange(styleFrame, aElement, changeHint);
  }

  // If our change hint is reconstruct, we delegate to the frame constructor,
  // which consumes the new style and expects the old style to be on the frame.
  //
  // XXXbholley: We should teach the frame constructor how to clear the dirty
  // descendants bit to avoid the traversal here.
  if (changeHint & nsChangeHint_ReconstructFrame) {
    ClearRestyleStateFromSubtree(aElement);
    return;
  }

  // TODO(emilio): We could avoid some refcount traffic here, specially in the
  // ServoComputedValues case, which uses atomic refcounting.
  //
  // Hold the old style context alive, because it could become a dangling
  // pointer during the replacement. In practice it's not a huge deal (on
  // GetNextContinuationWithSameStyle the pointer is not dereferenced, only
  // compared), but better not playing with dangling pointers if not needed.
  RefPtr<nsStyleContext> oldStyleContext =
    styleFrame ? styleFrame->StyleContext() : nullptr;

  UndisplayedNode* displayContentsNode = nullptr;
  // FIXME(emilio, bug 1303605): This can be simpler for Servo.
  // Note that we intentionally don't check for display: none content.
  if (!oldStyleContext) {
    displayContentsNode =
      PresContext()->FrameConstructor()->GetDisplayContentsNodeFor(aElement);
    if (displayContentsNode) {
      oldStyleContext = displayContentsNode->mStyle;
    }
  }

  RefPtr<ServoComputedValues> computedValues =
    aStyleSet->ResolveServoStyle(aElement);

  // Note that we rely in the fact that we don't cascade pseudo-element styles
  // separately right now (that is, if a pseudo style changes, the normal style
  // changes too).
  //
  // Otherwise we should probably encode that information somehow to avoid
  // expensive checks in the common case.
  //
  // Also, we're going to need to check for pseudos of display: contents
  // elements, though that is buggy right now even in non-stylo mode, see
  // bug 1251799.
  const bool recreateContext = oldStyleContext &&
    oldStyleContext->StyleSource().AsServoComputedValues() != computedValues;

  RefPtr<nsStyleContext> newContext = nullptr;
  if (recreateContext) {
    MOZ_ASSERT(styleFrame || displayContentsNode);

    auto pseudo = aElement->GetPseudoElementType();
    nsIAtom* pseudoTag = pseudo == CSSPseudoElementType::NotPseudo
      ? nullptr : nsCSSPseudoElements::GetPseudoAtom(pseudo);

    newContext =
      aStyleSet->GetContext(computedValues.forget(),
                            aParentContext,
                            pseudoTag,
                            pseudo,
                            aElement);

    newContext->EnsureSameStructsCached(oldStyleContext);

    // XXX This could not always work as expected: there are kinds of content
    // with the first split and the last sharing style, but others not. We
    // should handle those properly.
    // XXXbz I think the UpdateStyleOfOwnedAnonBoxes call below handles _that_
    // right, but not other cases where we happen to have different styles on
    // different continuations... (e.g. first-line).
    for (nsIFrame* f = styleFrame; f;
         f = GetNextContinuationWithSameStyle(f, oldStyleContext)) {
      f->SetStyleContext(newContext);
    }

    if (MOZ_UNLIKELY(displayContentsNode)) {
      MOZ_ASSERT(!styleFrame);
      displayContentsNode->mStyle = newContext;
    }

    if (styleFrame) {
      styleFrame->UpdateStyleOfOwnedAnonBoxes(*aStyleSet, aChangeList, changeHint);
    }
  }

  const bool descendantsNeedFrames =
    aElement->HasFlag(NODE_DESCENDANTS_NEED_FRAMES);
  const bool traverseElementChildren =
    aElement->HasDirtyDescendantsForServo() || descendantsNeedFrames;
  const bool traverseTextChildren = recreateContext || descendantsNeedFrames;
  if (traverseElementChildren || traverseTextChildren) {
    nsStyleContext* upToDateContext =
      recreateContext ? newContext : oldStyleContext;

    StyleChildrenIterator it(aElement);
    TextPostTraversalState textState(
        *upToDateContext, *aStyleSet, displayContentsNode && recreateContext);
    for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
      if (traverseElementChildren && n->IsElement()) {
        ProcessPostTraversal(n->AsElement(), upToDateContext,
                             aStyleSet, aChangeList);
      } else if (traverseTextChildren && n->IsNodeOfType(nsINode::eTEXT)) {
        ProcessPostTraversalForText(n, aChangeList, textState);
      }
    }
  }

  aElement->UnsetHasDirtyDescendantsForServo();
  aElement->UnsetFlags(NODE_DESCENDANTS_NEED_FRAMES);
}
Beispiel #3
0
void
ServoRestyleManager::RecreateStyleContexts(Element* aElement,
                                           nsStyleContext* aParentContext,
                                           ServoStyleSet* aStyleSet,
                                           nsStyleChangeList& aChangeListToProcess)
{
  nsIFrame* primaryFrame = aElement->GetPrimaryFrame();

  // FIXME(bholley): Once we transfer ownership of the styles to the frame, we
  // can fast-reject without the FFI call by checking mServoData for null.
  nsChangeHint changeHint = Servo_TakeChangeHint(aElement);
  if (changeHint) {
      aChangeListToProcess.AppendChange(primaryFrame, aElement, changeHint);
  }

  // If our change hint is reconstruct, we delegate to the frame constructor,
  // which consumes the new style and expects the old style to be on the frame.
  //
  // XXXbholley: We should teach the frame constructor how to clear the dirty
  // descendants bit to avoid the traversal here.
  if (changeHint & nsChangeHint_ReconstructFrame) {
    ClearDirtyDescendantsFromSubtree(aElement);
    return;
  }

  // If we have a frame and a non-zero + non-reconstruct change hint, we need to
  // attach a new style context.
  bool recreateContext = primaryFrame && changeHint;
  if (recreateContext) {
    RefPtr<ServoComputedValues> computedValues = aStyleSet->ResolveServoStyle(aElement);

    // Hold the old style context alive, because it could become a dangling
    // pointer during the replacement. In practice it's not a huge deal (on
    // GetNextContinuationWithSameStyle the pointer is not dereferenced, only
    // compared), but better not playing with dangling pointers if not needed.
    RefPtr<nsStyleContext> oldStyleContext = primaryFrame->StyleContext();
    MOZ_ASSERT(oldStyleContext);

    RefPtr<nsStyleContext> newContext =
      aStyleSet->GetContext(computedValues.forget(), aParentContext, nullptr,
                            CSSPseudoElementType::NotPseudo);

    // XXX This could not always work as expected: there are kinds of content
    // with the first split and the last sharing style, but others not. We
    // should handle those properly.
    for (nsIFrame* f = primaryFrame; f;
         f = GetNextContinuationWithSameStyle(f, oldStyleContext)) {
      f->SetStyleContext(newContext);
    }

    // Update pseudo-elements state if appropriate.
    const static CSSPseudoElementType pseudosToRestyle[] = {
      CSSPseudoElementType::before,
      CSSPseudoElementType::after,
    };

    for (CSSPseudoElementType pseudoType : pseudosToRestyle) {
      nsIAtom* pseudoTag = nsCSSPseudoElements::GetPseudoAtom(pseudoType);

      if (nsIFrame* pseudoFrame = FrameForPseudoElement(aElement, pseudoTag)) {
        // TODO: we could maybe make this more performant via calling into
        // Servo just once to know which pseudo-elements we've got to restyle?
        RefPtr<nsStyleContext> pseudoContext =
          aStyleSet->ProbePseudoElementStyle(aElement, pseudoType, newContext);
        MOZ_ASSERT(pseudoContext, "should have taken the ReconstructFrame path above");
        pseudoFrame->SetStyleContext(pseudoContext);

        // We only care restyling text nodes, since other type of nodes
        // (images), are still not supported. If that eventually changes, we
        // may have to write more code here... Or not, I don't think too
        // many inherited properties can affect those other frames.
        StyleChildrenIterator it(pseudoFrame->GetContent());
        for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
          if (n->IsNodeOfType(nsINode::eTEXT)) {
            RefPtr<nsStyleContext> childContext =
              aStyleSet->ResolveStyleForText(n, pseudoContext);
            MOZ_ASSERT(n->GetPrimaryFrame(),
                       "How? This node is created at FC time!");
            n->GetPrimaryFrame()->SetStyleContext(childContext);
          }
        }
      }
    }
  }

  bool traverseElementChildren = aElement->HasDirtyDescendantsForServo();
  bool traverseTextChildren = recreateContext;
  if (traverseElementChildren || traverseTextChildren) {
    StyleChildrenIterator it(aElement);
    for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
      if (traverseElementChildren && n->IsElement()) {
        if (!primaryFrame) {
          // The frame constructor presumably decided to suppress frame
          // construction on this subtree. Just clear the dirty descendants
          // bit from the subtree, since there's no point in harvesting the
          // change hints.
          MOZ_ASSERT(!n->AsElement()->GetPrimaryFrame(),
                     "Only display:contents should do this, and we don't handle that yet");
          ClearDirtyDescendantsFromSubtree(n->AsElement());
        } else {
          RecreateStyleContexts(n->AsElement(), primaryFrame->StyleContext(),
                                aStyleSet, aChangeListToProcess);
        }
      } else if (traverseTextChildren && n->IsNodeOfType(nsINode::eTEXT)) {
        RecreateStyleContextsForText(n, primaryFrame->StyleContext(),
                                     aStyleSet);
      }
    }
  }

  aElement->UnsetHasDirtyDescendantsForServo();
}