layout/svg/nsSVGEffects.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/svg/nsSVGEffects.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,771 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +// Main header first:
    1.10 +#include "nsSVGEffects.h"
    1.11 +
    1.12 +// Keep others in (case-insensitive) order:
    1.13 +#include "nsCSSFrameConstructor.h"
    1.14 +#include "nsISupportsImpl.h"
    1.15 +#include "nsSVGClipPathFrame.h"
    1.16 +#include "nsSVGPaintServerFrame.h"
    1.17 +#include "nsSVGPathGeometryElement.h"
    1.18 +#include "nsSVGFilterFrame.h"
    1.19 +#include "nsSVGMaskFrame.h"
    1.20 +#include "nsIReflowCallback.h"
    1.21 +#include "RestyleManager.h"
    1.22 +
    1.23 +using namespace mozilla;
    1.24 +using namespace mozilla::dom;
    1.25 +
    1.26 +// nsSVGRenderingObserver impl
    1.27 +NS_IMPL_ISUPPORTS(nsSVGRenderingObserver, nsIMutationObserver)
    1.28 +
    1.29 +void
    1.30 +nsSVGRenderingObserver::StartListening()
    1.31 +{
    1.32 +  Element* target = GetTarget();
    1.33 +  if (target) {
    1.34 +    target->AddMutationObserver(this);
    1.35 +  }
    1.36 +}
    1.37 +
    1.38 +void
    1.39 +nsSVGRenderingObserver::StopListening()
    1.40 +{
    1.41 +  Element* target = GetTarget();
    1.42 +
    1.43 +  if (target) {
    1.44 +    target->RemoveMutationObserver(this);
    1.45 +    if (mInObserverList) {
    1.46 +      nsSVGEffects::RemoveRenderingObserver(target, this);
    1.47 +      mInObserverList = false;
    1.48 +    }
    1.49 +  }
    1.50 +  NS_ASSERTION(!mInObserverList, "still in an observer list?");
    1.51 +}
    1.52 +
    1.53 +
    1.54 +
    1.55 +/**
    1.56 + * Note that in the current setup there are two separate observer lists.
    1.57 + *
    1.58 + * In nsSVGIDRenderingObserver's ctor, the new object adds itself to the
    1.59 + * mutation observer list maintained by the referenced element. In this way the
    1.60 + * nsSVGIDRenderingObserver is notified if there are any attribute or content
    1.61 + * tree changes to the element or any of its *descendants*.
    1.62 + *
    1.63 + * In nsSVGIDRenderingObserver::GetReferencedElement() the
    1.64 + * nsSVGIDRenderingObserver object also adds itself to an
    1.65 + * nsSVGRenderingObserverList object belonging to the referenced
    1.66 + * element.
    1.67 + *
    1.68 + * XXX: it would be nice to have a clear and concise executive summary of the
    1.69 + * benefits/necessity of maintaining a second observer list.
    1.70 + */
    1.71 +
    1.72 +nsSVGIDRenderingObserver::nsSVGIDRenderingObserver(nsIURI *aURI,
    1.73 +                                                   nsIFrame *aFrame,
    1.74 +                                                   bool aReferenceImage)
    1.75 +  : mElement(MOZ_THIS_IN_INITIALIZER_LIST()), mFrame(aFrame),
    1.76 +    mFramePresShell(aFrame->PresContext()->PresShell())
    1.77 +{
    1.78 +  // Start watching the target element
    1.79 +  mElement.Reset(aFrame->GetContent(), aURI, true, aReferenceImage);
    1.80 +  StartListening();
    1.81 +}
    1.82 +
    1.83 +nsSVGIDRenderingObserver::~nsSVGIDRenderingObserver()
    1.84 +{
    1.85 +  StopListening();
    1.86 +}
    1.87 +
    1.88 +static nsSVGRenderingObserverList *
    1.89 +GetObserverList(Element *aElement)
    1.90 +{
    1.91 +  return static_cast<nsSVGRenderingObserverList*>
    1.92 +    (aElement->GetProperty(nsGkAtoms::renderingobserverlist));
    1.93 +}
    1.94 +
    1.95 +Element*
    1.96 +nsSVGRenderingObserver::GetReferencedElement()
    1.97 +{
    1.98 +  Element* target = GetTarget();
    1.99 +#ifdef DEBUG
   1.100 +  if (target) {
   1.101 +    nsSVGRenderingObserverList *observerList = GetObserverList(target);
   1.102 +    bool inObserverList = observerList && observerList->Contains(this);
   1.103 +    NS_ASSERTION(inObserverList == mInObserverList, "failed to track whether we're in our referenced element's observer list!");
   1.104 +  } else {
   1.105 +    NS_ASSERTION(!mInObserverList, "In whose observer list are we, then?");
   1.106 +  }
   1.107 +#endif
   1.108 +  if (target && !mInObserverList) {
   1.109 +    nsSVGEffects::AddRenderingObserver(target, this);
   1.110 +    mInObserverList = true;
   1.111 +  }
   1.112 +  return target;
   1.113 +}
   1.114 +
   1.115 +nsIFrame*
   1.116 +nsSVGRenderingObserver::GetReferencedFrame()
   1.117 +{
   1.118 +  Element* referencedElement = GetReferencedElement();
   1.119 +  return referencedElement ? referencedElement->GetPrimaryFrame() : nullptr;
   1.120 +}
   1.121 +
   1.122 +nsIFrame*
   1.123 +nsSVGRenderingObserver::GetReferencedFrame(nsIAtom* aFrameType, bool* aOK)
   1.124 +{
   1.125 +  nsIFrame* frame = GetReferencedFrame();
   1.126 +  if (frame) {
   1.127 +    if (frame->GetType() == aFrameType)
   1.128 +      return frame;
   1.129 +    if (aOK) {
   1.130 +      *aOK = false;
   1.131 +    }
   1.132 +  }
   1.133 +  return nullptr;
   1.134 +}
   1.135 +
   1.136 +void
   1.137 +nsSVGIDRenderingObserver::DoUpdate()
   1.138 +{
   1.139 +  if (mFramePresShell->IsDestroying()) {
   1.140 +    // mFrame is no longer valid. Bail out.
   1.141 +    mFrame = nullptr;
   1.142 +    return;
   1.143 +  }
   1.144 +  if (mElement.get() && mInObserverList) {
   1.145 +    nsSVGEffects::RemoveRenderingObserver(mElement.get(), this);
   1.146 +    mInObserverList = false;
   1.147 +  }
   1.148 +  if (mFrame && mFrame->IsFrameOfType(nsIFrame::eSVG)) {
   1.149 +    // Changes should propagate out to things that might be observing
   1.150 +    // the referencing frame or its ancestors.
   1.151 +    nsSVGEffects::InvalidateRenderingObservers(mFrame);
   1.152 +  }
   1.153 +}
   1.154 +
   1.155 +void
   1.156 +nsSVGRenderingObserver::InvalidateViaReferencedElement()
   1.157 +{
   1.158 +  mInObserverList = false;
   1.159 +  DoUpdate();
   1.160 +}
   1.161 +
   1.162 +void
   1.163 +nsSVGRenderingObserver::NotifyEvictedFromRenderingObserverList()
   1.164 +{
   1.165 +  mInObserverList = false; // We've been removed from rendering-obs. list.
   1.166 +  StopListening();            // Remove ourselves from mutation-obs. list.
   1.167 +}
   1.168 +
   1.169 +void
   1.170 +nsSVGRenderingObserver::AttributeChanged(nsIDocument* aDocument,
   1.171 +                                         dom::Element* aElement,
   1.172 +                                         int32_t aNameSpaceID,
   1.173 +                                         nsIAtom* aAttribute,
   1.174 +                                         int32_t aModType)
   1.175 +{
   1.176 +  // An attribute belonging to the element that we are observing *or one of its
   1.177 +  // descendants* has changed.
   1.178 +  //
   1.179 +  // In the case of observing a gradient element, say, we want to know if any
   1.180 +  // of its 'stop' element children change, but we don't actually want to do
   1.181 +  // anything for changes to SMIL element children, for example. Maybe it's not
   1.182 +  // worth having logic to optimize for that, but in most cases it could be a
   1.183 +  // small check?
   1.184 +  //
   1.185 +  // XXXjwatt: do we really want to blindly break the link between our
   1.186 +  // observers and ourselves for all attribute changes? For non-ID changes
   1.187 +  // surely that is unnecessary.
   1.188 +
   1.189 +  DoUpdate();
   1.190 +}
   1.191 +
   1.192 +void
   1.193 +nsSVGRenderingObserver::ContentAppended(nsIDocument *aDocument,
   1.194 +                                        nsIContent *aContainer,
   1.195 +                                        nsIContent *aFirstNewContent,
   1.196 +                                        int32_t /* unused */)
   1.197 +{
   1.198 +  DoUpdate();
   1.199 +}
   1.200 +
   1.201 +void
   1.202 +nsSVGRenderingObserver::ContentInserted(nsIDocument *aDocument,
   1.203 +                                        nsIContent *aContainer,
   1.204 +                                        nsIContent *aChild,
   1.205 +                                        int32_t /* unused */)
   1.206 +{
   1.207 +  DoUpdate();
   1.208 +}
   1.209 +
   1.210 +void
   1.211 +nsSVGRenderingObserver::ContentRemoved(nsIDocument *aDocument,
   1.212 +                                       nsIContent *aContainer,
   1.213 +                                       nsIContent *aChild,
   1.214 +                                       int32_t aIndexInContainer,
   1.215 +                                       nsIContent *aPreviousSibling)
   1.216 +{
   1.217 +  DoUpdate();
   1.218 +}
   1.219 +
   1.220 +NS_IMPL_ISUPPORTS(nsSVGFilterProperty, nsISupports)
   1.221 +
   1.222 +nsSVGFilterProperty::nsSVGFilterProperty(const nsTArray<nsStyleFilter> &aFilters,
   1.223 +                                         nsIFrame *aFilteredFrame) :
   1.224 +  mFilters(aFilters)
   1.225 +{
   1.226 +  for (uint32_t i = 0; i < mFilters.Length(); i++) {
   1.227 +    if (mFilters[i].GetType() != NS_STYLE_FILTER_URL)
   1.228 +      continue;
   1.229 +
   1.230 +    nsSVGFilterReference *reference =
   1.231 +      new nsSVGFilterReference(mFilters[i].GetURL(), aFilteredFrame);
   1.232 +    NS_ADDREF(reference);
   1.233 +    mReferences.AppendElement(reference);
   1.234 +  }
   1.235 +}
   1.236 +
   1.237 +nsSVGFilterProperty::~nsSVGFilterProperty()
   1.238 +{
   1.239 +  for (uint32_t i = 0; i < mReferences.Length(); i++) {
   1.240 +    NS_RELEASE(mReferences[i]);
   1.241 +  }
   1.242 +}
   1.243 +
   1.244 +bool
   1.245 +nsSVGFilterProperty::ReferencesValidResources()
   1.246 +{
   1.247 +  for (uint32_t i = 0; i < mReferences.Length(); i++) {
   1.248 +    if (!mReferences[i]->ReferencesValidResource())
   1.249 +      return false;
   1.250 +  }
   1.251 +  return true;
   1.252 +}
   1.253 +
   1.254 +bool
   1.255 +nsSVGFilterProperty::IsInObserverLists() const
   1.256 +{
   1.257 +  for (uint32_t i = 0; i < mReferences.Length(); i++) {
   1.258 +    if (!mReferences[i]->IsInObserverList())
   1.259 +      return false;
   1.260 +  }
   1.261 +  return true;
   1.262 +}
   1.263 +
   1.264 +void
   1.265 +nsSVGFilterProperty::Invalidate()
   1.266 +{
   1.267 +  for (uint32_t i = 0; i < mReferences.Length(); i++) {
   1.268 +    mReferences[i]->Invalidate();
   1.269 +  }
   1.270 +}
   1.271 +
   1.272 +NS_IMPL_ISUPPORTS_INHERITED(nsSVGFilterReference,
   1.273 +                            nsSVGIDRenderingObserver,
   1.274 +                            nsISVGFilterReference);
   1.275 +
   1.276 +nsSVGFilterFrame *
   1.277 +nsSVGFilterReference::GetFilterFrame()
   1.278 +{
   1.279 +  return static_cast<nsSVGFilterFrame *>
   1.280 +    (GetReferencedFrame(nsGkAtoms::svgFilterFrame, nullptr));
   1.281 +}
   1.282 +
   1.283 +static void
   1.284 +InvalidateAllContinuations(nsIFrame* aFrame)
   1.285 +{
   1.286 +  for (nsIFrame* f = aFrame; f;
   1.287 +       f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) {
   1.288 +    f->InvalidateFrame();
   1.289 +  }
   1.290 +}
   1.291 +
   1.292 +void
   1.293 +nsSVGFilterReference::DoUpdate()
   1.294 +{
   1.295 +  nsSVGIDRenderingObserver::DoUpdate();
   1.296 +  if (!mFrame)
   1.297 +    return;
   1.298 +
   1.299 +  // Repaint asynchronously in case the filter frame is being torn down
   1.300 +  nsChangeHint changeHint =
   1.301 +    nsChangeHint(nsChangeHint_RepaintFrame);
   1.302 +
   1.303 +  // Don't need to request UpdateOverflow if we're being reflowed.
   1.304 +  if (!(mFrame->GetStateBits() & NS_FRAME_IN_REFLOW)) {
   1.305 +    NS_UpdateHint(changeHint, nsChangeHint_UpdateOverflow);
   1.306 +  }
   1.307 +  mFramePresShell->GetPresContext()->RestyleManager()->PostRestyleEvent(
   1.308 +    mFrame->GetContent()->AsElement(), nsRestyleHint(0), changeHint);
   1.309 +}
   1.310 +
   1.311 +void
   1.312 +nsSVGMarkerProperty::DoUpdate()
   1.313 +{
   1.314 +  nsSVGIDRenderingObserver::DoUpdate();
   1.315 +  if (!mFrame)
   1.316 +    return;
   1.317 +
   1.318 +  NS_ASSERTION(mFrame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected");
   1.319 +
   1.320 +  // Repaint asynchronously in case the marker frame is being torn down
   1.321 +  nsChangeHint changeHint =
   1.322 +    nsChangeHint(nsChangeHint_RepaintFrame);
   1.323 +  
   1.324 +  // Don't need to request ReflowFrame if we're being reflowed.
   1.325 +  if (!(mFrame->GetStateBits() & NS_FRAME_IN_REFLOW)) {
   1.326 +    // XXXjwatt: We need to unify SVG into standard reflow so we can just use
   1.327 +    // nsChangeHint_NeedReflow | nsChangeHint_NeedDirtyReflow here.
   1.328 +    nsSVGEffects::InvalidateRenderingObservers(mFrame);
   1.329 +    // XXXSDL KILL THIS!!!
   1.330 +    nsSVGUtils::ScheduleReflowSVG(mFrame);
   1.331 +  }
   1.332 +  mFramePresShell->GetPresContext()->RestyleManager()->PostRestyleEvent(
   1.333 +    mFrame->GetContent()->AsElement(), nsRestyleHint(0), changeHint);
   1.334 +}
   1.335 +
   1.336 +bool
   1.337 +nsSVGTextPathProperty::TargetIsValid()
   1.338 +{
   1.339 +  Element* target = GetTarget();
   1.340 +  return target && target->IsSVG(nsGkAtoms::path);
   1.341 +}
   1.342 +
   1.343 +void
   1.344 +nsSVGTextPathProperty::DoUpdate()
   1.345 +{
   1.346 +  nsSVGIDRenderingObserver::DoUpdate();
   1.347 +  if (!mFrame)
   1.348 +    return;
   1.349 +
   1.350 +  NS_ASSERTION(mFrame->IsFrameOfType(nsIFrame::eSVG) || mFrame->IsSVGText(),
   1.351 +               "SVG frame expected");
   1.352 +
   1.353 +  // Avoid getting into an infinite loop of reflows if the <textPath> is
   1.354 +  // pointing to one of its ancestors.  TargetIsValid returns true iff
   1.355 +  // the target element is a <path> element, and we would not have this
   1.356 +  // nsSVGTextPathProperty if this <textPath> were a descendant of the
   1.357 +  // target <path>.
   1.358 +  //
   1.359 +  // Note that we still have to post the restyle event when we
   1.360 +  // change from being valid to invalid, so that mPositions on the
   1.361 +  // SVGTextFrame gets updated, skipping the <textPath>, ensuring
   1.362 +  // that nothing gets painted for that element.
   1.363 +  bool nowValid = TargetIsValid();
   1.364 +  if (!mValid && !nowValid) {
   1.365 +    // Just return if we were previously invalid, and are still invalid.
   1.366 +    return;
   1.367 +  }
   1.368 +  mValid = nowValid;
   1.369 +
   1.370 +  // Repaint asynchronously in case the path frame is being torn down
   1.371 +  nsChangeHint changeHint =
   1.372 +    nsChangeHint(nsChangeHint_RepaintFrame | nsChangeHint_UpdateTextPath);
   1.373 +  mFramePresShell->GetPresContext()->RestyleManager()->PostRestyleEvent(
   1.374 +    mFrame->GetContent()->AsElement(), nsRestyleHint(0), changeHint);
   1.375 +}
   1.376 +
   1.377 +void
   1.378 +nsSVGPaintingProperty::DoUpdate()
   1.379 +{
   1.380 +  nsSVGIDRenderingObserver::DoUpdate();
   1.381 +  if (!mFrame)
   1.382 +    return;
   1.383 +
   1.384 +  if (mFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
   1.385 +    nsSVGEffects::InvalidateRenderingObservers(mFrame);
   1.386 +    mFrame->InvalidateFrameSubtree();
   1.387 +  } else {
   1.388 +    InvalidateAllContinuations(mFrame);
   1.389 +  }
   1.390 +}
   1.391 +
   1.392 +static nsSVGRenderingObserver *
   1.393 +CreateMarkerProperty(nsIURI *aURI, nsIFrame *aFrame, bool aReferenceImage)
   1.394 +{ return new nsSVGMarkerProperty(aURI, aFrame, aReferenceImage); }
   1.395 +
   1.396 +static nsSVGRenderingObserver *
   1.397 +CreateTextPathProperty(nsIURI *aURI, nsIFrame *aFrame, bool aReferenceImage)
   1.398 +{ return new nsSVGTextPathProperty(aURI, aFrame, aReferenceImage); }
   1.399 +
   1.400 +static nsSVGRenderingObserver *
   1.401 +CreatePaintingProperty(nsIURI *aURI, nsIFrame *aFrame, bool aReferenceImage)
   1.402 +{ return new nsSVGPaintingProperty(aURI, aFrame, aReferenceImage); }
   1.403 +
   1.404 +static nsSVGRenderingObserver *
   1.405 +GetEffectProperty(nsIURI *aURI, nsIFrame *aFrame,
   1.406 +                  const FramePropertyDescriptor *aProperty,
   1.407 +                  nsSVGRenderingObserver * (* aCreate)(nsIURI *, nsIFrame *, bool))
   1.408 +{
   1.409 +  if (!aURI)
   1.410 +    return nullptr;
   1.411 +
   1.412 +  FrameProperties props = aFrame->Properties();
   1.413 +  nsSVGRenderingObserver *prop =
   1.414 +    static_cast<nsSVGRenderingObserver*>(props.Get(aProperty));
   1.415 +  if (prop)
   1.416 +    return prop;
   1.417 +  prop = aCreate(aURI, aFrame, false);
   1.418 +  if (!prop)
   1.419 +    return nullptr;
   1.420 +  NS_ADDREF(prop);
   1.421 +  props.Set(aProperty, static_cast<nsISupports*>(prop));
   1.422 +  return prop;
   1.423 +}
   1.424 +
   1.425 +static nsSVGFilterProperty*
   1.426 +GetOrCreateFilterProperty(nsIFrame *aFrame)
   1.427 +{
   1.428 +  const nsStyleSVGReset* style = aFrame->StyleSVGReset();
   1.429 +  if (!style->HasFilters())
   1.430 +    return nullptr;
   1.431 +
   1.432 +  FrameProperties props = aFrame->Properties();
   1.433 +  nsSVGFilterProperty *prop =
   1.434 +    static_cast<nsSVGFilterProperty*>(props.Get(nsSVGEffects::FilterProperty()));
   1.435 +  if (prop)
   1.436 +    return prop;
   1.437 +  prop = new nsSVGFilterProperty(style->mFilters, aFrame);
   1.438 +  if (!prop)
   1.439 +    return nullptr;
   1.440 +  NS_ADDREF(prop);
   1.441 +  props.Set(nsSVGEffects::FilterProperty(), static_cast<nsISupports*>(prop));
   1.442 +  return prop;
   1.443 +}
   1.444 +
   1.445 +nsSVGMarkerProperty *
   1.446 +nsSVGEffects::GetMarkerProperty(nsIURI *aURI, nsIFrame *aFrame,
   1.447 +                                const FramePropertyDescriptor *aProp)
   1.448 +{
   1.449 +  NS_ABORT_IF_FALSE(aFrame->GetType() == nsGkAtoms::svgPathGeometryFrame &&
   1.450 +                      static_cast<nsSVGPathGeometryElement*>(aFrame->GetContent())->IsMarkable(),
   1.451 +                    "Bad frame");
   1.452 +  return static_cast<nsSVGMarkerProperty*>(
   1.453 +          GetEffectProperty(aURI, aFrame, aProp, CreateMarkerProperty));
   1.454 +}
   1.455 +
   1.456 +nsSVGTextPathProperty *
   1.457 +nsSVGEffects::GetTextPathProperty(nsIURI *aURI, nsIFrame *aFrame,
   1.458 +                                  const FramePropertyDescriptor *aProp)
   1.459 +{
   1.460 +  return static_cast<nsSVGTextPathProperty*>(
   1.461 +          GetEffectProperty(aURI, aFrame, aProp, CreateTextPathProperty));
   1.462 +}
   1.463 +
   1.464 +nsSVGPaintingProperty *
   1.465 +nsSVGEffects::GetPaintingProperty(nsIURI *aURI, nsIFrame *aFrame,
   1.466 +                                  const FramePropertyDescriptor *aProp)
   1.467 +{
   1.468 +  return static_cast<nsSVGPaintingProperty*>(
   1.469 +          GetEffectProperty(aURI, aFrame, aProp, CreatePaintingProperty));
   1.470 +}
   1.471 +
   1.472 +static nsSVGRenderingObserver *
   1.473 +GetEffectPropertyForURI(nsIURI *aURI, nsIFrame *aFrame,
   1.474 +                        const FramePropertyDescriptor *aProperty,
   1.475 +                        nsSVGRenderingObserver * (* aCreate)(nsIURI *, nsIFrame *, bool))
   1.476 +{
   1.477 +  if (!aURI)
   1.478 +    return nullptr;
   1.479 +
   1.480 +  FrameProperties props = aFrame->Properties();
   1.481 +  nsSVGEffects::URIObserverHashtable *hashtable =
   1.482 +    static_cast<nsSVGEffects::URIObserverHashtable*>(props.Get(aProperty));
   1.483 +  if (!hashtable) {
   1.484 +    hashtable = new nsSVGEffects::URIObserverHashtable();
   1.485 +    props.Set(aProperty, hashtable);
   1.486 +  }
   1.487 +  nsSVGRenderingObserver* prop =
   1.488 +    static_cast<nsSVGRenderingObserver*>(hashtable->GetWeak(aURI));
   1.489 +  if (!prop) {
   1.490 +    bool watchImage = aProperty == nsSVGEffects::BackgroundImageProperty();
   1.491 +    prop = aCreate(aURI, aFrame, watchImage);
   1.492 +    hashtable->Put(aURI, prop);
   1.493 +  }
   1.494 +  return prop;
   1.495 +}
   1.496 +
   1.497 +nsSVGPaintingProperty *
   1.498 +nsSVGEffects::GetPaintingPropertyForURI(nsIURI *aURI, nsIFrame *aFrame,
   1.499 +                                        const FramePropertyDescriptor *aProp)
   1.500 +{
   1.501 +  return static_cast<nsSVGPaintingProperty*>(
   1.502 +          GetEffectPropertyForURI(aURI, aFrame, aProp, CreatePaintingProperty));
   1.503 +}
   1.504 +
   1.505 +nsSVGEffects::EffectProperties
   1.506 +nsSVGEffects::GetEffectProperties(nsIFrame *aFrame)
   1.507 +{
   1.508 +  NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation");
   1.509 +
   1.510 +  EffectProperties result;
   1.511 +  const nsStyleSVGReset *style = aFrame->StyleSVGReset();
   1.512 +  result.mFilter = GetOrCreateFilterProperty(aFrame);
   1.513 +  result.mClipPath =
   1.514 +    GetPaintingProperty(style->mClipPath, aFrame, ClipPathProperty());
   1.515 +  result.mMask =
   1.516 +    GetPaintingProperty(style->mMask, aFrame, MaskProperty());
   1.517 +  return result;
   1.518 +}
   1.519 +
   1.520 +nsSVGPaintServerFrame *
   1.521 +nsSVGEffects::GetPaintServer(nsIFrame *aTargetFrame, const nsStyleSVGPaint *aPaint,
   1.522 +                             const FramePropertyDescriptor *aType)
   1.523 +{
   1.524 +  if (aPaint->mType != eStyleSVGPaintType_Server)
   1.525 +    return nullptr;
   1.526 +
   1.527 +  nsIFrame *frame = aTargetFrame->GetContent()->IsNodeOfType(nsINode::eTEXT) ?
   1.528 +                      aTargetFrame->GetParent() : aTargetFrame;
   1.529 +  nsSVGPaintingProperty *property =
   1.530 +    nsSVGEffects::GetPaintingProperty(aPaint->mPaint.mPaintServer, frame, aType);
   1.531 +  if (!property)
   1.532 +    return nullptr;
   1.533 +  nsIFrame *result = property->GetReferencedFrame();
   1.534 +  if (!result)
   1.535 +    return nullptr;
   1.536 +
   1.537 +  nsIAtom *type = result->GetType();
   1.538 +  if (type != nsGkAtoms::svgLinearGradientFrame &&
   1.539 +      type != nsGkAtoms::svgRadialGradientFrame &&
   1.540 +      type != nsGkAtoms::svgPatternFrame)
   1.541 +    return nullptr;
   1.542 +
   1.543 +  return static_cast<nsSVGPaintServerFrame*>(result);
   1.544 +}
   1.545 + 
   1.546 +nsSVGClipPathFrame *
   1.547 +nsSVGEffects::EffectProperties::GetClipPathFrame(bool *aOK)
   1.548 +{
   1.549 +  if (!mClipPath)
   1.550 +    return nullptr;
   1.551 +  nsSVGClipPathFrame *frame = static_cast<nsSVGClipPathFrame *>
   1.552 +    (mClipPath->GetReferencedFrame(nsGkAtoms::svgClipPathFrame, aOK));
   1.553 +  if (frame && aOK && *aOK) {
   1.554 +    *aOK = frame->IsValid();
   1.555 +  }
   1.556 +  return frame;
   1.557 +}
   1.558 +
   1.559 +nsSVGMaskFrame *
   1.560 +nsSVGEffects::EffectProperties::GetMaskFrame(bool *aOK)
   1.561 +{
   1.562 +  if (!mMask)
   1.563 +    return nullptr;
   1.564 +  return static_cast<nsSVGMaskFrame *>
   1.565 +    (mMask->GetReferencedFrame(nsGkAtoms::svgMaskFrame, aOK));
   1.566 +}
   1.567 +
   1.568 +void
   1.569 +nsSVGEffects::UpdateEffects(nsIFrame *aFrame)
   1.570 +{
   1.571 +  NS_ASSERTION(aFrame->GetContent()->IsElement(),
   1.572 +               "aFrame's content should be an element");
   1.573 +
   1.574 +  FrameProperties props = aFrame->Properties();
   1.575 +  props.Delete(FilterProperty());
   1.576 +  props.Delete(MaskProperty());
   1.577 +  props.Delete(ClipPathProperty());
   1.578 +  props.Delete(MarkerBeginProperty());
   1.579 +  props.Delete(MarkerMiddleProperty());
   1.580 +  props.Delete(MarkerEndProperty());
   1.581 +  props.Delete(FillProperty());
   1.582 +  props.Delete(StrokeProperty());
   1.583 +  props.Delete(BackgroundImageProperty());
   1.584 +
   1.585 +  // Ensure that the filter is repainted correctly
   1.586 +  // We can't do that in DoUpdate as the referenced frame may not be valid
   1.587 +  GetOrCreateFilterProperty(aFrame);
   1.588 +
   1.589 +  if (aFrame->GetType() == nsGkAtoms::svgPathGeometryFrame &&
   1.590 +      static_cast<nsSVGPathGeometryElement*>(aFrame->GetContent())->IsMarkable()) {
   1.591 +    // Set marker properties here to avoid reference loops
   1.592 +    const nsStyleSVG *style = aFrame->StyleSVG();
   1.593 +    GetEffectProperty(style->mMarkerStart, aFrame, MarkerBeginProperty(),
   1.594 +                      CreateMarkerProperty);
   1.595 +    GetEffectProperty(style->mMarkerMid, aFrame, MarkerMiddleProperty(),
   1.596 +                      CreateMarkerProperty);
   1.597 +    GetEffectProperty(style->mMarkerEnd, aFrame, MarkerEndProperty(),
   1.598 +                      CreateMarkerProperty);
   1.599 +  }
   1.600 +}
   1.601 +
   1.602 +nsSVGFilterProperty *
   1.603 +nsSVGEffects::GetFilterProperty(nsIFrame *aFrame)
   1.604 +{
   1.605 +  NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation");
   1.606 +
   1.607 +  if (!aFrame->StyleSVGReset()->HasFilters())
   1.608 +    return nullptr;
   1.609 +
   1.610 +  return static_cast<nsSVGFilterProperty *>
   1.611 +    (aFrame->Properties().Get(FilterProperty()));
   1.612 +}
   1.613 +
   1.614 +static PLDHashOperator
   1.615 +GatherEnumerator(nsPtrHashKey<nsSVGRenderingObserver>* aEntry, void* aArg)
   1.616 +{
   1.617 +  nsTArray<nsSVGRenderingObserver*>* array =
   1.618 +    static_cast<nsTArray<nsSVGRenderingObserver*>*>(aArg);
   1.619 +  array->AppendElement(aEntry->GetKey());
   1.620 +          
   1.621 +  return PL_DHASH_REMOVE;
   1.622 +}
   1.623 +
   1.624 +static PLDHashOperator
   1.625 +GatherEnumeratorForReflow(nsPtrHashKey<nsSVGRenderingObserver>* aEntry, void* aArg)
   1.626 +{
   1.627 +  if (!aEntry->GetKey()->ObservesReflow()) {
   1.628 +    return PL_DHASH_NEXT;
   1.629 +  }
   1.630 +
   1.631 +  nsTArray<nsSVGRenderingObserver*>* array =
   1.632 +    static_cast<nsTArray<nsSVGRenderingObserver*>*>(aArg);
   1.633 +  array->AppendElement(aEntry->GetKey());
   1.634 +          
   1.635 +  return PL_DHASH_REMOVE;
   1.636 +}
   1.637 +
   1.638 +void
   1.639 +nsSVGRenderingObserverList::InvalidateAll()
   1.640 +{
   1.641 +  if (mObservers.Count() == 0)
   1.642 +    return;
   1.643 +
   1.644 +  nsAutoTArray<nsSVGRenderingObserver*,10> observers;
   1.645 +
   1.646 +  // The PL_DHASH_REMOVE in GatherEnumerator drops all our observers here:
   1.647 +  mObservers.EnumerateEntries(GatherEnumerator, &observers);
   1.648 +
   1.649 +  for (uint32_t i = 0; i < observers.Length(); ++i) {
   1.650 +    observers[i]->InvalidateViaReferencedElement();
   1.651 +  }
   1.652 +}
   1.653 +
   1.654 +void
   1.655 +nsSVGRenderingObserverList::InvalidateAllForReflow()
   1.656 +{
   1.657 +  if (mObservers.Count() == 0)
   1.658 +    return;
   1.659 +
   1.660 +  nsAutoTArray<nsSVGRenderingObserver*,10> observers;
   1.661 +
   1.662 +  // The PL_DHASH_REMOVE in GatherEnumerator drops all our observers here:
   1.663 +  mObservers.EnumerateEntries(GatherEnumeratorForReflow, &observers);
   1.664 +
   1.665 +  for (uint32_t i = 0; i < observers.Length(); ++i) {
   1.666 +    observers[i]->InvalidateViaReferencedElement();
   1.667 +  }
   1.668 +}
   1.669 +
   1.670 +void
   1.671 +nsSVGRenderingObserverList::RemoveAll()
   1.672 +{
   1.673 +  nsAutoTArray<nsSVGRenderingObserver*,10> observers;
   1.674 +
   1.675 +  // The PL_DHASH_REMOVE in GatherEnumerator drops all our observers here:
   1.676 +  mObservers.EnumerateEntries(GatherEnumerator, &observers);
   1.677 +
   1.678 +  // Our list is now cleared.  We need to notify the observers we've removed,
   1.679 +  // so they can update their state & remove themselves as mutation-observers.
   1.680 +  for (uint32_t i = 0; i < observers.Length(); ++i) {
   1.681 +    observers[i]->NotifyEvictedFromRenderingObserverList();
   1.682 +  }
   1.683 +}
   1.684 +
   1.685 +void
   1.686 +nsSVGEffects::AddRenderingObserver(Element *aElement, nsSVGRenderingObserver *aObserver)
   1.687 +{
   1.688 +  nsSVGRenderingObserverList *observerList = GetObserverList(aElement);
   1.689 +  if (!observerList) {
   1.690 +    observerList = new nsSVGRenderingObserverList();
   1.691 +    if (!observerList)
   1.692 +      return;
   1.693 +    aElement->SetProperty(nsGkAtoms::renderingobserverlist, observerList,
   1.694 +                          nsINode::DeleteProperty<nsSVGRenderingObserverList>);
   1.695 +  }
   1.696 +  aElement->SetHasRenderingObservers(true);
   1.697 +  observerList->Add(aObserver);
   1.698 +}
   1.699 +
   1.700 +void
   1.701 +nsSVGEffects::RemoveRenderingObserver(Element *aElement, nsSVGRenderingObserver *aObserver)
   1.702 +{
   1.703 +  nsSVGRenderingObserverList *observerList = GetObserverList(aElement);
   1.704 +  if (observerList) {
   1.705 +    NS_ASSERTION(observerList->Contains(aObserver),
   1.706 +                 "removing observer from an element we're not observing?");
   1.707 +    observerList->Remove(aObserver);
   1.708 +    if (observerList->IsEmpty()) {
   1.709 +      aElement->SetHasRenderingObservers(false);
   1.710 +    }
   1.711 +  }
   1.712 +}
   1.713 +
   1.714 +void
   1.715 +nsSVGEffects::RemoveAllRenderingObservers(Element *aElement)
   1.716 +{
   1.717 +  nsSVGRenderingObserverList *observerList = GetObserverList(aElement);
   1.718 +  if (observerList) {
   1.719 +    observerList->RemoveAll();
   1.720 +    aElement->SetHasRenderingObservers(false);
   1.721 +  }
   1.722 +}
   1.723 +
   1.724 +void
   1.725 +nsSVGEffects::InvalidateRenderingObservers(nsIFrame *aFrame)
   1.726 +{
   1.727 +  NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame must be first continuation");
   1.728 +
   1.729 +  if (!aFrame->GetContent()->IsElement())
   1.730 +    return;
   1.731 +
   1.732 +  nsSVGRenderingObserverList *observerList =
   1.733 +    GetObserverList(aFrame->GetContent()->AsElement());
   1.734 +  if (observerList) {
   1.735 +    observerList->InvalidateAll();
   1.736 +    return;
   1.737 +  }
   1.738 +
   1.739 +  // Check ancestor SVG containers. The root frame cannot be of type
   1.740 +  // eSVGContainer so we don't have to check f for null here.
   1.741 +  for (nsIFrame *f = aFrame->GetParent();
   1.742 +       f->IsFrameOfType(nsIFrame::eSVGContainer); f = f->GetParent()) {
   1.743 +    if (f->GetContent()->IsElement()) {
   1.744 +      observerList = GetObserverList(f->GetContent()->AsElement());
   1.745 +      if (observerList) {
   1.746 +        observerList->InvalidateAll();
   1.747 +        return;
   1.748 +      }
   1.749 +    }
   1.750 +  }
   1.751 +}
   1.752 +
   1.753 +void
   1.754 +nsSVGEffects::InvalidateDirectRenderingObservers(Element *aElement, uint32_t aFlags /* = 0 */)
   1.755 +{
   1.756 +  if (aElement->HasRenderingObservers()) {
   1.757 +    nsSVGRenderingObserverList *observerList = GetObserverList(aElement);
   1.758 +    if (observerList) {
   1.759 +      if (aFlags & INVALIDATE_REFLOW) {
   1.760 +        observerList->InvalidateAllForReflow();
   1.761 +      } else {
   1.762 +        observerList->InvalidateAll();
   1.763 +      }
   1.764 +    }
   1.765 +  }
   1.766 +}
   1.767 +
   1.768 +void
   1.769 +nsSVGEffects::InvalidateDirectRenderingObservers(nsIFrame *aFrame, uint32_t aFlags /* = 0 */)
   1.770 +{
   1.771 +  if (aFrame->GetContent() && aFrame->GetContent()->IsElement()) {
   1.772 +    InvalidateDirectRenderingObservers(aFrame->GetContent()->AsElement(), aFlags);
   1.773 +  }
   1.774 +}

mercurial