1 /* 2 * Copyright (C) 2010 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 #include "FormSubmission.h" 33 34 #include "DOMFormData.h" 35 #include "Document.h" 36 #include "Event.h" 37 #include "FormData.h" 38 #include "FormDataBuilder.h" 39 #include "FormState.h" 40 #include "Frame.h" 41 #include "FrameLoadRequest.h" 42 #include "FrameLoader.h" 43 #include "HTMLFormControlElement.h" 44 #include "HTMLFormElement.h" 45 #include "HTMLInputElement.h" 46 #include "HTMLNames.h" 47 #include "HTMLParserIdioms.h" 48 #include "TextEncoding.h" 49 #include <wtf/CurrentTime.h> 50 #include <wtf/RandomNumber.h> 51 52 namespace WebCore { 53 54 using namespace HTMLNames; 55 56 static int64_t generateFormDataIdentifier() 57 { 58 // Initialize to the current time to reduce the likelihood of generating 59 // identifiers that overlap with those from past/future browser sessions. 60 static int64_t nextIdentifier = static_cast<int64_t>(currentTime() * 1000000.0); 61 return ++nextIdentifier; 62 } 63 64 static void appendMailtoPostFormDataToURL(KURL& url, const FormData& data, const String& encodingType) 65 { 66 String body = data.flattenToString(); 67 68 if (equalIgnoringCase(encodingType, "text/plain")) { 69 // Convention seems to be to decode, and s/&/\r\n/. Also, spaces are encoded as %20. 70 body = decodeURLEscapeSequences(body.replace('&', "\r\n").replace('+', ' ') + "\r\n"); 71 } 72 73 Vector<char> bodyData; 74 bodyData.append("body=", 5); 75 FormDataBuilder::encodeStringAsFormData(bodyData, body.utf8()); 76 body = String(bodyData.data(), bodyData.size()).replace('+', "%20"); 77 78 String query = url.query(); 79 if (!query.isEmpty()) 80 query.append('&'); 81 query.append(body); 82 url.setQuery(query); 83 } 84 85 void FormSubmission::Attributes::parseAction(const String& action) 86 { 87 // FIXME: Can we parse into a KURL? 88 m_action = stripLeadingAndTrailingHTMLSpaces(action); 89 } 90 91 void FormSubmission::Attributes::parseEncodingType(const String& type) 92 { 93 if (type.contains("multipart", false) || type.contains("form-data", false)) { 94 m_encodingType = "multipart/form-data"; 95 m_isMultiPartForm = true; 96 } else if (type.contains("text", false) || type.contains("plain", false)) { 97 m_encodingType = "text/plain"; 98 m_isMultiPartForm = false; 99 } else { 100 m_encodingType = "application/x-www-form-urlencoded"; 101 m_isMultiPartForm = false; 102 } 103 } 104 105 void FormSubmission::Attributes::parseMethodType(const String& type) 106 { 107 if (equalIgnoringCase(type, "post")) 108 m_method = FormSubmission::PostMethod; 109 else if (equalIgnoringCase(type, "get")) 110 m_method = FormSubmission::GetMethod; 111 } 112 113 void FormSubmission::Attributes::copyFrom(const Attributes& other) 114 { 115 m_method = other.m_method; 116 m_isMultiPartForm = other.m_isMultiPartForm; 117 118 m_action = other.m_action; 119 m_target = other.m_target; 120 m_encodingType = other.m_encodingType; 121 m_acceptCharset = other.m_acceptCharset; 122 } 123 124 inline FormSubmission::FormSubmission(Method method, const KURL& action, const String& target, const String& contentType, PassRefPtr<FormState> state, PassRefPtr<FormData> data, const String& boundary, bool lockHistory, PassRefPtr<Event> event) 125 : m_method(method) 126 , m_action(action) 127 , m_target(target) 128 , m_contentType(contentType) 129 , m_formState(state) 130 , m_formData(data) 131 , m_boundary(boundary) 132 , m_lockHistory(lockHistory) 133 , m_event(event) 134 { 135 } 136 137 PassRefPtr<FormSubmission> FormSubmission::create(HTMLFormElement* form, const Attributes& attributes, PassRefPtr<Event> event, bool lockHistory, FormSubmissionTrigger trigger) 138 { 139 ASSERT(form); 140 141 HTMLFormControlElement* submitButton = 0; 142 if (event && event->target() && event->target()->toNode()) 143 submitButton = static_cast<HTMLFormControlElement*>(event->target()->toNode()); 144 145 FormSubmission::Attributes copiedAttributes; 146 copiedAttributes.copyFrom(attributes); 147 if (submitButton) { 148 String attributeValue; 149 if (!(attributeValue = submitButton->getAttribute(formactionAttr)).isNull()) 150 copiedAttributes.parseAction(attributeValue); 151 if (!(attributeValue = submitButton->getAttribute(formenctypeAttr)).isNull()) 152 copiedAttributes.parseEncodingType(attributeValue); 153 if (!(attributeValue = submitButton->getAttribute(formmethodAttr)).isNull()) 154 copiedAttributes.parseMethodType(attributeValue); 155 if (!(attributeValue = submitButton->getAttribute(formtargetAttr)).isNull()) 156 copiedAttributes.setTarget(attributeValue); 157 } 158 159 Document* document = form->document(); 160 KURL actionURL = document->completeURL(copiedAttributes.action().isEmpty() ? document->url().string() : copiedAttributes.action()); 161 bool isMailtoForm = actionURL.protocolIs("mailto"); 162 bool isMultiPartForm = false; 163 String encodingType = copiedAttributes.encodingType(); 164 165 if (copiedAttributes.method() == PostMethod) { 166 isMultiPartForm = copiedAttributes.isMultiPartForm(); 167 if (isMultiPartForm && isMailtoForm) { 168 encodingType = "application/x-www-form-urlencoded"; 169 isMultiPartForm = false; 170 } 171 } 172 173 TextEncoding dataEncoding = isMailtoForm ? UTF8Encoding() : FormDataBuilder::encodingFromAcceptCharset(copiedAttributes.acceptCharset(), document); 174 RefPtr<DOMFormData> domFormData = DOMFormData::create(dataEncoding.encodingForFormSubmission()); 175 Vector<pair<String, String> > formValues; 176 177 for (unsigned i = 0; i < form->associatedElements().size(); ++i) { 178 FormAssociatedElement* control = form->associatedElements()[i]; 179 HTMLElement* element = toHTMLElement(control); 180 if (!element->disabled()) 181 control->appendFormData(*domFormData, isMultiPartForm); 182 if (element->hasLocalName(inputTag)) { 183 HTMLInputElement* input = static_cast<HTMLInputElement*>(control); 184 if (input->isTextField()) { 185 formValues.append(pair<String, String>(input->name(), input->value())); 186 if (input->isSearchField()) 187 input->addSearchResult(); 188 } 189 } 190 } 191 192 RefPtr<FormData> formData; 193 String boundary; 194 195 if (isMultiPartForm) { 196 formData = FormData::createMultiPart(*(static_cast<FormDataList*>(domFormData.get())), domFormData->encoding(), document); 197 boundary = formData->boundary().data(); 198 } else { 199 formData = FormData::create(*(static_cast<FormDataList*>(domFormData.get())), domFormData->encoding()); 200 if (copiedAttributes.method() == PostMethod && isMailtoForm) { 201 // Convert the form data into a string that we put into the URL. 202 appendMailtoPostFormDataToURL(actionURL, *formData, encodingType); 203 formData = FormData::create(); 204 } 205 } 206 207 formData->setIdentifier(generateFormDataIdentifier()); 208 String targetOrBaseTarget = copiedAttributes.target().isEmpty() ? document->baseTarget() : copiedAttributes.target(); 209 RefPtr<FormState> formState = FormState::create(form, formValues, document->frame(), trigger); 210 return adoptRef(new FormSubmission(copiedAttributes.method(), actionURL, targetOrBaseTarget, encodingType, formState.release(), formData.release(), boundary, lockHistory, event)); 211 } 212 213 KURL FormSubmission::requestURL() const 214 { 215 if (m_method == FormSubmission::PostMethod) 216 return m_action; 217 218 KURL requestURL(m_action); 219 requestURL.setQuery(m_formData->flattenToString()); 220 return requestURL; 221 } 222 223 void FormSubmission::populateFrameLoadRequest(FrameLoadRequest& frameRequest) 224 { 225 if (!m_target.isEmpty()) 226 frameRequest.setFrameName(m_target); 227 228 if (!m_referrer.isEmpty()) 229 frameRequest.resourceRequest().setHTTPReferrer(m_referrer); 230 231 if (m_method == FormSubmission::PostMethod) { 232 frameRequest.resourceRequest().setHTTPMethod("POST"); 233 frameRequest.resourceRequest().setHTTPBody(m_formData); 234 235 // construct some user headers if necessary 236 if (m_contentType.isNull() || m_contentType == "application/x-www-form-urlencoded") 237 frameRequest.resourceRequest().setHTTPContentType(m_contentType); 238 else // contentType must be "multipart/form-data" 239 frameRequest.resourceRequest().setHTTPContentType(m_contentType + "; boundary=" + m_boundary); 240 } 241 242 frameRequest.resourceRequest().setURL(requestURL()); 243 FrameLoader::addHTTPOriginIfNeeded(frameRequest.resourceRequest(), m_origin); 244 } 245 246 } 247