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(&copyOnMainThread, 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