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