From c5757607e20b21e57639e7c1f5565732e57f3358 Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Tue, 6 Nov 2012 18:23:13 -0500 Subject: [PATCH] Bug 781425 - Part 1: Enconding and dictionary. r=mounir, sr=sicking --- dom/file/ArchiveEvent.cpp | 25 ++++- dom/file/ArchiveEvent.h | 20 ++-- dom/file/ArchiveReader.cpp | 16 ++- dom/file/ArchiveReader.h | 6 + dom/file/ArchiveRequest.cpp | 12 +- dom/file/ArchiveRequest.h | 6 +- dom/file/ArchiveZipEvent.cpp | 73 +++++++++--- dom/file/ArchiveZipEvent.h | 27 ++++- dom/file/ArchiveZipFile.cpp | 62 +++++++---- dom/file/ArchiveZipFile.h | 3 + dom/file/nsIDOMArchiveReader.idl | 8 ++ dom/file/test/Makefile.in | 1 + .../test/test_archivereader_nonUnicode.html | 104 ++++++++++++++++++ js/xpconnect/src/dictionary_helper_gen.conf | 3 +- 14 files changed, 296 insertions(+), 70 deletions(-) create mode 100644 dom/file/test/test_archivereader_nonUnicode.html diff --git a/dom/file/ArchiveEvent.cpp b/dom/file/ArchiveEvent.cpp index ca2bd5b2e09..1088edcbdf8 100644 --- a/dom/file/ArchiveEvent.cpp +++ b/dom/file/ArchiveEvent.cpp @@ -28,7 +28,11 @@ ArchiveItem::~ArchiveItem() nsCString ArchiveItem::GetType() { - return mType.IsEmpty() ? nsCString("binary/octet-stream") : mType; + if (mType.IsEmpty()) { + return NS_LITERAL_CSTRING("binary/octet-stream"); + } + + return mType; } void @@ -109,20 +113,29 @@ ArchiveReaderEvent::ShareMainThread() for (uint32_t index = 0; index < mFileList.Length(); ++index) { nsRefPtr item = mFileList[index]; - int32_t offset = item->GetFilename().RFindChar('.'); + nsString tmp; + nsresult rv = item->GetFilename(tmp); + nsCString filename = NS_ConvertUTF16toUTF8(tmp); + if (NS_FAILED(rv)) { + continue; + } + + int32_t offset = filename.RFindChar('.'); if (offset != kNotFound) { - nsCString ext(item->GetFilename()); - ext.Cut(0, offset + 1); + filename.Cut(0, offset + 1); // Just to be sure, if something goes wrong, the mimetype is an empty string: nsCString type; - if (NS_SUCCEEDED(GetType(ext, type))) + if (NS_SUCCEEDED(GetType(filename, type))) { item->SetType(type); + } } // This is a nsDOMFile: nsRefPtr file = item->File(mArchiveReader); - fileList.AppendElement(file); + if (file) { + fileList.AppendElement(file); + } } } diff --git a/dom/file/ArchiveEvent.h b/dom/file/ArchiveEvent.h index 7c632fa4e2f..42e6a61c3ca 100644 --- a/dom/file/ArchiveEvent.h +++ b/dom/file/ArchiveEvent.h @@ -17,8 +17,10 @@ BEGIN_FILE_NAMESPACE -// This class contains all the info needed for a single item -// It must contain the implementation of the File() method. +/** + * This class contains all the info needed for a single item + * It must contain the implementation of the File() method. + */ class ArchiveItem : public nsISupports { public: @@ -28,11 +30,11 @@ public: virtual ~ArchiveItem(); // Getter/Setter for the type - virtual nsCString GetType(); - virtual void SetType(const nsCString& aType); + nsCString GetType(); + void SetType(const nsCString& aType); // Getter for the filename - virtual nsCString GetFilename() = 0; + virtual nsresult GetFilename(nsString& aFilename) = 0; // Generate a DOMFile virtual nsIDOMFile* File(ArchiveReader* aArchiveReader) = 0; @@ -41,9 +43,11 @@ protected: nsCString mType; }; -// This class must be extended by any archive format supported by ArchiveReader API -// This class runs in a different thread and it calls the 'exec()' method. -// The exec() must populate mFileList and mStatus then it must call RunShare(); +/** + * This class must be extended by any archive format supported by ArchiveReader API + * This class runs in a different thread and it calls the 'exec()' method. + * The exec() must populate mFileList and mStatus then it must call RunShare(); + */ class ArchiveReaderEvent : public nsRunnable { public: diff --git a/dom/file/ArchiveReader.cpp b/dom/file/ArchiveReader.cpp index a158001adb4..b7560b0f7ef 100644 --- a/dom/file/ArchiveReader.cpp +++ b/dom/file/ArchiveReader.cpp @@ -40,11 +40,11 @@ ArchiveReader::Initialize(nsISupports* aOwner, uint32_t aArgc, JS::Value* aArgv) { - NS_ENSURE_TRUE(aArgc > 0, NS_ERROR_UNEXPECTED); + NS_ENSURE_TRUE(aArgc == 1 || aArgc == 2, NS_ERROR_INVALID_ARG); // We expect to get a Blob object if (!aArgv[0].isObject()) { - return NS_ERROR_UNEXPECTED; // We're not interested + return NS_ERROR_INVALID_ARG; // We're not interested } JSObject* obj = &aArgv[0].toObject(); @@ -52,16 +52,22 @@ ArchiveReader::Initialize(nsISupports* aOwner, nsCOMPtr blob; blob = do_QueryInterface(nsContentUtils::XPConnect()->GetNativeOfWrapper(aCx, obj)); if (!blob) { - return NS_ERROR_UNEXPECTED; + return NS_ERROR_INVALID_ARG; } - mBlob = blob; + // Extra param is an object + if (aArgc > 1) { + nsresult rv = mOptions.Init(aCx, &aArgv[1]); + NS_ENSURE_SUCCESS(rv, rv); + } mWindow = do_QueryInterface(aOwner); if (!mWindow) { return NS_ERROR_UNEXPECTED; } + mBlob = blob; + return NS_OK; } @@ -122,7 +128,7 @@ ArchiveReader::OpenArchive() nsRefPtr event; /* FIXME: If we want to support more than 1 format we should check the content type here: */ - event = new ArchiveReaderZipEvent(this); + event = new ArchiveReaderZipEvent(this, mOptions); rv = target->Dispatch(event, NS_DISPATCH_NORMAL); NS_ENSURE_SUCCESS(rv, rv); diff --git a/dom/file/ArchiveReader.h b/dom/file/ArchiveReader.h index f0eea828570..0b8f628f68b 100644 --- a/dom/file/ArchiveReader.h +++ b/dom/file/ArchiveReader.h @@ -16,11 +16,15 @@ #include "nsIChannel.h" #include "nsIDOMFile.h" #include "mozilla/Attributes.h" +#include "DictionaryHelpers.h" BEGIN_FILE_NAMESPACE class ArchiveRequest; +/** + * This is the ArchiveReader object + */ class ArchiveReader MOZ_FINAL : public nsIDOMArchiveReader, public nsIJSNativeInitializer { @@ -90,6 +94,8 @@ protected: nsTArray > fileList; nsresult status; } mData; + + ArchiveReaderOptions mOptions; }; END_FILE_NAMESPACE diff --git a/dom/file/ArchiveRequest.cpp b/dom/file/ArchiveRequest.cpp index 30168eafdfb..4de7a88b98a 100644 --- a/dom/file/ArchiveRequest.cpp +++ b/dom/file/ArchiveRequest.cpp @@ -44,7 +44,7 @@ ArchiveRequestEvent::Run() return NS_OK; } -/* ArchiveRequest */ +// ArchiveRequest ArchiveRequest::ArchiveRequest(nsIDOMWindow* aWindow, ArchiveReader* aReader) @@ -90,8 +90,9 @@ ArchiveRequest::Run() // Register this request to the reader. // When the reader is ready to return data, a 'Ready()' will be called nsresult rv = mArchiveReader->RegisterRequest(this); - if (NS_FAILED(rv)) + if (NS_FAILED(rv)) { FireError(rv); + } } void @@ -187,7 +188,7 @@ ArchiveRequest::GetFilenamesResult(JSContext* aCx, if (!JS_FreezeObject(aCx, array)) { return NS_ERROR_FAILURE; } - + *aValue = OBJECT_TO_JSVAL(array); return NS_OK; } @@ -205,8 +206,9 @@ ArchiveRequest::GetFileResult(JSContext* aCx, NS_ENSURE_SUCCESS(rv, rv); if (filename == mFilename) { - JSObject* scope = JS_GetGlobalForScopeChain(aCx); - nsresult rv = nsContentUtils::WrapNative(aCx, scope, file, aValue, nullptr, true); + nsresult rv = nsContentUtils::WrapNative( + aCx, JS_GetGlobalForScopeChain(aCx), + file, &NS_GET_IID(nsIDOMFile), aValue); return rv; } } diff --git a/dom/file/ArchiveRequest.h b/dom/file/ArchiveRequest.h index 798af67b691..e28d0efd83b 100644 --- a/dom/file/ArchiveRequest.h +++ b/dom/file/ArchiveRequest.h @@ -16,11 +16,15 @@ BEGIN_FILE_NAMESPACE +/** + * This is the ArchiveRequest that handles any operation + * related to ArchiveReader + */ class ArchiveRequest : public mozilla::dom::DOMRequest, public nsIDOMArchiveRequest { public: - NS_DECL_ISUPPORTS + NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIDOMARCHIVEREQUEST NS_FORWARD_NSIDOMDOMREQUEST(DOMRequest::) diff --git a/dom/file/ArchiveZipEvent.cpp b/dom/file/ArchiveZipEvent.cpp index c3c3865d394..d9cb8c9b58f 100644 --- a/dom/file/ArchiveZipEvent.cpp +++ b/dom/file/ArchiveZipEvent.cpp @@ -8,19 +8,24 @@ #include "ArchiveZipFile.h" #include "nsContentUtils.h" +#include "nsIPlatformCharset.h" +#include "nsNativeCharsetUtils.h" #include "nsCExternalHandlerService.h" +using namespace mozilla::dom; + USING_FILE_NAMESPACE #ifndef PATH_MAX # define PATH_MAX 65536 // The filename length is stored in 2 bytes #endif -// ArchiveZipItem ArchiveZipItem::ArchiveZipItem(const char* aFilename, - ZipCentral& aCentralStruct) + const ZipCentral& aCentralStruct, + const ArchiveReaderOptions& aOptions) : mFilename(aFilename), - mCentralStruct(aCentralStruct) + mCentralStruct(aCentralStruct), + mOptions(aOptions) { MOZ_COUNT_CTOR(ArchiveZipItem); } @@ -30,25 +35,58 @@ ArchiveZipItem::~ArchiveZipItem() MOZ_COUNT_DTOR(ArchiveZipItem); } -// Getter/Setter for the filename -nsCString -ArchiveZipItem::GetFilename() +nsresult +ArchiveZipItem::ConvertFilename() { - return mFilename; + if (mOptions.encoding.IsEmpty()) { + return NS_ERROR_FAILURE; + } + + nsString filenameU; + nsresult rv = nsContentUtils::ConvertStringFromCharset( + NS_ConvertUTF16toUTF8(mOptions.encoding), + mFilename, filenameU); + NS_ENSURE_SUCCESS(rv, rv); + + if (filenameU.IsEmpty()) { + return NS_ERROR_FAILURE; + } + + mFilenameU = filenameU; + return NS_OK; } -void -ArchiveZipItem::SetFilename(const nsCString& aFilename) +nsresult +ArchiveZipItem::GetFilename(nsString& aFilename) { - mFilename = aFilename; -} + if (mFilenameU.IsEmpty()) { + // Maybe this string is UTF-8: + if (IsUTF8(mFilename, false)) { + mFilenameU = NS_ConvertUTF8toUTF16(mFilename); + } + // Let's use the enconding value for the dictionary + else { + nsresult rv = ConvertFilename(); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + aFilename = mFilenameU; + return NS_OK; +} // From zipItem to DOMFile: nsIDOMFile* ArchiveZipItem::File(ArchiveReader* aArchiveReader) { - return new ArchiveZipFile(NS_ConvertUTF8toUTF16(mFilename), + nsString filename; + + if (NS_FAILED(GetFilename(filename))) { + return nullptr; + } + + return new ArchiveZipFile(filename, NS_ConvertUTF8toUTF16(GetType()), StrToInt32(mCentralStruct.orglen), mCentralStruct, @@ -72,8 +110,10 @@ ArchiveZipItem::StrToInt16(const uint8_t* aStr) // ArchiveReaderZipEvent -ArchiveReaderZipEvent::ArchiveReaderZipEvent(ArchiveReader* aArchiveReader) -: ArchiveReaderEvent(aArchiveReader) +ArchiveReaderZipEvent::ArchiveReaderZipEvent(ArchiveReader* aArchiveReader, + const ArchiveReaderOptions& aOptions) +: ArchiveReaderEvent(aArchiveReader), + mOptions(aOptions) { } @@ -104,8 +144,7 @@ ArchiveReaderZipEvent::Exec() } // Reading backward.. looking for the ZipEnd signature - for (uint64_t curr = size - ZIPEND_SIZE; curr > 4; --curr) - { + for (uint64_t curr = size - ZIPEND_SIZE; curr > 4; --curr) { seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, curr); uint8_t buffer[ZIPEND_SIZE]; @@ -162,7 +201,7 @@ ArchiveReaderZipEvent::Exec() // We ignore the directories: if (filename[filenameLen - 1] != '/') { - mFileList.AppendElement(new ArchiveZipItem(filename, centralStruct)); + mFileList.AppendElement(new ArchiveZipItem(filename, centralStruct, mOptions)); } PR_Free(filename); diff --git a/dom/file/ArchiveZipEvent.h b/dom/file/ArchiveZipEvent.h index 261eaee2bfa..77f0d7ab51e 100644 --- a/dom/file/ArchiveZipEvent.h +++ b/dom/file/ArchiveZipEvent.h @@ -12,17 +12,22 @@ #include "FileCommon.h" #include "zipstruct.h" +#include "DictionaryHelpers.h" + BEGIN_FILE_NAMESPACE +/** + * ArchiveZipItem - ArchiveItem for ArchiveReaderZipEvent + */ class ArchiveZipItem : public ArchiveItem { public: ArchiveZipItem(const char* aFilename, - ZipCentral& aCentralStruct); + const ZipCentral& aCentralStruct, + const ArchiveReaderOptions& aOptions); virtual ~ArchiveZipItem(); - void SetFilename(const nsCString& aFilename); - nsCString GetFilename(); + nsresult GetFilename(nsString& aFilename); // From zipItem to DOMFile: virtual nsIDOMFile* File(ArchiveReader* aArchiveReader); @@ -31,17 +36,31 @@ public: // for the event static uint32_t StrToInt32(const uint8_t* aStr); static uint16_t StrToInt16(const uint8_t* aStr); +private: + nsresult ConvertFilename(); + private: // data nsCString mFilename; + + nsString mFilenameU; ZipCentral mCentralStruct; + + ArchiveReaderOptions mOptions; }; +/** + * ArchiveReaderEvent implements the ArchiveReaderEvent for the ZIP format + */ class ArchiveReaderZipEvent : public ArchiveReaderEvent { public: - ArchiveReaderZipEvent(ArchiveReader* aArchiveReader); + ArchiveReaderZipEvent(ArchiveReader* aArchiveReader, + const ArchiveReaderOptions& aOptions); nsresult Exec(); + +private: + ArchiveReaderOptions mOptions; }; END_FILE_NAMESPACE diff --git a/dom/file/ArchiveZipFile.cpp b/dom/file/ArchiveZipFile.cpp index eac84128332..2b13f62b137 100644 --- a/dom/file/ArchiveZipFile.cpp +++ b/dom/file/ArchiveZipFile.cpp @@ -15,8 +15,9 @@ USING_FILE_NAMESPACE #define ZIP_CHUNK 16384 -// a internat input stream object - +/** + * Input stream object for zip files + */ class ArchiveInputStream MOZ_FINAL : public nsIInputStream, public nsISeekableStream { @@ -92,22 +93,25 @@ ArchiveInputStream::Init() memset(&mZs, 0, sizeof(z_stream)); int zerr = inflateInit2(&mZs, -MAX_WBITS); - if (zerr != Z_OK) + if (zerr != Z_OK) { return NS_ERROR_OUT_OF_MEMORY; + } mData.sizeToBeRead = ArchiveZipItem::StrToInt32(mCentral.size); uint32_t offset = ArchiveZipItem::StrToInt32(mCentral.localhdr_offset); // The file is corrupt - if (offset + ZIPLOCAL_SIZE > mData.parentSize) + if (offset + ZIPLOCAL_SIZE > mData.parentSize) { return NS_ERROR_UNEXPECTED; + } // From the input stream to a seekable stream nsCOMPtr seekableStream; seekableStream = do_QueryInterface(mData.inputStream); - if (!seekableStream) + if (!seekableStream) { return NS_ERROR_UNEXPECTED; + } // Seek + read the ZipLocal struct seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, offset); @@ -115,12 +119,14 @@ ArchiveInputStream::Init() uint32_t ret; rv = mData.inputStream->Read((char*)buffer, ZIPLOCAL_SIZE, &ret); - if (NS_FAILED(rv) || ret != ZIPLOCAL_SIZE) + if (NS_FAILED(rv) || ret != ZIPLOCAL_SIZE) { return NS_ERROR_UNEXPECTED; + } // Signature check: - if (ArchiveZipItem::StrToInt32(buffer) != LOCALSIG) + if (ArchiveZipItem::StrToInt32(buffer) != LOCALSIG) { return NS_ERROR_UNEXPECTED; + } ZipLocal local; memcpy(&local, buffer, ZIPLOCAL_SIZE); @@ -131,8 +137,9 @@ ArchiveInputStream::Init() ArchiveZipItem::StrToInt16(local.extrafield_len); // The file is corrupt if there is not enough data - if (offset + mData.sizeToBeRead > mData.parentSize) + if (offset + mData.sizeToBeRead > mData.parentSize) { return NS_ERROR_UNEXPECTED; + } // Data starts here: seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, offset); @@ -182,8 +189,9 @@ ArchiveInputStream::Read(char* aBuffer, mStatus = Started; rv = Init(); - if (NS_FAILED(rv)) + if (NS_FAILED(rv)) { return rv; + } // Let's set avail_out to -1 so we read something from the stream. mZs.avail_out = (uInt)-1; @@ -196,8 +204,7 @@ ArchiveInputStream::Read(char* aBuffer, } // Stored file: - if (!mData.compressed) - { + if (!mData.compressed) { rv = mData.inputStream->Read(aBuffer, (mData.sizeToBeRead > aCount ? aCount : mData.sizeToBeRead), @@ -206,23 +213,24 @@ ArchiveInputStream::Read(char* aBuffer, mData.sizeToBeRead -= *_retval; mData.cursor += *_retval; - if (mData.sizeToBeRead == 0) + if (mData.sizeToBeRead == 0) { mStatus = Done; + } } return rv; } // We have nothing ready to be processed: - if (mZs.avail_out != 0 && mData.sizeToBeRead != 0) - { + if (mZs.avail_out != 0 && mData.sizeToBeRead != 0) { uint32_t ret; rv = mData.inputStream->Read((char*)mData.input, (mData.sizeToBeRead > sizeof(mData.input) ? sizeof(mData.input) : mData.sizeToBeRead), &ret); - if (NS_FAILED(rv)) + if (NS_FAILED(rv)) { return rv; + } // Terminator: if (ret == 0) { @@ -239,11 +247,13 @@ ArchiveInputStream::Read(char* aBuffer, mZs.next_out = (unsigned char*)aBuffer; int ret = inflate(&mZs, mData.sizeToBeRead ? Z_NO_FLUSH : Z_FINISH); - if (ret != Z_BUF_ERROR && ret != Z_OK && ret != Z_STREAM_END) + if (ret != Z_BUF_ERROR && ret != Z_OK && ret != Z_STREAM_END) { return NS_ERROR_UNEXPECTED; + } - if (ret == Z_STREAM_END) + if (ret == Z_STREAM_END) { mStatus = Done; + } *_retval = aCount - mZs.avail_out; mData.cursor += *_retval; @@ -291,11 +301,13 @@ ArchiveInputStream::Seek(int32_t aWhence, int64_t aOffset) return NS_ERROR_UNEXPECTED; } - if (pos == int64_t(mData.cursor)) + if (pos == int64_t(mData.cursor)) { return NS_OK; + } - if (pos < 0 || pos >= mLength) + if (pos < 0 || pos >= mLength) { return NS_ERROR_FAILURE; + } // We have to terminate the previous operation: nsresult rv; @@ -312,11 +324,13 @@ ArchiveInputStream::Seek(int32_t aWhence, int64_t aOffset) char buffer[1024]; while (pos > 0) { rv = Read(buffer, pos > int64_t(sizeof(buffer)) ? sizeof(buffer) : pos, &ret); - if (NS_FAILED(rv)) + if (NS_FAILED(rv)) { return rv; + } - if (ret == 0) + if (ret == 0) { return NS_ERROR_UNEXPECTED; + } pos -= ret; } @@ -342,8 +356,9 @@ ArchiveInputStream::SetEOF() NS_IMETHODIMP ArchiveZipFile::GetInternalStream(nsIInputStream** aStream) { - if (mLength > INT32_MAX) + if (mLength > INT32_MAX) { return NS_ERROR_FAILURE; + } uint64_t size; nsresult rv = mArchiveReader->GetSize(&size); @@ -351,8 +366,9 @@ ArchiveZipFile::GetInternalStream(nsIInputStream** aStream) nsCOMPtr inputStream; rv = mArchiveReader->GetInputStream(getter_AddRefs(inputStream)); - if (NS_FAILED(rv) || !inputStream) + if (NS_FAILED(rv) || !inputStream) { return NS_ERROR_UNEXPECTED; + } nsRefPtr stream = new ArchiveInputStream(size, inputStream, diff --git a/dom/file/ArchiveZipFile.h b/dom/file/ArchiveZipFile.h index cac3868257f..324b6b85dd2 100644 --- a/dom/file/ArchiveZipFile.h +++ b/dom/file/ArchiveZipFile.h @@ -16,6 +16,9 @@ BEGIN_FILE_NAMESPACE +/** + * ZipFile to DOMFileCC + */ class ArchiveZipFile : public nsDOMFileCC { public: diff --git a/dom/file/nsIDOMArchiveReader.idl b/dom/file/nsIDOMArchiveReader.idl index 254a500afae..9b21e141df9 100644 --- a/dom/file/nsIDOMArchiveReader.idl +++ b/dom/file/nsIDOMArchiveReader.idl @@ -14,6 +14,14 @@ interface nsIDOMArchiveReader : nsISupports nsIDOMArchiveRequest getFile(in DOMString filename); }; +/* This dictionary is the optional argument for the + * ArchiveReader constructor: + * var a = new ArchiveReader(blob, { encoding: "iso-8859-1" }); */ +dictionary ArchiveReaderOptions +{ + DOMString encoding = "windows-1252"; // Default fallback encoding +}; + %{ C++ #define NS_ARCHIVEREADER_CID \ {0xb6b8c817, 0x4e9a, 0x46f8, \ diff --git a/dom/file/test/Makefile.in b/dom/file/test/Makefile.in index 84a00464284..6680087680b 100644 --- a/dom/file/test/Makefile.in +++ b/dom/file/test/Makefile.in @@ -31,6 +31,7 @@ MOCHITEST_FILES = \ test_workers.html \ test_archivereader.html \ test_archivereader_zip_in_zip.html \ + test_archivereader_nonUnicode.html \ test_bug_793311.html \ $(NULL) diff --git a/dom/file/test/test_archivereader_nonUnicode.html b/dom/file/test/test_archivereader_nonUnicode.html new file mode 100644 index 00000000000..5d9da5d22e9 --- /dev/null +++ b/dom/file/test/test_archivereader_nonUnicode.html @@ -0,0 +1,104 @@ + + + + Archive Reader Non-Unicode Test + + + + + + + + + + +

+ +

+ + + + diff --git a/js/xpconnect/src/dictionary_helper_gen.conf b/js/xpconnect/src/dictionary_helper_gen.conf index 49be56f73b6..ebb095ecd11 100644 --- a/js/xpconnect/src/dictionary_helper_gen.conf +++ b/js/xpconnect/src/dictionary_helper_gen.conf @@ -20,7 +20,8 @@ dictionaries = [ [ 'CameraPosition', 'nsIDOMCameraManager.idl' ], [ 'CameraSelector', 'nsIDOMCameraManager.idl' ], [ 'CameraPictureOptions', 'nsIDOMCameraManager.idl' ], - [ 'CameraRecordingOptions', 'nsIDOMCameraManager.idl' ] + [ 'CameraRecordingOptions', 'nsIDOMCameraManager.idl' ], + [ 'ArchiveReaderOptions', 'nsIDOMArchiveReader.idl' ] ] # include file names