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