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