| /* |
| * Copyright (C) 2015 Apple 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: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. 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. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. |
| */ |
| |
| #pragma once |
| |
| #if ENABLE(INDEXED_DATABASE) |
| |
| #include "IDBRequest.h" |
| #include "IDBRequestData.h" |
| #include "IDBResourceIdentifier.h" |
| #include "IDBResultData.h" |
| #include "IDBTransaction.h" |
| #include <wtf/Function.h> |
| #include <wtf/MainThread.h> |
| #include <wtf/Threading.h> |
| |
| namespace WebCore { |
| |
| class IDBResultData; |
| |
| namespace IndexedDB { |
| enum class IndexRecordType; |
| } |
| |
| namespace IDBClient { |
| |
| class TransactionOperation : public ThreadSafeRefCounted<TransactionOperation> { |
| friend IDBRequestData::IDBRequestData(TransactionOperation&); |
| public: |
| virtual ~TransactionOperation() |
| { |
| ASSERT(m_originThread.ptr() == &Thread::current()); |
| } |
| |
| void perform() |
| { |
| ASSERT(m_originThread.ptr() == &Thread::current()); |
| ASSERT(m_performFunction); |
| m_performFunction(); |
| m_performFunction = { }; |
| } |
| |
| void transitionToCompleteOnThisThread(const IDBResultData& data) |
| { |
| ASSERT(m_originThread.ptr() == &Thread::current()); |
| m_transaction->operationCompletedOnServer(data, *this); |
| } |
| |
| void transitionToComplete(const IDBResultData& data, RefPtr<TransactionOperation>&& lastRef) |
| { |
| ASSERT(isMainThread()); |
| |
| if (m_originThread.ptr() == &Thread::current()) |
| transitionToCompleteOnThisThread(data); |
| else { |
| m_transaction->performCallbackOnOriginThread(*this, &TransactionOperation::transitionToCompleteOnThisThread, data); |
| m_transaction->callFunctionOnOriginThread([lastRef = WTFMove(lastRef)]() { |
| }); |
| } |
| } |
| |
| void doComplete(const IDBResultData& data) |
| { |
| ASSERT(m_originThread.ptr() == &Thread::current()); |
| |
| if (m_performFunction) |
| m_performFunction = { }; |
| |
| // Due to race conditions between the server sending an "operation complete" message and the client |
| // forcefully aborting an operation, it's unavoidable that this method might be called twice. |
| // It's okay to handle that gracefully with an early return. |
| if (m_didComplete) |
| return; |
| m_didComplete = true; |
| |
| if (m_completeFunction) { |
| m_completeFunction(data); |
| // m_completeFunction should not hold ref to this TransactionOperation after its execution. |
| m_completeFunction = { }; |
| } |
| m_transaction->operationCompletedOnClient(*this); |
| } |
| |
| const IDBResourceIdentifier& identifier() const { return m_identifier; } |
| |
| Thread& originThread() const { return m_originThread.get(); } |
| |
| IDBRequest* idbRequest() { return m_idbRequest.get(); } |
| |
| bool nextRequestCanGoToServer() const { return m_nextRequestCanGoToServer && m_idbRequest; } |
| void setNextRequestCanGoToServer(bool nextRequestCanGoToServer) { m_nextRequestCanGoToServer = nextRequestCanGoToServer; } |
| |
| protected: |
| TransactionOperation(IDBTransaction& transaction) |
| : m_transaction(transaction) |
| , m_identifier(transaction.connectionProxy()) |
| { |
| } |
| |
| TransactionOperation(IDBTransaction&, IDBRequest&); |
| |
| Ref<IDBTransaction> m_transaction; |
| IDBResourceIdentifier m_identifier; |
| uint64_t m_objectStoreIdentifier { 0 }; |
| uint64_t m_indexIdentifier { 0 }; |
| std::unique_ptr<IDBResourceIdentifier> m_cursorIdentifier; |
| IndexedDB::IndexRecordType m_indexRecordType; |
| Function<void()> m_performFunction; |
| Function<void(const IDBResultData&)> m_completeFunction; |
| |
| private: |
| IDBResourceIdentifier transactionIdentifier() const { return m_transaction->info().identifier(); } |
| uint64_t objectStoreIdentifier() const { return m_objectStoreIdentifier; } |
| uint64_t indexIdentifier() const { return m_indexIdentifier; } |
| IDBResourceIdentifier* cursorIdentifier() const { return m_cursorIdentifier.get(); } |
| IDBTransaction& transaction() { return m_transaction.get(); } |
| IndexedDB::IndexRecordType indexRecordType() const { return m_indexRecordType; } |
| |
| Ref<Thread> m_originThread { Thread::current() }; |
| RefPtr<IDBRequest> m_idbRequest; |
| bool m_nextRequestCanGoToServer { true }; |
| bool m_didComplete { false }; |
| }; |
| |
| class TransactionOperationImpl final : public TransactionOperation { |
| public: |
| template<typename... Args> static Ref<TransactionOperationImpl> create(Args&&... args) { return adoptRef(*new TransactionOperationImpl(std::forward<Args>(args)...)); } |
| private: |
| TransactionOperationImpl(IDBTransaction& transaction, Function<void(const IDBResultData&)> completeMethod, Function<void(TransactionOperation&)> performMethod) |
| : TransactionOperation(transaction) |
| { |
| ASSERT(performMethod); |
| m_performFunction = [protectedThis = makeRef(*this), performMethod = WTFMove(performMethod)] { |
| performMethod(protectedThis.get()); |
| }; |
| |
| if (completeMethod) { |
| m_completeFunction = [protectedThis = makeRef(*this), completeMethod = WTFMove(completeMethod)] (const IDBResultData& resultData) { |
| completeMethod(resultData); |
| }; |
| } |
| } |
| |
| TransactionOperationImpl(IDBTransaction& transaction, IDBRequest& request, Function<void(const IDBResultData&)> completeMethod, Function<void(TransactionOperation&)> performMethod) |
| : TransactionOperation(transaction, request) |
| { |
| ASSERT(performMethod); |
| m_performFunction = [protectedThis = makeRef(*this), performMethod = WTFMove(performMethod)] { |
| performMethod(protectedThis.get()); |
| }; |
| |
| if (completeMethod) { |
| m_completeFunction = [protectedThis = makeRef(*this), completeMethod = WTFMove(completeMethod)] (const IDBResultData& resultData) { |
| completeMethod(resultData); |
| }; |
| } |
| } |
| }; |
| |
| } // namespace IDBClient |
| } // namespace WebCore |
| |
| #endif // ENABLE(INDEXED_DATABASE) |