blob: 3039116540b67abcdcb5340d0b7e25f05427906d [file] [log] [blame]
/*
* Copyright (C) 2010, 2012 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include "WorkerFileSystemCallbacksBridge.h"
#include "WebCommonWorkerClient.h"
#include "WebFileSystemCallbacksImpl.h"
#include "WebWorkerBase.h"
#include "bindings/v8/WorkerScriptController.h"
#include "core/dom/CrossThreadTask.h"
#include "core/platform/network/BlobData.h"
#include "core/workers/WorkerGlobalScope.h"
#include "core/workers/WorkerLoaderProxy.h"
#include "core/workers/WorkerThread.h"
#include "public/platform/WebFileInfo.h"
#include "public/platform/WebFileSystemEntry.h"
#include "public/platform/WebString.h"
#include "public/platform/WebURL.h"
#include "weborigin/KURL.h"
#include "wtf/MainThread.h"
#include "wtf/Threading.h"
#include "wtf/UnusedParam.h"
namespace WebCore {
template<> struct CrossThreadCopierBase<false, false, WebKit::WebFileInfo> {
typedef WebKit::WebFileInfo Type;
static Type copy(const WebKit::WebFileInfo& info)
{
// Perform per-field copy to make sure we don't do any (unexpected) non-thread safe copy here.
struct WebKit::WebFileInfo newInfo;
newInfo.modificationTime = info.modificationTime;
newInfo.length = info.length;
newInfo.type = info.type;
newInfo.platformPath.assign(info.platformPath);
return newInfo;
}
};
template<> struct CrossThreadCopierBase<false, false, WebKit::WebVector<WebKit::WebFileSystemEntry> > {
typedef WebKit::WebVector<WebKit::WebFileSystemEntry> Type;
static Type copy(const WebKit::WebVector<WebKit::WebFileSystemEntry>& entries)
{
WebKit::WebVector<WebKit::WebFileSystemEntry> newEntries(entries.size());
for (size_t i = 0; i < entries.size(); ++i) {
String name = entries[i].name;
newEntries[i].isDirectory = entries[i].isDirectory;
newEntries[i].name = name.isolatedCopy();
}
return newEntries;
}
};
}
using namespace WebCore;
namespace WebKit {
// FileSystemCallbacks that are to be dispatched on the main thread.
class MainThreadFileSystemCallbacks : public WebFileSystemCallbacks {
public:
// Callbacks are self-destructed and we always return leaked pointer here.
static MainThreadFileSystemCallbacks* createLeakedPtr(PassRefPtr<WorkerFileSystemCallbacksBridge> bridge, const String& mode)
{
OwnPtr<MainThreadFileSystemCallbacks> callbacks = adoptPtr(new MainThreadFileSystemCallbacks(bridge, mode));
return callbacks.leakPtr();
}
virtual ~MainThreadFileSystemCallbacks()
{
}
virtual void didOpenFileSystem(const WebString& name, const WebURL& rootURL)
{
m_bridge->didOpenFileSystemOnMainThread(name, rootURL, m_mode);
delete this;
}
virtual void didFail(WebFileError error)
{
m_bridge->didFailOnMainThread(error, m_mode);
delete this;
}
virtual void didSucceed()
{
m_bridge->didSucceedOnMainThread(m_mode);
delete this;
}
virtual void didReadMetadata(const WebFileInfo& info)
{
m_bridge->didReadMetadataOnMainThread(info, m_mode);
delete this;
}
virtual void didCreateSnapshotFile(const WebFileInfo& info)
{
// It's important to create a BlobDataHandle that refers to the platform file path prior
// to return from this method so the underlying file will not be deleted.
OwnPtr<BlobData> blobData = BlobData::create();
blobData->appendFile(info.platformPath);
RefPtr<BlobDataHandle> snapshotBlob = BlobDataHandle::create(blobData.release(), info.length);
m_bridge->didCreateSnapshotFileOnMainThread(info, m_mode, snapshotBlob);
delete this;
}
virtual void didReadDirectory(const WebVector<WebFileSystemEntry>& entries, bool hasMore)
{
m_bridge->didReadDirectoryOnMainThread(entries, hasMore, m_mode);
delete this;
}
virtual bool shouldBlockUntilCompletion() const
{
return false;
}
private:
MainThreadFileSystemCallbacks(PassRefPtr<WorkerFileSystemCallbacksBridge> bridge, const String& mode)
: m_bridge(bridge)
, m_mode(mode)
{
ASSERT(m_bridge);
}
RefPtr<WorkerFileSystemCallbacksBridge> m_bridge;
const String m_mode;
};
// Observes the worker context. By keeping this separate, it is easier to verify
// that it only gets deleted on the worker context thread which is verified by ~Observer.
class WorkerFileSystemContextObserver : public WebCore::WorkerGlobalScope::Observer {
public:
static PassOwnPtr<WorkerFileSystemContextObserver> create(WorkerGlobalScope* context, PassRefPtr<WorkerFileSystemCallbacksBridge> bridge)
{
return adoptPtr(new WorkerFileSystemContextObserver(context, bridge));
}
// WorkerGlobalScope::Observer method.
virtual void notifyStop()
{
m_bridge->stop();
}
private:
WorkerFileSystemContextObserver(WorkerGlobalScope* context, PassRefPtr<WorkerFileSystemCallbacksBridge> bridge)
: WebCore::WorkerGlobalScope::Observer(context)
, m_bridge(bridge)
{
}
RefPtr<WorkerFileSystemCallbacksBridge> m_bridge;
};
void WorkerFileSystemCallbacksBridge::stop()
{
ASSERT(m_workerGlobalScope->isContextThread());
{
MutexLocker locker(m_loaderProxyMutex);
m_workerLoaderProxy = 0;
}
if (m_callbacksOnWorkerThread)
m_callbacksOnWorkerThread->didFail(WebFileErrorAbort);
cleanUpAfterCallback();
}
void WorkerFileSystemCallbacksBridge::cleanUpAfterCallback()
{
ASSERT(m_workerGlobalScope->isContextThread());
m_callbacksOnWorkerThread = 0;
if (m_workerGlobalScopeObserver) {
WorkerFileSystemContextObserver* observer = m_workerGlobalScopeObserver;
m_workerGlobalScopeObserver = 0;
// The next line may delete this.
delete observer;
}
}
void WorkerFileSystemCallbacksBridge::postOpenFileSystemToMainThread(WebCommonWorkerClient* commonClient, WebFileSystemType type, long long size, bool create, const String& mode)
{
dispatchTaskToMainThread(
createCallbackTask(&openFileSystemOnMainThread,
AllowCrossThreadAccess(commonClient), type, size, create,
this, mode));
}
void WorkerFileSystemCallbacksBridge::postMoveToMainThread(WebFileSystem* fileSystem, const KURL& sourcePath, const KURL& destinationPath, const String& mode)
{
dispatchTaskToMainThread(
createCallbackTask(&moveOnMainThread,
AllowCrossThreadAccess(fileSystem), sourcePath, destinationPath,
this, mode));
}
void WorkerFileSystemCallbacksBridge::postCopyToMainThread(WebFileSystem* fileSystem, const KURL& sourcePath, const KURL& destinationPath, const String& mode)
{
dispatchTaskToMainThread(
createCallbackTask(&copyOnMainThread,
AllowCrossThreadAccess(fileSystem), sourcePath, destinationPath,
this, mode));
}
void WorkerFileSystemCallbacksBridge::postRemoveToMainThread(WebFileSystem* fileSystem, const KURL& path, const String& mode)
{
ASSERT(fileSystem);
dispatchTaskToMainThread(
createCallbackTask(&removeOnMainThread,
AllowCrossThreadAccess(fileSystem), path,
this, mode));
}
void WorkerFileSystemCallbacksBridge::postRemoveRecursivelyToMainThread(WebFileSystem* fileSystem, const KURL& path, const String& mode)
{
ASSERT(fileSystem);
dispatchTaskToMainThread(
createCallbackTask(&removeRecursivelyOnMainThread,
AllowCrossThreadAccess(fileSystem), path,
this, mode));
}
void WorkerFileSystemCallbacksBridge::postReadMetadataToMainThread(WebFileSystem* fileSystem, const KURL& path, const String& mode)
{
ASSERT(fileSystem);
dispatchTaskToMainThread(
createCallbackTask(&readMetadataOnMainThread,
AllowCrossThreadAccess(fileSystem), path,
this, mode));
}
void WorkerFileSystemCallbacksBridge::postCreateFileToMainThread(WebFileSystem* fileSystem, const KURL& path, bool exclusive, const String& mode)
{
dispatchTaskToMainThread(
createCallbackTask(&createFileOnMainThread,
AllowCrossThreadAccess(fileSystem), path, exclusive,
this, mode));
}
void WorkerFileSystemCallbacksBridge::postCreateDirectoryToMainThread(WebFileSystem* fileSystem, const KURL& path, bool exclusive, const String& mode)
{
ASSERT(fileSystem);
dispatchTaskToMainThread(
createCallbackTask(&createDirectoryOnMainThread,
AllowCrossThreadAccess(fileSystem), path, exclusive,
this, mode));
}
void WorkerFileSystemCallbacksBridge::postFileExistsToMainThread(WebFileSystem* fileSystem, const KURL& path, const String& mode)
{
ASSERT(fileSystem);
dispatchTaskToMainThread(
createCallbackTask(&fileExistsOnMainThread,
AllowCrossThreadAccess(fileSystem), path,
this, mode));
}
void WorkerFileSystemCallbacksBridge::postDirectoryExistsToMainThread(WebFileSystem* fileSystem, const KURL& path, const String& mode)
{
ASSERT(fileSystem);
dispatchTaskToMainThread(
createCallbackTask(&directoryExistsOnMainThread,
AllowCrossThreadAccess(fileSystem), path,
this, mode));
}
void WorkerFileSystemCallbacksBridge::postReadDirectoryToMainThread(WebFileSystem* fileSystem, const KURL& path, const String& mode)
{
ASSERT(fileSystem);
dispatchTaskToMainThread(
createCallbackTask(&readDirectoryOnMainThread,
AllowCrossThreadAccess(fileSystem), path,
this, mode));
}
void WorkerFileSystemCallbacksBridge::postCreateSnapshotFileToMainThread(WebFileSystem* fileSystem, const KURL& path, const String& mode)
{
ASSERT(fileSystem);
dispatchTaskToMainThread(
createCallbackTask(&createSnapshotFileOnMainThread,
AllowCrossThreadAccess(fileSystem),
path, this, mode));
}
void WorkerFileSystemCallbacksBridge::openFileSystemOnMainThread(ScriptExecutionContext*, WebCommonWorkerClient* commonClient, WebFileSystemType type, long long size, bool create, PassRefPtr<WorkerFileSystemCallbacksBridge> bridge, const String& mode)
{
if (!commonClient)
bridge->didFailOnMainThread(WebFileErrorAbort, mode);
else {
commonClient->openFileSystem(type, size, create, MainThreadFileSystemCallbacks::createLeakedPtr(bridge, mode));
}
}
void WorkerFileSystemCallbacksBridge::moveOnMainThread(WebCore::ScriptExecutionContext*, WebFileSystem* fileSystem, const KURL& sourcePath, const KURL& destinationPath, PassRefPtr<WorkerFileSystemCallbacksBridge> bridge, const String& mode)
{
fileSystem->move(sourcePath, destinationPath, MainThreadFileSystemCallbacks::createLeakedPtr(bridge, mode));
}
void WorkerFileSystemCallbacksBridge::copyOnMainThread(WebCore::ScriptExecutionContext*, WebFileSystem* fileSystem, const KURL& sourcePath, const KURL& destinationPath, PassRefPtr<WorkerFileSystemCallbacksBridge> bridge, const String& mode)
{
fileSystem->copy(sourcePath, destinationPath, MainThreadFileSystemCallbacks::createLeakedPtr(bridge, mode));
}
void WorkerFileSystemCallbacksBridge::removeOnMainThread(WebCore::ScriptExecutionContext*, WebFileSystem* fileSystem, const KURL& path, PassRefPtr<WorkerFileSystemCallbacksBridge> bridge, const String& mode)
{
fileSystem->remove(path, MainThreadFileSystemCallbacks::createLeakedPtr(bridge, mode));
}
void WorkerFileSystemCallbacksBridge::removeRecursivelyOnMainThread(WebCore::ScriptExecutionContext*, WebFileSystem* fileSystem, const KURL& path, PassRefPtr<WorkerFileSystemCallbacksBridge> bridge, const String& mode)
{
fileSystem->removeRecursively(path, MainThreadFileSystemCallbacks::createLeakedPtr(bridge, mode));
}
void WorkerFileSystemCallbacksBridge::readMetadataOnMainThread(WebCore::ScriptExecutionContext*, WebFileSystem* fileSystem, const KURL& path, PassRefPtr<WorkerFileSystemCallbacksBridge> bridge, const String& mode)
{
fileSystem->readMetadata(path, MainThreadFileSystemCallbacks::createLeakedPtr(bridge, mode));
}
void WorkerFileSystemCallbacksBridge::createFileOnMainThread(WebCore::ScriptExecutionContext*, WebFileSystem* fileSystem, const KURL& path, bool exclusive, PassRefPtr<WorkerFileSystemCallbacksBridge> bridge, const String& mode)
{
fileSystem->createFile(path, exclusive, MainThreadFileSystemCallbacks::createLeakedPtr(bridge, mode));
}
void WorkerFileSystemCallbacksBridge::createDirectoryOnMainThread(WebCore::ScriptExecutionContext*, WebFileSystem* fileSystem, const KURL& path, bool exclusive, PassRefPtr<WorkerFileSystemCallbacksBridge> bridge, const String& mode)
{
fileSystem->createDirectory(path, exclusive, MainThreadFileSystemCallbacks::createLeakedPtr(bridge, mode));
}
void WorkerFileSystemCallbacksBridge::fileExistsOnMainThread(WebCore::ScriptExecutionContext*, WebFileSystem* fileSystem, const KURL& path, PassRefPtr<WorkerFileSystemCallbacksBridge> bridge, const String& mode)
{
fileSystem->fileExists(path, MainThreadFileSystemCallbacks::createLeakedPtr(bridge, mode));
}
void WorkerFileSystemCallbacksBridge::directoryExistsOnMainThread(WebCore::ScriptExecutionContext*, WebFileSystem* fileSystem, const KURL& path, PassRefPtr<WorkerFileSystemCallbacksBridge> bridge, const String& mode)
{
fileSystem->directoryExists(path, MainThreadFileSystemCallbacks::createLeakedPtr(bridge, mode));
}
void WorkerFileSystemCallbacksBridge::readDirectoryOnMainThread(WebCore::ScriptExecutionContext*, WebFileSystem* fileSystem, const KURL& path, PassRefPtr<WorkerFileSystemCallbacksBridge> bridge, const String& mode)
{
fileSystem->readDirectory(path, MainThreadFileSystemCallbacks::createLeakedPtr(bridge, mode));
}
void WorkerFileSystemCallbacksBridge::createSnapshotFileOnMainThread(WebCore::ScriptExecutionContext*, WebFileSystem* fileSystem, const KURL& path, PassRefPtr<WorkerFileSystemCallbacksBridge> bridge, const String& mode)
{
fileSystem->createSnapshotFileAndReadMetadata(path, MainThreadFileSystemCallbacks::createLeakedPtr(bridge, mode));
}
void WorkerFileSystemCallbacksBridge::didFailOnMainThread(WebFileError error, const String& mode)
{
mayPostTaskToWorker(createCallbackTask(&didFailOnWorkerThread, this, error), mode);
}
void WorkerFileSystemCallbacksBridge::didOpenFileSystemOnMainThread(const String& name, const KURL& rootURL, const String& mode)
{
mayPostTaskToWorker(createCallbackTask(&didOpenFileSystemOnWorkerThread,
this, name, rootURL), mode);
}
void WorkerFileSystemCallbacksBridge::didSucceedOnMainThread(const String& mode)
{
mayPostTaskToWorker(createCallbackTask(&didSucceedOnWorkerThread, this), mode);
}
void WorkerFileSystemCallbacksBridge::didReadMetadataOnMainThread(const WebFileInfo& info, const String& mode)
{
mayPostTaskToWorker(createCallbackTask(&didReadMetadataOnWorkerThread, this, info), mode);
}
void WorkerFileSystemCallbacksBridge::didCreateSnapshotFileOnMainThread(const WebFileInfo& info, const String& mode, PassRefPtr<BlobDataHandle> snapshotBlob)
{
mayPostTaskToWorker(createCallbackTask(&didCreateSnapshotFileOnWorkerThread, this, info, snapshotBlob), mode);
}
void WorkerFileSystemCallbacksBridge::didReadDirectoryOnMainThread(const WebVector<WebFileSystemEntry>& entries, bool hasMore, const String& mode)
{
mayPostTaskToWorker(
createCallbackTask(&didReadDirectoryOnWorkerThread,
this, entries, hasMore), mode);
}
WorkerFileSystemCallbacksBridge::WorkerFileSystemCallbacksBridge(WebCore::WorkerLoaderProxy* workerLoaderProxy, ScriptExecutionContext* scriptExecutionContext, WebFileSystemCallbacksImpl* callbacks)
: m_workerLoaderProxy(workerLoaderProxy)
, m_workerGlobalScope(scriptExecutionContext)
, m_workerGlobalScopeObserver(WorkerFileSystemContextObserver::create(toWorkerGlobalScope(m_workerGlobalScope), this).leakPtr())
, m_callbacksOnWorkerThread(callbacks)
{
ASSERT(m_workerGlobalScope->isContextThread());
}
WorkerFileSystemCallbacksBridge::~WorkerFileSystemCallbacksBridge()
{
ASSERT(!m_callbacksOnWorkerThread);
}
void WorkerFileSystemCallbacksBridge::didFailOnWorkerThread(ScriptExecutionContext*, PassRefPtr<WorkerFileSystemCallbacksBridge> bridge, WebFileError error)
{
bridge->m_callbacksOnWorkerThread->didFail(error);
}
void WorkerFileSystemCallbacksBridge::didOpenFileSystemOnWorkerThread(ScriptExecutionContext*, PassRefPtr<WorkerFileSystemCallbacksBridge> bridge, const String& name, const KURL& rootURL)
{
bridge->m_callbacksOnWorkerThread->didOpenFileSystem(name, rootURL);
}
void WorkerFileSystemCallbacksBridge::didSucceedOnWorkerThread(ScriptExecutionContext*, PassRefPtr<WorkerFileSystemCallbacksBridge> bridge)
{
bridge->m_callbacksOnWorkerThread->didSucceed();
}
void WorkerFileSystemCallbacksBridge::didReadMetadataOnWorkerThread(ScriptExecutionContext*, PassRefPtr<WorkerFileSystemCallbacksBridge> bridge, const WebFileInfo& info)
{
bridge->m_callbacksOnWorkerThread->didReadMetadata(info);
}
void WorkerFileSystemCallbacksBridge::didCreateSnapshotFileOnWorkerThread(ScriptExecutionContext*, PassRefPtr<WorkerFileSystemCallbacksBridge> bridge, const WebFileInfo& info, PassRefPtr<BlobDataHandle> snapshotBlob)
{
bridge->m_callbacksOnWorkerThread->didCreateSnapshotFile(info, snapshotBlob);
}
void WorkerFileSystemCallbacksBridge::didReadDirectoryOnWorkerThread(ScriptExecutionContext*, PassRefPtr<WorkerFileSystemCallbacksBridge> bridge, const WebVector<WebFileSystemEntry>& entries, bool hasMore)
{
bridge->m_callbacksOnWorkerThread->didReadDirectory(entries, hasMore);
}
void WorkerFileSystemCallbacksBridge::runTaskOnMainThread(WebCore::ScriptExecutionContext* scriptExecutionContext, PassRefPtr<WorkerFileSystemCallbacksBridge> bridge, PassOwnPtr<WebCore::ScriptExecutionContext::Task> taskToRun)
{
ASSERT(isMainThread());
taskToRun->performTask(scriptExecutionContext);
}
void WorkerFileSystemCallbacksBridge::runTaskOnWorkerThread(WebCore::ScriptExecutionContext* scriptExecutionContext, PassRefPtr<WorkerFileSystemCallbacksBridge> bridge, PassOwnPtr<WebCore::ScriptExecutionContext::Task> taskToRun)
{
if (!bridge->m_callbacksOnWorkerThread)
return;
ASSERT(bridge->m_workerGlobalScope->isContextThread());
taskToRun->performTask(scriptExecutionContext);
// taskToRun does the callback.
bridge->cleanUpAfterCallback();
// WorkerFileSystemCallbacksBridge may be deleted here when bridge goes out of scope.
}
void WorkerFileSystemCallbacksBridge::dispatchTaskToMainThread(PassOwnPtr<WebCore::ScriptExecutionContext::Task> task)
{
ASSERT(m_workerLoaderProxy);
ASSERT(m_workerGlobalScope->isContextThread());
WebWorkerBase::dispatchTaskToMainThread(createCallbackTask(&runTaskOnMainThread, RefPtr<WorkerFileSystemCallbacksBridge>(this).release(), task));
}
void WorkerFileSystemCallbacksBridge::mayPostTaskToWorker(PassOwnPtr<ScriptExecutionContext::Task> task, const String& mode)
{
// Relies on its caller (MainThreadFileSystemCallbacks:did*) to keep WorkerFileSystemCallbacksBridge alive.
ASSERT(isMainThread());
MutexLocker locker(m_loaderProxyMutex);
if (m_workerLoaderProxy)
m_workerLoaderProxy->postTaskForModeToWorkerGlobalScope(createCallbackTask(&runTaskOnWorkerThread, this, task), mode);
}
} // namespace WebCore