Home | History | Annotate | Download | only in src
      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 "WorkerFileSystemCallbacksBridge.h"
     33 
     34 #if ENABLE(FILE_SYSTEM)
     35 
     36 #include "CrossThreadTask.h"
     37 #include "WebCommonWorkerClient.h"
     38 #include "WebFileInfo.h"
     39 #include "WebFileSystemCallbacks.h"
     40 #include "WebFileSystemEntry.h"
     41 #include "WebString.h"
     42 #include "WebWorkerBase.h"
     43 #include "WorkerContext.h"
     44 #include "WorkerScriptController.h"
     45 #include "WorkerThread.h"
     46 #include <wtf/MainThread.h>
     47 #include <wtf/Threading.h>
     48 #include <wtf/UnusedParam.h>
     49 
     50 namespace WebCore {
     51 
     52 template<> struct CrossThreadCopierBase<false, false, WebKit::WebFileInfo> {
     53     typedef WebKit::WebFileInfo Type;
     54     static Type copy(const WebKit::WebFileInfo& info)
     55     {
     56         // Perform per-field copy to make sure we don't do any (unexpected) non-thread safe copy here.
     57         struct WebKit::WebFileInfo newInfo;
     58         newInfo.modificationTime = info.modificationTime;
     59         newInfo.length = info.length;
     60         newInfo.type = info.type;
     61         newInfo.platformPath.assign(info.platformPath.data(), info.platformPath.length());
     62         return newInfo;
     63     }
     64 };
     65 
     66 template<> struct CrossThreadCopierBase<false, false, WebKit::WebVector<WebKit::WebFileSystemEntry> > {
     67     typedef WebKit::WebVector<WebKit::WebFileSystemEntry> Type;
     68     static Type copy(const WebKit::WebVector<WebKit::WebFileSystemEntry>& entries)
     69     {
     70         WebKit::WebVector<WebKit::WebFileSystemEntry> newEntries(entries.size());
     71         for (size_t i = 0; i < entries.size(); ++i) {
     72             String name = entries[i].name;
     73             newEntries[i].isDirectory = entries[i].isDirectory;
     74             newEntries[i].name = name.crossThreadString();
     75         }
     76         return newEntries;
     77     }
     78 };
     79 
     80 }
     81 
     82 using namespace WebCore;
     83 
     84 namespace WebKit {
     85 
     86 // FileSystemCallbacks that are to be dispatched on the main thread.
     87 class MainThreadFileSystemCallbacks : public WebFileSystemCallbacks {
     88 public:
     89     // Callbacks are self-destructed and we always return leaked pointer here.
     90     static MainThreadFileSystemCallbacks* createLeakedPtr(WorkerFileSystemCallbacksBridge* bridge, const String& mode)
     91     {
     92         OwnPtr<MainThreadFileSystemCallbacks> callbacks = adoptPtr(new MainThreadFileSystemCallbacks(bridge, mode));
     93         return callbacks.leakPtr();
     94     }
     95 
     96     virtual ~MainThreadFileSystemCallbacks()
     97     {
     98     }
     99 
    100     virtual void didOpenFileSystem(const WebString& name, const WebString& path)
    101     {
    102         m_bridge->didOpenFileSystemOnMainThread(name, path, m_mode);
    103         delete this;
    104     }
    105 
    106     virtual void didFail(WebFileError error)
    107     {
    108         m_bridge->didFailOnMainThread(error, m_mode);
    109         delete this;
    110     }
    111 
    112     virtual void didSucceed()
    113     {
    114         m_bridge->didSucceedOnMainThread(m_mode);
    115         delete this;
    116     }
    117 
    118     virtual void didReadMetadata(const WebFileInfo& info)
    119     {
    120         m_bridge->didReadMetadataOnMainThread(info, m_mode);
    121         delete this;
    122     }
    123 
    124     virtual void didReadDirectory(const WebVector<WebFileSystemEntry>& entries, bool hasMore)
    125     {
    126         m_bridge->didReadDirectoryOnMainThread(entries, hasMore, m_mode);
    127         delete this;
    128     }
    129 
    130 private:
    131     MainThreadFileSystemCallbacks(WorkerFileSystemCallbacksBridge* bridge, const String& mode)
    132         : m_bridge(bridge)
    133         , m_mode(mode)
    134     {
    135         ASSERT(m_bridge);
    136     }
    137 
    138     friend class WorkerFileSystemCallbacksBridge;
    139     // The bridge pointer is kept by the bridge itself on the WorkerThread.
    140     WorkerFileSystemCallbacksBridge* m_bridge;
    141     const String m_mode;
    142 };
    143 
    144 void WorkerFileSystemCallbacksBridge::stop()
    145 {
    146     ASSERT(m_workerContext->isContextThread());
    147     MutexLocker locker(m_mutex);
    148     m_worker = 0;
    149 
    150     if (m_callbacksOnWorkerThread) {
    151         m_callbacksOnWorkerThread->didFail(WebFileErrorAbort);
    152         m_callbacksOnWorkerThread = 0;
    153     }
    154 }
    155 
    156 void WorkerFileSystemCallbacksBridge::postOpenFileSystemToMainThread(WebCommonWorkerClient* commonClient, WebFileSystem::Type type, long long size, bool create, const String& mode)
    157 {
    158     dispatchTaskToMainThread(createCallbackTask(&openFileSystemOnMainThread, commonClient, type, size, create, this, mode));
    159 }
    160 
    161 void WorkerFileSystemCallbacksBridge::postMoveToMainThread(WebFileSystem* fileSystem, const String& sourcePath, const String& destinationPath, const String& mode)
    162 {
    163     dispatchTaskToMainThread(createCallbackTask(&moveOnMainThread, fileSystem, sourcePath, destinationPath, this, mode));
    164 }
    165 
    166 void WorkerFileSystemCallbacksBridge::postCopyToMainThread(WebFileSystem* fileSystem, const String& sourcePath, const String& destinationPath, const String& mode)
    167 {
    168     dispatchTaskToMainThread(createCallbackTask(&copyOnMainThread, fileSystem, sourcePath, destinationPath, this, mode));
    169 }
    170 
    171 void WorkerFileSystemCallbacksBridge::postRemoveToMainThread(WebFileSystem* fileSystem, const String& path, const String& mode)
    172 {
    173     ASSERT(fileSystem);
    174     dispatchTaskToMainThread(createCallbackTask(&removeOnMainThread, fileSystem, path, this, mode));
    175 }
    176 
    177 void WorkerFileSystemCallbacksBridge::postRemoveRecursivelyToMainThread(WebFileSystem* fileSystem, const String& path, const String& mode)
    178 {
    179     ASSERT(fileSystem);
    180     dispatchTaskToMainThread(createCallbackTask(&removeRecursivelyOnMainThread, fileSystem, path, this, mode));
    181 }
    182 
    183 void WorkerFileSystemCallbacksBridge::postReadMetadataToMainThread(WebFileSystem* fileSystem, const String& path, const String& mode)
    184 {
    185     ASSERT(fileSystem);
    186     dispatchTaskToMainThread(createCallbackTask(&readMetadataOnMainThread, fileSystem, path, this, mode));
    187 }
    188 
    189 void WorkerFileSystemCallbacksBridge::postCreateFileToMainThread(WebFileSystem* fileSystem, const String& path, bool exclusive, const String& mode)
    190 {
    191     dispatchTaskToMainThread(createCallbackTask(&createFileOnMainThread, fileSystem, path, exclusive, this, mode));
    192 }
    193 
    194 void WorkerFileSystemCallbacksBridge::postCreateDirectoryToMainThread(WebFileSystem* fileSystem, const String& path, bool exclusive, const String& mode)
    195 {
    196     ASSERT(fileSystem);
    197     dispatchTaskToMainThread(createCallbackTask(&createDirectoryOnMainThread, fileSystem, path, exclusive, this, mode));
    198 }
    199 
    200 void WorkerFileSystemCallbacksBridge::postFileExistsToMainThread(WebFileSystem* fileSystem, const String& path, const String& mode)
    201 {
    202     ASSERT(fileSystem);
    203     dispatchTaskToMainThread(createCallbackTask(&fileExistsOnMainThread, fileSystem, path, this, mode));
    204 }
    205 
    206 void WorkerFileSystemCallbacksBridge::postDirectoryExistsToMainThread(WebFileSystem* fileSystem, const String& path, const String& mode)
    207 {
    208     ASSERT(fileSystem);
    209     dispatchTaskToMainThread(createCallbackTask(&directoryExistsOnMainThread, fileSystem, path, this, mode));
    210 }
    211 
    212 void WorkerFileSystemCallbacksBridge::postReadDirectoryToMainThread(WebFileSystem* fileSystem, const String& path, const String& mode)
    213 {
    214     ASSERT(fileSystem);
    215     dispatchTaskToMainThread(createCallbackTask(&readDirectoryOnMainThread, fileSystem, path, this, mode));
    216 }
    217 
    218 void WorkerFileSystemCallbacksBridge::openFileSystemOnMainThread(ScriptExecutionContext*, WebCommonWorkerClient* commonClient, WebFileSystem::Type type, long long size, bool create, WorkerFileSystemCallbacksBridge* bridge, const String& mode)
    219 {
    220     if (!commonClient)
    221         bridge->didFailOnMainThread(WebFileErrorAbort, mode);
    222     else {
    223         commonClient->openFileSystem(type, size, create, MainThreadFileSystemCallbacks::createLeakedPtr(bridge, mode));
    224     }
    225 }
    226 
    227 void WorkerFileSystemCallbacksBridge::moveOnMainThread(WebCore::ScriptExecutionContext*, WebFileSystem* fileSystem, const String& sourcePath, const String& destinationPath, WorkerFileSystemCallbacksBridge* bridge, const String& mode)
    228 {
    229     fileSystem->move(sourcePath, destinationPath, MainThreadFileSystemCallbacks::createLeakedPtr(bridge, mode));
    230 }
    231 
    232 void WorkerFileSystemCallbacksBridge::copyOnMainThread(WebCore::ScriptExecutionContext*, WebFileSystem* fileSystem, const String& sourcePath, const String& destinationPath, WorkerFileSystemCallbacksBridge* bridge, const String& mode)
    233 {
    234     fileSystem->copy(sourcePath, destinationPath, MainThreadFileSystemCallbacks::createLeakedPtr(bridge, mode));
    235 }
    236 
    237 void WorkerFileSystemCallbacksBridge::removeOnMainThread(WebCore::ScriptExecutionContext*, WebFileSystem* fileSystem, const String& path, WorkerFileSystemCallbacksBridge* bridge, const String& mode)
    238 {
    239     fileSystem->remove(path, MainThreadFileSystemCallbacks::createLeakedPtr(bridge, mode));
    240 }
    241 
    242 void WorkerFileSystemCallbacksBridge::removeRecursivelyOnMainThread(WebCore::ScriptExecutionContext*, WebFileSystem* fileSystem, const String& path, WorkerFileSystemCallbacksBridge* bridge, const String& mode)
    243 {
    244     fileSystem->removeRecursively(path, MainThreadFileSystemCallbacks::createLeakedPtr(bridge, mode));
    245 }
    246 
    247 void WorkerFileSystemCallbacksBridge::readMetadataOnMainThread(WebCore::ScriptExecutionContext*, WebFileSystem* fileSystem, const String& path, WorkerFileSystemCallbacksBridge* bridge, const String& mode)
    248 {
    249     fileSystem->readMetadata(path, MainThreadFileSystemCallbacks::createLeakedPtr(bridge, mode));
    250 }
    251 
    252 void WorkerFileSystemCallbacksBridge::createFileOnMainThread(WebCore::ScriptExecutionContext*, WebFileSystem* fileSystem, const String& path, bool exclusive, WorkerFileSystemCallbacksBridge* bridge, const String& mode)
    253 {
    254     fileSystem->createFile(path, exclusive, MainThreadFileSystemCallbacks::createLeakedPtr(bridge, mode));
    255 }
    256 
    257 void WorkerFileSystemCallbacksBridge::createDirectoryOnMainThread(WebCore::ScriptExecutionContext*, WebFileSystem* fileSystem, const String& path, bool exclusive, WorkerFileSystemCallbacksBridge* bridge, const String& mode)
    258 {
    259     fileSystem->createDirectory(path, exclusive, MainThreadFileSystemCallbacks::createLeakedPtr(bridge, mode));
    260 }
    261 
    262 void WorkerFileSystemCallbacksBridge::fileExistsOnMainThread(WebCore::ScriptExecutionContext*, WebFileSystem* fileSystem, const String& path, WorkerFileSystemCallbacksBridge* bridge, const String& mode)
    263 {
    264     fileSystem->fileExists(path, MainThreadFileSystemCallbacks::createLeakedPtr(bridge, mode));
    265 }
    266 
    267 void WorkerFileSystemCallbacksBridge::directoryExistsOnMainThread(WebCore::ScriptExecutionContext*, WebFileSystem* fileSystem, const String& path, WorkerFileSystemCallbacksBridge* bridge, const String& mode)
    268 {
    269     fileSystem->directoryExists(path, MainThreadFileSystemCallbacks::createLeakedPtr(bridge, mode));
    270 }
    271 
    272 void WorkerFileSystemCallbacksBridge::readDirectoryOnMainThread(WebCore::ScriptExecutionContext*, WebFileSystem* fileSystem, const String& path, WorkerFileSystemCallbacksBridge* bridge, const String& mode)
    273 {
    274     fileSystem->readDirectory(path, MainThreadFileSystemCallbacks::createLeakedPtr(bridge, mode));
    275 }
    276 
    277 void WorkerFileSystemCallbacksBridge::didFailOnMainThread(WebFileError error, const String& mode)
    278 {
    279     mayPostTaskToWorker(createCallbackTask(&didFailOnWorkerThread, this, error), mode);
    280 }
    281 
    282 void WorkerFileSystemCallbacksBridge::didOpenFileSystemOnMainThread(const String& name, const String& rootPath, const String& mode)
    283 {
    284     mayPostTaskToWorker(createCallbackTask(&didOpenFileSystemOnWorkerThread, this, name, rootPath), mode);
    285 }
    286 
    287 void WorkerFileSystemCallbacksBridge::didSucceedOnMainThread(const String& mode)
    288 {
    289     mayPostTaskToWorker(createCallbackTask(&didSucceedOnWorkerThread, this), mode);
    290 }
    291 
    292 void WorkerFileSystemCallbacksBridge::didReadMetadataOnMainThread(const WebFileInfo& info, const String& mode)
    293 {
    294     mayPostTaskToWorker(createCallbackTask(&didReadMetadataOnWorkerThread, this, info), mode);
    295 }
    296 
    297 void WorkerFileSystemCallbacksBridge::didReadDirectoryOnMainThread(const WebVector<WebFileSystemEntry>& entries, bool hasMore, const String& mode)
    298 {
    299     mayPostTaskToWorker(createCallbackTask(&didReadDirectoryOnWorkerThread, this, entries, hasMore), mode);
    300 }
    301 
    302 WorkerFileSystemCallbacksBridge::WorkerFileSystemCallbacksBridge(WebWorkerBase* worker, ScriptExecutionContext* scriptExecutionContext, WebFileSystemCallbacks* callbacks)
    303     : WorkerContext::Observer(static_cast<WorkerContext*>(scriptExecutionContext))
    304     , m_worker(worker)
    305     , m_workerContext(scriptExecutionContext)
    306     , m_callbacksOnWorkerThread(callbacks)
    307 {
    308     ASSERT(m_workerContext->isContextThread());
    309 }
    310 
    311 WorkerFileSystemCallbacksBridge::~WorkerFileSystemCallbacksBridge()
    312 {
    313     ASSERT(!m_callbacksOnWorkerThread);
    314 }
    315 
    316 void WorkerFileSystemCallbacksBridge::didFailOnWorkerThread(ScriptExecutionContext*, WorkerFileSystemCallbacksBridge* bridge, WebFileError error)
    317 {
    318     bridge->m_callbacksOnWorkerThread->didFail(error);
    319 }
    320 
    321 void WorkerFileSystemCallbacksBridge::didOpenFileSystemOnWorkerThread(ScriptExecutionContext*, WorkerFileSystemCallbacksBridge* bridge, const String& name, const String& rootPath)
    322 {
    323     bridge->m_callbacksOnWorkerThread->didOpenFileSystem(name, rootPath);
    324 }
    325 
    326 void WorkerFileSystemCallbacksBridge::didSucceedOnWorkerThread(ScriptExecutionContext*, WorkerFileSystemCallbacksBridge* bridge)
    327 {
    328     bridge->m_callbacksOnWorkerThread->didSucceed();
    329 }
    330 
    331 void WorkerFileSystemCallbacksBridge::didReadMetadataOnWorkerThread(ScriptExecutionContext*, WorkerFileSystemCallbacksBridge* bridge, const WebFileInfo& info)
    332 {
    333     bridge->m_callbacksOnWorkerThread->didReadMetadata(info);
    334 }
    335 
    336 void WorkerFileSystemCallbacksBridge::didReadDirectoryOnWorkerThread(ScriptExecutionContext*, WorkerFileSystemCallbacksBridge* bridge, const WebVector<WebFileSystemEntry>& entries, bool hasMore)
    337 {
    338     bridge->m_callbacksOnWorkerThread->didReadDirectory(entries, hasMore);
    339 }
    340 
    341 
    342 void WorkerFileSystemCallbacksBridge::runTaskOnMainThread(WebCore::ScriptExecutionContext* scriptExecutionContext, PassRefPtr<WorkerFileSystemCallbacksBridge> bridge, PassOwnPtr<WebCore::ScriptExecutionContext::Task> taskToRun)
    343 {
    344     ASSERT(isMainThread());
    345 
    346     // Every task run will result in one call to mayPostTaskToWorker, which is where this ref is released.
    347     WorkerFileSystemCallbacksBridge* leaked = bridge.leakRef();
    348     UNUSED_PARAM(leaked);
    349     taskToRun->performTask(scriptExecutionContext);
    350 }
    351 
    352 void WorkerFileSystemCallbacksBridge::runTaskOnWorkerThread(WebCore::ScriptExecutionContext* scriptExecutionContext, PassRefPtr<WorkerFileSystemCallbacksBridge> bridge, PassOwnPtr<WebCore::ScriptExecutionContext::Task> taskToRun)
    353 {
    354     if (!bridge->m_callbacksOnWorkerThread)
    355         return;
    356     ASSERT(bridge->m_workerContext->isContextThread());
    357     taskToRun->performTask(scriptExecutionContext);
    358     bridge->m_callbacksOnWorkerThread = 0;
    359     bridge->stopObserving();
    360 }
    361 
    362 void WorkerFileSystemCallbacksBridge::dispatchTaskToMainThread(PassOwnPtr<WebCore::ScriptExecutionContext::Task> task)
    363 {
    364     ASSERT(m_worker);
    365     ASSERT(m_workerContext->isContextThread());
    366     m_worker->dispatchTaskToMainThread(createCallbackTask(&runTaskOnMainThread, RefPtr<WorkerFileSystemCallbacksBridge>(this).release(), task));
    367 }
    368 
    369 void WorkerFileSystemCallbacksBridge::mayPostTaskToWorker(PassOwnPtr<ScriptExecutionContext::Task> task, const String& mode)
    370 {
    371     ASSERT(isMainThread());
    372 
    373     // Balancing out the ref() done in runTaskOnMainThread. (Since m_mutex is a member and the deref may result
    374     // in the destruction of WorkerFileSystemCallbacksBridge, the ordering of the RefPtr and the MutexLocker
    375     // is very important, to ensure that the m_mutex is still valid when it gets unlocked.)
    376     RefPtr<WorkerFileSystemCallbacksBridge> bridge = adoptRef(this);
    377     MutexLocker locker(m_mutex);
    378     if (m_worker)
    379         m_worker->postTaskForModeToWorkerContext(createCallbackTask(&runTaskOnWorkerThread, bridge, task), mode);
    380 }
    381 
    382 } // namespace WebCore
    383 
    384 #endif // ENABLE(FILE_SYSTEM)
    385