|
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 // Main header first: |
|
7 #include "nsSVGEffects.h" |
|
8 |
|
9 // Keep others in (case-insensitive) order: |
|
10 #include "nsCSSFrameConstructor.h" |
|
11 #include "nsISupportsImpl.h" |
|
12 #include "nsSVGClipPathFrame.h" |
|
13 #include "nsSVGPaintServerFrame.h" |
|
14 #include "nsSVGPathGeometryElement.h" |
|
15 #include "nsSVGFilterFrame.h" |
|
16 #include "nsSVGMaskFrame.h" |
|
17 #include "nsIReflowCallback.h" |
|
18 #include "RestyleManager.h" |
|
19 |
|
20 using namespace mozilla; |
|
21 using namespace mozilla::dom; |
|
22 |
|
23 // nsSVGRenderingObserver impl |
|
24 NS_IMPL_ISUPPORTS(nsSVGRenderingObserver, nsIMutationObserver) |
|
25 |
|
26 void |
|
27 nsSVGRenderingObserver::StartListening() |
|
28 { |
|
29 Element* target = GetTarget(); |
|
30 if (target) { |
|
31 target->AddMutationObserver(this); |
|
32 } |
|
33 } |
|
34 |
|
35 void |
|
36 nsSVGRenderingObserver::StopListening() |
|
37 { |
|
38 Element* target = GetTarget(); |
|
39 |
|
40 if (target) { |
|
41 target->RemoveMutationObserver(this); |
|
42 if (mInObserverList) { |
|
43 nsSVGEffects::RemoveRenderingObserver(target, this); |
|
44 mInObserverList = false; |
|
45 } |
|
46 } |
|
47 NS_ASSERTION(!mInObserverList, "still in an observer list?"); |
|
48 } |
|
49 |
|
50 |
|
51 |
|
52 /** |
|
53 * Note that in the current setup there are two separate observer lists. |
|
54 * |
|
55 * In nsSVGIDRenderingObserver's ctor, the new object adds itself to the |
|
56 * mutation observer list maintained by the referenced element. In this way the |
|
57 * nsSVGIDRenderingObserver is notified if there are any attribute or content |
|
58 * tree changes to the element or any of its *descendants*. |
|
59 * |
|
60 * In nsSVGIDRenderingObserver::GetReferencedElement() the |
|
61 * nsSVGIDRenderingObserver object also adds itself to an |
|
62 * nsSVGRenderingObserverList object belonging to the referenced |
|
63 * element. |
|
64 * |
|
65 * XXX: it would be nice to have a clear and concise executive summary of the |
|
66 * benefits/necessity of maintaining a second observer list. |
|
67 */ |
|
68 |
|
69 nsSVGIDRenderingObserver::nsSVGIDRenderingObserver(nsIURI *aURI, |
|
70 nsIFrame *aFrame, |
|
71 bool aReferenceImage) |
|
72 : mElement(MOZ_THIS_IN_INITIALIZER_LIST()), mFrame(aFrame), |
|
73 mFramePresShell(aFrame->PresContext()->PresShell()) |
|
74 { |
|
75 // Start watching the target element |
|
76 mElement.Reset(aFrame->GetContent(), aURI, true, aReferenceImage); |
|
77 StartListening(); |
|
78 } |
|
79 |
|
80 nsSVGIDRenderingObserver::~nsSVGIDRenderingObserver() |
|
81 { |
|
82 StopListening(); |
|
83 } |
|
84 |
|
85 static nsSVGRenderingObserverList * |
|
86 GetObserverList(Element *aElement) |
|
87 { |
|
88 return static_cast<nsSVGRenderingObserverList*> |
|
89 (aElement->GetProperty(nsGkAtoms::renderingobserverlist)); |
|
90 } |
|
91 |
|
92 Element* |
|
93 nsSVGRenderingObserver::GetReferencedElement() |
|
94 { |
|
95 Element* target = GetTarget(); |
|
96 #ifdef DEBUG |
|
97 if (target) { |
|
98 nsSVGRenderingObserverList *observerList = GetObserverList(target); |
|
99 bool inObserverList = observerList && observerList->Contains(this); |
|
100 NS_ASSERTION(inObserverList == mInObserverList, "failed to track whether we're in our referenced element's observer list!"); |
|
101 } else { |
|
102 NS_ASSERTION(!mInObserverList, "In whose observer list are we, then?"); |
|
103 } |
|
104 #endif |
|
105 if (target && !mInObserverList) { |
|
106 nsSVGEffects::AddRenderingObserver(target, this); |
|
107 mInObserverList = true; |
|
108 } |
|
109 return target; |
|
110 } |
|
111 |
|
112 nsIFrame* |
|
113 nsSVGRenderingObserver::GetReferencedFrame() |
|
114 { |
|
115 Element* referencedElement = GetReferencedElement(); |
|
116 return referencedElement ? referencedElement->GetPrimaryFrame() : nullptr; |
|
117 } |
|
118 |
|
119 nsIFrame* |
|
120 nsSVGRenderingObserver::GetReferencedFrame(nsIAtom* aFrameType, bool* aOK) |
|
121 { |
|
122 nsIFrame* frame = GetReferencedFrame(); |
|
123 if (frame) { |
|
124 if (frame->GetType() == aFrameType) |
|
125 return frame; |
|
126 if (aOK) { |
|
127 *aOK = false; |
|
128 } |
|
129 } |
|
130 return nullptr; |
|
131 } |
|
132 |
|
133 void |
|
134 nsSVGIDRenderingObserver::DoUpdate() |
|
135 { |
|
136 if (mFramePresShell->IsDestroying()) { |
|
137 // mFrame is no longer valid. Bail out. |
|
138 mFrame = nullptr; |
|
139 return; |
|
140 } |
|
141 if (mElement.get() && mInObserverList) { |
|
142 nsSVGEffects::RemoveRenderingObserver(mElement.get(), this); |
|
143 mInObserverList = false; |
|
144 } |
|
145 if (mFrame && mFrame->IsFrameOfType(nsIFrame::eSVG)) { |
|
146 // Changes should propagate out to things that might be observing |
|
147 // the referencing frame or its ancestors. |
|
148 nsSVGEffects::InvalidateRenderingObservers(mFrame); |
|
149 } |
|
150 } |
|
151 |
|
152 void |
|
153 nsSVGRenderingObserver::InvalidateViaReferencedElement() |
|
154 { |
|
155 mInObserverList = false; |
|
156 DoUpdate(); |
|
157 } |
|
158 |
|
159 void |
|
160 nsSVGRenderingObserver::NotifyEvictedFromRenderingObserverList() |
|
161 { |
|
162 mInObserverList = false; // We've been removed from rendering-obs. list. |
|
163 StopListening(); // Remove ourselves from mutation-obs. list. |
|
164 } |
|
165 |
|
166 void |
|
167 nsSVGRenderingObserver::AttributeChanged(nsIDocument* aDocument, |
|
168 dom::Element* aElement, |
|
169 int32_t aNameSpaceID, |
|
170 nsIAtom* aAttribute, |
|
171 int32_t aModType) |
|
172 { |
|
173 // An attribute belonging to the element that we are observing *or one of its |
|
174 // descendants* has changed. |
|
175 // |
|
176 // In the case of observing a gradient element, say, we want to know if any |
|
177 // of its 'stop' element children change, but we don't actually want to do |
|
178 // anything for changes to SMIL element children, for example. Maybe it's not |
|
179 // worth having logic to optimize for that, but in most cases it could be a |
|
180 // small check? |
|
181 // |
|
182 // XXXjwatt: do we really want to blindly break the link between our |
|
183 // observers and ourselves for all attribute changes? For non-ID changes |
|
184 // surely that is unnecessary. |
|
185 |
|
186 DoUpdate(); |
|
187 } |
|
188 |
|
189 void |
|
190 nsSVGRenderingObserver::ContentAppended(nsIDocument *aDocument, |
|
191 nsIContent *aContainer, |
|
192 nsIContent *aFirstNewContent, |
|
193 int32_t /* unused */) |
|
194 { |
|
195 DoUpdate(); |
|
196 } |
|
197 |
|
198 void |
|
199 nsSVGRenderingObserver::ContentInserted(nsIDocument *aDocument, |
|
200 nsIContent *aContainer, |
|
201 nsIContent *aChild, |
|
202 int32_t /* unused */) |
|
203 { |
|
204 DoUpdate(); |
|
205 } |
|
206 |
|
207 void |
|
208 nsSVGRenderingObserver::ContentRemoved(nsIDocument *aDocument, |
|
209 nsIContent *aContainer, |
|
210 nsIContent *aChild, |
|
211 int32_t aIndexInContainer, |
|
212 nsIContent *aPreviousSibling) |
|
213 { |
|
214 DoUpdate(); |
|
215 } |
|
216 |
|
217 NS_IMPL_ISUPPORTS(nsSVGFilterProperty, nsISupports) |
|
218 |
|
219 nsSVGFilterProperty::nsSVGFilterProperty(const nsTArray<nsStyleFilter> &aFilters, |
|
220 nsIFrame *aFilteredFrame) : |
|
221 mFilters(aFilters) |
|
222 { |
|
223 for (uint32_t i = 0; i < mFilters.Length(); i++) { |
|
224 if (mFilters[i].GetType() != NS_STYLE_FILTER_URL) |
|
225 continue; |
|
226 |
|
227 nsSVGFilterReference *reference = |
|
228 new nsSVGFilterReference(mFilters[i].GetURL(), aFilteredFrame); |
|
229 NS_ADDREF(reference); |
|
230 mReferences.AppendElement(reference); |
|
231 } |
|
232 } |
|
233 |
|
234 nsSVGFilterProperty::~nsSVGFilterProperty() |
|
235 { |
|
236 for (uint32_t i = 0; i < mReferences.Length(); i++) { |
|
237 NS_RELEASE(mReferences[i]); |
|
238 } |
|
239 } |
|
240 |
|
241 bool |
|
242 nsSVGFilterProperty::ReferencesValidResources() |
|
243 { |
|
244 for (uint32_t i = 0; i < mReferences.Length(); i++) { |
|
245 if (!mReferences[i]->ReferencesValidResource()) |
|
246 return false; |
|
247 } |
|
248 return true; |
|
249 } |
|
250 |
|
251 bool |
|
252 nsSVGFilterProperty::IsInObserverLists() const |
|
253 { |
|
254 for (uint32_t i = 0; i < mReferences.Length(); i++) { |
|
255 if (!mReferences[i]->IsInObserverList()) |
|
256 return false; |
|
257 } |
|
258 return true; |
|
259 } |
|
260 |
|
261 void |
|
262 nsSVGFilterProperty::Invalidate() |
|
263 { |
|
264 for (uint32_t i = 0; i < mReferences.Length(); i++) { |
|
265 mReferences[i]->Invalidate(); |
|
266 } |
|
267 } |
|
268 |
|
269 NS_IMPL_ISUPPORTS_INHERITED(nsSVGFilterReference, |
|
270 nsSVGIDRenderingObserver, |
|
271 nsISVGFilterReference); |
|
272 |
|
273 nsSVGFilterFrame * |
|
274 nsSVGFilterReference::GetFilterFrame() |
|
275 { |
|
276 return static_cast<nsSVGFilterFrame *> |
|
277 (GetReferencedFrame(nsGkAtoms::svgFilterFrame, nullptr)); |
|
278 } |
|
279 |
|
280 static void |
|
281 InvalidateAllContinuations(nsIFrame* aFrame) |
|
282 { |
|
283 for (nsIFrame* f = aFrame; f; |
|
284 f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) { |
|
285 f->InvalidateFrame(); |
|
286 } |
|
287 } |
|
288 |
|
289 void |
|
290 nsSVGFilterReference::DoUpdate() |
|
291 { |
|
292 nsSVGIDRenderingObserver::DoUpdate(); |
|
293 if (!mFrame) |
|
294 return; |
|
295 |
|
296 // Repaint asynchronously in case the filter frame is being torn down |
|
297 nsChangeHint changeHint = |
|
298 nsChangeHint(nsChangeHint_RepaintFrame); |
|
299 |
|
300 // Don't need to request UpdateOverflow if we're being reflowed. |
|
301 if (!(mFrame->GetStateBits() & NS_FRAME_IN_REFLOW)) { |
|
302 NS_UpdateHint(changeHint, nsChangeHint_UpdateOverflow); |
|
303 } |
|
304 mFramePresShell->GetPresContext()->RestyleManager()->PostRestyleEvent( |
|
305 mFrame->GetContent()->AsElement(), nsRestyleHint(0), changeHint); |
|
306 } |
|
307 |
|
308 void |
|
309 nsSVGMarkerProperty::DoUpdate() |
|
310 { |
|
311 nsSVGIDRenderingObserver::DoUpdate(); |
|
312 if (!mFrame) |
|
313 return; |
|
314 |
|
315 NS_ASSERTION(mFrame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected"); |
|
316 |
|
317 // Repaint asynchronously in case the marker frame is being torn down |
|
318 nsChangeHint changeHint = |
|
319 nsChangeHint(nsChangeHint_RepaintFrame); |
|
320 |
|
321 // Don't need to request ReflowFrame if we're being reflowed. |
|
322 if (!(mFrame->GetStateBits() & NS_FRAME_IN_REFLOW)) { |
|
323 // XXXjwatt: We need to unify SVG into standard reflow so we can just use |
|
324 // nsChangeHint_NeedReflow | nsChangeHint_NeedDirtyReflow here. |
|
325 nsSVGEffects::InvalidateRenderingObservers(mFrame); |
|
326 // XXXSDL KILL THIS!!! |
|
327 nsSVGUtils::ScheduleReflowSVG(mFrame); |
|
328 } |
|
329 mFramePresShell->GetPresContext()->RestyleManager()->PostRestyleEvent( |
|
330 mFrame->GetContent()->AsElement(), nsRestyleHint(0), changeHint); |
|
331 } |
|
332 |
|
333 bool |
|
334 nsSVGTextPathProperty::TargetIsValid() |
|
335 { |
|
336 Element* target = GetTarget(); |
|
337 return target && target->IsSVG(nsGkAtoms::path); |
|
338 } |
|
339 |
|
340 void |
|
341 nsSVGTextPathProperty::DoUpdate() |
|
342 { |
|
343 nsSVGIDRenderingObserver::DoUpdate(); |
|
344 if (!mFrame) |
|
345 return; |
|
346 |
|
347 NS_ASSERTION(mFrame->IsFrameOfType(nsIFrame::eSVG) || mFrame->IsSVGText(), |
|
348 "SVG frame expected"); |
|
349 |
|
350 // Avoid getting into an infinite loop of reflows if the <textPath> is |
|
351 // pointing to one of its ancestors. TargetIsValid returns true iff |
|
352 // the target element is a <path> element, and we would not have this |
|
353 // nsSVGTextPathProperty if this <textPath> were a descendant of the |
|
354 // target <path>. |
|
355 // |
|
356 // Note that we still have to post the restyle event when we |
|
357 // change from being valid to invalid, so that mPositions on the |
|
358 // SVGTextFrame gets updated, skipping the <textPath>, ensuring |
|
359 // that nothing gets painted for that element. |
|
360 bool nowValid = TargetIsValid(); |
|
361 if (!mValid && !nowValid) { |
|
362 // Just return if we were previously invalid, and are still invalid. |
|
363 return; |
|
364 } |
|
365 mValid = nowValid; |
|
366 |
|
367 // Repaint asynchronously in case the path frame is being torn down |
|
368 nsChangeHint changeHint = |
|
369 nsChangeHint(nsChangeHint_RepaintFrame | nsChangeHint_UpdateTextPath); |
|
370 mFramePresShell->GetPresContext()->RestyleManager()->PostRestyleEvent( |
|
371 mFrame->GetContent()->AsElement(), nsRestyleHint(0), changeHint); |
|
372 } |
|
373 |
|
374 void |
|
375 nsSVGPaintingProperty::DoUpdate() |
|
376 { |
|
377 nsSVGIDRenderingObserver::DoUpdate(); |
|
378 if (!mFrame) |
|
379 return; |
|
380 |
|
381 if (mFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) { |
|
382 nsSVGEffects::InvalidateRenderingObservers(mFrame); |
|
383 mFrame->InvalidateFrameSubtree(); |
|
384 } else { |
|
385 InvalidateAllContinuations(mFrame); |
|
386 } |
|
387 } |
|
388 |
|
389 static nsSVGRenderingObserver * |
|
390 CreateMarkerProperty(nsIURI *aURI, nsIFrame *aFrame, bool aReferenceImage) |
|
391 { return new nsSVGMarkerProperty(aURI, aFrame, aReferenceImage); } |
|
392 |
|
393 static nsSVGRenderingObserver * |
|
394 CreateTextPathProperty(nsIURI *aURI, nsIFrame *aFrame, bool aReferenceImage) |
|
395 { return new nsSVGTextPathProperty(aURI, aFrame, aReferenceImage); } |
|
396 |
|
397 static nsSVGRenderingObserver * |
|
398 CreatePaintingProperty(nsIURI *aURI, nsIFrame *aFrame, bool aReferenceImage) |
|
399 { return new nsSVGPaintingProperty(aURI, aFrame, aReferenceImage); } |
|
400 |
|
401 static nsSVGRenderingObserver * |
|
402 GetEffectProperty(nsIURI *aURI, nsIFrame *aFrame, |
|
403 const FramePropertyDescriptor *aProperty, |
|
404 nsSVGRenderingObserver * (* aCreate)(nsIURI *, nsIFrame *, bool)) |
|
405 { |
|
406 if (!aURI) |
|
407 return nullptr; |
|
408 |
|
409 FrameProperties props = aFrame->Properties(); |
|
410 nsSVGRenderingObserver *prop = |
|
411 static_cast<nsSVGRenderingObserver*>(props.Get(aProperty)); |
|
412 if (prop) |
|
413 return prop; |
|
414 prop = aCreate(aURI, aFrame, false); |
|
415 if (!prop) |
|
416 return nullptr; |
|
417 NS_ADDREF(prop); |
|
418 props.Set(aProperty, static_cast<nsISupports*>(prop)); |
|
419 return prop; |
|
420 } |
|
421 |
|
422 static nsSVGFilterProperty* |
|
423 GetOrCreateFilterProperty(nsIFrame *aFrame) |
|
424 { |
|
425 const nsStyleSVGReset* style = aFrame->StyleSVGReset(); |
|
426 if (!style->HasFilters()) |
|
427 return nullptr; |
|
428 |
|
429 FrameProperties props = aFrame->Properties(); |
|
430 nsSVGFilterProperty *prop = |
|
431 static_cast<nsSVGFilterProperty*>(props.Get(nsSVGEffects::FilterProperty())); |
|
432 if (prop) |
|
433 return prop; |
|
434 prop = new nsSVGFilterProperty(style->mFilters, aFrame); |
|
435 if (!prop) |
|
436 return nullptr; |
|
437 NS_ADDREF(prop); |
|
438 props.Set(nsSVGEffects::FilterProperty(), static_cast<nsISupports*>(prop)); |
|
439 return prop; |
|
440 } |
|
441 |
|
442 nsSVGMarkerProperty * |
|
443 nsSVGEffects::GetMarkerProperty(nsIURI *aURI, nsIFrame *aFrame, |
|
444 const FramePropertyDescriptor *aProp) |
|
445 { |
|
446 NS_ABORT_IF_FALSE(aFrame->GetType() == nsGkAtoms::svgPathGeometryFrame && |
|
447 static_cast<nsSVGPathGeometryElement*>(aFrame->GetContent())->IsMarkable(), |
|
448 "Bad frame"); |
|
449 return static_cast<nsSVGMarkerProperty*>( |
|
450 GetEffectProperty(aURI, aFrame, aProp, CreateMarkerProperty)); |
|
451 } |
|
452 |
|
453 nsSVGTextPathProperty * |
|
454 nsSVGEffects::GetTextPathProperty(nsIURI *aURI, nsIFrame *aFrame, |
|
455 const FramePropertyDescriptor *aProp) |
|
456 { |
|
457 return static_cast<nsSVGTextPathProperty*>( |
|
458 GetEffectProperty(aURI, aFrame, aProp, CreateTextPathProperty)); |
|
459 } |
|
460 |
|
461 nsSVGPaintingProperty * |
|
462 nsSVGEffects::GetPaintingProperty(nsIURI *aURI, nsIFrame *aFrame, |
|
463 const FramePropertyDescriptor *aProp) |
|
464 { |
|
465 return static_cast<nsSVGPaintingProperty*>( |
|
466 GetEffectProperty(aURI, aFrame, aProp, CreatePaintingProperty)); |
|
467 } |
|
468 |
|
469 static nsSVGRenderingObserver * |
|
470 GetEffectPropertyForURI(nsIURI *aURI, nsIFrame *aFrame, |
|
471 const FramePropertyDescriptor *aProperty, |
|
472 nsSVGRenderingObserver * (* aCreate)(nsIURI *, nsIFrame *, bool)) |
|
473 { |
|
474 if (!aURI) |
|
475 return nullptr; |
|
476 |
|
477 FrameProperties props = aFrame->Properties(); |
|
478 nsSVGEffects::URIObserverHashtable *hashtable = |
|
479 static_cast<nsSVGEffects::URIObserverHashtable*>(props.Get(aProperty)); |
|
480 if (!hashtable) { |
|
481 hashtable = new nsSVGEffects::URIObserverHashtable(); |
|
482 props.Set(aProperty, hashtable); |
|
483 } |
|
484 nsSVGRenderingObserver* prop = |
|
485 static_cast<nsSVGRenderingObserver*>(hashtable->GetWeak(aURI)); |
|
486 if (!prop) { |
|
487 bool watchImage = aProperty == nsSVGEffects::BackgroundImageProperty(); |
|
488 prop = aCreate(aURI, aFrame, watchImage); |
|
489 hashtable->Put(aURI, prop); |
|
490 } |
|
491 return prop; |
|
492 } |
|
493 |
|
494 nsSVGPaintingProperty * |
|
495 nsSVGEffects::GetPaintingPropertyForURI(nsIURI *aURI, nsIFrame *aFrame, |
|
496 const FramePropertyDescriptor *aProp) |
|
497 { |
|
498 return static_cast<nsSVGPaintingProperty*>( |
|
499 GetEffectPropertyForURI(aURI, aFrame, aProp, CreatePaintingProperty)); |
|
500 } |
|
501 |
|
502 nsSVGEffects::EffectProperties |
|
503 nsSVGEffects::GetEffectProperties(nsIFrame *aFrame) |
|
504 { |
|
505 NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation"); |
|
506 |
|
507 EffectProperties result; |
|
508 const nsStyleSVGReset *style = aFrame->StyleSVGReset(); |
|
509 result.mFilter = GetOrCreateFilterProperty(aFrame); |
|
510 result.mClipPath = |
|
511 GetPaintingProperty(style->mClipPath, aFrame, ClipPathProperty()); |
|
512 result.mMask = |
|
513 GetPaintingProperty(style->mMask, aFrame, MaskProperty()); |
|
514 return result; |
|
515 } |
|
516 |
|
517 nsSVGPaintServerFrame * |
|
518 nsSVGEffects::GetPaintServer(nsIFrame *aTargetFrame, const nsStyleSVGPaint *aPaint, |
|
519 const FramePropertyDescriptor *aType) |
|
520 { |
|
521 if (aPaint->mType != eStyleSVGPaintType_Server) |
|
522 return nullptr; |
|
523 |
|
524 nsIFrame *frame = aTargetFrame->GetContent()->IsNodeOfType(nsINode::eTEXT) ? |
|
525 aTargetFrame->GetParent() : aTargetFrame; |
|
526 nsSVGPaintingProperty *property = |
|
527 nsSVGEffects::GetPaintingProperty(aPaint->mPaint.mPaintServer, frame, aType); |
|
528 if (!property) |
|
529 return nullptr; |
|
530 nsIFrame *result = property->GetReferencedFrame(); |
|
531 if (!result) |
|
532 return nullptr; |
|
533 |
|
534 nsIAtom *type = result->GetType(); |
|
535 if (type != nsGkAtoms::svgLinearGradientFrame && |
|
536 type != nsGkAtoms::svgRadialGradientFrame && |
|
537 type != nsGkAtoms::svgPatternFrame) |
|
538 return nullptr; |
|
539 |
|
540 return static_cast<nsSVGPaintServerFrame*>(result); |
|
541 } |
|
542 |
|
543 nsSVGClipPathFrame * |
|
544 nsSVGEffects::EffectProperties::GetClipPathFrame(bool *aOK) |
|
545 { |
|
546 if (!mClipPath) |
|
547 return nullptr; |
|
548 nsSVGClipPathFrame *frame = static_cast<nsSVGClipPathFrame *> |
|
549 (mClipPath->GetReferencedFrame(nsGkAtoms::svgClipPathFrame, aOK)); |
|
550 if (frame && aOK && *aOK) { |
|
551 *aOK = frame->IsValid(); |
|
552 } |
|
553 return frame; |
|
554 } |
|
555 |
|
556 nsSVGMaskFrame * |
|
557 nsSVGEffects::EffectProperties::GetMaskFrame(bool *aOK) |
|
558 { |
|
559 if (!mMask) |
|
560 return nullptr; |
|
561 return static_cast<nsSVGMaskFrame *> |
|
562 (mMask->GetReferencedFrame(nsGkAtoms::svgMaskFrame, aOK)); |
|
563 } |
|
564 |
|
565 void |
|
566 nsSVGEffects::UpdateEffects(nsIFrame *aFrame) |
|
567 { |
|
568 NS_ASSERTION(aFrame->GetContent()->IsElement(), |
|
569 "aFrame's content should be an element"); |
|
570 |
|
571 FrameProperties props = aFrame->Properties(); |
|
572 props.Delete(FilterProperty()); |
|
573 props.Delete(MaskProperty()); |
|
574 props.Delete(ClipPathProperty()); |
|
575 props.Delete(MarkerBeginProperty()); |
|
576 props.Delete(MarkerMiddleProperty()); |
|
577 props.Delete(MarkerEndProperty()); |
|
578 props.Delete(FillProperty()); |
|
579 props.Delete(StrokeProperty()); |
|
580 props.Delete(BackgroundImageProperty()); |
|
581 |
|
582 // Ensure that the filter is repainted correctly |
|
583 // We can't do that in DoUpdate as the referenced frame may not be valid |
|
584 GetOrCreateFilterProperty(aFrame); |
|
585 |
|
586 if (aFrame->GetType() == nsGkAtoms::svgPathGeometryFrame && |
|
587 static_cast<nsSVGPathGeometryElement*>(aFrame->GetContent())->IsMarkable()) { |
|
588 // Set marker properties here to avoid reference loops |
|
589 const nsStyleSVG *style = aFrame->StyleSVG(); |
|
590 GetEffectProperty(style->mMarkerStart, aFrame, MarkerBeginProperty(), |
|
591 CreateMarkerProperty); |
|
592 GetEffectProperty(style->mMarkerMid, aFrame, MarkerMiddleProperty(), |
|
593 CreateMarkerProperty); |
|
594 GetEffectProperty(style->mMarkerEnd, aFrame, MarkerEndProperty(), |
|
595 CreateMarkerProperty); |
|
596 } |
|
597 } |
|
598 |
|
599 nsSVGFilterProperty * |
|
600 nsSVGEffects::GetFilterProperty(nsIFrame *aFrame) |
|
601 { |
|
602 NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation"); |
|
603 |
|
604 if (!aFrame->StyleSVGReset()->HasFilters()) |
|
605 return nullptr; |
|
606 |
|
607 return static_cast<nsSVGFilterProperty *> |
|
608 (aFrame->Properties().Get(FilterProperty())); |
|
609 } |
|
610 |
|
611 static PLDHashOperator |
|
612 GatherEnumerator(nsPtrHashKey<nsSVGRenderingObserver>* aEntry, void* aArg) |
|
613 { |
|
614 nsTArray<nsSVGRenderingObserver*>* array = |
|
615 static_cast<nsTArray<nsSVGRenderingObserver*>*>(aArg); |
|
616 array->AppendElement(aEntry->GetKey()); |
|
617 |
|
618 return PL_DHASH_REMOVE; |
|
619 } |
|
620 |
|
621 static PLDHashOperator |
|
622 GatherEnumeratorForReflow(nsPtrHashKey<nsSVGRenderingObserver>* aEntry, void* aArg) |
|
623 { |
|
624 if (!aEntry->GetKey()->ObservesReflow()) { |
|
625 return PL_DHASH_NEXT; |
|
626 } |
|
627 |
|
628 nsTArray<nsSVGRenderingObserver*>* array = |
|
629 static_cast<nsTArray<nsSVGRenderingObserver*>*>(aArg); |
|
630 array->AppendElement(aEntry->GetKey()); |
|
631 |
|
632 return PL_DHASH_REMOVE; |
|
633 } |
|
634 |
|
635 void |
|
636 nsSVGRenderingObserverList::InvalidateAll() |
|
637 { |
|
638 if (mObservers.Count() == 0) |
|
639 return; |
|
640 |
|
641 nsAutoTArray<nsSVGRenderingObserver*,10> observers; |
|
642 |
|
643 // The PL_DHASH_REMOVE in GatherEnumerator drops all our observers here: |
|
644 mObservers.EnumerateEntries(GatherEnumerator, &observers); |
|
645 |
|
646 for (uint32_t i = 0; i < observers.Length(); ++i) { |
|
647 observers[i]->InvalidateViaReferencedElement(); |
|
648 } |
|
649 } |
|
650 |
|
651 void |
|
652 nsSVGRenderingObserverList::InvalidateAllForReflow() |
|
653 { |
|
654 if (mObservers.Count() == 0) |
|
655 return; |
|
656 |
|
657 nsAutoTArray<nsSVGRenderingObserver*,10> observers; |
|
658 |
|
659 // The PL_DHASH_REMOVE in GatherEnumerator drops all our observers here: |
|
660 mObservers.EnumerateEntries(GatherEnumeratorForReflow, &observers); |
|
661 |
|
662 for (uint32_t i = 0; i < observers.Length(); ++i) { |
|
663 observers[i]->InvalidateViaReferencedElement(); |
|
664 } |
|
665 } |
|
666 |
|
667 void |
|
668 nsSVGRenderingObserverList::RemoveAll() |
|
669 { |
|
670 nsAutoTArray<nsSVGRenderingObserver*,10> observers; |
|
671 |
|
672 // The PL_DHASH_REMOVE in GatherEnumerator drops all our observers here: |
|
673 mObservers.EnumerateEntries(GatherEnumerator, &observers); |
|
674 |
|
675 // Our list is now cleared. We need to notify the observers we've removed, |
|
676 // so they can update their state & remove themselves as mutation-observers. |
|
677 for (uint32_t i = 0; i < observers.Length(); ++i) { |
|
678 observers[i]->NotifyEvictedFromRenderingObserverList(); |
|
679 } |
|
680 } |
|
681 |
|
682 void |
|
683 nsSVGEffects::AddRenderingObserver(Element *aElement, nsSVGRenderingObserver *aObserver) |
|
684 { |
|
685 nsSVGRenderingObserverList *observerList = GetObserverList(aElement); |
|
686 if (!observerList) { |
|
687 observerList = new nsSVGRenderingObserverList(); |
|
688 if (!observerList) |
|
689 return; |
|
690 aElement->SetProperty(nsGkAtoms::renderingobserverlist, observerList, |
|
691 nsINode::DeleteProperty<nsSVGRenderingObserverList>); |
|
692 } |
|
693 aElement->SetHasRenderingObservers(true); |
|
694 observerList->Add(aObserver); |
|
695 } |
|
696 |
|
697 void |
|
698 nsSVGEffects::RemoveRenderingObserver(Element *aElement, nsSVGRenderingObserver *aObserver) |
|
699 { |
|
700 nsSVGRenderingObserverList *observerList = GetObserverList(aElement); |
|
701 if (observerList) { |
|
702 NS_ASSERTION(observerList->Contains(aObserver), |
|
703 "removing observer from an element we're not observing?"); |
|
704 observerList->Remove(aObserver); |
|
705 if (observerList->IsEmpty()) { |
|
706 aElement->SetHasRenderingObservers(false); |
|
707 } |
|
708 } |
|
709 } |
|
710 |
|
711 void |
|
712 nsSVGEffects::RemoveAllRenderingObservers(Element *aElement) |
|
713 { |
|
714 nsSVGRenderingObserverList *observerList = GetObserverList(aElement); |
|
715 if (observerList) { |
|
716 observerList->RemoveAll(); |
|
717 aElement->SetHasRenderingObservers(false); |
|
718 } |
|
719 } |
|
720 |
|
721 void |
|
722 nsSVGEffects::InvalidateRenderingObservers(nsIFrame *aFrame) |
|
723 { |
|
724 NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame must be first continuation"); |
|
725 |
|
726 if (!aFrame->GetContent()->IsElement()) |
|
727 return; |
|
728 |
|
729 nsSVGRenderingObserverList *observerList = |
|
730 GetObserverList(aFrame->GetContent()->AsElement()); |
|
731 if (observerList) { |
|
732 observerList->InvalidateAll(); |
|
733 return; |
|
734 } |
|
735 |
|
736 // Check ancestor SVG containers. The root frame cannot be of type |
|
737 // eSVGContainer so we don't have to check f for null here. |
|
738 for (nsIFrame *f = aFrame->GetParent(); |
|
739 f->IsFrameOfType(nsIFrame::eSVGContainer); f = f->GetParent()) { |
|
740 if (f->GetContent()->IsElement()) { |
|
741 observerList = GetObserverList(f->GetContent()->AsElement()); |
|
742 if (observerList) { |
|
743 observerList->InvalidateAll(); |
|
744 return; |
|
745 } |
|
746 } |
|
747 } |
|
748 } |
|
749 |
|
750 void |
|
751 nsSVGEffects::InvalidateDirectRenderingObservers(Element *aElement, uint32_t aFlags /* = 0 */) |
|
752 { |
|
753 if (aElement->HasRenderingObservers()) { |
|
754 nsSVGRenderingObserverList *observerList = GetObserverList(aElement); |
|
755 if (observerList) { |
|
756 if (aFlags & INVALIDATE_REFLOW) { |
|
757 observerList->InvalidateAllForReflow(); |
|
758 } else { |
|
759 observerList->InvalidateAll(); |
|
760 } |
|
761 } |
|
762 } |
|
763 } |
|
764 |
|
765 void |
|
766 nsSVGEffects::InvalidateDirectRenderingObservers(nsIFrame *aFrame, uint32_t aFlags /* = 0 */) |
|
767 { |
|
768 if (aFrame->GetContent() && aFrame->GetContent()->IsElement()) { |
|
769 InvalidateDirectRenderingObservers(aFrame->GetContent()->AsElement(), aFlags); |
|
770 } |
|
771 } |