/* -*- 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 "HTMLFormSubmission.h" #include "HTMLFormElement.h" #include "nsCOMPtr.h" #include "nsIForm.h" #include "mozilla/dom/Document.h" #include "nsComponentManagerUtils.h" #include "nsGkAtoms.h" #include "nsIFormControl.h" #include "nsError.h" #include "nsGenericHTMLElement.h" #include "nsAttrValueInlines.h" #include "nsDirectoryServiceDefs.h" #include "nsStringStream.h" #include "nsIURI.h" #include "nsIURIMutator.h" #include "nsIURL.h" #include "nsNetUtil.h" #include "nsLinebreakConverter.h" #include "nsEscape.h" #include "nsUnicharUtils.h" #include "nsIMultiplexInputStream.h" #include "nsIMIMEInputStream.h" #include "nsIScriptError.h" #include "nsCExternalHandlerService.h" #include "nsContentUtils.h" #include "mozilla/dom/AncestorIterator.h" #include "mozilla/dom/Directory.h" #include "mozilla/dom/File.h" #include "mozilla/StaticPrefs_dom.h" #include "mozilla/RandomNum.h" namespace mozilla::dom { namespace { void SendJSWarning(Document* aDocument, const char* aWarningName, const nsTArray& aWarningArgs) { nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "HTML"_ns, aDocument, nsContentUtils::eFORMS_PROPERTIES, aWarningName, aWarningArgs); } void RetrieveFileName(Blob* aBlob, nsAString& aFilename) { if (!aBlob) { return; } RefPtr file = aBlob->ToFile(); if (file) { file->GetName(aFilename); } } void RetrieveDirectoryName(Directory* aDirectory, nsAString& aDirname) { MOZ_ASSERT(aDirectory); ErrorResult rv; aDirectory->GetName(aDirname, rv); if (NS_WARN_IF(rv.Failed())) { rv.SuppressException(); aDirname.Truncate(); } } // -------------------------------------------------------------------------- class FSURLEncoded : public EncodingFormSubmission { public: /** * @param aEncoding the character encoding of the form * @param aMethod the method of the submit (either NS_FORM_METHOD_GET or * NS_FORM_METHOD_POST). */ FSURLEncoded(nsIURI* aActionURL, const nsAString& aTarget, NotNull aEncoding, int32_t aMethod, Document* aDocument, Element* aSubmitter) : EncodingFormSubmission(aActionURL, aTarget, aEncoding, aSubmitter), mMethod(aMethod), mDocument(aDocument), mWarnedFileControl(false) {} virtual nsresult AddNameValuePair(const nsAString& aName, const nsAString& aValue) override; virtual nsresult AddNameBlobPair(const nsAString& aName, Blob* aBlob) override; virtual nsresult AddNameDirectoryPair(const nsAString& aName, Directory* aDirectory) override; virtual nsresult GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream, nsCOMPtr& aOutURI) override; protected: /** * URL encode a Unicode string by encoding it to bytes, converting linebreaks * properly, and then escaping many bytes as %xx. * * @param aStr the string to encode * @param aEncoded the encoded string [OUT] * @throws NS_ERROR_OUT_OF_MEMORY if we run out of memory */ nsresult URLEncode(const nsAString& aStr, nsACString& aEncoded); private: /** * The method of the submit (either NS_FORM_METHOD_GET or * NS_FORM_METHOD_POST). */ int32_t mMethod; /** The query string so far (the part after the ?) */ nsCString mQueryString; /** The document whose URI to use when reporting errors */ nsCOMPtr mDocument; /** Whether or not we have warned about a file control not being submitted */ bool mWarnedFileControl; }; nsresult FSURLEncoded::AddNameValuePair(const nsAString& aName, const nsAString& aValue) { // Encode value nsCString convValue; nsresult rv = URLEncode(aValue, convValue); NS_ENSURE_SUCCESS(rv, rv); // Encode name nsAutoCString convName; rv = URLEncode(aName, convName); NS_ENSURE_SUCCESS(rv, rv); // Append data to string if (mQueryString.IsEmpty()) { mQueryString += convName + "="_ns + convValue; } else { mQueryString += "&"_ns + convName + "="_ns + convValue; } return NS_OK; } nsresult FSURLEncoded::AddNameBlobPair(const nsAString& aName, Blob* aBlob) { if (!mWarnedFileControl) { SendJSWarning(mDocument, "ForgotFileEnctypeWarning", nsTArray()); mWarnedFileControl = true; } nsAutoString filename; RetrieveFileName(aBlob, filename); return AddNameValuePair(aName, filename); } nsresult FSURLEncoded::AddNameDirectoryPair(const nsAString& aName, Directory* aDirectory) { // No warning about because Directory objects are never sent via form. nsAutoString dirname; RetrieveDirectoryName(aDirectory, dirname); return AddNameValuePair(aName, dirname); } void HandleMailtoSubject(nsCString& aPath) { // Walk through the string and see if we have a subject already. bool hasSubject = false; bool hasParams = false; int32_t paramSep = aPath.FindChar('?'); while (paramSep != kNotFound && paramSep < (int32_t)aPath.Length()) { hasParams = true; // Get the end of the name at the = op. If it is *after* the next &, // assume that someone made a parameter without an = in it int32_t nameEnd = aPath.FindChar('=', paramSep + 1); int32_t nextParamSep = aPath.FindChar('&', paramSep + 1); if (nextParamSep == kNotFound) { nextParamSep = aPath.Length(); } // If the = op is after the &, this parameter is a name without value. // If there is no = op, same thing. if (nameEnd == kNotFound || nextParamSep < nameEnd) { nameEnd = nextParamSep; } if (nameEnd != kNotFound) { if (Substring(aPath, paramSep + 1, nameEnd - (paramSep + 1)) .LowerCaseEqualsLiteral("subject")) { hasSubject = true; break; } } paramSep = nextParamSep; } // If there is no subject, append a preformed subject to the mailto line if (!hasSubject) { if (hasParams) { aPath.Append('&'); } else { aPath.Append('?'); } // Get the default subject nsAutoString brandName; nsresult rv = nsContentUtils::GetLocalizedString( nsContentUtils::eBRAND_PROPERTIES, "brandShortName", brandName); if (NS_FAILED(rv)) return; nsAutoString subjectStr; rv = nsContentUtils::FormatLocalizedString( subjectStr, nsContentUtils::eFORMS_PROPERTIES, "DefaultFormSubject", brandName); if (NS_FAILED(rv)) return; aPath.AppendLiteral("subject="); nsCString subjectStrEscaped; rv = NS_EscapeURL(NS_ConvertUTF16toUTF8(subjectStr), esc_Query, subjectStrEscaped, mozilla::fallible); if (NS_FAILED(rv)) return; aPath.Append(subjectStrEscaped); } } nsresult FSURLEncoded::GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream, nsCOMPtr& aOutURI) { nsresult rv = NS_OK; aOutURI = aURI; *aPostDataStream = nullptr; if (mMethod == NS_FORM_METHOD_POST) { if (aURI->SchemeIs("mailto")) { nsAutoCString path; rv = aURI->GetPathQueryRef(path); NS_ENSURE_SUCCESS(rv, rv); HandleMailtoSubject(path); // Append the body to and force-plain-text args to the mailto line nsAutoCString escapedBody; if (NS_WARN_IF(!NS_Escape(mQueryString, escapedBody, url_XAlphas))) { return NS_ERROR_OUT_OF_MEMORY; } path += "&force-plain-text=Y&body="_ns + escapedBody; return NS_MutateURI(aURI).SetPathQueryRef(path).Finalize(aOutURI); } else { nsCOMPtr dataStream; rv = NS_NewCStringInputStream(getter_AddRefs(dataStream), std::move(mQueryString)); NS_ENSURE_SUCCESS(rv, rv); mQueryString.Truncate(); nsCOMPtr mimeStream( do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv)); NS_ENSURE_SUCCESS(rv, rv); mimeStream->AddHeader("Content-Type", "application/x-www-form-urlencoded"); mimeStream->SetData(dataStream); mimeStream.forget(aPostDataStream); } } else { // Get the full query string if (aURI->SchemeIs("javascript")) { return NS_OK; } nsCOMPtr url = do_QueryInterface(aURI); if (url) { // Make sure that we end up with a query component in the URL. If // mQueryString is empty, nsIURI::SetQuery() will remove the query // component, which is not what we want. rv = NS_MutateURI(aURI) .SetQuery(mQueryString.IsEmpty() ? "?"_ns : mQueryString) .Finalize(aOutURI); } else { nsAutoCString path; rv = aURI->GetPathQueryRef(path); NS_ENSURE_SUCCESS(rv, rv); // Bug 42616: Trim off named anchor and save it to add later int32_t namedAnchorPos = path.FindChar('#'); nsAutoCString namedAnchor; if (kNotFound != namedAnchorPos) { path.Right(namedAnchor, (path.Length() - namedAnchorPos)); path.Truncate(namedAnchorPos); } // Chop off old query string (bug 25330, 57333) // Only do this for GET not POST (bug 41585) int32_t queryStart = path.FindChar('?'); if (kNotFound != queryStart) { path.Truncate(queryStart); } path.Append('?'); // Bug 42616: Add named anchor to end after query string path.Append(mQueryString + namedAnchor); rv = NS_MutateURI(aURI).SetPathQueryRef(path).Finalize(aOutURI); } } return rv; } // i18n helper routines nsresult FSURLEncoded::URLEncode(const nsAString& aStr, nsACString& aEncoded) { // convert to CRLF breaks int32_t convertedBufLength = 0; char16_t* convertedBuf = nsLinebreakConverter::ConvertUnicharLineBreaks( aStr.BeginReading(), nsLinebreakConverter::eLinebreakAny, nsLinebreakConverter::eLinebreakNet, aStr.Length(), &convertedBufLength); NS_ENSURE_TRUE(convertedBuf, NS_ERROR_OUT_OF_MEMORY); nsAutoString convertedString; convertedString.Adopt(convertedBuf, convertedBufLength); nsAutoCString encodedBuf; nsresult rv = EncodeVal(convertedString, encodedBuf, false); NS_ENSURE_SUCCESS(rv, rv); if (NS_WARN_IF(!NS_Escape(encodedBuf, aEncoded, url_XPAlphas))) { return NS_ERROR_OUT_OF_MEMORY; } return NS_OK; } } // anonymous namespace // -------------------------------------------------------------------------- FSMultipartFormData::FSMultipartFormData(nsIURI* aActionURL, const nsAString& aTarget, NotNull aEncoding, Element* aSubmitter) : EncodingFormSubmission(aActionURL, aTarget, aEncoding, aSubmitter) { mPostData = do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1"); nsCOMPtr inputStream = do_QueryInterface(mPostData); MOZ_ASSERT(SameCOMIdentity(mPostData, inputStream)); mPostDataStream = inputStream; mTotalLength = 0; mBoundary.AssignLiteral("---------------------------"); mBoundary.AppendInt(static_cast(mozilla::RandomUint64OrDie())); mBoundary.AppendInt(static_cast(mozilla::RandomUint64OrDie())); mBoundary.AppendInt(static_cast(mozilla::RandomUint64OrDie())); } FSMultipartFormData::~FSMultipartFormData() { NS_ASSERTION(mPostDataChunk.IsEmpty(), "Left unsubmitted data"); } nsIInputStream* FSMultipartFormData::GetSubmissionBody( uint64_t* aContentLength) { // Finish data mPostDataChunk += "--"_ns + mBoundary + nsLiteralCString("--" CRLF); // Add final data input stream AddPostDataStream(); *aContentLength = mTotalLength; return mPostDataStream; } nsresult FSMultipartFormData::AddNameValuePair(const nsAString& aName, const nsAString& aValue) { nsCString valueStr; nsAutoCString encodedVal; nsresult rv = EncodeVal(aValue, encodedVal, false); NS_ENSURE_SUCCESS(rv, rv); int32_t convertedBufLength = 0; char* convertedBuf = nsLinebreakConverter::ConvertLineBreaks( encodedVal.get(), nsLinebreakConverter::eLinebreakAny, nsLinebreakConverter::eLinebreakNet, encodedVal.Length(), &convertedBufLength); valueStr.Adopt(convertedBuf, convertedBufLength); nsAutoCString nameStr; rv = EncodeVal(aName, nameStr, true); NS_ENSURE_SUCCESS(rv, rv); // Make MIME block for name/value pair // XXX: name parameter should be encoded per RFC 2231 // RFC 2388 specifies that RFC 2047 be used, but I think it's not // consistent with MIME standard. mPostDataChunk += "--"_ns + mBoundary + nsLiteralCString(CRLF) + "Content-Disposition: form-data; name=\""_ns + nameStr + nsLiteralCString("\"" CRLF CRLF) + valueStr + nsLiteralCString(CRLF); return NS_OK; } nsresult FSMultipartFormData::AddNameBlobPair(const nsAString& aName, Blob* aBlob) { MOZ_ASSERT(aBlob); // Encode the control name nsAutoCString nameStr; nsresult rv = EncodeVal(aName, nameStr, true); NS_ENSURE_SUCCESS(rv, rv); ErrorResult error; uint64_t size = 0; nsAutoCString filename; nsAutoCString contentType; nsCOMPtr fileStream; nsAutoString filename16; RefPtr file = aBlob->ToFile(); if (file) { nsAutoString relativePath; file->GetRelativePath(relativePath); if (StaticPrefs::dom_webkitBlink_dirPicker_enabled() && !relativePath.IsEmpty()) { filename16 = relativePath; } if (filename16.IsEmpty()) { RetrieveFileName(aBlob, filename16); } } rv = EncodeVal(filename16, filename, true); NS_ENSURE_SUCCESS(rv, rv); // Get content type nsAutoString contentType16; aBlob->GetType(contentType16); if (contentType16.IsEmpty()) { contentType16.AssignLiteral("application/octet-stream"); } NS_ConvertUTF16toUTF8 contentType8(contentType16); int32_t convertedBufLength = 0; char* convertedBuf = nsLinebreakConverter::ConvertLineBreaks( contentType8.get(), nsLinebreakConverter::eLinebreakAny, nsLinebreakConverter::eLinebreakSpace, contentType8.Length(), &convertedBufLength); contentType.Adopt(convertedBuf, convertedBufLength); // Get input stream aBlob->CreateInputStream(getter_AddRefs(fileStream), error); if (NS_WARN_IF(error.Failed())) { return error.StealNSResult(); } // Get size size = aBlob->GetSize(error); if (error.Failed()) { error.SuppressException(); fileStream = nullptr; } if (fileStream) { // Create buffered stream (for efficiency) nsCOMPtr bufferedStream; rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), fileStream.forget(), 8192); NS_ENSURE_SUCCESS(rv, rv); fileStream = bufferedStream; } AddDataChunk(nameStr, filename, contentType, fileStream, size); return NS_OK; } nsresult FSMultipartFormData::AddNameDirectoryPair(const nsAString& aName, Directory* aDirectory) { if (!StaticPrefs::dom_webkitBlink_dirPicker_enabled()) { return NS_OK; } // Encode the control name nsAutoCString nameStr; nsresult rv = EncodeVal(aName, nameStr, true); NS_ENSURE_SUCCESS(rv, rv); nsAutoCString dirname; nsAutoString dirname16; ErrorResult error; nsAutoString path; aDirectory->GetPath(path, error); if (NS_WARN_IF(error.Failed())) { error.SuppressException(); } else { dirname16 = path; } if (dirname16.IsEmpty()) { RetrieveDirectoryName(aDirectory, dirname16); } rv = EncodeVal(dirname16, dirname, true); NS_ENSURE_SUCCESS(rv, rv); AddDataChunk(nameStr, dirname, "application/octet-stream"_ns, nullptr, 0); return NS_OK; } void FSMultipartFormData::AddDataChunk(const nsACString& aName, const nsACString& aFilename, const nsACString& aContentType, nsIInputStream* aInputStream, uint64_t aInputStreamSize) { // // Make MIME block for name/value pair // // more appropriate than always using binary? mPostDataChunk += "--"_ns + mBoundary + nsLiteralCString(CRLF); // XXX: name/filename parameter should be encoded per RFC 2231 // RFC 2388 specifies that RFC 2047 be used, but I think it's not // consistent with the MIME standard. mPostDataChunk += "Content-Disposition: form-data; name=\""_ns + aName + "\"; filename=\""_ns + aFilename + nsLiteralCString("\"" CRLF) + "Content-Type: "_ns + aContentType + nsLiteralCString(CRLF CRLF); // We should not try to append an invalid stream. That will happen for example // if we try to update a file that actually do not exist. if (aInputStream) { // We need to dump the data up to this point into the POST data stream // here, since we're about to add the file input stream AddPostDataStream(); mPostData->AppendStream(aInputStream); mTotalLength += aInputStreamSize; } // CRLF after file mPostDataChunk.AppendLiteral(CRLF); } nsresult FSMultipartFormData::GetEncodedSubmission( nsIURI* aURI, nsIInputStream** aPostDataStream, nsCOMPtr& aOutURI) { nsresult rv; aOutURI = aURI; // Make header nsCOMPtr mimeStream = do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv); NS_ENSURE_SUCCESS(rv, rv); nsAutoCString contentType; GetContentType(contentType); mimeStream->AddHeader("Content-Type", contentType.get()); uint64_t bodySize; mimeStream->SetData(GetSubmissionBody(&bodySize)); mimeStream.forget(aPostDataStream); return NS_OK; } nsresult FSMultipartFormData::AddPostDataStream() { nsresult rv = NS_OK; nsCOMPtr postDataChunkStream; rv = NS_NewCStringInputStream(getter_AddRefs(postDataChunkStream), mPostDataChunk); NS_ASSERTION(postDataChunkStream, "Could not open a stream for POST!"); if (postDataChunkStream) { mPostData->AppendStream(postDataChunkStream); mTotalLength += mPostDataChunk.Length(); } mPostDataChunk.Truncate(); return rv; } // -------------------------------------------------------------------------- namespace { class FSTextPlain : public EncodingFormSubmission { public: FSTextPlain(nsIURI* aActionURL, const nsAString& aTarget, NotNull aEncoding, Element* aSubmitter) : EncodingFormSubmission(aActionURL, aTarget, aEncoding, aSubmitter) {} virtual nsresult AddNameValuePair(const nsAString& aName, const nsAString& aValue) override; virtual nsresult AddNameBlobPair(const nsAString& aName, Blob* aBlob) override; virtual nsresult AddNameDirectoryPair(const nsAString& aName, Directory* aDirectory) override; virtual nsresult GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream, nsCOMPtr& aOutURI) override; private: nsString mBody; }; nsresult FSTextPlain::AddNameValuePair(const nsAString& aName, const nsAString& aValue) { // XXX This won't work well with a name like "a=b" or "a\nb" but I suppose // text/plain doesn't care about that. Parsers aren't built for escaped // values so we'll have to live with it. mBody.Append(aName + u"="_ns + aValue + NS_LITERAL_STRING_FROM_CSTRING(CRLF)); return NS_OK; } nsresult FSTextPlain::AddNameBlobPair(const nsAString& aName, Blob* aBlob) { nsAutoString filename; RetrieveFileName(aBlob, filename); AddNameValuePair(aName, filename); return NS_OK; } nsresult FSTextPlain::AddNameDirectoryPair(const nsAString& aName, Directory* aDirectory) { nsAutoString dirname; RetrieveDirectoryName(aDirectory, dirname); AddNameValuePair(aName, dirname); return NS_OK; } nsresult FSTextPlain::GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream, nsCOMPtr& aOutURI) { nsresult rv = NS_OK; aOutURI = aURI; *aPostDataStream = nullptr; // XXX HACK We are using the standard URL mechanism to give the body to the // mailer instead of passing the post data stream to it, since that sounds // hard. if (aURI->SchemeIs("mailto")) { nsAutoCString path; rv = aURI->GetPathQueryRef(path); NS_ENSURE_SUCCESS(rv, rv); HandleMailtoSubject(path); // Append the body to and force-plain-text args to the mailto line nsAutoCString escapedBody; if (NS_WARN_IF(!NS_Escape(NS_ConvertUTF16toUTF8(mBody), escapedBody, url_XAlphas))) { return NS_ERROR_OUT_OF_MEMORY; } path += "&force-plain-text=Y&body="_ns + escapedBody; rv = NS_MutateURI(aURI).SetPathQueryRef(path).Finalize(aOutURI); } else { // Create data stream. // We do want to send the data through the charset encoder and we want to // normalize linebreaks to use the "standard net" format (\r\n), but we // don't want to perform any other encoding. This means that names and // values which contains '=' or newlines are potentially ambigiously // encoded, but that how text/plain is specced. nsCString cbody; EncodeVal(mBody, cbody, false); int32_t convertedBufLength = 0; char* convertedBuf = nsLinebreakConverter::ConvertLineBreaks( cbody.get(), nsLinebreakConverter::eLinebreakAny, nsLinebreakConverter::eLinebreakNet, cbody.Length(), &convertedBufLength); cbody.Adopt(convertedBuf, convertedBufLength); nsCOMPtr bodyStream; rv = NS_NewCStringInputStream(getter_AddRefs(bodyStream), std::move(cbody)); if (!bodyStream) { return NS_ERROR_OUT_OF_MEMORY; } // Create mime stream with headers and such nsCOMPtr mimeStream = do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv); NS_ENSURE_SUCCESS(rv, rv); mimeStream->AddHeader("Content-Type", "text/plain"); mimeStream->SetData(bodyStream); mimeStream.forget(aPostDataStream); } return rv; } } // anonymous namespace // -------------------------------------------------------------------------- HTMLFormSubmission::HTMLFormSubmission( nsIURI* aActionURL, const nsAString& aTarget, mozilla::NotNull aEncoding, Element* aSubmitter) : mActionURL(aActionURL), mTarget(aTarget), mEncoding(aEncoding), mSubmitter(aSubmitter), mInitiatedFromUserInput(UserActivation::IsHandlingUserInput()) { MOZ_COUNT_CTOR(HTMLFormSubmission); } EncodingFormSubmission::EncodingFormSubmission( nsIURI* aActionURL, const nsAString& aTarget, NotNull aEncoding, Element* aSubmitter) : HTMLFormSubmission(aActionURL, aTarget, aEncoding, aSubmitter) { if (!aEncoding->CanEncodeEverything()) { nsAutoCString name; aEncoding->Name(name); AutoTArray args; CopyUTF8toUTF16(name, *args.AppendElement()); SendJSWarning(aSubmitter ? aSubmitter->GetOwnerDocument() : nullptr, "CannotEncodeAllUnicode", args); } } EncodingFormSubmission::~EncodingFormSubmission() = default; // i18n helper routines nsresult EncodingFormSubmission::EncodeVal(const nsAString& aStr, nsCString& aOut, bool aHeaderEncode) { nsresult rv; const Encoding* ignored; Tie(rv, ignored) = mEncoding->Encode(aStr, aOut); if (NS_FAILED(rv)) { return rv; } if (aHeaderEncode) { int32_t convertedBufLength = 0; char* convertedBuf = nsLinebreakConverter::ConvertLineBreaks( aOut.get(), nsLinebreakConverter::eLinebreakAny, nsLinebreakConverter::eLinebreakSpace, aOut.Length(), &convertedBufLength); aOut.Adopt(convertedBuf, convertedBufLength); aOut.ReplaceSubstring("\""_ns, "\\\""_ns); } return NS_OK; } // -------------------------------------------------------------------------- namespace { void GetEnumAttr(nsGenericHTMLElement* aContent, nsAtom* atom, int32_t* aValue) { const nsAttrValue* value = aContent->GetParsedAttr(atom); if (value && value->Type() == nsAttrValue::eEnum) { *aValue = value->GetEnumValue(); } } } // anonymous namespace /* static */ nsresult HTMLFormSubmission::GetFromForm(HTMLFormElement* aForm, nsGenericHTMLElement* aSubmitter, NotNull& aEncoding, HTMLFormSubmission** aFormSubmission) { // Get all the information necessary to encode the form data NS_ASSERTION(aForm->GetComposedDoc(), "Should have doc if we're building submission!"); nsresult rv; // Get action nsCOMPtr actionURL; rv = aForm->GetActionURL(getter_AddRefs(actionURL), aSubmitter); NS_ENSURE_SUCCESS(rv, rv); // Check if CSP allows this form-action nsCOMPtr csp = aForm->GetCsp(); if (csp) { bool permitsFormAction = true; // form-action is only enforced if explicitly defined in the // policy - do *not* consult default-src, see: // http://www.w3.org/TR/CSP2/#directive-default-src rv = csp->Permits(aForm, nullptr /* nsICSPEventListener */, actionURL, nsIContentSecurityPolicy::FORM_ACTION_DIRECTIVE, true, &permitsFormAction); NS_ENSURE_SUCCESS(rv, rv); if (!permitsFormAction) { return NS_ERROR_CSP_FORM_ACTION_VIOLATION; } } // Get target // The target is the submitter element formtarget attribute if the element // is a submit control and has such an attribute. // Otherwise, the target is the form owner's target attribute, // if it has such an attribute. // Finally, if one of the child nodes of the head element is a base element // with a target attribute, then the value of the target attribute of the // first such base element; or, if there is no such element, the empty string. nsAutoString target; if (!(aSubmitter && aSubmitter->GetAttr(kNameSpaceID_None, nsGkAtoms::formtarget, target)) && !aForm->GetAttr(kNameSpaceID_None, nsGkAtoms::target, target)) { aForm->GetBaseTarget(target); } // Get encoding type (default: urlencoded) int32_t enctype = NS_FORM_ENCTYPE_URLENCODED; if (aSubmitter && aSubmitter->HasAttr(kNameSpaceID_None, nsGkAtoms::formenctype)) { GetEnumAttr(aSubmitter, nsGkAtoms::formenctype, &enctype); } else { GetEnumAttr(aForm, nsGkAtoms::enctype, &enctype); } // Get method (default: GET) int32_t method = NS_FORM_METHOD_GET; if (aSubmitter && aSubmitter->HasAttr(kNameSpaceID_None, nsGkAtoms::formmethod)) { GetEnumAttr(aSubmitter, nsGkAtoms::formmethod, &method); } else { GetEnumAttr(aForm, nsGkAtoms::method, &method); } if (method == NS_FORM_METHOD_DIALOG) { HTMLDialogElement* dialog = aForm->FirstAncestorOfType(); // If there isn't one, or if it does not have an open attribute, do // nothing. if (!dialog || !dialog->Open()) { return NS_ERROR_FAILURE; } nsAutoString result; if (aSubmitter) { aSubmitter->ResultForDialogSubmit(result); } *aFormSubmission = new DialogFormSubmission(result, actionURL, target, aEncoding, aSubmitter, dialog); return NS_OK; } MOZ_ASSERT(method != NS_FORM_METHOD_DIALOG); // Choose encoder if (method == NS_FORM_METHOD_POST && enctype == NS_FORM_ENCTYPE_MULTIPART) { *aFormSubmission = new FSMultipartFormData(actionURL, target, aEncoding, aSubmitter); } else if (method == NS_FORM_METHOD_POST && enctype == NS_FORM_ENCTYPE_TEXTPLAIN) { *aFormSubmission = new FSTextPlain(actionURL, target, aEncoding, aSubmitter); } else { Document* doc = aForm->OwnerDoc(); if (enctype == NS_FORM_ENCTYPE_MULTIPART || enctype == NS_FORM_ENCTYPE_TEXTPLAIN) { AutoTArray args; nsString& enctypeStr = *args.AppendElement(); if (aSubmitter && aSubmitter->HasAttr(kNameSpaceID_None, nsGkAtoms::formenctype)) { aSubmitter->GetAttr(kNameSpaceID_None, nsGkAtoms::formenctype, enctypeStr); } else { aForm->GetAttr(kNameSpaceID_None, nsGkAtoms::enctype, enctypeStr); } SendJSWarning(doc, "ForgotPostWarning", args); } *aFormSubmission = new FSURLEncoded(actionURL, target, aEncoding, method, doc, aSubmitter); } return NS_OK; } } // namespace mozilla::dom