1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
9 #include "DecodePool.h"
11 #include "mozilla/gfx/2D.h"
12 #include "mozilla/gfx/Logging.h"
13 #include "mozilla/RefPtr.h"
15 #include "mozilla/dom/Document.h"
17 #include "imgLoader.h"
18 #include "imgICache.h"
19 #include "imgIContainer.h"
20 #include "imgIEncoder.h"
21 #include "nsNetUtil.h" // for NS_NewBufferedInputStream
22 #include "nsStreamUtils.h"
23 #include "nsStringStream.h"
24 #include "nsContentUtils.h"
25 #include "nsProxyRelease.h"
26 #include "nsIStreamListener.h"
27 #include "ImageFactory.h"
29 #include "IProgressObserver.h"
30 #include "ScriptedNotificationObserver.h"
31 #include "imgIScriptedNotificationObserver.h"
32 #include "gfxPlatform.h"
33 #include "js/ArrayBuffer.h"
34 #include "js/RootingAPI.h" // JS::{Handle,Rooted}
35 #include "js/Value.h" // JS::Value
37 using namespace mozilla::gfx
;
44 static nsresult
sniff_mimetype_callback(nsIInputStream
* in
, void* data
,
45 const char* fromRawSegment
,
46 uint32_t toOffset
, uint32_t count
,
47 uint32_t* writeCount
) {
48 nsCString
* mimeType
= static_cast<nsCString
*>(data
);
49 MOZ_ASSERT(mimeType
, "mimeType is null!");
52 imgLoader::GetMimeTypeFromContent(fromRawSegment
, count
, *mimeType
);
56 return NS_ERROR_FAILURE
;
59 // Provides WeakPtr for imgINotificationObserver
60 class NotificationObserverWrapper
61 : public imgINotificationObserver
,
62 public mozilla::SupportsWeakPtr
<NotificationObserverWrapper
> {
65 NS_FORWARD_IMGINOTIFICATIONOBSERVER(mObserver
->)
66 MOZ_DECLARE_WEAKREFERENCE_TYPENAME(nsGeolocationRequest
)
68 explicit NotificationObserverWrapper(imgINotificationObserver
* observer
)
69 : mObserver(observer
) {}
72 virtual ~NotificationObserverWrapper() = default;
73 nsCOMPtr
<imgINotificationObserver
> mObserver
;
76 NS_IMPL_ISUPPORTS(NotificationObserverWrapper
, imgINotificationObserver
)
78 class ImageDecoderListener final
: public nsIStreamListener
,
79 public IProgressObserver
,
80 public imgIContainer
{
84 ImageDecoderListener(nsIURI
* aURI
, imgIContainerCallback
* aCallback
,
85 imgINotificationObserver
* aObserver
)
89 mObserver(aObserver
? new NotificationObserverWrapper(aObserver
)
91 MOZ_ASSERT(NS_IsMainThread());
95 OnDataAvailable(nsIRequest
* aRequest
, nsIInputStream
* aInputStream
,
96 uint64_t aOffset
, uint32_t aCount
) override
{
98 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aRequest
);
101 channel
->GetContentType(mimeType
);
104 // Look at the first few bytes and see if we can tell what the data is
105 // from that since servers tend to lie. :(
107 aInputStream
->ReadSegments(sniff_mimetype_callback
, &mimeType
, aCount
,
111 RefPtr
<ProgressTracker
> tracker
= new ProgressTracker();
113 tracker
->AddObserver(this);
116 mImage
= ImageFactory::CreateImage(channel
, tracker
, mimeType
, mURI
,
117 /* aIsMultiPart */ false, 0);
119 if (mImage
->HasError()) {
120 return NS_ERROR_FAILURE
;
124 return mImage
->OnImageDataAvailable(aRequest
, nullptr, aInputStream
,
129 OnStartRequest(nsIRequest
* aRequest
) override
{ return NS_OK
; }
132 OnStopRequest(nsIRequest
* aRequest
, nsresult aStatus
) override
{
133 // Depending on the error, we might not have received any data yet, in which
134 // case we would not have an |mImage|
136 mImage
->OnImageDataComplete(aRequest
, nullptr, aStatus
, true);
139 nsCOMPtr
<imgIContainer
> container
;
140 if (NS_SUCCEEDED(aStatus
)) {
144 mCallback
->OnImageReady(container
, aStatus
);
148 virtual void Notify(int32_t aType
,
149 const nsIntRect
* aRect
= nullptr) override
{
151 mObserver
->Notify(nullptr, aType
, aRect
);
155 virtual void OnLoadComplete(bool aLastPart
) override
{}
157 // Other notifications are ignored.
158 virtual void SetHasImage() override
{}
159 virtual bool NotificationsDeferred() const override
{ return false; }
160 virtual void MarkPendingNotify() override
{}
161 virtual void ClearPendingNotify() override
{}
164 NS_FORWARD_IMGICONTAINER(mImage
->)
166 nsresult
GetNativeSizes(nsTArray
<nsIntSize
>& aNativeSizes
) const override
{
167 return mImage
->GetNativeSizes(aNativeSizes
);
170 size_t GetNativeSizesLength() const override
{
171 return mImage
->GetNativeSizesLength();
175 virtual ~ImageDecoderListener() = default;
177 nsCOMPtr
<nsIURI
> mURI
;
178 RefPtr
<image::Image
> mImage
;
179 nsCOMPtr
<imgIContainerCallback
> mCallback
;
180 WeakPtr
<NotificationObserverWrapper
> mObserver
;
183 NS_IMPL_ISUPPORTS(ImageDecoderListener
, nsIStreamListener
, imgIContainer
)
185 class ImageDecoderHelper final
: public Runnable
,
186 public nsIInputStreamCallback
{
188 NS_DECL_ISUPPORTS_INHERITED
190 ImageDecoderHelper(already_AddRefed
<image::Image
> aImage
,
191 already_AddRefed
<nsIInputStream
> aInputStream
,
192 nsIEventTarget
* aEventTarget
,
193 imgIContainerCallback
* aCallback
,
194 nsIEventTarget
* aCallbackEventTarget
)
195 : Runnable("ImageDecoderHelper"),
196 mImage(std::move(aImage
)),
197 mInputStream(std::move(aInputStream
)),
198 mEventTarget(aEventTarget
),
199 mCallback(aCallback
),
200 mCallbackEventTarget(aCallbackEventTarget
),
202 MOZ_ASSERT(NS_IsMainThread());
207 // This runnable is dispatched on the Image thread when reading data, but
208 // at the end, it goes back to the main-thread in order to complete the
210 if (NS_IsMainThread()) {
211 // Let the Image know we've sent all the data.
212 mImage
->OnImageDataComplete(nullptr, nullptr, mStatus
, true);
214 RefPtr
<ProgressTracker
> tracker
= mImage
->GetProgressTracker();
215 tracker
->SyncNotifyProgress(FLAG_LOAD_COMPLETE
);
217 nsCOMPtr
<imgIContainer
> container
;
218 if (NS_SUCCEEDED(mStatus
)) {
222 mCallback
->OnImageReady(container
, mStatus
);
227 nsresult rv
= mInputStream
->Available(&length
);
228 if (rv
== NS_BASE_STREAM_CLOSED
) {
229 return OperationCompleted(NS_OK
);
232 if (NS_WARN_IF(NS_FAILED(rv
))) {
233 return OperationCompleted(rv
);
236 // Nothing else to read, but maybe we just need to wait.
238 nsCOMPtr
<nsIAsyncInputStream
> asyncInputStream
=
239 do_QueryInterface(mInputStream
);
240 if (asyncInputStream
) {
241 rv
= asyncInputStream
->AsyncWait(this, 0, 0, mEventTarget
);
242 if (NS_WARN_IF(NS_FAILED(rv
))) {
243 return OperationCompleted(rv
);
248 // We really have nothing else to read.
250 return OperationCompleted(NS_OK
);
254 // Send the source data to the Image.
255 rv
= mImage
->OnImageDataAvailable(nullptr, nullptr, mInputStream
, 0,
257 if (NS_WARN_IF(NS_FAILED(rv
))) {
258 return OperationCompleted(rv
);
261 rv
= mEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
);
262 if (NS_WARN_IF(NS_FAILED(rv
))) {
263 return OperationCompleted(rv
);
270 OnInputStreamReady(nsIAsyncInputStream
* aAsyncInputStream
) override
{
271 MOZ_ASSERT(!NS_IsMainThread());
275 nsresult
OperationCompleted(nsresult aStatus
) {
276 MOZ_ASSERT(!NS_IsMainThread());
279 mCallbackEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
);
284 ~ImageDecoderHelper() {
285 NS_ReleaseOnMainThreadSystemGroup("ImageDecoderHelper::mImage",
287 NS_ReleaseOnMainThreadSystemGroup("ImageDecoderHelper::mCallback",
291 RefPtr
<image::Image
> mImage
;
293 nsCOMPtr
<nsIInputStream
> mInputStream
;
294 nsCOMPtr
<nsIEventTarget
> mEventTarget
;
295 nsCOMPtr
<imgIContainerCallback
> mCallback
;
296 nsCOMPtr
<nsIEventTarget
> mCallbackEventTarget
;
301 NS_IMPL_ISUPPORTS_INHERITED(ImageDecoderHelper
, Runnable
,
302 nsIInputStreamCallback
)
306 /* ========== imgITools implementation ========== */
308 NS_IMPL_ISUPPORTS(imgTools
, imgITools
)
310 imgTools::imgTools() { /* member initializers and constructor code */
313 imgTools::~imgTools() { /* destructor code */
317 imgTools::DecodeImageFromArrayBuffer(JS::Handle
<JS::Value
> aArrayBuffer
,
318 const nsACString
& aMimeType
,
320 imgIContainer
** aContainer
) {
321 if (!aArrayBuffer
.isObject()) {
322 return NS_ERROR_FAILURE
;
325 JS::Rooted
<JSObject
*> obj(aCx
,
326 JS::UnwrapArrayBuffer(&aArrayBuffer
.toObject()));
328 return NS_ERROR_FAILURE
;
331 uint8_t* bufferData
= nullptr;
332 uint32_t bufferLength
= 0;
333 bool isSharedMemory
= false;
335 JS::GetArrayBufferLengthAndData(obj
, &bufferLength
, &isSharedMemory
,
337 return DecodeImageFromBuffer((char*)bufferData
, bufferLength
, aMimeType
,
342 imgTools::DecodeImageFromBuffer(const char* aBuffer
, uint32_t aSize
,
343 const nsACString
& aMimeType
,
344 imgIContainer
** aContainer
) {
345 MOZ_ASSERT(NS_IsMainThread());
347 NS_ENSURE_ARG_POINTER(aBuffer
);
349 // Create a new image container to hold the decoded data.
350 nsAutoCString
mimeType(aMimeType
);
351 RefPtr
<image::Image
> image
=
352 ImageFactory::CreateAnonymousImage(mimeType
, aSize
);
353 RefPtr
<ProgressTracker
> tracker
= image
->GetProgressTracker();
355 if (image
->HasError()) {
356 return NS_ERROR_FAILURE
;
359 // Let's create a temporary inputStream.
360 nsCOMPtr
<nsIInputStream
> stream
;
361 nsresult rv
= NS_NewByteInputStream(
362 getter_AddRefs(stream
), MakeSpan(aBuffer
, aSize
), NS_ASSIGNMENT_DEPEND
);
363 NS_ENSURE_SUCCESS(rv
, rv
);
365 MOZ_ASSERT(NS_InputStreamIsBuffered(stream
));
367 rv
= image
->OnImageDataAvailable(nullptr, nullptr, stream
, 0, aSize
);
368 NS_ENSURE_SUCCESS(rv
, rv
);
370 // Let the Image know we've sent all the data.
371 rv
= image
->OnImageDataComplete(nullptr, nullptr, NS_OK
, true);
372 tracker
->SyncNotifyProgress(FLAG_LOAD_COMPLETE
);
373 NS_ENSURE_SUCCESS(rv
, rv
);
376 image
.forget(aContainer
);
381 imgTools::DecodeImageFromChannelAsync(nsIURI
* aURI
, nsIChannel
* aChannel
,
382 imgIContainerCallback
* aCallback
,
383 imgINotificationObserver
* aObserver
) {
384 MOZ_ASSERT(NS_IsMainThread());
386 NS_ENSURE_ARG_POINTER(aURI
);
387 NS_ENSURE_ARG_POINTER(aChannel
);
388 NS_ENSURE_ARG_POINTER(aCallback
);
390 RefPtr
<ImageDecoderListener
> listener
=
391 new ImageDecoderListener(aURI
, aCallback
, aObserver
);
393 return aChannel
->AsyncOpen(listener
);
397 imgTools::DecodeImageAsync(nsIInputStream
* aInStr
, const nsACString
& aMimeType
,
398 imgIContainerCallback
* aCallback
,
399 nsIEventTarget
* aEventTarget
) {
400 MOZ_ASSERT(NS_IsMainThread());
402 NS_ENSURE_ARG_POINTER(aInStr
);
403 NS_ENSURE_ARG_POINTER(aCallback
);
404 NS_ENSURE_ARG_POINTER(aEventTarget
);
408 // Let's continuing the reading on a separate thread.
409 DecodePool
* decodePool
= DecodePool::Singleton();
410 MOZ_ASSERT(decodePool
);
412 RefPtr
<nsIEventTarget
> target
= decodePool
->GetIOEventTarget();
413 NS_ENSURE_TRUE(target
, NS_ERROR_FAILURE
);
415 // Prepare the input stream.
416 nsCOMPtr
<nsIInputStream
> stream
= aInStr
;
417 if (!NS_InputStreamIsBuffered(aInStr
)) {
418 nsCOMPtr
<nsIInputStream
> bufStream
;
419 rv
= NS_NewBufferedInputStream(getter_AddRefs(bufStream
), stream
.forget(),
421 NS_ENSURE_SUCCESS(rv
, rv
);
422 stream
= bufStream
.forget();
425 // Create a new image container to hold the decoded data.
426 nsAutoCString
mimeType(aMimeType
);
427 RefPtr
<image::Image
> image
= ImageFactory::CreateAnonymousImage(mimeType
, 0);
430 if (image
->HasError()) {
431 return NS_ERROR_FAILURE
;
434 RefPtr
<ImageDecoderHelper
> helper
= new ImageDecoderHelper(
435 image
.forget(), stream
.forget(), target
, aCallback
, aEventTarget
);
436 rv
= target
->Dispatch(helper
.forget(), NS_DISPATCH_NORMAL
);
437 NS_ENSURE_SUCCESS(rv
, rv
);
443 * This takes a DataSourceSurface rather than a SourceSurface because some
444 * of the callers have a DataSourceSurface and we don't want to call
445 * GetDataSurface on such surfaces since that may incur a conversion to
446 * SurfaceType::DATA which we don't need.
448 static nsresult
EncodeImageData(DataSourceSurface
* aDataSurface
,
449 DataSourceSurface::ScopedMap
& aMap
,
450 const nsACString
& aMimeType
,
451 const nsAString
& aOutputOptions
,
452 nsIInputStream
** aStream
) {
453 MOZ_ASSERT(aDataSurface
->GetFormat() == SurfaceFormat::B8G8R8A8
||
454 aDataSurface
->GetFormat() == SurfaceFormat::B8G8R8X8
,
455 "We're assuming B8G8R8A8/X8");
457 // Get an image encoder for the media type
458 nsAutoCString
encoderCID(
459 NS_LITERAL_CSTRING("@mozilla.org/image/encoder;2?type=") + aMimeType
);
461 nsCOMPtr
<imgIEncoder
> encoder
= do_CreateInstance(encoderCID
.get());
463 return NS_IMAGELIB_ERROR_NO_ENCODER
;
466 IntSize size
= aDataSurface
->GetSize();
467 uint32_t dataLength
= aMap
.GetStride() * size
.height
;
470 nsresult rv
= encoder
->InitFromData(
471 aMap
.GetData(), dataLength
, size
.width
, size
.height
, aMap
.GetStride(),
472 imgIEncoder::INPUT_FORMAT_HOSTARGB
, aOutputOptions
);
473 NS_ENSURE_SUCCESS(rv
, rv
);
475 encoder
.forget(aStream
);
479 static nsresult
EncodeImageData(DataSourceSurface
* aDataSurface
,
480 const nsACString
& aMimeType
,
481 const nsAString
& aOutputOptions
,
482 nsIInputStream
** aStream
) {
483 DataSourceSurface::ScopedMap
map(aDataSurface
, DataSourceSurface::READ
);
484 if (!map
.IsMapped()) {
485 return NS_ERROR_FAILURE
;
488 return EncodeImageData(aDataSurface
, map
, aMimeType
, aOutputOptions
, aStream
);
492 imgTools::EncodeImage(imgIContainer
* aContainer
, const nsACString
& aMimeType
,
493 const nsAString
& aOutputOptions
,
494 nsIInputStream
** aStream
) {
495 // Use frame 0 from the image container.
496 RefPtr
<SourceSurface
> frame
= aContainer
->GetFrame(
497 imgIContainer::FRAME_FIRST
,
498 imgIContainer::FLAG_SYNC_DECODE
| imgIContainer::FLAG_ASYNC_NOTIFY
);
499 NS_ENSURE_TRUE(frame
, NS_ERROR_FAILURE
);
501 RefPtr
<DataSourceSurface
> dataSurface
;
503 if (frame
->GetFormat() == SurfaceFormat::B8G8R8A8
||
504 frame
->GetFormat() == SurfaceFormat::B8G8R8X8
) {
505 dataSurface
= frame
->GetDataSurface();
507 // Convert format to SurfaceFormat::B8G8R8A8
508 dataSurface
= gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat(
509 frame
, SurfaceFormat::B8G8R8A8
);
512 NS_ENSURE_TRUE(dataSurface
, NS_ERROR_FAILURE
);
514 return EncodeImageData(dataSurface
, aMimeType
, aOutputOptions
, aStream
);
518 imgTools::EncodeScaledImage(imgIContainer
* aContainer
,
519 const nsACString
& aMimeType
, int32_t aScaledWidth
,
520 int32_t aScaledHeight
,
521 const nsAString
& aOutputOptions
,
522 nsIInputStream
** aStream
) {
523 NS_ENSURE_ARG(aScaledWidth
>= 0 && aScaledHeight
>= 0);
525 // If no scaled size is specified, we'll just encode the image at its
526 // original size (no scaling).
527 if (aScaledWidth
== 0 && aScaledHeight
== 0) {
528 return EncodeImage(aContainer
, aMimeType
, aOutputOptions
, aStream
);
531 // Retrieve the image's size.
532 int32_t imageWidth
= 0;
533 int32_t imageHeight
= 0;
534 aContainer
->GetWidth(&imageWidth
);
535 aContainer
->GetHeight(&imageHeight
);
537 // If the given width or height is zero we'll replace it with the image's
538 // original dimensions.
539 IntSize
scaledSize(aScaledWidth
== 0 ? imageWidth
: aScaledWidth
,
540 aScaledHeight
== 0 ? imageHeight
: aScaledHeight
);
542 // Use frame 0 from the image container.
543 RefPtr
<SourceSurface
> frame
= aContainer
->GetFrameAtSize(
544 scaledSize
, imgIContainer::FRAME_FIRST
,
545 imgIContainer::FLAG_HIGH_QUALITY_SCALING
|
546 imgIContainer::FLAG_SYNC_DECODE
| imgIContainer::FLAG_ASYNC_NOTIFY
);
547 NS_ENSURE_TRUE(frame
, NS_ERROR_FAILURE
);
549 // If the given surface is the right size/format, we can encode it directly.
550 if (scaledSize
== frame
->GetSize() &&
551 (frame
->GetFormat() == SurfaceFormat::B8G8R8A8
||
552 frame
->GetFormat() == SurfaceFormat::B8G8R8X8
)) {
553 RefPtr
<DataSourceSurface
> dataSurface
= frame
->GetDataSurface();
555 return EncodeImageData(dataSurface
, aMimeType
, aOutputOptions
, aStream
);
559 // Otherwise we need to scale it using a draw target.
560 RefPtr
<DataSourceSurface
> dataSurface
=
561 Factory::CreateDataSourceSurface(scaledSize
, SurfaceFormat::B8G8R8A8
);
562 if (NS_WARN_IF(!dataSurface
)) {
563 return NS_ERROR_FAILURE
;
566 DataSourceSurface::ScopedMap
map(dataSurface
, DataSourceSurface::READ_WRITE
);
567 if (!map
.IsMapped()) {
568 return NS_ERROR_FAILURE
;
571 RefPtr
<DrawTarget
> dt
= Factory::CreateDrawTargetForData(
572 BackendType::SKIA
, map
.GetData(), dataSurface
->GetSize(), map
.GetStride(),
573 SurfaceFormat::B8G8R8A8
);
575 gfxWarning() << "imgTools::EncodeImage failed in CreateDrawTargetForData";
576 return NS_ERROR_OUT_OF_MEMORY
;
579 IntSize frameSize
= frame
->GetSize();
580 dt
->DrawSurface(frame
, Rect(0, 0, scaledSize
.width
, scaledSize
.height
),
581 Rect(0, 0, frameSize
.width
, frameSize
.height
),
582 DrawSurfaceOptions(),
583 DrawOptions(1.0f
, CompositionOp::OP_SOURCE
));
585 return EncodeImageData(dataSurface
, map
, aMimeType
, aOutputOptions
, aStream
);
589 imgTools::EncodeCroppedImage(imgIContainer
* aContainer
,
590 const nsACString
& aMimeType
, int32_t aOffsetX
,
591 int32_t aOffsetY
, int32_t aWidth
, int32_t aHeight
,
592 const nsAString
& aOutputOptions
,
593 nsIInputStream
** aStream
) {
594 NS_ENSURE_ARG(aOffsetX
>= 0 && aOffsetY
>= 0 && aWidth
>= 0 && aHeight
>= 0);
596 // Offsets must be zero when no width and height are given or else we're out
598 NS_ENSURE_ARG(aWidth
+ aHeight
> 0 || aOffsetX
+ aOffsetY
== 0);
600 // If no size is specified then we'll preserve the image's original dimensions
601 // and don't need to crop.
602 if (aWidth
== 0 && aHeight
== 0) {
603 return EncodeImage(aContainer
, aMimeType
, aOutputOptions
, aStream
);
606 // Use frame 0 from the image container.
607 RefPtr
<SourceSurface
> frame
= aContainer
->GetFrame(
608 imgIContainer::FRAME_FIRST
,
609 imgIContainer::FLAG_SYNC_DECODE
| imgIContainer::FLAG_ASYNC_NOTIFY
);
610 NS_ENSURE_TRUE(frame
, NS_ERROR_FAILURE
);
612 int32_t frameWidth
= frame
->GetSize().width
;
613 int32_t frameHeight
= frame
->GetSize().height
;
615 // If the given width or height is zero we'll replace it with the image's
616 // original dimensions.
619 } else if (aHeight
== 0) {
620 aHeight
= frameHeight
;
623 // Check that the given crop rectangle is within image bounds.
624 NS_ENSURE_ARG(frameWidth
>= aOffsetX
+ aWidth
&&
625 frameHeight
>= aOffsetY
+ aHeight
);
627 RefPtr
<DataSourceSurface
> dataSurface
= Factory::CreateDataSourceSurface(
628 IntSize(aWidth
, aHeight
), SurfaceFormat::B8G8R8A8
,
630 if (NS_WARN_IF(!dataSurface
)) {
631 return NS_ERROR_FAILURE
;
634 DataSourceSurface::ScopedMap
map(dataSurface
, DataSourceSurface::READ_WRITE
);
635 if (!map
.IsMapped()) {
636 return NS_ERROR_FAILURE
;
639 RefPtr
<DrawTarget
> dt
= Factory::CreateDrawTargetForData(
640 BackendType::SKIA
, map
.GetData(), dataSurface
->GetSize(), map
.GetStride(),
641 SurfaceFormat::B8G8R8A8
);
644 << "imgTools::EncodeCroppedImage failed in CreateDrawTargetForData";
645 return NS_ERROR_OUT_OF_MEMORY
;
647 dt
->CopySurface(frame
, IntRect(aOffsetX
, aOffsetY
, aWidth
, aHeight
),
650 return EncodeImageData(dataSurface
, map
, aMimeType
, aOutputOptions
, aStream
);
654 imgTools::CreateScriptedObserver(imgIScriptedNotificationObserver
* aInner
,
655 imgINotificationObserver
** aObserver
) {
656 NS_ADDREF(*aObserver
= new ScriptedNotificationObserver(aInner
));
661 imgTools::GetImgLoaderForDocument(dom::Document
* aDoc
, imgILoader
** aLoader
) {
662 NS_IF_ADDREF(*aLoader
= nsContentUtils::GetImgLoaderForDocument(aDoc
));
667 imgTools::GetImgCacheForDocument(dom::Document
* aDoc
, imgICache
** aCache
) {
668 nsCOMPtr
<imgILoader
> loader
;
669 nsresult rv
= GetImgLoaderForDocument(aDoc
, getter_AddRefs(loader
));
670 NS_ENSURE_SUCCESS(rv
, rv
);
671 return CallQueryInterface(loader
, aCache
);
675 } // namespace mozilla