1/* 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 3 * (C) 1999 Antti Koivisto (koivisto@kde.org) 4 * (C) 2001 Dirk Mueller (mueller@kde.org) 5 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. 6 * (C) 2006 Alexey Proskuryakov (ap@nypop.com) 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Library General Public 10 * License as published by the Free Software Foundation; either 11 * version 2 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Library General Public License for more details. 17 * 18 * You should have received a copy of the GNU Library General Public License 19 * along with this library; see the file COPYING.LIB. If not, write to 20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 * Boston, MA 02110-1301, USA. 22 * 23 */ 24 25#include "config.h" 26#include "HTMLFormElement.h" 27 28#include "CSSHelper.h" 29#include "Chrome.h" 30#include "ChromeClient.h" 31#include "Document.h" 32#include "Event.h" 33#include "EventNames.h" 34#include "FileList.h" 35#include "FileSystem.h" 36#include "FormData.h" 37#include "FormDataList.h" 38#include "FormState.h" 39#include "Frame.h" 40#include "FrameLoader.h" 41#include "HTMLDocument.h" 42#include "HTMLFormCollection.h" 43#include "HTMLImageElement.h" 44#include "HTMLInputElement.h" 45#include "HTMLNames.h" 46#include "ScriptEventListener.h" 47#include "MIMETypeRegistry.h" 48#include "MappedAttribute.h" 49#include "Page.h" 50#include "RenderTextControl.h" 51#include "ValidityState.h" 52#include <limits> 53#include <wtf/CurrentTime.h> 54#include <wtf/RandomNumber.h> 55 56#if PLATFORM(WX) 57#include <wx/defs.h> 58#include <wx/filename.h> 59#endif 60 61using namespace std; 62 63namespace WebCore { 64 65using namespace HTMLNames; 66 67static int64_t generateFormDataIdentifier() 68{ 69 // Initialize to the current time to reduce the likelihood of generating 70 // identifiers that overlap with those from past/future browser sessions. 71 static int64_t nextIdentifier = static_cast<int64_t>(currentTime() * 1000000.0); 72 return ++nextIdentifier; 73} 74 75HTMLFormElement::HTMLFormElement(const QualifiedName& tagName, Document* doc) 76 : HTMLElement(tagName, doc) 77 , m_elementAliases(0) 78 , collectionInfo(0) 79 , m_autocomplete(true) 80 , m_insubmit(false) 81 , m_doingsubmit(false) 82 , m_inreset(false) 83 , m_malformed(false) 84 , m_demoted(false) 85{ 86 ASSERT(hasTagName(formTag)); 87} 88 89HTMLFormElement::~HTMLFormElement() 90{ 91 if (!m_autocomplete) 92 document()->unregisterForDocumentActivationCallbacks(this); 93 94 delete m_elementAliases; 95 delete collectionInfo; 96 97 for (unsigned i = 0; i < formElements.size(); ++i) 98 formElements[i]->formDestroyed(); 99 for (unsigned i = 0; i < imgElements.size(); ++i) 100 imgElements[i]->m_form = 0; 101} 102 103bool HTMLFormElement::formWouldHaveSecureSubmission(const String& url) 104{ 105 return document()->completeURL(url).protocolIs("https"); 106} 107 108void HTMLFormElement::attach() 109{ 110 HTMLElement::attach(); 111} 112 113bool HTMLFormElement::rendererIsNeeded(RenderStyle* style) 114{ 115 if (!isDemoted()) 116 return HTMLElement::rendererIsNeeded(style); 117 118 Node* node = parentNode(); 119 RenderObject* parentRenderer = node->renderer(); 120 bool parentIsTableElementPart = (parentRenderer->isTable() && node->hasTagName(tableTag)) 121 || (parentRenderer->isTableRow() && node->hasTagName(trTag)) 122 || (parentRenderer->isTableSection() && node->hasTagName(tbodyTag)) 123 || (parentRenderer->isTableCol() && node->hasTagName(colTag)) 124 || (parentRenderer->isTableCell() && node->hasTagName(trTag)); 125 126 if (!parentIsTableElementPart) 127 return true; 128 129 EDisplay display = style->display(); 130 bool formIsTablePart = display == TABLE || display == INLINE_TABLE || display == TABLE_ROW_GROUP 131 || display == TABLE_HEADER_GROUP || display == TABLE_FOOTER_GROUP || display == TABLE_ROW 132 || display == TABLE_COLUMN_GROUP || display == TABLE_COLUMN || display == TABLE_CELL 133 || display == TABLE_CAPTION; 134 135 return formIsTablePart; 136} 137 138void HTMLFormElement::insertedIntoDocument() 139{ 140 if (document()->isHTMLDocument()) 141 static_cast<HTMLDocument*>(document())->addNamedItem(m_name); 142 143 HTMLElement::insertedIntoDocument(); 144} 145 146void HTMLFormElement::removedFromDocument() 147{ 148 if (document()->isHTMLDocument()) 149 static_cast<HTMLDocument*>(document())->removeNamedItem(m_name); 150 151 HTMLElement::removedFromDocument(); 152} 153 154void HTMLFormElement::handleLocalEvents(Event* event) 155{ 156 Node* targetNode = event->target()->toNode(); 157 if (event->eventPhase() != Event::CAPTURING_PHASE && targetNode && targetNode != this && (event->type() == eventNames().submitEvent || event->type() == eventNames().resetEvent)) { 158 event->stopPropagation(); 159 return; 160 } 161 HTMLElement::handleLocalEvents(event); 162} 163 164unsigned HTMLFormElement::length() const 165{ 166 int len = 0; 167 for (unsigned i = 0; i < formElements.size(); ++i) 168 if (formElements[i]->isEnumeratable()) 169 ++len; 170 171 return len; 172} 173 174Node* HTMLFormElement::item(unsigned index) 175{ 176 return elements()->item(index); 177} 178 179void HTMLFormElement::submitClick(Event* event) 180{ 181 bool submitFound = false; 182 for (unsigned i = 0; i < formElements.size(); ++i) { 183 if (formElements[i]->hasLocalName(inputTag)) { 184 HTMLInputElement* element = static_cast<HTMLInputElement*>(formElements[i]); 185 if (element->isSuccessfulSubmitButton() && element->renderer()) { 186 submitFound = true; 187 element->dispatchSimulatedClick(event); 188 break; 189 } 190 } 191 } 192 if (!submitFound) // submit the form without a submit or image input 193 prepareSubmit(event); 194} 195 196TextEncoding HTMLFormElement::dataEncoding() const 197{ 198 if (isMailtoForm()) 199 return UTF8Encoding(); 200 201 return m_formDataBuilder.dataEncoding(document()); 202} 203 204PassRefPtr<FormData> HTMLFormElement::createFormData(const CString& boundary) 205{ 206 Vector<char> encodedData; 207 TextEncoding encoding = dataEncoding().encodingForFormSubmission(); 208 209 RefPtr<FormData> result = FormData::create(); 210 211 for (unsigned i = 0; i < formElements.size(); ++i) { 212 HTMLFormControlElement* control = formElements[i]; 213 FormDataList list(encoding); 214 215 if (!control->disabled() && control->appendFormData(list, m_formDataBuilder.isMultiPartForm())) { 216 size_t formDataListSize = list.list().size(); 217 ASSERT(formDataListSize % 2 == 0); 218 for (size_t j = 0; j < formDataListSize; j += 2) { 219 const FormDataList::Item& key = list.list()[j]; 220 const FormDataList::Item& value = list.list()[j + 1]; 221 if (!m_formDataBuilder.isMultiPartForm()) { 222 // Omit the name "isindex" if it's the first form data element. 223 // FIXME: Why is this a good rule? Is this obsolete now? 224 if (encodedData.isEmpty() && key.data() == "isindex") 225 FormDataBuilder::encodeStringAsFormData(encodedData, value.data()); 226 else 227 m_formDataBuilder.addKeyValuePairAsFormData(encodedData, key.data(), value.data()); 228 } else { 229 Vector<char> header; 230 m_formDataBuilder.beginMultiPartHeader(header, boundary, key.data()); 231 232 bool shouldGenerateFile = false; 233 // if the current type is FILE, then we also need to include the filename 234 if (value.file()) { 235 const String& path = value.file()->path(); 236 String fileName = value.file()->fileName(); 237 238 // Let the application specify a filename if it's going to generate a replacement file for the upload. 239 if (!path.isEmpty()) { 240 if (Page* page = document()->page()) { 241 String generatedFileName; 242 shouldGenerateFile = page->chrome()->client()->shouldReplaceWithGeneratedFileForUpload(path, generatedFileName); 243 if (shouldGenerateFile) 244 fileName = generatedFileName; 245 } 246 } 247 248 // We have to include the filename=".." part in the header, even if the filename is empty 249 m_formDataBuilder.addFilenameToMultiPartHeader(header, encoding, fileName); 250 251 if (!fileName.isEmpty()) { 252 // FIXME: The MIMETypeRegistry function's name makes it sound like it takes a path, 253 // not just a basename. But filename is not the path. But note that it's not safe to 254 // just use path instead since in the generated-file case it will not reflect the 255 // MIME type of the generated file. 256 String mimeType = MIMETypeRegistry::getMIMETypeForPath(fileName); 257 if (!mimeType.isEmpty()) 258 m_formDataBuilder.addContentTypeToMultiPartHeader(header, mimeType.latin1()); 259 } 260 } 261 262 m_formDataBuilder.finishMultiPartHeader(header); 263 264 // Append body 265 result->appendData(header.data(), header.size()); 266 if (size_t dataSize = value.data().length()) 267 result->appendData(value.data().data(), dataSize); 268 else if (value.file() && !value.file()->path().isEmpty()) 269 result->appendFile(value.file()->path(), shouldGenerateFile); 270 271 result->appendData("\r\n", 2); 272 } 273 } 274 } 275 } 276 277 if (m_formDataBuilder.isMultiPartForm()) 278 m_formDataBuilder.addBoundaryToMultiPartHeader(encodedData, boundary, true); 279 280 result->appendData(encodedData.data(), encodedData.size()); 281 282 result->setIdentifier(generateFormDataIdentifier()); 283 return result; 284} 285 286bool HTMLFormElement::isMailtoForm() const 287{ 288 return protocolIs(m_url, "mailto"); 289} 290 291bool HTMLFormElement::prepareSubmit(Event* event) 292{ 293 Frame* frame = document()->frame(); 294 if (m_insubmit || !frame) 295 return m_insubmit; 296 297 m_insubmit = true; 298 m_doingsubmit = false; 299 300 if (dispatchEvent(Event::create(eventNames().submitEvent, true, true)) && !m_doingsubmit) 301 m_doingsubmit = true; 302 303 m_insubmit = false; 304 305 if (m_doingsubmit) 306 submit(event, true, false, NotSubmittedByJavaScript); 307 308 return m_doingsubmit; 309} 310 311static void transferMailtoPostFormDataToURL(RefPtr<FormData>& data, KURL& url, const String& encodingType) 312{ 313 String body = data->flattenToString(); 314 data = FormData::create(); 315 316 if (equalIgnoringCase(encodingType, "text/plain")) { 317 // Convention seems to be to decode, and s/&/\r\n/. Also, spaces are encoded as %20. 318 body = decodeURLEscapeSequences(body.replace('&', "\r\n").replace('+', ' ') + "\r\n"); 319 } 320 321 Vector<char> bodyData; 322 bodyData.append("body=", 5); 323 FormDataBuilder::encodeStringAsFormData(bodyData, body.utf8()); 324 body = String(bodyData.data(), bodyData.size()).replace('+', "%20"); 325 326 String query = url.query(); 327 if (!query.isEmpty()) 328 query.append('&'); 329 query.append(body); 330 url.setQuery(query); 331} 332 333void HTMLFormElement::submit(Frame* javaScriptActiveFrame) 334{ 335 if (javaScriptActiveFrame) 336 submit(0, false, !javaScriptActiveFrame->script()->anyPageIsProcessingUserGesture(), SubmittedByJavaScript); 337 else 338 submit(0, false, false, NotSubmittedByJavaScript); 339} 340 341void HTMLFormElement::submit(Event* event, bool activateSubmitButton, bool lockHistory, FormSubmissionTrigger formSubmissionTrigger) 342{ 343 FrameView* view = document()->view(); 344 Frame* frame = document()->frame(); 345 if (!view || !frame) 346 return; 347 348 if (m_insubmit) { 349 m_doingsubmit = true; 350 return; 351 } 352 353 m_insubmit = true; 354 355 HTMLFormControlElement* firstSuccessfulSubmitButton = 0; 356 bool needButtonActivation = activateSubmitButton; // do we need to activate a submit button? 357 358 Vector<pair<String, String> > formValues; 359 360 for (unsigned i = 0; i < formElements.size(); ++i) { 361 HTMLFormControlElement* control = formElements[i]; 362 if (control->hasLocalName(inputTag)) { 363 HTMLInputElement* input = static_cast<HTMLInputElement*>(control); 364 if (input->isTextField()) { 365 formValues.append(pair<String, String>(input->name(), input->value())); 366 if (input->isSearchField()) 367 input->addSearchResult(); 368 } 369 } 370 if (needButtonActivation) { 371 if (control->isActivatedSubmit()) 372 needButtonActivation = false; 373 else if (firstSuccessfulSubmitButton == 0 && control->isSuccessfulSubmitButton()) 374 firstSuccessfulSubmitButton = control; 375 } 376 } 377 378 RefPtr<FormState> formState = FormState::create(this, formValues, frame, formSubmissionTrigger); 379 380 if (needButtonActivation && firstSuccessfulSubmitButton) 381 firstSuccessfulSubmitButton->setActivatedSubmit(true); 382 383 if (m_url.isEmpty()) 384 m_url = document()->url().string(); 385 386 if (m_formDataBuilder.isPostMethod()) { 387 if (m_formDataBuilder.isMultiPartForm() && isMailtoForm()) { 388 setEnctype("application/x-www-form-urlencoded"); 389 ASSERT(!m_formDataBuilder.isMultiPartForm()); 390 } 391 392 if (!m_formDataBuilder.isMultiPartForm()) { 393 RefPtr<FormData> data = createFormData(CString()); 394 395 if (isMailtoForm()) { 396 // Convert the form data into a string that we put into the URL. 397 KURL url = document()->completeURL(m_url); 398 transferMailtoPostFormDataToURL(data, url, m_formDataBuilder.encodingType()); 399 m_url = url.string(); 400 } 401 402 frame->loader()->submitForm("POST", m_url, data.release(), m_target, m_formDataBuilder.encodingType(), String(), lockHistory, event, formState.release()); 403 } else { 404 Vector<char> boundary = m_formDataBuilder.generateUniqueBoundaryString(); 405 frame->loader()->submitForm("POST", m_url, createFormData(boundary.data()), m_target, m_formDataBuilder.encodingType(), boundary.data(), lockHistory, event, formState.release()); 406 } 407 } else { 408 m_formDataBuilder.setIsMultiPartForm(false); 409 frame->loader()->submitForm("GET", m_url, createFormData(CString()), m_target, String(), String(), lockHistory, event, formState.release()); 410 } 411 412 if (needButtonActivation && firstSuccessfulSubmitButton) 413 firstSuccessfulSubmitButton->setActivatedSubmit(false); 414 415 m_doingsubmit = m_insubmit = false; 416} 417 418void HTMLFormElement::reset() 419{ 420 Frame* frame = document()->frame(); 421 if (m_inreset || !frame) 422 return; 423 424 m_inreset = true; 425 426 // ### DOM2 labels this event as not cancelable, however 427 // common browsers( sick! ) allow it be cancelled. 428 if (!dispatchEvent(Event::create(eventNames().resetEvent, true, true))) { 429 m_inreset = false; 430 return; 431 } 432 433 for (unsigned i = 0; i < formElements.size(); ++i) 434 formElements[i]->reset(); 435 436 m_inreset = false; 437} 438 439void HTMLFormElement::parseMappedAttribute(MappedAttribute* attr) 440{ 441 if (attr->name() == actionAttr) 442 m_url = deprecatedParseURL(attr->value()); 443 else if (attr->name() == targetAttr) 444 m_target = attr->value(); 445 else if (attr->name() == methodAttr) 446 m_formDataBuilder.parseMethodType(attr->value()); 447 else if (attr->name() == enctypeAttr) 448 m_formDataBuilder.parseEncodingType(attr->value()); 449 else if (attr->name() == accept_charsetAttr) 450 // space separated list of charsets the server 451 // accepts - see rfc2045 452 m_formDataBuilder.setAcceptCharset(attr->value()); 453 else if (attr->name() == acceptAttr) { 454 // ignore this one for the moment... 455 } else if (attr->name() == autocompleteAttr) { 456 m_autocomplete = !equalIgnoringCase(attr->value(), "off"); 457 if (!m_autocomplete) 458 document()->registerForDocumentActivationCallbacks(this); 459 else 460 document()->unregisterForDocumentActivationCallbacks(this); 461 } else if (attr->name() == onsubmitAttr) 462 setAttributeEventListener(eventNames().submitEvent, createAttributeEventListener(this, attr)); 463 else if (attr->name() == onresetAttr) 464 setAttributeEventListener(eventNames().resetEvent, createAttributeEventListener(this, attr)); 465 else if (attr->name() == nameAttr) { 466 const AtomicString& newName = attr->value(); 467 if (inDocument() && document()->isHTMLDocument()) { 468 HTMLDocument* document = static_cast<HTMLDocument*>(this->document()); 469 document->removeNamedItem(m_name); 470 document->addNamedItem(newName); 471 } 472 m_name = newName; 473 } else 474 HTMLElement::parseMappedAttribute(attr); 475} 476 477template<class T, size_t n> static void removeFromVector(Vector<T*, n> & vec, T* item) 478{ 479 size_t size = vec.size(); 480 for (size_t i = 0; i != size; ++i) 481 if (vec[i] == item) { 482 vec.remove(i); 483 break; 484 } 485} 486 487unsigned HTMLFormElement::formElementIndex(HTMLFormControlElement* e) 488{ 489 // Check for the special case where this element is the very last thing in 490 // the form's tree of children; we don't want to walk the entire tree in that 491 // common case that occurs during parsing; instead we'll just return a value 492 // that says "add this form element to the end of the array". 493 if (e->traverseNextNode(this)) { 494 unsigned i = 0; 495 for (Node* node = this; node; node = node->traverseNextNode(this)) { 496 if (node == e) 497 return i; 498 if (node->isHTMLElement() 499 && static_cast<Element*>(node)->isFormControlElement() 500 && static_cast<HTMLFormControlElement*>(node)->form() == this) 501 ++i; 502 } 503 } 504 return formElements.size(); 505} 506 507void HTMLFormElement::registerFormElement(HTMLFormControlElement* e) 508{ 509 document()->checkedRadioButtons().removeButton(e); 510 m_checkedRadioButtons.addButton(e); 511 formElements.insert(formElementIndex(e), e); 512} 513 514void HTMLFormElement::removeFormElement(HTMLFormControlElement* e) 515{ 516 m_checkedRadioButtons.removeButton(e); 517 removeFromVector(formElements, e); 518} 519 520bool HTMLFormElement::isURLAttribute(Attribute* attr) const 521{ 522 return attr->name() == actionAttr; 523} 524 525void HTMLFormElement::registerImgElement(HTMLImageElement* e) 526{ 527 ASSERT(imgElements.find(e) == notFound); 528 imgElements.append(e); 529} 530 531void HTMLFormElement::removeImgElement(HTMLImageElement* e) 532{ 533 ASSERT(imgElements.find(e) != notFound); 534 removeFromVector(imgElements, e); 535} 536 537PassRefPtr<HTMLCollection> HTMLFormElement::elements() 538{ 539 return HTMLFormCollection::create(this); 540} 541 542String HTMLFormElement::name() const 543{ 544 return getAttribute(nameAttr); 545} 546 547void HTMLFormElement::setName(const String &value) 548{ 549 setAttribute(nameAttr, value); 550} 551 552bool HTMLFormElement::noValidate() const 553{ 554 return !getAttribute(novalidateAttr).isNull(); 555} 556 557void HTMLFormElement::setNoValidate(bool novalidate) 558{ 559 setAttribute(novalidateAttr, novalidate ? "" : 0); 560} 561 562void HTMLFormElement::setAcceptCharset(const String &value) 563{ 564 setAttribute(accept_charsetAttr, value); 565} 566 567String HTMLFormElement::action() const 568{ 569 return getAttribute(actionAttr); 570} 571 572void HTMLFormElement::setAction(const String &value) 573{ 574 setAttribute(actionAttr, value); 575} 576 577void HTMLFormElement::setEnctype(const String &value) 578{ 579 setAttribute(enctypeAttr, value); 580} 581 582String HTMLFormElement::method() const 583{ 584 return getAttribute(methodAttr); 585} 586 587void HTMLFormElement::setMethod(const String &value) 588{ 589 setAttribute(methodAttr, value); 590} 591 592String HTMLFormElement::target() const 593{ 594 return getAttribute(targetAttr); 595} 596 597void HTMLFormElement::setTarget(const String &value) 598{ 599 setAttribute(targetAttr, value); 600} 601 602HTMLFormControlElement* HTMLFormElement::defaultButton() const 603{ 604 for (unsigned i = 0; i < formElements.size(); ++i) { 605 HTMLFormControlElement* control = formElements[i]; 606 if (control->isSuccessfulSubmitButton()) 607 return control; 608 } 609 610 return 0; 611} 612 613bool HTMLFormElement::checkValidity() 614{ 615 // TODO: Check for unhandled invalid controls, see #27452 for tips. 616 617 bool hasOnlyValidControls = true; 618 for (unsigned i = 0; i < formElements.size(); ++i) { 619 HTMLFormControlElement* control = formElements[i]; 620 if (!control->checkValidity()) 621 hasOnlyValidControls = false; 622 } 623 624 return hasOnlyValidControls; 625} 626 627PassRefPtr<HTMLFormControlElement> HTMLFormElement::elementForAlias(const AtomicString& alias) 628{ 629 if (alias.isEmpty() || !m_elementAliases) 630 return 0; 631 return m_elementAliases->get(alias.impl()); 632} 633 634void HTMLFormElement::addElementAlias(HTMLFormControlElement* element, const AtomicString& alias) 635{ 636 if (alias.isEmpty()) 637 return; 638 if (!m_elementAliases) 639 m_elementAliases = new AliasMap; 640 m_elementAliases->set(alias.impl(), element); 641} 642 643void HTMLFormElement::getNamedElements(const AtomicString& name, Vector<RefPtr<Node> >& namedItems) 644{ 645 elements()->namedItems(name, namedItems); 646 647 // see if we have seen something with this name before 648 RefPtr<HTMLFormControlElement> aliasElem; 649 if (aliasElem = elementForAlias(name)) { 650 bool found = false; 651 for (unsigned n = 0; n < namedItems.size(); n++) { 652 if (namedItems[n] == aliasElem.get()) { 653 found = true; 654 break; 655 } 656 } 657 if (!found) 658 // we have seen it before but it is gone now. still, we need to return it. 659 namedItems.append(aliasElem.get()); 660 } 661 // name has been accessed, remember it 662 if (namedItems.size() && aliasElem != namedItems.first()) 663 addElementAlias(static_cast<HTMLFormControlElement*>(namedItems.first().get()), name); 664} 665 666void HTMLFormElement::documentDidBecomeActive() 667{ 668 ASSERT(!m_autocomplete); 669 670 for (unsigned i = 0; i < formElements.size(); ++i) 671 formElements[i]->reset(); 672} 673 674void HTMLFormElement::willMoveToNewOwnerDocument() 675{ 676 if (!m_autocomplete) 677 document()->unregisterForDocumentActivationCallbacks(this); 678 HTMLElement::willMoveToNewOwnerDocument(); 679} 680 681void HTMLFormElement::didMoveToNewOwnerDocument() 682{ 683 if (!m_autocomplete) 684 document()->registerForDocumentActivationCallbacks(this); 685 HTMLElement::didMoveToNewOwnerDocument(); 686} 687 688} // namespace 689