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 "WorkerFileWriterCallbacksBridge.h" 33 34 #include "WebFileWriter.h" 35 #include "WebWorkerBase.h" 36 #include "core/dom/CrossThreadTask.h" 37 #include "core/workers/WorkerGlobalScope.h" 38 #include "core/workers/WorkerLoaderProxy.h" 39 #include "core/workers/WorkerThread.h" 40 #include "modules/filesystem/AsyncFileWriterClient.h" 41 #include "public/platform/Platform.h" 42 #include "public/platform/WebCString.h" 43 #include "public/platform/WebFileSystem.h" 44 #include "wtf/MainThread.h" 45 #include "wtf/Threading.h" 46 47 using namespace WebCore; 48 49 namespace WebKit { 50 51 void WorkerFileWriterCallbacksBridge::notifyStop() 52 { 53 ASSERT(m_workerGlobalScope->isContextThread()); 54 m_clientOnWorkerThread = 0; 55 { 56 MutexLocker locker(m_loaderProxyMutex); 57 m_proxy = 0; 58 } 59 } 60 61 void WorkerFileWriterCallbacksBridge::postWriteToMainThread(long long position, const KURL& data) 62 { 63 ASSERT(!m_operationInProgress); 64 m_operationInProgress = true; 65 dispatchTaskToMainThread(createCallbackTask(&writeOnMainThread, 66 this, position, data)); 67 } 68 69 void WorkerFileWriterCallbacksBridge::postTruncateToMainThread(long long length) 70 { 71 ASSERT(!m_operationInProgress); 72 m_operationInProgress = true; 73 dispatchTaskToMainThread(createCallbackTask(&truncateOnMainThread, 74 this, length)); 75 } 76 77 void WorkerFileWriterCallbacksBridge::postAbortToMainThread() 78 { 79 ASSERT(m_operationInProgress); 80 dispatchTaskToMainThread(createCallbackTask(&abortOnMainThread, this)); 81 } 82 83 void WorkerFileWriterCallbacksBridge::postShutdownToMainThread(PassRefPtr<WorkerFileWriterCallbacksBridge> bridge) 84 { 85 ASSERT(m_workerGlobalScope->isContextThread()); 86 m_clientOnWorkerThread = 0; 87 stopObserving(); 88 dispatchTaskToMainThread(createCallbackTask(&shutdownOnMainThread, bridge)); 89 } 90 91 void WorkerFileWriterCallbacksBridge::writeOnMainThread(ScriptExecutionContext*, PassRefPtr<WorkerFileWriterCallbacksBridge> bridge, long long position, const KURL& data) 92 { 93 bridge->m_writer->write(position, WebURL(data)); 94 } 95 96 void WorkerFileWriterCallbacksBridge::truncateOnMainThread(ScriptExecutionContext*, PassRefPtr<WorkerFileWriterCallbacksBridge> bridge, long long length) 97 { 98 bridge->m_writer->truncate(length); 99 } 100 101 void WorkerFileWriterCallbacksBridge::abortOnMainThread(ScriptExecutionContext*, PassRefPtr<WorkerFileWriterCallbacksBridge> bridge) 102 { 103 bridge->m_writer->cancel(); 104 } 105 106 void WorkerFileWriterCallbacksBridge::initOnMainThread(ScriptExecutionContext*, PassRefPtr<WorkerFileWriterCallbacksBridge> bridge, const KURL& path) 107 { 108 ASSERT(!bridge->m_writer); 109 bridge->m_writer = adoptPtr(WebKit::Platform::current()->fileSystem()->createFileWriter(path, bridge.get())); 110 } 111 112 void WorkerFileWriterCallbacksBridge::shutdownOnMainThread(ScriptExecutionContext*, PassRefPtr<WorkerFileWriterCallbacksBridge> bridge) 113 { 114 bridge->m_writerDeleted = true; 115 bridge->m_writer.clear(); 116 } 117 118 void WorkerFileWriterCallbacksBridge::didWrite(long long bytes, bool complete) 119 { 120 dispatchTaskToWorkerThread(createCallbackTask(&didWriteOnWorkerThread, this, bytes, complete)); 121 } 122 123 void WorkerFileWriterCallbacksBridge::didFail(WebFileError error) 124 { 125 dispatchTaskToWorkerThread(createCallbackTask(&didFailOnWorkerThread, this, error)); 126 } 127 128 void WorkerFileWriterCallbacksBridge::didTruncate() 129 { 130 dispatchTaskToWorkerThread(createCallbackTask(&didTruncateOnWorkerThread, this)); 131 } 132 133 static const char fileWriterOperationsMode[] = "fileWriterOperationsMode"; 134 135 WorkerFileWriterCallbacksBridge::WorkerFileWriterCallbacksBridge(const KURL& path, WorkerLoaderProxy* proxy, ScriptExecutionContext* scriptExecutionContext, AsyncFileWriterClient* client) 136 : WorkerGlobalScope::Observer(toWorkerGlobalScope(scriptExecutionContext)) 137 , m_proxy(proxy) 138 , m_workerGlobalScope(scriptExecutionContext) 139 , m_clientOnWorkerThread(client) 140 , m_writerDeleted(false) 141 , m_operationInProgress(false) 142 { 143 ASSERT(m_workerGlobalScope->isContextThread()); 144 m_mode = fileWriterOperationsMode; 145 m_mode.append(String::number(toWorkerGlobalScope(scriptExecutionContext)->thread()->runLoop().createUniqueId())); 146 postInitToMainThread(path); 147 } 148 149 void WorkerFileWriterCallbacksBridge::postInitToMainThread(const KURL& path) 150 { 151 dispatchTaskToMainThread( 152 createCallbackTask(&initOnMainThread, this, path)); 153 } 154 155 WorkerFileWriterCallbacksBridge::~WorkerFileWriterCallbacksBridge() 156 { 157 ASSERT(!m_clientOnWorkerThread); 158 ASSERT(!m_writer); 159 } 160 161 // We know m_clientOnWorkerThread is still valid because it is only cleared on the context thread, and because we check in runTaskOnWorkerThread before calling any of these methods. 162 void WorkerFileWriterCallbacksBridge::didWriteOnWorkerThread(ScriptExecutionContext*, PassRefPtr<WorkerFileWriterCallbacksBridge> bridge, long long length, bool complete) 163 { 164 ASSERT(bridge->m_workerGlobalScope->isContextThread()); 165 ASSERT(bridge->m_operationInProgress); 166 if (complete) 167 bridge->m_operationInProgress = false; 168 bridge->m_clientOnWorkerThread->didWrite(length, complete); 169 } 170 171 void WorkerFileWriterCallbacksBridge::didFailOnWorkerThread(ScriptExecutionContext*, PassRefPtr<WorkerFileWriterCallbacksBridge> bridge, WebFileError error) 172 { 173 ASSERT(bridge->m_workerGlobalScope->isContextThread()); 174 ASSERT(bridge->m_operationInProgress); 175 bridge->m_operationInProgress = false; 176 bridge->m_clientOnWorkerThread->didFail(static_cast<FileError::ErrorCode>(error)); 177 } 178 179 void WorkerFileWriterCallbacksBridge::didTruncateOnWorkerThread(ScriptExecutionContext*, PassRefPtr<WorkerFileWriterCallbacksBridge> bridge) 180 { 181 ASSERT(bridge->m_workerGlobalScope->isContextThread()); 182 ASSERT(bridge->m_operationInProgress); 183 bridge->m_operationInProgress = false; 184 bridge->m_clientOnWorkerThread->didTruncate(); 185 } 186 187 void WorkerFileWriterCallbacksBridge::runTaskOnMainThread(ScriptExecutionContext* scriptExecutionContext, PassRefPtr<WorkerFileWriterCallbacksBridge> bridge, PassOwnPtr<ScriptExecutionContext::Task> taskToRun) 188 { 189 ASSERT(isMainThread()); 190 if (!bridge->m_writerDeleted) 191 taskToRun->performTask(scriptExecutionContext); 192 } 193 194 void WorkerFileWriterCallbacksBridge::runTaskOnWorkerThread(ScriptExecutionContext* scriptExecutionContext, PassRefPtr<WorkerFileWriterCallbacksBridge> bridge, PassOwnPtr<ScriptExecutionContext::Task> taskToRun) 195 { 196 ASSERT(bridge->m_workerGlobalScope->isContextThread()); 197 if (bridge->m_clientOnWorkerThread) 198 taskToRun->performTask(scriptExecutionContext); 199 } 200 201 void WorkerFileWriterCallbacksBridge::dispatchTaskToMainThread(PassOwnPtr<ScriptExecutionContext::Task> task) 202 { 203 ASSERT(m_workerGlobalScope->isContextThread()); 204 WebWorkerBase::dispatchTaskToMainThread( 205 createCallbackTask(&runTaskOnMainThread, this, task)); 206 } 207 208 void WorkerFileWriterCallbacksBridge::dispatchTaskToWorkerThread(PassOwnPtr<ScriptExecutionContext::Task> task) 209 { 210 ASSERT(isMainThread()); 211 212 MutexLocker locker(m_loaderProxyMutex); 213 if (m_proxy) 214 m_proxy->postTaskForModeToWorkerGlobalScope( 215 createCallbackTask(&runTaskOnWorkerThread, this, task), m_mode); 216 } 217 218 bool WorkerFileWriterCallbacksBridge::waitForOperationToComplete() 219 { 220 while (m_operationInProgress) { 221 WorkerGlobalScope* context = toWorkerGlobalScope(m_workerGlobalScope); 222 if (context->thread()->runLoop().runInMode(context, m_mode) == MessageQueueTerminated) 223 return false; 224 } 225 return true; 226 } 227 228 } // namespace WebKit 229