void
ServoRestyleManager::RecreateStyleContexts(nsIContent* aContent,
                                           nsStyleContext* aParentContext,
                                           ServoStyleSet* aStyleSet,
                                           nsStyleChangeList& aChangeListToProcess)
{
  MOZ_ASSERT(aContent->IsElement() || aContent->IsNodeOfType(nsINode::eTEXT));

  nsIFrame* primaryFrame = aContent->GetPrimaryFrame();
  if (!primaryFrame && !aContent->IsDirtyForServo()) {
    // This happens when, for example, a display: none child of a
    // HAS_DIRTY_DESCENDANTS content is reached as part of the traversal.
    return;
  }

  // Work on text before.
  if (!aContent->IsElement()) {
    if (primaryFrame) {
      RefPtr<nsStyleContext> oldStyleContext = primaryFrame->StyleContext();
      RefPtr<nsStyleContext> newContext =
        aStyleSet->ResolveStyleForText(aContent, aParentContext);

      for (nsIFrame* f = primaryFrame; f;
           f = GetNextContinuationWithSameStyle(f, oldStyleContext)) {
        f->SetStyleContext(newContext);
      }
    }

    aContent->UnsetIsDirtyForServo();
    return;
  }

  Element* element = aContent->AsElement();
  if (element->IsDirtyForServo()) {
    RefPtr<ServoComputedValues> computedValues =
      Servo_ComputedValues_Get(aContent).Consume();
    MOZ_ASSERT(computedValues);

    nsChangeHint changeHint = nsChangeHint(0);

    // Add an explicit change hint if appropriate.
    ServoElementSnapshot* snapshot;
    if (mModifiedElements.Get(element, &snapshot)) {
      changeHint |= snapshot->ExplicitChangeHint();
    }

    // Add the stored change hint if there's a frame. If there isn't a frame,
    // generate a ReconstructFrame change hint if the new display value
    // (which we can get from the ComputedValues stored on the node) is not
    // none.
    if (primaryFrame) {
      changeHint |= primaryFrame->StyleContext()->ConsumeStoredChangeHint();
    } else {
      const nsStyleDisplay* currentDisplay =
        Servo_GetStyleDisplay(computedValues);
      if (currentDisplay->mDisplay != StyleDisplay::None) {
        changeHint |= nsChangeHint_ReconstructFrame;
      }
    }

    // Add the new change hint to the list of elements to process if
    // we need to do any work.
    if (changeHint) {
      aChangeListToProcess.AppendChange(primaryFrame, element, changeHint);
    }

    // The frame reconstruction step (if needed) will ask for the descendants'
    // style correctly. If not needed, we're done too.
    if (!primaryFrame) {
      aContent->UnsetIsDirtyForServo();
      return;
    }

    // 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(element, 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(element, pseudoType, newContext);

        // If pseudoContext is null here, it means the frame is going away, so
        // our change hint computation should have already indicated we need
        // to reframe.
        MOZ_ASSERT_IF(!pseudoContext,
                      changeHint & nsChangeHint_ReconstructFrame);
        if (pseudoContext) {
          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);
            }
          }
        }
      }
    }

    aContent->UnsetIsDirtyForServo();
  }

  if (aContent->HasDirtyDescendantsForServo()) {
    MOZ_ASSERT(primaryFrame,
               "Frame construction should be scheduled, and it takes the "
               "correct style for the children, so no need to be here.");
    StyleChildrenIterator it(aContent);
    for (nsIContent* n = it.GetNextChild(); n; n = it.GetNextChild()) {
      if (n->IsElement() || n->IsNodeOfType(nsINode::eTEXT)) {
        RecreateStyleContexts(n, primaryFrame->StyleContext(),
                              aStyleSet, aChangeListToProcess);
      }
    }
    aContent->UnsetHasDirtyDescendantsForServo();
  }
}
Beispiel #2
0
void
ServoRestyleManager::ProcessPendingRestyles()
{
  MOZ_ASSERT(PresContext()->Document(), "No document?  Pshaw!");
  MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(), "Missing a script blocker!");

  if (MOZ_UNLIKELY(!PresContext()->PresShell()->DidInitialize())) {
    // PresShell::FlushPendingNotifications doesn't early-return in the case
    // where the PreShell hasn't yet been initialized (and therefore we haven't
    // yet done the initial style traversal of the DOM tree). We should arguably
    // fix up the callers and assert against this case, but we just detect and
    // handle it for now.
    return;
  }

  if (!HasPendingRestyles()) {
    return;
  }

  ServoStyleSet* styleSet = StyleSet();
  nsIDocument* doc = PresContext()->Document();

  // XXXbholley: Should this be while() per bug 1316247?
  if (HasPendingRestyles()) {
    mInStyleRefresh = true;
    styleSet->StyleDocument();

    // First do any queued-up frame creation. (see bugs 827239 and 997506).
    //
    // XXXEmilio I'm calling this to avoid random behavior changes, since we
    // delay frame construction after styling we should re-check once our
    // model is more stable whether we can skip this call.
    //
    // Note this has to be *after* restyling, because otherwise frame
    // construction will find unstyled nodes, and that's not funny.
    PresContext()->FrameConstructor()->CreateNeededFrames();

    // Recreate style contexts and queue up change hints.
    nsStyleChangeList currentChanges;
    DocumentStyleRootIterator iter(doc);
    while (Element* root = iter.GetNextStyleRoot()) {
      RecreateStyleContexts(root, nullptr, styleSet, currentChanges);
    }

    // Process the change hints.
    //
    // Unfortunately, the frame constructor can generate new change hints while
    // processing existing ones. We redirect those into a secondary queue and
    // iterate until there's nothing left.
    ReentrantChangeList newChanges;
    mReentrantChanges = &newChanges;
    while (!currentChanges.IsEmpty()) {
      ProcessRestyledFrames(currentChanges);
      MOZ_ASSERT(currentChanges.IsEmpty());
      for (ReentrantChange& change: newChanges)  {
        currentChanges.AppendChange(change.mContent->GetPrimaryFrame(),
                                    change.mContent, change.mHint);
      }
      newChanges.Clear();
    }
    mReentrantChanges = nullptr;

    styleSet->AssertTreeIsClean();
    mInStyleRefresh = false;
  }

  IncrementRestyleGeneration();
}
void
ServoRestyleManager::ProcessPendingRestyles()
{
  MOZ_ASSERT(PresContext()->Document(), "No document?  Pshaw!");
  MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(), "Missing a script blocker!");

  if (MOZ_UNLIKELY(!PresContext()->PresShell()->DidInitialize())) {
    // PresShell::FlushPendingNotifications doesn't early-return in the case
    // where the PreShell hasn't yet been initialized (and therefore we haven't
    // yet done the initial style traversal of the DOM tree). We should arguably
    // fix up the callers and assert against this case, but we just detect and
    // handle it for now.
    return;
  }

  if (!HasPendingRestyles()) {
    return;
  }

  ServoStyleSet* styleSet = StyleSet();
  nsIDocument* doc = PresContext()->Document();
  Element* root = doc->GetRootElement();
  if (root) {
    for (auto iter = mModifiedElements.Iter(); !iter.Done(); iter.Next()) {
      ServoElementSnapshot* snapshot = iter.UserData();
      Element* element = iter.Key();

      // TODO: avoid the ComputeRestyleHint call if we already have the highest
      // explicit restyle hint?
      nsRestyleHint hint = styleSet->ComputeRestyleHint(element, snapshot);
      hint |= snapshot->ExplicitRestyleHint();

      if (hint) {
        NoteRestyleHint(element, hint);
      }
    }

    if (root->IsDirtyForServo() || root->HasDirtyDescendantsForServo()) {
      mInStyleRefresh = true;
      styleSet->StyleDocument(/* aLeaveDirtyBits = */ true);

      // First do any queued-up frame creation. (see bugs 827239 and 997506).
      //
      // XXXEmilio I'm calling this to avoid random behavior changes, since we
      // delay frame construction after styling we should re-check once our
      // model is more stable whether we can skip this call.
      //
      // Note this has to be *after* restyling, because otherwise frame
      // construction will find unstyled nodes, and that's not funny.
      PresContext()->FrameConstructor()->CreateNeededFrames();

      nsStyleChangeList changeList;
      RecreateStyleContexts(root, nullptr, styleSet, changeList);
      ProcessRestyledFrames(changeList);

      mInStyleRefresh = false;
    }
  }

  MOZ_ASSERT(!doc->IsDirtyForServo());
  doc->UnsetHasDirtyDescendantsForServo();

  mModifiedElements.Clear();

  IncrementRestyleGeneration();
}
Beispiel #4
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();
}