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(©OnMainThread, 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