Backed out 8 changesets (bug 1173320) for adding dom/filesystem/tests/test_basic.html without making it work on Android

Backed out changeset 7e3a105b9160 (bug 1173320)
Backed out changeset 094819fbb07f (bug 1173320)
Backed out changeset 23b0e55ff1db (bug 1173320)
Backed out changeset 8bf8e2fcedad (bug 1173320)
Backed out changeset 3de390c6f47f (bug 1173320)
Backed out changeset fb298010e12e (bug 1173320)
Backed out changeset f1a965c2f796 (bug 1173320)
Backed out changeset 176128ba757f (bug 1173320)
This commit is contained in:
Phil Ringnalda 2016-03-19 19:44:22 -07:00
parent b4a736d2a2
commit b47ec4d2bd
65 changed files with 1244 additions and 2198 deletions

View File

@ -227,6 +227,12 @@ Blob::IsFile() const
return mImpl->IsFile();
}
bool
Blob::IsDirectory() const
{
return mImpl->IsDirectory();
}
const nsTArray<RefPtr<BlobImpl>>*
Blob::GetSubBlobImpls() const
{
@ -414,10 +420,11 @@ File::Create(nsISupports* aParent, BlobImpl* aImpl)
/* static */ already_AddRefed<File>
File::Create(nsISupports* aParent, const nsAString& aName,
const nsAString& aContentType, uint64_t aLength,
int64_t aLastModifiedDate)
int64_t aLastModifiedDate, BlobDirState aDirState)
{
RefPtr<File> file = new File(aParent,
new BlobImplBase(aName, aContentType, aLength, aLastModifiedDate));
new BlobImplBase(aName, aContentType, aLength, aLastModifiedDate,
aDirState));
return file.forget();
}
@ -939,6 +946,17 @@ BlobImplFile::SetPath(const nsAString& aPath)
mPath = aPath;
}
void
BlobImplFile::LookupAndCacheIsDirectory()
{
MOZ_ASSERT(mIsFile,
"This should only be called when this object has been created "
"from an nsIFile to note that the nsIFile is a directory");
bool isDir;
mFile->IsDirectory(&isDir);
mDirState = isDir ? BlobDirState::eIsDir : BlobDirState::eIsNotDir;
}
////////////////////////////////////////////////////////////////////////////
// EmptyBlobImpl implementation

View File

@ -44,6 +44,18 @@ class BlobImpl;
class File;
class OwningArrayBufferOrArrayBufferViewOrBlobOrString;
/**
* Used to indicate when a Blob/BlobImpl that was created from an nsIFile
* (when IsFile() will return true) was from an nsIFile for which
* nsIFile::IsDirectory() returned true. This is a tri-state to enable us to
* assert that the state is always set when callers request it.
*/
enum BlobDirState : uint32_t {
eIsDir,
eIsNotDir,
eUnknownIfDir
};
class Blob : public nsIDOMBlob
, public nsIXHRSendable
, public nsIMutable
@ -89,6 +101,12 @@ public:
bool IsFile() const;
/**
* This may return true if the Blob was created from an nsIFile that is a
* directory.
*/
bool IsDirectory() const;
const nsTArray<RefPtr<BlobImpl>>* GetSubBlobImpls() const;
// This method returns null if this Blob is not a File; it returns
@ -96,6 +114,9 @@ public:
// otherwise it returns a new File object with the same BlobImpl.
already_AddRefed<File> ToFile();
// XXXjwatt Consider having a ToDirectory() method. The need for a FileSystem
// object complicates that though.
// This method creates a new File object with the given name and the same
// BlobImpl.
already_AddRefed<File> ToFile(const nsAString& aName,
@ -173,7 +194,7 @@ public:
static already_AddRefed<File>
Create(nsISupports* aParent, const nsAString& aName,
const nsAString& aContentType, uint64_t aLength,
int64_t aLastModifiedDate);
int64_t aLastModifiedDate, BlobDirState aDirState);
// The returned File takes ownership of aMemoryBuffer. aMemoryBuffer will be
// freed by free so it must be allocated by malloc or something
@ -318,6 +339,28 @@ public:
virtual bool IsFile() const = 0;
/**
* Called when this BlobImpl was created from an nsIFile in order to call
* nsIFile::IsDirectory() and cache the result so that when the BlobImpl is
* copied to another process that informaton is available.
* nsIFile::IsDirectory() does synchronous I/O, and BlobImpl objects may be
* created on the main thread or in a non-chrome process (where I/O is not
* allowed). Do not call this on a non-chrome process, and preferably do not
* call it on the main thread.
*
* Not all creators of BlobImplFile will call this method, in which case
* calling IsDirectory will MOZ_ASSERT.
*/
virtual void LookupAndCacheIsDirectory() = 0;
virtual void SetIsDirectory(bool aIsDir) = 0;
virtual bool IsDirectory() const = 0;
/**
* Prefer IsDirectory(). This exists to help consumer code pass on state from
* one BlobImpl when creating another.
*/
virtual BlobDirState GetDirState() const = 0;
// True if this implementation can be sent to other threads.
virtual bool MayBeClonedToOtherThreads() const
{
@ -334,9 +377,11 @@ class BlobImplBase : public BlobImpl
{
public:
BlobImplBase(const nsAString& aName, const nsAString& aContentType,
uint64_t aLength, int64_t aLastModifiedDate)
uint64_t aLength, int64_t aLastModifiedDate,
BlobDirState aDirState = BlobDirState::eUnknownIfDir)
: mIsFile(true)
, mImmutable(false)
, mDirState(aDirState)
, mContentType(aContentType)
, mName(aName)
, mStart(0)
@ -352,6 +397,7 @@ public:
uint64_t aLength)
: mIsFile(true)
, mImmutable(false)
, mDirState(BlobDirState::eUnknownIfDir)
, mContentType(aContentType)
, mName(aName)
, mStart(0)
@ -366,6 +412,7 @@ public:
BlobImplBase(const nsAString& aContentType, uint64_t aLength)
: mIsFile(false)
, mImmutable(false)
, mDirState(BlobDirState::eUnknownIfDir)
, mContentType(aContentType)
, mStart(0)
, mLength(aLength)
@ -380,6 +427,7 @@ public:
uint64_t aLength)
: mIsFile(false)
, mImmutable(false)
, mDirState(BlobDirState::eUnknownIfDir)
, mContentType(aContentType)
, mStart(aStart)
, mLength(aLength)
@ -470,6 +518,36 @@ public:
return mIsFile;
}
virtual void LookupAndCacheIsDirectory() override
{
MOZ_ASSERT(false, "Why is this being called on a non-BlobImplFile?");
}
virtual void SetIsDirectory(bool aIsDir) override
{
MOZ_ASSERT(mIsFile,
"This should only be called when this object has been created "
"from an nsIFile to note that the nsIFile is a directory");
mDirState = aIsDir ? BlobDirState::eIsDir : BlobDirState::eIsNotDir;
}
/**
* Returns true if the nsIFile that this object wraps is a directory.
*/
virtual bool IsDirectory() const override
{
MOZ_ASSERT(mDirState != BlobDirState::eUnknownIfDir,
"Must only be used by callers for whom the code paths are "
"know to call LookupAndCacheIsDirectory() or "
"SetIsDirectory()");
return mDirState == BlobDirState::eIsDir;
}
virtual BlobDirState GetDirState() const override
{
return mDirState;
}
virtual bool IsSizeUnknown() const override
{
return mLength == UINT64_MAX;
@ -487,6 +565,7 @@ protected:
bool mIsFile;
bool mImmutable;
BlobDirState mDirState;
nsString mContentType;
nsString mName;
@ -511,7 +590,8 @@ public:
BlobImplMemory(void* aMemoryBuffer, uint64_t aLength, const nsAString& aName,
const nsAString& aContentType, int64_t aLastModifiedDate)
: BlobImplBase(aName, aContentType, aLength, aLastModifiedDate)
: BlobImplBase(aName, aContentType, aLength, aLastModifiedDate,
BlobDirState::eIsNotDir)
, mDataOwner(new DataOwner(aMemoryBuffer, aLength))
{
NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data");
@ -712,6 +792,8 @@ public:
void SetPath(const nsAString& aFullPath);
virtual void LookupAndCacheIsDirectory() override;
// We always have size and date for this kind of blob.
virtual bool IsSizeUnknown() const override { return false; }
virtual bool IsDateUnknown() const override { return false; }

View File

@ -4,7 +4,6 @@
* 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 "mozilla/dom/Directory.h"
#include "mozilla/dom/FileList.h"
#include "mozilla/dom/FileListBinding.h"
#include "mozilla/dom/File.h"
@ -12,7 +11,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 +28,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)
{
@ -52,76 +37,12 @@ FileList::GetLength(uint32_t* aLength)
}
NS_IMETHODIMP
FileList::Item(uint32_t aIndex, nsISupports** aValue)
FileList::Item(uint32_t aIndex, nsISupports** aFile)
{
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(aFile);
return NS_OK;
}
void
FileList::Item(uint32_t aIndex, Nullable<OwningFileOrDirectory>& aValue,
ErrorResult& aRv) const
{
if (aIndex >= mFilesOrDirectories.Length()) {
aValue.SetNull();
return;
}
aValue.SetValue(mFilesOrDirectories[aIndex]);
}
void
FileList::IndexedGetter(uint32_t aIndex, bool& aFound,
Nullable<OwningFileOrDirectory>& aFileOrDirectory,
ErrorResult& aRv) const
{
aFound = aIndex < mFilesOrDirectories.Length();
Item(aIndex, aFileOrDirectory, aRv);
}
void
FileList::ToSequence(Sequence<OwningFileOrDirectory>& aSequence,
ErrorResult& aRv) const
{
MOZ_ASSERT(aSequence.IsEmpty());
if (mFilesOrDirectories.IsEmpty()) {
return;
}
if (!aSequence.SetLength(mFilesOrDirectories.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];
}
}
bool
FileList::ClonableToDifferentThreadOrProcess() const
{
for (uint32_t i = 0; i < mFilesOrDirectories.Length(); ++i) {
if (mFilesOrDirectories[i].IsDirectory()) {
return false;
}
}
return true;
}
} // namespace dom
} // namespace mozilla

View File

@ -8,7 +8,6 @@
#define mozilla_dom_FileList_h
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/UnionTypes.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIDOMFileList.h"
#include "nsWrapperCache.h"
@ -40,13 +39,15 @@ public:
return mParent;
}
void Append(File* aFile);
void Append(Directory* aDirectory);
bool Append(File* aFile)
{
return mFiles.AppendElement(aFile);
}
bool Remove(uint32_t aIndex)
{
if (aIndex < mFilesOrDirectories.Length()) {
mFilesOrDirectories.RemoveElementAt(aIndex);
if (aIndex < mFiles.Length()) {
mFiles.RemoveElementAt(aIndex);
return true;
}
@ -55,7 +56,7 @@ public:
void Clear()
{
return mFilesOrDirectories.Clear();
return mFiles.Clear();
}
static FileList* FromSupports(nsISupports* aSupports)
@ -75,34 +76,29 @@ public:
return static_cast<FileList*>(aSupports);
}
const OwningFileOrDirectory& UnsafeItem(uint32_t aIndex) const
File* Item(uint32_t aIndex)
{
MOZ_ASSERT(aIndex < Length());
return mFilesOrDirectories[aIndex];
return mFiles.SafeElementAt(aIndex);
}
void Item(uint32_t aIndex,
Nullable<OwningFileOrDirectory>& aFileOrDirectory,
ErrorResult& aRv) const;
void IndexedGetter(uint32_t aIndex, bool& aFound,
Nullable<OwningFileOrDirectory>& aFileOrDirectory,
ErrorResult& aRv) const;
uint32_t Length() const
File* IndexedGetter(uint32_t aIndex, bool& aFound)
{
return mFilesOrDirectories.Length();
aFound = aIndex < mFiles.Length();
if (!aFound) {
return nullptr;
}
return mFiles.ElementAt(aIndex);
}
void ToSequence(Sequence<OwningFileOrDirectory>& aSequence,
ErrorResult& aRv) const;
bool ClonableToDifferentThreadOrProcess() const;
uint32_t Length()
{
return mFiles.Length();
}
private:
~FileList() {}
nsTArray<OwningFileOrDirectory> mFilesOrDirectories;
nsTArray<RefPtr<File>> mFiles;
nsCOMPtr<nsISupports> mParent;
};

View File

@ -10,7 +10,6 @@
#include "mozilla/AutoRestore.h"
#include "mozilla/dom/BlobBinding.h"
#include "mozilla/dom/CryptoKey.h"
#include "mozilla/dom/Directory.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/FileList.h"
#include "mozilla/dom/FileListBinding.h"
@ -724,59 +723,36 @@ ReadFileList(JSContext* aCx,
{
RefPtr<FileList> fileList = new FileList(aHolder->ParentDuringRead());
// |aCount| is the number of Files or Directory for this FileList.
uint32_t tag, offset;
// Offset is the index of the blobImpl from which we can find the blobImpl
// for this FileList.
if (!JS_ReadUint32Pair(aReader, &tag, &offset)) {
return nullptr;
}
MOZ_ASSERT(tag == 0);
// |aCount| is the number of BlobImpls to use from the |offset|.
for (uint32_t i = 0; i < aCount; ++i) {
uint32_t tagOrDirectoryType, indexOrLengthOfString;
if (!JS_ReadUint32Pair(aReader, &tagOrDirectoryType,
&indexOrLengthOfString)) {
uint32_t index = offset + i;
MOZ_ASSERT(index < aHolder->BlobImpls().Length());
RefPtr<BlobImpl> blobImpl = aHolder->BlobImpls()[index];
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)) {
@ -789,13 +765,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,
@ -805,8 +775,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;
}
@ -814,39 +789,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;
}
@ -1050,9 +1004,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

@ -7625,6 +7625,7 @@ nsContentUtils::TransferableToIPCTransferable(nsITransferable* aTransferable,
// has this data available to it when passed over:
blobImpl->GetSize(rv);
blobImpl->GetLastModified(rv);
blobImpl->LookupAndCacheIsDirectory();
} else {
if (aInSyncMessage) {
// Can't do anything.

View File

@ -1,25 +1,15 @@
var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.importGlobalProperties(["File"]);
addMessageListener("file.open", function () {
var testFile = Cc["@mozilla.org/file/directory_service;1"]
.getService(Ci.nsIDirectoryService)
.QueryInterface(Ci.nsIProperties)
.get("ProfD", Ci.nsIFile);
testFile.append("prefs.js");
var testFile = Cc["@mozilla.org/file/directory_service;1"]
.getService(Ci.nsIDirectoryService)
.QueryInterface(Ci.nsIProperties)
.get("ProfD", Ci.nsIFile);
testFile.append("prefs.js");
addMessageListener("file.open", function () {
sendAsyncMessage("file.opened", {
file: new File(testFile)
});
});
addMessageListener("dir.open", function () {
var testFile = Cc["@mozilla.org/file/directory_service;1"]
.getService(Ci.nsIDirectoryService)
.QueryInterface(Ci.nsIProperties)
.get("ProfD", Ci.nsIFile);
sendAsyncMessage("dir.opened", {
dir: testFile.path
});
});

View File

@ -61,18 +61,18 @@ function compare(a, b) {
}
var clonableObjects = [
{ crossThreads: true, data: 'hello world' },
{ crossThreads: true, data: 123 },
{ crossThreads: true, data: null },
{ crossThreads: true, data: true },
{ crossThreads: true, data: new Date() },
{ crossThreads: true, data: [ 1, 'test', true, new Date() ] },
{ crossThreads: true, data: { a: true, b: null, c: new Date(), d: [ true, false, {} ] } },
{ crossThreads: true, data: new Blob([123], { type: 'plain/text' }) },
{ crossThreads: true, data: new ImageData(2, 2) },
'hello world',
123,
null,
true,
new Date(),
[ 1, 'test', true, new Date() ],
{ a: true, b: null, c: new Date(), d: [ true, false, {} ] },
new Blob([123], { type: 'plain/text' }),
new ImageData(2, 2),
];
function create_fileList_forFile() {
function create_fileList() {
var url = SimpleTest.getTestFileURL("script_postmessages_fileList.js");
var script = SpecialPowers.loadChromeScript(url);
@ -84,7 +84,7 @@ function create_fileList_forFile() {
var domFile = fileList.files[0];
is(domFile.name, "prefs.js", "fileName should be prefs.js");
clonableObjects.push({ crossThreads: true, data: fileList.files });
clonableObjects.push(fileList.files);
script.destroy();
next();
}
@ -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({ crossThreads: false, data: fileList.files });
script.destroy();
next();
}
script.addMessageListener("dir.opened", onOpened);
script.sendAsyncMessage("dir.open");
}
function runTests(obj) {
ok(('clonableObjects' in obj) &&
('transferableObjects' in obj) &&
@ -134,16 +113,8 @@ function runTests(obj) {
}
var object = clonableObjects[clonableObjectsId++];
// If this test requires a cross-thread structured clone algorithm, maybe
// we have to skip it.
if (!object.crossThread && obj.crossThread) {
runClonableTest();
return;
}
obj.send(object.data, []).then(function(received) {
compare(received.data, object.data);
obj.send(object, []).then(function(received) {
compare(received.data, object);
runClonableTest();
});
}
@ -232,7 +203,6 @@ function test_windowToWindow() {
runTests({
clonableObjects: true,
transferableObjects: true,
crossThread: false,
send: function(what, ports) {
return new Promise(function(r, rr) {
resolve = r;
@ -286,7 +256,6 @@ function test_windowToIframeURL(url) {
runTests({
clonableObjects: true,
transferableObjects: true,
crossThread: false,
send: function(what, ports) {
return new Promise(function(r, rr) {
resolve = r;
@ -334,7 +303,6 @@ function test_workers() {
runTests({
clonableObjects: true,
transferableObjects: true,
crossThread: true,
send: function(what, ports) {
return new Promise(function(r, rr) {
resolve = r;
@ -378,7 +346,6 @@ function test_broadcastChannel() {
runTests({
clonableObjects: true,
transferableObjects: false,
crossThread: true,
send: function(what, ports) {
return new Promise(function(r, rr) {
if (ports.length) {
@ -424,7 +391,6 @@ function test_broadcastChannel_inWorkers() {
runTests({
clonableObjects: true,
transferableObjects: false,
crossThread: true,
send: function(what, ports) {
return new Promise(function(r, rr) {
if (ports.length) {
@ -466,7 +432,6 @@ function test_messagePort() {
runTests({
clonableObjects: true,
transferableObjects: true,
crossThread: true,
send: function(what, ports) {
return new Promise(function(r, rr) {
resolve = r;
@ -512,7 +477,6 @@ function test_messagePort_inWorkers() {
runTests({
clonableObjects: true,
transferableObjects: true,
crossThread: true,
send: function(what, ports) {
return new Promise(function(r, rr) {
resolve = r;
@ -534,8 +498,7 @@ function test_messagePort_inWorkers() {
}
var tests = [
create_fileList_forFile,
create_fileList_forDir,
create_fileList,
test_windowToWindow,
test_windowToIframe,

View File

@ -101,9 +101,8 @@ public:
// we want to make sure that the names of file can't reach
// outside of the type of storage the user asked for.
bool IsSafePath() const;
bool ValidateAndSplitPath(const nsAString& aPath,
nsTArray<nsString>* aParts = nullptr) const;
bool IsSafePath();
bool IsSafePath(const nsAString& aPath);
void Dump(const char* label);
@ -138,6 +137,7 @@ public:
private:
~DeviceStorageFile() {}
void Init();
void NormalizeFilePath();
void AppendRelativePath(const nsAString& aPath);
void AccumDirectoryUsage(nsIFile* aFile,
uint64_t* aPicturesSoFar,

View File

@ -582,16 +582,7 @@ DeviceStorageStatics::ResetOverrideRootDir()
nsCOMPtr<nsIFile> f;
DS_LOG_INFO("");
// For users running on desktop, it's convenient to be able to override
// all of the directories to point to a single tree, much like what happens
// on a real device.
const nsAdoptingString& overrideRootDir =
mozilla::Preferences::GetString(kPrefOverrideRootDir);
if (overrideRootDir && !overrideRootDir.IsEmpty()) {
NS_NewLocalFile(overrideRootDir, false, getter_AddRefs(f));
}
if (!f && Preferences::GetBool(kPrefTesting, false)) {
if (Preferences::GetBool(kPrefTesting, false)) {
DS_LOG_INFO("temp");
nsCOMPtr<nsIProperties> dirService
= do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
@ -601,6 +592,15 @@ DeviceStorageStatics::ResetOverrideRootDir()
f->AppendRelativeNativePath(
NS_LITERAL_CSTRING("device-storage-testing"));
}
} else {
// For users running on desktop, it's convenient to be able to override
// all of the directories to point to a single tree, much like what happens
// on a real device.
const nsAdoptingString& overrideRootDir =
mozilla::Preferences::GetString(kPrefOverrideRootDir);
if (overrideRootDir && !overrideRootDir.IsEmpty()) {
NS_NewLocalFile(overrideRootDir, false, getter_AddRefs(f));
}
}
if (f) {

View File

@ -30,7 +30,6 @@
#include "nsArrayUtils.h"
#include "nsAutoPtr.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsGlobalWindow.h"
#include "nsServiceManagerUtils.h"
#include "nsIFile.h"
@ -77,35 +76,10 @@ using namespace mozilla::dom;
using namespace mozilla::dom::devicestorage;
using namespace mozilla::ipc;
namespace mozilla
{
namespace mozilla {
MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc, PRFileDesc, PR_Close);
} // namespace mozilla
namespace {
void
NormalizeFilePath(nsAString& aPath)
{
#if defined(XP_WIN)
char16_t* cur = aPath.BeginWriting();
char16_t* end = aPath.EndWriting();
for (; cur < end; ++cur) {
if (char16_t('\\') == *cur) {
*cur = FILESYSTEM_DOM_PATH_SEPARATOR_CHAR;
}
}
#endif
}
bool
TokenizerIgnoreNothing(char16_t /* aChar */)
{
return false;
}
} // anonymous namespace
StaticAutoPtr<DeviceStorageUsedSpaceCache>
DeviceStorageUsedSpaceCache::sDeviceStorageUsedSpaceCache;
@ -536,8 +510,7 @@ DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType,
if (!mPath.EqualsLiteral("")) {
AppendRelativePath(mPath);
}
NormalizeFilePath(mPath);
NormalizeFilePath();
}
DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType,
@ -552,7 +525,7 @@ DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType,
{
Init();
AppendRelativePath(aPath);
NormalizeFilePath(mPath);
NormalizeFilePath();
}
DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType,
@ -761,7 +734,7 @@ DeviceStorageFile::CreateUnique(const nsAString& aStorageType,
void
DeviceStorageFile::SetPath(const nsAString& aPath) {
mPath.Assign(aPath);
NormalizeFilePath(mPath);
NormalizeFilePath();
}
void
@ -772,14 +745,13 @@ DeviceStorageFile::SetEditable(bool aEditable) {
// we want to make sure that the names of file can't reach
// outside of the type of storage the user asked for.
bool
DeviceStorageFile::IsSafePath() const
DeviceStorageFile::IsSafePath()
{
return ValidateAndSplitPath(mRootDir) && ValidateAndSplitPath(mPath);
return IsSafePath(mRootDir) && IsSafePath(mPath);
}
bool
DeviceStorageFile::ValidateAndSplitPath(const nsAString& aPath,
nsTArray<nsString>* aParts) const
DeviceStorageFile::IsSafePath(const nsAString& aPath)
{
nsAString::const_iterator start, end;
aPath.BeginReading(start);
@ -792,43 +764,33 @@ DeviceStorageFile::ValidateAndSplitPath(const nsAString& aPath,
StringBeginsWith(aPath, tildeSlash)) {
NS_WARNING("Path name starts with tilde!");
return false;
}
}
// split on /. if any token is "", ., or .., return false.
NS_ConvertUTF16toUTF8 cname(aPath);
char* buffer = cname.BeginWriting();
const char* token;
NS_NAMED_LITERAL_STRING(kCurrentDir, ".");
NS_NAMED_LITERAL_STRING(kParentDir, "..");
// Split path and check each path component.
nsCharSeparatedTokenizerTemplate<TokenizerIgnoreNothing>
tokenizer(aPath, FILESYSTEM_DOM_PATH_SEPARATOR_CHAR);
while (tokenizer.hasMoreTokens()) {
nsDependentSubstring pathComponent = tokenizer.nextToken();
// The path containing empty components, such as "foo//bar", is invalid.
// We don't allow paths, such as "../foo", "foo/./bar" and "foo/../bar",
// to walk up the directory.
if (pathComponent.IsEmpty() ||
pathComponent.Equals(kCurrentDir) ||
pathComponent.Equals(kParentDir)) {
while ((token = nsCRT::strtok(buffer, "/", &buffer))) {
if (PL_strcmp(token, "") == 0 ||
PL_strcmp(token, ".") == 0 ||
PL_strcmp(token, "..") == 0 ) {
return false;
}
if (aParts) {
aParts->AppendElement(pathComponent);
}
}
return true;
}
void
DeviceStorageFile::AppendRelativePath(const nsAString& aPath)
{
DeviceStorageFile::NormalizeFilePath() {
FileSystemUtils::LocalPathToNormalizedPath(mPath, mPath);
}
void
DeviceStorageFile::AppendRelativePath(const nsAString& aPath) {
if (!mFile) {
return;
}
nsTArray<nsString> parts;
if (!ValidateAndSplitPath(aPath, &parts)) {
if (!IsSafePath(aPath)) {
// All of the APIs (in the child) do checks to verify that the path is
// valid and return PERMISSION_DENIED if a non-safe path is entered.
// This check is done in the parent and prevents a compromised
@ -838,13 +800,9 @@ DeviceStorageFile::AppendRelativePath(const nsAString& aPath)
NS_WARNING(NS_LossyConvertUTF16toASCII(aPath).get());
return;
}
for (uint32_t i = 0; i < parts.Length(); ++i) {
nsresult rv = mFile->AppendRelativePath(parts[i]);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
}
nsString localPath;
FileSystemUtils::NormalizedPathToLocalPath(aPath, localPath);
mFile->AppendRelativePath(localPath);
}
nsresult

View File

@ -20,20 +20,12 @@ Object.defineProperty(Array.prototype, "remove", {
function devicestorage_setup(callback) {
SimpleTest.waitForExplicitFinish();
const Cc = SpecialPowers.Cc;
const Ci = SpecialPowers.Ci;
var directoryService = Cc["@mozilla.org/file/directory_service;1"]
.getService(Ci.nsIProperties);
var f = directoryService.get("TmpD", Ci.nsIFile);
f.appendRelativePath("device-storage-testing");
let script = SpecialPowers.loadChromeScript(SimpleTest.getTestFileURL('remove_testing_directory.js'));
script.addMessageListener('directory-removed', function listener () {
script.removeMessageListener('directory-removed', listener);
var prefs = [["device.storage.enabled", true],
["device.storage.testing", true],
["device.storage.overrideRootDir", f.path],
["device.storage.prompt.testing", true]];
SpecialPowers.pushPrefEnv({"set": prefs}, callback);
});

View File

@ -34,14 +34,14 @@ function testCreateDirectory(rootDir, path) {
}
function createDirectorySuccess(d) {
is(d.name, gName, "Failed to create directory: name mismatch.");
ok(d.name === gName, "Failed to create directory: name mismatch.");
// Get the new created directory from the root.
gRoot.get(gPath).then(getSuccess, cbError);
}
function getSuccess(d) {
is(d.name, gName, "Should get directory - " + (gPath || "[root]") + ".");
ok(d.name === gName, "Should get directory - " + (gPath || "[root]") + ".");
switch (gTestCount) {
case 0:
gRoot = d;

View File

@ -149,7 +149,8 @@ try {
// The remove will fail if the directory doesn't exist, which is fine.
f.remove(true);
} catch (e) {}
SpecialPowers.pushPrefEnv({'set': [["device.storage.overrideRootDir", f.path]]},
SpecialPowers.pushPrefEnv({'set': [["device.storage.overrideRootDir", f.path],
["device.storage.testing", false]]},
function() {
startTest();
});

View File

@ -49,7 +49,7 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(DataTransfer)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DataTransfer)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFileList)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFiles)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mItems)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragTarget)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragImage)
@ -57,7 +57,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DataTransfer)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DataTransfer)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFileList)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFiles)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mItems)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragTarget)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragImage)
@ -281,12 +281,11 @@ DataTransfer::GetMozUserCancelled(bool* aUserCancelled)
FileList*
DataTransfer::GetFiles(ErrorResult& aRv)
{
return GetFileListInternal(aRv, nsContentUtils::SubjectPrincipal());
return GetFilesInternal(aRv, nsContentUtils::SubjectPrincipal());
}
FileList*
DataTransfer::GetFileListInternal(ErrorResult& aRv,
nsIPrincipal* aSubjectPrincipal)
DataTransfer::GetFilesInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal)
{
if (mEventMessage != eDrop &&
mEventMessage != eLegacyDragDrop &&
@ -294,15 +293,14 @@ DataTransfer::GetFileListInternal(ErrorResult& aRv,
return nullptr;
}
if (!mFileList) {
mFileList = new FileList(static_cast<nsIDOMDataTransfer*>(this));
if (!mFiles) {
mFiles = new FileList(static_cast<nsIDOMDataTransfer*>(this));
uint32_t count = mItems.Length();
for (uint32_t i = 0; i < count; i++) {
nsCOMPtr<nsIVariant> variant;
aRv = GetDataAtInternal(NS_ConvertUTF8toUTF16(kFileMime), i,
aSubjectPrincipal, getter_AddRefs(variant));
aRv = GetDataAtInternal(NS_ConvertUTF8toUTF16(kFileMime), i, aSubjectPrincipal, getter_AddRefs(variant));
if (aRv.Failed()) {
return nullptr;
}
@ -340,18 +338,21 @@ DataTransfer::GetFileListInternal(ErrorResult& aRv,
MOZ_ASSERT(domFile);
}
mFileList->Append(domFile);
if (!mFiles->Append(domFile)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
}
}
return mFileList;
return mFiles;
}
NS_IMETHODIMP
DataTransfer::GetFiles(nsIDOMFileList** aFileList)
{
ErrorResult rv;
NS_IF_ADDREF(*aFileList = GetFileListInternal(rv, nsContentUtils::GetSystemPrincipal()));
NS_IF_ADDREF(*aFileList = GetFilesInternal(rv, nsContentUtils::GetSystemPrincipal()));
return rv.StealNSResult();
}
@ -853,7 +854,7 @@ DataTransfer::GetFilesAndDirectories(ErrorResult& aRv)
return nullptr;
}
if (!mFileList) {
if (!mFiles) {
GetFiles(aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
@ -861,9 +862,40 @@ DataTransfer::GetFilesAndDirectories(ErrorResult& aRv)
}
Sequence<OwningFileOrDirectory> filesAndDirsSeq;
mFileList->ToSequence(filesAndDirsSeq, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
if (mFiles && mFiles->Length()) {
if (!filesAndDirsSeq.SetLength(mFiles->Length(), mozilla::fallible_t())) {
p->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
return p.forget();
}
for (uint32_t i = 0; i < mFiles->Length(); ++i) {
if (mFiles->Item(i)->Impl()->IsDirectory()) {
#if defined(ANDROID) || defined(MOZ_B2G)
MOZ_ASSERT(false,
"Directory picking should have been redirected to normal "
"file picking for platforms that don't have a directory "
"picker");
#endif
nsAutoString path;
mFiles->Item(i)->GetMozFullPathInternal(path, aRv);
if (aRv.Failed()) {
return nullptr;
}
int32_t leafSeparatorIndex = path.RFind(FILE_PATH_SEPARATOR);
nsDependentSubstring dirname = Substring(path, 0, leafSeparatorIndex);
nsDependentSubstring basename = Substring(path, leafSeparatorIndex);
RefPtr<OSFileSystem> fs = new OSFileSystem(dirname);
fs->Init(parentNode->OwnerDoc()->GetInnerWindow());
RefPtr<Directory> directory = new Directory(fs, basename);
directory->SetContentFilters(NS_LITERAL_STRING("filter-out-sensitive"));
filesAndDirsSeq[i].SetAsDirectory() = directory;
} else {
filesAndDirsSeq[i].SetAsFile() = mFiles->Item(i);
}
}
}
p->MaybeResolve(filesAndDirsSeq);

View File

@ -252,7 +252,7 @@ protected:
void FillInExternalData(TransferItem& aItem, uint32_t aIndex);
FileList* GetFileListInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal);
FileList* GetFilesInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal);
nsresult GetDataAtInternal(const nsAString& aFormat, uint32_t aIndex,
nsIPrincipal* aSubjectPrincipal, nsIVariant** aData);
nsresult SetDataAtInternal(const nsAString& aFormat, nsIVariant* aData, uint32_t aIndex,
@ -300,9 +300,8 @@ protected:
// array of items, each containing an array of format->data pairs
nsTArray<nsTArray<TransferItem> > mItems;
// array of files and directories, containing only the files present in the
// dataTransfer
RefPtr<FileList> mFileList;
// array of files, containing only the files present in the dataTransfer
RefPtr<FileList> mFiles;
// the target of the drag. The drag and dragend events will fire at this.
nsCOMPtr<mozilla::dom::Element> mDragTarget;

View File

@ -318,7 +318,8 @@ ConvertActorToFile(FileHandleBase* aFileHandle,
actor->SetMysteryBlobInfo(mutableFile->Name(),
mutableFile->Type(),
size.get_uint64_t(),
lastModified.get_int64_t());
lastModified.get_int64_t(),
BlobDirState::eUnknownIfDir);
RefPtr<BlobImpl> blobImpl = actor->GetBlobImpl();
MOZ_ASSERT(blobImpl);

View File

@ -16,73 +16,33 @@
namespace mozilla {
namespace dom {
/* static */ already_AddRefed<CreateDirectoryTask>
CreateDirectoryTask::Create(FileSystemBase* aFileSystem,
nsIFile* aTargetPath,
ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
RefPtr<CreateDirectoryTask> task =
new CreateDirectoryTask(aFileSystem, aTargetPath);
// aTargetPath can be null. In this case SetError will be called.
nsCOMPtr<nsIGlobalObject> globalObject =
do_QueryInterface(aFileSystem->GetParentObject());
if (NS_WARN_IF(!globalObject)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
task->mPromise = Promise::Create(globalObject, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return task.forget();
}
/* static */ already_AddRefed<CreateDirectoryTask>
CreateDirectoryTask::Create(FileSystemBase* aFileSystem,
const FileSystemCreateDirectoryParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
RefPtr<CreateDirectoryTask> task =
new CreateDirectoryTask(aFileSystem, aParam, aParent);
NS_ConvertUTF16toUTF8 path(aParam.realPath());
aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return task.forget();
}
CreateDirectoryTask::CreateDirectoryTask(FileSystemBase* aFileSystem,
nsIFile* aTargetPath)
const nsAString& aPath,
ErrorResult& aRv)
: FileSystemTaskBase(aFileSystem)
, mTargetPath(aTargetPath)
, mTargetRealPath(aPath)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
nsCOMPtr<nsIGlobalObject> globalObject =
do_QueryInterface(aFileSystem->GetWindow());
if (!globalObject) {
return;
}
mPromise = Promise::Create(globalObject, aRv);
}
CreateDirectoryTask::CreateDirectoryTask(FileSystemBase* aFileSystem,
const FileSystemCreateDirectoryParams& aParam,
FileSystemRequestParent* aParent)
CreateDirectoryTask::CreateDirectoryTask(
FileSystemBase* aFileSystem,
const FileSystemCreateDirectoryParams& aParam,
FileSystemRequestParent* aParent)
: FileSystemTaskBase(aFileSystem, aParam, aParent)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
MOZ_ASSERT(XRE_IsParentProcess(),
"Only call from parent process!");
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
mTargetRealPath = aParam.realPath();
}
CreateDirectoryTask::~CreateDirectoryTask()
@ -99,44 +59,25 @@ CreateDirectoryTask::GetPromise()
}
FileSystemParams
CreateDirectoryTask::GetRequestParams(const nsString& aSerializedDOMPath,
ErrorResult& aRv) const
CreateDirectoryTask::GetRequestParams(const nsString& aFileSystem) const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
nsAutoString path;
aRv = mTargetPath->GetPath(path);
if (NS_WARN_IF(aRv.Failed())) {
return FileSystemCreateDirectoryParams();
}
return FileSystemCreateDirectoryParams(aSerializedDOMPath, path);
return FileSystemCreateDirectoryParams(aFileSystem, mTargetRealPath);
}
FileSystemResponseValue
CreateDirectoryTask::GetSuccessRequestResult(ErrorResult& aRv) const
CreateDirectoryTask::GetSuccessRequestResult() const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
nsAutoString path;
aRv = mTargetPath->GetPath(path);
if (NS_WARN_IF(aRv.Failed())) {
return FileSystemDirectoryResponse();
}
return FileSystemDirectoryResponse(path);
return FileSystemDirectoryResponse(mTargetRealPath);
}
void
CreateDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv)
CreateDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
FileSystemDirectoryResponse r = aValue;
NS_ConvertUTF16toUTF8 path(r.realPath());
aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath));
NS_WARN_IF(aRv.Failed());
mTargetRealPath = r.realPath();
}
nsresult
@ -150,8 +91,13 @@ CreateDirectoryTask::Work()
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIFile> file = mFileSystem->GetLocalFile(mTargetRealPath);
if (!file) {
return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
}
bool fileExists;
nsresult rv = mTargetPath->Exists(&fileExists);
nsresult rv = file->Exists(&fileExists);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -160,12 +106,8 @@ CreateDirectoryTask::Work()
return NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR;
}
rv = mTargetPath->Create(nsIFile::DIRECTORY_TYPE, 0770);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
rv = file->Create(nsIFile::DIRECTORY_TYPE, 0770);
return rv;
}
void
@ -182,12 +124,7 @@ CreateDirectoryTask::HandlerCallback()
mPromise = nullptr;
return;
}
RefPtr<Directory> dir = Directory::Create(mFileSystem->GetParentObject(),
mTargetPath,
Directory::eNotDOMRootDirectory,
mFileSystem);
MOZ_ASSERT(dir);
RefPtr<Directory> dir = new Directory(mFileSystem, mTargetRealPath);
mPromise->MaybeResolve(dir);
mPromise = nullptr;
}

View File

@ -16,19 +16,16 @@ namespace dom {
class Promise;
class CreateDirectoryTask final : public FileSystemTaskBase
class CreateDirectoryTask final
: public FileSystemTaskBase
{
public:
static already_AddRefed<CreateDirectoryTask>
Create(FileSystemBase* aFileSystem,
nsIFile* aTargetPath,
ErrorResult& aRv);
static already_AddRefed<CreateDirectoryTask>
Create(FileSystemBase* aFileSystem,
const FileSystemCreateDirectoryParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv);
CreateDirectoryTask(FileSystemBase* aFileSystem,
const nsAString& aPath,
ErrorResult& aRv);
CreateDirectoryTask(FileSystemBase* aFileSystem,
const FileSystemCreateDirectoryParams& aParam,
FileSystemRequestParent* aParent);
virtual
~CreateDirectoryTask();
@ -41,15 +38,13 @@ public:
protected:
virtual FileSystemParams
GetRequestParams(const nsString& aSerializedDOMPath,
ErrorResult& aRv) const override;
GetRequestParams(const nsString& aFileSystem) const override;
virtual FileSystemResponseValue
GetSuccessRequestResult(ErrorResult& aRv) const override;
GetSuccessRequestResult() const override;
virtual void
SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv) override;
SetSuccessRequestResult(const FileSystemResponseValue& aValue) override;
virtual nsresult
Work() override;
@ -58,15 +53,8 @@ protected:
HandlerCallback() override;
private:
CreateDirectoryTask(FileSystemBase* aFileSystem,
nsIFile* aTargetPath);
CreateDirectoryTask(FileSystemBase* aFileSystem,
const FileSystemCreateDirectoryParams& aParam,
FileSystemRequestParent* aParent);
RefPtr<Promise> mPromise;
nsCOMPtr<nsIFile> mTargetPath;
nsString mTargetRealPath;
};
} // namespace dom

View File

@ -25,104 +25,34 @@ namespace dom {
uint32_t CreateFileTask::sOutputBufferSize = 0;
/* static */ already_AddRefed<CreateFileTask>
CreateFileTask::Create(FileSystemBase* aFileSystem,
nsIFile* aTargetPath,
Blob* aBlobData,
InfallibleTArray<uint8_t>& aArrayData,
bool aReplace,
ErrorResult& aRv)
CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem,
const nsAString& aPath,
Blob* aBlobData,
InfallibleTArray<uint8_t>& aArrayData,
bool replace,
ErrorResult& aRv)
: FileSystemTaskBase(aFileSystem)
, mTargetRealPath(aPath)
, mReplace(replace)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
RefPtr<CreateFileTask> task =
new CreateFileTask(aFileSystem, aTargetPath, aReplace);
// aTargetPath can be null. In this case SetError will be called.
task->GetOutputBufferSize();
GetOutputBufferSize();
if (aBlobData) {
if (XRE_IsParentProcess()) {
aBlobData->GetInternalStream(getter_AddRefs(task->mBlobStream), aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
aBlobData->GetInternalStream(getter_AddRefs(mBlobStream), aRv);
NS_WARN_IF(aRv.Failed());
} else {
task->mBlobData = aBlobData;
mBlobData = aBlobData;
}
}
task->mArrayData.SwapElements(aArrayData);
mArrayData.SwapElements(aArrayData);
nsCOMPtr<nsIGlobalObject> globalObject =
do_QueryInterface(aFileSystem->GetParentObject());
if (NS_WARN_IF(!globalObject)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
do_QueryInterface(aFileSystem->GetWindow());
if (!globalObject) {
return;
}
task->mPromise = Promise::Create(globalObject, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return task.forget();
}
/* static */ already_AddRefed<CreateFileTask>
CreateFileTask::Create(FileSystemBase* aFileSystem,
const FileSystemCreateFileParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
RefPtr<CreateFileTask> task =
new CreateFileTask(aFileSystem, aParam, aParent);
task->GetOutputBufferSize();
NS_ConvertUTF16toUTF8 path(aParam.realPath());
aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
task->mReplace = aParam.replace();
auto& data = aParam.data();
if (data.type() == FileSystemFileDataValue::TArrayOfuint8_t) {
task->mArrayData = data;
return task.forget();
}
BlobParent* bp = static_cast<BlobParent*>(static_cast<PBlobParent*>(data));
RefPtr<BlobImpl> blobImpl = bp->GetBlobImpl();
MOZ_ASSERT(blobImpl, "blobData should not be null.");
ErrorResult rv;
blobImpl->GetInternalStream(getter_AddRefs(task->mBlobStream), rv);
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();
}
return task.forget();
}
CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem,
nsIFile* aTargetPath,
bool aReplace)
: FileSystemTaskBase(aFileSystem)
, mTargetPath(aTargetPath)
, mReplace(aReplace)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
mPromise = Promise::Create(globalObject, aRv);
}
CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem,
@ -131,9 +61,32 @@ CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem,
: FileSystemTaskBase(aFileSystem, aParam, aParent)
, mReplace(false)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
MOZ_ASSERT(XRE_IsParentProcess(),
"Only call from parent process!");
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
GetOutputBufferSize();
mTargetRealPath = aParam.realPath();
mReplace = aParam.replace();
auto& data = aParam.data();
if (data.type() == FileSystemFileDataValue::TArrayOfuint8_t) {
mArrayData = data;
return;
}
BlobParent* bp = static_cast<BlobParent*>(static_cast<PBlobParent*>(data));
RefPtr<BlobImpl> blobImpl = bp->GetBlobImpl();
MOZ_ASSERT(blobImpl, "blobData should not be null.");
ErrorResult rv;
blobImpl->GetInternalStream(getter_AddRefs(mBlobStream), rv);
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();
}
}
CreateFileTask::~CreateFileTask()
@ -154,22 +107,16 @@ CreateFileTask::GetPromise()
}
FileSystemParams
CreateFileTask::GetRequestParams(const nsString& aSerializedDOMPath,
ErrorResult& aRv) const
CreateFileTask::GetRequestParams(const nsString& aFileSystem) const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
FileSystemCreateFileParams param;
param.filesystem() = aSerializedDOMPath;
aRv = mTargetPath->GetPath(param.realPath());
if (NS_WARN_IF(aRv.Failed())) {
return param;
}
param.filesystem() = aFileSystem;
param.realPath() = mTargetRealPath;
param.replace() = mReplace;
if (mBlobData) {
BlobChild* actor =
ContentChild::GetSingleton()->GetOrCreateActorForBlob(mBlobData);
BlobChild* actor
= ContentChild::GetSingleton()->GetOrCreateActorForBlob(mBlobData);
if (actor) {
param.data() = actor;
}
@ -180,7 +127,7 @@ CreateFileTask::GetRequestParams(const nsString& aSerializedDOMPath,
}
FileSystemResponseValue
CreateFileTask::GetSuccessRequestResult(ErrorResult& aRv) const
CreateFileTask::GetSuccessRequestResult() const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
BlobParent* actor = GetBlobParent(mTargetBlobImpl);
@ -193,8 +140,7 @@ CreateFileTask::GetSuccessRequestResult(ErrorResult& aRv) const
}
void
CreateFileTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv)
CreateFileTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
FileSystemFileResponse r = aValue;
@ -205,7 +151,7 @@ CreateFileTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
nsresult
CreateFileTask::Work()
{
class MOZ_RAII AutoClose final
class AutoClose
{
public:
explicit AutoClose(nsIOutputStream* aStream)
@ -218,7 +164,6 @@ CreateFileTask::Work()
{
mStream->Close();
}
private:
nsCOMPtr<nsIOutputStream> mStream;
};
@ -231,19 +176,24 @@ CreateFileTask::Work()
return NS_ERROR_FAILURE;
}
if (!mFileSystem->IsSafeFile(mTargetPath)) {
nsCOMPtr<nsIFile> file = mFileSystem->GetLocalFile(mTargetRealPath);
if (!file) {
return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
}
if (!mFileSystem->IsSafeFile(file)) {
return NS_ERROR_DOM_SECURITY_ERR;
}
bool exists = false;
nsresult rv = mTargetPath->Exists(&exists);
nsresult rv = file->Exists(&exists);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (exists) {
bool isFile = false;
rv = mTargetPath->IsFile(&isFile);
rv = file->IsFile(&isFile);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -257,19 +207,19 @@ CreateFileTask::Work()
}
// Remove the old file before creating.
rv = mTargetPath->Remove(false);
rv = file->Remove(false);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
rv = mTargetPath->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsIOutputStream> outputStream;
rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mTargetPath);
rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -312,7 +262,7 @@ CreateFileTask::Work()
return NS_ERROR_FAILURE;
}
mTargetBlobImpl = new BlobImplFile(mTargetPath);
mTargetBlobImpl = new BlobImplFile(file);
return NS_OK;
}
@ -331,7 +281,7 @@ CreateFileTask::Work()
return NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR;
}
mTargetBlobImpl = new BlobImplFile(mTargetPath);
mTargetBlobImpl = new BlobImplFile(file);
return NS_OK;
}
@ -352,8 +302,7 @@ CreateFileTask::HandlerCallback()
return;
}
RefPtr<Blob> blob = Blob::Create(mFileSystem->GetParentObject(),
mTargetBlobImpl);
RefPtr<Blob> blob = Blob::Create(mFileSystem->GetWindow(), mTargetBlobImpl);
mPromise->MaybeResolve(blob);
mPromise = nullptr;
mBlobData = nullptr;

View File

@ -20,22 +20,19 @@ class Blob;
class BlobImpl;
class Promise;
class CreateFileTask final : public FileSystemTaskBase
class CreateFileTask final
: public FileSystemTaskBase
{
public:
static already_AddRefed<CreateFileTask>
Create(FileSystemBase* aFileSystem,
nsIFile* aFile,
Blob* aBlobData,
InfallibleTArray<uint8_t>& aArrayData,
bool replace,
ErrorResult& aRv);
static already_AddRefed<CreateFileTask>
Create(FileSystemBase* aFileSystem,
const FileSystemCreateFileParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv);
CreateFileTask(FileSystemBase* aFileSystem,
const nsAString& aPath,
Blob* aBlobData,
InfallibleTArray<uint8_t>& aArrayData,
bool replace,
ErrorResult& aRv);
CreateFileTask(FileSystemBase* aFileSystem,
const FileSystemCreateFileParams& aParam,
FileSystemRequestParent* aParent);
virtual
~CreateFileTask();
@ -48,15 +45,13 @@ public:
protected:
virtual FileSystemParams
GetRequestParams(const nsString& aSerializedDOMPath,
ErrorResult& aRv) const override;
GetRequestParams(const nsString& aFileSystem) const override;
virtual FileSystemResponseValue
GetSuccessRequestResult(ErrorResult& aRv) const override;
GetSuccessRequestResult() const override;
virtual void
SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv) override;
SetSuccessRequestResult(const FileSystemResponseValue& aValue) override;
virtual nsresult
Work() override;
@ -65,20 +60,12 @@ protected:
HandlerCallback() override;
private:
CreateFileTask(FileSystemBase* aFileSystem,
nsIFile* aFile,
bool aReplace);
CreateFileTask(FileSystemBase* aFileSystem,
const FileSystemCreateFileParams& aParam,
FileSystemRequestParent* aParent);
void
GetOutputBufferSize() const;
static uint32_t sOutputBufferSize;
RefPtr<Promise> mPromise;
nsCOMPtr<nsIFile> mTargetPath;
nsString mTargetRealPath;
// Not thread-safe and should be released on main thread.
RefPtr<Blob> mBlobData;

View File

@ -21,8 +21,9 @@
namespace mozilla {
namespace dom {
DeviceStorageFileSystem::DeviceStorageFileSystem(const nsAString& aStorageType,
const nsAString& aStorageName)
DeviceStorageFileSystem::DeviceStorageFileSystem(
const nsAString& aStorageType,
const nsAString& aStorageName)
: mWindowId(0)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
@ -30,6 +31,12 @@ DeviceStorageFileSystem::DeviceStorageFileSystem(const nsAString& aStorageType,
mStorageType = aStorageType;
mStorageName = aStorageName;
// Generate the string representation of the file system.
mString.AppendLiteral("devicestorage-");
mString.Append(mStorageType);
mString.Append('-');
mString.Append(mStorageName);
mRequiresPermissionChecks =
!mozilla::Preferences::GetBool("device.storage.prompt.testing", false);
@ -39,16 +46,19 @@ DeviceStorageFileSystem::DeviceStorageFileSystem(const nsAString& aStorageType,
NS_WARN_IF(NS_FAILED(rv));
// Get the local path of the file system root.
// Since the child process is not allowed to access the file system, we only
// do this from the parent process.
if (!XRE_IsParentProcess()) {
return;
}
nsCOMPtr<nsIFile> rootFile;
DeviceStorageFile::GetRootDirectoryForType(aStorageType,
aStorageName,
getter_AddRefs(rootFile));
NS_WARN_IF(!rootFile || NS_FAILED(rootFile->GetPath(mLocalRootPath)));
if (!XRE_IsParentProcess()) {
return;
}
FileSystemUtils::LocalPathToNormalizedPath(mLocalRootPath,
mNormalizedLocalRootPath);
// DeviceStorageTypeChecker is a singleton object and must be initialized on
// the main thread. We initialize it here so that we can use it on the worker
@ -79,8 +89,8 @@ DeviceStorageFileSystem::Shutdown()
mShutdown = true;
}
nsISupports*
DeviceStorageFileSystem::GetParentObject() const
nsPIDOMWindowInner*
DeviceStorageFileSystem::GetWindow() const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
@ -101,15 +111,12 @@ DeviceStorageFileSystem::IsSafeFile(nsIFile* aFile) const
"Should be on parent process!");
MOZ_ASSERT(aFile);
nsCOMPtr<nsIFile> rootPath;
nsresult rv = NS_NewLocalFile(GetLocalRootPath(), false,
getter_AddRefs(rootPath));
if (NS_WARN_IF(NS_FAILED(rv))) {
// Check if this file belongs to this storage.
nsAutoString path;
if (NS_FAILED(aFile->GetPath(path))) {
return false;
}
// Check if this file belongs to this storage.
if (NS_WARN_IF(!FileSystemUtils::IsDescendantPath(rootPath, aFile))) {
if (!LocalPathToRealPath(path, path)) {
return false;
}
@ -125,32 +132,10 @@ DeviceStorageFileSystem::IsSafeDirectory(Directory* aDir) const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aDir);
ErrorResult rv;
RefPtr<FileSystemBase> fs = aDir->GetFileSystem(rv);
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();
return false;
}
nsAutoString fsSerialization;
fs->SerializeDOMPath(fsSerialization);
nsAutoString thisSerialization;
SerializeDOMPath(thisSerialization);
RefPtr<FileSystemBase> fs = aDir->GetFileSystem();
MOZ_ASSERT(fs);
// Check if the given directory is from this storage.
return fsSerialization == thisSerialization;
}
void
DeviceStorageFileSystem::SerializeDOMPath(nsAString& aString) const
{
// Generate the string representation of the file system.
aString.AssignLiteral("devicestorage-");
aString.Append(mStorageType);
aString.Append('-');
aString.Append(mStorageName);
return fs->ToString() == mString;
}
} // namespace dom

View File

@ -30,8 +30,8 @@ public:
virtual void
Shutdown() override;
virtual nsISupports*
GetParentObject() const override;
virtual nsPIDOMWindowInner*
GetWindow() const override;
virtual void
GetRootName(nsAString& aRetval) const override;
@ -41,10 +41,6 @@ public:
virtual bool
IsSafeDirectory(Directory* aDir) const override;
virtual void
SerializeDOMPath(nsAString& aSerializedString) const override;
private:
virtual
~DeviceStorageFileSystem();

View File

@ -18,7 +18,6 @@
#include "mozilla/dom/DirectoryBinding.h"
#include "mozilla/dom/FileSystemBase.h"
#include "mozilla/dom/FileSystemUtils.h"
#include "mozilla/dom/OSFileSystem.h"
// Resolve the name collision of Microsoft's API name with macros defined in
// Windows header files. Undefine the macro of CreateDirectory to avoid
@ -35,70 +34,15 @@
namespace mozilla {
namespace dom {
namespace {
bool
TokenizerIgnoreNothing(char16_t /* aChar */)
{
return false;
}
bool
IsValidRelativeDOMPath(const nsString& aPath, nsTArray<nsString>& aParts)
{
// We don't allow empty relative path to access the root.
if (aPath.IsEmpty()) {
return false;
}
// Leading and trailing "/" are not allowed.
if (aPath.First() == FILESYSTEM_DOM_PATH_SEPARATOR_CHAR ||
aPath.Last() == FILESYSTEM_DOM_PATH_SEPARATOR_CHAR) {
return false;
}
NS_NAMED_LITERAL_STRING(kCurrentDir, ".");
NS_NAMED_LITERAL_STRING(kParentDir, "..");
// Split path and check each path component.
nsCharSeparatedTokenizerTemplate<TokenizerIgnoreNothing>
tokenizer(aPath, FILESYSTEM_DOM_PATH_SEPARATOR_CHAR);
while (tokenizer.hasMoreTokens()) {
nsDependentSubstring pathComponent = tokenizer.nextToken();
// The path containing empty components, such as "foo//bar", is invalid.
// We don't allow paths, such as "../foo", "foo/./bar" and "foo/../bar",
// to walk up the directory.
if (pathComponent.IsEmpty() ||
pathComponent.Equals(kCurrentDir) ||
pathComponent.Equals(kParentDir)) {
return false;
}
aParts.AppendElement(pathComponent);
}
return true;
}
} // anonymous namespace
NS_IMPL_CYCLE_COLLECTION_CLASS(Directory)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Directory)
if (tmp->mFileSystem) {
tmp->mFileSystem->Unlink();
tmp->mFileSystem = nullptr;
}
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
tmp->mFileSystem->Unlink();
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Directory)
if (tmp->mFileSystem) {
tmp->mFileSystem->Traverse(cb);
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
tmp->mFileSystem->Traverse(cb);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@ -115,67 +59,33 @@ NS_INTERFACE_MAP_END
already_AddRefed<Promise>
Directory::GetRoot(FileSystemBase* aFileSystem, ErrorResult& aRv)
{
MOZ_ASSERT(aFileSystem);
nsCOMPtr<nsIFile> path;
aRv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(aFileSystem->GetLocalRootPath()),
true, getter_AddRefs(path));
if (NS_WARN_IF(aRv.Failed())) {
RefPtr<GetFileOrDirectoryTask> task = new GetFileOrDirectoryTask(
aFileSystem, EmptyString(), true, aRv);
if (aRv.Failed()) {
return nullptr;
}
RefPtr<GetFileOrDirectoryTask> task =
GetFileOrDirectoryTask::Create(aFileSystem, path, eDOMRootDirectory, true, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
FileSystemPermissionRequest::RequestForTask(task);
return task->GetPromise();
}
/* static */ already_AddRefed<Directory>
Directory::Create(nsISupports* aParent, nsIFile* aFile,
DirectoryType aType, FileSystemBase* aFileSystem)
Directory::Directory(FileSystemBase* aFileSystem,
const nsAString& aPath)
: mFileSystem(aFileSystem)
, mPath(aPath)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aParent);
MOZ_ASSERT(aFile);
#ifdef DEBUG
bool isDir;
nsresult rv = aFile->IsDirectory(&isDir);
MOZ_ASSERT(NS_SUCCEEDED(rv) && isDir);
#endif
RefPtr<Directory> directory =
new Directory(aParent, aFile, aType, aFileSystem);
return directory.forget();
}
Directory::Directory(nsISupports* aParent,
nsIFile* aFile,
DirectoryType aType,
FileSystemBase* aFileSystem)
: mParent(aParent)
, mFileSystem(aFileSystem)
, mFile(aFile)
, mType(aType)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aFile);
// aFileSystem can be null. In this case we create a OSFileSystem when needed.
MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
// Remove the trailing "/".
mPath.Trim(FILESYSTEM_DOM_PATH_SEPARATOR, false, true);
}
Directory::~Directory()
{
}
nsISupports*
nsPIDOMWindowInner*
Directory::GetParentObject() const
{
return mParent;
return mFileSystem->GetWindow();
}
JSObject*
@ -185,28 +95,25 @@ Directory::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
}
void
Directory::GetName(nsAString& aRetval, ErrorResult& aRv)
Directory::GetName(nsAString& aRetval) const
{
aRetval.Truncate();
if (mType == eDOMRootDirectory) {
RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
fs->GetRootName(aRetval);
if (mPath.IsEmpty()) {
mFileSystem->GetRootName(aRetval);
return;
}
aRv = mFile->GetLeafName(aRetval);
NS_WARN_IF(aRv.Failed());
aRetval = Substring(mPath,
mPath.RFindChar(FileSystemUtils::kSeparatorChar) + 1);
}
already_AddRefed<Promise>
Directory::CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions,
ErrorResult& aRv)
{
nsresult error = NS_OK;
nsAutoString realPath;
RefPtr<Blob> blobData;
InfallibleTArray<uint8_t> arrayData;
bool replace = (aOptions.mIfExists == CreateIfExistsMode::Replace);
@ -231,20 +138,15 @@ Directory::CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions,
}
}
nsCOMPtr<nsIFile> realPath;
nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath));
RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
if (!DOMPathToRealPath(aPath, realPath)) {
error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
}
RefPtr<CreateFileTask> task =
CreateFileTask::Create(fs, realPath, blobData, arrayData, replace, aRv);
if (NS_WARN_IF(aRv.Failed())) {
new CreateFileTask(mFileSystem, realPath, blobData, arrayData, replace, aRv);
if (aRv.Failed()) {
return nullptr;
}
task->SetError(error);
FileSystemPermissionRequest::RequestForTask(task);
return task->GetPromise();
@ -253,20 +155,16 @@ Directory::CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions,
already_AddRefed<Promise>
Directory::CreateDirectory(const nsAString& aPath, ErrorResult& aRv)
{
nsCOMPtr<nsIFile> realPath;
nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath));
RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
if (NS_WARN_IF(aRv.Failed())) {
nsresult error = NS_OK;
nsAutoString realPath;
if (!DOMPathToRealPath(aPath, realPath)) {
error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
}
RefPtr<CreateDirectoryTask> task = new CreateDirectoryTask(
mFileSystem, realPath, aRv);
if (aRv.Failed()) {
return nullptr;
}
RefPtr<CreateDirectoryTask> task =
CreateDirectoryTask::Create(fs, realPath, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
task->SetError(error);
FileSystemPermissionRequest::RequestForTask(task);
return task->GetPromise();
@ -275,21 +173,16 @@ Directory::CreateDirectory(const nsAString& aPath, ErrorResult& aRv)
already_AddRefed<Promise>
Directory::Get(const nsAString& aPath, ErrorResult& aRv)
{
nsCOMPtr<nsIFile> realPath;
nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath));
RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
if (NS_WARN_IF(aRv.Failed())) {
nsresult error = NS_OK;
nsAutoString realPath;
if (!DOMPathToRealPath(aPath, realPath)) {
error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
}
RefPtr<GetFileOrDirectoryTask> task = new GetFileOrDirectoryTask(
mFileSystem, realPath, false, aRv);
if (aRv.Failed()) {
return nullptr;
}
RefPtr<GetFileOrDirectoryTask> task =
GetFileOrDirectoryTask::Create(fs, realPath, eNotDOMRootDirectory, false,
aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
task->SetError(error);
FileSystemPermissionRequest::RequestForTask(task);
return task->GetPromise();
@ -312,33 +205,30 @@ Directory::RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive,
ErrorResult& aRv)
{
nsresult error = NS_OK;
nsCOMPtr<nsIFile> realPath;
nsAutoString realPath;
RefPtr<BlobImpl> blob;
// Check and get the target path.
RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
if (aPath.IsFile()) {
blob = aPath.GetAsFile().Impl();
} else if (aPath.IsString()) {
error = DOMPathToRealPath(aPath.GetAsString(), getter_AddRefs(realPath));
} else if (!fs->IsSafeDirectory(&aPath.GetAsDirectory())) {
if (!DOMPathToRealPath(aPath.GetAsString(), realPath)) {
error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
}
} else if (!mFileSystem->IsSafeDirectory(&aPath.GetAsDirectory())) {
error = NS_ERROR_DOM_SECURITY_ERR;
} else {
realPath = aPath.GetAsDirectory().mFile;
realPath = aPath.GetAsDirectory().mPath;
// The target must be a descendant of this directory.
if (!FileSystemUtils::IsDescendantPath(mFile, realPath)) {
if (!FileSystemUtils::IsDescendantPath(mPath, realPath)) {
error = NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR;
}
}
RefPtr<RemoveTask> task =
RemoveTask::Create(fs, mFile, blob, realPath, aRecursive, aRv);
if (NS_WARN_IF(aRv.Failed())) {
RefPtr<RemoveTask> task = new RemoveTask(mFileSystem, mPath, blob, realPath,
aRecursive, aRv);
if (aRv.Failed()) {
return nullptr;
}
task->SetError(error);
@ -347,38 +237,23 @@ Directory::RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive,
}
void
Directory::GetPath(nsAString& aRetval, ErrorResult& aRv)
Directory::GetPath(nsAString& aRetval) const
{
if (mType == eDOMRootDirectory) {
aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
if (mPath.IsEmpty()) {
// The Directory ctor removes any trailing '/'; this is the root directory.
aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR);
} else {
// TODO: this should be a bit different...
GetName(aRetval, aRv);
aRetval = mPath;
}
}
nsresult
Directory::GetFullRealPath(nsAString& aPath)
{
nsresult rv = mFile->GetPath(aPath);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
already_AddRefed<Promise>
Directory::GetFilesAndDirectories(ErrorResult& aRv)
Directory::GetFilesAndDirectories()
{
RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
ErrorResult rv;
RefPtr<GetDirectoryListingTask> task =
GetDirectoryListingTask::Create(fs, mFile, mType, mFilters, aRv);
if (NS_WARN_IF(aRv.Failed())) {
new GetDirectoryListingTask(mFileSystem, mPath, mFilters, rv);
if (NS_WARN_IF(rv.Failed())) {
return nullptr;
}
@ -393,38 +268,16 @@ Directory::SetContentFilters(const nsAString& aFilters)
}
FileSystemBase*
Directory::GetFileSystem(ErrorResult& aRv)
Directory::GetFileSystem() const
{
if (!mFileSystem) {
nsCOMPtr<nsIFile> parent;
aRv = mFile->GetParent(getter_AddRefs(parent));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
// Parent can be null if mFile is pointing to the top directory.
if (!parent) {
parent = mFile;
}
nsAutoString path;
aRv = parent->GetPath(path);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
RefPtr<OSFileSystem> fs = new OSFileSystem(path);
fs->Init(mParent);
mFileSystem = fs;
}
return mFileSystem;
return mFileSystem.get();
}
nsresult
Directory::DOMPathToRealPath(const nsAString& aPath, nsIFile** aFile) const
bool
Directory::DOMPathToRealPath(const nsAString& aPath, nsAString& aRealPath) const
{
aRealPath.Truncate();
nsString relativePath;
relativePath = aPath;
@ -432,26 +285,49 @@ Directory::DOMPathToRealPath(const nsAString& aPath, nsIFile** aFile) const
static const char kWhitespace[] = "\b\t\r\n ";
relativePath.Trim(kWhitespace);
nsTArray<nsString> parts;
if (!IsValidRelativeDOMPath(relativePath, parts)) {
return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
if (!IsValidRelativePath(relativePath)) {
return false;
}
nsCOMPtr<nsIFile> file;
nsresult rv = mFile->Clone(getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
aRealPath = mPath + NS_LITERAL_STRING(FILESYSTEM_DOM_PATH_SEPARATOR) +
relativePath;
return true;
}
// static
bool
Directory::IsValidRelativePath(const nsString& aPath)
{
// We don't allow empty relative path to access the root.
if (aPath.IsEmpty()) {
return false;
}
for (uint32_t i = 0; i < parts.Length(); ++i) {
rv = file->AppendRelativePath(parts[i]);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
// Leading and trailing "/" are not allowed.
if (aPath.First() == FileSystemUtils::kSeparatorChar ||
aPath.Last() == FileSystemUtils::kSeparatorChar) {
return false;
}
NS_NAMED_LITERAL_STRING(kCurrentDir, ".");
NS_NAMED_LITERAL_STRING(kParentDir, "..");
// Split path and check each path component.
nsCharSeparatedTokenizer tokenizer(aPath, FileSystemUtils::kSeparatorChar);
while (tokenizer.hasMoreTokens()) {
nsDependentSubstring pathComponent = tokenizer.nextToken();
// The path containing empty components, such as "foo//bar", is invalid.
// We don't allow paths, such as "../foo", "foo/./bar" and "foo/../bar",
// to walk up the directory.
if (pathComponent.IsEmpty() ||
pathComponent.Equals(kCurrentDir) ||
pathComponent.Equals(kParentDir)) {
return false;
}
}
file.forget(aFile);
return NS_OK;
return true;
}
} // namespace dom

View File

@ -13,6 +13,7 @@
#include "mozilla/dom/File.h"
#include "nsAutoPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsPIDOMWindow.h"
#include "nsWrapperCache.h"
// Resolve the name collision of Microsoft's API name with macros defined in
@ -40,48 +41,25 @@ class Directory final
, public nsWrapperCache
{
public:
struct BlobImplOrDirectoryPath
{
RefPtr<BlobImpl> mBlobImpl;
nsString mDirectoryPath;
enum {
eBlobImpl,
eDirectoryPath
} mType;
};
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Directory)
public:
static already_AddRefed<Promise>
GetRoot(FileSystemBase* aFileSystem, ErrorResult& aRv);
enum DirectoryType {
// When a directory is selected using a HTMLInputElement, that will be the
// DOM root directory and its name will be '/'. All the sub directory will
// be called with they real name. We use this enum to mark what we must
// consider the '/' of this DOM filesystem.
eDOMRootDirectory,
// All the sub directories of the '/' will be marked using this other value.
eNotDOMRootDirectory
};
static already_AddRefed<Directory>
Create(nsISupports* aParent, nsIFile* aDirectory,
DirectoryType aType, FileSystemBase* aFileSystem = 0);
Directory(FileSystemBase* aFileSystem, const nsAString& aPath);
// ========= Begin WebIDL bindings. ===========
nsISupports*
nsPIDOMWindowInner*
GetParentObject() const;
virtual JSObject*
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
void
GetName(nsAString& aRetval, ErrorResult& aRv);
GetName(nsAString& aRetval) const;
already_AddRefed<Promise>
CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions,
@ -102,13 +80,10 @@ public:
// From https://microsoftedge.github.io/directory-upload/proposal.html#directory-interface :
void
GetPath(nsAString& aRetval, ErrorResult& aRv);
nsresult
GetFullRealPath(nsAString& aPath);
GetPath(nsAString& aRetval) const;
already_AddRefed<Promise>
GetFilesAndDirectories(ErrorResult& aRv);
GetFilesAndDirectories();
// =========== End WebIDL bindings.============
@ -138,34 +113,26 @@ public:
SetContentFilters(const nsAString& aFilters);
FileSystemBase*
GetFileSystem(ErrorResult& aRv);
DirectoryType Type() const
{
return mType;
}
GetFileSystem() const;
private:
Directory(nsISupports* aParent,
nsIFile* aFile, DirectoryType aType,
FileSystemBase* aFileSystem = nullptr);
~Directory();
static bool
IsValidRelativePath(const nsString& aPath);
/*
* Convert relative DOM path to the absolute real path.
* @return true if succeed. false if the DOM path is invalid.
*/
nsresult
DOMPathToRealPath(const nsAString& aPath, nsIFile** aFile) const;
bool
DOMPathToRealPath(const nsAString& aPath, nsAString& aRealPath) const;
already_AddRefed<Promise>
RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive,
ErrorResult& aRv);
nsCOMPtr<nsISupports> mParent;
RefPtr<FileSystemBase> mFileSystem;
nsCOMPtr<nsIFile> mFile;
DirectoryType mType;
nsString mPath;
nsString mFilters;
};

View File

@ -15,7 +15,7 @@ namespace dom {
// static
already_AddRefed<FileSystemBase>
FileSystemBase::DeserializeDOMPath(const nsAString& aString)
FileSystemBase::FromString(const nsAString& aString)
{
if (StringBeginsWith(aString, NS_LITERAL_STRING("devicestorage-"))) {
// The string representation of devicestorage file system is of the format:
@ -38,7 +38,6 @@ FileSystemBase::DeserializeDOMPath(const nsAString& aString)
new DeviceStorageFileSystem(storageType, storageName);
return f.forget();
}
return RefPtr<OSFileSystem>(new OSFileSystem(aString)).forget();
}
@ -58,19 +57,37 @@ FileSystemBase::Shutdown()
mShutdown = true;
}
nsISupports*
FileSystemBase::GetParentObject() const
nsPIDOMWindowInner*
FileSystemBase::GetWindow() const
{
return nullptr;
}
already_AddRefed<nsIFile>
FileSystemBase::GetLocalFile(const nsAString& aRealPath) const
{
MOZ_ASSERT(XRE_IsParentProcess(),
"Should be on parent process!");
nsAutoString localPath;
FileSystemUtils::NormalizedPathToLocalPath(aRealPath, localPath);
localPath = mLocalRootPath + localPath;
nsCOMPtr<nsIFile> file;
nsresult rv = NS_NewLocalFile(localPath, false, getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
return file.forget();
}
bool
FileSystemBase::GetRealPath(BlobImpl* aFile, nsIFile** aPath) const
FileSystemBase::GetRealPath(BlobImpl* aFile, nsAString& aRealPath) const
{
MOZ_ASSERT(XRE_IsParentProcess(),
"Should be on parent process!");
MOZ_ASSERT(aFile, "aFile Should not be null.");
aRealPath.Truncate();
nsAutoString filePath;
ErrorResult rv;
aFile->GetMozFullPathInternal(filePath, rv);
@ -78,13 +95,7 @@ FileSystemBase::GetRealPath(BlobImpl* aFile, nsIFile** aPath) const
return false;
}
rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(filePath),
true, aPath);
if (NS_WARN_IF(rv.Failed())) {
return false;
}
return true;
return LocalPathToRealPath(filePath, aRealPath);
}
bool
@ -99,5 +110,19 @@ FileSystemBase::IsSafeDirectory(Directory* aDir) const
return false;
}
bool
FileSystemBase::LocalPathToRealPath(const nsAString& aLocalPath,
nsAString& aRealPath) const
{
nsAutoString path;
FileSystemUtils::LocalPathToNormalizedPath(aLocalPath, path);
if (!FileSystemUtils::IsDescendantPath(mNormalizedLocalRootPath, path)) {
aRealPath.Truncate();
return false;
}
aRealPath = Substring(path, mNormalizedLocalRootPath.Length());
return true;
}
} // namespace dom
} // namespace mozilla

View File

@ -10,6 +10,8 @@
#include "nsAutoPtr.h"
#include "nsString.h"
class nsPIDOMWindowInner;
namespace mozilla {
namespace dom {
@ -23,19 +25,28 @@ public:
// Create file system object from its string representation.
static already_AddRefed<FileSystemBase>
DeserializeDOMPath(const nsAString& aString);
FromString(const nsAString& aString);
FileSystemBase();
virtual void
Shutdown();
// SerializeDOMPath the FileSystem to string.
virtual void
SerializeDOMPath(nsAString& aOutput) const = 0;
// Get the string representation of the file system.
const nsString&
ToString() const
{
return mString;
}
virtual nsISupports*
GetParentObject() const;
virtual nsPIDOMWindowInner*
GetWindow() const;
/**
* Create nsIFile object from the given real path (absolute DOM path).
*/
already_AddRefed<nsIFile>
GetLocalFile(const nsAString& aRealPath) const;
/*
* Get the virtual name of the root directory. This name will be exposed to
@ -62,8 +73,13 @@ public:
virtual bool
IsSafeDirectory(Directory* aDir) const;
/*
* Get the real path (absolute DOM path) of the DOM file in the file system.
* If succeeded, returns true. Otherwise, returns false and set aRealPath to
* empty string.
*/
bool
GetRealPath(BlobImpl* aFile, nsIFile** aPath) const;
GetRealPath(BlobImpl* aFile, nsAString& aRealPath) const;
/*
* Get the permission name required to access this file system.
@ -87,12 +103,21 @@ public:
protected:
virtual ~FileSystemBase();
bool
LocalPathToRealPath(const nsAString& aLocalPath, nsAString& aRealPath) const;
// The local path of the root (i.e. the OS path, with OS path separators, of
// the OS directory that acts as the root of this OSFileSystem).
// Only available in the parent process.
// In the child process, we don't use it and its value should be empty.
nsString mLocalRootPath;
// The same, but with path separators normalized to "/".
nsString mNormalizedLocalRootPath;
// The string representation of the file system.
nsString mString;
bool mShutdown;
// The permission name required to access the file system.

View File

@ -15,8 +15,7 @@
namespace mozilla {
namespace dom {
NS_IMPL_ISUPPORTS(FileSystemPermissionRequest, nsIRunnable,
nsIContentPermissionRequest)
NS_IMPL_ISUPPORTS(FileSystemPermissionRequest, nsIRunnable, nsIContentPermissionRequest)
// static
void
@ -29,7 +28,8 @@ FileSystemPermissionRequest::RequestForTask(FileSystemTaskBase* aTask)
NS_DispatchToCurrentThread(request);
}
FileSystemPermissionRequest::FileSystemPermissionRequest(FileSystemTaskBase* aTask)
FileSystemPermissionRequest::FileSystemPermissionRequest(
FileSystemTaskBase* aTask)
: mTask(aTask)
{
MOZ_ASSERT(mTask, "aTask should not be null!");
@ -44,8 +44,8 @@ FileSystemPermissionRequest::FileSystemPermissionRequest(FileSystemTaskBase* aTa
mPermissionType = filesystem->GetPermission();
mWindow = do_QueryInterface(filesystem->GetParentObject());
if (NS_WARN_IF(!mWindow)) {
mWindow = filesystem->GetWindow();
if (!mWindow) {
return;
}

View File

@ -28,11 +28,8 @@ FileSystemRequestParent::~FileSystemRequestParent()
#define FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(name) \
case FileSystemParams::TFileSystem##name##Params: { \
const FileSystem##name##Params& p = aParams; \
mFileSystem = FileSystemBase::DeserializeDOMPath(p.filesystem()); \
task = name##Task::Create(mFileSystem, p, this, rv); \
if (NS_WARN_IF(rv.Failed())) { \
return false; \
} \
mFileSystem = FileSystemBase::FromString(p.filesystem()); \
task = new name##Task(mFileSystem, p, this); \
break; \
}
@ -42,8 +39,6 @@ FileSystemRequestParent::Dispatch(ContentParent* aParent,
{
MOZ_ASSERT(aParent, "aParent should not be null.");
RefPtr<FileSystemTaskBase> task;
ErrorResult rv;
switch (aParams.type()) {
FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(CreateDirectory)

View File

@ -23,7 +23,7 @@ namespace dom {
namespace {
class FileSystemReleaseRunnable final : public nsRunnable
class FileSystemReleaseRunnable : public nsRunnable
{
public:
explicit FileSystemReleaseRunnable(RefPtr<FileSystemBase>& aDoomed)
@ -106,22 +106,12 @@ FileSystemTaskBase::Start()
return;
}
nsAutoString serialization;
mFileSystem->SerializeDOMPath(serialization);
ErrorResult rv;
FileSystemParams params = GetRequestParams(serialization, rv);
if (NS_WARN_IF(rv.Failed())) {
return;
}
// Retain a reference so the task object isn't deleted without IPDL's
// knowledge. The reference will be released by
// mozilla::dom::ContentChild::DeallocPFileSystemRequestChild.
NS_ADDREF_THIS();
ContentChild::GetSingleton()->SendPFileSystemRequestConstructor(this,
params);
GetRequestParams(mFileSystem->ToString()));
}
NS_IMETHODIMP
@ -164,17 +154,11 @@ FileSystemTaskBase::GetRequestResult() const
MOZ_ASSERT(XRE_IsParentProcess(),
"Only call from parent process!");
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
if (!HasError()) {
ErrorResult rv;
FileSystemResponseValue value = GetSuccessRequestResult(rv);
if (NS_WARN_IF(rv.Failed())) {
return FileSystemErrorResponse(rv.StealNSResult());
}
return value;
if (HasError()) {
return FileSystemErrorResponse(mErrorValue);
} else {
return GetSuccessRequestResult();
}
return FileSystemErrorResponse(mErrorValue);
}
void
@ -187,9 +171,7 @@ FileSystemTaskBase::SetRequestResult(const FileSystemResponseValue& aValue)
FileSystemErrorResponse r = aValue;
mErrorValue = r.error();
} else {
ErrorResult rv;
SetSuccessRequestResult(aValue, rv);
mErrorValue = rv.StealNSResult();
SetSuccessRequestResult(aValue);
}
}

View File

@ -176,8 +176,7 @@ protected:
* @param filesystem The string representation of the file system.
*/
virtual FileSystemParams
GetRequestParams(const nsString& aSerializedDOMPath,
ErrorResult& aRv) const = 0;
GetRequestParams(const nsString& aFileSystem) const = 0;
/*
* Wrap the task success result to FileSystemResponseValue for sending it
@ -186,7 +185,7 @@ protected:
* send the task success result back to the child process.
*/
virtual FileSystemResponseValue
GetSuccessRequestResult(ErrorResult& aRv) const = 0;
GetSuccessRequestResult() const = 0;
/*
* Unwrap the IPC message to get the task success result.
@ -195,8 +194,7 @@ protected:
* success result.
*/
virtual void
SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv) = 0;
SetSuccessRequestResult(const FileSystemResponseValue& aValue) = 0;
bool
HasError() const { return mErrorValue != NS_OK; }

View File

@ -6,28 +6,59 @@
#include "mozilla/dom/FileSystemUtils.h"
#include "nsXULAppAPI.h"
namespace mozilla {
namespace dom {
/* static */ bool
FileSystemUtils::IsDescendantPath(nsIFile* aFile,
nsIFile* aDescendantFile)
// static
void
FileSystemUtils::LocalPathToNormalizedPath(const nsAString& aLocal,
nsAString& aNorm)
{
nsAutoString path;
nsresult rv = aFile->GetPath(path);
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
nsString result;
result = aLocal;
#if defined(XP_WIN)
char16_t* cur = result.BeginWriting();
char16_t* end = result.EndWriting();
for (; cur < end; ++cur) {
if (char16_t('\\') == *cur)
*cur = char16_t('/');
}
#endif
aNorm = result;
}
nsAutoString descendantPath;
rv = aDescendantFile->GetPath(descendantPath);
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
// static
void
FileSystemUtils::NormalizedPathToLocalPath(const nsAString& aNorm,
nsAString& aLocal)
{
nsString result;
result = aNorm;
#if defined(XP_WIN)
char16_t* cur = result.BeginWriting();
char16_t* end = result.EndWriting();
for (; cur < end; ++cur) {
if (char16_t('/') == *cur)
*cur = char16_t('\\');
}
#endif
aLocal = result;
}
// static
bool
FileSystemUtils::IsDescendantPath(const nsAString& aPath,
const nsAString& aDescendantPath)
{
// The descendant path should begin with its ancestor path.
nsAutoString prefix;
prefix = aPath + NS_LITERAL_STRING(FILESYSTEM_DOM_PATH_SEPARATOR);
// Check the sub-directory path to see if it has the parent path as prefix.
if (descendantPath.Length() <= path.Length() ||
!StringBeginsWith(descendantPath, path)) {
if (aDescendantPath.Length() < prefix.Length() ||
!StringBeginsWith(aDescendantPath, prefix)) {
return false;
}

View File

@ -7,13 +7,12 @@
#ifndef mozilla_dom_FileSystemUtils_h
#define mozilla_dom_FileSystemUtils_h
class nsIFile;
#include "nsString.h"
namespace mozilla {
namespace dom {
#define FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL "/"
#define FILESYSTEM_DOM_PATH_SEPARATOR_CHAR '/'
#define FILESYSTEM_DOM_PATH_SEPARATOR "/"
/*
* This class is for error handling.
@ -23,10 +22,26 @@ class FileSystemUtils
{
public:
/*
* Return true if aDescendantPath is a descendant of aPath.
* Convert the path separator to "/".
*/
static void
LocalPathToNormalizedPath(const nsAString& aLocal, nsAString& aNorm);
/*
* Convert the normalized path separator "/" to the system dependent path
* separator, which is "/" on Mac and Linux, and "\" on Windows.
*/
static void
NormalizedPathToLocalPath(const nsAString& aNorm, nsAString& aLocal);
/*
* Return true if aDescendantPath is a descendant of aPath. Both aPath and
* aDescendantPath are absolute DOM path.
*/
static bool
IsDescendantPath(nsIFile* aPath, nsIFile* aDescendantPath);
IsDescendantPath(const nsAString& aPath, const nsAString& aDescendantPath);
static const char16_t kSeparatorChar = char16_t('/');
};
} // namespace dom

View File

@ -8,6 +8,7 @@
#include "HTMLSplitOnSpacesTokenizer.h"
#include "js/Value.h"
#include "mozilla/dom/Directory.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/FileSystemBase.h"
#include "mozilla/dom/FileSystemUtils.h"
@ -20,80 +21,33 @@
namespace mozilla {
namespace dom {
/* static */ already_AddRefed<GetDirectoryListingTask>
GetDirectoryListingTask::Create(FileSystemBase* aFileSystem,
nsIFile* aTargetPath,
Directory::DirectoryType aType,
const nsAString& aFilters,
ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
RefPtr<GetDirectoryListingTask> task =
new GetDirectoryListingTask(aFileSystem, aTargetPath, aType, aFilters);
// aTargetPath can be null. In this case SetError will be called.
nsCOMPtr<nsIGlobalObject> globalObject =
do_QueryInterface(aFileSystem->GetParentObject());
if (NS_WARN_IF(!globalObject)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
task->mPromise = Promise::Create(globalObject, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return task.forget();
}
/* static */ already_AddRefed<GetDirectoryListingTask>
GetDirectoryListingTask::Create(FileSystemBase* aFileSystem,
const FileSystemGetDirectoryListingParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
RefPtr<GetDirectoryListingTask> task =
new GetDirectoryListingTask(aFileSystem, aParam, aParent);
NS_ConvertUTF16toUTF8 path(aParam.realPath());
aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
task->mType = aParam.isRoot()
? Directory::eDOMRootDirectory : Directory::eNotDOMRootDirectory;
return task.forget();
}
GetDirectoryListingTask::GetDirectoryListingTask(FileSystemBase* aFileSystem,
nsIFile* aTargetPath,
Directory::DirectoryType aType,
const nsAString& aFilters)
const nsAString& aTargetPath,
const nsAString& aFilters,
ErrorResult& aRv)
: FileSystemTaskBase(aFileSystem)
, mTargetPath(aTargetPath)
, mTargetRealPath(aTargetPath)
, mFilters(aFilters)
, mType(aType)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
nsCOMPtr<nsIGlobalObject> globalObject =
do_QueryInterface(aFileSystem->GetWindow());
if (!globalObject) {
return;
}
mPromise = Promise::Create(globalObject, aRv);
}
GetDirectoryListingTask::GetDirectoryListingTask(FileSystemBase* aFileSystem,
const FileSystemGetDirectoryListingParams& aParam,
FileSystemRequestParent* aParent)
: FileSystemTaskBase(aFileSystem, aParam, aParent)
, mTargetRealPath(aParam.realPath())
, mFilters(aParam.filters())
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
MOZ_ASSERT(XRE_IsParentProcess(),
"Only call from parent process!");
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
}
@ -112,79 +66,43 @@ GetDirectoryListingTask::GetPromise()
}
FileSystemParams
GetDirectoryListingTask::GetRequestParams(const nsString& aSerializedDOMPath,
ErrorResult& aRv) const
GetDirectoryListingTask::GetRequestParams(const nsString& aFileSystem) const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
nsAutoString path;
aRv = mTargetPath->GetPath(path);
if (NS_WARN_IF(aRv.Failed())) {
return FileSystemGetDirectoryListingParams();
}
return FileSystemGetDirectoryListingParams(aSerializedDOMPath, path,
mType == Directory::eDOMRootDirectory,
return FileSystemGetDirectoryListingParams(aFileSystem, mTargetRealPath,
mFilters);
}
FileSystemResponseValue
GetDirectoryListingTask::GetSuccessRequestResult(ErrorResult& aRv) const
GetDirectoryListingTask::GetSuccessRequestResult() const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
InfallibleTArray<PBlobParent*> blobs;
nsTArray<FileSystemDirectoryListingResponseData> inputs;
for (unsigned i = 0; i < mTargetData.Length(); i++) {
if (mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eBlobImpl) {
BlobParent* blobParent = GetBlobParent(mTargetData[i].mBlobImpl);
if (!blobParent) {
continue;
}
FileSystemDirectoryListingResponseBlob blobData;
blobData.blobParent() = blobParent;
inputs.AppendElement(blobData);
} else {
MOZ_ASSERT(mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eDirectoryPath);
FileSystemDirectoryListingResponseDirectory directoryData;
directoryData.directoryRealPath() = mTargetData[i].mDirectoryPath;
inputs.AppendElement(directoryData);
for (unsigned i = 0; i < mTargetBlobImpls.Length(); i++) {
BlobParent* blobParent = GetBlobParent(mTargetBlobImpls[i]);
if (blobParent) {
blobs.AppendElement(blobParent);
}
}
FileSystemDirectoryListingResponse response;
response.data().SwapElements(inputs);
response.blobsParent().SwapElements(blobs);
return response;
}
void
GetDirectoryListingTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv)
GetDirectoryListingTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aValue.type() ==
FileSystemResponseValue::TFileSystemDirectoryListingResponse);
FileSystemDirectoryListingResponse r = aValue;
for (uint32_t i = 0; i < r.data().Length(); ++i) {
const FileSystemDirectoryListingResponseData& data = r.data()[i];
nsTArray<PBlobChild*>& blobs = r.blobsChild();
if (data.type() == FileSystemDirectoryListingResponseData::TFileSystemDirectoryListingResponseBlob) {
PBlobChild* blob = data.get_FileSystemDirectoryListingResponseBlob().blobChild();
Directory::BlobImplOrDirectoryPath* element = mTargetData.AppendElement();
element->mType = Directory::BlobImplOrDirectoryPath::eBlobImpl;
element->mBlobImpl = static_cast<BlobChild*>(blob)->GetBlobImpl();
} else {
MOZ_ASSERT(data.type() == FileSystemDirectoryListingResponseData::TFileSystemDirectoryListingResponseDirectory);
Directory::BlobImplOrDirectoryPath* element = mTargetData.AppendElement();
element->mType = Directory::BlobImplOrDirectoryPath::eDirectoryPath;
element->mDirectoryPath = data.get_FileSystemDirectoryListingResponseDirectory().directoryRealPath();
}
for (unsigned i = 0; i < blobs.Length(); i++) {
mTargetBlobImpls.AppendElement(static_cast<BlobChild*>(blobs[i])->GetBlobImpl());
}
}
@ -199,19 +117,27 @@ GetDirectoryListingTask::Work()
return NS_ERROR_FAILURE;
}
// Whether we want to get the root directory.
bool getRoot = mTargetRealPath.IsEmpty();
nsCOMPtr<nsIFile> dir = mFileSystem->GetLocalFile(mTargetRealPath);
if (!dir) {
return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
}
bool exists;
nsresult rv = mTargetPath->Exists(&exists);
nsresult rv = dir->Exists(&exists);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!exists) {
if (mType == Directory::eNotDOMRootDirectory) {
if (!getRoot) {
return NS_ERROR_DOM_FILE_NOT_FOUND_ERR;
}
// If the root directory doesn't exit, create it.
rv = mTargetPath->Create(nsIFile::DIRECTORY_TYPE, 0777);
rv = dir->Create(nsIFile::DIRECTORY_TYPE, 0777);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -219,7 +145,7 @@ GetDirectoryListingTask::Work()
// Get isDirectory.
bool isDir;
rv = mTargetPath->IsDirectory(&isDir);
rv = dir->IsDirectory(&isDir);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -229,7 +155,7 @@ GetDirectoryListingTask::Work()
}
nsCOMPtr<nsISimpleEnumerator> entries;
rv = mTargetPath->GetDirectoryEntries(getter_AddRefs(entries));
rv = dir->GetDirectoryEntries(getter_AddRefs(entries));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -260,7 +186,7 @@ GetDirectoryListingTask::Work()
nsCOMPtr<nsIFile> currFile = do_QueryInterface(supp);
MOZ_ASSERT(currFile);
bool isLink, isSpecial, isFile;
if (NS_WARN_IF(NS_FAILED(currFile->IsSymlink(&isLink)) ||
NS_FAILED(currFile->IsSpecial(&isSpecial))) ||
@ -287,22 +213,9 @@ GetDirectoryListingTask::Work()
}
}
if (isDir) {
nsAutoString path;
if (NS_WARN_IF(NS_FAILED(currFile->GetPath(path)))) {
continue;
}
Directory::BlobImplOrDirectoryPath* element = mTargetData.AppendElement();
element->mType = Directory::BlobImplOrDirectoryPath::eDirectoryPath;
element->mDirectoryPath = path;
} else {
BlobImplFile* impl = new BlobImplFile(currFile);
Directory::BlobImplOrDirectoryPath* element = mTargetData.AppendElement();
element->mType = Directory::BlobImplOrDirectoryPath::eBlobImpl;
element->mBlobImpl = impl;
}
BlobImplFile* impl = new BlobImplFile(currFile);
impl->LookupAndCacheIsDirectory();
mTargetBlobImpls.AppendElement(impl);
}
return NS_OK;
}
@ -322,55 +235,37 @@ GetDirectoryListingTask::HandlerCallback()
return;
}
size_t count = mTargetData.Length();
size_t count = mTargetBlobImpls.Length();
Sequence<OwningFileOrDirectory> listing;
if (!listing.SetLength(count, mozilla::fallible_t())) {
mPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
mPromise->MaybeReject(NS_ERROR_FAILURE);
mPromise = nullptr;
return;
}
for (unsigned i = 0; i < count; i++) {
if (mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eDirectoryPath) {
nsCOMPtr<nsIFile> directoryPath;
NS_ConvertUTF16toUTF8 path(mTargetData[i].mDirectoryPath);
nsresult rv = NS_NewNativeLocalFile(path, true,
getter_AddRefs(directoryPath));
if (NS_WARN_IF(NS_FAILED(rv))) {
mPromise->MaybeReject(rv);
mPromise = nullptr;
return;
}
if (mTargetBlobImpls[i]->IsDirectory()) {
nsAutoString name;
mTargetBlobImpls[i]->GetName(name);
nsAutoString path(mTargetRealPath);
path.AppendLiteral(FILESYSTEM_DOM_PATH_SEPARATOR);
path.Append(name);
#ifdef DEBUG
nsCOMPtr<nsIFile> rootPath;
rv = NS_NewLocalFile(mFileSystem->GetLocalRootPath(), false,
getter_AddRefs(rootPath));
if (NS_WARN_IF(NS_FAILED(rv))) {
mPromise->MaybeReject(rv);
mPromise = nullptr;
return;
if (XRE_IsParentProcess()) {
nsCOMPtr<nsIFile> file = mFileSystem->GetLocalFile(path);
bool exist;
file->Exists(&exist);
MOZ_ASSERT(exist);
}
MOZ_ASSERT(FileSystemUtils::IsDescendantPath(rootPath, directoryPath));
#endif
RefPtr<Directory> directory =
Directory::Create(mFileSystem->GetParentObject(),
directoryPath,
Directory::eNotDOMRootDirectory,
mFileSystem);
MOZ_ASSERT(directory);
RefPtr<Directory> directory = new Directory(mFileSystem, path);
// Propogate mFilter onto sub-Directory object:
directory->SetContentFilters(mFilters);
listing[i].SetAsDirectory() = directory;
} else {
MOZ_ASSERT(mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eBlobImpl);
listing[i].SetAsFile() =
File::Create(mFileSystem->GetParentObject(), mTargetData[i].mBlobImpl);
listing[i].SetAsFile() = File::Create(mFileSystem->GetWindow(), mTargetBlobImpls[i]);
}
}

View File

@ -7,7 +7,6 @@
#ifndef mozilla_dom_GetDirectoryListing_h
#define mozilla_dom_GetDirectoryListing_h
#include "mozilla/dom/Directory.h"
#include "mozilla/dom/FileSystemTaskBase.h"
#include "mozilla/ErrorResult.h"
#include "nsAutoPtr.h"
@ -17,21 +16,18 @@ namespace dom {
class BlobImpl;
class GetDirectoryListingTask final : public FileSystemTaskBase
class GetDirectoryListingTask final
: public FileSystemTaskBase
{
public:
static already_AddRefed<GetDirectoryListingTask>
Create(FileSystemBase* aFileSystem,
nsIFile* aTargetPath,
Directory::DirectoryType aType,
const nsAString& aFilters,
ErrorResult& aRv);
static already_AddRefed<GetDirectoryListingTask>
Create(FileSystemBase* aFileSystem,
const FileSystemGetDirectoryListingParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv);
// If aDirectoryOnly is set, we should ensure that the target is a directory.
GetDirectoryListingTask(FileSystemBase* aFileSystem,
const nsAString& aTargetPath,
const nsAString& aFilters,
ErrorResult& aRv);
GetDirectoryListingTask(FileSystemBase* aFileSystem,
const FileSystemGetDirectoryListingParams& aParam,
FileSystemRequestParent* aParent);
virtual
~GetDirectoryListingTask();
@ -41,28 +37,15 @@ public:
virtual void
GetPermissionAccessType(nsCString& aAccess) const override;
private:
// If aDirectoryOnly is set, we should ensure that the target is a directory.
GetDirectoryListingTask(FileSystemBase* aFileSystem,
nsIFile* aTargetPath,
Directory::DirectoryType aType,
const nsAString& aFilters);
GetDirectoryListingTask(FileSystemBase* aFileSystem,
const FileSystemGetDirectoryListingParams& aParam,
FileSystemRequestParent* aParent);
protected:
virtual FileSystemParams
GetRequestParams(const nsString& aSerializedDOMPath,
ErrorResult& aRv) const override;
GetRequestParams(const nsString& aFileSystem) const override;
virtual FileSystemResponseValue
GetSuccessRequestResult(ErrorResult& aRv) const override;
GetSuccessRequestResult() const override;
virtual void
SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv) override;
SetSuccessRequestResult(const FileSystemResponseValue& aValue) override;
virtual nsresult
Work() override;
@ -70,14 +53,14 @@ private:
virtual void
HandlerCallback() override;
private:
RefPtr<Promise> mPromise;
nsCOMPtr<nsIFile> mTargetPath;
nsString mTargetRealPath;
nsString mFilters;
Directory::DirectoryType mType;
// We cannot store File or Directory objects bacause this object is created
// on a different thread and File and Directory are not thread-safe.
nsTArray<Directory::BlobImplOrDirectoryPath> mTargetData;
nsTArray<RefPtr<BlobImpl>> mTargetBlobImpls;
};
} // namespace dom

View File

@ -7,6 +7,7 @@
#include "GetFileOrDirectoryTask.h"
#include "js/Value.h"
#include "mozilla/dom/Directory.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/FileSystemBase.h"
#include "mozilla/dom/FileSystemUtils.h"
@ -19,82 +20,37 @@
namespace mozilla {
namespace dom {
/* static */ already_AddRefed<GetFileOrDirectoryTask>
GetFileOrDirectoryTask::Create(FileSystemBase* aFileSystem,
nsIFile* aTargetPath,
Directory::DirectoryType aType,
bool aDirectoryOnly,
ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
RefPtr<GetFileOrDirectoryTask> task =
new GetFileOrDirectoryTask(aFileSystem, aTargetPath, aType, aDirectoryOnly);
// aTargetPath can be null. In this case SetError will be called.
nsCOMPtr<nsIGlobalObject> globalObject =
do_QueryInterface(aFileSystem->GetParentObject());
if (NS_WARN_IF(!globalObject)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
task->mPromise = Promise::Create(globalObject, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return task.forget();
}
/* static */ already_AddRefed<GetFileOrDirectoryTask>
GetFileOrDirectoryTask::Create(FileSystemBase* aFileSystem,
const FileSystemGetFileOrDirectoryParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
RefPtr<GetFileOrDirectoryTask> task =
new GetFileOrDirectoryTask(aFileSystem, aParam, aParent);
NS_ConvertUTF16toUTF8 path(aParam.realPath());
aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
task->mType = aParam.isRoot()
? Directory::eDOMRootDirectory : Directory::eNotDOMRootDirectory;
return task.forget();
}
GetFileOrDirectoryTask::GetFileOrDirectoryTask(FileSystemBase* aFileSystem,
nsIFile* aTargetPath,
Directory::DirectoryType aType,
bool aDirectoryOnly)
GetFileOrDirectoryTask::GetFileOrDirectoryTask(
FileSystemBase* aFileSystem,
const nsAString& aTargetPath,
bool aDirectoryOnly,
ErrorResult& aRv)
: FileSystemTaskBase(aFileSystem)
, mTargetPath(aTargetPath)
, mTargetRealPath(aTargetPath)
, mIsDirectory(aDirectoryOnly)
, mType(aType)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
nsCOMPtr<nsIGlobalObject> globalObject =
do_QueryInterface(aFileSystem->GetWindow());
if (!globalObject) {
return;
}
mPromise = Promise::Create(globalObject, aRv);
}
GetFileOrDirectoryTask::GetFileOrDirectoryTask(FileSystemBase* aFileSystem,
const FileSystemGetFileOrDirectoryParams& aParam,
FileSystemRequestParent* aParent)
GetFileOrDirectoryTask::GetFileOrDirectoryTask(
FileSystemBase* aFileSystem,
const FileSystemGetFileOrDirectoryParams& aParam,
FileSystemRequestParent* aParent)
: FileSystemTaskBase(aFileSystem, aParam, aParent)
, mIsDirectory(false)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
MOZ_ASSERT(XRE_IsParentProcess(),
"Only call from parent process!");
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
mTargetRealPath = aParam.realPath();
}
GetFileOrDirectoryTask::~GetFileOrDirectoryTask()
@ -111,33 +67,18 @@ GetFileOrDirectoryTask::GetPromise()
}
FileSystemParams
GetFileOrDirectoryTask::GetRequestParams(const nsString& aSerializedDOMPath,
ErrorResult& aRv) const
GetFileOrDirectoryTask::GetRequestParams(const nsString& aFileSystem) const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
nsAutoString path;
aRv = mTargetPath->GetPath(path);
if (NS_WARN_IF(aRv.Failed())) {
return FileSystemGetFileOrDirectoryParams();
}
return FileSystemGetFileOrDirectoryParams(aSerializedDOMPath, path,
mType == Directory::eDOMRootDirectory);
return FileSystemGetFileOrDirectoryParams(aFileSystem, mTargetRealPath);
}
FileSystemResponseValue
GetFileOrDirectoryTask::GetSuccessRequestResult(ErrorResult& aRv) const
GetFileOrDirectoryTask::GetSuccessRequestResult() const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
if (mIsDirectory) {
nsAutoString path;
aRv = mTargetPath->GetPath(path);
if (NS_WARN_IF(aRv.Failed())) {
return FileSystemDirectoryResponse();
}
return FileSystemDirectoryResponse(path);
return FileSystemDirectoryResponse(mTargetRealPath);
}
BlobParent* actor = GetBlobParent(mTargetBlobImpl);
@ -150,8 +91,7 @@ GetFileOrDirectoryTask::GetSuccessRequestResult(ErrorResult& aRv) const
}
void
GetFileOrDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv)
GetFileOrDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
switch (aValue.type()) {
@ -164,13 +104,7 @@ GetFileOrDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& a
}
case FileSystemResponseValue::TFileSystemDirectoryResponse: {
FileSystemDirectoryResponse r = aValue;
NS_ConvertUTF16toUTF8 path(r.realPath());
aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath));
if (NS_WARN_IF(aRv.Failed())) {
return;
}
mTargetRealPath = r.realPath();
mIsDirectory = true;
break;
}
@ -193,26 +127,33 @@ GetFileOrDirectoryTask::Work()
}
// Whether we want to get the root directory.
bool getRoot = mTargetRealPath.IsEmpty();
nsCOMPtr<nsIFile> file = mFileSystem->GetLocalFile(mTargetRealPath);
if (!file) {
return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
}
bool exists;
nsresult rv = mTargetPath->Exists(&exists);
nsresult rv = file->Exists(&exists);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!exists) {
if (mType == Directory::eNotDOMRootDirectory) {
if (!getRoot) {
return NS_ERROR_DOM_FILE_NOT_FOUND_ERR;
}
// If the root directory doesn't exit, create it.
rv = mTargetPath->Create(nsIFile::DIRECTORY_TYPE, 0777);
rv = file->Create(nsIFile::DIRECTORY_TYPE, 0777);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
// Get isDirectory.
rv = mTargetPath->IsDirectory(&mIsDirectory);
rv = file->IsDirectory(&mIsDirectory);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -222,13 +163,13 @@ GetFileOrDirectoryTask::Work()
}
// Check if the root is a directory.
if (mType == Directory::eDOMRootDirectory) {
if (getRoot) {
return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
}
bool isFile;
// Get isFile
rv = mTargetPath->IsFile(&isFile);
rv = file->IsFile(&isFile);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -238,11 +179,11 @@ GetFileOrDirectoryTask::Work()
return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
}
if (!mFileSystem->IsSafeFile(mTargetPath)) {
if (!mFileSystem->IsSafeFile(file)) {
return NS_ERROR_DOM_SECURITY_ERR;
}
mTargetBlobImpl = new BlobImplFile(mTargetPath);
mTargetBlobImpl = new BlobImplFile(file);
return NS_OK;
}
@ -263,19 +204,13 @@ GetFileOrDirectoryTask::HandlerCallback()
}
if (mIsDirectory) {
RefPtr<Directory> dir = Directory::Create(mFileSystem->GetParentObject(),
mTargetPath,
mType,
mFileSystem);
MOZ_ASSERT(dir);
RefPtr<Directory> dir = new Directory(mFileSystem, mTargetRealPath);
mPromise->MaybeResolve(dir);
mPromise = nullptr;
return;
}
RefPtr<Blob> blob = Blob::Create(mFileSystem->GetParentObject(),
mTargetBlobImpl);
RefPtr<Blob> blob = Blob::Create(mFileSystem->GetWindow(), mTargetBlobImpl);
mPromise->MaybeResolve(blob);
mPromise = nullptr;
}

View File

@ -7,7 +7,6 @@
#ifndef mozilla_dom_GetFileOrDirectory_h
#define mozilla_dom_GetFileOrDirectory_h
#include "mozilla/dom/Directory.h"
#include "mozilla/dom/FileSystemTaskBase.h"
#include "nsAutoPtr.h"
#include "mozilla/ErrorResult.h"
@ -17,21 +16,18 @@ namespace dom {
class BlobImpl;
class GetFileOrDirectoryTask final : public FileSystemTaskBase
class GetFileOrDirectoryTask final
: public FileSystemTaskBase
{
public:
static already_AddRefed<GetFileOrDirectoryTask>
Create(FileSystemBase* aFileSystem,
nsIFile* aTargetPath,
Directory::DirectoryType aType,
bool aDirectoryOnly,
ErrorResult& aRv);
static already_AddRefed<GetFileOrDirectoryTask>
Create(FileSystemBase* aFileSystem,
const FileSystemGetFileOrDirectoryParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv);
// If aDirectoryOnly is set, we should ensure that the target is a directory.
GetFileOrDirectoryTask(FileSystemBase* aFileSystem,
const nsAString& aTargetPath,
bool aDirectoryOnly,
ErrorResult& aRv);
GetFileOrDirectoryTask(FileSystemBase* aFileSystem,
const FileSystemGetFileOrDirectoryParams& aParam,
FileSystemRequestParent* aParent);
virtual
~GetFileOrDirectoryTask();
@ -43,15 +39,13 @@ public:
GetPermissionAccessType(nsCString& aAccess) const override;
protected:
virtual FileSystemParams
GetRequestParams(const nsString& aSerializedDOMPath,
ErrorResult& aRv) const override;
GetRequestParams(const nsString& aFileSystem) const override;
virtual FileSystemResponseValue
GetSuccessRequestResult(ErrorResult& aRv) const override;
GetSuccessRequestResult() const override;
virtual void
SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv) override;
SetSuccessRequestResult(const FileSystemResponseValue& aValue) override;
virtual nsresult
Work() override;
@ -60,22 +54,10 @@ protected:
HandlerCallback() override;
private:
// If aDirectoryOnly is set, we should ensure that the target is a directory.
GetFileOrDirectoryTask(FileSystemBase* aFileSystem,
nsIFile* aTargetPath,
Directory::DirectoryType aType,
bool aDirectoryOnly);
GetFileOrDirectoryTask(FileSystemBase* aFileSystem,
const FileSystemGetFileOrDirectoryParams& aParam,
FileSystemRequestParent* aParent);
RefPtr<Promise> mPromise;
nsCOMPtr<nsIFile> mTargetPath;
nsString mTargetRealPath;
// Whether we get a directory.
bool mIsDirectory;
Directory::DirectoryType mType;
// This cannot be a File bacause this object is created on a different
// thread and File is not thread-safe. Let's use the BlobImpl instead.

View File

@ -9,10 +9,10 @@
#include "mozilla/dom/Directory.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/FileSystemUtils.h"
#include "nsIGlobalObject.h"
#include "nsCOMPtr.h"
#include "nsDebug.h"
#include "nsIFile.h"
#include "nsPIDOMWindow.h"
namespace mozilla {
namespace dom {
@ -20,41 +20,40 @@ namespace dom {
OSFileSystem::OSFileSystem(const nsAString& aRootDir)
{
mLocalRootPath = aRootDir;
FileSystemUtils::LocalPathToNormalizedPath(mLocalRootPath,
mNormalizedLocalRootPath);
// Non-mobile devices don't have the concept of separate permissions to
// access different parts of devices storage like Pictures, or Videos, etc.
mRequiresPermissionChecks = false;
mString = mLocalRootPath;
#ifdef DEBUG
mPermission.AssignLiteral("never-used");
#endif
}
void
OSFileSystem::Init(nsISupports* aParent)
OSFileSystem::Init(nsPIDOMWindowInner* aWindow)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(!mParent, "No duple Init() calls");
MOZ_ASSERT(aParent);
mParent = aParent;
#ifdef DEBUG
nsCOMPtr<nsIGlobalObject> obj = do_QueryInterface(aParent);
MOZ_ASSERT(obj);
#endif
MOZ_ASSERT(!mWindow, "No duple Init() calls");
MOZ_ASSERT(aWindow);
mWindow = aWindow;
}
nsISupports*
OSFileSystem::GetParentObject() const
nsPIDOMWindowInner*
OSFileSystem::GetWindow() const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
return mParent;
return mWindow;
}
void
OSFileSystem::GetRootName(nsAString& aRetval) const
{
aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
return aRetval.AssignLiteral("/");
}
bool
@ -80,20 +79,14 @@ OSFileSystem::IsSafeDirectory(Directory* aDir) const
void
OSFileSystem::Unlink()
{
mParent = nullptr;
mWindow = nullptr;
}
void
OSFileSystem::Traverse(nsCycleCollectionTraversalCallback &cb)
{
OSFileSystem* tmp = this;
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent);
}
void
OSFileSystem::SerializeDOMPath(nsAString& aOutput) const
{
aOutput = mLocalRootPath;
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow);
}
} // namespace dom

View File

@ -18,12 +18,12 @@ public:
explicit OSFileSystem(const nsAString& aRootDir);
void
Init(nsISupports* aParent);
Init(nsPIDOMWindowInner* aWindow);
// Overrides FileSystemBase
virtual nsISupports*
GetParentObject() const override;
virtual nsPIDOMWindowInner*
GetWindow() const override;
virtual void
GetRootName(nsAString& aRetval) const override;
@ -34,9 +34,6 @@ public:
virtual bool
IsSafeDirectory(Directory* aDir) const override;
virtual void
SerializeDOMPath(nsAString& aOutput) const override;
// CC methods
virtual void Unlink() override;
virtual void Traverse(nsCycleCollectionTraversalCallback &cb) override;
@ -44,7 +41,7 @@ public:
private:
virtual ~OSFileSystem() {}
nsCOMPtr<nsISupports> mParent;
nsCOMPtr<nsPIDOMWindowInner> mWindow;
};
} // namespace dom

View File

@ -20,26 +20,9 @@ struct FileSystemDirectoryResponse
nsString realPath;
};
struct FileSystemDirectoryListingResponseBlob
{
PBlob blob;
};
struct FileSystemDirectoryListingResponseDirectory
{
// This is the full real path for the directory that we are sending via IPC.
nsString directoryRealPath;
};
union FileSystemDirectoryListingResponseData
{
FileSystemDirectoryListingResponseBlob;
FileSystemDirectoryListingResponseDirectory;
};
struct FileSystemDirectoryListingResponse
{
FileSystemDirectoryListingResponseData[] data;
PBlob[] blobs;
};
struct FileSystemErrorResponse

View File

@ -18,94 +18,27 @@
namespace mozilla {
namespace dom {
/* static */ already_AddRefed<RemoveTask>
RemoveTask::Create(FileSystemBase* aFileSystem,
nsIFile* aDirPath,
BlobImpl* aTargetBlob,
nsIFile* aTargetPath,
bool aRecursive,
ErrorResult& aRv)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
MOZ_ASSERT(aDirPath);
RefPtr<RemoveTask> task =
new RemoveTask(aFileSystem, aDirPath, aTargetBlob, aTargetPath, aRecursive);
// aTargetPath can be null. In this case SetError will be called.
nsCOMPtr<nsIGlobalObject> globalObject =
do_QueryInterface(aFileSystem->GetParentObject());
if (NS_WARN_IF(!globalObject)) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
task->mPromise = Promise::Create(globalObject, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return task.forget();
}
/* static */ already_AddRefed<RemoveTask>
RemoveTask::Create(FileSystemBase* aFileSystem,
const FileSystemRemoveParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
RefPtr<RemoveTask> task =
new RemoveTask(aFileSystem, aParam, aParent);
NS_ConvertUTF16toUTF8 directoryPath(aParam.directory());
aRv = NS_NewNativeLocalFile(directoryPath, true,
getter_AddRefs(task->mDirPath));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
task->mRecursive = aParam.recursive();
const FileSystemPathOrFileValue& target = aParam.target();
if (target.type() == FileSystemPathOrFileValue::TnsString) {
NS_ConvertUTF16toUTF8 path(target);
aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(task->mTargetPath));
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return task.forget();
}
BlobParent* bp = static_cast<BlobParent*>(static_cast<PBlobParent*>(target));
task->mTargetBlobImpl = bp->GetBlobImpl();
MOZ_ASSERT(task->mTargetBlobImpl);
return task.forget();
}
RemoveTask::RemoveTask(FileSystemBase* aFileSystem,
nsIFile* aDirPath,
const nsAString& aDirPath,
BlobImpl* aTargetBlob,
nsIFile* aTargetPath,
bool aRecursive)
const nsAString& aTargetPath,
bool aRecursive,
ErrorResult& aRv)
: FileSystemTaskBase(aFileSystem)
, mDirPath(aDirPath)
, mDirRealPath(aDirPath)
, mTargetBlobImpl(aTargetBlob)
, mTargetPath(aTargetPath)
, mTargetRealPath(aTargetPath)
, mRecursive(aRecursive)
, mReturnValue(false)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
MOZ_ASSERT(aDirPath);
nsCOMPtr<nsIGlobalObject> globalObject =
do_QueryInterface(aFileSystem->GetWindow());
if (!globalObject) {
return;
}
mPromise = Promise::Create(globalObject, aRv);
}
RemoveTask::RemoveTask(FileSystemBase* aFileSystem,
@ -115,9 +48,25 @@ RemoveTask::RemoveTask(FileSystemBase* aFileSystem,
, mRecursive(false)
, mReturnValue(false)
{
MOZ_ASSERT(XRE_IsParentProcess(), "Only call from parent process!");
MOZ_ASSERT(XRE_IsParentProcess(),
"Only call from parent process!");
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
MOZ_ASSERT(aFileSystem);
mDirRealPath = aParam.directory();
mRecursive = aParam.recursive();
const FileSystemPathOrFileValue& target = aParam.target();
if (target.type() == FileSystemPathOrFileValue::TnsString) {
mTargetRealPath = target;
return;
}
BlobParent* bp = static_cast<BlobParent*>(static_cast<PBlobParent*>(target));
mTargetBlobImpl = bp->GetBlobImpl();
MOZ_ASSERT(mTargetBlobImpl);
}
RemoveTask::~RemoveTask()
@ -134,49 +83,36 @@ RemoveTask::GetPromise()
}
FileSystemParams
RemoveTask::GetRequestParams(const nsString& aSerializedDOMPath,
ErrorResult& aRv) const
RemoveTask::GetRequestParams(const nsString& aFileSystem) const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
FileSystemRemoveParams param;
param.filesystem() = aSerializedDOMPath;
aRv = mDirPath->GetPath(param.directory());
if (NS_WARN_IF(aRv.Failed())) {
return param;
}
param.filesystem() = aFileSystem;
param.directory() = mDirRealPath;
param.recursive() = mRecursive;
if (mTargetBlobImpl) {
RefPtr<Blob> blob = Blob::Create(mFileSystem->GetParentObject(),
mTargetBlobImpl);
RefPtr<Blob> blob = Blob::Create(mFileSystem->GetWindow(),
mTargetBlobImpl);
BlobChild* actor
= ContentChild::GetSingleton()->GetOrCreateActorForBlob(blob);
if (actor) {
param.target() = actor;
}
} else {
nsAutoString path;
aRv = mTargetPath->GetPath(path);
if (NS_WARN_IF(aRv.Failed())) {
return param;
}
param.target() = path;
param.target() = mTargetRealPath;
}
return param;
}
FileSystemResponseValue
RemoveTask::GetSuccessRequestResult(ErrorResult& aRv) const
RemoveTask::GetSuccessRequestResult() const
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
return FileSystemBooleanResponse(mReturnValue);
}
void
RemoveTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv)
RemoveTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue)
{
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
FileSystemBooleanResponse r = aValue;
@ -194,19 +130,23 @@ RemoveTask::Work()
return NS_ERROR_FAILURE;
}
// Get the path if a File is passed as the target.
// Get the DOM path if a File is passed as the target.
if (mTargetBlobImpl) {
if (!mFileSystem->GetRealPath(mTargetBlobImpl,
getter_AddRefs(mTargetPath))) {
if (!mFileSystem->GetRealPath(mTargetBlobImpl, mTargetRealPath)) {
return NS_ERROR_DOM_SECURITY_ERR;
}
if (!FileSystemUtils::IsDescendantPath(mDirPath, mTargetPath)) {
if (!FileSystemUtils::IsDescendantPath(mDirRealPath, mTargetRealPath)) {
return NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR;
}
}
nsCOMPtr<nsIFile> file = mFileSystem->GetLocalFile(mTargetRealPath);
if (!file) {
return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
}
bool exists = false;
nsresult rv = mTargetPath->Exists(&exists);
nsresult rv = file->Exists(&exists);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -217,16 +157,16 @@ RemoveTask::Work()
}
bool isFile = false;
rv = mTargetPath->IsFile(&isFile);
rv = file->IsFile(&isFile);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (isFile && !mFileSystem->IsSafeFile(mTargetPath)) {
if (isFile && !mFileSystem->IsSafeFile(file)) {
return NS_ERROR_DOM_SECURITY_ERR;
}
rv = mTargetPath->Remove(mRecursive);
rv = file->Remove(mRecursive);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}

View File

@ -17,22 +17,19 @@ namespace dom {
class BlobImpl;
class Promise;
class RemoveTask final : public FileSystemTaskBase
class RemoveTask final
: public FileSystemTaskBase
{
public:
static already_AddRefed<RemoveTask>
Create(FileSystemBase* aFileSystem,
nsIFile* aDirPath,
BlobImpl* aTargetBlob,
nsIFile* aTargetPath,
bool aRecursive,
ErrorResult& aRv);
static already_AddRefed<RemoveTask>
Create(FileSystemBase* aFileSystem,
const FileSystemRemoveParams& aParam,
FileSystemRequestParent* aParent,
ErrorResult& aRv);
RemoveTask(FileSystemBase* aFileSystem,
const nsAString& aDirPath,
BlobImpl* aTargetBlob,
const nsAString& aTargetPath,
bool aRecursive,
ErrorResult& aRv);
RemoveTask(FileSystemBase* aFileSystem,
const FileSystemRemoveParams& aParam,
FileSystemRequestParent* aParent);
virtual
~RemoveTask();
@ -45,15 +42,13 @@ public:
protected:
virtual FileSystemParams
GetRequestParams(const nsString& aSerializedDOMPath,
ErrorResult& aRv) const override;
GetRequestParams(const nsString& aFileSystem) const override;
virtual FileSystemResponseValue
GetSuccessRequestResult(ErrorResult& aRv) const override;
GetSuccessRequestResult() const override;
virtual void
SetSuccessRequestResult(const FileSystemResponseValue& aValue,
ErrorResult& aRv) override;
SetSuccessRequestResult(const FileSystemResponseValue& aValue) override;
virtual nsresult
Work() override;
@ -62,23 +57,12 @@ protected:
HandlerCallback() override;
private:
RemoveTask(FileSystemBase* aFileSystem,
nsIFile* aDirPath,
BlobImpl* aTargetBlob,
nsIFile* aTargetPath,
bool aRecursive);
RemoveTask(FileSystemBase* aFileSystem,
const FileSystemRemoveParams& aParam,
FileSystemRequestParent* aParent);
RefPtr<Promise> mPromise;
nsCOMPtr<nsIFile> mDirPath;
nsString mDirRealPath;
// This cannot be a File because this object will be used on a different
// thread and File is not thread-safe. Let's use the BlobImpl instead.
RefPtr<BlobImpl> mTargetBlobImpl;
nsCOMPtr<nsIFile> mTargetPath;
nsString mTargetRealPath;
bool mRecursive;
bool mReturnValue;
};

View File

@ -4,8 +4,6 @@
# 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/.
TEST_DIRS += ['tests']
EXPORTS.mozilla.dom += [
'DeviceStorageFileSystem.h',
'Directory.h',

View File

@ -1,5 +0,0 @@
[DEFAULT]
support-files =
script_fileList.js
[test_basic.html]

View File

@ -1,7 +0,0 @@
# -*- Mode: python; c-basic-offset: 4; 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/.
MOCHITEST_MANIFESTS += ['mochitest.ini']

View File

@ -1,25 +0,0 @@
var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.importGlobalProperties(["File"]);
addMessageListener("file.open", function () {
var testFile = Cc["@mozilla.org/file/directory_service;1"]
.getService(Ci.nsIDirectoryService)
.QueryInterface(Ci.nsIProperties)
.get("ProfD", Ci.nsIFile);
testFile.append("prefs.js");
sendAsyncMessage("file.opened", {
file: new File(testFile)
});
});
addMessageListener("dir.open", function () {
var testFile = Cc["@mozilla.org/file/directory_service;1"]
.getService(Ci.nsIDirectoryService)
.QueryInterface(Ci.nsIProperties)
.get("ProfD", Ci.nsIFile);
sendAsyncMessage("dir.opened", {
dir: testFile.path
});
});

View File

@ -1,103 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for Directory API</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<input id="fileList" type="file"></input>
<script type="application/javascript;version=1.7">
var directory;
function create_fileList() {
var url = SimpleTest.getTestFileURL("script_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.");
directory = fileList.files[0];
script.destroy();
next();
}
script.addMessageListener("dir.opened", onOpened);
script.sendAsyncMessage("dir.open");
}
function test_basic() {
ok(directory, "Directory exists.");
ok(directory instanceof Directory, "We have a directory.");
is(directory.name, '/', "directory.name must be '/'");
is(directory.path, '/', "directory.path must be '/'");
next();
}
function checkSubDir(dir) {
dir.getFilesAndDirectories().then(
function(data) {
for (var i = 0; i < data.length; ++i) {
ok (data[i] instanceof File || data[i] instanceof Directory, "Just Files or Directories");
if (data[i] instanceof Directory) {
isnot(data[i].name, '/', "Subdirectory should be called with the leafname");
isnot(data[i].path, '/', "Subdirectory path should be called with the leafname");
}
}
}
);
}
function getFilesAndDirectories() {
directory.getFilesAndDirectories().then(
function(data) {
ok(data.length, "We should have some data.");
var promises = [];
for (var i = 0; i < data.length; ++i) {
ok (data[i] instanceof File || data[i] instanceof Directory, "Just Files or Directories");
if (data[i] instanceof Directory) {
isnot(data[i].name, '/', "Subdirectory should be called with the leafname");
isnot(data[i].path, '/', "Subdirectory path should be called with the leafname");
promises.push(checkSubDir(data[i]));
}
}
return Promise.all(promises);
},
function() {
ok(false, "Something when wrong");
}
).then(next);
}
var tests = [
create_fileList,
test_basic,
getFilesAndDirectories,
];
function next() {
if (!tests.length) {
SimpleTest.finish();
return;
}
var test = tests.shift();
test();
}
SimpleTest.waitForExplicitFinish();
next();
</script>
</body>
</html>

View File

@ -11,6 +11,8 @@
#include "mozilla/DebugOnly.h"
#include "mozilla/dom/Date.h"
#include "mozilla/dom/Directory.h"
#include "mozilla/dom/FileSystemUtils.h"
#include "mozilla/dom/OSFileSystem.h"
#include "nsAttrValueInlines.h"
#include "nsCRTGlue.h"
@ -249,63 +251,16 @@ class HTMLInputElementState final : public nsISupports
mValue = aValue;
}
void
GetFilesOrDirectories(nsPIDOMWindowInner* aWindow,
nsTArray<OwningFileOrDirectory>& aResult) const
const nsTArray<RefPtr<BlobImpl>>& GetBlobImpls()
{
for (uint32_t i = 0; i < mBlobImplsOrDirectoryPaths.Length(); ++i) {
if (mBlobImplsOrDirectoryPaths[i].mType == Directory::BlobImplOrDirectoryPath::eBlobImpl) {
RefPtr<File> file =
File::Create(aWindow,
mBlobImplsOrDirectoryPaths[i].mBlobImpl);
MOZ_ASSERT(file);
OwningFileOrDirectory* element = aResult.AppendElement();
element->SetAsFile() = file;
} else {
MOZ_ASSERT(mBlobImplsOrDirectoryPaths[i].mType == Directory::BlobImplOrDirectoryPath::eDirectoryPath);
nsCOMPtr<nsIFile> file;
NS_ConvertUTF16toUTF8 path(mBlobImplsOrDirectoryPaths[i].mDirectoryPath);
nsresult rv = NS_NewNativeLocalFile(path, true, getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(rv))) {
continue;
}
RefPtr<Directory> directory = Directory::Create(aWindow, file,
Directory::eDOMRootDirectory);
MOZ_ASSERT(directory);
OwningFileOrDirectory* element = aResult.AppendElement();
element->SetAsDirectory() = directory;
}
}
return mBlobImpls;
}
void SetFilesOrDirectories(const nsTArray<OwningFileOrDirectory>& aArray)
void SetBlobImpls(const nsTArray<RefPtr<File>>& aFile)
{
mBlobImplsOrDirectoryPaths.Clear();
for (uint32_t i = 0; i < aArray.Length(); ++i) {
if (aArray[i].IsFile()) {
Directory::BlobImplOrDirectoryPath* data =
mBlobImplsOrDirectoryPaths.AppendElement();
data->mBlobImpl = aArray[i].GetAsFile()->Impl();
data->mType = Directory::BlobImplOrDirectoryPath::eBlobImpl;
} else {
MOZ_ASSERT(aArray[i].IsDirectory());
nsAutoString fullPath;
nsresult rv = aArray[i].GetAsDirectory()->GetFullRealPath(fullPath);
if (NS_WARN_IF(NS_FAILED(rv))) {
continue;
}
Directory::BlobImplOrDirectoryPath* data =
mBlobImplsOrDirectoryPaths.AppendElement();
data->mDirectoryPath = fullPath;
data->mType = Directory::BlobImplOrDirectoryPath::eDirectoryPath;
}
mBlobImpls.Clear();
for (uint32_t i = 0, len = aFile.Length(); i < len; ++i) {
mBlobImpls.AppendElement(aFile[i]->Impl());
}
}
@ -319,9 +274,7 @@ class HTMLInputElementState final : public nsISupports
~HTMLInputElementState() {}
nsString mValue;
nsTArray<Directory::BlobImplOrDirectoryPath> mBlobImplsOrDirectoryPaths;
nsTArray<RefPtr<BlobImpl>> mBlobImpls;
bool mChecked;
bool mCheckedSet;
};
@ -387,65 +340,33 @@ UploadLastDir::ContentPrefCallback::HandleError(nsresult error)
namespace {
/**
* This may return nullptr if the DOM File's implementation of
* This may return nullptr if aDomFile's implementation of
* File::mozFullPathInternal does not successfully return a non-empty
* string that is a valid path. This can happen on Firefox OS, for example,
* where the file picker can create Blobs.
*/
static already_AddRefed<nsIFile>
DOMFileOrDirectoryToLocalFile(const OwningFileOrDirectory& aData)
DOMFileToLocalFile(File* aDomFile)
{
nsString path;
if (aData.IsFile()) {
ErrorResult rv;
aData.GetAsFile()->GetMozFullPathInternal(path, rv);
if (rv.Failed() || path.IsEmpty()) {
rv.SuppressException();
return nullptr;
}
} else {
MOZ_ASSERT(aData.IsDirectory());
aData.GetAsDirectory()->GetFullRealPath(path);
ErrorResult rv;
aDomFile->GetMozFullPathInternal(path, rv);
if (rv.Failed() || path.IsEmpty()) {
rv.SuppressException();
return nullptr;
}
nsCOMPtr<nsIFile> localFile;
nsresult rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(path), true,
getter_AddRefs(localFile));
if (NS_WARN_IF(NS_FAILED(rv))) {
rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(path), true,
getter_AddRefs(localFile));
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();
return nullptr;
}
return localFile.forget();
}
void
GetDOMFileOrDirectoryName(const OwningFileOrDirectory& aData,
nsAString& aName)
{
if (aData.IsFile()) {
aData.GetAsFile()->GetName(aName);
} else {
MOZ_ASSERT(aData.IsDirectory());
ErrorResult rv;
aData.GetAsDirectory()->GetName(aName, rv);
NS_WARN_IF(rv.Failed());
}
}
void
GetDOMFileOrDirectoryPath(const OwningFileOrDirectory& aData,
nsAString& aPath,
ErrorResult& aRv)
{
if (aData.IsFile()) {
aData.GetAsFile()->GetMozFullPathInternal(aPath, aRv);
} else {
MOZ_ASSERT(aData.IsDirectory());
aData.GetAsDirectory()->GetFullRealPath(aPath);
}
}
} // namespace
@ -462,7 +383,7 @@ HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult)
mFilePicker->GetMode(&mode);
// Collect new selected filenames
nsTArray<OwningFileOrDirectory> newFilesOrDirectories;
nsTArray<RefPtr<File>> newFiles;
if (mode == static_cast<int16_t>(nsIFilePicker::modeOpenMultiple)) {
nsCOMPtr<nsISimpleEnumerator> iter;
nsresult rv =
@ -481,12 +402,9 @@ HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult)
nsCOMPtr<nsIDOMBlob> domBlob = do_QueryInterface(tmp);
MOZ_ASSERT(domBlob,
"Null file object from FilePicker's file enumerator?");
if (!domBlob) {
continue;
if (domBlob) {
newFiles.AppendElement(static_cast<File*>(domBlob.get()));
}
OwningFileOrDirectory* element = newFilesOrDirectories.AppendElement();
element->SetAsFile() = static_cast<File*>(domBlob.get());
}
} else {
MOZ_ASSERT(mode == static_cast<int16_t>(nsIFilePicker::modeOpen) ||
@ -498,25 +416,16 @@ HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult)
nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(tmp);
if (blob) {
RefPtr<File> file = static_cast<Blob*>(blob.get())->ToFile();
MOZ_ASSERT(file);
OwningFileOrDirectory* element = newFilesOrDirectories.AppendElement();
element->SetAsFile() = file;
} else if (tmp) {
RefPtr<Directory> directory = static_cast<Directory*>(tmp.get());
OwningFileOrDirectory* element = newFilesOrDirectories.AppendElement();
element->SetAsDirectory() = directory;
newFiles.AppendElement(file);
}
}
if (newFilesOrDirectories.IsEmpty()) {
if (newFiles.IsEmpty()) {
return NS_OK;
}
// Store the last used directory using the content pref service:
nsCOMPtr<nsIFile> file =
DOMFileOrDirectoryToLocalFile(newFilesOrDirectories[0]);
nsCOMPtr<nsIFile> file = DOMFileToLocalFile(newFiles[0]);
if (file) {
nsCOMPtr<nsIFile> lastUsedDir;
file->GetParent(getter_AddRefs(lastUsedDir));
@ -527,7 +436,7 @@ HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult)
// The text control frame (if there is one) isn't going to send a change
// event because it will think this is done by a script.
// So, we can safely send one by ourself.
mInput->SetFilesOrDirectories(newFilesOrDirectories, true);
mInput->SetFiles(newFiles, true);
return nsContentUtils::DispatchTrustedEvent(mInput->OwnerDoc(),
static_cast<nsIDOMHTMLInputElement*>(mInput.get()),
NS_LITERAL_STRING("change"), true,
@ -763,8 +672,7 @@ HTMLInputElement::InitFilePicker(FilePickerType aType)
// Set default directry and filename
nsAutoString defaultName;
const nsTArray<OwningFileOrDirectory>& oldFiles =
GetFilesOrDirectoriesInternal();
const nsTArray<RefPtr<File>>& oldFiles = GetFilesInternal();
nsCOMPtr<nsIFilePickerShownCallback> callback =
new HTMLInputElement::nsFilePickerShownCallback(this, filePicker);
@ -773,10 +681,18 @@ HTMLInputElement::InitFilePicker(FilePickerType aType)
aType != FILE_PICKER_DIRECTORY) {
nsString path;
nsCOMPtr<nsIFile> localFile = DOMFileOrDirectoryToLocalFile(oldFiles[0]);
if (localFile) {
ErrorResult error;
oldFiles[0]->GetMozFullPathInternal(path, error);
if (NS_WARN_IF(error.Failed())) {
return error.StealNSResult();
}
nsCOMPtr<nsIFile> localFile;
rv = NS_NewLocalFile(path, false, getter_AddRefs(localFile));
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIFile> parentFile;
nsresult rv = localFile->GetParent(getter_AddRefs(parentFile));
rv = localFile->GetParent(getter_AddRefs(parentFile));
if (NS_SUCCEEDED(rv)) {
filePicker->SetDisplayDirectory(parentFile);
}
@ -787,8 +703,7 @@ HTMLInputElement::InitFilePicker(FilePickerType aType)
// one file was selected before.
if (oldFiles.Length() == 1) {
nsAutoString leafName;
GetDOMFileOrDirectoryName(oldFiles[0], leafName);
oldFiles[0]->GetName(leafName);
if (!leafName.IsEmpty()) {
filePicker->SetDefaultString(leafName);
}
@ -841,7 +756,7 @@ UploadLastDir::FetchDirectoryAndDisplayPicker(nsIDocument* aDoc,
NS_PRECONDITION(docURI, "docURI is null");
nsCOMPtr<nsILoadContext> loadContext = aDoc->GetLoadContext();
nsCOMPtr<nsIContentPrefCallback2> prefCallback =
nsCOMPtr<nsIContentPrefCallback2> prefCallback =
new UploadLastDir::ContentPrefCallback(aFilePicker, aFpCallback);
#ifdef MOZ_B2G
@ -1020,7 +935,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLInputElement,
if (tmp->IsSingleLineTextControl(false)) {
tmp->mInputData.mState->Traverse(cb);
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFilesOrDirectories)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFiles)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFileList)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFilesAndDirectoriesPromise)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
@ -1029,7 +944,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLInputElement,
nsGenericHTMLFormElementWithState)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mValidity)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFilesOrDirectories)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFiles)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFileList)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFilesAndDirectoriesPromise)
if (tmp->IsSingleLineTextControl(false)) {
@ -1088,8 +1003,8 @@ HTMLInputElement::Clone(mozilla::dom::NodeInfo* aNodeInfo, nsINode** aResult) co
// we can just grab the pretty string and use it as wallpaper
GetDisplayFileName(it->mStaticDocFileList);
} else {
it->mFilesOrDirectories.Clear();
it->mFilesOrDirectories.AppendElements(mFilesOrDirectories);
it->mFiles.Clear();
it->mFiles.AppendElements(mFiles);
}
break;
case VALUE_MODE_DEFAULT_ON:
@ -1527,9 +1442,9 @@ HTMLInputElement::GetValueInternal(nsAString& aValue) const
#else
// XXX We'd love to assert that this can't happen, but some mochitests
// use SpecialPowers to circumvent our more sane security model.
if (!mFilesOrDirectories.IsEmpty()) {
if (!mFiles.IsEmpty()) {
ErrorResult rv;
GetDOMFileOrDirectoryPath(mFilesOrDirectories[0], aValue, rv);
mFiles[0]->GetMozFullPath(aValue, rv);
if (NS_WARN_IF(rv.Failed())) {
return rv.StealNSResult();
}
@ -1541,10 +1456,10 @@ HTMLInputElement::GetValueInternal(nsAString& aValue) const
#endif
} else {
// Just return the leaf name
if (mFilesOrDirectories.IsEmpty()) {
if (mFiles.IsEmpty()) {
aValue.Truncate();
} else {
GetDOMFileOrDirectoryName(mFilesOrDirectories[0], aValue);
mFiles[0]->GetName(aValue);
}
}
@ -1579,8 +1494,8 @@ HTMLInputElement::IsValueEmpty() const
void
HTMLInputElement::ClearFiles(bool aSetValueChanged)
{
nsTArray<OwningFileOrDirectory> data;
SetFilesOrDirectories(data, aSetValueChanged);
nsTArray<RefPtr<File>> files;
SetFiles(files, aSetValueChanged);
}
/* static */ Decimal
@ -2150,9 +2065,9 @@ void
HTMLInputElement::MozGetFileNameArray(nsTArray<nsString>& aArray,
ErrorResult& aRv)
{
for (uint32_t i = 0; i < mFilesOrDirectories.Length(); i++) {
nsAutoString str;
GetDOMFileOrDirectoryPath(mFilesOrDirectories[i], str, aRv);
for (uint32_t i = 0; i < mFiles.Length(); i++) {
nsString str;
mFiles[i]->GetMozFullPathInternal(str, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
@ -2202,29 +2117,25 @@ HTMLInputElement::MozSetFileArray(const Sequence<OwningNonNull<File>>& aFiles)
if (!global) {
return;
}
nsTArray<OwningFileOrDirectory> files;
nsTArray<RefPtr<File>> files;
for (uint32_t i = 0; i < aFiles.Length(); ++i) {
RefPtr<File> file = File::Create(global, aFiles[i].get()->Impl());
MOZ_ASSERT(file);
OwningFileOrDirectory* element = files.AppendElement();
element->SetAsFile() = file;
files.AppendElement(file);
}
SetFilesOrDirectories(files, true);
SetFiles(files, true);
}
void
HTMLInputElement::MozSetFileNameArray(const Sequence<nsString>& aFileNames,
ErrorResult& aRv)
HTMLInputElement::MozSetFileNameArray(const Sequence< nsString >& aFileNames, ErrorResult& aRv)
{
if (XRE_IsContentProcess()) {
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return;
}
nsTArray<OwningFileOrDirectory> files;
nsTArray<RefPtr<File>> files;
for (uint32_t i = 0; i < aFileNames.Length(); ++i) {
nsCOMPtr<nsIFile> file;
@ -2241,28 +2152,21 @@ HTMLInputElement::MozSetFileNameArray(const Sequence<nsString>& aFileNames,
NS_NewLocalFile(aFileNames[i], false, getter_AddRefs(file));
}
if (!file) {
if (file) {
nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
RefPtr<File> domFile = File::CreateFromFile(global, file);
files.AppendElement(domFile);
} else {
continue; // Not much we can do if the file doesn't exist
}
nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
if (!global) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
RefPtr<File> domFile = File::CreateFromFile(global, file);
OwningFileOrDirectory* element = files.AppendElement();
element->SetAsFile() = domFile;
}
SetFilesOrDirectories(files, true);
SetFiles(files, true);
}
NS_IMETHODIMP
HTMLInputElement::MozSetFileNameArray(const char16_t** aFileNames,
uint32_t aLength)
HTMLInputElement::MozSetFileNameArray(const char16_t** aFileNames, uint32_t aLength)
{
if (!nsContentUtils::IsCallerChrome()) {
// setting the value of a "FILE" input widget requires chrome privilege
@ -2284,34 +2188,6 @@ HTMLInputElement::MozSetFileNameArray(const char16_t** aFileNames,
return rv.StealNSResult();
}
void
HTMLInputElement::MozSetDirectory(const nsAString& aDirectoryPath,
ErrorResult& aRv)
{
nsCOMPtr<nsIFile> file;
NS_ConvertUTF16toUTF8 path(aDirectoryPath);
aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(file));
if (NS_WARN_IF(aRv.Failed())) {
return;
}
nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
if (NS_WARN_IF(!window)) {
aRv.Throw(NS_ERROR_FAILURE);
return;
}
RefPtr<Directory> directory = Directory::Create(window, file,
Directory::eDOMRootDirectory);
MOZ_ASSERT(directory);
nsTArray<OwningFileOrDirectory> array;
OwningFileOrDirectory* element = array.AppendElement();
element->SetAsDirectory() = directory;
SetFilesOrDirectories(array, true);
}
bool
HTMLInputElement::MozIsTextField(bool aExcludePassword)
{
@ -2503,14 +2379,14 @@ HTMLInputElement::GetDisplayFileName(nsAString& aValue) const
return;
}
if (mFilesOrDirectories.Length() == 1) {
GetDOMFileOrDirectoryName(mFilesOrDirectories[0], aValue);
if (mFiles.Length() == 1) {
mFiles[0]->GetName(aValue);
return;
}
nsXPIDLString value;
if (mFilesOrDirectories.IsEmpty()) {
if (mFiles.IsEmpty()) {
if (HasAttr(kNameSpaceID_None, nsGkAtoms::multiple)) {
nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
"NoFilesSelected", value);
@ -2520,7 +2396,7 @@ HTMLInputElement::GetDisplayFileName(nsAString& aValue) const
}
} else {
nsString count;
count.AppendInt(int(mFilesOrDirectories.Length()));
count.AppendInt(int(mFiles.Length()));
const char16_t* params[] = { count.get() };
nsContentUtils::FormatLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
@ -2531,13 +2407,13 @@ HTMLInputElement::GetDisplayFileName(nsAString& aValue) const
}
void
HTMLInputElement::SetFilesOrDirectories(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories,
bool aSetValueChanged)
HTMLInputElement::SetFiles(const nsTArray<RefPtr<File>>& aFiles,
bool aSetValueChanged)
{
mFilesOrDirectories.Clear();
mFilesOrDirectories.AppendElements(aFilesOrDirectories);
mFiles.Clear();
mFiles.AppendElements(aFiles);
AfterSetFilesOrDirectories(aSetValueChanged);
AfterSetFiles(aSetValueChanged);
}
void
@ -2545,22 +2421,22 @@ HTMLInputElement::SetFiles(nsIDOMFileList* aFiles,
bool aSetValueChanged)
{
RefPtr<FileList> files = static_cast<FileList*>(aFiles);
mFilesOrDirectories.Clear();
mFiles.Clear();
if (aFiles) {
uint32_t listLength;
aFiles->GetLength(&listLength);
for (uint32_t i = 0; i < listLength; i++) {
OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement();
*element = files->UnsafeItem(i);
RefPtr<File> file = files->Item(i);
mFiles.AppendElement(file);
}
}
AfterSetFilesOrDirectories(aSetValueChanged);
AfterSetFiles(aSetValueChanged);
}
void
HTMLInputElement::AfterSetFilesOrDirectories(bool aSetValueChanged)
HTMLInputElement::AfterSetFiles(bool aSetValueChanged)
{
// No need to flush here, if there's no frame at this point we
// don't need to force creation of one just to tell it about this
@ -2578,11 +2454,11 @@ HTMLInputElement::AfterSetFilesOrDirectories(bool aSetValueChanged)
// call under GetMozFullPath won't be rejected for not being urgent.
// XXX Protected by the ifndef because the blob code doesn't allow us to send
// this message in b2g.
if (mFilesOrDirectories.IsEmpty()) {
if (mFiles.IsEmpty()) {
mFirstFilePath.Truncate();
} else {
ErrorResult rv;
GetDOMFileOrDirectoryPath(mFilesOrDirectories[0], mFirstFilePath, rv);
mFiles[0]->GetMozFullPath(mFirstFilePath, rv);
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();
}
@ -2656,27 +2532,24 @@ HTMLInputElement::HandleNumberControlSpin(void* aData)
}
}
void
nsresult
HTMLInputElement::UpdateFileList()
{
if (mFileList) {
mFileList->Clear();
const nsTArray<OwningFileOrDirectory>& array =
GetFilesOrDirectoriesInternal();
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());
const nsTArray<RefPtr<File>>& files = GetFilesInternal();
for (uint32_t i = 0; i < files.Length(); ++i) {
if (!mFileList->Append(files[i])) {
return NS_ERROR_FAILURE;
}
}
}
// Make sure we (lazily) create a new Promise for GetFilesAndDirectories:
mFilesAndDirectoriesPromise = nullptr;
return NS_OK;
}
nsresult
@ -4115,7 +3988,7 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
nsNumberControlFrame* numberControlFrame =
do_QueryFrame(GetPrimaryFrame());
if (numberControlFrame) {
if (aVisitor.mEvent->mMessage == eMouseDown &&
if (aVisitor.mEvent->mMessage == eMouseDown &&
IsMutable()) {
switch (numberControlFrame->GetSpinButtonForPointerEvent(
aVisitor.mEvent->AsMouseEvent())) {
@ -4666,7 +4539,7 @@ HTMLInputElement::GetValueAsDate(const nsAString& aValue,
}
uint32_t endOfYearOffset = aValue.Length() - 6;
if (aValue[endOfYearOffset] != '-' ||
aValue[endOfYearOffset + 3] != '-') {
return false;
@ -5034,38 +4907,45 @@ HTMLInputElement::GetFilesAndDirectories(ErrorResult& aRv)
return nullptr;
}
const nsTArray<OwningFileOrDirectory>& filesAndDirs =
GetFilesOrDirectoriesInternal();
const nsTArray<RefPtr<File>>& filesAndDirs = GetFilesInternal();
Sequence<OwningFileOrDirectory> filesAndDirsSeq;
if (!filesAndDirsSeq.SetLength(filesAndDirs.Length(),
mozilla::fallible_t())) {
if (!filesAndDirsSeq.SetLength(filesAndDirs.Length(), mozilla::fallible_t())) {
p->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
return p.forget();
}
for (uint32_t i = 0; i < filesAndDirs.Length(); ++i) {
if (filesAndDirs[i].IsDirectory()) {
if (filesAndDirs[i]->IsDirectory()) {
#if defined(ANDROID) || defined(MOZ_B2G)
MOZ_ASSERT(false,
"Directory picking should have been redirected to normal "
"file picking for platforms that don't have a directory "
"picker");
#endif
nsAutoString path;
filesAndDirs[i]->GetMozFullPathInternal(path, aRv);
if (aRv.Failed()) {
return nullptr;
}
int32_t leafSeparatorIndex = path.RFind(FILE_PATH_SEPARATOR);
nsDependentSubstring dirname = Substring(path, 0, leafSeparatorIndex);
RefPtr<Directory> directory = filesAndDirs[i].GetAsDirectory();
RefPtr<OSFileSystem> fs = new OSFileSystem(dirname);
fs->Init(OwnerDoc()->GetInnerWindow());
nsAutoString dompath(NS_LITERAL_STRING(FILESYSTEM_DOM_PATH_SEPARATOR));
dompath.Append(Substring(path, leafSeparatorIndex + 1));
RefPtr<Directory> directory = new Directory(fs, dompath);
// In future we could refactor SetFilePickerFiltersFromAccept to return a
// semicolon separated list of file extensions and include that in the
// filter string passed here.
directory->SetContentFilters(NS_LITERAL_STRING("filter-out-sensitive"));
filesAndDirsSeq[i].SetAsDirectory() = directory;
} else {
MOZ_ASSERT(filesAndDirs[i].IsFile());
// This file was directly selected by the user, so don't filter it.
filesAndDirsSeq[i].SetAsFile() = filesAndDirs[i].GetAsFile();
filesAndDirsSeq[i].SetAsFile() = filesAndDirs[i];
}
}
@ -5699,18 +5579,13 @@ HTMLInputElement::SubmitNamesValues(nsFormSubmission* aFormSubmission)
if (mType == NS_FORM_INPUT_FILE) {
// Submit files
const nsTArray<OwningFileOrDirectory>& files =
GetFilesOrDirectoriesInternal();
const nsTArray<RefPtr<File>>& files = GetFilesInternal();
bool hasBlobs = false;
for (uint32_t i = 0; i < files.Length(); ++i) {
if (files[i].IsFile()) {
hasBlobs = true;
aFormSubmission->AddNameBlobOrNullPair(name, files[i].GetAsFile());
}
aFormSubmission->AddNameBlobOrNullPair(name, files[i]);
}
if (!hasBlobs) {
if (files.IsEmpty()) {
aFormSubmission->AddNameBlobOrNullPair(name, nullptr);
}
@ -5744,9 +5619,9 @@ HTMLInputElement::SaveState()
}
break;
case VALUE_MODE_FILENAME:
if (!mFilesOrDirectories.IsEmpty()) {
if (!mFiles.IsEmpty()) {
inputState = new HTMLInputElementState();
inputState->SetFilesOrDirectories(mFilesOrDirectories);
inputState->SetBlobImpls(mFiles);
}
break;
case VALUE_MODE_VALUE:
@ -5915,7 +5790,7 @@ HTMLInputElement::AddStates(EventStates aStates)
}
}
}
nsGenericHTMLFormElementWithState::AddStates(aStates);
nsGenericHTMLFormElementWithState::AddStates(aStates);
}
void
@ -5952,13 +5827,20 @@ HTMLInputElement::RestoreState(nsPresState* aState)
break;
case VALUE_MODE_FILENAME:
{
nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
if (window) {
nsTArray<OwningFileOrDirectory> array;
inputState->GetFilesOrDirectories(window, array);
const nsTArray<RefPtr<BlobImpl>>& blobImpls = inputState->GetBlobImpls();
SetFilesOrDirectories(array, true);
nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
MOZ_ASSERT(global);
nsTArray<RefPtr<File>> files;
for (uint32_t i = 0, len = blobImpls.Length(); i < len; ++i) {
RefPtr<File> file = File::Create(global, blobImpls[i]);
MOZ_ASSERT(file);
files.AppendElement(file);
}
SetFiles(files, true);
}
break;
case VALUE_MODE_VALUE:
@ -6468,15 +6350,15 @@ HTMLInputElement::IsValueMissing() const
switch (GetValueMode()) {
case VALUE_MODE_VALUE:
return IsValueEmpty();
case VALUE_MODE_FILENAME:
return GetFilesOrDirectoriesInternal().IsEmpty();
{
const nsTArray<RefPtr<File>>& files = GetFilesInternal();
return files.IsEmpty();
}
case VALUE_MODE_DEFAULT_ON:
// This should not be used for type radio.
// See the MOZ_ASSERT at the beginning of the method.
return !mChecked;
case VALUE_MODE_DEFAULT:
default:
return false;

View File

@ -20,7 +20,6 @@
#include "mozilla/dom/HTMLFormElement.h" // for HasEverTriedInvalidSubmit()
#include "mozilla/dom/HTMLInputElementBinding.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/UnionTypes.h"
#include "nsIFilePicker.h"
#include "nsIContentPrefService2.h"
#include "mozilla/Decimal.h"
@ -221,13 +220,12 @@ public:
void GetDisplayFileName(nsAString& aFileName) const;
const nsTArray<OwningFileOrDirectory>& GetFilesOrDirectoriesInternal() const
const nsTArray<RefPtr<File>>& GetFilesInternal() const
{
return mFilesOrDirectories;
return mFiles;
}
void SetFilesOrDirectories(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories,
bool aSetValueChanged);
void SetFiles(const nsTArray<RefPtr<File>>& aFiles, bool aSetValueChanged);
void SetFiles(nsIDOMFileList* aFiles, bool aSetValueChanged);
// Called when a nsIFilePicker or a nsIColorPicker terminate.
@ -724,7 +722,6 @@ public:
void MozSetFileNameArray(const Sequence< nsString >& aFileNames, ErrorResult& aRv);
void MozSetFileArray(const Sequence<OwningNonNull<File>>& aFiles);
void MozSetDirectory(const nsAString& aDirectoryPath, ErrorResult& aRv);
HTMLInputElement* GetOwnerNumberControl();
@ -925,12 +922,12 @@ protected:
/**
* Update mFileList with the currently selected file.
*/
void UpdateFileList();
nsresult UpdateFileList();
/**
* Called after calling one of the SetFilesOrDirectories() functions.
* Called after calling one of the SetFiles() functions.
*/
void AfterSetFilesOrDirectories(bool aSetValueChanged);
void AfterSetFiles(bool aSetValueChanged);
/**
* Determine whether the editor needs to be initialized explicitly for
@ -1269,16 +1266,16 @@ protected:
} mInputData;
/**
* The value of the input if it is a file input. This is the list of files or
* directories DOM objects used when uploading a file. It is vital that this
* is kept separate from mValue so that it won't be possible to 'leak' the
* value from a text-input to a file-input. Additionally, the logic for this
* value is kept as simple as possible to avoid accidental errors where the
* wrong filename is used. Therefor the list of filenames is always owned by
* this member, never by the frame. Whenever the frame wants to change the
* filename it has to call SetFilesOrDirectories to update this member.
* The value of the input if it is a file input. This is the list of filenames
* used when uploading a file. It is vital that this is kept separate from
* mValue so that it won't be possible to 'leak' the value from a text-input
* to a file-input. Additionally, the logic for this value is kept as simple
* as possible to avoid accidental errors where the wrong filename is used.
* Therefor the list of filenames is always owned by this member, never by
* the frame. Whenever the frame wants to change the filename it has to call
* SetFileNames to update this member.
*/
nsTArray<OwningFileOrDirectory> mFilesOrDirectories;
nsTArray<RefPtr<File>> mFiles;
#ifndef MOZ_CHILD_PERMISSIONS
/**

View File

@ -187,6 +187,30 @@ private:
return mBlobImpl->IsFile();
}
virtual void
LookupAndCacheIsDirectory() override
{
mBlobImpl->LookupAndCacheIsDirectory();
}
virtual void
SetIsDirectory(bool aIsDir) override
{
return mBlobImpl->SetIsDirectory(aIsDir);
}
virtual bool
IsDirectory() const override
{
return mBlobImpl->IsDirectory();
}
virtual BlobDirState
GetDirState() const override
{
return mBlobImpl->GetDirState();
}
virtual bool
MayBeClonedToOtherThreads() const override
{

View File

@ -444,7 +444,8 @@ ResolveMysteryFile(BlobImpl* aImpl,
BlobChild* actor = ActorFromRemoteBlobImpl(aImpl);
if (actor) {
return actor->SetMysteryBlobInfo(aName, aContentType,
aSize, aLastModifiedDate);
aSize, aLastModifiedDate,
BlobDirState::eUnknownIfDir);
}
return true;
}

View File

@ -670,7 +670,8 @@ public:
EmptyBlobImpl(const nsAString& aName,
const nsAString& aContentType,
int64_t aLastModifiedDate)
: BlobImplBase(aName, aContentType, 0, aLastModifiedDate)
: BlobImplBase(aName, aContentType, 0, aLastModifiedDate,
BlobDirState::eIsNotDir)
{
mImmutable = true;
}
@ -715,12 +716,14 @@ struct MOZ_STACK_CLASS CreateBlobImplMetadata final
nsString mName;
uint64_t mLength;
int64_t mLastModifiedDate;
BlobDirState mDirState;
bool mHasRecursed;
const bool mIsSameProcessActor;
explicit CreateBlobImplMetadata(bool aIsSameProcessActor)
: mLength(0)
, mLastModifiedDate(0)
, mDirState(BlobDirState::eUnknownIfDir)
, mHasRecursed(false)
, mIsSameProcessActor(aIsSameProcessActor)
{
@ -962,6 +965,7 @@ CreateBlobImpl(const ParentBlobConstructorParams& aParams,
metadata.mName = params.name();
metadata.mLength = params.length();
metadata.mLastModifiedDate = params.modDate();
metadata.mDirState = BlobDirState(params.dirState());
}
RefPtr<BlobImpl> blobImpl =
@ -1771,6 +1775,7 @@ public:
const nsAString& aContentType,
uint64_t aLength,
int64_t aModDate,
BlobDirState aDirState,
bool aIsSameProcessBlob);
// For Blob.
@ -2048,6 +2053,18 @@ public:
virtual bool
IsFile() const override;
virtual void
LookupAndCacheIsDirectory() override;
virtual void
SetIsDirectory(bool aIsDir) override;
virtual bool
IsDirectory() const override;
virtual BlobDirState
GetDirState() const override;
virtual bool
MayBeClonedToOtherThreads() const override;
@ -2079,8 +2096,9 @@ RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
const nsAString& aContentType,
uint64_t aLength,
int64_t aModDate,
BlobDirState aDirState,
bool aIsSameProcessBlob)
: BlobImplBase(aName, aContentType, aLength, aModDate)
: BlobImplBase(aName, aContentType, aLength, aModDate, aDirState)
, mIsSlice(false)
{
if (aIsSameProcessBlob) {
@ -2796,6 +2814,34 @@ RemoteBlobImpl::IsFile() const
return mBlobImpl->IsFile();
}
void
BlobParent::
RemoteBlobImpl::LookupAndCacheIsDirectory()
{
return mBlobImpl->LookupAndCacheIsDirectory();
}
void
BlobParent::
RemoteBlobImpl::SetIsDirectory(bool aIsDir)
{
return mBlobImpl->SetIsDirectory(aIsDir);
}
bool
BlobParent::
RemoteBlobImpl::IsDirectory() const
{
return mBlobImpl->IsDirectory();
}
BlobDirState
BlobParent::
RemoteBlobImpl::GetDirState() const
{
return mBlobImpl->GetDirState();
}
bool
BlobParent::
RemoteBlobImpl::MayBeClonedToOtherThreads() const
@ -2987,7 +3033,8 @@ BlobChild::CommonInit(BlobChild* aOther, BlobImpl* aBlobImpl)
MOZ_ASSERT(!rv.Failed());
remoteBlob = new RemoteBlobImpl(this, otherImpl, name, contentType, length,
modDate, false /* SameProcessBlobImpl */);
modDate, otherImpl->GetDirState(),
false /* SameProcessBlobImpl */);
} else {
remoteBlob = new RemoteBlobImpl(this, otherImpl, contentType, length,
false /* SameProcessBlobImpl */);
@ -3039,6 +3086,7 @@ BlobChild::CommonInit(const ChildBlobConstructorParams& aParams)
params.contentType(),
params.length(),
params.modDate(),
BlobDirState(params.dirState()),
false /* SameProcessBlobImpl */);
break;
}
@ -3074,6 +3122,7 @@ BlobChild::CommonInit(const ChildBlobConstructorParams& aParams)
contentType,
size,
lastModifiedDate,
blobImpl->GetDirState(),
true /* SameProcessBlobImpl */);
} else {
remoteBlob = new RemoteBlobImpl(this, blobImpl, contentType, size,
@ -3254,7 +3303,8 @@ BlobChild::GetOrCreateFromImpl(ChildManagerType* aManager,
MOZ_ASSERT(!rv.Failed());
blobParams =
FileBlobConstructorParams(name, contentType, length, modDate, blobData);
FileBlobConstructorParams(name, contentType, length, modDate,
aBlobImpl->GetDirState(), blobData);
} else {
blobParams = NormalBlobConstructorParams(contentType, length, blobData);
}
@ -3427,7 +3477,8 @@ bool
BlobChild::SetMysteryBlobInfo(const nsString& aName,
const nsString& aContentType,
uint64_t aLength,
int64_t aLastModifiedDate)
int64_t aLastModifiedDate,
BlobDirState aDirState)
{
AssertIsOnOwningThread();
MOZ_ASSERT(mBlobImpl);
@ -3440,6 +3491,7 @@ BlobChild::SetMysteryBlobInfo(const nsString& aName,
aContentType,
aLength,
aLastModifiedDate,
aDirState,
void_t() /* optionalBlobData */);
return SendResolveMystery(params);
}
@ -3802,7 +3854,7 @@ BlobParent::GetOrCreateFromImpl(ParentManagerType* aManager,
blobParams =
FileBlobConstructorParams(name, contentType, length, modDate,
void_t());
aBlobImpl->GetDirState(), void_t());
} else {
blobParams = NormalBlobConstructorParams(contentType, length, void_t());
}

View File

@ -31,6 +31,8 @@ class ContentChild;
class nsIContentChild;
class PBlobStreamChild;
enum BlobDirState : uint32_t;
class BlobChild final
: public PBlobChild
{
@ -115,7 +117,8 @@ public:
SetMysteryBlobInfo(const nsString& aName,
const nsString& aContentType,
uint64_t aLength,
int64_t aLastModifiedDate);
int64_t aLastModifiedDate,
BlobDirState aDirState);
// Use this for non-file blobs.
bool

View File

@ -73,6 +73,7 @@ struct FileBlobConstructorParams
nsString contentType;
uint64_t length;
int64_t modDate;
uint32_t dirState;
// This must be of type BlobData in a child->parent message, and will always
// be of type void_t in a parent->child message.

View File

@ -53,18 +53,15 @@ FilePickerParent::~FilePickerParent()
// 2. The stream transport thread stat()s the file in Run() and then dispatches
// the same runnable on the main thread.
// 3. The main thread sends the results over IPC.
FilePickerParent::IORunnable::IORunnable(FilePickerParent *aFPParent,
nsTArray<nsCOMPtr<nsIFile>>& aFiles,
bool aIsDirectory)
FilePickerParent::FileSizeAndDateRunnable::FileSizeAndDateRunnable(FilePickerParent *aFPParent,
nsTArray<RefPtr<BlobImpl>>& aBlobs)
: mFilePickerParent(aFPParent)
, mIsDirectory(aIsDirectory)
{
mFiles.SwapElements(aFiles);
MOZ_ASSERT_IF(aIsDirectory, mFiles.Length() == 1);
mBlobs.SwapElements(aBlobs);
}
bool
FilePickerParent::IORunnable::Dispatch()
FilePickerParent::FileSizeAndDateRunnable::Dispatch()
{
MOZ_ASSERT(NS_IsMainThread());
@ -78,49 +75,23 @@ FilePickerParent::IORunnable::Dispatch()
}
NS_IMETHODIMP
FilePickerParent::IORunnable::Run()
FilePickerParent::FileSizeAndDateRunnable::Run()
{
// If we're on the main thread, then that means we're done. Just send the
// results.
if (NS_IsMainThread()) {
if (mFilePickerParent) {
mFilePickerParent->SendFilesOrDirectories(mResults);
mFilePickerParent->SendFiles(mBlobs);
}
return NS_OK;
}
// We're not on the main thread, so do the IO.
for (uint32_t i = 0; i < mFiles.Length(); ++i) {
if (mIsDirectory) {
nsAutoString path;
nsresult rv = mFiles[i]->GetPath(path);
if (NS_WARN_IF(NS_FAILED(rv))) {
continue;
}
BlobImplOrString* data = mResults.AppendElement();
data->mType = BlobImplOrString::eDirectoryPath;
data->mDirectoryPath = path;
continue;
}
RefPtr<BlobImpl> blobImpl = new BlobImplFile(mFiles[i]);
ErrorResult error;
blobImpl->GetSize(error);
if (NS_WARN_IF(error.Failed())) {
continue;
}
blobImpl->GetLastModified(error);
if (NS_WARN_IF(error.Failed())) {
continue;
}
BlobImplOrString* data = mResults.AppendElement();
data->mType = BlobImplOrString::eBlobImpl;
data->mBlobImpl = blobImpl;
// We're not on the main thread, so do the stat().
for (unsigned i = 0; i < mBlobs.Length(); i++) {
ErrorResult rv;
mBlobs[i]->GetSize(rv);
mBlobs[i]->GetLastModified(rv);
mBlobs[i]->LookupAndCacheIsDirectory();
}
// Dispatch ourselves back on the main thread.
@ -130,46 +101,29 @@ FilePickerParent::IORunnable::Run()
// thread.
MOZ_CRASH();
}
return NS_OK;
}
void
FilePickerParent::IORunnable::Destroy()
FilePickerParent::FileSizeAndDateRunnable::Destroy()
{
mFilePickerParent = nullptr;
}
void
FilePickerParent::SendFilesOrDirectories(const nsTArray<BlobImplOrString>& aData)
FilePickerParent::SendFiles(const nsTArray<RefPtr<BlobImpl>>& aBlobs)
{
if (mMode == nsIFilePicker::modeGetFolder) {
MOZ_ASSERT(aData.Length() <= 1);
if (aData.IsEmpty()) {
Unused << Send__delete__(this, void_t(), mResult);
return;
}
MOZ_ASSERT(aData[0].mType == BlobImplOrString::eDirectoryPath);
InputDirectory input;
input.directoryPath() = aData[0].mDirectoryPath;
Unused << Send__delete__(this, input, mResult);
return;
}
nsIContentParent* parent = TabParent::GetFrom(Manager())->Manager();
InfallibleTArray<PBlobParent*> blobs;
for (unsigned i = 0; i < aData.Length(); i++) {
MOZ_ASSERT(aData[i].mType == BlobImplOrString::eBlobImpl);
BlobParent* blobParent = parent->GetOrCreateActorForBlobImpl(aData[i].mBlobImpl);
for (unsigned i = 0; i < aBlobs.Length(); i++) {
BlobParent* blobParent = parent->GetOrCreateActorForBlobImpl(aBlobs[i]);
if (blobParent) {
blobs.AppendElement(blobParent);
}
}
InputBlobs inblobs;
InputFiles inblobs;
inblobs.blobsParent().SwapElements(blobs);
Unused << Send__delete__(this, inblobs, mResult);
}
@ -184,7 +138,7 @@ FilePickerParent::Done(int16_t aResult)
return;
}
nsTArray<nsCOMPtr<nsIFile>> files;
nsTArray<RefPtr<BlobImpl>> blobs;
if (mMode == nsIFilePicker::modeOpenMultiple) {
nsCOMPtr<nsISimpleEnumerator> iter;
NS_ENSURE_SUCCESS_VOID(mFilePicker->GetFiles(getter_AddRefs(iter)));
@ -195,26 +149,22 @@ FilePickerParent::Done(int16_t aResult)
iter->GetNext(getter_AddRefs(supports));
if (supports) {
nsCOMPtr<nsIFile> file = do_QueryInterface(supports);
MOZ_ASSERT(file);
files.AppendElement(file);
RefPtr<BlobImpl> blobimpl = new BlobImplFile(file);
blobs.AppendElement(blobimpl);
}
}
} else {
nsCOMPtr<nsIFile> file;
mFilePicker->GetFile(getter_AddRefs(file));
if (file) {
files.AppendElement(file);
RefPtr<BlobImpl> blobimpl = new BlobImplFile(file);
blobs.AppendElement(blobimpl);
}
}
if (files.IsEmpty()) {
Unused << Send__delete__(this, void_t(), mResult);
return;
}
MOZ_ASSERT(!mRunnable);
mRunnable = new IORunnable(this, files, mMode == nsIFilePicker::modeGetFolder);
mRunnable = new FileSizeAndDateRunnable(this, blobs);
// Dispatch to background thread to do I/O:
if (!mRunnable->Dispatch()) {
Unused << Send__delete__(this, void_t(), nsIFilePicker::returnCancel);

View File

@ -14,8 +14,6 @@
#include "mozilla/dom/File.h"
#include "mozilla/dom/PFilePickerParent.h"
class nsIFile;
namespace mozilla {
namespace dom {
@ -31,19 +29,7 @@ class FilePickerParent : public PFilePickerParent
virtual ~FilePickerParent();
void Done(int16_t aResult);
struct BlobImplOrString
{
RefPtr<BlobImpl> mBlobImpl;
nsString mDirectoryPath;
enum {
eBlobImpl,
eDirectoryPath
} mType;
};
void SendFilesOrDirectories(const nsTArray<BlobImplOrString>& aData);
void SendFiles(const nsTArray<RefPtr<BlobImpl>>& aDomBlobs);
virtual bool RecvOpen(const int16_t& aSelectedType,
const bool& aAddToRecentDocs,
@ -75,26 +61,21 @@ class FilePickerParent : public PFilePickerParent
private:
bool CreateFilePicker();
// This runnable is used to do some I/O operation on a separate thread.
class IORunnable : public nsRunnable
class FileSizeAndDateRunnable : public nsRunnable
{
FilePickerParent* mFilePickerParent;
nsTArray<nsCOMPtr<nsIFile>> mFiles;
nsTArray<BlobImplOrString> mResults;
nsTArray<RefPtr<BlobImpl>> mBlobs;
nsCOMPtr<nsIEventTarget> mEventTarget;
bool mIsDirectory;
public:
IORunnable(FilePickerParent *aFPParent,
nsTArray<nsCOMPtr<nsIFile>>& aFiles,
bool aIsDirectory);
FileSizeAndDateRunnable(FilePickerParent *aFPParent,
nsTArray<RefPtr<BlobImpl>>& aBlobs);
bool Dispatch();
NS_IMETHOD Run();
void Destroy();
};
RefPtr<IORunnable> mRunnable;
RefPtr<FileSizeAndDateRunnable> mRunnable;
RefPtr<FilePickerShownCallback> mCallback;
nsCOMPtr<nsIFilePicker> mFilePicker;

View File

@ -289,7 +289,6 @@ struct FileSystemGetDirectoryListingParams
{
nsString filesystem;
nsString realPath;
bool isRoot;
// 'filters' could be an array rather than a semicolon separated string
// (we'd then use InfallibleTArray<nsString> internally), but that is
// wasteful. E10s requires us to pass the filters over as a string anyway,
@ -306,7 +305,6 @@ struct FileSystemGetFileOrDirectoryParams
{
nsString filesystem;
nsString realPath;
bool isRoot;
};
union FileSystemPathOrFileValue

View File

@ -12,20 +12,14 @@ using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
namespace mozilla {
namespace dom {
struct InputBlobs
struct InputFiles
{
PBlob[] blobs;
};
struct InputDirectory
union MaybeInputFiles
{
nsString directoryPath;
};
union MaybeInputData
{
InputBlobs;
InputDirectory;
InputFiles;
void_t;
};
@ -39,7 +33,7 @@ parent:
nsString displayDirectory);
child:
async __delete__(MaybeInputData data, int16_t result);
async __delete__(MaybeInputFiles files, int16_t result);
};
} // namespace dom

View File

@ -20,7 +20,6 @@ interface Directory {
/*
* The leaf name of the directory.
*/
[Throws]
readonly attribute DOMString name;
/*
@ -106,13 +105,11 @@ partial interface Directory {
* to obtain this Directory. Full filesystem paths are not exposed to
* unprivilaged content.
*/
[Throws]
readonly attribute DOMString path;
/*
* Getter for the immediate children of this directory.
*/
[Throws]
Promise<sequence<(File or Directory)>> getFilesAndDirectories();
};

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

@ -155,10 +155,6 @@ partial interface HTMLInputElement {
[ChromeOnly]
void mozSetFileArray(sequence<File> files);
// This method is meant to use for testing only.
[ChromeOnly, Throws]
void mozSetDirectory(DOMString directoryPath);
// Number controls (<input type=number>) have an anonymous text control
// (<input type=text>) in the anonymous shadow tree that they contain. On
// such an anonymous text control this property provides access to the

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

View File

@ -17,7 +17,6 @@
#include "nsCOMArray.h"
#include "nsIFile.h"
#include "nsEnumeratorUtils.h"
#include "mozilla/dom/Directory.h"
#include "mozilla/dom/File.h"
#include "mozilla/Services.h"
#include "WidgetUtils.h"
@ -31,36 +30,6 @@ using namespace mozilla::dom;
#define FILEPICKER_TITLES "chrome://global/locale/filepicker.properties"
#define FILEPICKER_FILTERS "chrome://global/content/filepicker.properties"
namespace {
nsresult
LocalFileToDirectoryOrBlob(nsPIDOMWindowInner* aWindow,
bool aIsDirectory,
nsIFile* aFile,
nsISupports** aResult)
{
if (aIsDirectory) {
#ifdef DEBUG
bool isDir;
aFile->IsDirectory(&isDir);
MOZ_ASSERT(isDir);
#endif
RefPtr<Directory> directory =
Directory::Create(aWindow, aFile, Directory::eDOMRootDirectory);
MOZ_ASSERT(directory);
directory.forget(aResult);
return NS_OK;
}
nsCOMPtr<nsIDOMBlob> blob = File::CreateFromFile(aWindow, aFile);
blob.forget(aResult);
return NS_OK;
}
} // anonymous namespace
/**
* A runnable to dispatch from the main thread to the main thread to display
* the file picker while letting the showAsync method return right away.
@ -105,9 +74,9 @@ class nsBaseFilePickerEnumerator : public nsISimpleEnumerator
public:
NS_DECL_ISUPPORTS
nsBaseFilePickerEnumerator(nsPIDOMWindowOuter* aParent,
nsISimpleEnumerator* iterator,
int16_t aMode)
explicit nsBaseFilePickerEnumerator(nsPIDOMWindowOuter* aParent,
nsISimpleEnumerator* iterator,
int16_t aMode)
: mIterator(iterator)
, mParent(aParent->GetCurrentInnerWindow())
, mMode(aMode)
@ -129,10 +98,32 @@ public:
return NS_ERROR_FAILURE;
}
return LocalFileToDirectoryOrBlob(mParent,
mMode == nsIFilePicker::modeGetFolder,
localFile,
aResult);
RefPtr<File> domFile = File::CreateFromFile(mParent, localFile);
// Right now we're on the main thread of the chrome process. We need
// to call SetIsDirectory on the BlobImpl, but it's preferable not to
// call nsIFile::IsDirectory to determine what argument to pass since
// IsDirectory does synchronous I/O. It's true that since we've just
// been called synchronously directly after nsIFilePicker::Show blocked
// the main thread while the picker was being shown and the OS did file
// system access, doing more I/O to stat the selected files probably
// wouldn't be the end of the world. However, we can simply check
// mMode and avoid calling IsDirectory.
//
// In future we may take advantage of OS X's ability to allow both
// files and directories to be picked at the same time, so we do assert
// in debug builds that the mMode trick produces the correct results.
// If we do add that support maybe it's better to use IsDirectory
// directly, but in an nsRunnable punted off to a background thread.
#ifdef DEBUG
bool isDir;
localFile->IsDirectory(&isDir);
MOZ_ASSERT(isDir == (mMode == nsIFilePicker::modeGetFolder));
#endif
domFile->Impl()->SetIsDirectory(mMode == nsIFilePicker::modeGetFolder);
nsCOMPtr<nsIDOMBlob>(domFile).forget(aResult);
return NS_OK;
}
NS_IMETHOD
@ -358,10 +349,10 @@ nsBaseFilePicker::GetDomFileOrDirectory(nsISupports** aValue)
auto* innerParent = mParent ? mParent->GetCurrentInnerWindow() : nullptr;
return LocalFileToDirectoryOrBlob(innerParent,
mMode == nsIFilePicker::modeGetFolder,
localFile,
aValue);
RefPtr<File> domFile = File::CreateFromFile(innerParent, localFile);
domFile->Impl()->SetIsDirectory(mMode == nsIFilePicker::modeGetFolder);
nsCOMPtr<nsIDOMBlob>(domFile).forget(aValue);
return NS_OK;
}
NS_IMETHODIMP

View File

@ -7,7 +7,6 @@
#include "nsFilePickerProxy.h"
#include "nsComponentManagerUtils.h"
#include "nsIFile.h"
#include "mozilla/dom/Directory.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/TabChild.h"
#include "mozilla/dom/ipc/BlobChild.h"
@ -143,11 +142,11 @@ nsFilePickerProxy::Open(nsIFilePickerShownCallback* aCallback)
}
bool
nsFilePickerProxy::Recv__delete__(const MaybeInputData& aData,
nsFilePickerProxy::Recv__delete__(const MaybeInputFiles& aFiles,
const int16_t& aResult)
{
if (aData.type() == MaybeInputData::TInputBlobs) {
const InfallibleTArray<PBlobChild*>& blobs = aData.get_InputBlobs().blobsChild();
if (aFiles.type() == MaybeInputFiles::TInputFiles) {
const InfallibleTArray<PBlobChild*>& blobs = aFiles.get_InputFiles().blobsChild();
for (uint32_t i = 0; i < blobs.Length(); ++i) {
BlobChild* actor = static_cast<BlobChild*>(blobs[i]);
RefPtr<BlobImpl> blobImpl = actor->GetBlobImpl();
@ -162,24 +161,8 @@ nsFilePickerProxy::Recv__delete__(const MaybeInputData& aData,
RefPtr<File> file = File::Create(inner, blobImpl);
MOZ_ASSERT(file);
OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement();
element->SetAsFile() = file;
mFilesOrDirectories.AppendElement(file);
}
} else if (aData.type() == MaybeInputData::TInputDirectory) {
nsCOMPtr<nsIFile> file;
NS_ConvertUTF16toUTF8 path(aData.get_InputDirectory().directoryPath());
nsresult rv = NS_NewNativeLocalFile(path, true, getter_AddRefs(file));
if (NS_WARN_IF(NS_FAILED(rv))) {
return true;
}
RefPtr<Directory> directory =
Directory::Create(mParent->GetCurrentInnerWindow(), file,
Directory::eDOMRootDirectory);
MOZ_ASSERT(directory);
OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement();
element->SetAsDirectory() = directory;
}
if (mCallback) {
@ -199,16 +182,8 @@ nsFilePickerProxy::GetDomFileOrDirectory(nsISupports** aValue)
}
MOZ_ASSERT(mFilesOrDirectories.Length() == 1);
if (mFilesOrDirectories[0].IsFile()) {
nsCOMPtr<nsIDOMBlob> blob = mFilesOrDirectories[0].GetAsFile().get();
blob.forget(aValue);
return NS_OK;
}
MOZ_ASSERT(mFilesOrDirectories[0].IsDirectory());
RefPtr<Directory> directory = mFilesOrDirectories[0].GetAsDirectory();
directory.forget(aValue);
nsCOMPtr<nsIDOMBlob> blob = mFilesOrDirectories[0].get();
blob.forget(aValue);
return NS_OK;
}
@ -219,9 +194,8 @@ class SimpleEnumerator final : public nsISimpleEnumerator
public:
NS_DECL_ISUPPORTS
explicit
SimpleEnumerator(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories)
: mFilesOrDirectories(aFilesOrDirectories)
explicit SimpleEnumerator(const nsTArray<RefPtr<File>>& aFiles)
: mFiles(aFiles)
, mIndex(0)
{}
@ -229,26 +203,17 @@ public:
HasMoreElements(bool* aRetvalue) override
{
MOZ_ASSERT(aRetvalue);
*aRetvalue = mIndex < mFilesOrDirectories.Length();
*aRetvalue = mIndex < mFiles.Length();
return NS_OK;
}
NS_IMETHOD
GetNext(nsISupports** aValue) override
GetNext(nsISupports** aSupports) override
{
NS_ENSURE_TRUE(mIndex < mFilesOrDirectories.Length(), NS_ERROR_FAILURE);
NS_ENSURE_TRUE(mIndex < mFiles.Length(), NS_ERROR_FAILURE);
uint32_t index = mIndex++;
if (mFilesOrDirectories[index].IsFile()) {
nsCOMPtr<nsIDOMBlob> blob = mFilesOrDirectories[index].GetAsFile().get();
blob.forget(aValue);
return NS_OK;
}
MOZ_ASSERT(mFilesOrDirectories[index].IsDirectory());
RefPtr<Directory> directory = mFilesOrDirectories[index].GetAsDirectory();
directory.forget(aValue);
nsCOMPtr<nsIDOMBlob> blob = mFiles[mIndex++].get();
blob.forget(aSupports);
return NS_OK;
}
@ -256,7 +221,7 @@ private:
~SimpleEnumerator()
{}
nsTArray<mozilla::dom::OwningFileOrDirectory> mFilesOrDirectories;
nsTArray<RefPtr<File>> mFiles;
uint32_t mIndex;
};
@ -267,8 +232,7 @@ NS_IMPL_ISUPPORTS(SimpleEnumerator, nsISimpleEnumerator)
NS_IMETHODIMP
nsFilePickerProxy::GetDomFileOrDirectoryEnumerator(nsISimpleEnumerator** aDomfiles)
{
RefPtr<SimpleEnumerator> enumerator =
new SimpleEnumerator(mFilesOrDirectories);
RefPtr<SimpleEnumerator> enumerator = new SimpleEnumerator(mFilesOrDirectories);
enumerator.forget(aDomfiles);
return NS_OK;
}

View File

@ -13,12 +13,17 @@
#include "nsCOMArray.h"
#include "mozilla/dom/PFilePickerChild.h"
#include "mozilla/dom/UnionTypes.h"
class nsIWidget;
class nsIFile;
class nsPIDOMWindowInner;
namespace mozilla {
namespace dom {
class File;
} // namespace dom
} // namespace mozilla
/**
This class creates a proxy file picker to be used in content processes.
The file picker just collects the initialization data and when Show() is
@ -54,13 +59,13 @@ public:
// PFilePickerChild
virtual bool
Recv__delete__(const MaybeInputData& aData, const int16_t& aResult) override;
Recv__delete__(const MaybeInputFiles& aFiles, const int16_t& aResult) override;
private:
~nsFilePickerProxy();
void InitNative(nsIWidget*, const nsAString&) override;
nsTArray<mozilla::dom::OwningFileOrDirectory> mFilesOrDirectories;
nsTArray<RefPtr<mozilla::dom::File>> mFilesOrDirectories;
nsCOMPtr<nsIFilePickerShownCallback> mCallback;
int16_t mSelectedType;