Bug 1258482 - FileList should contain only Files, not Directories, r=smaug

This commit is contained in:
Andrea Marchesini 2016-04-12 08:51:52 -04:00
parent a8c342535b
commit ed43c1f6e4
11 changed files with 99 additions and 231 deletions

View File

@ -12,7 +12,7 @@
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileList, mFilesOrDirectories, mParent)
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileList, mFiles, mParent)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileList)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
@ -29,20 +29,6 @@ FileList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
return mozilla::dom::FileListBinding::Wrap(aCx, this, aGivenProto);
}
void
FileList::Append(File* aFile)
{
OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement();
element->SetAsFile() = aFile;
}
void
FileList::Append(Directory* aDirectory)
{
OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement();
element->SetAsDirectory() = aDirectory;
}
NS_IMETHODIMP
FileList::GetLength(uint32_t* aLength)
{
@ -54,75 +40,47 @@ FileList::GetLength(uint32_t* aLength)
NS_IMETHODIMP
FileList::Item(uint32_t aIndex, nsISupports** aValue)
{
if (aIndex >= mFilesOrDirectories.Length()) {
return NS_ERROR_FAILURE;
}
if (mFilesOrDirectories[aIndex].IsFile()) {
nsCOMPtr<nsIDOMBlob> file = mFilesOrDirectories[aIndex].GetAsFile();
file.forget(aValue);
return NS_OK;
}
MOZ_ASSERT(mFilesOrDirectories[aIndex].IsDirectory());
RefPtr<Directory> directory = mFilesOrDirectories[aIndex].GetAsDirectory();
directory.forget(aValue);
nsCOMPtr<nsIDOMBlob> file = Item(aIndex);
file.forget(aValue);
return NS_OK;
}
void
FileList::Item(uint32_t aIndex, Nullable<OwningFileOrDirectory>& aValue,
ErrorResult& aRv) const
File*
FileList::Item(uint32_t aIndex) const
{
if (aIndex >= mFilesOrDirectories.Length()) {
aValue.SetNull();
return;
if (aIndex >= mFiles.Length()) {
return nullptr;
}
aValue.SetValue(mFilesOrDirectories[aIndex]);
return mFiles[aIndex];
}
void
FileList::IndexedGetter(uint32_t aIndex, bool& aFound,
Nullable<OwningFileOrDirectory>& aFileOrDirectory,
ErrorResult& aRv) const
File*
FileList::IndexedGetter(uint32_t aIndex, bool& aFound) const
{
aFound = aIndex < mFilesOrDirectories.Length();
Item(aIndex, aFileOrDirectory, aRv);
aFound = aIndex < mFiles.Length();
return Item(aIndex);
}
void
FileList::ToSequence(Sequence<OwningFileOrDirectory>& aSequence,
FileList::ToSequence(Sequence<RefPtr<File>>& aSequence,
ErrorResult& aRv) const
{
MOZ_ASSERT(aSequence.IsEmpty());
if (mFilesOrDirectories.IsEmpty()) {
if (mFiles.IsEmpty()) {
return;
}
if (!aSequence.SetLength(mFilesOrDirectories.Length(),
if (!aSequence.SetLength(mFiles.Length(),
mozilla::fallible_t())) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return;
}
for (uint32_t i = 0; i < mFilesOrDirectories.Length(); ++i) {
aSequence[i] = mFilesOrDirectories[i];
for (uint32_t i = 0; i < mFiles.Length(); ++i) {
aSequence[i] = mFiles[i];
}
}
bool
FileList::ClonableToDifferentThreadOrProcess() const
{
for (uint32_t i = 0; i < mFilesOrDirectories.Length(); ++i) {
if (mFilesOrDirectories[i].IsDirectory() &&
!mFilesOrDirectories[i].GetAsDirectory()->ClonableToDifferentThreadOrProcess()) {
return false;
}
}
return true;
}
} // namespace dom
} // namespace mozilla

View File

@ -40,13 +40,16 @@ public:
return mParent;
}
void Append(File* aFile);
void Append(Directory* aDirectory);
bool Append(File* aFile)
{
MOZ_ASSERT(aFile);
return mFiles.AppendElement(aFile, fallible);
}
bool Remove(uint32_t aIndex)
{
if (aIndex < mFilesOrDirectories.Length()) {
mFilesOrDirectories.RemoveElementAt(aIndex);
if (aIndex < mFiles.Length()) {
mFiles.RemoveElementAt(aIndex);
return true;
}
@ -55,7 +58,7 @@ public:
void Clear()
{
return mFilesOrDirectories.Clear();
return mFiles.Clear();
}
static FileList* FromSupports(nsISupports* aSupports)
@ -75,34 +78,22 @@ public:
return static_cast<FileList*>(aSupports);
}
const OwningFileOrDirectory& UnsafeItem(uint32_t aIndex) const
{
MOZ_ASSERT(aIndex < Length());
return mFilesOrDirectories[aIndex];
}
File* Item(uint32_t aIndex) const;
void Item(uint32_t aIndex,
Nullable<OwningFileOrDirectory>& aFileOrDirectory,
ErrorResult& aRv) const;
void IndexedGetter(uint32_t aIndex, bool& aFound,
Nullable<OwningFileOrDirectory>& aFileOrDirectory,
ErrorResult& aRv) const;
File* IndexedGetter(uint32_t aIndex, bool& aFound) const;
uint32_t Length() const
{
return mFilesOrDirectories.Length();
return mFiles.Length();
}
void ToSequence(Sequence<OwningFileOrDirectory>& aSequence,
void ToSequence(Sequence<RefPtr<File>>& aSequence,
ErrorResult& aRv) const;
bool ClonableToDifferentThreadOrProcess() const;
private:
~FileList() {}
nsTArray<OwningFileOrDirectory> mFilesOrDirectories;
FallibleTArray<RefPtr<File>> mFiles;
nsCOMPtr<nsISupports> mParent;
};

View File

@ -794,59 +794,35 @@ ReadFileList(JSContext* aCx,
{
RefPtr<FileList> fileList = new FileList(aHolder->ParentDuringRead());
// |aCount| is the number of Files or Directory for this FileList.
uint32_t zero, index;
// |index| is the index of the first blobImpl.
if (!JS_ReadUint32Pair(aReader, &zero, &index)) {
return nullptr;
}
MOZ_ASSERT(zero == 0);
// |aCount| is the number of BlobImpls to use from the |index|.
for (uint32_t i = 0; i < aCount; ++i) {
uint32_t tagOrDirectoryType, indexOrLengthOfString;
if (!JS_ReadUint32Pair(aReader, &tagOrDirectoryType,
&indexOrLengthOfString)) {
uint32_t pos = index + i;
MOZ_ASSERT(pos < aHolder->BlobImpls().Length());
RefPtr<BlobImpl> blobImpl = aHolder->BlobImpls()[pos];
MOZ_ASSERT(blobImpl->IsFile());
ErrorResult rv;
blobImpl = EnsureBlobForBackgroundManager(blobImpl, nullptr, rv);
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();
return nullptr;
}
MOZ_ASSERT(tagOrDirectoryType == SCTAG_DOM_BLOB ||
tagOrDirectoryType == Directory::eDOMRootDirectory ||
tagOrDirectoryType == Directory::eNotDOMRootDirectory);
MOZ_ASSERT(blobImpl);
if (tagOrDirectoryType == SCTAG_DOM_BLOB) {
MOZ_ASSERT(indexOrLengthOfString < aHolder->BlobImpls().Length());
RefPtr<BlobImpl> blobImpl =
aHolder->BlobImpls()[indexOrLengthOfString];
MOZ_ASSERT(blobImpl->IsFile());
ErrorResult rv;
blobImpl = EnsureBlobForBackgroundManager(blobImpl, nullptr, rv);
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();
return nullptr;
}
RefPtr<File> file =
File::Create(aHolder->ParentDuringRead(), blobImpl);
MOZ_ASSERT(file);
fileList->Append(file);
continue;
}
nsAutoString path;
path.SetLength(indexOrLengthOfString);
size_t charSize = sizeof(nsString::char_type);
if (!JS_ReadBytes(aReader, (void*) path.BeginWriting(),
indexOrLengthOfString * charSize)) {
RefPtr<File> file = File::Create(aHolder->ParentDuringRead(), blobImpl);
if (!fileList->Append(file)) {
return nullptr;
}
nsCOMPtr<nsIFile> file;
nsresult rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(path), true,
getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
RefPtr<Directory> directory =
Directory::Create(aHolder->ParentDuringRead(), file,
(Directory::DirectoryType) tagOrDirectoryType);
fileList->Append(directory);
}
if (!ToJSValue(aCx, fileList, &val)) {
@ -859,13 +835,7 @@ ReadFileList(JSContext* aCx,
// The format of the FileList serialization is:
// - pair of ints: SCTAG_DOM_FILELIST, Length of the FileList
// - for each element of the FileList:
// - if it's a blob:
// - pair of ints: SCTAG_DOM_BLOB, index of the BlobImpl in the array
// mBlobImplArray.
// - else:
// - pair of ints: 0/1 is root, string length
// - value string
// - pair of ints: 0, The offset of the BlobImpl array
bool
WriteFileList(JSStructuredCloneWriter* aWriter,
FileList* aFileList,
@ -875,8 +845,13 @@ WriteFileList(JSStructuredCloneWriter* aWriter,
MOZ_ASSERT(aFileList);
MOZ_ASSERT(aHolder);
// A FileList is serialized writing the X number of elements and the offset
// from mBlobImplArray. The Read will take X elements from mBlobImplArray
// starting from the offset.
if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_FILELIST,
aFileList->Length())) {
aFileList->Length()) ||
!JS_WriteUint32Pair(aWriter, 0,
aHolder->BlobImpls().Length())) {
return false;
}
@ -884,39 +859,18 @@ WriteFileList(JSStructuredCloneWriter* aWriter,
nsTArray<RefPtr<BlobImpl>> blobImpls;
for (uint32_t i = 0; i < aFileList->Length(); ++i) {
const OwningFileOrDirectory& data = aFileList->UnsafeItem(i);
if (data.IsFile()) {
RefPtr<BlobImpl> blobImpl =
EnsureBlobForBackgroundManager(data.GetAsFile()->Impl(), nullptr, rv);
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();
return false;
}
if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_BLOB,
aHolder->BlobImpls().Length())) {
return false;
}
aHolder->BlobImpls().AppendElement(blobImpl);
continue;
}
MOZ_ASSERT(data.IsDirectory());
nsAutoString path;
data.GetAsDirectory()->GetFullRealPath(path);
size_t charSize = sizeof(nsString::char_type);
if (!JS_WriteUint32Pair(aWriter,
(uint32_t)data.GetAsDirectory()->Type(),
path.Length()) ||
!JS_WriteBytes(aWriter, path.get(), path.Length() * charSize)) {
RefPtr<BlobImpl> blobImpl =
EnsureBlobForBackgroundManager(aFileList->Item(i)->Impl(), nullptr, rv);
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();
return false;
}
MOZ_ASSERT(blobImpl);
blobImpls.AppendElement(blobImpl);
}
aHolder->BlobImpls().AppendElements(blobImpls);
return true;
}
@ -1132,9 +1086,7 @@ StructuredCloneHolder::CustomWriteHandler(JSContext* aCx,
// See if this is a FileList object.
{
FileList* fileList = nullptr;
if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, aObj, fileList)) &&
(mSupportedContext == SameProcessSameThread ||
fileList->ClonableToDifferentThreadOrProcess())) {
if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, aObj, fileList))) {
return WriteFileList(aWriter, fileList, this);
}
}

View File

@ -72,7 +72,7 @@ var clonableObjects = [
new ImageData(2, 2),
];
function create_fileList_forFile() {
function create_fileList() {
var url = SimpleTest.getTestFileURL("script_postmessages_fileList.js");
var script = SpecialPowers.loadChromeScript(url);
@ -93,27 +93,6 @@ function create_fileList_forFile() {
script.sendAsyncMessage("file.open");
}
function create_fileList_forDir() {
var url = SimpleTest.getTestFileURL("script_postmessages_fileList.js");
var script = SpecialPowers.loadChromeScript(url);
function onOpened(message) {
var fileList = document.getElementById('fileList');
SpecialPowers.wrap(fileList).mozSetDirectory(message.dir);
// Just a simple test
is(fileList.files.length, 1, "Filelist has 1 element");
ok(fileList.files[0] instanceof Directory, "We have a directory.");
clonableObjects.push(fileList.files);
script.destroy();
next();
}
script.addMessageListener("dir.opened", onOpened);
script.sendAsyncMessage("dir.open");
}
function create_directory() {
if (navigator.userAgent.toLowerCase().indexOf('Android') != -1) {
next();
@ -548,8 +527,7 @@ function test_messagePort_inWorkers() {
}
var tests = [
create_fileList_forFile,
create_fileList_forDir,
create_fileList,
create_directory,
test_windowToWindow,

View File

@ -866,13 +866,13 @@ DataTransfer::GetFilesAndDirectories(ErrorResult& aRv)
}
}
Sequence<OwningFileOrDirectory> filesAndDirsSeq;
mFileList->ToSequence(filesAndDirsSeq, aRv);
Sequence<RefPtr<File>> filesSeq;
mFileList->ToSequence(filesSeq, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
p->MaybeResolve(filesAndDirsSeq);
p->MaybeResolve(filesSeq);
return p.forget();
}

View File

@ -20,14 +20,14 @@ function create_fileList(aPath) {
var fileList = document.getElementById('fileList');
SpecialPowers.wrap(fileList).mozSetDirectory(message.dir);
// Just a simple test
is(fileList.files.length, 1, "Filelist has 1 element");
ok(fileList.files[0] instanceof Directory, "We have a directory.");
fileList.getFilesAndDirectories().then(function(array) {
is(array.length, 1, "We want just 1 directory.");
ok(array[0] instanceof Directory, "We want just 1 directory.");
directory = fileList.files[0];
script.destroy();
next();
directory = array[0];
script.destroy();
next();
});
}
script.addMessageListener("dir.opened", onOpened);

View File

@ -17,11 +17,6 @@ function create_fileList() {
function onOpened(message) {
var fileList = document.getElementById('fileList');
SpecialPowers.wrap(fileList).mozSetDirectory(message.dir);
// Just a simple test
is(fileList.files.length, 1, "Filelist has 1 element");
ok(fileList.files[0] instanceof Directory, "We have a directory.");
script.destroy();
next();
}
@ -32,20 +27,21 @@ function create_fileList() {
function test_worker() {
var fileList = document.getElementById('fileList');
fileList.getFilesAndDirectories().then(function(array) {
var worker = new Worker('worker_basic.js');
worker.onmessage = function(e) {
if (e.data.type == 'finish') {
next();
return;
}
var worker = new Worker('worker_basic.js');
worker.onmessage = function(e) {
if (e.data.type == 'finish') {
next();
return;
if (e.data.type == 'test') {
ok(e.data.test, e.data.message);
}
}
if (e.data.type == 'test') {
ok(e.data.test, e.data.message);
}
}
worker.postMessage(fileList.files);
worker.postMessage(array[0]);
});
}
var tests = [

View File

@ -31,12 +31,10 @@ function checkSubDir(dir) {
}
onmessage = function(e) {
var fileList = e.data;
ok(fileList instanceof FileList, "This is a fileList.");
is(fileList.length, 1, "We want just 1 element.");
ok(fileList[0] instanceof Directory, "This is a directory.");
var directory = e.data;
ok(directory instanceof Directory, "This is a directory.");
fileList[0].getFilesAndDirectories().then(
directory.getFilesAndDirectories().then(
function(data) {
ok(data.length, "We should have some data.");
var promises = [];

View File

@ -2562,7 +2562,7 @@ HTMLInputElement::SetFiles(nsIDOMFileList* aFiles,
aFiles->GetLength(&listLength);
for (uint32_t i = 0; i < listLength; i++) {
OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement();
*element = files->UnsafeItem(i);
element->SetAsFile() = files->Item(i);
}
}
@ -2678,9 +2678,6 @@ HTMLInputElement::UpdateFileList()
for (uint32_t i = 0; i < array.Length(); ++i) {
if (array[i].IsFile()) {
mFileList->Append(array[i].GetAsFile());
} else {
MOZ_ASSERT(array[i].IsDirectory());
mFileList->Append(array[i].GetAsDirectory());
}
}
}

View File

@ -11,8 +11,6 @@
*/
interface FileList {
[Throws]
getter (File or Directory)? item(unsigned long index);
getter File? item(unsigned long index);
readonly attribute unsigned long length;
};

View File

@ -1186,7 +1186,7 @@ DefaultTooltipTextProvider::GetNodeText(nsIDOMNode* aNode, char16_t** aText,
NS_ENSURE_SUCCESS(rv, rv);
} else {
FileList* fl = static_cast<FileList*>(fileList.get());
fl->UnsafeItem(0).GetAsFile()->GetName(outText);
fl->Item(0)->GetName(outText);
// For UX and performance (jank) reasons we cap the number of
// files that we list in the tooltip to 20 plus a "and xxx more"
@ -1195,7 +1195,7 @@ DefaultTooltipTextProvider::GetNodeText(nsIDOMNode* aNode, char16_t** aText,
uint32_t count = std::min(listLength, TRUNCATED_FILE_COUNT);
for (uint32_t i = 1; i < count; ++i) {
nsString fileName;
fl->UnsafeItem(i).GetAsFile()->GetName(fileName);
fl->Item(i)->GetName(fileName);
outText.Append(NS_LITERAL_STRING("\n"));
outText.Append(fileName);
}