michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "ArchiveReader.h" michael@0: #include "ArchiveRequest.h" michael@0: #include "ArchiveEvent.h" michael@0: #include "ArchiveZipEvent.h" michael@0: michael@0: #include "nsIURI.h" michael@0: #include "nsNetUtil.h" michael@0: michael@0: #include "mozilla/dom/ArchiveReaderBinding.h" michael@0: #include "mozilla/dom/BindingDeclarations.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/dom/EncodingUtils.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: USING_FILE_NAMESPACE michael@0: michael@0: /* static */ already_AddRefed michael@0: ArchiveReader::Constructor(const GlobalObject& aGlobal, michael@0: nsIDOMBlob* aBlob, michael@0: const ArchiveReaderOptions& aOptions, michael@0: ErrorResult& aError) michael@0: { michael@0: MOZ_ASSERT(aBlob); michael@0: michael@0: nsCOMPtr window = do_QueryInterface(aGlobal.GetAsSupports()); michael@0: if (!window) { michael@0: aError.Throw(NS_ERROR_UNEXPECTED); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsAutoCString encoding; michael@0: if (!EncodingUtils::FindEncodingForLabel(aOptions.mEncoding, encoding) || michael@0: encoding.EqualsLiteral("replacement")) { michael@0: aError.ThrowTypeError(MSG_ENCODING_NOT_SUPPORTED, &aOptions.mEncoding); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr reader = michael@0: new ArchiveReader(aBlob, window, encoding); michael@0: return reader.forget(); michael@0: } michael@0: michael@0: ArchiveReader::ArchiveReader(nsIDOMBlob* aBlob, nsPIDOMWindow* aWindow, michael@0: const nsACString& aEncoding) michael@0: : mBlob(aBlob) michael@0: , mWindow(aWindow) michael@0: , mStatus(NOT_STARTED) michael@0: , mEncoding(aEncoding) michael@0: { michael@0: MOZ_ASSERT(aBlob); michael@0: MOZ_ASSERT(aWindow); michael@0: michael@0: SetIsDOMBinding(); michael@0: } michael@0: michael@0: ArchiveReader::~ArchiveReader() michael@0: { michael@0: } michael@0: michael@0: /* virtual */ JSObject* michael@0: ArchiveReader::WrapObject(JSContext* aCx) michael@0: { michael@0: return ArchiveReaderBinding::Wrap(aCx, this); michael@0: } michael@0: michael@0: nsresult michael@0: ArchiveReader::RegisterRequest(ArchiveRequest* aRequest) michael@0: { michael@0: switch (mStatus) { michael@0: // Append to the list and let's start to work: michael@0: case NOT_STARTED: michael@0: mRequests.AppendElement(aRequest); michael@0: return OpenArchive(); michael@0: michael@0: // Just append to the list: michael@0: case WORKING: michael@0: mRequests.AppendElement(aRequest); michael@0: return NS_OK; michael@0: michael@0: // Return data! michael@0: case READY: michael@0: RequestReady(aRequest); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_ASSERTION(false, "unexpected mStatus value"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // This returns the input stream michael@0: nsresult michael@0: ArchiveReader::GetInputStream(nsIInputStream** aInputStream) michael@0: { michael@0: // Getting the input stream michael@0: mBlob->GetInternalStream(aInputStream); michael@0: NS_ENSURE_TRUE(*aInputStream, NS_ERROR_UNEXPECTED); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: ArchiveReader::GetSize(uint64_t* aSize) michael@0: { michael@0: nsresult rv = mBlob->GetSize(aSize); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Here we open the archive: michael@0: nsresult michael@0: ArchiveReader::OpenArchive() michael@0: { michael@0: mStatus = WORKING; michael@0: nsresult rv; michael@0: michael@0: // Target: michael@0: nsCOMPtr target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); michael@0: NS_ASSERTION(target, "Must have stream transport service"); michael@0: michael@0: // Here a Event to make everything async: michael@0: nsRefPtr event; michael@0: michael@0: /* FIXME: If we want to support more than 1 format we should check the content type here: */ michael@0: event = new ArchiveReaderZipEvent(this, mEncoding); michael@0: rv = target->Dispatch(event, NS_DISPATCH_NORMAL); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // In order to be sure that this object exists when the event finishes its task, michael@0: // we increase the refcount here: michael@0: AddRef(); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Data received from the dispatched event: michael@0: void michael@0: ArchiveReader::Ready(nsTArray >& aFileList, michael@0: nsresult aStatus) michael@0: { michael@0: mStatus = READY; michael@0: michael@0: // Let's store the values: michael@0: mData.fileList = aFileList; michael@0: mData.status = aStatus; michael@0: michael@0: // Propagate the results: michael@0: for (uint32_t index = 0; index < mRequests.Length(); ++index) { michael@0: nsRefPtr request = mRequests[index]; michael@0: RequestReady(request); michael@0: } michael@0: michael@0: mRequests.Clear(); michael@0: michael@0: // The async operation is concluded, we can decrease the reference: michael@0: Release(); michael@0: } michael@0: michael@0: void michael@0: ArchiveReader::RequestReady(ArchiveRequest* aRequest) michael@0: { michael@0: // The request will do the rest: michael@0: aRequest->ReaderReady(mData.fileList, mData.status); michael@0: } michael@0: michael@0: already_AddRefed michael@0: ArchiveReader::GetFilenames() michael@0: { michael@0: nsRefPtr request = GenerateArchiveRequest(); michael@0: request->OpGetFilenames(); michael@0: michael@0: return request.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: ArchiveReader::GetFile(const nsAString& filename) michael@0: { michael@0: nsRefPtr request = GenerateArchiveRequest(); michael@0: request->OpGetFile(filename); michael@0: michael@0: return request.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: ArchiveReader::GetFiles() michael@0: { michael@0: nsRefPtr request = GenerateArchiveRequest(); michael@0: request->OpGetFiles(); michael@0: michael@0: return request.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: ArchiveReader::GenerateArchiveRequest() michael@0: { michael@0: NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); michael@0: return ArchiveRequest::Create(mWindow, this); michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_4(ArchiveReader, michael@0: mBlob, michael@0: mWindow, michael@0: mData.fileList, michael@0: mRequests) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ArchiveReader) michael@0: NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY michael@0: NS_INTERFACE_MAP_ENTRY(nsISupports) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(ArchiveReader) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(ArchiveReader)