mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-18 15:55:36 +00:00
54e16b14e3
Backed out changeset 74c27d7d13c5 (bug 1518442) Backed out changeset a092042068ab (bug 1518442) Backed out changeset 1df8f544b3b7 (bug 1518442) Backed out changeset 65f7059b0080 (bug 1518442) Backed out changeset c8fbe42eba38 (bug 1518442) Backed out changeset e7a459689227 (bug 1518442) --HG-- extra : rebase_source : 938405a9643076c7aa894059bcdd8d20a8110873
330 lines
9.9 KiB
C++
330 lines
9.9 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* 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/. */
|
|
|
|
#include "FormData.h"
|
|
#include "nsIInputStream.h"
|
|
#include "mozilla/dom/File.h"
|
|
#include "mozilla/dom/Directory.h"
|
|
#include "mozilla/dom/HTMLFormElement.h"
|
|
#include "mozilla/Encoding.h"
|
|
|
|
#include "MultipartBlobImpl.h"
|
|
|
|
using namespace mozilla;
|
|
using namespace mozilla::dom;
|
|
|
|
FormData::FormData(nsISupports* aOwner)
|
|
: HTMLFormSubmission(nullptr, EmptyString(), UTF_8_ENCODING, nullptr),
|
|
mOwner(aOwner) {}
|
|
|
|
namespace {
|
|
|
|
already_AddRefed<File> GetOrCreateFileCalledBlob(Blob& aBlob,
|
|
ErrorResult& aRv) {
|
|
// If this is file, we can just use it
|
|
RefPtr<File> file = aBlob.ToFile();
|
|
if (file) {
|
|
return file.forget();
|
|
}
|
|
|
|
// Forcing 'blob' as filename
|
|
file = aBlob.ToFile(NS_LITERAL_STRING("blob"), aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
return file.forget();
|
|
}
|
|
|
|
already_AddRefed<File> GetBlobForFormDataStorage(
|
|
Blob& aBlob, const Optional<nsAString>& aFilename, ErrorResult& aRv) {
|
|
// Forcing a filename
|
|
if (aFilename.WasPassed()) {
|
|
RefPtr<File> file = aBlob.ToFile(aFilename.Value(), aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return nullptr;
|
|
}
|
|
|
|
return file.forget();
|
|
}
|
|
|
|
return GetOrCreateFileCalledBlob(aBlob, aRv);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// -------------------------------------------------------------------------
|
|
// nsISupports
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_CLASS(FormData)
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FormData)
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner)
|
|
|
|
for (uint32_t i = 0, len = tmp->mFormData.Length(); i < len; ++i) {
|
|
ImplCycleCollectionUnlink(tmp->mFormData[i].value);
|
|
}
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
|
|
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(FormData)
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
|
|
|
|
for (uint32_t i = 0, len = tmp->mFormData.Length(); i < len; ++i) {
|
|
ImplCycleCollectionTraverse(cb, tmp->mFormData[i].value,
|
|
"mFormData[i].GetAsBlob()", 0);
|
|
}
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(FormData)
|
|
|
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(FormData)
|
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(FormData)
|
|
|
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FormData)
|
|
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
|
NS_INTERFACE_MAP_END
|
|
|
|
// -------------------------------------------------------------------------
|
|
// HTMLFormSubmission
|
|
nsresult FormData::GetEncodedSubmission(nsIURI* aURI,
|
|
nsIInputStream** aPostDataStream,
|
|
nsCOMPtr<nsIURI>& aOutURI) {
|
|
MOZ_ASSERT_UNREACHABLE("Shouldn't call FormData::GetEncodedSubmission");
|
|
return NS_OK;
|
|
}
|
|
|
|
void FormData::Append(const nsAString& aName, const nsAString& aValue,
|
|
ErrorResult& aRv) {
|
|
AddNameValuePair(aName, aValue);
|
|
}
|
|
|
|
void FormData::Append(const nsAString& aName, Blob& aBlob,
|
|
const Optional<nsAString>& aFilename, ErrorResult& aRv) {
|
|
RefPtr<File> file = GetBlobForFormDataStorage(aBlob, aFilename, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return;
|
|
}
|
|
|
|
AddNameBlobOrNullPair(aName, file);
|
|
}
|
|
|
|
void FormData::Append(const nsAString& aName, Directory* aDirectory) {
|
|
AddNameDirectoryPair(aName, aDirectory);
|
|
}
|
|
|
|
void FormData::Delete(const nsAString& aName) {
|
|
// We have to use this slightly awkward for loop since uint32_t >= 0 is an
|
|
// error for being always true.
|
|
for (uint32_t i = mFormData.Length(); i-- > 0;) {
|
|
if (aName.Equals(mFormData[i].name)) {
|
|
mFormData.RemoveElementAt(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
void FormData::Get(const nsAString& aName,
|
|
Nullable<OwningBlobOrDirectoryOrUSVString>& aOutValue) {
|
|
for (uint32_t i = 0; i < mFormData.Length(); ++i) {
|
|
if (aName.Equals(mFormData[i].name)) {
|
|
aOutValue.SetValue() = mFormData[i].value;
|
|
return;
|
|
}
|
|
}
|
|
|
|
aOutValue.SetNull();
|
|
}
|
|
|
|
void FormData::GetAll(const nsAString& aName,
|
|
nsTArray<OwningBlobOrDirectoryOrUSVString>& aValues) {
|
|
for (uint32_t i = 0; i < mFormData.Length(); ++i) {
|
|
if (aName.Equals(mFormData[i].name)) {
|
|
OwningBlobOrDirectoryOrUSVString* element = aValues.AppendElement();
|
|
*element = mFormData[i].value;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool FormData::Has(const nsAString& aName) {
|
|
for (uint32_t i = 0; i < mFormData.Length(); ++i) {
|
|
if (aName.Equals(mFormData[i].name)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
nsresult FormData::AddNameBlobOrNullPair(const nsAString& aName, Blob* aBlob) {
|
|
RefPtr<File> file;
|
|
|
|
if (!aBlob) {
|
|
FormDataTuple* data = mFormData.AppendElement();
|
|
SetNameValuePair(data, aName, EmptyString(), true /* aWasNullBlob */);
|
|
return NS_OK;
|
|
}
|
|
|
|
ErrorResult rv;
|
|
file = GetOrCreateFileCalledBlob(*aBlob, rv);
|
|
if (NS_WARN_IF(rv.Failed())) {
|
|
return rv.StealNSResult();
|
|
}
|
|
|
|
FormDataTuple* data = mFormData.AppendElement();
|
|
SetNameFilePair(data, aName, file);
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult FormData::AddNameDirectoryPair(const nsAString& aName,
|
|
Directory* aDirectory) {
|
|
MOZ_ASSERT(aDirectory);
|
|
|
|
FormDataTuple* data = mFormData.AppendElement();
|
|
SetNameDirectoryPair(data, aName, aDirectory);
|
|
return NS_OK;
|
|
}
|
|
|
|
FormData::FormDataTuple* FormData::RemoveAllOthersAndGetFirstFormDataTuple(
|
|
const nsAString& aName) {
|
|
FormDataTuple* lastFoundTuple = nullptr;
|
|
uint32_t lastFoundIndex = mFormData.Length();
|
|
// We have to use this slightly awkward for loop since uint32_t >= 0 is an
|
|
// error for being always true.
|
|
for (uint32_t i = mFormData.Length(); i-- > 0;) {
|
|
if (aName.Equals(mFormData[i].name)) {
|
|
if (lastFoundTuple) {
|
|
// The one we found earlier was not the first one, we can remove it.
|
|
mFormData.RemoveElementAt(lastFoundIndex);
|
|
}
|
|
|
|
lastFoundTuple = &mFormData[i];
|
|
lastFoundIndex = i;
|
|
}
|
|
}
|
|
|
|
return lastFoundTuple;
|
|
}
|
|
|
|
void FormData::Set(const nsAString& aName, Blob& aBlob,
|
|
const Optional<nsAString>& aFilename, ErrorResult& aRv) {
|
|
FormDataTuple* tuple = RemoveAllOthersAndGetFirstFormDataTuple(aName);
|
|
if (tuple) {
|
|
RefPtr<File> file = GetBlobForFormDataStorage(aBlob, aFilename, aRv);
|
|
if (NS_WARN_IF(aRv.Failed())) {
|
|
return;
|
|
}
|
|
|
|
SetNameFilePair(tuple, aName, file);
|
|
} else {
|
|
Append(aName, aBlob, aFilename, aRv);
|
|
}
|
|
}
|
|
|
|
void FormData::Set(const nsAString& aName, const nsAString& aValue,
|
|
ErrorResult& aRv) {
|
|
FormDataTuple* tuple = RemoveAllOthersAndGetFirstFormDataTuple(aName);
|
|
if (tuple) {
|
|
SetNameValuePair(tuple, aName, aValue);
|
|
} else {
|
|
Append(aName, aValue, aRv);
|
|
}
|
|
}
|
|
|
|
uint32_t FormData::GetIterableLength() const { return mFormData.Length(); }
|
|
|
|
const nsAString& FormData::GetKeyAtIndex(uint32_t aIndex) const {
|
|
MOZ_ASSERT(aIndex < mFormData.Length());
|
|
return mFormData[aIndex].name;
|
|
}
|
|
|
|
const OwningBlobOrDirectoryOrUSVString& FormData::GetValueAtIndex(
|
|
uint32_t aIndex) const {
|
|
MOZ_ASSERT(aIndex < mFormData.Length());
|
|
return mFormData[aIndex].value;
|
|
}
|
|
|
|
void FormData::SetNameValuePair(FormDataTuple* aData, const nsAString& aName,
|
|
const nsAString& aValue, bool aWasNullBlob) {
|
|
MOZ_ASSERT(aData);
|
|
aData->name = aName;
|
|
aData->wasNullBlob = aWasNullBlob;
|
|
aData->value.SetAsUSVString() = aValue;
|
|
}
|
|
|
|
void FormData::SetNameFilePair(FormDataTuple* aData, const nsAString& aName,
|
|
File* aFile) {
|
|
MOZ_ASSERT(aData);
|
|
MOZ_ASSERT(aFile);
|
|
|
|
aData->name = aName;
|
|
aData->wasNullBlob = false;
|
|
aData->value.SetAsBlob() = aFile;
|
|
}
|
|
|
|
void FormData::SetNameDirectoryPair(FormDataTuple* aData,
|
|
const nsAString& aName,
|
|
Directory* aDirectory) {
|
|
MOZ_ASSERT(aData);
|
|
MOZ_ASSERT(aDirectory);
|
|
|
|
aData->name = aName;
|
|
aData->wasNullBlob = false;
|
|
aData->value.SetAsDirectory() = aDirectory;
|
|
}
|
|
|
|
/* virtual */
|
|
JSObject* FormData::WrapObject(JSContext* aCx,
|
|
JS::Handle<JSObject*> aGivenProto) {
|
|
return FormData_Binding::Wrap(aCx, this, aGivenProto);
|
|
}
|
|
|
|
/* static */
|
|
already_AddRefed<FormData> FormData::Constructor(
|
|
const GlobalObject& aGlobal,
|
|
const Optional<NonNull<HTMLFormElement> >& aFormElement, ErrorResult& aRv) {
|
|
RefPtr<FormData> formData = new FormData(aGlobal.GetAsSupports());
|
|
if (aFormElement.WasPassed()) {
|
|
aRv = aFormElement.Value().WalkFormElements(formData);
|
|
}
|
|
return formData.forget();
|
|
}
|
|
|
|
// contentTypeWithCharset can be set to the contentType or
|
|
// contentType+charset based on what the spec says.
|
|
// See: https://fetch.spec.whatwg.org/#concept-bodyinit-extract
|
|
nsresult FormData::GetSendInfo(nsIInputStream** aBody, uint64_t* aContentLength,
|
|
nsACString& aContentTypeWithCharset,
|
|
nsACString& aCharset) const {
|
|
FSMultipartFormData fs(nullptr, EmptyString(), UTF_8_ENCODING, nullptr);
|
|
|
|
for (uint32_t i = 0; i < mFormData.Length(); ++i) {
|
|
if (mFormData[i].wasNullBlob) {
|
|
MOZ_ASSERT(mFormData[i].value.IsUSVString());
|
|
fs.AddNameBlobOrNullPair(mFormData[i].name, nullptr);
|
|
} else if (mFormData[i].value.IsUSVString()) {
|
|
fs.AddNameValuePair(mFormData[i].name,
|
|
mFormData[i].value.GetAsUSVString());
|
|
} else if (mFormData[i].value.IsBlob()) {
|
|
fs.AddNameBlobOrNullPair(mFormData[i].name,
|
|
mFormData[i].value.GetAsBlob());
|
|
} else {
|
|
MOZ_ASSERT(mFormData[i].value.IsDirectory());
|
|
fs.AddNameDirectoryPair(mFormData[i].name,
|
|
mFormData[i].value.GetAsDirectory());
|
|
}
|
|
}
|
|
|
|
fs.GetContentType(aContentTypeWithCharset);
|
|
aCharset.Truncate();
|
|
*aContentLength = 0;
|
|
NS_ADDREF(*aBody = fs.GetSubmissionBody(aContentLength));
|
|
|
|
return NS_OK;
|
|
}
|