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 "nsComponentManagerUtils.h"
22 #include "nsNetUtil.h" // for NS_NewBufferedInputStream
23 #include "nsStreamUtils.h"
24 #include "nsStringStream.h"
25 #include "nsContentUtils.h"
26 #include "nsProxyRelease.h"
27 #include "nsIStreamListener.h"
28 #include "ImageFactory.h"
30 #include "IProgressObserver.h"
31 #include "ScriptedNotificationObserver.h"
32 #include "imgIScriptedNotificationObserver.h"
33 #include "gfxPlatform.h"
34 #include "js/ArrayBuffer.h"
35 #include "js/RootingAPI.h" // JS::{Handle,Rooted}
36 #include "js/Value.h" // JS::Value
37 #include "Orientation.h"
39 using namespace mozilla::gfx
;
46 static nsresult
sniff_mimetype_callback(nsIInputStream
* in
, void* data
,
47 const char* fromRawSegment
,
48 uint32_t toOffset
, uint32_t count
,
49 uint32_t* writeCount
) {
50 nsCString
* mimeType
= static_cast<nsCString
*>(data
);
51 MOZ_ASSERT(mimeType
, "mimeType is null!");
54 imgLoader::GetMimeTypeFromContent(fromRawSegment
, count
, *mimeType
);
58 return NS_ERROR_FAILURE
;
61 class ImageDecoderListener final
: public nsIStreamListener
,
62 public IProgressObserver
,
63 public imgIContainer
{
67 ImageDecoderListener(nsIURI
* aURI
, imgIContainerCallback
* aCallback
,
68 imgINotificationObserver
* aObserver
)
72 mObserver(aObserver
) {
73 MOZ_ASSERT(NS_IsMainThread());
77 OnDataAvailable(nsIRequest
* aRequest
, nsIInputStream
* aInputStream
,
78 uint64_t aOffset
, uint32_t aCount
) override
{
80 nsCOMPtr
<nsIChannel
> channel
= do_QueryInterface(aRequest
);
83 channel
->GetContentType(mimeType
);
86 // Look at the first few bytes and see if we can tell what the data is
87 // from that since servers tend to lie. :(
89 aInputStream
->ReadSegments(sniff_mimetype_callback
, &mimeType
, aCount
,
93 RefPtr
<ProgressTracker
> tracker
= new ProgressTracker();
95 tracker
->AddObserver(this);
98 mImage
= ImageFactory::CreateImage(channel
, tracker
, mimeType
, mURI
,
99 /* aIsMultiPart */ false, 0);
101 if (mImage
->HasError()) {
102 return NS_ERROR_FAILURE
;
106 return mImage
->OnImageDataAvailable(aRequest
, aInputStream
, aOffset
,
111 OnStartRequest(nsIRequest
* aRequest
) override
{ return NS_OK
; }
114 OnStopRequest(nsIRequest
* aRequest
, nsresult aStatus
) override
{
115 // Encouter a fetch error, or no data could be fetched.
116 if (!mImage
|| NS_FAILED(aStatus
)) {
117 mCallback
->OnImageReady(nullptr, mImage
? aStatus
: NS_ERROR_FAILURE
);
121 mImage
->OnImageDataComplete(aRequest
, aStatus
, true);
122 nsCOMPtr
<imgIContainer
> container
= this;
123 mCallback
->OnImageReady(container
, aStatus
);
127 virtual void Notify(int32_t aType
,
128 const nsIntRect
* aRect
= nullptr) override
{
130 mObserver
->Notify(nullptr, aType
, aRect
);
134 virtual void OnLoadComplete(bool aLastPart
) override
{}
136 // Other notifications are ignored.
137 virtual void SetHasImage() override
{}
138 virtual bool NotificationsDeferred() const override
{ return false; }
139 virtual void MarkPendingNotify() override
{}
140 virtual void ClearPendingNotify() override
{}
143 NS_FORWARD_IMGICONTAINER(mImage
->)
146 virtual ~ImageDecoderListener() = default;
148 nsCOMPtr
<nsIURI
> mURI
;
149 RefPtr
<image::Image
> mImage
;
150 nsCOMPtr
<imgIContainerCallback
> mCallback
;
151 nsCOMPtr
<imgINotificationObserver
> mObserver
;
154 NS_IMPL_ISUPPORTS(ImageDecoderListener
, nsIStreamListener
, imgIContainer
)
156 class ImageDecoderHelper final
: public Runnable
,
157 public nsIInputStreamCallback
{
159 NS_DECL_ISUPPORTS_INHERITED
161 ImageDecoderHelper(already_AddRefed
<image::Image
> aImage
,
162 already_AddRefed
<nsIInputStream
> aInputStream
,
163 nsIEventTarget
* aEventTarget
,
164 imgIContainerCallback
* aCallback
,
165 nsIEventTarget
* aCallbackEventTarget
)
166 : Runnable("ImageDecoderHelper"),
167 mImage(std::move(aImage
)),
168 mInputStream(std::move(aInputStream
)),
169 mEventTarget(aEventTarget
),
170 mCallback(aCallback
),
171 mCallbackEventTarget(aCallbackEventTarget
),
173 MOZ_ASSERT(NS_IsMainThread());
178 // This runnable is dispatched on the Image thread when reading data, but
179 // at the end, it goes back to the main-thread in order to complete the
181 if (NS_IsMainThread()) {
182 // Let the Image know we've sent all the data.
183 mImage
->OnImageDataComplete(nullptr, mStatus
, true);
185 RefPtr
<ProgressTracker
> tracker
= mImage
->GetProgressTracker();
186 tracker
->SyncNotifyProgress(FLAG_LOAD_COMPLETE
);
188 nsCOMPtr
<imgIContainer
> container
;
189 if (NS_SUCCEEDED(mStatus
)) {
193 mCallback
->OnImageReady(container
, mStatus
);
198 nsresult rv
= mInputStream
->Available(&length
);
199 if (rv
== NS_BASE_STREAM_CLOSED
) {
200 return OperationCompleted(NS_OK
);
203 if (NS_WARN_IF(NS_FAILED(rv
))) {
204 return OperationCompleted(rv
);
207 // Nothing else to read, but maybe we just need to wait.
209 nsCOMPtr
<nsIAsyncInputStream
> asyncInputStream
=
210 do_QueryInterface(mInputStream
);
211 if (asyncInputStream
) {
212 rv
= asyncInputStream
->AsyncWait(this, 0, 0, mEventTarget
);
213 if (NS_WARN_IF(NS_FAILED(rv
))) {
214 return OperationCompleted(rv
);
219 // We really have nothing else to read.
221 return OperationCompleted(NS_OK
);
225 // Send the source data to the Image.
226 rv
= mImage
->OnImageDataAvailable(nullptr, mInputStream
, 0,
228 if (NS_WARN_IF(NS_FAILED(rv
))) {
229 return OperationCompleted(rv
);
232 rv
= mEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
);
233 if (NS_WARN_IF(NS_FAILED(rv
))) {
234 return OperationCompleted(rv
);
241 OnInputStreamReady(nsIAsyncInputStream
* aAsyncInputStream
) override
{
242 MOZ_ASSERT(!NS_IsMainThread());
246 nsresult
OperationCompleted(nsresult aStatus
) {
247 MOZ_ASSERT(!NS_IsMainThread());
250 mCallbackEventTarget
->Dispatch(this, NS_DISPATCH_NORMAL
);
255 ~ImageDecoderHelper() {
256 SurfaceCache::ReleaseImageOnMainThread(mImage
.forget());
257 NS_ReleaseOnMainThread("ImageDecoderHelper::mCallback", mCallback
.forget());
260 RefPtr
<image::Image
> mImage
;
262 nsCOMPtr
<nsIInputStream
> mInputStream
;
263 nsCOMPtr
<nsIEventTarget
> mEventTarget
;
264 nsCOMPtr
<imgIContainerCallback
> mCallback
;
265 nsCOMPtr
<nsIEventTarget
> mCallbackEventTarget
;
270 NS_IMPL_ISUPPORTS_INHERITED(ImageDecoderHelper
, Runnable
,
271 nsIInputStreamCallback
)
275 /* ========== imgITools implementation ========== */
277 NS_IMPL_ISUPPORTS(imgTools
, imgITools
)
279 imgTools::imgTools() { /* member initializers and constructor code */
282 imgTools::~imgTools() { /* destructor code */
286 imgTools::DecodeImageFromArrayBuffer(JS::Handle
<JS::Value
> aArrayBuffer
,
287 const nsACString
& aMimeType
,
289 imgIContainer
** aContainer
) {
290 if (!aArrayBuffer
.isObject()) {
291 return NS_ERROR_FAILURE
;
294 JS::Rooted
<JSObject
*> obj(aCx
,
295 JS::UnwrapArrayBuffer(&aArrayBuffer
.toObject()));
297 return NS_ERROR_FAILURE
;
300 uint8_t* bufferData
= nullptr;
301 size_t bufferLength
= 0;
302 bool isSharedMemory
= false;
304 JS::GetArrayBufferLengthAndData(obj
, &bufferLength
, &isSharedMemory
,
307 // Throw for large ArrayBuffers to prevent truncation.
308 if (bufferLength
> INT32_MAX
) {
309 return NS_ERROR_ILLEGAL_VALUE
;
312 return DecodeImageFromBuffer((char*)bufferData
, bufferLength
, aMimeType
,
317 imgTools::DecodeImageFromBuffer(const char* aBuffer
, uint32_t aSize
,
318 const nsACString
& aMimeType
,
319 imgIContainer
** aContainer
) {
320 MOZ_ASSERT(NS_IsMainThread());
322 NS_ENSURE_ARG_POINTER(aBuffer
);
324 // Create a new image container to hold the decoded data.
325 nsAutoCString
mimeType(aMimeType
);
326 RefPtr
<image::Image
> image
=
327 ImageFactory::CreateAnonymousImage(mimeType
, aSize
);
328 RefPtr
<ProgressTracker
> tracker
= image
->GetProgressTracker();
330 if (image
->HasError()) {
331 return NS_ERROR_FAILURE
;
334 // Let's create a temporary inputStream.
335 nsCOMPtr
<nsIInputStream
> stream
;
336 nsresult rv
= NS_NewByteInputStream(
337 getter_AddRefs(stream
), Span(aBuffer
, aSize
), NS_ASSIGNMENT_DEPEND
);
338 NS_ENSURE_SUCCESS(rv
, rv
);
340 MOZ_ASSERT(NS_InputStreamIsBuffered(stream
));
342 rv
= image
->OnImageDataAvailable(nullptr, stream
, 0, aSize
);
343 NS_ENSURE_SUCCESS(rv
, rv
);
345 // Let the Image know we've sent all the data.
346 rv
= image
->OnImageDataComplete(nullptr, NS_OK
, true);
347 tracker
->SyncNotifyProgress(FLAG_LOAD_COMPLETE
);
348 NS_ENSURE_SUCCESS(rv
, rv
);
351 image
.forget(aContainer
);
356 imgTools::DecodeImageFromChannelAsync(nsIURI
* aURI
, nsIChannel
* aChannel
,
357 imgIContainerCallback
* aCallback
,
358 imgINotificationObserver
* aObserver
) {
359 MOZ_ASSERT(NS_IsMainThread());
361 NS_ENSURE_ARG_POINTER(aURI
);
362 NS_ENSURE_ARG_POINTER(aChannel
);
363 NS_ENSURE_ARG_POINTER(aCallback
);
365 RefPtr
<ImageDecoderListener
> listener
=
366 new ImageDecoderListener(aURI
, aCallback
, aObserver
);
368 return aChannel
->AsyncOpen(listener
);
372 imgTools::DecodeImageAsync(nsIInputStream
* aInStr
, const nsACString
& aMimeType
,
373 imgIContainerCallback
* aCallback
,
374 nsIEventTarget
* aEventTarget
) {
375 MOZ_ASSERT(NS_IsMainThread());
377 NS_ENSURE_ARG_POINTER(aInStr
);
378 NS_ENSURE_ARG_POINTER(aCallback
);
379 NS_ENSURE_ARG_POINTER(aEventTarget
);
383 // Let's continuing the reading on a separate thread.
384 DecodePool
* decodePool
= DecodePool::Singleton();
385 MOZ_ASSERT(decodePool
);
387 RefPtr
<nsIEventTarget
> target
= decodePool
->GetIOEventTarget();
388 NS_ENSURE_TRUE(target
, NS_ERROR_FAILURE
);
390 // Prepare the input stream.
391 nsCOMPtr
<nsIInputStream
> stream
= aInStr
;
392 if (!NS_InputStreamIsBuffered(aInStr
)) {
393 nsCOMPtr
<nsIInputStream
> bufStream
;
394 rv
= NS_NewBufferedInputStream(getter_AddRefs(bufStream
), stream
.forget(),
396 NS_ENSURE_SUCCESS(rv
, rv
);
397 stream
= std::move(bufStream
);
400 // Create a new image container to hold the decoded data.
401 nsAutoCString
mimeType(aMimeType
);
402 RefPtr
<image::Image
> image
= ImageFactory::CreateAnonymousImage(mimeType
, 0);
405 if (image
->HasError()) {
406 return NS_ERROR_FAILURE
;
409 RefPtr
<ImageDecoderHelper
> helper
= new ImageDecoderHelper(
410 image
.forget(), stream
.forget(), target
, aCallback
, aEventTarget
);
411 rv
= target
->Dispatch(helper
.forget(), NS_DISPATCH_NORMAL
);
412 NS_ENSURE_SUCCESS(rv
, rv
);
418 * This takes a DataSourceSurface rather than a SourceSurface because some
419 * of the callers have a DataSourceSurface and we don't want to call
420 * GetDataSurface on such surfaces since that may incur a conversion to
421 * SurfaceType::DATA which we don't need.
423 static nsresult
EncodeImageData(DataSourceSurface
* aDataSurface
,
424 DataSourceSurface::ScopedMap
& aMap
,
425 const nsACString
& aMimeType
,
426 const nsAString
& aOutputOptions
,
427 nsIInputStream
** aStream
) {
428 MOZ_ASSERT(aDataSurface
->GetFormat() == SurfaceFormat::B8G8R8A8
||
429 aDataSurface
->GetFormat() == SurfaceFormat::B8G8R8X8
,
430 "We're assuming B8G8R8A8/X8");
432 // Get an image encoder for the media type
433 nsAutoCString
encoderCID("@mozilla.org/image/encoder;2?type="_ns
+ aMimeType
);
435 nsCOMPtr
<imgIEncoder
> encoder
= do_CreateInstance(encoderCID
.get());
437 return NS_IMAGELIB_ERROR_NO_ENCODER
;
440 IntSize size
= aDataSurface
->GetSize();
441 uint32_t dataLength
= aMap
.GetStride() * size
.height
;
444 nsresult rv
= encoder
->InitFromData(
445 aMap
.GetData(), dataLength
, size
.width
, size
.height
, aMap
.GetStride(),
446 imgIEncoder::INPUT_FORMAT_HOSTARGB
, aOutputOptions
);
447 NS_ENSURE_SUCCESS(rv
, rv
);
449 encoder
.forget(aStream
);
453 static nsresult
EncodeImageData(DataSourceSurface
* aDataSurface
,
454 const nsACString
& aMimeType
,
455 const nsAString
& aOutputOptions
,
456 nsIInputStream
** aStream
) {
457 DataSourceSurface::ScopedMap
map(aDataSurface
, DataSourceSurface::READ
);
458 if (!map
.IsMapped()) {
459 return NS_ERROR_FAILURE
;
462 return EncodeImageData(aDataSurface
, map
, aMimeType
, aOutputOptions
, aStream
);
466 imgTools::EncodeImage(imgIContainer
* aContainer
, const nsACString
& aMimeType
,
467 const nsAString
& aOutputOptions
,
468 nsIInputStream
** aStream
) {
469 // Use frame 0 from the image container.
470 RefPtr
<SourceSurface
> frame
= aContainer
->GetFrame(
471 imgIContainer::FRAME_FIRST
,
472 imgIContainer::FLAG_SYNC_DECODE
| imgIContainer::FLAG_ASYNC_NOTIFY
);
473 NS_ENSURE_TRUE(frame
, NS_ERROR_FAILURE
);
475 RefPtr
<DataSourceSurface
> dataSurface
;
477 if (frame
->GetFormat() == SurfaceFormat::B8G8R8A8
||
478 frame
->GetFormat() == SurfaceFormat::B8G8R8X8
) {
479 dataSurface
= frame
->GetDataSurface();
481 // Convert format to SurfaceFormat::B8G8R8A8
482 dataSurface
= gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat(
483 frame
, SurfaceFormat::B8G8R8A8
);
486 NS_ENSURE_TRUE(dataSurface
, NS_ERROR_FAILURE
);
488 return EncodeImageData(dataSurface
, aMimeType
, aOutputOptions
, aStream
);
492 imgTools::EncodeScaledImage(imgIContainer
* aContainer
,
493 const nsACString
& aMimeType
, int32_t aScaledWidth
,
494 int32_t aScaledHeight
,
495 const nsAString
& aOutputOptions
,
496 nsIInputStream
** aStream
) {
497 NS_ENSURE_ARG(aScaledWidth
>= 0 && aScaledHeight
>= 0);
499 // If no scaled size is specified, we'll just encode the image at its
500 // original size (no scaling).
501 if (aScaledWidth
== 0 && aScaledHeight
== 0) {
502 return EncodeImage(aContainer
, aMimeType
, aOutputOptions
, aStream
);
505 // Retrieve the image's size.
506 int32_t imageWidth
= 0;
507 int32_t imageHeight
= 0;
508 aContainer
->GetWidth(&imageWidth
);
509 aContainer
->GetHeight(&imageHeight
);
511 // If the given width or height is zero we'll replace it with the image's
512 // original dimensions.
513 IntSize
scaledSize(aScaledWidth
== 0 ? imageWidth
: aScaledWidth
,
514 aScaledHeight
== 0 ? imageHeight
: aScaledHeight
);
516 // Use frame 0 from the image container.
517 RefPtr
<SourceSurface
> frame
= aContainer
->GetFrameAtSize(
518 scaledSize
, imgIContainer::FRAME_FIRST
,
519 imgIContainer::FLAG_HIGH_QUALITY_SCALING
|
520 imgIContainer::FLAG_SYNC_DECODE
| imgIContainer::FLAG_ASYNC_NOTIFY
);
521 NS_ENSURE_TRUE(frame
, NS_ERROR_FAILURE
);
523 // If the given surface is the right size/format, we can encode it directly.
524 if (scaledSize
== frame
->GetSize() &&
525 (frame
->GetFormat() == SurfaceFormat::B8G8R8A8
||
526 frame
->GetFormat() == SurfaceFormat::B8G8R8X8
)) {
527 RefPtr
<DataSourceSurface
> dataSurface
= frame
->GetDataSurface();
529 return EncodeImageData(dataSurface
, aMimeType
, aOutputOptions
, aStream
);
533 // Otherwise we need to scale it using a draw target.
534 RefPtr
<DataSourceSurface
> dataSurface
=
535 Factory::CreateDataSourceSurface(scaledSize
, SurfaceFormat::B8G8R8A8
);
536 if (NS_WARN_IF(!dataSurface
)) {
537 return NS_ERROR_FAILURE
;
540 DataSourceSurface::ScopedMap
map(dataSurface
, DataSourceSurface::READ_WRITE
);
541 if (!map
.IsMapped()) {
542 return NS_ERROR_FAILURE
;
545 RefPtr
<DrawTarget
> dt
= Factory::CreateDrawTargetForData(
546 BackendType::SKIA
, map
.GetData(), dataSurface
->GetSize(), map
.GetStride(),
547 SurfaceFormat::B8G8R8A8
);
549 gfxWarning() << "imgTools::EncodeImage failed in CreateDrawTargetForData";
550 return NS_ERROR_OUT_OF_MEMORY
;
553 IntSize frameSize
= frame
->GetSize();
554 dt
->DrawSurface(frame
, Rect(0, 0, scaledSize
.width
, scaledSize
.height
),
555 Rect(0, 0, frameSize
.width
, frameSize
.height
),
556 DrawSurfaceOptions(),
557 DrawOptions(1.0f
, CompositionOp::OP_SOURCE
));
559 return EncodeImageData(dataSurface
, map
, aMimeType
, aOutputOptions
, aStream
);
563 imgTools::EncodeCroppedImage(imgIContainer
* aContainer
,
564 const nsACString
& aMimeType
, int32_t aOffsetX
,
565 int32_t aOffsetY
, int32_t aWidth
, int32_t aHeight
,
566 const nsAString
& aOutputOptions
,
567 nsIInputStream
** aStream
) {
568 NS_ENSURE_ARG(aOffsetX
>= 0 && aOffsetY
>= 0 && aWidth
>= 0 && aHeight
>= 0);
570 // Offsets must be zero when no width and height are given or else we're out
572 NS_ENSURE_ARG(aWidth
+ aHeight
> 0 || aOffsetX
+ aOffsetY
== 0);
574 // If no size is specified then we'll preserve the image's original dimensions
575 // and don't need to crop.
576 if (aWidth
== 0 && aHeight
== 0) {
577 return EncodeImage(aContainer
, aMimeType
, aOutputOptions
, aStream
);
580 // Use frame 0 from the image container.
581 RefPtr
<SourceSurface
> frame
= aContainer
->GetFrame(
582 imgIContainer::FRAME_FIRST
,
583 imgIContainer::FLAG_SYNC_DECODE
| imgIContainer::FLAG_ASYNC_NOTIFY
);
584 NS_ENSURE_TRUE(frame
, NS_ERROR_FAILURE
);
586 int32_t frameWidth
= frame
->GetSize().width
;
587 int32_t frameHeight
= frame
->GetSize().height
;
589 // If the given width or height is zero we'll replace it with the image's
590 // original dimensions.
593 } else if (aHeight
== 0) {
594 aHeight
= frameHeight
;
597 // Check that the given crop rectangle is within image bounds.
598 NS_ENSURE_ARG(frameWidth
>= aOffsetX
+ aWidth
&&
599 frameHeight
>= aOffsetY
+ aHeight
);
601 RefPtr
<DataSourceSurface
> dataSurface
= Factory::CreateDataSourceSurface(
602 IntSize(aWidth
, aHeight
), SurfaceFormat::B8G8R8A8
,
604 if (NS_WARN_IF(!dataSurface
)) {
605 return NS_ERROR_FAILURE
;
608 DataSourceSurface::ScopedMap
map(dataSurface
, DataSourceSurface::READ_WRITE
);
609 if (!map
.IsMapped()) {
610 return NS_ERROR_FAILURE
;
613 RefPtr
<DrawTarget
> dt
= Factory::CreateDrawTargetForData(
614 BackendType::SKIA
, map
.GetData(), dataSurface
->GetSize(), map
.GetStride(),
615 SurfaceFormat::B8G8R8A8
);
618 << "imgTools::EncodeCroppedImage failed in CreateDrawTargetForData";
619 return NS_ERROR_OUT_OF_MEMORY
;
621 dt
->CopySurface(frame
, IntRect(aOffsetX
, aOffsetY
, aWidth
, aHeight
),
624 return EncodeImageData(dataSurface
, map
, aMimeType
, aOutputOptions
, aStream
);
628 imgTools::CreateScriptedObserver(imgIScriptedNotificationObserver
* aInner
,
629 imgINotificationObserver
** aObserver
) {
630 NS_ADDREF(*aObserver
= new ScriptedNotificationObserver(aInner
));
635 imgTools::GetImgLoaderForDocument(dom::Document
* aDoc
, imgILoader
** aLoader
) {
636 NS_IF_ADDREF(*aLoader
= nsContentUtils::GetImgLoaderForDocument(aDoc
));
641 imgTools::GetImgCacheForDocument(dom::Document
* aDoc
, imgICache
** aCache
) {
642 nsCOMPtr
<imgILoader
> loader
;
643 nsresult rv
= GetImgLoaderForDocument(aDoc
, getter_AddRefs(loader
));
644 NS_ENSURE_SUCCESS(rv
, rv
);
645 return CallQueryInterface(loader
, aCache
);
649 } // namespace mozilla