2008-10-14  Darin Adler  <darin@apple.com>

        Reviewed by Sam Weinig (all but the FormDataListItem rename).

        - https://bugs.webkit.org/show_bug.cgi?id=21593
          Bug 21593: add multiple-file support to HTMLFormElement

        * html/FormDataList.cpp: Removed appendFile since it's now inline.
        Also took incorrect old copyrights off of this file that are left
        over from when it was split.
        * html/FormDataList.h: Ditto. Renamed FormDataListItem to Item and
        made it a member of FormDataList. Changed it to hold a File object
        instead of a path. And made its data members private. Changed
        FormDataList::appendFile accordingly.

        * html/HTMLFormElement.cpp:
        (WebCore::HTMLFormElement::formData): Updated for above changes.
        Removed the code that special-cases HTMLInputElement, because now
        the FormDataListItem has everything we need. And if there are
        multiple files, we'll get multiple list items for them.

        * html/HTMLInputElement.cpp:
        (WebCore::HTMLInputElement::appendFormData): Changed the code for
        <input type=file> to use File objects instead of path strings and
        also to append multiple files if the file list has them.



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@37589 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/WebCore/ChangeLog b/WebCore/ChangeLog
index c3a5fcd..a096ab4 100644
--- a/WebCore/ChangeLog
+++ b/WebCore/ChangeLog
@@ -1,3 +1,29 @@
+2008-10-14  Darin Adler  <darin@apple.com>
+
+        Reviewed by Sam Weinig (all but the FormDataListItem rename).
+
+        - https://bugs.webkit.org/show_bug.cgi?id=21593
+          Bug 21593: add multiple-file support to HTMLFormElement
+
+        * html/FormDataList.cpp: Removed appendFile since it's now inline.
+        Also took incorrect old copyrights off of this file that are left
+        over from when it was split.
+        * html/FormDataList.h: Ditto. Renamed FormDataListItem to Item and
+        made it a member of FormDataList. Changed it to hold a File object
+        instead of a path. And made its data members private. Changed
+        FormDataList::appendFile accordingly.
+
+        * html/HTMLFormElement.cpp:
+        (WebCore::HTMLFormElement::formData): Updated for above changes.
+        Removed the code that special-cases HTMLInputElement, because now
+        the FormDataListItem has everything we need. And if there are
+        multiple files, we'll get multiple list items for them.
+
+        * html/HTMLInputElement.cpp:
+        (WebCore::HTMLInputElement::appendFormData): Changed the code for
+        <input type=file> to use File objects instead of path strings and
+        also to append multiple files if the file list has them.
+
 2008-10-14  Tor Arne Vestbø  <tavestbo@trolltech.com>
 
         Reviewed by Simon.
diff --git a/WebCore/html/FormDataList.cpp b/WebCore/html/FormDataList.cpp
index 15ca9a8..281c9fe 100644
--- a/WebCore/html/FormDataList.cpp
+++ b/WebCore/html/FormDataList.cpp
@@ -1,11 +1,5 @@
 /*
- * This file is part of the DOM implementation for KDE.
- *
- * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
- *           (C) 1999 Antti Koivisto (koivisto@kde.org)
- *           (C) 2001 Dirk Mueller (mueller@kde.org)
- * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
- *           (C) 2006 Alexey Proskuryakov (ap@nypop.com)
+ * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -40,11 +34,11 @@
 }
 
 // Change plain CR and plain LF to CRLF pairs.
-static CString fixLineBreaks(const CString &s)
+static CString fixLineBreaks(const CString& s)
 {
     // Compute the length.
     unsigned newLen = 0;
-    const char *p = s.data();
+    const char* p = s.data();
     while (char c = *p++) {
         if (c == '\r') {
             // Safe to look ahead because of trailing '\0'.
@@ -66,7 +60,7 @@
     
     // Make a copy of the string.
     p = s.data();
-    char *q;
+    char* q;
     CString result = CString::newUninitialized(newLen, q);
     while (char c = *p++) {
         if (c == '\r') {
@@ -94,10 +88,4 @@
     m_list.append(cstr);
 }
 
-void FormDataList::appendFile(const String& key, const String& filename)
-{
-    appendString(key);
-    m_list.append(filename);
-}
-
 } // namespace
diff --git a/WebCore/html/FormDataList.h b/WebCore/html/FormDataList.h
index cdff8f7..aec1a52 100644
--- a/WebCore/html/FormDataList.h
+++ b/WebCore/html/FormDataList.h
@@ -1,11 +1,5 @@
 /*
- * This file is part of the DOM implementation for KDE.
- *
- * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
- *           (C) 1999 Antti Koivisto (koivisto@kde.org)
- *           (C) 2001 Dirk Mueller (mueller@kde.org)
- * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
- *           (C) 2006 Alexey Proskuryakov (ap@nypop.com)
+ * Copyright (C) 2005, 2006, 2008 Apple Inc. All rights reserved.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -28,21 +22,11 @@
 #define FormDataList_h
 
 #include "CString.h"
-#include "PlatformString.h"
+#include "File.h"
 #include "TextEncoding.h"
-#include <wtf/Vector.h>
 
 namespace WebCore {
 
-struct FormDataListItem {
-    FormDataListItem() { }
-    FormDataListItem(const CString& data) : m_data(data) { }
-    FormDataListItem(const String& path) : m_path(path) { }
-
-    String m_path;
-    CString m_data;
-};
-
 class FormDataList {
 public:
     FormDataList(const TextEncoding&);
@@ -53,16 +37,31 @@
         { appendString(key); appendString(value); }
     void appendData(const String& key, int value)
         { appendString(key); appendString(String::number(value)); }
-    void appendFile(const String& key, const String& filename);
+    void appendFile(const String& key, PassRefPtr<File> file)
+        { appendString(key); m_list.append(file); }
 
-    const Vector<FormDataListItem>& list() const { return m_list; }
+    class Item {
+    public:
+        Item() { }
+        Item(const CString& data) : m_data(data) { }
+        Item(PassRefPtr<File> file) : m_file(file) { }
+
+        const CString& data() const { return m_data; }
+        File* file() const { return m_file.get(); }
+
+    private:
+        CString m_data;
+        RefPtr<File> m_file;
+    };
+
+    const Vector<Item>& list() const { return m_list; }
 
 private:
     void appendString(const CString&);
     void appendString(const String&);
 
     TextEncoding m_encoding;
-    Vector<FormDataListItem> m_list;
+    Vector<Item> m_list;
 };
 
 } // namespace WebCore
diff --git a/WebCore/html/HTMLFormElement.cpp b/WebCore/html/HTMLFormElement.cpp
index 1a83d3a..a691d7f 100644
--- a/WebCore/html/HTMLFormElement.cpp
+++ b/WebCore/html/HTMLFormElement.cpp
@@ -248,18 +248,18 @@
         if (!control->disabled() && control->appendFormData(list, m_multipart)) {
             size_t ln = list.list().size();
             for (size_t j = 0; j < ln; ++j) {
-                const FormDataListItem& item = list.list()[j];
+                const FormDataList::Item& item = list.list()[j];
                 if (!m_multipart) {
                     // 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() && item.m_data == "isindex")
-                        appendEncodedString(encodedData, list.list()[++j].m_data);
+                    if (encodedData.isEmpty() && item.data() == "isindex")
+                        appendEncodedString(encodedData, list.list()[++j].data());
                     else {
                         if (!encodedData.isEmpty())
                             encodedData.append('&');
-                        appendEncodedString(encodedData, item.m_data);
+                        appendEncodedString(encodedData, item.data());
                         encodedData.append('=');
-                        appendEncodedString(encodedData, list.list()[++j].m_data);
+                        appendEncodedString(encodedData, list.list()[++j].data());
                     }
                 } else {
                     Vector<char> header;
@@ -267,30 +267,24 @@
                     appendString(header, boundary);
                     appendString(header, "\r\n");
                     appendString(header, "Content-Disposition: form-data; name=\"");
-                    header.append(item.m_data.data(), item.m_data.length());
+                    header.append(item.data().data(), item.data().length());
                     header.append('"');
 
                     bool shouldGenerateFile = false;
                     // if the current type is FILE, then we also need to
                     // include the filename
-                    if (control->hasLocalName(inputTag)
-                            && static_cast<HTMLInputElement*>(control)->inputType() == HTMLInputElement::FILE) {
-                        String path;
-                        String filename;
-                        const FileList* files = static_cast<HTMLInputElement*>(control)->files();
-                        if (!files->isEmpty()) {
-                            // NOTE: We currently only handle one file; some day we'll add multiple file support.
-                            const File* file = files->item(0);
-                            path = file->path();
-                            filename = file->fileName();
-                        }
+                    if (item.file()) {
+                        const String& path = item.file()->path();
+                        String filename = item.file()->fileName();
 
                         // Let the application specify a filename if it's going to generate a replacement file for the upload.
-                        if (Page* page = document()->page()) {
-                            String generatedFilename;
-                            shouldGenerateFile = page->chrome()->client()->shouldReplaceWithGeneratedFileForUpload(path, generatedFilename);
-                            if (shouldGenerateFile)
-                                filename = generatedFilename;
+                        if (!path.isEmpty()) {
+                            if (Page* page = document()->page()) {
+                                String generatedFilename;
+                                shouldGenerateFile = page->chrome()->client()->shouldReplaceWithGeneratedFileForUpload(path, generatedFilename);
+                                if (shouldGenerateFile)
+                                    filename = generatedFilename;
+                            }
                         }
 
                         // FIXME: This won't work if the filename includes a " mark,
@@ -302,9 +296,10 @@
                         header.append('"');
 
                         if (!filename.isEmpty()) {
-                            // FIXME: This 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.
+                            // 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: ");
@@ -317,11 +312,11 @@
 
                     // append body
                     result->appendData(header.data(), header.size());
-                    const FormDataListItem& item = list.list()[j + 1];
-                    if (size_t dataSize = item.m_data.length())
-                        result->appendData(item.m_data.data(), dataSize);
-                    else if (!item.m_path.isEmpty())
-                        result->appendFile(item.m_path, shouldGenerateFile);
+                    const FormDataList::Item& item = list.list()[j + 1];
+                    if (size_t dataSize = item.data().length())
+                        result->appendData(item.data().data(), dataSize);
+                    else if (item.file() && !item.file()->path().isEmpty())
+                        result->appendFile(item.file()->path(), shouldGenerateFile);
                     result->appendData("\r\n", 2);
 
                     ++j;
diff --git a/WebCore/html/HTMLInputElement.cpp b/WebCore/html/HTMLInputElement.cpp
index 4963b83..93791a2 100644
--- a/WebCore/html/HTMLInputElement.cpp
+++ b/WebCore/html/HTMLInputElement.cpp
@@ -2,7 +2,7 @@
  * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
  *           (C) 1999 Antti Koivisto (koivisto@kde.org)
  *           (C) 2001 Dirk Mueller (mueller@kde.org)
- * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved.
+ * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
  *           (C) 2006 Alexey Proskuryakov (ap@nypop.com)
  * Copyright (C) 2007 Samuel Weinig (sam@webkit.org)
  *
@@ -868,20 +868,23 @@
             }
             break;
 
-        case FILE:
+        case FILE: {
             // Can't submit file on GET.
             if (!multipart)
                 return false;
 
             // If no filename at all is entered, return successful but empty.
             // Null would be more logical, but Netscape posts an empty file. Argh.
-            if (m_fileList->isEmpty()) {
-                encoding.appendData(name(), String(""));
+            unsigned numFiles = m_fileList->length();
+            if (!numFiles) {
+                encoding.appendFile(name(), File::create(""));
                 return true;
             }
 
-            encoding.appendFile(name(), m_fileList->item(0)->path());
+            for (unsigned i = 0; i < numFiles; ++i)
+                encoding.appendFile(name(), m_fileList->item(i));
             return true;
+        }
     }
     return false;
 }