From 5a7749a26f023e402dbfaf4f6744d9a8f89bc4bb Mon Sep 17 00:00:00 2001 From: Andrea Marchesini Date: Tue, 7 Jun 2016 00:55:16 +0200 Subject: [PATCH] Bug 1265767 - Subset of Blink FileSystem API - patch 2 - Entries, r=smaug --- dom/filesystem/compat/DirectoryEntry.cpp | 30 +++++- dom/filesystem/compat/DirectoryEntry.h | 21 +++- dom/filesystem/compat/Entry.cpp | 26 ++++- dom/filesystem/compat/Entry.h | 30 +++--- dom/filesystem/compat/FileEntry.cpp | 30 +++++- dom/filesystem/compat/FileEntry.h | 33 +++++-- dom/filesystem/compat/moz.build | 2 + dom/filesystem/compat/tests/mochitest.ini | 5 + dom/filesystem/compat/tests/moz.build | 7 ++ dom/filesystem/compat/tests/script_entries.js | 35 +++++++ dom/filesystem/compat/tests/test_basic.html | 97 +++++++++++++++++++ dom/html/HTMLInputElement.cpp | 37 +++++++ dom/html/HTMLInputElement.h | 10 +- dom/webidl/DOMFileSystem.webidl | 5 +- dom/webidl/HTMLInputElement.webidl | 6 +- js/xpconnect/src/Sandbox.cpp | 7 ++ js/xpconnect/src/xpcprivate.h | 1 + 17 files changed, 341 insertions(+), 41 deletions(-) create mode 100644 dom/filesystem/compat/tests/mochitest.ini create mode 100644 dom/filesystem/compat/tests/moz.build create mode 100644 dom/filesystem/compat/tests/script_entries.js create mode 100644 dom/filesystem/compat/tests/test_basic.html diff --git a/dom/filesystem/compat/DirectoryEntry.cpp b/dom/filesystem/compat/DirectoryEntry.cpp index 5f4f1037f667..87a67e8be854 100644 --- a/dom/filesystem/compat/DirectoryEntry.cpp +++ b/dom/filesystem/compat/DirectoryEntry.cpp @@ -5,13 +5,27 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "DirectoryEntry.h" +#include "mozilla/dom/Directory.h" namespace mozilla { namespace dom { -DirectoryEntry::DirectoryEntry(nsIGlobalObject* aGlobal) +NS_IMPL_CYCLE_COLLECTION_INHERITED(DirectoryEntry, Entry, mDirectory) + +NS_IMPL_ADDREF_INHERITED(DirectoryEntry, Entry) +NS_IMPL_RELEASE_INHERITED(DirectoryEntry, Entry) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DirectoryEntry) +NS_INTERFACE_MAP_END_INHERITING(Entry) + +DirectoryEntry::DirectoryEntry(nsIGlobalObject* aGlobal, + Directory* aDirectory) : Entry(aGlobal) -{} + , mDirectory(aDirectory) +{ + MOZ_ASSERT(aGlobal); + MOZ_ASSERT(mDirectory); +} DirectoryEntry::~DirectoryEntry() {} @@ -22,5 +36,17 @@ DirectoryEntry::WrapObject(JSContext* aCx, JS::Handle aGivenProto) return DirectoryEntryBinding::Wrap(aCx, this, aGivenProto); } +void +DirectoryEntry::GetName(nsAString& aName, ErrorResult& aRv) const +{ + mDirectory->GetName(aName, aRv); +} + +void +DirectoryEntry::GetFullPath(nsAString& aPath, ErrorResult& aRv) const +{ + mDirectory->GetPath(aPath, aRv); +} + } // dom namespace } // mozilla namespace diff --git a/dom/filesystem/compat/DirectoryEntry.h b/dom/filesystem/compat/DirectoryEntry.h index c8eaf06007cd..d29e7dc2eef8 100644 --- a/dom/filesystem/compat/DirectoryEntry.h +++ b/dom/filesystem/compat/DirectoryEntry.h @@ -13,14 +13,31 @@ namespace mozilla { namespace dom { +class Directory; + class DirectoryEntry final : public Entry { public: - explicit DirectoryEntry(nsIGlobalObject* aGlobalObject); + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(DirectoryEntry, Entry) + + DirectoryEntry(nsIGlobalObject* aGlobalObject, Directory* aDirectory); virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + virtual bool + IsDirectory() const override + { + return true; + } + + virtual void + GetName(nsAString& aName, ErrorResult& aRv) const override; + + virtual void + GetFullPath(nsAString& aFullPath, ErrorResult& aRv) const override; + already_AddRefed CreateReader(ErrorResult& aRv) const { @@ -56,6 +73,8 @@ public: private: ~DirectoryEntry(); + + RefPtr mDirectory; }; } // namespace dom diff --git a/dom/filesystem/compat/Entry.cpp b/dom/filesystem/compat/Entry.cpp index 5aa340a0fd5b..70aded33648b 100644 --- a/dom/filesystem/compat/Entry.cpp +++ b/dom/filesystem/compat/Entry.cpp @@ -5,6 +5,10 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "Entry.h" +#include "DirectoryEntry.h" +#include "FileEntry.h" + +#include "mozilla/dom/UnionTypes.h" namespace mozilla { namespace dom { @@ -19,9 +23,29 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Entry) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END +/* static */ already_AddRefed +Entry::Create(nsIGlobalObject* aGlobalObject, + const OwningFileOrDirectory& aFileOrDirectory) +{ + MOZ_ASSERT(aGlobalObject); + + RefPtr entry; + if (aFileOrDirectory.IsFile()) { + entry = new FileEntry(aGlobalObject, aFileOrDirectory.GetAsFile()); + } else { + MOZ_ASSERT(aFileOrDirectory.IsDirectory()); + entry = new DirectoryEntry(aGlobalObject, + aFileOrDirectory.GetAsDirectory()); + } + + return entry.forget(); +} + Entry::Entry(nsIGlobalObject* aGlobal) : mParent(aGlobal) -{} +{ + MOZ_ASSERT(aGlobal); +} Entry::~Entry() {} diff --git a/dom/filesystem/compat/Entry.h b/dom/filesystem/compat/Entry.h index 92bd3ddef56e..b4b1ce32a568 100644 --- a/dom/filesystem/compat/Entry.h +++ b/dom/filesystem/compat/Entry.h @@ -17,6 +17,7 @@ namespace mozilla { namespace dom { class DOMFileSystem; +class OwningFileOrDirectory; class Entry : public nsISupports @@ -26,7 +27,9 @@ public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Entry) - explicit Entry(nsIGlobalObject* aGlobalObject); + static already_AddRefed + Create(nsIGlobalObject* aGlobalObject, + const OwningFileOrDirectory& aFileOrDirectory); nsIGlobalObject* GetParentObject() const @@ -37,31 +40,23 @@ public: virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; - bool - GetIsFile(ErrorResult& aRv) const + virtual bool + IsFile() const { - aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); return false; } - bool - GetIsDirectory(ErrorResult& aRv) const + virtual bool + IsDirectory() const { - aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); return false; } - void - GetName(nsAString& aName, ErrorResult& aRv) const - { - aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); - } + virtual void + GetName(nsAString& aName, ErrorResult& aRv) const = 0; - void - GetFullPath(nsAString& aFullPath, ErrorResult& aRv) const - { - aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); - } + virtual void + GetFullPath(nsAString& aFullPath, ErrorResult& aRv) const = 0; DOMFileSystem* GetFilesystem(ErrorResult& aRv) const @@ -71,6 +66,7 @@ public: } protected: + Entry(nsIGlobalObject* aGlobalObject); virtual ~Entry(); private: diff --git a/dom/filesystem/compat/FileEntry.cpp b/dom/filesystem/compat/FileEntry.cpp index 6b3a2ef26ee9..b4ceb7d2b03b 100644 --- a/dom/filesystem/compat/FileEntry.cpp +++ b/dom/filesystem/compat/FileEntry.cpp @@ -5,13 +5,27 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "FileEntry.h" +#include "mozilla/dom/File.h" namespace mozilla { namespace dom { -FileEntry::FileEntry(nsIGlobalObject* aGlobal) +NS_IMPL_CYCLE_COLLECTION_INHERITED(FileEntry, Entry, mFile) + +NS_IMPL_ADDREF_INHERITED(FileEntry, Entry) +NS_IMPL_RELEASE_INHERITED(FileEntry, Entry) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FileEntry) +NS_INTERFACE_MAP_END_INHERITING(Entry) + +FileEntry::FileEntry(nsIGlobalObject* aGlobal, + File* aFile) : Entry(aGlobal) -{} + , mFile(aFile) +{ + MOZ_ASSERT(aGlobal); + MOZ_ASSERT(mFile); +} FileEntry::~FileEntry() {} @@ -22,5 +36,17 @@ FileEntry::WrapObject(JSContext* aCx, JS::Handle aGivenProto) return FileEntryBinding::Wrap(aCx, this, aGivenProto); } +void +FileEntry::GetName(nsAString& aName, ErrorResult& aRv) const +{ + mFile->GetName(aName); +} + +void +FileEntry::GetFullPath(nsAString& aPath, ErrorResult& aRv) const +{ + mFile->GetPath(aPath); +} + } // dom namespace } // mozilla namespace diff --git a/dom/filesystem/compat/FileEntry.h b/dom/filesystem/compat/FileEntry.h index 81cba2f81ccf..ec3e3577a98b 100644 --- a/dom/filesystem/compat/FileEntry.h +++ b/dom/filesystem/compat/FileEntry.h @@ -7,23 +7,36 @@ #ifndef mozilla_dom_FileEntry_h #define mozilla_dom_FileEntry_h -#include "mozilla/Attributes.h" -#include "mozilla/ErrorResult.h" -#include "mozilla/dom/BindingDeclarations.h" -#include "nsCycleCollectionParticipant.h" -#include "nsWrapperCache.h" +#include "mozilla/dom/Entry.h" namespace mozilla { namespace dom { +class File; + class FileEntry final : public Entry { public: - explicit FileEntry(nsIGlobalObject* aGlobalObject); + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FileEntry, Entry) + + FileEntry(nsIGlobalObject* aGlobalObject, File* aFile); virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; + virtual bool + IsFile() const override + { + return true; + } + + virtual void + GetName(nsAString& aName, ErrorResult& aRv) const override; + + virtual void + GetFullPath(nsAString& aFullPath, ErrorResult& aRv) const override; + void CreateWriter(VoidCallback& aSuccessCallback, const Optional>& aErrorCallback, @@ -33,15 +46,17 @@ public: } void - File(BlobCallback& aSuccessCallback, - const Optional>& aErrorCallback, - ErrorResult& aRv) const + GetFile(BlobCallback& aSuccessCallback, + const Optional>& aErrorCallback, + ErrorResult& aRv) const { aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); } private: ~FileEntry(); + + RefPtr mFile; }; } // namespace dom diff --git a/dom/filesystem/compat/moz.build b/dom/filesystem/compat/moz.build index 4ab2142dada9..18c67ec807b9 100644 --- a/dom/filesystem/compat/moz.build +++ b/dom/filesystem/compat/moz.build @@ -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 += [ 'DirectoryEntry.h', 'DirectoryReader.h', diff --git a/dom/filesystem/compat/tests/mochitest.ini b/dom/filesystem/compat/tests/mochitest.ini new file mode 100644 index 000000000000..9d411c9860a9 --- /dev/null +++ b/dom/filesystem/compat/tests/mochitest.ini @@ -0,0 +1,5 @@ +[DEFAULT] +support-files = + script_entries.js + +[test_basic.html] diff --git a/dom/filesystem/compat/tests/moz.build b/dom/filesystem/compat/tests/moz.build new file mode 100644 index 000000000000..8421b15157a7 --- /dev/null +++ b/dom/filesystem/compat/tests/moz.build @@ -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'] diff --git a/dom/filesystem/compat/tests/script_entries.js b/dom/filesystem/compat/tests/script_entries.js new file mode 100644 index 000000000000..f20bb1eba8fa --- /dev/null +++ b/dom/filesystem/compat/tests/script_entries.js @@ -0,0 +1,35 @@ +var { classes: Cc, interfaces: Ci, utils: Cu } = Components; +Cu.importGlobalProperties(["File", "Directory"]); + +addMessageListener("entries.open", function (e) { + var tmpFile = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIDirectoryService) + .QueryInterface(Ci.nsIProperties) + .get('TmpD', Ci.nsIFile) + tmpFile.append('file.txt'); + tmpFile.createUnique(Components.interfaces.nsIFile.FILE_TYPE, 0o600); + + var tmpDir = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIDirectoryService) + .QueryInterface(Ci.nsIProperties) + .get('TmpD', Ci.nsIFile) + + tmpDir.append('dir-test'); + tmpDir.createUnique(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o700); + + var file1 = tmpDir.clone(); + file1.append('foo.txt'); + file1.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0o600); + + var dir = tmpDir.clone(); + dir.append('subdir'); + dir.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0o700); + + var file2 = dir.clone(); + file2.append('bar.txt'); + file2.create(Components.interfaces.nsIFile.NORMAL_FILE_TYPE, 0o600); + + sendAsyncMessage("entries.opened", { + data: [ new Directory(tmpDir.path), new File(tmpFile) ] + }); +}); diff --git a/dom/filesystem/compat/tests/test_basic.html b/dom/filesystem/compat/tests/test_basic.html new file mode 100644 index 000000000000..40e31fda76c3 --- /dev/null +++ b/dom/filesystem/compat/tests/test_basic.html @@ -0,0 +1,97 @@ + + + + Test for Blink FileSystem API - subset + + + + + + + + + diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index 2ee9300d0354..ef0b8be957b7 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -79,6 +79,7 @@ #include "nsIRadioGroupContainer.h" // input type=file +#include "mozilla/dom/Entry.h" #include "mozilla/dom/File.h" #include "mozilla/dom/FileList.h" #include "nsIFile.h" @@ -1533,6 +1534,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLInputElement, } NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFileList) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEntries) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLInputElement, @@ -1541,6 +1543,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLInputElement, NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers) NS_IMPL_CYCLE_COLLECTION_UNLINK(mFilesOrDirectories) NS_IMPL_CYCLE_COLLECTION_UNLINK(mFileList) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mEntries) if (tmp->IsSingleLineTextControl(false)) { tmp->mInputData.mState->Unlink(); } @@ -3074,6 +3077,16 @@ HTMLInputElement::SetFiles(nsIDOMFileList* aFiles, AfterSetFilesOrDirectories(aSetValueChanged); } +void +HTMLInputElement::MozSetDndFilesAndDirectories(const nsTArray& aFilesOrDirectories) +{ + if (Preferences::GetBool("dom.webkitBlink.filesystem.enabled", false)) { + UpdateEntries(aFilesOrDirectories); + } + + SetFilesOrDirectories(aFilesOrDirectories, true); +} + void HTMLInputElement::AfterSetFilesOrDirectories(bool aSetValueChanged) { @@ -8183,6 +8196,30 @@ HTMLInputElement::ExploreDirectoryRecursively(bool aSetValueChanged) helper->AddCallback(callback); } +void +HTMLInputElement::UpdateEntries(const nsTArray& aFilesOrDirectories) +{ + mEntries.Clear(); + + nsCOMPtr global = OwnerDoc()->GetScopeObject(); + MOZ_ASSERT(global); + + for (uint32_t i = 0; i < aFilesOrDirectories.Length(); ++i) { + RefPtr entry = Entry::Create(global, aFilesOrDirectories[i]); + MOZ_ASSERT(entry); + + if (!mEntries.AppendElement(entry, fallible)) { + return; + } + } +} + +void +HTMLInputElement::GetWebkitEntries(nsTArray>& aSequence) +{ + aSequence.AppendElements(mEntries); +} + } // namespace dom } // namespace mozilla diff --git a/dom/html/HTMLInputElement.h b/dom/html/HTMLInputElement.h index 27a8fba55592..df77ceed5471 100644 --- a/dom/html/HTMLInputElement.h +++ b/dom/html/HTMLInputElement.h @@ -238,6 +238,8 @@ public: bool aSetValueChanged); void SetFiles(nsIDOMFileList* aFiles, bool aSetValueChanged); + void MozSetDndFilesAndDirectories(const nsTArray& aSequence); + // Called when a nsIFilePicker or a nsIColorPicker terminate. void PickerClosed(); @@ -716,10 +718,7 @@ public: SetHTMLBoolAttr(nsGkAtoms::webkitdirectory, aValue, aRv); } - void GetWebkitEntries(nsTArray>& aSequence, ErrorResult& aRv) - { - aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); - } + void GetWebkitEntries(nsTArray>& aSequence); bool IsFilesAndDirectoriesSupported() const; @@ -957,6 +956,8 @@ protected: */ void UpdateFileList(); + void UpdateEntries(const nsTArray& aFilesOrDirectories); + /** * Called after calling one of the SetFilesOrDirectories() functions. * This method can explore the directory recursively if needed. @@ -1334,6 +1335,7 @@ protected: #endif RefPtr mFileList; + Sequence> mEntries; nsString mStaticDocFileList; diff --git a/dom/webidl/DOMFileSystem.webidl b/dom/webidl/DOMFileSystem.webidl index 9d81a449ea9d..f474bf30b056 100644 --- a/dom/webidl/DOMFileSystem.webidl +++ b/dom/webidl/DOMFileSystem.webidl @@ -6,10 +6,7 @@ [NoInterfaceObject] interface Entry { - [GetterThrows] readonly attribute boolean isFile; - - [GetterThrows] readonly attribute boolean isDirectory; [GetterThrows] @@ -85,7 +82,7 @@ interface FileEntry : Entry { [Throws] void createWriter (VoidCallback successCallback, optional ErrorCallback errorCallback); - [Throws] + [Throws,BinaryName="GetFile"] void file (BlobCallback successCallback, optional ErrorCallback errorCallback); }; diff --git a/dom/webidl/HTMLInputElement.webidl b/dom/webidl/HTMLInputElement.webidl index 4dc33c591260..b15f8a126960 100644 --- a/dom/webidl/HTMLInputElement.webidl +++ b/dom/webidl/HTMLInputElement.webidl @@ -160,6 +160,10 @@ partial interface HTMLInputElement { [ChromeOnly, Throws] void mozSetDirectory(DOMString directoryPath); + // This method is meant to use for testing only. + [ChromeOnly] + void mozSetDndFilesAndDirectories(sequence<(File or Directory)> list); + // Number controls () have an anonymous text control // () in the anonymous shadow tree that they contain. On // such an anonymous text control this property provides access to the @@ -222,7 +226,7 @@ HTMLInputElement implements MozPhonetic; // Webkit/Blink partial interface HTMLInputElement { - [Pref="dom.webkitBlink.filesystem.enabled", Cached, Constant, GetterThrows] + [Pref="dom.webkitBlink.filesystem.enabled", Cached, Constant] readonly attribute sequence webkitEntries; [Pref="dom.webkitBlink.dirPicker.enabled", BinaryName="WebkitDirectoryAttr", SetterThrows] diff --git a/js/xpconnect/src/Sandbox.cpp b/js/xpconnect/src/Sandbox.cpp index 65348a06f587..fcbd149694ec 100644 --- a/js/xpconnect/src/Sandbox.cpp +++ b/js/xpconnect/src/Sandbox.cpp @@ -31,6 +31,7 @@ #include "mozilla/dom/BlobBinding.h" #include "mozilla/dom/cache/CacheStorage.h" #include "mozilla/dom/CSSBinding.h" +#include "mozilla/dom/DirectoryBinding.h" #include "mozilla/dom/IndexedDatabaseManager.h" #include "mozilla/dom/Fetch.h" #include "mozilla/dom/FileBinding.h" @@ -909,6 +910,8 @@ xpc::GlobalProperties::Parse(JSContext* cx, JS::HandleObject obj) btoa = true; } else if (!strcmp(name.ptr(), "Blob")) { Blob = true; + } else if (!strcmp(name.ptr(), "Directory")) { + Directory = true; } else if (!strcmp(name.ptr(), "File")) { File = true; } else if (!strcmp(name.ptr(), "crypto")) { @@ -974,6 +977,10 @@ xpc::GlobalProperties::Define(JSContext* cx, JS::HandleObject obj) !dom::BlobBinding::GetConstructorObject(cx, obj)) return false; + if (Directory && + !dom::DirectoryBinding::GetConstructorObject(cx, obj)) + return false; + if (File && !dom::FileBinding::GetConstructorObject(cx, obj)) return false; diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index 32ca3f4b5618..0445331d1a9b 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -3333,6 +3333,7 @@ struct GlobalProperties { bool atob : 1; bool btoa : 1; bool Blob : 1; + bool Directory : 1; bool File : 1; bool crypto : 1; bool rtcIdentityProvider : 1;