layout/svg/nsSVGEffects.h

branch
TOR_BUG_9701
changeset 8
97036ab72558
equal deleted inserted replaced
-1:000000000000 0:f4e79b133a0d
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6 #ifndef NSSVGEFFECTS_H_
7 #define NSSVGEFFECTS_H_
8
9 #include "mozilla/Attributes.h"
10 #include "FramePropertyTable.h"
11 #include "mozilla/dom/Element.h"
12 #include "nsHashKeys.h"
13 #include "nsID.h"
14 #include "nsIFrame.h"
15 #include "nsIMutationObserver.h"
16 #include "nsInterfaceHashtable.h"
17 #include "nsISupportsBase.h"
18 #include "nsISupportsImpl.h"
19 #include "nsReferencedElement.h"
20 #include "nsStubMutationObserver.h"
21 #include "nsSVGUtils.h"
22 #include "nsTHashtable.h"
23 #include "nsURIHashKey.h"
24
25 class nsIAtom;
26 class nsIPresShell;
27 class nsIURI;
28 class nsSVGClipPathFrame;
29 class nsSVGPaintServerFrame;
30 class nsSVGFilterFrame;
31 class nsSVGMaskFrame;
32
33 /*
34 * This interface allows us to be notified when a piece of SVG content is
35 * re-rendered.
36 *
37 * Concrete implementations of this interface need to implement
38 * "GetTarget()" to specify the piece of SVG content that they'd like to
39 * monitor, and they need to implement "DoUpdate" to specify how we'll react
40 * when that content gets re-rendered. They also need to implement a
41 * constructor and destructor, which should call StartListening and
42 * StopListening, respectively.
43 */
44 class nsSVGRenderingObserver : public nsStubMutationObserver {
45 public:
46 typedef mozilla::dom::Element Element;
47 nsSVGRenderingObserver()
48 : mInObserverList(false)
49 {}
50 virtual ~nsSVGRenderingObserver()
51 {}
52
53 // nsISupports
54 NS_DECL_ISUPPORTS
55
56 // nsIMutationObserver
57 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
58 NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
59 NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
60 NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
61
62 void InvalidateViaReferencedElement();
63
64 // When a nsSVGRenderingObserver list gets forcibly cleared, it uses this
65 // callback to notify every observer that's cleared from it, so they can
66 // react.
67 void NotifyEvictedFromRenderingObserverList();
68
69 bool IsInObserverList() const { return mInObserverList; }
70
71 nsIFrame* GetReferencedFrame();
72 /**
73 * @param aOK this is only for the convenience of callers. We set *aOK to false
74 * if the frame is the wrong type
75 */
76 nsIFrame* GetReferencedFrame(nsIAtom* aFrameType, bool* aOK);
77
78 Element* GetReferencedElement();
79
80 virtual bool ObservesReflow() { return true; }
81
82 protected:
83 // Non-virtual protected methods
84 void StartListening();
85 void StopListening();
86
87 // Virtual protected methods
88 virtual void DoUpdate() = 0; // called when the referenced resource changes.
89
90 // This is an internally-used version of GetReferencedElement that doesn't
91 // forcibly add us as an observer. (whereas GetReferencedElement does)
92 virtual Element* GetTarget() = 0;
93
94 // Whether we're in our referenced element's observer list at this time.
95 bool mInObserverList;
96 };
97
98
99 /*
100 * SVG elements reference supporting resources by element ID. We need to
101 * track when those resources change and when the DOM changes in ways
102 * that affect which element is referenced by a given ID (e.g., when
103 * element IDs change). The code here is responsible for that.
104 *
105 * When a frame references a supporting resource, we create a property
106 * object derived from nsSVGIDRenderingObserver to manage the relationship. The
107 * property object is attached to the referencing frame.
108 */
109 class nsSVGIDRenderingObserver : public nsSVGRenderingObserver {
110 public:
111 typedef mozilla::dom::Element Element;
112 nsSVGIDRenderingObserver(nsIURI* aURI, nsIFrame *aFrame,
113 bool aReferenceImage);
114 virtual ~nsSVGIDRenderingObserver();
115
116 protected:
117 Element* GetTarget() MOZ_OVERRIDE { return mElement.get(); }
118
119 // This is called when the referenced resource changes.
120 virtual void DoUpdate() MOZ_OVERRIDE;
121
122 class SourceReference : public nsReferencedElement {
123 public:
124 SourceReference(nsSVGIDRenderingObserver* aContainer) : mContainer(aContainer) {}
125 protected:
126 virtual void ElementChanged(Element* aFrom, Element* aTo) MOZ_OVERRIDE {
127 mContainer->StopListening();
128 nsReferencedElement::ElementChanged(aFrom, aTo);
129 mContainer->StartListening();
130 mContainer->DoUpdate();
131 }
132 /**
133 * Override IsPersistent because we want to keep tracking the element
134 * for the ID even when it changes.
135 */
136 virtual bool IsPersistent() MOZ_OVERRIDE { return true; }
137 private:
138 nsSVGIDRenderingObserver* mContainer;
139 };
140
141 SourceReference mElement;
142 // The frame that this property is attached to
143 nsIFrame *mFrame;
144 // When a presshell is torn down, we don't delete the properties for
145 // each frame until after the frames are destroyed. So here we remember
146 // the presshell for the frames we care about and, before we use the frame,
147 // we test the presshell to see if it's destroying itself. If it is,
148 // then the frame pointer is not valid and we know the frame has gone away.
149 nsIPresShell *mFramePresShell;
150 };
151
152 /**
153 * In a filter chain, there can be multiple SVG reference filters.
154 * e.g. filter: url(#svg-filter-1) blur(10px) url(#svg-filter-2);
155 *
156 * This class keeps track of one SVG reference filter in a filter chain.
157 * e.g. url(#svg-filter-1)
158 *
159 * It fires invalidations when the SVG filter element's id changes or when
160 * the SVG filter element's content changes.
161 *
162 * The nsSVGFilterProperty class manages a list of nsSVGFilterReferences.
163 */
164 class nsSVGFilterReference :
165 public nsSVGIDRenderingObserver, public nsISVGFilterReference {
166 public:
167 nsSVGFilterReference(nsIURI *aURI, nsIFrame *aFilteredFrame)
168 : nsSVGIDRenderingObserver(aURI, aFilteredFrame, false) {}
169
170 bool ReferencesValidResource() { return GetFilterFrame(); }
171
172 /**
173 * @return the filter frame, or null if there is no filter frame
174 */
175 nsSVGFilterFrame *GetFilterFrame();
176
177 // nsISupports
178 NS_DECL_ISUPPORTS
179
180 // nsISVGFilterReference
181 virtual void Invalidate() MOZ_OVERRIDE { DoUpdate(); };
182
183 private:
184 // nsSVGIDRenderingObserver
185 virtual void DoUpdate() MOZ_OVERRIDE;
186 };
187
188 /**
189 * This class manages a list of nsSVGFilterReferences, which represent SVG
190 * reference filters in a filter chain.
191 * e.g. filter: url(#svg-filter-1) blur(10px) url(#svg-filter-2);
192 *
193 * In the above example, the nsSVGFilterProperty will manage two
194 * nsSVGFilterReferences, one for each SVG reference filter. CSS filters like
195 * "blur(10px)" don't reference filter elements, so they don't need an
196 * nsSVGFilterReference. The style system invalidates changes to CSS filters.
197 */
198 class nsSVGFilterProperty : public nsISupports {
199 public:
200 nsSVGFilterProperty(const nsTArray<nsStyleFilter> &aFilters,
201 nsIFrame *aFilteredFrame);
202 virtual ~nsSVGFilterProperty();
203
204 const nsTArray<nsStyleFilter>& GetFilters() { return mFilters; }
205 bool ReferencesValidResources();
206 bool IsInObserverLists() const;
207 void Invalidate();
208
209 // nsISupports
210 NS_DECL_ISUPPORTS
211
212 private:
213 nsTArray<nsSVGFilterReference*> mReferences;
214 nsTArray<nsStyleFilter> mFilters;
215 };
216
217 class nsSVGMarkerProperty : public nsSVGIDRenderingObserver {
218 public:
219 nsSVGMarkerProperty(nsIURI *aURI, nsIFrame *aFrame, bool aReferenceImage)
220 : nsSVGIDRenderingObserver(aURI, aFrame, aReferenceImage) {}
221
222 protected:
223 virtual void DoUpdate() MOZ_OVERRIDE;
224 };
225
226 class nsSVGTextPathProperty : public nsSVGIDRenderingObserver {
227 public:
228 nsSVGTextPathProperty(nsIURI *aURI, nsIFrame *aFrame, bool aReferenceImage)
229 : nsSVGIDRenderingObserver(aURI, aFrame, aReferenceImage)
230 , mValid(true) {}
231
232 virtual bool ObservesReflow() MOZ_OVERRIDE { return false; }
233
234 protected:
235 virtual void DoUpdate() MOZ_OVERRIDE;
236
237 private:
238 /**
239 * Returns true if the target of the textPath is the frame of a 'path' element.
240 */
241 bool TargetIsValid();
242
243 bool mValid;
244 };
245
246 class nsSVGPaintingProperty : public nsSVGIDRenderingObserver {
247 public:
248 nsSVGPaintingProperty(nsIURI *aURI, nsIFrame *aFrame, bool aReferenceImage)
249 : nsSVGIDRenderingObserver(aURI, aFrame, aReferenceImage) {}
250
251 protected:
252 virtual void DoUpdate() MOZ_OVERRIDE;
253 };
254
255 /**
256 * A manager for one-shot nsSVGRenderingObserver tracking.
257 * nsSVGRenderingObservers can be added or removed. They are not strongly
258 * referenced so an observer must be removed before it dies.
259 * When InvalidateAll is called, all outstanding references get
260 * InvalidateViaReferencedElement()
261 * called on them and the list is cleared. The intent is that
262 * the observer will force repainting of whatever part of the document
263 * is needed, and then at paint time the observer will do a clean lookup
264 * of the referenced element and [re-]add itself to the element's observer list.
265 *
266 * InvalidateAll must be called before this object is destroyed, i.e.
267 * before the referenced frame is destroyed. This should normally happen
268 * via nsSVGContainerFrame::RemoveFrame, since only frames in the frame
269 * tree should be referenced.
270 */
271 class nsSVGRenderingObserverList {
272 public:
273 nsSVGRenderingObserverList()
274 : mObservers(5)
275 {
276 MOZ_COUNT_CTOR(nsSVGRenderingObserverList);
277 }
278
279 ~nsSVGRenderingObserverList() {
280 InvalidateAll();
281 MOZ_COUNT_DTOR(nsSVGRenderingObserverList);
282 }
283
284 void Add(nsSVGRenderingObserver* aObserver)
285 { mObservers.PutEntry(aObserver); }
286 void Remove(nsSVGRenderingObserver* aObserver)
287 { mObservers.RemoveEntry(aObserver); }
288 #ifdef DEBUG
289 bool Contains(nsSVGRenderingObserver* aObserver)
290 { return (mObservers.GetEntry(aObserver) != nullptr); }
291 #endif
292 bool IsEmpty()
293 { return mObservers.Count() == 0; }
294
295 /**
296 * Drop all our observers, and notify them that we have changed and dropped
297 * our reference to them.
298 */
299 void InvalidateAll();
300
301 /**
302 * Drop all observers that observe reflow, and notify them that we have changed and dropped
303 * our reference to them.
304 */
305 void InvalidateAllForReflow();
306
307 /**
308 * Drop all our observers, and notify them that we have dropped our reference
309 * to them.
310 */
311 void RemoveAll();
312
313 private:
314 nsTHashtable<nsPtrHashKey<nsSVGRenderingObserver> > mObservers;
315 };
316
317 class nsSVGEffects {
318 public:
319 typedef mozilla::dom::Element Element;
320 typedef mozilla::FramePropertyDescriptor FramePropertyDescriptor;
321 typedef nsInterfaceHashtable<nsURIHashKey, nsIMutationObserver>
322 URIObserverHashtable;
323
324 static void DestroySupports(void* aPropertyValue)
325 {
326 (static_cast<nsISupports*>(aPropertyValue))->Release();
327 }
328
329 static void DestroyHashtable(void* aPropertyValue)
330 {
331 delete static_cast<URIObserverHashtable*> (aPropertyValue);
332 }
333
334 NS_DECLARE_FRAME_PROPERTY(FilterProperty, DestroySupports)
335 NS_DECLARE_FRAME_PROPERTY(MaskProperty, DestroySupports)
336 NS_DECLARE_FRAME_PROPERTY(ClipPathProperty, DestroySupports)
337 NS_DECLARE_FRAME_PROPERTY(MarkerBeginProperty, DestroySupports)
338 NS_DECLARE_FRAME_PROPERTY(MarkerMiddleProperty, DestroySupports)
339 NS_DECLARE_FRAME_PROPERTY(MarkerEndProperty, DestroySupports)
340 NS_DECLARE_FRAME_PROPERTY(FillProperty, DestroySupports)
341 NS_DECLARE_FRAME_PROPERTY(StrokeProperty, DestroySupports)
342 NS_DECLARE_FRAME_PROPERTY(HrefProperty, DestroySupports)
343 NS_DECLARE_FRAME_PROPERTY(BackgroundImageProperty, DestroyHashtable)
344
345 /**
346 * Get the paint server for a aTargetFrame.
347 */
348 static nsSVGPaintServerFrame *GetPaintServer(nsIFrame *aTargetFrame,
349 const nsStyleSVGPaint *aPaint,
350 const FramePropertyDescriptor *aProperty);
351
352 struct EffectProperties {
353 nsSVGFilterProperty* mFilter;
354 nsSVGPaintingProperty* mMask;
355 nsSVGPaintingProperty* mClipPath;
356
357 /**
358 * @return the clip-path frame, or null if there is no clip-path frame
359 * @param aOK if a clip-path was specified and the designated element
360 * exists but is an element of the wrong type, *aOK is set to false.
361 * Otherwise *aOK is untouched.
362 */
363 nsSVGClipPathFrame *GetClipPathFrame(bool *aOK);
364 /**
365 * @return the mask frame, or null if there is no mask frame
366 * @param aOK if a mask was specified and the designated element
367 * exists but is an element of the wrong type, *aOK is set to false.
368 * Otherwise *aOK is untouched.
369 */
370 nsSVGMaskFrame *GetMaskFrame(bool *aOK);
371
372 bool HasValidFilter() {
373 return mFilter && mFilter->ReferencesValidResources();
374 }
375
376 bool HasNoFilterOrHasValidFilter() {
377 return !mFilter || mFilter->ReferencesValidResources();
378 }
379 };
380
381 /**
382 * @param aFrame should be the first continuation
383 */
384 static EffectProperties GetEffectProperties(nsIFrame *aFrame);
385
386 /**
387 * Called when changes to an element (e.g. CSS property changes) cause its
388 * frame to start/stop referencing (or reference different) SVG resource
389 * elements. (_Not_ called for changes to referenced resource elements.)
390 *
391 * This function handles such changes by discarding _all_ the frame's SVG
392 * effects frame properties (causing those properties to stop watching their
393 * target element). It also synchronously (re)creates the filter and marker
394 * frame properties (XXX why not the other properties?), which makes it
395 * useful for initializing those properties during first reflow.
396 *
397 * XXX rename to something more meaningful like RefreshResourceReferences?
398 */
399 static void UpdateEffects(nsIFrame *aFrame);
400
401 /**
402 * @param aFrame should be the first continuation
403 */
404 static nsSVGFilterProperty *GetFilterProperty(nsIFrame *aFrame);
405
406 /**
407 * @param aFrame must be a first-continuation.
408 */
409 static void AddRenderingObserver(Element *aElement, nsSVGRenderingObserver *aObserver);
410 /**
411 * @param aFrame must be a first-continuation.
412 */
413 static void RemoveRenderingObserver(Element *aElement, nsSVGRenderingObserver *aObserver);
414
415 /**
416 * Removes all rendering observers from aElement.
417 */
418 static void RemoveAllRenderingObservers(Element *aElement);
419
420 /**
421 * This can be called on any frame. We invalidate the observers of aFrame's
422 * element, if any, or else walk up to the nearest observable SVG parent
423 * frame with observers and invalidate them instead.
424 *
425 * Note that this method is very different to e.g.
426 * nsNodeUtils::AttributeChanged which walks up the content node tree all the
427 * way to the root node (not stopping if it encounters a non-container SVG
428 * node) invalidating all mutation observers (not just
429 * nsSVGRenderingObservers) on all nodes along the way (not just the first
430 * node it finds with observers). In other words, by doing all the
431 * things in parentheses in the preceding sentence, this method uses
432 * knowledge about our implementation and what can be affected by SVG effects
433 * to make invalidation relatively lightweight when an SVG effect changes.
434 */
435 static void InvalidateRenderingObservers(nsIFrame *aFrame);
436
437 enum {
438 INVALIDATE_REFLOW = 1
439 };
440
441 /**
442 * This can be called on any element or frame. Only direct observers of this
443 * (frame's) element, if any, are invalidated.
444 */
445 static void InvalidateDirectRenderingObservers(Element *aElement, uint32_t aFlags = 0);
446 static void InvalidateDirectRenderingObservers(nsIFrame *aFrame, uint32_t aFlags = 0);
447
448 /**
449 * Get an nsSVGMarkerProperty for the frame, creating a fresh one if necessary
450 */
451 static nsSVGMarkerProperty *
452 GetMarkerProperty(nsIURI *aURI, nsIFrame *aFrame,
453 const FramePropertyDescriptor *aProperty);
454 /**
455 * Get an nsSVGTextPathProperty for the frame, creating a fresh one if necessary
456 */
457 static nsSVGTextPathProperty *
458 GetTextPathProperty(nsIURI *aURI, nsIFrame *aFrame,
459 const FramePropertyDescriptor *aProperty);
460 /**
461 * Get an nsSVGPaintingProperty for the frame, creating a fresh one if necessary
462 */
463 static nsSVGPaintingProperty *
464 GetPaintingProperty(nsIURI *aURI, nsIFrame *aFrame,
465 const FramePropertyDescriptor *aProperty);
466 /**
467 * Get an nsSVGPaintingProperty for the frame for that URI, creating a fresh
468 * one if necessary
469 */
470 static nsSVGPaintingProperty *
471 GetPaintingPropertyForURI(nsIURI *aURI, nsIFrame *aFrame,
472 const FramePropertyDescriptor *aProp);
473 };
474
475 #endif /*NSSVGEFFECTS_H_*/

mercurial