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_*/