| /* |
| * 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()); |
| |
| // 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_completeFunction) |
| return; |
| |
| m_completeFunction(data); |
| m_transaction->operationCompletedOnClient(*this); |
| |
| // m_completeFunction might be holding the last ref to this TransactionOperation, |
| // so we need to do this trick to null it out without first destroying it. |
| WTF::Function<void (const IDBResultData&)> oldCompleteFunction; |
| std::swap(m_completeFunction, oldCompleteFunction); |
| } |
| |
| 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; |
| WTF::Function<void ()> m_performFunction; |
| WTF::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 }; |
| }; |
| |
| template <typename... Arguments> |
| class TransactionOperationImpl final : public TransactionOperation { |
| public: |
| TransactionOperationImpl(IDBTransaction& transaction, void (IDBTransaction::*completeMethod)(const IDBResultData&), void (IDBTransaction::*performMethod)(TransactionOperation&, Arguments...), Arguments&&... arguments) |
| : TransactionOperation(transaction) |
| { |
| RefPtr<TransactionOperation> protectedThis(this); |
| |
| ASSERT(performMethod); |
| m_performFunction = [protectedThis, this, performMethod, arguments...] { |
| (&m_transaction.get()->*performMethod)(*this, arguments...); |
| }; |
| |
| if (completeMethod) { |
| m_completeFunction = [protectedThis, this, completeMethod](const IDBResultData& resultData) { |
| if (completeMethod) |
| (&m_transaction.get()->*completeMethod)(resultData); |
| }; |
| } |
| } |
| |
| TransactionOperationImpl(IDBTransaction& transaction, IDBRequest& request, void (IDBTransaction::*completeMethod)(IDBRequest&, const IDBResultData&), void (IDBTransaction::*performMethod)(TransactionOperation&, Arguments...), Arguments&&... arguments) |
| : TransactionOperation(transaction, request) |
| { |
| RefPtr<TransactionOperation> protectedThis(this); |
| |
| ASSERT(performMethod); |
| m_performFunction = [protectedThis, this, performMethod, arguments...] { |
| (&m_transaction.get()->*performMethod)(*this, arguments...); |
| }; |
| |
| if (completeMethod) { |
| RefPtr<IDBRequest> refRequest(&request); |
| m_completeFunction = [protectedThis, this, refRequest, completeMethod](const IDBResultData& resultData) { |
| if (completeMethod) |
| (&m_transaction.get()->*completeMethod)(*refRequest, resultData); |
| }; |
| } |
| } |
| }; |
| |
| inline RefPtr<TransactionOperation> createTransactionOperation( |
| IDBTransaction& transaction, |
| void (IDBTransaction::*complete)(const IDBResultData&), |
| void (IDBTransaction::*perform)(TransactionOperation&)) |
| { |
| auto operation = new TransactionOperationImpl<>(transaction, complete, perform); |
| return adoptRef(operation); |
| } |
| |
| template<typename MP1, typename P1> |
| RefPtr<TransactionOperation> createTransactionOperation( |
| IDBTransaction& transaction, |
| void (IDBTransaction::*complete)(const IDBResultData&), |
| void (IDBTransaction::*perform)(TransactionOperation&, MP1), |
| const P1& parameter1) |
| { |
| auto operation = new TransactionOperationImpl<MP1>(transaction, complete, perform, parameter1); |
| return adoptRef(operation); |
| } |
| |
| template<typename MP1, typename P1, typename MP2, typename P2> |
| RefPtr<TransactionOperation> createTransactionOperation( |
| IDBTransaction& transaction, |
| void (IDBTransaction::*complete)(const IDBResultData&), |
| void (IDBTransaction::*perform)(TransactionOperation&, MP1, MP2), |
| const P1& parameter1, |
| const P2& parameter2) |
| { |
| auto operation = new TransactionOperationImpl<MP1, MP2>(transaction, complete, perform, parameter1, parameter2); |
| return adoptRef(operation); |
| } |
| |
| template<typename MP1, typename P1, typename MP2, typename P2, typename MP3, typename P3> |
| RefPtr<TransactionOperation> createTransactionOperation( |
| IDBTransaction& transaction, |
| void (IDBTransaction::*complete)(const IDBResultData&), |
| void (IDBTransaction::*perform)(TransactionOperation&, MP1, MP2, MP3), |
| const P1& parameter1, |
| const P2& parameter2, |
| const P3& parameter3) |
| { |
| auto operation = new TransactionOperationImpl<MP1, MP2, MP3>(transaction, complete, perform, parameter1, parameter2, parameter3); |
| return adoptRef(operation); |
| } |
| |
| template<typename MP1, typename P1> |
| RefPtr<TransactionOperation> createTransactionOperation( |
| IDBTransaction& transaction, |
| IDBRequest& request, |
| void (IDBTransaction::*complete)(IDBRequest&, const IDBResultData&), |
| void (IDBTransaction::*perform)(TransactionOperation&, MP1), |
| const P1& parameter1) |
| { |
| auto operation = new TransactionOperationImpl<MP1>(transaction, request, complete, perform, parameter1); |
| return adoptRef(operation); |
| } |
| |
| template<typename MP1, typename P1, typename MP2, typename P2> |
| RefPtr<TransactionOperation> createTransactionOperation( |
| IDBTransaction& transaction, |
| IDBRequest& request, |
| void (IDBTransaction::*complete)(IDBRequest&, const IDBResultData&), |
| void (IDBTransaction::*perform)(TransactionOperation&, MP1, MP2), |
| const P1& parameter1, |
| const P2& parameter2) |
| { |
| auto operation = new TransactionOperationImpl<MP1, MP2>(transaction, request, complete, perform, parameter1, parameter2); |
| return adoptRef(operation); |
| } |
| |
| template<typename MP1, typename MP2, typename MP3, typename P1, typename P2, typename P3> |
| RefPtr<TransactionOperation> createTransactionOperation( |
| IDBTransaction& transaction, |
| IDBRequest& request, |
| void (IDBTransaction::*complete)(IDBRequest&, const IDBResultData&), |
| void (IDBTransaction::*perform)(TransactionOperation&, MP1, MP2, MP3), |
| const P1& parameter1, |
| const P2& parameter2, |
| const P3& parameter3) |
| { |
| auto operation = new TransactionOperationImpl<MP1, MP2, MP3>(transaction, request, complete, perform, parameter1, parameter2, parameter3); |
| return adoptRef(operation); |
| } |
| |
| } // namespace IDBClient |
| } // namespace WebCore |
| |
| #endif // ENABLE(INDEXED_DATABASE) |