/src/mozilla-central/layout/svg/SVGObserverUtils.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | // Main header first: |
8 | | #include "SVGObserverUtils.h" |
9 | | |
10 | | // Keep others in (case-insensitive) order: |
11 | | #include "mozilla/RestyleManager.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 "SVGTextPathElement.h" |
22 | | #include "SVGUseElement.h" |
23 | | #include "ImageLoader.h" |
24 | | #include "mozilla/net/ReferrerPolicy.h" |
25 | | |
26 | | using namespace mozilla::dom; |
27 | | |
28 | | namespace mozilla { |
29 | | |
30 | | void |
31 | | SVGRenderingObserver::StartObserving() |
32 | 0 | { |
33 | 0 | Element* target = GetTarget(); |
34 | 0 | if (target) { |
35 | 0 | target->AddMutationObserver(this); |
36 | 0 | } |
37 | 0 | } |
38 | | |
39 | | void |
40 | | SVGRenderingObserver::StopObserving() |
41 | 0 | { |
42 | 0 | Element* target = GetTarget(); |
43 | 0 |
|
44 | 0 | if (target) { |
45 | 0 | target->RemoveMutationObserver(this); |
46 | 0 | if (mInObserverList) { |
47 | 0 | SVGObserverUtils::RemoveRenderingObserver(target, this); |
48 | 0 | mInObserverList = false; |
49 | 0 | } |
50 | 0 | } |
51 | 0 | NS_ASSERTION(!mInObserverList, "still in an observer list?"); |
52 | 0 | } |
53 | | |
54 | | static SVGRenderingObserverList* |
55 | | GetObserverList(Element *aElement) |
56 | 0 | { |
57 | 0 | return static_cast<SVGRenderingObserverList*> |
58 | 0 | (aElement->GetProperty(nsGkAtoms::renderingobserverlist)); |
59 | 0 | } |
60 | | |
61 | | Element* |
62 | | SVGRenderingObserver::GetReferencedElement() |
63 | 0 | { |
64 | 0 | Element* target = GetTarget(); |
65 | | #ifdef DEBUG |
66 | | if (target) { |
67 | | SVGRenderingObserverList* observerList = GetObserverList(target); |
68 | | bool inObserverList = observerList && observerList->Contains(this); |
69 | | NS_ASSERTION(inObserverList == mInObserverList, "failed to track whether we're in our referenced element's observer list!"); |
70 | | } else { |
71 | | NS_ASSERTION(!mInObserverList, "In whose observer list are we, then?"); |
72 | | } |
73 | | #endif |
74 | 0 | if (target && !mInObserverList) { |
75 | 0 | SVGObserverUtils::AddRenderingObserver(target, this); |
76 | 0 | mInObserverList = true; |
77 | 0 | } |
78 | 0 | return target; |
79 | 0 | } |
80 | | |
81 | | nsIFrame* |
82 | | SVGRenderingObserver::GetReferencedFrame() |
83 | 0 | { |
84 | 0 | Element* referencedElement = GetReferencedElement(); |
85 | 0 | return referencedElement ? referencedElement->GetPrimaryFrame() : nullptr; |
86 | 0 | } |
87 | | |
88 | | nsIFrame* |
89 | | SVGRenderingObserver::GetReferencedFrame(LayoutFrameType aFrameType, |
90 | | bool* aOK) |
91 | 0 | { |
92 | 0 | nsIFrame* frame = GetReferencedFrame(); |
93 | 0 | if (frame) { |
94 | 0 | if (frame->Type() == aFrameType) |
95 | 0 | return frame; |
96 | 0 | if (aOK) { |
97 | 0 | *aOK = false; |
98 | 0 | } |
99 | 0 | } |
100 | 0 | return nullptr; |
101 | 0 | } |
102 | | |
103 | | void |
104 | | SVGRenderingObserver::OnNonDOMMutationRenderingChange() |
105 | 0 | { |
106 | 0 | mInObserverList = false; |
107 | 0 | OnRenderingChange(); |
108 | 0 | } |
109 | | |
110 | | void |
111 | | SVGRenderingObserver::NotifyEvictedFromRenderingObserverList() |
112 | 0 | { |
113 | 0 | mInObserverList = false; // We've been removed from rendering-obs. list. |
114 | 0 | StopObserving(); // Remove ourselves from mutation-obs. list. |
115 | 0 | } |
116 | | |
117 | | void |
118 | | SVGRenderingObserver::AttributeChanged(dom::Element* aElement, |
119 | | int32_t aNameSpaceID, |
120 | | nsAtom* aAttribute, |
121 | | int32_t aModType, |
122 | | const nsAttrValue* aOldValue) |
123 | 0 | { |
124 | 0 | // An attribute belonging to the element that we are observing *or one of its |
125 | 0 | // descendants* has changed. |
126 | 0 | // |
127 | 0 | // In the case of observing a gradient element, say, we want to know if any |
128 | 0 | // of its 'stop' element children change, but we don't actually want to do |
129 | 0 | // anything for changes to SMIL element children, for example. Maybe it's not |
130 | 0 | // worth having logic to optimize for that, but in most cases it could be a |
131 | 0 | // small check? |
132 | 0 | // |
133 | 0 | // XXXjwatt: do we really want to blindly break the link between our |
134 | 0 | // observers and ourselves for all attribute changes? For non-ID changes |
135 | 0 | // surely that is unnecessary. |
136 | 0 |
|
137 | 0 | OnRenderingChange(); |
138 | 0 | } |
139 | | |
140 | | void |
141 | | SVGRenderingObserver::ContentAppended(nsIContent* aFirstNewContent) |
142 | 0 | { |
143 | 0 | OnRenderingChange(); |
144 | 0 | } |
145 | | |
146 | | void |
147 | | SVGRenderingObserver::ContentInserted(nsIContent* aChild) |
148 | 0 | { |
149 | 0 | OnRenderingChange(); |
150 | 0 | } |
151 | | |
152 | | void |
153 | | SVGRenderingObserver::ContentRemoved(nsIContent* aChild, |
154 | | nsIContent* aPreviousSibling) |
155 | 0 | { |
156 | 0 | OnRenderingChange(); |
157 | 0 | } |
158 | | |
159 | | /** |
160 | | * Note that in the current setup there are two separate observer lists. |
161 | | * |
162 | | * In SVGIDRenderingObserver's ctor, the new object adds itself to the |
163 | | * mutation observer list maintained by the referenced element. In this way the |
164 | | * SVGIDRenderingObserver is notified if there are any attribute or content |
165 | | * tree changes to the element or any of its *descendants*. |
166 | | * |
167 | | * In SVGIDRenderingObserver::GetReferencedElement() the |
168 | | * SVGIDRenderingObserver object also adds itself to an |
169 | | * SVGRenderingObserverList object belonging to the referenced |
170 | | * element. |
171 | | * |
172 | | * XXX: it would be nice to have a clear and concise executive summary of the |
173 | | * benefits/necessity of maintaining a second observer list. |
174 | | */ |
175 | | |
176 | | SVGIDRenderingObserver::SVGIDRenderingObserver(URLAndReferrerInfo* aURI, |
177 | | nsIContent* aObservingContent, |
178 | | bool aReferenceImage) |
179 | | : mObservedElementTracker(this) |
180 | 0 | { |
181 | 0 | // Start watching the target element |
182 | 0 | nsCOMPtr<nsIURI> uri; |
183 | 0 | nsCOMPtr<nsIURI> referrer; |
184 | 0 | uint32_t referrerPolicy = mozilla::net::RP_Unset; |
185 | 0 | if (aURI) { |
186 | 0 | uri = aURI->GetURI(); |
187 | 0 | referrer = aURI->GetReferrer(); |
188 | 0 | referrerPolicy = aURI->GetReferrerPolicy(); |
189 | 0 | } |
190 | 0 |
|
191 | 0 | mObservedElementTracker.Reset(aObservingContent, uri, referrer, |
192 | 0 | referrerPolicy, true, aReferenceImage); |
193 | 0 | StartObserving(); |
194 | 0 | } |
195 | | |
196 | | SVGIDRenderingObserver::~SVGIDRenderingObserver() |
197 | 0 | { |
198 | 0 | StopObserving(); |
199 | 0 | } |
200 | | |
201 | | void |
202 | | SVGIDRenderingObserver::OnRenderingChange() |
203 | 0 | { |
204 | 0 | if (mObservedElementTracker.get() && mInObserverList) { |
205 | 0 | SVGObserverUtils::RemoveRenderingObserver(mObservedElementTracker.get(), this); |
206 | 0 | mInObserverList = false; |
207 | 0 | } |
208 | 0 | } |
209 | | |
210 | | void |
211 | | nsSVGFrameReferenceFromProperty::Detach() |
212 | 0 | { |
213 | 0 | mFrame = nullptr; |
214 | 0 | mFramePresShell = nullptr; |
215 | 0 | } |
216 | | |
217 | | nsIFrame* |
218 | | nsSVGFrameReferenceFromProperty::Get() |
219 | 0 | { |
220 | 0 | if (mFramePresShell && mFramePresShell->IsDestroying()) { |
221 | 0 | // mFrame is no longer valid. |
222 | 0 | Detach(); |
223 | 0 | } |
224 | 0 | return mFrame; |
225 | 0 | } |
226 | | |
227 | | |
228 | | NS_IMPL_ISUPPORTS(SVGTemplateElementObserver, nsIMutationObserver) |
229 | | |
230 | | void |
231 | | SVGTemplateElementObserver::OnRenderingChange() |
232 | 0 | { |
233 | 0 | SVGIDRenderingObserver::OnRenderingChange(); |
234 | 0 |
|
235 | 0 | if (nsIFrame* frame = mFrameReference.Get()) { |
236 | 0 | // We know that we don't need to walk the parent chain notifying rendering |
237 | 0 | // observers since changes to a gradient etc. do not affect ancestor |
238 | 0 | // elements. So we only invalidate *direct* rendering observers here. |
239 | 0 | // Since we don't need to walk the parent chain, we don't need to worry |
240 | 0 | // about coalescing multiple invalidations by using a change hint as we do |
241 | 0 | // in nsSVGRenderingObserverProperty::OnRenderingChange. |
242 | 0 | SVGObserverUtils::InvalidateDirectRenderingObservers(frame); |
243 | 0 | } |
244 | 0 | } |
245 | | |
246 | | |
247 | | NS_IMPL_ISUPPORTS(nsSVGRenderingObserverProperty, nsIMutationObserver) |
248 | | |
249 | | void |
250 | | nsSVGRenderingObserverProperty::OnRenderingChange() |
251 | 0 | { |
252 | 0 | SVGIDRenderingObserver::OnRenderingChange(); |
253 | 0 |
|
254 | 0 | nsIFrame* frame = mFrameReference.Get(); |
255 | 0 |
|
256 | 0 | if (frame && frame->HasAllStateBits(NS_FRAME_SVG_LAYOUT)) { |
257 | 0 | // We need to notify anything that is observing the referencing frame or |
258 | 0 | // any of its ancestors that the referencing frame has been invalidated. |
259 | 0 | // Since walking the parent chain checking for observers is expensive we |
260 | 0 | // do that using a change hint (multiple change hints of the same type are |
261 | 0 | // coalesced). |
262 | 0 | nsLayoutUtils::PostRestyleEvent( |
263 | 0 | frame->GetContent()->AsElement(), nsRestyleHint(0), |
264 | 0 | nsChangeHint_InvalidateRenderingObservers); |
265 | 0 | } |
266 | 0 | } |
267 | | |
268 | | |
269 | | NS_IMPL_CYCLE_COLLECTING_ADDREF(SVGFilterObserver) |
270 | | NS_IMPL_CYCLE_COLLECTING_RELEASE(SVGFilterObserver) |
271 | | |
272 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGFilterObserver) |
273 | 0 | NS_INTERFACE_MAP_ENTRY(nsISupports) |
274 | 0 | NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) |
275 | 0 | NS_INTERFACE_MAP_ENTRY(SVGFilterObserver) |
276 | 0 | NS_INTERFACE_MAP_END |
277 | | |
278 | | NS_IMPL_CYCLE_COLLECTION_CLASS(SVGFilterObserver) |
279 | | |
280 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SVGFilterObserver) |
281 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObservedElementTracker) |
282 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
283 | | |
284 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SVGFilterObserver) |
285 | 0 | tmp->StopObserving(); |
286 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mObservedElementTracker); |
287 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
288 | | |
289 | | nsSVGFilterFrame * |
290 | | SVGFilterObserver::GetFilterFrame() |
291 | 0 | { |
292 | 0 | return static_cast<nsSVGFilterFrame*>( |
293 | 0 | GetReferencedFrame(LayoutFrameType::SVGFilter, nullptr)); |
294 | 0 | } |
295 | | |
296 | | void |
297 | | SVGFilterObserver::OnRenderingChange() |
298 | 0 | { |
299 | 0 | SVGIDRenderingObserver::OnRenderingChange(); |
300 | 0 |
|
301 | 0 | if (mFilterObserverList) { |
302 | 0 | mFilterObserverList->Invalidate(); |
303 | 0 | } |
304 | 0 | } |
305 | | |
306 | | NS_IMPL_CYCLE_COLLECTING_ADDREF(SVGFilterObserverList) |
307 | | NS_IMPL_CYCLE_COLLECTING_RELEASE(SVGFilterObserverList) |
308 | | |
309 | | NS_IMPL_CYCLE_COLLECTION_CLASS(SVGFilterObserverList) |
310 | | |
311 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SVGFilterObserverList) |
312 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mObservers) |
313 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
314 | | |
315 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SVGFilterObserverList) |
316 | 0 | tmp->DetachObservers(); |
317 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mObservers); |
318 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
319 | | |
320 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SVGFilterObserverList) |
321 | 0 | NS_INTERFACE_MAP_ENTRY(nsISupports) |
322 | 0 | NS_INTERFACE_MAP_END |
323 | | |
324 | | SVGFilterObserverList::SVGFilterObserverList(const nsTArray<nsStyleFilter>& aFilters, |
325 | | nsIContent* aFilteredElement, |
326 | | nsIFrame* aFilteredFrame) |
327 | 0 | { |
328 | 0 | for (uint32_t i = 0; i < aFilters.Length(); i++) { |
329 | 0 | if (aFilters[i].GetType() != NS_STYLE_FILTER_URL) |
330 | 0 | continue; |
331 | 0 | |
332 | 0 | // aFilteredFrame can be null if this filter belongs to a |
333 | 0 | // CanvasRenderingContext2D. |
334 | 0 | RefPtr<URLAndReferrerInfo> filterURL; |
335 | 0 | if (aFilteredFrame) { |
336 | 0 | filterURL = SVGObserverUtils::GetFilterURI(aFilteredFrame, i); |
337 | 0 | } else { |
338 | 0 | nsCOMPtr<nsIURI> resolvedURI = |
339 | 0 | aFilters[i].GetURL()->ResolveLocalRef(aFilteredElement); |
340 | 0 | if (resolvedURI) { |
341 | 0 | filterURL = new URLAndReferrerInfo( |
342 | 0 | resolvedURI, |
343 | 0 | aFilters[i].GetURL()->mExtraData->GetReferrer(), |
344 | 0 | aFilters[i].GetURL()->mExtraData->GetReferrerPolicy()); |
345 | 0 | } |
346 | 0 | } |
347 | 0 |
|
348 | 0 | RefPtr<SVGFilterObserver> observer = |
349 | 0 | new SVGFilterObserver(filterURL, aFilteredElement, this); |
350 | 0 | mObservers.AppendElement(observer); |
351 | 0 | } |
352 | 0 | } |
353 | | |
354 | | SVGFilterObserverList::~SVGFilterObserverList() |
355 | 0 | { |
356 | 0 | DetachObservers(); |
357 | 0 | } |
358 | | |
359 | | bool |
360 | | SVGFilterObserverList::ReferencesValidResources() |
361 | 0 | { |
362 | 0 | for (uint32_t i = 0; i < mObservers.Length(); i++) { |
363 | 0 | if (!mObservers[i]->ReferencesValidResource()) { |
364 | 0 | return false; |
365 | 0 | } |
366 | 0 | } |
367 | 0 | return true; |
368 | 0 | } |
369 | | |
370 | | bool |
371 | | SVGFilterObserverList::IsInObserverLists() const |
372 | 0 | { |
373 | 0 | for (uint32_t i = 0; i < mObservers.Length(); i++) { |
374 | 0 | if (!mObservers[i]->IsInObserverList()) { |
375 | 0 | return false; |
376 | 0 | } |
377 | 0 | } |
378 | 0 | return true; |
379 | 0 | } |
380 | | |
381 | | void |
382 | | SVGFilterObserverListForCSSProp::OnRenderingChange() |
383 | 0 | { |
384 | 0 | nsIFrame* frame = mFrameReference.Get(); |
385 | 0 | if (!frame) |
386 | 0 | return; |
387 | 0 | |
388 | 0 | // Repaint asynchronously in case the filter frame is being torn down |
389 | 0 | nsChangeHint changeHint = |
390 | 0 | nsChangeHint(nsChangeHint_RepaintFrame); |
391 | 0 |
|
392 | 0 | // Since we don't call nsSVGRenderingObserverProperty:: |
393 | 0 | // OnRenderingChange, we have to add this bit ourselves. |
394 | 0 | if (frame->HasAllStateBits(NS_FRAME_SVG_LAYOUT)) { |
395 | 0 | // Changes should propagate out to things that might be observing |
396 | 0 | // the referencing frame or its ancestors. |
397 | 0 | changeHint |= nsChangeHint_InvalidateRenderingObservers; |
398 | 0 | } |
399 | 0 |
|
400 | 0 | // Don't need to request UpdateOverflow if we're being reflowed. |
401 | 0 | if (!(frame->GetStateBits() & NS_FRAME_IN_REFLOW)) { |
402 | 0 | changeHint |= nsChangeHint_UpdateOverflow; |
403 | 0 | } |
404 | 0 | frame->PresContext()->RestyleManager()->PostRestyleEvent( |
405 | 0 | frame->GetContent()->AsElement(), nsRestyleHint(0), changeHint); |
406 | 0 | } |
407 | | |
408 | | void |
409 | | SVGMarkerObserver::OnRenderingChange() |
410 | 0 | { |
411 | 0 | nsSVGRenderingObserverProperty::OnRenderingChange(); |
412 | 0 |
|
413 | 0 | nsIFrame* frame = mFrameReference.Get(); |
414 | 0 | if (!frame) |
415 | 0 | return; |
416 | 0 | |
417 | 0 | NS_ASSERTION(frame->IsFrameOfType(nsIFrame::eSVG), "SVG frame expected"); |
418 | 0 |
|
419 | 0 | // Don't need to request ReflowFrame if we're being reflowed. |
420 | 0 | if (!(frame->GetStateBits() & NS_FRAME_IN_REFLOW)) { |
421 | 0 | // XXXjwatt: We need to unify SVG into standard reflow so we can just use |
422 | 0 | // nsChangeHint_NeedReflow | nsChangeHint_NeedDirtyReflow here. |
423 | 0 | // XXXSDL KILL THIS!!! |
424 | 0 | nsSVGUtils::ScheduleReflowSVG(frame); |
425 | 0 | } |
426 | 0 | frame->PresContext()->RestyleManager()->PostRestyleEvent( |
427 | 0 | frame->GetContent()->AsElement(), nsRestyleHint(0), |
428 | 0 | nsChangeHint_RepaintFrame); |
429 | 0 | } |
430 | | |
431 | | NS_IMPL_ISUPPORTS(SVGMaskObserverList, nsISupports) |
432 | | |
433 | | SVGMaskObserverList::SVGMaskObserverList(nsIFrame* aFrame) |
434 | | : mFrame(aFrame) |
435 | 0 | { |
436 | 0 | const nsStyleSVGReset *svgReset = aFrame->StyleSVGReset(); |
437 | 0 |
|
438 | 0 | for (uint32_t i = 0; i < svgReset->mMask.mImageCount; i++) { |
439 | 0 | RefPtr<URLAndReferrerInfo> maskUri = |
440 | 0 | SVGObserverUtils::GetMaskURI(aFrame, i); |
441 | 0 | bool hasRef = false; |
442 | 0 | if (maskUri) { |
443 | 0 | maskUri->GetURI()->GetHasRef(&hasRef); |
444 | 0 | } |
445 | 0 |
|
446 | 0 | // Accrording to maskUri, nsSVGPaintingProperty's ctor may trigger an |
447 | 0 | // external SVG resource download, so we should pass maskUri in only if |
448 | 0 | // maskUri has a chance pointing to an SVG mask resource. |
449 | 0 | // |
450 | 0 | // And, an URL may refer to an SVG mask resource if it consists of |
451 | 0 | // a fragment. |
452 | 0 | nsSVGPaintingProperty* prop = |
453 | 0 | new nsSVGPaintingProperty(hasRef ? maskUri.get() : nullptr, |
454 | 0 | aFrame, false); |
455 | 0 | mProperties.AppendElement(prop); |
456 | 0 | } |
457 | 0 | } |
458 | | |
459 | | void |
460 | | SVGMaskObserverList::ResolveImage(uint32_t aIndex) |
461 | 0 | { |
462 | 0 | const nsStyleSVGReset* svgReset = mFrame->StyleSVGReset(); |
463 | 0 | MOZ_ASSERT(aIndex < svgReset->mMask.mImageCount); |
464 | 0 |
|
465 | 0 | nsStyleImage& image = |
466 | 0 | const_cast<nsStyleImage&>(svgReset->mMask.mLayers[aIndex].mImage); |
467 | 0 |
|
468 | 0 | if (!image.IsResolved()) { |
469 | 0 | MOZ_ASSERT(image.GetType() == nsStyleImageType::eStyleImageType_Image); |
470 | 0 | image.ResolveImage(mFrame->PresContext(), nullptr); |
471 | 0 |
|
472 | 0 | mozilla::css::ImageLoader* imageLoader = |
473 | 0 | mFrame->PresContext()->Document()->StyleImageLoader(); |
474 | 0 | if (imgRequestProxy* req = image.GetImageData()) { |
475 | 0 | imageLoader->AssociateRequestToFrame(req, mFrame, 0); |
476 | 0 | } |
477 | 0 | } |
478 | 0 | } |
479 | | |
480 | | bool |
481 | | SVGTextPathObserver::TargetIsValid() |
482 | 0 | { |
483 | 0 | Element* target = GetTarget(); |
484 | 0 | return target && target->IsSVGElement(nsGkAtoms::path); |
485 | 0 | } |
486 | | |
487 | | void |
488 | | SVGTextPathObserver::OnRenderingChange() |
489 | 0 | { |
490 | 0 | nsSVGRenderingObserverProperty::OnRenderingChange(); |
491 | 0 |
|
492 | 0 | nsIFrame* frame = mFrameReference.Get(); |
493 | 0 | if (!frame) |
494 | 0 | return; |
495 | 0 | |
496 | 0 | NS_ASSERTION(frame->IsFrameOfType(nsIFrame::eSVG) || |
497 | 0 | nsSVGUtils::IsInSVGTextSubtree(frame), |
498 | 0 | "SVG frame expected"); |
499 | 0 |
|
500 | 0 | // Avoid getting into an infinite loop of reflows if the <textPath> is |
501 | 0 | // pointing to one of its ancestors. TargetIsValid returns true iff |
502 | 0 | // the target element is a <path> element, and we would not have this |
503 | 0 | // SVGTextPathObserver if this <textPath> were a descendant of the |
504 | 0 | // target <path>. |
505 | 0 | // |
506 | 0 | // Note that we still have to post the restyle event when we |
507 | 0 | // change from being valid to invalid, so that mPositions on the |
508 | 0 | // SVGTextFrame gets updated, skipping the <textPath>, ensuring |
509 | 0 | // that nothing gets painted for that element. |
510 | 0 | bool nowValid = TargetIsValid(); |
511 | 0 | if (!mValid && !nowValid) { |
512 | 0 | // Just return if we were previously invalid, and are still invalid. |
513 | 0 | return; |
514 | 0 | } |
515 | 0 | mValid = nowValid; |
516 | 0 |
|
517 | 0 | // Repaint asynchronously in case the path frame is being torn down |
518 | 0 | nsChangeHint changeHint = |
519 | 0 | nsChangeHint(nsChangeHint_RepaintFrame | nsChangeHint_UpdateTextPath); |
520 | 0 | frame->PresContext()->RestyleManager()->PostRestyleEvent( |
521 | 0 | frame->GetContent()->AsElement(), nsRestyleHint(0), changeHint); |
522 | 0 | } |
523 | | |
524 | | static void |
525 | | InvalidateAllContinuations(nsIFrame* aFrame) |
526 | 0 | { |
527 | 0 | for (nsIFrame* f = aFrame; f; |
528 | 0 | f = nsLayoutUtils::GetNextContinuationOrIBSplitSibling(f)) { |
529 | 0 | f->InvalidateFrame(); |
530 | 0 | } |
531 | 0 | } |
532 | | |
533 | | void |
534 | | nsSVGPaintingProperty::OnRenderingChange() |
535 | 0 | { |
536 | 0 | nsSVGRenderingObserverProperty::OnRenderingChange(); |
537 | 0 |
|
538 | 0 | nsIFrame* frame = mFrameReference.Get(); |
539 | 0 | if (!frame) |
540 | 0 | return; |
541 | 0 | |
542 | 0 | if (frame->GetStateBits() & NS_FRAME_SVG_LAYOUT) { |
543 | 0 | frame->InvalidateFrameSubtree(); |
544 | 0 | } else { |
545 | 0 | InvalidateAllContinuations(frame); |
546 | 0 | } |
547 | 0 | } |
548 | | |
549 | | static SVGFilterObserverListForCSSProp* |
550 | | GetOrCreateFilterObserverListForCSS(nsIFrame* aFrame) |
551 | 0 | { |
552 | 0 | const nsStyleEffects* effects = aFrame->StyleEffects(); |
553 | 0 | if (!effects->HasFilters()) |
554 | 0 | return nullptr; |
555 | 0 | |
556 | 0 | SVGFilterObserverListForCSSProp* observers = |
557 | 0 | aFrame->GetProperty(SVGObserverUtils::FilterProperty()); |
558 | 0 | if (observers) { |
559 | 0 | return observers; |
560 | 0 | } |
561 | 0 | observers = new SVGFilterObserverListForCSSProp(effects->mFilters, aFrame); |
562 | 0 | NS_ADDREF(observers); |
563 | 0 | aFrame->SetProperty(SVGObserverUtils::FilterProperty(), observers); |
564 | 0 | return observers; |
565 | 0 | } |
566 | | |
567 | | static SVGMaskObserverList* |
568 | | GetOrCreateMaskProperty(nsIFrame* aFrame) |
569 | 0 | { |
570 | 0 | SVGMaskObserverList *prop = |
571 | 0 | aFrame->GetProperty(SVGObserverUtils::MaskProperty()); |
572 | 0 | if (prop) |
573 | 0 | return prop; |
574 | 0 | |
575 | 0 | prop = new SVGMaskObserverList(aFrame); |
576 | 0 | NS_ADDREF(prop); |
577 | 0 | aFrame->SetProperty(SVGObserverUtils::MaskProperty(), prop); |
578 | 0 | return prop; |
579 | 0 | } |
580 | | |
581 | | template<class T> |
582 | | static T* |
583 | | GetEffectProperty(URLAndReferrerInfo* aURI, nsIFrame* aFrame, |
584 | | const mozilla::FramePropertyDescriptor<T>* aProperty) |
585 | 0 | { |
586 | 0 | if (!aURI) |
587 | 0 | return nullptr; |
588 | 0 | |
589 | 0 | T* prop = aFrame->GetProperty(aProperty); |
590 | 0 | if (prop) |
591 | 0 | return prop; |
592 | 0 | prop = new T(aURI, aFrame, false); |
593 | 0 | NS_ADDREF(prop); |
594 | 0 | aFrame->SetProperty(aProperty, prop); |
595 | 0 | return prop; |
596 | 0 | } Unexecuted instantiation: Unified_cpp_layout_svg0.cpp:mozilla::SVGMarkerObserver* mozilla::GetEffectProperty<mozilla::SVGMarkerObserver>(mozilla::URLAndReferrerInfo*, nsIFrame*, mozilla::FramePropertyDescriptor<mozilla::SVGMarkerObserver> const*) Unexecuted instantiation: Unified_cpp_layout_svg0.cpp:mozilla::SVGTextPathObserver* mozilla::GetEffectProperty<mozilla::SVGTextPathObserver>(mozilla::URLAndReferrerInfo*, nsIFrame*, mozilla::FramePropertyDescriptor<mozilla::SVGTextPathObserver> const*) Unexecuted instantiation: Unified_cpp_layout_svg0.cpp:mozilla::SVGTemplateElementObserver* mozilla::GetEffectProperty<mozilla::SVGTemplateElementObserver>(mozilla::URLAndReferrerInfo*, nsIFrame*, mozilla::FramePropertyDescriptor<mozilla::SVGTemplateElementObserver> const*) Unexecuted instantiation: Unified_cpp_layout_svg0.cpp:mozilla::nsSVGPaintingProperty* mozilla::GetEffectProperty<mozilla::nsSVGPaintingProperty>(mozilla::URLAndReferrerInfo*, nsIFrame*, mozilla::FramePropertyDescriptor<mozilla::nsSVGPaintingProperty> const*) |
597 | | |
598 | | bool |
599 | | SVGObserverUtils::GetMarkerFrames(nsIFrame* aMarkedFrame, |
600 | | nsSVGMarkerFrame*(*aFrames)[3]) |
601 | 0 | { |
602 | 0 | MOZ_ASSERT(!aMarkedFrame->GetPrevContinuation() && |
603 | 0 | aMarkedFrame->IsSVGGeometryFrame() && |
604 | 0 | static_cast<SVGGeometryElement*>(aMarkedFrame->GetContent())->IsMarkable(), |
605 | 0 | "Bad frame"); |
606 | 0 |
|
607 | 0 | bool foundMarker = false; |
608 | 0 | RefPtr<URLAndReferrerInfo> markerURL; |
609 | 0 | SVGMarkerObserver* observer; |
610 | 0 | nsIFrame* marker; |
611 | 0 |
|
612 | 0 | #define GET_MARKER(type) \ |
613 | 0 | markerURL = GetMarkerURI(aMarkedFrame, &nsStyleSVG::mMarker##type); \ |
614 | 0 | observer = GetEffectProperty(markerURL, aMarkedFrame, \ |
615 | 0 | SVGObserverUtils::Marker##type##Property()); \ |
616 | 0 | marker = observer ? \ |
617 | 0 | observer->GetReferencedFrame(LayoutFrameType::SVGMarker, nullptr) :\ |
618 | 0 | nullptr; \ |
619 | 0 | foundMarker = foundMarker || bool(marker); \ |
620 | 0 | (*aFrames)[nsSVGMark::e##type] = static_cast<nsSVGMarkerFrame*>(marker); |
621 | 0 |
|
622 | 0 | GET_MARKER(Start) |
623 | 0 | GET_MARKER(Mid) |
624 | 0 | GET_MARKER(End) |
625 | 0 |
|
626 | 0 | #undef GET_MARKER |
627 | 0 |
|
628 | 0 | return foundMarker; |
629 | 0 | } |
630 | | |
631 | | SVGGeometryElement* |
632 | | SVGObserverUtils::GetTextPathsReferencedPath(nsIFrame* aTextPathFrame) |
633 | 0 | { |
634 | 0 | SVGTextPathObserver* property = |
635 | 0 | aTextPathFrame->GetProperty(SVGObserverUtils::HrefAsTextPathProperty()); |
636 | 0 |
|
637 | 0 | if (!property) { |
638 | 0 | nsIContent* content = aTextPathFrame->GetContent(); |
639 | 0 | nsAutoString href; |
640 | 0 | static_cast<SVGTextPathElement*>(content)->HrefAsString(href); |
641 | 0 | if (href.IsEmpty()) { |
642 | 0 | return nullptr; // no URL |
643 | 0 | } |
644 | 0 | |
645 | 0 | nsCOMPtr<nsIURI> targetURI; |
646 | 0 | nsCOMPtr<nsIURI> base = content->GetBaseURI(); |
647 | 0 | nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI), href, |
648 | 0 | content->GetUncomposedDoc(), base); |
649 | 0 |
|
650 | 0 | // There's no clear refererer policy spec about non-CSS SVG resource references |
651 | 0 | // Bug 1415044 to investigate which referrer we should use |
652 | 0 | RefPtr<URLAndReferrerInfo> target = |
653 | 0 | new URLAndReferrerInfo(targetURI, |
654 | 0 | content->OwnerDoc()->GetDocumentURI(), |
655 | 0 | content->OwnerDoc()->GetReferrerPolicy()); |
656 | 0 |
|
657 | 0 | property = GetEffectProperty(target, aTextPathFrame, |
658 | 0 | HrefAsTextPathProperty()); |
659 | 0 | if (!property) { |
660 | 0 | return nullptr; |
661 | 0 | } |
662 | 0 | } |
663 | 0 | |
664 | 0 | Element* element = property->GetReferencedElement(); |
665 | 0 | return (element && element->IsNodeOfType(nsINode::eSHAPE)) ? |
666 | 0 | static_cast<SVGGeometryElement*>(element) : nullptr; |
667 | 0 | } |
668 | | |
669 | | void |
670 | | SVGObserverUtils::RemoveTextPathObserver(nsIFrame* aTextPathFrame) |
671 | 0 | { |
672 | 0 | aTextPathFrame->DeleteProperty(HrefAsTextPathProperty()); |
673 | 0 | } |
674 | | |
675 | | SVGTemplateElementObserver* |
676 | | SVGObserverUtils::GetTemplateElementObserver(URLAndReferrerInfo* aURI, |
677 | | nsIFrame* aFrame, |
678 | | const mozilla::FramePropertyDescriptor<SVGTemplateElementObserver>* aProperty) |
679 | 0 | { |
680 | 0 | return GetEffectProperty(aURI, aFrame, aProperty); |
681 | 0 | } |
682 | | |
683 | | nsSVGPaintingProperty* |
684 | | SVGObserverUtils::GetPaintingProperty(URLAndReferrerInfo* aURI, nsIFrame* aFrame, |
685 | | const mozilla::FramePropertyDescriptor<nsSVGPaintingProperty>* aProperty) |
686 | 0 | { |
687 | 0 | return GetEffectProperty(aURI, aFrame, aProperty); |
688 | 0 | } |
689 | | |
690 | | nsSVGPaintingProperty* |
691 | | SVGObserverUtils::GetPaintingPropertyForURI(URLAndReferrerInfo* aURI, |
692 | | nsIFrame* aFrame, |
693 | | URIObserverHashtablePropertyDescriptor aProperty) |
694 | 0 | { |
695 | 0 | if (!aURI) |
696 | 0 | return nullptr; |
697 | 0 | |
698 | 0 | SVGObserverUtils::URIObserverHashtable *hashtable = |
699 | 0 | aFrame->GetProperty(aProperty); |
700 | 0 | if (!hashtable) { |
701 | 0 | hashtable = new SVGObserverUtils::URIObserverHashtable(); |
702 | 0 | aFrame->SetProperty(aProperty, hashtable); |
703 | 0 | } |
704 | 0 | nsSVGPaintingProperty* prop = |
705 | 0 | static_cast<nsSVGPaintingProperty*>(hashtable->GetWeak(aURI)); |
706 | 0 | if (!prop) { |
707 | 0 | bool watchImage = aProperty == SVGObserverUtils::BackgroundImageProperty(); |
708 | 0 | prop = new nsSVGPaintingProperty(aURI, aFrame, watchImage); |
709 | 0 | hashtable->Put(aURI, prop); |
710 | 0 | } |
711 | 0 | return prop; |
712 | 0 | } |
713 | | |
714 | | SVGObserverUtils::EffectProperties |
715 | | SVGObserverUtils::GetEffectProperties(nsIFrame* aFrame) |
716 | 0 | { |
717 | 0 | NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation"); |
718 | 0 |
|
719 | 0 | EffectProperties result; |
720 | 0 | const nsStyleSVGReset *style = aFrame->StyleSVGReset(); |
721 | 0 |
|
722 | 0 | result.mFilterObservers = GetOrCreateFilterObserverListForCSS(aFrame); |
723 | 0 |
|
724 | 0 | if (style->mClipPath.GetType() == StyleShapeSourceType::URL) { |
725 | 0 | RefPtr<URLAndReferrerInfo> pathURI = SVGObserverUtils::GetClipPathURI(aFrame); |
726 | 0 | result.mClipPath = |
727 | 0 | GetPaintingProperty(pathURI, aFrame, ClipPathProperty()); |
728 | 0 | } else { |
729 | 0 | result.mClipPath = nullptr; |
730 | 0 | } |
731 | 0 |
|
732 | 0 | MOZ_ASSERT(style->mMask.mImageCount > 0); |
733 | 0 | result.mMaskObservers = style->HasMask() |
734 | 0 | ? GetOrCreateMaskProperty(aFrame) : nullptr; |
735 | 0 |
|
736 | 0 | return result; |
737 | 0 | } |
738 | | |
739 | | nsSVGPaintServerFrame * |
740 | | SVGObserverUtils::GetPaintServer(nsIFrame* aTargetFrame, |
741 | | nsStyleSVGPaint nsStyleSVG::* aPaint) |
742 | 0 | { |
743 | 0 | // If we're looking at a frame within SVG text, then we need to look up |
744 | 0 | // to find the right frame to get the painting property off. We should at |
745 | 0 | // least look up past a text frame, and if the text frame's parent is the |
746 | 0 | // anonymous block frame, then we look up to its parent (the SVGTextFrame). |
747 | 0 | nsIFrame* frame = aTargetFrame; |
748 | 0 | if (frame->GetContent()->IsText()) { |
749 | 0 | frame = frame->GetParent(); |
750 | 0 | nsIFrame* grandparent = frame->GetParent(); |
751 | 0 | if (grandparent && grandparent->IsSVGTextFrame()) { |
752 | 0 | frame = grandparent; |
753 | 0 | } |
754 | 0 | } |
755 | 0 |
|
756 | 0 | const nsStyleSVG* svgStyle = frame->StyleSVG(); |
757 | 0 | if ((svgStyle->*aPaint).Type() != eStyleSVGPaintType_Server) |
758 | 0 | return nullptr; |
759 | 0 | |
760 | 0 | RefPtr<URLAndReferrerInfo> paintServerURL = |
761 | 0 | SVGObserverUtils::GetPaintURI(frame, aPaint); |
762 | 0 | MOZ_ASSERT(aPaint == &nsStyleSVG::mFill || aPaint == &nsStyleSVG::mStroke); |
763 | 0 | PaintingPropertyDescriptor propDesc = (aPaint == &nsStyleSVG::mFill) ? |
764 | 0 | SVGObserverUtils::FillProperty() : |
765 | 0 | SVGObserverUtils::StrokeProperty(); |
766 | 0 | nsSVGPaintingProperty *property = |
767 | 0 | SVGObserverUtils::GetPaintingProperty(paintServerURL, frame, propDesc); |
768 | 0 | if (!property) |
769 | 0 | return nullptr; |
770 | 0 | nsIFrame* result = property->GetReferencedFrame(); |
771 | 0 | if (!result) |
772 | 0 | return nullptr; |
773 | 0 | |
774 | 0 | LayoutFrameType type = result->Type(); |
775 | 0 | if (type != LayoutFrameType::SVGLinearGradient && |
776 | 0 | type != LayoutFrameType::SVGRadialGradient && |
777 | 0 | type != LayoutFrameType::SVGPattern) |
778 | 0 | return nullptr; |
779 | 0 | |
780 | 0 | return static_cast<nsSVGPaintServerFrame*>(result); |
781 | 0 | } |
782 | | |
783 | | nsSVGClipPathFrame * |
784 | | SVGObserverUtils::EffectProperties::GetClipPathFrame() |
785 | 0 | { |
786 | 0 | if (!mClipPath) |
787 | 0 | return nullptr; |
788 | 0 | |
789 | 0 | nsSVGClipPathFrame* frame = static_cast<nsSVGClipPathFrame*>( |
790 | 0 | mClipPath->GetReferencedFrame(LayoutFrameType::SVGClipPath, nullptr)); |
791 | 0 |
|
792 | 0 | return frame; |
793 | 0 | } |
794 | | |
795 | | nsTArray<nsSVGMaskFrame *> |
796 | | SVGObserverUtils::EffectProperties::GetMaskFrames() |
797 | 0 | { |
798 | 0 | nsTArray<nsSVGMaskFrame *> result; |
799 | 0 | if (!mMaskObservers) { |
800 | 0 | return result; |
801 | 0 | } |
802 | 0 | |
803 | 0 | bool ok = true; |
804 | 0 | const nsTArray<RefPtr<nsSVGPaintingProperty>>& observers = |
805 | 0 | mMaskObservers->GetObservers(); |
806 | 0 | for (size_t i = 0; i < observers.Length(); i++) { |
807 | 0 | nsSVGMaskFrame* maskFrame = static_cast<nsSVGMaskFrame*>( |
808 | 0 | observers[i]->GetReferencedFrame(LayoutFrameType::SVGMask, &ok)); |
809 | 0 | MOZ_ASSERT(!maskFrame || ok); |
810 | 0 | if (!ok) { |
811 | 0 | // We can not find the specific SVG mask resource in the downloaded SVG |
812 | 0 | // document. There are two possibilities: |
813 | 0 | // 1. The given resource id is invalid. |
814 | 0 | // 2. The given resource id refers to a viewbox. |
815 | 0 | // |
816 | 0 | // Hand it over to the style image. |
817 | 0 | mMaskObservers->ResolveImage(i); |
818 | 0 | } |
819 | 0 | result.AppendElement(maskFrame); |
820 | 0 | } |
821 | 0 |
|
822 | 0 | return result; |
823 | 0 | } |
824 | | |
825 | | bool |
826 | | SVGObserverUtils::EffectProperties::HasNoOrValidEffects() |
827 | 0 | { |
828 | 0 | return HasNoOrValidClipPath() && HasNoOrValidMask() && HasNoOrValidFilter(); |
829 | 0 | } |
830 | | |
831 | | bool |
832 | | SVGObserverUtils::EffectProperties::HasNoOrValidClipPath() |
833 | 0 | { |
834 | 0 | if (mClipPath) { |
835 | 0 | bool ok = true; |
836 | 0 | nsSVGClipPathFrame* frame = static_cast<nsSVGClipPathFrame*>( |
837 | 0 | mClipPath->GetReferencedFrame(LayoutFrameType::SVGClipPath, &ok)); |
838 | 0 | if (!ok || (frame && !frame->IsValid())) { |
839 | 0 | return false; |
840 | 0 | } |
841 | 0 | } |
842 | 0 | |
843 | 0 | return true; |
844 | 0 | } |
845 | | |
846 | | bool |
847 | | SVGObserverUtils::EffectProperties::HasNoOrValidMask() |
848 | 0 | { |
849 | 0 | if (mMaskObservers) { |
850 | 0 | bool ok = true; |
851 | 0 | const nsTArray<RefPtr<nsSVGPaintingProperty>>& observers = |
852 | 0 | mMaskObservers->GetObservers(); |
853 | 0 | for (size_t i = 0; i < observers.Length(); i++) { |
854 | 0 | observers[i]->GetReferencedFrame(LayoutFrameType::SVGMask, &ok); |
855 | 0 | if (!ok) { |
856 | 0 | return false; |
857 | 0 | } |
858 | 0 | } |
859 | 0 | } |
860 | 0 |
|
861 | 0 | return true; |
862 | 0 | } |
863 | | |
864 | | void |
865 | | SVGObserverUtils::UpdateEffects(nsIFrame* aFrame) |
866 | 0 | { |
867 | 0 | NS_ASSERTION(aFrame->GetContent()->IsElement(), |
868 | 0 | "aFrame's content should be an element"); |
869 | 0 |
|
870 | 0 | aFrame->DeleteProperty(FilterProperty()); |
871 | 0 | aFrame->DeleteProperty(MaskProperty()); |
872 | 0 | aFrame->DeleteProperty(ClipPathProperty()); |
873 | 0 | aFrame->DeleteProperty(MarkerStartProperty()); |
874 | 0 | aFrame->DeleteProperty(MarkerMidProperty()); |
875 | 0 | aFrame->DeleteProperty(MarkerEndProperty()); |
876 | 0 | aFrame->DeleteProperty(FillProperty()); |
877 | 0 | aFrame->DeleteProperty(StrokeProperty()); |
878 | 0 | aFrame->DeleteProperty(BackgroundImageProperty()); |
879 | 0 |
|
880 | 0 | // Ensure that the filter is repainted correctly |
881 | 0 | // We can't do that in OnRenderingChange as the referenced frame may |
882 | 0 | // not be valid |
883 | 0 | GetOrCreateFilterObserverListForCSS(aFrame); |
884 | 0 |
|
885 | 0 | if (aFrame->IsSVGGeometryFrame() && |
886 | 0 | static_cast<SVGGeometryElement*>(aFrame->GetContent())->IsMarkable()) { |
887 | 0 | // Set marker properties here to avoid reference loops |
888 | 0 | RefPtr<URLAndReferrerInfo> markerURL = |
889 | 0 | GetMarkerURI(aFrame, &nsStyleSVG::mMarkerStart); |
890 | 0 | GetEffectProperty(markerURL, aFrame, MarkerStartProperty()); |
891 | 0 | markerURL = GetMarkerURI(aFrame, &nsStyleSVG::mMarkerMid); |
892 | 0 | GetEffectProperty(markerURL, aFrame, MarkerMidProperty()); |
893 | 0 | markerURL = GetMarkerURI(aFrame, &nsStyleSVG::mMarkerEnd); |
894 | 0 | GetEffectProperty(markerURL, aFrame, MarkerEndProperty()); |
895 | 0 | } |
896 | 0 | } |
897 | | |
898 | | SVGFilterObserverListForCSSProp* |
899 | | SVGObserverUtils::GetFilterObserverList(nsIFrame* aFrame) |
900 | 0 | { |
901 | 0 | NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation"); |
902 | 0 |
|
903 | 0 | if (!aFrame->StyleEffects()->HasFilters()) |
904 | 0 | return nullptr; |
905 | 0 | |
906 | 0 | return aFrame->GetProperty(FilterProperty()); |
907 | 0 | } |
908 | | |
909 | | void |
910 | | SVGRenderingObserverList::InvalidateAll() |
911 | 0 | { |
912 | 0 | if (mObservers.Count() == 0) |
913 | 0 | return; |
914 | 0 | |
915 | 0 | AutoTArray<SVGRenderingObserver*,10> observers; |
916 | 0 |
|
917 | 0 | for (auto it = mObservers.Iter(); !it.Done(); it.Next()) { |
918 | 0 | observers.AppendElement(it.Get()->GetKey()); |
919 | 0 | } |
920 | 0 | mObservers.Clear(); |
921 | 0 |
|
922 | 0 | for (uint32_t i = 0; i < observers.Length(); ++i) { |
923 | 0 | observers[i]->OnNonDOMMutationRenderingChange(); |
924 | 0 | } |
925 | 0 | } |
926 | | |
927 | | void |
928 | | SVGRenderingObserverList::InvalidateAllForReflow() |
929 | 0 | { |
930 | 0 | if (mObservers.Count() == 0) |
931 | 0 | return; |
932 | 0 | |
933 | 0 | AutoTArray<SVGRenderingObserver*,10> observers; |
934 | 0 |
|
935 | 0 | for (auto it = mObservers.Iter(); !it.Done(); it.Next()) { |
936 | 0 | SVGRenderingObserver* obs = it.Get()->GetKey(); |
937 | 0 | if (obs->ObservesReflow()) { |
938 | 0 | observers.AppendElement(obs); |
939 | 0 | it.Remove(); |
940 | 0 | } |
941 | 0 | } |
942 | 0 |
|
943 | 0 | for (uint32_t i = 0; i < observers.Length(); ++i) { |
944 | 0 | observers[i]->OnNonDOMMutationRenderingChange(); |
945 | 0 | } |
946 | 0 | } |
947 | | |
948 | | void |
949 | | SVGRenderingObserverList::RemoveAll() |
950 | 0 | { |
951 | 0 | AutoTArray<SVGRenderingObserver*,10> observers; |
952 | 0 |
|
953 | 0 | for (auto it = mObservers.Iter(); !it.Done(); it.Next()) { |
954 | 0 | observers.AppendElement(it.Get()->GetKey()); |
955 | 0 | } |
956 | 0 | mObservers.Clear(); |
957 | 0 |
|
958 | 0 | // Our list is now cleared. We need to notify the observers we've removed, |
959 | 0 | // so they can update their state & remove themselves as mutation-observers. |
960 | 0 | for (uint32_t i = 0; i < observers.Length(); ++i) { |
961 | 0 | observers[i]->NotifyEvictedFromRenderingObserverList(); |
962 | 0 | } |
963 | 0 | } |
964 | | |
965 | | void |
966 | | SVGObserverUtils::AddRenderingObserver(Element* aElement, |
967 | | SVGRenderingObserver* aObserver) |
968 | 0 | { |
969 | 0 | SVGRenderingObserverList* observerList = GetObserverList(aElement); |
970 | 0 | if (!observerList) { |
971 | 0 | observerList = new SVGRenderingObserverList(); |
972 | 0 | if (!observerList) |
973 | 0 | return; |
974 | 0 | aElement->SetProperty(nsGkAtoms::renderingobserverlist, observerList, |
975 | 0 | nsINode::DeleteProperty<SVGRenderingObserverList>); |
976 | 0 | } |
977 | 0 | aElement->SetHasRenderingObservers(true); |
978 | 0 | observerList->Add(aObserver); |
979 | 0 | } |
980 | | |
981 | | void |
982 | | SVGObserverUtils::RemoveRenderingObserver(Element* aElement, |
983 | | SVGRenderingObserver* aObserver) |
984 | 0 | { |
985 | 0 | SVGRenderingObserverList* observerList = GetObserverList(aElement); |
986 | 0 | if (observerList) { |
987 | 0 | NS_ASSERTION(observerList->Contains(aObserver), |
988 | 0 | "removing observer from an element we're not observing?"); |
989 | 0 | observerList->Remove(aObserver); |
990 | 0 | if (observerList->IsEmpty()) { |
991 | 0 | aElement->SetHasRenderingObservers(false); |
992 | 0 | } |
993 | 0 | } |
994 | 0 | } |
995 | | |
996 | | void |
997 | | SVGObserverUtils::RemoveAllRenderingObservers(Element* aElement) |
998 | 0 | { |
999 | 0 | SVGRenderingObserverList* observerList = GetObserverList(aElement); |
1000 | 0 | if (observerList) { |
1001 | 0 | observerList->RemoveAll(); |
1002 | 0 | aElement->SetHasRenderingObservers(false); |
1003 | 0 | } |
1004 | 0 | } |
1005 | | |
1006 | | void |
1007 | | SVGObserverUtils::InvalidateRenderingObservers(nsIFrame* aFrame) |
1008 | 0 | { |
1009 | 0 | NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame must be first continuation"); |
1010 | 0 |
|
1011 | 0 | nsIContent* content = aFrame->GetContent(); |
1012 | 0 | if (!content || !content->IsElement()) |
1013 | 0 | return; |
1014 | 0 | |
1015 | 0 | // If the rendering has changed, the bounds may well have changed too: |
1016 | 0 | aFrame->DeleteProperty(nsSVGUtils::ObjectBoundingBoxProperty()); |
1017 | 0 |
|
1018 | 0 | SVGRenderingObserverList* observerList = |
1019 | 0 | GetObserverList(content->AsElement()); |
1020 | 0 | if (observerList) { |
1021 | 0 | observerList->InvalidateAll(); |
1022 | 0 | return; |
1023 | 0 | } |
1024 | 0 | |
1025 | 0 | // Check ancestor SVG containers. The root frame cannot be of type |
1026 | 0 | // eSVGContainer so we don't have to check f for null here. |
1027 | 0 | for (nsIFrame *f = aFrame->GetParent(); |
1028 | 0 | f->IsFrameOfType(nsIFrame::eSVGContainer); f = f->GetParent()) { |
1029 | 0 | if (f->GetContent()->IsElement()) { |
1030 | 0 | observerList = GetObserverList(f->GetContent()->AsElement()); |
1031 | 0 | if (observerList) { |
1032 | 0 | observerList->InvalidateAll(); |
1033 | 0 | return; |
1034 | 0 | } |
1035 | 0 | } |
1036 | 0 | } |
1037 | 0 | } |
1038 | | |
1039 | | void |
1040 | | SVGObserverUtils::InvalidateDirectRenderingObservers(Element* aElement, |
1041 | | uint32_t aFlags /* = 0 */) |
1042 | 0 | { |
1043 | 0 | nsIFrame* frame = aElement->GetPrimaryFrame(); |
1044 | 0 | if (frame) { |
1045 | 0 | // If the rendering has changed, the bounds may well have changed too: |
1046 | 0 | frame->DeleteProperty(nsSVGUtils::ObjectBoundingBoxProperty()); |
1047 | 0 | } |
1048 | 0 |
|
1049 | 0 | if (aElement->HasRenderingObservers()) { |
1050 | 0 | SVGRenderingObserverList* observerList = GetObserverList(aElement); |
1051 | 0 | if (observerList) { |
1052 | 0 | if (aFlags & INVALIDATE_REFLOW) { |
1053 | 0 | observerList->InvalidateAllForReflow(); |
1054 | 0 | } else { |
1055 | 0 | observerList->InvalidateAll(); |
1056 | 0 | } |
1057 | 0 | } |
1058 | 0 | } |
1059 | 0 | } |
1060 | | |
1061 | | void |
1062 | | SVGObserverUtils::InvalidateDirectRenderingObservers(nsIFrame* aFrame, |
1063 | | uint32_t aFlags /* = 0 */) |
1064 | 0 | { |
1065 | 0 | nsIContent* content = aFrame->GetContent(); |
1066 | 0 | if (content && content->IsElement()) { |
1067 | 0 | InvalidateDirectRenderingObservers(content->AsElement(), aFlags); |
1068 | 0 | } |
1069 | 0 | } |
1070 | | |
1071 | | already_AddRefed<nsIURI> |
1072 | | SVGObserverUtils::GetBaseURLForLocalRef(nsIContent* content, nsIURI* aDocURI) |
1073 | 0 | { |
1074 | 0 | MOZ_ASSERT(content); |
1075 | 0 |
|
1076 | 0 | // For a local-reference URL, resolve that fragment against the current |
1077 | 0 | // document that relative URLs are resolved against. |
1078 | 0 | nsCOMPtr<nsIURI> baseURI = content->OwnerDoc()->GetDocumentURI(); |
1079 | 0 |
|
1080 | 0 | nsCOMPtr<nsIURI> originalURI; |
1081 | 0 | // Content is in a shadow tree. If this URL was specified in the subtree |
1082 | 0 | // referenced by the <use>(or -moz-binding) element, and that subtree came |
1083 | 0 | // from a separate resource document, then we want the fragment-only URL |
1084 | 0 | // to resolve to an element from the resource document. Otherwise, the |
1085 | 0 | // URL was specified somewhere in the document with the <use> element, and |
1086 | 0 | // we want the fragment-only URL to resolve to an element in that document. |
1087 | 0 | if (SVGUseElement* use = content->GetContainingSVGUseShadowHost()) { |
1088 | 0 | originalURI = use->GetSourceDocURI(); |
1089 | 0 | } else if (content->IsInAnonymousSubtree()) { |
1090 | 0 | nsIContent* bindingParent = content->GetBindingParent(); |
1091 | 0 |
|
1092 | 0 | if (bindingParent) { |
1093 | 0 | nsXBLBinding* binding = bindingParent->GetXBLBinding(); |
1094 | 0 | if (binding) { |
1095 | 0 | originalURI = binding->GetSourceDocURI(); |
1096 | 0 | } else { |
1097 | 0 | MOZ_ASSERT(content->IsInNativeAnonymousSubtree(), |
1098 | 0 | "a non-native anonymous tree which is not from " |
1099 | 0 | "an XBL binding?"); |
1100 | 0 | } |
1101 | 0 | } |
1102 | 0 | } |
1103 | 0 |
|
1104 | 0 | if (originalURI) { |
1105 | 0 | bool isEqualsExceptRef = false; |
1106 | 0 | aDocURI->EqualsExceptRef(originalURI, &isEqualsExceptRef); |
1107 | 0 | if (isEqualsExceptRef) { |
1108 | 0 | return originalURI.forget(); |
1109 | 0 | } |
1110 | 0 | } |
1111 | 0 | |
1112 | 0 | return baseURI.forget(); |
1113 | 0 | } |
1114 | | |
1115 | | static already_AddRefed<URLAndReferrerInfo> |
1116 | | ResolveURLUsingLocalRef(nsIFrame* aFrame, const css::URLValueData* aURL) |
1117 | 0 | { |
1118 | 0 | MOZ_ASSERT(aFrame); |
1119 | 0 |
|
1120 | 0 | if (!aURL) { |
1121 | 0 | return nullptr; |
1122 | 0 | } |
1123 | 0 | |
1124 | 0 | nsCOMPtr<nsIURI> uri = aURL->GetURI(); |
1125 | 0 | RefPtr<URLAndReferrerInfo> result; |
1126 | 0 |
|
1127 | 0 | // Non-local-reference URL. |
1128 | 0 | if (!aURL->IsLocalRef()) { |
1129 | 0 | if (!uri) { |
1130 | 0 | return nullptr; |
1131 | 0 | } |
1132 | 0 | result = new URLAndReferrerInfo(uri, |
1133 | 0 | aURL->mExtraData->GetReferrer(), |
1134 | 0 | aURL->mExtraData->GetReferrerPolicy()); |
1135 | 0 | return result.forget(); |
1136 | 0 | } |
1137 | 0 | |
1138 | 0 | nsCOMPtr<nsIURI> baseURI = |
1139 | 0 | SVGObserverUtils::GetBaseURLForLocalRef(aFrame->GetContent(), uri); |
1140 | 0 |
|
1141 | 0 | nsCOMPtr<nsIURI> resolvedURI = aURL->ResolveLocalRef(baseURI); |
1142 | 0 | if (!resolvedURI) { |
1143 | 0 | return nullptr; |
1144 | 0 | } |
1145 | 0 | |
1146 | 0 | result = new URLAndReferrerInfo(resolvedURI, |
1147 | 0 | aURL->mExtraData->GetReferrer(), |
1148 | 0 | aURL->mExtraData->GetReferrerPolicy()); |
1149 | 0 | return result.forget(); |
1150 | 0 | } |
1151 | | |
1152 | | already_AddRefed<URLAndReferrerInfo> |
1153 | | SVGObserverUtils::GetMarkerURI(nsIFrame* aFrame, |
1154 | | RefPtr<css::URLValue> nsStyleSVG::* aMarker) |
1155 | 0 | { |
1156 | 0 | return ResolveURLUsingLocalRef(aFrame, aFrame->StyleSVG()->*aMarker); |
1157 | 0 | } |
1158 | | |
1159 | | already_AddRefed<URLAndReferrerInfo> |
1160 | | SVGObserverUtils::GetClipPathURI(nsIFrame* aFrame) |
1161 | 0 | { |
1162 | 0 | const nsStyleSVGReset* svgResetStyle = aFrame->StyleSVGReset(); |
1163 | 0 | MOZ_ASSERT(svgResetStyle->mClipPath.GetType() == StyleShapeSourceType::URL); |
1164 | 0 |
|
1165 | 0 | css::URLValue* url = svgResetStyle->mClipPath.GetURL(); |
1166 | 0 | return ResolveURLUsingLocalRef(aFrame, url); |
1167 | 0 | } |
1168 | | |
1169 | | already_AddRefed<URLAndReferrerInfo> |
1170 | | SVGObserverUtils::GetFilterURI(nsIFrame* aFrame, uint32_t aIndex) |
1171 | 0 | { |
1172 | 0 | const nsStyleEffects* effects = aFrame->StyleEffects(); |
1173 | 0 | MOZ_ASSERT(effects->mFilters.Length() > aIndex); |
1174 | 0 | MOZ_ASSERT(effects->mFilters[aIndex].GetType() == NS_STYLE_FILTER_URL); |
1175 | 0 |
|
1176 | 0 | return ResolveURLUsingLocalRef(aFrame, effects->mFilters[aIndex].GetURL()); |
1177 | 0 | } |
1178 | | |
1179 | | already_AddRefed<URLAndReferrerInfo> |
1180 | | SVGObserverUtils::GetFilterURI(nsIFrame* aFrame, const nsStyleFilter& aFilter) |
1181 | 0 | { |
1182 | 0 | MOZ_ASSERT(aFrame->StyleEffects()->mFilters.Length()); |
1183 | 0 | MOZ_ASSERT(aFilter.GetType() == NS_STYLE_FILTER_URL); |
1184 | 0 |
|
1185 | 0 | return ResolveURLUsingLocalRef(aFrame, aFilter.GetURL()); |
1186 | 0 | } |
1187 | | |
1188 | | already_AddRefed<URLAndReferrerInfo> |
1189 | | SVGObserverUtils::GetPaintURI(nsIFrame* aFrame, |
1190 | | nsStyleSVGPaint nsStyleSVG::* aPaint) |
1191 | 0 | { |
1192 | 0 | const nsStyleSVG* svgStyle = aFrame->StyleSVG(); |
1193 | 0 | MOZ_ASSERT((svgStyle->*aPaint).Type() == |
1194 | 0 | nsStyleSVGPaintType::eStyleSVGPaintType_Server); |
1195 | 0 |
|
1196 | 0 | return ResolveURLUsingLocalRef(aFrame, |
1197 | 0 | (svgStyle->*aPaint).GetPaintServer()); |
1198 | 0 | } |
1199 | | |
1200 | | already_AddRefed<URLAndReferrerInfo> |
1201 | | SVGObserverUtils::GetMaskURI(nsIFrame* aFrame, uint32_t aIndex) |
1202 | 0 | { |
1203 | 0 | const nsStyleSVGReset* svgReset = aFrame->StyleSVGReset(); |
1204 | 0 | MOZ_ASSERT(svgReset->mMask.mLayers.Length() > aIndex); |
1205 | 0 |
|
1206 | 0 | css::URLValueData* data = |
1207 | 0 | svgReset->mMask.mLayers[aIndex].mImage.GetURLValue(); |
1208 | 0 | return ResolveURLUsingLocalRef(aFrame, data); |
1209 | 0 | } |
1210 | | |
1211 | | } // namespace mozilla |
1212 | | |