layout/svg/nsSVGEffects.h

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/svg/nsSVGEffects.h	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,475 @@
     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 +#ifndef NSSVGEFFECTS_H_
    1.10 +#define NSSVGEFFECTS_H_
    1.11 +
    1.12 +#include "mozilla/Attributes.h"
    1.13 +#include "FramePropertyTable.h"
    1.14 +#include "mozilla/dom/Element.h"
    1.15 +#include "nsHashKeys.h"
    1.16 +#include "nsID.h"
    1.17 +#include "nsIFrame.h"
    1.18 +#include "nsIMutationObserver.h"
    1.19 +#include "nsInterfaceHashtable.h"
    1.20 +#include "nsISupportsBase.h"
    1.21 +#include "nsISupportsImpl.h"
    1.22 +#include "nsReferencedElement.h"
    1.23 +#include "nsStubMutationObserver.h"
    1.24 +#include "nsSVGUtils.h"
    1.25 +#include "nsTHashtable.h"
    1.26 +#include "nsURIHashKey.h"
    1.27 +
    1.28 +class nsIAtom;
    1.29 +class nsIPresShell;
    1.30 +class nsIURI;
    1.31 +class nsSVGClipPathFrame;
    1.32 +class nsSVGPaintServerFrame;
    1.33 +class nsSVGFilterFrame;
    1.34 +class nsSVGMaskFrame;
    1.35 +
    1.36 +/*
    1.37 + * This interface allows us to be notified when a piece of SVG content is
    1.38 + * re-rendered.
    1.39 + *
    1.40 + * Concrete implementations of this interface need to implement
    1.41 + * "GetTarget()" to specify the piece of SVG content that they'd like to
    1.42 + * monitor, and they need to implement "DoUpdate" to specify how we'll react
    1.43 + * when that content gets re-rendered. They also need to implement a
    1.44 + * constructor and destructor, which should call StartListening and
    1.45 + * StopListening, respectively.
    1.46 + */
    1.47 +class nsSVGRenderingObserver : public nsStubMutationObserver {
    1.48 +public:
    1.49 +  typedef mozilla::dom::Element Element;
    1.50 +  nsSVGRenderingObserver()
    1.51 +    : mInObserverList(false)
    1.52 +    {}
    1.53 +  virtual ~nsSVGRenderingObserver()
    1.54 +    {}
    1.55 +
    1.56 +  // nsISupports
    1.57 +  NS_DECL_ISUPPORTS
    1.58 +
    1.59 +  // nsIMutationObserver
    1.60 +  NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
    1.61 +  NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
    1.62 +  NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
    1.63 +  NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
    1.64 +
    1.65 +  void InvalidateViaReferencedElement();
    1.66 +
    1.67 +  // When a nsSVGRenderingObserver list gets forcibly cleared, it uses this
    1.68 +  // callback to notify every observer that's cleared from it, so they can
    1.69 +  // react.
    1.70 +  void NotifyEvictedFromRenderingObserverList();
    1.71 +
    1.72 +  bool IsInObserverList() const { return mInObserverList; }
    1.73 +
    1.74 +  nsIFrame* GetReferencedFrame();
    1.75 +  /**
    1.76 +   * @param aOK this is only for the convenience of callers. We set *aOK to false
    1.77 +   * if the frame is the wrong type
    1.78 +   */
    1.79 +  nsIFrame* GetReferencedFrame(nsIAtom* aFrameType, bool* aOK);
    1.80 +
    1.81 +  Element* GetReferencedElement();
    1.82 +
    1.83 +  virtual bool ObservesReflow() { return true; }
    1.84 +
    1.85 +protected:
    1.86 +  // Non-virtual protected methods
    1.87 +  void StartListening();
    1.88 +  void StopListening();
    1.89 +
    1.90 +  // Virtual protected methods
    1.91 +  virtual void DoUpdate() = 0; // called when the referenced resource changes.
    1.92 +
    1.93 +  // This is an internally-used version of GetReferencedElement that doesn't
    1.94 +  // forcibly add us as an observer. (whereas GetReferencedElement does)
    1.95 +  virtual Element* GetTarget() = 0;
    1.96 +
    1.97 +  // Whether we're in our referenced element's observer list at this time.
    1.98 +  bool mInObserverList;
    1.99 +};
   1.100 +
   1.101 +
   1.102 +/*
   1.103 + * SVG elements reference supporting resources by element ID. We need to
   1.104 + * track when those resources change and when the DOM changes in ways
   1.105 + * that affect which element is referenced by a given ID (e.g., when
   1.106 + * element IDs change). The code here is responsible for that.
   1.107 + * 
   1.108 + * When a frame references a supporting resource, we create a property
   1.109 + * object derived from nsSVGIDRenderingObserver to manage the relationship. The
   1.110 + * property object is attached to the referencing frame.
   1.111 + */
   1.112 +class nsSVGIDRenderingObserver : public nsSVGRenderingObserver {
   1.113 +public:
   1.114 +  typedef mozilla::dom::Element Element;
   1.115 +  nsSVGIDRenderingObserver(nsIURI* aURI, nsIFrame *aFrame,
   1.116 +                         bool aReferenceImage);
   1.117 +  virtual ~nsSVGIDRenderingObserver();
   1.118 +
   1.119 +protected:
   1.120 +  Element* GetTarget() MOZ_OVERRIDE { return mElement.get(); }
   1.121 +
   1.122 +  // This is called when the referenced resource changes.
   1.123 +  virtual void DoUpdate() MOZ_OVERRIDE;
   1.124 +
   1.125 +  class SourceReference : public nsReferencedElement {
   1.126 +  public:
   1.127 +    SourceReference(nsSVGIDRenderingObserver* aContainer) : mContainer(aContainer) {}
   1.128 +  protected:
   1.129 +    virtual void ElementChanged(Element* aFrom, Element* aTo) MOZ_OVERRIDE {
   1.130 +      mContainer->StopListening();
   1.131 +      nsReferencedElement::ElementChanged(aFrom, aTo);
   1.132 +      mContainer->StartListening();
   1.133 +      mContainer->DoUpdate();
   1.134 +    }
   1.135 +    /**
   1.136 +     * Override IsPersistent because we want to keep tracking the element
   1.137 +     * for the ID even when it changes.
   1.138 +     */
   1.139 +    virtual bool IsPersistent() MOZ_OVERRIDE { return true; }
   1.140 +  private:
   1.141 +    nsSVGIDRenderingObserver* mContainer;
   1.142 +  };
   1.143 +  
   1.144 +  SourceReference mElement;
   1.145 +  // The frame that this property is attached to
   1.146 +  nsIFrame *mFrame;
   1.147 +  // When a presshell is torn down, we don't delete the properties for
   1.148 +  // each frame until after the frames are destroyed. So here we remember
   1.149 +  // the presshell for the frames we care about and, before we use the frame,
   1.150 +  // we test the presshell to see if it's destroying itself. If it is,
   1.151 +  // then the frame pointer is not valid and we know the frame has gone away.
   1.152 +  nsIPresShell *mFramePresShell;
   1.153 +};
   1.154 +
   1.155 +/**
   1.156 + * In a filter chain, there can be multiple SVG reference filters.
   1.157 + * e.g. filter: url(#svg-filter-1) blur(10px) url(#svg-filter-2);
   1.158 + *
   1.159 + * This class keeps track of one SVG reference filter in a filter chain.
   1.160 + * e.g. url(#svg-filter-1)
   1.161 + *
   1.162 + * It fires invalidations when the SVG filter element's id changes or when
   1.163 + * the SVG filter element's content changes.
   1.164 + *
   1.165 + * The nsSVGFilterProperty class manages a list of nsSVGFilterReferences.
   1.166 + */
   1.167 +class nsSVGFilterReference :
   1.168 +  public nsSVGIDRenderingObserver, public nsISVGFilterReference {
   1.169 +public:
   1.170 +  nsSVGFilterReference(nsIURI *aURI, nsIFrame *aFilteredFrame)
   1.171 +    : nsSVGIDRenderingObserver(aURI, aFilteredFrame, false) {}
   1.172 +
   1.173 +  bool ReferencesValidResource() { return GetFilterFrame(); }
   1.174 +
   1.175 +  /**
   1.176 +   * @return the filter frame, or null if there is no filter frame
   1.177 +   */
   1.178 +  nsSVGFilterFrame *GetFilterFrame();
   1.179 +
   1.180 +  // nsISupports
   1.181 +  NS_DECL_ISUPPORTS
   1.182 +
   1.183 +  // nsISVGFilterReference
   1.184 +  virtual void Invalidate() MOZ_OVERRIDE { DoUpdate(); };
   1.185 +
   1.186 +private:
   1.187 +  // nsSVGIDRenderingObserver
   1.188 +  virtual void DoUpdate() MOZ_OVERRIDE;
   1.189 +};
   1.190 +
   1.191 +/**
   1.192 + * This class manages a list of nsSVGFilterReferences, which represent SVG
   1.193 + * reference filters in a filter chain.
   1.194 + * e.g. filter: url(#svg-filter-1) blur(10px) url(#svg-filter-2);
   1.195 + *
   1.196 + * In the above example, the nsSVGFilterProperty will manage two
   1.197 + * nsSVGFilterReferences, one for each SVG reference filter. CSS filters like
   1.198 + * "blur(10px)" don't reference filter elements, so they don't need an
   1.199 + * nsSVGFilterReference. The style system invalidates changes to CSS filters.
   1.200 + */
   1.201 +class nsSVGFilterProperty : public nsISupports {
   1.202 +public:
   1.203 +  nsSVGFilterProperty(const nsTArray<nsStyleFilter> &aFilters,
   1.204 +                      nsIFrame *aFilteredFrame);
   1.205 +  virtual ~nsSVGFilterProperty();
   1.206 +
   1.207 +  const nsTArray<nsStyleFilter>& GetFilters() { return mFilters; }
   1.208 +  bool ReferencesValidResources();
   1.209 +  bool IsInObserverLists() const;
   1.210 +  void Invalidate();
   1.211 +
   1.212 +  // nsISupports
   1.213 +  NS_DECL_ISUPPORTS
   1.214 +
   1.215 +private:
   1.216 +  nsTArray<nsSVGFilterReference*> mReferences;
   1.217 +  nsTArray<nsStyleFilter> mFilters;
   1.218 +};
   1.219 +
   1.220 +class nsSVGMarkerProperty : public nsSVGIDRenderingObserver {
   1.221 +public:
   1.222 +  nsSVGMarkerProperty(nsIURI *aURI, nsIFrame *aFrame, bool aReferenceImage)
   1.223 +    : nsSVGIDRenderingObserver(aURI, aFrame, aReferenceImage) {}
   1.224 +
   1.225 +protected:
   1.226 +  virtual void DoUpdate() MOZ_OVERRIDE;
   1.227 +};
   1.228 +
   1.229 +class nsSVGTextPathProperty : public nsSVGIDRenderingObserver {
   1.230 +public:
   1.231 +  nsSVGTextPathProperty(nsIURI *aURI, nsIFrame *aFrame, bool aReferenceImage)
   1.232 +    : nsSVGIDRenderingObserver(aURI, aFrame, aReferenceImage)
   1.233 +    , mValid(true) {}
   1.234 +
   1.235 +  virtual bool ObservesReflow() MOZ_OVERRIDE { return false; }
   1.236 +
   1.237 +protected:
   1.238 +  virtual void DoUpdate() MOZ_OVERRIDE;
   1.239 +
   1.240 +private:
   1.241 +  /**
   1.242 +   * Returns true if the target of the textPath is the frame of a 'path' element.
   1.243 +   */
   1.244 +  bool TargetIsValid();
   1.245 +
   1.246 +  bool mValid;
   1.247 +};
   1.248 + 
   1.249 +class nsSVGPaintingProperty : public nsSVGIDRenderingObserver {
   1.250 +public:
   1.251 +  nsSVGPaintingProperty(nsIURI *aURI, nsIFrame *aFrame, bool aReferenceImage)
   1.252 +    : nsSVGIDRenderingObserver(aURI, aFrame, aReferenceImage) {}
   1.253 +
   1.254 +protected:
   1.255 +  virtual void DoUpdate() MOZ_OVERRIDE;
   1.256 +};
   1.257 +
   1.258 +/**
   1.259 + * A manager for one-shot nsSVGRenderingObserver tracking.
   1.260 + * nsSVGRenderingObservers can be added or removed. They are not strongly
   1.261 + * referenced so an observer must be removed before it dies.
   1.262 + * When InvalidateAll is called, all outstanding references get
   1.263 + * InvalidateViaReferencedElement()
   1.264 + * called on them and the list is cleared. The intent is that
   1.265 + * the observer will force repainting of whatever part of the document
   1.266 + * is needed, and then at paint time the observer will do a clean lookup
   1.267 + * of the referenced element and [re-]add itself to the element's observer list.
   1.268 + * 
   1.269 + * InvalidateAll must be called before this object is destroyed, i.e.
   1.270 + * before the referenced frame is destroyed. This should normally happen
   1.271 + * via nsSVGContainerFrame::RemoveFrame, since only frames in the frame
   1.272 + * tree should be referenced.
   1.273 + */
   1.274 +class nsSVGRenderingObserverList {
   1.275 +public:
   1.276 +  nsSVGRenderingObserverList()
   1.277 +    : mObservers(5)
   1.278 +  {
   1.279 +    MOZ_COUNT_CTOR(nsSVGRenderingObserverList);
   1.280 +  }
   1.281 +
   1.282 +  ~nsSVGRenderingObserverList() {
   1.283 +    InvalidateAll();
   1.284 +    MOZ_COUNT_DTOR(nsSVGRenderingObserverList);
   1.285 +  }
   1.286 +
   1.287 +  void Add(nsSVGRenderingObserver* aObserver)
   1.288 +  { mObservers.PutEntry(aObserver); }
   1.289 +  void Remove(nsSVGRenderingObserver* aObserver)
   1.290 +  { mObservers.RemoveEntry(aObserver); }
   1.291 +#ifdef DEBUG
   1.292 +  bool Contains(nsSVGRenderingObserver* aObserver)
   1.293 +  { return (mObservers.GetEntry(aObserver) != nullptr); }
   1.294 +#endif
   1.295 +  bool IsEmpty()
   1.296 +  { return mObservers.Count() == 0; }
   1.297 +
   1.298 +  /**
   1.299 +   * Drop all our observers, and notify them that we have changed and dropped
   1.300 +   * our reference to them.
   1.301 +   */
   1.302 +  void InvalidateAll();
   1.303 +
   1.304 +  /**
   1.305 +   * Drop all observers that observe reflow, and notify them that we have changed and dropped
   1.306 +   * our reference to them.
   1.307 +   */
   1.308 +  void InvalidateAllForReflow();
   1.309 +
   1.310 +  /**
   1.311 +   * Drop all our observers, and notify them that we have dropped our reference
   1.312 +   * to them.
   1.313 +   */
   1.314 +  void RemoveAll();
   1.315 +
   1.316 +private:
   1.317 +  nsTHashtable<nsPtrHashKey<nsSVGRenderingObserver> > mObservers;
   1.318 +};
   1.319 +
   1.320 +class nsSVGEffects {
   1.321 +public:
   1.322 +  typedef mozilla::dom::Element Element;
   1.323 +  typedef mozilla::FramePropertyDescriptor FramePropertyDescriptor;
   1.324 +  typedef nsInterfaceHashtable<nsURIHashKey, nsIMutationObserver>
   1.325 +    URIObserverHashtable;
   1.326 +
   1.327 +  static void DestroySupports(void* aPropertyValue)
   1.328 +  {
   1.329 +    (static_cast<nsISupports*>(aPropertyValue))->Release();
   1.330 +  }
   1.331 +
   1.332 +  static void DestroyHashtable(void* aPropertyValue)
   1.333 +  {
   1.334 +    delete static_cast<URIObserverHashtable*> (aPropertyValue);
   1.335 +  }
   1.336 +
   1.337 +  NS_DECLARE_FRAME_PROPERTY(FilterProperty, DestroySupports)
   1.338 +  NS_DECLARE_FRAME_PROPERTY(MaskProperty, DestroySupports)
   1.339 +  NS_DECLARE_FRAME_PROPERTY(ClipPathProperty, DestroySupports)
   1.340 +  NS_DECLARE_FRAME_PROPERTY(MarkerBeginProperty, DestroySupports)
   1.341 +  NS_DECLARE_FRAME_PROPERTY(MarkerMiddleProperty, DestroySupports)
   1.342 +  NS_DECLARE_FRAME_PROPERTY(MarkerEndProperty, DestroySupports)
   1.343 +  NS_DECLARE_FRAME_PROPERTY(FillProperty, DestroySupports)
   1.344 +  NS_DECLARE_FRAME_PROPERTY(StrokeProperty, DestroySupports)
   1.345 +  NS_DECLARE_FRAME_PROPERTY(HrefProperty, DestroySupports)
   1.346 +  NS_DECLARE_FRAME_PROPERTY(BackgroundImageProperty, DestroyHashtable)
   1.347 +
   1.348 +  /**
   1.349 +   * Get the paint server for a aTargetFrame.
   1.350 +   */
   1.351 +  static nsSVGPaintServerFrame *GetPaintServer(nsIFrame *aTargetFrame,
   1.352 +                                               const nsStyleSVGPaint *aPaint,
   1.353 +                                               const FramePropertyDescriptor *aProperty);
   1.354 +
   1.355 +  struct EffectProperties {
   1.356 +    nsSVGFilterProperty*   mFilter;
   1.357 +    nsSVGPaintingProperty* mMask;
   1.358 +    nsSVGPaintingProperty* mClipPath;
   1.359 +
   1.360 +    /**
   1.361 +     * @return the clip-path frame, or null if there is no clip-path frame
   1.362 +     * @param aOK if a clip-path was specified and the designated element
   1.363 +     * exists but is an element of the wrong type, *aOK is set to false.
   1.364 +     * Otherwise *aOK is untouched.
   1.365 +     */
   1.366 +    nsSVGClipPathFrame *GetClipPathFrame(bool *aOK);
   1.367 +    /**
   1.368 +     * @return the mask frame, or null if there is no mask frame
   1.369 +     * @param aOK if a mask was specified and the designated element
   1.370 +     * exists but is an element of the wrong type, *aOK is set to false.
   1.371 +     * Otherwise *aOK is untouched.
   1.372 +     */
   1.373 +    nsSVGMaskFrame *GetMaskFrame(bool *aOK);
   1.374 +
   1.375 +    bool HasValidFilter() {
   1.376 +      return mFilter && mFilter->ReferencesValidResources();
   1.377 +    }
   1.378 +
   1.379 +    bool HasNoFilterOrHasValidFilter() {
   1.380 +      return !mFilter || mFilter->ReferencesValidResources();
   1.381 +    }
   1.382 +  };
   1.383 +
   1.384 +  /**
   1.385 +   * @param aFrame should be the first continuation
   1.386 +   */
   1.387 +  static EffectProperties GetEffectProperties(nsIFrame *aFrame);
   1.388 +
   1.389 +  /**
   1.390 +   * Called when changes to an element (e.g. CSS property changes) cause its
   1.391 +   * frame to start/stop referencing (or reference different) SVG resource
   1.392 +   * elements. (_Not_ called for changes to referenced resource elements.)
   1.393 +   *
   1.394 +   * This function handles such changes by discarding _all_ the frame's SVG
   1.395 +   * effects frame properties (causing those properties to stop watching their
   1.396 +   * target element). It also synchronously (re)creates the filter and marker
   1.397 +   * frame properties (XXX why not the other properties?), which makes it
   1.398 +   * useful for initializing those properties during first reflow.
   1.399 +   *
   1.400 +   * XXX rename to something more meaningful like RefreshResourceReferences?
   1.401 +   */
   1.402 +  static void UpdateEffects(nsIFrame *aFrame);
   1.403 +
   1.404 +  /**
   1.405 +   * @param aFrame should be the first continuation
   1.406 +   */
   1.407 +  static nsSVGFilterProperty *GetFilterProperty(nsIFrame *aFrame);
   1.408 +
   1.409 +  /**
   1.410 +   * @param aFrame must be a first-continuation.
   1.411 +   */
   1.412 +  static void AddRenderingObserver(Element *aElement, nsSVGRenderingObserver *aObserver);
   1.413 +  /**
   1.414 +   * @param aFrame must be a first-continuation.
   1.415 +   */
   1.416 +  static void RemoveRenderingObserver(Element *aElement, nsSVGRenderingObserver *aObserver);
   1.417 +
   1.418 +  /**
   1.419 +   * Removes all rendering observers from aElement.
   1.420 +   */
   1.421 +  static void RemoveAllRenderingObservers(Element *aElement);
   1.422 +
   1.423 +  /**
   1.424 +   * This can be called on any frame. We invalidate the observers of aFrame's
   1.425 +   * element, if any, or else walk up to the nearest observable SVG parent
   1.426 +   * frame with observers and invalidate them instead.
   1.427 +   *
   1.428 +   * Note that this method is very different to e.g.
   1.429 +   * nsNodeUtils::AttributeChanged which walks up the content node tree all the
   1.430 +   * way to the root node (not stopping if it encounters a non-container SVG
   1.431 +   * node) invalidating all mutation observers (not just
   1.432 +   * nsSVGRenderingObservers) on all nodes along the way (not just the first
   1.433 +   * node it finds with observers). In other words, by doing all the
   1.434 +   * things in parentheses in the preceding sentence, this method uses
   1.435 +   * knowledge about our implementation and what can be affected by SVG effects
   1.436 +   * to make invalidation relatively lightweight when an SVG effect changes.
   1.437 +   */
   1.438 +  static void InvalidateRenderingObservers(nsIFrame *aFrame);
   1.439 +
   1.440 +  enum {
   1.441 +    INVALIDATE_REFLOW = 1
   1.442 +  };
   1.443 +
   1.444 +  /**
   1.445 +   * This can be called on any element or frame. Only direct observers of this
   1.446 +   * (frame's) element, if any, are invalidated.
   1.447 +   */
   1.448 +  static void InvalidateDirectRenderingObservers(Element *aElement, uint32_t aFlags = 0);
   1.449 +  static void InvalidateDirectRenderingObservers(nsIFrame *aFrame, uint32_t aFlags = 0);
   1.450 +
   1.451 +  /**
   1.452 +   * Get an nsSVGMarkerProperty for the frame, creating a fresh one if necessary
   1.453 +   */
   1.454 +  static nsSVGMarkerProperty *
   1.455 +  GetMarkerProperty(nsIURI *aURI, nsIFrame *aFrame,
   1.456 +                    const FramePropertyDescriptor *aProperty);
   1.457 +  /**
   1.458 +   * Get an nsSVGTextPathProperty for the frame, creating a fresh one if necessary
   1.459 +   */
   1.460 +  static nsSVGTextPathProperty *
   1.461 +  GetTextPathProperty(nsIURI *aURI, nsIFrame *aFrame,
   1.462 +                      const FramePropertyDescriptor *aProperty);
   1.463 +  /**
   1.464 +   * Get an nsSVGPaintingProperty for the frame, creating a fresh one if necessary
   1.465 +   */
   1.466 +  static nsSVGPaintingProperty *
   1.467 +  GetPaintingProperty(nsIURI *aURI, nsIFrame *aFrame,
   1.468 +                      const FramePropertyDescriptor *aProperty);
   1.469 +  /**
   1.470 +   * Get an nsSVGPaintingProperty for the frame for that URI, creating a fresh
   1.471 +   * one if necessary
   1.472 +   */
   1.473 +  static nsSVGPaintingProperty *
   1.474 +  GetPaintingPropertyForURI(nsIURI *aURI, nsIFrame *aFrame,
   1.475 +                            const FramePropertyDescriptor *aProp);
   1.476 +};
   1.477 +
   1.478 +#endif /*NSSVGEFFECTS_H_*/

mercurial