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.
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.
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.
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.
23 #include "FileInputType.h"
29 #include "FileSystem.h"
30 #include "FormDataList.h"
32 #include "HTMLInputElement.h"
33 #include "HTMLNames.h"
35 #include "LocalizedStrings.h"
36 #include "RenderFileUploadControl.h"
37 #include "ScriptController.h"
38 #include "ShadowRoot.h"
39 #include <wtf/PassOwnPtr.h>
40 #include <wtf/text/WTFString.h>
44 using namespace HTMLNames;
46 class UploadButtonElement : public HTMLInputElement {
48 static PassRefPtr<UploadButtonElement> create(Document*);
49 static PassRefPtr<UploadButtonElement> createForMultiple(Document*);
52 UploadButtonElement(Document*);
54 virtual const AtomicString& shadowPseudoId() const;
57 PassRefPtr<UploadButtonElement> UploadButtonElement::create(Document* document)
59 RefPtr<UploadButtonElement> button = adoptRef(new UploadButtonElement(document));
60 button->setType("button");
61 button->setValue(fileButtonChooseFileLabel());
62 return button.release();
65 PassRefPtr<UploadButtonElement> UploadButtonElement::createForMultiple(Document* document)
67 RefPtr<UploadButtonElement> button = adoptRef(new UploadButtonElement(document));
68 button->setType("button");
69 button->setValue(fileButtonChooseMultipleFilesLabel());
70 return button.release();
73 UploadButtonElement::UploadButtonElement(Document* document)
74 : HTMLInputElement(inputTag, document, 0, false)
78 const AtomicString& UploadButtonElement::shadowPseudoId() const
80 DEFINE_STATIC_LOCAL(AtomicString, pseudoId, ("-webkit-file-upload-button"));
84 inline FileInputType::FileInputType(HTMLInputElement* element)
85 : BaseButtonInputType(element)
86 , m_fileList(FileList::create())
90 PassOwnPtr<InputType> FileInputType::create(HTMLInputElement* element)
92 return adoptPtr(new FileInputType(element));
95 const AtomicString& FileInputType::formControlType() const
97 return InputTypeNames::file();
100 bool FileInputType::appendFormData(FormDataList& encoding, bool multipart) const
102 FileList* fileList = element()->files();
103 unsigned numFiles = fileList->length();
105 // Send only the basenames.
106 // 4.10.16.4 and 4.10.16.6 sections in HTML5.
108 // Unlike the multipart case, we have no special handling for the empty
109 // fileList because Netscape doesn't support for non-multipart
110 // submission of file inputs, and Firefox doesn't add "name=" query
112 for (unsigned i = 0; i < numFiles; ++i)
113 encoding.appendData(element()->name(), fileList->item(i)->fileName());
117 // If no filename at all is entered, return successful but empty.
118 // Null would be more logical, but Netscape posts an empty file. Argh.
120 encoding.appendBlob(element()->name(), File::create(""));
124 for (unsigned i = 0; i < numFiles; ++i)
125 encoding.appendBlob(element()->name(), fileList->item(i));
129 bool FileInputType::valueMissing(const String& value) const
131 return value.isEmpty();
134 String FileInputType::valueMissingText() const
136 return element()->multiple() ? validationMessageValueMissingForMultipleFileText() : validationMessageValueMissingForFileText();
139 void FileInputType::handleDOMActivateEvent(Event* event)
141 if (element()->disabled() || !element()->renderer())
144 if (!ScriptController::processingUserGesture())
147 if (Chrome* chrome = this->chrome()) {
148 FileChooserSettings settings;
149 HTMLInputElement* input = element();
150 #if ENABLE(DIRECTORY_UPLOAD)
151 settings.allowsDirectoryUpload = input->fastHasAttribute(webkitdirectoryAttr);
152 settings.allowsMultipleFiles = settings.allowsDirectoryUpload || input->fastHasAttribute(multipleAttr);
154 settings.allowsMultipleFiles = input->fastHasAttribute(multipleAttr);
156 settings.acceptTypes = input->accept();
157 settings.selectedFiles = m_fileList->paths();
158 chrome->runOpenPanel(input->document()->frame(), newFileChooser(settings));
160 event->setDefaultHandled();
163 RenderObject* FileInputType::createRenderer(RenderArena* arena, RenderStyle*) const
165 return new (arena) RenderFileUploadControl(element());
168 bool FileInputType::canSetStringValue() const
173 bool FileInputType::canChangeFromAnotherType() const
175 // Don't allow the type to be changed to file after the first type change.
176 // In other engines this might mean a JavaScript programmer could set a text
177 // field's value to something like /etc/passwd and then change it to a file input.
178 // I don't think this would actually occur in WebKit, but this rule still may be
179 // important for compatibility.
183 FileList* FileInputType::files()
185 return m_fileList.get();
188 bool FileInputType::canSetValue(const String& value)
190 // For security reasons, we don't allow setting the filename, but we do allow clearing it.
191 // The HTML5 spec (as of the 10/24/08 working draft) says that the value attribute isn't
192 // applicable to the file upload control at all, but for now we are keeping this behavior
193 // to avoid breaking existing websites that may be relying on this.
194 return value.isEmpty();
197 bool FileInputType::getTypeSpecificValue(String& value)
199 if (m_fileList->isEmpty()) {
204 // HTML5 tells us that we're supposed to use this goofy value for
205 // file input controls. Historically, browsers revealed the real
206 // file path, but that's a privacy problem. Code on the web
207 // decided to try to parse the value by looking for backslashes
208 // (because that's what Windows file paths use). To be compatible
209 // with that code, we make up a fake path for the file.
210 value = "C:\\fakepath\\" + m_fileList->item(0)->fileName();
214 bool FileInputType::storesValueSeparateFromAttribute()
219 void FileInputType::setValue(const String&, bool, bool)
223 element()->setNeedsStyleRecalc();
226 void FileInputType::setFileList(const Vector<String>& paths)
229 size_t size = paths.size();
231 #if ENABLE(DIRECTORY_UPLOAD)
232 // If a directory is being selected, the UI allows a directory to be chosen
233 // and the paths provided here share a root directory somewhere up the tree;
234 // we want to store only the relative paths from that point.
235 if (size && element()->fastHasAttribute(webkitdirectoryAttr)) {
236 // Find the common root path.
237 String rootPath = directoryName(paths[0]);
238 for (size_t i = 1; i < size; i++) {
239 while (!paths[i].startsWith(rootPath))
240 rootPath = directoryName(rootPath);
242 rootPath = directoryName(rootPath);
243 ASSERT(rootPath.length());
244 for (size_t i = 0; i < size; i++) {
245 // Normalize backslashes to slashes before exposing the relative path to script.
246 String relativePath = paths[i].substring(1 + rootPath.length()).replace('\\', '/');
247 m_fileList->append(File::createWithRelativePath(paths[i], relativePath));
253 for (size_t i = 0; i < size; i++)
254 m_fileList->append(File::create(paths[i]));
257 bool FileInputType::isFileUpload() const
262 void FileInputType::createShadowSubtree()
264 ExceptionCode ec = 0;
265 element()->ensureShadowRoot()->appendChild(element()->multiple() ? UploadButtonElement::createForMultiple(element()->document()): UploadButtonElement::create(element()->document()), ec);
268 void FileInputType::multipleAttributeChanged()
270 UploadButtonElement* button = static_cast<UploadButtonElement*>(element()->ensureShadowRoot()->firstChild());
272 button->setValue(element()->multiple() ? fileButtonChooseMultipleFilesLabel() : fileButtonChooseFileLabel());
275 void FileInputType::requestIcon(const Vector<String>& paths)
280 if (Chrome* chrome = this->chrome())
281 chrome->loadIconForFiles(paths, newFileIconLoader());
284 void FileInputType::filesChosen(const Vector<String>& paths)
286 RefPtr<HTMLInputElement> input = element();
288 bool pathsChanged = false;
289 if (paths.size() != m_fileList->length())
292 for (unsigned i = 0; i < paths.size(); ++i) {
293 if (paths[i] != m_fileList->item(i)->path()) {
302 input->setFormControlValueMatchesRenderer(true);
303 input->notifyFormStateChanged();
304 input->setNeedsValidityCheck();
308 if (input->renderer())
309 input->renderer()->repaint();
312 // This call may cause destruction of this instance.
313 // input instance is safe since it is ref-counted.
314 input->HTMLElement::dispatchChangeEvent();
316 input->setChangedSinceLastFormControlChangeEvent(false);
319 #if ENABLE(DIRECTORY_UPLOAD)
320 void FileInputType::receiveDropForDirectoryUpload(const Vector<String>& paths)
322 if (Chrome* chrome = this->chrome()) {
323 FileChooserSettings settings;
324 settings.allowsDirectoryUpload = true;
325 settings.allowsMultipleFiles = true;
326 settings.selectedFiles.append(paths[0]);
327 chrome->enumerateChosenDirectory(newFileChooser(settings));
332 void FileInputType::updateRendering(PassRefPtr<Icon> icon)
338 if (element()->renderer())
339 element()->renderer()->repaint();
342 void FileInputType::receiveDroppedFiles(const Vector<String>& paths)
344 HTMLInputElement* input = element();
345 #if ENABLE(DIRECTORY_UPLOAD)
346 if (input->fastHasAttribute(webkitdirectoryAttr)) {
347 receiveDropForDirectoryUpload(paths);
352 if (input->fastHasAttribute(multipleAttr))
355 Vector<String> firstPathOnly;
356 firstPathOnly.append(paths[0]);
357 filesChosen(firstPathOnly);
361 Icon* FileInputType::icon() const
366 } // namespace WebCore