Home | History | Annotate | Download | only in filesystem
      1 /*
      2  * Copyright (C) 2010 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions are
      6  * met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *     * Redistributions in binary form must reproduce the above
     11  * copyright notice, this list of conditions and the following disclaimer
     12  * in the documentation and/or other materials provided with the
     13  * distribution.
     14  *     * Neither the name of Google Inc. nor the names of its
     15  * contributors may be used to endorse or promote products derived from
     16  * this software without specific prior written permission.
     17  *
     18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     29  */
     30 
     31 #include "config.h"
     32 #include "modules/filesystem/DOMFileSystemBase.h"
     33 
     34 #include "core/dom/ExecutionContext.h"
     35 #include "core/fileapi/FileError.h"
     36 #include "core/html/VoidCallback.h"
     37 #include "modules/filesystem/DOMFilePath.h"
     38 #include "modules/filesystem/DirectoryEntry.h"
     39 #include "modules/filesystem/DirectoryReaderBase.h"
     40 #include "modules/filesystem/EntriesCallback.h"
     41 #include "modules/filesystem/Entry.h"
     42 #include "modules/filesystem/EntryBase.h"
     43 #include "modules/filesystem/EntryCallback.h"
     44 #include "modules/filesystem/ErrorCallback.h"
     45 #include "modules/filesystem/FileSystemCallbacks.h"
     46 #include "modules/filesystem/MetadataCallback.h"
     47 #include "platform/weborigin/SecurityOrigin.h"
     48 #include "public/platform/Platform.h"
     49 #include "public/platform/WebFileSystem.h"
     50 #include "public/platform/WebFileSystemCallbacks.h"
     51 #include "wtf/OwnPtr.h"
     52 #include "wtf/text/StringBuilder.h"
     53 
     54 namespace WebCore {
     55 
     56 const char DOMFileSystemBase::persistentPathPrefix[] = "persistent";
     57 const char DOMFileSystemBase::temporaryPathPrefix[] = "temporary";
     58 const char DOMFileSystemBase::isolatedPathPrefix[] = "isolated";
     59 const char DOMFileSystemBase::externalPathPrefix[] = "external";
     60 
     61 DOMFileSystemBase::DOMFileSystemBase(ExecutionContext* context, const String& name, FileSystemType type, const KURL& rootURL)
     62     : m_context(context)
     63     , m_name(name)
     64     , m_type(type)
     65     , m_filesystemRootURL(rootURL)
     66     , m_clonable(false)
     67 {
     68 }
     69 
     70 DOMFileSystemBase::~DOMFileSystemBase()
     71 {
     72 }
     73 
     74 blink::WebFileSystem* DOMFileSystemBase::fileSystem() const
     75 {
     76     return blink::Platform::current()->fileSystem();
     77 }
     78 
     79 SecurityOrigin* DOMFileSystemBase::securityOrigin() const
     80 {
     81     return m_context->securityOrigin();
     82 }
     83 
     84 bool DOMFileSystemBase::isValidType(FileSystemType type)
     85 {
     86     return type == FileSystemTypeTemporary || type == FileSystemTypePersistent || type == FileSystemTypeIsolated || type == FileSystemTypeExternal;
     87 }
     88 
     89 bool DOMFileSystemBase::crackFileSystemURL(const KURL& url, FileSystemType& type, String& filePath)
     90 {
     91     if (!url.protocolIs("filesystem"))
     92         return false;
     93 
     94     if (!url.innerURL())
     95         return false;
     96 
     97     String typeString = url.innerURL()->path().substring(1);
     98     if (!pathPrefixToFileSystemType(typeString, type))
     99         return false;
    100 
    101     filePath = decodeURLEscapeSequences(url.path());
    102     return true;
    103 }
    104 
    105 KURL DOMFileSystemBase::createFileSystemRootURL(const String& origin, FileSystemType type)
    106 {
    107     String typeString;
    108     if (type == FileSystemTypeTemporary)
    109         typeString = temporaryPathPrefix;
    110     else if (type == FileSystemTypePersistent)
    111         typeString = persistentPathPrefix;
    112     else if (type == FileSystemTypeExternal)
    113         typeString = externalPathPrefix;
    114     else
    115         return KURL();
    116 
    117     String result = "filesystem:" + origin + "/" + typeString + "/";
    118     return KURL(ParsedURLString, result);
    119 }
    120 
    121 bool DOMFileSystemBase::supportsToURL() const
    122 {
    123     ASSERT(isValidType(m_type));
    124     return m_type != FileSystemTypeIsolated;
    125 }
    126 
    127 KURL DOMFileSystemBase::createFileSystemURL(const EntryBase* entry) const
    128 {
    129     return createFileSystemURL(entry->fullPath());
    130 }
    131 
    132 KURL DOMFileSystemBase::createFileSystemURL(const String& fullPath) const
    133 {
    134     ASSERT(DOMFilePath::isAbsolute(fullPath));
    135 
    136     if (type() == FileSystemTypeExternal) {
    137         // For external filesystem originString could be different from what we have in m_filesystemRootURL.
    138         StringBuilder result;
    139         result.append("filesystem:");
    140         result.append(securityOrigin()->toString());
    141         result.append("/");
    142         result.append(externalPathPrefix);
    143         result.append(m_filesystemRootURL.path());
    144         // Remove the extra leading slash.
    145         result.append(encodeWithURLEscapeSequences(fullPath.substring(1)));
    146         return KURL(ParsedURLString, result.toString());
    147     }
    148 
    149     // For regular types we can just append the entry's fullPath to the m_filesystemRootURL that should look like 'filesystem:<origin>/<typePrefix>'.
    150     ASSERT(!m_filesystemRootURL.isEmpty());
    151     KURL url = m_filesystemRootURL;
    152     // Remove the extra leading slash.
    153     url.setPath(url.path() + encodeWithURLEscapeSequences(fullPath.substring(1)));
    154     return url;
    155 }
    156 
    157 bool DOMFileSystemBase::pathToAbsolutePath(FileSystemType type, const EntryBase* base, String path, String& absolutePath)
    158 {
    159     ASSERT(base);
    160 
    161     if (!DOMFilePath::isAbsolute(path))
    162         path = DOMFilePath::append(base->fullPath(), path);
    163     absolutePath = DOMFilePath::removeExtraParentReferences(path);
    164 
    165     return (type != FileSystemTypeTemporary && type != FileSystemTypePersistent) || DOMFilePath::isValidPath(absolutePath);
    166 }
    167 
    168 bool DOMFileSystemBase::pathPrefixToFileSystemType(const String& pathPrefix, FileSystemType& type)
    169 {
    170     if (pathPrefix == temporaryPathPrefix) {
    171         type = FileSystemTypeTemporary;
    172         return true;
    173     }
    174 
    175     if (pathPrefix == persistentPathPrefix) {
    176         type = FileSystemTypePersistent;
    177         return true;
    178     }
    179 
    180     if (pathPrefix == externalPathPrefix) {
    181         type = FileSystemTypeExternal;
    182         return true;
    183     }
    184 
    185     return false;
    186 }
    187 
    188 void DOMFileSystemBase::getMetadata(const EntryBase* entry, PassOwnPtr<MetadataCallback> successCallback, PassOwnPtr<ErrorCallback> errorCallback, SynchronousType synchronousType)
    189 {
    190     OwnPtr<AsyncFileSystemCallbacks> callbacks(MetadataCallbacks::create(successCallback, errorCallback, this));
    191     callbacks->setShouldBlockUntilCompletion(synchronousType == Synchronous);
    192     fileSystem()->readMetadata(createFileSystemURL(entry), callbacks.release());
    193 }
    194 
    195 static bool verifyAndGetDestinationPathForCopyOrMove(const EntryBase* source, EntryBase* parent, const String& newName, String& destinationPath)
    196 {
    197     ASSERT(source);
    198 
    199     if (!parent || !parent->isDirectory())
    200         return false;
    201 
    202     if (!newName.isEmpty() && !DOMFilePath::isValidName(newName))
    203         return false;
    204 
    205     const bool isSameFileSystem = (*source->filesystem() == *parent->filesystem());
    206 
    207     // It is an error to try to copy or move an entry inside itself at any depth if it is a directory.
    208     if (source->isDirectory() && isSameFileSystem && DOMFilePath::isParentOf(source->fullPath(), parent->fullPath()))
    209         return false;
    210 
    211     // It is an error to copy or move an entry into its parent if a name different from its current one isn't provided.
    212     if (isSameFileSystem && (newName.isEmpty() || source->name() == newName) && DOMFilePath::getDirectory(source->fullPath()) == parent->fullPath())
    213         return false;
    214 
    215     destinationPath = parent->fullPath();
    216     if (!newName.isEmpty())
    217         destinationPath = DOMFilePath::append(destinationPath, newName);
    218     else
    219         destinationPath = DOMFilePath::append(destinationPath, source->name());
    220 
    221     return true;
    222 }
    223 
    224 void DOMFileSystemBase::move(const EntryBase* source, EntryBase* parent, const String& newName, PassOwnPtr<EntryCallback> successCallback, PassOwnPtr<ErrorCallback> errorCallback, SynchronousType synchronousType)
    225 {
    226     String destinationPath;
    227     if (!verifyAndGetDestinationPathForCopyOrMove(source, parent, newName, destinationPath)) {
    228         reportError(errorCallback, FileError::create(FileError::INVALID_MODIFICATION_ERR));
    229         return;
    230     }
    231 
    232     OwnPtr<AsyncFileSystemCallbacks> callbacks(EntryCallbacks::create(successCallback, errorCallback, parent->filesystem(), destinationPath, source->isDirectory()));
    233     callbacks->setShouldBlockUntilCompletion(synchronousType == Synchronous);
    234 
    235     fileSystem()->move(createFileSystemURL(source), parent->filesystem()->createFileSystemURL(destinationPath), callbacks.release());
    236 }
    237 
    238 void DOMFileSystemBase::copy(const EntryBase* source, EntryBase* parent, const String& newName, PassOwnPtr<EntryCallback> successCallback, PassOwnPtr<ErrorCallback> errorCallback, SynchronousType synchronousType)
    239 {
    240     String destinationPath;
    241     if (!verifyAndGetDestinationPathForCopyOrMove(source, parent, newName, destinationPath)) {
    242         reportError(errorCallback, FileError::create(FileError::INVALID_MODIFICATION_ERR));
    243         return;
    244     }
    245 
    246     OwnPtr<AsyncFileSystemCallbacks> callbacks(EntryCallbacks::create(successCallback, errorCallback, parent->filesystem(), destinationPath, source->isDirectory()));
    247     callbacks->setShouldBlockUntilCompletion(synchronousType == Synchronous);
    248 
    249     fileSystem()->copy(createFileSystemURL(source), parent->filesystem()->createFileSystemURL(destinationPath), callbacks.release());
    250 }
    251 
    252 void DOMFileSystemBase::remove(const EntryBase* entry, PassOwnPtr<VoidCallback> successCallback, PassOwnPtr<ErrorCallback> errorCallback, SynchronousType synchronousType)
    253 {
    254     ASSERT(entry);
    255     // We don't allow calling remove() on the root directory.
    256     if (entry->fullPath() == String(DOMFilePath::root)) {
    257         reportError(errorCallback, FileError::create(FileError::INVALID_MODIFICATION_ERR));
    258         return;
    259     }
    260 
    261     OwnPtr<AsyncFileSystemCallbacks> callbacks(VoidCallbacks::create(successCallback, errorCallback, this));
    262     callbacks->setShouldBlockUntilCompletion(synchronousType == Synchronous);
    263 
    264     fileSystem()->remove(createFileSystemURL(entry), callbacks.release());
    265 }
    266 
    267 void DOMFileSystemBase::removeRecursively(const EntryBase* entry, PassOwnPtr<VoidCallback> successCallback, PassOwnPtr<ErrorCallback> errorCallback, SynchronousType synchronousType)
    268 {
    269     ASSERT(entry && entry->isDirectory());
    270     // We don't allow calling remove() on the root directory.
    271     if (entry->fullPath() == String(DOMFilePath::root)) {
    272         reportError(errorCallback, FileError::create(FileError::INVALID_MODIFICATION_ERR));
    273         return;
    274     }
    275 
    276     OwnPtr<AsyncFileSystemCallbacks> callbacks(VoidCallbacks::create(successCallback, errorCallback, this));
    277     callbacks->setShouldBlockUntilCompletion(synchronousType == Synchronous);
    278 
    279     fileSystem()->removeRecursively(createFileSystemURL(entry), callbacks.release());
    280 }
    281 
    282 void DOMFileSystemBase::getParent(const EntryBase* entry, PassOwnPtr<EntryCallback> successCallback, PassOwnPtr<ErrorCallback> errorCallback)
    283 {
    284     ASSERT(entry);
    285     String path = DOMFilePath::getDirectory(entry->fullPath());
    286     fileSystem()->directoryExists(createFileSystemURL(path), EntryCallbacks::create(successCallback, errorCallback, this, path, true));
    287 }
    288 
    289 void DOMFileSystemBase::getFile(const EntryBase* entry, const String& path, const FileSystemFlags& flags, PassOwnPtr<EntryCallback> successCallback, PassOwnPtr<ErrorCallback> errorCallback, SynchronousType synchronousType)
    290 {
    291     String absolutePath;
    292     if (!pathToAbsolutePath(m_type, entry, path, absolutePath)) {
    293         reportError(errorCallback, FileError::create(FileError::INVALID_MODIFICATION_ERR));
    294         return;
    295     }
    296 
    297     OwnPtr<AsyncFileSystemCallbacks> callbacks(EntryCallbacks::create(successCallback, errorCallback, this, absolutePath, false));
    298     callbacks->setShouldBlockUntilCompletion(synchronousType == Synchronous);
    299 
    300     if (flags.create)
    301         fileSystem()->createFile(createFileSystemURL(absolutePath), flags.exclusive, callbacks.release());
    302     else
    303         fileSystem()->fileExists(createFileSystemURL(absolutePath), callbacks.release());
    304 }
    305 
    306 void DOMFileSystemBase::getDirectory(const EntryBase* entry, const String& path, const FileSystemFlags& flags, PassOwnPtr<EntryCallback> successCallback, PassOwnPtr<ErrorCallback> errorCallback, SynchronousType synchronousType)
    307 {
    308     String absolutePath;
    309     if (!pathToAbsolutePath(m_type, entry, path, absolutePath)) {
    310         reportError(errorCallback, FileError::create(FileError::INVALID_MODIFICATION_ERR));
    311         return;
    312     }
    313 
    314     OwnPtr<AsyncFileSystemCallbacks> callbacks(EntryCallbacks::create(successCallback, errorCallback, this, absolutePath, true));
    315     callbacks->setShouldBlockUntilCompletion(synchronousType == Synchronous);
    316 
    317     if (flags.create)
    318         fileSystem()->createDirectory(createFileSystemURL(absolutePath), flags.exclusive, callbacks.release());
    319     else
    320         fileSystem()->directoryExists(createFileSystemURL(absolutePath), callbacks.release());
    321 }
    322 
    323 bool DOMFileSystemBase::readDirectory(PassRefPtr<DirectoryReaderBase> reader, const String& path, PassOwnPtr<EntriesCallback> successCallback, PassOwnPtr<ErrorCallback> errorCallback, SynchronousType synchronousType)
    324 {
    325     ASSERT(DOMFilePath::isAbsolute(path));
    326 
    327     OwnPtr<AsyncFileSystemCallbacks> callbacks(EntriesCallbacks::create(successCallback, errorCallback, reader, path));
    328     callbacks->setShouldBlockUntilCompletion(synchronousType == Synchronous);
    329 
    330     fileSystem()->readDirectory(createFileSystemURL(path), callbacks.release());
    331     return true;
    332 }
    333 
    334 } // namespace WebCore
    335