gecko-dev/dom/base/FormData.cpp
Cosmin Sabou 54e16b14e3 Backed out 6 changesets (bug 1518442) as requested by jdai.
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
2019-09-06 17:58:57 +03:00

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