|
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 #include "VectorImage.h" |
|
7 |
|
8 #include "gfx2DGlue.h" |
|
9 #include "gfxContext.h" |
|
10 #include "gfxDrawable.h" |
|
11 #include "gfxPlatform.h" |
|
12 #include "gfxUtils.h" |
|
13 #include "imgDecoderObserver.h" |
|
14 #include "mozilla/AutoRestore.h" |
|
15 #include "mozilla/MemoryReporting.h" |
|
16 #include "mozilla/dom/SVGSVGElement.h" |
|
17 #include "mozilla/gfx/2D.h" |
|
18 #include "mozilla/RefPtr.h" |
|
19 #include "nsIDOMEvent.h" |
|
20 #include "nsIPresShell.h" |
|
21 #include "nsIStreamListener.h" |
|
22 #include "nsMimeTypes.h" |
|
23 #include "nsPresContext.h" |
|
24 #include "nsRect.h" |
|
25 #include "nsStubDocumentObserver.h" |
|
26 #include "nsSVGEffects.h" // for nsSVGRenderingObserver |
|
27 #include "Orientation.h" |
|
28 #include "SVGDocumentWrapper.h" |
|
29 #include "nsIDOMEventListener.h" |
|
30 #include "SurfaceCache.h" |
|
31 |
|
32 // undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK |
|
33 #undef GetCurrentTime |
|
34 |
|
35 namespace mozilla { |
|
36 |
|
37 using namespace dom; |
|
38 using namespace gfx; |
|
39 using namespace layers; |
|
40 |
|
41 namespace image { |
|
42 |
|
43 // Helper-class: SVGRootRenderingObserver |
|
44 class SVGRootRenderingObserver MOZ_FINAL : public nsSVGRenderingObserver { |
|
45 public: |
|
46 SVGRootRenderingObserver(SVGDocumentWrapper* aDocWrapper, |
|
47 VectorImage* aVectorImage) |
|
48 : nsSVGRenderingObserver() |
|
49 , mDocWrapper(aDocWrapper) |
|
50 , mVectorImage(aVectorImage) |
|
51 , mHonoringInvalidations(true) |
|
52 { |
|
53 MOZ_ASSERT(mDocWrapper, "Need a non-null SVG document wrapper"); |
|
54 MOZ_ASSERT(mVectorImage, "Need a non-null VectorImage"); |
|
55 |
|
56 StartListening(); |
|
57 Element* elem = GetTarget(); |
|
58 MOZ_ASSERT(elem, "no root SVG node for us to observe"); |
|
59 |
|
60 nsSVGEffects::AddRenderingObserver(elem, this); |
|
61 mInObserverList = true; |
|
62 } |
|
63 |
|
64 virtual ~SVGRootRenderingObserver() |
|
65 { |
|
66 StopListening(); |
|
67 } |
|
68 |
|
69 void ResumeHonoringInvalidations() |
|
70 { |
|
71 mHonoringInvalidations = true; |
|
72 } |
|
73 |
|
74 protected: |
|
75 virtual Element* GetTarget() MOZ_OVERRIDE |
|
76 { |
|
77 return mDocWrapper->GetRootSVGElem(); |
|
78 } |
|
79 |
|
80 virtual void DoUpdate() MOZ_OVERRIDE |
|
81 { |
|
82 Element* elem = GetTarget(); |
|
83 MOZ_ASSERT(elem, "missing root SVG node"); |
|
84 |
|
85 if (mHonoringInvalidations && !mDocWrapper->ShouldIgnoreInvalidation()) { |
|
86 nsIFrame* frame = elem->GetPrimaryFrame(); |
|
87 if (!frame || frame->PresContext()->PresShell()->IsDestroying()) { |
|
88 // We're being destroyed. Bail out. |
|
89 return; |
|
90 } |
|
91 |
|
92 // Ignore further invalidations until we draw. |
|
93 mHonoringInvalidations = false; |
|
94 |
|
95 mVectorImage->InvalidateObserversOnNextRefreshDriverTick(); |
|
96 } |
|
97 |
|
98 // Our caller might've removed us from rendering-observer list. |
|
99 // Add ourselves back! |
|
100 if (!mInObserverList) { |
|
101 nsSVGEffects::AddRenderingObserver(elem, this); |
|
102 mInObserverList = true; |
|
103 } |
|
104 } |
|
105 |
|
106 // Private data |
|
107 const nsRefPtr<SVGDocumentWrapper> mDocWrapper; |
|
108 VectorImage* const mVectorImage; // Raw pointer because it owns me. |
|
109 bool mHonoringInvalidations; |
|
110 }; |
|
111 |
|
112 class SVGParseCompleteListener MOZ_FINAL : public nsStubDocumentObserver { |
|
113 public: |
|
114 NS_DECL_ISUPPORTS |
|
115 |
|
116 SVGParseCompleteListener(nsIDocument* aDocument, |
|
117 VectorImage* aImage) |
|
118 : mDocument(aDocument) |
|
119 , mImage(aImage) |
|
120 { |
|
121 MOZ_ASSERT(mDocument, "Need an SVG document"); |
|
122 MOZ_ASSERT(mImage, "Need an image"); |
|
123 |
|
124 mDocument->AddObserver(this); |
|
125 } |
|
126 |
|
127 ~SVGParseCompleteListener() |
|
128 { |
|
129 if (mDocument) { |
|
130 // The document must have been destroyed before we got our event. |
|
131 // Otherwise this can't happen, since documents hold strong references to |
|
132 // their observers. |
|
133 Cancel(); |
|
134 } |
|
135 } |
|
136 |
|
137 void EndLoad(nsIDocument* aDocument) MOZ_OVERRIDE |
|
138 { |
|
139 MOZ_ASSERT(aDocument == mDocument, "Got EndLoad for wrong document?"); |
|
140 |
|
141 // OnSVGDocumentParsed will release our owner's reference to us, so ensure |
|
142 // we stick around long enough to complete our work. |
|
143 nsRefPtr<SVGParseCompleteListener> kungFuDeathGroup(this); |
|
144 |
|
145 mImage->OnSVGDocumentParsed(); |
|
146 } |
|
147 |
|
148 void Cancel() |
|
149 { |
|
150 MOZ_ASSERT(mDocument, "Duplicate call to Cancel"); |
|
151 if (mDocument) { |
|
152 mDocument->RemoveObserver(this); |
|
153 mDocument = nullptr; |
|
154 } |
|
155 } |
|
156 |
|
157 private: |
|
158 nsCOMPtr<nsIDocument> mDocument; |
|
159 VectorImage* const mImage; // Raw pointer to owner. |
|
160 }; |
|
161 |
|
162 NS_IMPL_ISUPPORTS(SVGParseCompleteListener, nsIDocumentObserver) |
|
163 |
|
164 class SVGLoadEventListener MOZ_FINAL : public nsIDOMEventListener { |
|
165 public: |
|
166 NS_DECL_ISUPPORTS |
|
167 |
|
168 SVGLoadEventListener(nsIDocument* aDocument, |
|
169 VectorImage* aImage) |
|
170 : mDocument(aDocument) |
|
171 , mImage(aImage) |
|
172 { |
|
173 MOZ_ASSERT(mDocument, "Need an SVG document"); |
|
174 MOZ_ASSERT(mImage, "Need an image"); |
|
175 |
|
176 mDocument->AddEventListener(NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"), this, true, false); |
|
177 mDocument->AddEventListener(NS_LITERAL_STRING("SVGAbort"), this, true, false); |
|
178 mDocument->AddEventListener(NS_LITERAL_STRING("SVGError"), this, true, false); |
|
179 } |
|
180 |
|
181 ~SVGLoadEventListener() |
|
182 { |
|
183 if (mDocument) { |
|
184 // The document must have been destroyed before we got our event. |
|
185 // Otherwise this can't happen, since documents hold strong references to |
|
186 // their observers. |
|
187 Cancel(); |
|
188 } |
|
189 } |
|
190 |
|
191 NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) MOZ_OVERRIDE |
|
192 { |
|
193 MOZ_ASSERT(mDocument, "Need an SVG document. Received multiple events?"); |
|
194 |
|
195 // OnSVGDocumentLoaded/OnSVGDocumentError will release our owner's reference |
|
196 // to us, so ensure we stick around long enough to complete our work. |
|
197 nsRefPtr<SVGLoadEventListener> kungFuDeathGroup(this); |
|
198 |
|
199 nsAutoString eventType; |
|
200 aEvent->GetType(eventType); |
|
201 MOZ_ASSERT(eventType.EqualsLiteral("MozSVGAsImageDocumentLoad") || |
|
202 eventType.EqualsLiteral("SVGAbort") || |
|
203 eventType.EqualsLiteral("SVGError"), |
|
204 "Received unexpected event"); |
|
205 |
|
206 if (eventType.EqualsLiteral("MozSVGAsImageDocumentLoad")) { |
|
207 mImage->OnSVGDocumentLoaded(); |
|
208 } else { |
|
209 mImage->OnSVGDocumentError(); |
|
210 } |
|
211 |
|
212 return NS_OK; |
|
213 } |
|
214 |
|
215 void Cancel() |
|
216 { |
|
217 MOZ_ASSERT(mDocument, "Duplicate call to Cancel"); |
|
218 if (mDocument) { |
|
219 mDocument->RemoveEventListener(NS_LITERAL_STRING("MozSVGAsImageDocumentLoad"), this, true); |
|
220 mDocument->RemoveEventListener(NS_LITERAL_STRING("SVGAbort"), this, true); |
|
221 mDocument->RemoveEventListener(NS_LITERAL_STRING("SVGError"), this, true); |
|
222 mDocument = nullptr; |
|
223 } |
|
224 } |
|
225 |
|
226 private: |
|
227 nsCOMPtr<nsIDocument> mDocument; |
|
228 VectorImage* const mImage; // Raw pointer to owner. |
|
229 }; |
|
230 |
|
231 NS_IMPL_ISUPPORTS(SVGLoadEventListener, nsIDOMEventListener) |
|
232 |
|
233 // Helper-class: SVGDrawingCallback |
|
234 class SVGDrawingCallback : public gfxDrawingCallback { |
|
235 public: |
|
236 SVGDrawingCallback(SVGDocumentWrapper* aSVGDocumentWrapper, |
|
237 const nsIntRect& aViewport, |
|
238 const gfxSize& aScale, |
|
239 uint32_t aImageFlags) : |
|
240 mSVGDocumentWrapper(aSVGDocumentWrapper), |
|
241 mViewport(aViewport), |
|
242 mScale(aScale), |
|
243 mImageFlags(aImageFlags) |
|
244 {} |
|
245 virtual bool operator()(gfxContext* aContext, |
|
246 const gfxRect& aFillRect, |
|
247 const GraphicsFilter& aFilter, |
|
248 const gfxMatrix& aTransform); |
|
249 private: |
|
250 nsRefPtr<SVGDocumentWrapper> mSVGDocumentWrapper; |
|
251 const nsIntRect mViewport; |
|
252 const gfxSize mScale; |
|
253 uint32_t mImageFlags; |
|
254 }; |
|
255 |
|
256 // Based loosely on nsSVGIntegrationUtils' PaintFrameCallback::operator() |
|
257 bool |
|
258 SVGDrawingCallback::operator()(gfxContext* aContext, |
|
259 const gfxRect& aFillRect, |
|
260 const GraphicsFilter& aFilter, |
|
261 const gfxMatrix& aTransform) |
|
262 { |
|
263 MOZ_ASSERT(mSVGDocumentWrapper, "need an SVGDocumentWrapper"); |
|
264 |
|
265 // Get (& sanity-check) the helper-doc's presShell |
|
266 nsCOMPtr<nsIPresShell> presShell; |
|
267 if (NS_FAILED(mSVGDocumentWrapper->GetPresShell(getter_AddRefs(presShell)))) { |
|
268 NS_WARNING("Unable to draw -- presShell lookup failed"); |
|
269 return false; |
|
270 } |
|
271 MOZ_ASSERT(presShell, "GetPresShell succeeded but returned null"); |
|
272 |
|
273 gfxContextAutoSaveRestore contextRestorer(aContext); |
|
274 |
|
275 // Clip to aFillRect so that we don't paint outside. |
|
276 aContext->NewPath(); |
|
277 aContext->Rectangle(aFillRect); |
|
278 aContext->Clip(); |
|
279 |
|
280 gfxContextMatrixAutoSaveRestore contextMatrixRestorer(aContext); |
|
281 aContext->Multiply(gfxMatrix(aTransform).Invert()); |
|
282 aContext->Scale(1.0 / mScale.width, 1.0 / mScale.height); |
|
283 |
|
284 nsPresContext* presContext = presShell->GetPresContext(); |
|
285 MOZ_ASSERT(presContext, "pres shell w/out pres context"); |
|
286 |
|
287 nsRect svgRect(presContext->DevPixelsToAppUnits(mViewport.x), |
|
288 presContext->DevPixelsToAppUnits(mViewport.y), |
|
289 presContext->DevPixelsToAppUnits(mViewport.width), |
|
290 presContext->DevPixelsToAppUnits(mViewport.height)); |
|
291 |
|
292 uint32_t renderDocFlags = nsIPresShell::RENDER_IGNORE_VIEWPORT_SCROLLING; |
|
293 if (!(mImageFlags & imgIContainer::FLAG_SYNC_DECODE)) { |
|
294 renderDocFlags |= nsIPresShell::RENDER_ASYNC_DECODE_IMAGES; |
|
295 } |
|
296 |
|
297 presShell->RenderDocument(svgRect, renderDocFlags, |
|
298 NS_RGBA(0, 0, 0, 0), // transparent |
|
299 aContext); |
|
300 |
|
301 return true; |
|
302 } |
|
303 |
|
304 // Implement VectorImage's nsISupports-inherited methods |
|
305 NS_IMPL_ISUPPORTS(VectorImage, |
|
306 imgIContainer, |
|
307 nsIStreamListener, |
|
308 nsIRequestObserver) |
|
309 |
|
310 //------------------------------------------------------------------------------ |
|
311 // Constructor / Destructor |
|
312 |
|
313 VectorImage::VectorImage(imgStatusTracker* aStatusTracker, |
|
314 ImageURL* aURI /* = nullptr */) : |
|
315 ImageResource(aURI), // invoke superclass's constructor |
|
316 mIsInitialized(false), |
|
317 mIsFullyLoaded(false), |
|
318 mIsDrawing(false), |
|
319 mHaveAnimations(false), |
|
320 mHasPendingInvalidation(false) |
|
321 { |
|
322 mStatusTrackerInit = new imgStatusTrackerInit(this, aStatusTracker); |
|
323 } |
|
324 |
|
325 VectorImage::~VectorImage() |
|
326 { |
|
327 CancelAllListeners(); |
|
328 SurfaceCache::Discard(this); |
|
329 } |
|
330 |
|
331 //------------------------------------------------------------------------------ |
|
332 // Methods inherited from Image.h |
|
333 |
|
334 nsresult |
|
335 VectorImage::Init(const char* aMimeType, |
|
336 uint32_t aFlags) |
|
337 { |
|
338 // We don't support re-initialization |
|
339 if (mIsInitialized) |
|
340 return NS_ERROR_ILLEGAL_VALUE; |
|
341 |
|
342 MOZ_ASSERT(!mIsFullyLoaded && !mHaveAnimations && !mError, |
|
343 "Flags unexpectedly set before initialization"); |
|
344 MOZ_ASSERT(!strcmp(aMimeType, IMAGE_SVG_XML), "Unexpected mimetype"); |
|
345 |
|
346 mIsInitialized = true; |
|
347 return NS_OK; |
|
348 } |
|
349 |
|
350 nsIntRect |
|
351 VectorImage::FrameRect(uint32_t aWhichFrame) |
|
352 { |
|
353 return nsIntRect::GetMaxSizedIntRect(); |
|
354 } |
|
355 |
|
356 size_t |
|
357 VectorImage::HeapSizeOfSourceWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const |
|
358 { |
|
359 // We're not storing the source data -- we just feed that directly to |
|
360 // our helper SVG document as we receive it, for it to parse. |
|
361 // So 0 is an appropriate return value here. |
|
362 return 0; |
|
363 } |
|
364 |
|
365 size_t |
|
366 VectorImage::HeapSizeOfDecodedWithComputedFallback(mozilla::MallocSizeOf aMallocSizeOf) const |
|
367 { |
|
368 // XXXdholbert TODO: return num bytes used by helper SVG doc. (bug 590790) |
|
369 return 0; |
|
370 } |
|
371 |
|
372 size_t |
|
373 VectorImage::NonHeapSizeOfDecoded() const |
|
374 { |
|
375 return 0; |
|
376 } |
|
377 |
|
378 size_t |
|
379 VectorImage::OutOfProcessSizeOfDecoded() const |
|
380 { |
|
381 return 0; |
|
382 } |
|
383 |
|
384 nsresult |
|
385 VectorImage::OnImageDataComplete(nsIRequest* aRequest, |
|
386 nsISupports* aContext, |
|
387 nsresult aStatus, |
|
388 bool aLastPart) |
|
389 { |
|
390 // Call our internal OnStopRequest method, which only talks to our embedded |
|
391 // SVG document. This won't have any effect on our imgStatusTracker. |
|
392 nsresult finalStatus = OnStopRequest(aRequest, aContext, aStatus); |
|
393 |
|
394 // Give precedence to Necko failure codes. |
|
395 if (NS_FAILED(aStatus)) |
|
396 finalStatus = aStatus; |
|
397 |
|
398 // Actually fire OnStopRequest. |
|
399 if (mStatusTracker) { |
|
400 // XXX(seth): Is this seriously the least insane way to do this? |
|
401 nsRefPtr<imgStatusTracker> clone = mStatusTracker->CloneForRecording(); |
|
402 imgDecoderObserver* observer = clone->GetDecoderObserver(); |
|
403 observer->OnStopRequest(aLastPart, finalStatus); |
|
404 ImageStatusDiff diff = mStatusTracker->Difference(clone); |
|
405 mStatusTracker->ApplyDifference(diff); |
|
406 mStatusTracker->SyncNotifyDifference(diff); |
|
407 } |
|
408 return finalStatus; |
|
409 } |
|
410 |
|
411 nsresult |
|
412 VectorImage::OnImageDataAvailable(nsIRequest* aRequest, |
|
413 nsISupports* aContext, |
|
414 nsIInputStream* aInStr, |
|
415 uint64_t aSourceOffset, |
|
416 uint32_t aCount) |
|
417 { |
|
418 return OnDataAvailable(aRequest, aContext, aInStr, aSourceOffset, aCount); |
|
419 } |
|
420 |
|
421 nsresult |
|
422 VectorImage::OnNewSourceData() |
|
423 { |
|
424 return NS_OK; |
|
425 } |
|
426 |
|
427 nsresult |
|
428 VectorImage::StartAnimation() |
|
429 { |
|
430 if (mError) |
|
431 return NS_ERROR_FAILURE; |
|
432 |
|
433 MOZ_ASSERT(ShouldAnimate(), "Should not animate!"); |
|
434 |
|
435 mSVGDocumentWrapper->StartAnimation(); |
|
436 return NS_OK; |
|
437 } |
|
438 |
|
439 nsresult |
|
440 VectorImage::StopAnimation() |
|
441 { |
|
442 nsresult rv = NS_OK; |
|
443 if (mError) { |
|
444 rv = NS_ERROR_FAILURE; |
|
445 } else { |
|
446 MOZ_ASSERT(mIsFullyLoaded && mHaveAnimations, |
|
447 "Should not have been animating!"); |
|
448 |
|
449 mSVGDocumentWrapper->StopAnimation(); |
|
450 } |
|
451 |
|
452 mAnimating = false; |
|
453 return rv; |
|
454 } |
|
455 |
|
456 bool |
|
457 VectorImage::ShouldAnimate() |
|
458 { |
|
459 return ImageResource::ShouldAnimate() && mIsFullyLoaded && mHaveAnimations; |
|
460 } |
|
461 |
|
462 NS_IMETHODIMP_(void) |
|
463 VectorImage::SetAnimationStartTime(const mozilla::TimeStamp& aTime) |
|
464 { |
|
465 // We don't care about animation start time. |
|
466 } |
|
467 |
|
468 //------------------------------------------------------------------------------ |
|
469 // imgIContainer methods |
|
470 |
|
471 //****************************************************************************** |
|
472 /* readonly attribute int32_t width; */ |
|
473 NS_IMETHODIMP |
|
474 VectorImage::GetWidth(int32_t* aWidth) |
|
475 { |
|
476 if (mError || !mIsFullyLoaded) { |
|
477 *aWidth = 0; |
|
478 return NS_ERROR_FAILURE; |
|
479 } |
|
480 |
|
481 if (!mSVGDocumentWrapper->GetWidthOrHeight(SVGDocumentWrapper::eWidth, |
|
482 *aWidth)) { |
|
483 *aWidth = 0; |
|
484 return NS_ERROR_FAILURE; |
|
485 } |
|
486 |
|
487 return NS_OK; |
|
488 } |
|
489 |
|
490 //****************************************************************************** |
|
491 /* [notxpcom] void requestRefresh ([const] in TimeStamp aTime); */ |
|
492 NS_IMETHODIMP_(void) |
|
493 VectorImage::RequestRefresh(const mozilla::TimeStamp& aTime) |
|
494 { |
|
495 // TODO: Implement for b666446. |
|
496 EvaluateAnimation(); |
|
497 |
|
498 if (mHasPendingInvalidation) { |
|
499 SendInvalidationNotifications(); |
|
500 mHasPendingInvalidation = false; |
|
501 } |
|
502 } |
|
503 |
|
504 void |
|
505 VectorImage::SendInvalidationNotifications() |
|
506 { |
|
507 // Animated images don't send out invalidation notifications as soon as |
|
508 // they're generated. Instead, InvalidateObserversOnNextRefreshDriverTick |
|
509 // records that there are pending invalidations and then returns immediately. |
|
510 // The notifications are actually sent from RequestRefresh(). We send these |
|
511 // notifications there to ensure that there is actually a document observing |
|
512 // us. Otherwise, the notifications are just wasted effort. |
|
513 // |
|
514 // Non-animated images call this method directly from |
|
515 // InvalidateObserversOnNextRefreshDriverTick, because RequestRefresh is never |
|
516 // called for them. Ordinarily this isn't needed, since we send out |
|
517 // invalidation notifications in OnSVGDocumentLoaded, but in rare cases the |
|
518 // SVG document may not be 100% ready to render at that time. In those cases |
|
519 // we would miss the subsequent invalidations if we didn't send out the |
|
520 // notifications directly in |InvalidateObservers...|. |
|
521 |
|
522 if (mStatusTracker) { |
|
523 SurfaceCache::Discard(this); |
|
524 mStatusTracker->FrameChanged(&nsIntRect::GetMaxSizedIntRect()); |
|
525 mStatusTracker->OnStopFrame(); |
|
526 } |
|
527 } |
|
528 |
|
529 //****************************************************************************** |
|
530 /* readonly attribute int32_t height; */ |
|
531 NS_IMETHODIMP |
|
532 VectorImage::GetHeight(int32_t* aHeight) |
|
533 { |
|
534 if (mError || !mIsFullyLoaded) { |
|
535 *aHeight = 0; |
|
536 return NS_ERROR_FAILURE; |
|
537 } |
|
538 |
|
539 if (!mSVGDocumentWrapper->GetWidthOrHeight(SVGDocumentWrapper::eHeight, |
|
540 *aHeight)) { |
|
541 *aHeight = 0; |
|
542 return NS_ERROR_FAILURE; |
|
543 } |
|
544 |
|
545 return NS_OK; |
|
546 } |
|
547 |
|
548 //****************************************************************************** |
|
549 /* [noscript] readonly attribute nsSize intrinsicSize; */ |
|
550 NS_IMETHODIMP |
|
551 VectorImage::GetIntrinsicSize(nsSize* aSize) |
|
552 { |
|
553 if (mError || !mIsFullyLoaded) |
|
554 return NS_ERROR_FAILURE; |
|
555 |
|
556 nsIFrame* rootFrame = mSVGDocumentWrapper->GetRootLayoutFrame(); |
|
557 if (!rootFrame) |
|
558 return NS_ERROR_FAILURE; |
|
559 |
|
560 *aSize = nsSize(-1, -1); |
|
561 IntrinsicSize rfSize = rootFrame->GetIntrinsicSize(); |
|
562 if (rfSize.width.GetUnit() == eStyleUnit_Coord) |
|
563 aSize->width = rfSize.width.GetCoordValue(); |
|
564 if (rfSize.height.GetUnit() == eStyleUnit_Coord) |
|
565 aSize->height = rfSize.height.GetCoordValue(); |
|
566 |
|
567 return NS_OK; |
|
568 } |
|
569 |
|
570 //****************************************************************************** |
|
571 /* [noscript] readonly attribute nsSize intrinsicRatio; */ |
|
572 NS_IMETHODIMP |
|
573 VectorImage::GetIntrinsicRatio(nsSize* aRatio) |
|
574 { |
|
575 if (mError || !mIsFullyLoaded) |
|
576 return NS_ERROR_FAILURE; |
|
577 |
|
578 nsIFrame* rootFrame = mSVGDocumentWrapper->GetRootLayoutFrame(); |
|
579 if (!rootFrame) |
|
580 return NS_ERROR_FAILURE; |
|
581 |
|
582 *aRatio = rootFrame->GetIntrinsicRatio(); |
|
583 return NS_OK; |
|
584 } |
|
585 |
|
586 NS_IMETHODIMP_(Orientation) |
|
587 VectorImage::GetOrientation() |
|
588 { |
|
589 return Orientation(); |
|
590 } |
|
591 |
|
592 //****************************************************************************** |
|
593 /* readonly attribute unsigned short type; */ |
|
594 NS_IMETHODIMP |
|
595 VectorImage::GetType(uint16_t* aType) |
|
596 { |
|
597 NS_ENSURE_ARG_POINTER(aType); |
|
598 |
|
599 *aType = GetType(); |
|
600 return NS_OK; |
|
601 } |
|
602 |
|
603 //****************************************************************************** |
|
604 /* [noscript, notxpcom] uint16_t GetType(); */ |
|
605 NS_IMETHODIMP_(uint16_t) |
|
606 VectorImage::GetType() |
|
607 { |
|
608 return imgIContainer::TYPE_VECTOR; |
|
609 } |
|
610 |
|
611 //****************************************************************************** |
|
612 /* readonly attribute boolean animated; */ |
|
613 NS_IMETHODIMP |
|
614 VectorImage::GetAnimated(bool* aAnimated) |
|
615 { |
|
616 if (mError || !mIsFullyLoaded) |
|
617 return NS_ERROR_FAILURE; |
|
618 |
|
619 *aAnimated = mSVGDocumentWrapper->IsAnimated(); |
|
620 return NS_OK; |
|
621 } |
|
622 |
|
623 //****************************************************************************** |
|
624 /* [notxpcom] int32_t getFirstFrameDelay (); */ |
|
625 int32_t |
|
626 VectorImage::GetFirstFrameDelay() |
|
627 { |
|
628 if (mError) |
|
629 return -1; |
|
630 |
|
631 if (!mSVGDocumentWrapper->IsAnimated()) |
|
632 return -1; |
|
633 |
|
634 // We don't really have a frame delay, so just pretend that we constantly |
|
635 // need updates. |
|
636 return 0; |
|
637 } |
|
638 |
|
639 |
|
640 //****************************************************************************** |
|
641 /* [notxpcom] boolean frameIsOpaque(in uint32_t aWhichFrame); */ |
|
642 NS_IMETHODIMP_(bool) |
|
643 VectorImage::FrameIsOpaque(uint32_t aWhichFrame) |
|
644 { |
|
645 if (aWhichFrame > FRAME_MAX_VALUE) |
|
646 NS_WARNING("aWhichFrame outside valid range!"); |
|
647 |
|
648 return false; // In general, SVG content is not opaque. |
|
649 } |
|
650 |
|
651 //****************************************************************************** |
|
652 /* [noscript] SourceSurface getFrame(in uint32_t aWhichFrame, |
|
653 * in uint32_t aFlags; */ |
|
654 NS_IMETHODIMP_(TemporaryRef<SourceSurface>) |
|
655 VectorImage::GetFrame(uint32_t aWhichFrame, |
|
656 uint32_t aFlags) |
|
657 { |
|
658 MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE); |
|
659 |
|
660 if (aWhichFrame > FRAME_MAX_VALUE) |
|
661 return nullptr; |
|
662 |
|
663 if (mError) |
|
664 return nullptr; |
|
665 |
|
666 // Look up height & width |
|
667 // ---------------------- |
|
668 nsIntSize imageIntSize; |
|
669 if (!mSVGDocumentWrapper->GetWidthOrHeight(SVGDocumentWrapper::eWidth, |
|
670 imageIntSize.width) || |
|
671 !mSVGDocumentWrapper->GetWidthOrHeight(SVGDocumentWrapper::eHeight, |
|
672 imageIntSize.height)) { |
|
673 // We'll get here if our SVG doc has a percent-valued width or height. |
|
674 return nullptr; |
|
675 } |
|
676 |
|
677 // Make our surface the size of what will ultimately be drawn to it. |
|
678 // (either the full image size, or the restricted region) |
|
679 RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()-> |
|
680 CreateOffscreenContentDrawTarget(IntSize(imageIntSize.width, |
|
681 imageIntSize.height), |
|
682 SurfaceFormat::B8G8R8A8); |
|
683 nsRefPtr<gfxContext> context = new gfxContext(dt); |
|
684 |
|
685 nsresult rv = Draw(context, GraphicsFilter::FILTER_NEAREST, gfxMatrix(), |
|
686 gfxRect(gfxPoint(0,0), gfxIntSize(imageIntSize.width, |
|
687 imageIntSize.height)), |
|
688 nsIntRect(nsIntPoint(0,0), imageIntSize), |
|
689 imageIntSize, nullptr, aWhichFrame, aFlags); |
|
690 |
|
691 NS_ENSURE_SUCCESS(rv, nullptr); |
|
692 return dt->Snapshot(); |
|
693 } |
|
694 |
|
695 //****************************************************************************** |
|
696 /* [noscript] ImageContainer getImageContainer(); */ |
|
697 NS_IMETHODIMP |
|
698 VectorImage::GetImageContainer(LayerManager* aManager, |
|
699 mozilla::layers::ImageContainer** _retval) |
|
700 { |
|
701 *_retval = nullptr; |
|
702 return NS_OK; |
|
703 } |
|
704 |
|
705 struct SVGDrawingParameters |
|
706 { |
|
707 SVGDrawingParameters(gfxContext* aContext, |
|
708 GraphicsFilter aFilter, |
|
709 const gfxMatrix& aUserSpaceToImageSpace, |
|
710 const gfxRect& aFill, |
|
711 const nsIntRect& aSubimage, |
|
712 const nsIntSize& aViewportSize, |
|
713 const SVGImageContext* aSVGContext, |
|
714 float aAnimationTime, |
|
715 uint32_t aFlags) |
|
716 : context(aContext) |
|
717 , filter(aFilter) |
|
718 , fill(aFill) |
|
719 , viewportSize(aViewportSize) |
|
720 , animationTime(aAnimationTime) |
|
721 , svgContext(aSVGContext) |
|
722 , flags(aFlags) |
|
723 { |
|
724 // gfxUtils::DrawPixelSnapped may rasterize this image to a temporary surface |
|
725 // if we hit the tiling path. Unfortunately, the temporary surface isn't |
|
726 // created at the size at which we'll ultimately draw, causing fuzzy output. |
|
727 // To fix this we pre-apply the transform's scaling to the drawing parameters |
|
728 // and remove the scaling from the transform, so the fact that temporary |
|
729 // surfaces won't take the scaling into account doesn't matter. (Bug 600207.) |
|
730 scale = aUserSpaceToImageSpace.ScaleFactors(true); |
|
731 gfxPoint translation(aUserSpaceToImageSpace.GetTranslation()); |
|
732 |
|
733 // Remove the scaling from the transform. |
|
734 gfxMatrix unscale; |
|
735 unscale.Translate(gfxPoint(translation.x / scale.width, |
|
736 translation.y / scale.height)); |
|
737 unscale.Scale(1.0 / scale.width, 1.0 / scale.height); |
|
738 unscale.Translate(-translation); |
|
739 userSpaceToImageSpace = aUserSpaceToImageSpace * unscale; |
|
740 |
|
741 // Rescale drawing parameters. |
|
742 IntSize drawableSize(aViewportSize.width / scale.width, |
|
743 aViewportSize.height / scale.height); |
|
744 sourceRect = userSpaceToImageSpace.Transform(aFill); |
|
745 imageRect = IntRect(IntPoint(0, 0), drawableSize); |
|
746 subimage = gfxRect(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height); |
|
747 subimage.ScaleRoundOut(1.0 / scale.width, 1.0 / scale.height); |
|
748 } |
|
749 |
|
750 gfxContext* context; |
|
751 GraphicsFilter filter; |
|
752 gfxMatrix userSpaceToImageSpace; |
|
753 gfxRect fill; |
|
754 gfxRect subimage; |
|
755 gfxRect sourceRect; |
|
756 IntRect imageRect; |
|
757 nsIntSize viewportSize; |
|
758 gfxSize scale; |
|
759 float animationTime; |
|
760 const SVGImageContext* svgContext; |
|
761 uint32_t flags; |
|
762 }; |
|
763 |
|
764 //****************************************************************************** |
|
765 /* [noscript] void draw(in gfxContext aContext, |
|
766 * in gfxGraphicsFilter aFilter, |
|
767 * [const] in gfxMatrix aUserSpaceToImageSpace, |
|
768 * [const] in gfxRect aFill, |
|
769 * [const] in nsIntRect aSubimage, |
|
770 * [const] in nsIntSize aViewportSize, |
|
771 * [const] in SVGImageContext aSVGContext, |
|
772 * in uint32_t aWhichFrame, |
|
773 * in uint32_t aFlags); */ |
|
774 NS_IMETHODIMP |
|
775 VectorImage::Draw(gfxContext* aContext, |
|
776 GraphicsFilter aFilter, |
|
777 const gfxMatrix& aUserSpaceToImageSpace, |
|
778 const gfxRect& aFill, |
|
779 const nsIntRect& aSubimage, |
|
780 const nsIntSize& aViewportSize, |
|
781 const SVGImageContext* aSVGContext, |
|
782 uint32_t aWhichFrame, |
|
783 uint32_t aFlags) |
|
784 { |
|
785 if (aWhichFrame > FRAME_MAX_VALUE) |
|
786 return NS_ERROR_INVALID_ARG; |
|
787 |
|
788 NS_ENSURE_ARG_POINTER(aContext); |
|
789 if (mError || !mIsFullyLoaded) |
|
790 return NS_ERROR_FAILURE; |
|
791 |
|
792 if (mIsDrawing) { |
|
793 NS_WARNING("Refusing to make re-entrant call to VectorImage::Draw"); |
|
794 return NS_ERROR_FAILURE; |
|
795 } |
|
796 |
|
797 if (mAnimationConsumers == 0 && mStatusTracker) { |
|
798 mStatusTracker->OnUnlockedDraw(); |
|
799 } |
|
800 |
|
801 AutoRestore<bool> autoRestoreIsDrawing(mIsDrawing); |
|
802 mIsDrawing = true; |
|
803 |
|
804 float animTime = (aWhichFrame == FRAME_FIRST) ? 0.0f |
|
805 : mSVGDocumentWrapper->GetCurrentTime(); |
|
806 AutoSVGRenderingState autoSVGState(aSVGContext, animTime, |
|
807 mSVGDocumentWrapper->GetRootSVGElem()); |
|
808 |
|
809 // Pack up the drawing parameters. |
|
810 SVGDrawingParameters params(aContext, aFilter, aUserSpaceToImageSpace, aFill, |
|
811 aSubimage, aViewportSize, aSVGContext, animTime, aFlags); |
|
812 |
|
813 // Check the cache. |
|
814 nsRefPtr<gfxDrawable> drawable = |
|
815 SurfaceCache::Lookup(ImageKey(this), |
|
816 SurfaceKey(params.imageRect.Size(), params.scale, |
|
817 aSVGContext, animTime, aFlags)); |
|
818 |
|
819 // Draw. |
|
820 if (drawable) { |
|
821 Show(drawable, params); |
|
822 } else { |
|
823 CreateDrawableAndShow(params); |
|
824 } |
|
825 |
|
826 return NS_OK; |
|
827 } |
|
828 |
|
829 void |
|
830 VectorImage::CreateDrawableAndShow(const SVGDrawingParameters& aParams) |
|
831 { |
|
832 mSVGDocumentWrapper->UpdateViewportBounds(aParams.viewportSize); |
|
833 mSVGDocumentWrapper->FlushImageTransformInvalidation(); |
|
834 |
|
835 nsRefPtr<gfxDrawingCallback> cb = |
|
836 new SVGDrawingCallback(mSVGDocumentWrapper, |
|
837 nsIntRect(nsIntPoint(0, 0), aParams.viewportSize), |
|
838 aParams.scale, |
|
839 aParams.flags); |
|
840 |
|
841 nsRefPtr<gfxDrawable> svgDrawable = |
|
842 new gfxCallbackDrawable(cb, ThebesIntSize(aParams.imageRect.Size())); |
|
843 |
|
844 // Refuse to cache animated images. |
|
845 // XXX(seth): We may remove this restriction in bug 922893. |
|
846 if (mHaveAnimations) |
|
847 return Show(svgDrawable, aParams); |
|
848 |
|
849 // If the image is too big to fit in the cache, don't go any further. |
|
850 if (!SurfaceCache::CanHold(aParams.imageRect.Size())) |
|
851 return Show(svgDrawable, aParams); |
|
852 |
|
853 // Try to create an offscreen surface. |
|
854 mozilla::RefPtr<mozilla::gfx::DrawTarget> target = |
|
855 gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(aParams.imageRect.Size(), gfx::SurfaceFormat::B8G8R8A8); |
|
856 |
|
857 // If we couldn't create the draw target, it was probably because it would end |
|
858 // up way too big. Generally it also wouldn't fit in the cache, but the prefs |
|
859 // could be set such that the cache isn't the limiting factor. |
|
860 if (!target) |
|
861 return Show(svgDrawable, aParams); |
|
862 |
|
863 nsRefPtr<gfxContext> ctx = new gfxContext(target); |
|
864 |
|
865 // Actually draw. (We use FILTER_NEAREST since we never scale here.) |
|
866 gfxUtils::DrawPixelSnapped(ctx, svgDrawable, gfxMatrix(), |
|
867 ThebesIntRect(aParams.imageRect), |
|
868 ThebesIntRect(aParams.imageRect), |
|
869 ThebesIntRect(aParams.imageRect), |
|
870 ThebesIntRect(aParams.imageRect), |
|
871 gfxImageFormat::ARGB32, |
|
872 GraphicsFilter::FILTER_NEAREST, aParams.flags); |
|
873 |
|
874 // Attempt to cache the resulting surface. |
|
875 SurfaceCache::Insert(target, |
|
876 ImageKey(this), |
|
877 SurfaceKey(aParams.imageRect.Size(), aParams.scale, |
|
878 aParams.svgContext, aParams.animationTime, |
|
879 aParams.flags)); |
|
880 |
|
881 // Draw. Note that if SurfaceCache::Insert failed for whatever reason, |
|
882 // then |target| is all that is keeping the pixel data alive, so we have |
|
883 // to draw before returning from this function. |
|
884 nsRefPtr<gfxDrawable> drawable = |
|
885 new gfxSurfaceDrawable(target, ThebesIntSize(aParams.imageRect.Size())); |
|
886 Show(drawable, aParams); |
|
887 } |
|
888 |
|
889 |
|
890 void |
|
891 VectorImage::Show(gfxDrawable* aDrawable, const SVGDrawingParameters& aParams) |
|
892 { |
|
893 MOZ_ASSERT(aDrawable, "Should have a gfxDrawable by now"); |
|
894 gfxUtils::DrawPixelSnapped(aParams.context, aDrawable, |
|
895 aParams.userSpaceToImageSpace, |
|
896 aParams.subimage, aParams.sourceRect, |
|
897 ThebesIntRect(aParams.imageRect), aParams.fill, |
|
898 gfxImageFormat::ARGB32, |
|
899 aParams.filter, aParams.flags); |
|
900 |
|
901 MOZ_ASSERT(mRenderingObserver, "Should have a rendering observer by now"); |
|
902 mRenderingObserver->ResumeHonoringInvalidations(); |
|
903 } |
|
904 |
|
905 //****************************************************************************** |
|
906 /* void requestDecode() */ |
|
907 NS_IMETHODIMP |
|
908 VectorImage::RequestDecode() |
|
909 { |
|
910 // Nothing to do for SVG images |
|
911 return NS_OK; |
|
912 } |
|
913 |
|
914 NS_IMETHODIMP |
|
915 VectorImage::StartDecoding() |
|
916 { |
|
917 // Nothing to do for SVG images |
|
918 return NS_OK; |
|
919 } |
|
920 |
|
921 bool |
|
922 VectorImage::IsDecoded() |
|
923 { |
|
924 return mIsFullyLoaded || mError; |
|
925 } |
|
926 |
|
927 //****************************************************************************** |
|
928 /* void lockImage() */ |
|
929 NS_IMETHODIMP |
|
930 VectorImage::LockImage() |
|
931 { |
|
932 // This method is for image-discarding, which only applies to RasterImages. |
|
933 return NS_OK; |
|
934 } |
|
935 |
|
936 //****************************************************************************** |
|
937 /* void unlockImage() */ |
|
938 NS_IMETHODIMP |
|
939 VectorImage::UnlockImage() |
|
940 { |
|
941 // This method is for image-discarding, which only applies to RasterImages. |
|
942 return NS_OK; |
|
943 } |
|
944 |
|
945 //****************************************************************************** |
|
946 /* void requestDiscard() */ |
|
947 NS_IMETHODIMP |
|
948 VectorImage::RequestDiscard() |
|
949 { |
|
950 SurfaceCache::Discard(this); |
|
951 return NS_OK; |
|
952 } |
|
953 |
|
954 //****************************************************************************** |
|
955 /* void resetAnimation (); */ |
|
956 NS_IMETHODIMP |
|
957 VectorImage::ResetAnimation() |
|
958 { |
|
959 if (mError) |
|
960 return NS_ERROR_FAILURE; |
|
961 |
|
962 if (!mIsFullyLoaded || !mHaveAnimations) { |
|
963 return NS_OK; // There are no animations to be reset. |
|
964 } |
|
965 |
|
966 mSVGDocumentWrapper->ResetAnimation(); |
|
967 |
|
968 return NS_OK; |
|
969 } |
|
970 |
|
971 NS_IMETHODIMP_(float) |
|
972 VectorImage::GetFrameIndex(uint32_t aWhichFrame) |
|
973 { |
|
974 MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE, "Invalid argument"); |
|
975 return aWhichFrame == FRAME_FIRST |
|
976 ? 0.0f |
|
977 : mSVGDocumentWrapper->GetCurrentTime(); |
|
978 } |
|
979 |
|
980 //------------------------------------------------------------------------------ |
|
981 // nsIRequestObserver methods |
|
982 |
|
983 //****************************************************************************** |
|
984 /* void onStartRequest(in nsIRequest request, in nsISupports ctxt); */ |
|
985 NS_IMETHODIMP |
|
986 VectorImage::OnStartRequest(nsIRequest* aRequest, nsISupports* aCtxt) |
|
987 { |
|
988 MOZ_ASSERT(!mSVGDocumentWrapper, |
|
989 "Repeated call to OnStartRequest -- can this happen?"); |
|
990 |
|
991 mSVGDocumentWrapper = new SVGDocumentWrapper(); |
|
992 nsresult rv = mSVGDocumentWrapper->OnStartRequest(aRequest, aCtxt); |
|
993 if (NS_FAILED(rv)) { |
|
994 mSVGDocumentWrapper = nullptr; |
|
995 mError = true; |
|
996 return rv; |
|
997 } |
|
998 |
|
999 // Sending StartDecode will block page load until the document's ready. (We |
|
1000 // unblock it by sending StopDecode in OnSVGDocumentLoaded or |
|
1001 // OnSVGDocumentError.) |
|
1002 if (mStatusTracker) { |
|
1003 nsRefPtr<imgStatusTracker> clone = mStatusTracker->CloneForRecording(); |
|
1004 imgDecoderObserver* observer = clone->GetDecoderObserver(); |
|
1005 observer->OnStartDecode(); |
|
1006 ImageStatusDiff diff = mStatusTracker->Difference(clone); |
|
1007 mStatusTracker->ApplyDifference(diff); |
|
1008 mStatusTracker->SyncNotifyDifference(diff); |
|
1009 } |
|
1010 |
|
1011 // Create a listener to wait until the SVG document is fully loaded, which |
|
1012 // will signal that this image is ready to render. Certain error conditions |
|
1013 // will prevent us from ever getting this notification, so we also create a |
|
1014 // listener that waits for parsing to complete and cancels the |
|
1015 // SVGLoadEventListener if needed. The listeners are automatically attached |
|
1016 // to the document by their constructors. |
|
1017 nsIDocument* document = mSVGDocumentWrapper->GetDocument(); |
|
1018 mLoadEventListener = new SVGLoadEventListener(document, this); |
|
1019 mParseCompleteListener = new SVGParseCompleteListener(document, this); |
|
1020 |
|
1021 return NS_OK; |
|
1022 } |
|
1023 |
|
1024 //****************************************************************************** |
|
1025 /* void onStopRequest(in nsIRequest request, in nsISupports ctxt, |
|
1026 in nsresult status); */ |
|
1027 NS_IMETHODIMP |
|
1028 VectorImage::OnStopRequest(nsIRequest* aRequest, nsISupports* aCtxt, |
|
1029 nsresult aStatus) |
|
1030 { |
|
1031 if (mError) |
|
1032 return NS_ERROR_FAILURE; |
|
1033 |
|
1034 return mSVGDocumentWrapper->OnStopRequest(aRequest, aCtxt, aStatus); |
|
1035 } |
|
1036 |
|
1037 void |
|
1038 VectorImage::OnSVGDocumentParsed() |
|
1039 { |
|
1040 MOZ_ASSERT(mParseCompleteListener, "Should have the parse complete listener"); |
|
1041 MOZ_ASSERT(mLoadEventListener, "Should have the load event listener"); |
|
1042 |
|
1043 if (!mSVGDocumentWrapper->GetRootSVGElem()) { |
|
1044 // This is an invalid SVG document. It may have failed to parse, or it may |
|
1045 // be missing the <svg> root element, or the <svg> root element may not |
|
1046 // declare the correct namespace. In any of these cases, we'll never be |
|
1047 // notified that the SVG finished loading, so we need to treat this as an error. |
|
1048 OnSVGDocumentError(); |
|
1049 } |
|
1050 } |
|
1051 |
|
1052 void |
|
1053 VectorImage::CancelAllListeners() |
|
1054 { |
|
1055 if (mParseCompleteListener) { |
|
1056 mParseCompleteListener->Cancel(); |
|
1057 mParseCompleteListener = nullptr; |
|
1058 } |
|
1059 if (mLoadEventListener) { |
|
1060 mLoadEventListener->Cancel(); |
|
1061 mLoadEventListener = nullptr; |
|
1062 } |
|
1063 } |
|
1064 |
|
1065 void |
|
1066 VectorImage::OnSVGDocumentLoaded() |
|
1067 { |
|
1068 MOZ_ASSERT(mSVGDocumentWrapper->GetRootSVGElem(), |
|
1069 "Should have parsed successfully"); |
|
1070 MOZ_ASSERT(!mIsFullyLoaded && !mHaveAnimations, |
|
1071 "These flags shouldn't get set until OnSVGDocumentLoaded. " |
|
1072 "Duplicate calls to OnSVGDocumentLoaded?"); |
|
1073 |
|
1074 CancelAllListeners(); |
|
1075 |
|
1076 // XXX Flushing is wasteful if embedding frame hasn't had initial reflow. |
|
1077 mSVGDocumentWrapper->FlushLayout(); |
|
1078 |
|
1079 mIsFullyLoaded = true; |
|
1080 mHaveAnimations = mSVGDocumentWrapper->IsAnimated(); |
|
1081 |
|
1082 // Start listening to our image for rendering updates. |
|
1083 mRenderingObserver = new SVGRootRenderingObserver(mSVGDocumentWrapper, this); |
|
1084 |
|
1085 // Tell *our* observers that we're done loading. |
|
1086 if (mStatusTracker) { |
|
1087 nsRefPtr<imgStatusTracker> clone = mStatusTracker->CloneForRecording(); |
|
1088 imgDecoderObserver* observer = clone->GetDecoderObserver(); |
|
1089 |
|
1090 observer->OnStartContainer(); // Signal that width/height are available. |
|
1091 observer->FrameChanged(&nsIntRect::GetMaxSizedIntRect()); |
|
1092 observer->OnStopFrame(); |
|
1093 observer->OnStopDecode(NS_OK); // Unblock page load. |
|
1094 |
|
1095 ImageStatusDiff diff = mStatusTracker->Difference(clone); |
|
1096 mStatusTracker->ApplyDifference(diff); |
|
1097 mStatusTracker->SyncNotifyDifference(diff); |
|
1098 } |
|
1099 |
|
1100 EvaluateAnimation(); |
|
1101 } |
|
1102 |
|
1103 void |
|
1104 VectorImage::OnSVGDocumentError() |
|
1105 { |
|
1106 CancelAllListeners(); |
|
1107 |
|
1108 // XXXdholbert Need to do something more for the parsing failed case -- right |
|
1109 // now, this just makes us draw the "object" icon, rather than the (jagged) |
|
1110 // "broken image" icon. See bug 594505. |
|
1111 mError = true; |
|
1112 |
|
1113 if (mStatusTracker) { |
|
1114 nsRefPtr<imgStatusTracker> clone = mStatusTracker->CloneForRecording(); |
|
1115 imgDecoderObserver* observer = clone->GetDecoderObserver(); |
|
1116 |
|
1117 // Unblock page load. |
|
1118 observer->OnStopDecode(NS_ERROR_FAILURE); |
|
1119 ImageStatusDiff diff = mStatusTracker->Difference(clone); |
|
1120 mStatusTracker->ApplyDifference(diff); |
|
1121 mStatusTracker->SyncNotifyDifference(diff); |
|
1122 } |
|
1123 } |
|
1124 |
|
1125 //------------------------------------------------------------------------------ |
|
1126 // nsIStreamListener method |
|
1127 |
|
1128 //****************************************************************************** |
|
1129 /* void onDataAvailable(in nsIRequest request, in nsISupports ctxt, |
|
1130 in nsIInputStream inStr, in unsigned long sourceOffset, |
|
1131 in unsigned long count); */ |
|
1132 NS_IMETHODIMP |
|
1133 VectorImage::OnDataAvailable(nsIRequest* aRequest, nsISupports* aCtxt, |
|
1134 nsIInputStream* aInStr, uint64_t aSourceOffset, |
|
1135 uint32_t aCount) |
|
1136 { |
|
1137 if (mError) |
|
1138 return NS_ERROR_FAILURE; |
|
1139 |
|
1140 return mSVGDocumentWrapper->OnDataAvailable(aRequest, aCtxt, aInStr, |
|
1141 aSourceOffset, aCount); |
|
1142 } |
|
1143 |
|
1144 // -------------------------- |
|
1145 // Invalidation helper method |
|
1146 |
|
1147 void |
|
1148 VectorImage::InvalidateObserversOnNextRefreshDriverTick() |
|
1149 { |
|
1150 if (mHaveAnimations) { |
|
1151 mHasPendingInvalidation = true; |
|
1152 } else { |
|
1153 SendInvalidationNotifications(); |
|
1154 } |
|
1155 } |
|
1156 |
|
1157 } // namespace image |
|
1158 } // namespace mozilla |