1/*
2 * Copyright (C) 2010 Google Inc.  All rights reserved.
3 * Copyright (C) 2012 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 *     * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *     * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 *     * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include "config.h"
33
34#if ENABLE(BLOB)
35
36#include "AsyncFileStream.h"
37
38#include "Blob.h"
39#include "FileStream.h"
40#include "FileStreamClient.h"
41#include "FileThread.h"
42#include "FileThreadTask.h"
43#include "MainThreadTask.h"
44#include <wtf/MainThread.h>
45#include <wtf/text/WTFString.h>
46
47namespace WebCore {
48
49static PassRefPtr<FileThread> createFileThread()
50{
51    RefPtr<FileThread> thread = FileThread::create();
52    if (!thread->start())
53        return 0;
54    return thread.release();
55}
56
57static FileThread* fileThread()
58{
59    ASSERT(isMainThread());
60    static FileThread* thread = createFileThread().leakRef();
61    return thread;
62}
63
64inline AsyncFileStream::AsyncFileStream(FileStreamClient* client)
65    : m_stream(FileStream::create())
66    , m_client(client)
67{
68    ASSERT(isMainThread());
69}
70
71PassRefPtr<AsyncFileStream> AsyncFileStream::create(FileStreamClient* client)
72{
73    RefPtr<AsyncFileStream> proxy = adoptRef(new AsyncFileStream(client));
74
75    // Hold a reference so that the instance will not get deleted while there are tasks on the file thread.
76    // This is balanced by the deref in derefProxyOnContext below.
77    proxy->ref();
78
79    fileThread()->postTask(createFileThreadTask(proxy.get(), &AsyncFileStream::startOnFileThread));
80
81    return proxy.release();
82}
83
84AsyncFileStream::~AsyncFileStream()
85{
86}
87
88static void didStart(AsyncFileStream* proxy)
89{
90    if (proxy->client())
91        proxy->client()->didStart();
92}
93
94void AsyncFileStream::startOnFileThread()
95{
96    // FIXME: It is not correct to check m_client from a secondary thread - stop() could be racing with this check.
97    if (!m_client)
98        return;
99    m_stream->start();
100    callOnMainThread(didStart, AllowCrossThreadAccess(this));
101}
102
103void AsyncFileStream::stop()
104{
105    // Clear the client so that we won't be invoking callbacks on the client.
106    setClient(0);
107
108    fileThread()->unscheduleTasks(m_stream.get());
109    fileThread()->postTask(createFileThreadTask(this, &AsyncFileStream::stopOnFileThread));
110}
111
112static void derefProxyOnMainThread(AsyncFileStream* proxy)
113{
114    ASSERT(proxy->hasOneRef());
115    proxy->deref();
116}
117
118void AsyncFileStream::stopOnFileThread()
119{
120    m_stream->stop();
121    callOnMainThread(derefProxyOnMainThread, AllowCrossThreadAccess(this));
122}
123
124static void didGetSize(AsyncFileStream* proxy, long long size)
125{
126    if (proxy->client())
127        proxy->client()->didGetSize(size);
128}
129
130void AsyncFileStream::getSize(const String& path, double expectedModificationTime)
131{
132    fileThread()->postTask(createFileThreadTask(this, &AsyncFileStream::getSizeOnFileThread, path, expectedModificationTime));
133}
134
135void AsyncFileStream::getSizeOnFileThread(const String& path, double expectedModificationTime)
136{
137    long long size = m_stream->getSize(path, expectedModificationTime);
138    callOnMainThread(didGetSize, AllowCrossThreadAccess(this), size);
139}
140
141static void didOpen(AsyncFileStream* proxy, bool success)
142{
143    if (proxy->client())
144        proxy->client()->didOpen(success);
145}
146
147void AsyncFileStream::openForRead(const String& path, long long offset, long long length)
148{
149    fileThread()->postTask(createFileThreadTask(this, &AsyncFileStream::openForReadOnFileThread, path, offset, length));
150}
151
152void AsyncFileStream::openForReadOnFileThread(const String& path, long long offset, long long length)
153{
154    bool success = m_stream->openForRead(path, offset, length);
155    callOnMainThread(didOpen, AllowCrossThreadAccess(this), success);
156}
157
158void AsyncFileStream::openForWrite(const String& path)
159{
160    fileThread()->postTask(
161        createFileThreadTask(this,
162                             &AsyncFileStream::openForWriteOnFileThread, path));
163}
164
165void AsyncFileStream::openForWriteOnFileThread(const String& path)
166{
167    bool success = m_stream->openForWrite(path);
168    callOnMainThread(didOpen, AllowCrossThreadAccess(this), success);
169}
170
171void AsyncFileStream::close()
172{
173    fileThread()->postTask(createFileThreadTask(this, &AsyncFileStream::closeOnFileThread));
174}
175
176void AsyncFileStream::closeOnFileThread()
177{
178    m_stream->close();
179}
180
181static void didRead(AsyncFileStream* proxy, int bytesRead)
182{
183    if (proxy->client())
184        proxy->client()->didRead(bytesRead);
185}
186
187void AsyncFileStream::read(char* buffer, int length)
188{
189    fileThread()->postTask(
190        createFileThreadTask(this, &AsyncFileStream::readOnFileThread,
191                             AllowCrossThreadAccess(buffer), length));
192}
193
194void AsyncFileStream::readOnFileThread(char* buffer, int length)
195{
196    int bytesRead = m_stream->read(buffer, length);
197    callOnMainThread(didRead, AllowCrossThreadAccess(this), bytesRead);
198}
199
200static void didWrite(AsyncFileStream* proxy, int bytesWritten)
201{
202    if (proxy->client())
203        proxy->client()->didWrite(bytesWritten);
204}
205
206void AsyncFileStream::write(const KURL& blobURL, long long position, int length)
207{
208    fileThread()->postTask(createFileThreadTask(this, &AsyncFileStream::writeOnFileThread, blobURL, position, length));
209}
210
211void AsyncFileStream::writeOnFileThread(const KURL& blobURL, long long position, int length)
212{
213    int bytesWritten = m_stream->write(blobURL, position, length);
214    callOnMainThread(didWrite, AllowCrossThreadAccess(this), bytesWritten);
215}
216
217static void didTruncate(AsyncFileStream* proxy, bool success)
218{
219    if (proxy->client())
220        proxy->client()->didTruncate(success);
221}
222
223void AsyncFileStream::truncate(long long position)
224{
225    fileThread()->postTask(createFileThreadTask(this, &AsyncFileStream::truncateOnFileThread, position));
226}
227
228void AsyncFileStream::truncateOnFileThread(long long position)
229{
230    bool success = m_stream->truncate(position);
231    callOnMainThread(didTruncate, AllowCrossThreadAccess(this), success);
232}
233
234} // namespace WebCore
235
236#endif // ENABLE(BLOB)
237