HTMLFormSubmission.h (9760B)
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/. */ 6 7 #ifndef mozilla_dom_HTMLFormSubmission_h 8 #define mozilla_dom_HTMLFormSubmission_h 9 10 #include "mozilla/Encoding.h" 11 #include "mozilla/dom/HTMLDialogElement.h" 12 #include "mozilla/dom/UserActivation.h" 13 #include "nsCOMPtr.h" 14 #include "nsString.h" 15 16 class nsIURI; 17 class nsIInputStream; 18 class nsGenericHTMLElement; 19 class nsIMultiplexInputStream; 20 21 namespace mozilla::dom { 22 23 class Blob; 24 class DialogFormSubmission; 25 class Directory; 26 class Element; 27 class HTMLFormElement; 28 29 /** 30 * Class for form submissions; encompasses the function to call to submit as 31 * well as the form submission name/value pairs 32 */ 33 class HTMLFormSubmission { 34 public: 35 /** 36 * Get a submission object based on attributes in the form (ENCTYPE and 37 * METHOD) 38 * 39 * @param aForm the form to get a submission object based on 40 * @param aSubmitter the submitter element (can be null) 41 * @param aEncoding the submiter element's encoding 42 * @param aFormSubmission the form submission object (out param) 43 */ 44 static nsresult GetFromForm(HTMLFormElement* aForm, 45 nsGenericHTMLElement* aSubmitter, 46 NotNull<const Encoding*>& aEncoding, 47 FormData* aFormData, 48 HTMLFormSubmission** aFormSubmission); 49 50 MOZ_COUNTED_DTOR_VIRTUAL(HTMLFormSubmission) 51 52 /** 53 * Submit a name/value pair 54 * 55 * @param aName the name of the parameter 56 * @param aValue the value of the parameter 57 */ 58 virtual nsresult AddNameValuePair(const nsAString& aName, 59 const nsAString& aValue) = 0; 60 61 /** 62 * Submit a name/blob pair 63 * 64 * @param aName the name of the parameter 65 * @param aBlob the blob to submit. The file's name will be used if the Blob 66 * is actually a File, otherwise 'blob' string is used instead. Must not be 67 * null. 68 */ 69 virtual nsresult AddNameBlobPair(const nsAString& aName, Blob* aBlob) = 0; 70 71 /** 72 * Submit a name/directory pair 73 * 74 * @param aName the name of the parameter 75 * @param aBlob the directory to submit. 76 */ 77 virtual nsresult AddNameDirectoryPair(const nsAString& aName, 78 Directory* aDirectory) = 0; 79 80 /** 81 * Given a URI and the current submission, create the final URI and data 82 * stream that will be submitted. Subclasses *must* implement this. 83 * 84 * @param aURI the URI being submitted to [IN] 85 * @param aPostDataStream a data stream for POST data [OUT] 86 * @param aOutURI the resulting URI. May be the same as aURI [OUT] 87 */ 88 virtual nsresult GetEncodedSubmission(nsIURI* aURI, 89 nsIInputStream** aPostDataStream, 90 nsCOMPtr<nsIURI>& aOutURI) = 0; 91 92 /** 93 * Get the charset that will be used for submission. 94 */ 95 void GetCharset(nsACString& aCharset) { mEncoding->Name(aCharset); } 96 97 /** 98 * Get the action URI that will be used for submission. 99 */ 100 nsIURI* GetActionURL() const { return mActionURL; } 101 102 /** 103 * Get the target that will be used for submission. 104 */ 105 void GetTarget(nsAString& aTarget) { aTarget = mTarget; } 106 107 /** 108 * Return true if this form submission was user-initiated. 109 */ 110 bool IsInitiatedFromUserInput() const { return mInitiatedFromUserInput; } 111 112 virtual DialogFormSubmission* GetAsDialogSubmission() { return nullptr; } 113 114 FormData* GetFormData() const { return mFormData; } 115 116 protected: 117 /** 118 * Can only be constructed by subclasses. 119 * 120 * @param aEncoding the character encoding of the form 121 */ 122 HTMLFormSubmission(nsIURI* aActionURL, const nsAString& aTarget, 123 mozilla::NotNull<const mozilla::Encoding*> aEncoding); 124 125 // The action url. 126 nsCOMPtr<nsIURI> mActionURL; 127 128 // The target. 129 nsString mTarget; 130 131 // The character encoding of this form submission 132 mozilla::NotNull<const mozilla::Encoding*> mEncoding; 133 134 RefPtr<FormData> mFormData; 135 136 // Keep track of whether this form submission was user-initiated or not 137 bool mInitiatedFromUserInput; 138 }; 139 140 class EncodingFormSubmission : public HTMLFormSubmission { 141 public: 142 EncodingFormSubmission(nsIURI* aActionURL, const nsAString& aTarget, 143 mozilla::NotNull<const mozilla::Encoding*> aEncoding, 144 Element* aSubmitter); 145 146 virtual ~EncodingFormSubmission(); 147 148 // Indicates the type of newline normalization and escaping to perform in 149 // `EncodeVal`, in addition to encoding the string into bytes. 150 enum EncodeType { 151 // Normalizes newlines to CRLF and then escapes for use in 152 // `Content-Disposition`. (Useful for `multipart/form-data` entry names.) 153 eNameEncode, 154 // Escapes for use in `Content-Disposition`. (Useful for 155 // `multipart/form-data` filenames.) 156 eFilenameEncode, 157 // Normalizes newlines to CRLF. 158 eValueEncode, 159 }; 160 161 /** 162 * Encode a Unicode string to bytes, additionally performing escapes or 163 * normalizations. 164 * @param aStr the string to encode 165 * @param aOut the encoded string [OUT] 166 * @param aEncodeType The type of escapes or normalizations to perform on the 167 * encoded string. 168 * @throws an error if UnicodeToNewBytes fails 169 */ 170 nsresult EncodeVal(const nsAString& aStr, nsCString& aOut, 171 EncodeType aEncodeType); 172 }; 173 174 class DialogFormSubmission final : public HTMLFormSubmission { 175 public: 176 DialogFormSubmission(nsAString& aResult, NotNull<const Encoding*> aEncoding, 177 HTMLDialogElement* aDialogElement) 178 : HTMLFormSubmission(nullptr, u""_ns, aEncoding), 179 mDialogElement(aDialogElement), 180 mReturnValue(aResult) {} 181 nsresult AddNameValuePair(const nsAString& aName, 182 const nsAString& aValue) override { 183 MOZ_CRASH("This method should not be called"); 184 return NS_OK; 185 } 186 187 nsresult AddNameBlobPair(const nsAString& aName, Blob* aBlob) override { 188 MOZ_CRASH("This method should not be called"); 189 return NS_OK; 190 } 191 192 nsresult AddNameDirectoryPair(const nsAString& aName, 193 Directory* aDirectory) override { 194 MOZ_CRASH("This method should not be called"); 195 return NS_OK; 196 } 197 198 nsresult GetEncodedSubmission(nsIURI* aURI, nsIInputStream** aPostDataStream, 199 nsCOMPtr<nsIURI>& aOutURI) override { 200 MOZ_CRASH("This method should not be called"); 201 return NS_OK; 202 } 203 204 DialogFormSubmission* GetAsDialogSubmission() override { return this; } 205 206 HTMLDialogElement* DialogElement() { return mDialogElement; } 207 208 nsString& ReturnValue() { return mReturnValue; } 209 210 private: 211 const RefPtr<HTMLDialogElement> mDialogElement; 212 nsString mReturnValue; 213 }; 214 215 /** 216 * Handle multipart/form-data encoding, which does files as well as normal 217 * inputs. This always does POST. 218 */ 219 class FSMultipartFormData : public EncodingFormSubmission { 220 public: 221 /** 222 * @param aEncoding the character encoding of the form 223 */ 224 FSMultipartFormData(nsIURI* aActionURL, const nsAString& aTarget, 225 mozilla::NotNull<const mozilla::Encoding*> aEncoding, 226 Element* aSubmitter); 227 ~FSMultipartFormData(); 228 229 virtual nsresult AddNameValuePair(const nsAString& aName, 230 const nsAString& aValue) override; 231 232 virtual nsresult AddNameBlobPair(const nsAString& aName, 233 Blob* aBlob) override; 234 235 virtual nsresult AddNameDirectoryPair(const nsAString& aName, 236 Directory* aDirectory) override; 237 238 virtual nsresult GetEncodedSubmission(nsIURI* aURI, 239 nsIInputStream** aPostDataStream, 240 nsCOMPtr<nsIURI>& aOutURI) override; 241 242 void GetContentType(nsACString& aContentType) { 243 aContentType = "multipart/form-data; boundary="_ns + mBoundary; 244 } 245 246 nsIInputStream* GetSubmissionBody(uint64_t* aContentLength); 247 248 protected: 249 /** 250 * Roll up the data we have so far and add it to the multiplexed data stream. 251 */ 252 nsresult AddPostDataStream(); 253 254 private: 255 void AddDataChunk(const nsACString& aName, const nsACString& aFilename, 256 const nsACString& aContentType, 257 nsIInputStream* aInputStream, uint64_t aInputStreamSize); 258 /** 259 * The post data stream as it is so far. This is a collection of smaller 260 * chunks--string streams and file streams interleaved to make one big POST 261 * stream. 262 */ 263 nsCOMPtr<nsIMultiplexInputStream> mPostData; 264 265 /** 266 * The same stream, but as an nsIInputStream. 267 * Raw pointers because it is just QI of mInputStream. 268 */ 269 nsIInputStream* mPostDataStream; 270 271 /** 272 * The current string chunk. When a file is hit, the string chunk gets 273 * wrapped up into an input stream and put into mPostDataStream so that the 274 * file input stream can then be appended and everything is in the right 275 * order. Then the string chunk gets appended to again as we process more 276 * name/value pairs. 277 */ 278 nsCString mPostDataChunk; 279 280 /** 281 * The boundary string to use after each "part" (the boundary that marks the 282 * end of a value). This is computed randomly and is different for each 283 * submission. 284 */ 285 nsCString mBoundary; 286 287 /** 288 * The total length in bytes of the streams that make up mPostDataStream 289 */ 290 uint64_t mTotalLength; 291 }; 292 293 } // namespace mozilla::dom 294 295 #endif /* mozilla_dom_HTMLFormSubmission_h */