1/*
2 * Copyright (C) 2015 Apple 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
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#pragma once
27
28#if ENABLE(INDEXED_DATABASE)
29
30#include "IDBRequest.h"
31#include "IDBRequestData.h"
32#include "IDBResourceIdentifier.h"
33#include "IDBResultData.h"
34#include "IDBTransaction.h"
35#include <wtf/Function.h>
36#include <wtf/MainThread.h>
37#include <wtf/Threading.h>
38
39namespace WebCore {
40
41class IDBResultData;
42
43namespace IndexedDB {
44enum class IndexRecordType;
45}
46
47namespace IDBClient {
48
49class TransactionOperation : public ThreadSafeRefCounted<TransactionOperation> {
50 friend IDBRequestData::IDBRequestData(TransactionOperation&);
51public:
52 virtual ~TransactionOperation()
53 {
54 ASSERT(m_originThread.ptr() == &Thread::current());
55 }
56
57 void perform()
58 {
59 ASSERT(m_originThread.ptr() == &Thread::current());
60 ASSERT(m_performFunction);
61 m_performFunction();
62 m_performFunction = { };
63 }
64
65 void transitionToCompleteOnThisThread(const IDBResultData& data)
66 {
67 ASSERT(m_originThread.ptr() == &Thread::current());
68 m_transaction->operationCompletedOnServer(data, *this);
69 }
70
71 void transitionToComplete(const IDBResultData& data, RefPtr<TransactionOperation>&& lastRef)
72 {
73 ASSERT(isMainThread());
74
75 if (m_originThread.ptr() == &Thread::current())
76 transitionToCompleteOnThisThread(data);
77 else {
78 m_transaction->performCallbackOnOriginThread(*this, &TransactionOperation::transitionToCompleteOnThisThread, data);
79 m_transaction->callFunctionOnOriginThread([lastRef = WTFMove(lastRef)]() {
80 });
81 }
82 }
83
84 void doComplete(const IDBResultData& data)
85 {
86 ASSERT(m_originThread.ptr() == &Thread::current());
87
88 if (m_performFunction)
89 m_performFunction = { };
90
91 // Due to race conditions between the server sending an "operation complete" message and the client
92 // forcefully aborting an operation, it's unavoidable that this method might be called twice.
93 // It's okay to handle that gracefully with an early return.
94 if (m_didComplete)
95 return;
96 m_didComplete = true;
97
98 if (m_completeFunction) {
99 m_completeFunction(data);
100 // m_completeFunction should not hold ref to this TransactionOperation after its execution.
101 m_completeFunction = { };
102 }
103 m_transaction->operationCompletedOnClient(*this);
104 }
105
106 const IDBResourceIdentifier& identifier() const { return m_identifier; }
107
108 Thread& originThread() const { return m_originThread.get(); }
109
110 IDBRequest* idbRequest() { return m_idbRequest.get(); }
111
112 bool nextRequestCanGoToServer() const { return m_nextRequestCanGoToServer && m_idbRequest; }
113 void setNextRequestCanGoToServer(bool nextRequestCanGoToServer) { m_nextRequestCanGoToServer = nextRequestCanGoToServer; }
114
115protected:
116 TransactionOperation(IDBTransaction& transaction)
117 : m_transaction(transaction)
118 , m_identifier(transaction.connectionProxy())
119 {
120 }
121
122 TransactionOperation(IDBTransaction&, IDBRequest&);
123
124 Ref<IDBTransaction> m_transaction;
125 IDBResourceIdentifier m_identifier;
126 uint64_t m_objectStoreIdentifier { 0 };
127 uint64_t m_indexIdentifier { 0 };
128 std::unique_ptr<IDBResourceIdentifier> m_cursorIdentifier;
129 IndexedDB::IndexRecordType m_indexRecordType;
130 Function<void()> m_performFunction;
131 Function<void(const IDBResultData&)> m_completeFunction;
132
133private:
134 IDBResourceIdentifier transactionIdentifier() const { return m_transaction->info().identifier(); }
135 uint64_t objectStoreIdentifier() const { return m_objectStoreIdentifier; }
136 uint64_t indexIdentifier() const { return m_indexIdentifier; }
137 IDBResourceIdentifier* cursorIdentifier() const { return m_cursorIdentifier.get(); }
138 IDBTransaction& transaction() { return m_transaction.get(); }
139 IndexedDB::IndexRecordType indexRecordType() const { return m_indexRecordType; }
140
141 Ref<Thread> m_originThread { Thread::current() };
142 RefPtr<IDBRequest> m_idbRequest;
143 bool m_nextRequestCanGoToServer { true };
144 bool m_didComplete { false };
145};
146
147class TransactionOperationImpl final : public TransactionOperation {
148public:
149 template<typename... Args> static Ref<TransactionOperationImpl> create(Args&&... args) { return adoptRef(*new TransactionOperationImpl(std::forward<Args>(args)...)); }
150private:
151 TransactionOperationImpl(IDBTransaction& transaction, Function<void(const IDBResultData&)> completeMethod, Function<void(TransactionOperation&)> performMethod)
152 : TransactionOperation(transaction)
153 {
154 ASSERT(performMethod);
155 m_performFunction = [protectedThis = makeRef(*this), performMethod = WTFMove(performMethod)] {
156 performMethod(protectedThis.get());
157 };
158
159 if (completeMethod) {
160 m_completeFunction = [protectedThis = makeRef(*this), completeMethod = WTFMove(completeMethod)] (const IDBResultData& resultData) {
161 completeMethod(resultData);
162 };
163 }
164 }
165
166 TransactionOperationImpl(IDBTransaction& transaction, IDBRequest& request, Function<void(const IDBResultData&)> completeMethod, Function<void(TransactionOperation&)> performMethod)
167 : TransactionOperation(transaction, request)
168 {
169 ASSERT(performMethod);
170 m_performFunction = [protectedThis = makeRef(*this), performMethod = WTFMove(performMethod)] {
171 performMethod(protectedThis.get());
172 };
173
174 if (completeMethod) {
175 m_completeFunction = [protectedThis = makeRef(*this), completeMethod = WTFMove(completeMethod)] (const IDBResultData& resultData) {
176 completeMethod(resultData);
177 };
178 }
179 }
180};
181
182} // namespace IDBClient
183} // namespace WebCore
184
185#endif // ENABLE(INDEXED_DATABASE)
186