Bug 781425 - Part 1: Enconding and dictionary. r=mounir, sr=sicking

This commit is contained in:
Andrea Marchesini 2012-11-06 18:23:13 -05:00
Родитель d9a04350b5
Коммит b971f025e1
14 изменённых файлов: 296 добавлений и 70 удалений

Просмотреть файл

@ -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<ArchiveItem> 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<nsIDOMFile> file = item->File(mArchiveReader);
fileList.AppendElement(file);
if (file) {
fileList.AppendElement(file);
}
}
}

Просмотреть файл

@ -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:

Просмотреть файл

@ -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<nsIDOMBlob> 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<ArchiveReaderEvent> 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);

Просмотреть файл

@ -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<nsCOMPtr<nsIDOMFile> > fileList;
nsresult status;
} mData;
ArchiveReaderOptions mOptions;
};
END_FILE_NAMESPACE

Просмотреть файл

@ -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;
}
}

Просмотреть файл

@ -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::)

Просмотреть файл

@ -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);

Просмотреть файл

@ -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

Просмотреть файл

@ -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<nsISeekableStream> 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<nsIInputStream> inputStream;
rv = mArchiveReader->GetInputStream(getter_AddRefs(inputStream));
if (NS_FAILED(rv) || !inputStream)
if (NS_FAILED(rv) || !inputStream) {
return NS_ERROR_UNEXPECTED;
}
nsRefPtr<ArchiveInputStream> stream = new ArchiveInputStream(size,
inputStream,

Просмотреть файл

@ -16,6 +16,9 @@
BEGIN_FILE_NAMESPACE
/**
* ZipFile to DOMFileCC
*/
class ArchiveZipFile : public nsDOMFileCC
{
public:

Просмотреть файл

@ -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, \

Просмотреть файл

@ -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)

Просмотреть файл

@ -0,0 +1,104 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<html>
<head>
<title>Archive Reader Non-Unicode Test</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<script type="text/javascript;version=1.7">
function createNonUnicodeData() {
const Cc = SpecialPowers.wrap(Components).classes;
const Ci = SpecialPowers.wrap(Components).interfaces;
var dirSvc = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
var testFile = dirSvc.get("ProfD", Ci.nsIFile);
testFile.append("fileArchiveReader_nonUnicode.zip");
var outStream = Cc["@mozilla.org/network/file-output-stream;1"].createInstance(Ci.nsIFileOutputStream);
outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate
0666, 0);
var binaryData = "";
for (var i = 0, len = binaryString.length / 2; i < len; ++i) {
var hex = binaryString[i * 2] + binaryString[i * 2 + 1];
binaryData += String.fromCharCode(parseInt(hex,16));
}
outStream.write(binaryData, binaryData.length);
outStream.close();
var fileList = document.getElementById('fileList');
SpecialPowers.wrap(fileList).value = testFile.path;
return fileList.files[0];
}
function test1()
{
var binaryFile = createNonUnicodeData();
var r = new ArchiveReader(binaryFile, { encoding: "ISO-8859-1" });
isnot(r, null, "ArchiveReader cannot be null");
// GetFilename
var handle = r.getFilenames();
isnot(handle, null, "ArchiveReader.getFilenames() cannot be null");
handle.onsuccess = function() {
ok(true, "ArchiveReader.getFilenames() should return a 'success'");
is(this.result instanceof Array, true, "ArchiveReader.getFilenames() should return an array");
is(this.result.length, 1, "ArchiveReader.getFilenames(): the array contains 1 item");
ok(this.reader, r, "ArchiveRequest.reader should be == ArchiveReader");
dump('Content: ' + this.result[0] + '\n');
test2();
}
}
function test2()
{
var binaryFile = createNonUnicodeData();
var r = new ArchiveReader(binaryFile, { encoding: "random stuff" });
isnot(r, null, "ArchiveReader cannot be null");
// GetFilename
var handle = r.getFilenames();
isnot(handle, null, "ArchiveReader.getFilenames() cannot be null");
handle.onsuccess = function() {
ok(true, "ArchiveReader.getFilenames() should return a 'success'");
is(this.result instanceof Array, true, "ArchiveReader.getFilenames() should return an array");
is(this.result.length, 0, "ArchiveReader.getFilenames(): the array contains 0 item");
ok(this.reader, r, "ArchiveRequest.reader should be == ArchiveReader");
finishTest();
}
}
function testSteps()
{
test1();
yield;
}
</script>
<script type="text/javascript;version=1.7" src="helpers.js"></script>
</head>
<body onload="runTest();">
<p id="display">
<input id="fileList" type="file"></input>
</p>
<script type="text/javascript;version=1.7">
var binaryString = '' +
'504B0304140000000000255D094100000000000000000000000002000000912F504B03040A0000000000285D09416BB50A5A' +
'010000000100000007000000912F9B2E747874D8504B01023F00140000000000255D09410000000000000000000000000200' +
'24000000000000001000000000000000912F0A002000000000000100180062505F1A1376CD0162505F1A1376CD01FE3F8D59' +
'1176CD01504B01023F000A0000000000285D09416BB50A5A0100000001000000070024000000000000002000000020000000' +
'912F9B2E7478740A0020000000000001001800565EF41D1376CD0107BD73631176CD0107BD73631176CD01504B0506000000' +
'0002000200AD000000460000000000';
</script>
</body>
</html>

Просмотреть файл

@ -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