1/*
2 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All rights reserved.
3 * Copyright (C) 2010 Google Inc. All rights reserved.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public License
16 * along with this library; see the file COPYING.LIB.  If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 *
20 */
21
22#include "config.h"
23#include "FileInputType.h"
24
25#include "Event.h"
26#include "File.h"
27#include "FileList.h"
28#include "FileSystem.h"
29#include "FormDataList.h"
30#include "HTMLInputElement.h"
31#include "HTMLNames.h"
32#include "LocalizedStrings.h"
33#include "RenderFileUploadControl.h"
34#include <wtf/PassOwnPtr.h>
35#include <wtf/text/WTFString.h>
36
37namespace WebCore {
38
39using namespace HTMLNames;
40
41inline FileInputType::FileInputType(HTMLInputElement* element)
42    : BaseButtonInputType(element)
43    , m_fileList(FileList::create())
44{
45}
46
47PassOwnPtr<InputType> FileInputType::create(HTMLInputElement* element)
48{
49    return adoptPtr(new FileInputType(element));
50}
51
52const AtomicString& FileInputType::formControlType() const
53{
54    return InputTypeNames::file();
55}
56
57bool FileInputType::appendFormData(FormDataList& encoding, bool multipart) const
58{
59    FileList* fileList = element()->files();
60    unsigned numFiles = fileList->length();
61    if (!multipart) {
62        // Send only the basenames.
63        // 4.10.16.4 and 4.10.16.6 sections in HTML5.
64
65        // Unlike the multipart case, we have no special handling for the empty
66        // fileList because Netscape doesn't support for non-multipart
67        // submission of file inputs, and Firefox doesn't add "name=" query
68        // parameter.
69        for (unsigned i = 0; i < numFiles; ++i)
70            encoding.appendData(element()->name(), fileList->item(i)->fileName());
71        return true;
72    }
73
74    // If no filename at all is entered, return successful but empty.
75    // Null would be more logical, but Netscape posts an empty file. Argh.
76    if (!numFiles) {
77        encoding.appendBlob(element()->name(), File::create(""));
78        return true;
79    }
80
81    for (unsigned i = 0; i < numFiles; ++i)
82        encoding.appendBlob(element()->name(), fileList->item(i));
83    return true;
84}
85
86bool FileInputType::valueMissing(const String& value) const
87{
88    return value.isEmpty();
89}
90
91String FileInputType::valueMissingText() const
92{
93    return element()->multiple() ? validationMessageValueMissingForMultipleFileText() : validationMessageValueMissingForFileText();
94}
95
96void FileInputType::handleDOMActivateEvent(Event* event)
97{
98    if (element()->disabled() || !element()->renderer())
99        return;
100    toRenderFileUploadControl(element()->renderer())->click();
101    event->setDefaultHandled();
102}
103
104RenderObject* FileInputType::createRenderer(RenderArena* arena, RenderStyle*) const
105{
106    return new (arena) RenderFileUploadControl(element());
107}
108
109bool FileInputType::canSetStringValue() const
110{
111    return false;
112}
113
114bool FileInputType::canChangeFromAnotherType() const
115{
116    // Don't allow the type to be changed to file after the first type change.
117    // In other engines this might mean a JavaScript programmer could set a text
118    // field's value to something like /etc/passwd and then change it to a file input.
119    // I don't think this would actually occur in WebKit, but this rule still may be
120    // important for compatibility.
121    return false;
122}
123
124FileList* FileInputType::files()
125{
126    return m_fileList.get();
127}
128
129bool FileInputType::canSetValue(const String& value)
130{
131    // For security reasons, we don't allow setting the filename, but we do allow clearing it.
132    // The HTML5 spec (as of the 10/24/08 working draft) says that the value attribute isn't
133    // applicable to the file upload control at all, but for now we are keeping this behavior
134    // to avoid breaking existing websites that may be relying on this.
135    return value.isEmpty();
136}
137
138bool FileInputType::getTypeSpecificValue(String& value)
139{
140    if (m_fileList->isEmpty()) {
141        value = String();
142        return true;
143    }
144
145    // HTML5 tells us that we're supposed to use this goofy value for
146    // file input controls. Historically, browsers revealed the real
147    // file path, but that's a privacy problem. Code on the web
148    // decided to try to parse the value by looking for backslashes
149    // (because that's what Windows file paths use). To be compatible
150    // with that code, we make up a fake path for the file.
151    value = "C:\\fakepath\\" + m_fileList->item(0)->fileName();
152    return true;
153}
154
155bool FileInputType::storesValueSeparateFromAttribute()
156{
157    return true;
158}
159
160void FileInputType::setFileList(const Vector<String>& paths)
161{
162    m_fileList->clear();
163    size_t size = paths.size();
164
165#if ENABLE(DIRECTORY_UPLOAD)
166    // If a directory is being selected, the UI allows a directory to be chosen
167    // and the paths provided here share a root directory somewhere up the tree;
168    // we want to store only the relative paths from that point.
169    if (size && element()->fastHasAttribute(webkitdirectoryAttr)) {
170        // Find the common root path.
171        String rootPath = directoryName(paths[0]);
172        for (size_t i = 1; i < size; i++) {
173            while (!paths[i].startsWith(rootPath))
174                rootPath = directoryName(rootPath);
175        }
176        rootPath = directoryName(rootPath);
177        ASSERT(rootPath.length());
178        for (size_t i = 0; i < size; i++) {
179            // Normalize backslashes to slashes before exposing the relative path to script.
180            String relativePath = paths[i].substring(1 + rootPath.length()).replace('\\', '/');
181            m_fileList->append(File::create(relativePath, paths[i]));
182        }
183        return;
184    }
185#endif
186
187    for (size_t i = 0; i < size; i++)
188        m_fileList->append(File::create(paths[i]));
189}
190
191bool FileInputType::isFileUpload() const
192{
193    return true;
194}
195
196} // namespace WebCore
197