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