/* * 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 #include namespace WebCore { class IDBResultData; namespace IndexedDB { enum class IndexRecordType; } namespace IDBClient { class TransactionOperation : public ThreadSafeRefCounted { friend IDBRequestData::IDBRequestData(TransactionOperation&); public: virtual ~TransactionOperation() { ASSERT(m_originThreadID == currentThread()); } void perform() { ASSERT(m_originThreadID == currentThread()); ASSERT(m_performFunction); m_performFunction(); m_performFunction = { }; } void transitionToCompleteOnThisThread(const IDBResultData& data) { ASSERT(m_originThreadID == currentThread()); m_transaction->operationCompletedOnServer(data, *this); } void transitionToComplete(const IDBResultData& data, RefPtr&& lastRef) { ASSERT(isMainThread()); if (m_originThreadID == currentThread()) transitionToCompleteOnThisThread(data); else { m_transaction->performCallbackOnOriginThread(*this, &TransactionOperation::transitionToCompleteOnThisThread, data); m_transaction->callFunctionOnOriginThread([lastRef = WTFMove(lastRef)]() { }); } } void doComplete(const IDBResultData& data) { ASSERT(m_originThreadID == currentThread()); // 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. std::function oldCompleteFunction; std::swap(m_completeFunction, oldCompleteFunction); } const IDBResourceIdentifier& identifier() const { return m_identifier; } ThreadIdentifier originThreadID() const { return m_originThreadID; } 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 m_transaction; IDBResourceIdentifier m_identifier; uint64_t m_objectStoreIdentifier { 0 }; uint64_t m_indexIdentifier { 0 }; std::unique_ptr m_cursorIdentifier; IndexedDB::IndexRecordType m_indexRecordType; std::function m_performFunction; std::function 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; } ThreadIdentifier m_originThreadID { currentThread() }; RefPtr m_idbRequest; bool m_nextRequestCanGoToServer { true }; }; template class TransactionOperationImpl final : public TransactionOperation { public: TransactionOperationImpl(IDBTransaction& transaction, void (IDBTransaction::*completeMethod)(const IDBResultData&), void (IDBTransaction::*performMethod)(TransactionOperation&, Arguments...), Arguments&&... arguments) : TransactionOperation(transaction) { RefPtr 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 protectedThis(this); ASSERT(performMethod); m_performFunction = [protectedThis, this, performMethod, arguments...] { (&m_transaction.get()->*performMethod)(*this, arguments...); }; if (completeMethod) { RefPtr refRequest(&request); m_completeFunction = [protectedThis, this, refRequest, completeMethod](const IDBResultData& resultData) { if (completeMethod) (&m_transaction.get()->*completeMethod)(*refRequest, resultData); }; } } }; inline RefPtr createTransactionOperation( IDBTransaction& transaction, void (IDBTransaction::*complete)(const IDBResultData&), void (IDBTransaction::*perform)(TransactionOperation&)) { auto operation = new TransactionOperationImpl<>(transaction, complete, perform); return adoptRef(operation); } template RefPtr createTransactionOperation( IDBTransaction& transaction, void (IDBTransaction::*complete)(const IDBResultData&), void (IDBTransaction::*perform)(TransactionOperation&, MP1), const P1& parameter1) { auto operation = new TransactionOperationImpl(transaction, complete, perform, parameter1); return adoptRef(operation); } template RefPtr createTransactionOperation( IDBTransaction& transaction, void (IDBTransaction::*complete)(const IDBResultData&), void (IDBTransaction::*perform)(TransactionOperation&, MP1, MP2), const P1& parameter1, const P2& parameter2) { auto operation = new TransactionOperationImpl(transaction, complete, perform, parameter1, parameter2); return adoptRef(operation); } template RefPtr 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(transaction, complete, perform, parameter1, parameter2, parameter3); return adoptRef(operation); } template RefPtr createTransactionOperation( IDBTransaction& transaction, IDBRequest& request, void (IDBTransaction::*complete)(IDBRequest&, const IDBResultData&), void (IDBTransaction::*perform)(TransactionOperation&, MP1), const P1& parameter1) { auto operation = new TransactionOperationImpl(transaction, request, complete, perform, parameter1); return adoptRef(operation); } template RefPtr 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(transaction, request, complete, perform, parameter1, parameter2); return adoptRef(operation); } template RefPtr 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(transaction, request, complete, perform, parameter1, parameter2, parameter3); return adoptRef(operation); } } // namespace IDBClient } // namespace WebCore #endif // ENABLE(INDEXED_DATABASE)