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 "modules/filesystem/FileSystemCallbacks.h"
33
34#include "core/dom/ExecutionContext.h"
35#include "core/fileapi/File.h"
36#include "core/fileapi/FileError.h"
37#include "core/html/VoidCallback.h"
38#include "core/inspector/InspectorInstrumentation.h"
39#include "modules/filesystem/DOMFilePath.h"
40#include "modules/filesystem/DOMFileSystem.h"
41#include "modules/filesystem/DOMFileSystemBase.h"
42#include "modules/filesystem/DirectoryEntry.h"
43#include "modules/filesystem/DirectoryReader.h"
44#include "modules/filesystem/Entry.h"
45#include "modules/filesystem/EntryCallback.h"
46#include "modules/filesystem/ErrorCallback.h"
47#include "modules/filesystem/FileCallback.h"
48#include "modules/filesystem/FileEntry.h"
49#include "modules/filesystem/FileSystemCallback.h"
50#include "modules/filesystem/FileWriterBase.h"
51#include "modules/filesystem/FileWriterBaseCallback.h"
52#include "modules/filesystem/Metadata.h"
53#include "modules/filesystem/MetadataCallback.h"
54#include "platform/FileMetadata.h"
55#include "public/platform/WebFileWriter.h"
56
57namespace blink {
58
59FileSystemCallbacksBase::FileSystemCallbacksBase(ErrorCallback* errorCallback, DOMFileSystemBase* fileSystem, ExecutionContext* context)
60    : m_errorCallback(errorCallback)
61    , m_fileSystem(fileSystem)
62    , m_executionContext(context)
63    , m_asyncOperationId(0)
64{
65    if (m_fileSystem)
66        m_fileSystem->addPendingCallbacks();
67    if (m_executionContext)
68        m_asyncOperationId = InspectorInstrumentation::traceAsyncOperationStarting(m_executionContext.get(), "FileSystem");
69}
70
71FileSystemCallbacksBase::~FileSystemCallbacksBase()
72{
73    if (m_fileSystem)
74        m_fileSystem->removePendingCallbacks();
75    if (m_asyncOperationId && m_executionContext)
76        InspectorInstrumentation::traceAsyncOperationCompleted(m_executionContext.get(), m_asyncOperationId);
77}
78
79void FileSystemCallbacksBase::didFail(int code)
80{
81    if (m_errorCallback)
82        handleEventOrScheduleCallback(m_errorCallback.release(), FileError::create(static_cast<FileError::ErrorCode>(code)));
83}
84
85bool FileSystemCallbacksBase::shouldScheduleCallback() const
86{
87    return !shouldBlockUntilCompletion() && m_executionContext && m_executionContext->activeDOMObjectsAreSuspended();
88}
89
90#if !ENABLE(OILPAN)
91template <typename CB, typename CBArg>
92void FileSystemCallbacksBase::handleEventOrScheduleCallback(RawPtr<CB> callback, RawPtr<CBArg> arg)
93{
94    handleEventOrScheduleCallback(callback, arg.get());
95}
96#endif
97
98template <typename CB, typename CBArg>
99void FileSystemCallbacksBase::handleEventOrScheduleCallback(RawPtr<CB> callback, CBArg* arg)
100{
101    ASSERT(callback);
102    InspectorInstrumentationCookie cookie = InspectorInstrumentation::traceAsyncOperationCompletedCallbackStarting(m_executionContext.get(), m_asyncOperationId);
103    if (shouldScheduleCallback())
104        DOMFileSystem::scheduleCallback(m_executionContext.get(), callback.get(), arg);
105    else if (callback)
106        callback->handleEvent(arg);
107    m_executionContext.clear();
108    InspectorInstrumentation::traceAsyncCallbackCompleted(cookie);
109}
110
111template <typename CB, typename CBArg>
112void FileSystemCallbacksBase::handleEventOrScheduleCallback(RawPtr<CB> callback, PassRefPtrWillBeRawPtr<CBArg> arg)
113{
114    ASSERT(callback);
115    InspectorInstrumentationCookie cookie = InspectorInstrumentation::traceAsyncOperationCompletedCallbackStarting(m_executionContext.get(), m_asyncOperationId);
116    if (shouldScheduleCallback())
117        DOMFileSystem::scheduleCallback(m_executionContext.get(), callback.get(), arg);
118    else if (callback)
119        callback->handleEvent(arg.get());
120    m_executionContext.clear();
121    InspectorInstrumentation::traceAsyncCallbackCompleted(cookie);
122}
123
124template <typename CB>
125void FileSystemCallbacksBase::handleEventOrScheduleCallback(RawPtr<CB> callback)
126{
127    ASSERT(callback);
128    InspectorInstrumentationCookie cookie = InspectorInstrumentation::traceAsyncOperationCompletedCallbackStarting(m_executionContext.get(), m_asyncOperationId);
129    if (shouldScheduleCallback())
130        DOMFileSystem::scheduleCallback(m_executionContext.get(), callback.get());
131    else if (callback)
132        callback->handleEvent();
133    m_executionContext.clear();
134    InspectorInstrumentation::traceAsyncCallbackCompleted(cookie);
135}
136
137// EntryCallbacks -------------------------------------------------------------
138
139PassOwnPtr<AsyncFileSystemCallbacks> EntryCallbacks::create(EntryCallback* successCallback, ErrorCallback* errorCallback, ExecutionContext* context, DOMFileSystemBase* fileSystem, const String& expectedPath, bool isDirectory)
140{
141    return adoptPtr(new EntryCallbacks(successCallback, errorCallback, context, fileSystem, expectedPath, isDirectory));
142}
143
144EntryCallbacks::EntryCallbacks(EntryCallback* successCallback, ErrorCallback* errorCallback, ExecutionContext* context, DOMFileSystemBase* fileSystem, const String& expectedPath, bool isDirectory)
145    : FileSystemCallbacksBase(errorCallback, fileSystem, context)
146    , m_successCallback(successCallback)
147    , m_expectedPath(expectedPath)
148    , m_isDirectory(isDirectory)
149{
150}
151
152void EntryCallbacks::didSucceed()
153{
154    if (m_successCallback) {
155        if (m_isDirectory)
156            handleEventOrScheduleCallback(m_successCallback.release(), DirectoryEntry::create(m_fileSystem, m_expectedPath));
157        else
158            handleEventOrScheduleCallback(m_successCallback.release(), FileEntry::create(m_fileSystem, m_expectedPath));
159    }
160}
161
162// EntriesCallbacks -----------------------------------------------------------
163
164PassOwnPtr<AsyncFileSystemCallbacks> EntriesCallbacks::create(EntriesCallback* successCallback, ErrorCallback* errorCallback, ExecutionContext* context, DirectoryReaderBase* directoryReader, const String& basePath)
165{
166    return adoptPtr(new EntriesCallbacks(successCallback, errorCallback, context, directoryReader, basePath));
167}
168
169EntriesCallbacks::EntriesCallbacks(EntriesCallback* successCallback, ErrorCallback* errorCallback, ExecutionContext* context, DirectoryReaderBase* directoryReader, const String& basePath)
170    : FileSystemCallbacksBase(errorCallback, directoryReader->filesystem(), context)
171    , m_successCallback(successCallback)
172    , m_directoryReader(directoryReader)
173    , m_basePath(basePath)
174{
175    ASSERT(m_directoryReader);
176}
177
178void EntriesCallbacks::didReadDirectoryEntry(const String& name, bool isDirectory)
179{
180    if (isDirectory)
181        m_entries.append(DirectoryEntry::create(m_directoryReader->filesystem(), DOMFilePath::append(m_basePath, name)));
182    else
183        m_entries.append(FileEntry::create(m_directoryReader->filesystem(), DOMFilePath::append(m_basePath, name)));
184}
185
186void EntriesCallbacks::didReadDirectoryEntries(bool hasMore)
187{
188    m_directoryReader->setHasMoreEntries(hasMore);
189    EntryHeapVector entries;
190    entries.swap(m_entries);
191    // FIXME: delay the callback iff shouldScheduleCallback() is true.
192    InspectorInstrumentationCookie cookie = InspectorInstrumentation::traceAsyncCallbackStarting(m_executionContext.get(), m_asyncOperationId);
193    if (m_successCallback)
194        m_successCallback->handleEvent(entries);
195    InspectorInstrumentation::traceAsyncCallbackCompleted(cookie);
196    if (!hasMore)
197        InspectorInstrumentation::traceAsyncOperationCompleted(m_executionContext.get(), m_asyncOperationId);
198}
199
200// FileSystemCallbacks --------------------------------------------------------
201
202PassOwnPtr<AsyncFileSystemCallbacks> FileSystemCallbacks::create(FileSystemCallback* successCallback, ErrorCallback* errorCallback, ExecutionContext* context, FileSystemType type)
203{
204    return adoptPtr(new FileSystemCallbacks(successCallback, errorCallback, context, type));
205}
206
207FileSystemCallbacks::FileSystemCallbacks(FileSystemCallback* successCallback, ErrorCallback* errorCallback, ExecutionContext* context, FileSystemType type)
208    : FileSystemCallbacksBase(errorCallback, nullptr, context)
209    , m_successCallback(successCallback)
210    , m_type(type)
211{
212}
213
214void FileSystemCallbacks::didOpenFileSystem(const String& name, const KURL& rootURL)
215{
216    if (m_successCallback)
217        handleEventOrScheduleCallback(m_successCallback.release(), DOMFileSystem::create(m_executionContext.get(), name, m_type, rootURL));
218}
219
220// ResolveURICallbacks --------------------------------------------------------
221
222PassOwnPtr<AsyncFileSystemCallbacks> ResolveURICallbacks::create(EntryCallback* successCallback, ErrorCallback* errorCallback, ExecutionContext* context)
223{
224    return adoptPtr(new ResolveURICallbacks(successCallback, errorCallback, context));
225}
226
227ResolveURICallbacks::ResolveURICallbacks(EntryCallback* successCallback, ErrorCallback* errorCallback, ExecutionContext* context)
228    : FileSystemCallbacksBase(errorCallback, nullptr, context)
229    , m_successCallback(successCallback)
230{
231}
232
233void ResolveURICallbacks::didResolveURL(const String& name, const KURL& rootURL, FileSystemType type, const String& filePath, bool isDirectory)
234{
235    DOMFileSystem* filesystem = DOMFileSystem::create(m_executionContext.get(), name, type, rootURL);
236    DirectoryEntry* root = filesystem->root();
237
238    String absolutePath;
239    if (!DOMFileSystemBase::pathToAbsolutePath(type, root, filePath, absolutePath)) {
240        handleEventOrScheduleCallback(m_errorCallback.release(), FileError::create(FileError::INVALID_MODIFICATION_ERR));
241        return;
242    }
243
244    if (isDirectory)
245        handleEventOrScheduleCallback(m_successCallback.release(), DirectoryEntry::create(filesystem, absolutePath));
246    else
247        handleEventOrScheduleCallback(m_successCallback.release(), FileEntry::create(filesystem, absolutePath));
248}
249
250// MetadataCallbacks ----------------------------------------------------------
251
252PassOwnPtr<AsyncFileSystemCallbacks> MetadataCallbacks::create(MetadataCallback* successCallback, ErrorCallback* errorCallback, ExecutionContext* context, DOMFileSystemBase* fileSystem)
253{
254    return adoptPtr(new MetadataCallbacks(successCallback, errorCallback, context, fileSystem));
255}
256
257MetadataCallbacks::MetadataCallbacks(MetadataCallback* successCallback, ErrorCallback* errorCallback, ExecutionContext* context, DOMFileSystemBase* fileSystem)
258    : FileSystemCallbacksBase(errorCallback, fileSystem, context)
259    , m_successCallback(successCallback)
260{
261}
262
263void MetadataCallbacks::didReadMetadata(const FileMetadata& metadata)
264{
265    if (m_successCallback)
266        handleEventOrScheduleCallback(m_successCallback.release(), Metadata::create(metadata));
267}
268
269// FileWriterBaseCallbacks ----------------------------------------------------
270
271PassOwnPtr<AsyncFileSystemCallbacks> FileWriterBaseCallbacks::create(PassRefPtrWillBeRawPtr<FileWriterBase> fileWriter, FileWriterBaseCallback* successCallback, ErrorCallback* errorCallback, ExecutionContext* context)
272{
273    return adoptPtr(new FileWriterBaseCallbacks(fileWriter, successCallback, errorCallback, context));
274}
275
276FileWriterBaseCallbacks::FileWriterBaseCallbacks(PassRefPtrWillBeRawPtr<FileWriterBase> fileWriter, FileWriterBaseCallback* successCallback, ErrorCallback* errorCallback, ExecutionContext* context)
277    : FileSystemCallbacksBase(errorCallback, nullptr, context)
278    , m_fileWriter(fileWriter.get())
279    , m_successCallback(successCallback)
280{
281}
282
283void FileWriterBaseCallbacks::didCreateFileWriter(PassOwnPtr<WebFileWriter> fileWriter, long long length)
284{
285    m_fileWriter->initialize(fileWriter, length);
286    if (m_successCallback)
287        handleEventOrScheduleCallback(m_successCallback.release(), m_fileWriter.release());
288}
289
290// SnapshotFileCallback -------------------------------------------------------
291
292PassOwnPtr<AsyncFileSystemCallbacks> SnapshotFileCallback::create(DOMFileSystemBase* filesystem, const String& name, const KURL& url, FileCallback* successCallback, ErrorCallback* errorCallback, ExecutionContext* context)
293{
294    return adoptPtr(new SnapshotFileCallback(filesystem, name, url, successCallback, errorCallback, context));
295}
296
297SnapshotFileCallback::SnapshotFileCallback(DOMFileSystemBase* filesystem, const String& name, const KURL& url, FileCallback* successCallback, ErrorCallback* errorCallback, ExecutionContext* context)
298    : FileSystemCallbacksBase(errorCallback, filesystem, context)
299    , m_name(name)
300    , m_url(url)
301    , m_successCallback(successCallback)
302{
303}
304
305void SnapshotFileCallback::didCreateSnapshotFile(const FileMetadata& metadata, PassRefPtr<BlobDataHandle> snapshot)
306{
307    if (!m_successCallback)
308        return;
309
310    // We can't directly use the snapshot blob data handle because the content type on it hasn't been set.
311    // The |snapshot| param is here to provide a a chain of custody thru thread bridging that is held onto until
312    // *after* we've coined a File with a new handle that has the correct type set on it. This allows the
313    // blob storage system to track when a temp file can and can't be safely deleted.
314
315    handleEventOrScheduleCallback(m_successCallback.release(), DOMFileSystemBase::createFile(metadata, m_url, m_fileSystem->type(), m_name));
316}
317
318// VoidCallbacks --------------------------------------------------------------
319
320PassOwnPtr<AsyncFileSystemCallbacks> VoidCallbacks::create(VoidCallback* successCallback, ErrorCallback* errorCallback, ExecutionContext* context, DOMFileSystemBase* fileSystem)
321{
322    return adoptPtr(new VoidCallbacks(successCallback, errorCallback, context, fileSystem));
323}
324
325VoidCallbacks::VoidCallbacks(VoidCallback* successCallback, ErrorCallback* errorCallback, ExecutionContext* context, DOMFileSystemBase* fileSystem)
326    : FileSystemCallbacksBase(errorCallback, fileSystem, context)
327    , m_successCallback(successCallback)
328{
329}
330
331void VoidCallbacks::didSucceed()
332{
333    if (m_successCallback)
334        handleEventOrScheduleCallback(m_successCallback.release());
335}
336
337} // namespace blink
338