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 50namespace WebCore { 51 52template<> 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 66template<> 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 82using namespace WebCore; 83 84namespace WebKit { 85 86// FileSystemCallbacks that are to be dispatched on the main thread. 87class MainThreadFileSystemCallbacks : public WebFileSystemCallbacks { 88public: 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 130private: 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 144void 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 156void 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 161void 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 166void WorkerFileSystemCallbacksBridge::postCopyToMainThread(WebFileSystem* fileSystem, const String& sourcePath, const String& destinationPath, const String& mode) 167{ 168 dispatchTaskToMainThread(createCallbackTask(©OnMainThread, fileSystem, sourcePath, destinationPath, this, mode)); 169} 170 171void WorkerFileSystemCallbacksBridge::postRemoveToMainThread(WebFileSystem* fileSystem, const String& path, const String& mode) 172{ 173 ASSERT(fileSystem); 174 dispatchTaskToMainThread(createCallbackTask(&removeOnMainThread, fileSystem, path, this, mode)); 175} 176 177void WorkerFileSystemCallbacksBridge::postRemoveRecursivelyToMainThread(WebFileSystem* fileSystem, const String& path, const String& mode) 178{ 179 ASSERT(fileSystem); 180 dispatchTaskToMainThread(createCallbackTask(&removeRecursivelyOnMainThread, fileSystem, path, this, mode)); 181} 182 183void WorkerFileSystemCallbacksBridge::postReadMetadataToMainThread(WebFileSystem* fileSystem, const String& path, const String& mode) 184{ 185 ASSERT(fileSystem); 186 dispatchTaskToMainThread(createCallbackTask(&readMetadataOnMainThread, fileSystem, path, this, mode)); 187} 188 189void WorkerFileSystemCallbacksBridge::postCreateFileToMainThread(WebFileSystem* fileSystem, const String& path, bool exclusive, const String& mode) 190{ 191 dispatchTaskToMainThread(createCallbackTask(&createFileOnMainThread, fileSystem, path, exclusive, this, mode)); 192} 193 194void 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 200void WorkerFileSystemCallbacksBridge::postFileExistsToMainThread(WebFileSystem* fileSystem, const String& path, const String& mode) 201{ 202 ASSERT(fileSystem); 203 dispatchTaskToMainThread(createCallbackTask(&fileExistsOnMainThread, fileSystem, path, this, mode)); 204} 205 206void WorkerFileSystemCallbacksBridge::postDirectoryExistsToMainThread(WebFileSystem* fileSystem, const String& path, const String& mode) 207{ 208 ASSERT(fileSystem); 209 dispatchTaskToMainThread(createCallbackTask(&directoryExistsOnMainThread, fileSystem, path, this, mode)); 210} 211 212void WorkerFileSystemCallbacksBridge::postReadDirectoryToMainThread(WebFileSystem* fileSystem, const String& path, const String& mode) 213{ 214 ASSERT(fileSystem); 215 dispatchTaskToMainThread(createCallbackTask(&readDirectoryOnMainThread, fileSystem, path, this, mode)); 216} 217 218void 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 227void 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 232void 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 237void 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 242void 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 247void 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 252void 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 257void 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 262void 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 267void 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 272void 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 277void WorkerFileSystemCallbacksBridge::didFailOnMainThread(WebFileError error, const String& mode) 278{ 279 mayPostTaskToWorker(createCallbackTask(&didFailOnWorkerThread, this, error), mode); 280} 281 282void WorkerFileSystemCallbacksBridge::didOpenFileSystemOnMainThread(const String& name, const String& rootPath, const String& mode) 283{ 284 mayPostTaskToWorker(createCallbackTask(&didOpenFileSystemOnWorkerThread, this, name, rootPath), mode); 285} 286 287void WorkerFileSystemCallbacksBridge::didSucceedOnMainThread(const String& mode) 288{ 289 mayPostTaskToWorker(createCallbackTask(&didSucceedOnWorkerThread, this), mode); 290} 291 292void WorkerFileSystemCallbacksBridge::didReadMetadataOnMainThread(const WebFileInfo& info, const String& mode) 293{ 294 mayPostTaskToWorker(createCallbackTask(&didReadMetadataOnWorkerThread, this, info), mode); 295} 296 297void WorkerFileSystemCallbacksBridge::didReadDirectoryOnMainThread(const WebVector<WebFileSystemEntry>& entries, bool hasMore, const String& mode) 298{ 299 mayPostTaskToWorker(createCallbackTask(&didReadDirectoryOnWorkerThread, this, entries, hasMore), mode); 300} 301 302WorkerFileSystemCallbacksBridge::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 311WorkerFileSystemCallbacksBridge::~WorkerFileSystemCallbacksBridge() 312{ 313 ASSERT(!m_callbacksOnWorkerThread); 314} 315 316void WorkerFileSystemCallbacksBridge::didFailOnWorkerThread(ScriptExecutionContext*, WorkerFileSystemCallbacksBridge* bridge, WebFileError error) 317{ 318 bridge->m_callbacksOnWorkerThread->didFail(error); 319} 320 321void WorkerFileSystemCallbacksBridge::didOpenFileSystemOnWorkerThread(ScriptExecutionContext*, WorkerFileSystemCallbacksBridge* bridge, const String& name, const String& rootPath) 322{ 323 bridge->m_callbacksOnWorkerThread->didOpenFileSystem(name, rootPath); 324} 325 326void WorkerFileSystemCallbacksBridge::didSucceedOnWorkerThread(ScriptExecutionContext*, WorkerFileSystemCallbacksBridge* bridge) 327{ 328 bridge->m_callbacksOnWorkerThread->didSucceed(); 329} 330 331void WorkerFileSystemCallbacksBridge::didReadMetadataOnWorkerThread(ScriptExecutionContext*, WorkerFileSystemCallbacksBridge* bridge, const WebFileInfo& info) 332{ 333 bridge->m_callbacksOnWorkerThread->didReadMetadata(info); 334} 335 336void WorkerFileSystemCallbacksBridge::didReadDirectoryOnWorkerThread(ScriptExecutionContext*, WorkerFileSystemCallbacksBridge* bridge, const WebVector<WebFileSystemEntry>& entries, bool hasMore) 337{ 338 bridge->m_callbacksOnWorkerThread->didReadDirectory(entries, hasMore); 339} 340 341 342void 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 352void 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 362void 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 369void 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