layout/svg/nsSVGEffects.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:87645da29c00
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 }

mercurial