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
37 namespace WebCore {
38
39 using namespace HTMLNames;
40
FileInputType(HTMLInputElement * element)41 inline FileInputType::FileInputType(HTMLInputElement* element)
42 : BaseButtonInputType(element)
43 , m_fileList(FileList::create())
44 {
45 }
46
create(HTMLInputElement * element)47 PassOwnPtr<InputType> FileInputType::create(HTMLInputElement* element)
48 {
49 return adoptPtr(new FileInputType(element));
50 }
51
formControlType() const52 const AtomicString& FileInputType::formControlType() const
53 {
54 return InputTypeNames::file();
55 }
56
appendFormData(FormDataList & encoding,bool multipart) const57 bool 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
valueMissing(const String & value) const86 bool FileInputType::valueMissing(const String& value) const
87 {
88 return value.isEmpty();
89 }
90
valueMissingText() const91 String FileInputType::valueMissingText() const
92 {
93 return element()->multiple() ? validationMessageValueMissingForMultipleFileText() : validationMessageValueMissingForFileText();
94 }
95
handleDOMActivateEvent(Event * event)96 void FileInputType::handleDOMActivateEvent(Event* event)
97 {
98 if (element()->disabled() || !element()->renderer())
99 return;
100 toRenderFileUploadControl(element()->renderer())->click();
101 event->setDefaultHandled();
102 }
103
createRenderer(RenderArena * arena,RenderStyle *) const104 RenderObject* FileInputType::createRenderer(RenderArena* arena, RenderStyle*) const
105 {
106 return new (arena) RenderFileUploadControl(element());
107 }
108
canSetStringValue() const109 bool FileInputType::canSetStringValue() const
110 {
111 return false;
112 }
113
canChangeFromAnotherType() const114 bool 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
files()124 FileList* FileInputType::files()
125 {
126 return m_fileList.get();
127 }
128
canSetValue(const String & value)129 bool 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
getTypeSpecificValue(String & value)138 bool 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
storesValueSeparateFromAttribute()155 bool FileInputType::storesValueSeparateFromAttribute()
156 {
157 return true;
158 }
159
setFileList(const Vector<String> & paths)160 void 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
isFileUpload() const191 bool FileInputType::isFileUpload() const
192 {
193 return true;
194 }
195
196 } // namespace WebCore
197