mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-08 02:14:43 +00:00
61e273f1ff
Differential Revision: https://phabricator.services.mozilla.com/D148318
239 lines
7.4 KiB
C++
239 lines
7.4 KiB
C++
/* -*- 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 "FileSystemDirectoryIteratorFactory.h"
|
|
|
|
#include "FileSystemEntryMetadataArray.h"
|
|
#include "fs/FileSystemRequestHandler.h"
|
|
#include "jsapi.h"
|
|
#include "mozilla/dom/FileSystemDirectoryHandle.h"
|
|
#include "mozilla/dom/FileSystemDirectoryIterator.h"
|
|
#include "mozilla/dom/FileSystemFileHandle.h"
|
|
#include "mozilla/dom/FileSystemHandle.h"
|
|
#include "mozilla/dom/FileSystemLog.h"
|
|
#include "mozilla/dom/FileSystemManager.h"
|
|
#include "mozilla/dom/IterableIterator.h"
|
|
#include "mozilla/dom/Promise.h"
|
|
#include "mozilla/dom/PromiseNativeHandler.h"
|
|
|
|
namespace mozilla::dom::fs {
|
|
|
|
namespace {
|
|
|
|
template <IterableIteratorBase::IteratorType Type>
|
|
struct ValueResolver;
|
|
|
|
template <>
|
|
struct ValueResolver<IterableIteratorBase::Keys> {
|
|
void operator()(nsIGlobalObject* aGlobal, RefPtr<FileSystemManager>& aManager,
|
|
const FileSystemEntryMetadata& aValue,
|
|
const RefPtr<Promise>& aPromise) {
|
|
aPromise->MaybeResolve(aValue.entryName());
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct ValueResolver<IterableIteratorBase::Values> {
|
|
void operator()(nsIGlobalObject* aGlobal, RefPtr<FileSystemManager>& aManager,
|
|
const FileSystemEntryMetadata& aValue,
|
|
const RefPtr<Promise>& aPromise) {
|
|
RefPtr<FileSystemHandle> handle;
|
|
|
|
if (aValue.directory()) {
|
|
handle = new FileSystemDirectoryHandle(aGlobal, aManager, aValue);
|
|
} else {
|
|
handle = new FileSystemFileHandle(aGlobal, aManager, aValue);
|
|
}
|
|
|
|
aPromise->MaybeResolve(std::move(handle));
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct ValueResolver<IterableIteratorBase::Entries> {
|
|
void operator()(nsIGlobalObject* aGlobal, RefPtr<FileSystemManager>& aManager,
|
|
const FileSystemEntryMetadata& aValue,
|
|
const RefPtr<Promise>& aPromise) {
|
|
RefPtr<FileSystemHandle> handle;
|
|
|
|
if (aValue.directory()) {
|
|
handle = new FileSystemDirectoryHandle(aGlobal, aManager, aValue);
|
|
} else {
|
|
handle = new FileSystemFileHandle(aGlobal, aManager, aValue);
|
|
}
|
|
|
|
iterator_utils::ResolvePromiseWithKeyAndValue(aPromise, aValue.entryName(),
|
|
handle);
|
|
}
|
|
};
|
|
|
|
// TODO: PageSize could be compile-time shared between content and parent
|
|
template <class ValueResolver, size_t PageSize = 1024u>
|
|
class DoubleBufferQueueImpl
|
|
: public mozilla::dom::FileSystemDirectoryIterator::Impl {
|
|
static_assert(PageSize > 0u);
|
|
|
|
public:
|
|
using DataType = FileSystemEntryMetadata;
|
|
explicit DoubleBufferQueueImpl(const FileSystemEntryMetadata& aMetadata)
|
|
: mEntryId(aMetadata.entryId()),
|
|
mData(),
|
|
mWithinPageEnd(0u),
|
|
mWithinPageIndex(0u),
|
|
mCurrentPageIsLastPage(true),
|
|
mPageNumber(0u) {}
|
|
|
|
// XXX This doesn't have to be public
|
|
void ResolveValue(nsIGlobalObject* aGlobal,
|
|
RefPtr<FileSystemManager>& aManager,
|
|
const Maybe<DataType>& aValue, RefPtr<Promise> aPromise) {
|
|
MOZ_ASSERT(aPromise);
|
|
MOZ_ASSERT(aPromise.get());
|
|
|
|
if (!aValue) {
|
|
iterator_utils::ResolvePromiseForFinished(aPromise);
|
|
return;
|
|
}
|
|
|
|
ValueResolver{}(aGlobal, aManager, *aValue, aPromise);
|
|
}
|
|
|
|
already_AddRefed<Promise> Next(nsIGlobalObject* aGlobal,
|
|
RefPtr<FileSystemManager>& aManager,
|
|
ErrorResult& aError) override {
|
|
RefPtr<Promise> promise = Promise::Create(aGlobal, aError);
|
|
if (aError.Failed()) {
|
|
return nullptr;
|
|
}
|
|
|
|
next(aGlobal, aManager, promise, aError);
|
|
if (aError.Failed()) {
|
|
return nullptr;
|
|
}
|
|
|
|
return promise.forget();
|
|
}
|
|
|
|
~DoubleBufferQueueImpl() = default;
|
|
|
|
protected:
|
|
void next(nsIGlobalObject* aGlobal, RefPtr<FileSystemManager>& aManager,
|
|
RefPtr<Promise> aResult, ErrorResult& aError) {
|
|
LOG_VERBOSE(("next"));
|
|
MOZ_ASSERT(aResult);
|
|
|
|
Maybe<DataType> rawValue;
|
|
|
|
// TODO: Would it be better to prefetch the items before
|
|
// we hit the end of a page?
|
|
// How likely it is that it would that lead to wasted fetch operations?
|
|
if (0u == mWithinPageIndex) {
|
|
RefPtr<Promise> promise = Promise::Create(aGlobal, aError);
|
|
if (aError.Failed()) {
|
|
return;
|
|
}
|
|
|
|
auto newPage = MakeRefPtr<FileSystemEntryMetadataArray>();
|
|
|
|
RefPtr<DomPromiseListener> listener = new DomPromiseListener(
|
|
[global = nsCOMPtr<nsIGlobalObject>(aGlobal),
|
|
manager = RefPtr<FileSystemManager>(aManager), newPage, aResult,
|
|
this](JSContext* aCx, JS::Handle<JS::Value> aValue) mutable {
|
|
MOZ_ASSERT(0u == mWithinPageIndex);
|
|
|
|
// XXX Do we need this extra copy ?
|
|
nsTArray<DataType> batch;
|
|
for (const auto& it : *newPage) {
|
|
batch.AppendElement(it);
|
|
}
|
|
|
|
const size_t batchSize = std::min(PageSize, newPage->Length());
|
|
mData.InsertElementsAt(
|
|
PageSize * static_cast<size_t>(!mCurrentPageIsLastPage),
|
|
batch.Elements(), batchSize);
|
|
mWithinPageEnd += batchSize;
|
|
|
|
Maybe<DataType> value;
|
|
if (0 != newPage->Length()) {
|
|
nextInternal(value);
|
|
}
|
|
|
|
ResolveValue(global, manager, value, aResult);
|
|
},
|
|
[aResult](nsresult aRv) { aResult->MaybeReject(aRv); });
|
|
promise->AppendNativeHandler(listener);
|
|
|
|
FileSystemRequestHandler{}.GetEntries(aManager, mEntryId, mPageNumber,
|
|
promise, newPage, aError);
|
|
if (aError.Failed()) {
|
|
return;
|
|
}
|
|
|
|
++mPageNumber;
|
|
return;
|
|
}
|
|
|
|
nextInternal(rawValue);
|
|
|
|
ResolveValue(aGlobal, aManager, rawValue, aResult);
|
|
}
|
|
|
|
bool nextInternal(Maybe<DataType>& aNext) {
|
|
if (mWithinPageIndex >= mWithinPageEnd) {
|
|
return false;
|
|
}
|
|
|
|
const auto previous =
|
|
static_cast<size_t>(!mCurrentPageIsLastPage) * PageSize +
|
|
mWithinPageIndex;
|
|
MOZ_ASSERT(2u * PageSize > previous);
|
|
MOZ_ASSERT(previous < mData.Length());
|
|
|
|
++mWithinPageIndex;
|
|
|
|
if (mWithinPageIndex == PageSize) {
|
|
// Page end reached
|
|
mWithinPageIndex = 0u;
|
|
mCurrentPageIsLastPage = !mCurrentPageIsLastPage;
|
|
}
|
|
|
|
aNext = Some(mData[previous]);
|
|
return true;
|
|
}
|
|
|
|
const EntryId mEntryId;
|
|
|
|
nsTArray<DataType> mData; // TODO: Fixed size above one page?
|
|
|
|
size_t mWithinPageEnd = 0u;
|
|
size_t mWithinPageIndex = 0u;
|
|
bool mCurrentPageIsLastPage = true; // In the beginning, first page is free
|
|
PageNumber mPageNumber = 0u;
|
|
};
|
|
|
|
template <IterableIteratorBase::IteratorType Type>
|
|
using UnderlyingQueue = DoubleBufferQueueImpl<ValueResolver<Type>>;
|
|
|
|
} // namespace
|
|
|
|
UniquePtr<mozilla::dom::FileSystemDirectoryIterator::Impl>
|
|
FileSystemDirectoryIteratorFactory::Create(
|
|
const FileSystemEntryMetadata& aMetadata,
|
|
IterableIteratorBase::IteratorType aType) {
|
|
if (IterableIteratorBase::Entries == aType) {
|
|
return MakeUnique<UnderlyingQueue<IterableIteratorBase::Entries>>(
|
|
aMetadata);
|
|
}
|
|
|
|
if (IterableIteratorBase::Values == aType) {
|
|
return MakeUnique<UnderlyingQueue<IterableIteratorBase::Values>>(aMetadata);
|
|
}
|
|
|
|
return MakeUnique<UnderlyingQueue<IterableIteratorBase::Keys>>(aMetadata);
|
|
}
|
|
|
|
} // namespace mozilla::dom::fs
|