Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 : * This Source Code Form is subject to the terms of the Mozilla Public
3 : * License, v. 2.0. If a copy of the MPL was not distributed with this
4 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 :
6 : // Must #include ImageLogging.h before any IPDL-generated files or other files
7 : // that #include prlog.h
8 : #include "ImageLogging.h"
9 :
10 : #include "RasterImage.h"
11 :
12 : #include "gfxPlatform.h"
13 : #include "nsComponentManagerUtils.h"
14 : #include "nsError.h"
15 : #include "Decoder.h"
16 : #include "prenv.h"
17 : #include "prsystem.h"
18 : #include "IDecodingTask.h"
19 : #include "ImageContainer.h"
20 : #include "ImageRegion.h"
21 : #include "Layers.h"
22 : #include "LookupResult.h"
23 : #include "nsIConsoleService.h"
24 : #include "nsIInputStream.h"
25 : #include "nsIScriptError.h"
26 : #include "nsISupportsPrimitives.h"
27 : #include "nsPresContext.h"
28 : #include "SourceBuffer.h"
29 : #include "SurfaceCache.h"
30 : #include "FrameAnimator.h"
31 :
32 : #include "gfxContext.h"
33 :
34 : #include "mozilla/gfx/2D.h"
35 : #include "mozilla/DebugOnly.h"
36 : #include "mozilla/Likely.h"
37 : #include "mozilla/RefPtr.h"
38 : #include "mozilla/Move.h"
39 : #include "mozilla/MemoryReporting.h"
40 : #include "mozilla/Services.h"
41 : #include <stdint.h>
42 : #include "mozilla/Telemetry.h"
43 : #include "mozilla/TimeStamp.h"
44 : #include "mozilla/Tuple.h"
45 : #include "mozilla/ClearOnShutdown.h"
46 : #include "mozilla/gfx/Scale.h"
47 :
48 : #include "GeckoProfiler.h"
49 : #include "gfx2DGlue.h"
50 : #include "gfxPrefs.h"
51 : #include <algorithm>
52 :
53 : namespace mozilla {
54 :
55 : using namespace gfx;
56 : using namespace layers;
57 :
58 : namespace image {
59 :
60 : using std::ceil;
61 : using std::min;
62 :
63 : #ifndef DEBUG
64 : NS_IMPL_ISUPPORTS(RasterImage, imgIContainer, nsIProperties)
65 : #else
66 5143 : NS_IMPL_ISUPPORTS(RasterImage, imgIContainer, nsIProperties,
67 : imgIContainerDebug)
68 : #endif
69 :
70 : //******************************************************************************
71 20 : RasterImage::RasterImage(ImageURL* aURI /* = nullptr */) :
72 : ImageResource(aURI), // invoke superclass's constructor
73 : mSize(0,0),
74 : mLockCount(0),
75 : mDecodeCount(0),
76 20 : mImageProducerID(ImageContainer::AllocateProducerID()),
77 : mLastFrameID(0),
78 : mLastImageContainerDrawResult(DrawResult::NOT_READY),
79 : #ifdef DEBUG
80 : mFramesNotified(0),
81 : #endif
82 40 : mSourceBuffer(WrapNotNull(new SourceBuffer())),
83 : mHasSize(false),
84 : mTransient(false),
85 : mSyncLoad(false),
86 : mDiscardable(false),
87 : mSomeSourceData(false),
88 : mAllSourceData(false),
89 : mHasBeenDecoded(false),
90 : mPendingAnimation(false),
91 : mAnimationFinished(false),
92 80 : mWantFullDecode(false)
93 : {
94 20 : }
95 :
96 : //******************************************************************************
97 3 : RasterImage::~RasterImage()
98 : {
99 : // Make sure our SourceBuffer is marked as complete. This will ensure that any
100 : // outstanding decoders terminate.
101 1 : if (!mSourceBuffer->IsComplete()) {
102 0 : mSourceBuffer->Complete(NS_ERROR_ABORT);
103 : }
104 :
105 : // Release all frames from the surface cache.
106 1 : SurfaceCache::RemoveImage(ImageKey(this));
107 :
108 : // Record Telemetry.
109 1 : Telemetry::Accumulate(Telemetry::IMAGE_DECODE_COUNT, mDecodeCount);
110 1 : if (mAnimationState) {
111 0 : Telemetry::Accumulate(Telemetry::IMAGE_ANIMATED_DECODE_COUNT, mDecodeCount);
112 : }
113 3 : }
114 :
115 : nsresult
116 20 : RasterImage::Init(const char* aMimeType,
117 : uint32_t aFlags)
118 : {
119 : // We don't support re-initialization
120 20 : if (mInitialized) {
121 0 : return NS_ERROR_ILLEGAL_VALUE;
122 : }
123 :
124 : // Not sure an error can happen before init, but be safe
125 20 : if (mError) {
126 0 : return NS_ERROR_FAILURE;
127 : }
128 :
129 : // We want to avoid redecodes for transient images.
130 20 : MOZ_ASSERT_IF(aFlags & INIT_FLAG_TRANSIENT,
131 : !(aFlags & INIT_FLAG_DISCARDABLE));
132 :
133 : // Store initialization data
134 20 : mDiscardable = !!(aFlags & INIT_FLAG_DISCARDABLE);
135 20 : mWantFullDecode = !!(aFlags & INIT_FLAG_DECODE_IMMEDIATELY);
136 20 : mTransient = !!(aFlags & INIT_FLAG_TRANSIENT);
137 20 : mSyncLoad = !!(aFlags & INIT_FLAG_SYNC_LOAD);
138 :
139 : // Use the MIME type to select a decoder type, and make sure there *is* a
140 : // decoder for this MIME type.
141 20 : NS_ENSURE_ARG_POINTER(aMimeType);
142 20 : mDecoderType = DecoderFactory::GetDecoderType(aMimeType);
143 20 : if (mDecoderType == DecoderType::UNKNOWN) {
144 1 : return NS_ERROR_FAILURE;
145 : }
146 :
147 : // Lock this image's surfaces in the SurfaceCache if we're not discardable.
148 19 : if (!mDiscardable) {
149 19 : mLockCount++;
150 19 : SurfaceCache::LockImage(ImageKey(this));
151 : }
152 :
153 : // Mark us as initialized
154 19 : mInitialized = true;
155 :
156 19 : return NS_OK;
157 : }
158 :
159 : //******************************************************************************
160 : NS_IMETHODIMP_(void)
161 15 : RasterImage::RequestRefresh(const TimeStamp& aTime)
162 : {
163 15 : if (HadRecentRefresh(aTime)) {
164 6 : return;
165 : }
166 :
167 14 : EvaluateAnimation();
168 :
169 14 : if (!mAnimating) {
170 4 : return;
171 : }
172 :
173 10 : RefreshResult res;
174 10 : if (mAnimationState) {
175 10 : MOZ_ASSERT(mFrameAnimator);
176 10 : res = mFrameAnimator->RequestRefresh(*mAnimationState, aTime, mAnimationFinished);
177 : }
178 :
179 10 : if (res.mFrameAdvanced) {
180 : // Notify listeners that our frame has actually changed, but do this only
181 : // once for all frames that we've now passed (if AdvanceFrame() was called
182 : // more than once).
183 : #ifdef DEBUG
184 8 : mFramesNotified++;
185 : #endif
186 :
187 8 : NotifyProgress(NoProgress, res.mDirtyRect);
188 : }
189 :
190 10 : if (res.mAnimationFinished) {
191 0 : mAnimationFinished = true;
192 0 : EvaluateAnimation();
193 : }
194 : }
195 :
196 : //******************************************************************************
197 : NS_IMETHODIMP
198 219 : RasterImage::GetWidth(int32_t* aWidth)
199 : {
200 219 : NS_ENSURE_ARG_POINTER(aWidth);
201 :
202 219 : if (mError) {
203 0 : *aWidth = 0;
204 0 : return NS_ERROR_FAILURE;
205 : }
206 :
207 219 : *aWidth = mSize.width;
208 219 : return NS_OK;
209 : }
210 :
211 : //******************************************************************************
212 : NS_IMETHODIMP
213 219 : RasterImage::GetHeight(int32_t* aHeight)
214 : {
215 219 : NS_ENSURE_ARG_POINTER(aHeight);
216 :
217 219 : if (mError) {
218 0 : *aHeight = 0;
219 0 : return NS_ERROR_FAILURE;
220 : }
221 :
222 219 : *aHeight = mSize.height;
223 219 : return NS_OK;
224 : }
225 :
226 : //******************************************************************************
227 : nsresult
228 0 : RasterImage::GetNativeSizes(nsTArray<IntSize>& aNativeSizes) const
229 : {
230 0 : if (mError) {
231 0 : return NS_ERROR_FAILURE;
232 : }
233 :
234 0 : if (mNativeSizes.IsEmpty()) {
235 0 : aNativeSizes.Clear();
236 0 : aNativeSizes.AppendElement(mSize);
237 : } else {
238 0 : aNativeSizes = mNativeSizes;
239 : }
240 :
241 0 : return NS_OK;
242 : }
243 :
244 : //******************************************************************************
245 : NS_IMETHODIMP
246 0 : RasterImage::GetIntrinsicSize(nsSize* aSize)
247 : {
248 0 : if (mError) {
249 0 : return NS_ERROR_FAILURE;
250 : }
251 :
252 0 : *aSize = nsSize(nsPresContext::CSSPixelsToAppUnits(mSize.width),
253 : nsPresContext::CSSPixelsToAppUnits(mSize.height));
254 0 : return NS_OK;
255 : }
256 :
257 : //******************************************************************************
258 : NS_IMETHODIMP
259 179 : RasterImage::GetIntrinsicRatio(nsSize* aRatio)
260 : {
261 179 : if (mError) {
262 0 : return NS_ERROR_FAILURE;
263 : }
264 :
265 179 : *aRatio = nsSize(mSize.width, mSize.height);
266 179 : return NS_OK;
267 : }
268 :
269 : NS_IMETHODIMP_(Orientation)
270 0 : RasterImage::GetOrientation()
271 : {
272 0 : return mOrientation;
273 : }
274 :
275 : //******************************************************************************
276 : NS_IMETHODIMP
277 72 : RasterImage::GetType(uint16_t* aType)
278 : {
279 72 : NS_ENSURE_ARG_POINTER(aType);
280 :
281 72 : *aType = imgIContainer::TYPE_RASTER;
282 72 : return NS_OK;
283 : }
284 :
285 : LookupResult
286 60 : RasterImage::LookupFrameInternal(const IntSize& aSize,
287 : uint32_t aFlags,
288 : PlaybackType aPlaybackType)
289 : {
290 60 : if (mAnimationState && aPlaybackType == PlaybackType::eAnimated) {
291 13 : MOZ_ASSERT(mFrameAnimator);
292 13 : MOZ_ASSERT(ToSurfaceFlags(aFlags) == DefaultSurfaceFlags(),
293 : "Can't composite frames with non-default surface flags");
294 13 : return mFrameAnimator->GetCompositedFrame(*mAnimationState);
295 : }
296 :
297 47 : SurfaceFlags surfaceFlags = ToSurfaceFlags(aFlags);
298 :
299 : // We don't want any substitution for sync decodes, and substitution would be
300 : // illegal when high quality downscaling is disabled, so we use
301 : // SurfaceCache::Lookup in this case.
302 47 : if ((aFlags & FLAG_SYNC_DECODE) || !(aFlags & FLAG_HIGH_QUALITY_SCALING)) {
303 : return SurfaceCache::Lookup(ImageKey(this),
304 46 : RasterSurfaceKey(aSize,
305 : surfaceFlags,
306 23 : PlaybackType::eStatic));
307 : }
308 :
309 : // We'll return the best match we can find to the requested frame.
310 : return SurfaceCache::LookupBestMatch(ImageKey(this),
311 48 : RasterSurfaceKey(aSize,
312 : surfaceFlags,
313 24 : PlaybackType::eStatic));
314 : }
315 :
316 : DrawableSurface
317 57 : RasterImage::LookupFrame(const IntSize& aSize,
318 : uint32_t aFlags,
319 : PlaybackType aPlaybackType)
320 : {
321 57 : MOZ_ASSERT(NS_IsMainThread());
322 :
323 : // If we're opaque, we don't need to care about premultiplied alpha, because
324 : // that can only matter for frames with transparency.
325 57 : if (IsOpaque()) {
326 0 : aFlags &= ~FLAG_DECODE_NO_PREMULTIPLY_ALPHA;
327 : }
328 :
329 57 : IntSize requestedSize = CanDownscaleDuringDecode(aSize, aFlags)
330 57 : ? aSize : mSize;
331 57 : if (requestedSize.IsEmpty()) {
332 0 : return DrawableSurface(); // Can't decode to a surface of zero size.
333 : }
334 :
335 : LookupResult result =
336 114 : LookupFrameInternal(requestedSize, aFlags, aPlaybackType);
337 :
338 57 : if (!result && !mHasSize) {
339 : // We can't request a decode without knowing our intrinsic size. Give up.
340 0 : return DrawableSurface();
341 : }
342 :
343 157 : if (result.Type() == MatchType::NOT_FOUND ||
344 114 : result.Type() == MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND ||
345 43 : ((aFlags & FLAG_SYNC_DECODE) && !result)) {
346 : // We don't have a copy of this frame, and there's no decoder working on
347 : // one. (Or we're sync decoding and the existing decoder hasn't even started
348 : // yet.) Trigger decoding so it'll be available next time.
349 14 : MOZ_ASSERT(aPlaybackType != PlaybackType::eAnimated ||
350 : gfxPrefs::ImageMemAnimatedDiscardable() ||
351 : !mAnimationState || mAnimationState->KnownFrameCount() < 1,
352 : "Animated frames should be locked");
353 :
354 14 : bool ranSync = Decode(requestedSize, aFlags, aPlaybackType);
355 :
356 : // If we can or did sync decode, we should already have the frame.
357 14 : if (ranSync || (aFlags & FLAG_SYNC_DECODE)) {
358 3 : result = LookupFrameInternal(requestedSize, aFlags, aPlaybackType);
359 : }
360 : }
361 :
362 57 : if (!result) {
363 : // We still weren't able to get a frame. Give up.
364 11 : return DrawableSurface();
365 : }
366 :
367 46 : if (result.Surface()->GetCompositingFailed()) {
368 0 : return DrawableSurface();
369 : }
370 :
371 46 : MOZ_ASSERT(!result.Surface()->GetIsPaletted(),
372 : "Should not have a paletted frame");
373 :
374 : // Sync decoding guarantees that we got the frame, but if it's owned by an
375 : // async decoder that's currently running, the contents of the frame may not
376 : // be available yet. Make sure we get everything.
377 46 : if (mAllSourceData && (aFlags & FLAG_SYNC_DECODE)) {
378 0 : result.Surface()->WaitUntilFinished();
379 : }
380 :
381 : // If we could have done some decoding in this function we need to check if
382 : // that decoding encountered an error and hence aborted the surface. We want
383 : // to avoid calling IsAborted if we weren't passed any sync decode flag because
384 : // IsAborted acquires the monitor for the imgFrame.
385 70 : if (aFlags & (FLAG_SYNC_DECODE | FLAG_SYNC_DECODE_IF_FAST) &&
386 24 : result.Surface()->IsAborted()) {
387 0 : return DrawableSurface();
388 : }
389 :
390 46 : return Move(result.Surface());
391 : }
392 :
393 : bool
394 157 : RasterImage::IsOpaque()
395 : {
396 157 : if (mError) {
397 0 : return false;
398 : }
399 :
400 157 : Progress progress = mProgressTracker->GetProgress();
401 :
402 : // If we haven't yet finished decoding, the safe answer is "not opaque".
403 157 : if (!(progress & FLAG_DECODE_COMPLETE)) {
404 40 : return false;
405 : }
406 :
407 : // Other, we're opaque if FLAG_HAS_TRANSPARENCY is not set.
408 117 : return !(progress & FLAG_HAS_TRANSPARENCY);
409 : }
410 :
411 : NS_IMETHODIMP_(bool)
412 86 : RasterImage::WillDrawOpaqueNow()
413 : {
414 86 : if (!IsOpaque()) {
415 86 : return false;
416 : }
417 :
418 0 : if (mAnimationState) {
419 0 : if (!gfxPrefs::ImageMemAnimatedDiscardable()) {
420 : // We never discard frames of animated images.
421 0 : return true;
422 : } else {
423 0 : if (mAnimationState->GetCompositedFrameInvalid()) {
424 : // We're not going to draw anything at all.
425 0 : return false;
426 : }
427 : }
428 : }
429 :
430 : // If we are not locked our decoded data could get discard at any time (ie
431 : // between the call to this function and when we are asked to draw), so we
432 : // have to return false if we are unlocked.
433 0 : if (mLockCount == 0) {
434 0 : return false;
435 : }
436 :
437 : LookupResult result =
438 : SurfaceCache::LookupBestMatch(ImageKey(this),
439 0 : RasterSurfaceKey(mSize,
440 : DefaultSurfaceFlags(),
441 0 : PlaybackType::eStatic));
442 0 : MatchType matchType = result.Type();
443 0 : if (matchType == MatchType::NOT_FOUND || matchType == MatchType::PENDING ||
444 0 : !result.Surface()->IsFinished()) {
445 0 : return false;
446 : }
447 :
448 0 : return true;
449 : }
450 :
451 : void
452 0 : RasterImage::OnSurfaceDiscarded(const SurfaceKey& aSurfaceKey)
453 : {
454 0 : MOZ_ASSERT(mProgressTracker);
455 :
456 : bool animatedFramesDiscarded =
457 0 : mAnimationState && aSurfaceKey.Playback() == PlaybackType::eAnimated;
458 :
459 0 : RefPtr<RasterImage> image = this;
460 0 : NS_DispatchToMainThread(NS_NewRunnableFunction(
461 : "RasterImage::OnSurfaceDiscarded",
462 0 : [=]() -> void {
463 0 : image->OnSurfaceDiscardedInternal(animatedFramesDiscarded);
464 0 : }));
465 0 : }
466 :
467 : void
468 0 : RasterImage::OnSurfaceDiscardedInternal(bool aAnimatedFramesDiscarded)
469 : {
470 0 : MOZ_ASSERT(NS_IsMainThread());
471 :
472 0 : if (aAnimatedFramesDiscarded && mAnimationState) {
473 0 : MOZ_ASSERT(gfxPrefs::ImageMemAnimatedDiscardable());
474 0 : mImageContainer = nullptr;
475 : gfx::IntRect rect =
476 0 : mAnimationState->UpdateState(mAnimationFinished, this, mSize);
477 0 : NotifyProgress(NoProgress, rect);
478 : }
479 :
480 0 : if (mProgressTracker) {
481 0 : mProgressTracker->OnDiscard();
482 : }
483 0 : }
484 :
485 : //******************************************************************************
486 : NS_IMETHODIMP
487 4 : RasterImage::GetAnimated(bool* aAnimated)
488 : {
489 4 : if (mError) {
490 0 : return NS_ERROR_FAILURE;
491 : }
492 :
493 4 : NS_ENSURE_ARG_POINTER(aAnimated);
494 :
495 : // If we have an AnimationState, we can know for sure.
496 4 : if (mAnimationState) {
497 4 : *aAnimated = true;
498 4 : return NS_OK;
499 : }
500 :
501 : // Otherwise, we need to have been decoded to know for sure, since if we were
502 : // decoded at least once mAnimationState would have been created for animated
503 : // images. This is true even though we check for animation during the
504 : // metadata decode, because we may still discover animation only during the
505 : // full decode for corrupt images.
506 0 : if (!mHasBeenDecoded) {
507 0 : return NS_ERROR_NOT_AVAILABLE;
508 : }
509 :
510 : // We know for sure
511 0 : *aAnimated = false;
512 :
513 0 : return NS_OK;
514 : }
515 :
516 : //******************************************************************************
517 : NS_IMETHODIMP_(int32_t)
518 4 : RasterImage::GetFirstFrameDelay()
519 : {
520 4 : if (mError) {
521 0 : return -1;
522 : }
523 :
524 4 : bool animated = false;
525 4 : if (NS_FAILED(GetAnimated(&animated)) || !animated) {
526 0 : return -1;
527 : }
528 :
529 4 : MOZ_ASSERT(mAnimationState, "Animated images should have an AnimationState");
530 4 : return mAnimationState->FirstFrameTimeout().AsEncodedValueDeprecated();
531 : }
532 :
533 : NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
534 0 : RasterImage::GetFrame(uint32_t aWhichFrame,
535 : uint32_t aFlags)
536 : {
537 0 : return GetFrameAtSize(mSize, aWhichFrame, aFlags);
538 : }
539 :
540 : NS_IMETHODIMP_(already_AddRefed<SourceSurface>)
541 0 : RasterImage::GetFrameAtSize(const IntSize& aSize,
542 : uint32_t aWhichFrame,
543 : uint32_t aFlags)
544 : {
545 : RefPtr<SourceSurface> surf =
546 0 : GetFrameInternal(aSize, aWhichFrame, aFlags).second().forget();
547 : // If we are here, it suggests the image is embedded in a canvas or some
548 : // other path besides layers, and we won't need the file handle.
549 0 : MarkSurfaceShared(surf);
550 0 : return surf.forget();
551 : }
552 :
553 : Pair<DrawResult, RefPtr<SourceSurface>>
554 0 : RasterImage::GetFrameInternal(const IntSize& aSize,
555 : uint32_t aWhichFrame,
556 : uint32_t aFlags)
557 : {
558 0 : MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE);
559 :
560 0 : if (aSize.IsEmpty()) {
561 0 : return MakePair(DrawResult::BAD_ARGS, RefPtr<SourceSurface>());
562 : }
563 :
564 0 : if (aWhichFrame > FRAME_MAX_VALUE) {
565 0 : return MakePair(DrawResult::BAD_ARGS, RefPtr<SourceSurface>());
566 : }
567 :
568 0 : if (mError) {
569 0 : return MakePair(DrawResult::BAD_IMAGE, RefPtr<SourceSurface>());
570 : }
571 :
572 : // Get the frame. If it's not there, it's probably the caller's fault for
573 : // not waiting for the data to be loaded from the network or not passing
574 : // FLAG_SYNC_DECODE.
575 : DrawableSurface surface =
576 0 : LookupFrame(aSize, aFlags, ToPlaybackType(aWhichFrame));
577 0 : if (!surface) {
578 : // The OS threw this frame away and we couldn't redecode it.
579 0 : return MakePair(DrawResult::TEMPORARY_ERROR, RefPtr<SourceSurface>());
580 : }
581 :
582 0 : RefPtr<SourceSurface> sourceSurface = surface->GetSourceSurface();
583 :
584 0 : if (!surface->IsFinished()) {
585 0 : return MakePair(DrawResult::INCOMPLETE, Move(sourceSurface));
586 : }
587 :
588 0 : return MakePair(DrawResult::SUCCESS, Move(sourceSurface));
589 : }
590 :
591 : Pair<DrawResult, RefPtr<layers::Image>>
592 0 : RasterImage::GetCurrentImage(ImageContainer* aContainer, uint32_t aFlags)
593 : {
594 0 : MOZ_ASSERT(NS_IsMainThread());
595 0 : MOZ_ASSERT(aContainer);
596 :
597 : DrawResult drawResult;
598 0 : RefPtr<SourceSurface> surface;
599 0 : Tie(drawResult, surface) =
600 0 : GetFrameInternal(mSize, FRAME_CURRENT, aFlags | FLAG_ASYNC_NOTIFY);
601 0 : if (!surface) {
602 : // The OS threw out some or all of our buffer. We'll need to wait for the
603 : // redecode (which was automatically triggered by GetFrame) to complete.
604 0 : return MakePair(drawResult, RefPtr<layers::Image>());
605 : }
606 :
607 0 : RefPtr<layers::Image> image = new layers::SourceSurfaceImage(surface);
608 0 : return MakePair(drawResult, Move(image));
609 : }
610 :
611 : NS_IMETHODIMP_(bool)
612 14 : RasterImage::IsImageContainerAvailable(LayerManager* aManager, uint32_t aFlags)
613 : {
614 14 : int32_t maxTextureSize = aManager->GetMaxTextureSize();
615 25 : if (!mHasSize ||
616 22 : mSize.width > maxTextureSize ||
617 11 : mSize.height > maxTextureSize) {
618 3 : return false;
619 : }
620 :
621 11 : return true;
622 : }
623 :
624 : NS_IMETHODIMP_(already_AddRefed<ImageContainer>)
625 0 : RasterImage::GetImageContainer(LayerManager* aManager, uint32_t aFlags)
626 : {
627 0 : MOZ_ASSERT(NS_IsMainThread());
628 0 : MOZ_ASSERT(aManager);
629 0 : MOZ_ASSERT((aFlags & ~(FLAG_SYNC_DECODE |
630 : FLAG_SYNC_DECODE_IF_FAST |
631 : FLAG_ASYNC_NOTIFY))
632 : == FLAG_NONE,
633 : "Unsupported flag passed to GetImageContainer");
634 :
635 0 : int32_t maxTextureSize = aManager->GetMaxTextureSize();
636 0 : if (!mHasSize ||
637 0 : mSize.width > maxTextureSize ||
638 0 : mSize.height > maxTextureSize) {
639 0 : return nullptr;
640 : }
641 :
642 0 : if (mAnimationConsumers == 0) {
643 0 : SendOnUnlockedDraw(aFlags);
644 : }
645 :
646 0 : RefPtr<layers::ImageContainer> container = mImageContainer.get();
647 :
648 : bool mustRedecode =
649 0 : (aFlags & (FLAG_SYNC_DECODE | FLAG_SYNC_DECODE_IF_FAST)) &&
650 0 : mLastImageContainerDrawResult != DrawResult::SUCCESS &&
651 0 : mLastImageContainerDrawResult != DrawResult::BAD_IMAGE;
652 :
653 0 : if (container && !mustRedecode) {
654 0 : return container.forget();
655 : }
656 :
657 : // We need a new ImageContainer, so create one.
658 0 : container = LayerManager::CreateImageContainer();
659 :
660 : DrawResult drawResult;
661 0 : RefPtr<layers::Image> image;
662 0 : Tie(drawResult, image) = GetCurrentImage(container, aFlags);
663 0 : if (!image) {
664 0 : return nullptr;
665 : }
666 :
667 : // |image| holds a reference to a SourceSurface which in turn holds a lock on
668 : // the current frame's data buffer, ensuring that it doesn't get freed as
669 : // long as the layer system keeps this ImageContainer alive.
670 0 : AutoTArray<ImageContainer::NonOwningImage, 1> imageList;
671 0 : imageList.AppendElement(ImageContainer::NonOwningImage(image, TimeStamp(),
672 0 : mLastFrameID++,
673 0 : mImageProducerID));
674 0 : container->SetCurrentImagesInTransaction(imageList);
675 :
676 0 : mLastImageContainerDrawResult = drawResult;
677 0 : mImageContainer = container;
678 :
679 0 : return container.forget();
680 : }
681 :
682 : void
683 22 : RasterImage::UpdateImageContainer()
684 : {
685 22 : MOZ_ASSERT(NS_IsMainThread());
686 :
687 22 : RefPtr<layers::ImageContainer> container = mImageContainer.get();
688 22 : if (!container) {
689 22 : return;
690 : }
691 :
692 : DrawResult drawResult;
693 0 : RefPtr<layers::Image> image;
694 0 : Tie(drawResult, image) = GetCurrentImage(container, FLAG_NONE);
695 0 : if (!image) {
696 0 : return;
697 : }
698 :
699 0 : mLastImageContainerDrawResult = drawResult;
700 0 : AutoTArray<ImageContainer::NonOwningImage, 1> imageList;
701 0 : imageList.AppendElement(ImageContainer::NonOwningImage(image, TimeStamp(),
702 0 : mLastFrameID++,
703 0 : mImageProducerID));
704 0 : container->SetCurrentImages(imageList);
705 : }
706 :
707 : size_t
708 19 : RasterImage::SizeOfSourceWithComputedFallback(MallocSizeOf aMallocSizeOf) const
709 : {
710 19 : return mSourceBuffer->SizeOfIncludingThisWithComputedFallback(aMallocSizeOf);
711 : }
712 :
713 : void
714 0 : RasterImage::CollectSizeOfSurfaces(nsTArray<SurfaceMemoryCounter>& aCounters,
715 : MallocSizeOf aMallocSizeOf) const
716 : {
717 0 : SurfaceCache::CollectSizeOfSurfaces(ImageKey(this), aCounters, aMallocSizeOf);
718 0 : if (mFrameAnimator) {
719 0 : mFrameAnimator->CollectSizeOfCompositingSurfaces(aCounters, aMallocSizeOf);
720 : }
721 0 : }
722 :
723 : bool
724 33 : RasterImage::SetMetadata(const ImageMetadata& aMetadata,
725 : bool aFromMetadataDecode)
726 : {
727 33 : MOZ_ASSERT(NS_IsMainThread());
728 :
729 33 : if (mError) {
730 0 : return true;
731 : }
732 :
733 33 : if (aMetadata.HasSize()) {
734 33 : IntSize size = aMetadata.GetSize();
735 33 : if (size.width < 0 || size.height < 0) {
736 0 : NS_WARNING("Image has negative intrinsic size");
737 0 : DoError();
738 0 : return true;
739 : }
740 :
741 33 : MOZ_ASSERT(aMetadata.HasOrientation());
742 33 : Orientation orientation = aMetadata.GetOrientation();
743 :
744 : // If we already have a size, check the new size against the old one.
745 33 : if (mHasSize && (size != mSize || orientation != mOrientation)) {
746 : NS_WARNING("Image changed size or orientation on redecode! "
747 0 : "This should not happen!");
748 0 : DoError();
749 0 : return true;
750 : }
751 :
752 : // Set the size and flag that we have it.
753 33 : mSize = size;
754 33 : mOrientation = orientation;
755 33 : mNativeSizes = aMetadata.GetNativeSizes();
756 33 : mHasSize = true;
757 : }
758 :
759 33 : if (mHasSize && aMetadata.HasAnimation() && !mAnimationState) {
760 : // We're becoming animated, so initialize animation stuff.
761 2 : mAnimationState.emplace(mAnimationMode);
762 2 : mFrameAnimator = MakeUnique<FrameAnimator>(this, mSize);
763 :
764 2 : if (!gfxPrefs::ImageMemAnimatedDiscardable()) {
765 : // We don't support discarding animated images (See bug 414259).
766 : // Lock the image and throw away the key.
767 0 : LockImage();
768 : }
769 :
770 2 : if (!aFromMetadataDecode) {
771 : // The metadata decode reported that this image isn't animated, but we
772 : // discovered that it actually was during the full decode. This is a
773 : // rare failure that only occurs for corrupt images. To recover, we need
774 : // to discard all existing surfaces and redecode.
775 0 : return false;
776 : }
777 : }
778 :
779 33 : if (mAnimationState) {
780 4 : mAnimationState->SetLoopCount(aMetadata.GetLoopCount());
781 4 : mAnimationState->SetFirstFrameTimeout(aMetadata.GetFirstFrameTimeout());
782 :
783 4 : if (aMetadata.HasLoopLength()) {
784 2 : mAnimationState->SetLoopLength(aMetadata.GetLoopLength());
785 : }
786 4 : if (aMetadata.HasFirstFrameRefreshArea()) {
787 : mAnimationState
788 2 : ->SetFirstFrameRefreshArea(aMetadata.GetFirstFrameRefreshArea());
789 : }
790 : }
791 :
792 33 : if (aMetadata.HasHotspot()) {
793 0 : IntPoint hotspot = aMetadata.GetHotspot();
794 :
795 : nsCOMPtr<nsISupportsPRUint32> intwrapx =
796 0 : do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID);
797 : nsCOMPtr<nsISupportsPRUint32> intwrapy =
798 0 : do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID);
799 0 : intwrapx->SetData(hotspot.x);
800 0 : intwrapy->SetData(hotspot.y);
801 :
802 0 : Set("hotspotX", intwrapx);
803 0 : Set("hotspotY", intwrapy);
804 : }
805 :
806 33 : return true;
807 : }
808 :
809 : NS_IMETHODIMP
810 19 : RasterImage::SetAnimationMode(uint16_t aAnimationMode)
811 : {
812 19 : if (mAnimationState) {
813 2 : mAnimationState->SetAnimationMode(aAnimationMode);
814 : }
815 19 : return SetAnimationModeInternal(aAnimationMode);
816 : }
817 :
818 : //******************************************************************************
819 :
820 : nsresult
821 2 : RasterImage::StartAnimation()
822 : {
823 2 : if (mError) {
824 0 : return NS_ERROR_FAILURE;
825 : }
826 :
827 2 : MOZ_ASSERT(ShouldAnimate(), "Should not animate!");
828 :
829 : // If we're not ready to animate, then set mPendingAnimation, which will cause
830 : // us to start animating if and when we do become ready.
831 2 : mPendingAnimation = !mAnimationState || mAnimationState->KnownFrameCount() < 1;
832 2 : if (mPendingAnimation) {
833 0 : return NS_OK;
834 : }
835 :
836 : // Don't bother to animate if we're displaying the first frame forever.
837 10 : if (mAnimationState->GetCurrentAnimationFrameIndex() == 0 &&
838 10 : mAnimationState->FirstFrameTimeout() == FrameTimeout::Forever()) {
839 0 : mAnimationFinished = true;
840 0 : return NS_ERROR_ABORT;
841 : }
842 :
843 : // We need to set the time that this initial frame was first displayed, as
844 : // this is used in AdvanceFrame().
845 2 : mAnimationState->InitAnimationFrameTimeIfNecessary();
846 :
847 2 : return NS_OK;
848 : }
849 :
850 : //******************************************************************************
851 : nsresult
852 0 : RasterImage::StopAnimation()
853 : {
854 0 : MOZ_ASSERT(mAnimating, "Should be animating!");
855 :
856 0 : nsresult rv = NS_OK;
857 0 : if (mError) {
858 0 : rv = NS_ERROR_FAILURE;
859 : } else {
860 0 : mAnimationState->SetAnimationFrameTime(TimeStamp());
861 : }
862 :
863 0 : mAnimating = false;
864 0 : return rv;
865 : }
866 :
867 : //******************************************************************************
868 : NS_IMETHODIMP
869 0 : RasterImage::ResetAnimation()
870 : {
871 0 : if (mError) {
872 0 : return NS_ERROR_FAILURE;
873 : }
874 :
875 0 : mPendingAnimation = false;
876 :
877 0 : if (mAnimationMode == kDontAnimMode || !mAnimationState ||
878 0 : mAnimationState->GetCurrentAnimationFrameIndex() == 0) {
879 0 : return NS_OK;
880 : }
881 :
882 0 : mAnimationFinished = false;
883 :
884 0 : if (mAnimating) {
885 0 : StopAnimation();
886 : }
887 :
888 0 : MOZ_ASSERT(mAnimationState, "Should have AnimationState");
889 0 : mAnimationState->ResetAnimation();
890 :
891 0 : NotifyProgress(NoProgress, mAnimationState->FirstFrameRefreshArea());
892 :
893 : // Start the animation again. It may not have been running before, if
894 : // mAnimationFinished was true before entering this function.
895 0 : EvaluateAnimation();
896 :
897 0 : return NS_OK;
898 : }
899 :
900 : //******************************************************************************
901 : NS_IMETHODIMP_(void)
902 2 : RasterImage::SetAnimationStartTime(const TimeStamp& aTime)
903 : {
904 2 : if (mError || mAnimationMode == kDontAnimMode || mAnimating || !mAnimationState) {
905 0 : return;
906 : }
907 :
908 2 : mAnimationState->SetAnimationFrameTime(aTime);
909 : }
910 :
911 : NS_IMETHODIMP_(float)
912 0 : RasterImage::GetFrameIndex(uint32_t aWhichFrame)
913 : {
914 0 : MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE, "Invalid argument");
915 0 : return (aWhichFrame == FRAME_FIRST || !mAnimationState)
916 0 : ? 0.0f
917 0 : : mAnimationState->GetCurrentAnimationFrameIndex();
918 : }
919 :
920 : NS_IMETHODIMP_(IntRect)
921 0 : RasterImage::GetImageSpaceInvalidationRect(const IntRect& aRect)
922 : {
923 0 : return aRect;
924 : }
925 :
926 : nsresult
927 20 : RasterImage::OnImageDataComplete(nsIRequest*, nsISupports*, nsresult aStatus,
928 : bool aLastPart)
929 : {
930 20 : MOZ_ASSERT(NS_IsMainThread());
931 :
932 : // Record that we have all the data we're going to get now.
933 20 : mAllSourceData = true;
934 :
935 : // Let decoders know that there won't be any more data coming.
936 20 : mSourceBuffer->Complete(aStatus);
937 :
938 : // Allow a synchronous metadata decode if mSyncLoad was set, or if we're
939 : // running on a single thread (in which case waiting for the async metadata
940 : // decoder could delay this image's load event quite a bit), or if this image
941 : // is transient.
942 40 : bool canSyncDecodeMetadata = mSyncLoad || mTransient ||
943 40 : DecodePool::NumberOfCores() < 2;
944 :
945 20 : if (canSyncDecodeMetadata && !mHasSize) {
946 : // We're loading this image synchronously, so it needs to be usable after
947 : // this call returns. Since we haven't gotten our size yet, we need to do a
948 : // synchronous metadata decode here.
949 0 : DecodeMetadata(FLAG_SYNC_DECODE);
950 : }
951 :
952 : // Determine our final status, giving precedence to Necko failure codes. We
953 : // check after running the metadata decode in case it triggered an error.
954 20 : nsresult finalStatus = mError ? NS_ERROR_FAILURE : NS_OK;
955 20 : if (NS_FAILED(aStatus)) {
956 1 : finalStatus = aStatus;
957 : }
958 :
959 : // If loading failed, report an error.
960 20 : if (NS_FAILED(finalStatus)) {
961 1 : DoError();
962 : }
963 :
964 20 : Progress loadProgress = LoadCompleteProgress(aLastPart, mError, finalStatus);
965 :
966 20 : if (!mHasSize && !mError) {
967 : // We don't have our size yet, so we'll fire the load event in SetSize().
968 19 : MOZ_ASSERT(!canSyncDecodeMetadata,
969 : "Firing load async after metadata sync decode?");
970 19 : NotifyProgress(FLAG_ONLOAD_BLOCKED);
971 19 : mLoadProgress = Some(loadProgress);
972 19 : return finalStatus;
973 : }
974 :
975 1 : NotifyForLoadEvent(loadProgress);
976 :
977 1 : return finalStatus;
978 : }
979 :
980 : void
981 20 : RasterImage::NotifyForLoadEvent(Progress aProgress)
982 : {
983 20 : MOZ_ASSERT(mHasSize || mError, "Need to know size before firing load event");
984 20 : MOZ_ASSERT(!mHasSize ||
985 : (mProgressTracker->GetProgress() & FLAG_SIZE_AVAILABLE),
986 : "Should have notified that the size is available if we have it");
987 :
988 : // If we encountered an error, make sure we notify for that as well.
989 20 : if (mError) {
990 1 : aProgress |= FLAG_HAS_ERROR;
991 : }
992 :
993 : // Notify our listeners, which will fire this image's load event.
994 20 : NotifyProgress(aProgress);
995 20 : }
996 :
997 : nsresult
998 19 : RasterImage::OnImageDataAvailable(nsIRequest*,
999 : nsISupports*,
1000 : nsIInputStream* aInputStream,
1001 : uint64_t,
1002 : uint32_t aCount)
1003 : {
1004 19 : nsresult rv = mSourceBuffer->AppendFromInputStream(aInputStream, aCount);
1005 19 : if (NS_SUCCEEDED(rv) && !mSomeSourceData) {
1006 19 : mSomeSourceData = true;
1007 19 : if (!mSyncLoad) {
1008 : // Create an async metadata decoder and verify we succeed in doing so.
1009 19 : rv = DecodeMetadata(DECODE_FLAGS_DEFAULT);
1010 : }
1011 : }
1012 :
1013 19 : if (NS_FAILED(rv)) {
1014 0 : DoError();
1015 : }
1016 19 : return rv;
1017 : }
1018 :
1019 : nsresult
1020 19 : RasterImage::SetSourceSizeHint(uint32_t aSizeHint)
1021 : {
1022 19 : return mSourceBuffer->ExpectLength(aSizeHint);
1023 : }
1024 :
1025 : /********* Methods to implement lazy allocation of nsIProperties object *******/
1026 : NS_IMETHODIMP
1027 0 : RasterImage::Get(const char* prop, const nsIID& iid, void** result)
1028 : {
1029 0 : if (!mProperties) {
1030 0 : return NS_ERROR_FAILURE;
1031 : }
1032 0 : return mProperties->Get(prop, iid, result);
1033 : }
1034 :
1035 : NS_IMETHODIMP
1036 0 : RasterImage::Set(const char* prop, nsISupports* value)
1037 : {
1038 0 : if (!mProperties) {
1039 0 : mProperties = do_CreateInstance("@mozilla.org/properties;1");
1040 : }
1041 0 : if (!mProperties) {
1042 0 : return NS_ERROR_OUT_OF_MEMORY;
1043 : }
1044 0 : return mProperties->Set(prop, value);
1045 : }
1046 :
1047 : NS_IMETHODIMP
1048 0 : RasterImage::Has(const char* prop, bool* _retval)
1049 : {
1050 0 : NS_ENSURE_ARG_POINTER(_retval);
1051 0 : if (!mProperties) {
1052 0 : *_retval = false;
1053 0 : return NS_OK;
1054 : }
1055 0 : return mProperties->Has(prop, _retval);
1056 : }
1057 :
1058 : NS_IMETHODIMP
1059 0 : RasterImage::Undefine(const char* prop)
1060 : {
1061 0 : if (!mProperties) {
1062 0 : return NS_ERROR_FAILURE;
1063 : }
1064 0 : return mProperties->Undefine(prop);
1065 : }
1066 :
1067 : NS_IMETHODIMP
1068 0 : RasterImage::GetKeys(uint32_t* count, char*** keys)
1069 : {
1070 0 : if (!mProperties) {
1071 0 : *count = 0;
1072 0 : *keys = nullptr;
1073 0 : return NS_OK;
1074 : }
1075 0 : return mProperties->GetKeys(count, keys);
1076 : }
1077 :
1078 : void
1079 0 : RasterImage::Discard()
1080 : {
1081 0 : MOZ_ASSERT(NS_IsMainThread());
1082 0 : MOZ_ASSERT(CanDiscard(), "Asked to discard but can't");
1083 0 : MOZ_ASSERT(!mAnimationState || gfxPrefs::ImageMemAnimatedDiscardable(),
1084 : "Asked to discard for animated image");
1085 :
1086 : // Delete all the decoded frames.
1087 0 : SurfaceCache::RemoveImage(ImageKey(this));
1088 :
1089 0 : if (mAnimationState) {
1090 0 : mImageContainer = nullptr;
1091 : gfx::IntRect rect =
1092 0 : mAnimationState->UpdateState(mAnimationFinished, this, mSize);
1093 0 : NotifyProgress(NoProgress, rect);
1094 : }
1095 :
1096 : // Notify that we discarded.
1097 0 : if (mProgressTracker) {
1098 0 : mProgressTracker->OnDiscard();
1099 : }
1100 0 : }
1101 :
1102 : bool
1103 0 : RasterImage::CanDiscard() {
1104 0 : return mAllSourceData &&
1105 : // Can discard animated images if the pref is set
1106 0 : (!mAnimationState || gfxPrefs::ImageMemAnimatedDiscardable());
1107 : }
1108 :
1109 : NS_IMETHODIMP
1110 14 : RasterImage::StartDecoding(uint32_t aFlags)
1111 : {
1112 14 : if (mError) {
1113 1 : return NS_ERROR_FAILURE;
1114 : }
1115 :
1116 13 : if (!mHasSize) {
1117 11 : mWantFullDecode = true;
1118 11 : return NS_OK;
1119 : }
1120 :
1121 2 : uint32_t flags = (aFlags & FLAG_ASYNC_NOTIFY) | FLAG_SYNC_DECODE_IF_FAST;
1122 2 : return RequestDecodeForSize(mSize, flags);
1123 : }
1124 :
1125 : bool
1126 9 : RasterImage::StartDecodingWithResult(uint32_t aFlags)
1127 : {
1128 9 : if (mError) {
1129 0 : return false;
1130 : }
1131 :
1132 9 : if (!mHasSize) {
1133 0 : mWantFullDecode = true;
1134 0 : return false;
1135 : }
1136 :
1137 9 : uint32_t flags = (aFlags & FLAG_ASYNC_NOTIFY) | FLAG_SYNC_DECODE_IF_FAST;
1138 18 : DrawableSurface surface = RequestDecodeForSizeInternal(mSize, flags);
1139 9 : return surface && surface->IsFinished();
1140 : }
1141 :
1142 : NS_IMETHODIMP
1143 13 : RasterImage::RequestDecodeForSize(const IntSize& aSize, uint32_t aFlags)
1144 : {
1145 13 : MOZ_ASSERT(NS_IsMainThread());
1146 :
1147 13 : if (mError) {
1148 0 : return NS_ERROR_FAILURE;
1149 : }
1150 :
1151 13 : RequestDecodeForSizeInternal(aSize, aFlags);
1152 :
1153 13 : return NS_OK;
1154 : }
1155 :
1156 : DrawableSurface
1157 22 : RasterImage::RequestDecodeForSizeInternal(const IntSize& aSize, uint32_t aFlags)
1158 : {
1159 22 : MOZ_ASSERT(NS_IsMainThread());
1160 :
1161 22 : if (mError) {
1162 0 : return DrawableSurface();
1163 : }
1164 :
1165 22 : if (!mHasSize) {
1166 0 : mWantFullDecode = true;
1167 0 : return DrawableSurface();
1168 : }
1169 :
1170 : // Decide whether to sync decode images we can decode quickly. Here we are
1171 : // explicitly trading off flashing for responsiveness in the case that we're
1172 : // redecoding an image (see bug 845147).
1173 : bool shouldSyncDecodeIfFast =
1174 22 : !mHasBeenDecoded && (aFlags & FLAG_SYNC_DECODE_IF_FAST);
1175 :
1176 : uint32_t flags = shouldSyncDecodeIfFast
1177 22 : ? aFlags
1178 22 : : aFlags & ~FLAG_SYNC_DECODE_IF_FAST;
1179 :
1180 : // Perform a frame lookup, which will implicitly start decoding if needed.
1181 : return LookupFrame(aSize, flags, mAnimationState ? PlaybackType::eAnimated
1182 22 : : PlaybackType::eStatic);
1183 : }
1184 :
1185 : static bool
1186 33 : LaunchDecodingTask(IDecodingTask* aTask,
1187 : RasterImage* aImage,
1188 : uint32_t aFlags,
1189 : bool aHaveSourceData)
1190 : {
1191 33 : if (aHaveSourceData) {
1192 25 : nsCString uri(aImage->GetURIString());
1193 :
1194 : // If we have all the data, we can sync decode if requested.
1195 14 : if (aFlags & imgIContainer::FLAG_SYNC_DECODE) {
1196 0 : DecodePool::Singleton()->SyncRunIfPossible(aTask, uri);
1197 0 : return true;
1198 : }
1199 :
1200 14 : if (aFlags & imgIContainer::FLAG_SYNC_DECODE_IF_FAST) {
1201 3 : return DecodePool::Singleton()->SyncRunIfPreferred(aTask, uri);
1202 : }
1203 : }
1204 :
1205 : // Perform an async decode. We also take this path if we don't have all the
1206 : // source data yet, since sync decoding is impossible in that situation.
1207 30 : DecodePool::Singleton()->AsyncRun(aTask);
1208 30 : return false;
1209 : }
1210 :
1211 : bool
1212 14 : RasterImage::Decode(const IntSize& aSize,
1213 : uint32_t aFlags,
1214 : PlaybackType aPlaybackType)
1215 : {
1216 14 : MOZ_ASSERT(NS_IsMainThread());
1217 :
1218 14 : if (mError) {
1219 0 : return false;
1220 : }
1221 :
1222 : // If we don't have a size yet, we can't do any other decoding.
1223 14 : if (!mHasSize) {
1224 0 : mWantFullDecode = true;
1225 0 : return false;
1226 : }
1227 :
1228 : // We're about to decode again, which may mean that some of the previous sizes
1229 : // we've decoded at aren't useful anymore. We can allow them to expire from
1230 : // the cache by unlocking them here. When the decode finishes, it will send an
1231 : // invalidation that will cause all instances of this image to redraw. If this
1232 : // image is locked, any surfaces that are still useful will become locked
1233 : // again when LookupFrame touches them, and the remainder will eventually
1234 : // expire.
1235 14 : SurfaceCache::UnlockEntries(ImageKey(this));
1236 :
1237 : // Determine which flags we need to decode this image with.
1238 14 : DecoderFlags decoderFlags = DefaultDecoderFlags();
1239 14 : if (aFlags & FLAG_ASYNC_NOTIFY) {
1240 3 : decoderFlags |= DecoderFlags::ASYNC_NOTIFY;
1241 : }
1242 14 : if (mTransient) {
1243 0 : decoderFlags |= DecoderFlags::IMAGE_IS_TRANSIENT;
1244 : }
1245 14 : if (mHasBeenDecoded) {
1246 0 : decoderFlags |= DecoderFlags::IS_REDECODE;
1247 : }
1248 :
1249 14 : SurfaceFlags surfaceFlags = ToSurfaceFlags(aFlags);
1250 14 : if (IsOpaque()) {
1251 : // If there's no transparency, it doesn't matter whether we premultiply
1252 : // alpha or not.
1253 0 : surfaceFlags &= ~SurfaceFlags::NO_PREMULTIPLY_ALPHA;
1254 : }
1255 :
1256 : // Create a decoder.
1257 28 : RefPtr<IDecodingTask> task;
1258 14 : if (mAnimationState && aPlaybackType == PlaybackType::eAnimated) {
1259 4 : task = DecoderFactory::CreateAnimationDecoder(mDecoderType, WrapNotNull(this),
1260 : mSourceBuffer, mSize,
1261 2 : decoderFlags, surfaceFlags);
1262 : // We pass false for aAllowInvalidation because we may be asked to use
1263 : // async notifications. Any potential invalidation here will be sent when
1264 : // RequestRefresh is called, or NotifyDecodeComplete.
1265 : #ifdef DEBUG
1266 : gfx::IntRect rect =
1267 : #endif
1268 2 : mAnimationState->UpdateState(mAnimationFinished, this, mSize, false);
1269 2 : MOZ_ASSERT(rect.IsEmpty());
1270 : } else {
1271 24 : task = DecoderFactory::CreateDecoder(mDecoderType, WrapNotNull(this),
1272 : mSourceBuffer, mSize, aSize,
1273 12 : decoderFlags, surfaceFlags);
1274 : }
1275 :
1276 : // Make sure DecoderFactory was able to create a decoder successfully.
1277 14 : if (!task) {
1278 0 : return false;
1279 : }
1280 :
1281 14 : mDecodeCount++;
1282 :
1283 : // We're ready to decode; start the decoder.
1284 14 : return LaunchDecodingTask(task, this, aFlags, mAllSourceData);
1285 : }
1286 :
1287 : NS_IMETHODIMP
1288 19 : RasterImage::DecodeMetadata(uint32_t aFlags)
1289 : {
1290 19 : if (mError) {
1291 0 : return NS_ERROR_FAILURE;
1292 : }
1293 :
1294 19 : MOZ_ASSERT(!mHasSize, "Should not do unnecessary metadata decodes");
1295 :
1296 : // Create a decoder.
1297 : RefPtr<IDecodingTask> task =
1298 38 : DecoderFactory::CreateMetadataDecoder(mDecoderType, WrapNotNull(this),
1299 38 : mSourceBuffer);
1300 :
1301 : // Make sure DecoderFactory was able to create a decoder successfully.
1302 19 : if (!task) {
1303 0 : return NS_ERROR_FAILURE;
1304 : }
1305 :
1306 : // We're ready to decode; start the decoder.
1307 19 : LaunchDecodingTask(task, this, aFlags, mAllSourceData);
1308 19 : return NS_OK;
1309 : }
1310 :
1311 : void
1312 0 : RasterImage::RecoverFromInvalidFrames(const IntSize& aSize, uint32_t aFlags)
1313 : {
1314 0 : if (!mHasSize) {
1315 0 : return;
1316 : }
1317 :
1318 0 : NS_WARNING("A RasterImage's frames became invalid. Attempting to recover...");
1319 :
1320 : // Discard all existing frames, since they're probably all now invalid.
1321 0 : SurfaceCache::RemoveImage(ImageKey(this));
1322 :
1323 : // Relock the image if it's supposed to be locked.
1324 0 : if (mLockCount > 0) {
1325 0 : SurfaceCache::LockImage(ImageKey(this));
1326 : }
1327 :
1328 : // Animated images require some special handling, because we normally require
1329 : // that they never be discarded.
1330 0 : if (mAnimationState) {
1331 0 : Decode(mSize, aFlags | FLAG_SYNC_DECODE, PlaybackType::eAnimated);
1332 0 : ResetAnimation();
1333 0 : return;
1334 : }
1335 :
1336 : // For non-animated images, it's fine to recover using an async decode.
1337 0 : Decode(aSize, aFlags, PlaybackType::eStatic);
1338 : }
1339 :
1340 : static bool
1341 92 : HaveSkia()
1342 : {
1343 : #ifdef MOZ_ENABLE_SKIA
1344 92 : return true;
1345 : #else
1346 : return false;
1347 : #endif
1348 : }
1349 :
1350 : bool
1351 92 : RasterImage::CanDownscaleDuringDecode(const IntSize& aSize, uint32_t aFlags)
1352 : {
1353 : // Check basic requirements: downscale-during-decode is enabled, Skia is
1354 : // available, this image isn't transient, we have all the source data and know
1355 : // our size, and the flags allow us to do it.
1356 368 : if (!mHasSize || mTransient || !HaveSkia() ||
1357 276 : !gfxPrefs::ImageDownscaleDuringDecodeEnabled() ||
1358 92 : !(aFlags & imgIContainer::FLAG_HIGH_QUALITY_SCALING)) {
1359 22 : return false;
1360 : }
1361 :
1362 : // We don't downscale animated images during decode.
1363 70 : if (mAnimationState) {
1364 22 : return false;
1365 : }
1366 :
1367 : // Never upscale.
1368 48 : if (aSize.width >= mSize.width || aSize.height >= mSize.height) {
1369 48 : return false;
1370 : }
1371 :
1372 : // Zero or negative width or height is unacceptable.
1373 0 : if (aSize.width < 1 || aSize.height < 1) {
1374 0 : return false;
1375 : }
1376 :
1377 : // There's no point in scaling if we can't store the result.
1378 0 : if (!SurfaceCache::CanHold(aSize)) {
1379 0 : return false;
1380 : }
1381 :
1382 0 : return true;
1383 : }
1384 :
1385 : DrawResult
1386 35 : RasterImage::DrawInternal(DrawableSurface&& aSurface,
1387 : gfxContext* aContext,
1388 : const IntSize& aSize,
1389 : const ImageRegion& aRegion,
1390 : SamplingFilter aSamplingFilter,
1391 : uint32_t aFlags,
1392 : float aOpacity)
1393 : {
1394 70 : gfxContextMatrixAutoSaveRestore saveMatrix(aContext);
1395 35 : ImageRegion region(aRegion);
1396 35 : bool frameIsFinished = aSurface->IsFinished();
1397 :
1398 : #ifdef DEBUG
1399 : // Record the image drawing for startup performance testing.
1400 35 : if (NS_IsMainThread()) {
1401 70 : nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
1402 35 : NS_WARNING_ASSERTION(obs, "Can't get an observer service handle");
1403 35 : if (obs) {
1404 70 : nsCOMPtr<nsIURI> imageURI = mURI->ToIURI();
1405 70 : nsAutoCString spec;
1406 35 : imageURI->GetSpec(spec);
1407 35 : obs->NotifyObservers(nullptr, "image-drawing", NS_ConvertUTF8toUTF16(spec).get());
1408 : }
1409 : }
1410 : #endif
1411 :
1412 : // By now we may have a frame with the requested size. If not, we need to
1413 : // adjust the drawing parameters accordingly.
1414 35 : IntSize finalSize = aSurface->GetImageSize();
1415 35 : bool couldRedecodeForBetterFrame = false;
1416 35 : if (finalSize != aSize) {
1417 0 : gfx::Size scale(double(aSize.width) / finalSize.width,
1418 0 : double(aSize.height) / finalSize.height);
1419 0 : aContext->Multiply(gfxMatrix::Scaling(scale.width, scale.height));
1420 0 : region.Scale(1.0 / scale.width, 1.0 / scale.height);
1421 :
1422 0 : couldRedecodeForBetterFrame = CanDownscaleDuringDecode(aSize, aFlags);
1423 : }
1424 :
1425 35 : if (!aSurface->Draw(aContext, region, aSamplingFilter, aFlags, aOpacity)) {
1426 0 : RecoverFromInvalidFrames(aSize, aFlags);
1427 0 : return DrawResult::TEMPORARY_ERROR;
1428 : }
1429 35 : if (!frameIsFinished) {
1430 0 : return DrawResult::INCOMPLETE;
1431 : }
1432 35 : if (couldRedecodeForBetterFrame) {
1433 0 : return DrawResult::WRONG_SIZE;
1434 : }
1435 35 : return DrawResult::SUCCESS;
1436 : }
1437 :
1438 : //******************************************************************************
1439 : NS_IMETHODIMP_(DrawResult)
1440 35 : RasterImage::Draw(gfxContext* aContext,
1441 : const IntSize& aSize,
1442 : const ImageRegion& aRegion,
1443 : uint32_t aWhichFrame,
1444 : SamplingFilter aSamplingFilter,
1445 : const Maybe<SVGImageContext>& /*aSVGContext - ignored*/,
1446 : uint32_t aFlags,
1447 : float aOpacity)
1448 : {
1449 35 : if (aWhichFrame > FRAME_MAX_VALUE) {
1450 0 : return DrawResult::BAD_ARGS;
1451 : }
1452 :
1453 35 : if (mError) {
1454 0 : return DrawResult::BAD_IMAGE;
1455 : }
1456 :
1457 : // Illegal -- you can't draw with non-default decode flags.
1458 : // (Disabling colorspace conversion might make sense to allow, but
1459 : // we don't currently.)
1460 35 : if (ToSurfaceFlags(aFlags) != DefaultSurfaceFlags()) {
1461 0 : return DrawResult::BAD_ARGS;
1462 : }
1463 :
1464 35 : if (!aContext) {
1465 0 : return DrawResult::BAD_ARGS;
1466 : }
1467 :
1468 35 : if (mAnimationConsumers == 0) {
1469 0 : SendOnUnlockedDraw(aFlags);
1470 : }
1471 :
1472 :
1473 : // If we're not using SamplingFilter::GOOD, we shouldn't high-quality scale or
1474 : // downscale during decode.
1475 : uint32_t flags = aSamplingFilter == SamplingFilter::GOOD
1476 35 : ? aFlags
1477 35 : : aFlags & ~FLAG_HIGH_QUALITY_SCALING;
1478 :
1479 : DrawableSurface surface =
1480 70 : LookupFrame(aSize, flags, ToPlaybackType(aWhichFrame));
1481 35 : if (!surface) {
1482 : // Getting the frame (above) touches the image and kicks off decoding.
1483 0 : if (mDrawStartTime.IsNull()) {
1484 0 : mDrawStartTime = TimeStamp::Now();
1485 : }
1486 0 : return DrawResult::NOT_READY;
1487 : }
1488 :
1489 35 : bool shouldRecordTelemetry = !mDrawStartTime.IsNull() &&
1490 35 : surface->IsFinished();
1491 :
1492 35 : auto result = DrawInternal(Move(surface), aContext, aSize,
1493 35 : aRegion, aSamplingFilter, flags, aOpacity);
1494 :
1495 35 : if (shouldRecordTelemetry) {
1496 0 : TimeDuration drawLatency = TimeStamp::Now() - mDrawStartTime;
1497 0 : Telemetry::Accumulate(Telemetry::IMAGE_DECODE_ON_DRAW_LATENCY,
1498 0 : int32_t(drawLatency.ToMicroseconds()));
1499 0 : if (mAnimationState) {
1500 0 : Telemetry::Accumulate(Telemetry::IMAGE_ANIMATED_DECODE_ON_DRAW_LATENCY,
1501 0 : int32_t(drawLatency.ToMicroseconds()));
1502 :
1503 : }
1504 0 : mDrawStartTime = TimeStamp();
1505 : }
1506 :
1507 35 : return result;
1508 : }
1509 :
1510 : //******************************************************************************
1511 :
1512 : NS_IMETHODIMP
1513 56 : RasterImage::LockImage()
1514 : {
1515 56 : MOZ_ASSERT(NS_IsMainThread(),
1516 : "Main thread to encourage serialization with UnlockImage");
1517 56 : if (mError) {
1518 1 : return NS_ERROR_FAILURE;
1519 : }
1520 :
1521 : // Increment the lock count
1522 55 : mLockCount++;
1523 :
1524 : // Lock this image's surfaces in the SurfaceCache.
1525 55 : if (mLockCount == 1) {
1526 0 : SurfaceCache::LockImage(ImageKey(this));
1527 : }
1528 :
1529 55 : return NS_OK;
1530 : }
1531 :
1532 : //******************************************************************************
1533 :
1534 : NS_IMETHODIMP
1535 33 : RasterImage::UnlockImage()
1536 : {
1537 33 : MOZ_ASSERT(NS_IsMainThread(),
1538 : "Main thread to encourage serialization with LockImage");
1539 33 : if (mError) {
1540 1 : return NS_ERROR_FAILURE;
1541 : }
1542 :
1543 : // It's an error to call this function if the lock count is 0
1544 32 : MOZ_ASSERT(mLockCount > 0,
1545 : "Calling UnlockImage with mLockCount == 0!");
1546 32 : if (mLockCount == 0) {
1547 0 : return NS_ERROR_ABORT;
1548 : }
1549 :
1550 : // Decrement our lock count
1551 32 : mLockCount--;
1552 :
1553 : // Unlock this image's surfaces in the SurfaceCache.
1554 32 : if (mLockCount == 0 ) {
1555 0 : SurfaceCache::UnlockImage(ImageKey(this));
1556 : }
1557 :
1558 32 : return NS_OK;
1559 : }
1560 :
1561 : //******************************************************************************
1562 :
1563 : NS_IMETHODIMP
1564 0 : RasterImage::RequestDiscard()
1565 : {
1566 0 : if (mDiscardable && // Enabled at creation time...
1567 0 : mLockCount == 0 && // ...not temporarily disabled...
1568 0 : CanDiscard()) {
1569 0 : Discard();
1570 : }
1571 :
1572 0 : return NS_OK;
1573 : }
1574 :
1575 : // Indempotent error flagging routine. If a decoder is open, shuts it down.
1576 : void
1577 1 : RasterImage::DoError()
1578 : {
1579 : // If we've flagged an error before, we have nothing to do
1580 1 : if (mError) {
1581 1 : return;
1582 : }
1583 :
1584 : // We can't safely handle errors off-main-thread, so dispatch a worker to
1585 : // do it.
1586 0 : if (!NS_IsMainThread()) {
1587 0 : HandleErrorWorker::DispatchIfNeeded(this);
1588 0 : return;
1589 : }
1590 :
1591 : // Put the container in an error state.
1592 0 : mError = true;
1593 :
1594 : // Stop animation and release our FrameAnimator.
1595 0 : if (mAnimating) {
1596 0 : StopAnimation();
1597 : }
1598 0 : mAnimationState = Nothing();
1599 0 : mFrameAnimator = nullptr;
1600 :
1601 : // Release all locks.
1602 0 : mLockCount = 0;
1603 0 : SurfaceCache::UnlockImage(ImageKey(this));
1604 :
1605 : // Release all frames from the surface cache.
1606 0 : SurfaceCache::RemoveImage(ImageKey(this));
1607 :
1608 : // Invalidate to get rid of any partially-drawn image content.
1609 0 : NotifyProgress(NoProgress, IntRect(0, 0, mSize.width, mSize.height));
1610 :
1611 0 : MOZ_LOG(gImgLog, LogLevel::Error,
1612 : ("RasterImage: [this=%p] Error detected for image\n", this));
1613 : }
1614 :
1615 : /* static */ void
1616 0 : RasterImage::HandleErrorWorker::DispatchIfNeeded(RasterImage* aImage)
1617 : {
1618 0 : RefPtr<HandleErrorWorker> worker = new HandleErrorWorker(aImage);
1619 0 : NS_DispatchToMainThread(worker);
1620 0 : }
1621 :
1622 0 : RasterImage::HandleErrorWorker::HandleErrorWorker(RasterImage* aImage)
1623 : : Runnable("image::RasterImage::HandleErrorWorker")
1624 0 : , mImage(aImage)
1625 : {
1626 0 : MOZ_ASSERT(mImage, "Should have image");
1627 0 : }
1628 :
1629 : NS_IMETHODIMP
1630 0 : RasterImage::HandleErrorWorker::Run()
1631 : {
1632 0 : mImage->DoError();
1633 :
1634 0 : return NS_OK;
1635 : }
1636 :
1637 : bool
1638 16 : RasterImage::ShouldAnimate()
1639 : {
1640 32 : return ImageResource::ShouldAnimate() &&
1641 32 : mAnimationState &&
1642 44 : mAnimationState->KnownFrameCount() >= 1 &&
1643 28 : !mAnimationFinished;
1644 : }
1645 :
1646 : #ifdef DEBUG
1647 : NS_IMETHODIMP
1648 0 : RasterImage::GetFramesNotified(uint32_t* aFramesNotified)
1649 : {
1650 0 : NS_ENSURE_ARG_POINTER(aFramesNotified);
1651 :
1652 0 : *aFramesNotified = mFramesNotified;
1653 :
1654 0 : return NS_OK;
1655 : }
1656 : #endif
1657 :
1658 : void
1659 133 : RasterImage::NotifyProgress(Progress aProgress,
1660 : const IntRect& aInvalidRect /* = IntRect() */,
1661 : const Maybe<uint32_t>& aFrameCount /* = Nothing() */,
1662 : DecoderFlags aDecoderFlags
1663 : /* = DefaultDecoderFlags() */,
1664 : SurfaceFlags aSurfaceFlags
1665 : /* = DefaultSurfaceFlags() */)
1666 : {
1667 133 : MOZ_ASSERT(NS_IsMainThread());
1668 :
1669 : // Ensure that we stay alive long enough to finish notifying.
1670 266 : RefPtr<RasterImage> image = this;
1671 :
1672 133 : const bool wasDefaultFlags = aSurfaceFlags == DefaultSurfaceFlags();
1673 :
1674 133 : if (!aInvalidRect.IsEmpty() && wasDefaultFlags) {
1675 : // Update our image container since we're invalidating.
1676 22 : UpdateImageContainer();
1677 : }
1678 :
1679 133 : if (!(aDecoderFlags & DecoderFlags::FIRST_FRAME_ONLY)) {
1680 : // We may have decoded new animation frames; update our animation state.
1681 121 : MOZ_ASSERT_IF(aFrameCount && *aFrameCount > 1, mAnimationState || mError);
1682 121 : if (mAnimationState && aFrameCount) {
1683 36 : mAnimationState->UpdateKnownFrameCount(*aFrameCount);
1684 : }
1685 :
1686 : // If we should start animating right now, do so.
1687 294 : if (mAnimationState && aFrameCount == Some(1u) &&
1688 244 : mPendingAnimation && ShouldAnimate()) {
1689 0 : StartAnimation();
1690 : }
1691 : }
1692 :
1693 : // Tell the observers what happened.
1694 133 : image->mProgressTracker->SyncNotifyProgress(aProgress, aInvalidRect);
1695 133 : }
1696 :
1697 : void
1698 33 : RasterImage::NotifyDecodeComplete(const DecoderFinalStatus& aStatus,
1699 : const ImageMetadata& aMetadata,
1700 : const DecoderTelemetry& aTelemetry,
1701 : Progress aProgress,
1702 : const IntRect& aInvalidRect,
1703 : const Maybe<uint32_t>& aFrameCount,
1704 : DecoderFlags aDecoderFlags,
1705 : SurfaceFlags aSurfaceFlags)
1706 : {
1707 33 : MOZ_ASSERT(NS_IsMainThread());
1708 :
1709 : // If the decoder detected an error, log it to the error console.
1710 33 : if (aStatus.mShouldReportError) {
1711 0 : ReportDecoderError();
1712 : }
1713 :
1714 : // Record all the metadata the decoder gathered about this image.
1715 33 : bool metadataOK = SetMetadata(aMetadata, aStatus.mWasMetadataDecode);
1716 33 : if (!metadataOK) {
1717 : // This indicates a serious error that requires us to discard all existing
1718 : // surfaces and redecode to recover. We'll drop the results from this
1719 : // decoder on the floor, since they aren't valid.
1720 0 : RecoverFromInvalidFrames(mSize,
1721 0 : FromSurfaceFlags(aSurfaceFlags));
1722 0 : return;
1723 : }
1724 :
1725 33 : MOZ_ASSERT(mError || mHasSize || !aMetadata.HasSize(),
1726 : "SetMetadata should've gotten a size");
1727 :
1728 33 : if (!aStatus.mWasMetadataDecode && aStatus.mFinished) {
1729 : // Flag that we've been decoded before.
1730 14 : mHasBeenDecoded = true;
1731 : }
1732 :
1733 : // Send out any final notifications.
1734 : NotifyProgress(aProgress, aInvalidRect, aFrameCount,
1735 33 : aDecoderFlags, aSurfaceFlags);
1736 :
1737 120 : if (!(aDecoderFlags & DecoderFlags::FIRST_FRAME_ONLY) &&
1738 101 : mHasBeenDecoded && mAnimationState) {
1739 : // We've finished a full decode of all animation frames and our AnimationState
1740 : // has been notified about them all, so let it know not to expect anymore.
1741 2 : mAnimationState->NotifyDecodeComplete();
1742 2 : gfx::IntRect rect = mAnimationState->UpdateState(mAnimationFinished, this, mSize);
1743 2 : if (!rect.IsEmpty()) {
1744 0 : NotifyProgress(NoProgress, rect);
1745 : }
1746 : }
1747 :
1748 : // Do some telemetry if this isn't a metadata decode.
1749 33 : if (!aStatus.mWasMetadataDecode) {
1750 14 : if (aTelemetry.mChunkCount) {
1751 14 : Telemetry::Accumulate(Telemetry::IMAGE_DECODE_CHUNKS, aTelemetry.mChunkCount);
1752 : }
1753 :
1754 14 : if (aStatus.mFinished) {
1755 14 : Telemetry::Accumulate(Telemetry::IMAGE_DECODE_TIME,
1756 28 : int32_t(aTelemetry.mDecodeTime.ToMicroseconds()));
1757 :
1758 14 : if (mAnimationState) {
1759 2 : Telemetry::Accumulate(Telemetry::IMAGE_ANIMATED_DECODE_TIME,
1760 4 : int32_t(aTelemetry.mDecodeTime.ToMicroseconds()));
1761 : }
1762 :
1763 14 : if (aTelemetry.mSpeedHistogram) {
1764 14 : Telemetry::Accumulate(*aTelemetry.mSpeedHistogram, aTelemetry.Speed());
1765 : }
1766 : }
1767 : }
1768 :
1769 : // Only act on errors if we have no usable frames from the decoder.
1770 33 : if (aStatus.mHadError &&
1771 0 : (!mAnimationState || mAnimationState->KnownFrameCount() == 0)) {
1772 0 : DoError();
1773 33 : } else if (aStatus.mWasMetadataDecode && !mHasSize) {
1774 0 : DoError();
1775 : }
1776 :
1777 : // XXX(aosmond): Can we get this far without mFinished == true?
1778 33 : if (aStatus.mFinished && aStatus.mWasMetadataDecode) {
1779 : // If we were waiting to fire the load event, go ahead and fire it now.
1780 19 : if (mLoadProgress) {
1781 19 : NotifyForLoadEvent(*mLoadProgress);
1782 19 : mLoadProgress = Nothing();
1783 19 : NotifyProgress(FLAG_ONLOAD_UNBLOCKED);
1784 : }
1785 :
1786 : // If we were a metadata decode and a full decode was requested, do it.
1787 19 : if (mWantFullDecode) {
1788 11 : mWantFullDecode = false;
1789 11 : RequestDecodeForSize(mSize, DECODE_FLAGS_DEFAULT);
1790 : }
1791 : }
1792 : }
1793 :
1794 : void
1795 0 : RasterImage::ReportDecoderError()
1796 : {
1797 : nsCOMPtr<nsIConsoleService> consoleService =
1798 0 : do_GetService(NS_CONSOLESERVICE_CONTRACTID);
1799 : nsCOMPtr<nsIScriptError> errorObject =
1800 0 : do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
1801 :
1802 0 : if (consoleService && errorObject) {
1803 0 : nsAutoString msg(NS_LITERAL_STRING("Image corrupt or truncated."));
1804 0 : nsAutoString src;
1805 0 : if (GetURI()) {
1806 0 : nsCString uri;
1807 0 : if (GetURI()->GetSpecTruncatedTo1k(uri) == ImageURL::TruncatedTo1k) {
1808 0 : msg += NS_LITERAL_STRING(" URI in this note truncated due to length.");
1809 : }
1810 0 : src = NS_ConvertUTF8toUTF16(uri);
1811 : }
1812 0 : if (NS_SUCCEEDED(errorObject->InitWithWindowID(
1813 : msg,
1814 : src,
1815 : EmptyString(), 0, 0, nsIScriptError::errorFlag,
1816 : "Image", InnerWindowID()
1817 : ))) {
1818 0 : consoleService->LogMessage(errorObject);
1819 : }
1820 : }
1821 0 : }
1822 :
1823 : already_AddRefed<imgIContainer>
1824 0 : RasterImage::Unwrap()
1825 : {
1826 0 : nsCOMPtr<imgIContainer> self(this);
1827 0 : return self.forget();
1828 : }
1829 :
1830 : void
1831 14 : RasterImage::PropagateUseCounters(nsIDocument*)
1832 : {
1833 : // No use counters.
1834 14 : }
1835 :
1836 : IntSize
1837 35 : RasterImage::OptimalImageSizeForDest(const gfxSize& aDest, uint32_t aWhichFrame,
1838 : SamplingFilter aSamplingFilter, uint32_t aFlags)
1839 : {
1840 35 : MOZ_ASSERT(aDest.width >= 0 || ceil(aDest.width) <= INT32_MAX ||
1841 : aDest.height >= 0 || ceil(aDest.height) <= INT32_MAX,
1842 : "Unexpected destination size");
1843 :
1844 35 : if (mSize.IsEmpty() || aDest.IsEmpty()) {
1845 0 : return IntSize(0, 0);
1846 : }
1847 :
1848 35 : IntSize destSize = IntSize::Ceil(aDest.width, aDest.height);
1849 :
1850 70 : if (aSamplingFilter == SamplingFilter::GOOD &&
1851 35 : CanDownscaleDuringDecode(destSize, aFlags)) {
1852 0 : return destSize;
1853 : }
1854 :
1855 : // We can't scale to this size. Use our intrinsic size for now.
1856 35 : return mSize;
1857 : }
1858 :
1859 : } // namespace image
1860 : } // namespace mozilla
|