/src/mozilla-central/layout/svg/nsSVGImageFrame.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 | | #include "nsSVGImageFrame.h" |
8 | | |
9 | | // Keep in (case-insensitive) order: |
10 | | #include "gfxContext.h" |
11 | | #include "gfxPlatform.h" |
12 | | #include "mozilla/gfx/2D.h" |
13 | | #include "imgIContainer.h" |
14 | | #include "nsContainerFrame.h" |
15 | | #include "nsIImageLoadingContent.h" |
16 | | #include "nsLayoutUtils.h" |
17 | | #include "imgINotificationObserver.h" |
18 | | #include "SVGObserverUtils.h" |
19 | | #include "nsSVGUtils.h" |
20 | | #include "SVGContentUtils.h" |
21 | | #include "SVGGeometryFrame.h" |
22 | | #include "SVGImageContext.h" |
23 | | #include "mozilla/dom/MutationEventBinding.h" |
24 | | #include "mozilla/dom/SVGImageElement.h" |
25 | | #include "nsIReflowCallback.h" |
26 | | #include "mozilla/Unused.h" |
27 | | |
28 | | using namespace mozilla; |
29 | | using namespace mozilla::dom; |
30 | | using namespace mozilla::gfx; |
31 | | using namespace mozilla::image; |
32 | | |
33 | | // --------------------------------------------------------------------- |
34 | | // nsQueryFrame methods |
35 | 0 | NS_QUERYFRAME_HEAD(nsSVGImageFrame) |
36 | 0 | NS_QUERYFRAME_ENTRY(nsSVGImageFrame) |
37 | 0 | NS_QUERYFRAME_TAIL_INHERITING(SVGGeometryFrame) |
38 | | |
39 | | nsIFrame* |
40 | | NS_NewSVGImageFrame(nsIPresShell* aPresShell, ComputedStyle* aStyle) |
41 | 0 | { |
42 | 0 | return new (aPresShell) nsSVGImageFrame(aStyle); |
43 | 0 | } |
44 | | |
45 | | NS_IMPL_FRAMEARENA_HELPERS(nsSVGImageFrame) |
46 | | |
47 | | nsSVGImageFrame::~nsSVGImageFrame() |
48 | 0 | { |
49 | 0 | // set the frame to null so we don't send messages to a dead object. |
50 | 0 | if (mListener) { |
51 | 0 | nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(GetContent()); |
52 | 0 | if (imageLoader) { |
53 | 0 | imageLoader->RemoveNativeObserver(mListener); |
54 | 0 | } |
55 | 0 | reinterpret_cast<nsSVGImageListener*>(mListener.get())->SetFrame(nullptr); |
56 | 0 | } |
57 | 0 | mListener = nullptr; |
58 | 0 | } |
59 | | |
60 | | void |
61 | | nsSVGImageFrame::Init(nsIContent* aContent, |
62 | | nsContainerFrame* aParent, |
63 | | nsIFrame* aPrevInFlow) |
64 | 0 | { |
65 | 0 | NS_ASSERTION(aContent->IsSVGElement(nsGkAtoms::image), |
66 | 0 | "Content is not an SVG image!"); |
67 | 0 |
|
68 | 0 | SVGGeometryFrame::Init(aContent, aParent, aPrevInFlow); |
69 | 0 |
|
70 | 0 | if (GetStateBits() & NS_FRAME_IS_NONDISPLAY) { |
71 | 0 | // Non-display frames are likely to be patterns, masks or the like. |
72 | 0 | // Treat them as always visible. |
73 | 0 | // This call must happen before the FrameCreated. This is because the |
74 | 0 | // primary frame pointer on our content node isn't set until after this |
75 | 0 | // function ends, so there is no way for the resulting OnVisibilityChange |
76 | 0 | // notification to get a frame. FrameCreated has a workaround for this in |
77 | 0 | // that it passes our frame around so it can be accessed. OnVisibilityChange |
78 | 0 | // doesn't have that workaround. |
79 | 0 | IncApproximateVisibleCount(); |
80 | 0 | } |
81 | 0 |
|
82 | 0 | mListener = new nsSVGImageListener(this); |
83 | 0 | nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(GetContent()); |
84 | 0 | if (!imageLoader) { |
85 | 0 | MOZ_CRASH("Why is this not an image loading content?"); |
86 | 0 | } |
87 | 0 |
|
88 | 0 | // We should have a PresContext now, so let's notify our image loader that |
89 | 0 | // we need to register any image animations with the refresh driver. |
90 | 0 | imageLoader->FrameCreated(this); |
91 | 0 |
|
92 | 0 | imageLoader->AddNativeObserver(mListener); |
93 | 0 | } |
94 | | |
95 | | /* virtual */ void |
96 | | nsSVGImageFrame::DestroyFrom(nsIFrame* aDestructRoot, PostDestroyData& aPostDestroyData) |
97 | 0 | { |
98 | 0 | if (GetStateBits() & NS_FRAME_IS_NONDISPLAY) { |
99 | 0 | DecApproximateVisibleCount(); |
100 | 0 | } |
101 | 0 |
|
102 | 0 | if (mReflowCallbackPosted) { |
103 | 0 | PresShell()->CancelReflowCallback(this); |
104 | 0 | mReflowCallbackPosted = false; |
105 | 0 | } |
106 | 0 |
|
107 | 0 | nsCOMPtr<nsIImageLoadingContent> imageLoader = |
108 | 0 | do_QueryInterface(nsFrame::mContent); |
109 | 0 |
|
110 | 0 | if (imageLoader) { |
111 | 0 | imageLoader->FrameDestroyed(this); |
112 | 0 | } |
113 | 0 |
|
114 | 0 | nsFrame::DestroyFrom(aDestructRoot, aPostDestroyData); |
115 | 0 | } |
116 | | |
117 | | //---------------------------------------------------------------------- |
118 | | // nsIFrame methods: |
119 | | |
120 | | nsresult |
121 | | nsSVGImageFrame::AttributeChanged(int32_t aNameSpaceID, |
122 | | nsAtom* aAttribute, |
123 | | int32_t aModType) |
124 | 0 | { |
125 | 0 | if (aNameSpaceID == kNameSpaceID_None) { |
126 | 0 | if (aAttribute == nsGkAtoms::x || |
127 | 0 | aAttribute == nsGkAtoms::y || |
128 | 0 | aAttribute == nsGkAtoms::width || |
129 | 0 | aAttribute == nsGkAtoms::height) { |
130 | 0 | nsLayoutUtils::PostRestyleEvent( |
131 | 0 | mContent->AsElement(), nsRestyleHint(0), |
132 | 0 | nsChangeHint_InvalidateRenderingObservers); |
133 | 0 | nsSVGUtils::ScheduleReflowSVG(this); |
134 | 0 | return NS_OK; |
135 | 0 | } |
136 | 0 | else if (aAttribute == nsGkAtoms::preserveAspectRatio) { |
137 | 0 | // We don't paint the content of the image using display lists, therefore |
138 | 0 | // we have to invalidate for this children-only transform changes since |
139 | 0 | // there is no layer tree to notice that the transform changed and |
140 | 0 | // recomposite. |
141 | 0 | InvalidateFrame(); |
142 | 0 | return NS_OK; |
143 | 0 | } |
144 | 0 | } |
145 | 0 | |
146 | 0 | // Currently our SMIL implementation does not modify the DOM attributes. Once |
147 | 0 | // we implement the SVG 2 SMIL behaviour this can be removed |
148 | 0 | // SVGImageElement::AfterSetAttr's implementation will be sufficient. |
149 | 0 | if (aModType == MutationEvent_Binding::SMIL && |
150 | 0 | aAttribute == nsGkAtoms::href && |
151 | 0 | (aNameSpaceID == kNameSpaceID_XLink || |
152 | 0 | aNameSpaceID == kNameSpaceID_None)) { |
153 | 0 | SVGImageElement* element = static_cast<SVGImageElement*>(GetContent()); |
154 | 0 |
|
155 | 0 | bool hrefIsSet = |
156 | 0 | element->mStringAttributes[SVGImageElement::HREF].IsExplicitlySet() || |
157 | 0 | element->mStringAttributes[SVGImageElement::XLINK_HREF].IsExplicitlySet(); |
158 | 0 | if (hrefIsSet) { |
159 | 0 | element->LoadSVGImage(true, true); |
160 | 0 | } else { |
161 | 0 | element->CancelImageRequests(true); |
162 | 0 | } |
163 | 0 | } |
164 | 0 |
|
165 | 0 | return SVGGeometryFrame::AttributeChanged(aNameSpaceID, |
166 | 0 | aAttribute, aModType); |
167 | 0 | } |
168 | | |
169 | | void |
170 | | nsSVGImageFrame::OnVisibilityChange(Visibility aNewVisibility, |
171 | | const Maybe<OnNonvisible>& aNonvisibleAction) |
172 | 0 | { |
173 | 0 | nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(GetContent()); |
174 | 0 | if (!imageLoader) { |
175 | 0 | SVGGeometryFrame::OnVisibilityChange(aNewVisibility, aNonvisibleAction); |
176 | 0 | return; |
177 | 0 | } |
178 | 0 | |
179 | 0 | imageLoader->OnVisibilityChange(aNewVisibility, aNonvisibleAction); |
180 | 0 |
|
181 | 0 | SVGGeometryFrame::OnVisibilityChange(aNewVisibility, aNonvisibleAction); |
182 | 0 | } |
183 | | |
184 | | gfx::Matrix |
185 | | nsSVGImageFrame::GetRasterImageTransform(int32_t aNativeWidth, |
186 | | int32_t aNativeHeight) |
187 | 0 | { |
188 | 0 | float x, y, width, height; |
189 | 0 | SVGImageElement *element = static_cast<SVGImageElement*>(GetContent()); |
190 | 0 | element->GetAnimatedLengthValues(&x, &y, &width, &height, nullptr); |
191 | 0 |
|
192 | 0 | Matrix viewBoxTM = |
193 | 0 | SVGContentUtils::GetViewBoxTransform(width, height, |
194 | 0 | 0, 0, aNativeWidth, aNativeHeight, |
195 | 0 | element->mPreserveAspectRatio); |
196 | 0 |
|
197 | 0 | return viewBoxTM * gfx::Matrix::Translation(x, y); |
198 | 0 | } |
199 | | |
200 | | gfx::Matrix |
201 | | nsSVGImageFrame::GetVectorImageTransform() |
202 | 0 | { |
203 | 0 | float x, y, width, height; |
204 | 0 | SVGImageElement *element = static_cast<SVGImageElement*>(GetContent()); |
205 | 0 | element->GetAnimatedLengthValues(&x, &y, &width, &height, nullptr); |
206 | 0 |
|
207 | 0 | // No viewBoxTM needed here -- our height/width overrides any concept of |
208 | 0 | // "native size" that the SVG image has, and it will handle viewBox and |
209 | 0 | // preserveAspectRatio on its own once we give it a region to draw into. |
210 | 0 |
|
211 | 0 | return gfx::Matrix::Translation(x, y); |
212 | 0 | } |
213 | | |
214 | | bool |
215 | | nsSVGImageFrame::TransformContextForPainting(gfxContext* aGfxContext, |
216 | | const gfxMatrix& aTransform) |
217 | 0 | { |
218 | 0 | gfx::Matrix imageTransform; |
219 | 0 | if (mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) { |
220 | 0 | imageTransform = GetVectorImageTransform() * ToMatrix(aTransform); |
221 | 0 | } else { |
222 | 0 | int32_t nativeWidth, nativeHeight; |
223 | 0 | if (NS_FAILED(mImageContainer->GetWidth(&nativeWidth)) || |
224 | 0 | NS_FAILED(mImageContainer->GetHeight(&nativeHeight)) || |
225 | 0 | nativeWidth == 0 || nativeHeight == 0) { |
226 | 0 | return false; |
227 | 0 | } |
228 | 0 | imageTransform = |
229 | 0 | GetRasterImageTransform(nativeWidth, nativeHeight) * ToMatrix(aTransform); |
230 | 0 |
|
231 | 0 | // NOTE: We need to cancel out the effects of Full-Page-Zoom, or else |
232 | 0 | // it'll get applied an extra time by DrawSingleUnscaledImage. |
233 | 0 | nscoord appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel(); |
234 | 0 | gfxFloat pageZoomFactor = |
235 | 0 | nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPx); |
236 | 0 | imageTransform.PreScale(pageZoomFactor, pageZoomFactor); |
237 | 0 | } |
238 | 0 |
|
239 | 0 | if (imageTransform.IsSingular()) { |
240 | 0 | return false; |
241 | 0 | } |
242 | 0 | |
243 | 0 | aGfxContext->Multiply(ThebesMatrix(imageTransform)); |
244 | 0 | return true; |
245 | 0 | } |
246 | | |
247 | | //---------------------------------------------------------------------- |
248 | | // nsSVGDisplayableFrame methods: |
249 | | void |
250 | | nsSVGImageFrame::PaintSVG(gfxContext& aContext, |
251 | | const gfxMatrix& aTransform, |
252 | | imgDrawingParams& aImgParams, |
253 | | const nsIntRect *aDirtyRect) |
254 | 0 | { |
255 | 0 | if (!StyleVisibility()->IsVisible()) { |
256 | 0 | return; |
257 | 0 | } |
258 | 0 | |
259 | 0 | float x, y, width, height; |
260 | 0 | SVGImageElement *imgElem = static_cast<SVGImageElement*>(GetContent()); |
261 | 0 | imgElem->GetAnimatedLengthValues(&x, &y, &width, &height, nullptr); |
262 | 0 | NS_ASSERTION(width > 0 && height > 0, |
263 | 0 | "Should only be painting things with valid width/height"); |
264 | 0 |
|
265 | 0 | if (!mImageContainer) { |
266 | 0 | nsCOMPtr<imgIRequest> currentRequest; |
267 | 0 | nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(GetContent()); |
268 | 0 | if (imageLoader) |
269 | 0 | imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, |
270 | 0 | getter_AddRefs(currentRequest)); |
271 | 0 |
|
272 | 0 | if (currentRequest) |
273 | 0 | currentRequest->GetImage(getter_AddRefs(mImageContainer)); |
274 | 0 | } |
275 | 0 |
|
276 | 0 | if (mImageContainer) { |
277 | 0 | gfxContextAutoSaveRestore autoRestorer(&aContext); |
278 | 0 |
|
279 | 0 | if (StyleDisplay()->IsScrollableOverflow()) { |
280 | 0 | gfxRect clipRect = nsSVGUtils::GetClipRectForFrame(this, x, y, |
281 | 0 | width, height); |
282 | 0 | nsSVGUtils::SetClipRect(&aContext, aTransform, clipRect); |
283 | 0 | } |
284 | 0 |
|
285 | 0 | if (!TransformContextForPainting(&aContext, aTransform)) { |
286 | 0 | return ; |
287 | 0 | } |
288 | 0 | |
289 | 0 | // fill-opacity doesn't affect <image>, so if we're allowed to |
290 | 0 | // optimize group opacity, the opacity used for compositing the |
291 | 0 | // image into the current canvas is just the group opacity. |
292 | 0 | float opacity = 1.0f; |
293 | 0 | if (nsSVGUtils::CanOptimizeOpacity(this)) { |
294 | 0 | opacity = StyleEffects()->mOpacity; |
295 | 0 | } |
296 | 0 |
|
297 | 0 | if (opacity != 1.0f || StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { |
298 | 0 | aContext.PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, opacity); |
299 | 0 | } |
300 | 0 |
|
301 | 0 | nscoord appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel(); |
302 | 0 | nsRect dirtyRect; // only used if aDirtyRect is non-null |
303 | 0 | if (aDirtyRect) { |
304 | 0 | NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() || |
305 | 0 | (mState & NS_FRAME_IS_NONDISPLAY), |
306 | 0 | "Display lists handle dirty rect intersection test"); |
307 | 0 | dirtyRect = ToAppUnits(*aDirtyRect, appUnitsPerDevPx); |
308 | 0 | // Adjust dirtyRect to match our local coordinate system. |
309 | 0 | nsRect rootRect = |
310 | 0 | nsSVGUtils::TransformFrameRectToOuterSVG(mRect, aTransform, |
311 | 0 | PresContext()); |
312 | 0 | dirtyRect.MoveBy(-rootRect.TopLeft()); |
313 | 0 | } |
314 | 0 |
|
315 | 0 | uint32_t flags = aImgParams.imageFlags; |
316 | 0 | if (mForceSyncDecoding) { |
317 | 0 | flags |= imgIContainer::FLAG_SYNC_DECODE; |
318 | 0 | } |
319 | 0 |
|
320 | 0 | if (mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) { |
321 | 0 | // Package up the attributes of this image element which can override the |
322 | 0 | // attributes of mImageContainer's internal SVG document. The 'width' & |
323 | 0 | // 'height' values we're passing in here are in CSS units (though they |
324 | 0 | // come from width/height *attributes* in SVG). They influence the region |
325 | 0 | // of the SVG image's internal document that is visible, in combination |
326 | 0 | // with preserveAspectRatio and viewBox. |
327 | 0 | const Maybe<SVGImageContext> context( |
328 | 0 | Some(SVGImageContext(Some(CSSIntSize::Truncate(width, height)), |
329 | 0 | Some(imgElem->mPreserveAspectRatio.GetAnimValue())))); |
330 | 0 |
|
331 | 0 | // For the actual draw operation to draw crisply (and at the right size), |
332 | 0 | // our destination rect needs to be |width|x|height|, *in dev pixels*. |
333 | 0 | LayoutDeviceSize devPxSize(width, height); |
334 | 0 | nsRect destRect(nsPoint(), |
335 | 0 | LayoutDevicePixel::ToAppUnits(devPxSize, |
336 | 0 | appUnitsPerDevPx)); |
337 | 0 |
|
338 | 0 | // Note: Can't use DrawSingleUnscaledImage for the TYPE_VECTOR case. |
339 | 0 | // That method needs our image to have a fixed native width & height, |
340 | 0 | // and that's not always true for TYPE_VECTOR images. |
341 | 0 | aImgParams.result &= nsLayoutUtils::DrawSingleImage( |
342 | 0 | aContext, |
343 | 0 | PresContext(), |
344 | 0 | mImageContainer, |
345 | 0 | nsLayoutUtils::GetSamplingFilterForFrame(this), |
346 | 0 | destRect, |
347 | 0 | aDirtyRect ? dirtyRect : destRect, |
348 | 0 | context, |
349 | 0 | flags); |
350 | 0 | } else { // mImageContainer->GetType() == TYPE_RASTER |
351 | 0 | aImgParams.result &= nsLayoutUtils::DrawSingleUnscaledImage( |
352 | 0 | aContext, |
353 | 0 | PresContext(), |
354 | 0 | mImageContainer, |
355 | 0 | nsLayoutUtils::GetSamplingFilterForFrame(this), |
356 | 0 | nsPoint(0, 0), |
357 | 0 | aDirtyRect ? &dirtyRect : nullptr, |
358 | 0 | Nothing(), |
359 | 0 | flags); |
360 | 0 | } |
361 | 0 |
|
362 | 0 | if (opacity != 1.0f || StyleEffects()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { |
363 | 0 | aContext.PopGroupAndBlend(); |
364 | 0 | } |
365 | 0 | // gfxContextAutoSaveRestore goes out of scope & cleans up our gfxContext |
366 | 0 | } |
367 | 0 | } |
368 | | |
369 | | nsIFrame* |
370 | | nsSVGImageFrame::GetFrameForPoint(const gfxPoint& aPoint) |
371 | 0 | { |
372 | 0 | if (!(GetStateBits() & NS_STATE_SVG_CLIPPATH_CHILD) && !GetHitTestFlags()) { |
373 | 0 | return nullptr; |
374 | 0 | } |
375 | 0 | |
376 | 0 | Rect rect; |
377 | 0 | SVGImageElement *element = static_cast<SVGImageElement*>(GetContent()); |
378 | 0 | element->GetAnimatedLengthValues(&rect.x, &rect.y, |
379 | 0 | &rect.width, &rect.height, nullptr); |
380 | 0 |
|
381 | 0 | if (!rect.Contains(ToPoint(aPoint))) { |
382 | 0 | return nullptr; |
383 | 0 | } |
384 | 0 | |
385 | 0 | // Special case for raster images -- we only want to accept points that fall |
386 | 0 | // in the underlying image's (scaled to fit) native bounds. That region |
387 | 0 | // doesn't necessarily map to our <image> element's [x,y,width,height] if the |
388 | 0 | // raster image's aspect ratio is being preserved. We have to look up the |
389 | 0 | // native image size & our viewBox transform in order to filter out points |
390 | 0 | // that fall outside that area. (This special case doesn't apply to vector |
391 | 0 | // images because they don't limit their drawing to explicit "native |
392 | 0 | // bounds" -- they have an infinite canvas on which to place content.) |
393 | 0 | if (StyleDisplay()->IsScrollableOverflow() && mImageContainer) { |
394 | 0 | if (mImageContainer->GetType() == imgIContainer::TYPE_RASTER) { |
395 | 0 | int32_t nativeWidth, nativeHeight; |
396 | 0 | if (NS_FAILED(mImageContainer->GetWidth(&nativeWidth)) || |
397 | 0 | NS_FAILED(mImageContainer->GetHeight(&nativeHeight)) || |
398 | 0 | nativeWidth == 0 || nativeHeight == 0) { |
399 | 0 | return nullptr; |
400 | 0 | } |
401 | 0 | Matrix viewBoxTM = |
402 | 0 | SVGContentUtils::GetViewBoxTransform(rect.width, rect.height, |
403 | 0 | 0, 0, nativeWidth, nativeHeight, |
404 | 0 | element->mPreserveAspectRatio); |
405 | 0 | if (!nsSVGUtils::HitTestRect(viewBoxTM, |
406 | 0 | 0, 0, nativeWidth, nativeHeight, |
407 | 0 | aPoint.x - rect.x, aPoint.y - rect.y)) { |
408 | 0 | return nullptr; |
409 | 0 | } |
410 | 0 | } |
411 | 0 | } |
412 | 0 | |
413 | 0 | return this; |
414 | 0 | } |
415 | | |
416 | | //---------------------------------------------------------------------- |
417 | | // SVGGeometryFrame methods: |
418 | | |
419 | | // Lie about our fill/stroke so that covered region and hit detection work properly |
420 | | |
421 | | void |
422 | | nsSVGImageFrame::ReflowSVG() |
423 | 0 | { |
424 | 0 | NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this), |
425 | 0 | "This call is probably a wasteful mistake"); |
426 | 0 |
|
427 | 0 | MOZ_ASSERT(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY), |
428 | 0 | "ReflowSVG mechanism not designed for this"); |
429 | 0 |
|
430 | 0 | if (!nsSVGUtils::NeedsReflowSVG(this)) { |
431 | 0 | return; |
432 | 0 | } |
433 | 0 | |
434 | 0 | float x, y, width, height; |
435 | 0 | SVGImageElement *element = static_cast<SVGImageElement*>(GetContent()); |
436 | 0 | element->GetAnimatedLengthValues(&x, &y, &width, &height, nullptr); |
437 | 0 |
|
438 | 0 | Rect extent(x, y, width, height); |
439 | 0 |
|
440 | 0 | if (!extent.IsEmpty()) { |
441 | 0 | mRect = nsLayoutUtils::RoundGfxRectToAppRect(extent, |
442 | 0 | AppUnitsPerCSSPixel()); |
443 | 0 | } else { |
444 | 0 | mRect.SetEmpty(); |
445 | 0 | } |
446 | 0 |
|
447 | 0 | if (mState & NS_FRAME_FIRST_REFLOW) { |
448 | 0 | // Make sure we have our filter property (if any) before calling |
449 | 0 | // FinishAndStoreOverflow (subsequent filter changes are handled off |
450 | 0 | // nsChangeHint_UpdateEffects): |
451 | 0 | SVGObserverUtils::UpdateEffects(this); |
452 | 0 |
|
453 | 0 | if (!mReflowCallbackPosted) { |
454 | 0 | nsIPresShell* shell = PresShell(); |
455 | 0 | mReflowCallbackPosted = true; |
456 | 0 | shell->PostReflowCallback(this); |
457 | 0 | } |
458 | 0 | } |
459 | 0 |
|
460 | 0 | nsRect overflow = nsRect(nsPoint(0,0), mRect.Size()); |
461 | 0 | nsOverflowAreas overflowAreas(overflow, overflow); |
462 | 0 | FinishAndStoreOverflow(overflowAreas, mRect.Size()); |
463 | 0 |
|
464 | 0 | RemoveStateBits(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY | |
465 | 0 | NS_FRAME_HAS_DIRTY_CHILDREN); |
466 | 0 |
|
467 | 0 | // Invalidate, but only if this is not our first reflow (since if it is our |
468 | 0 | // first reflow then we haven't had our first paint yet). |
469 | 0 | if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) { |
470 | 0 | InvalidateFrame(); |
471 | 0 | } |
472 | 0 | } |
473 | | |
474 | | bool |
475 | | nsSVGImageFrame::ReflowFinished() |
476 | 0 | { |
477 | 0 | mReflowCallbackPosted = false; |
478 | 0 |
|
479 | 0 | // XXX(seth): We don't need this. The purpose of updating visibility |
480 | 0 | // synchronously is to ensure that animated images start animating |
481 | 0 | // immediately. In the short term, however, |
482 | 0 | // nsImageLoadingContent::OnUnlockedDraw() is enough to ensure that |
483 | 0 | // animations start as soon as the image is painted for the first time, and in |
484 | 0 | // the long term we want to update visibility information from the display |
485 | 0 | // list whenever we paint, so we don't actually need to do this. However, to |
486 | 0 | // avoid behavior changes during the transition from the old image visibility |
487 | 0 | // code, we'll leave it in for now. |
488 | 0 | UpdateVisibilitySynchronously(); |
489 | 0 |
|
490 | 0 | return false; |
491 | 0 | } |
492 | | |
493 | | void |
494 | | nsSVGImageFrame::ReflowCallbackCanceled() |
495 | 0 | { |
496 | 0 | mReflowCallbackPosted = false; |
497 | 0 | } |
498 | | |
499 | | uint16_t |
500 | | nsSVGImageFrame::GetHitTestFlags() |
501 | 0 | { |
502 | 0 | uint16_t flags = 0; |
503 | 0 |
|
504 | 0 | switch (StyleUI()->mPointerEvents) { |
505 | 0 | case NS_STYLE_POINTER_EVENTS_NONE: |
506 | 0 | break; |
507 | 0 | case NS_STYLE_POINTER_EVENTS_VISIBLEPAINTED: |
508 | 0 | case NS_STYLE_POINTER_EVENTS_AUTO: |
509 | 0 | if (StyleVisibility()->IsVisible()) { |
510 | 0 | /* XXX: should check pixel transparency */ |
511 | 0 | flags |= SVG_HIT_TEST_FILL; |
512 | 0 | } |
513 | 0 | break; |
514 | 0 | case NS_STYLE_POINTER_EVENTS_VISIBLEFILL: |
515 | 0 | case NS_STYLE_POINTER_EVENTS_VISIBLESTROKE: |
516 | 0 | case NS_STYLE_POINTER_EVENTS_VISIBLE: |
517 | 0 | if (StyleVisibility()->IsVisible()) { |
518 | 0 | flags |= SVG_HIT_TEST_FILL; |
519 | 0 | } |
520 | 0 | break; |
521 | 0 | case NS_STYLE_POINTER_EVENTS_PAINTED: |
522 | 0 | /* XXX: should check pixel transparency */ |
523 | 0 | flags |= SVG_HIT_TEST_FILL; |
524 | 0 | break; |
525 | 0 | case NS_STYLE_POINTER_EVENTS_FILL: |
526 | 0 | case NS_STYLE_POINTER_EVENTS_STROKE: |
527 | 0 | case NS_STYLE_POINTER_EVENTS_ALL: |
528 | 0 | flags |= SVG_HIT_TEST_FILL; |
529 | 0 | break; |
530 | 0 | default: |
531 | 0 | NS_ERROR("not reached"); |
532 | 0 | break; |
533 | 0 | } |
534 | 0 |
|
535 | 0 | return flags; |
536 | 0 | } |
537 | | |
538 | | //---------------------------------------------------------------------- |
539 | | // nsSVGImageListener implementation |
540 | | |
541 | | NS_IMPL_ISUPPORTS(nsSVGImageListener, imgINotificationObserver) |
542 | | |
543 | | nsSVGImageListener::nsSVGImageListener(nsSVGImageFrame *aFrame) : mFrame(aFrame) |
544 | 0 | { |
545 | 0 | } |
546 | | |
547 | | NS_IMETHODIMP |
548 | | nsSVGImageListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData) |
549 | 0 | { |
550 | 0 | if (!mFrame) |
551 | 0 | return NS_ERROR_FAILURE; |
552 | 0 | |
553 | 0 | if (aType == imgINotificationObserver::LOAD_COMPLETE) { |
554 | 0 | mFrame->InvalidateFrame(); |
555 | 0 | nsLayoutUtils::PostRestyleEvent( |
556 | 0 | mFrame->GetContent()->AsElement(), nsRestyleHint(0), |
557 | 0 | nsChangeHint_InvalidateRenderingObservers); |
558 | 0 | nsSVGUtils::ScheduleReflowSVG(mFrame); |
559 | 0 | } |
560 | 0 |
|
561 | 0 | if (aType == imgINotificationObserver::FRAME_UPDATE) { |
562 | 0 | // No new dimensions, so we don't need to call |
563 | 0 | // nsSVGUtils::InvalidateAndScheduleBoundsUpdate. |
564 | 0 | nsLayoutUtils::PostRestyleEvent( |
565 | 0 | mFrame->GetContent()->AsElement(), nsRestyleHint(0), |
566 | 0 | nsChangeHint_InvalidateRenderingObservers); |
567 | 0 | mFrame->InvalidateFrame(); |
568 | 0 | } |
569 | 0 |
|
570 | 0 | if (aType == imgINotificationObserver::SIZE_AVAILABLE) { |
571 | 0 | // Called once the resource's dimensions have been obtained. |
572 | 0 | aRequest->GetImage(getter_AddRefs(mFrame->mImageContainer)); |
573 | 0 | mFrame->InvalidateFrame(); |
574 | 0 | nsLayoutUtils::PostRestyleEvent( |
575 | 0 | mFrame->GetContent()->AsElement(), nsRestyleHint(0), |
576 | 0 | nsChangeHint_InvalidateRenderingObservers); |
577 | 0 | nsSVGUtils::ScheduleReflowSVG(mFrame); |
578 | 0 | } |
579 | 0 |
|
580 | 0 | return NS_OK; |
581 | 0 | } |
582 | | |