void
ServoRestyleManager::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
                                      nsIAtom* aAttribute, int32_t aModType,
                                      const nsAttrValue* aOldValue)
{
  MOZ_ASSERT(!mInStyleRefresh);
  MOZ_ASSERT_IF(mSnapshots.Get(aElement), mSnapshots.Get(aElement)->HasAttrs());

  nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
  if (primaryFrame) {
    primaryFrame->AttributeChanged(aNameSpaceID, aAttribute, aModType);
  }

  nsChangeHint hint = aElement->GetAttributeChangeHint(aAttribute, aModType);
  if (hint) {
    PostRestyleEvent(aElement, nsRestyleHint(0), hint);
  }

  if (aAttribute == nsGkAtoms::style) {
    PostRestyleEvent(aElement, eRestyle_StyleAttribute, nsChangeHint(0));
  }
  // <td> is affected by the cellpadding on its ancestor table,
  // so we should restyle the whole subtree
  if (aAttribute == nsGkAtoms::cellpadding && aElement->IsHTMLElement(nsGkAtoms::table)) {
    PostRestyleEvent(aElement, eRestyle_Subtree, nsChangeHint(0));
  }
  if (aElement->IsAttributeMapped(aAttribute)) {
    Servo_NoteExplicitHints(aElement, eRestyle_Self, nsChangeHint(0));
  }
}
void
KeyframeEffectReadOnly::CalculateCumulativeChangeHint(
  nsStyleContext *aStyleContext)
{
  mCumulativeChangeHint = nsChangeHint(0);

  for (const AnimationProperty& property : mProperties) {
    for (const AnimationPropertySegment& segment : property.mSegments) {
      RefPtr<nsStyleContext> fromContext =
        CreateStyleContextForAnimationValue(property.mProperty,
                                            segment.mFromValue, aStyleContext);

      RefPtr<nsStyleContext> toContext =
        CreateStyleContextForAnimationValue(property.mProperty,
                                            segment.mToValue, aStyleContext);

      uint32_t equalStructs = 0;
      uint32_t samePointerStructs = 0;
      nsChangeHint changeHint =
        fromContext->CalcStyleDifference(toContext,
                                         nsChangeHint(0),
                                         &equalStructs,
                                         &samePointerStructs);

      mCumulativeChangeHint |= changeHint;
    }
  }
}
nsresult
nsMathMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                            nsIContent* aBindingParent,
                            PRBool aCompileEventHandlers)
{
  static const char kMathMLStyleSheetURI[] = "resource://gre/res/mathml.css";

  nsresult rv = nsMathMLElementBase::BindToTree(aDocument, aParent,
                                                aBindingParent,
                                                aCompileEventHandlers);
  NS_ENSURE_SUCCESS(rv, rv);

  if (aDocument && !aDocument->GetMathMLEnabled()) {
    // Enable MathML and setup the style sheet during binding, not element
    // construction, because we could move a MathML element from the document
    // that created it to another document.
    aDocument->SetMathMLEnabled();
    aDocument->EnsureCatalogStyleSheet(kMathMLStyleSheetURI);

    // Rebuild style data for all the presshells, because style system
    // optimizations may have taken place assuming MathML was disabled.
    // (See nsRuleNode::CheckSpecifiedProperties.)
    // nsPresShellIterator skips hidden presshells, but that's OK because
    // if we're changing the document for one of those presshells the whole
    // presshell will be torn down.
    nsPresShellIterator iter(aDocument);
    nsCOMPtr<nsIPresShell> shell;
    while ((shell = iter.GetNextShell()) != nsnull) {
      shell->GetPresContext()->PostRebuildAllStyleDataEvent(nsChangeHint(0));
    }
  }

  return rv;
}
Пример #4
0
bool
RestyleTracker::GetRestyleData(Element* aElement, nsAutoPtr<RestyleData>& aData)
{
  NS_PRECONDITION(aElement->GetCrossShadowCurrentDoc() == Document(),
                  "Unexpected document; this will lead to incorrect behavior!");

  if (!aElement->HasFlag(RestyleBit())) {
    NS_ASSERTION(!aElement->HasFlag(RootBit()), "Bogus root bit?");
    return false;
  }

  mPendingRestyles.RemoveAndForget(aElement, aData);
  NS_ASSERTION(aData.get(), "Must have data if restyle bit is set");

  if (aData->mRestyleHint & eRestyle_LaterSiblings) {
    // Someone readded the eRestyle_LaterSiblings hint for this
    // element.  Leave it around for now, but remove the other restyle
    // hints and the change hint for it.  Also unset its root bit,
    // since it's no longer a root with the new restyle data.
    NS_ASSERTION(aData->mDescendants.IsEmpty(),
                 "expected descendants to be handled by now");
    RestyleData* newData = new RestyleData;
    newData->mChangeHint = nsChangeHint(0);
    newData->mRestyleHint = eRestyle_LaterSiblings;
    mPendingRestyles.Put(aElement, newData);
    aElement->UnsetFlags(RootBit());
    aData->mRestyleHint =
      nsRestyleHint(aData->mRestyleHint & ~eRestyle_LaterSiblings);
  } else {
    aElement->UnsetFlags(mRestyleBits);
  }

  return true;
}
Пример #5
0
nsresult
nsMathMLElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                            nsIContent* aBindingParent,
                            PRBool aCompileEventHandlers)
{
  static const char kMathMLStyleSheetURI[] = "resource://gre-resources/mathml.css";

  nsresult rv = nsMathMLElementBase::BindToTree(aDocument, aParent,
                                                aBindingParent,
                                                aCompileEventHandlers);
  NS_ENSURE_SUCCESS(rv, rv);

  if (aDocument && !aDocument->GetMathMLEnabled()) {
    // Enable MathML and setup the style sheet during binding, not element
    // construction, because we could move a MathML element from the document
    // that created it to another document.
    aDocument->SetMathMLEnabled();
    aDocument->EnsureCatalogStyleSheet(kMathMLStyleSheetURI);

    // Rebuild style data for the presshell, because style system
    // optimizations may have taken place assuming MathML was disabled.
    // (See nsRuleNode::CheckSpecifiedProperties.)
    nsCOMPtr<nsIPresShell> shell = aDocument->GetShell();
    if (shell) {
      shell->GetPresContext()->PostRebuildAllStyleDataEvent(nsChangeHint(0));
    }
  }

  return rv;
}
void
ServoRestyleManager::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
                                      nsIAtom* aAttribute, int32_t aModType,
                                      const nsAttrValue* aOldValue)
{
  MOZ_ASSERT(SnapshotForElement(aElement)->HasAttrs());
  if (aAttribute == nsGkAtoms::style) {
    PostRestyleEvent(aElement, eRestyle_StyleAttribute, nsChangeHint(0));
  }
}
ServoElementSnapshot::ServoElementSnapshot(Element* aElement)
  : mContains(Flags(0))
  , mState(0)
  , mExplicitRestyleHint(nsRestyleHint(0))
  , mExplicitChangeHint(nsChangeHint(0))
{
  mIsHTMLElementInHTMLDocument =
    aElement->IsHTMLElement() && aElement->IsInHTMLDocument();
  mIsInChromeDocument =
    nsContentUtils::IsChromeDoc(aElement->OwnerDoc());
}
Пример #8
0
void
ServoRestyleManager::AttributeChanged(Element* aElement, int32_t aNameSpaceID,
                                      nsIAtom* aAttribute, int32_t aModType,
                                      const nsAttrValue* aOldValue)
{
#ifdef DEBUG
  ServoElementSnapshot* snapshot = Servo_Element_GetSnapshot(aElement);
  MOZ_ASSERT_IF(snapshot, snapshot->HasAttrs());
#endif
  if (aAttribute == nsGkAtoms::style) {
    PostRestyleEvent(aElement, eRestyle_StyleAttribute, nsChangeHint(0));
  }
}
Пример #9
0
bool
RestyleTracker::GetRestyleData(Element* aElement, nsAutoPtr<RestyleData>& aData)
{
  NS_PRECONDITION(aElement->GetComposedDoc() == Document(),
                  "Unexpected document; this will lead to incorrect behavior!");

  if (!aElement->HasFlag(RestyleBit())) {
    NS_ASSERTION(!aElement->HasFlag(RootBit()), "Bogus root bit?");
    return false;
  }

  mPendingRestyles.RemoveAndForget(aElement, aData);
  NS_ASSERTION(aData.get(), "Must have data if restyle bit is set");

  if (aData->mRestyleHint & eRestyle_LaterSiblings) {
    // Someone readded the eRestyle_LaterSiblings hint for this
    // element.  Leave it around for now, but remove the other restyle
    // hints and the change hint for it.  Also unset its root bit,
    // since it's no longer a root with the new restyle data.

    // During a normal restyle, we should have already processed the
    // mDescendants array the last time we processed the restyle
    // for this element.  But in RebuildAllStyleData, we don't initially
    // expand out eRestyle_LaterSiblings, so we can get in here the
    // first time we need to process a restyle for this element.  In that
    // case, it's fine for us to have a non-empty mDescendants, since
    // we know that RebuildAllStyleData adds eRestyle_ForceDescendants
    // and we're guaranteed we'll restyle the entire tree.
    NS_ASSERTION(mRestyleManager->InRebuildAllStyleData() ||
                 aData->mDescendants.IsEmpty(),
                 "expected descendants to be handled by now");

    RestyleData* newData = new RestyleData;
    newData->mChangeHint = nsChangeHint(0);
    newData->mRestyleHint = eRestyle_LaterSiblings;
    mPendingRestyles.Put(aElement, newData);
    aElement->UnsetFlags(RootBit());
    aData->mRestyleHint =
      nsRestyleHint(aData->mRestyleHint & ~eRestyle_LaterSiblings);
  } else {
    aElement->UnsetFlags(mRestyleBits);
  }

  return true;
}
Пример #10
0
bool
RestyleTracker::GetRestyleData(Element* aElement, RestyleData* aData)
{
  NS_PRECONDITION(aElement->GetCurrentDoc() == Document(),
                  "Unexpected document; this will lead to incorrect behavior!");

  if (!aElement->HasFlag(RestyleBit())) {
    NS_ASSERTION(!aElement->HasFlag(RootBit()), "Bogus root bit?");
    return false;
  }

#ifdef DEBUG
  bool gotData =
#endif
  mPendingRestyles.Get(aElement, aData);
  NS_ASSERTION(gotData, "Must have data if restyle bit is set");

  if (aData->mRestyleHint & eRestyle_LaterSiblings) {
    // Someone readded the eRestyle_LaterSiblings hint for this
    // element.  Leave it around for now, but remove the other restyle
    // hints and the change hint for it.  Also unset its root bit,
    // since it's no longer a root with the new restyle data.
    RestyleData newData;
    newData.mChangeHint = nsChangeHint(0);
    newData.mRestyleHint = eRestyle_LaterSiblings;
    mPendingRestyles.Put(aElement, newData);
    aElement->UnsetFlags(RootBit());
    aData->mRestyleHint =
      nsRestyleHint(aData->mRestyleHint & ~eRestyle_LaterSiblings);
  } else {
    mPendingRestyles.Remove(aElement);
    aElement->UnsetFlags(mRestyleBits);
  }

  return true;
}
/* static */ void
ServoRestyleManager::PostRestyleEventForAnimations(Element* aElement,
                                                   nsRestyleHint aRestyleHint)
{
  Servo_NoteExplicitHints(aElement, aRestyleHint, nsChangeHint(0));
}
Пример #12
0
void
RestyleTracker::DoProcessRestyles()
{
  nsAutoCString docURL;
  if (profiler_is_active()) {
    nsIURI *uri = Document()->GetDocumentURI();
    if (uri) {
      uri->GetSpec(docURL);
    } else {
      docURL = "N/A";
    }
  }
  PROFILER_LABEL_PRINTF("RestyleTracker", "ProcessRestyles",
                        js::ProfileEntry::Category::CSS, "(%s)", docURL.get());

  nsDocShell* docShell = static_cast<nsDocShell*>(mRestyleManager->PresContext()->GetDocShell());
  RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
  bool isTimelineRecording = timelines && timelines->HasConsumer(docShell);

  // Create a AnimationsWithDestroyedFrame during restyling process to
  // stop animations and transitions on elements that have no frame at the end
  // of the restyling process.
  RestyleManager::AnimationsWithDestroyedFrame
    animationsWithDestroyedFrame(mRestyleManager);

  // Create a ReframingStyleContexts struct on the stack and put it in our
  // mReframingStyleContexts for almost all of the remaining scope of
  // this function.
  //
  // It needs to be *in* scope during BeginProcessingRestyles, which
  // might (if mDoRebuildAllStyleData is true) do substantial amounts of
  // restyle processing.
  //
  // However, it needs to be *out* of scope during
  // EndProcessingRestyles, since we should release the style contexts
  // it holds prior to any EndReconstruct call that
  // EndProcessingRestyles makes.  This is because in EndReconstruct we
  // try to destroy the old rule tree using the GC mechanism, which
  // means it only gets destroyed if it's unreferenced (and if it's
  // referenced, we assert).  So we want the ReframingStyleContexts
  // (which holds old style contexts) to be destroyed before the
  // EndReconstruct so those style contexts go away before
  // EndReconstruct.
  {
    RestyleManager::ReframingStyleContexts
      reframingStyleContexts(mRestyleManager);

    mRestyleManager->BeginProcessingRestyles(*this);

    LOG_RESTYLE("Processing %d pending %srestyles with %d restyle roots for %s",
                mPendingRestyles.Count(),
                mRestyleManager->PresContext()->TransitionManager()->
                  InAnimationOnlyStyleUpdate()
                  ? (const char*) "animation " : (const char*) "",
                static_cast<int>(mRestyleRoots.Length()),
                GetDocumentURI(Document()).get());
    LOG_RESTYLE_INDENT();

    // loop so that we process any restyle events generated by processing
    while (mPendingRestyles.Count()) {
      if (mHaveLaterSiblingRestyles) {
        // Convert them to individual restyles on all the later siblings
        AutoTArray<RefPtr<Element>, RESTYLE_ARRAY_STACKSIZE> laterSiblingArr;
        for (auto iter = mPendingRestyles.Iter(); !iter.Done(); iter.Next()) {
          auto element = static_cast<dom::Element*>(iter.Key());
          MOZ_ASSERT(!element->IsStyledByServo(), "Should not have Servo-styled elements here");
          // Only collect the entries that actually need restyling by us (and
          // haven't, for example, already been restyled).
          // It's important to not mess with the flags on entries not in our
          // document.
          if (element->GetComposedDoc() == Document() &&
              element->HasFlag(RestyleBit()) &&
              (iter.Data()->mRestyleHint & eRestyle_LaterSiblings)) {
            laterSiblingArr.AppendElement(element);
          }
        }
        for (uint32_t i = 0; i < laterSiblingArr.Length(); ++i) {
          Element* element = laterSiblingArr[i];
          MOZ_ASSERT(!element->IsStyledByServo());
          for (nsIContent* sibling = element->GetNextSibling();
               sibling;
               sibling = sibling->GetNextSibling()) {
            if (sibling->IsElement()) {
              LOG_RESTYLE("adding pending restyle for %s due to "
                          "eRestyle_LaterSiblings hint on %s",
                          FrameTagToString(sibling->AsElement()).get(),
                          FrameTagToString(element->AsElement()).get());
              if (AddPendingRestyle(sibling->AsElement(), eRestyle_Subtree,
                                    nsChangeHint(0))) {
                  // Nothing else to do here; we'll handle the following
                  // siblings when we get to |sibling| in laterSiblingArr.
                break;
              }
            }
          }
        }

        // Now remove all those eRestyle_LaterSiblings bits
        for (uint32_t i = 0; i < laterSiblingArr.Length(); ++i) {
          Element* element = laterSiblingArr[i];
          NS_ASSERTION(element->HasFlag(RestyleBit()), "How did that happen?");
          RestyleData* data;
#ifdef DEBUG
          bool found =
#endif
            mPendingRestyles.Get(element, &data);
          NS_ASSERTION(found, "Where did our entry go?");
          data->mRestyleHint =
            nsRestyleHint(data->mRestyleHint & ~eRestyle_LaterSiblings);
        }

        LOG_RESTYLE("%d pending restyles after expanding out "
                    "eRestyle_LaterSiblings", mPendingRestyles.Count());

        mHaveLaterSiblingRestyles = false;
      }

      uint32_t rootCount;
      while ((rootCount = mRestyleRoots.Length())) {
        // Make sure to pop the element off our restyle root array, so
        // that we can freely append to the array as we process this
        // element.
        RefPtr<Element> element;
        element.swap(mRestyleRoots[rootCount - 1]);
        mRestyleRoots.RemoveElementAt(rootCount - 1);

        LOG_RESTYLE("processing style root %s at index %d",
                    FrameTagToString(element).get(), rootCount - 1);
        LOG_RESTYLE_INDENT();

        // Do the document check before calling GetRestyleData, since we
        // don't want to do the sibling-processing GetRestyleData does if
        // the node is no longer relevant.
        if (element->GetComposedDoc() != Document()) {
          // Content node has been removed from our document; nothing else
          // to do here
          LOG_RESTYLE("skipping, no longer in the document");
          continue;
        }

        nsAutoPtr<RestyleData> data;
        if (!GetRestyleData(element, data)) {
          LOG_RESTYLE("skipping, already restyled");
          continue;
        }

        if (isTimelineRecording) {
          timelines->AddMarkerForDocShell(docShell, Move(
            MakeUnique<RestyleTimelineMarker>(
              data->mRestyleHint, MarkerTracingType::START)));
        }

#if defined(MOZ_ENABLE_PROFILER_SPS)
        Maybe<GeckoProfilerTracingRAII> profilerRAII;
        if (profiler_feature_active("restyle")) {
          profilerRAII.emplace("Paint", "Styles", Move(data->mBacktrace));
        }
#endif
        ProcessOneRestyle(element, data->mRestyleHint, data->mChangeHint,
                          data->mRestyleHintData);
        AddRestyleRootsIfAwaitingRestyle(data->mDescendants);

        if (isTimelineRecording) {
          timelines->AddMarkerForDocShell(docShell, Move(
            MakeUnique<RestyleTimelineMarker>(
              data->mRestyleHint, MarkerTracingType::END)));
        }
      }

      if (mHaveLaterSiblingRestyles) {
        // Keep processing restyles for now
        continue;
      }

      // Now we only have entries with change hints left.  To be safe in
      // case of reentry from the handing of the change hint, use a
      // scratch array instead of calling out to ProcessOneRestyle while
      // enumerating the hashtable.  Use the stack if we can, otherwise
      // fall back on heap-allocation.
      AutoTArray<RestyleEnumerateData, RESTYLE_ARRAY_STACKSIZE> restyleArr;
      RestyleEnumerateData* restylesToProcess =
        restyleArr.AppendElements(mPendingRestyles.Count());
      if (restylesToProcess) {
        RestyleEnumerateData* restyle = restylesToProcess;
#ifdef RESTYLE_LOGGING
        uint32_t count = 0;
#endif
        for (auto iter = mPendingRestyles.Iter(); !iter.Done(); iter.Next()) {
          auto element = static_cast<dom::Element*>(iter.Key());
          RestyleTracker::RestyleData* data = iter.Data();

          // Only collect the entries that actually need restyling by us (and
          // haven't, for example, already been restyled).
          // It's important to not mess with the flags on entries not in our
          // document.
          if (element->GetComposedDoc() != Document() ||
              !element->HasFlag(RestyleBit())) {
            LOG_RESTYLE("skipping pending restyle %s, already restyled or no "
                        "longer in the document",
                        FrameTagToString(element).get());
            continue;
          }

          NS_ASSERTION(
            !element->HasFlag(RootBit()) ||
            // Maybe we're just not reachable via the frame tree?
            (element->GetFlattenedTreeParent() &&
             (!element->GetFlattenedTreeParent()->GetPrimaryFrame() ||
              element->GetFlattenedTreeParent()->GetPrimaryFrame()->IsLeaf() ||
              element->GetComposedDoc()->GetShell()->FrameManager()
                ->GetDisplayContentsStyleFor(element))) ||
            // Or not reachable due to an async reinsert we have
            // pending?  If so, we'll have a reframe hint around.
            // That incidentally makes it safe that we still have
            // the bit, since any descendants that didn't get added
            // to the roots list because we had the bits will be
            // completely restyled in a moment.
            (data->mChangeHint & nsChangeHint_ReconstructFrame),
            "Why did this not get handled while processing mRestyleRoots?");

          // Unset the restyle bits now, so if they get readded later as we
          // process we won't clobber that adding of the bit.
          element->UnsetFlags(RestyleBit() |
                              RootBit() |
                              ConditionalDescendantsBit());

          restyle->mElement = element;
          restyle->mRestyleHint = data->mRestyleHint;
          restyle->mChangeHint = data->mChangeHint;
          // We can move data since we'll be clearing mPendingRestyles after
          // we finish enumerating it.
          restyle->mRestyleHintData = Move(data->mRestyleHintData);
#if defined(MOZ_ENABLE_PROFILER_SPS)
          restyle->mBacktrace = Move(data->mBacktrace);
#endif

#ifdef RESTYLE_LOGGING
          count++;
#endif

          // Increment to the next slot in the array
          restyle++;
        }

        RestyleEnumerateData* lastRestyle = restyle;

        // Clear the hashtable now that we don't need it anymore
        mPendingRestyles.Clear();

#ifdef RESTYLE_LOGGING
        uint32_t index = 0;
#endif
        for (RestyleEnumerateData* currentRestyle = restylesToProcess;
             currentRestyle != lastRestyle;
             ++currentRestyle) {
          LOG_RESTYLE("processing pending restyle %s at index %d/%d",
                      FrameTagToString(currentRestyle->mElement).get(),
                      index++, count);
          LOG_RESTYLE_INDENT();

#if defined(MOZ_ENABLE_PROFILER_SPS)
          Maybe<GeckoProfilerTracingRAII> profilerRAII;
          if (profiler_feature_active("restyle")) {
            profilerRAII.emplace("Paint", "Styles", Move(currentRestyle->mBacktrace));
          }
#endif
          if (isTimelineRecording) {
            timelines->AddMarkerForDocShell(docShell, Move(
              MakeUnique<RestyleTimelineMarker>(
                currentRestyle->mRestyleHint, MarkerTracingType::START)));
          }

          ProcessOneRestyle(currentRestyle->mElement,
                            currentRestyle->mRestyleHint,
                            currentRestyle->mChangeHint,
                            currentRestyle->mRestyleHintData);

          if (isTimelineRecording) {
            timelines->AddMarkerForDocShell(docShell, Move(
              MakeUnique<RestyleTimelineMarker>(
                currentRestyle->mRestyleHint, MarkerTracingType::END)));
          }
        }
      }
    }
  }

  // mPendingRestyles is now empty.
  mHaveSelectors = false;

  mRestyleManager->EndProcessingRestyles();
}
Пример #13
0
static void
DoApplyRenderingChangeToTree(nsIFrame* aFrame,
                             nsChangeHint aChange)
{
  NS_PRECONDITION(gInApplyRenderingChangeToTree,
                  "should only be called within ApplyRenderingChangeToTree");

  for ( ; aFrame; aFrame = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(aFrame)) {
    // Invalidate and sync views on all descendant frames, following placeholders.
    // We don't need to update transforms in SyncViewsAndInvalidateDescendants, because
    // there can't be any out-of-flows or popups that need to be transformed;
    // all out-of-flow descendants of the transformed element must also be
    // descendants of the transformed frame.
    SyncViewsAndInvalidateDescendants(aFrame,
      nsChangeHint(aChange & (nsChangeHint_RepaintFrame |
                              nsChangeHint_SyncFrameView |
                              nsChangeHint_UpdateOpacityLayer |
                              nsChangeHint_SchedulePaint)));
    // This must be set to true if the rendering change needs to
    // invalidate content.  If it's false, a composite-only paint
    // (empty transaction) will be scheduled.
    bool needInvalidatingPaint = false;

    // if frame has view, will already be invalidated
    if (aChange & nsChangeHint_RepaintFrame) {
      // Note that this whole block will be skipped when painting is suppressed
      // (due to our caller ApplyRendingChangeToTree() discarding the
      // nsChangeHint_RepaintFrame hint).  If you add handling for any other
      // hints within this block, be sure that they too should be ignored when
      // painting is suppressed.
      needInvalidatingPaint = true;
      aFrame->InvalidateFrameSubtree();
      if ((aChange & nsChangeHint_UpdateEffects) &&
          aFrame->IsFrameOfType(nsIFrame::eSVG) &&
          !(aFrame->GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
        // Need to update our overflow rects:
        nsSVGUtils::ScheduleReflowSVG(aFrame);
      }
    }
    if (aChange & nsChangeHint_UpdateTextPath) {
      if (aFrame->IsSVGText()) {
        // Invalidate and reflow the entire SVGTextFrame:
        NS_ASSERTION(aFrame->GetContent()->IsSVGElement(nsGkAtoms::textPath),
                     "expected frame for a <textPath> element");
        nsIFrame* text =
          nsLayoutUtils::GetClosestFrameOfType(aFrame, nsGkAtoms::svgTextFrame);
        NS_ASSERTION(text, "expected to find an ancestor SVGTextFrame");
        static_cast<SVGTextFrame*>(text)->NotifyGlyphMetricsChange();
      } else {
        MOZ_ASSERT(false, "unexpected frame got nsChangeHint_UpdateTextPath");
      }
    }
    if (aChange & nsChangeHint_UpdateOpacityLayer) {
      // FIXME/bug 796697: we can get away with empty transactions for
      // opacity updates in many cases.
      needInvalidatingPaint = true;

      ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_opacity);
      if (nsSVGIntegrationUtils::UsingEffectsForFrame(aFrame)) {
        // SVG effects paints the opacity without using
        // nsDisplayOpacity. We need to invalidate manually.
        aFrame->InvalidateFrameSubtree();
      }
    }
    if ((aChange & nsChangeHint_UpdateTransformLayer) &&
        aFrame->IsTransformed()) {
      ActiveLayerTracker::NotifyRestyle(aFrame, eCSSProperty_transform);
      // If we're not already going to do an invalidating paint, see
      // if we can get away with only updating the transform on a
      // layer for this frame, and not scheduling an invalidating
      // paint.
      if (!needInvalidatingPaint) {
        Layer* layer;
        needInvalidatingPaint |= !aFrame->TryUpdateTransformOnly(&layer);

        if (!needInvalidatingPaint) {
          // Since we're not going to paint, we need to resend animation
          // data to the layer.
          MOZ_ASSERT(layer, "this can't happen if there's no layer");
          nsDisplayListBuilder::AddAnimationsAndTransitionsToLayer(
            layer, nullptr, nullptr, aFrame, eCSSProperty_transform);
        }
      }
    }
    if (aChange & nsChangeHint_ChildrenOnlyTransform) {
      needInvalidatingPaint = true;
      nsIFrame* childFrame =
        GetFrameForChildrenOnlyTransformHint(aFrame)->PrincipalChildList().FirstChild();
      for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
        ActiveLayerTracker::NotifyRestyle(childFrame, eCSSProperty_transform);
      }
    }
    if (aChange & nsChangeHint_SchedulePaint) {
      needInvalidatingPaint = true;
    }
    aFrame->SchedulePaint(needInvalidatingPaint
                            ? nsIFrame::PAINT_DEFAULT
                            : nsIFrame::PAINT_COMPOSITE_ONLY);
  }
}
Пример #14
0
/**
 * Calculates the change hint and the restyle hint for a given content state
 * change.
 *
 * This is called from both Restyle managers.
 */
void
RestyleManagerBase::ContentStateChangedInternal(Element* aElement,
                                                EventStates aStateMask,
                                                nsChangeHint* aOutChangeHint,
                                                nsRestyleHint* aOutRestyleHint)
{
  MOZ_ASSERT(aOutChangeHint);
  MOZ_ASSERT(aOutRestyleHint);

  StyleSetHandle styleSet = PresContext()->StyleSet();
  NS_ASSERTION(styleSet, "couldn't get style set");

  *aOutChangeHint = nsChangeHint(0);
  // Any change to a content state that affects which frames we construct
  // must lead to a frame reconstruct here if we already have a frame.
  // Note that we never decide through non-CSS means to not create frames
  // based on content states, so if we already don't have a frame we don't
  // need to force a reframe -- if it's needed, the HasStateDependentStyle
  // call will handle things.
  nsIFrame* primaryFrame = aElement->GetPrimaryFrame();
  CSSPseudoElementType pseudoType = CSSPseudoElementType::NotPseudo;
  if (primaryFrame) {
    // If it's generated content, ignore LOADING/etc state changes on it.
    if (!primaryFrame->IsGeneratedContentFrame() &&
        aStateMask.HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN |
                                         NS_EVENT_STATE_USERDISABLED |
                                         NS_EVENT_STATE_SUPPRESSED |
                                         NS_EVENT_STATE_LOADING)) {
      *aOutChangeHint = nsChangeHint_ReconstructFrame;
    } else {
      uint8_t app = primaryFrame->StyleDisplay()->mAppearance;
      if (app) {
        nsITheme* theme = PresContext()->GetTheme();
        if (theme &&
            theme->ThemeSupportsWidget(PresContext(), primaryFrame, app)) {
          bool repaint = false;
          theme->WidgetStateChanged(primaryFrame, app, nullptr, &repaint,
                                    nullptr);
          if (repaint) {
            *aOutChangeHint |= nsChangeHint_RepaintFrame;
          }
        }
      }
    }

    pseudoType = primaryFrame->StyleContext()->GetPseudoType();

    primaryFrame->ContentStatesChanged(aStateMask);
  }

  if (pseudoType >= CSSPseudoElementType::Count) {
    *aOutRestyleHint = styleSet->HasStateDependentStyle(aElement, aStateMask);
  } else if (nsCSSPseudoElements::PseudoElementSupportsUserActionState(
               pseudoType)) {
    // If aElement is a pseudo-element, we want to check to see whether there
    // are any state-dependent rules applying to that pseudo.
    Element* ancestor =
      ElementForStyleContext(nullptr, primaryFrame, pseudoType);
    *aOutRestyleHint = styleSet->HasStateDependentStyle(ancestor, pseudoType,
                                                        aElement, aStateMask);
  } else {
    *aOutRestyleHint = nsRestyleHint(0);
  }

  if (aStateMask.HasState(NS_EVENT_STATE_HOVER) && *aOutRestyleHint != 0) {
    IncrementHoverGeneration();
  }

  if (aStateMask.HasState(NS_EVENT_STATE_VISITED)) {
    // Exposing information to the page about whether the link is
    // visited or not isn't really something we can worry about here.
    // FIXME: We could probably do this a bit better.
    *aOutChangeHint |= nsChangeHint_RepaintFrame;
  }
}
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();
  }
}