Bug 1164310, part 4 - Implement the new HTMLInputElement API including the new Promise returning GetFilesAndDirectories. r=baku

This commit is contained in:
Jonathan Watt
2015-07-10 18:55:52 +01:00
parent e1c23abf45
commit 9c6f79e87b
6 changed files with 199 additions and 8 deletions

View File

@@ -10,7 +10,11 @@
#include "mozilla/AsyncEventDispatcher.h"
#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"
#include "nsIDOMHTMLInputElement.h"
#include "nsITextControlElement.h"
@@ -373,7 +377,8 @@ HTMLInputElement::nsFilePickerShownCallback::Done(int16_t aResult)
// Collect new selected filenames
nsTArray<nsRefPtr<File>> newFiles;
if (mode == static_cast<int16_t>(nsIFilePicker::modeOpenMultiple)) {
if (mode == static_cast<int16_t>(nsIFilePicker::modeOpenMultiple) ||
mode == static_cast<int16_t>(nsIFilePicker::modeGetFolder)) {
nsCOMPtr<nsISimpleEnumerator> iter;
nsresult rv = mFilePicker->GetDomfiles(getter_AddRefs(iter));
NS_ENSURE_SUCCESS(rv, rv);
@@ -2876,7 +2881,9 @@ HTMLInputElement::Focus(ErrorResult& aError)
return;
}
// For file inputs, focus the button instead.
// For file inputs, focus the first button instead. In the case of there
// being two buttons (when the picker is a directory picker) the user can
// tab to the next one.
nsIFrame* frame = GetPrimaryFrame();
if (frame) {
for (nsIFrame* childFrame = frame->GetFirstPrincipalChild();
@@ -3544,7 +3551,21 @@ HTMLInputElement::MaybeInitPickers(EventChainPostVisitor& aVisitor)
return NS_OK;
}
if (mType == NS_FORM_INPUT_FILE) {
return InitFilePicker(FILE_PICKER_FILE);
// If the user clicked on the "Choose folder..." button we open the
// directory picker, else we open the file picker.
FilePickerType type = FILE_PICKER_FILE;
nsCOMPtr<nsIContent> target =
do_QueryInterface(aVisitor.mEvent->originalTarget);
if (target &&
target->GetParent() == this &&
target->IsRootOfNativeAnonymousSubtree() &&
target->HasAttr(kNameSpaceID_None, nsGkAtoms::directory)) {
MOZ_ASSERT(Preferences::GetBool("dom.input.dirpicker", false),
"No API or UI should have been exposed to allow this code to "
"be reached");
type = FILE_PICKER_DIRECTORY;
}
return InitFilePicker(type);
}
if (mType == NS_FORM_INPUT_COLOR) {
return InitColorPicker();
@@ -4797,6 +4818,110 @@ HTMLInputElement::GetAttributeMappingFunction() const
}
// Directory picking methods:
bool
HTMLInputElement::IsFilesAndDirectoriesSupported() const
{
// This method is supposed to return true if a file and directory picker
// supports the selection of both files and directories *at the same time*.
// Only Mac currently supports that. We could implement it for Mac, but
// currently we do not.
return false;
}
void
HTMLInputElement::ChooseDirectory(ErrorResult& aRv)
{
if (mType != NS_FORM_INPUT_FILE) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
InitFilePicker(
#if defined(ANDROID) || defined(MOZ_B2G)
// No native directory picker - redirect to plain file picker
FILE_PICKER_FILE
#else
FILE_PICKER_DIRECTORY
#endif
);
}
static already_AddRefed<OSFileSystem>
MakeOrReuseFileSystem(const nsAString& aNewLocalRootPath,
OSFileSystem* aFS,
nsPIDOMWindow* aWindow)
{
MOZ_ASSERT(aWindow);
nsRefPtr<OSFileSystem> fs;
if (aFS) {
const nsAString& prevLocalRootPath = aFS->GetLocalRootPath();
if (aNewLocalRootPath == prevLocalRootPath) {
fs = aFS;
}
}
if (!fs) {
fs = new OSFileSystem(aNewLocalRootPath);
fs->Init(aWindow);
}
return fs.forget();
}
already_AddRefed<Promise>
HTMLInputElement::GetFilesAndDirectories(ErrorResult& aRv)
{
nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
MOZ_ASSERT(global);
if (!global) {
return nullptr;
}
nsRefPtr<Promise> p = Promise::Create(global, aRv);
if (aRv.Failed()) {
return nullptr;
}
const nsTArray<nsRefPtr<File>>& filesAndDirs = GetFilesInternal();
Sequence<OwningFileOrDirectory> filesAndDirsSeq;
if (!filesAndDirsSeq.SetLength(filesAndDirs.Length(), mozilla::fallible_t())) {
p->MaybeReject(NS_ERROR_OUT_OF_MEMORY);
return p.forget();
}
nsPIDOMWindow* window = OwnerDoc()->GetInnerWindow();
nsRefPtr<OSFileSystem> fs;
for (uint32_t i = 0; i < filesAndDirs.Length(); ++i) {
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);
nsDependentSubstring basename = Substring(path, leafSeparatorIndex);
fs = MakeOrReuseFileSystem(dirname, fs, window);
filesAndDirsSeq[i].SetAsDirectory() = new Directory(fs, basename);
} else {
filesAndDirsSeq[i].SetAsFile() = filesAndDirs[i];
}
}
p->MaybeResolve(filesAndDirsSeq);
return p.forget();
}
// Controllers Methods
nsIControllers*