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
notifyStop()54 void WorkerFileWriterCallbacksBridge::notifyStop()
55 {
56 ASSERT(m_workerContext->isContextThread());
57 m_clientOnWorkerThread = 0;
58 }
59
postWriteToMainThread(long long position,const KURL & data)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
postTruncateToMainThread(long long length)67 void WorkerFileWriterCallbacksBridge::postTruncateToMainThread(long long length)
68 {
69 ASSERT(!m_operationInProgress);
70 m_operationInProgress = true;
71 dispatchTaskToMainThread(createCallbackTask(&truncateOnMainThread, this, length));
72 }
73
postAbortToMainThread()74 void WorkerFileWriterCallbacksBridge::postAbortToMainThread()
75 {
76 ASSERT(m_operationInProgress);
77 dispatchTaskToMainThread(createCallbackTask(&abortOnMainThread, this));
78 }
79
postShutdownToMainThread(PassRefPtr<WorkerFileWriterCallbacksBridge> bridge)80 void WorkerFileWriterCallbacksBridge::postShutdownToMainThread(PassRefPtr<WorkerFileWriterCallbacksBridge> bridge)
81 {
82 ASSERT(m_workerContext->isContextThread());
83 m_clientOnWorkerThread = 0;
84 dispatchTaskToMainThread(createCallbackTask(&shutdownOnMainThread, bridge));
85 }
86
writeOnMainThread(ScriptExecutionContext *,PassRefPtr<WorkerFileWriterCallbacksBridge> bridge,long long position,const KURL & data)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
truncateOnMainThread(ScriptExecutionContext *,PassRefPtr<WorkerFileWriterCallbacksBridge> bridge,long long length)92 void WorkerFileWriterCallbacksBridge::truncateOnMainThread(ScriptExecutionContext*, PassRefPtr<WorkerFileWriterCallbacksBridge> bridge, long long length)
93 {
94 bridge->m_writer->truncate(length);
95 }
96
abortOnMainThread(ScriptExecutionContext *,PassRefPtr<WorkerFileWriterCallbacksBridge> bridge)97 void WorkerFileWriterCallbacksBridge::abortOnMainThread(ScriptExecutionContext*, PassRefPtr<WorkerFileWriterCallbacksBridge> bridge)
98 {
99 bridge->m_writer->cancel();
100 }
101
initOnMainThread(ScriptExecutionContext *,PassRefPtr<WorkerFileWriterCallbacksBridge> bridge,const String & path)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
shutdownOnMainThread(ScriptExecutionContext *,PassRefPtr<WorkerFileWriterCallbacksBridge> bridge)108 void WorkerFileWriterCallbacksBridge::shutdownOnMainThread(ScriptExecutionContext*, PassRefPtr<WorkerFileWriterCallbacksBridge> bridge)
109 {
110 bridge->m_writerDeleted = true;
111 bridge->m_writer.clear();
112 }
113
didWrite(long long bytes,bool complete)114 void WorkerFileWriterCallbacksBridge::didWrite(long long bytes, bool complete)
115 {
116 dispatchTaskToWorkerThread(createCallbackTask(&didWriteOnWorkerThread, this, bytes, complete));
117 }
118
didFail(WebFileError error)119 void WorkerFileWriterCallbacksBridge::didFail(WebFileError error)
120 {
121 dispatchTaskToWorkerThread(createCallbackTask(&didFailOnWorkerThread, this, error));
122 }
123
didTruncate()124 void WorkerFileWriterCallbacksBridge::didTruncate()
125 {
126 dispatchTaskToWorkerThread(createCallbackTask(&didTruncateOnWorkerThread, this));
127 }
128
129 static const char fileWriterOperationsMode[] = "fileWriterOperationsMode";
130
WorkerFileWriterCallbacksBridge(const String & path,WorkerLoaderProxy * proxy,ScriptExecutionContext * scriptExecutionContext,AsyncFileWriterClient * client)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
postInitToMainThread(const String & path)145 void WorkerFileWriterCallbacksBridge::postInitToMainThread(const String& path)
146 {
147 dispatchTaskToMainThread(createCallbackTask(&initOnMainThread, this, path));
148 }
149
~WorkerFileWriterCallbacksBridge()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.
didWriteOnWorkerThread(ScriptExecutionContext *,PassRefPtr<WorkerFileWriterCallbacksBridge> bridge,long long length,bool complete)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
didFailOnWorkerThread(ScriptExecutionContext *,PassRefPtr<WorkerFileWriterCallbacksBridge> bridge,WebFileError error)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
didTruncateOnWorkerThread(ScriptExecutionContext *,PassRefPtr<WorkerFileWriterCallbacksBridge> bridge)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
runTaskOnMainThread(ScriptExecutionContext * scriptExecutionContext,PassRefPtr<WorkerFileWriterCallbacksBridge> bridge,PassOwnPtr<ScriptExecutionContext::Task> taskToRun)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
runTaskOnWorkerThread(ScriptExecutionContext * scriptExecutionContext,PassRefPtr<WorkerFileWriterCallbacksBridge> bridge,PassOwnPtr<ScriptExecutionContext::Task> taskToRun)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
dispatchTaskToMainThread(PassOwnPtr<ScriptExecutionContext::Task> task)196 void WorkerFileWriterCallbacksBridge::dispatchTaskToMainThread(PassOwnPtr<ScriptExecutionContext::Task> task)
197 {
198 ASSERT(m_workerContext->isContextThread());
199 WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&runTaskOnMainThread, this, task));
200 }
201
dispatchTaskToWorkerThread(PassOwnPtr<ScriptExecutionContext::Task> task)202 void WorkerFileWriterCallbacksBridge::dispatchTaskToWorkerThread(PassOwnPtr<ScriptExecutionContext::Task> task)
203 {
204 ASSERT(isMainThread());
205 m_proxy->postTaskForModeToWorkerContext(createCallbackTask(&runTaskOnWorkerThread, this, task), m_mode);
206 }
207
waitForOperationToComplete()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