1/*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 * Copyright (C) 2015 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Apple Inc. ("Apple") nor the names of
15 * its contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30#include "config.h"
31#include "InspectorDatabaseAgent.h"
32
33#include "Database.h"
34#include "DatabaseTracker.h"
35#include "InspectorDatabaseResource.h"
36#include "InstrumentingAgents.h"
37#include "SQLError.h"
38#include "SQLResultSet.h"
39#include "SQLResultSetRowList.h"
40#include "SQLStatementCallback.h"
41#include "SQLStatementErrorCallback.h"
42#include "SQLTransaction.h"
43#include "SQLTransactionCallback.h"
44#include "SQLTransactionErrorCallback.h"
45#include "SQLValue.h"
46#include "VoidCallback.h"
47#include <JavaScriptCore/InspectorFrontendRouter.h>
48#include <wtf/JSONValues.h>
49#include <wtf/Vector.h>
50
51
52namespace WebCore {
53
54using namespace Inspector;
55
56using ExecuteSQLCallback = Inspector::DatabaseBackendDispatcherHandler::ExecuteSQLCallback;
57
58namespace {
59
60void reportTransactionFailed(ExecuteSQLCallback& requestCallback, SQLError& error)
61{
62 auto errorObject = Inspector::Protocol::Database::Error::create()
63 .setMessage(error.message())
64 .setCode(error.code())
65 .release();
66 requestCallback.sendSuccess(nullptr, nullptr, WTFMove(errorObject));
67}
68
69class StatementCallback final : public SQLStatementCallback {
70public:
71 static Ref<StatementCallback> create(ScriptExecutionContext* context, Ref<ExecuteSQLCallback>&& requestCallback)
72 {
73 return adoptRef(*new StatementCallback(context, WTFMove(requestCallback)));
74 }
75
76private:
77 StatementCallback(ScriptExecutionContext* context, Ref<ExecuteSQLCallback>&& requestCallback)
78 : SQLStatementCallback(context)
79 , m_requestCallback(WTFMove(requestCallback))
80 {
81 }
82
83 CallbackResult<void> handleEvent(SQLTransaction&, SQLResultSet& resultSet) final
84 {
85 auto& rowList = resultSet.rows();
86
87 auto columnNames = JSON::ArrayOf<String>::create();
88 for (auto& column : rowList.columnNames())
89 columnNames->addItem(column);
90
91 auto values = JSON::ArrayOf<JSON::Value>::create();
92 for (auto& value : rowList.values()) {
93 auto inspectorValue = WTF::switchOn(value,
94 [] (const std::nullptr_t&) { return JSON::Value::null(); },
95 [] (const String& string) { return JSON::Value::create(string); },
96 [] (double number) { return JSON::Value::create(number); }
97 );
98 values->addItem(WTFMove(inspectorValue));
99 }
100 m_requestCallback->sendSuccess(WTFMove(columnNames), WTFMove(values), nullptr);
101 return { };
102 }
103
104 Ref<ExecuteSQLCallback> m_requestCallback;
105};
106
107class StatementErrorCallback final : public SQLStatementErrorCallback {
108public:
109 static Ref<StatementErrorCallback> create(ScriptExecutionContext* context, Ref<ExecuteSQLCallback>&& requestCallback)
110 {
111 return adoptRef(*new StatementErrorCallback(context, WTFMove(requestCallback)));
112 }
113
114private:
115 StatementErrorCallback(ScriptExecutionContext* context, Ref<ExecuteSQLCallback>&& requestCallback)
116 : SQLStatementErrorCallback(context)
117 , m_requestCallback(WTFMove(requestCallback))
118 {
119 }
120
121 CallbackResult<bool> handleEvent(SQLTransaction&, SQLError& error) final
122 {
123 reportTransactionFailed(m_requestCallback.copyRef(), error);
124 return true;
125 }
126
127 Ref<ExecuteSQLCallback> m_requestCallback;
128};
129
130class TransactionCallback final : public SQLTransactionCallback {
131public:
132 static Ref<TransactionCallback> create(ScriptExecutionContext* context, const String& sqlStatement, Ref<ExecuteSQLCallback>&& requestCallback)
133 {
134 return adoptRef(*new TransactionCallback(context, sqlStatement, WTFMove(requestCallback)));
135 }
136
137private:
138 TransactionCallback(ScriptExecutionContext* context, const String& sqlStatement, Ref<ExecuteSQLCallback>&& requestCallback)
139 : SQLTransactionCallback(context)
140 , m_sqlStatement(sqlStatement)
141 , m_requestCallback(WTFMove(requestCallback))
142 {
143 }
144
145 CallbackResult<void> handleEvent(SQLTransaction& transaction) final
146 {
147 if (!m_requestCallback->isActive())
148 return { };
149
150 Ref<SQLStatementCallback> callback(StatementCallback::create(scriptExecutionContext(), m_requestCallback.copyRef()));
151 Ref<SQLStatementErrorCallback> errorCallback(StatementErrorCallback::create(scriptExecutionContext(), m_requestCallback.copyRef()));
152 transaction.executeSql(m_sqlStatement, { }, WTFMove(callback), WTFMove(errorCallback));
153 return { };
154 }
155
156 String m_sqlStatement;
157 Ref<ExecuteSQLCallback> m_requestCallback;
158};
159
160class TransactionErrorCallback final : public SQLTransactionErrorCallback {
161public:
162 static Ref<TransactionErrorCallback> create(ScriptExecutionContext* context, Ref<ExecuteSQLCallback>&& requestCallback)
163 {
164 return adoptRef(*new TransactionErrorCallback(context, WTFMove(requestCallback)));
165 }
166
167private:
168 TransactionErrorCallback(ScriptExecutionContext* context, Ref<ExecuteSQLCallback>&& requestCallback)
169 : SQLTransactionErrorCallback(context)
170 , m_requestCallback(WTFMove(requestCallback))
171 {
172 }
173
174 CallbackResult<void> handleEvent(SQLError& error) final
175 {
176 reportTransactionFailed(m_requestCallback.get(), error);
177 return { };
178 }
179
180 Ref<ExecuteSQLCallback> m_requestCallback;
181};
182
183class TransactionSuccessCallback final : public VoidCallback {
184public:
185 static Ref<TransactionSuccessCallback> create(ScriptExecutionContext* context)
186 {
187 return adoptRef(*new TransactionSuccessCallback(context));
188 }
189
190 CallbackResult<void> handleEvent() final { return { }; }
191
192private:
193 TransactionSuccessCallback(ScriptExecutionContext* context)
194 : VoidCallback(context)
195 {
196 }
197};
198
199} // namespace
200
201void InspectorDatabaseAgent::didCommitLoad()
202{
203 m_resources.clear();
204}
205
206void InspectorDatabaseAgent::didOpenDatabase(Database& database)
207{
208 if (auto resource = findByFileName(database.fileName())) {
209 resource->setDatabase(database);
210 return;
211 }
212
213 auto resource = InspectorDatabaseResource::create(database, database.securityOrigin().host, database.stringIdentifier(), database.expectedVersion());
214 m_resources.add(resource->id(), resource.ptr());
215 resource->bind(*m_frontendDispatcher);
216}
217
218InspectorDatabaseAgent::InspectorDatabaseAgent(WebAgentContext& context)
219 : InspectorAgentBase("Database"_s, context)
220 , m_frontendDispatcher(std::make_unique<Inspector::DatabaseFrontendDispatcher>(context.frontendRouter))
221 , m_backendDispatcher(Inspector::DatabaseBackendDispatcher::create(context.backendDispatcher, this))
222{
223}
224
225void InspectorDatabaseAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*)
226{
227}
228
229void InspectorDatabaseAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason)
230{
231 ErrorString unused;
232 disable(unused);
233}
234
235void InspectorDatabaseAgent::enable(ErrorString&)
236{
237 if (m_instrumentingAgents.inspectorDatabaseAgent() == this)
238 return;
239
240 m_instrumentingAgents.setInspectorDatabaseAgent(this);
241
242 for (auto& database : DatabaseTracker::singleton().openDatabases())
243 didOpenDatabase(database.get());
244}
245
246void InspectorDatabaseAgent::disable(ErrorString&)
247{
248 m_instrumentingAgents.setInspectorDatabaseAgent(nullptr);
249
250 m_resources.clear();
251}
252
253void InspectorDatabaseAgent::getDatabaseTableNames(ErrorString& errorString, const String& databaseId, RefPtr<JSON::ArrayOf<String>>& names)
254{
255 if (m_instrumentingAgents.inspectorDatabaseAgent() != this) {
256 errorString = "Database agent is not enabled"_s;
257 return;
258 }
259
260 names = JSON::ArrayOf<String>::create();
261
262 if (auto* database = databaseForId(databaseId)) {
263 for (auto& tableName : database->tableNames())
264 names->addItem(tableName);
265 }
266}
267
268void InspectorDatabaseAgent::executeSQL(const String& databaseId, const String& query, Ref<ExecuteSQLCallback>&& requestCallback)
269{
270 if (m_instrumentingAgents.inspectorDatabaseAgent() != this) {
271 requestCallback->sendFailure("Database agent is not enabled"_s);
272 return;
273 }
274
275 auto* database = databaseForId(databaseId);
276 if (!database) {
277 requestCallback->sendFailure("Database not found");
278 return;
279 }
280
281 database->transaction(TransactionCallback::create(&database->scriptExecutionContext(), query, requestCallback.copyRef()),
282 TransactionErrorCallback::create(&database->scriptExecutionContext(), requestCallback.copyRef()),
283 TransactionSuccessCallback::create(&database->scriptExecutionContext()));
284}
285
286String InspectorDatabaseAgent::databaseId(Database& database)
287{
288 for (auto& resource : m_resources) {
289 if (&resource.value->database() == &database)
290 return resource.key;
291 }
292 return String();
293}
294
295InspectorDatabaseResource* InspectorDatabaseAgent::findByFileName(const String& fileName)
296{
297 for (auto& resource : m_resources.values()) {
298 if (resource->database().fileName() == fileName)
299 return resource.get();
300 }
301 return nullptr;
302}
303
304Database* InspectorDatabaseAgent::databaseForId(const String& databaseId)
305{
306 if (auto resource = m_resources.get(databaseId))
307 return &resource->database();
308 return nullptr;
309}
310
311} // namespace WebCore
312