DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Header

Mercurial (b6d82b1a6b02)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "nsSVGUseFrame.h"

#include "mozilla/PresShell.h"
#include "mozilla/dom/MutationEvent.h"
#include "mozilla/dom/SVGUseElement.h"
#include "SVGObserverUtils.h"

using namespace mozilla;
using namespace mozilla::dom;

//----------------------------------------------------------------------
// Implementation

nsIFrame* NS_NewSVGUseFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
  return new (aPresShell) nsSVGUseFrame(aStyle, aPresShell->GetPresContext());
}

NS_IMPL_FRAMEARENA_HELPERS(nsSVGUseFrame)

//----------------------------------------------------------------------
// nsIFrame methods:

void nsSVGUseFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
                         nsIFrame* aPrevInFlow) {
  NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::use),
               "Content is not an SVG use!");

  mHasValidDimensions =
      static_cast<SVGUseElement*>(aContent)->HasValidDimensions();

  nsSVGGFrame::Init(aContent, aParent, aPrevInFlow);
}

nsresult nsSVGUseFrame::AttributeChanged(int32_t aNamespaceID,
                                         nsAtom* aAttribute, int32_t aModType) {
  // Currently our SMIL implementation does not modify the DOM attributes. Once
  // we implement the SVG 2 SMIL behaviour this can be removed
  // SVGUseElement::AfterSetAttr's implementation will be sufficient.
  if (aModType == MutationEvent_Binding::SMIL) {
    auto* content = SVGUseElement::FromNode(GetContent());
    content->ProcessAttributeChange(aNamespaceID, aAttribute);
  }

  return nsSVGGFrame::AttributeChanged(aNamespaceID, aAttribute, aModType);
}

void nsSVGUseFrame::PositionAttributeChanged() {
  // make sure our cached transform matrix gets (lazily) updated
  mCanvasTM = nullptr;
  nsLayoutUtils::PostRestyleEvent(GetContent()->AsElement(), RestyleHint{0},
                                  nsChangeHint_InvalidateRenderingObservers);
  nsSVGUtils::ScheduleReflowSVG(this);
  nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED);
}

void nsSVGUseFrame::DimensionAttributeChanged(bool aHadValidDimensions,
                                              bool aAttributeIsUsed) {
  bool invalidate = aAttributeIsUsed;
  if (mHasValidDimensions != aHadValidDimensions) {
    mHasValidDimensions = !mHasValidDimensions;
    invalidate = true;
  }

  if (invalidate) {
    nsLayoutUtils::PostRestyleEvent(GetContent()->AsElement(), RestyleHint{0},
                                    nsChangeHint_InvalidateRenderingObservers);
    nsSVGUtils::ScheduleReflowSVG(this);
  }
}

void nsSVGUseFrame::HrefChanged() {
  nsLayoutUtils::PostRestyleEvent(GetContent()->AsElement(), RestyleHint{0},
                                  nsChangeHint_InvalidateRenderingObservers);
  nsSVGUtils::ScheduleReflowSVG(this);
}

//----------------------------------------------------------------------
// nsSVGDisplayableFrame methods

void nsSVGUseFrame::ReflowSVG() {
  // We only handle x/y offset here, since any width/height that is in force is
  // handled by the nsSVGOuterSVGFrame for the anonymous <svg> that will be
  // created for that purpose.
  float x, y;
  static_cast<SVGUseElement*>(GetContent())
      ->GetAnimatedLengthValues(&x, &y, nullptr);
  mRect.MoveTo(nsLayoutUtils::RoundGfxRectToAppRect(gfxRect(x, y, 0.0, 0.0),
                                                    AppUnitsPerCSSPixel())
                   .TopLeft());

  // If we have a filter, we need to invalidate ourselves because filter
  // output can change even if none of our descendants need repainting.
  if (StyleEffects()->HasFilters()) {
    InvalidateFrame();
  }

  nsSVGGFrame::ReflowSVG();
}

void nsSVGUseFrame::NotifySVGChanged(uint32_t aFlags) {
  if (aFlags & COORD_CONTEXT_CHANGED && !(aFlags & TRANSFORM_CHANGED)) {
    // Coordinate context changes affect mCanvasTM if we have a
    // percentage 'x' or 'y'
    SVGUseElement* use = static_cast<SVGUseElement*>(GetContent());
    if (use->mLengthAttributes[SVGUseElement::ATTR_X].IsPercentage() ||
        use->mLengthAttributes[SVGUseElement::ATTR_Y].IsPercentage()) {
      aFlags |= TRANSFORM_CHANGED;
      // Ancestor changes can't affect how we render from the perspective of
      // any rendering observers that we may have, so we don't need to
      // invalidate them. We also don't need to invalidate ourself, since our
      // changed ancestor will have invalidated its entire area, which includes
      // our area.
      // For perf reasons we call this before calling NotifySVGChanged() below.
      nsSVGUtils::ScheduleReflowSVG(this);
    }
  }

  // We don't remove the TRANSFORM_CHANGED flag here if we have a viewBox or
  // non-percentage width/height, since if they're set then they are cloned to
  // an anonymous child <svg>, and its nsSVGInnerSVGFrame will do that.

  nsSVGGFrame::NotifySVGChanged(aFlags);
}