diff options
Diffstat (limited to 'WebCore/html/HTMLFormElement.cpp')
-rw-r--r-- | WebCore/html/HTMLFormElement.cpp | 271 |
1 files changed, 51 insertions, 220 deletions
diff --git a/WebCore/html/HTMLFormElement.cpp b/WebCore/html/HTMLFormElement.cpp index 0e4d88e..afc9c1e 100644 --- a/WebCore/html/HTMLFormElement.cpp +++ b/WebCore/html/HTMLFormElement.cpp @@ -44,10 +44,9 @@ #include "MIMETypeRegistry.h" #include "Page.h" #include "RenderTextControl.h" +#include <wtf/RandomNumber.h> -#if PLATFORM(QT) -#include <QtCore/QFileInfo> -#endif +#include <limits> #if PLATFORM(WX) #include <wx/defs.h> @@ -62,21 +61,17 @@ namespace WebCore { using namespace HTMLNames; -static const char hexDigits[17] = "0123456789ABCDEF"; - -HTMLFormElement::HTMLFormElement(Document* doc) - : HTMLElement(formTag, doc) +HTMLFormElement::HTMLFormElement(const QualifiedName& tagName, Document* doc) + : HTMLElement(tagName, doc) , m_elementAliases(0) , collectionInfo(0) - , m_enctype("application/x-www-form-urlencoded") - , m_post(false) - , m_multipart(false) , m_autocomplete(true) , m_insubmit(false) , m_doingsubmit(false) , m_inreset(false) , m_malformed(false) { + ASSERT(hasTagName(formTag)); } HTMLFormElement::~HTMLFormElement() @@ -161,193 +156,93 @@ void HTMLFormElement::submitClick(Event* event) prepareSubmit(event); } -static void appendString(Vector<char>& buffer, const char* string) -{ - buffer.append(string, strlen(string)); -} - -static void appendString(Vector<char>& buffer, const CString& string) -{ - buffer.append(string.data(), string.length()); -} - -static void appendEncodedString(Vector<char>& buffer, const CString& string) -{ - // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1 - int length = string.length(); - for (int i = 0; i < length; i++) { - unsigned char c = string.data()[i]; - - // Same safe characters as Netscape for compatibility. - static const char safe[] = "-._*"; - if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || strchr(safe, c)) - buffer.append(c); - else if (c == ' ') - buffer.append('+'); - else if (c == '\n' || (c == '\r' && (i + 1 >= length || string.data()[i + 1] != '\n'))) - appendString(buffer, "%0D%0A"); - else if (c != '\r') { - buffer.append('%'); - buffer.append(hexDigits[c >> 4]); - buffer.append(hexDigits[c & 0xF]); - } - } -} - -// FIXME: Move to platform directory? -static int randomNumber() -{ - static bool randomSeeded = false; - -#if PLATFORM(DARWIN) - if (!randomSeeded) { - srandomdev(); - randomSeeded = true; - } - return random(); -#else - if (!randomSeeded) { - srand(static_cast<unsigned>(time(0))); - randomSeeded = true; - } - return rand(); -#endif -} - TextEncoding HTMLFormElement::dataEncoding() const { if (isMailtoForm()) return UTF8Encoding(); - TextEncoding encoding; - String str = m_acceptcharset; - str.replace(',', ' '); - Vector<String> charsets; - str.split(' ', charsets); - Vector<String>::const_iterator end = charsets.end(); - for (Vector<String>::const_iterator it = charsets.begin(); it != end; ++it) - if ((encoding = TextEncoding(*it)).isValid()) - return encoding; - if (Frame* frame = document()->frame()) - return frame->loader()->encoding(); - return Latin1Encoding(); + return m_formDataBuilder.dataEncoding(document()); } -PassRefPtr<FormData> HTMLFormElement::formData(const char* boundary) const +PassRefPtr<FormData> HTMLFormElement::createFormData(const CString& boundary) { Vector<char> encodedData; - TextEncoding encoding = dataEncoding(); + TextEncoding encoding = dataEncoding().encodingForFormSubmission(); RefPtr<FormData> result = FormData::create(); - + for (unsigned i = 0; i < formElements.size(); ++i) { HTMLFormControlElement* control = formElements[i]; FormDataList list(encoding); - if (!control->disabled() && control->appendFormData(list, m_multipart)) { + if (!control->disabled() && control->appendFormData(list, m_formDataBuilder.isMultiPartForm())) { size_t formDataListSize = list.list().size(); ASSERT(formDataListSize % 2 == 0); for (size_t j = 0; j < formDataListSize; j += 2) { const FormDataList::Item& key = list.list()[j]; const FormDataList::Item& value = list.list()[j + 1]; - if (!m_multipart) { + if (!m_formDataBuilder.isMultiPartForm()) { // Omit the name "isindex" if it's the first form data element. // FIXME: Why is this a good rule? Is this obsolete now? if (encodedData.isEmpty() && key.data() == "isindex") - appendEncodedString(encodedData, value.data()); - else { - if (!encodedData.isEmpty()) - encodedData.append('&'); - appendEncodedString(encodedData, key.data()); - encodedData.append('='); - appendEncodedString(encodedData, value.data()); - } + FormDataBuilder::encodeStringAsFormData(encodedData, value.data()); + else + m_formDataBuilder.addKeyValuePairAsFormData(encodedData, key.data(), value.data()); } else { Vector<char> header; - appendString(header, "--"); - appendString(header, boundary); - appendString(header, "\r\n"); - appendString(header, "Content-Disposition: form-data; name=\""); - header.append(key.data().data(), key.data().length()); - header.append('"'); + m_formDataBuilder.beginMultiPartHeader(header, boundary, key.data()); bool shouldGenerateFile = false; - // if the current type is FILE, then we also need to - // include the filename + // if the current type is FILE, then we also need to include the filename if (value.file()) { const String& path = value.file()->path(); - String filename = value.file()->fileName(); + String fileName = value.file()->fileName(); // Let the application specify a filename if it's going to generate a replacement file for the upload. if (!path.isEmpty()) { if (Page* page = document()->page()) { - String generatedFilename; - shouldGenerateFile = page->chrome()->client()->shouldReplaceWithGeneratedFileForUpload(path, generatedFilename); - if (shouldGenerateFile) - filename = generatedFilename; + String generatedFileName; + if (shouldGenerateFile = page->chrome()->client()->shouldReplaceWithGeneratedFileForUpload(path, generatedFileName)) + fileName = generatedFileName; } } - // FIXME: This won't work if the filename includes a " mark, - // or control characters like CR or LF. This also does strange - // things if the filename includes characters you can't encode - // in the website's character set. - appendString(header, "; filename=\""); - appendString(header, encoding.encode(filename.characters(), filename.length(), QuestionMarksForUnencodables)); - header.append('"'); + // We have to include the filename=".." part in the header, even if the filename is empty + m_formDataBuilder.addFilenameToMultiPartHeader(header, encoding, fileName); - if (!filename.isEmpty()) { + if (!fileName.isEmpty()) { // FIXME: The MIMETypeRegistry function's name makes it sound like it takes a path, // not just a basename. But filename is not the path. But note that it's not safe to // just use path instead since in the generated-file case it will not reflect the // MIME type of the generated file. - String mimeType = MIMETypeRegistry::getMIMETypeForPath(filename); - if (!mimeType.isEmpty()) { - appendString(header, "\r\nContent-Type: "); - appendString(header, mimeType.latin1()); - } + String mimeType = MIMETypeRegistry::getMIMETypeForPath(fileName); + if (!mimeType.isEmpty()) + m_formDataBuilder.addContentTypeToMultiPartHeader(header, mimeType.latin1()); } } - appendString(header, "\r\n\r\n"); + m_formDataBuilder.finishMultiPartHeader(header); - // append body + // Append body result->appendData(header.data(), header.size()); if (size_t dataSize = value.data().length()) result->appendData(value.data().data(), dataSize); else if (value.file() && !value.file()->path().isEmpty()) result->appendFile(value.file()->path(), shouldGenerateFile); + result->appendData("\r\n", 2); } } } } - - if (m_multipart) { - appendString(encodedData, "--"); - appendString(encodedData, boundary); - appendString(encodedData, "--\r\n"); - } + if (m_formDataBuilder.isMultiPartForm()) + m_formDataBuilder.addBoundaryToMultiPartHeader(encodedData, boundary, true); result->appendData(encodedData.data(), encodedData.size()); return result; } -void HTMLFormElement::parseEnctype(const String& type) -{ - if(type.contains("multipart", false) || type.contains("form-data", false)) { - m_enctype = "multipart/form-data"; - m_multipart = true; - } else if (type.contains("text", false) || type.contains("plain", false)) { - m_enctype = "text/plain"; - m_multipart = false; - } else { - m_enctype = "application/x-www-form-urlencoded"; - m_multipart = false; - } -} - bool HTMLFormElement::isMailtoForm() const { return protocolIs(m_url, "mailto"); @@ -373,62 +268,7 @@ bool HTMLFormElement::prepareSubmit(Event* event) return m_doingsubmit; } -void HTMLFormElement::submit() -{ - submit(0, false); -} - -// Returns a 0-terminated C string in the vector. -static void getUniqueBoundaryString(Vector<char>& boundary) -{ - // The RFC 2046 spec says the AlphaNumeric characters plus the following characters - // are legal for boundaries: '()+_,-./:=? - // However the following characters, though legal, cause some sites to fail: - // (),./:= - // http://bugs.webkit.org/show_bug.cgi?id=13352 - static const char AlphaNumericEncMap[64] = - { - 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, - 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, - 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, - 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, - 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, - 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, - 0x77, 0x78, 0x79, 0x7A, 0x30, 0x31, 0x32, 0x33, - 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2B, 0x41 - // FIXME <rdar://problem/5252577> gmail does not accept legal characters in the form boundary - // As stated above, some legal characters cause, sites to fail. Specifically - // the / character which was the last character in the above array. I have - // replaced the last character with another character already in the array - // (notice the first and last values are both 0x41, A). Instead of picking - // another unique legal character for boundary strings that, because it has - // never been tested, may or may not break other sites, I simply - // replaced / with A. This means A is twice as likely to occur in our boundary - // strings than any other character but I think this is fine for the time being. - // The FIXME here is about restoring the / character once the aforementioned - // radar has been resolved. - }; - - // Start with an informative prefix. - const char boundaryPrefix[] = "----WebKitFormBoundary"; - boundary.append(boundaryPrefix, strlen(boundaryPrefix)); - - // Append 16 random 7bit ascii AlphaNumeric characters. - Vector<char> randomBytes; - - for (int i = 0; i < 4; ++i) { - int randomness = randomNumber(); - randomBytes.append(AlphaNumericEncMap[(randomness >> 24) & 0x3F]); - randomBytes.append(AlphaNumericEncMap[(randomness >> 16) & 0x3F]); - randomBytes.append(AlphaNumericEncMap[(randomness >> 8) & 0x3F]); - randomBytes.append(AlphaNumericEncMap[randomness & 0x3F]); - } - - boundary.append(randomBytes); - boundary.append(0); // Add a 0 at the end so we can use this as a C-style string. -} - -void HTMLFormElement::submit(Event* event, bool activateSubmitButton) +void HTMLFormElement::submit(Event* event, bool activateSubmitButton, bool lockHistory, bool lockBackForwardList) { FrameView* view = document()->view(); Frame* frame = document()->frame(); @@ -471,34 +311,33 @@ void HTMLFormElement::submit(Event* event, bool activateSubmitButton) if (m_url.isEmpty()) m_url = document()->url().string(); - if (m_post) { - if (m_multipart && isMailtoForm()) { + if (m_formDataBuilder.isPostMethod()) { + if (m_formDataBuilder.isMultiPartForm() && isMailtoForm()) { setEnctype("application/x-www-form-urlencoded"); - m_multipart = false; + ASSERT(!m_formDataBuilder.isMultiPartForm()); } - if (!m_multipart) { - RefPtr<FormData> data = formData(0); + if (!m_formDataBuilder.isMultiPartForm()) { + RefPtr<FormData> data = createFormData(CString()); if (isMailtoForm()) { String body = data->flattenToString(); - if (equalIgnoringCase(enctype(), "text/plain")) { + if (equalIgnoringCase(m_formDataBuilder.encodingType(), "text/plain")) { // Convention seems to be to decode, and s/&/\r\n/. Also, spaces are encoded as %20. body = decodeURLEscapeSequences(body.replace('&', "\r\n").replace('+', ' ') + "\r\n"); } Vector<char> bodyData; - appendString(bodyData, "body="); - appendEncodedString(bodyData, body.utf8()); + bodyData.append("body=", 5); + FormDataBuilder::encodeStringAsFormData(bodyData, body.utf8()); data = FormData::create(String(bodyData.data(), bodyData.size()).replace('+', "%20").latin1()); } - frame->loader()->submitForm("POST", m_url, data, m_target, enctype(), String(), event); + frame->loader()->submitForm("POST", m_url, data, m_target, m_formDataBuilder.encodingType(), String(), event, lockHistory, lockBackForwardList); } else { - Vector<char> boundary; - getUniqueBoundaryString(boundary); - frame->loader()->submitForm("POST", m_url, formData(boundary.data()), m_target, enctype(), boundary.data(), event); + Vector<char> boundary = m_formDataBuilder.generateUniqueBoundaryString(); + frame->loader()->submitForm("POST", m_url, createFormData(boundary.data()), m_target, m_formDataBuilder.encodingType(), boundary.data(), event, lockHistory, lockBackForwardList); } } else { - m_multipart = false; - frame->loader()->submitForm("GET", m_url, formData(0), m_target, String(), String(), event); + m_formDataBuilder.setIsMultiPartForm(false); + frame->loader()->submitForm("GET", m_url, createFormData(CString()), m_target, String(), String(), event, lockHistory, lockBackForwardList); } if (needButtonActivation && firstSuccessfulSubmitButton) @@ -534,17 +373,14 @@ void HTMLFormElement::parseMappedAttribute(MappedAttribute* attr) m_url = parseURL(attr->value()); else if (attr->name() == targetAttr) m_target = attr->value(); - else if (attr->name() == methodAttr) { - if (equalIgnoringCase(attr->value(), "post")) - m_post = true; - else if (equalIgnoringCase(attr->value(), "get")) - m_post = false; - } else if (attr->name() == enctypeAttr) - parseEnctype(attr->value()); + else if (attr->name() == methodAttr) + m_formDataBuilder.parseMethodType(attr->value()); + else if (attr->name() == enctypeAttr) + m_formDataBuilder.parseEncodingType(attr->value()); else if (attr->name() == accept_charsetAttr) // space separated list of charsets the server // accepts - see rfc2045 - m_acceptcharset = attr->value(); + m_formDataBuilder.setAcceptCharset(attr->value()); else if (attr->name() == acceptAttr) { // ignore this one for the moment... } else if (attr->name() == autocompleteAttr) { @@ -591,7 +427,7 @@ unsigned HTMLFormElement::formElementIndex(HTMLFormControlElement* e) if (node == e) return i; if (node->isHTMLElement() - && static_cast<HTMLElement*>(node)->isGenericFormElement() + && static_cast<Element*>(node)->isFormControlElement() && static_cast<HTMLFormControlElement*>(node)->form() == this) ++i; } @@ -642,11 +478,6 @@ void HTMLFormElement::setName(const String &value) setAttribute(nameAttr, value); } -String HTMLFormElement::acceptCharset() const -{ - return getAttribute(accept_charsetAttr); -} - void HTMLFormElement::setAcceptCharset(const String &value) { setAttribute(accept_charsetAttr, value); |