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