|
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 // Keep in (case-insensitive) order: |
|
7 #include "gfxContext.h" |
|
8 #include "gfxPlatform.h" |
|
9 #include "mozilla/gfx/2D.h" |
|
10 #include "imgIContainer.h" |
|
11 #include "nsIImageLoadingContent.h" |
|
12 #include "nsLayoutUtils.h" |
|
13 #include "nsRenderingContext.h" |
|
14 #include "imgINotificationObserver.h" |
|
15 #include "nsSVGEffects.h" |
|
16 #include "nsSVGPathGeometryFrame.h" |
|
17 #include "mozilla/dom/SVGSVGElement.h" |
|
18 #include "nsSVGUtils.h" |
|
19 #include "SVGContentUtils.h" |
|
20 #include "SVGImageContext.h" |
|
21 #include "mozilla/dom/SVGImageElement.h" |
|
22 #include "nsContentUtils.h" |
|
23 #include "nsIReflowCallback.h" |
|
24 |
|
25 using namespace mozilla; |
|
26 using namespace mozilla::dom; |
|
27 using namespace mozilla::gfx; |
|
28 |
|
29 class nsSVGImageFrame; |
|
30 |
|
31 class nsSVGImageListener MOZ_FINAL : public imgINotificationObserver |
|
32 { |
|
33 public: |
|
34 nsSVGImageListener(nsSVGImageFrame *aFrame); |
|
35 |
|
36 NS_DECL_ISUPPORTS |
|
37 NS_DECL_IMGINOTIFICATIONOBSERVER |
|
38 |
|
39 void SetFrame(nsSVGImageFrame *frame) { mFrame = frame; } |
|
40 |
|
41 private: |
|
42 nsSVGImageFrame *mFrame; |
|
43 }; |
|
44 |
|
45 typedef nsSVGPathGeometryFrame nsSVGImageFrameBase; |
|
46 |
|
47 class nsSVGImageFrame : public nsSVGImageFrameBase, |
|
48 public nsIReflowCallback |
|
49 { |
|
50 friend nsIFrame* |
|
51 NS_NewSVGImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext); |
|
52 |
|
53 protected: |
|
54 nsSVGImageFrame(nsStyleContext* aContext) : nsSVGImageFrameBase(aContext), |
|
55 mReflowCallbackPosted(false) {} |
|
56 virtual ~nsSVGImageFrame(); |
|
57 |
|
58 public: |
|
59 NS_DECL_FRAMEARENA_HELPERS |
|
60 |
|
61 // nsISVGChildFrame interface: |
|
62 virtual nsresult PaintSVG(nsRenderingContext *aContext, |
|
63 const nsIntRect *aDirtyRect, |
|
64 nsIFrame* aTransformRoot) MOZ_OVERRIDE; |
|
65 virtual nsIFrame* GetFrameForPoint(const nsPoint &aPoint) MOZ_OVERRIDE; |
|
66 virtual void ReflowSVG() MOZ_OVERRIDE; |
|
67 |
|
68 // nsSVGPathGeometryFrame methods: |
|
69 virtual uint16_t GetHitTestFlags() MOZ_OVERRIDE; |
|
70 |
|
71 // nsIFrame interface: |
|
72 virtual nsresult AttributeChanged(int32_t aNameSpaceID, |
|
73 nsIAtom* aAttribute, |
|
74 int32_t aModType) MOZ_OVERRIDE; |
|
75 virtual void Init(nsIContent* aContent, |
|
76 nsIFrame* aParent, |
|
77 nsIFrame* aPrevInFlow) MOZ_OVERRIDE; |
|
78 virtual void DestroyFrom(nsIFrame* aDestructRoot) MOZ_OVERRIDE; |
|
79 |
|
80 /** |
|
81 * Get the "type" of the frame |
|
82 * |
|
83 * @see nsGkAtoms::svgImageFrame |
|
84 */ |
|
85 virtual nsIAtom* GetType() const MOZ_OVERRIDE; |
|
86 |
|
87 #ifdef DEBUG_FRAME_DUMP |
|
88 virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE |
|
89 { |
|
90 return MakeFrameName(NS_LITERAL_STRING("SVGImage"), aResult); |
|
91 } |
|
92 #endif |
|
93 |
|
94 // nsIReflowCallback |
|
95 virtual bool ReflowFinished() MOZ_OVERRIDE; |
|
96 virtual void ReflowCallbackCanceled() MOZ_OVERRIDE; |
|
97 |
|
98 private: |
|
99 gfx::Matrix GetRasterImageTransform(int32_t aNativeWidth, |
|
100 int32_t aNativeHeight, |
|
101 uint32_t aFor, |
|
102 nsIFrame* aTransformRoot = nullptr); |
|
103 gfx::Matrix GetVectorImageTransform(uint32_t aFor, |
|
104 nsIFrame* aTransformRoot = nullptr); |
|
105 bool TransformContextForPainting(gfxContext* aGfxContext, |
|
106 nsIFrame* aTransformRoot); |
|
107 |
|
108 nsCOMPtr<imgINotificationObserver> mListener; |
|
109 |
|
110 nsCOMPtr<imgIContainer> mImageContainer; |
|
111 |
|
112 bool mReflowCallbackPosted; |
|
113 |
|
114 friend class nsSVGImageListener; |
|
115 }; |
|
116 |
|
117 //---------------------------------------------------------------------- |
|
118 // Implementation |
|
119 |
|
120 nsIFrame* |
|
121 NS_NewSVGImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) |
|
122 { |
|
123 return new (aPresShell) nsSVGImageFrame(aContext); |
|
124 } |
|
125 |
|
126 NS_IMPL_FRAMEARENA_HELPERS(nsSVGImageFrame) |
|
127 |
|
128 nsSVGImageFrame::~nsSVGImageFrame() |
|
129 { |
|
130 // set the frame to null so we don't send messages to a dead object. |
|
131 if (mListener) { |
|
132 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent); |
|
133 if (imageLoader) { |
|
134 imageLoader->RemoveObserver(mListener); |
|
135 } |
|
136 reinterpret_cast<nsSVGImageListener*>(mListener.get())->SetFrame(nullptr); |
|
137 } |
|
138 mListener = nullptr; |
|
139 } |
|
140 |
|
141 void |
|
142 nsSVGImageFrame::Init(nsIContent* aContent, |
|
143 nsIFrame* aParent, |
|
144 nsIFrame* aPrevInFlow) |
|
145 { |
|
146 NS_ASSERTION(aContent->IsSVG(nsGkAtoms::image), |
|
147 "Content is not an SVG image!"); |
|
148 |
|
149 nsSVGImageFrameBase::Init(aContent, aParent, aPrevInFlow); |
|
150 |
|
151 mListener = new nsSVGImageListener(this); |
|
152 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent); |
|
153 if (!imageLoader) { |
|
154 NS_RUNTIMEABORT("Why is this not an image loading content?"); |
|
155 } |
|
156 |
|
157 // We should have a PresContext now, so let's notify our image loader that |
|
158 // we need to register any image animations with the refresh driver. |
|
159 imageLoader->FrameCreated(this); |
|
160 |
|
161 imageLoader->AddObserver(mListener); |
|
162 } |
|
163 |
|
164 /* virtual */ void |
|
165 nsSVGImageFrame::DestroyFrom(nsIFrame* aDestructRoot) |
|
166 { |
|
167 if (mReflowCallbackPosted) { |
|
168 PresContext()->PresShell()->CancelReflowCallback(this); |
|
169 mReflowCallbackPosted = false; |
|
170 } |
|
171 |
|
172 nsCOMPtr<nsIImageLoadingContent> imageLoader = |
|
173 do_QueryInterface(nsFrame::mContent); |
|
174 |
|
175 if (imageLoader) { |
|
176 imageLoader->FrameDestroyed(this); |
|
177 } |
|
178 |
|
179 nsFrame::DestroyFrom(aDestructRoot); |
|
180 } |
|
181 |
|
182 //---------------------------------------------------------------------- |
|
183 // nsIFrame methods: |
|
184 |
|
185 nsresult |
|
186 nsSVGImageFrame::AttributeChanged(int32_t aNameSpaceID, |
|
187 nsIAtom* aAttribute, |
|
188 int32_t aModType) |
|
189 { |
|
190 if (aNameSpaceID == kNameSpaceID_None) { |
|
191 if (aAttribute == nsGkAtoms::x || |
|
192 aAttribute == nsGkAtoms::y || |
|
193 aAttribute == nsGkAtoms::width || |
|
194 aAttribute == nsGkAtoms::height) { |
|
195 nsSVGEffects::InvalidateRenderingObservers(this); |
|
196 nsSVGUtils::ScheduleReflowSVG(this); |
|
197 return NS_OK; |
|
198 } |
|
199 else if (aAttribute == nsGkAtoms::preserveAspectRatio) { |
|
200 // We don't paint the content of the image using display lists, therefore |
|
201 // we have to invalidate for this children-only transform changes since |
|
202 // there is no layer tree to notice that the transform changed and |
|
203 // recomposite. |
|
204 InvalidateFrame(); |
|
205 return NS_OK; |
|
206 } |
|
207 } |
|
208 if (aNameSpaceID == kNameSpaceID_XLink && |
|
209 aAttribute == nsGkAtoms::href) { |
|
210 |
|
211 // Prevent setting image.src by exiting early |
|
212 if (nsContentUtils::IsImageSrcSetDisabled()) { |
|
213 return NS_OK; |
|
214 } |
|
215 SVGImageElement *element = static_cast<SVGImageElement*>(mContent); |
|
216 |
|
217 if (element->mStringAttributes[SVGImageElement::HREF].IsExplicitlySet()) { |
|
218 element->LoadSVGImage(true, true); |
|
219 } else { |
|
220 element->CancelImageRequests(true); |
|
221 } |
|
222 } |
|
223 |
|
224 return nsSVGImageFrameBase::AttributeChanged(aNameSpaceID, |
|
225 aAttribute, aModType); |
|
226 } |
|
227 |
|
228 gfx::Matrix |
|
229 nsSVGImageFrame::GetRasterImageTransform(int32_t aNativeWidth, |
|
230 int32_t aNativeHeight, |
|
231 uint32_t aFor, |
|
232 nsIFrame* aTransformRoot) |
|
233 { |
|
234 float x, y, width, height; |
|
235 SVGImageElement *element = static_cast<SVGImageElement*>(mContent); |
|
236 element->GetAnimatedLengthValues(&x, &y, &width, &height, nullptr); |
|
237 |
|
238 Matrix viewBoxTM = |
|
239 SVGContentUtils::GetViewBoxTransform(width, height, |
|
240 0, 0, aNativeWidth, aNativeHeight, |
|
241 element->mPreserveAspectRatio); |
|
242 |
|
243 return viewBoxTM * |
|
244 gfx::Matrix::Translation(x, y) * |
|
245 gfx::ToMatrix(GetCanvasTM(aFor, aTransformRoot)); |
|
246 } |
|
247 |
|
248 gfx::Matrix |
|
249 nsSVGImageFrame::GetVectorImageTransform(uint32_t aFor, |
|
250 nsIFrame* aTransformRoot) |
|
251 { |
|
252 float x, y, width, height; |
|
253 SVGImageElement *element = static_cast<SVGImageElement*>(mContent); |
|
254 element->GetAnimatedLengthValues(&x, &y, &width, &height, nullptr); |
|
255 |
|
256 // No viewBoxTM needed here -- our height/width overrides any concept of |
|
257 // "native size" that the SVG image has, and it will handle viewBox and |
|
258 // preserveAspectRatio on its own once we give it a region to draw into. |
|
259 |
|
260 return gfx::Matrix::Translation(x, y) * |
|
261 gfx::ToMatrix(GetCanvasTM(aFor, aTransformRoot)); |
|
262 } |
|
263 |
|
264 bool |
|
265 nsSVGImageFrame::TransformContextForPainting(gfxContext* aGfxContext, |
|
266 nsIFrame* aTransformRoot) |
|
267 { |
|
268 gfx::Matrix imageTransform; |
|
269 if (mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) { |
|
270 imageTransform = GetVectorImageTransform(FOR_PAINTING, aTransformRoot); |
|
271 } else { |
|
272 int32_t nativeWidth, nativeHeight; |
|
273 if (NS_FAILED(mImageContainer->GetWidth(&nativeWidth)) || |
|
274 NS_FAILED(mImageContainer->GetHeight(&nativeHeight)) || |
|
275 nativeWidth == 0 || nativeHeight == 0) { |
|
276 return false; |
|
277 } |
|
278 imageTransform = |
|
279 GetRasterImageTransform(nativeWidth, nativeHeight, FOR_PAINTING, |
|
280 aTransformRoot); |
|
281 |
|
282 // NOTE: We need to cancel out the effects of Full-Page-Zoom, or else |
|
283 // it'll get applied an extra time by DrawSingleUnscaledImage. |
|
284 nscoord appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel(); |
|
285 gfxFloat pageZoomFactor = |
|
286 nsPresContext::AppUnitsToFloatCSSPixels(appUnitsPerDevPx); |
|
287 imageTransform.Scale(pageZoomFactor, pageZoomFactor); |
|
288 } |
|
289 |
|
290 if (imageTransform.IsSingular()) { |
|
291 return false; |
|
292 } |
|
293 |
|
294 aGfxContext->Multiply(ThebesMatrix(imageTransform)); |
|
295 return true; |
|
296 } |
|
297 |
|
298 //---------------------------------------------------------------------- |
|
299 // nsISVGChildFrame methods: |
|
300 nsresult |
|
301 nsSVGImageFrame::PaintSVG(nsRenderingContext *aContext, |
|
302 const nsIntRect *aDirtyRect, |
|
303 nsIFrame* aTransformRoot) |
|
304 { |
|
305 nsresult rv = NS_OK; |
|
306 |
|
307 if (!StyleVisibility()->IsVisible()) |
|
308 return NS_OK; |
|
309 |
|
310 float x, y, width, height; |
|
311 SVGImageElement *imgElem = static_cast<SVGImageElement*>(mContent); |
|
312 imgElem->GetAnimatedLengthValues(&x, &y, &width, &height, nullptr); |
|
313 NS_ASSERTION(width > 0 && height > 0, |
|
314 "Should only be painting things with valid width/height"); |
|
315 |
|
316 if (!mImageContainer) { |
|
317 nsCOMPtr<imgIRequest> currentRequest; |
|
318 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent); |
|
319 if (imageLoader) |
|
320 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, |
|
321 getter_AddRefs(currentRequest)); |
|
322 |
|
323 if (currentRequest) |
|
324 currentRequest->GetImage(getter_AddRefs(mImageContainer)); |
|
325 } |
|
326 |
|
327 if (mImageContainer) { |
|
328 gfxContext* ctx = aContext->ThebesContext(); |
|
329 gfxContextAutoSaveRestore autoRestorer(ctx); |
|
330 |
|
331 if (StyleDisplay()->IsScrollableOverflow()) { |
|
332 gfxRect clipRect = nsSVGUtils::GetClipRectForFrame(this, x, y, |
|
333 width, height); |
|
334 nsSVGUtils::SetClipRect(ctx, GetCanvasTM(FOR_PAINTING, aTransformRoot), |
|
335 clipRect); |
|
336 } |
|
337 |
|
338 if (!TransformContextForPainting(ctx, aTransformRoot)) { |
|
339 return NS_ERROR_FAILURE; |
|
340 } |
|
341 |
|
342 // fill-opacity doesn't affect <image>, so if we're allowed to |
|
343 // optimize group opacity, the opacity used for compositing the |
|
344 // image into the current canvas is just the group opacity. |
|
345 float opacity = 1.0f; |
|
346 if (nsSVGUtils::CanOptimizeOpacity(this)) { |
|
347 opacity = StyleDisplay()->mOpacity; |
|
348 } |
|
349 |
|
350 if (opacity != 1.0f || StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { |
|
351 ctx->PushGroup(gfxContentType::COLOR_ALPHA); |
|
352 } |
|
353 |
|
354 nscoord appUnitsPerDevPx = PresContext()->AppUnitsPerDevPixel(); |
|
355 nsRect dirtyRect; // only used if aDirtyRect is non-null |
|
356 if (aDirtyRect) { |
|
357 NS_ASSERTION(!NS_SVGDisplayListPaintingEnabled() || |
|
358 (mState & NS_FRAME_IS_NONDISPLAY), |
|
359 "Display lists handle dirty rect intersection test"); |
|
360 dirtyRect = aDirtyRect->ToAppUnits(appUnitsPerDevPx); |
|
361 // Adjust dirtyRect to match our local coordinate system. |
|
362 nsRect rootRect = |
|
363 nsSVGUtils::TransformFrameRectToOuterSVG(mRect, |
|
364 GetCanvasTM(FOR_PAINTING), PresContext()); |
|
365 dirtyRect.MoveBy(-rootRect.TopLeft()); |
|
366 } |
|
367 |
|
368 // XXXbholley - I don't think huge images in SVGs are common enough to |
|
369 // warrant worrying about the responsiveness impact of doing synchronous |
|
370 // decodes. The extra code complexity of determinining when we want to |
|
371 // force sync probably just isn't worth it, so always pass FLAG_SYNC_DECODE |
|
372 uint32_t drawFlags = imgIContainer::FLAG_SYNC_DECODE; |
|
373 |
|
374 if (mImageContainer->GetType() == imgIContainer::TYPE_VECTOR) { |
|
375 // Package up the attributes of this image element which can override the |
|
376 // attributes of mImageContainer's internal SVG document. |
|
377 SVGImageContext context(imgElem->mPreserveAspectRatio.GetAnimValue()); |
|
378 |
|
379 nsRect destRect(0, 0, |
|
380 appUnitsPerDevPx * width, |
|
381 appUnitsPerDevPx * height); |
|
382 |
|
383 // Note: Can't use DrawSingleUnscaledImage for the TYPE_VECTOR case. |
|
384 // That method needs our image to have a fixed native width & height, |
|
385 // and that's not always true for TYPE_VECTOR images. |
|
386 nsLayoutUtils::DrawSingleImage( |
|
387 aContext, |
|
388 mImageContainer, |
|
389 nsLayoutUtils::GetGraphicsFilterForFrame(this), |
|
390 destRect, |
|
391 aDirtyRect ? dirtyRect : destRect, |
|
392 &context, |
|
393 drawFlags); |
|
394 } else { // mImageContainer->GetType() == TYPE_RASTER |
|
395 nsLayoutUtils::DrawSingleUnscaledImage( |
|
396 aContext, |
|
397 mImageContainer, |
|
398 nsLayoutUtils::GetGraphicsFilterForFrame(this), |
|
399 nsPoint(0, 0), |
|
400 aDirtyRect ? &dirtyRect : nullptr, |
|
401 drawFlags); |
|
402 } |
|
403 |
|
404 if (opacity != 1.0f || StyleDisplay()->mMixBlendMode != NS_STYLE_BLEND_NORMAL) { |
|
405 ctx->PopGroupToSource(); |
|
406 ctx->SetOperator(gfxContext::OPERATOR_OVER); |
|
407 ctx->Paint(opacity); |
|
408 } |
|
409 // gfxContextAutoSaveRestore goes out of scope & cleans up our gfxContext |
|
410 } |
|
411 |
|
412 return rv; |
|
413 } |
|
414 |
|
415 nsIFrame* |
|
416 nsSVGImageFrame::GetFrameForPoint(const nsPoint &aPoint) |
|
417 { |
|
418 // Special case for raster images -- we only want to accept points that fall |
|
419 // in the underlying image's (transformed) native bounds. That region |
|
420 // doesn't necessarily map to our <image> element's [x,y,width,height]. So, |
|
421 // we have to look up the native image size & our image transform in order |
|
422 // to filter out points that fall outside that area. |
|
423 if (StyleDisplay()->IsScrollableOverflow() && mImageContainer) { |
|
424 if (mImageContainer->GetType() == imgIContainer::TYPE_RASTER) { |
|
425 int32_t nativeWidth, nativeHeight; |
|
426 if (NS_FAILED(mImageContainer->GetWidth(&nativeWidth)) || |
|
427 NS_FAILED(mImageContainer->GetHeight(&nativeHeight)) || |
|
428 nativeWidth == 0 || nativeHeight == 0) { |
|
429 return nullptr; |
|
430 } |
|
431 |
|
432 if (!nsSVGUtils::HitTestRect( |
|
433 GetRasterImageTransform(nativeWidth, nativeHeight, |
|
434 FOR_HIT_TESTING), |
|
435 0, 0, nativeWidth, nativeHeight, |
|
436 PresContext()->AppUnitsToDevPixels(aPoint.x), |
|
437 PresContext()->AppUnitsToDevPixels(aPoint.y))) { |
|
438 return nullptr; |
|
439 } |
|
440 } |
|
441 // The special case above doesn't apply to vector images, because they |
|
442 // don't limit their drawing to explicit "native bounds" -- they have |
|
443 // an infinite canvas on which to place content. So it's reasonable to |
|
444 // just fall back on our <image> element's own bounds here. |
|
445 } |
|
446 |
|
447 return nsSVGImageFrameBase::GetFrameForPoint(aPoint); |
|
448 } |
|
449 |
|
450 nsIAtom * |
|
451 nsSVGImageFrame::GetType() const |
|
452 { |
|
453 return nsGkAtoms::svgImageFrame; |
|
454 } |
|
455 |
|
456 //---------------------------------------------------------------------- |
|
457 // nsSVGPathGeometryFrame methods: |
|
458 |
|
459 // Lie about our fill/stroke so that covered region and hit detection work properly |
|
460 |
|
461 void |
|
462 nsSVGImageFrame::ReflowSVG() |
|
463 { |
|
464 NS_ASSERTION(nsSVGUtils::OuterSVGIsCallingReflowSVG(this), |
|
465 "This call is probably a wasteful mistake"); |
|
466 |
|
467 NS_ABORT_IF_FALSE(!(GetStateBits() & NS_FRAME_IS_NONDISPLAY), |
|
468 "ReflowSVG mechanism not designed for this"); |
|
469 |
|
470 if (!nsSVGUtils::NeedsReflowSVG(this)) { |
|
471 return; |
|
472 } |
|
473 |
|
474 float x, y, width, height; |
|
475 SVGImageElement *element = static_cast<SVGImageElement*>(mContent); |
|
476 element->GetAnimatedLengthValues(&x, &y, &width, &height, nullptr); |
|
477 |
|
478 Rect extent(x, y, width, height); |
|
479 |
|
480 if (!extent.IsEmpty()) { |
|
481 mRect = nsLayoutUtils::RoundGfxRectToAppRect(extent, |
|
482 PresContext()->AppUnitsPerCSSPixel()); |
|
483 } else { |
|
484 mRect.SetEmpty(); |
|
485 } |
|
486 |
|
487 if (mState & NS_FRAME_FIRST_REFLOW) { |
|
488 // Make sure we have our filter property (if any) before calling |
|
489 // FinishAndStoreOverflow (subsequent filter changes are handled off |
|
490 // nsChangeHint_UpdateEffects): |
|
491 nsSVGEffects::UpdateEffects(this); |
|
492 |
|
493 if (!mReflowCallbackPosted) { |
|
494 nsIPresShell* shell = PresContext()->PresShell(); |
|
495 mReflowCallbackPosted = true; |
|
496 shell->PostReflowCallback(this); |
|
497 } |
|
498 } |
|
499 |
|
500 nsRect overflow = nsRect(nsPoint(0,0), mRect.Size()); |
|
501 nsOverflowAreas overflowAreas(overflow, overflow); |
|
502 FinishAndStoreOverflow(overflowAreas, mRect.Size()); |
|
503 |
|
504 mState &= ~(NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY | |
|
505 NS_FRAME_HAS_DIRTY_CHILDREN); |
|
506 |
|
507 // Invalidate, but only if this is not our first reflow (since if it is our |
|
508 // first reflow then we haven't had our first paint yet). |
|
509 if (!(GetParent()->GetStateBits() & NS_FRAME_FIRST_REFLOW)) { |
|
510 InvalidateFrame(); |
|
511 } |
|
512 } |
|
513 |
|
514 bool |
|
515 nsSVGImageFrame::ReflowFinished() |
|
516 { |
|
517 mReflowCallbackPosted = false; |
|
518 |
|
519 nsLayoutUtils::UpdateImageVisibilityForFrame(this); |
|
520 |
|
521 return false; |
|
522 } |
|
523 |
|
524 void |
|
525 nsSVGImageFrame::ReflowCallbackCanceled() |
|
526 { |
|
527 mReflowCallbackPosted = false; |
|
528 } |
|
529 |
|
530 uint16_t |
|
531 nsSVGImageFrame::GetHitTestFlags() |
|
532 { |
|
533 uint16_t flags = 0; |
|
534 |
|
535 switch(StyleVisibility()->mPointerEvents) { |
|
536 case NS_STYLE_POINTER_EVENTS_NONE: |
|
537 break; |
|
538 case NS_STYLE_POINTER_EVENTS_VISIBLEPAINTED: |
|
539 case NS_STYLE_POINTER_EVENTS_AUTO: |
|
540 if (StyleVisibility()->IsVisible()) { |
|
541 /* XXX: should check pixel transparency */ |
|
542 flags |= SVG_HIT_TEST_FILL; |
|
543 } |
|
544 break; |
|
545 case NS_STYLE_POINTER_EVENTS_VISIBLEFILL: |
|
546 case NS_STYLE_POINTER_EVENTS_VISIBLESTROKE: |
|
547 case NS_STYLE_POINTER_EVENTS_VISIBLE: |
|
548 if (StyleVisibility()->IsVisible()) { |
|
549 flags |= SVG_HIT_TEST_FILL; |
|
550 } |
|
551 break; |
|
552 case NS_STYLE_POINTER_EVENTS_PAINTED: |
|
553 /* XXX: should check pixel transparency */ |
|
554 flags |= SVG_HIT_TEST_FILL; |
|
555 break; |
|
556 case NS_STYLE_POINTER_EVENTS_FILL: |
|
557 case NS_STYLE_POINTER_EVENTS_STROKE: |
|
558 case NS_STYLE_POINTER_EVENTS_ALL: |
|
559 flags |= SVG_HIT_TEST_FILL; |
|
560 break; |
|
561 default: |
|
562 NS_ERROR("not reached"); |
|
563 break; |
|
564 } |
|
565 |
|
566 return flags; |
|
567 } |
|
568 |
|
569 //---------------------------------------------------------------------- |
|
570 // nsSVGImageListener implementation |
|
571 |
|
572 NS_IMPL_ISUPPORTS(nsSVGImageListener, imgINotificationObserver) |
|
573 |
|
574 nsSVGImageListener::nsSVGImageListener(nsSVGImageFrame *aFrame) : mFrame(aFrame) |
|
575 { |
|
576 } |
|
577 |
|
578 NS_IMETHODIMP |
|
579 nsSVGImageListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData) |
|
580 { |
|
581 if (!mFrame) |
|
582 return NS_ERROR_FAILURE; |
|
583 |
|
584 if (aType == imgINotificationObserver::LOAD_COMPLETE) { |
|
585 mFrame->InvalidateFrame(); |
|
586 nsSVGEffects::InvalidateRenderingObservers(mFrame); |
|
587 nsSVGUtils::ScheduleReflowSVG(mFrame); |
|
588 } |
|
589 |
|
590 if (aType == imgINotificationObserver::FRAME_UPDATE) { |
|
591 // No new dimensions, so we don't need to call |
|
592 // nsSVGUtils::InvalidateAndScheduleBoundsUpdate. |
|
593 nsSVGEffects::InvalidateRenderingObservers(mFrame); |
|
594 mFrame->InvalidateFrame(); |
|
595 } |
|
596 |
|
597 if (aType == imgINotificationObserver::SIZE_AVAILABLE) { |
|
598 // Called once the resource's dimensions have been obtained. |
|
599 aRequest->GetImage(getter_AddRefs(mFrame->mImageContainer)); |
|
600 mFrame->InvalidateFrame(); |
|
601 nsSVGEffects::InvalidateRenderingObservers(mFrame); |
|
602 nsSVGUtils::ScheduleReflowSVG(mFrame); |
|
603 } |
|
604 |
|
605 return NS_OK; |
|
606 } |
|
607 |