Home | History | Annotate | Download | only in web
      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 #include "WebFileWriter.h"
     35 #include "WebWorkerBase.h"
     36 #include "core/dom/CrossThreadTask.h"
     37 #include "core/workers/WorkerGlobalScope.h"
     38 #include "core/workers/WorkerLoaderProxy.h"
     39 #include "core/workers/WorkerThread.h"
     40 #include "modules/filesystem/AsyncFileWriterClient.h"
     41 #include "public/platform/Platform.h"
     42 #include "public/platform/WebCString.h"
     43 #include "public/platform/WebFileSystem.h"
     44 #include "wtf/MainThread.h"
     45 #include "wtf/Threading.h"
     46 
     47 using namespace WebCore;
     48 
     49 namespace WebKit {
     50 
     51 void WorkerFileWriterCallbacksBridge::notifyStop()
     52 {
     53     ASSERT(m_workerGlobalScope->isContextThread());
     54     m_clientOnWorkerThread = 0;
     55     {
     56         MutexLocker locker(m_loaderProxyMutex);
     57         m_proxy = 0;
     58     }
     59 }
     60 
     61 void WorkerFileWriterCallbacksBridge::postWriteToMainThread(long long position, const KURL& data)
     62 {
     63     ASSERT(!m_operationInProgress);
     64     m_operationInProgress = true;
     65     dispatchTaskToMainThread(createCallbackTask(&writeOnMainThread,
     66                                                 this, position, data));
     67 }
     68 
     69 void WorkerFileWriterCallbacksBridge::postTruncateToMainThread(long long length)
     70 {
     71     ASSERT(!m_operationInProgress);
     72     m_operationInProgress = true;
     73     dispatchTaskToMainThread(createCallbackTask(&truncateOnMainThread,
     74                                                 this, length));
     75 }
     76 
     77 void WorkerFileWriterCallbacksBridge::postAbortToMainThread()
     78 {
     79     ASSERT(m_operationInProgress);
     80     dispatchTaskToMainThread(createCallbackTask(&abortOnMainThread, this));
     81 }
     82 
     83 void WorkerFileWriterCallbacksBridge::postShutdownToMainThread(PassRefPtr<WorkerFileWriterCallbacksBridge> bridge)
     84 {
     85     ASSERT(m_workerGlobalScope->isContextThread());
     86     m_clientOnWorkerThread = 0;
     87     stopObserving();
     88     dispatchTaskToMainThread(createCallbackTask(&shutdownOnMainThread, bridge));
     89 }
     90 
     91 void WorkerFileWriterCallbacksBridge::writeOnMainThread(ScriptExecutionContext*, PassRefPtr<WorkerFileWriterCallbacksBridge> bridge, long long position, const KURL& data)
     92 {
     93     bridge->m_writer->write(position, WebURL(data));
     94 }
     95 
     96 void WorkerFileWriterCallbacksBridge::truncateOnMainThread(ScriptExecutionContext*, PassRefPtr<WorkerFileWriterCallbacksBridge> bridge, long long length)
     97 {
     98     bridge->m_writer->truncate(length);
     99 }
    100 
    101 void WorkerFileWriterCallbacksBridge::abortOnMainThread(ScriptExecutionContext*, PassRefPtr<WorkerFileWriterCallbacksBridge> bridge)
    102 {
    103     bridge->m_writer->cancel();
    104 }
    105 
    106 void WorkerFileWriterCallbacksBridge::initOnMainThread(ScriptExecutionContext*, PassRefPtr<WorkerFileWriterCallbacksBridge> bridge, const KURL& path)
    107 {
    108     ASSERT(!bridge->m_writer);
    109     bridge->m_writer = adoptPtr(WebKit::Platform::current()->fileSystem()->createFileWriter(path, bridge.get()));
    110 }
    111 
    112 void WorkerFileWriterCallbacksBridge::shutdownOnMainThread(ScriptExecutionContext*, PassRefPtr<WorkerFileWriterCallbacksBridge> bridge)
    113 {
    114     bridge->m_writerDeleted = true;
    115     bridge->m_writer.clear();
    116 }
    117 
    118 void WorkerFileWriterCallbacksBridge::didWrite(long long bytes, bool complete)
    119 {
    120     dispatchTaskToWorkerThread(createCallbackTask(&didWriteOnWorkerThread, this, bytes, complete));
    121 }
    122 
    123 void WorkerFileWriterCallbacksBridge::didFail(WebFileError error)
    124 {
    125     dispatchTaskToWorkerThread(createCallbackTask(&didFailOnWorkerThread, this, error));
    126 }
    127 
    128 void WorkerFileWriterCallbacksBridge::didTruncate()
    129 {
    130     dispatchTaskToWorkerThread(createCallbackTask(&didTruncateOnWorkerThread, this));
    131 }
    132 
    133 static const char fileWriterOperationsMode[] = "fileWriterOperationsMode";
    134 
    135 WorkerFileWriterCallbacksBridge::WorkerFileWriterCallbacksBridge(const KURL& path, WorkerLoaderProxy* proxy, ScriptExecutionContext* scriptExecutionContext, AsyncFileWriterClient* client)
    136     : WorkerGlobalScope::Observer(toWorkerGlobalScope(scriptExecutionContext))
    137     , m_proxy(proxy)
    138     , m_workerGlobalScope(scriptExecutionContext)
    139     , m_clientOnWorkerThread(client)
    140     , m_writerDeleted(false)
    141     , m_operationInProgress(false)
    142 {
    143     ASSERT(m_workerGlobalScope->isContextThread());
    144     m_mode = fileWriterOperationsMode;
    145     m_mode.append(String::number(toWorkerGlobalScope(scriptExecutionContext)->thread()->runLoop().createUniqueId()));
    146     postInitToMainThread(path);
    147 }
    148 
    149 void WorkerFileWriterCallbacksBridge::postInitToMainThread(const KURL& path)
    150 {
    151     dispatchTaskToMainThread(
    152         createCallbackTask(&initOnMainThread, this, path));
    153 }
    154 
    155 WorkerFileWriterCallbacksBridge::~WorkerFileWriterCallbacksBridge()
    156 {
    157     ASSERT(!m_clientOnWorkerThread);
    158     ASSERT(!m_writer);
    159 }
    160 
    161 // 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.
    162 void WorkerFileWriterCallbacksBridge::didWriteOnWorkerThread(ScriptExecutionContext*, PassRefPtr<WorkerFileWriterCallbacksBridge> bridge, long long length, bool complete)
    163 {
    164     ASSERT(bridge->m_workerGlobalScope->isContextThread());
    165     ASSERT(bridge->m_operationInProgress);
    166     if (complete)
    167         bridge->m_operationInProgress = false;
    168     bridge->m_clientOnWorkerThread->didWrite(length, complete);
    169 }
    170 
    171 void WorkerFileWriterCallbacksBridge::didFailOnWorkerThread(ScriptExecutionContext*, PassRefPtr<WorkerFileWriterCallbacksBridge> bridge, WebFileError error)
    172 {
    173     ASSERT(bridge->m_workerGlobalScope->isContextThread());
    174     ASSERT(bridge->m_operationInProgress);
    175     bridge->m_operationInProgress = false;
    176     bridge->m_clientOnWorkerThread->didFail(static_cast<FileError::ErrorCode>(error));
    177 }
    178 
    179 void WorkerFileWriterCallbacksBridge::didTruncateOnWorkerThread(ScriptExecutionContext*, PassRefPtr<WorkerFileWriterCallbacksBridge> bridge)
    180 {
    181     ASSERT(bridge->m_workerGlobalScope->isContextThread());
    182     ASSERT(bridge->m_operationInProgress);
    183     bridge->m_operationInProgress = false;
    184     bridge->m_clientOnWorkerThread->didTruncate();
    185 }
    186 
    187 void WorkerFileWriterCallbacksBridge::runTaskOnMainThread(ScriptExecutionContext* scriptExecutionContext, PassRefPtr<WorkerFileWriterCallbacksBridge> bridge, PassOwnPtr<ScriptExecutionContext::Task> taskToRun)
    188 {
    189     ASSERT(isMainThread());
    190     if (!bridge->m_writerDeleted)
    191         taskToRun->performTask(scriptExecutionContext);
    192 }
    193 
    194 void WorkerFileWriterCallbacksBridge::runTaskOnWorkerThread(ScriptExecutionContext* scriptExecutionContext, PassRefPtr<WorkerFileWriterCallbacksBridge> bridge, PassOwnPtr<ScriptExecutionContext::Task> taskToRun)
    195 {
    196     ASSERT(bridge->m_workerGlobalScope->isContextThread());
    197     if (bridge->m_clientOnWorkerThread)
    198         taskToRun->performTask(scriptExecutionContext);
    199 }
    200 
    201 void WorkerFileWriterCallbacksBridge::dispatchTaskToMainThread(PassOwnPtr<ScriptExecutionContext::Task> task)
    202 {
    203     ASSERT(m_workerGlobalScope->isContextThread());
    204     WebWorkerBase::dispatchTaskToMainThread(
    205         createCallbackTask(&runTaskOnMainThread, this, task));
    206 }
    207 
    208 void WorkerFileWriterCallbacksBridge::dispatchTaskToWorkerThread(PassOwnPtr<ScriptExecutionContext::Task> task)
    209 {
    210     ASSERT(isMainThread());
    211 
    212     MutexLocker locker(m_loaderProxyMutex);
    213     if (m_proxy)
    214         m_proxy->postTaskForModeToWorkerGlobalScope(
    215             createCallbackTask(&runTaskOnWorkerThread, this, task), m_mode);
    216 }
    217 
    218 bool WorkerFileWriterCallbacksBridge::waitForOperationToComplete()
    219 {
    220     while (m_operationInProgress) {
    221         WorkerGlobalScope* context = toWorkerGlobalScope(m_workerGlobalScope);
    222         if (context->thread()->runLoop().runInMode(context, m_mode) == MessageQueueTerminated)
    223             return false;
    224     }
    225     return true;
    226 }
    227 
    228 } // namespace WebKit
    229