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