From 5f8de423f190bbb79a62f804151bc24824fa32d8 Mon Sep 17 00:00:00 2001 From: "Matt A. Tobin" Date: Fri, 2 Feb 2018 04:16:08 -0500 Subject: Add m-esr52 at 52.6.0 --- dom/archivereader/ArchiveEvent.cpp | 131 +++++++++ dom/archivereader/ArchiveEvent.h | 84 ++++++ dom/archivereader/ArchiveReader.cpp | 217 +++++++++++++++ dom/archivereader/ArchiveReader.h | 119 ++++++++ dom/archivereader/ArchiveReaderCommon.h | 24 ++ dom/archivereader/ArchiveRequest.cpp | 277 +++++++++++++++++++ dom/archivereader/ArchiveRequest.h | 88 ++++++ dom/archivereader/ArchiveZipEvent.cpp | 216 +++++++++++++++ dom/archivereader/ArchiveZipEvent.h | 70 +++++ dom/archivereader/ArchiveZipFile.cpp | 402 ++++++++++++++++++++++++++++ dom/archivereader/ArchiveZipFile.h | 81 ++++++ dom/archivereader/moz.build | 30 +++ dom/archivereader/test/helpers.js | 31 +++ dom/archivereader/test/mochitest.ini | 7 + dom/archivereader/test/test_basic.html | 227 ++++++++++++++++ dom/archivereader/test/test_nonUnicode.html | 77 ++++++ dom/archivereader/test/test_zip_in_zip.html | 111 ++++++++ 17 files changed, 2192 insertions(+) create mode 100644 dom/archivereader/ArchiveEvent.cpp create mode 100644 dom/archivereader/ArchiveEvent.h create mode 100644 dom/archivereader/ArchiveReader.cpp create mode 100644 dom/archivereader/ArchiveReader.h create mode 100644 dom/archivereader/ArchiveReaderCommon.h create mode 100644 dom/archivereader/ArchiveRequest.cpp create mode 100644 dom/archivereader/ArchiveRequest.h create mode 100644 dom/archivereader/ArchiveZipEvent.cpp create mode 100644 dom/archivereader/ArchiveZipEvent.h create mode 100644 dom/archivereader/ArchiveZipFile.cpp create mode 100644 dom/archivereader/ArchiveZipFile.h create mode 100644 dom/archivereader/moz.build create mode 100644 dom/archivereader/test/helpers.js create mode 100644 dom/archivereader/test/mochitest.ini create mode 100644 dom/archivereader/test/test_basic.html create mode 100644 dom/archivereader/test/test_nonUnicode.html create mode 100644 dom/archivereader/test/test_zip_in_zip.html (limited to 'dom/archivereader') diff --git a/dom/archivereader/ArchiveEvent.cpp b/dom/archivereader/ArchiveEvent.cpp new file mode 100644 index 0000000000..b1b3dcd823 --- /dev/null +++ b/dom/archivereader/ArchiveEvent.cpp @@ -0,0 +1,131 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ArchiveEvent.h" + +#include "nsCExternalHandlerService.h" +#include "nsProxyRelease.h" + +USING_ARCHIVEREADER_NAMESPACE + +NS_IMPL_ISUPPORTS0(ArchiveItem) + +ArchiveItem::ArchiveItem() +{ + MOZ_COUNT_CTOR(ArchiveItem); +} + +ArchiveItem::~ArchiveItem() +{ + MOZ_COUNT_DTOR(ArchiveItem); +} + + +nsCString +ArchiveItem::GetType() +{ + if (mType.IsEmpty()) { + return NS_LITERAL_CSTRING("binary/octet-stream"); + } + + return mType; +} + +void +ArchiveItem::SetType(const nsCString& aType) +{ + mType = aType; +} + +ArchiveReaderEvent::ArchiveReaderEvent(ArchiveReader* aArchiveReader) +: mArchiveReader(aArchiveReader) +{ + MOZ_COUNT_CTOR(ArchiveReaderEvent); +} + +ArchiveReaderEvent::~ArchiveReaderEvent() +{ + if (!NS_IsMainThread()) { + NS_ReleaseOnMainThread(mMimeService.forget()); + } + + MOZ_COUNT_DTOR(ArchiveReaderEvent); +} + +// From the filename to the mimetype: +nsresult +ArchiveReaderEvent::GetType(nsCString& aExt, + nsCString& aMimeType) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsresult rv; + + if (mMimeService.get() == nullptr) { + mMimeService = do_GetService(NS_MIMESERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + } + + rv = mMimeService->GetTypeFromExtension(aExt, aMimeType); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +NS_IMETHODIMP +ArchiveReaderEvent::Run() +{ + return Exec(); +} + +nsresult +ArchiveReaderEvent::RunShare(nsresult aStatus) +{ + mStatus = aStatus; + + NS_DispatchToMainThread(NewRunnableMethod(this, &ArchiveReaderEvent::ShareMainThread)); + + return NS_OK; +} + +void +ArchiveReaderEvent::ShareMainThread() +{ + nsTArray> fileList; + + if (!NS_FAILED(mStatus)) { + // This extra step must run in the main thread: + for (uint32_t index = 0; index < mFileList.Length(); ++index) { + RefPtr item = mFileList[index]; + + 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) { + 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(filename, type))) { + item->SetType(type); + } + } + + // This is a File: + RefPtr file = item->GetFile(mArchiveReader); + if (file) { + fileList.AppendElement(file); + } + } + } + + mArchiveReader->Ready(fileList, mStatus); +} diff --git a/dom/archivereader/ArchiveEvent.h b/dom/archivereader/ArchiveEvent.h new file mode 100644 index 0000000000..92ac2774d0 --- /dev/null +++ b/dom/archivereader/ArchiveEvent.h @@ -0,0 +1,84 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_archivereader_domarchiveevent_h__ +#define mozilla_dom_archivereader_domarchiveevent_h__ + +#include "ArchiveReader.h" + +#include "mozilla/dom/File.h" +#include "nsISeekableStream.h" +#include "nsIMIMEService.h" + +#include "ArchiveReaderCommon.h" + +BEGIN_ARCHIVEREADER_NAMESPACE + +/** + * 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: + NS_DECL_THREADSAFE_ISUPPORTS + + ArchiveItem(); + + // Getter/Setter for the type + nsCString GetType(); + void SetType(const nsCString& aType); + + // Getter for the filename + virtual nsresult GetFilename(nsString& aFilename) = 0; + + // Generate a File + virtual already_AddRefed GetFile(ArchiveReader* aArchiveReader) = 0; + +protected: + virtual ~ArchiveItem(); + + 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(); + */ +class ArchiveReaderEvent : public Runnable +{ +public: + NS_DECL_NSIRUNNABLE + + explicit ArchiveReaderEvent(ArchiveReader* aArchiveReader); + +protected: + virtual ~ArchiveReaderEvent(); + +public: + // This must be implemented + virtual nsresult Exec() = 0; + +protected: + nsresult GetType(nsCString& aExt, + nsCString& aMimeType); + + nsresult RunShare(nsresult aStatus); + void ShareMainThread(); + +protected: // data + ArchiveReader* mArchiveReader; + + nsCOMPtr mMimeService; + + nsTArray > mFileList; // this must be populated + nsresult mStatus; +}; + +END_ARCHIVEREADER_NAMESPACE + +#endif // mozilla_dom_archivereader_domarchiveevent_h__ diff --git a/dom/archivereader/ArchiveReader.cpp b/dom/archivereader/ArchiveReader.cpp new file mode 100644 index 0000000000..f6985d989e --- /dev/null +++ b/dom/archivereader/ArchiveReader.cpp @@ -0,0 +1,217 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ArchiveReader.h" +#include "ArchiveRequest.h" +#include "ArchiveEvent.h" +#include "ArchiveZipEvent.h" + +#include "nsIURI.h" +#include "nsNetCID.h" + +#include "mozilla/dom/ArchiveReaderBinding.h" +#include "mozilla/dom/BindingDeclarations.h" +#include "mozilla/dom/File.h" +#include "mozilla/dom/EncodingUtils.h" +#include "mozilla/Preferences.h" + +using namespace mozilla; +using namespace mozilla::dom; +USING_ARCHIVEREADER_NAMESPACE + +/* static */ already_AddRefed +ArchiveReader::Constructor(const GlobalObject& aGlobal, + Blob& aBlob, + const ArchiveReaderOptions& aOptions, + ErrorResult& aError) +{ + nsCOMPtr window = do_QueryInterface(aGlobal.GetAsSupports()); + if (!window) { + aError.Throw(NS_ERROR_UNEXPECTED); + return nullptr; + } + + nsAutoCString encoding; + if (!EncodingUtils::FindEncodingForLabelNoReplacement(aOptions.mEncoding, + encoding)) { + aError.ThrowRangeError(aOptions.mEncoding); + return nullptr; + } + + RefPtr reader = + new ArchiveReader(aBlob, window, encoding); + return reader.forget(); +} + +ArchiveReader::ArchiveReader(Blob& aBlob, nsPIDOMWindowInner* aWindow, + const nsACString& aEncoding) + : mBlobImpl(aBlob.Impl()) + , mWindow(aWindow) + , mStatus(NOT_STARTED) + , mEncoding(aEncoding) +{ + MOZ_ASSERT(aWindow); +} + +ArchiveReader::~ArchiveReader() +{ +} + +/* virtual */ JSObject* +ArchiveReader::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return ArchiveReaderBinding::Wrap(aCx, this, aGivenProto); +} + +nsresult +ArchiveReader::RegisterRequest(ArchiveRequest* aRequest) +{ + switch (mStatus) { + // Append to the list and let's start to work: + case NOT_STARTED: + mRequests.AppendElement(aRequest); + return OpenArchive(); + + // Just append to the list: + case WORKING: + mRequests.AppendElement(aRequest); + return NS_OK; + + // Return data! + case READY: + RequestReady(aRequest); + return NS_OK; + } + + NS_ASSERTION(false, "unexpected mStatus value"); + return NS_OK; +} + +// This returns the input stream +nsresult +ArchiveReader::GetInputStream(nsIInputStream** aInputStream) +{ + // Getting the input stream + ErrorResult rv; + mBlobImpl->GetInternalStream(aInputStream, rv); + if (NS_WARN_IF(rv.Failed())) { + return rv.StealNSResult(); + } + + return NS_OK; +} + +nsresult +ArchiveReader::GetSize(uint64_t* aSize) +{ + ErrorResult rv; + *aSize = mBlobImpl->GetSize(rv); + return rv.StealNSResult(); +} + +// Here we open the archive: +nsresult +ArchiveReader::OpenArchive() +{ + mStatus = WORKING; + nsresult rv; + + // Target: + nsCOMPtr target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); + NS_ASSERTION(target, "Must have stream transport service"); + + // Here a Event to make everything async: + RefPtr event; + + /* FIXME: If we want to support more than 1 format we should check the content type here: */ + event = new ArchiveReaderZipEvent(this, mEncoding); + rv = target->Dispatch(event, NS_DISPATCH_NORMAL); + NS_ENSURE_SUCCESS(rv, rv); + + // In order to be sure that this object exists when the event finishes its task, + // we increase the refcount here: + AddRef(); + + return NS_OK; +} + +// Data received from the dispatched event: +void +ArchiveReader::Ready(nsTArray>& aFileList, + nsresult aStatus) +{ + mStatus = READY; + + // Let's store the values: + mData.fileList = aFileList; + mData.status = aStatus; + + // Propagate the results: + for (uint32_t index = 0; index < mRequests.Length(); ++index) { + RefPtr request = mRequests[index]; + RequestReady(request); + } + + mRequests.Clear(); + + // The async operation is concluded, we can decrease the reference: + Release(); +} + +void +ArchiveReader::RequestReady(ArchiveRequest* aRequest) +{ + // The request will do the rest: + aRequest->ReaderReady(mData.fileList, mData.status); +} + +already_AddRefed +ArchiveReader::GetFilenames() +{ + RefPtr request = GenerateArchiveRequest(); + request->OpGetFilenames(); + + return request.forget(); +} + +already_AddRefed +ArchiveReader::GetFile(const nsAString& filename) +{ + RefPtr request = GenerateArchiveRequest(); + request->OpGetFile(filename); + + return request.forget(); +} + +already_AddRefed +ArchiveReader::GetFiles() +{ + RefPtr request = GenerateArchiveRequest(); + request->OpGetFiles(); + + return request.forget(); +} + +already_AddRefed +ArchiveReader::GenerateArchiveRequest() +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + return ArchiveRequest::Create(mWindow, this); +} + +NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ArchiveReader, + mBlobImpl, + mWindow, + mData.fileList, + mRequests) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ArchiveReader) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(ArchiveReader) +NS_IMPL_CYCLE_COLLECTING_RELEASE(ArchiveReader) diff --git a/dom/archivereader/ArchiveReader.h b/dom/archivereader/ArchiveReader.h new file mode 100644 index 0000000000..687392e56b --- /dev/null +++ b/dom/archivereader/ArchiveReader.h @@ -0,0 +1,119 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_archivereader_domarchivereader_h__ +#define mozilla_dom_archivereader_domarchivereader_h__ + +#include "nsWrapperCache.h" + +#include "ArchiveReaderCommon.h" + +#include "nsCOMArray.h" +#include "nsIChannel.h" +#include "mozilla/Attributes.h" + +namespace mozilla { +namespace dom { +struct ArchiveReaderOptions; +class Blob; +class BlobImpl; +class File; +class GlobalObject; +} // namespace dom +} // namespace mozilla + +BEGIN_ARCHIVEREADER_NAMESPACE + +class ArchiveRequest; + +/** + * This is the ArchiveReader object + */ +class ArchiveReader final : public nsISupports, + public nsWrapperCache +{ +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ArchiveReader) + + static already_AddRefed + Constructor(const GlobalObject& aGlobal, Blob& aBlob, + const ArchiveReaderOptions& aOptions, ErrorResult& aError); + + ArchiveReader(Blob& aBlob, nsPIDOMWindowInner* aWindow, + const nsACString& aEncoding); + + nsPIDOMWindowInner* GetParentObject() const + { + return mWindow; + } + + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + + already_AddRefed GetFilenames(); + already_AddRefed GetFile(const nsAString& filename); + already_AddRefed GetFiles(); + + nsresult GetInputStream(nsIInputStream** aInputStream); + nsresult GetSize(uint64_t* aSize); + +public: // for the ArchiveRequest: + nsresult RegisterRequest(ArchiveRequest* aRequest); + +public: // For events: + BlobImpl* GetBlobImpl() const + { + return mBlobImpl; + } + + void Ready(nsTArray>& aFileList, nsresult aStatus); + +private: + ~ArchiveReader(); + + already_AddRefed GenerateArchiveRequest(); + + nsresult OpenArchive(); + + void RequestReady(ArchiveRequest* aRequest); + +protected: + // The archive blob/file + RefPtr mBlobImpl; + + // The window is needed by the requests + nsCOMPtr mWindow; + + // Are we ready to return data? + enum { + NOT_STARTED = 0, + WORKING, + READY + } mStatus; + + // State of the read: + enum { + Header, // We are collecting the header: 30bytes + Name, // The name length is contained in the header + Data, // The length of the data segment COULD be written in the header + Search // ... if the data length is unknown (== 0) we wait until we read a new header + } mReadStatus; + + // List of requests to be processed + nsTArray > mRequests; + + // Everything related to the blobs and the status: + struct { + nsTArray> fileList; + nsresult status; + } mData; + + nsCString mEncoding; +}; + +END_ARCHIVEREADER_NAMESPACE + +#endif // mozilla_dom_archivereader_domarchivereader_h__ diff --git a/dom/archivereader/ArchiveReaderCommon.h b/dom/archivereader/ArchiveReaderCommon.h new file mode 100644 index 0000000000..ab40c3bef4 --- /dev/null +++ b/dom/archivereader/ArchiveReaderCommon.h @@ -0,0 +1,24 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_archivereader_archivereader_h +#define mozilla_dom_archivereader_archivereader_h + +#include "mozilla/DOMEventTargetHelper.h" +#include "nsCOMPtr.h" +#include "nsCycleCollectionParticipant.h" +#include "nsDebug.h" +#include "nsString.h" +#include "nsTArray.h" + +#define BEGIN_ARCHIVEREADER_NAMESPACE \ + namespace mozilla { namespace dom { namespace archivereader { +#define END_ARCHIVEREADER_NAMESPACE \ + } /* namespace archivereader */ } /* namespace dom */ } /* namespace mozilla */ +#define USING_ARCHIVEREADER_NAMESPACE \ + using namespace mozilla::dom::archivereader; + +#endif // mozilla_dom_archivereader_archivereadercommon_h diff --git a/dom/archivereader/ArchiveRequest.cpp b/dom/archivereader/ArchiveRequest.cpp new file mode 100644 index 0000000000..ec16868042 --- /dev/null +++ b/dom/archivereader/ArchiveRequest.cpp @@ -0,0 +1,277 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ArchiveRequest.h" + +#include "mozilla/EventDispatcher.h" +#include "mozilla/dom/ArchiveRequestBinding.h" +#include "mozilla/dom/ScriptSettings.h" +#include "nsContentUtils.h" + +using namespace mozilla; + +USING_ARCHIVEREADER_NAMESPACE + +/** + * Class used to make asynchronous the ArchiveRequest. + */ +class ArchiveRequestEvent : public Runnable +{ +public: + NS_DECL_NSIRUNNABLE + + explicit ArchiveRequestEvent(ArchiveRequest* aRequest) + : mRequest(aRequest) + { + MOZ_COUNT_CTOR(ArchiveRequestEvent); + } + +protected: + ~ArchiveRequestEvent() + { + MOZ_COUNT_DTOR(ArchiveRequestEvent); + } + +private: //data + RefPtr mRequest; +}; + +NS_IMETHODIMP +ArchiveRequestEvent::Run() +{ + MOZ_ASSERT(mRequest, "the request is not longer valid"); + mRequest->Run(); + return NS_OK; +} + +// ArchiveRequest + +ArchiveRequest::ArchiveRequest(nsPIDOMWindowInner* aWindow, + ArchiveReader* aReader) +: DOMRequest(aWindow), + mArchiveReader(aReader) +{ + MOZ_ASSERT(aReader); + + MOZ_COUNT_CTOR(ArchiveRequest); + + /* An event to make this request asynchronous: */ + RefPtr event = new ArchiveRequestEvent(this); + NS_DispatchToCurrentThread(event); +} + +ArchiveRequest::~ArchiveRequest() +{ + MOZ_COUNT_DTOR(ArchiveRequest); +} + +nsresult +ArchiveRequest::PreHandleEvent(EventChainPreVisitor& aVisitor) +{ + aVisitor.mCanHandle = true; + aVisitor.mParentTarget = nullptr; + return NS_OK; +} + +/* virtual */ JSObject* +ArchiveRequest::WrapObject(JSContext* aCx, JS::Handle aGivenProto) +{ + return ArchiveRequestBinding::Wrap(aCx, this, aGivenProto); +} + +ArchiveReader* +ArchiveRequest::Reader() const +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + return mArchiveReader; +} + +// Here the request is processed: +void +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)) { + FireError(rv); + } +} + +void +ArchiveRequest::OpGetFilenames() +{ + mOperation = GetFilenames; +} + +void +ArchiveRequest::OpGetFile(const nsAString& aFilename) +{ + mOperation = GetFile; + mFilename = aFilename; +} + +void +ArchiveRequest::OpGetFiles() +{ + mOperation = GetFiles; +} + +nsresult +ArchiveRequest::ReaderReady(nsTArray>& aFileList, + nsresult aStatus) +{ + if (NS_FAILED(aStatus)) { + FireError(aStatus); + return NS_OK; + } + + nsresult rv; + + AutoJSAPI jsapi; + if (NS_WARN_IF(!jsapi.Init(GetOwner()))) { + return NS_ERROR_UNEXPECTED; + } + JSContext* cx = jsapi.cx(); + + JS::Rooted result(cx); + switch (mOperation) { + case GetFilenames: + rv = GetFilenamesResult(cx, result.address(), aFileList); + break; + + case GetFile: + rv = GetFileResult(cx, &result, aFileList); + break; + + case GetFiles: + rv = GetFilesResult(cx, &result, aFileList); + break; + + default: + rv = NS_ERROR_UNEXPECTED; + break; + } + + if (NS_FAILED(rv)) { + NS_WARNING("Get*Result failed!"); + } + + if (NS_SUCCEEDED(rv)) { + FireSuccess(result); + } + else { + FireError(rv); + } + + return NS_OK; +} + +nsresult +ArchiveRequest::GetFilenamesResult(JSContext* aCx, + JS::Value* aValue, + nsTArray>& aFileList) +{ + JS::Rooted array(aCx, JS_NewArrayObject(aCx, aFileList.Length())); + + if (!array) { + return NS_ERROR_OUT_OF_MEMORY; + } + + JS::Rooted str(aCx); + for (uint32_t i = 0; i < aFileList.Length(); ++i) { + RefPtr file = aFileList[i]; + + nsString filename; + file->GetName(filename); + + str = JS_NewUCStringCopyZ(aCx, filename.get()); + NS_ENSURE_TRUE(str, NS_ERROR_OUT_OF_MEMORY); + + if (!JS_DefineElement(aCx, array, i, str, JSPROP_ENUMERATE)) { + return NS_ERROR_FAILURE; + } + } + + if (!JS_FreezeObject(aCx, array)) { + return NS_ERROR_FAILURE; + } + + aValue->setObject(*array); + return NS_OK; +} + +nsresult +ArchiveRequest::GetFileResult(JSContext* aCx, + JS::MutableHandle aValue, + nsTArray>& aFileList) +{ + for (uint32_t i = 0; i < aFileList.Length(); ++i) { + RefPtr file = aFileList[i]; + + nsString filename; + file->GetName(filename); + + if (filename == mFilename) { + if (!ToJSValue(aCx, file, aValue)) { + return NS_ERROR_FAILURE; + } + + return NS_OK; + } + } + + return NS_ERROR_FAILURE; +} + +nsresult +ArchiveRequest::GetFilesResult(JSContext* aCx, + JS::MutableHandle aValue, + nsTArray>& aFileList) +{ + JS::Rooted array(aCx, JS_NewArrayObject(aCx, aFileList.Length())); + if (!array) { + return NS_ERROR_OUT_OF_MEMORY; + } + + for (uint32_t i = 0; i < aFileList.Length(); ++i) { + RefPtr file = aFileList[i]; + + JS::Rooted value(aCx); + if (!ToJSValue(aCx, file, &value)) { + return NS_ERROR_FAILURE; + } + + if (!JS_DefineElement(aCx, array, i, value, JSPROP_ENUMERATE)) { + return NS_ERROR_FAILURE; + } + } + + aValue.setObject(*array); + return NS_OK; +} + +// static +already_AddRefed +ArchiveRequest::Create(nsPIDOMWindowInner* aOwner, + ArchiveReader* aReader) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + RefPtr request = new ArchiveRequest(aOwner, aReader); + + return request.forget(); +} + +NS_IMPL_CYCLE_COLLECTION_INHERITED(ArchiveRequest, DOMRequest, + mArchiveReader) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ArchiveRequest) +NS_INTERFACE_MAP_END_INHERITING(DOMRequest) + +NS_IMPL_ADDREF_INHERITED(ArchiveRequest, DOMRequest) +NS_IMPL_RELEASE_INHERITED(ArchiveRequest, DOMRequest) diff --git a/dom/archivereader/ArchiveRequest.h b/dom/archivereader/ArchiveRequest.h new file mode 100644 index 0000000000..3988f1aa15 --- /dev/null +++ b/dom/archivereader/ArchiveRequest.h @@ -0,0 +1,88 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_archivereader_domarchiverequest_h__ +#define mozilla_dom_archivereader_domarchiverequest_h__ + +#include "mozilla/Attributes.h" +#include "ArchiveReader.h" +#include "DOMRequest.h" + +#include "ArchiveReaderCommon.h" + +namespace mozilla { +class EventChainPreVisitor; +} // namespace mozilla + +BEGIN_ARCHIVEREADER_NAMESPACE + +/** + * This is the ArchiveRequest that handles any operation + * related to ArchiveReader + */ +class ArchiveRequest : public mozilla::dom::DOMRequest +{ +public: + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + + ArchiveReader* Reader() const; + + NS_DECL_ISUPPORTS_INHERITED + + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ArchiveRequest, DOMRequest) + + ArchiveRequest(nsPIDOMWindowInner* aWindow, + ArchiveReader* aReader); + + // nsIDOMEventTarget + virtual nsresult PreHandleEvent(EventChainPreVisitor& aVisitor) override; + +public: + // This is called by the DOMArchiveRequestEvent + void Run(); + + // Set the types for this request + void OpGetFilenames(); + void OpGetFile(const nsAString& aFilename); + void OpGetFiles(); + + nsresult ReaderReady(nsTArray>& aFileList, nsresult aStatus); + +public: // static + static already_AddRefed Create(nsPIDOMWindowInner* aOwner, + ArchiveReader* aReader); + +private: + ~ArchiveRequest(); + + nsresult GetFilenamesResult(JSContext* aCx, + JS::Value* aValue, + nsTArray>& aFileList); + nsresult GetFileResult(JSContext* aCx, + JS::MutableHandle aValue, + nsTArray>& aFileList); + nsresult GetFilesResult(JSContext* aCx, + JS::MutableHandle aValue, + nsTArray>& aFileList); + +protected: + // The reader: + RefPtr mArchiveReader; + + // The operation: + enum { + GetFilenames, + GetFile, + GetFiles + } mOperation; + + // The filename (needed by GetFile): + nsString mFilename; +}; + +END_ARCHIVEREADER_NAMESPACE + +#endif // mozilla_dom_archivereader_domarchiverequest_h__ diff --git a/dom/archivereader/ArchiveZipEvent.cpp b/dom/archivereader/ArchiveZipEvent.cpp new file mode 100644 index 0000000000..56251eef66 --- /dev/null +++ b/dom/archivereader/ArchiveZipEvent.cpp @@ -0,0 +1,216 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ArchiveZipEvent.h" +#include "ArchiveZipFile.h" + +#include "nsContentUtils.h" +#include "nsCExternalHandlerService.h" + +#include "mozilla/UniquePtr.h" + +using namespace mozilla; +using namespace mozilla::dom; + +USING_ARCHIVEREADER_NAMESPACE + +#ifndef PATH_MAX +# define PATH_MAX 65536 // The filename length is stored in 2 bytes +#endif + +ArchiveZipItem::ArchiveZipItem(const char* aFilename, + const ZipCentral& aCentralStruct, + const nsACString& aEncoding) +: mFilename(aFilename), + mCentralStruct(aCentralStruct), + mEncoding(aEncoding) +{ + MOZ_COUNT_CTOR(ArchiveZipItem); +} + +ArchiveZipItem::~ArchiveZipItem() +{ + MOZ_COUNT_DTOR(ArchiveZipItem); +} + +nsresult +ArchiveZipItem::ConvertFilename() +{ + if (mEncoding.IsEmpty()) { + return NS_ERROR_FAILURE; + } + + nsString filenameU; + nsresult rv = nsContentUtils::ConvertStringFromEncoding( + mEncoding, + mFilename, filenameU); + NS_ENSURE_SUCCESS(rv, rv); + + if (filenameU.IsEmpty()) { + return NS_ERROR_FAILURE; + } + + mFilenameU = filenameU; + return NS_OK; +} + +nsresult +ArchiveZipItem::GetFilename(nsString& 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 File: +already_AddRefed +ArchiveZipItem::GetFile(ArchiveReader* aArchiveReader) +{ + nsString filename; + + if (NS_FAILED(GetFilename(filename))) { + return nullptr; + } + + RefPtr file = dom::File::Create(aArchiveReader, + new ArchiveZipBlobImpl(filename, + NS_ConvertUTF8toUTF16(GetType()), + StrToInt32(mCentralStruct.orglen), + mCentralStruct, aArchiveReader->GetBlobImpl())); + MOZ_ASSERT(file); + return file.forget(); +} + +uint32_t +ArchiveZipItem::StrToInt32(const uint8_t* aStr) +{ + return (uint32_t)( (aStr [0] << 0) | + (aStr [1] << 8) | + (aStr [2] << 16) | + (aStr [3] << 24) ); +} + +uint16_t +ArchiveZipItem::StrToInt16(const uint8_t* aStr) +{ + return (uint16_t) ((aStr [0]) | (aStr [1] << 8)); +} + +// ArchiveReaderZipEvent + +ArchiveReaderZipEvent::ArchiveReaderZipEvent(ArchiveReader* aArchiveReader, + const nsACString& aEncoding) +: ArchiveReaderEvent(aArchiveReader), + mEncoding(aEncoding) +{ +} + +// NOTE: this runs in a different thread!! +nsresult +ArchiveReaderZipEvent::Exec() +{ + uint32_t centralOffset(0); + nsresult rv; + + nsCOMPtr inputStream; + rv = mArchiveReader->GetInputStream(getter_AddRefs(inputStream)); + if (NS_FAILED(rv) || !inputStream) { + return RunShare(NS_ERROR_UNEXPECTED); + } + + // From the input stream to a seekable stream + nsCOMPtr seekableStream; + seekableStream = do_QueryInterface(inputStream); + if (!seekableStream) { + return RunShare(NS_ERROR_UNEXPECTED); + } + + uint64_t size; + rv = mArchiveReader->GetSize(&size); + if (NS_FAILED(rv)) { + return RunShare(NS_ERROR_UNEXPECTED); + } + + // Reading backward.. looking for the ZipEnd signature + for (uint64_t curr = size - ZIPEND_SIZE; curr > 4; --curr) { + seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, curr); + + uint8_t buffer[ZIPEND_SIZE]; + uint32_t ret; + + rv = inputStream->Read((char*)buffer, sizeof(buffer), &ret); + if (NS_FAILED(rv) || ret != sizeof(buffer)) { + return RunShare(NS_ERROR_UNEXPECTED); + } + + // Here we are: + if (ArchiveZipItem::StrToInt32(buffer) == ENDSIG) { + centralOffset = ArchiveZipItem::StrToInt32(((ZipEnd*)buffer)->offset_central_dir); + break; + } + } + + // No central Offset + if (!centralOffset || centralOffset >= size - ZIPEND_SIZE) { + return RunShare(NS_ERROR_FAILURE); + } + + // Seek to the first central directory: + seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, centralOffset); + + // For each central directory: + while (centralOffset <= size - ZIPCENTRAL_SIZE) { + ZipCentral centralStruct; + uint32_t ret; + + rv = inputStream->Read((char*)¢ralStruct, ZIPCENTRAL_SIZE, &ret); + if (NS_FAILED(rv) || ret != ZIPCENTRAL_SIZE) { + return RunShare(NS_ERROR_UNEXPECTED); + } + + uint16_t filenameLen = ArchiveZipItem::StrToInt16(centralStruct.filename_len); + uint16_t extraLen = ArchiveZipItem::StrToInt16(centralStruct.extrafield_len); + uint16_t commentLen = ArchiveZipItem::StrToInt16(centralStruct.commentfield_len); + + // Point to the next item at the top of loop + centralOffset += ZIPCENTRAL_SIZE + filenameLen + extraLen + commentLen; + if (filenameLen == 0 || filenameLen >= PATH_MAX || centralOffset >= size) { + return RunShare(NS_ERROR_FILE_CORRUPTED); + } + + // Read the name: + auto filename = MakeUnique(filenameLen + 1); + rv = inputStream->Read(filename.get(), filenameLen, &ret); + if (NS_FAILED(rv) || ret != filenameLen) { + return RunShare(NS_ERROR_UNEXPECTED); + } + + filename[filenameLen] = 0; + + // We ignore the directories: + if (filename[filenameLen - 1] != '/') { + mFileList.AppendElement(new ArchiveZipItem(filename.get(), centralStruct, + mEncoding)); + } + + // Ignore the rest + seekableStream->Seek(nsISeekableStream::NS_SEEK_CUR, extraLen + commentLen); + } + + return RunShare(NS_OK); +} diff --git a/dom/archivereader/ArchiveZipEvent.h b/dom/archivereader/ArchiveZipEvent.h new file mode 100644 index 0000000000..2c25740f36 --- /dev/null +++ b/dom/archivereader/ArchiveZipEvent.h @@ -0,0 +1,70 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_archivereader_domarchivezipevent_h__ +#define mozilla_dom_archivereader_domarchivezipevent_h__ + +#include "mozilla/Attributes.h" +#include "ArchiveEvent.h" + +#include "ArchiveReaderCommon.h" +#include "zipstruct.h" + +BEGIN_ARCHIVEREADER_NAMESPACE + +/** + * ArchiveZipItem - ArchiveItem for ArchiveReaderZipEvent + */ +class ArchiveZipItem : public ArchiveItem +{ +public: + ArchiveZipItem(const char* aFilename, + const ZipCentral& aCentralStruct, + const nsACString& aEncoding); +protected: + virtual ~ArchiveZipItem(); + +public: + nsresult GetFilename(nsString& aFilename) override; + + // From zipItem to File: + virtual already_AddRefed + GetFile(ArchiveReader* aArchiveReader) override; + +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; + + nsCString mEncoding; +}; + +/** + * ArchiveReaderEvent implements the ArchiveReaderEvent for the ZIP format + */ +class ArchiveReaderZipEvent : public ArchiveReaderEvent +{ +public: + ArchiveReaderZipEvent(ArchiveReader* aArchiveReader, + const nsACString& aEncoding); + + nsresult Exec() override; + +private: + nsCString mEncoding; +}; + +END_ARCHIVEREADER_NAMESPACE + +#endif // mozilla_dom_archivereader_domarchivezipevent_h__ diff --git a/dom/archivereader/ArchiveZipFile.cpp b/dom/archivereader/ArchiveZipFile.cpp new file mode 100644 index 0000000000..d374fe91f0 --- /dev/null +++ b/dom/archivereader/ArchiveZipFile.cpp @@ -0,0 +1,402 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ArchiveZipFile.h" +#include "ArchiveZipEvent.h" + +#include "nsIInputStream.h" +#include "zlib.h" +#include "mozilla/Attributes.h" +#include "mozilla/dom/File.h" + +using namespace mozilla::dom; +USING_ARCHIVEREADER_NAMESPACE + +#define ZIP_CHUNK 16384 + +/** + * Input stream object for zip files + */ +class ArchiveInputStream final : public nsIInputStream, + public nsISeekableStream +{ +public: + ArchiveInputStream(uint64_t aParentSize, + nsIInputStream* aInputStream, + nsString& aFilename, + uint32_t aStart, + uint32_t aLength, + ZipCentral& aCentral) + : mCentral(aCentral), + mFilename(aFilename), + mStart(aStart), + mLength(aLength), + mStatus(NotStarted) + { + MOZ_COUNT_CTOR(ArchiveInputStream); + + // Reset the data: + memset(&mData, 0, sizeof(mData)); + + mData.parentSize = aParentSize; + mData.inputStream = aInputStream; + } + + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIINPUTSTREAM + NS_DECL_NSISEEKABLESTREAM + +private: + virtual ~ArchiveInputStream() + { + MOZ_COUNT_DTOR(ArchiveInputStream); + Close(); + } + + nsresult Init(); + +private: // data + ZipCentral mCentral; + nsString mFilename; + uint32_t mStart; + uint32_t mLength; + + z_stream mZs; + + enum { + NotStarted, + Started, + Done + } mStatus; + + struct { + uint64_t parentSize; + nsCOMPtr inputStream; + + unsigned char input[ZIP_CHUNK]; + uint32_t sizeToBeRead; + uint32_t cursor; + + bool compressed; // a zip file can contain stored or compressed files + } mData; +}; + +NS_IMPL_ISUPPORTS(ArchiveInputStream, + nsIInputStream, + nsISeekableStream) + +nsresult +ArchiveInputStream::Init() +{ + nsresult rv; + + memset(&mZs, 0, sizeof(z_stream)); + int zerr = inflateInit2(&mZs, -MAX_WBITS); + 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 (mData.parentSize < ZIPLOCAL_SIZE || + offset > mData.parentSize - ZIPLOCAL_SIZE) { + return NS_ERROR_UNEXPECTED; + } + + // From the input stream to a seekable stream + nsCOMPtr seekableStream; + seekableStream = do_QueryInterface(mData.inputStream); + if (!seekableStream) { + return NS_ERROR_UNEXPECTED; + } + + // Seek + read the ZipLocal struct + seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, offset); + uint8_t buffer[ZIPLOCAL_SIZE]; + uint32_t ret; + + rv = mData.inputStream->Read((char*)buffer, ZIPLOCAL_SIZE, &ret); + if (NS_FAILED(rv) || ret != ZIPLOCAL_SIZE) { + return NS_ERROR_UNEXPECTED; + } + + // Signature check: + if (ArchiveZipItem::StrToInt32(buffer) != LOCALSIG) { + return NS_ERROR_UNEXPECTED; + } + + ZipLocal local; + memcpy(&local, buffer, ZIPLOCAL_SIZE); + + // Seek to the real data: + offset += ZIPLOCAL_SIZE + + ArchiveZipItem::StrToInt16(local.filename_len) + + ArchiveZipItem::StrToInt16(local.extrafield_len); + + // The file is corrupt if there is not enough data + if (mData.parentSize < mData.sizeToBeRead || + offset > mData.parentSize - mData.sizeToBeRead) { + return NS_ERROR_UNEXPECTED; + } + + // Data starts here: + seekableStream->Seek(nsISeekableStream::NS_SEEK_SET, offset); + + // The file is compressed or not? + mData.compressed = (ArchiveZipItem::StrToInt16(mCentral.method) != 0); + + // We have to skip the first mStart bytes: + if (mStart != 0) { + rv = Seek(NS_SEEK_SET, mStart); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +NS_IMETHODIMP +ArchiveInputStream::Close() +{ + if (mStatus != NotStarted) { + inflateEnd(&mZs); + mStatus = NotStarted; + } + + return NS_OK; +} + +NS_IMETHODIMP +ArchiveInputStream::Available(uint64_t* _retval) +{ + *_retval = mLength - mData.cursor - mStart; + return NS_OK; +} + +NS_IMETHODIMP +ArchiveInputStream::Read(char* aBuffer, + uint32_t aCount, + uint32_t* _retval) +{ + NS_ENSURE_ARG_POINTER(aBuffer); + NS_ENSURE_ARG_POINTER(_retval); + + nsresult rv; + + // This is the first time: + if (mStatus == NotStarted) { + mStatus = Started; + + rv = Init(); + 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; + } + + // Nothing more can be read + if (mStatus == Done) { + *_retval = 0; + return NS_OK; + } + + // Stored file: + if (!mData.compressed) { + rv = mData.inputStream->Read(aBuffer, + (mData.sizeToBeRead > aCount ? + aCount : mData.sizeToBeRead), + _retval); + if (NS_SUCCEEDED(rv)) { + mData.sizeToBeRead -= *_retval; + mData.cursor += *_retval; + + if (mData.sizeToBeRead == 0) { + mStatus = Done; + } + } + + return rv; + } + + // We have nothing ready to be processed: + 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)) { + return rv; + } + + // Terminator: + if (ret == 0) { + *_retval = 0; + return NS_OK; + } + + mData.sizeToBeRead -= ret; + mZs.avail_in = ret; + mZs.next_in = mData.input; + } + + mZs.avail_out = aCount; + 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) { + return NS_ERROR_UNEXPECTED; + } + + if (ret == Z_STREAM_END) { + mStatus = Done; + } + + *_retval = aCount - mZs.avail_out; + mData.cursor += *_retval; + return NS_OK; +} + +NS_IMETHODIMP +ArchiveInputStream::ReadSegments(nsWriteSegmentFun aWriter, + void* aClosure, + uint32_t aCount, + uint32_t* _retval) +{ + // don't have a buffer to read from, so this better not be called! + NS_NOTREACHED("Consumers should be using Read()!"); + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +ArchiveInputStream::IsNonBlocking(bool* _retval) +{ + // We are blocking + *_retval = false; + return NS_OK; +} + +NS_IMETHODIMP +ArchiveInputStream::Seek(int32_t aWhence, int64_t aOffset) +{ + int64_t pos = aOffset; + + switch (aWhence) { + case NS_SEEK_SET: + break; + + case NS_SEEK_CUR: + pos += mData.cursor; + break; + + case NS_SEEK_END: + pos += mLength; + break; + + default: + NS_NOTREACHED("unexpected whence value"); + return NS_ERROR_UNEXPECTED; + } + + if (pos == int64_t(mData.cursor)) { + return NS_OK; + } + + if (pos < 0 || pos >= mLength) { + return NS_ERROR_FAILURE; + } + + // We have to terminate the previous operation: + nsresult rv; + if (mStatus != NotStarted) { + rv = Close(); + NS_ENSURE_SUCCESS(rv, rv); + } + + // Reset the cursor: + mData.cursor = 0; + + // Note: This code is heavy but inflate does not have any seek() support: + uint32_t ret; + char buffer[1024]; + while (pos > 0) { + rv = Read(buffer, pos > int64_t(sizeof(buffer)) ? sizeof(buffer) : pos, &ret); + if (NS_FAILED(rv)) { + return rv; + } + + if (ret == 0) { + return NS_ERROR_UNEXPECTED; + } + + pos -= ret; + } + + return NS_OK; +} + +NS_IMETHODIMP +ArchiveInputStream::Tell(int64_t *aResult) +{ + *aResult = mData.cursor; + return NS_OK; +} + +NS_IMETHODIMP +ArchiveInputStream::SetEOF() +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +// ArchiveZipBlobImpl + +void +ArchiveZipBlobImpl::GetInternalStream(nsIInputStream** aStream, + ErrorResult& aRv) +{ + if (mLength > INT32_MAX) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } + + uint64_t size = mBlobImpl->GetSize(aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + nsCOMPtr inputStream; + mBlobImpl->GetInternalStream(getter_AddRefs(inputStream), aRv); + if (NS_WARN_IF(aRv.Failed())) { + return; + } + + RefPtr stream = new ArchiveInputStream(size, + inputStream, + mFilename, + mStart, + mLength, + mCentral); + + stream.forget(aStream); +} + +already_AddRefed +ArchiveZipBlobImpl::CreateSlice(uint64_t aStart, + uint64_t aLength, + const nsAString& aContentType, + mozilla::ErrorResult& aRv) +{ + RefPtr impl = + new ArchiveZipBlobImpl(mFilename, mContentType, aStart, mLength, mCentral, + mBlobImpl); + return impl.forget(); +} + +NS_IMPL_ISUPPORTS_INHERITED0(ArchiveZipBlobImpl, BlobImpl) diff --git a/dom/archivereader/ArchiveZipFile.h b/dom/archivereader/ArchiveZipFile.h new file mode 100644 index 0000000000..d1196486c4 --- /dev/null +++ b/dom/archivereader/ArchiveZipFile.h @@ -0,0 +1,81 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_archivereader_domarchivefile_h__ +#define mozilla_dom_archivereader_domarchivefile_h__ + +#include "mozilla/Attributes.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/File.h" + +#include "ArchiveReader.h" + +#include "ArchiveReaderCommon.h" +#include "zipstruct.h" + +BEGIN_ARCHIVEREADER_NAMESPACE + +/** + * ArchiveZipBlobImpl to BlobImpl + */ +class ArchiveZipBlobImpl : public BlobImplBase +{ +public: + NS_DECL_ISUPPORTS_INHERITED + + ArchiveZipBlobImpl(const nsAString& aName, + const nsAString& aContentType, + uint64_t aLength, + ZipCentral& aCentral, + BlobImpl* aBlobImpl) + : BlobImplBase(aName, aContentType, aLength), + mCentral(aCentral), + mBlobImpl(aBlobImpl), + mFilename(aName) + { + MOZ_ASSERT(mBlobImpl); + MOZ_COUNT_CTOR(ArchiveZipBlobImpl); + } + + ArchiveZipBlobImpl(const nsAString& aName, + const nsAString& aContentType, + uint64_t aStart, + uint64_t aLength, + ZipCentral& aCentral, + BlobImpl* aBlobImpl) + : BlobImplBase(aContentType, aStart, aLength), + mCentral(aCentral), + mBlobImpl(aBlobImpl), + mFilename(aName) + { + MOZ_ASSERT(mBlobImpl); + MOZ_COUNT_CTOR(ArchiveZipBlobImpl); + } + + // Overrides: + virtual void GetInternalStream(nsIInputStream** aInputStream, + ErrorResult& aRv) override; + +protected: + virtual ~ArchiveZipBlobImpl() + { + MOZ_COUNT_DTOR(ArchiveZipBlobImpl); + } + + virtual already_AddRefed + CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType, + mozilla::ErrorResult& aRv) override; + +private: // Data + ZipCentral mCentral; + RefPtr mBlobImpl; + + nsString mFilename; +}; + +END_ARCHIVEREADER_NAMESPACE + +#endif // mozilla_dom_archivereader_domarchivefile_h__ diff --git a/dom/archivereader/moz.build b/dom/archivereader/moz.build new file mode 100644 index 0000000000..57dbfa1210 --- /dev/null +++ b/dom/archivereader/moz.build @@ -0,0 +1,30 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +EXPORTS.mozilla.dom.archivereader += [ + 'ArchiveEvent.h', + 'ArchiveReader.h', + 'ArchiveReaderCommon.h', + 'ArchiveRequest.h', + 'ArchiveZipEvent.h', + 'ArchiveZipFile.h', +] + +UNIFIED_SOURCES += [ + 'ArchiveEvent.cpp', + 'ArchiveReader.cpp', + 'ArchiveRequest.cpp', + 'ArchiveZipEvent.cpp', + 'ArchiveZipFile.cpp', +] + +LOCAL_INCLUDES += [ + '../base', +] + +FINAL_LIBRARY = 'xul' + +MOCHITEST_MANIFESTS += ['test/mochitest.ini'] diff --git a/dom/archivereader/test/helpers.js b/dom/archivereader/test/helpers.js new file mode 100644 index 0000000000..3ba4304360 --- /dev/null +++ b/dom/archivereader/test/helpers.js @@ -0,0 +1,31 @@ +/** + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +var testGenerator; + +function runTest() +{ + SimpleTest.waitForExplicitFinish(); + + SpecialPowers.pushPrefEnv({'set': [ ["dom.archivereader.enabled", true] ]}, function() { + SpecialPowers.createFiles(filesToCreate(), + function (files) { + testGenerator = testSteps(files); + return testGenerator.next(); + }, + function (msg) { + ok(false, "File creation error: " + msg); + finishTest(); + }); + }); +} + +function finishTest() +{ + SpecialPowers.popPrefEnv(function() { + testGenerator.close(); + SimpleTest.finish(); + }); +} diff --git a/dom/archivereader/test/mochitest.ini b/dom/archivereader/test/mochitest.ini new file mode 100644 index 0000000000..6cf9e9c43b --- /dev/null +++ b/dom/archivereader/test/mochitest.ini @@ -0,0 +1,7 @@ +[DEFAULT] +support-files = + helpers.js + +[test_basic.html] +[test_nonUnicode.html] +[test_zip_in_zip.html] diff --git a/dom/archivereader/test/test_basic.html b/dom/archivereader/test/test_basic.html new file mode 100644 index 0000000000..b3e5ab852a --- /dev/null +++ b/dom/archivereader/test/test_basic.html @@ -0,0 +1,227 @@ + + + + Archive Reader Test + + + + + + + + + +

+

+ + + diff --git a/dom/archivereader/test/test_nonUnicode.html b/dom/archivereader/test/test_nonUnicode.html new file mode 100644 index 0000000000..6237ce10f9 --- /dev/null +++ b/dom/archivereader/test/test_nonUnicode.html @@ -0,0 +1,77 @@ + + + + Archive Reader Non-Unicode Test + + + + + + + + + + +

+

+ + + + diff --git a/dom/archivereader/test/test_zip_in_zip.html b/dom/archivereader/test/test_zip_in_zip.html new file mode 100644 index 0000000000..7e381a1efe --- /dev/null +++ b/dom/archivereader/test/test_zip_in_zip.html @@ -0,0 +1,111 @@ + + + + Archive Reader Zip-In-Zip Test + + + + + + + + + + +

+

+ + + + -- cgit v1.2.3