Line data Source code
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 "mozilla/RestyleManager.h"
11 : #include "mozilla/RestyleManagerInlines.h"
12 : #include "nsCSSFrameConstructor.h"
13 : #include "nsISupportsImpl.h"
14 : #include "nsSVGClipPathFrame.h"
15 : #include "nsSVGPaintServerFrame.h"
16 : #include "nsSVGFilterFrame.h"
17 : #include "nsSVGMaskFrame.h"
18 : #include "nsIReflowCallback.h"
19 : #include "nsCycleCollectionParticipant.h"
20 : #include "SVGGeometryElement.h"
21 : #include "SVGUseElement.h"
22 :
23 : using namespace mozilla;
24 : using namespace mozilla::dom;
25 :
26 : void
27 24 : nsSVGRenderingObserver::StartListening()
28 : {
29 24 : Element* target = GetTarget();
30 24 : if (target) {
31 23 : target->AddMutationObserver(this);
32 : }
33 24 : }
34 :
35 : void
36 0 : nsSVGRenderingObserver::StopListening()
37 : {
38 0 : Element* target = GetTarget();
39 :
40 0 : if (target) {
41 0 : target->RemoveMutationObserver(this);
42 0 : if (mInObserverList) {
43 0 : nsSVGEffects::RemoveRenderingObserver(target, this);
44 0 : mInObserverList = false;
45 : }
46 : }
47 0 : NS_ASSERTION(!mInObserverList, "still in an observer list?");
48 0 : }
49 :
50 : static nsSVGRenderingObserverList *
51 271 : GetObserverList(Element *aElement)
52 : {
53 : return static_cast<nsSVGRenderingObserverList*>
54 271 : (aElement->GetProperty(nsGkAtoms::renderingobserverlist));
55 : }
56 :
57 : Element*
58 20 : nsSVGRenderingObserver::GetReferencedElement()
59 : {
60 20 : Element* target = GetTarget();
61 : #ifdef DEBUG
62 20 : if (target) {
63 6 : nsSVGRenderingObserverList *observerList = GetObserverList(target);
64 6 : bool inObserverList = observerList && observerList->Contains(this);
65 6 : NS_ASSERTION(inObserverList == mInObserverList, "failed to track whether we're in our referenced element's observer list!");
66 : } else {
67 14 : NS_ASSERTION(!mInObserverList, "In whose observer list are we, then?");
68 : }
69 : #endif
70 20 : if (target && !mInObserverList) {
71 2 : nsSVGEffects::AddRenderingObserver(target, this);
72 2 : mInObserverList = true;
73 : }
74 20 : return target;
75 : }
76 :
77 : nsIFrame*
78 20 : nsSVGRenderingObserver::GetReferencedFrame()
79 : {
80 20 : Element* referencedElement = GetReferencedElement();
81 20 : return referencedElement ? referencedElement->GetPrimaryFrame() : nullptr;
82 : }
83 :
84 : nsIFrame*
85 20 : nsSVGRenderingObserver::GetReferencedFrame(LayoutFrameType aFrameType,
86 : bool* aOK)
87 : {
88 20 : nsIFrame* frame = GetReferencedFrame();
89 20 : if (frame) {
90 6 : if (frame->Type() == aFrameType)
91 6 : return frame;
92 0 : if (aOK) {
93 0 : *aOK = false;
94 : }
95 : }
96 14 : return nullptr;
97 : }
98 :
99 : void
100 72 : nsSVGRenderingObserver::InvalidateViaReferencedElement()
101 : {
102 72 : mInObserverList = false;
103 72 : DoUpdate();
104 72 : }
105 :
106 : void
107 0 : nsSVGRenderingObserver::NotifyEvictedFromRenderingObserverList()
108 : {
109 0 : mInObserverList = false; // We've been removed from rendering-obs. list.
110 0 : StopListening(); // Remove ourselves from mutation-obs. list.
111 0 : }
112 :
113 : void
114 0 : nsSVGRenderingObserver::AttributeChanged(nsIDocument* aDocument,
115 : dom::Element* aElement,
116 : int32_t aNameSpaceID,
117 : nsIAtom* aAttribute,
118 : int32_t aModType,
119 : const nsAttrValue* aOldValue)
120 : {
121 : // An attribute belonging to the element that we are observing *or one of its
122 : // descendants* has changed.
123 : //
124 : // In the case of observing a gradient element, say, we want to know if any
125 : // of its 'stop' element children change, but we don't actually want to do
126 : // anything for changes to SMIL element children, for example. Maybe it's not
127 : // worth having logic to optimize for that, but in most cases it could be a
128 : // small check?
129 : //
130 : // XXXjwatt: do we really want to blindly break the link between our
131 : // observers and ourselves for all attribute changes? For non-ID changes
132 : // surely that is unnecessary.
133 :
134 0 : DoUpdate();
135 0 : }
136 :
137 : void
138 0 : nsSVGRenderingObserver::ContentAppended(nsIDocument* aDocument,
139 : nsIContent* aContainer,
140 : nsIContent* aFirstNewContent,
141 : int32_t /* unused */)
142 : {
143 0 : DoUpdate();
144 0 : }
145 :
146 : void
147 0 : nsSVGRenderingObserver::ContentInserted(nsIDocument* aDocument,
148 : nsIContent* aContainer,
149 : nsIContent* aChild,
150 : int32_t /* unused */)
151 : {
152 0 : DoUpdate();
153 0 : }
154 :
155 : void
156 0 : nsSVGRenderingObserver::ContentRemoved(nsIDocument* aDocument,
157 : nsIContent* aContainer,
158 : nsIContent* aChild,
159 : int32_t aIndexInContainer,
160 : nsIContent* aPreviousSibling)
161 : {
162 0 : DoUpdate();
163 0 : }
164 :
165 : /**
166 : * Note that in the current setup there are two separate observer lists.
167 : *
168 : * In nsSVGIDRenderingObserver's ctor, the new object adds itself to the
169 : * mutation observer list maintained by the referenced element. In this way the
170 : * nsSVGIDRenderingObserver is notified if there are any attribute or content
171 : * tree changes to the element or any of its *descendants*.
172 : *
173 : * In nsSVGIDRenderingObserver::GetReferencedElement() the
174 : * nsSVGIDRenderingObserver object also adds itself to an
175 : * nsSVGRenderingObserverList object belonging to the referenced
176 : * element.
177 : *
178 : * XXX: it would be nice to have a clear and concise executive summary of the
179 : * benefits/necessity of maintaining a second observer list.
180 : */
181 :
182 3 : nsSVGIDRenderingObserver::nsSVGIDRenderingObserver(nsIURI* aURI,
183 : nsIContent* aObservingContent,
184 3 : bool aReferenceImage)
185 3 : : mElement(this)
186 : {
187 : // Start watching the target element
188 3 : mElement.Reset(aObservingContent, aURI, true, aReferenceImage);
189 3 : StartListening();
190 3 : }
191 :
192 0 : nsSVGIDRenderingObserver::~nsSVGIDRenderingObserver()
193 : {
194 0 : StopListening();
195 0 : }
196 :
197 : void
198 0 : nsSVGIDRenderingObserver::DoUpdate()
199 : {
200 0 : if (mElement.get() && mInObserverList) {
201 0 : nsSVGEffects::RemoveRenderingObserver(mElement.get(), this);
202 0 : mInObserverList = false;
203 : }
204 0 : }
205 :
206 : void
207 0 : nsSVGFrameReferenceFromProperty::Detach()
208 : {
209 0 : mFrame = nullptr;
210 0 : mFramePresShell = nullptr;
211 0 : }
212 :
213 : nsIFrame*
214 0 : nsSVGFrameReferenceFromProperty::Get()
215 : {
216 0 : if (mFramePresShell && mFramePresShell->IsDestroying()) {
217 : // mFrame is no longer valid.
218 0 : Detach();
219 : }
220 0 : return mFrame;
221 : }
222 :
223 3 : NS_IMPL_ISUPPORTS(nsSVGRenderingObserverProperty, nsIMutationObserver)
224 :
225 : void
226 0 : nsSVGRenderingObserverProperty::DoUpdate()
227 : {
228 0 : nsSVGIDRenderingObserver::DoUpdate();
229 :
230 0 : nsIFrame* frame = mFrameReference.Get();
231 0 : if (frame && frame->IsFrameOfType(nsIFrame::eSVG)) {
232 : // Changes should propagate out to things that might be observing
233 : // the referencing frame or its ancestors.
234 0 : nsLayoutUtils::PostRestyleEvent(
235 0 : frame->GetContent()->AsElement(), nsRestyleHint(0),
236 0 : nsChangeHint_InvalidateRenderingObservers);
237 : }
238 0 : }
239 :
240 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSVGFilterReference)
241 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSVGFilterReference)
242 :
243 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsSVGFilterReference)
244 :
245 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsSVGFilterReference)
246 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElement)
247 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
248 :
249 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsSVGFilterReference)
250 0 : tmp->StopListening();
251 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mElement);
252 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
253 :
254 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSVGFilterReference)
255 0 : NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsSVGIDRenderingObserver)
256 0 : NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
257 0 : NS_INTERFACE_MAP_ENTRY(nsISVGFilterReference)
258 0 : NS_INTERFACE_MAP_END
259 :
260 : nsSVGFilterFrame *
261 0 : nsSVGFilterReference::GetFilterFrame()
262 : {
263 : return static_cast<nsSVGFilterFrame*>(
264 0 : GetReferencedFrame(LayoutFrameType::SVGFilter, nullptr));
265 : }
266 :
267 : void
268 0 : nsSVGFilterReference::DoUpdate()
269 : {
270 0 : nsSVGIDRenderingObserver::DoUpdate();
271 :
272 0 : if (mFilterChainObserver) {
273 0 : mFilterChainObserver->Invalidate();
274 : }
275 0 : }
276 :
277 0 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsSVGFilterChainObserver)
278 0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsSVGFilterChainObserver)
279 :
280 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsSVGFilterChainObserver)
281 :
282 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsSVGFilterChainObserver)
283 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReferences)
284 0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
285 :
286 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsSVGFilterChainObserver)
287 0 : tmp->DetachReferences();
288 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mReferences);
289 0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_END
290 :
291 0 : NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsSVGFilterChainObserver)
292 0 : NS_INTERFACE_MAP_ENTRY(nsISupports)
293 0 : NS_INTERFACE_MAP_END
294 :
295 0 : nsSVGFilterChainObserver::nsSVGFilterChainObserver(const nsTArray<nsStyleFilter>& aFilters,
296 : nsIContent* aFilteredElement,
297 0 : nsIFrame* aFilteredFrame)
298 : {
299 0 : for (uint32_t i = 0; i < aFilters.Length(); i++) {
300 0 : if (aFilters[i].GetType() != NS_STYLE_FILTER_URL)
301 0 : continue;
302 :
303 : // aFilteredFrame can be null if this filter belongs to a
304 : // CanvasRenderingContext2D.
305 : nsCOMPtr<nsIURI> filterURL = aFilteredFrame
306 0 : ? nsSVGEffects::GetFilterURI(aFilteredFrame, i)
307 0 : : aFilters[i].GetURL()->ResolveLocalRef(aFilteredElement);
308 :
309 : RefPtr<nsSVGFilterReference> reference =
310 0 : new nsSVGFilterReference(filterURL, aFilteredElement, this);
311 0 : mReferences.AppendElement(reference);
312 : }
313 0 : }
314 :
315 0 : nsSVGFilterChainObserver::~nsSVGFilterChainObserver()
316 : {
317 0 : DetachReferences();
318 0 : }
319 :
320 : bool
321 0 : nsSVGFilterChainObserver::ReferencesValidResources()
322 : {
323 0 : for (uint32_t i = 0; i < mReferences.Length(); i++) {
324 0 : if (!mReferences[i]->ReferencesValidResource())
325 0 : return false;
326 : }
327 0 : return true;
328 : }
329 :
330 : bool
331 0 : nsSVGFilterChainObserver::IsInObserverLists() const
332 : {
333 0 : for (uint32_t i = 0; i < mReferences.Length(); i++) {
334 0 : if (!mReferences[i]->IsInObserverList())
335 0 : return false;
336 : }
337 0 : return true;
338 : }
339 :
340 : void
341 0 : nsSVGFilterProperty::DoUpdate()
342 : {
343 0 : nsIFrame* frame = mFrameReference.Get();
344 0 : if (!frame)
345 0 : return;
346 :
347 : // Repaint asynchronously in case the filter frame is being torn down
348 : nsChangeHint changeHint =
349 0 : nsChangeHint(nsChangeHint_RepaintFrame);
350 :
351 0 : if (frame && frame->IsFrameOfType(nsIFrame::eSVG)) {
352 : // Changes should propagate out to things that might be observing
353 : // the referencing frame or its ancestors.
354 0 : changeHint |= nsChangeHint_InvalidateRenderingObservers;
355 : }
356 :
357 : // Don't need to request UpdateOverflow if we're being reflowed.
358 0 : if (!(frame->GetStateBits() & NS_FRAME_IN_REFLOW)) {
359 0 : changeHint |= nsChangeHint_UpdateOverflow;
360 : }
361 0 : frame->PresContext()->RestyleManager()->PostRestyleEvent(
362 0 : frame->GetContent()->AsElement(), nsRestyleHint(0), changeHint);
363 : }
364 :
365 : void
366 0 : nsSVGMarkerProperty::DoUpdate()
367 : {
368 0 : nsSVGRenderingObserverProperty::DoUpdate();
369 :
370 0 : nsIFrame* frame = mFrameReference.Get();
371 0 : if (!frame)
372 0 : return;
373 :
374 0 : NS_ASSERTION(frame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected");
375 :
376 : // Repaint asynchronously in case the marker frame is being torn down
377 : nsChangeHint changeHint =
378 0 : nsChangeHint(nsChangeHint_RepaintFrame);
379 :
380 : // Don't need to request ReflowFrame if we're being reflowed.
381 0 : if (!(frame->GetStateBits() & NS_FRAME_IN_REFLOW)) {
382 0 : changeHint |= nsChangeHint_InvalidateRenderingObservers;
383 : // XXXjwatt: We need to unify SVG into standard reflow so we can just use
384 : // nsChangeHint_NeedReflow | nsChangeHint_NeedDirtyReflow here.
385 : // XXXSDL KILL THIS!!!
386 0 : nsSVGUtils::ScheduleReflowSVG(frame);
387 : }
388 0 : frame->PresContext()->RestyleManager()->PostRestyleEvent(
389 0 : frame->GetContent()->AsElement(), nsRestyleHint(0), changeHint);
390 : }
391 :
392 1 : NS_IMPL_ISUPPORTS(nsSVGMaskProperty, nsISupports)
393 :
394 1 : nsSVGMaskProperty::nsSVGMaskProperty(nsIFrame* aFrame)
395 : {
396 1 : const nsStyleSVGReset *svgReset = aFrame->StyleSVGReset();
397 :
398 2 : for (uint32_t i = 0; i < svgReset->mMask.mImageCount; i++) {
399 2 : nsCOMPtr<nsIURI> maskUri = nsSVGEffects::GetMaskURI(aFrame, i);
400 : nsSVGPaintingProperty* prop = new nsSVGPaintingProperty(maskUri, aFrame,
401 2 : false);
402 1 : mProperties.AppendElement(prop);
403 : }
404 1 : }
405 :
406 : bool
407 0 : nsSVGTextPathProperty::TargetIsValid()
408 : {
409 0 : Element* target = GetTarget();
410 0 : return target && target->IsSVGElement(nsGkAtoms::path);
411 : }
412 :
413 : void
414 0 : nsSVGTextPathProperty::DoUpdate()
415 : {
416 0 : nsSVGRenderingObserverProperty::DoUpdate();
417 :
418 0 : nsIFrame* frame = mFrameReference.Get();
419 0 : if (!frame)
420 0 : return;
421 :
422 0 : NS_ASSERTION(frame->IsFrameOfType(nsIFrame::eSVG) ||
423 : nsSVGUtils::IsInSVGTextSubtree(frame),
424 : "SVG frame expected");
425 :
426 : // Avoid getting into an infinite loop of reflows if the <textPath> is
427 : // pointing to one of its ancestors. TargetIsValid returns true iff
428 : // the target element is a <path> element, and we would not have this
429 : // nsSVGTextPathProperty if this <textPath> were a descendant of the
430 : // target <path>.
431 : //
432 : // Note that we still have to post the restyle event when we
433 : // change from being valid to invalid, so that mPositions on the
434 : // SVGTextFrame gets updated, skipping the <textPath>, ensuring
435 : // that nothing gets painted for that element.
436 0 : bool nowValid = TargetIsValid();
437 0 : if (!mValid && !nowValid) {
438 : // Just return if we were previously invalid, and are still invalid.
439 0 : return;
440 : }
441 0 : mValid = nowValid;
442 :
443 : // Repaint asynchronously in case the path frame is being torn down
444 : nsChangeHint changeHint =
445 0 : nsChangeHint(nsChangeHint_RepaintFrame | nsChangeHint_UpdateTextPath);
446 0 : frame->PresContext()->RestyleManager()->PostRestyleEvent(
447 0 : frame->GetContent()->AsElement(), nsRestyleHint(0), changeHint);
448 : }
449 :
450 : static void
451 0 : InvalidateAllContinuations(nsIFrame* aFrame)
452 : {
453 0 : for (nsIFrame* f = aFrame; f;
454 : f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) {
455 0 : f->InvalidateFrame();
456 : }
457 0 : }
458 :
459 : void
460 0 : nsSVGPaintingProperty::DoUpdate()
461 : {
462 0 : nsSVGRenderingObserverProperty::DoUpdate();
463 :
464 0 : nsIFrame* frame = mFrameReference.Get();
465 0 : if (!frame)
466 0 : return;
467 :
468 0 : if (frame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
469 0 : nsLayoutUtils::PostRestyleEvent(
470 0 : frame->GetContent()->AsElement(), nsRestyleHint(0),
471 0 : nsChangeHint_InvalidateRenderingObservers);
472 0 : frame->InvalidateFrameSubtree();
473 : } else {
474 0 : InvalidateAllContinuations(frame);
475 : }
476 : }
477 :
478 : static nsSVGFilterProperty*
479 137 : GetOrCreateFilterProperty(nsIFrame* aFrame)
480 : {
481 137 : const nsStyleEffects* effects = aFrame->StyleEffects();
482 137 : if (!effects->HasFilters())
483 137 : return nullptr;
484 :
485 : nsSVGFilterProperty *prop =
486 0 : aFrame->GetProperty(nsSVGEffects::FilterProperty());
487 0 : if (prop)
488 0 : return prop;
489 0 : prop = new nsSVGFilterProperty(effects->mFilters, aFrame);
490 0 : NS_ADDREF(prop);
491 0 : aFrame->SetProperty(nsSVGEffects::FilterProperty(), prop);
492 0 : return prop;
493 : }
494 :
495 : static nsSVGMaskProperty*
496 15 : GetOrCreateMaskProperty(nsIFrame* aFrame)
497 : {
498 15 : nsSVGMaskProperty *prop = aFrame->GetProperty(nsSVGEffects::MaskProperty());
499 15 : if (prop)
500 14 : return prop;
501 :
502 1 : prop = new nsSVGMaskProperty(aFrame);
503 1 : NS_ADDREF(prop);
504 1 : aFrame->SetProperty(nsSVGEffects::MaskProperty(), prop);
505 1 : return prop;
506 : }
507 :
508 : template<class T>
509 : static T*
510 392 : GetEffectProperty(nsIURI* aURI, nsIFrame* aFrame,
511 : const mozilla::FramePropertyDescriptor<T>* aProperty)
512 : {
513 392 : if (!aURI)
514 384 : return nullptr;
515 :
516 8 : T* prop = aFrame->GetProperty(aProperty);
517 8 : if (prop)
518 6 : return prop;
519 2 : prop = new T(aURI, aFrame, false);
520 2 : NS_ADDREF(prop);
521 2 : aFrame->SetProperty(aProperty, prop);
522 2 : return prop;
523 : }
524 :
525 : nsSVGMarkerProperty*
526 384 : nsSVGEffects::GetMarkerProperty(nsIURI* aURI, nsIFrame* aFrame,
527 : const mozilla::FramePropertyDescriptor<nsSVGMarkerProperty>* aProperty)
528 : {
529 384 : MOZ_ASSERT(aFrame->IsSVGGeometryFrame() &&
530 : static_cast<SVGGeometryElement*>(aFrame->GetContent())->IsMarkable(),
531 : "Bad frame");
532 384 : return GetEffectProperty(aURI, aFrame, aProperty);
533 : }
534 :
535 : nsSVGTextPathProperty*
536 0 : nsSVGEffects::GetTextPathProperty(nsIURI* aURI, nsIFrame* aFrame,
537 : const mozilla::FramePropertyDescriptor<nsSVGTextPathProperty>* aProperty)
538 : {
539 0 : return GetEffectProperty(aURI, aFrame, aProperty);
540 : }
541 :
542 : nsSVGPaintingProperty*
543 8 : nsSVGEffects::GetPaintingProperty(nsIURI* aURI, nsIFrame* aFrame,
544 : const mozilla::FramePropertyDescriptor<nsSVGPaintingProperty>* aProperty)
545 : {
546 8 : return GetEffectProperty(aURI, aFrame, aProperty);
547 : }
548 :
549 : nsSVGPaintingProperty*
550 0 : nsSVGEffects::GetPaintingPropertyForURI(nsIURI* aURI, nsIFrame* aFrame,
551 : URIObserverHashtablePropertyDescriptor aProperty)
552 : {
553 0 : if (!aURI)
554 0 : return nullptr;
555 :
556 : nsSVGEffects::URIObserverHashtable *hashtable =
557 0 : aFrame->GetProperty(aProperty);
558 0 : if (!hashtable) {
559 0 : hashtable = new nsSVGEffects::URIObserverHashtable();
560 0 : aFrame->SetProperty(aProperty, hashtable);
561 : }
562 : nsSVGPaintingProperty* prop =
563 0 : static_cast<nsSVGPaintingProperty*>(hashtable->GetWeak(aURI));
564 0 : if (!prop) {
565 0 : bool watchImage = aProperty == nsSVGEffects::BackgroundImageProperty();
566 0 : prop = new nsSVGPaintingProperty(aURI, aFrame, watchImage);
567 0 : hashtable->Put(aURI, prop);
568 : }
569 0 : return prop;
570 : }
571 :
572 : nsSVGEffects::EffectProperties
573 37 : nsSVGEffects::GetEffectProperties(nsIFrame* aFrame)
574 : {
575 37 : NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation");
576 :
577 : EffectProperties result;
578 37 : const nsStyleSVGReset *style = aFrame->StyleSVGReset();
579 :
580 37 : result.mFilter = GetOrCreateFilterProperty(aFrame);
581 :
582 37 : if (style->mClipPath.GetType() == StyleShapeSourceType::URL) {
583 16 : nsCOMPtr<nsIURI> pathURI = nsSVGEffects::GetClipPathURI(aFrame);
584 8 : result.mClipPath =
585 8 : GetPaintingProperty(pathURI, aFrame, ClipPathProperty());
586 : } else {
587 29 : result.mClipPath = nullptr;
588 : }
589 :
590 37 : MOZ_ASSERT(style->mMask.mImageCount > 0);
591 74 : result.mMask = style->HasMask()
592 37 : ? GetOrCreateMaskProperty(aFrame) : nullptr;
593 :
594 37 : return result;
595 : }
596 :
597 : nsSVGPaintServerFrame *
598 34 : nsSVGEffects::GetPaintServer(nsIFrame* aTargetFrame,
599 : nsStyleSVGPaint nsStyleSVG::* aPaint,
600 : PaintingPropertyDescriptor aType)
601 : {
602 34 : const nsStyleSVG* svgStyle = aTargetFrame->StyleSVG();
603 34 : if ((svgStyle->*aPaint).Type() != eStyleSVGPaintType_Server)
604 34 : return nullptr;
605 :
606 : // If we're looking at a frame within SVG text, then we need to look up
607 : // to find the right frame to get the painting property off. We should at
608 : // least look up past a text frame, and if the text frame's parent is the
609 : // anonymous block frame, then we look up to its parent (the SVGTextFrame).
610 0 : nsIFrame* frame = aTargetFrame;
611 0 : if (frame->GetContent()->IsNodeOfType(nsINode::eTEXT)) {
612 0 : frame = frame->GetParent();
613 0 : nsIFrame* grandparent = frame->GetParent();
614 0 : if (grandparent && grandparent->IsSVGTextFrame()) {
615 0 : frame = grandparent;
616 : }
617 : }
618 :
619 0 : nsCOMPtr<nsIURI> paintServerURL = nsSVGEffects::GetPaintURI(frame, aPaint);
620 : nsSVGPaintingProperty *property =
621 0 : nsSVGEffects::GetPaintingProperty(paintServerURL, frame, aType);
622 0 : if (!property)
623 0 : return nullptr;
624 0 : nsIFrame* result = property->GetReferencedFrame();
625 0 : if (!result)
626 0 : return nullptr;
627 :
628 0 : LayoutFrameType type = result->Type();
629 0 : if (type != LayoutFrameType::SVGLinearGradient &&
630 0 : type != LayoutFrameType::SVGRadialGradient &&
631 : type != LayoutFrameType::SVGPattern)
632 0 : return nullptr;
633 :
634 0 : return static_cast<nsSVGPaintServerFrame*>(result);
635 : }
636 :
637 : nsSVGClipPathFrame *
638 22 : nsSVGEffects::EffectProperties::GetClipPathFrame()
639 : {
640 22 : if (!mClipPath)
641 18 : return nullptr;
642 :
643 : nsSVGClipPathFrame* frame = static_cast<nsSVGClipPathFrame*>(
644 4 : mClipPath->GetReferencedFrame(LayoutFrameType::SVGClipPath, nullptr));
645 :
646 4 : return frame;
647 : }
648 :
649 : nsTArray<nsSVGMaskFrame *>
650 15 : nsSVGEffects::EffectProperties::GetMaskFrames()
651 : {
652 15 : nsTArray<nsSVGMaskFrame *> result;
653 15 : if (!mMask)
654 6 : return result;
655 :
656 9 : bool ok = true;
657 9 : const nsTArray<RefPtr<nsSVGPaintingProperty>>& props = mMask->GetProps();
658 18 : for (size_t i = 0; i < props.Length(); i++) {
659 : nsSVGMaskFrame* maskFrame = static_cast<nsSVGMaskFrame*>(
660 9 : props[i]->GetReferencedFrame(LayoutFrameType::SVGMask, &ok));
661 9 : MOZ_ASSERT(!maskFrame || ok);
662 9 : result.AppendElement(maskFrame);
663 : }
664 :
665 9 : return result;
666 : }
667 :
668 : bool
669 0 : nsSVGEffects::EffectProperties::HasNoOrValidEffects()
670 : {
671 0 : return HasNoOrValidClipPath() && HasNoOrValidMask() && HasNoOrValidFilter();
672 : }
673 :
674 : bool
675 9 : nsSVGEffects::EffectProperties::HasNoOrValidClipPath()
676 : {
677 9 : if (mClipPath) {
678 2 : bool ok = true;
679 : nsSVGClipPathFrame* frame = static_cast<nsSVGClipPathFrame*>(
680 2 : mClipPath->GetReferencedFrame(LayoutFrameType::SVGClipPath, &ok));
681 2 : if (!ok || (frame && !frame->IsValid())) {
682 0 : return false;
683 : }
684 : }
685 :
686 9 : return true;
687 : }
688 :
689 : bool
690 7 : nsSVGEffects::EffectProperties::HasNoOrValidMask()
691 : {
692 7 : if (mMask) {
693 5 : bool ok = true;
694 5 : const nsTArray<RefPtr<nsSVGPaintingProperty>>& props = mMask->GetProps();
695 10 : for (size_t i = 0; i < props.Length(); i++) {
696 5 : props[i]->GetReferencedFrame(LayoutFrameType::SVGMask, &ok);
697 5 : if (!ok) {
698 0 : return false;
699 : }
700 : }
701 : }
702 :
703 7 : return true;
704 : }
705 :
706 : void
707 100 : nsSVGEffects::UpdateEffects(nsIFrame* aFrame)
708 : {
709 100 : NS_ASSERTION(aFrame->GetContent()->IsElement(),
710 : "aFrame's content should be an element");
711 :
712 100 : aFrame->DeleteProperty(FilterProperty());
713 100 : aFrame->DeleteProperty(MaskProperty());
714 100 : aFrame->DeleteProperty(ClipPathProperty());
715 100 : aFrame->DeleteProperty(MarkerBeginProperty());
716 100 : aFrame->DeleteProperty(MarkerMiddleProperty());
717 100 : aFrame->DeleteProperty(MarkerEndProperty());
718 100 : aFrame->DeleteProperty(FillProperty());
719 100 : aFrame->DeleteProperty(StrokeProperty());
720 100 : aFrame->DeleteProperty(BackgroundImageProperty());
721 :
722 : // Ensure that the filter is repainted correctly
723 : // We can't do that in DoUpdate as the referenced frame may not be valid
724 100 : GetOrCreateFilterProperty(aFrame);
725 :
726 148 : if (aFrame->IsSVGGeometryFrame() &&
727 48 : static_cast<SVGGeometryElement*>(aFrame->GetContent())->IsMarkable()) {
728 : // Set marker properties here to avoid reference loops
729 : nsCOMPtr<nsIURI> markerURL =
730 76 : GetMarkerURI(aFrame, &nsStyleSVG::mMarkerStart);
731 38 : GetMarkerProperty(markerURL, aFrame, MarkerBeginProperty());
732 38 : markerURL = GetMarkerURI(aFrame, &nsStyleSVG::mMarkerMid);
733 38 : GetMarkerProperty(markerURL, aFrame, MarkerMiddleProperty());
734 38 : markerURL = GetMarkerURI(aFrame, &nsStyleSVG::mMarkerEnd);
735 38 : GetMarkerProperty(markerURL, aFrame, MarkerEndProperty());
736 : }
737 100 : }
738 :
739 : nsSVGFilterProperty*
740 11 : nsSVGEffects::GetFilterProperty(nsIFrame* aFrame)
741 : {
742 11 : NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation");
743 :
744 11 : if (!aFrame->StyleEffects()->HasFilters())
745 11 : return nullptr;
746 :
747 0 : return aFrame->GetProperty(FilterProperty());
748 : }
749 :
750 : void
751 18 : nsSVGRenderingObserverList::InvalidateAll()
752 : {
753 18 : if (mObservers.Count() == 0)
754 0 : return;
755 :
756 36 : AutoTArray<nsSVGRenderingObserver*,10> observers;
757 :
758 36 : for (auto it = mObservers.Iter(); !it.Done(); it.Next()) {
759 18 : observers.AppendElement(it.Get()->GetKey());
760 : }
761 18 : mObservers.Clear();
762 :
763 36 : for (uint32_t i = 0; i < observers.Length(); ++i) {
764 18 : observers[i]->InvalidateViaReferencedElement();
765 : }
766 : }
767 :
768 : void
769 54 : nsSVGRenderingObserverList::InvalidateAllForReflow()
770 : {
771 54 : if (mObservers.Count() == 0)
772 0 : return;
773 :
774 108 : AutoTArray<nsSVGRenderingObserver*,10> observers;
775 :
776 108 : for (auto it = mObservers.Iter(); !it.Done(); it.Next()) {
777 54 : nsSVGRenderingObserver* obs = it.Get()->GetKey();
778 54 : if (obs->ObservesReflow()) {
779 54 : observers.AppendElement(obs);
780 54 : it.Remove();
781 : }
782 : }
783 :
784 108 : for (uint32_t i = 0; i < observers.Length(); ++i) {
785 54 : observers[i]->InvalidateViaReferencedElement();
786 : }
787 : }
788 :
789 : void
790 0 : nsSVGRenderingObserverList::RemoveAll()
791 : {
792 0 : AutoTArray<nsSVGRenderingObserver*,10> observers;
793 :
794 0 : for (auto it = mObservers.Iter(); !it.Done(); it.Next()) {
795 0 : observers.AppendElement(it.Get()->GetKey());
796 : }
797 0 : mObservers.Clear();
798 :
799 : // Our list is now cleared. We need to notify the observers we've removed,
800 : // so they can update their state & remove themselves as mutation-observers.
801 0 : for (uint32_t i = 0; i < observers.Length(); ++i) {
802 0 : observers[i]->NotifyEvictedFromRenderingObserverList();
803 : }
804 0 : }
805 :
806 : void
807 95 : nsSVGEffects::AddRenderingObserver(Element* aElement,
808 : nsSVGRenderingObserver* aObserver)
809 : {
810 95 : nsSVGRenderingObserverList *observerList = GetObserverList(aElement);
811 95 : if (!observerList) {
812 23 : observerList = new nsSVGRenderingObserverList();
813 23 : if (!observerList)
814 0 : return;
815 23 : aElement->SetProperty(nsGkAtoms::renderingobserverlist, observerList,
816 46 : nsINode::DeleteProperty<nsSVGRenderingObserverList>);
817 : }
818 95 : aElement->SetHasRenderingObservers(true);
819 95 : observerList->Add(aObserver);
820 : }
821 :
822 : void
823 0 : nsSVGEffects::RemoveRenderingObserver(Element* aElement,
824 : nsSVGRenderingObserver* aObserver)
825 : {
826 0 : nsSVGRenderingObserverList *observerList = GetObserverList(aElement);
827 0 : if (observerList) {
828 0 : NS_ASSERTION(observerList->Contains(aObserver),
829 : "removing observer from an element we're not observing?");
830 0 : observerList->Remove(aObserver);
831 0 : if (observerList->IsEmpty()) {
832 0 : aElement->SetHasRenderingObservers(false);
833 : }
834 : }
835 0 : }
836 :
837 : void
838 0 : nsSVGEffects::RemoveAllRenderingObservers(Element* aElement)
839 : {
840 0 : nsSVGRenderingObserverList *observerList = GetObserverList(aElement);
841 0 : if (observerList) {
842 0 : observerList->RemoveAll();
843 0 : aElement->SetHasRenderingObservers(false);
844 : }
845 0 : }
846 :
847 : void
848 23 : nsSVGEffects::InvalidateRenderingObservers(nsIFrame* aFrame)
849 : {
850 23 : NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame must be first continuation");
851 :
852 23 : nsIContent* content = aFrame->GetContent();
853 23 : if (!content || !content->IsElement())
854 0 : return;
855 :
856 : // If the rendering has changed, the bounds may well have changed too:
857 23 : aFrame->DeleteProperty(nsSVGUtils::ObjectBoundingBoxProperty());
858 :
859 : nsSVGRenderingObserverList *observerList =
860 23 : GetObserverList(content->AsElement());
861 23 : if (observerList) {
862 0 : observerList->InvalidateAll();
863 0 : return;
864 : }
865 :
866 : // Check ancestor SVG containers. The root frame cannot be of type
867 : // eSVGContainer so we don't have to check f for null here.
868 196 : for (nsIFrame *f = aFrame->GetParent();
869 98 : f->IsFrameOfType(nsIFrame::eSVGContainer); f = f->GetParent()) {
870 75 : if (f->GetContent()->IsElement()) {
871 75 : observerList = GetObserverList(f->GetContent()->AsElement());
872 75 : if (observerList) {
873 0 : observerList->InvalidateAll();
874 0 : return;
875 : }
876 : }
877 : }
878 : }
879 :
880 : void
881 3404 : nsSVGEffects::InvalidateDirectRenderingObservers(Element* aElement, uint32_t aFlags /* = 0 */)
882 : {
883 3404 : nsIFrame* frame = aElement->GetPrimaryFrame();
884 3404 : if (frame) {
885 : // If the rendering has changed, the bounds may well have changed too:
886 3344 : frame->DeleteProperty(nsSVGUtils::ObjectBoundingBoxProperty());
887 : }
888 :
889 3404 : if (aElement->HasRenderingObservers()) {
890 72 : nsSVGRenderingObserverList *observerList = GetObserverList(aElement);
891 72 : if (observerList) {
892 72 : if (aFlags & INVALIDATE_REFLOW) {
893 54 : observerList->InvalidateAllForReflow();
894 : } else {
895 18 : observerList->InvalidateAll();
896 : }
897 : }
898 : }
899 3404 : }
900 :
901 : void
902 3842 : nsSVGEffects::InvalidateDirectRenderingObservers(nsIFrame* aFrame, uint32_t aFlags /* = 0 */)
903 : {
904 3842 : nsIContent* content = aFrame->GetContent();
905 3842 : if (content && content->IsElement()) {
906 3400 : InvalidateDirectRenderingObservers(content->AsElement(), aFlags);
907 : }
908 3842 : }
909 :
910 : already_AddRefed<nsIURI>
911 31 : nsSVGEffects::GetBaseURLForLocalRef(nsIContent* content, nsIURI* aDocURI)
912 : {
913 31 : MOZ_ASSERT(content);
914 :
915 : // For a local-reference URL, resolve that fragment against the current
916 : // document that relative URLs are resolved against.
917 62 : nsCOMPtr<nsIURI> baseURI = content->OwnerDoc()->GetDocumentURI();
918 :
919 31 : if (content->IsInAnonymousSubtree()) {
920 0 : nsIContent* bindingParent = content->GetBindingParent();
921 0 : nsCOMPtr<nsIURI> originalURI;
922 :
923 : // content is in a shadow tree. If this URL was specified in the subtree
924 : // referenced by the <use>(or -moz-binding) element, and that subtree came
925 : // from a separate resource document, then we want the fragment-only URL
926 : // to resolve to an element from the resource document. Otherwise, the
927 : // URL was specified somewhere in the document with the <use> element, and
928 : // we want the fragment-only URL to resolve to an element in that document.
929 0 : if (bindingParent) {
930 0 : if (content->IsAnonymousContentInSVGUseSubtree()) {
931 0 : SVGUseElement* useElement = static_cast<SVGUseElement*>(bindingParent);
932 0 : originalURI = useElement->GetSourceDocURI();
933 : } else {
934 0 : nsXBLBinding* binding = bindingParent->GetXBLBinding();
935 0 : if (binding) {
936 0 : originalURI = binding->GetSourceDocURI();
937 : } else {
938 0 : MOZ_ASSERT(content->IsInNativeAnonymousSubtree(),
939 : "an non-native anonymous tree which is not from "
940 : "an XBL binding?");
941 : }
942 : }
943 :
944 0 : if (originalURI) {
945 0 : bool isEqualsExceptRef = false;
946 0 : aDocURI->EqualsExceptRef(originalURI, &isEqualsExceptRef);
947 0 : if (isEqualsExceptRef) {
948 0 : baseURI = originalURI;
949 : }
950 : }
951 : }
952 : }
953 :
954 62 : return baseURI.forget();
955 : }
956 :
957 : static already_AddRefed<nsIURI>
958 393 : ResolveURLUsingLocalRef(nsIFrame* aFrame, const css::URLValueData* aURL)
959 : {
960 393 : MOZ_ASSERT(aFrame);
961 :
962 393 : if (!aURL) {
963 385 : return nullptr;
964 : }
965 :
966 : // Non-local-reference URL.
967 8 : if (!aURL->IsLocalRef()) {
968 0 : nsCOMPtr<nsIURI> result = aURL->GetURI();
969 0 : return result.forget();
970 : }
971 :
972 : nsCOMPtr<nsIURI> baseURI =
973 16 : nsSVGEffects::GetBaseURLForLocalRef(aFrame->GetContent(), aURL->GetURI());
974 :
975 8 : return aURL->ResolveLocalRef(baseURI);
976 : }
977 :
978 : already_AddRefed<nsIURI>
979 384 : nsSVGEffects::GetMarkerURI(nsIFrame* aFrame,
980 : RefPtr<css::URLValue> nsStyleSVG::* aMarker)
981 : {
982 384 : return ResolveURLUsingLocalRef(aFrame, aFrame->StyleSVG()->*aMarker);
983 : }
984 :
985 : already_AddRefed<nsIURI>
986 8 : nsSVGEffects::GetClipPathURI(nsIFrame* aFrame)
987 : {
988 8 : const nsStyleSVGReset* svgResetStyle = aFrame->StyleSVGReset();
989 8 : MOZ_ASSERT(svgResetStyle->mClipPath.GetType() == StyleShapeSourceType::URL);
990 :
991 8 : css::URLValue* url = svgResetStyle->mClipPath.GetURL();
992 8 : return ResolveURLUsingLocalRef(aFrame, url);
993 : }
994 :
995 : already_AddRefed<nsIURI>
996 0 : nsSVGEffects::GetFilterURI(nsIFrame* aFrame, uint32_t aIndex)
997 : {
998 0 : const nsStyleEffects* effects = aFrame->StyleEffects();
999 0 : MOZ_ASSERT(effects->mFilters.Length() > aIndex);
1000 0 : MOZ_ASSERT(effects->mFilters[aIndex].GetType() == NS_STYLE_FILTER_URL);
1001 :
1002 0 : return ResolveURLUsingLocalRef(aFrame, effects->mFilters[aIndex].GetURL());
1003 : }
1004 :
1005 : already_AddRefed<nsIURI>
1006 0 : nsSVGEffects::GetFilterURI(nsIFrame* aFrame, const nsStyleFilter& aFilter)
1007 : {
1008 0 : MOZ_ASSERT(aFrame->StyleEffects()->mFilters.Length());
1009 0 : MOZ_ASSERT(aFilter.GetType() == NS_STYLE_FILTER_URL);
1010 :
1011 0 : return ResolveURLUsingLocalRef(aFrame, aFilter.GetURL());
1012 : }
1013 :
1014 : already_AddRefed<nsIURI>
1015 0 : nsSVGEffects::GetPaintURI(nsIFrame* aFrame,
1016 : nsStyleSVGPaint nsStyleSVG::* aPaint)
1017 : {
1018 0 : const nsStyleSVG* svgStyle = aFrame->StyleSVG();
1019 0 : MOZ_ASSERT((svgStyle->*aPaint).Type() ==
1020 : nsStyleSVGPaintType::eStyleSVGPaintType_Server);
1021 :
1022 : return ResolveURLUsingLocalRef(aFrame,
1023 0 : (svgStyle->*aPaint).GetPaintServer());
1024 : }
1025 :
1026 : already_AddRefed<nsIURI>
1027 1 : nsSVGEffects::GetMaskURI(nsIFrame* aFrame, uint32_t aIndex)
1028 : {
1029 1 : const nsStyleSVGReset* svgReset = aFrame->StyleSVGReset();
1030 1 : MOZ_ASSERT(svgReset->mMask.mLayers.Length() > aIndex);
1031 :
1032 : mozilla::css::URLValueData* data =
1033 1 : svgReset->mMask.mLayers[aIndex].mImage.GetURLValue();
1034 1 : return ResolveURLUsingLocalRef(aFrame, data);
1035 : }
|