1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "HTMLFormSubmission.h"
11 #include "mozilla/dom/Document.h"
12 #include "nsGkAtoms.h"
13 #include "nsIFormControl.h"
15 #include "nsGenericHTMLElement.h"
16 #include "nsAttrValueInlines.h"
18 #include "nsDirectoryServiceDefs.h"
19 #include "nsStringStream.h"
21 #include "nsIURIMutator.h"
23 #include "nsNetUtil.h"
24 #include "nsLinebreakConverter.h"
26 #include "nsUnicharUtils.h"
27 #include "nsIMultiplexInputStream.h"
28 #include "nsIMIMEInputStream.h"
29 #include "nsIMIMEService.h"
30 #include "nsIConsoleService.h"
31 #include "nsIScriptError.h"
32 #include "nsIStringBundle.h"
33 #include "nsCExternalHandlerService.h"
34 #include "nsIFileStreams.h"
35 #include "nsContentUtils.h"
37 #include "mozilla/dom/Directory.h"
38 #include "mozilla/dom/File.h"
39 #include "mozilla/StaticPrefs.h"
46 void SendJSWarning(Document
* aDocument
, const char* aWarningName
,
47 const nsTArray
<nsString
>& aWarningArgs
) {
48 nsContentUtils::ReportToConsole(
49 nsIScriptError::warningFlag
, NS_LITERAL_CSTRING("HTML"), aDocument
,
50 nsContentUtils::eFORMS_PROPERTIES
, aWarningName
, aWarningArgs
);
53 void RetrieveFileName(Blob
* aBlob
, nsAString
& aFilename
) {
58 RefPtr
<File
> file
= aBlob
->ToFile();
60 file
->GetName(aFilename
);
64 void RetrieveDirectoryName(Directory
* aDirectory
, nsAString
& aDirname
) {
65 MOZ_ASSERT(aDirectory
);
68 aDirectory
->GetName(aDirname
, rv
);
69 if (NS_WARN_IF(rv
.Failed())) {
70 rv
.SuppressException();
75 // --------------------------------------------------------------------------
77 class FSURLEncoded
: public EncodingFormSubmission
{
80 * @param aEncoding the character encoding of the form
81 * @param aMethod the method of the submit (either NS_FORM_METHOD_GET or
82 * NS_FORM_METHOD_POST).
84 FSURLEncoded(nsIURI
* aActionURL
, const nsAString
& aTarget
,
85 NotNull
<const Encoding
*> aEncoding
, int32_t aMethod
,
86 Document
* aDocument
, Element
* aOriginatingElement
)
87 : EncodingFormSubmission(aActionURL
, aTarget
, aEncoding
,
91 mWarnedFileControl(false) {}
93 virtual nsresult
AddNameValuePair(const nsAString
& aName
,
94 const nsAString
& aValue
) override
;
96 virtual nsresult
AddNameBlobOrNullPair(const nsAString
& aName
,
97 Blob
* aBlob
) override
;
99 virtual nsresult
AddNameDirectoryPair(const nsAString
& aName
,
100 Directory
* aDirectory
) override
;
102 virtual nsresult
GetEncodedSubmission(nsIURI
* aURI
,
103 nsIInputStream
** aPostDataStream
,
104 nsCOMPtr
<nsIURI
>& aOutURI
) override
;
108 * URL encode a Unicode string by encoding it to bytes, converting linebreaks
109 * properly, and then escaping many bytes as %xx.
111 * @param aStr the string to encode
112 * @param aEncoded the encoded string [OUT]
113 * @throws NS_ERROR_OUT_OF_MEMORY if we run out of memory
115 nsresult
URLEncode(const nsAString
& aStr
, nsACString
& aEncoded
);
119 * The method of the submit (either NS_FORM_METHOD_GET or
120 * NS_FORM_METHOD_POST).
124 /** The query string so far (the part after the ?) */
125 nsCString mQueryString
;
127 /** The document whose URI to use when reporting errors */
128 nsCOMPtr
<Document
> mDocument
;
130 /** Whether or not we have warned about a file control not being submitted */
131 bool mWarnedFileControl
;
134 nsresult
FSURLEncoded::AddNameValuePair(const nsAString
& aName
,
135 const nsAString
& aValue
) {
138 nsresult rv
= URLEncode(aValue
, convValue
);
139 NS_ENSURE_SUCCESS(rv
, rv
);
142 nsAutoCString convName
;
143 rv
= URLEncode(aName
, convName
);
144 NS_ENSURE_SUCCESS(rv
, rv
);
146 // Append data to string
147 if (mQueryString
.IsEmpty()) {
148 mQueryString
+= convName
+ NS_LITERAL_CSTRING("=") + convValue
;
150 mQueryString
+= NS_LITERAL_CSTRING("&") + convName
+
151 NS_LITERAL_CSTRING("=") + convValue
;
157 nsresult
FSURLEncoded::AddNameBlobOrNullPair(const nsAString
& aName
,
159 if (!mWarnedFileControl
) {
160 SendJSWarning(mDocument
, "ForgotFileEnctypeWarning", nsTArray
<nsString
>());
161 mWarnedFileControl
= true;
164 nsAutoString filename
;
165 RetrieveFileName(aBlob
, filename
);
166 return AddNameValuePair(aName
, filename
);
169 nsresult
FSURLEncoded::AddNameDirectoryPair(const nsAString
& aName
,
170 Directory
* aDirectory
) {
171 // No warning about because Directory objects are never sent via form.
173 nsAutoString dirname
;
174 RetrieveDirectoryName(aDirectory
, dirname
);
175 return AddNameValuePair(aName
, dirname
);
178 void HandleMailtoSubject(nsCString
& aPath
) {
179 // Walk through the string and see if we have a subject already.
180 bool hasSubject
= false;
181 bool hasParams
= false;
182 int32_t paramSep
= aPath
.FindChar('?');
183 while (paramSep
!= kNotFound
&& paramSep
< (int32_t)aPath
.Length()) {
186 // Get the end of the name at the = op. If it is *after* the next &,
187 // assume that someone made a parameter without an = in it
188 int32_t nameEnd
= aPath
.FindChar('=', paramSep
+ 1);
189 int32_t nextParamSep
= aPath
.FindChar('&', paramSep
+ 1);
190 if (nextParamSep
== kNotFound
) {
191 nextParamSep
= aPath
.Length();
194 // If the = op is after the &, this parameter is a name without value.
195 // If there is no = op, same thing.
196 if (nameEnd
== kNotFound
|| nextParamSep
< nameEnd
) {
197 nameEnd
= nextParamSep
;
200 if (nameEnd
!= kNotFound
) {
201 if (Substring(aPath
, paramSep
+ 1, nameEnd
- (paramSep
+ 1))
202 .LowerCaseEqualsLiteral("subject")) {
208 paramSep
= nextParamSep
;
211 // If there is no subject, append a preformed subject to the mailto line
219 // Get the default subject
220 nsAutoString brandName
;
221 nsresult rv
= nsContentUtils::GetLocalizedString(
222 nsContentUtils::eBRAND_PROPERTIES
, "brandShortName", brandName
);
223 if (NS_FAILED(rv
)) return;
224 nsAutoString subjectStr
;
225 rv
= nsContentUtils::FormatLocalizedString(
226 subjectStr
, nsContentUtils::eFORMS_PROPERTIES
, "DefaultFormSubject",
228 if (NS_FAILED(rv
)) return;
229 aPath
.AppendLiteral("subject=");
230 nsCString subjectStrEscaped
;
231 rv
= NS_EscapeURL(NS_ConvertUTF16toUTF8(subjectStr
), esc_Query
,
232 subjectStrEscaped
, mozilla::fallible
);
233 if (NS_FAILED(rv
)) return;
235 aPath
.Append(subjectStrEscaped
);
239 nsresult
FSURLEncoded::GetEncodedSubmission(nsIURI
* aURI
,
240 nsIInputStream
** aPostDataStream
,
241 nsCOMPtr
<nsIURI
>& aOutURI
) {
245 *aPostDataStream
= nullptr;
247 if (mMethod
== NS_FORM_METHOD_POST
) {
248 bool isMailto
= false;
249 aURI
->SchemeIs("mailto", &isMailto
);
252 rv
= aURI
->GetPathQueryRef(path
);
253 NS_ENSURE_SUCCESS(rv
, rv
);
255 HandleMailtoSubject(path
);
257 // Append the body to and force-plain-text args to the mailto line
258 nsAutoCString escapedBody
;
259 if (NS_WARN_IF(!NS_Escape(mQueryString
, escapedBody
, url_XAlphas
))) {
260 return NS_ERROR_OUT_OF_MEMORY
;
263 path
+= NS_LITERAL_CSTRING("&force-plain-text=Y&body=") + escapedBody
;
265 return NS_MutateURI(aURI
).SetPathQueryRef(path
).Finalize(aOutURI
);
267 nsCOMPtr
<nsIInputStream
> dataStream
;
268 rv
= NS_NewCStringInputStream(getter_AddRefs(dataStream
),
269 std::move(mQueryString
));
270 NS_ENSURE_SUCCESS(rv
, rv
);
271 mQueryString
.Truncate();
273 nsCOMPtr
<nsIMIMEInputStream
> mimeStream(
274 do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv
));
275 NS_ENSURE_SUCCESS(rv
, rv
);
277 mimeStream
->AddHeader("Content-Type",
278 "application/x-www-form-urlencoded");
279 mimeStream
->SetData(dataStream
);
281 mimeStream
.forget(aPostDataStream
);
285 // Get the full query string
286 bool schemeIsJavaScript
;
287 rv
= aURI
->SchemeIs("javascript", &schemeIsJavaScript
);
288 NS_ENSURE_SUCCESS(rv
, rv
);
289 if (schemeIsJavaScript
) {
293 nsCOMPtr
<nsIURL
> url
= do_QueryInterface(aURI
);
295 rv
= NS_MutateURI(aURI
).SetQuery(mQueryString
).Finalize(aOutURI
);
298 rv
= aURI
->GetPathQueryRef(path
);
299 NS_ENSURE_SUCCESS(rv
, rv
);
300 // Bug 42616: Trim off named anchor and save it to add later
301 int32_t namedAnchorPos
= path
.FindChar('#');
302 nsAutoCString namedAnchor
;
303 if (kNotFound
!= namedAnchorPos
) {
304 path
.Right(namedAnchor
, (path
.Length() - namedAnchorPos
));
305 path
.Truncate(namedAnchorPos
);
308 // Chop off old query string (bug 25330, 57333)
309 // Only do this for GET not POST (bug 41585)
310 int32_t queryStart
= path
.FindChar('?');
311 if (kNotFound
!= queryStart
) {
312 path
.Truncate(queryStart
);
316 // Bug 42616: Add named anchor to end after query string
317 path
.Append(mQueryString
+ namedAnchor
);
319 rv
= NS_MutateURI(aURI
).SetPathQueryRef(path
).Finalize(aOutURI
);
326 // i18n helper routines
327 nsresult
FSURLEncoded::URLEncode(const nsAString
& aStr
, nsACString
& aEncoded
) {
328 // convert to CRLF breaks
329 int32_t convertedBufLength
= 0;
330 char16_t
* convertedBuf
= nsLinebreakConverter::ConvertUnicharLineBreaks(
331 aStr
.BeginReading(), nsLinebreakConverter::eLinebreakAny
,
332 nsLinebreakConverter::eLinebreakNet
, aStr
.Length(), &convertedBufLength
);
333 NS_ENSURE_TRUE(convertedBuf
, NS_ERROR_OUT_OF_MEMORY
);
335 nsAutoString convertedString
;
336 convertedString
.Adopt(convertedBuf
, convertedBufLength
);
338 nsAutoCString encodedBuf
;
339 nsresult rv
= EncodeVal(convertedString
, encodedBuf
, false);
340 NS_ENSURE_SUCCESS(rv
, rv
);
342 if (NS_WARN_IF(!NS_Escape(encodedBuf
, aEncoded
, url_XPAlphas
))) {
343 return NS_ERROR_OUT_OF_MEMORY
;
349 } // anonymous namespace
351 // --------------------------------------------------------------------------
353 FSMultipartFormData::FSMultipartFormData(nsIURI
* aActionURL
,
354 const nsAString
& aTarget
,
355 NotNull
<const Encoding
*> aEncoding
,
356 Element
* aOriginatingElement
)
357 : EncodingFormSubmission(aActionURL
, aTarget
, aEncoding
,
358 aOriginatingElement
) {
359 mPostData
= do_CreateInstance("@mozilla.org/io/multiplex-input-stream;1");
361 nsCOMPtr
<nsIInputStream
> inputStream
= do_QueryInterface(mPostData
);
362 MOZ_ASSERT(SameCOMIdentity(mPostData
, inputStream
));
363 mPostDataStream
= inputStream
;
367 mBoundary
.AssignLiteral("---------------------------");
368 mBoundary
.AppendInt(rand());
369 mBoundary
.AppendInt(rand());
370 mBoundary
.AppendInt(rand());
373 FSMultipartFormData::~FSMultipartFormData() {
374 NS_ASSERTION(mPostDataChunk
.IsEmpty(), "Left unsubmitted data");
377 nsIInputStream
* FSMultipartFormData::GetSubmissionBody(
378 uint64_t* aContentLength
) {
381 NS_LITERAL_CSTRING("--") + mBoundary
+ NS_LITERAL_CSTRING("--" CRLF
);
383 // Add final data input stream
386 *aContentLength
= mTotalLength
;
387 return mPostDataStream
;
390 nsresult
FSMultipartFormData::AddNameValuePair(const nsAString
& aName
,
391 const nsAString
& aValue
) {
393 nsAutoCString encodedVal
;
394 nsresult rv
= EncodeVal(aValue
, encodedVal
, false);
395 NS_ENSURE_SUCCESS(rv
, rv
);
397 valueStr
.Adopt(nsLinebreakConverter::ConvertLineBreaks(
398 encodedVal
.get(), nsLinebreakConverter::eLinebreakAny
,
399 nsLinebreakConverter::eLinebreakNet
));
401 nsAutoCString nameStr
;
402 rv
= EncodeVal(aName
, nameStr
, true);
403 NS_ENSURE_SUCCESS(rv
, rv
);
405 // Make MIME block for name/value pair
407 // XXX: name parameter should be encoded per RFC 2231
408 // RFC 2388 specifies that RFC 2047 be used, but I think it's not
409 // consistent with MIME standard.
411 NS_LITERAL_CSTRING("--") + mBoundary
+ NS_LITERAL_CSTRING(CRLF
) +
412 NS_LITERAL_CSTRING("Content-Disposition: form-data; name=\"") + nameStr
+
413 NS_LITERAL_CSTRING("\"" CRLF CRLF
) + valueStr
+ NS_LITERAL_CSTRING(CRLF
);
418 nsresult
FSMultipartFormData::AddNameBlobOrNullPair(const nsAString
& aName
,
420 // Encode the control name
421 nsAutoCString nameStr
;
422 nsresult rv
= EncodeVal(aName
, nameStr
, true);
423 NS_ENSURE_SUCCESS(rv
, rv
);
428 nsAutoCString filename
;
429 nsAutoCString contentType
;
430 nsCOMPtr
<nsIInputStream
> fileStream
;
433 nsAutoString filename16
;
435 RefPtr
<File
> file
= aBlob
->ToFile();
437 nsAutoString relativePath
;
438 file
->GetRelativePath(relativePath
);
439 if (StaticPrefs::dom_webkitBlink_dirPicker_enabled() &&
440 !relativePath
.IsEmpty()) {
441 filename16
= relativePath
;
444 if (filename16
.IsEmpty()) {
445 RetrieveFileName(aBlob
, filename16
);
449 rv
= EncodeVal(filename16
, filename
, true);
450 NS_ENSURE_SUCCESS(rv
, rv
);
453 nsAutoString contentType16
;
454 aBlob
->GetType(contentType16
);
455 if (contentType16
.IsEmpty()) {
456 contentType16
.AssignLiteral("application/octet-stream");
459 contentType
.Adopt(nsLinebreakConverter::ConvertLineBreaks(
460 NS_ConvertUTF16toUTF8(contentType16
).get(),
461 nsLinebreakConverter::eLinebreakAny
,
462 nsLinebreakConverter::eLinebreakSpace
));
465 aBlob
->CreateInputStream(getter_AddRefs(fileStream
), error
);
466 if (NS_WARN_IF(error
.Failed())) {
467 return error
.StealNSResult();
471 size
= aBlob
->GetSize(error
);
472 if (error
.Failed()) {
473 error
.SuppressException();
474 fileStream
= nullptr;
478 // Create buffered stream (for efficiency)
479 nsCOMPtr
<nsIInputStream
> bufferedStream
;
480 rv
= NS_NewBufferedInputStream(getter_AddRefs(bufferedStream
),
481 fileStream
.forget(), 8192);
482 NS_ENSURE_SUCCESS(rv
, rv
);
484 fileStream
= bufferedStream
;
487 contentType
.AssignLiteral("application/octet-stream");
490 AddDataChunk(nameStr
, filename
, contentType
, fileStream
, size
);
494 nsresult
FSMultipartFormData::AddNameDirectoryPair(const nsAString
& aName
,
495 Directory
* aDirectory
) {
496 if (!StaticPrefs::dom_webkitBlink_dirPicker_enabled()) {
500 // Encode the control name
501 nsAutoCString nameStr
;
502 nsresult rv
= EncodeVal(aName
, nameStr
, true);
503 NS_ENSURE_SUCCESS(rv
, rv
);
505 nsAutoCString dirname
;
506 nsAutoString dirname16
;
510 aDirectory
->GetPath(path
, error
);
511 if (NS_WARN_IF(error
.Failed())) {
512 error
.SuppressException();
517 if (dirname16
.IsEmpty()) {
518 RetrieveDirectoryName(aDirectory
, dirname16
);
521 rv
= EncodeVal(dirname16
, dirname
, true);
522 NS_ENSURE_SUCCESS(rv
, rv
);
524 AddDataChunk(nameStr
, dirname
, NS_LITERAL_CSTRING("application/octet-stream"),
529 void FSMultipartFormData::AddDataChunk(const nsACString
& aName
,
530 const nsACString
& aFilename
,
531 const nsACString
& aContentType
,
532 nsIInputStream
* aInputStream
,
533 uint64_t aInputStreamSize
) {
535 // Make MIME block for name/value pair
537 // more appropriate than always using binary?
539 NS_LITERAL_CSTRING("--") + mBoundary
+ NS_LITERAL_CSTRING(CRLF
);
540 // XXX: name/filename parameter should be encoded per RFC 2231
541 // RFC 2388 specifies that RFC 2047 be used, but I think it's not
542 // consistent with the MIME standard.
544 NS_LITERAL_CSTRING("Content-Disposition: form-data; name=\"") + aName
+
545 NS_LITERAL_CSTRING("\"; filename=\"") + aFilename
+
546 NS_LITERAL_CSTRING("\"" CRLF
) + NS_LITERAL_CSTRING("Content-Type: ") +
547 aContentType
+ NS_LITERAL_CSTRING(CRLF CRLF
);
549 // We should not try to append an invalid stream. That will happen for example
550 // if we try to update a file that actually do not exist.
552 // We need to dump the data up to this point into the POST data stream
553 // here, since we're about to add the file input stream
556 mPostData
->AppendStream(aInputStream
);
557 mTotalLength
+= aInputStreamSize
;
561 mPostDataChunk
.AppendLiteral(CRLF
);
564 nsresult
FSMultipartFormData::GetEncodedSubmission(
565 nsIURI
* aURI
, nsIInputStream
** aPostDataStream
, nsCOMPtr
<nsIURI
>& aOutURI
) {
570 nsCOMPtr
<nsIMIMEInputStream
> mimeStream
=
571 do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv
);
572 NS_ENSURE_SUCCESS(rv
, rv
);
574 nsAutoCString contentType
;
575 GetContentType(contentType
);
576 mimeStream
->AddHeader("Content-Type", contentType
.get());
579 mimeStream
->SetData(GetSubmissionBody(&bodySize
));
581 mimeStream
.forget(aPostDataStream
);
586 nsresult
FSMultipartFormData::AddPostDataStream() {
589 nsCOMPtr
<nsIInputStream
> postDataChunkStream
;
590 rv
= NS_NewCStringInputStream(getter_AddRefs(postDataChunkStream
),
592 NS_ASSERTION(postDataChunkStream
, "Could not open a stream for POST!");
593 if (postDataChunkStream
) {
594 mPostData
->AppendStream(postDataChunkStream
);
595 mTotalLength
+= mPostDataChunk
.Length();
598 mPostDataChunk
.Truncate();
603 // --------------------------------------------------------------------------
607 class FSTextPlain
: public EncodingFormSubmission
{
609 FSTextPlain(nsIURI
* aActionURL
, const nsAString
& aTarget
,
610 NotNull
<const Encoding
*> aEncoding
, Element
* aOriginatingElement
)
611 : EncodingFormSubmission(aActionURL
, aTarget
, aEncoding
,
612 aOriginatingElement
) {}
614 virtual nsresult
AddNameValuePair(const nsAString
& aName
,
615 const nsAString
& aValue
) override
;
617 virtual nsresult
AddNameBlobOrNullPair(const nsAString
& aName
,
618 Blob
* aBlob
) override
;
620 virtual nsresult
AddNameDirectoryPair(const nsAString
& aName
,
621 Directory
* aDirectory
) override
;
623 virtual nsresult
GetEncodedSubmission(nsIURI
* aURI
,
624 nsIInputStream
** aPostDataStream
,
625 nsCOMPtr
<nsIURI
>& aOutURI
) override
;
631 nsresult
FSTextPlain::AddNameValuePair(const nsAString
& aName
,
632 const nsAString
& aValue
) {
633 // XXX This won't work well with a name like "a=b" or "a\nb" but I suppose
634 // text/plain doesn't care about that. Parsers aren't built for escaped
635 // values so we'll have to live with it.
636 mBody
.Append(aName
+ NS_LITERAL_STRING("=") + aValue
+
637 NS_LITERAL_STRING(CRLF
));
642 nsresult
FSTextPlain::AddNameBlobOrNullPair(const nsAString
& aName
,
644 nsAutoString filename
;
645 RetrieveFileName(aBlob
, filename
);
646 AddNameValuePair(aName
, filename
);
650 nsresult
FSTextPlain::AddNameDirectoryPair(const nsAString
& aName
,
651 Directory
* aDirectory
) {
652 nsAutoString dirname
;
653 RetrieveDirectoryName(aDirectory
, dirname
);
654 AddNameValuePair(aName
, dirname
);
658 nsresult
FSTextPlain::GetEncodedSubmission(nsIURI
* aURI
,
659 nsIInputStream
** aPostDataStream
,
660 nsCOMPtr
<nsIURI
>& aOutURI
) {
664 *aPostDataStream
= nullptr;
666 // XXX HACK We are using the standard URL mechanism to give the body to the
667 // mailer instead of passing the post data stream to it, since that sounds
669 bool isMailto
= false;
670 aURI
->SchemeIs("mailto", &isMailto
);
673 rv
= aURI
->GetPathQueryRef(path
);
674 NS_ENSURE_SUCCESS(rv
, rv
);
676 HandleMailtoSubject(path
);
678 // Append the body to and force-plain-text args to the mailto line
679 nsAutoCString escapedBody
;
680 if (NS_WARN_IF(!NS_Escape(NS_ConvertUTF16toUTF8(mBody
), escapedBody
,
682 return NS_ERROR_OUT_OF_MEMORY
;
685 path
+= NS_LITERAL_CSTRING("&force-plain-text=Y&body=") + escapedBody
;
687 rv
= NS_MutateURI(aURI
).SetPathQueryRef(path
).Finalize(aOutURI
);
689 // Create data stream.
690 // We do want to send the data through the charset encoder and we want to
691 // normalize linebreaks to use the "standard net" format (\r\n), but we
692 // don't want to perform any other encoding. This means that names and
693 // values which contains '=' or newlines are potentially ambigiously
694 // encoded, but that how text/plain is specced.
696 EncodeVal(mBody
, cbody
, false);
697 cbody
.Adopt(nsLinebreakConverter::ConvertLineBreaks(
698 cbody
.get(), nsLinebreakConverter::eLinebreakAny
,
699 nsLinebreakConverter::eLinebreakNet
));
700 nsCOMPtr
<nsIInputStream
> bodyStream
;
701 rv
= NS_NewCStringInputStream(getter_AddRefs(bodyStream
), std::move(cbody
));
703 return NS_ERROR_OUT_OF_MEMORY
;
706 // Create mime stream with headers and such
707 nsCOMPtr
<nsIMIMEInputStream
> mimeStream
=
708 do_CreateInstance("@mozilla.org/network/mime-input-stream;1", &rv
);
709 NS_ENSURE_SUCCESS(rv
, rv
);
711 mimeStream
->AddHeader("Content-Type", "text/plain");
712 mimeStream
->SetData(bodyStream
);
713 mimeStream
.forget(aPostDataStream
);
719 } // anonymous namespace
721 // --------------------------------------------------------------------------
723 EncodingFormSubmission::EncodingFormSubmission(
724 nsIURI
* aActionURL
, const nsAString
& aTarget
,
725 NotNull
<const Encoding
*> aEncoding
, Element
* aOriginatingElement
)
726 : HTMLFormSubmission(aActionURL
, aTarget
, aEncoding
, aOriginatingElement
) {
727 if (!aEncoding
->CanEncodeEverything()) {
729 aEncoding
->Name(name
);
730 AutoTArray
<nsString
, 1> args
;
731 CopyUTF8toUTF16(name
, *args
.AppendElement());
733 aOriginatingElement
? aOriginatingElement
->GetOwnerDocument() : nullptr,
734 "CannotEncodeAllUnicode", args
);
738 EncodingFormSubmission::~EncodingFormSubmission() {}
740 // i18n helper routines
741 nsresult
EncodingFormSubmission::EncodeVal(const nsAString
& aStr
,
743 bool aHeaderEncode
) {
745 const Encoding
* ignored
;
746 Tie(rv
, ignored
) = mEncoding
->Encode(aStr
, aOut
);
752 aOut
.Adopt(nsLinebreakConverter::ConvertLineBreaks(
753 aOut
.get(), nsLinebreakConverter::eLinebreakAny
,
754 nsLinebreakConverter::eLinebreakSpace
));
755 aOut
.ReplaceSubstring(NS_LITERAL_CSTRING("\""), NS_LITERAL_CSTRING("\\\""));
761 // --------------------------------------------------------------------------
765 NotNull
<const Encoding
*> GetSubmitEncoding(nsGenericHTMLElement
* aForm
) {
766 nsAutoString acceptCharsetValue
;
767 aForm
->GetAttr(kNameSpaceID_None
, nsGkAtoms::acceptcharset
,
770 int32_t charsetLen
= acceptCharsetValue
.Length();
771 if (charsetLen
> 0) {
774 // get charset from charsets one by one
776 spPos
= acceptCharsetValue
.FindChar(char16_t(' '), offset
);
777 int32_t cnt
= ((-1 == spPos
) ? (charsetLen
- offset
) : (spPos
- offset
));
779 nsAutoString uCharset
;
780 acceptCharsetValue
.Mid(uCharset
, offset
, cnt
);
782 auto encoding
= Encoding::ForLabelNoReplacement(uCharset
);
784 return WrapNotNull(encoding
);
788 } while (spPos
!= -1);
790 // if there are no accept-charset or all the charset are not supported
791 // Get the charset from document
792 Document
* doc
= aForm
->GetComposedDoc();
794 return doc
->GetDocumentCharacterSet();
796 return UTF_8_ENCODING
;
799 void GetEnumAttr(nsGenericHTMLElement
* aContent
, nsAtom
* atom
,
801 const nsAttrValue
* value
= aContent
->GetParsedAttr(atom
);
802 if (value
&& value
->Type() == nsAttrValue::eEnum
) {
803 *aValue
= value
->GetEnumValue();
807 } // anonymous namespace
810 nsresult
HTMLFormSubmission::GetFromForm(
811 HTMLFormElement
* aForm
, nsGenericHTMLElement
* aOriginatingElement
,
812 HTMLFormSubmission
** aFormSubmission
) {
813 // Get all the information necessary to encode the form data
814 NS_ASSERTION(aForm
->GetComposedDoc(),
815 "Should have doc if we're building submission!");
820 nsCOMPtr
<nsIURI
> actionURL
;
821 rv
= aForm
->GetActionURL(getter_AddRefs(actionURL
), aOriginatingElement
);
822 NS_ENSURE_SUCCESS(rv
, rv
);
824 // Check if CSP allows this form-action
825 nsCOMPtr
<nsIContentSecurityPolicy
> csp
= aForm
->GetCsp();
827 bool permitsFormAction
= true;
829 // form-action is only enforced if explicitly defined in the
830 // policy - do *not* consult default-src, see:
831 // http://www.w3.org/TR/CSP2/#directive-default-src
832 rv
= csp
->Permits(aForm
, nullptr /* nsICSPEventListener */, actionURL
,
833 nsIContentSecurityPolicy::FORM_ACTION_DIRECTIVE
, true,
835 NS_ENSURE_SUCCESS(rv
, rv
);
836 if (!permitsFormAction
) {
837 return NS_ERROR_CSP_FORM_ACTION_VIOLATION
;
842 // The target is the originating element formtarget attribute if the element
843 // is a submit control and has such an attribute.
844 // Otherwise, the target is the form owner's target attribute,
845 // if it has such an attribute.
846 // Finally, if one of the child nodes of the head element is a base element
847 // with a target attribute, then the value of the target attribute of the
848 // first such base element; or, if there is no such element, the empty string.
850 if (!(aOriginatingElement
&&
851 aOriginatingElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::formtarget
,
853 !aForm
->GetAttr(kNameSpaceID_None
, nsGkAtoms::target
, target
)) {
854 aForm
->GetBaseTarget(target
);
857 // Get encoding type (default: urlencoded)
858 int32_t enctype
= NS_FORM_ENCTYPE_URLENCODED
;
859 if (aOriginatingElement
&&
860 aOriginatingElement
->HasAttr(kNameSpaceID_None
, nsGkAtoms::formenctype
)) {
861 GetEnumAttr(aOriginatingElement
, nsGkAtoms::formenctype
, &enctype
);
863 GetEnumAttr(aForm
, nsGkAtoms::enctype
, &enctype
);
866 // Get method (default: GET)
867 int32_t method
= NS_FORM_METHOD_GET
;
868 if (aOriginatingElement
&&
869 aOriginatingElement
->HasAttr(kNameSpaceID_None
, nsGkAtoms::formmethod
)) {
870 GetEnumAttr(aOriginatingElement
, nsGkAtoms::formmethod
, &method
);
872 GetEnumAttr(aForm
, nsGkAtoms::method
, &method
);
876 auto encoding
= GetSubmitEncoding(aForm
)->OutputEncoding();
879 if (method
== NS_FORM_METHOD_POST
&& enctype
== NS_FORM_ENCTYPE_MULTIPART
) {
880 *aFormSubmission
= new FSMultipartFormData(actionURL
, target
, encoding
,
881 aOriginatingElement
);
882 } else if (method
== NS_FORM_METHOD_POST
&&
883 enctype
== NS_FORM_ENCTYPE_TEXTPLAIN
) {
885 new FSTextPlain(actionURL
, target
, encoding
, aOriginatingElement
);
887 Document
* doc
= aForm
->OwnerDoc();
888 if (enctype
== NS_FORM_ENCTYPE_MULTIPART
||
889 enctype
== NS_FORM_ENCTYPE_TEXTPLAIN
) {
890 AutoTArray
<nsString
, 1> args
;
891 nsString
& enctypeStr
= *args
.AppendElement();
892 if (aOriginatingElement
&&
893 aOriginatingElement
->HasAttr(kNameSpaceID_None
,
894 nsGkAtoms::formenctype
)) {
895 aOriginatingElement
->GetAttr(kNameSpaceID_None
, nsGkAtoms::formenctype
,
898 aForm
->GetAttr(kNameSpaceID_None
, nsGkAtoms::enctype
, enctypeStr
);
901 SendJSWarning(doc
, "ForgotPostWarning", args
);
903 *aFormSubmission
= new FSURLEncoded(actionURL
, target
, encoding
, method
,
904 doc
, aOriginatingElement
);
911 } // namespace mozilla