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 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
/* -*- 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/. */

// Main header first:
#include "nsSVGFilterFrame.h"

// Keep others in (case-insensitive) order:
#include "AutoReferenceChainGuard.h"
#include "gfxUtils.h"
#include "mozilla/PresShell.h"
#include "mozilla/dom/SVGFilterElement.h"
#include "nsGkAtoms.h"
#include "SVGObserverUtils.h"
#include "SVGElement.h"
#include "nsSVGFilterInstance.h"
#include "nsSVGIntegrationUtils.h"
#include "nsSVGUtils.h"
#include "nsContentUtils.h"

using namespace mozilla;
using namespace mozilla::dom;

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

NS_IMPL_FRAMEARENA_HELPERS(nsSVGFilterFrame)

uint16_t nsSVGFilterFrame::GetEnumValue(uint32_t aIndex, nsIContent* aDefault) {
  SVGAnimatedEnumeration& thisEnum =
      static_cast<SVGFilterElement*>(GetContent())->mEnumAttributes[aIndex];

  if (thisEnum.IsExplicitlySet()) {
    return thisEnum.GetAnimValue();
  }

  // Before we recurse, make sure we'll break reference loops and over long
  // reference chains:
  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
                                        &sRefChainLengthCounter);
  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
    // Break reference chain
    return static_cast<SVGFilterElement*>(aDefault)
        ->mEnumAttributes[aIndex]
        .GetAnimValue();
  }

  nsSVGFilterFrame* next = GetReferencedFilter();

  return next ? next->GetEnumValue(aIndex, aDefault)
              : static_cast<SVGFilterElement*>(aDefault)
                    ->mEnumAttributes[aIndex]
                    .GetAnimValue();
}

const SVGAnimatedLength* nsSVGFilterFrame::GetLengthValue(
    uint32_t aIndex, nsIContent* aDefault) {
  const SVGAnimatedLength* thisLength =
      &static_cast<SVGFilterElement*>(GetContent())->mLengthAttributes[aIndex];

  if (thisLength->IsExplicitlySet()) {
    return thisLength;
  }

  // Before we recurse, make sure we'll break reference loops and over long
  // reference chains:
  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
                                        &sRefChainLengthCounter);
  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
    // Break reference chain
    return &static_cast<SVGFilterElement*>(aDefault)->mLengthAttributes[aIndex];
  }

  nsSVGFilterFrame* next = GetReferencedFilter();

  return next ? next->GetLengthValue(aIndex, aDefault)
              : &static_cast<SVGFilterElement*>(aDefault)
                     ->mLengthAttributes[aIndex];
}

const SVGFilterElement* nsSVGFilterFrame::GetFilterContent(
    nsIContent* aDefault) {
  for (nsIContent* child = mContent->GetFirstChild(); child;
       child = child->GetNextSibling()) {
    RefPtr<SVGFE> primitive;
    CallQueryInterface(child, (SVGFE**)getter_AddRefs(primitive));
    if (primitive) {
      return static_cast<SVGFilterElement*>(GetContent());
    }
  }

  // Before we recurse, make sure we'll break reference loops and over long
  // reference chains:
  static int16_t sRefChainLengthCounter = AutoReferenceChainGuard::noChain;
  AutoReferenceChainGuard refChainGuard(this, &mLoopFlag,
                                        &sRefChainLengthCounter);
  if (MOZ_UNLIKELY(!refChainGuard.Reference())) {
    // Break reference chain
    return static_cast<SVGFilterElement*>(aDefault);
  }

  nsSVGFilterFrame* next = GetReferencedFilter();

  return next ? next->GetFilterContent(aDefault)
              : static_cast<SVGFilterElement*>(aDefault);
}

nsSVGFilterFrame* nsSVGFilterFrame::GetReferencedFilter() {
  if (mNoHRefURI) {
    return nullptr;
  }

  auto GetHref = [this](nsAString& aHref) {
    SVGFilterElement* filter = static_cast<SVGFilterElement*>(GetContent());
    if (filter->mStringAttributes[SVGFilterElement::HREF].IsExplicitlySet()) {
      filter->mStringAttributes[SVGFilterElement::HREF].GetAnimValue(aHref,
                                                                     filter);
    } else {
      filter->mStringAttributes[SVGFilterElement::XLINK_HREF].GetAnimValue(
          aHref, filter);
    }
    this->mNoHRefURI = aHref.IsEmpty();
  };

  nsIFrame* tframe = SVGObserverUtils::GetAndObserveTemplate(this, GetHref);
  if (tframe) {
    LayoutFrameType frameType = tframe->Type();
    if (frameType == LayoutFrameType::SVGFilter) {
      return static_cast<nsSVGFilterFrame*>(tframe);
    }
    // We don't call SVGObserverUtils::RemoveTemplateObserver and set
    // `mNoHRefURI = false` here since we want to be invalidated if the ID
    // specified by our href starts resolving to a different/valid element.
  }

  return nullptr;
}

nsresult nsSVGFilterFrame::AttributeChanged(int32_t aNameSpaceID,
                                            nsAtom* aAttribute,
                                            int32_t aModType) {
  if (aNameSpaceID == kNameSpaceID_None &&
      (aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y ||
       aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height ||
       aAttribute == nsGkAtoms::filterUnits ||
       aAttribute == nsGkAtoms::primitiveUnits)) {
    SVGObserverUtils::InvalidateDirectRenderingObservers(this);
  } else if ((aNameSpaceID == kNameSpaceID_XLink ||
              aNameSpaceID == kNameSpaceID_None) &&
             aAttribute == nsGkAtoms::href) {
    // Blow away our reference, if any
    SVGObserverUtils::RemoveTemplateObserver(this);
    mNoHRefURI = false;
    // And update whoever references us
    SVGObserverUtils::InvalidateDirectRenderingObservers(this);
  }
  return nsSVGContainerFrame::AttributeChanged(aNameSpaceID, aAttribute,
                                               aModType);
}

#ifdef DEBUG
void nsSVGFilterFrame::Init(nsIContent* aContent, nsContainerFrame* aParent,
                            nsIFrame* aPrevInFlow) {
  NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::filter),
               "Content is not an SVG filter");

  nsSVGContainerFrame::Init(aContent, aParent, aPrevInFlow);
}
#endif /* DEBUG */