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 52namespace WebCore { 53 54using namespace HTMLNames; 55 56static 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 64static void appendMailtoPostFormDataToURL(URL& 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.replaceWithLiteral('&', "\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()).replaceWithLiteral('+', "%20"); 77 78 String query = url.query(); 79 if (query.isEmpty()) 80 url.setQuery(body); 81 else 82 url.setQuery(query + '&' + body); 83} 84 85void FormSubmission::Attributes::parseAction(const String& action) 86{ 87 // FIXME: Can we parse into a URL? 88 m_action = stripLeadingAndTrailingHTMLSpaces(action); 89} 90 91String FormSubmission::Attributes::parseEncodingType(const String& type) 92{ 93 if (equalIgnoringCase(type, "multipart/form-data")) 94 return "multipart/form-data"; 95 if (equalIgnoringCase(type, "text/plain")) 96 return "text/plain"; 97 return "application/x-www-form-urlencoded"; 98} 99 100void FormSubmission::Attributes::updateEncodingType(const String& type) 101{ 102 m_encodingType = parseEncodingType(type); 103 m_isMultiPartForm = (m_encodingType == "multipart/form-data"); 104} 105 106FormSubmission::Method FormSubmission::Attributes::parseMethodType(const String& type) 107{ 108 return equalIgnoringCase(type, "post") ? FormSubmission::PostMethod : FormSubmission::GetMethod; 109} 110 111void FormSubmission::Attributes::updateMethodType(const String& type) 112{ 113 m_method = parseMethodType(type); 114} 115 116void FormSubmission::Attributes::copyFrom(const Attributes& other) 117{ 118 m_method = other.m_method; 119 m_isMultiPartForm = other.m_isMultiPartForm; 120 121 m_action = other.m_action; 122 m_target = other.m_target; 123 m_encodingType = other.m_encodingType; 124 m_acceptCharset = other.m_acceptCharset; 125} 126 127inline FormSubmission::FormSubmission(Method method, const URL& action, const String& target, const String& contentType, PassRefPtr<FormState> state, PassRefPtr<FormData> data, const String& boundary, LockHistory lockHistory, PassRefPtr<Event> event) 128 : m_method(method) 129 , m_action(action) 130 , m_target(target) 131 , m_contentType(contentType) 132 , m_formState(state) 133 , m_formData(data) 134 , m_boundary(boundary) 135 , m_lockHistory(lockHistory) 136 , m_event(event) 137{ 138} 139 140PassRefPtr<FormSubmission> FormSubmission::create(HTMLFormElement* form, const Attributes& attributes, PassRefPtr<Event> event, LockHistory lockHistory, FormSubmissionTrigger trigger) 141{ 142 ASSERT(form); 143 144 HTMLFormControlElement* submitButton = 0; 145 if (event && event->target()) { 146 for (Node* node = event->target()->toNode(); node; node = node->parentNode()) { 147 if (node->isElementNode() && toElement(node)->isFormControlElement()) { 148 submitButton = toHTMLFormControlElement(node); 149 break; 150 } 151 } 152 } 153 154 FormSubmission::Attributes copiedAttributes; 155 copiedAttributes.copyFrom(attributes); 156 if (submitButton) { 157 String attributeValue; 158 if (!(attributeValue = submitButton->getAttribute(formactionAttr)).isNull()) 159 copiedAttributes.parseAction(attributeValue); 160 if (!(attributeValue = submitButton->getAttribute(formenctypeAttr)).isNull()) 161 copiedAttributes.updateEncodingType(attributeValue); 162 if (!(attributeValue = submitButton->getAttribute(formmethodAttr)).isNull()) 163 copiedAttributes.updateMethodType(attributeValue); 164 if (!(attributeValue = submitButton->getAttribute(formtargetAttr)).isNull()) 165 copiedAttributes.setTarget(attributeValue); 166 } 167 168 Document& document = form->document(); 169 URL actionURL = document.completeURL(copiedAttributes.action().isEmpty() ? document.url().string() : copiedAttributes.action()); 170 bool isMailtoForm = actionURL.protocolIs("mailto"); 171 bool isMultiPartForm = false; 172 String encodingType = copiedAttributes.encodingType(); 173 174 if (copiedAttributes.method() == PostMethod) { 175 isMultiPartForm = copiedAttributes.isMultiPartForm(); 176 if (isMultiPartForm && isMailtoForm) { 177 encodingType = "application/x-www-form-urlencoded"; 178 isMultiPartForm = false; 179 } 180 } 181 182 TextEncoding dataEncoding = isMailtoForm ? UTF8Encoding() : FormDataBuilder::encodingFromAcceptCharset(copiedAttributes.acceptCharset(), document); 183 RefPtr<DOMFormData> domFormData = DOMFormData::create(dataEncoding.encodingForFormSubmission()); 184 Vector<std::pair<String, String>> formValues; 185 186 bool containsPasswordData = false; 187 for (unsigned i = 0; i < form->associatedElements().size(); ++i) { 188 FormAssociatedElement& control = *form->associatedElements()[i]; 189 HTMLElement& element = control.asHTMLElement(); 190 if (!element.isDisabledFormControl()) 191 control.appendFormData(*domFormData, isMultiPartForm); 192 if (isHTMLInputElement(element)) { 193 HTMLInputElement& input = toHTMLInputElement(element); 194 if (input.isTextField()) { 195 formValues.append(std::pair<String, String>(input.name().string(), input.value())); 196 input.addSearchResult(); 197 } 198 if (input.isPasswordField() && !input.value().isEmpty()) 199 containsPasswordData = true; 200 } 201 } 202 203 RefPtr<FormData> formData; 204 String boundary; 205 206 if (isMultiPartForm) { 207 formData = FormData::createMultiPart(*(static_cast<FormDataList*>(domFormData.get())), domFormData->encoding(), &document); 208 boundary = formData->boundary().data(); 209 } else { 210 formData = FormData::create(*(static_cast<FormDataList*>(domFormData.get())), domFormData->encoding(), attributes.method() == GetMethod ? FormData::FormURLEncoded : FormData::parseEncodingType(encodingType)); 211 if (copiedAttributes.method() == PostMethod && isMailtoForm) { 212 // Convert the form data into a string that we put into the URL. 213 appendMailtoPostFormDataToURL(actionURL, *formData, encodingType); 214 formData = FormData::create(); 215 } 216 } 217 218 formData->setIdentifier(generateFormDataIdentifier()); 219 formData->setContainsPasswordData(containsPasswordData); 220 String targetOrBaseTarget = copiedAttributes.target().isEmpty() ? document.baseTarget() : copiedAttributes.target(); 221 RefPtr<FormState> formState = FormState::create(form, formValues, &document, trigger); 222 return adoptRef(new FormSubmission(copiedAttributes.method(), actionURL, targetOrBaseTarget, encodingType, formState.release(), formData.release(), boundary, lockHistory, event)); 223} 224 225URL FormSubmission::requestURL() const 226{ 227 if (m_method == FormSubmission::PostMethod) 228 return m_action; 229 230 URL requestURL(m_action); 231 requestURL.setQuery(m_formData->flattenToString()); 232 return requestURL; 233} 234 235void FormSubmission::populateFrameLoadRequest(FrameLoadRequest& frameRequest) 236{ 237 if (!m_target.isEmpty()) 238 frameRequest.setFrameName(m_target); 239 240 if (!m_referrer.isEmpty()) 241 frameRequest.resourceRequest().setHTTPReferrer(m_referrer); 242 243 if (m_method == FormSubmission::PostMethod) { 244 frameRequest.resourceRequest().setHTTPMethod("POST"); 245 frameRequest.resourceRequest().setHTTPBody(m_formData); 246 247 // construct some user headers if necessary 248 if (m_boundary.isEmpty()) 249 frameRequest.resourceRequest().setHTTPContentType(m_contentType); 250 else 251 frameRequest.resourceRequest().setHTTPContentType(m_contentType + "; boundary=" + m_boundary); 252 } 253 254 frameRequest.resourceRequest().setURL(requestURL()); 255 FrameLoader::addHTTPOriginIfNeeded(frameRequest.resourceRequest(), m_origin); 256} 257 258} 259