|
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 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 "Base64.h" |
|
29 #include "CSSHelper.h" |
|
30 #include "CString.h" |
|
31 #include "Event.h" |
|
32 #include "EventNames.h" |
|
33 #include "FormData.h" |
|
34 #include "FormDataList.h" |
|
35 #include "Frame.h" |
|
36 #include "FrameLoader.h" |
|
37 #include "HTMLDocument.h" |
|
38 #include "HTMLFormCollection.h" |
|
39 #include "HTMLImageElement.h" |
|
40 #include "HTMLInputElement.h" |
|
41 #include "HTMLNames.h" |
|
42 #include "MIMETypeRegistry.h" |
|
43 #include "RenderTextControl.h" |
|
44 |
|
45 #if PLATFORM(QT) |
|
46 #include <QtCore/QFileInfo> |
|
47 #endif |
|
48 |
|
49 #if PLATFORM(WIN_OS) |
|
50 #include <shlwapi.h> |
|
51 #endif |
|
52 |
|
53 namespace WebCore { |
|
54 |
|
55 using namespace EventNames; |
|
56 using namespace HTMLNames; |
|
57 |
|
58 HTMLFormElement::HTMLFormElement(Document* doc) |
|
59 : HTMLElement(formTag, doc) |
|
60 , m_elementAliases(0) |
|
61 , collectionInfo(0) |
|
62 , m_enctype("application/x-www-form-urlencoded") |
|
63 , m_post(false) |
|
64 , m_multipart(false) |
|
65 , m_autocomplete(true) |
|
66 , m_insubmit(false) |
|
67 , m_doingsubmit(false) |
|
68 , m_inreset(false) |
|
69 , m_malformed(false) |
|
70 { |
|
71 } |
|
72 |
|
73 HTMLFormElement::~HTMLFormElement() |
|
74 { |
|
75 delete m_elementAliases; |
|
76 delete collectionInfo; |
|
77 |
|
78 for (unsigned i = 0; i < formElements.size(); ++i) |
|
79 formElements[i]->formDestroyed(); |
|
80 for (unsigned i = 0; i < imgElements.size(); ++i) |
|
81 imgElements[i]->m_form = 0; |
|
82 } |
|
83 |
|
84 bool HTMLFormElement::formWouldHaveSecureSubmission(const String &url) |
|
85 { |
|
86 if (url.isNull()) { |
|
87 return false; |
|
88 } |
|
89 return document()->completeURL(url.deprecatedString()).startsWith("https:", false); |
|
90 } |
|
91 |
|
92 void HTMLFormElement::attach() |
|
93 { |
|
94 HTMLElement::attach(); |
|
95 } |
|
96 |
|
97 void HTMLFormElement::insertedIntoDocument() |
|
98 { |
|
99 if (document()->isHTMLDocument()) { |
|
100 HTMLDocument *doc = static_cast<HTMLDocument *>(document()); |
|
101 doc->addNamedItem(oldNameAttr); |
|
102 } |
|
103 |
|
104 HTMLElement::insertedIntoDocument(); |
|
105 } |
|
106 |
|
107 void HTMLFormElement::removedFromDocument() |
|
108 { |
|
109 if (document()->isHTMLDocument()) { |
|
110 HTMLDocument *doc = static_cast<HTMLDocument *>(document()); |
|
111 doc->removeNamedItem(oldNameAttr); |
|
112 } |
|
113 |
|
114 HTMLElement::removedFromDocument(); |
|
115 } |
|
116 |
|
117 void HTMLFormElement::handleLocalEvents(Event* event, bool useCapture) |
|
118 { |
|
119 EventTargetNode* targetNode = event->target()->toNode(); |
|
120 if (!useCapture && targetNode && targetNode != this && (event->type() == submitEvent || event->type() == resetEvent)) { |
|
121 event->stopPropagation(); |
|
122 return; |
|
123 } |
|
124 HTMLElement::handleLocalEvents(event, useCapture); |
|
125 } |
|
126 |
|
127 unsigned HTMLFormElement::length() const |
|
128 { |
|
129 int len = 0; |
|
130 for (unsigned i = 0; i < formElements.size(); ++i) |
|
131 if (formElements[i]->isEnumeratable()) |
|
132 ++len; |
|
133 |
|
134 return len; |
|
135 } |
|
136 |
|
137 Node* HTMLFormElement::item(unsigned index) |
|
138 { |
|
139 return elements()->item(index); |
|
140 } |
|
141 |
|
142 void HTMLFormElement::submitClick(Event* event) |
|
143 { |
|
144 bool submitFound = false; |
|
145 for (unsigned i = 0; i < formElements.size(); ++i) { |
|
146 if (formElements[i]->hasLocalName(inputTag)) { |
|
147 HTMLInputElement *element = static_cast<HTMLInputElement *>(formElements[i]); |
|
148 if (element->isSuccessfulSubmitButton() && element->renderer()) { |
|
149 submitFound = true; |
|
150 element->dispatchSimulatedClick(event); |
|
151 break; |
|
152 } |
|
153 } |
|
154 } |
|
155 if (!submitFound) // submit the form without a submit or image input |
|
156 prepareSubmit(event); |
|
157 } |
|
158 |
|
159 static DeprecatedCString encodeCString(const CString& cstr) |
|
160 { |
|
161 DeprecatedCString e = cstr.deprecatedCString(); |
|
162 |
|
163 // http://www.w3.org/TR/html4/interact/forms.html#h-17.13.4.1 |
|
164 // same safe characters as Netscape for compatibility |
|
165 static const char *safe = "-._*"; |
|
166 int elen = e.length(); |
|
167 DeprecatedCString encoded((elen + e.contains('\n')) * 3 + 1); |
|
168 int enclen = 0; |
|
169 |
|
170 for (int pos = 0; pos < elen; pos++) { |
|
171 unsigned char c = e[pos]; |
|
172 |
|
173 if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || strchr(safe, c)) |
|
174 encoded[enclen++] = c; |
|
175 else if (c == ' ') |
|
176 encoded[enclen++] = '+'; |
|
177 else if (c == '\n' || (c == '\r' && e[pos + 1] != '\n')) { |
|
178 encoded[enclen++] = '%'; |
|
179 encoded[enclen++] = '0'; |
|
180 encoded[enclen++] = 'D'; |
|
181 encoded[enclen++] = '%'; |
|
182 encoded[enclen++] = '0'; |
|
183 encoded[enclen++] = 'A'; |
|
184 } else if (c != '\r') { |
|
185 encoded[enclen++] = '%'; |
|
186 unsigned int h = c / 16; |
|
187 h += (h > 9) ? ('A' - 10) : '0'; |
|
188 encoded[enclen++] = h; |
|
189 |
|
190 unsigned int l = c % 16; |
|
191 l += (l > 9) ? ('A' - 10) : '0'; |
|
192 encoded[enclen++] = l; |
|
193 } |
|
194 } |
|
195 encoded[enclen++] = '\0'; |
|
196 encoded.truncate(enclen); |
|
197 |
|
198 return encoded; |
|
199 } |
|
200 |
|
201 static int randomNumber() |
|
202 { |
|
203 static bool randomSeeded = false; |
|
204 |
|
205 #if PLATFORM(DARWIN) |
|
206 if (!randomSeeded) { |
|
207 srandomdev(); |
|
208 randomSeeded = true; |
|
209 } |
|
210 return random(); |
|
211 #else |
|
212 if (!randomSeeded) { |
|
213 srand(static_cast<unsigned>(time(0))); |
|
214 randomSeeded = true; |
|
215 } |
|
216 return rand(); |
|
217 #endif |
|
218 } |
|
219 |
|
220 // Warning: this helper doesn't currently have a reliable cross-platform behavior in certain edge cases |
|
221 // (see basename(3) specification for examples). |
|
222 // Consider this if it ever needs to become a general purpose method. |
|
223 static String pathGetFilename(String path) |
|
224 { |
|
225 #if PLATFORM(QT) |
|
226 return QFileInfo(path).fileName(); |
|
227 #elif PLATFORM(WIN_OS) |
|
228 return String(PathFindFileName(path.charactersWithNullTermination())); |
|
229 #else |
|
230 return path.substring(path.reverseFind('/') + 1); |
|
231 #endif |
|
232 } |
|
233 |
|
234 PassRefPtr<FormData> HTMLFormElement::formData(const char* boundary) const |
|
235 { |
|
236 DeprecatedCString enc_string = ""; |
|
237 |
|
238 String str = m_acceptcharset; |
|
239 str.replace(',', ' '); |
|
240 Vector<String> charsets = str.split(' '); |
|
241 TextEncoding encoding; |
|
242 Frame* frame = document()->frame(); |
|
243 Vector<String>::const_iterator end = charsets.end(); |
|
244 for (Vector<String>::const_iterator it = charsets.begin(); it != end; ++it) |
|
245 if ((encoding = TextEncoding(*it)).isValid()) |
|
246 break; |
|
247 if (!encoding.isValid()) { |
|
248 if (frame) |
|
249 encoding = frame->loader()->encoding(); |
|
250 else |
|
251 encoding = Latin1Encoding(); |
|
252 } |
|
253 |
|
254 RefPtr<FormData> result = new FormData; |
|
255 |
|
256 for (unsigned i = 0; i < formElements.size(); ++i) { |
|
257 HTMLGenericFormElement* current = formElements[i]; |
|
258 FormDataList lst(encoding); |
|
259 |
|
260 if (!current->disabled() && current->appendFormData(lst, m_multipart)) { |
|
261 size_t ln = lst.list().size(); |
|
262 for (size_t j = 0; j < ln; ++j) { |
|
263 const FormDataListItem& item = lst.list()[j]; |
|
264 if (!m_multipart) { |
|
265 // handle ISINDEX / <input name=isindex> special |
|
266 // but only if its the first entry |
|
267 if (enc_string.isEmpty() && item.m_data == "isindex") { |
|
268 enc_string += encodeCString(lst.list()[j + 1].m_data); |
|
269 ++j; |
|
270 } else { |
|
271 if (!enc_string.isEmpty()) |
|
272 enc_string += '&'; |
|
273 |
|
274 enc_string += encodeCString(item.m_data); |
|
275 enc_string += "="; |
|
276 enc_string += encodeCString(lst.list()[j + 1].m_data); |
|
277 ++j; |
|
278 } |
|
279 } |
|
280 else |
|
281 { |
|
282 DeprecatedCString hstr("--"); |
|
283 hstr += boundary; |
|
284 hstr += "\r\n"; |
|
285 hstr += "Content-Disposition: form-data; name=\""; |
|
286 hstr += item.m_data.data(); |
|
287 hstr += "\""; |
|
288 |
|
289 // if the current type is FILE, then we also need to |
|
290 // include the filename |
|
291 if (current->hasLocalName(inputTag) && |
|
292 static_cast<HTMLInputElement*>(current)->inputType() == HTMLInputElement::FILE) { |
|
293 String path = static_cast<HTMLInputElement*>(current)->value(); |
|
294 String filename = pathGetFilename(path); |
|
295 |
|
296 // FIXME: This won't work if the filename includes a " mark, |
|
297 // or control characters like CR or LF. This also does strange |
|
298 // things if the filename includes characters you can't encode |
|
299 // in the website's character set. |
|
300 hstr += "; filename=\""; |
|
301 hstr += encoding.encode(reinterpret_cast<const UChar*>(filename.characters()), filename.length(), true).data(); |
|
302 hstr += "\""; |
|
303 |
|
304 if (!static_cast<HTMLInputElement*>(current)->value().isEmpty()) { |
|
305 DeprecatedString mimeType = MIMETypeRegistry::getMIMETypeForPath(path).deprecatedString(); |
|
306 if (!mimeType.isEmpty()) { |
|
307 hstr += "\r\nContent-Type: "; |
|
308 hstr += mimeType.ascii(); |
|
309 } |
|
310 } |
|
311 } |
|
312 |
|
313 hstr += "\r\n\r\n"; |
|
314 |
|
315 // append body |
|
316 result->appendData(hstr.data(), hstr.length()); |
|
317 const FormDataListItem& item = lst.list()[j + 1]; |
|
318 if (size_t dataSize = item.m_data.length()) |
|
319 result->appendData(item.m_data.data(), dataSize); |
|
320 else if (!item.m_path.isEmpty()) |
|
321 result->appendFile(item.m_path); |
|
322 result->appendData("\r\n", 2); |
|
323 |
|
324 ++j; |
|
325 } |
|
326 } |
|
327 } |
|
328 } |
|
329 |
|
330 |
|
331 if (m_multipart) { |
|
332 enc_string = "--"; |
|
333 enc_string += boundary; |
|
334 enc_string += "--\r\n"; |
|
335 } |
|
336 |
|
337 result->appendData(enc_string.data(), enc_string.length()); |
|
338 return result; |
|
339 } |
|
340 |
|
341 void HTMLFormElement::parseEnctype(const String& type) |
|
342 { |
|
343 if(type.contains("multipart", false) || type.contains("form-data", false)) { |
|
344 m_enctype = "multipart/form-data"; |
|
345 m_multipart = true; |
|
346 } else if (type.contains("text", false) || type.contains("plain", false)) { |
|
347 m_enctype = "text/plain"; |
|
348 m_multipart = false; |
|
349 } else { |
|
350 m_enctype = "application/x-www-form-urlencoded"; |
|
351 m_multipart = false; |
|
352 } |
|
353 } |
|
354 |
|
355 bool HTMLFormElement::prepareSubmit(Event* event) |
|
356 { |
|
357 Frame* frame = document()->frame(); |
|
358 if (m_insubmit || !frame) |
|
359 return m_insubmit; |
|
360 |
|
361 m_insubmit = true; |
|
362 m_doingsubmit = false; |
|
363 |
|
364 if (dispatchHTMLEvent(submitEvent, true, true) && !m_doingsubmit) |
|
365 m_doingsubmit = true; |
|
366 |
|
367 m_insubmit = false; |
|
368 |
|
369 if (m_doingsubmit) |
|
370 submit(event, true); |
|
371 |
|
372 return m_doingsubmit; |
|
373 } |
|
374 |
|
375 void HTMLFormElement::submit() |
|
376 { |
|
377 submit(0, false); |
|
378 } |
|
379 |
|
380 // Returns a 0-terminated C string in the vector. |
|
381 static void getUniqueBoundaryString(Vector<char>& boundary) |
|
382 { |
|
383 // The RFC 2046 spec says the AlphaNumeric characters plus the following characters |
|
384 // are legal for boundaries: '()+_,-./:=? |
|
385 // However the following characters, though legal, cause some sites to fail: |
|
386 // (),./:= |
|
387 // http://bugs.webkit.org/show_bug.cgi?id=13352 |
|
388 static const char AlphaNumericEncMap[64] = |
|
389 { |
|
390 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, |
|
391 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, |
|
392 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, |
|
393 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, |
|
394 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, |
|
395 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, |
|
396 0x77, 0x78, 0x79, 0x7A, 0x30, 0x31, 0x32, 0x33, |
|
397 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2B, 0x41 |
|
398 // FIXME <rdar://problem/5252577> gmail does not accept legal characters in the form boundary |
|
399 // As stated above, some legal characters cause, sites to fail. Specifically |
|
400 // the / character which was the last character in the above array. I have |
|
401 // replaced the last character with another character already in the array |
|
402 // (notice the first and last values are both 0x41, A). Instead of picking |
|
403 // another unique legal character for boundary strings that, because it has |
|
404 // never been tested, may or may not break other sites, I simply |
|
405 // replaced / with A. This means A is twice as likely to occur in our boundary |
|
406 // strings than any other character but I think this is fine for the time being. |
|
407 // The FIXME here is about restoring the / character once the aforementioned |
|
408 // radar has been resolved. |
|
409 }; |
|
410 |
|
411 // Start with an informative prefix. |
|
412 const char boundaryPrefix[] = "----WebKitFormBoundary"; |
|
413 boundary.append(boundaryPrefix, strlen(boundaryPrefix)); |
|
414 |
|
415 // Append 16 random 7bit ascii AlphaNumeric characters. |
|
416 Vector<char> randomBytes; |
|
417 |
|
418 for (int i = 0; i < 4; ++i) { |
|
419 int randomness = randomNumber(); |
|
420 randomBytes.append(AlphaNumericEncMap[(randomness >> 24) & 0x3F]); |
|
421 randomBytes.append(AlphaNumericEncMap[(randomness >> 16) & 0x3F]); |
|
422 randomBytes.append(AlphaNumericEncMap[(randomness >> 8) & 0x3F]); |
|
423 randomBytes.append(AlphaNumericEncMap[randomness & 0x3F]); |
|
424 } |
|
425 |
|
426 boundary.append(randomBytes); |
|
427 boundary.append(0); // Add a 0 at the end so we can use this as a C-style string. |
|
428 } |
|
429 |
|
430 void HTMLFormElement::submit(Event* event, bool activateSubmitButton) |
|
431 { |
|
432 FrameView *view = document()->view(); |
|
433 Frame *frame = document()->frame(); |
|
434 if (!view || !frame) |
|
435 return; |
|
436 |
|
437 if (m_insubmit) { |
|
438 m_doingsubmit = true; |
|
439 return; |
|
440 } |
|
441 |
|
442 m_insubmit = true; |
|
443 |
|
444 HTMLGenericFormElement* firstSuccessfulSubmitButton = 0; |
|
445 bool needButtonActivation = activateSubmitButton; // do we need to activate a submit button? |
|
446 |
|
447 frame->loader()->clearRecordedFormValues(); |
|
448 for (unsigned i = 0; i < formElements.size(); ++i) { |
|
449 HTMLGenericFormElement* current = formElements[i]; |
|
450 if (current->hasLocalName(inputTag)) { |
|
451 HTMLInputElement* input = static_cast<HTMLInputElement*>(current); |
|
452 if (input->isTextField()) { |
|
453 frame->loader()->recordFormValue(input->name(), input->value(), this); |
|
454 if (input->isSearchField()) |
|
455 input->addSearchResult(); |
|
456 } |
|
457 } |
|
458 if (needButtonActivation) { |
|
459 if (current->isActivatedSubmit()) |
|
460 needButtonActivation = false; |
|
461 else if (firstSuccessfulSubmitButton == 0 && current->isSuccessfulSubmitButton()) |
|
462 firstSuccessfulSubmitButton = current; |
|
463 } |
|
464 } |
|
465 |
|
466 if (needButtonActivation && firstSuccessfulSubmitButton) |
|
467 firstSuccessfulSubmitButton->setActivatedSubmit(true); |
|
468 |
|
469 #if PLATFORM(SYMBIAN) |
|
470 // if html form did not specify an 'action' url - use document's url |
|
471 if(!m_url.length()) { |
|
472 KURL url = frame->loader()->URL(); |
|
473 if (!m_post) { |
|
474 url.setQuery(DeprecatedString("")); |
|
475 } |
|
476 m_url += String(url); |
|
477 } |
|
478 #endif |
|
479 |
|
480 if (m_post) { |
|
481 if (!m_multipart) |
|
482 frame->loader()->submitForm("POST", m_url, formData(0), m_target, enctype(), String(), event); |
|
483 else { |
|
484 Vector<char> boundary; |
|
485 getUniqueBoundaryString(boundary); |
|
486 frame->loader()->submitForm("POST", m_url, formData(boundary.data()), m_target, enctype(), boundary.data(), event); |
|
487 } |
|
488 } else { |
|
489 m_multipart = false; |
|
490 frame->loader()->submitForm("GET", m_url, formData(0), m_target, String(), String(), event); |
|
491 } |
|
492 |
|
493 if (needButtonActivation && firstSuccessfulSubmitButton) |
|
494 firstSuccessfulSubmitButton->setActivatedSubmit(false); |
|
495 |
|
496 m_doingsubmit = m_insubmit = false; |
|
497 } |
|
498 |
|
499 void HTMLFormElement::reset() |
|
500 { |
|
501 Frame *frame = document()->frame(); |
|
502 if (m_inreset || !frame) |
|
503 return; |
|
504 |
|
505 m_inreset = true; |
|
506 |
|
507 // ### DOM2 labels this event as not cancelable, however |
|
508 // common browsers( sick! ) allow it be cancelled. |
|
509 if ( !dispatchHTMLEvent(resetEvent,true, true) ) { |
|
510 m_inreset = false; |
|
511 return; |
|
512 } |
|
513 |
|
514 for (unsigned i = 0; i < formElements.size(); ++i) |
|
515 formElements[i]->reset(); |
|
516 |
|
517 m_inreset = false; |
|
518 } |
|
519 |
|
520 void HTMLFormElement::parseMappedAttribute(MappedAttribute *attr) |
|
521 { |
|
522 if (attr->name() == actionAttr) |
|
523 m_url = parseURL(attr->value()); |
|
524 else if (attr->name() == targetAttr) |
|
525 m_target = attr->value(); |
|
526 else if (attr->name() == methodAttr) { |
|
527 if (equalIgnoringCase(attr->value(), "post")) |
|
528 m_post = true; |
|
529 else if (equalIgnoringCase(attr->value(), "get")) |
|
530 m_post = false; |
|
531 } else if (attr->name() == enctypeAttr) |
|
532 parseEnctype(attr->value()); |
|
533 else if (attr->name() == accept_charsetAttr) |
|
534 // space separated list of charsets the server |
|
535 // accepts - see rfc2045 |
|
536 m_acceptcharset = attr->value(); |
|
537 else if (attr->name() == acceptAttr) { |
|
538 // ignore this one for the moment... |
|
539 } else if (attr->name() == autocompleteAttr) |
|
540 m_autocomplete = !equalIgnoringCase(attr->value(), "off"); |
|
541 else if (attr->name() == onsubmitAttr) |
|
542 setHTMLEventListener(submitEvent, attr); |
|
543 else if (attr->name() == onresetAttr) |
|
544 setHTMLEventListener(resetEvent, attr); |
|
545 else if (attr->name() == nameAttr) { |
|
546 String newNameAttr = attr->value(); |
|
547 if (inDocument() && document()->isHTMLDocument()) { |
|
548 HTMLDocument *doc = static_cast<HTMLDocument *>(document()); |
|
549 doc->removeNamedItem(oldNameAttr); |
|
550 doc->addNamedItem(newNameAttr); |
|
551 } |
|
552 oldNameAttr = newNameAttr; |
|
553 } else |
|
554 HTMLElement::parseMappedAttribute(attr); |
|
555 } |
|
556 |
|
557 template<class T, size_t n> static void removeFromVector(Vector<T*, n> & vec, T* item) |
|
558 { |
|
559 size_t size = vec.size(); |
|
560 for (size_t i = 0; i != size; ++i) |
|
561 if (vec[i] == item) { |
|
562 vec.remove(i); |
|
563 break; |
|
564 } |
|
565 } |
|
566 |
|
567 unsigned HTMLFormElement::formElementIndex(HTMLGenericFormElement *e) |
|
568 { |
|
569 // Check for the special case where this element is the very last thing in |
|
570 // the form's tree of children; we don't want to walk the entire tree in that |
|
571 // common case that occurs during parsing; instead we'll just return a value |
|
572 // that says "add this form element to the end of the array". |
|
573 if (e->traverseNextNode(this)) { |
|
574 unsigned i = 0; |
|
575 for (Node *node = this; node; node = node->traverseNextNode(this)) { |
|
576 if (node == e) |
|
577 return i; |
|
578 if (node->isHTMLElement() |
|
579 && static_cast<HTMLElement *>(node)->isGenericFormElement() |
|
580 && static_cast<HTMLGenericFormElement *>(node)->form() == this) |
|
581 ++i; |
|
582 } |
|
583 } |
|
584 return formElements.size(); |
|
585 } |
|
586 |
|
587 void HTMLFormElement::registerFormElement(HTMLGenericFormElement* e) |
|
588 { |
|
589 Document* doc = document(); |
|
590 doc->checkedRadioButtons().removeButton(e); |
|
591 m_checkedRadioButtons.addButton(e); |
|
592 formElements.insert(formElementIndex(e), e); |
|
593 doc->incDOMTreeVersion(); |
|
594 } |
|
595 |
|
596 void HTMLFormElement::removeFormElement(HTMLGenericFormElement* e) |
|
597 { |
|
598 m_checkedRadioButtons.removeButton(e); |
|
599 removeFromVector(formElements, e); |
|
600 document()->incDOMTreeVersion(); |
|
601 } |
|
602 |
|
603 bool HTMLFormElement::isURLAttribute(Attribute *attr) const |
|
604 { |
|
605 return attr->name() == actionAttr; |
|
606 } |
|
607 |
|
608 void HTMLFormElement::registerImgElement(HTMLImageElement *e) |
|
609 { |
|
610 imgElements.append(e); |
|
611 } |
|
612 |
|
613 void HTMLFormElement::removeImgElement(HTMLImageElement *e) |
|
614 { |
|
615 removeFromVector(imgElements, e); |
|
616 } |
|
617 |
|
618 PassRefPtr<HTMLCollection> HTMLFormElement::elements() |
|
619 { |
|
620 return new HTMLFormCollection(this); |
|
621 } |
|
622 |
|
623 String HTMLFormElement::name() const |
|
624 { |
|
625 return getAttribute(nameAttr); |
|
626 } |
|
627 |
|
628 void HTMLFormElement::setName(const String &value) |
|
629 { |
|
630 setAttribute(nameAttr, value); |
|
631 } |
|
632 |
|
633 String HTMLFormElement::acceptCharset() const |
|
634 { |
|
635 return getAttribute(accept_charsetAttr); |
|
636 } |
|
637 |
|
638 void HTMLFormElement::setAcceptCharset(const String &value) |
|
639 { |
|
640 setAttribute(accept_charsetAttr, value); |
|
641 } |
|
642 |
|
643 String HTMLFormElement::action() const |
|
644 { |
|
645 return getAttribute(actionAttr); |
|
646 } |
|
647 |
|
648 void HTMLFormElement::setAction(const String &value) |
|
649 { |
|
650 setAttribute(actionAttr, value); |
|
651 } |
|
652 |
|
653 void HTMLFormElement::setEnctype(const String &value) |
|
654 { |
|
655 setAttribute(enctypeAttr, value); |
|
656 } |
|
657 |
|
658 String HTMLFormElement::method() const |
|
659 { |
|
660 return getAttribute(methodAttr); |
|
661 } |
|
662 |
|
663 void HTMLFormElement::setMethod(const String &value) |
|
664 { |
|
665 setAttribute(methodAttr, value); |
|
666 } |
|
667 |
|
668 String HTMLFormElement::target() const |
|
669 { |
|
670 return getAttribute(targetAttr); |
|
671 } |
|
672 |
|
673 void HTMLFormElement::setTarget(const String &value) |
|
674 { |
|
675 setAttribute(targetAttr, value); |
|
676 } |
|
677 |
|
678 PassRefPtr<HTMLGenericFormElement> HTMLFormElement::elementForAlias(const AtomicString& alias) |
|
679 { |
|
680 if (alias.isEmpty() || !m_elementAliases) |
|
681 return 0; |
|
682 return m_elementAliases->get(alias.impl()); |
|
683 } |
|
684 |
|
685 void HTMLFormElement::addElementAlias(HTMLGenericFormElement* element, const AtomicString& alias) |
|
686 { |
|
687 if (alias.isEmpty()) |
|
688 return; |
|
689 if (!m_elementAliases) |
|
690 m_elementAliases = new AliasMap; |
|
691 m_elementAliases->set(alias.impl(), element); |
|
692 } |
|
693 |
|
694 void HTMLFormElement::getNamedElements(const AtomicString& name, Vector<RefPtr<Node> >& namedItems) |
|
695 { |
|
696 elements()->namedItems(name, namedItems); |
|
697 |
|
698 // see if we have seen something with this name before |
|
699 RefPtr<HTMLGenericFormElement> aliasElem; |
|
700 if (aliasElem = elementForAlias(name)) { |
|
701 bool found = false; |
|
702 for (unsigned n = 0; n < namedItems.size(); n++) { |
|
703 if (namedItems[n] == aliasElem.get()) { |
|
704 found = true; |
|
705 break; |
|
706 } |
|
707 } |
|
708 if (!found) |
|
709 // we have seen it before but it is gone now. still, we need to return it. |
|
710 namedItems.append(aliasElem.get()); |
|
711 } |
|
712 // name has been accessed, remember it |
|
713 if (namedItems.size() && aliasElem != namedItems.first()) |
|
714 addElementAlias(static_cast<HTMLGenericFormElement*>(namedItems.first().get()), name); |
|
715 } |
|
716 |
|
717 void HTMLFormElement::CheckedRadioButtons::addButton(HTMLGenericFormElement* element) |
|
718 { |
|
719 // We only want to add radio buttons. |
|
720 if (!element->isRadioButton()) |
|
721 return; |
|
722 |
|
723 // Without a name, there is no group. |
|
724 if (element->name().isEmpty()) |
|
725 return; |
|
726 |
|
727 HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(element); |
|
728 // We only track checked buttons. |
|
729 if (!inputElement->checked()) |
|
730 return; |
|
731 |
|
732 if (!m_nameToCheckedRadioButtonMap) |
|
733 m_nameToCheckedRadioButtonMap.set(new NameToInputMap); |
|
734 else { |
|
735 HTMLInputElement* currentCheckedRadio = m_nameToCheckedRadioButtonMap->get(element->name().impl()); |
|
736 if (currentCheckedRadio && currentCheckedRadio != element) |
|
737 currentCheckedRadio->setChecked(false); |
|
738 } |
|
739 |
|
740 m_nameToCheckedRadioButtonMap->set(element->name().impl(), inputElement); |
|
741 } |
|
742 |
|
743 HTMLInputElement* HTMLFormElement::CheckedRadioButtons::checkedButtonForGroup(const AtomicString& name) const |
|
744 { |
|
745 if (!m_nameToCheckedRadioButtonMap) |
|
746 return 0; |
|
747 |
|
748 return m_nameToCheckedRadioButtonMap->get(name.impl()); |
|
749 } |
|
750 |
|
751 void HTMLFormElement::CheckedRadioButtons::removeButton(HTMLGenericFormElement* element) |
|
752 { |
|
753 if (element->name().isEmpty() || !m_nameToCheckedRadioButtonMap) |
|
754 return; |
|
755 |
|
756 NameToInputMap::iterator it = m_nameToCheckedRadioButtonMap->find(element->name().impl()); |
|
757 if (it == m_nameToCheckedRadioButtonMap->end() || it->second != element) |
|
758 return; |
|
759 |
|
760 ASSERT(element->isRadioButton()); |
|
761 ASSERT(element->isChecked()); |
|
762 |
|
763 m_nameToCheckedRadioButtonMap->remove(it); |
|
764 if (m_nameToCheckedRadioButtonMap->isEmpty()) |
|
765 m_nameToCheckedRadioButtonMap.clear(); |
|
766 } |
|
767 |
|
768 } // namespace |