mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-22 18:32:00 +00:00
merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
commit
0db9291841
@ -245,6 +245,7 @@ cairo-atsui.h
|
||||
cairo-beos.h
|
||||
cairo-ft.h
|
||||
cairo-glitz.h
|
||||
cairo-gobject.h
|
||||
cairo-pdf.h
|
||||
cairo-ps.h
|
||||
cairo-tee.h
|
||||
|
@ -227,12 +227,6 @@ Blob::IsFile() const
|
||||
return mImpl->IsFile();
|
||||
}
|
||||
|
||||
bool
|
||||
Blob::IsDirectory() const
|
||||
{
|
||||
return mImpl->IsDirectory();
|
||||
}
|
||||
|
||||
const nsTArray<RefPtr<BlobImpl>>*
|
||||
Blob::GetSubBlobImpls() const
|
||||
{
|
||||
@ -420,11 +414,10 @@ 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, BlobDirState aDirState)
|
||||
int64_t aLastModifiedDate)
|
||||
{
|
||||
RefPtr<File> file = new File(aParent,
|
||||
new BlobImplBase(aName, aContentType, aLength, aLastModifiedDate,
|
||||
aDirState));
|
||||
new BlobImplBase(aName, aContentType, aLength, aLastModifiedDate));
|
||||
return file.forget();
|
||||
}
|
||||
|
||||
@ -946,17 +939,6 @@ 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
|
||||
|
||||
|
@ -44,18 +44,6 @@ 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
|
||||
@ -101,12 +89,6 @@ 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
|
||||
@ -114,9 +96,6 @@ 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,
|
||||
@ -194,7 +173,7 @@ public:
|
||||
static already_AddRefed<File>
|
||||
Create(nsISupports* aParent, const nsAString& aName,
|
||||
const nsAString& aContentType, uint64_t aLength,
|
||||
int64_t aLastModifiedDate, BlobDirState aDirState);
|
||||
int64_t aLastModifiedDate);
|
||||
|
||||
// The returned File takes ownership of aMemoryBuffer. aMemoryBuffer will be
|
||||
// freed by free so it must be allocated by malloc or something
|
||||
@ -339,28 +318,6 @@ 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
|
||||
{
|
||||
@ -377,11 +334,9 @@ class BlobImplBase : public BlobImpl
|
||||
{
|
||||
public:
|
||||
BlobImplBase(const nsAString& aName, const nsAString& aContentType,
|
||||
uint64_t aLength, int64_t aLastModifiedDate,
|
||||
BlobDirState aDirState = BlobDirState::eUnknownIfDir)
|
||||
uint64_t aLength, int64_t aLastModifiedDate)
|
||||
: mIsFile(true)
|
||||
, mImmutable(false)
|
||||
, mDirState(aDirState)
|
||||
, mContentType(aContentType)
|
||||
, mName(aName)
|
||||
, mStart(0)
|
||||
@ -397,7 +352,6 @@ public:
|
||||
uint64_t aLength)
|
||||
: mIsFile(true)
|
||||
, mImmutable(false)
|
||||
, mDirState(BlobDirState::eUnknownIfDir)
|
||||
, mContentType(aContentType)
|
||||
, mName(aName)
|
||||
, mStart(0)
|
||||
@ -412,7 +366,6 @@ public:
|
||||
BlobImplBase(const nsAString& aContentType, uint64_t aLength)
|
||||
: mIsFile(false)
|
||||
, mImmutable(false)
|
||||
, mDirState(BlobDirState::eUnknownIfDir)
|
||||
, mContentType(aContentType)
|
||||
, mStart(0)
|
||||
, mLength(aLength)
|
||||
@ -427,7 +380,6 @@ public:
|
||||
uint64_t aLength)
|
||||
: mIsFile(false)
|
||||
, mImmutable(false)
|
||||
, mDirState(BlobDirState::eUnknownIfDir)
|
||||
, mContentType(aContentType)
|
||||
, mStart(aStart)
|
||||
, mLength(aLength)
|
||||
@ -518,36 +470,6 @@ 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;
|
||||
@ -565,7 +487,6 @@ protected:
|
||||
|
||||
bool mIsFile;
|
||||
bool mImmutable;
|
||||
BlobDirState mDirState;
|
||||
|
||||
nsString mContentType;
|
||||
nsString mName;
|
||||
@ -590,8 +511,7 @@ public:
|
||||
|
||||
BlobImplMemory(void* aMemoryBuffer, uint64_t aLength, const nsAString& aName,
|
||||
const nsAString& aContentType, int64_t aLastModifiedDate)
|
||||
: BlobImplBase(aName, aContentType, aLength, aLastModifiedDate,
|
||||
BlobDirState::eIsNotDir)
|
||||
: BlobImplBase(aName, aContentType, aLength, aLastModifiedDate)
|
||||
, mDataOwner(new DataOwner(aMemoryBuffer, aLength))
|
||||
{
|
||||
NS_ASSERTION(mDataOwner && mDataOwner->mData, "must have data");
|
||||
@ -792,8 +712,6 @@ 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; }
|
||||
|
@ -4,6 +4,7 @@
|
||||
* 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"
|
||||
@ -11,7 +12,7 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileList, mFiles, mParent)
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileList, mFilesOrDirectories, mParent)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileList)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
@ -28,6 +29,20 @@ 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)
|
||||
{
|
||||
@ -37,12 +52,76 @@ FileList::GetLength(uint32_t* aLength)
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FileList::Item(uint32_t aIndex, nsISupports** aFile)
|
||||
FileList::Item(uint32_t aIndex, nsISupports** aValue)
|
||||
{
|
||||
nsCOMPtr<nsIDOMBlob> file = Item(aIndex);
|
||||
file.forget(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);
|
||||
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
|
||||
|
@ -8,6 +8,7 @@
|
||||
#define mozilla_dom_FileList_h
|
||||
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/UnionTypes.h"
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsIDOMFileList.h"
|
||||
#include "nsWrapperCache.h"
|
||||
@ -39,15 +40,13 @@ public:
|
||||
return mParent;
|
||||
}
|
||||
|
||||
bool Append(File* aFile)
|
||||
{
|
||||
return mFiles.AppendElement(aFile);
|
||||
}
|
||||
void Append(File* aFile);
|
||||
void Append(Directory* aDirectory);
|
||||
|
||||
bool Remove(uint32_t aIndex)
|
||||
{
|
||||
if (aIndex < mFiles.Length()) {
|
||||
mFiles.RemoveElementAt(aIndex);
|
||||
if (aIndex < mFilesOrDirectories.Length()) {
|
||||
mFilesOrDirectories.RemoveElementAt(aIndex);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -56,7 +55,7 @@ public:
|
||||
|
||||
void Clear()
|
||||
{
|
||||
return mFiles.Clear();
|
||||
return mFilesOrDirectories.Clear();
|
||||
}
|
||||
|
||||
static FileList* FromSupports(nsISupports* aSupports)
|
||||
@ -76,29 +75,34 @@ public:
|
||||
return static_cast<FileList*>(aSupports);
|
||||
}
|
||||
|
||||
File* Item(uint32_t aIndex)
|
||||
const OwningFileOrDirectory& UnsafeItem(uint32_t aIndex) const
|
||||
{
|
||||
return mFiles.SafeElementAt(aIndex);
|
||||
MOZ_ASSERT(aIndex < Length());
|
||||
return mFilesOrDirectories[aIndex];
|
||||
}
|
||||
|
||||
File* IndexedGetter(uint32_t aIndex, bool& aFound)
|
||||
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
|
||||
{
|
||||
aFound = aIndex < mFiles.Length();
|
||||
if (!aFound) {
|
||||
return nullptr;
|
||||
}
|
||||
return mFiles.ElementAt(aIndex);
|
||||
return mFilesOrDirectories.Length();
|
||||
}
|
||||
|
||||
uint32_t Length()
|
||||
{
|
||||
return mFiles.Length();
|
||||
}
|
||||
void ToSequence(Sequence<OwningFileOrDirectory>& aSequence,
|
||||
ErrorResult& aRv) const;
|
||||
|
||||
bool ClonableToDifferentThreadOrProcess() const;
|
||||
|
||||
private:
|
||||
~FileList() {}
|
||||
|
||||
nsTArray<RefPtr<File>> mFiles;
|
||||
nsTArray<OwningFileOrDirectory> mFilesOrDirectories;
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
};
|
||||
|
||||
|
@ -10,6 +10,7 @@
|
||||
#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"
|
||||
@ -723,36 +724,59 @@ ReadFileList(JSContext* aCx,
|
||||
{
|
||||
RefPtr<FileList> fileList = new FileList(aHolder->ParentDuringRead());
|
||||
|
||||
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|.
|
||||
// |aCount| is the number of Files or Directory for this FileList.
|
||||
for (uint32_t i = 0; i < aCount; ++i) {
|
||||
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();
|
||||
uint32_t tagOrDirectoryType, indexOrLengthOfString;
|
||||
if (!JS_ReadUint32Pair(aReader, &tagOrDirectoryType,
|
||||
&indexOrLengthOfString)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(blobImpl);
|
||||
MOZ_ASSERT(tagOrDirectoryType == SCTAG_DOM_BLOB ||
|
||||
tagOrDirectoryType == Directory::eDOMRootDirectory ||
|
||||
tagOrDirectoryType == Directory::eNotDOMRootDirectory);
|
||||
|
||||
RefPtr<File> file = File::Create(aHolder->ParentDuringRead(), blobImpl);
|
||||
if (!fileList->Append(file)) {
|
||||
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)) {
|
||||
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)) {
|
||||
@ -765,7 +789,13 @@ ReadFileList(JSContext* aCx,
|
||||
|
||||
// The format of the FileList serialization is:
|
||||
// - pair of ints: SCTAG_DOM_FILELIST, Length of the FileList
|
||||
// - pair of ints: 0, The offset of the BlobImpl array
|
||||
// - 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
|
||||
bool
|
||||
WriteFileList(JSStructuredCloneWriter* aWriter,
|
||||
FileList* aFileList,
|
||||
@ -775,13 +805,8 @@ 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()) ||
|
||||
!JS_WriteUint32Pair(aWriter, 0,
|
||||
aHolder->BlobImpls().Length())) {
|
||||
aFileList->Length())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -789,18 +814,39 @@ WriteFileList(JSStructuredCloneWriter* aWriter,
|
||||
nsTArray<RefPtr<BlobImpl>> blobImpls;
|
||||
|
||||
for (uint32_t i = 0; i < aFileList->Length(); ++i) {
|
||||
RefPtr<BlobImpl> blobImpl =
|
||||
EnsureBlobForBackgroundManager(aFileList->Item(i)->Impl(), nullptr, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
rv.SuppressException();
|
||||
return false;
|
||||
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(blobImpl);
|
||||
blobImpls.AppendElement(blobImpl);
|
||||
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)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
aHolder->BlobImpls().AppendElements(blobImpls);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1004,7 +1050,9 @@ StructuredCloneHolder::CustomWriteHandler(JSContext* aCx,
|
||||
// See if this is a FileList object.
|
||||
{
|
||||
FileList* fileList = nullptr;
|
||||
if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, aObj, fileList))) {
|
||||
if (NS_SUCCEEDED(UNWRAP_OBJECT(FileList, aObj, fileList)) &&
|
||||
(mSupportedContext == SameProcessSameThread ||
|
||||
fileList->ClonableToDifferentThreadOrProcess())) {
|
||||
return WriteFileList(aWriter, fileList, this);
|
||||
}
|
||||
}
|
||||
|
@ -7625,7 +7625,6 @@ 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.
|
||||
|
@ -1,15 +1,25 @@
|
||||
var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
Cu.importGlobalProperties(["File"]);
|
||||
|
||||
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 () {
|
||||
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
|
||||
});
|
||||
});
|
||||
|
@ -61,18 +61,18 @@ function compare(a, b) {
|
||||
}
|
||||
|
||||
var clonableObjects = [
|
||||
'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),
|
||||
{ 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) },
|
||||
];
|
||||
|
||||
function create_fileList() {
|
||||
function create_fileList_forFile() {
|
||||
var url = SimpleTest.getTestFileURL("script_postmessages_fileList.js");
|
||||
var script = SpecialPowers.loadChromeScript(url);
|
||||
|
||||
@ -84,7 +84,7 @@ function create_fileList() {
|
||||
var domFile = fileList.files[0];
|
||||
is(domFile.name, "prefs.js", "fileName should be prefs.js");
|
||||
|
||||
clonableObjects.push(fileList.files);
|
||||
clonableObjects.push({ crossThreads: true, data: fileList.files });
|
||||
script.destroy();
|
||||
next();
|
||||
}
|
||||
@ -93,6 +93,27 @@ function create_fileList() {
|
||||
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) &&
|
||||
@ -113,8 +134,16 @@ function runTests(obj) {
|
||||
}
|
||||
|
||||
var object = clonableObjects[clonableObjectsId++];
|
||||
obj.send(object, []).then(function(received) {
|
||||
compare(received.data, object);
|
||||
|
||||
// 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);
|
||||
runClonableTest();
|
||||
});
|
||||
}
|
||||
@ -203,6 +232,7 @@ function test_windowToWindow() {
|
||||
runTests({
|
||||
clonableObjects: true,
|
||||
transferableObjects: true,
|
||||
crossThread: false,
|
||||
send: function(what, ports) {
|
||||
return new Promise(function(r, rr) {
|
||||
resolve = r;
|
||||
@ -256,6 +286,7 @@ function test_windowToIframeURL(url) {
|
||||
runTests({
|
||||
clonableObjects: true,
|
||||
transferableObjects: true,
|
||||
crossThread: false,
|
||||
send: function(what, ports) {
|
||||
return new Promise(function(r, rr) {
|
||||
resolve = r;
|
||||
@ -303,6 +334,7 @@ function test_workers() {
|
||||
runTests({
|
||||
clonableObjects: true,
|
||||
transferableObjects: true,
|
||||
crossThread: true,
|
||||
send: function(what, ports) {
|
||||
return new Promise(function(r, rr) {
|
||||
resolve = r;
|
||||
@ -346,6 +378,7 @@ function test_broadcastChannel() {
|
||||
runTests({
|
||||
clonableObjects: true,
|
||||
transferableObjects: false,
|
||||
crossThread: true,
|
||||
send: function(what, ports) {
|
||||
return new Promise(function(r, rr) {
|
||||
if (ports.length) {
|
||||
@ -391,6 +424,7 @@ function test_broadcastChannel_inWorkers() {
|
||||
runTests({
|
||||
clonableObjects: true,
|
||||
transferableObjects: false,
|
||||
crossThread: true,
|
||||
send: function(what, ports) {
|
||||
return new Promise(function(r, rr) {
|
||||
if (ports.length) {
|
||||
@ -432,6 +466,7 @@ function test_messagePort() {
|
||||
runTests({
|
||||
clonableObjects: true,
|
||||
transferableObjects: true,
|
||||
crossThread: true,
|
||||
send: function(what, ports) {
|
||||
return new Promise(function(r, rr) {
|
||||
resolve = r;
|
||||
@ -477,6 +512,7 @@ function test_messagePort_inWorkers() {
|
||||
runTests({
|
||||
clonableObjects: true,
|
||||
transferableObjects: true,
|
||||
crossThread: true,
|
||||
send: function(what, ports) {
|
||||
return new Promise(function(r, rr) {
|
||||
resolve = r;
|
||||
@ -498,7 +534,8 @@ function test_messagePort_inWorkers() {
|
||||
}
|
||||
|
||||
var tests = [
|
||||
create_fileList,
|
||||
create_fileList_forFile,
|
||||
create_fileList_forDir,
|
||||
|
||||
test_windowToWindow,
|
||||
test_windowToIframe,
|
||||
|
@ -94,6 +94,10 @@ WebGL2Context::ClearBufferfv_base(GLenum buffer, GLint drawbuffer, const GLfloat
|
||||
void
|
||||
WebGL2Context::ClearBufferiv(GLenum buffer, GLint drawbuffer, const dom::Int32Array& value)
|
||||
{
|
||||
if (IsContextLost()) {
|
||||
return;
|
||||
}
|
||||
|
||||
value.ComputeLengthAndData();
|
||||
if (!ValidateClearBuffer("clearBufferiv", buffer, drawbuffer, value.Length())) {
|
||||
return;
|
||||
@ -105,6 +109,10 @@ WebGL2Context::ClearBufferiv(GLenum buffer, GLint drawbuffer, const dom::Int32Ar
|
||||
void
|
||||
WebGL2Context::ClearBufferiv(GLenum buffer, GLint drawbuffer, const dom::Sequence<GLint>& value)
|
||||
{
|
||||
if (IsContextLost()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ValidateClearBuffer("clearBufferiv", buffer, drawbuffer, value.Length())) {
|
||||
return;
|
||||
}
|
||||
@ -115,6 +123,10 @@ WebGL2Context::ClearBufferiv(GLenum buffer, GLint drawbuffer, const dom::Sequenc
|
||||
void
|
||||
WebGL2Context::ClearBufferuiv(GLenum buffer, GLint drawbuffer, const dom::Uint32Array& value)
|
||||
{
|
||||
if (IsContextLost()) {
|
||||
return;
|
||||
}
|
||||
|
||||
value.ComputeLengthAndData();
|
||||
if (!ValidateClearBuffer("clearBufferuiv", buffer, drawbuffer, value.Length())) {
|
||||
return;
|
||||
@ -126,6 +138,10 @@ WebGL2Context::ClearBufferuiv(GLenum buffer, GLint drawbuffer, const dom::Uint32
|
||||
void
|
||||
WebGL2Context::ClearBufferuiv(GLenum buffer, GLint drawbuffer, const dom::Sequence<GLuint>& value)
|
||||
{
|
||||
if (IsContextLost()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ValidateClearBuffer("clearBufferuiv", buffer, drawbuffer, value.Length())) {
|
||||
return;
|
||||
}
|
||||
@ -136,6 +152,10 @@ WebGL2Context::ClearBufferuiv(GLenum buffer, GLint drawbuffer, const dom::Sequen
|
||||
void
|
||||
WebGL2Context::ClearBufferfv(GLenum buffer, GLint drawbuffer, const dom::Float32Array& value)
|
||||
{
|
||||
if (IsContextLost()) {
|
||||
return;
|
||||
}
|
||||
|
||||
value.ComputeLengthAndData();
|
||||
if (!ValidateClearBuffer("clearBufferfv", buffer, drawbuffer, value.Length())) {
|
||||
return;
|
||||
@ -147,6 +167,10 @@ WebGL2Context::ClearBufferfv(GLenum buffer, GLint drawbuffer, const dom::Float32
|
||||
void
|
||||
WebGL2Context::ClearBufferfv(GLenum buffer, GLint drawbuffer, const dom::Sequence<GLfloat>& value)
|
||||
{
|
||||
if (IsContextLost()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ValidateClearBuffer("clearBufferfv", buffer, drawbuffer, value.Length())) {
|
||||
return;
|
||||
}
|
||||
@ -157,6 +181,10 @@ WebGL2Context::ClearBufferfv(GLenum buffer, GLint drawbuffer, const dom::Sequenc
|
||||
void
|
||||
WebGL2Context::ClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth, GLint stencil)
|
||||
{
|
||||
if (IsContextLost()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (buffer != LOCAL_GL_DEPTH_STENCIL) {
|
||||
return ErrorInvalidEnumInfo("clearBufferfi: buffer", buffer);
|
||||
}
|
||||
|
@ -624,6 +624,12 @@ WebGLFramebuffer::FramebufferTexture2D(GLenum attachment, TexImageTarget texImag
|
||||
return;
|
||||
|
||||
if (tex) {
|
||||
if (!tex->HasEverBeenBound()) {
|
||||
mContext->ErrorInvalidOperation("framebufferTexture2D: the texture"
|
||||
" is not the name of a texture.");
|
||||
return;
|
||||
}
|
||||
|
||||
bool isTexture2D = tex->Target() == LOCAL_GL_TEXTURE_2D;
|
||||
bool isTexTarget2D = texImageTarget == LOCAL_GL_TEXTURE_2D;
|
||||
if (isTexture2D != isTexTarget2D) {
|
||||
|
@ -101,8 +101,9 @@ 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();
|
||||
bool IsSafePath(const nsAString& aPath);
|
||||
bool IsSafePath() const;
|
||||
bool ValidateAndSplitPath(const nsAString& aPath,
|
||||
nsTArray<nsString>* aParts = nullptr) const;
|
||||
|
||||
void Dump(const char* label);
|
||||
|
||||
@ -137,7 +138,6 @@ public:
|
||||
private:
|
||||
~DeviceStorageFile() {}
|
||||
void Init();
|
||||
void NormalizeFilePath();
|
||||
void AppendRelativePath(const nsAString& aPath);
|
||||
void AccumDirectoryUsage(nsIFile* aFile,
|
||||
uint64_t* aPicturesSoFar,
|
||||
|
@ -582,7 +582,16 @@ DeviceStorageStatics::ResetOverrideRootDir()
|
||||
nsCOMPtr<nsIFile> f;
|
||||
DS_LOG_INFO("");
|
||||
|
||||
if (Preferences::GetBool(kPrefTesting, false)) {
|
||||
// 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)) {
|
||||
DS_LOG_INFO("temp");
|
||||
nsCOMPtr<nsIProperties> dirService
|
||||
= do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
|
||||
@ -592,15 +601,6 @@ 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) {
|
||||
|
@ -30,6 +30,7 @@
|
||||
|
||||
#include "nsArrayUtils.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCharSeparatedTokenizer.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsIFile.h"
|
||||
@ -76,10 +77,35 @@ 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;
|
||||
|
||||
@ -510,7 +536,8 @@ DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType,
|
||||
if (!mPath.EqualsLiteral("")) {
|
||||
AppendRelativePath(mPath);
|
||||
}
|
||||
NormalizeFilePath();
|
||||
|
||||
NormalizeFilePath(mPath);
|
||||
}
|
||||
|
||||
DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType,
|
||||
@ -525,7 +552,7 @@ DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType,
|
||||
{
|
||||
Init();
|
||||
AppendRelativePath(aPath);
|
||||
NormalizeFilePath();
|
||||
NormalizeFilePath(mPath);
|
||||
}
|
||||
|
||||
DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType,
|
||||
@ -734,7 +761,7 @@ DeviceStorageFile::CreateUnique(const nsAString& aStorageType,
|
||||
void
|
||||
DeviceStorageFile::SetPath(const nsAString& aPath) {
|
||||
mPath.Assign(aPath);
|
||||
NormalizeFilePath();
|
||||
NormalizeFilePath(mPath);
|
||||
}
|
||||
|
||||
void
|
||||
@ -745,13 +772,14 @@ 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()
|
||||
DeviceStorageFile::IsSafePath() const
|
||||
{
|
||||
return IsSafePath(mRootDir) && IsSafePath(mPath);
|
||||
return ValidateAndSplitPath(mRootDir) && ValidateAndSplitPath(mPath);
|
||||
}
|
||||
|
||||
bool
|
||||
DeviceStorageFile::IsSafePath(const nsAString& aPath)
|
||||
DeviceStorageFile::ValidateAndSplitPath(const nsAString& aPath,
|
||||
nsTArray<nsString>* aParts) const
|
||||
{
|
||||
nsAString::const_iterator start, end;
|
||||
aPath.BeginReading(start);
|
||||
@ -764,33 +792,43 @@ DeviceStorageFile::IsSafePath(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;
|
||||
}
|
||||
|
||||
while ((token = nsCRT::strtok(buffer, "/", &buffer))) {
|
||||
if (PL_strcmp(token, "") == 0 ||
|
||||
PL_strcmp(token, ".") == 0 ||
|
||||
PL_strcmp(token, "..") == 0 ) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (aParts) {
|
||||
aParts->AppendElement(pathComponent);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
DeviceStorageFile::NormalizeFilePath() {
|
||||
FileSystemUtils::LocalPathToNormalizedPath(mPath, mPath);
|
||||
}
|
||||
|
||||
void
|
||||
DeviceStorageFile::AppendRelativePath(const nsAString& aPath) {
|
||||
DeviceStorageFile::AppendRelativePath(const nsAString& aPath)
|
||||
{
|
||||
if (!mFile) {
|
||||
return;
|
||||
}
|
||||
if (!IsSafePath(aPath)) {
|
||||
|
||||
nsTArray<nsString> parts;
|
||||
|
||||
if (!ValidateAndSplitPath(aPath, &parts)) {
|
||||
// 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
|
||||
@ -800,9 +838,13 @@ DeviceStorageFile::AppendRelativePath(const nsAString& aPath) {
|
||||
NS_WARNING(NS_LossyConvertUTF16toASCII(aPath).get());
|
||||
return;
|
||||
}
|
||||
nsString localPath;
|
||||
FileSystemUtils::NormalizedPathToLocalPath(aPath, localPath);
|
||||
mFile->AppendRelativePath(localPath);
|
||||
|
||||
for (uint32_t i = 0; i < parts.Length(); ++i) {
|
||||
nsresult rv = mFile->AppendRelativePath(parts[i]);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -20,12 +20,20 @@ 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);
|
||||
});
|
||||
|
@ -34,14 +34,14 @@ function testCreateDirectory(rootDir, path) {
|
||||
}
|
||||
|
||||
function createDirectorySuccess(d) {
|
||||
ok(d.name === gName, "Failed to create directory: name mismatch.");
|
||||
is(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) {
|
||||
ok(d.name === gName, "Should get directory - " + (gPath || "[root]") + ".");
|
||||
is(d.name, gName, "Should get directory - " + (gPath || "[root]") + ".");
|
||||
switch (gTestCount) {
|
||||
case 0:
|
||||
gRoot = d;
|
||||
|
@ -149,8 +149,7 @@ 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],
|
||||
["device.storage.testing", false]]},
|
||||
SpecialPowers.pushPrefEnv({'set': [["device.storage.overrideRootDir", f.path]]},
|
||||
function() {
|
||||
startTest();
|
||||
});
|
||||
|
@ -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(mFiles)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFileList)
|
||||
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(mFiles)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFileList)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mItems)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragTarget)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragImage)
|
||||
@ -281,11 +281,12 @@ DataTransfer::GetMozUserCancelled(bool* aUserCancelled)
|
||||
FileList*
|
||||
DataTransfer::GetFiles(ErrorResult& aRv)
|
||||
{
|
||||
return GetFilesInternal(aRv, nsContentUtils::SubjectPrincipal());
|
||||
return GetFileListInternal(aRv, nsContentUtils::SubjectPrincipal());
|
||||
}
|
||||
|
||||
FileList*
|
||||
DataTransfer::GetFilesInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal)
|
||||
DataTransfer::GetFileListInternal(ErrorResult& aRv,
|
||||
nsIPrincipal* aSubjectPrincipal)
|
||||
{
|
||||
if (mEventMessage != eDrop &&
|
||||
mEventMessage != eLegacyDragDrop &&
|
||||
@ -293,14 +294,15 @@ DataTransfer::GetFilesInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!mFiles) {
|
||||
mFiles = new FileList(static_cast<nsIDOMDataTransfer*>(this));
|
||||
if (!mFileList) {
|
||||
mFileList = 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;
|
||||
}
|
||||
@ -338,21 +340,18 @@ DataTransfer::GetFilesInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal
|
||||
MOZ_ASSERT(domFile);
|
||||
}
|
||||
|
||||
if (!mFiles->Append(domFile)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
mFileList->Append(domFile);
|
||||
}
|
||||
}
|
||||
|
||||
return mFiles;
|
||||
return mFileList;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
DataTransfer::GetFiles(nsIDOMFileList** aFileList)
|
||||
{
|
||||
ErrorResult rv;
|
||||
NS_IF_ADDREF(*aFileList = GetFilesInternal(rv, nsContentUtils::GetSystemPrincipal()));
|
||||
NS_IF_ADDREF(*aFileList = GetFileListInternal(rv, nsContentUtils::GetSystemPrincipal()));
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
|
||||
@ -854,7 +853,7 @@ DataTransfer::GetFilesAndDirectories(ErrorResult& aRv)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!mFiles) {
|
||||
if (!mFileList) {
|
||||
GetFiles(aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
@ -862,40 +861,9 @@ DataTransfer::GetFilesAndDirectories(ErrorResult& aRv)
|
||||
}
|
||||
|
||||
Sequence<OwningFileOrDirectory> filesAndDirsSeq;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
mFileList->ToSequence(filesAndDirsSeq, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
p->MaybeResolve(filesAndDirsSeq);
|
||||
|
@ -252,7 +252,7 @@ protected:
|
||||
void FillInExternalData(TransferItem& aItem, uint32_t aIndex);
|
||||
|
||||
|
||||
FileList* GetFilesInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal);
|
||||
FileList* GetFileListInternal(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,8 +300,9 @@ protected:
|
||||
// array of items, each containing an array of format->data pairs
|
||||
nsTArray<nsTArray<TransferItem> > mItems;
|
||||
|
||||
// array of files, containing only the files present in the dataTransfer
|
||||
RefPtr<FileList> mFiles;
|
||||
// array of files and directories, containing only the files present in the
|
||||
// dataTransfer
|
||||
RefPtr<FileList> mFileList;
|
||||
|
||||
// the target of the drag. The drag and dragend events will fire at this.
|
||||
nsCOMPtr<mozilla::dom::Element> mDragTarget;
|
||||
|
@ -318,8 +318,7 @@ ConvertActorToFile(FileHandleBase* aFileHandle,
|
||||
actor->SetMysteryBlobInfo(mutableFile->Name(),
|
||||
mutableFile->Type(),
|
||||
size.get_uint64_t(),
|
||||
lastModified.get_int64_t(),
|
||||
BlobDirState::eUnknownIfDir);
|
||||
lastModified.get_int64_t());
|
||||
|
||||
RefPtr<BlobImpl> blobImpl = actor->GetBlobImpl();
|
||||
MOZ_ASSERT(blobImpl);
|
||||
|
@ -16,33 +16,73 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
CreateDirectoryTask::CreateDirectoryTask(FileSystemBase* aFileSystem,
|
||||
const nsAString& aPath,
|
||||
ErrorResult& aRv)
|
||||
: FileSystemTaskBase(aFileSystem)
|
||||
, mTargetRealPath(aPath)
|
||||
/* 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->GetWindow());
|
||||
if (!globalObject) {
|
||||
return;
|
||||
do_QueryInterface(aFileSystem->GetParentObject());
|
||||
if (NS_WARN_IF(!globalObject)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
mPromise = Promise::Create(globalObject, aRv);
|
||||
|
||||
task->mPromise = Promise::Create(globalObject, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return task.forget();
|
||||
}
|
||||
|
||||
CreateDirectoryTask::CreateDirectoryTask(
|
||||
FileSystemBase* aFileSystem,
|
||||
const FileSystemCreateDirectoryParams& aParam,
|
||||
FileSystemRequestParent* aParent)
|
||||
: FileSystemTaskBase(aFileSystem, aParam, aParent)
|
||||
/* 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(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)
|
||||
: FileSystemTaskBase(aFileSystem)
|
||||
, mTargetPath(aTargetPath)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(aFileSystem);
|
||||
}
|
||||
|
||||
CreateDirectoryTask::CreateDirectoryTask(FileSystemBase* aFileSystem,
|
||||
const FileSystemCreateDirectoryParams& aParam,
|
||||
FileSystemRequestParent* aParent)
|
||||
: FileSystemTaskBase(aFileSystem, aParam, aParent)
|
||||
{
|
||||
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()
|
||||
@ -59,25 +99,44 @@ CreateDirectoryTask::GetPromise()
|
||||
}
|
||||
|
||||
FileSystemParams
|
||||
CreateDirectoryTask::GetRequestParams(const nsString& aFileSystem) const
|
||||
CreateDirectoryTask::GetRequestParams(const nsString& aSerializedDOMPath,
|
||||
ErrorResult& aRv) const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
return FileSystemCreateDirectoryParams(aFileSystem, mTargetRealPath);
|
||||
|
||||
nsAutoString path;
|
||||
aRv = mTargetPath->GetPath(path);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return FileSystemCreateDirectoryParams();
|
||||
}
|
||||
|
||||
return FileSystemCreateDirectoryParams(aSerializedDOMPath, path);
|
||||
}
|
||||
|
||||
FileSystemResponseValue
|
||||
CreateDirectoryTask::GetSuccessRequestResult() const
|
||||
CreateDirectoryTask::GetSuccessRequestResult(ErrorResult& aRv) const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
return FileSystemDirectoryResponse(mTargetRealPath);
|
||||
|
||||
nsAutoString path;
|
||||
aRv = mTargetPath->GetPath(path);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return FileSystemDirectoryResponse();
|
||||
}
|
||||
|
||||
return FileSystemDirectoryResponse(path);
|
||||
}
|
||||
|
||||
void
|
||||
CreateDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue)
|
||||
CreateDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
FileSystemDirectoryResponse r = aValue;
|
||||
mTargetRealPath = r.realPath();
|
||||
|
||||
NS_ConvertUTF16toUTF8 path(r.realPath());
|
||||
aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath));
|
||||
NS_WARN_IF(aRv.Failed());
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -91,13 +150,8 @@ 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 = file->Exists(&fileExists);
|
||||
nsresult rv = mTargetPath->Exists(&fileExists);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@ -106,8 +160,12 @@ CreateDirectoryTask::Work()
|
||||
return NS_ERROR_DOM_FILESYSTEM_PATH_EXISTS_ERR;
|
||||
}
|
||||
|
||||
rv = file->Create(nsIFile::DIRECTORY_TYPE, 0770);
|
||||
return rv;
|
||||
rv = mTargetPath->Create(nsIFile::DIRECTORY_TYPE, 0770);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
@ -124,7 +182,12 @@ CreateDirectoryTask::HandlerCallback()
|
||||
mPromise = nullptr;
|
||||
return;
|
||||
}
|
||||
RefPtr<Directory> dir = new Directory(mFileSystem, mTargetRealPath);
|
||||
RefPtr<Directory> dir = Directory::Create(mFileSystem->GetParentObject(),
|
||||
mTargetPath,
|
||||
Directory::eNotDOMRootDirectory,
|
||||
mFileSystem);
|
||||
MOZ_ASSERT(dir);
|
||||
|
||||
mPromise->MaybeResolve(dir);
|
||||
mPromise = nullptr;
|
||||
}
|
||||
|
@ -16,16 +16,19 @@ namespace dom {
|
||||
|
||||
class Promise;
|
||||
|
||||
class CreateDirectoryTask final
|
||||
: public FileSystemTaskBase
|
||||
class CreateDirectoryTask final : public FileSystemTaskBase
|
||||
{
|
||||
public:
|
||||
CreateDirectoryTask(FileSystemBase* aFileSystem,
|
||||
const nsAString& aPath,
|
||||
ErrorResult& aRv);
|
||||
CreateDirectoryTask(FileSystemBase* aFileSystem,
|
||||
const FileSystemCreateDirectoryParams& aParam,
|
||||
FileSystemRequestParent* aParent);
|
||||
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);
|
||||
|
||||
virtual
|
||||
~CreateDirectoryTask();
|
||||
@ -38,13 +41,15 @@ public:
|
||||
|
||||
protected:
|
||||
virtual FileSystemParams
|
||||
GetRequestParams(const nsString& aFileSystem) const override;
|
||||
GetRequestParams(const nsString& aSerializedDOMPath,
|
||||
ErrorResult& aRv) const override;
|
||||
|
||||
virtual FileSystemResponseValue
|
||||
GetSuccessRequestResult() const override;
|
||||
GetSuccessRequestResult(ErrorResult& aRv) const override;
|
||||
|
||||
virtual void
|
||||
SetSuccessRequestResult(const FileSystemResponseValue& aValue) override;
|
||||
SetSuccessRequestResult(const FileSystemResponseValue& aValue,
|
||||
ErrorResult& aRv) override;
|
||||
|
||||
virtual nsresult
|
||||
Work() override;
|
||||
@ -53,8 +58,15 @@ protected:
|
||||
HandlerCallback() override;
|
||||
|
||||
private:
|
||||
CreateDirectoryTask(FileSystemBase* aFileSystem,
|
||||
nsIFile* aTargetPath);
|
||||
|
||||
CreateDirectoryTask(FileSystemBase* aFileSystem,
|
||||
const FileSystemCreateDirectoryParams& aParam,
|
||||
FileSystemRequestParent* aParent);
|
||||
|
||||
RefPtr<Promise> mPromise;
|
||||
nsString mTargetRealPath;
|
||||
nsCOMPtr<nsIFile> mTargetPath;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -25,34 +25,104 @@ namespace dom {
|
||||
|
||||
uint32_t CreateFileTask::sOutputBufferSize = 0;
|
||||
|
||||
CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem,
|
||||
const nsAString& aPath,
|
||||
Blob* aBlobData,
|
||||
InfallibleTArray<uint8_t>& aArrayData,
|
||||
bool replace,
|
||||
ErrorResult& aRv)
|
||||
: FileSystemTaskBase(aFileSystem)
|
||||
, mTargetRealPath(aPath)
|
||||
, mReplace(replace)
|
||||
/* static */ already_AddRefed<CreateFileTask>
|
||||
CreateFileTask::Create(FileSystemBase* aFileSystem,
|
||||
nsIFile* aTargetPath,
|
||||
Blob* aBlobData,
|
||||
InfallibleTArray<uint8_t>& aArrayData,
|
||||
bool aReplace,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(aFileSystem);
|
||||
GetOutputBufferSize();
|
||||
|
||||
RefPtr<CreateFileTask> task =
|
||||
new CreateFileTask(aFileSystem, aTargetPath, aReplace);
|
||||
|
||||
// aTargetPath can be null. In this case SetError will be called.
|
||||
|
||||
task->GetOutputBufferSize();
|
||||
|
||||
if (aBlobData) {
|
||||
if (XRE_IsParentProcess()) {
|
||||
aBlobData->GetInternalStream(getter_AddRefs(mBlobStream), aRv);
|
||||
NS_WARN_IF(aRv.Failed());
|
||||
aBlobData->GetInternalStream(getter_AddRefs(task->mBlobStream), aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
mBlobData = aBlobData;
|
||||
task->mBlobData = aBlobData;
|
||||
}
|
||||
}
|
||||
mArrayData.SwapElements(aArrayData);
|
||||
|
||||
task->mArrayData.SwapElements(aArrayData);
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> globalObject =
|
||||
do_QueryInterface(aFileSystem->GetWindow());
|
||||
if (!globalObject) {
|
||||
return;
|
||||
do_QueryInterface(aFileSystem->GetParentObject());
|
||||
if (NS_WARN_IF(!globalObject)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
mPromise = Promise::Create(globalObject, aRv);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
CreateFileTask::CreateFileTask(FileSystemBase* aFileSystem,
|
||||
@ -61,32 +131,9 @@ 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()
|
||||
@ -107,16 +154,22 @@ CreateFileTask::GetPromise()
|
||||
}
|
||||
|
||||
FileSystemParams
|
||||
CreateFileTask::GetRequestParams(const nsString& aFileSystem) const
|
||||
CreateFileTask::GetRequestParams(const nsString& aSerializedDOMPath,
|
||||
ErrorResult& aRv) const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
FileSystemCreateFileParams param;
|
||||
param.filesystem() = aFileSystem;
|
||||
param.realPath() = mTargetRealPath;
|
||||
param.filesystem() = aSerializedDOMPath;
|
||||
|
||||
aRv = mTargetPath->GetPath(param.realPath());
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return param;
|
||||
}
|
||||
|
||||
param.replace() = mReplace;
|
||||
if (mBlobData) {
|
||||
BlobChild* actor
|
||||
= ContentChild::GetSingleton()->GetOrCreateActorForBlob(mBlobData);
|
||||
BlobChild* actor =
|
||||
ContentChild::GetSingleton()->GetOrCreateActorForBlob(mBlobData);
|
||||
if (actor) {
|
||||
param.data() = actor;
|
||||
}
|
||||
@ -127,7 +180,7 @@ CreateFileTask::GetRequestParams(const nsString& aFileSystem) const
|
||||
}
|
||||
|
||||
FileSystemResponseValue
|
||||
CreateFileTask::GetSuccessRequestResult() const
|
||||
CreateFileTask::GetSuccessRequestResult(ErrorResult& aRv) const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
BlobParent* actor = GetBlobParent(mTargetBlobImpl);
|
||||
@ -140,7 +193,8 @@ CreateFileTask::GetSuccessRequestResult() const
|
||||
}
|
||||
|
||||
void
|
||||
CreateFileTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue)
|
||||
CreateFileTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
FileSystemFileResponse r = aValue;
|
||||
@ -151,7 +205,7 @@ CreateFileTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue)
|
||||
nsresult
|
||||
CreateFileTask::Work()
|
||||
{
|
||||
class AutoClose
|
||||
class MOZ_RAII AutoClose final
|
||||
{
|
||||
public:
|
||||
explicit AutoClose(nsIOutputStream* aStream)
|
||||
@ -164,6 +218,7 @@ CreateFileTask::Work()
|
||||
{
|
||||
mStream->Close();
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIOutputStream> mStream;
|
||||
};
|
||||
@ -176,24 +231,19 @@ CreateFileTask::Work()
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> file = mFileSystem->GetLocalFile(mTargetRealPath);
|
||||
if (!file) {
|
||||
return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
|
||||
}
|
||||
|
||||
if (!mFileSystem->IsSafeFile(file)) {
|
||||
if (!mFileSystem->IsSafeFile(mTargetPath)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
bool exists = false;
|
||||
nsresult rv = file->Exists(&exists);
|
||||
nsresult rv = mTargetPath->Exists(&exists);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (exists) {
|
||||
bool isFile = false;
|
||||
rv = file->IsFile(&isFile);
|
||||
rv = mTargetPath->IsFile(&isFile);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@ -207,19 +257,19 @@ CreateFileTask::Work()
|
||||
}
|
||||
|
||||
// Remove the old file before creating.
|
||||
rv = file->Remove(false);
|
||||
rv = mTargetPath->Remove(false);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
rv = file->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
|
||||
rv = mTargetPath->Create(nsIFile::NORMAL_FILE_TYPE, 0600);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIOutputStream> outputStream;
|
||||
rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file);
|
||||
rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), mTargetPath);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@ -262,7 +312,7 @@ CreateFileTask::Work()
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mTargetBlobImpl = new BlobImplFile(file);
|
||||
mTargetBlobImpl = new BlobImplFile(mTargetPath);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -281,7 +331,7 @@ CreateFileTask::Work()
|
||||
return NS_ERROR_DOM_FILESYSTEM_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
mTargetBlobImpl = new BlobImplFile(file);
|
||||
mTargetBlobImpl = new BlobImplFile(mTargetPath);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -302,7 +352,8 @@ CreateFileTask::HandlerCallback()
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<Blob> blob = Blob::Create(mFileSystem->GetWindow(), mTargetBlobImpl);
|
||||
RefPtr<Blob> blob = Blob::Create(mFileSystem->GetParentObject(),
|
||||
mTargetBlobImpl);
|
||||
mPromise->MaybeResolve(blob);
|
||||
mPromise = nullptr;
|
||||
mBlobData = nullptr;
|
||||
|
@ -20,19 +20,22 @@ class Blob;
|
||||
class BlobImpl;
|
||||
class Promise;
|
||||
|
||||
class CreateFileTask final
|
||||
: public FileSystemTaskBase
|
||||
class CreateFileTask final : public FileSystemTaskBase
|
||||
{
|
||||
public:
|
||||
CreateFileTask(FileSystemBase* aFileSystem,
|
||||
const nsAString& aPath,
|
||||
Blob* aBlobData,
|
||||
InfallibleTArray<uint8_t>& aArrayData,
|
||||
bool replace,
|
||||
ErrorResult& aRv);
|
||||
CreateFileTask(FileSystemBase* aFileSystem,
|
||||
const FileSystemCreateFileParams& aParam,
|
||||
FileSystemRequestParent* aParent);
|
||||
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);
|
||||
|
||||
virtual
|
||||
~CreateFileTask();
|
||||
@ -45,13 +48,15 @@ public:
|
||||
|
||||
protected:
|
||||
virtual FileSystemParams
|
||||
GetRequestParams(const nsString& aFileSystem) const override;
|
||||
GetRequestParams(const nsString& aSerializedDOMPath,
|
||||
ErrorResult& aRv) const override;
|
||||
|
||||
virtual FileSystemResponseValue
|
||||
GetSuccessRequestResult() const override;
|
||||
GetSuccessRequestResult(ErrorResult& aRv) const override;
|
||||
|
||||
virtual void
|
||||
SetSuccessRequestResult(const FileSystemResponseValue& aValue) override;
|
||||
SetSuccessRequestResult(const FileSystemResponseValue& aValue,
|
||||
ErrorResult& aRv) override;
|
||||
|
||||
virtual nsresult
|
||||
Work() override;
|
||||
@ -60,12 +65,20 @@ 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;
|
||||
nsString mTargetRealPath;
|
||||
nsCOMPtr<nsIFile> mTargetPath;
|
||||
|
||||
// Not thread-safe and should be released on main thread.
|
||||
RefPtr<Blob> mBlobData;
|
||||
|
@ -21,9 +21,8 @@
|
||||
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!");
|
||||
@ -31,12 +30,6 @@ DeviceStorageFileSystem::DeviceStorageFileSystem(
|
||||
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);
|
||||
|
||||
@ -46,19 +39,16 @@ DeviceStorageFileSystem::DeviceStorageFileSystem(
|
||||
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)));
|
||||
FileSystemUtils::LocalPathToNormalizedPath(mLocalRootPath,
|
||||
mNormalizedLocalRootPath);
|
||||
|
||||
if (!XRE_IsParentProcess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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
|
||||
@ -72,6 +62,17 @@ DeviceStorageFileSystem::~DeviceStorageFileSystem()
|
||||
{
|
||||
}
|
||||
|
||||
already_AddRefed<FileSystemBase>
|
||||
DeviceStorageFileSystem::Clone()
|
||||
{
|
||||
RefPtr<DeviceStorageFileSystem> fs =
|
||||
new DeviceStorageFileSystem(mStorageType, mStorageName);
|
||||
|
||||
fs->mWindowId = mWindowId;
|
||||
|
||||
return fs.forget();
|
||||
}
|
||||
|
||||
void
|
||||
DeviceStorageFileSystem::Init(nsDOMDeviceStorage* aDeviceStorage)
|
||||
{
|
||||
@ -89,8 +90,8 @@ DeviceStorageFileSystem::Shutdown()
|
||||
mShutdown = true;
|
||||
}
|
||||
|
||||
nsPIDOMWindowInner*
|
||||
DeviceStorageFileSystem::GetWindow() const
|
||||
nsISupports*
|
||||
DeviceStorageFileSystem::GetParentObject() const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
|
||||
@ -111,12 +112,15 @@ DeviceStorageFileSystem::IsSafeFile(nsIFile* aFile) const
|
||||
"Should be on parent process!");
|
||||
MOZ_ASSERT(aFile);
|
||||
|
||||
// Check if this file belongs to this storage.
|
||||
nsAutoString path;
|
||||
if (NS_FAILED(aFile->GetPath(path))) {
|
||||
nsCOMPtr<nsIFile> rootPath;
|
||||
nsresult rv = NS_NewLocalFile(GetLocalRootPath(), false,
|
||||
getter_AddRefs(rootPath));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
if (!LocalPathToRealPath(path, path)) {
|
||||
|
||||
// Check if this file belongs to this storage.
|
||||
if (NS_WARN_IF(!FileSystemUtils::IsDescendantPath(rootPath, aFile))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -132,10 +136,32 @@ DeviceStorageFileSystem::IsSafeDirectory(Directory* aDir) const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(aDir);
|
||||
RefPtr<FileSystemBase> fs = aDir->GetFileSystem();
|
||||
MOZ_ASSERT(fs);
|
||||
|
||||
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);
|
||||
|
||||
// Check if the given directory is from this storage.
|
||||
return fs->ToString() == mString;
|
||||
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);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
@ -27,11 +27,14 @@ public:
|
||||
|
||||
// Overrides FileSystemBase
|
||||
|
||||
virtual already_AddRefed<FileSystemBase>
|
||||
Clone() override;
|
||||
|
||||
virtual void
|
||||
Shutdown() override;
|
||||
|
||||
virtual nsPIDOMWindowInner*
|
||||
GetWindow() const override;
|
||||
virtual nsISupports*
|
||||
GetParentObject() const override;
|
||||
|
||||
virtual void
|
||||
GetRootName(nsAString& aRetval) const override;
|
||||
@ -41,6 +44,10 @@ public:
|
||||
|
||||
virtual bool
|
||||
IsSafeDirectory(Directory* aDir) const override;
|
||||
|
||||
virtual void
|
||||
SerializeDOMPath(nsAString& aSerializedString) const override;
|
||||
|
||||
private:
|
||||
virtual
|
||||
~DeviceStorageFileSystem();
|
||||
|
@ -18,6 +18,7 @@
|
||||
#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
|
||||
@ -34,15 +35,70 @@
|
||||
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)
|
||||
tmp->mFileSystem->Unlink();
|
||||
if (tmp->mFileSystem) {
|
||||
tmp->mFileSystem->Unlink();
|
||||
tmp->mFileSystem = nullptr;
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Directory)
|
||||
tmp->mFileSystem->Traverse(cb);
|
||||
if (tmp->mFileSystem) {
|
||||
tmp->mFileSystem->Traverse(cb);
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
@ -59,33 +115,73 @@ NS_INTERFACE_MAP_END
|
||||
already_AddRefed<Promise>
|
||||
Directory::GetRoot(FileSystemBase* aFileSystem, ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<GetFileOrDirectoryTask> task = new GetFileOrDirectoryTask(
|
||||
aFileSystem, EmptyString(), true, aRv);
|
||||
if (aRv.Failed()) {
|
||||
MOZ_ASSERT(aFileSystem);
|
||||
|
||||
nsCOMPtr<nsIFile> path;
|
||||
aRv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(aFileSystem->GetLocalRootPath()),
|
||||
true, getter_AddRefs(path));
|
||||
if (NS_WARN_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();
|
||||
}
|
||||
|
||||
Directory::Directory(FileSystemBase* aFileSystem,
|
||||
const nsAString& aPath)
|
||||
: mFileSystem(aFileSystem)
|
||||
, mPath(aPath)
|
||||
/* static */ already_AddRefed<Directory>
|
||||
Directory::Create(nsISupports* aParent, nsIFile* aFile,
|
||||
DirectoryType aType, FileSystemBase* aFileSystem)
|
||||
{
|
||||
MOZ_ASSERT(aFileSystem, "aFileSystem should not be null.");
|
||||
// Remove the trailing "/".
|
||||
mPath.Trim(FILESYSTEM_DOM_PATH_SEPARATOR, false, true);
|
||||
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)
|
||||
, mFile(aFile)
|
||||
, mType(aType)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aFile);
|
||||
|
||||
// aFileSystem can be null. In this case we create a OSFileSystem when needed.
|
||||
if (aFileSystem) {
|
||||
// More likely, this is a OSFileSystem. This object keeps a reference of
|
||||
// mParent but it's not cycle collectable and to avoid manual
|
||||
// addref/release, it's better to have 1 object per directory. For this
|
||||
// reason we clone it here.
|
||||
mFileSystem = aFileSystem->Clone();
|
||||
}
|
||||
}
|
||||
|
||||
Directory::~Directory()
|
||||
{
|
||||
}
|
||||
|
||||
nsPIDOMWindowInner*
|
||||
nsISupports*
|
||||
Directory::GetParentObject() const
|
||||
{
|
||||
return mFileSystem->GetWindow();
|
||||
return mParent;
|
||||
}
|
||||
|
||||
JSObject*
|
||||
@ -95,25 +191,28 @@ Directory::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
}
|
||||
|
||||
void
|
||||
Directory::GetName(nsAString& aRetval) const
|
||||
Directory::GetName(nsAString& aRetval, ErrorResult& aRv)
|
||||
{
|
||||
aRetval.Truncate();
|
||||
|
||||
if (mPath.IsEmpty()) {
|
||||
mFileSystem->GetRootName(aRetval);
|
||||
if (mType == eDOMRootDirectory) {
|
||||
RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
fs->GetRootName(aRetval);
|
||||
return;
|
||||
}
|
||||
|
||||
aRetval = Substring(mPath,
|
||||
mPath.RFindChar(FileSystemUtils::kSeparatorChar) + 1);
|
||||
aRv = mFile->GetLeafName(aRetval);
|
||||
NS_WARN_IF(aRv.Failed());
|
||||
}
|
||||
|
||||
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);
|
||||
@ -138,15 +237,20 @@ Directory::CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions,
|
||||
}
|
||||
}
|
||||
|
||||
if (!DOMPathToRealPath(aPath, realPath)) {
|
||||
error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
|
||||
nsCOMPtr<nsIFile> realPath;
|
||||
nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath));
|
||||
|
||||
RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<CreateFileTask> task =
|
||||
new CreateFileTask(mFileSystem, realPath, blobData, arrayData, replace, aRv);
|
||||
if (aRv.Failed()) {
|
||||
CreateFileTask::Create(fs, realPath, blobData, arrayData, replace, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
task->SetError(error);
|
||||
FileSystemPermissionRequest::RequestForTask(task);
|
||||
return task->GetPromise();
|
||||
@ -155,16 +259,20 @@ Directory::CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions,
|
||||
already_AddRefed<Promise>
|
||||
Directory::CreateDirectory(const nsAString& aPath, ErrorResult& aRv)
|
||||
{
|
||||
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()) {
|
||||
nsCOMPtr<nsIFile> realPath;
|
||||
nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath));
|
||||
|
||||
RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
|
||||
if (NS_WARN_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();
|
||||
@ -173,16 +281,21 @@ Directory::CreateDirectory(const nsAString& aPath, ErrorResult& aRv)
|
||||
already_AddRefed<Promise>
|
||||
Directory::Get(const nsAString& aPath, ErrorResult& aRv)
|
||||
{
|
||||
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()) {
|
||||
nsCOMPtr<nsIFile> realPath;
|
||||
nsresult error = DOMPathToRealPath(aPath, getter_AddRefs(realPath));
|
||||
|
||||
RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
|
||||
if (NS_WARN_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();
|
||||
@ -205,30 +318,33 @@ Directory::RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsresult error = NS_OK;
|
||||
nsAutoString realPath;
|
||||
nsCOMPtr<nsIFile> 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()) {
|
||||
if (!DOMPathToRealPath(aPath.GetAsString(), realPath)) {
|
||||
error = NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
|
||||
}
|
||||
} else if (!mFileSystem->IsSafeDirectory(&aPath.GetAsDirectory())) {
|
||||
error = DOMPathToRealPath(aPath.GetAsString(), getter_AddRefs(realPath));
|
||||
} else if (!fs->IsSafeDirectory(&aPath.GetAsDirectory())) {
|
||||
error = NS_ERROR_DOM_SECURITY_ERR;
|
||||
} else {
|
||||
realPath = aPath.GetAsDirectory().mPath;
|
||||
realPath = aPath.GetAsDirectory().mFile;
|
||||
// The target must be a descendant of this directory.
|
||||
if (!FileSystemUtils::IsDescendantPath(mPath, realPath)) {
|
||||
if (!FileSystemUtils::IsDescendantPath(mFile, realPath)) {
|
||||
error = NS_ERROR_DOM_FILESYSTEM_NO_MODIFICATION_ALLOWED_ERR;
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<RemoveTask> task = new RemoveTask(mFileSystem, mPath, blob, realPath,
|
||||
aRecursive, aRv);
|
||||
if (aRv.Failed()) {
|
||||
RefPtr<RemoveTask> task =
|
||||
RemoveTask::Create(fs, mFile, blob, realPath, aRecursive, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
task->SetError(error);
|
||||
@ -237,23 +353,38 @@ Directory::RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive,
|
||||
}
|
||||
|
||||
void
|
||||
Directory::GetPath(nsAString& aRetval) const
|
||||
Directory::GetPath(nsAString& aRetval, ErrorResult& aRv)
|
||||
{
|
||||
if (mPath.IsEmpty()) {
|
||||
// The Directory ctor removes any trailing '/'; this is the root directory.
|
||||
aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR);
|
||||
if (mType == eDOMRootDirectory) {
|
||||
aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
|
||||
} else {
|
||||
aRetval = mPath;
|
||||
// TODO: this should be a bit different...
|
||||
GetName(aRetval, aRv);
|
||||
}
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Directory::GetFilesAndDirectories()
|
||||
nsresult
|
||||
Directory::GetFullRealPath(nsAString& aPath)
|
||||
{
|
||||
ErrorResult rv;
|
||||
nsresult rv = mFile->GetPath(aPath);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Directory::GetFilesAndDirectories(ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<FileSystemBase> fs = GetFileSystem(aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<GetDirectoryListingTask> task =
|
||||
new GetDirectoryListingTask(mFileSystem, mPath, mFilters, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
GetDirectoryListingTask::Create(fs, mFile, mType, mFilters, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -268,16 +399,38 @@ Directory::SetContentFilters(const nsAString& aFilters)
|
||||
}
|
||||
|
||||
FileSystemBase*
|
||||
Directory::GetFileSystem() const
|
||||
Directory::GetFileSystem(ErrorResult& aRv)
|
||||
{
|
||||
return mFileSystem.get();
|
||||
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;
|
||||
}
|
||||
|
||||
bool
|
||||
Directory::DOMPathToRealPath(const nsAString& aPath, nsAString& aRealPath) const
|
||||
nsresult
|
||||
Directory::DOMPathToRealPath(const nsAString& aPath, nsIFile** aFile) const
|
||||
{
|
||||
aRealPath.Truncate();
|
||||
|
||||
nsString relativePath;
|
||||
relativePath = aPath;
|
||||
|
||||
@ -285,49 +438,26 @@ Directory::DOMPathToRealPath(const nsAString& aPath, nsAString& aRealPath) const
|
||||
static const char kWhitespace[] = "\b\t\r\n ";
|
||||
relativePath.Trim(kWhitespace);
|
||||
|
||||
if (!IsValidRelativePath(relativePath)) {
|
||||
return false;
|
||||
nsTArray<nsString> parts;
|
||||
if (!IsValidRelativeDOMPath(relativePath, parts)) {
|
||||
return NS_ERROR_DOM_FILESYSTEM_INVALID_PATH_ERR;
|
||||
}
|
||||
|
||||
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;
|
||||
nsCOMPtr<nsIFile> file;
|
||||
nsresult rv = mFile->Clone(getter_AddRefs(file));
|
||||
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;
|
||||
for (uint32_t i = 0; i < parts.Length(); ++i) {
|
||||
rv = file->AppendRelativePath(parts[i]);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
file.forget(aFile);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
@ -13,7 +13,6 @@
|
||||
#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
|
||||
@ -41,25 +40,48 @@ 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);
|
||||
|
||||
Directory(FileSystemBase* aFileSystem, const nsAString& aPath);
|
||||
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);
|
||||
|
||||
// ========= Begin WebIDL bindings. ===========
|
||||
|
||||
nsPIDOMWindowInner*
|
||||
nsISupports*
|
||||
GetParentObject() const;
|
||||
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
void
|
||||
GetName(nsAString& aRetval) const;
|
||||
GetName(nsAString& aRetval, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
CreateFile(const nsAString& aPath, const CreateFileOptions& aOptions,
|
||||
@ -80,10 +102,13 @@ public:
|
||||
// From https://microsoftedge.github.io/directory-upload/proposal.html#directory-interface :
|
||||
|
||||
void
|
||||
GetPath(nsAString& aRetval) const;
|
||||
GetPath(nsAString& aRetval, ErrorResult& aRv);
|
||||
|
||||
nsresult
|
||||
GetFullRealPath(nsAString& aPath);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
GetFilesAndDirectories();
|
||||
GetFilesAndDirectories(ErrorResult& aRv);
|
||||
|
||||
// =========== End WebIDL bindings.============
|
||||
|
||||
@ -113,26 +138,34 @@ public:
|
||||
SetContentFilters(const nsAString& aFilters);
|
||||
|
||||
FileSystemBase*
|
||||
GetFileSystem() const;
|
||||
private:
|
||||
~Directory();
|
||||
GetFileSystem(ErrorResult& aRv);
|
||||
|
||||
static bool
|
||||
IsValidRelativePath(const nsString& aPath);
|
||||
DirectoryType Type() const
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
|
||||
private:
|
||||
Directory(nsISupports* aParent,
|
||||
nsIFile* aFile, DirectoryType aType,
|
||||
FileSystemBase* aFileSystem = nullptr);
|
||||
~Directory();
|
||||
|
||||
/*
|
||||
* Convert relative DOM path to the absolute real path.
|
||||
* @return true if succeed. false if the DOM path is invalid.
|
||||
*/
|
||||
bool
|
||||
DOMPathToRealPath(const nsAString& aPath, nsAString& aRealPath) const;
|
||||
nsresult
|
||||
DOMPathToRealPath(const nsAString& aPath, nsIFile** aFile) const;
|
||||
|
||||
already_AddRefed<Promise>
|
||||
RemoveInternal(const StringOrFileOrDirectory& aPath, bool aRecursive,
|
||||
ErrorResult& aRv);
|
||||
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
RefPtr<FileSystemBase> mFileSystem;
|
||||
nsString mPath;
|
||||
nsCOMPtr<nsIFile> mFile;
|
||||
DirectoryType mType;
|
||||
|
||||
nsString mFilters;
|
||||
};
|
||||
|
||||
|
@ -15,7 +15,7 @@ namespace dom {
|
||||
|
||||
// static
|
||||
already_AddRefed<FileSystemBase>
|
||||
FileSystemBase::FromString(const nsAString& aString)
|
||||
FileSystemBase::DeserializeDOMPath(const nsAString& aString)
|
||||
{
|
||||
if (StringBeginsWith(aString, NS_LITERAL_STRING("devicestorage-"))) {
|
||||
// The string representation of devicestorage file system is of the format:
|
||||
@ -38,6 +38,7 @@ FileSystemBase::FromString(const nsAString& aString)
|
||||
new DeviceStorageFileSystem(storageType, storageName);
|
||||
return f.forget();
|
||||
}
|
||||
|
||||
return RefPtr<OSFileSystem>(new OSFileSystem(aString)).forget();
|
||||
}
|
||||
|
||||
@ -57,37 +58,19 @@ FileSystemBase::Shutdown()
|
||||
mShutdown = true;
|
||||
}
|
||||
|
||||
nsPIDOMWindowInner*
|
||||
FileSystemBase::GetWindow() const
|
||||
nsISupports*
|
||||
FileSystemBase::GetParentObject() 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, nsAString& aRealPath) const
|
||||
FileSystemBase::GetRealPath(BlobImpl* aFile, nsIFile** aPath) 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);
|
||||
@ -95,7 +78,13 @@ FileSystemBase::GetRealPath(BlobImpl* aFile, nsAString& aRealPath) const
|
||||
return false;
|
||||
}
|
||||
|
||||
return LocalPathToRealPath(filePath, aRealPath);
|
||||
rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(filePath),
|
||||
true, aPath);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
@ -110,19 +99,5 @@ 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
|
||||
|
@ -10,8 +10,6 @@
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsString.h"
|
||||
|
||||
class nsPIDOMWindowInner;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
@ -25,28 +23,22 @@ public:
|
||||
|
||||
// Create file system object from its string representation.
|
||||
static already_AddRefed<FileSystemBase>
|
||||
FromString(const nsAString& aString);
|
||||
DeserializeDOMPath(const nsAString& aString);
|
||||
|
||||
FileSystemBase();
|
||||
|
||||
virtual void
|
||||
Shutdown();
|
||||
|
||||
// Get the string representation of the file system.
|
||||
const nsString&
|
||||
ToString() const
|
||||
{
|
||||
return mString;
|
||||
}
|
||||
// SerializeDOMPath the FileSystem to string.
|
||||
virtual void
|
||||
SerializeDOMPath(nsAString& aOutput) const = 0;
|
||||
|
||||
virtual nsPIDOMWindowInner*
|
||||
GetWindow() const;
|
||||
virtual already_AddRefed<FileSystemBase>
|
||||
Clone() = 0;
|
||||
|
||||
/**
|
||||
* Create nsIFile object from the given real path (absolute DOM path).
|
||||
*/
|
||||
already_AddRefed<nsIFile>
|
||||
GetLocalFile(const nsAString& aRealPath) const;
|
||||
virtual nsISupports*
|
||||
GetParentObject() const;
|
||||
|
||||
/*
|
||||
* Get the virtual name of the root directory. This name will be exposed to
|
||||
@ -73,13 +65,8 @@ 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, nsAString& aRealPath) const;
|
||||
GetRealPath(BlobImpl* aFile, nsIFile** aPath) const;
|
||||
|
||||
/*
|
||||
* Get the permission name required to access this file system.
|
||||
@ -103,21 +90,12 @@ 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.
|
||||
|
@ -15,7 +15,8 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_ISUPPORTS(FileSystemPermissionRequest, nsIRunnable, nsIContentPermissionRequest)
|
||||
NS_IMPL_ISUPPORTS(FileSystemPermissionRequest, nsIRunnable,
|
||||
nsIContentPermissionRequest)
|
||||
|
||||
// static
|
||||
void
|
||||
@ -28,8 +29,7 @@ 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(
|
||||
|
||||
mPermissionType = filesystem->GetPermission();
|
||||
|
||||
mWindow = filesystem->GetWindow();
|
||||
if (!mWindow) {
|
||||
mWindow = do_QueryInterface(filesystem->GetParentObject());
|
||||
if (NS_WARN_IF(!mWindow)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -28,8 +28,11 @@ FileSystemRequestParent::~FileSystemRequestParent()
|
||||
#define FILESYSTEM_REQUEST_PARENT_DISPATCH_ENTRY(name) \
|
||||
case FileSystemParams::TFileSystem##name##Params: { \
|
||||
const FileSystem##name##Params& p = aParams; \
|
||||
mFileSystem = FileSystemBase::FromString(p.filesystem()); \
|
||||
task = new name##Task(mFileSystem, p, this); \
|
||||
mFileSystem = FileSystemBase::DeserializeDOMPath(p.filesystem()); \
|
||||
task = name##Task::Create(mFileSystem, p, this, rv); \
|
||||
if (NS_WARN_IF(rv.Failed())) { \
|
||||
return false; \
|
||||
} \
|
||||
break; \
|
||||
}
|
||||
|
||||
@ -39,6 +42,8 @@ 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)
|
||||
|
@ -23,7 +23,7 @@ namespace dom {
|
||||
|
||||
namespace {
|
||||
|
||||
class FileSystemReleaseRunnable : public nsRunnable
|
||||
class FileSystemReleaseRunnable final : public nsRunnable
|
||||
{
|
||||
public:
|
||||
explicit FileSystemReleaseRunnable(RefPtr<FileSystemBase>& aDoomed)
|
||||
@ -106,12 +106,22 @@ 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,
|
||||
GetRequestParams(mFileSystem->ToString()));
|
||||
params);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -154,11 +164,17 @@ FileSystemTaskBase::GetRequestResult() const
|
||||
MOZ_ASSERT(XRE_IsParentProcess(),
|
||||
"Only call from parent process!");
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
if (HasError()) {
|
||||
return FileSystemErrorResponse(mErrorValue);
|
||||
} else {
|
||||
return GetSuccessRequestResult();
|
||||
if (!HasError()) {
|
||||
ErrorResult rv;
|
||||
FileSystemResponseValue value = GetSuccessRequestResult(rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
return FileSystemErrorResponse(rv.StealNSResult());
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
return FileSystemErrorResponse(mErrorValue);
|
||||
}
|
||||
|
||||
void
|
||||
@ -171,7 +187,9 @@ FileSystemTaskBase::SetRequestResult(const FileSystemResponseValue& aValue)
|
||||
FileSystemErrorResponse r = aValue;
|
||||
mErrorValue = r.error();
|
||||
} else {
|
||||
SetSuccessRequestResult(aValue);
|
||||
ErrorResult rv;
|
||||
SetSuccessRequestResult(aValue, rv);
|
||||
mErrorValue = rv.StealNSResult();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -176,7 +176,8 @@ protected:
|
||||
* @param filesystem The string representation of the file system.
|
||||
*/
|
||||
virtual FileSystemParams
|
||||
GetRequestParams(const nsString& aFileSystem) const = 0;
|
||||
GetRequestParams(const nsString& aSerializedDOMPath,
|
||||
ErrorResult& aRv) const = 0;
|
||||
|
||||
/*
|
||||
* Wrap the task success result to FileSystemResponseValue for sending it
|
||||
@ -185,7 +186,7 @@ protected:
|
||||
* send the task success result back to the child process.
|
||||
*/
|
||||
virtual FileSystemResponseValue
|
||||
GetSuccessRequestResult() const = 0;
|
||||
GetSuccessRequestResult(ErrorResult& aRv) const = 0;
|
||||
|
||||
/*
|
||||
* Unwrap the IPC message to get the task success result.
|
||||
@ -194,7 +195,8 @@ protected:
|
||||
* success result.
|
||||
*/
|
||||
virtual void
|
||||
SetSuccessRequestResult(const FileSystemResponseValue& aValue) = 0;
|
||||
SetSuccessRequestResult(const FileSystemResponseValue& aValue,
|
||||
ErrorResult& aRv) = 0;
|
||||
|
||||
bool
|
||||
HasError() const { return mErrorValue != NS_OK; }
|
||||
|
@ -6,59 +6,28 @@
|
||||
|
||||
#include "mozilla/dom/FileSystemUtils.h"
|
||||
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// static
|
||||
void
|
||||
FileSystemUtils::LocalPathToNormalizedPath(const nsAString& aLocal,
|
||||
nsAString& aNorm)
|
||||
/* static */ bool
|
||||
FileSystemUtils::IsDescendantPath(nsIFile* aFile,
|
||||
nsIFile* aDescendantFile)
|
||||
{
|
||||
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('/');
|
||||
nsAutoString path;
|
||||
nsresult rv = aFile->GetPath(path);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
aNorm = result;
|
||||
}
|
||||
|
||||
// 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('\\');
|
||||
nsAutoString descendantPath;
|
||||
rv = aDescendantFile->GetPath(descendantPath);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return false;
|
||||
}
|
||||
#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 (aDescendantPath.Length() < prefix.Length() ||
|
||||
!StringBeginsWith(aDescendantPath, prefix)) {
|
||||
if (descendantPath.Length() <= path.Length() ||
|
||||
!StringBeginsWith(descendantPath, path)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -7,12 +7,13 @@
|
||||
#ifndef mozilla_dom_FileSystemUtils_h
|
||||
#define mozilla_dom_FileSystemUtils_h
|
||||
|
||||
#include "nsString.h"
|
||||
class nsIFile;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
#define FILESYSTEM_DOM_PATH_SEPARATOR "/"
|
||||
#define FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL "/"
|
||||
#define FILESYSTEM_DOM_PATH_SEPARATOR_CHAR '/'
|
||||
|
||||
/*
|
||||
* This class is for error handling.
|
||||
@ -22,26 +23,10 @@ class FileSystemUtils
|
||||
{
|
||||
public:
|
||||
/*
|
||||
* 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.
|
||||
* Return true if aDescendantPath is a descendant of aPath.
|
||||
*/
|
||||
static bool
|
||||
IsDescendantPath(const nsAString& aPath, const nsAString& aDescendantPath);
|
||||
|
||||
static const char16_t kSeparatorChar = char16_t('/');
|
||||
IsDescendantPath(nsIFile* aPath, nsIFile* aDescendantPath);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -8,7 +8,6 @@
|
||||
|
||||
#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"
|
||||
@ -21,33 +20,80 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
GetDirectoryListingTask::GetDirectoryListingTask(FileSystemBase* aFileSystem,
|
||||
const nsAString& aTargetPath,
|
||||
const nsAString& aFilters,
|
||||
ErrorResult& aRv)
|
||||
: FileSystemTaskBase(aFileSystem)
|
||||
, mTargetRealPath(aTargetPath)
|
||||
, mFilters(aFilters)
|
||||
/* 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->GetWindow());
|
||||
if (!globalObject) {
|
||||
return;
|
||||
do_QueryInterface(aFileSystem->GetParentObject());
|
||||
if (NS_WARN_IF(!globalObject)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
mPromise = Promise::Create(globalObject, aRv);
|
||||
|
||||
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)
|
||||
: FileSystemTaskBase(aFileSystem)
|
||||
, mTargetPath(aTargetPath)
|
||||
, mFilters(aFilters)
|
||||
, mType(aType)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(aFileSystem);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@ -66,43 +112,79 @@ GetDirectoryListingTask::GetPromise()
|
||||
}
|
||||
|
||||
FileSystemParams
|
||||
GetDirectoryListingTask::GetRequestParams(const nsString& aFileSystem) const
|
||||
GetDirectoryListingTask::GetRequestParams(const nsString& aSerializedDOMPath,
|
||||
ErrorResult& aRv) const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
return FileSystemGetDirectoryListingParams(aFileSystem, mTargetRealPath,
|
||||
|
||||
nsAutoString path;
|
||||
aRv = mTargetPath->GetPath(path);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return FileSystemGetDirectoryListingParams();
|
||||
}
|
||||
|
||||
return FileSystemGetDirectoryListingParams(aSerializedDOMPath, path,
|
||||
mType == Directory::eDOMRootDirectory,
|
||||
mFilters);
|
||||
}
|
||||
|
||||
FileSystemResponseValue
|
||||
GetDirectoryListingTask::GetSuccessRequestResult() const
|
||||
GetDirectoryListingTask::GetSuccessRequestResult(ErrorResult& aRv) const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
|
||||
InfallibleTArray<PBlobParent*> blobs;
|
||||
|
||||
for (unsigned i = 0; i < mTargetBlobImpls.Length(); i++) {
|
||||
BlobParent* blobParent = GetBlobParent(mTargetBlobImpls[i]);
|
||||
if (blobParent) {
|
||||
blobs.AppendElement(blobParent);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
FileSystemDirectoryListingResponse response;
|
||||
response.blobsParent().SwapElements(blobs);
|
||||
response.data().SwapElements(inputs);
|
||||
return response;
|
||||
}
|
||||
|
||||
void
|
||||
GetDirectoryListingTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue)
|
||||
GetDirectoryListingTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(aValue.type() ==
|
||||
FileSystemResponseValue::TFileSystemDirectoryListingResponse);
|
||||
|
||||
FileSystemDirectoryListingResponse r = aValue;
|
||||
nsTArray<PBlobChild*>& blobs = r.blobsChild();
|
||||
for (uint32_t i = 0; i < r.data().Length(); ++i) {
|
||||
const FileSystemDirectoryListingResponseData& data = r.data()[i];
|
||||
|
||||
for (unsigned i = 0; i < blobs.Length(); i++) {
|
||||
mTargetBlobImpls.AppendElement(static_cast<BlobChild*>(blobs[i])->GetBlobImpl());
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,27 +199,19 @@ 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 = dir->Exists(&exists);
|
||||
nsresult rv = mTargetPath->Exists(&exists);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!exists) {
|
||||
if (!getRoot) {
|
||||
if (mType == Directory::eNotDOMRootDirectory) {
|
||||
return NS_ERROR_DOM_FILE_NOT_FOUND_ERR;
|
||||
}
|
||||
|
||||
// If the root directory doesn't exit, create it.
|
||||
rv = dir->Create(nsIFile::DIRECTORY_TYPE, 0777);
|
||||
rv = mTargetPath->Create(nsIFile::DIRECTORY_TYPE, 0777);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@ -145,7 +219,7 @@ GetDirectoryListingTask::Work()
|
||||
|
||||
// Get isDirectory.
|
||||
bool isDir;
|
||||
rv = dir->IsDirectory(&isDir);
|
||||
rv = mTargetPath->IsDirectory(&isDir);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@ -155,7 +229,7 @@ GetDirectoryListingTask::Work()
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISimpleEnumerator> entries;
|
||||
rv = dir->GetDirectoryEntries(getter_AddRefs(entries));
|
||||
rv = mTargetPath->GetDirectoryEntries(getter_AddRefs(entries));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@ -186,7 +260,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))) ||
|
||||
@ -213,9 +287,22 @@ GetDirectoryListingTask::Work()
|
||||
}
|
||||
}
|
||||
|
||||
BlobImplFile* impl = new BlobImplFile(currFile);
|
||||
impl->LookupAndCacheIsDirectory();
|
||||
mTargetBlobImpls.AppendElement(impl);
|
||||
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;
|
||||
}
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
@ -235,37 +322,55 @@ GetDirectoryListingTask::HandlerCallback()
|
||||
return;
|
||||
}
|
||||
|
||||
size_t count = mTargetBlobImpls.Length();
|
||||
size_t count = mTargetData.Length();
|
||||
|
||||
Sequence<OwningFileOrDirectory> listing;
|
||||
|
||||
if (!listing.SetLength(count, mozilla::fallible_t())) {
|
||||
mPromise->MaybeReject(NS_ERROR_FAILURE);
|
||||
mPromise->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
|
||||
mPromise = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < count; i++) {
|
||||
if (mTargetBlobImpls[i]->IsDirectory()) {
|
||||
nsAutoString name;
|
||||
mTargetBlobImpls[i]->GetName(name);
|
||||
nsAutoString path(mTargetRealPath);
|
||||
path.AppendLiteral(FILESYSTEM_DOM_PATH_SEPARATOR);
|
||||
path.Append(name);
|
||||
#ifdef DEBUG
|
||||
if (XRE_IsParentProcess()) {
|
||||
nsCOMPtr<nsIFile> file = mFileSystem->GetLocalFile(path);
|
||||
bool exist;
|
||||
file->Exists(&exist);
|
||||
MOZ_ASSERT(exist);
|
||||
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;
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(FileSystemUtils::IsDescendantPath(rootPath, directoryPath));
|
||||
#endif
|
||||
RefPtr<Directory> directory = new Directory(mFileSystem, path);
|
||||
|
||||
RefPtr<Directory> directory =
|
||||
Directory::Create(mFileSystem->GetParentObject(),
|
||||
directoryPath,
|
||||
Directory::eNotDOMRootDirectory,
|
||||
mFileSystem);
|
||||
MOZ_ASSERT(directory);
|
||||
|
||||
// Propogate mFilter onto sub-Directory object:
|
||||
directory->SetContentFilters(mFilters);
|
||||
listing[i].SetAsDirectory() = directory;
|
||||
} else {
|
||||
listing[i].SetAsFile() = File::Create(mFileSystem->GetWindow(), mTargetBlobImpls[i]);
|
||||
MOZ_ASSERT(mTargetData[i].mType == Directory::BlobImplOrDirectoryPath::eBlobImpl);
|
||||
listing[i].SetAsFile() =
|
||||
File::Create(mFileSystem->GetParentObject(), mTargetData[i].mBlobImpl);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#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"
|
||||
@ -16,18 +17,21 @@ namespace dom {
|
||||
|
||||
class BlobImpl;
|
||||
|
||||
class GetDirectoryListingTask final
|
||||
: public FileSystemTaskBase
|
||||
class GetDirectoryListingTask final : public FileSystemTaskBase
|
||||
{
|
||||
public:
|
||||
// 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);
|
||||
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);
|
||||
|
||||
virtual
|
||||
~GetDirectoryListingTask();
|
||||
@ -37,15 +41,28 @@ public:
|
||||
|
||||
virtual void
|
||||
GetPermissionAccessType(nsCString& aAccess) const override;
|
||||
protected:
|
||||
|
||||
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);
|
||||
|
||||
virtual FileSystemParams
|
||||
GetRequestParams(const nsString& aFileSystem) const override;
|
||||
GetRequestParams(const nsString& aSerializedDOMPath,
|
||||
ErrorResult& aRv) const override;
|
||||
|
||||
virtual FileSystemResponseValue
|
||||
GetSuccessRequestResult() const override;
|
||||
GetSuccessRequestResult(ErrorResult& aRv) const override;
|
||||
|
||||
virtual void
|
||||
SetSuccessRequestResult(const FileSystemResponseValue& aValue) override;
|
||||
SetSuccessRequestResult(const FileSystemResponseValue& aValue,
|
||||
ErrorResult& aRv) override;
|
||||
|
||||
virtual nsresult
|
||||
Work() override;
|
||||
@ -53,14 +70,14 @@ protected:
|
||||
virtual void
|
||||
HandlerCallback() override;
|
||||
|
||||
private:
|
||||
RefPtr<Promise> mPromise;
|
||||
nsString mTargetRealPath;
|
||||
nsCOMPtr<nsIFile> mTargetPath;
|
||||
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<RefPtr<BlobImpl>> mTargetBlobImpls;
|
||||
nsTArray<Directory::BlobImplOrDirectoryPath> mTargetData;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -7,7 +7,6 @@
|
||||
#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"
|
||||
@ -20,37 +19,82 @@
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
GetFileOrDirectoryTask::GetFileOrDirectoryTask(
|
||||
FileSystemBase* aFileSystem,
|
||||
const nsAString& aTargetPath,
|
||||
bool aDirectoryOnly,
|
||||
ErrorResult& aRv)
|
||||
: FileSystemTaskBase(aFileSystem)
|
||||
, mTargetRealPath(aTargetPath)
|
||||
, mIsDirectory(aDirectoryOnly)
|
||||
/* 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->GetWindow());
|
||||
if (!globalObject) {
|
||||
return;
|
||||
do_QueryInterface(aFileSystem->GetParentObject());
|
||||
if (NS_WARN_IF(!globalObject)) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
mPromise = Promise::Create(globalObject, aRv);
|
||||
|
||||
task->mPromise = Promise::Create(globalObject, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return task.forget();
|
||||
}
|
||||
|
||||
GetFileOrDirectoryTask::GetFileOrDirectoryTask(
|
||||
FileSystemBase* aFileSystem,
|
||||
const FileSystemGetFileOrDirectoryParams& aParam,
|
||||
FileSystemRequestParent* aParent)
|
||||
/* 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)
|
||||
: FileSystemTaskBase(aFileSystem)
|
||||
, mTargetPath(aTargetPath)
|
||||
, mIsDirectory(aDirectoryOnly)
|
||||
, mType(aType)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(aFileSystem);
|
||||
}
|
||||
|
||||
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()
|
||||
@ -67,18 +111,33 @@ GetFileOrDirectoryTask::GetPromise()
|
||||
}
|
||||
|
||||
FileSystemParams
|
||||
GetFileOrDirectoryTask::GetRequestParams(const nsString& aFileSystem) const
|
||||
GetFileOrDirectoryTask::GetRequestParams(const nsString& aSerializedDOMPath,
|
||||
ErrorResult& aRv) const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
return FileSystemGetFileOrDirectoryParams(aFileSystem, mTargetRealPath);
|
||||
|
||||
nsAutoString path;
|
||||
aRv = mTargetPath->GetPath(path);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return FileSystemGetFileOrDirectoryParams();
|
||||
}
|
||||
|
||||
return FileSystemGetFileOrDirectoryParams(aSerializedDOMPath, path,
|
||||
mType == Directory::eDOMRootDirectory);
|
||||
}
|
||||
|
||||
FileSystemResponseValue
|
||||
GetFileOrDirectoryTask::GetSuccessRequestResult() const
|
||||
GetFileOrDirectoryTask::GetSuccessRequestResult(ErrorResult& aRv) const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
if (mIsDirectory) {
|
||||
return FileSystemDirectoryResponse(mTargetRealPath);
|
||||
nsAutoString path;
|
||||
aRv = mTargetPath->GetPath(path);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return FileSystemDirectoryResponse();
|
||||
}
|
||||
|
||||
return FileSystemDirectoryResponse(path);
|
||||
}
|
||||
|
||||
BlobParent* actor = GetBlobParent(mTargetBlobImpl);
|
||||
@ -91,7 +150,8 @@ GetFileOrDirectoryTask::GetSuccessRequestResult() const
|
||||
}
|
||||
|
||||
void
|
||||
GetFileOrDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue)
|
||||
GetFileOrDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
switch (aValue.type()) {
|
||||
@ -104,7 +164,13 @@ GetFileOrDirectoryTask::SetSuccessRequestResult(const FileSystemResponseValue& a
|
||||
}
|
||||
case FileSystemResponseValue::TFileSystemDirectoryResponse: {
|
||||
FileSystemDirectoryResponse r = aValue;
|
||||
mTargetRealPath = r.realPath();
|
||||
|
||||
NS_ConvertUTF16toUTF8 path(r.realPath());
|
||||
aRv = NS_NewNativeLocalFile(path, true, getter_AddRefs(mTargetPath));
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
mIsDirectory = true;
|
||||
break;
|
||||
}
|
||||
@ -127,33 +193,26 @@ 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 = file->Exists(&exists);
|
||||
nsresult rv = mTargetPath->Exists(&exists);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!exists) {
|
||||
if (!getRoot) {
|
||||
if (mType == Directory::eNotDOMRootDirectory) {
|
||||
return NS_ERROR_DOM_FILE_NOT_FOUND_ERR;
|
||||
}
|
||||
|
||||
// If the root directory doesn't exit, create it.
|
||||
rv = file->Create(nsIFile::DIRECTORY_TYPE, 0777);
|
||||
rv = mTargetPath->Create(nsIFile::DIRECTORY_TYPE, 0777);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
// Get isDirectory.
|
||||
rv = file->IsDirectory(&mIsDirectory);
|
||||
rv = mTargetPath->IsDirectory(&mIsDirectory);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@ -163,13 +222,13 @@ GetFileOrDirectoryTask::Work()
|
||||
}
|
||||
|
||||
// Check if the root is a directory.
|
||||
if (getRoot) {
|
||||
if (mType == Directory::eDOMRootDirectory) {
|
||||
return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
|
||||
}
|
||||
|
||||
bool isFile;
|
||||
// Get isFile
|
||||
rv = file->IsFile(&isFile);
|
||||
rv = mTargetPath->IsFile(&isFile);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@ -179,11 +238,11 @@ GetFileOrDirectoryTask::Work()
|
||||
return NS_ERROR_DOM_FILESYSTEM_TYPE_MISMATCH_ERR;
|
||||
}
|
||||
|
||||
if (!mFileSystem->IsSafeFile(file)) {
|
||||
if (!mFileSystem->IsSafeFile(mTargetPath)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
mTargetBlobImpl = new BlobImplFile(file);
|
||||
mTargetBlobImpl = new BlobImplFile(mTargetPath);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
@ -204,13 +263,19 @@ GetFileOrDirectoryTask::HandlerCallback()
|
||||
}
|
||||
|
||||
if (mIsDirectory) {
|
||||
RefPtr<Directory> dir = new Directory(mFileSystem, mTargetRealPath);
|
||||
RefPtr<Directory> dir = Directory::Create(mFileSystem->GetParentObject(),
|
||||
mTargetPath,
|
||||
mType,
|
||||
mFileSystem);
|
||||
MOZ_ASSERT(dir);
|
||||
|
||||
mPromise->MaybeResolve(dir);
|
||||
mPromise = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<Blob> blob = Blob::Create(mFileSystem->GetWindow(), mTargetBlobImpl);
|
||||
RefPtr<Blob> blob = Blob::Create(mFileSystem->GetParentObject(),
|
||||
mTargetBlobImpl);
|
||||
mPromise->MaybeResolve(blob);
|
||||
mPromise = nullptr;
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
#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"
|
||||
@ -16,18 +17,21 @@ namespace dom {
|
||||
|
||||
class BlobImpl;
|
||||
|
||||
class GetFileOrDirectoryTask final
|
||||
: public FileSystemTaskBase
|
||||
class GetFileOrDirectoryTask final : public FileSystemTaskBase
|
||||
{
|
||||
public:
|
||||
// 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);
|
||||
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);
|
||||
|
||||
virtual
|
||||
~GetFileOrDirectoryTask();
|
||||
@ -39,13 +43,15 @@ public:
|
||||
GetPermissionAccessType(nsCString& aAccess) const override;
|
||||
protected:
|
||||
virtual FileSystemParams
|
||||
GetRequestParams(const nsString& aFileSystem) const override;
|
||||
GetRequestParams(const nsString& aSerializedDOMPath,
|
||||
ErrorResult& aRv) const override;
|
||||
|
||||
virtual FileSystemResponseValue
|
||||
GetSuccessRequestResult() const override;
|
||||
GetSuccessRequestResult(ErrorResult& aRv) const override;
|
||||
|
||||
virtual void
|
||||
SetSuccessRequestResult(const FileSystemResponseValue& aValue) override;
|
||||
SetSuccessRequestResult(const FileSystemResponseValue& aValue,
|
||||
ErrorResult& aRv) override;
|
||||
|
||||
virtual nsresult
|
||||
Work() override;
|
||||
@ -54,10 +60,22 @@ 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;
|
||||
nsString mTargetRealPath;
|
||||
nsCOMPtr<nsIFile> mTargetPath;
|
||||
|
||||
// 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.
|
||||
|
@ -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,40 +20,52 @@ 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(nsPIDOMWindowInner* aWindow)
|
||||
already_AddRefed<FileSystemBase>
|
||||
OSFileSystem::Clone()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
MOZ_ASSERT(!mWindow, "No duple Init() calls");
|
||||
MOZ_ASSERT(aWindow);
|
||||
mWindow = aWindow;
|
||||
RefPtr<OSFileSystem> fs = new OSFileSystem(mLocalRootPath);
|
||||
if (mParent) {
|
||||
fs->Init(mParent);
|
||||
}
|
||||
|
||||
return fs.forget();
|
||||
}
|
||||
|
||||
nsPIDOMWindowInner*
|
||||
OSFileSystem::GetWindow() const
|
||||
void
|
||||
OSFileSystem::Init(nsISupports* aParent)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
return mWindow;
|
||||
MOZ_ASSERT(!mParent, "No duple Init() calls");
|
||||
MOZ_ASSERT(aParent);
|
||||
mParent = aParent;
|
||||
|
||||
#ifdef DEBUG
|
||||
nsCOMPtr<nsIGlobalObject> obj = do_QueryInterface(aParent);
|
||||
MOZ_ASSERT(obj);
|
||||
#endif
|
||||
}
|
||||
|
||||
nsISupports*
|
||||
OSFileSystem::GetParentObject() const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
return mParent;
|
||||
}
|
||||
|
||||
void
|
||||
OSFileSystem::GetRootName(nsAString& aRetval) const
|
||||
{
|
||||
return aRetval.AssignLiteral("/");
|
||||
aRetval.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -79,14 +91,20 @@ OSFileSystem::IsSafeDirectory(Directory* aDir) const
|
||||
void
|
||||
OSFileSystem::Unlink()
|
||||
{
|
||||
mWindow = nullptr;
|
||||
mParent = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
OSFileSystem::Traverse(nsCycleCollectionTraversalCallback &cb)
|
||||
{
|
||||
OSFileSystem* tmp = this;
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent);
|
||||
}
|
||||
|
||||
void
|
||||
OSFileSystem::SerializeDOMPath(nsAString& aOutput) const
|
||||
{
|
||||
aOutput = mLocalRootPath;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
|
@ -18,12 +18,15 @@ public:
|
||||
explicit OSFileSystem(const nsAString& aRootDir);
|
||||
|
||||
void
|
||||
Init(nsPIDOMWindowInner* aWindow);
|
||||
Init(nsISupports* aParent);
|
||||
|
||||
// Overrides FileSystemBase
|
||||
|
||||
virtual nsPIDOMWindowInner*
|
||||
GetWindow() const override;
|
||||
virtual already_AddRefed<FileSystemBase>
|
||||
Clone() override;
|
||||
|
||||
virtual nsISupports*
|
||||
GetParentObject() const override;
|
||||
|
||||
virtual void
|
||||
GetRootName(nsAString& aRetval) const override;
|
||||
@ -34,6 +37,9 @@ 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;
|
||||
@ -41,7 +47,7 @@ public:
|
||||
private:
|
||||
virtual ~OSFileSystem() {}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> mWindow;
|
||||
nsCOMPtr<nsISupports> mParent;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -20,9 +20,26 @@ 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
|
||||
{
|
||||
PBlob[] blobs;
|
||||
FileSystemDirectoryListingResponseData[] data;
|
||||
};
|
||||
|
||||
struct FileSystemErrorResponse
|
||||
|
@ -18,27 +18,94 @@
|
||||
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,
|
||||
const nsAString& aDirPath,
|
||||
nsIFile* aDirPath,
|
||||
BlobImpl* aTargetBlob,
|
||||
const nsAString& aTargetPath,
|
||||
bool aRecursive,
|
||||
ErrorResult& aRv)
|
||||
nsIFile* aTargetPath,
|
||||
bool aRecursive)
|
||||
: FileSystemTaskBase(aFileSystem)
|
||||
, mDirRealPath(aDirPath)
|
||||
, mDirPath(aDirPath)
|
||||
, mTargetBlobImpl(aTargetBlob)
|
||||
, mTargetRealPath(aTargetPath)
|
||||
, mTargetPath(aTargetPath)
|
||||
, mRecursive(aRecursive)
|
||||
, mReturnValue(false)
|
||||
{
|
||||
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);
|
||||
MOZ_ASSERT(aDirPath);
|
||||
}
|
||||
|
||||
RemoveTask::RemoveTask(FileSystemBase* aFileSystem,
|
||||
@ -48,25 +115,9 @@ 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()
|
||||
@ -83,36 +134,49 @@ RemoveTask::GetPromise()
|
||||
}
|
||||
|
||||
FileSystemParams
|
||||
RemoveTask::GetRequestParams(const nsString& aFileSystem) const
|
||||
RemoveTask::GetRequestParams(const nsString& aSerializedDOMPath,
|
||||
ErrorResult& aRv) const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
FileSystemRemoveParams param;
|
||||
param.filesystem() = aFileSystem;
|
||||
param.directory() = mDirRealPath;
|
||||
param.filesystem() = aSerializedDOMPath;
|
||||
|
||||
aRv = mDirPath->GetPath(param.directory());
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return param;
|
||||
}
|
||||
|
||||
param.recursive() = mRecursive;
|
||||
if (mTargetBlobImpl) {
|
||||
RefPtr<Blob> blob = Blob::Create(mFileSystem->GetWindow(),
|
||||
mTargetBlobImpl);
|
||||
RefPtr<Blob> blob = Blob::Create(mFileSystem->GetParentObject(),
|
||||
mTargetBlobImpl);
|
||||
BlobChild* actor
|
||||
= ContentChild::GetSingleton()->GetOrCreateActorForBlob(blob);
|
||||
if (actor) {
|
||||
param.target() = actor;
|
||||
}
|
||||
} else {
|
||||
param.target() = mTargetRealPath;
|
||||
nsAutoString path;
|
||||
aRv = mTargetPath->GetPath(path);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return param;
|
||||
}
|
||||
|
||||
param.target() = path;
|
||||
}
|
||||
return param;
|
||||
}
|
||||
|
||||
FileSystemResponseValue
|
||||
RemoveTask::GetSuccessRequestResult() const
|
||||
RemoveTask::GetSuccessRequestResult(ErrorResult& aRv) const
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
return FileSystemBooleanResponse(mReturnValue);
|
||||
}
|
||||
|
||||
void
|
||||
RemoveTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue)
|
||||
RemoveTask::SetSuccessRequestResult(const FileSystemResponseValue& aValue,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!");
|
||||
FileSystemBooleanResponse r = aValue;
|
||||
@ -130,23 +194,19 @@ RemoveTask::Work()
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Get the DOM path if a File is passed as the target.
|
||||
// Get the path if a File is passed as the target.
|
||||
if (mTargetBlobImpl) {
|
||||
if (!mFileSystem->GetRealPath(mTargetBlobImpl, mTargetRealPath)) {
|
||||
if (!mFileSystem->GetRealPath(mTargetBlobImpl,
|
||||
getter_AddRefs(mTargetPath))) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
if (!FileSystemUtils::IsDescendantPath(mDirRealPath, mTargetRealPath)) {
|
||||
if (!FileSystemUtils::IsDescendantPath(mDirPath, mTargetPath)) {
|
||||
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 = file->Exists(&exists);
|
||||
nsresult rv = mTargetPath->Exists(&exists);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
@ -157,16 +217,16 @@ RemoveTask::Work()
|
||||
}
|
||||
|
||||
bool isFile = false;
|
||||
rv = file->IsFile(&isFile);
|
||||
rv = mTargetPath->IsFile(&isFile);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (isFile && !mFileSystem->IsSafeFile(file)) {
|
||||
if (isFile && !mFileSystem->IsSafeFile(mTargetPath)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
rv = file->Remove(mRecursive);
|
||||
rv = mTargetPath->Remove(mRecursive);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
@ -17,19 +17,22 @@ namespace dom {
|
||||
class BlobImpl;
|
||||
class Promise;
|
||||
|
||||
class RemoveTask final
|
||||
: public FileSystemTaskBase
|
||||
class RemoveTask final : public FileSystemTaskBase
|
||||
{
|
||||
public:
|
||||
RemoveTask(FileSystemBase* aFileSystem,
|
||||
const nsAString& aDirPath,
|
||||
BlobImpl* aTargetBlob,
|
||||
const nsAString& aTargetPath,
|
||||
bool aRecursive,
|
||||
ErrorResult& aRv);
|
||||
RemoveTask(FileSystemBase* aFileSystem,
|
||||
const FileSystemRemoveParams& aParam,
|
||||
FileSystemRequestParent* aParent);
|
||||
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);
|
||||
|
||||
virtual
|
||||
~RemoveTask();
|
||||
@ -42,13 +45,15 @@ public:
|
||||
|
||||
protected:
|
||||
virtual FileSystemParams
|
||||
GetRequestParams(const nsString& aFileSystem) const override;
|
||||
GetRequestParams(const nsString& aSerializedDOMPath,
|
||||
ErrorResult& aRv) const override;
|
||||
|
||||
virtual FileSystemResponseValue
|
||||
GetSuccessRequestResult() const override;
|
||||
GetSuccessRequestResult(ErrorResult& aRv) const override;
|
||||
|
||||
virtual void
|
||||
SetSuccessRequestResult(const FileSystemResponseValue& aValue) override;
|
||||
SetSuccessRequestResult(const FileSystemResponseValue& aValue,
|
||||
ErrorResult& aRv) override;
|
||||
|
||||
virtual nsresult
|
||||
Work() override;
|
||||
@ -57,12 +62,23 @@ 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;
|
||||
nsString mDirRealPath;
|
||||
nsCOMPtr<nsIFile> mDirPath;
|
||||
|
||||
// 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;
|
||||
nsString mTargetRealPath;
|
||||
nsCOMPtr<nsIFile> mTargetPath;
|
||||
bool mRecursive;
|
||||
bool mReturnValue;
|
||||
};
|
||||
|
@ -4,6 +4,8 @@
|
||||
# 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',
|
||||
|
5
dom/filesystem/tests/mochitest.ini
Normal file
5
dom/filesystem/tests/mochitest.ini
Normal file
@ -0,0 +1,5 @@
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
script_fileList.js
|
||||
|
||||
[test_basic.html]
|
7
dom/filesystem/tests/moz.build
Normal file
7
dom/filesystem/tests/moz.build
Normal file
@ -0,0 +1,7 @@
|
||||
# -*- 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']
|
13
dom/filesystem/tests/script_fileList.js
Normal file
13
dom/filesystem/tests/script_fileList.js
Normal file
@ -0,0 +1,13 @@
|
||||
var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
Cu.importGlobalProperties(["File"]);
|
||||
|
||||
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
|
||||
});
|
||||
});
|
103
dom/filesystem/tests/test_basic.html
Normal file
103
dom/filesystem/tests/test_basic.html
Normal file
@ -0,0 +1,103 @@
|
||||
<!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) {
|
||||
return 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>
|
@ -292,15 +292,8 @@ HTMLButtonElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
|
||||
eKeyPress == aVisitor.mEvent->mMessage) ||
|
||||
(keyEvent->keyCode == NS_VK_SPACE &&
|
||||
eKeyUp == aVisitor.mEvent->mMessage)) {
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
|
||||
WidgetMouseEvent event(aVisitor.mEvent->mFlags.mIsTrusted,
|
||||
eMouseClick, nullptr,
|
||||
WidgetMouseEvent::eReal);
|
||||
event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD;
|
||||
EventDispatcher::Dispatch(static_cast<nsIContent*>(this),
|
||||
aVisitor.mPresContext, &event, nullptr,
|
||||
&status);
|
||||
DispatchSimulatedClick(this, aVisitor.mEvent->mFlags.mIsTrusted,
|
||||
aVisitor.mPresContext);
|
||||
aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
}
|
||||
|
@ -11,8 +11,6 @@
|
||||
#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"
|
||||
|
||||
@ -251,16 +249,63 @@ class HTMLInputElementState final : public nsISupports
|
||||
mValue = aValue;
|
||||
}
|
||||
|
||||
const nsTArray<RefPtr<BlobImpl>>& GetBlobImpls()
|
||||
void
|
||||
GetFilesOrDirectories(nsPIDOMWindowInner* aWindow,
|
||||
nsTArray<OwningFileOrDirectory>& aResult) const
|
||||
{
|
||||
return mBlobImpls;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SetBlobImpls(const nsTArray<RefPtr<File>>& aFile)
|
||||
void SetFilesOrDirectories(const nsTArray<OwningFileOrDirectory>& aArray)
|
||||
{
|
||||
mBlobImpls.Clear();
|
||||
for (uint32_t i = 0, len = aFile.Length(); i < len; ++i) {
|
||||
mBlobImpls.AppendElement(aFile[i]->Impl());
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -274,7 +319,9 @@ class HTMLInputElementState final : public nsISupports
|
||||
~HTMLInputElementState() {}
|
||||
|
||||
nsString mValue;
|
||||
nsTArray<RefPtr<BlobImpl>> mBlobImpls;
|
||||
|
||||
nsTArray<Directory::BlobImplOrDirectoryPath> mBlobImplsOrDirectoryPaths;
|
||||
|
||||
bool mChecked;
|
||||
bool mCheckedSet;
|
||||
};
|
||||
@ -340,33 +387,65 @@ UploadLastDir::ContentPrefCallback::HandleError(nsresult error)
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* This may return nullptr if aDomFile's implementation of
|
||||
* This may return nullptr if the DOM File'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>
|
||||
DOMFileToLocalFile(File* aDomFile)
|
||||
DOMFileOrDirectoryToLocalFile(const OwningFileOrDirectory& aData)
|
||||
{
|
||||
nsString path;
|
||||
ErrorResult rv;
|
||||
aDomFile->GetMozFullPathInternal(path, rv);
|
||||
if (rv.Failed() || path.IsEmpty()) {
|
||||
rv.SuppressException();
|
||||
return nullptr;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIFile> localFile;
|
||||
rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(path), true,
|
||||
getter_AddRefs(localFile));
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
rv.SuppressException();
|
||||
nsresult rv = NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(path), true,
|
||||
getter_AddRefs(localFile));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
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
|
||||
|
||||
|
||||
@ -383,7 +462,7 @@ HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult)
|
||||
mFilePicker->GetMode(&mode);
|
||||
|
||||
// Collect new selected filenames
|
||||
nsTArray<RefPtr<File>> newFiles;
|
||||
nsTArray<OwningFileOrDirectory> newFilesOrDirectories;
|
||||
if (mode == static_cast<int16_t>(nsIFilePicker::modeOpenMultiple)) {
|
||||
nsCOMPtr<nsISimpleEnumerator> iter;
|
||||
nsresult rv =
|
||||
@ -402,9 +481,12 @@ 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) {
|
||||
newFiles.AppendElement(static_cast<File*>(domBlob.get()));
|
||||
if (!domBlob) {
|
||||
continue;
|
||||
}
|
||||
|
||||
OwningFileOrDirectory* element = newFilesOrDirectories.AppendElement();
|
||||
element->SetAsFile() = static_cast<File*>(domBlob.get());
|
||||
}
|
||||
} else {
|
||||
MOZ_ASSERT(mode == static_cast<int16_t>(nsIFilePicker::modeOpen) ||
|
||||
@ -416,16 +498,25 @@ HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult)
|
||||
nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(tmp);
|
||||
if (blob) {
|
||||
RefPtr<File> file = static_cast<Blob*>(blob.get())->ToFile();
|
||||
newFiles.AppendElement(file);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (newFiles.IsEmpty()) {
|
||||
if (newFilesOrDirectories.IsEmpty()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Store the last used directory using the content pref service:
|
||||
nsCOMPtr<nsIFile> file = DOMFileToLocalFile(newFiles[0]);
|
||||
nsCOMPtr<nsIFile> file =
|
||||
DOMFileOrDirectoryToLocalFile(newFilesOrDirectories[0]);
|
||||
|
||||
if (file) {
|
||||
nsCOMPtr<nsIFile> lastUsedDir;
|
||||
file->GetParent(getter_AddRefs(lastUsedDir));
|
||||
@ -436,7 +527,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->SetFiles(newFiles, true);
|
||||
mInput->SetFilesOrDirectories(newFilesOrDirectories, true);
|
||||
return nsContentUtils::DispatchTrustedEvent(mInput->OwnerDoc(),
|
||||
static_cast<nsIDOMHTMLInputElement*>(mInput.get()),
|
||||
NS_LITERAL_STRING("change"), true,
|
||||
@ -672,7 +763,8 @@ HTMLInputElement::InitFilePicker(FilePickerType aType)
|
||||
// Set default directry and filename
|
||||
nsAutoString defaultName;
|
||||
|
||||
const nsTArray<RefPtr<File>>& oldFiles = GetFilesInternal();
|
||||
const nsTArray<OwningFileOrDirectory>& oldFiles =
|
||||
GetFilesOrDirectoriesInternal();
|
||||
|
||||
nsCOMPtr<nsIFilePickerShownCallback> callback =
|
||||
new HTMLInputElement::nsFilePickerShownCallback(this, filePicker);
|
||||
@ -681,18 +773,10 @@ HTMLInputElement::InitFilePicker(FilePickerType aType)
|
||||
aType != FILE_PICKER_DIRECTORY) {
|
||||
nsString path;
|
||||
|
||||
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> localFile = DOMFileOrDirectoryToLocalFile(oldFiles[0]);
|
||||
if (localFile) {
|
||||
nsCOMPtr<nsIFile> parentFile;
|
||||
rv = localFile->GetParent(getter_AddRefs(parentFile));
|
||||
nsresult rv = localFile->GetParent(getter_AddRefs(parentFile));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
filePicker->SetDisplayDirectory(parentFile);
|
||||
}
|
||||
@ -703,7 +787,8 @@ HTMLInputElement::InitFilePicker(FilePickerType aType)
|
||||
// one file was selected before.
|
||||
if (oldFiles.Length() == 1) {
|
||||
nsAutoString leafName;
|
||||
oldFiles[0]->GetName(leafName);
|
||||
GetDOMFileOrDirectoryName(oldFiles[0], leafName);
|
||||
|
||||
if (!leafName.IsEmpty()) {
|
||||
filePicker->SetDefaultString(leafName);
|
||||
}
|
||||
@ -756,7 +841,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
|
||||
@ -935,7 +1020,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLInputElement,
|
||||
if (tmp->IsSingleLineTextControl(false)) {
|
||||
tmp->mInputData.mState->Traverse(cb);
|
||||
}
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFiles)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFilesOrDirectories)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFileList)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFilesAndDirectoriesPromise)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
@ -944,7 +1029,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(mFiles)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFilesOrDirectories)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFileList)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFilesAndDirectoriesPromise)
|
||||
if (tmp->IsSingleLineTextControl(false)) {
|
||||
@ -1003,8 +1088,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->mFiles.Clear();
|
||||
it->mFiles.AppendElements(mFiles);
|
||||
it->mFilesOrDirectories.Clear();
|
||||
it->mFilesOrDirectories.AppendElements(mFilesOrDirectories);
|
||||
}
|
||||
break;
|
||||
case VALUE_MODE_DEFAULT_ON:
|
||||
@ -1442,9 +1527,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 (!mFiles.IsEmpty()) {
|
||||
if (!mFilesOrDirectories.IsEmpty()) {
|
||||
ErrorResult rv;
|
||||
mFiles[0]->GetMozFullPath(aValue, rv);
|
||||
GetDOMFileOrDirectoryPath(mFilesOrDirectories[0], aValue, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
return rv.StealNSResult();
|
||||
}
|
||||
@ -1456,10 +1541,10 @@ HTMLInputElement::GetValueInternal(nsAString& aValue) const
|
||||
#endif
|
||||
} else {
|
||||
// Just return the leaf name
|
||||
if (mFiles.IsEmpty()) {
|
||||
if (mFilesOrDirectories.IsEmpty()) {
|
||||
aValue.Truncate();
|
||||
} else {
|
||||
mFiles[0]->GetName(aValue);
|
||||
GetDOMFileOrDirectoryName(mFilesOrDirectories[0], aValue);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1494,8 +1579,8 @@ HTMLInputElement::IsValueEmpty() const
|
||||
void
|
||||
HTMLInputElement::ClearFiles(bool aSetValueChanged)
|
||||
{
|
||||
nsTArray<RefPtr<File>> files;
|
||||
SetFiles(files, aSetValueChanged);
|
||||
nsTArray<OwningFileOrDirectory> data;
|
||||
SetFilesOrDirectories(data, aSetValueChanged);
|
||||
}
|
||||
|
||||
/* static */ Decimal
|
||||
@ -2065,9 +2150,9 @@ void
|
||||
HTMLInputElement::MozGetFileNameArray(nsTArray<nsString>& aArray,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
for (uint32_t i = 0; i < mFiles.Length(); i++) {
|
||||
nsString str;
|
||||
mFiles[i]->GetMozFullPathInternal(str, aRv);
|
||||
for (uint32_t i = 0; i < mFilesOrDirectories.Length(); i++) {
|
||||
nsAutoString str;
|
||||
GetDOMFileOrDirectoryPath(mFilesOrDirectories[i], str, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
@ -2117,25 +2202,29 @@ HTMLInputElement::MozSetFileArray(const Sequence<OwningNonNull<File>>& aFiles)
|
||||
if (!global) {
|
||||
return;
|
||||
}
|
||||
nsTArray<RefPtr<File>> files;
|
||||
|
||||
nsTArray<OwningFileOrDirectory> files;
|
||||
for (uint32_t i = 0; i < aFiles.Length(); ++i) {
|
||||
RefPtr<File> file = File::Create(global, aFiles[i].get()->Impl());
|
||||
MOZ_ASSERT(file);
|
||||
|
||||
files.AppendElement(file);
|
||||
OwningFileOrDirectory* element = files.AppendElement();
|
||||
element->SetAsFile() = file;
|
||||
}
|
||||
SetFiles(files, true);
|
||||
|
||||
SetFilesOrDirectories(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<RefPtr<File>> files;
|
||||
nsTArray<OwningFileOrDirectory> files;
|
||||
for (uint32_t i = 0; i < aFileNames.Length(); ++i) {
|
||||
nsCOMPtr<nsIFile> file;
|
||||
|
||||
@ -2152,21 +2241,28 @@ HTMLInputElement::MozSetFileNameArray(const Sequence< nsString >& aFileNames, Er
|
||||
NS_NewLocalFile(aFileNames[i], false, getter_AddRefs(file));
|
||||
}
|
||||
|
||||
if (file) {
|
||||
nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
|
||||
RefPtr<File> domFile = File::CreateFromFile(global, file);
|
||||
files.AppendElement(domFile);
|
||||
} else {
|
||||
if (!file) {
|
||||
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;
|
||||
}
|
||||
|
||||
SetFiles(files, true);
|
||||
SetFilesOrDirectories(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
|
||||
@ -2188,6 +2284,34 @@ HTMLInputElement::MozSetFileNameArray(const char16_t** aFileNames, uint32_t aLen
|
||||
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)
|
||||
{
|
||||
@ -2379,14 +2503,14 @@ HTMLInputElement::GetDisplayFileName(nsAString& aValue) const
|
||||
return;
|
||||
}
|
||||
|
||||
if (mFiles.Length() == 1) {
|
||||
mFiles[0]->GetName(aValue);
|
||||
if (mFilesOrDirectories.Length() == 1) {
|
||||
GetDOMFileOrDirectoryName(mFilesOrDirectories[0], aValue);
|
||||
return;
|
||||
}
|
||||
|
||||
nsXPIDLString value;
|
||||
|
||||
if (mFiles.IsEmpty()) {
|
||||
if (mFilesOrDirectories.IsEmpty()) {
|
||||
if (HasAttr(kNameSpaceID_None, nsGkAtoms::multiple)) {
|
||||
nsContentUtils::GetLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
|
||||
"NoFilesSelected", value);
|
||||
@ -2396,7 +2520,7 @@ HTMLInputElement::GetDisplayFileName(nsAString& aValue) const
|
||||
}
|
||||
} else {
|
||||
nsString count;
|
||||
count.AppendInt(int(mFiles.Length()));
|
||||
count.AppendInt(int(mFilesOrDirectories.Length()));
|
||||
|
||||
const char16_t* params[] = { count.get() };
|
||||
nsContentUtils::FormatLocalizedString(nsContentUtils::eFORMS_PROPERTIES,
|
||||
@ -2407,13 +2531,13 @@ HTMLInputElement::GetDisplayFileName(nsAString& aValue) const
|
||||
}
|
||||
|
||||
void
|
||||
HTMLInputElement::SetFiles(const nsTArray<RefPtr<File>>& aFiles,
|
||||
bool aSetValueChanged)
|
||||
HTMLInputElement::SetFilesOrDirectories(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories,
|
||||
bool aSetValueChanged)
|
||||
{
|
||||
mFiles.Clear();
|
||||
mFiles.AppendElements(aFiles);
|
||||
mFilesOrDirectories.Clear();
|
||||
mFilesOrDirectories.AppendElements(aFilesOrDirectories);
|
||||
|
||||
AfterSetFiles(aSetValueChanged);
|
||||
AfterSetFilesOrDirectories(aSetValueChanged);
|
||||
}
|
||||
|
||||
void
|
||||
@ -2421,22 +2545,22 @@ HTMLInputElement::SetFiles(nsIDOMFileList* aFiles,
|
||||
bool aSetValueChanged)
|
||||
{
|
||||
RefPtr<FileList> files = static_cast<FileList*>(aFiles);
|
||||
mFiles.Clear();
|
||||
mFilesOrDirectories.Clear();
|
||||
|
||||
if (aFiles) {
|
||||
uint32_t listLength;
|
||||
aFiles->GetLength(&listLength);
|
||||
for (uint32_t i = 0; i < listLength; i++) {
|
||||
RefPtr<File> file = files->Item(i);
|
||||
mFiles.AppendElement(file);
|
||||
OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement();
|
||||
*element = files->UnsafeItem(i);
|
||||
}
|
||||
}
|
||||
|
||||
AfterSetFiles(aSetValueChanged);
|
||||
AfterSetFilesOrDirectories(aSetValueChanged);
|
||||
}
|
||||
|
||||
void
|
||||
HTMLInputElement::AfterSetFiles(bool aSetValueChanged)
|
||||
HTMLInputElement::AfterSetFilesOrDirectories(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
|
||||
@ -2454,11 +2578,11 @@ HTMLInputElement::AfterSetFiles(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 (mFiles.IsEmpty()) {
|
||||
if (mFilesOrDirectories.IsEmpty()) {
|
||||
mFirstFilePath.Truncate();
|
||||
} else {
|
||||
ErrorResult rv;
|
||||
mFiles[0]->GetMozFullPath(mFirstFilePath, rv);
|
||||
GetDOMFileOrDirectoryPath(mFilesOrDirectories[0], mFirstFilePath, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
rv.SuppressException();
|
||||
}
|
||||
@ -2532,24 +2656,27 @@ HTMLInputElement::HandleNumberControlSpin(void* aData)
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
void
|
||||
HTMLInputElement::UpdateFileList()
|
||||
{
|
||||
if (mFileList) {
|
||||
mFileList->Clear();
|
||||
|
||||
const nsTArray<RefPtr<File>>& files = GetFilesInternal();
|
||||
for (uint32_t i = 0; i < files.Length(); ++i) {
|
||||
if (!mFileList->Append(files[i])) {
|
||||
return NS_ERROR_FAILURE;
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we (lazily) create a new Promise for GetFilesAndDirectories:
|
||||
mFilesAndDirectoriesPromise = nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
@ -3824,15 +3951,8 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
|
||||
case NS_FORM_INPUT_IMAGE: // Bug 34418
|
||||
case NS_FORM_INPUT_COLOR:
|
||||
{
|
||||
WidgetMouseEvent event(aVisitor.mEvent->mFlags.mIsTrusted,
|
||||
eMouseClick, nullptr,
|
||||
WidgetMouseEvent::eReal);
|
||||
event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD;
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
|
||||
EventDispatcher::Dispatch(static_cast<nsIContent*>(this),
|
||||
aVisitor.mPresContext, &event,
|
||||
nullptr, &status);
|
||||
DispatchSimulatedClick(this, aVisitor.mEvent->mFlags.mIsTrusted,
|
||||
aVisitor.mPresContext);
|
||||
aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
|
||||
} // case
|
||||
} // switch
|
||||
@ -3859,15 +3979,9 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
|
||||
if (selectedRadioButton) {
|
||||
rv = selectedRadioButton->Focus();
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
nsEventStatus status = nsEventStatus_eIgnore;
|
||||
WidgetMouseEvent event(aVisitor.mEvent->mFlags.mIsTrusted,
|
||||
eMouseClick, nullptr,
|
||||
WidgetMouseEvent::eReal);
|
||||
event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD;
|
||||
rv =
|
||||
EventDispatcher::Dispatch(ToSupports(selectedRadioButton),
|
||||
aVisitor.mPresContext,
|
||||
&event, nullptr, &status);
|
||||
rv = DispatchSimulatedClick(selectedRadioButton,
|
||||
aVisitor.mEvent->mFlags.mIsTrusted,
|
||||
aVisitor.mPresContext);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
@ -3988,7 +4102,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())) {
|
||||
@ -4539,7 +4653,7 @@ HTMLInputElement::GetValueAsDate(const nsAString& aValue,
|
||||
}
|
||||
|
||||
uint32_t endOfYearOffset = aValue.Length() - 6;
|
||||
|
||||
|
||||
if (aValue[endOfYearOffset] != '-' ||
|
||||
aValue[endOfYearOffset + 3] != '-') {
|
||||
return false;
|
||||
@ -4907,45 +5021,38 @@ HTMLInputElement::GetFilesAndDirectories(ErrorResult& aRv)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const nsTArray<RefPtr<File>>& filesAndDirs = GetFilesInternal();
|
||||
const nsTArray<OwningFileOrDirectory>& filesAndDirs =
|
||||
GetFilesOrDirectoriesInternal();
|
||||
|
||||
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<OSFileSystem> fs = new OSFileSystem(dirname);
|
||||
fs->Init(OwnerDoc()->GetInnerWindow());
|
||||
RefPtr<Directory> directory = filesAndDirs[i].GetAsDirectory();
|
||||
|
||||
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];
|
||||
filesAndDirsSeq[i].SetAsFile() = filesAndDirs[i].GetAsFile();
|
||||
}
|
||||
}
|
||||
|
||||
@ -5579,13 +5686,18 @@ HTMLInputElement::SubmitNamesValues(nsFormSubmission* aFormSubmission)
|
||||
if (mType == NS_FORM_INPUT_FILE) {
|
||||
// Submit files
|
||||
|
||||
const nsTArray<RefPtr<File>>& files = GetFilesInternal();
|
||||
const nsTArray<OwningFileOrDirectory>& files =
|
||||
GetFilesOrDirectoriesInternal();
|
||||
|
||||
bool hasBlobs = false;
|
||||
for (uint32_t i = 0; i < files.Length(); ++i) {
|
||||
aFormSubmission->AddNameBlobOrNullPair(name, files[i]);
|
||||
if (files[i].IsFile()) {
|
||||
hasBlobs = true;
|
||||
aFormSubmission->AddNameBlobOrNullPair(name, files[i].GetAsFile());
|
||||
}
|
||||
}
|
||||
|
||||
if (files.IsEmpty()) {
|
||||
if (!hasBlobs) {
|
||||
aFormSubmission->AddNameBlobOrNullPair(name, nullptr);
|
||||
}
|
||||
|
||||
@ -5619,9 +5731,9 @@ HTMLInputElement::SaveState()
|
||||
}
|
||||
break;
|
||||
case VALUE_MODE_FILENAME:
|
||||
if (!mFiles.IsEmpty()) {
|
||||
if (!mFilesOrDirectories.IsEmpty()) {
|
||||
inputState = new HTMLInputElementState();
|
||||
inputState->SetBlobImpls(mFiles);
|
||||
inputState->SetFilesOrDirectories(mFilesOrDirectories);
|
||||
}
|
||||
break;
|
||||
case VALUE_MODE_VALUE:
|
||||
@ -5790,7 +5902,7 @@ HTMLInputElement::AddStates(EventStates aStates)
|
||||
}
|
||||
}
|
||||
}
|
||||
nsGenericHTMLFormElementWithState::AddStates(aStates);
|
||||
nsGenericHTMLFormElementWithState::AddStates(aStates);
|
||||
}
|
||||
|
||||
void
|
||||
@ -5827,20 +5939,13 @@ HTMLInputElement::RestoreState(nsPresState* aState)
|
||||
break;
|
||||
case VALUE_MODE_FILENAME:
|
||||
{
|
||||
const nsTArray<RefPtr<BlobImpl>>& blobImpls = inputState->GetBlobImpls();
|
||||
nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow();
|
||||
if (window) {
|
||||
nsTArray<OwningFileOrDirectory> array;
|
||||
inputState->GetFilesOrDirectories(window, array);
|
||||
|
||||
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);
|
||||
SetFilesOrDirectories(array, true);
|
||||
}
|
||||
|
||||
SetFiles(files, true);
|
||||
}
|
||||
break;
|
||||
case VALUE_MODE_VALUE:
|
||||
@ -6350,15 +6455,15 @@ HTMLInputElement::IsValueMissing() const
|
||||
switch (GetValueMode()) {
|
||||
case VALUE_MODE_VALUE:
|
||||
return IsValueEmpty();
|
||||
|
||||
case VALUE_MODE_FILENAME:
|
||||
{
|
||||
const nsTArray<RefPtr<File>>& files = GetFilesInternal();
|
||||
return files.IsEmpty();
|
||||
}
|
||||
return GetFilesOrDirectoriesInternal().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;
|
||||
|
@ -20,6 +20,7 @@
|
||||
#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"
|
||||
@ -220,12 +221,13 @@ public:
|
||||
|
||||
void GetDisplayFileName(nsAString& aFileName) const;
|
||||
|
||||
const nsTArray<RefPtr<File>>& GetFilesInternal() const
|
||||
const nsTArray<OwningFileOrDirectory>& GetFilesOrDirectoriesInternal() const
|
||||
{
|
||||
return mFiles;
|
||||
return mFilesOrDirectories;
|
||||
}
|
||||
|
||||
void SetFiles(const nsTArray<RefPtr<File>>& aFiles, bool aSetValueChanged);
|
||||
void SetFilesOrDirectories(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories,
|
||||
bool aSetValueChanged);
|
||||
void SetFiles(nsIDOMFileList* aFiles, bool aSetValueChanged);
|
||||
|
||||
// Called when a nsIFilePicker or a nsIColorPicker terminate.
|
||||
@ -722,6 +724,7 @@ 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();
|
||||
|
||||
@ -922,12 +925,12 @@ protected:
|
||||
/**
|
||||
* Update mFileList with the currently selected file.
|
||||
*/
|
||||
nsresult UpdateFileList();
|
||||
void UpdateFileList();
|
||||
|
||||
/**
|
||||
* Called after calling one of the SetFiles() functions.
|
||||
* Called after calling one of the SetFilesOrDirectories() functions.
|
||||
*/
|
||||
void AfterSetFiles(bool aSetValueChanged);
|
||||
void AfterSetFilesOrDirectories(bool aSetValueChanged);
|
||||
|
||||
/**
|
||||
* Determine whether the editor needs to be initialized explicitly for
|
||||
@ -1266,16 +1269,16 @@ protected:
|
||||
} mInputData;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* 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.
|
||||
*/
|
||||
nsTArray<RefPtr<File>> mFiles;
|
||||
nsTArray<OwningFileOrDirectory> mFilesOrDirectories;
|
||||
|
||||
#ifndef MOZ_CHILD_PERMISSIONS
|
||||
/**
|
||||
|
@ -10,6 +10,8 @@
|
||||
#include "mozilla/dom/HTMLUnknownElement.h"
|
||||
#include "mozilla/MouseEvents.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/TextEvents.h"
|
||||
#include "nsFocusManager.h"
|
||||
|
||||
// Expand NS_IMPL_NS_NEW_HTML_ELEMENT(Summary) to add pref check.
|
||||
nsGenericHTMLElement*
|
||||
@ -44,34 +46,99 @@ HTMLSummaryElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
|
||||
return rv;
|
||||
}
|
||||
|
||||
auto toggleDetails = false;
|
||||
auto* event = aVisitor.mEvent;
|
||||
|
||||
if (event->HasMouseEventMessage()) {
|
||||
auto* mouseEvent = event->AsMouseEvent();
|
||||
toggleDetails = mouseEvent->IsLeftClickEvent();
|
||||
}
|
||||
|
||||
// Todo: Bug 634004: Implement toggle details by keyboard.
|
||||
|
||||
if (!toggleDetails || !IsMainSummary()) {
|
||||
if (!IsMainSummary()) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
auto* details = GetDetails();
|
||||
MOZ_ASSERT(details, "Expected to find details since this is the main summary!");
|
||||
WidgetEvent* const event = aVisitor.mEvent;
|
||||
|
||||
// When dispatching a synthesized mouse click event to a details with
|
||||
// 'display: none', both Chrome and Safari do not toggle the 'open' attribute.
|
||||
// We follow them by checking whether details has a frame or not.
|
||||
if (details->GetPrimaryFrame()) {
|
||||
details->ToggleOpen();
|
||||
aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
if (event->HasMouseEventMessage()) {
|
||||
WidgetMouseEvent* mouseEvent = event->AsMouseEvent();
|
||||
|
||||
if (mouseEvent->IsLeftClickEvent()) {
|
||||
RefPtr<HTMLDetailsElement> details = GetDetails();
|
||||
MOZ_ASSERT(details,
|
||||
"Expected to find details since this is the main summary!");
|
||||
|
||||
// When dispatching a synthesized mouse click event to a details element
|
||||
// with 'display: none', both Chrome and Safari do not toggle the 'open'
|
||||
// attribute. We follow them by checking whether the details element has a
|
||||
// frame or not.
|
||||
if (details->GetPrimaryFrame(Flush_Frames)) {
|
||||
details->ToggleOpen();
|
||||
aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
} // event->HasMouseEventMessage()
|
||||
|
||||
if (event->HasKeyEventMessage()) {
|
||||
WidgetKeyboardEvent* keyboardEvent = event->AsKeyboardEvent();
|
||||
bool dispatchClick = false;
|
||||
|
||||
switch (event->mMessage) {
|
||||
case eKeyPress:
|
||||
if (keyboardEvent->charCode == nsIDOMKeyEvent::DOM_VK_SPACE) {
|
||||
// Consume 'space' key to prevent scrolling the page down.
|
||||
aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
|
||||
dispatchClick = keyboardEvent->keyCode == nsIDOMKeyEvent::DOM_VK_RETURN;
|
||||
break;
|
||||
|
||||
case eKeyUp:
|
||||
dispatchClick = keyboardEvent->keyCode == nsIDOMKeyEvent::DOM_VK_SPACE;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (dispatchClick) {
|
||||
rv = DispatchSimulatedClick(this, event->mFlags.mIsTrusted,
|
||||
aVisitor.mPresContext);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
aVisitor.mEventStatus = nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
}
|
||||
} // event->HasKeyEventMessage()
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLSummaryElement::IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable,
|
||||
int32_t* aTabIndex)
|
||||
{
|
||||
bool disallowOverridingFocusability =
|
||||
nsGenericHTMLElement::IsHTMLFocusable(aWithMouse, aIsFocusable, aTabIndex);
|
||||
|
||||
if (disallowOverridingFocusability || !IsMainSummary()) {
|
||||
return disallowOverridingFocusability;
|
||||
}
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
// The parent does not have strong opinion about the focusability of this main
|
||||
// summary element, but we'd like to override it when mouse clicking on Mac OS
|
||||
// like other form elements.
|
||||
*aIsFocusable = !aWithMouse || nsFocusManager::sMouseFocusesFormControl;
|
||||
#else
|
||||
// The main summary element is focusable on other platforms.
|
||||
*aIsFocusable = true;
|
||||
#endif
|
||||
|
||||
// Give a chance to allow the subclass to override aIsFocusable.
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t
|
||||
HTMLSummaryElement::TabIndexDefault()
|
||||
{
|
||||
// Make the main summary be able to navigate via tab, and be focusable.
|
||||
// See nsGenericHTMLElement::IsHTMLFocusable().
|
||||
return IsMainSummary() ? 0 : nsGenericHTMLElement::TabIndexDefault();
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLSummaryElement::IsMainSummary() const
|
||||
{
|
||||
|
@ -33,6 +33,11 @@ public:
|
||||
|
||||
nsresult PostHandleEvent(EventChainPostVisitor& aVisitor) override;
|
||||
|
||||
bool IsHTMLFocusable(bool aWithMouse, bool* aIsFocusable,
|
||||
int32_t* aTabIndex) override;
|
||||
|
||||
int32_t TabIndexDefault() override;
|
||||
|
||||
// Return true if this is the first summary element child of a details or the
|
||||
// default summary element generated by DetailsFrame.
|
||||
bool IsMainSummary() const;
|
||||
|
@ -2722,11 +2722,12 @@ nsGenericHTMLElement::IsHTMLFocusable(bool aWithMouse,
|
||||
}
|
||||
|
||||
int32_t tabIndex = TabIndex();
|
||||
bool disabled = false;
|
||||
bool disallowOverridingFocusability = true;
|
||||
|
||||
bool override, disabled = false;
|
||||
if (IsEditableRoot()) {
|
||||
// Editable roots should always be focusable.
|
||||
override = true;
|
||||
disallowOverridingFocusability = true;
|
||||
|
||||
// Ignore the disabled attribute in editable contentEditable/designMode
|
||||
// roots.
|
||||
@ -2737,7 +2738,7 @@ nsGenericHTMLElement::IsHTMLFocusable(bool aWithMouse,
|
||||
}
|
||||
}
|
||||
else {
|
||||
override = false;
|
||||
disallowOverridingFocusability = false;
|
||||
|
||||
// Just check for disabled attribute on form controls
|
||||
disabled = IsDisabled();
|
||||
@ -2751,10 +2752,10 @@ nsGenericHTMLElement::IsHTMLFocusable(bool aWithMouse,
|
||||
}
|
||||
|
||||
// If a tabindex is specified at all, or the default tabindex is 0, we're focusable
|
||||
*aIsFocusable =
|
||||
*aIsFocusable =
|
||||
(tabIndex >= 0 || (!disabled && HasAttr(kNameSpaceID_None, nsGkAtoms::tabindex)));
|
||||
|
||||
return override;
|
||||
return disallowOverridingFocusability;
|
||||
}
|
||||
|
||||
void
|
||||
@ -2804,20 +2805,25 @@ nsGenericHTMLElement::PerformAccesskey(bool aKeyCausesActivation,
|
||||
|
||||
if (aKeyCausesActivation) {
|
||||
// Click on it if the users prefs indicate to do so.
|
||||
WidgetMouseEvent event(aIsTrustedEvent, eMouseClick, nullptr,
|
||||
WidgetMouseEvent::eReal);
|
||||
event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD;
|
||||
|
||||
nsAutoPopupStatePusher popupStatePusher(aIsTrustedEvent ?
|
||||
openAllowed : openAbused);
|
||||
|
||||
EventDispatcher::Dispatch(static_cast<nsIContent*>(this),
|
||||
presContext, &event);
|
||||
DispatchSimulatedClick(this, aIsTrustedEvent, presContext);
|
||||
}
|
||||
|
||||
return focused;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsGenericHTMLElement::DispatchSimulatedClick(nsGenericHTMLElement* aElement,
|
||||
bool aIsTrusted,
|
||||
nsPresContext* aPresContext)
|
||||
{
|
||||
WidgetMouseEvent event(aIsTrusted, eMouseClick, nullptr,
|
||||
WidgetMouseEvent::eReal);
|
||||
event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD;
|
||||
return EventDispatcher::Dispatch(ToSupports(aElement), aPresContext, &event);
|
||||
}
|
||||
|
||||
const nsAttrName*
|
||||
nsGenericHTMLElement::InternalGetExistingAttrNameFromQName(const nsAString& aStr) const
|
||||
{
|
||||
|
@ -1027,6 +1027,13 @@ protected:
|
||||
|
||||
virtual const nsAttrName* InternalGetExistingAttrNameFromQName(const nsAString& aStr) const override;
|
||||
|
||||
/**
|
||||
* Dispatch a simulated mouse click by keyboard to the given element.
|
||||
*/
|
||||
nsresult DispatchSimulatedClick(nsGenericHTMLElement* aElement,
|
||||
bool aIsTrusted,
|
||||
nsPresContext* aPresContext);
|
||||
|
||||
/**
|
||||
* Create a URI for the given aURISpec string.
|
||||
* Returns INVALID_STATE_ERR and nulls *aURI if aURISpec is empty
|
||||
|
@ -187,30 +187,6 @@ 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
|
||||
{
|
||||
|
@ -444,8 +444,7 @@ ResolveMysteryFile(BlobImpl* aImpl,
|
||||
BlobChild* actor = ActorFromRemoteBlobImpl(aImpl);
|
||||
if (actor) {
|
||||
return actor->SetMysteryBlobInfo(aName, aContentType,
|
||||
aSize, aLastModifiedDate,
|
||||
BlobDirState::eUnknownIfDir);
|
||||
aSize, aLastModifiedDate);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -670,8 +670,7 @@ public:
|
||||
EmptyBlobImpl(const nsAString& aName,
|
||||
const nsAString& aContentType,
|
||||
int64_t aLastModifiedDate)
|
||||
: BlobImplBase(aName, aContentType, 0, aLastModifiedDate,
|
||||
BlobDirState::eIsNotDir)
|
||||
: BlobImplBase(aName, aContentType, 0, aLastModifiedDate)
|
||||
{
|
||||
mImmutable = true;
|
||||
}
|
||||
@ -716,14 +715,12 @@ 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)
|
||||
{
|
||||
@ -965,7 +962,6 @@ CreateBlobImpl(const ParentBlobConstructorParams& aParams,
|
||||
metadata.mName = params.name();
|
||||
metadata.mLength = params.length();
|
||||
metadata.mLastModifiedDate = params.modDate();
|
||||
metadata.mDirState = BlobDirState(params.dirState());
|
||||
}
|
||||
|
||||
RefPtr<BlobImpl> blobImpl =
|
||||
@ -1775,7 +1771,6 @@ public:
|
||||
const nsAString& aContentType,
|
||||
uint64_t aLength,
|
||||
int64_t aModDate,
|
||||
BlobDirState aDirState,
|
||||
bool aIsSameProcessBlob);
|
||||
|
||||
// For Blob.
|
||||
@ -2053,18 +2048,6 @@ 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;
|
||||
|
||||
@ -2096,9 +2079,8 @@ RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
|
||||
const nsAString& aContentType,
|
||||
uint64_t aLength,
|
||||
int64_t aModDate,
|
||||
BlobDirState aDirState,
|
||||
bool aIsSameProcessBlob)
|
||||
: BlobImplBase(aName, aContentType, aLength, aModDate, aDirState)
|
||||
: BlobImplBase(aName, aContentType, aLength, aModDate)
|
||||
, mIsSlice(false)
|
||||
{
|
||||
if (aIsSameProcessBlob) {
|
||||
@ -2814,34 +2796,6 @@ 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
|
||||
@ -3033,8 +2987,7 @@ BlobChild::CommonInit(BlobChild* aOther, BlobImpl* aBlobImpl)
|
||||
MOZ_ASSERT(!rv.Failed());
|
||||
|
||||
remoteBlob = new RemoteBlobImpl(this, otherImpl, name, contentType, length,
|
||||
modDate, otherImpl->GetDirState(),
|
||||
false /* SameProcessBlobImpl */);
|
||||
modDate, false /* SameProcessBlobImpl */);
|
||||
} else {
|
||||
remoteBlob = new RemoteBlobImpl(this, otherImpl, contentType, length,
|
||||
false /* SameProcessBlobImpl */);
|
||||
@ -3086,7 +3039,6 @@ BlobChild::CommonInit(const ChildBlobConstructorParams& aParams)
|
||||
params.contentType(),
|
||||
params.length(),
|
||||
params.modDate(),
|
||||
BlobDirState(params.dirState()),
|
||||
false /* SameProcessBlobImpl */);
|
||||
break;
|
||||
}
|
||||
@ -3122,7 +3074,6 @@ BlobChild::CommonInit(const ChildBlobConstructorParams& aParams)
|
||||
contentType,
|
||||
size,
|
||||
lastModifiedDate,
|
||||
blobImpl->GetDirState(),
|
||||
true /* SameProcessBlobImpl */);
|
||||
} else {
|
||||
remoteBlob = new RemoteBlobImpl(this, blobImpl, contentType, size,
|
||||
@ -3303,8 +3254,7 @@ BlobChild::GetOrCreateFromImpl(ChildManagerType* aManager,
|
||||
MOZ_ASSERT(!rv.Failed());
|
||||
|
||||
blobParams =
|
||||
FileBlobConstructorParams(name, contentType, length, modDate,
|
||||
aBlobImpl->GetDirState(), blobData);
|
||||
FileBlobConstructorParams(name, contentType, length, modDate, blobData);
|
||||
} else {
|
||||
blobParams = NormalBlobConstructorParams(contentType, length, blobData);
|
||||
}
|
||||
@ -3477,8 +3427,7 @@ bool
|
||||
BlobChild::SetMysteryBlobInfo(const nsString& aName,
|
||||
const nsString& aContentType,
|
||||
uint64_t aLength,
|
||||
int64_t aLastModifiedDate,
|
||||
BlobDirState aDirState)
|
||||
int64_t aLastModifiedDate)
|
||||
{
|
||||
AssertIsOnOwningThread();
|
||||
MOZ_ASSERT(mBlobImpl);
|
||||
@ -3491,7 +3440,6 @@ BlobChild::SetMysteryBlobInfo(const nsString& aName,
|
||||
aContentType,
|
||||
aLength,
|
||||
aLastModifiedDate,
|
||||
aDirState,
|
||||
void_t() /* optionalBlobData */);
|
||||
return SendResolveMystery(params);
|
||||
}
|
||||
@ -3854,7 +3802,7 @@ BlobParent::GetOrCreateFromImpl(ParentManagerType* aManager,
|
||||
|
||||
blobParams =
|
||||
FileBlobConstructorParams(name, contentType, length, modDate,
|
||||
aBlobImpl->GetDirState(), void_t());
|
||||
void_t());
|
||||
} else {
|
||||
blobParams = NormalBlobConstructorParams(contentType, length, void_t());
|
||||
}
|
||||
|
@ -31,8 +31,6 @@ class ContentChild;
|
||||
class nsIContentChild;
|
||||
class PBlobStreamChild;
|
||||
|
||||
enum BlobDirState : uint32_t;
|
||||
|
||||
class BlobChild final
|
||||
: public PBlobChild
|
||||
{
|
||||
@ -117,8 +115,7 @@ public:
|
||||
SetMysteryBlobInfo(const nsString& aName,
|
||||
const nsString& aContentType,
|
||||
uint64_t aLength,
|
||||
int64_t aLastModifiedDate,
|
||||
BlobDirState aDirState);
|
||||
int64_t aLastModifiedDate);
|
||||
|
||||
// Use this for non-file blobs.
|
||||
bool
|
||||
|
@ -73,7 +73,6 @@ 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.
|
||||
|
@ -53,15 +53,18 @@ 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::FileSizeAndDateRunnable::FileSizeAndDateRunnable(FilePickerParent *aFPParent,
|
||||
nsTArray<RefPtr<BlobImpl>>& aBlobs)
|
||||
FilePickerParent::IORunnable::IORunnable(FilePickerParent *aFPParent,
|
||||
nsTArray<nsCOMPtr<nsIFile>>& aFiles,
|
||||
bool aIsDirectory)
|
||||
: mFilePickerParent(aFPParent)
|
||||
, mIsDirectory(aIsDirectory)
|
||||
{
|
||||
mBlobs.SwapElements(aBlobs);
|
||||
mFiles.SwapElements(aFiles);
|
||||
MOZ_ASSERT_IF(aIsDirectory, mFiles.Length() == 1);
|
||||
}
|
||||
|
||||
bool
|
||||
FilePickerParent::FileSizeAndDateRunnable::Dispatch()
|
||||
FilePickerParent::IORunnable::Dispatch()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
@ -75,23 +78,49 @@ FilePickerParent::FileSizeAndDateRunnable::Dispatch()
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FilePickerParent::FileSizeAndDateRunnable::Run()
|
||||
FilePickerParent::IORunnable::Run()
|
||||
{
|
||||
// If we're on the main thread, then that means we're done. Just send the
|
||||
// results.
|
||||
if (NS_IsMainThread()) {
|
||||
if (mFilePickerParent) {
|
||||
mFilePickerParent->SendFiles(mBlobs);
|
||||
mFilePickerParent->SendFilesOrDirectories(mResults);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// 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();
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Dispatch ourselves back on the main thread.
|
||||
@ -101,29 +130,46 @@ FilePickerParent::FileSizeAndDateRunnable::Run()
|
||||
// thread.
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
FilePickerParent::FileSizeAndDateRunnable::Destroy()
|
||||
FilePickerParent::IORunnable::Destroy()
|
||||
{
|
||||
mFilePickerParent = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
FilePickerParent::SendFiles(const nsTArray<RefPtr<BlobImpl>>& aBlobs)
|
||||
FilePickerParent::SendFilesOrDirectories(const nsTArray<BlobImplOrString>& aData)
|
||||
{
|
||||
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 < aBlobs.Length(); i++) {
|
||||
BlobParent* blobParent = parent->GetOrCreateActorForBlobImpl(aBlobs[i]);
|
||||
for (unsigned i = 0; i < aData.Length(); i++) {
|
||||
MOZ_ASSERT(aData[i].mType == BlobImplOrString::eBlobImpl);
|
||||
BlobParent* blobParent = parent->GetOrCreateActorForBlobImpl(aData[i].mBlobImpl);
|
||||
if (blobParent) {
|
||||
blobs.AppendElement(blobParent);
|
||||
}
|
||||
}
|
||||
|
||||
InputFiles inblobs;
|
||||
InputBlobs inblobs;
|
||||
inblobs.blobsParent().SwapElements(blobs);
|
||||
Unused << Send__delete__(this, inblobs, mResult);
|
||||
}
|
||||
@ -138,7 +184,7 @@ FilePickerParent::Done(int16_t aResult)
|
||||
return;
|
||||
}
|
||||
|
||||
nsTArray<RefPtr<BlobImpl>> blobs;
|
||||
nsTArray<nsCOMPtr<nsIFile>> files;
|
||||
if (mMode == nsIFilePicker::modeOpenMultiple) {
|
||||
nsCOMPtr<nsISimpleEnumerator> iter;
|
||||
NS_ENSURE_SUCCESS_VOID(mFilePicker->GetFiles(getter_AddRefs(iter)));
|
||||
@ -149,22 +195,26 @@ FilePickerParent::Done(int16_t aResult)
|
||||
iter->GetNext(getter_AddRefs(supports));
|
||||
if (supports) {
|
||||
nsCOMPtr<nsIFile> file = do_QueryInterface(supports);
|
||||
|
||||
RefPtr<BlobImpl> blobimpl = new BlobImplFile(file);
|
||||
blobs.AppendElement(blobimpl);
|
||||
MOZ_ASSERT(file);
|
||||
files.AppendElement(file);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
nsCOMPtr<nsIFile> file;
|
||||
mFilePicker->GetFile(getter_AddRefs(file));
|
||||
if (file) {
|
||||
RefPtr<BlobImpl> blobimpl = new BlobImplFile(file);
|
||||
blobs.AppendElement(blobimpl);
|
||||
files.AppendElement(file);
|
||||
}
|
||||
}
|
||||
|
||||
if (files.IsEmpty()) {
|
||||
Unused << Send__delete__(this, void_t(), mResult);
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!mRunnable);
|
||||
mRunnable = new FileSizeAndDateRunnable(this, blobs);
|
||||
mRunnable = new IORunnable(this, files, mMode == nsIFilePicker::modeGetFolder);
|
||||
|
||||
// Dispatch to background thread to do I/O:
|
||||
if (!mRunnable->Dispatch()) {
|
||||
Unused << Send__delete__(this, void_t(), nsIFilePicker::returnCancel);
|
||||
|
@ -14,6 +14,8 @@
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/PFilePickerParent.h"
|
||||
|
||||
class nsIFile;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
@ -29,7 +31,19 @@ class FilePickerParent : public PFilePickerParent
|
||||
virtual ~FilePickerParent();
|
||||
|
||||
void Done(int16_t aResult);
|
||||
void SendFiles(const nsTArray<RefPtr<BlobImpl>>& aDomBlobs);
|
||||
|
||||
struct BlobImplOrString
|
||||
{
|
||||
RefPtr<BlobImpl> mBlobImpl;
|
||||
nsString mDirectoryPath;
|
||||
|
||||
enum {
|
||||
eBlobImpl,
|
||||
eDirectoryPath
|
||||
} mType;
|
||||
};
|
||||
|
||||
void SendFilesOrDirectories(const nsTArray<BlobImplOrString>& aData);
|
||||
|
||||
virtual bool RecvOpen(const int16_t& aSelectedType,
|
||||
const bool& aAddToRecentDocs,
|
||||
@ -61,21 +75,26 @@ class FilePickerParent : public PFilePickerParent
|
||||
private:
|
||||
bool CreateFilePicker();
|
||||
|
||||
class FileSizeAndDateRunnable : public nsRunnable
|
||||
// This runnable is used to do some I/O operation on a separate thread.
|
||||
class IORunnable : public nsRunnable
|
||||
{
|
||||
FilePickerParent* mFilePickerParent;
|
||||
nsTArray<RefPtr<BlobImpl>> mBlobs;
|
||||
nsTArray<nsCOMPtr<nsIFile>> mFiles;
|
||||
nsTArray<BlobImplOrString> mResults;
|
||||
nsCOMPtr<nsIEventTarget> mEventTarget;
|
||||
bool mIsDirectory;
|
||||
|
||||
public:
|
||||
FileSizeAndDateRunnable(FilePickerParent *aFPParent,
|
||||
nsTArray<RefPtr<BlobImpl>>& aBlobs);
|
||||
IORunnable(FilePickerParent *aFPParent,
|
||||
nsTArray<nsCOMPtr<nsIFile>>& aFiles,
|
||||
bool aIsDirectory);
|
||||
|
||||
bool Dispatch();
|
||||
NS_IMETHOD Run();
|
||||
void Destroy();
|
||||
};
|
||||
|
||||
RefPtr<FileSizeAndDateRunnable> mRunnable;
|
||||
RefPtr<IORunnable> mRunnable;
|
||||
RefPtr<FilePickerShownCallback> mCallback;
|
||||
nsCOMPtr<nsIFilePicker> mFilePicker;
|
||||
|
||||
|
@ -289,6 +289,7 @@ 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,
|
||||
@ -305,6 +306,7 @@ struct FileSystemGetFileOrDirectoryParams
|
||||
{
|
||||
nsString filesystem;
|
||||
nsString realPath;
|
||||
bool isRoot;
|
||||
};
|
||||
|
||||
union FileSystemPathOrFileValue
|
||||
|
@ -12,14 +12,20 @@ using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct InputFiles
|
||||
struct InputBlobs
|
||||
{
|
||||
PBlob[] blobs;
|
||||
};
|
||||
|
||||
union MaybeInputFiles
|
||||
struct InputDirectory
|
||||
{
|
||||
InputFiles;
|
||||
nsString directoryPath;
|
||||
};
|
||||
|
||||
union MaybeInputData
|
||||
{
|
||||
InputBlobs;
|
||||
InputDirectory;
|
||||
void_t;
|
||||
};
|
||||
|
||||
@ -33,7 +39,7 @@ parent:
|
||||
nsString displayDirectory);
|
||||
|
||||
child:
|
||||
async __delete__(MaybeInputFiles files, int16_t result);
|
||||
async __delete__(MaybeInputData data, int16_t result);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
|
@ -87,6 +87,14 @@ void FileBlockCache::Close()
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Container, typename Value>
|
||||
bool
|
||||
ContainerContains(const Container& aContainer, const Value& value)
|
||||
{
|
||||
return std::find(aContainer.begin(), aContainer.end(), value)
|
||||
!= aContainer.end();
|
||||
}
|
||||
|
||||
nsresult FileBlockCache::WriteBlock(uint32_t aBlockIndex, const uint8_t* aData)
|
||||
{
|
||||
MonitorAutoLock mon(mDataMonitor);
|
||||
@ -99,16 +107,16 @@ nsresult FileBlockCache::WriteBlock(uint32_t aBlockIndex, const uint8_t* aData)
|
||||
bool blockAlreadyHadPendingChange = mBlockChanges[aBlockIndex] != nullptr;
|
||||
mBlockChanges[aBlockIndex] = new BlockChange(aData);
|
||||
|
||||
if (!blockAlreadyHadPendingChange || !mChangeIndexList.Contains(aBlockIndex)) {
|
||||
if (!blockAlreadyHadPendingChange || !ContainerContains(mChangeIndexList, aBlockIndex)) {
|
||||
// We either didn't already have a pending change for this block, or we
|
||||
// did but we didn't have an entry for it in mChangeIndexList (we're in the process
|
||||
// of writing it and have removed the block's index out of mChangeIndexList
|
||||
// in Run() but not finished writing the block to file yet). Add the blocks
|
||||
// index to the end of mChangeIndexList to ensure the block is written as
|
||||
// as soon as possible.
|
||||
mChangeIndexList.PushBack(aBlockIndex);
|
||||
mChangeIndexList.push_back(aBlockIndex);
|
||||
}
|
||||
NS_ASSERTION(mChangeIndexList.Contains(aBlockIndex), "Must have entry for new block");
|
||||
NS_ASSERTION(ContainerContains(mChangeIndexList, aBlockIndex), "Must have entry for new block");
|
||||
|
||||
EnsureWriteScheduled();
|
||||
|
||||
@ -196,10 +204,10 @@ nsresult FileBlockCache::Run()
|
||||
{
|
||||
MonitorAutoLock mon(mDataMonitor);
|
||||
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
|
||||
NS_ASSERTION(!mChangeIndexList.IsEmpty(), "Only dispatch when there's work to do");
|
||||
NS_ASSERTION(!mChangeIndexList.empty(), "Only dispatch when there's work to do");
|
||||
NS_ASSERTION(mIsWriteScheduled, "Should report write running or scheduled.");
|
||||
|
||||
while (!mChangeIndexList.IsEmpty()) {
|
||||
while (!mChangeIndexList.empty()) {
|
||||
if (!mIsOpen) {
|
||||
// We've been closed, abort, discarding unwritten changes.
|
||||
mIsWriteScheduled = false;
|
||||
@ -217,7 +225,8 @@ nsresult FileBlockCache::Run()
|
||||
// Hold a reference to the change, in case another change
|
||||
// overwrites the mBlockChanges entry for this block while we drop
|
||||
// mDataMonitor to take mFileMonitor.
|
||||
int32_t blockIndex = mChangeIndexList.PopFront();
|
||||
int32_t blockIndex = mChangeIndexList.front();
|
||||
mChangeIndexList.pop_front();
|
||||
RefPtr<BlockChange> change = mBlockChanges[blockIndex];
|
||||
MOZ_ASSERT(change,
|
||||
"Change index list should only contain entries for blocks "
|
||||
@ -326,14 +335,14 @@ nsresult FileBlockCache::MoveBlock(int32_t aSourceBlockIndex, int32_t aDestBlock
|
||||
}
|
||||
|
||||
if (mBlockChanges[aDestBlockIndex] == nullptr ||
|
||||
!mChangeIndexList.Contains(aDestBlockIndex)) {
|
||||
!ContainerContains(mChangeIndexList, aDestBlockIndex)) {
|
||||
// Only add another entry to the change index list if we don't already
|
||||
// have one for this block. We won't have an entry when either there's
|
||||
// no pending change for this block, or if there is a pending change for
|
||||
// this block and we're in the process of writing it (we've popped the
|
||||
// block's index out of mChangeIndexList in Run() but not finished writing
|
||||
// the block to file yet.
|
||||
mChangeIndexList.PushBack(aDestBlockIndex);
|
||||
mChangeIndexList.push_back(aDestBlockIndex);
|
||||
}
|
||||
|
||||
// If the source block hasn't yet been written to file then the dest block
|
||||
@ -346,7 +355,7 @@ nsresult FileBlockCache::MoveBlock(int32_t aSourceBlockIndex, int32_t aDestBlock
|
||||
|
||||
EnsureWriteScheduled();
|
||||
|
||||
NS_ASSERTION(mChangeIndexList.Contains(aDestBlockIndex),
|
||||
NS_ASSERTION(ContainerContains(mChangeIndexList, aDestBlockIndex),
|
||||
"Should have scheduled block for change");
|
||||
|
||||
return NS_OK;
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "MediaCache.h"
|
||||
#include "nsDeque.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include <deque>
|
||||
|
||||
struct PRFileDesc;
|
||||
|
||||
@ -124,38 +125,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class Int32Queue : private nsDeque {
|
||||
public:
|
||||
int32_t PopFront() {
|
||||
int32_t front = ObjectAt(0);
|
||||
nsDeque::PopFront();
|
||||
return front;
|
||||
}
|
||||
|
||||
void PushBack(int32_t aValue) {
|
||||
nsDeque::Push(reinterpret_cast<void*>(aValue));
|
||||
}
|
||||
|
||||
bool Contains(int32_t aValue) {
|
||||
for (size_t i = 0; i < GetSize(); ++i) {
|
||||
if (ObjectAt(i) == aValue) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsEmpty() {
|
||||
return nsDeque::GetSize() == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
int32_t ObjectAt(size_t aIndex) {
|
||||
void* v = nsDeque::ObjectAt(aIndex);
|
||||
return reinterpret_cast<uintptr_t>(v);
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
int64_t BlockIndexToOffset(int32_t aBlockIndex) {
|
||||
return static_cast<int64_t>(aBlockIndex) * BLOCK_SIZE;
|
||||
@ -202,8 +171,7 @@ private:
|
||||
// main thread).
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
// Queue of pending block indexes that need to be written or moved.
|
||||
//AutoTArray<int32_t, 8> mChangeIndexList;
|
||||
Int32Queue mChangeIndexList;
|
||||
std::deque<int32_t> mChangeIndexList;
|
||||
// True if we've dispatched an event to commit all pending block changes
|
||||
// to file on mThread.
|
||||
bool mIsWriteScheduled;
|
||||
|
@ -93,7 +93,7 @@ static const uint32_t LOW_AUDIO_USECS = 300000;
|
||||
// decoding more audio. If we increase the low audio threshold (see
|
||||
// LOW_AUDIO_USECS above) we'll also increase this value to ensure it's not
|
||||
// less than the low audio threshold.
|
||||
const int64_t AMPLE_AUDIO_USECS = 1000000;
|
||||
static const int64_t AMPLE_AUDIO_USECS = 2000000;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
@ -2931,6 +2931,15 @@ MediaDecoderStateMachine::SetAudioCaptured(bool aCaptured)
|
||||
|
||||
mAudioCaptured = aCaptured;
|
||||
ScheduleStateMachine();
|
||||
|
||||
// Don't buffer as much when audio is captured because we don't need to worry
|
||||
// about high latency audio devices.
|
||||
mAmpleAudioThresholdUsecs = mAudioCaptured ?
|
||||
detail::AMPLE_AUDIO_USECS / 2 :
|
||||
detail::AMPLE_AUDIO_USECS;
|
||||
if (mIsAudioPrerolling && DonePrerollingAudio()) {
|
||||
StopPrerollingAudio();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t MediaDecoderStateMachine::GetAmpleVideoFrames() const
|
||||
|
@ -952,7 +952,7 @@ private:
|
||||
uint32_t AudioPrerollUsecs() const
|
||||
{
|
||||
MOZ_ASSERT(OnTaskQueue());
|
||||
return IsRealTime() ? 0 : mAmpleAudioThresholdUsecs;
|
||||
return IsRealTime() ? 0 : mAmpleAudioThresholdUsecs / 2;
|
||||
}
|
||||
|
||||
uint32_t VideoPrerollFrames() const
|
||||
|
4
dom/media/test/external/MANIFEST.in
vendored
4
dom/media/test/external/MANIFEST.in
vendored
@ -1,6 +1,4 @@
|
||||
exclude MANIFEST.in
|
||||
include external-media-tests-requirements.txt
|
||||
include requirements.txt
|
||||
recursive-include external_media_harness *
|
||||
recursive-include external_media_tests *
|
||||
|
||||
|
||||
|
@ -152,17 +152,16 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
|
||||
|
||||
init: function init(nfc) {
|
||||
this.nfc = nfc;
|
||||
|
||||
if (!NFC.DEBUG_NFC) {
|
||||
let lock = gSettingsService.createLock();
|
||||
lock.get(NFC.SETTING_NFC_DEBUG, this.nfc);
|
||||
Services.obs.addObserver(this, NFC.TOPIC_MOZSETTINGS_CHANGED, false);
|
||||
}
|
||||
|
||||
Services.obs.addObserver(this, NFC.TOPIC_XPCOM_SHUTDOWN, false);
|
||||
this._registerMessageListeners();
|
||||
},
|
||||
|
||||
listenDebugEvent: function listenDebugEvent() {
|
||||
let lock = gSettingsService.createLock();
|
||||
lock.get(NFC.SETTING_NFC_DEBUG, this.nfc);
|
||||
Services.obs.addObserver(this, NFC.TOPIC_MOZSETTINGS_CHANGED, false);
|
||||
},
|
||||
|
||||
_shutdown: function _shutdown() {
|
||||
this.nfc.shutdown();
|
||||
this.nfc = null;
|
||||
@ -509,8 +508,15 @@ var SessionHelper = {
|
||||
}
|
||||
};
|
||||
|
||||
function Nfc() {
|
||||
function Nfc(isXPCShell) {
|
||||
// TODO: Bug 1239954: xpcshell test timed out with
|
||||
// SettingsSevice.createlock().get()
|
||||
// gSettingsService.createLock will cause timeout while running xpshell-test,
|
||||
// so we try to prevent to run gSettingsService under xpcshell-test here.
|
||||
gMessageManager.init(this);
|
||||
if (!isXPCShell && !NFC.DEBUG_NFC) {
|
||||
gMessageManager.listenDebugEvent();
|
||||
}
|
||||
|
||||
this.targetsByRequestId = {};
|
||||
}
|
||||
|
42
dom/nfc/tests/unit/header_helpers.js
Normal file
42
dom/nfc/tests/unit/header_helpers.js
Normal file
@ -0,0 +1,42 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
let NFC_CONSTS = {};
|
||||
let subscriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
||||
.getService(Ci.mozIJSSubScriptLoader);
|
||||
|
||||
Cu.import("resource://gre/modules/nfc_consts.js", NFC_CONSTS);
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||
"@mozilla.org/childprocessmessagemanager;1",
|
||||
"nsISyncMessageSender");
|
||||
|
||||
function waitAsyncMessage(msg, callback) {
|
||||
let handler = {
|
||||
receiveMessage: function (message) {
|
||||
if (message.name !== msg) {
|
||||
return;
|
||||
}
|
||||
cpmm.removeMessageListener(msg, handler);
|
||||
callback(message);
|
||||
}
|
||||
};
|
||||
|
||||
cpmm.addMessageListener(msg, handler);
|
||||
}
|
||||
|
||||
function sendAsyncMessage(msg, rsp, payload, callback) {
|
||||
waitAsyncMessage(rsp, callback);
|
||||
cpmm.sendAsyncMessage(msg, payload);
|
||||
}
|
||||
|
||||
function sendSyncMessage(msg, payload) {
|
||||
return cpmm.sendSyncMessage(msg, payload);
|
||||
}
|
||||
|
@ -3,13 +3,7 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
/* globals run_next_test, add_test, ok, equal, Components, XPCOMUtils */
|
||||
/* exported run_test */
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
const MANIFEST_URL = "app://system.gaiamobile.org/manifest.webapp";
|
||||
// secure element access rule format: <secure element name>/<hex string aid>
|
||||
|
479
dom/nfc/tests/unit/test_Nfc.js
Normal file
479
dom/nfc/tests/unit/test_Nfc.js
Normal file
@ -0,0 +1,479 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
let NFC = {};
|
||||
subscriptLoader.loadSubScript("resource://gre/components/Nfc.js", NFC);
|
||||
|
||||
// Mock nfc service
|
||||
// loadSubScript could not load const and let variables, type defined
|
||||
// here should be consistent with type defined in Nfc.js
|
||||
const MockReqType = {
|
||||
CHANGE_RF_STATE: "changeRFState",
|
||||
READ_NDEF: "readNDEF",
|
||||
WRITE_NDEF: "writeNDEF",
|
||||
MAKE_READ_ONLY: "makeReadOnly",
|
||||
FORMAT: "format",
|
||||
TRANSCEIVE: "transceive"
|
||||
};
|
||||
|
||||
const MockRspType = {};
|
||||
MockRspType[MockReqType.CHANGE_RF_STATE] = "changeRFStateRsp";
|
||||
MockRspType[MockReqType.READ_NDEF] = "readNDEFRsp";
|
||||
MockRspType[MockReqType.WRITE_NDEF] = "writeNDEFRsp";
|
||||
MockRspType[MockReqType.MAKE_READ_ONLY] = "makeReadOnlyRsp";
|
||||
MockRspType[MockReqType.FORMAT] = "formatRsp";
|
||||
MockRspType[MockReqType.TRANSCEIVE] = "transceiveRsp";
|
||||
|
||||
const MockNtfType = {
|
||||
INITIALIZED: "initialized",
|
||||
TECH_DISCOVERED: "techDiscovered",
|
||||
TECH_LOST: "techLost",
|
||||
};
|
||||
|
||||
const MOCK_RECORDS = {"payload":{"0":4,"1":119,"2":119,
|
||||
"3":119,"4":46,"5":109,
|
||||
"6":111,"7":122,"8":105},
|
||||
"tnf":"well-known",
|
||||
"type":{"0":85}};
|
||||
|
||||
let MockNfcService = {
|
||||
listener: null,
|
||||
|
||||
setListener: function (listener) {
|
||||
this.listener = listener;
|
||||
},
|
||||
|
||||
notifyEvent: function (event) {
|
||||
if (this.listener && this.listener.onEvent) {
|
||||
this.listener.onEvent(event);
|
||||
}
|
||||
},
|
||||
|
||||
start: function (eventListener) {
|
||||
this.setListener(eventListener);
|
||||
},
|
||||
|
||||
shutdown: function () {
|
||||
this.listener = null;
|
||||
},
|
||||
|
||||
sendCommand: function (message) {
|
||||
switch (message.type) {
|
||||
case MockReqType.CHANGE_RF_STATE:
|
||||
this.notifyEvent({ errorMsg: "",
|
||||
requestId: message.requestId,
|
||||
rfState: message.rfState,
|
||||
rspType: MockRspType[message.type] });
|
||||
break;
|
||||
case MockReqType.READ_NDEF:
|
||||
this.notifyEvent({ errorMsg: "",
|
||||
requestId: message.requestId,
|
||||
records: MOCK_RECORDS,
|
||||
sessionId: message.sessionId,
|
||||
rspType: MockRspType[message.type] });
|
||||
break;
|
||||
case MockReqType.WRITE_NDEF:
|
||||
case MockReqType.MAKE_READ_ONLY:
|
||||
case MockReqType.TRANSCEIVE:
|
||||
case MockReqType.FORMAT:
|
||||
this.notifyEvent({ errorMsg: "",
|
||||
requestId: message.requestId,
|
||||
sessionId: message.sessionId,
|
||||
rspType: MockRspType[message.type] });
|
||||
break;
|
||||
default:
|
||||
throw new Error("Don't know about this message type: " + message.type);
|
||||
}
|
||||
}
|
||||
};
|
||||
// end of nfc service mock.
|
||||
|
||||
let messageManager = NFC.gMessageManager;
|
||||
let sessionHelper = NFC.SessionHelper;
|
||||
|
||||
new NFC.Nfc(/* isXPCShell */ true);
|
||||
|
||||
// It would better to use MockRegistrar but nsINfcService contains
|
||||
// method marked with implicit_context which may not be implemented in js.
|
||||
// Use a simple assignment to mock nfcService
|
||||
|
||||
MockNfcService.start(messageManager.nfc);
|
||||
messageManager.nfc.nfcService = MockNfcService;
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_test(function test_setFocusTab() {
|
||||
let tabId = 1;
|
||||
let rsp = "NFC:DOMEvent";
|
||||
let focusOnCb = function (message) {
|
||||
let result = message.data;
|
||||
|
||||
deepEqual(result,
|
||||
{ tabId: tabId,
|
||||
event: NFC_CONSTS.FOCUS_CHANGED,
|
||||
focus: true },
|
||||
"Correct result SetFocusTab On");
|
||||
|
||||
let focusOffCb = function (msg) {
|
||||
let result = msg.data;
|
||||
|
||||
deepEqual(result,
|
||||
{ tabId: tabId,
|
||||
event: NFC_CONSTS.FOCUS_CHANGED,
|
||||
focus: false },
|
||||
"Correct result SetFocusTab Off");
|
||||
|
||||
equal(messageManager.focusId, NFC_CONSTS.SYSTEM_APP_ID);
|
||||
|
||||
sendSyncMessage("NFC:RemoveEventListener", { tabId: tabId });
|
||||
|
||||
run_next_test();
|
||||
};
|
||||
|
||||
sendAsyncMessage("NFC:SetFocusTab", rsp, { tabId: tabId, isFocus: false },
|
||||
focusOffCb);
|
||||
};
|
||||
|
||||
sendSyncMessage("NFC:AddEventListener", { tabId: tabId });
|
||||
sendAsyncMessage("NFC:SetFocusTab", rsp, { tabId: tabId, isFocus: true },
|
||||
focusOnCb);
|
||||
});
|
||||
|
||||
add_test(function test_checkP2PRegistrationSucceed() {
|
||||
let appId = 1;
|
||||
let requestId = 10;
|
||||
let rsp = "NFC:CheckP2PRegistrationResponse";
|
||||
let sessionId = 1;
|
||||
let isP2P = true;
|
||||
let callback = function (message) {
|
||||
let result = message.data;
|
||||
|
||||
equal(result.requestId, requestId);
|
||||
equal(result.errorMsg, undefined);
|
||||
|
||||
sendSyncMessage("NFC:UnregisterPeerReadyTarget", { appId: appId });
|
||||
sessionHelper.tokenMap = {};
|
||||
|
||||
run_next_test();
|
||||
};
|
||||
|
||||
sessionHelper.registerSession(sessionId, isP2P);
|
||||
sendSyncMessage("NFC:RegisterPeerReadyTarget", { appId: appId });
|
||||
sendAsyncMessage("NFC:CheckP2PRegistration",
|
||||
rsp,
|
||||
{ appId: appId,
|
||||
requestId: requestId },
|
||||
callback);
|
||||
});
|
||||
|
||||
add_test(function test_checkP2PRegistrationFailed() {
|
||||
let appId = 1;
|
||||
let requestId = 10;
|
||||
let rsp = "NFC:CheckP2PRegistrationResponse";
|
||||
let errorMsg =
|
||||
NFC_CONSTS.NFC_ERROR_MSG[NFC_CONSTS.NFC_GECKO_ERROR_P2P_REG_INVALID];
|
||||
let callback = function (message) {
|
||||
let result = message.data;
|
||||
|
||||
deepEqual(result,
|
||||
{ requestId: requestId,
|
||||
errorMsg: errorMsg },
|
||||
"Correct result checkP2PRegistrationFailed");
|
||||
|
||||
run_next_test();
|
||||
};
|
||||
|
||||
sendAsyncMessage("NFC:CheckP2PRegistration",
|
||||
rsp,
|
||||
{ appId: 10,
|
||||
requestId: requestId },
|
||||
callback);
|
||||
});
|
||||
|
||||
add_test(function test_notifyUserAcceptedP2P() {
|
||||
let appId = 1;
|
||||
let requestId = 10;
|
||||
let rsp = "NFC:DOMEvent";
|
||||
let sessionId = 1;
|
||||
let isP2P = true;
|
||||
let callback = function (message) {
|
||||
let result = message.data;
|
||||
|
||||
ok(true, "received CheckP2PRegistrationResponse")
|
||||
|
||||
sendSyncMessage("NFC:UnregisterPeerReadyTarget", { appId: appId });
|
||||
sessionHelper.tokenMap = {};
|
||||
|
||||
run_next_test();
|
||||
};
|
||||
|
||||
sessionHelper.registerSession(sessionId, isP2P);
|
||||
sendSyncMessage("NFC:RegisterPeerReadyTarget", { appId: appId });
|
||||
sendAsyncMessage("NFC:NotifyUserAcceptedP2P", rsp, { appId: appId },
|
||||
callback);
|
||||
});
|
||||
|
||||
add_test(function test_changeRFState() {
|
||||
let tabId = 1;
|
||||
let requestId = 10;
|
||||
let msg = "NFC:DOMEvent";
|
||||
let rfState = NFC_CONSTS.NFC_RF_STATE_LISTEN;
|
||||
let callback = function (message) {
|
||||
let result = message.data;
|
||||
|
||||
deepEqual(result,
|
||||
{ tabId: tabId,
|
||||
event: NFC_CONSTS.RF_EVENT_STATE_CHANGED,
|
||||
rfState: rfState },
|
||||
"Correct result onRFStateChanged");
|
||||
|
||||
sendSyncMessage("NFC:RemoveEventListener", { tabId: tabId });
|
||||
|
||||
run_next_test();
|
||||
};
|
||||
|
||||
sendSyncMessage("NFC:AddEventListener", { tabId: tabId });
|
||||
sendAsyncMessage("NFC:ChangeRFState",
|
||||
msg,
|
||||
{ requestId: requestId,
|
||||
rfState: rfState },
|
||||
callback);
|
||||
});
|
||||
|
||||
add_test(function test_queryRFState() {
|
||||
equal(sendSyncMessage("NFC:QueryInfo")[0].rfState, NFC_CONSTS.NFC_RF_STATE_LISTEN);
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_test(function test_onTagFound() {
|
||||
let tabId = 1;
|
||||
let msg = "NFC:DOMEvent";
|
||||
let sessionId = 10;
|
||||
let setFocusCb = function (focusMsg) {
|
||||
let callback = function (message) {
|
||||
let result = message.data;
|
||||
|
||||
equal(result.tabId, tabId);
|
||||
equal(result.event, NFC_CONSTS.TAG_EVENT_FOUND);
|
||||
|
||||
run_next_test();
|
||||
};
|
||||
|
||||
waitAsyncMessage(msg, callback);
|
||||
MockNfcService.notifyEvent({ ntfType: MockNtfType.TECH_DISCOVERED,
|
||||
sessionId: sessionId,
|
||||
isP2P: false });
|
||||
};
|
||||
|
||||
sendSyncMessage("NFC:AddEventListener", { tabId: tabId });
|
||||
sendAsyncMessage("NFC:SetFocusTab",
|
||||
msg,
|
||||
{ tabId: tabId,
|
||||
isFocus: true },
|
||||
setFocusCb);
|
||||
});
|
||||
|
||||
add_test(function test_onTagLost() {
|
||||
let tabId = 1;
|
||||
let msg = "NFC:DOMEvent";
|
||||
let sessionId = 10;
|
||||
let callback = function (message) {
|
||||
let result = message.data;
|
||||
|
||||
equal(result.tabId, tabId);
|
||||
equal(result.event, NFC_CONSTS.TAG_EVENT_LOST);
|
||||
|
||||
sendSyncMessage("NFC:RemoveEventListener", { tabId: tabId });
|
||||
sessionHelper.tokenMap = {};
|
||||
sendSyncMessage("NFC:SetFocusTab",
|
||||
{ tabId: tabId,
|
||||
isFocus: false });
|
||||
|
||||
run_next_test();
|
||||
};
|
||||
|
||||
waitAsyncMessage(msg, callback);
|
||||
MockNfcService.notifyEvent({ ntfType: MockNtfType.TECH_LOST,
|
||||
sessionId: sessionId,
|
||||
isP2P: false });
|
||||
});
|
||||
|
||||
add_test(function test_onPeerFound() {
|
||||
let tabId = 1;
|
||||
let msg = "NFC:DOMEvent";
|
||||
let sessionId = 10;
|
||||
let setFocusCb = function (focusMsg) {
|
||||
let callback = function (message) {
|
||||
let result = message.data;
|
||||
|
||||
equal(result.tabId, tabId);
|
||||
equal(result.event, NFC_CONSTS.PEER_EVENT_FOUND);
|
||||
|
||||
run_next_test();
|
||||
};
|
||||
|
||||
waitAsyncMessage(msg, callback);
|
||||
MockNfcService.notifyEvent({ ntfType: MockNtfType.TECH_DISCOVERED,
|
||||
sessionId: sessionId,
|
||||
isP2P: true });
|
||||
};
|
||||
|
||||
sendSyncMessage("NFC:AddEventListener", { tabId: tabId });
|
||||
sendAsyncMessage("NFC:SetFocusTab",
|
||||
msg,
|
||||
{ tabId: tabId,
|
||||
isFocus: true },
|
||||
setFocusCb);
|
||||
|
||||
});
|
||||
|
||||
add_test(function test_onPeerLost() {
|
||||
let tabId = 1;
|
||||
let msg = "NFC:DOMEvent";
|
||||
let sessionId = 10;
|
||||
let callback = function (message) {
|
||||
let result = message.data;
|
||||
|
||||
equal(result.tabId, tabId);
|
||||
equal(result.event, NFC_CONSTS.PEER_EVENT_LOST);
|
||||
|
||||
sendSyncMessage("NFC:RemoveEventListener", { tabId: tabId });
|
||||
sessionHelper.tokenMap = {};
|
||||
sendSyncMessage("NFC:SetFocusTab",
|
||||
{ tabId: tabId,
|
||||
isFocus: false });
|
||||
|
||||
run_next_test();
|
||||
};
|
||||
|
||||
waitAsyncMessage(msg, callback);
|
||||
MockNfcService.notifyEvent({ ntfType: MockNtfType.TECH_LOST,
|
||||
sessionId: sessionId,
|
||||
isP2P: true });
|
||||
|
||||
});
|
||||
|
||||
add_test(function test_readNDEF() {
|
||||
let requestId = 10;
|
||||
let sessionId = 15;
|
||||
let msg = "NFC:ReadNDEFResponse";
|
||||
let sessionToken = sessionHelper.registerSession(sessionId, false);
|
||||
let callback = function (message) {
|
||||
let result = message.data;
|
||||
|
||||
equal(result.requestId, requestId);
|
||||
equal(result.sessionId, sessionId);
|
||||
deepEqual(result.records, MOCK_RECORDS);
|
||||
|
||||
sessionHelper.tokenMap = {};
|
||||
|
||||
run_next_test();
|
||||
};
|
||||
|
||||
sendAsyncMessage("NFC:ReadNDEF",
|
||||
msg,
|
||||
{ requestId: requestId,
|
||||
sessionToken: sessionToken },
|
||||
callback);
|
||||
});
|
||||
|
||||
add_test(function test_writeNDEF() {
|
||||
let requestId = 10;
|
||||
let sessionId = 15;
|
||||
let msg = "NFC:WriteNDEFResponse";
|
||||
let sessionToken = sessionHelper.registerSession(sessionId, false);
|
||||
let callback = function (message) {
|
||||
let result = message.data;
|
||||
|
||||
equal(result.requestId, requestId);
|
||||
equal(result.sessionId, sessionId);
|
||||
|
||||
sessionHelper.tokenMap = {};
|
||||
|
||||
run_next_test();
|
||||
};
|
||||
|
||||
sendAsyncMessage("NFC:WriteNDEF",
|
||||
msg,
|
||||
{ requestId: requestId,
|
||||
sessionToken: sessionToken,
|
||||
records: MOCK_RECORDS,
|
||||
isP2P: true },
|
||||
callback);
|
||||
});
|
||||
|
||||
add_test(function test_format() {
|
||||
let requestId = 10;
|
||||
let sessionId = 15;
|
||||
let msg = "NFC:FormatResponse";
|
||||
let sessionToken = sessionHelper.registerSession(sessionId, false);
|
||||
let callback = function (message) {
|
||||
let result = message.data;
|
||||
|
||||
equal(result.requestId, requestId);
|
||||
equal(result.sessionId, sessionId);
|
||||
|
||||
sessionHelper.tokenMap = {};
|
||||
|
||||
run_next_test();
|
||||
};
|
||||
|
||||
sendAsyncMessage("NFC:Format",
|
||||
msg,
|
||||
{ requestId: requestId,
|
||||
sessionToken: sessionToken },
|
||||
callback);
|
||||
});
|
||||
|
||||
add_test(function test_makeReadOnly() {
|
||||
let requestId = 10;
|
||||
let sessionId = 15;
|
||||
let msg = "NFC:MakeReadOnlyResponse";
|
||||
let sessionToken = sessionHelper.registerSession(sessionId, false);
|
||||
let callback = function (message) {
|
||||
let result = message.data;
|
||||
|
||||
equal(result.requestId, requestId);
|
||||
equal(result.sessionId, sessionId);
|
||||
|
||||
sessionHelper.tokenMap = {};
|
||||
|
||||
run_next_test();
|
||||
};
|
||||
|
||||
sendAsyncMessage("NFC:MakeReadOnly",
|
||||
msg,
|
||||
{ requestId: requestId,
|
||||
sessionToken: sessionToken },
|
||||
callback);
|
||||
});
|
||||
|
||||
|
||||
add_test(function test_transceive() {
|
||||
let requestId = 10;
|
||||
let sessionId = 15;
|
||||
let msg = "NFC:TransceiveResponse";
|
||||
let sessionToken = sessionHelper.registerSession(sessionId, false);
|
||||
let callback = function (message) {
|
||||
let result = message.data;
|
||||
|
||||
equal(result.requestId, requestId);
|
||||
equal(result.sessionId, sessionId);
|
||||
|
||||
sessionHelper.tokenMap = {};
|
||||
|
||||
run_next_test();
|
||||
};
|
||||
|
||||
sendAsyncMessage("NFC:Transceive",
|
||||
msg,
|
||||
{ requestId: requestId,
|
||||
sessionToken: sessionToken,
|
||||
technology: "NFCA",
|
||||
command: "0x50" },
|
||||
callback);
|
||||
});
|
@ -1,5 +1,6 @@
|
||||
[DEFAULT]
|
||||
head =
|
||||
head = header_helpers.js
|
||||
tail =
|
||||
|
||||
[test_HCIEventTransactionSystemMessageConfigurator.js]
|
||||
[test_Nfc.js]
|
||||
|
@ -434,7 +434,7 @@ nsMixedContentBlocker::ShouldLoad(bool aHadInsecureImageRedirect,
|
||||
// content websockets within the websockets implementation, so we don't need
|
||||
// to do any blocking here, nor do we need to provide a way to undo or
|
||||
// override the blocking. Websockets without TLS are very flaky anyway in the
|
||||
// face of many HTTP-aware proxies. Compared to psasive content, there is
|
||||
// face of many HTTP-aware proxies. Compared to passive content, there is
|
||||
// additional risk that the script using WebSockets will disclose sensitive
|
||||
// information from the HTTPS page and/or eval (directly or indirectly)
|
||||
// received data.
|
||||
|
@ -20,6 +20,7 @@ interface Directory {
|
||||
/*
|
||||
* The leaf name of the directory.
|
||||
*/
|
||||
[Throws]
|
||||
readonly attribute DOMString name;
|
||||
|
||||
/*
|
||||
@ -105,11 +106,13 @@ 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();
|
||||
};
|
||||
|
||||
|
@ -11,6 +11,8 @@
|
||||
*/
|
||||
|
||||
interface FileList {
|
||||
getter File? item(unsigned long index);
|
||||
[Throws]
|
||||
getter (File or Directory)? item(unsigned long index);
|
||||
|
||||
readonly attribute unsigned long length;
|
||||
};
|
||||
|
@ -155,6 +155,10 @@ 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
|
||||
|
@ -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->Item(0)->GetName(outText);
|
||||
fl->UnsafeItem(0).GetAsFile()->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->Item(i)->GetName(fileName);
|
||||
fl->UnsafeItem(i).GetAsFile()->GetName(fileName);
|
||||
outText.Append(NS_LITERAL_STRING("\n"));
|
||||
outText.Append(fileName);
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
|
||||
<html class="reftest-wait">
|
||||
<script>
|
||||
function runTest() {
|
||||
// Dispatch 'return' key on second summary should not collapse details.
|
||||
var summary = document.getElementById("summary");
|
||||
summary.dispatchEvent(new KeyboardEvent("keypress", {"keyCode": 13}));
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
</script>
|
||||
<body onload="runTest();">
|
||||
<details open>
|
||||
<summary>Summary</summary>
|
||||
<summary id="summary">Second Summary</summary>
|
||||
<p>This is the details.</p>
|
||||
</details>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
|
||||
<html class="reftest-wait">
|
||||
<script>
|
||||
function runTest() {
|
||||
var summary = document.getElementById("summary");
|
||||
summary.addEventListener('click', function(e) {
|
||||
// Prevent the details from toggling by key events.
|
||||
e.preventDefault();
|
||||
});
|
||||
// Dispatch 'return' key to the summary element.
|
||||
summary.dispatchEvent(new KeyboardEvent("keypress", {"keyCode": 13}));
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
</script>
|
||||
<body onload="runTest();">
|
||||
<details>
|
||||
<summary id="summary">Summary</summary>
|
||||
<p>This is the details.</p>
|
||||
</details>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
|
||||
<html class="reftest-wait">
|
||||
<script>
|
||||
function runTest() {
|
||||
var summary = document.getElementById("summary");
|
||||
// Dispatch 'return' key to the summary element.
|
||||
summary.dispatchEvent(new KeyboardEvent("keypress", {"keyCode": 13}));
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
</script>
|
||||
<body onload="runTest();">
|
||||
<details>
|
||||
<summary id="summary">Summary</summary>
|
||||
<p>This is the details.</p>
|
||||
</details>
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
- http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
|
||||
<html class="reftest-wait">
|
||||
<script>
|
||||
function runTest() {
|
||||
var summary = document.getElementById("summary");
|
||||
// Dispatch 'space' keys to the summary element.
|
||||
summary.dispatchEvent(new KeyboardEvent("keydown", {"keyCode": 32}));
|
||||
summary.dispatchEvent(new KeyboardEvent("keyup", {"keyCode": 32}));
|
||||
document.documentElement.removeAttribute("class");
|
||||
}
|
||||
</script>
|
||||
<body onload="runTest();">
|
||||
<details>
|
||||
<summary id="summary">Summary</summary>
|
||||
<p>This is the details.</p>
|
||||
</details>
|
||||
</body>
|
||||
</html>
|
@ -72,3 +72,9 @@ pref(dom.details_element.enabled,true) == mouse-click-fixed-summary.html open-fi
|
||||
pref(dom.details_element.enabled,true) == mouse-click-twice-fixed-summary.html fixed-summary.html
|
||||
pref(dom.details_element.enabled,true) == mouse-click-float-details.html open-float-details.html
|
||||
pref(dom.details_element.enabled,true) == mouse-click-twice-float-details.html float-details.html
|
||||
|
||||
# Dispatch keyboard event to summary
|
||||
pref(dom.details_element.enabled,true) == key-enter-single-summary.html open-single-summary.html
|
||||
pref(dom.details_element.enabled,true) == key-enter-open-second-summary.html open-multiple-summary.html
|
||||
pref(dom.details_element.enabled,true) == key-enter-prevent-default.html single-summary.html
|
||||
pref(dom.details_element.enabled,true) == key-space-single-summary.html open-single-summary.html
|
||||
|
@ -1788,6 +1788,40 @@ Preferences::AddUintVarCache(uint32_t* aCache,
|
||||
return RegisterCallback(UintVarChanged, aPref, data);
|
||||
}
|
||||
|
||||
template <MemoryOrdering Order>
|
||||
static void AtomicUintVarChanged(const char* aPref, void* aClosure)
|
||||
{
|
||||
CacheData* cache = static_cast<CacheData*>(aClosure);
|
||||
*((Atomic<uint32_t, Order>*)cache->cacheLocation) =
|
||||
Preferences::GetUint(aPref, cache->defaultValueUint);
|
||||
}
|
||||
|
||||
template <MemoryOrdering Order>
|
||||
// static
|
||||
nsresult
|
||||
Preferences::AddAtomicUintVarCache(Atomic<uint32_t, Order>* aCache,
|
||||
const char* aPref,
|
||||
uint32_t aDefault)
|
||||
{
|
||||
NS_ASSERTION(aCache, "aCache must not be NULL");
|
||||
#ifdef DEBUG
|
||||
AssertNotAlreadyCached("uint", aPref, aCache);
|
||||
#endif
|
||||
*aCache = Preferences::GetUint(aPref, aDefault);
|
||||
CacheData* data = new CacheData();
|
||||
data->cacheLocation = aCache;
|
||||
data->defaultValueUint = aDefault;
|
||||
gCacheData->AppendElement(data);
|
||||
return RegisterCallback(AtomicUintVarChanged<Order>, aPref, data);
|
||||
}
|
||||
|
||||
// Since the definition of this template function is not in a header file,
|
||||
// we need to explicitly specify the instantiations that are required.
|
||||
// Currently only the order=Relaxed variant is needed.
|
||||
template
|
||||
nsresult Preferences::AddAtomicUintVarCache(Atomic<uint32_t,Relaxed>*,
|
||||
const char*, uint32_t);
|
||||
|
||||
static void FloatVarChanged(const char* aPref, void* aClosure)
|
||||
{
|
||||
CacheData* cache = static_cast<CacheData*>(aClosure);
|
||||
|
@ -274,6 +274,10 @@ public:
|
||||
static nsresult AddUintVarCache(uint32_t* aVariable,
|
||||
const char* aPref,
|
||||
uint32_t aDefault = 0);
|
||||
template <MemoryOrdering Order>
|
||||
static nsresult AddAtomicUintVarCache(Atomic<uint32_t, Order>* aVariable,
|
||||
const char* aPref,
|
||||
uint32_t aDefault = 0);
|
||||
static nsresult AddFloatVarCache(float* aVariable,
|
||||
const char* aPref,
|
||||
float aDefault = 0.0f);
|
||||
|
@ -51,7 +51,8 @@ int32_t CacheObserver::sMemoryCacheCapacity = kDefaultMemoryCacheCapacity;
|
||||
int32_t CacheObserver::sAutoMemoryCacheCapacity = -1;
|
||||
|
||||
static uint32_t const kDefaultDiskCacheCapacity = 250 * 1024; // 250 MB
|
||||
uint32_t CacheObserver::sDiskCacheCapacity = kDefaultDiskCacheCapacity;
|
||||
Atomic<uint32_t,Relaxed> CacheObserver::sDiskCacheCapacity
|
||||
(kDefaultDiskCacheCapacity);
|
||||
|
||||
static uint32_t const kDefaultDiskFreeSpaceSoftLimit = 5 * 1024; // 5MB
|
||||
uint32_t CacheObserver::sDiskFreeSpaceSoftLimit = kDefaultDiskFreeSpaceSoftLimit;
|
||||
@ -158,7 +159,7 @@ CacheObserver::AttachToPreferences()
|
||||
mozilla::Preferences::AddUintVarCache(
|
||||
&sMetadataMemoryLimit, "browser.cache.disk.metadata_memory_limit", kDefaultMetadataMemoryLimit);
|
||||
|
||||
mozilla::Preferences::AddUintVarCache(
|
||||
mozilla::Preferences::AddAtomicUintVarCache(
|
||||
&sDiskCacheCapacity, "browser.cache.disk.capacity", kDefaultDiskCacheCapacity);
|
||||
mozilla::Preferences::AddBoolVarCache(
|
||||
&sSmartCacheSizeEnabled, "browser.cache.disk.smart_size.enabled", kDefaultSmartCacheSizeEnabled);
|
||||
|
@ -87,7 +87,7 @@ private:
|
||||
static uint32_t sMetadataMemoryLimit;
|
||||
static int32_t sMemoryCacheCapacity;
|
||||
static int32_t sAutoMemoryCacheCapacity;
|
||||
static uint32_t sDiskCacheCapacity;
|
||||
static Atomic<uint32_t, Relaxed> sDiskCacheCapacity;
|
||||
static uint32_t sDiskFreeSpaceSoftLimit;
|
||||
static uint32_t sDiskFreeSpaceHardLimit;
|
||||
static bool sSmartCacheSizeEnabled;
|
||||
|
@ -551,6 +551,8 @@ STUB(gtk_style_context_get_border_color)
|
||||
STUB(gtk_style_context_get_color)
|
||||
STUB(gtk_style_context_get_margin)
|
||||
STUB(gtk_style_context_get_padding)
|
||||
STUB(gtk_style_context_get_property)
|
||||
STUB(gtk_style_context_get_state)
|
||||
STUB(gtk_style_context_has_class)
|
||||
STUB(gtk_style_context_new)
|
||||
STUB(gtk_style_context_remove_class)
|
||||
@ -559,6 +561,7 @@ STUB(gtk_style_context_restore)
|
||||
STUB(gtk_style_context_save)
|
||||
STUB(gtk_style_context_set_path)
|
||||
STUB(gtk_style_context_set_state)
|
||||
STUB(gtk_style_properties_lookup_property)
|
||||
STUB(gtk_tree_view_column_get_button)
|
||||
STUB(gtk_widget_get_preferred_size)
|
||||
STUB(gtk_widget_get_state_flags)
|
||||
|
@ -29,6 +29,10 @@
|
||||
|
||||
#include "mozilla/gfx/2D.h"
|
||||
|
||||
#if MOZ_WIDGET_GTK != 2
|
||||
#include <cairo-gobject.h>
|
||||
#endif
|
||||
|
||||
using mozilla::LookAndFeel;
|
||||
|
||||
#define GDK_COLOR_TO_NS_RGB(c) \
|
||||
@ -61,6 +65,121 @@ nsLookAndFeel::~nsLookAndFeel()
|
||||
#endif
|
||||
}
|
||||
|
||||
#if MOZ_WIDGET_GTK != 2
|
||||
static void
|
||||
GetLightAndDarkness(const GdkRGBA& aColor,
|
||||
double* aLightness, double* aDarkness)
|
||||
{
|
||||
double sum = aColor.red + aColor.green + aColor.blue;
|
||||
*aLightness = sum * aColor.alpha;
|
||||
*aDarkness = (3.0 - sum) * aColor.alpha;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetGradientColors(const GValue* aValue,
|
||||
GdkRGBA* aLightColor, GdkRGBA* aDarkColor)
|
||||
{
|
||||
if (!G_TYPE_CHECK_VALUE_TYPE(aValue, CAIRO_GOBJECT_TYPE_PATTERN))
|
||||
return false;
|
||||
|
||||
auto pattern = static_cast<cairo_pattern_t*>(g_value_get_boxed(aValue));
|
||||
|
||||
// Just picking the lightest and darkest colors as simple samples rather
|
||||
// than trying to blend, which could get messy if there are many stops.
|
||||
if (CAIRO_STATUS_SUCCESS !=
|
||||
cairo_pattern_get_color_stop_rgba(pattern, 0, nullptr, &aDarkColor->red,
|
||||
&aDarkColor->green, &aDarkColor->blue,
|
||||
&aDarkColor->alpha))
|
||||
return false;
|
||||
|
||||
double maxLightness, maxDarkness;
|
||||
GetLightAndDarkness(*aDarkColor, &maxLightness, &maxDarkness);
|
||||
*aLightColor = *aDarkColor;
|
||||
|
||||
GdkRGBA stop;
|
||||
for (int index = 1;
|
||||
CAIRO_STATUS_SUCCESS ==
|
||||
cairo_pattern_get_color_stop_rgba(pattern, index, nullptr,
|
||||
&stop.red, &stop.green,
|
||||
&stop.blue, &stop.alpha);
|
||||
++index) {
|
||||
double lightness, darkness;
|
||||
GetLightAndDarkness(stop, &lightness, &darkness);
|
||||
if (lightness > maxLightness) {
|
||||
maxLightness = lightness;
|
||||
*aLightColor = stop;
|
||||
}
|
||||
if (darkness > maxDarkness) {
|
||||
maxDarkness = darkness;
|
||||
*aDarkColor = stop;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetUnicoBorderGradientColors(GtkStyleContext* aContext,
|
||||
GdkRGBA* aLightColor, GdkRGBA* aDarkColor)
|
||||
{
|
||||
// Ubuntu 12.04 has GTK engine Unico-1.0.2, which overrides render_frame,
|
||||
// providing its own border code. Ubuntu 14.04 has
|
||||
// Unico-1.0.3+14.04.20140109, which does not override render_frame, and
|
||||
// so does not need special attention. The earlier Unico can be detected
|
||||
// by the -unico-border-gradient style property it registers.
|
||||
// gtk_style_properties_lookup_property() is checked first to avoid the
|
||||
// warning from gtk_style_context_get_property() when the property does
|
||||
// not exist. (gtk_render_frame() of GTK+ 3.16 no longer uses the
|
||||
// engine.)
|
||||
const char* propertyName = "-unico-border-gradient";
|
||||
if (!gtk_style_properties_lookup_property(propertyName, nullptr, nullptr))
|
||||
return false;
|
||||
|
||||
// -unico-border-gradient is used only when the CSS node's engine is Unico.
|
||||
GtkThemingEngine* engine;
|
||||
GtkStateFlags state = gtk_style_context_get_state(aContext);
|
||||
gtk_style_context_get(aContext, state, "engine", &engine, nullptr);
|
||||
if (strcmp(g_type_name(G_TYPE_FROM_INSTANCE(engine)), "UnicoEngine") != 0)
|
||||
return false;
|
||||
|
||||
// draw_border() of Unico engine uses -unico-border-gradient
|
||||
// in preference to border-color.
|
||||
GValue value = G_VALUE_INIT;
|
||||
gtk_style_context_get_property(aContext, propertyName, state, &value);
|
||||
|
||||
bool result = GetGradientColors(&value, aLightColor, aDarkColor);
|
||||
|
||||
g_value_unset(&value);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
GetBorderColors(GtkStyleContext* aContext,
|
||||
GdkRGBA* aLightColor, GdkRGBA* aDarkColor)
|
||||
{
|
||||
if (GetUnicoBorderGradientColors(aContext, aLightColor, aDarkColor))
|
||||
return;
|
||||
|
||||
GtkStateFlags state = gtk_style_context_get_state(aContext);
|
||||
gtk_style_context_get_border_color(aContext, state, aDarkColor);
|
||||
// TODO GTK3 - update aLightColor
|
||||
// for GTK_BORDER_STYLE_INSET/OUTSET/GROVE/RIDGE border styles.
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=978172#c25
|
||||
*aLightColor = *aDarkColor;
|
||||
}
|
||||
|
||||
static void
|
||||
GetBorderColors(GtkStyleContext* aContext,
|
||||
nscolor* aLightColor, nscolor* aDarkColor)
|
||||
{
|
||||
GdkRGBA lightColor, darkColor;
|
||||
GetBorderColors(aContext, &lightColor, &darkColor);
|
||||
*aLightColor = GDK_RGBA_TO_NS_RGBA(lightColor);
|
||||
*aDarkColor = GDK_RGBA_TO_NS_RGBA(darkColor);
|
||||
}
|
||||
#endif
|
||||
|
||||
nsresult
|
||||
nsLookAndFeel::NativeGetColor(ColorID aID, nscolor& aColor)
|
||||
{
|
||||
@ -1201,12 +1320,8 @@ nsLookAndFeel::Init()
|
||||
|
||||
GtkWidget *frame = gtk_frame_new(nullptr);
|
||||
gtk_container_add(GTK_CONTAINER(parent), frame);
|
||||
|
||||
// TODO GTK3 - update sFrameOuterLightBorder
|
||||
// for GTK_BORDER_STYLE_INSET/OUTSET/GROVE/RIDGE border styles (Bug 978172).
|
||||
style = gtk_widget_get_style_context(frame);
|
||||
gtk_style_context_get_border_color(style, GTK_STATE_FLAG_NORMAL, &color);
|
||||
sFrameInnerDarkBorder = sFrameOuterLightBorder = GDK_RGBA_TO_NS_RGBA(color);
|
||||
GetBorderColors(style, &sFrameOuterLightBorder, &sFrameInnerDarkBorder);
|
||||
|
||||
gtk_widget_path_free(path);
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
#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"
|
||||
@ -30,6 +31,36 @@ 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.
|
||||
@ -74,9 +105,9 @@ class nsBaseFilePickerEnumerator : public nsISimpleEnumerator
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
explicit nsBaseFilePickerEnumerator(nsPIDOMWindowOuter* aParent,
|
||||
nsISimpleEnumerator* iterator,
|
||||
int16_t aMode)
|
||||
nsBaseFilePickerEnumerator(nsPIDOMWindowOuter* aParent,
|
||||
nsISimpleEnumerator* iterator,
|
||||
int16_t aMode)
|
||||
: mIterator(iterator)
|
||||
, mParent(aParent->GetCurrentInnerWindow())
|
||||
, mMode(aMode)
|
||||
@ -98,32 +129,10 @@ public:
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
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;
|
||||
return LocalFileToDirectoryOrBlob(mParent,
|
||||
mMode == nsIFilePicker::modeGetFolder,
|
||||
localFile,
|
||||
aResult);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
@ -349,10 +358,10 @@ nsBaseFilePicker::GetDomFileOrDirectory(nsISupports** aValue)
|
||||
|
||||
auto* innerParent = mParent ? mParent->GetCurrentInnerWindow() : nullptr;
|
||||
|
||||
RefPtr<File> domFile = File::CreateFromFile(innerParent, localFile);
|
||||
domFile->Impl()->SetIsDirectory(mMode == nsIFilePicker::modeGetFolder);
|
||||
nsCOMPtr<nsIDOMBlob>(domFile).forget(aValue);
|
||||
return NS_OK;
|
||||
return LocalFileToDirectoryOrBlob(innerParent,
|
||||
mMode == nsIFilePicker::modeGetFolder,
|
||||
localFile,
|
||||
aValue);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -7,6 +7,7 @@
|
||||
#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"
|
||||
@ -142,11 +143,11 @@ nsFilePickerProxy::Open(nsIFilePickerShownCallback* aCallback)
|
||||
}
|
||||
|
||||
bool
|
||||
nsFilePickerProxy::Recv__delete__(const MaybeInputFiles& aFiles,
|
||||
nsFilePickerProxy::Recv__delete__(const MaybeInputData& aData,
|
||||
const int16_t& aResult)
|
||||
{
|
||||
if (aFiles.type() == MaybeInputFiles::TInputFiles) {
|
||||
const InfallibleTArray<PBlobChild*>& blobs = aFiles.get_InputFiles().blobsChild();
|
||||
if (aData.type() == MaybeInputData::TInputBlobs) {
|
||||
const InfallibleTArray<PBlobChild*>& blobs = aData.get_InputBlobs().blobsChild();
|
||||
for (uint32_t i = 0; i < blobs.Length(); ++i) {
|
||||
BlobChild* actor = static_cast<BlobChild*>(blobs[i]);
|
||||
RefPtr<BlobImpl> blobImpl = actor->GetBlobImpl();
|
||||
@ -161,8 +162,24 @@ nsFilePickerProxy::Recv__delete__(const MaybeInputFiles& aFiles,
|
||||
RefPtr<File> file = File::Create(inner, blobImpl);
|
||||
MOZ_ASSERT(file);
|
||||
|
||||
mFilesOrDirectories.AppendElement(file);
|
||||
OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement();
|
||||
element->SetAsFile() = 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) {
|
||||
@ -182,8 +199,16 @@ nsFilePickerProxy::GetDomFileOrDirectory(nsISupports** aValue)
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mFilesOrDirectories.Length() == 1);
|
||||
nsCOMPtr<nsIDOMBlob> blob = mFilesOrDirectories[0].get();
|
||||
blob.forget(aValue);
|
||||
|
||||
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);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -194,8 +219,9 @@ class SimpleEnumerator final : public nsISimpleEnumerator
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
explicit SimpleEnumerator(const nsTArray<RefPtr<File>>& aFiles)
|
||||
: mFiles(aFiles)
|
||||
explicit
|
||||
SimpleEnumerator(const nsTArray<OwningFileOrDirectory>& aFilesOrDirectories)
|
||||
: mFilesOrDirectories(aFilesOrDirectories)
|
||||
, mIndex(0)
|
||||
{}
|
||||
|
||||
@ -203,17 +229,26 @@ public:
|
||||
HasMoreElements(bool* aRetvalue) override
|
||||
{
|
||||
MOZ_ASSERT(aRetvalue);
|
||||
*aRetvalue = mIndex < mFiles.Length();
|
||||
*aRetvalue = mIndex < mFilesOrDirectories.Length();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
GetNext(nsISupports** aSupports) override
|
||||
GetNext(nsISupports** aValue) override
|
||||
{
|
||||
NS_ENSURE_TRUE(mIndex < mFiles.Length(), NS_ERROR_FAILURE);
|
||||
NS_ENSURE_TRUE(mIndex < mFilesOrDirectories.Length(), NS_ERROR_FAILURE);
|
||||
|
||||
nsCOMPtr<nsIDOMBlob> blob = mFiles[mIndex++].get();
|
||||
blob.forget(aSupports);
|
||||
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);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -221,7 +256,7 @@ private:
|
||||
~SimpleEnumerator()
|
||||
{}
|
||||
|
||||
nsTArray<RefPtr<File>> mFiles;
|
||||
nsTArray<mozilla::dom::OwningFileOrDirectory> mFilesOrDirectories;
|
||||
uint32_t mIndex;
|
||||
};
|
||||
|
||||
@ -232,7 +267,8 @@ 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;
|
||||
}
|
||||
|
@ -13,17 +13,12 @@
|
||||
#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
|
||||
@ -59,13 +54,13 @@ public:
|
||||
|
||||
// PFilePickerChild
|
||||
virtual bool
|
||||
Recv__delete__(const MaybeInputFiles& aFiles, const int16_t& aResult) override;
|
||||
Recv__delete__(const MaybeInputData& aData, const int16_t& aResult) override;
|
||||
|
||||
private:
|
||||
~nsFilePickerProxy();
|
||||
void InitNative(nsIWidget*, const nsAString&) override;
|
||||
|
||||
nsTArray<RefPtr<mozilla::dom::File>> mFilesOrDirectories;
|
||||
nsTArray<mozilla::dom::OwningFileOrDirectory> mFilesOrDirectories;
|
||||
nsCOMPtr<nsIFilePickerShownCallback> mCallback;
|
||||
|
||||
int16_t mSelectedType;
|
||||
|
Loading…
x
Reference in New Issue
Block a user