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 | |
52 | namespace WebCore { |
53 | |
54 | using namespace Inspector; |
55 | |
56 | using ExecuteSQLCallback = Inspector::DatabaseBackendDispatcherHandler::ExecuteSQLCallback; |
57 | |
58 | namespace { |
59 | |
60 | void 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 | |
69 | class StatementCallback final : public SQLStatementCallback { |
70 | public: |
71 | static Ref<StatementCallback> create(ScriptExecutionContext* context, Ref<ExecuteSQLCallback>&& requestCallback) |
72 | { |
73 | return adoptRef(*new StatementCallback(context, WTFMove(requestCallback))); |
74 | } |
75 | |
76 | private: |
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 | |
107 | class StatementErrorCallback final : public SQLStatementErrorCallback { |
108 | public: |
109 | static Ref<StatementErrorCallback> create(ScriptExecutionContext* context, Ref<ExecuteSQLCallback>&& requestCallback) |
110 | { |
111 | return adoptRef(*new StatementErrorCallback(context, WTFMove(requestCallback))); |
112 | } |
113 | |
114 | private: |
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 | |
130 | class TransactionCallback final : public SQLTransactionCallback { |
131 | public: |
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 | |
137 | private: |
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 | |
160 | class TransactionErrorCallback final : public SQLTransactionErrorCallback { |
161 | public: |
162 | static Ref<TransactionErrorCallback> create(ScriptExecutionContext* context, Ref<ExecuteSQLCallback>&& requestCallback) |
163 | { |
164 | return adoptRef(*new TransactionErrorCallback(context, WTFMove(requestCallback))); |
165 | } |
166 | |
167 | private: |
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 | |
183 | class TransactionSuccessCallback final : public VoidCallback { |
184 | public: |
185 | static Ref<TransactionSuccessCallback> create(ScriptExecutionContext* context) |
186 | { |
187 | return adoptRef(*new TransactionSuccessCallback(context)); |
188 | } |
189 | |
190 | CallbackResult<void> handleEvent() final { return { }; } |
191 | |
192 | private: |
193 | TransactionSuccessCallback(ScriptExecutionContext* context) |
194 | : VoidCallback(context) |
195 | { |
196 | } |
197 | }; |
198 | |
199 | } // namespace |
200 | |
201 | void InspectorDatabaseAgent::didCommitLoad() |
202 | { |
203 | m_resources.clear(); |
204 | } |
205 | |
206 | void 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 | |
218 | InspectorDatabaseAgent::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 | |
225 | void InspectorDatabaseAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*) |
226 | { |
227 | } |
228 | |
229 | void InspectorDatabaseAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason) |
230 | { |
231 | ErrorString unused; |
232 | disable(unused); |
233 | } |
234 | |
235 | void 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 | |
246 | void InspectorDatabaseAgent::disable(ErrorString&) |
247 | { |
248 | m_instrumentingAgents.setInspectorDatabaseAgent(nullptr); |
249 | |
250 | m_resources.clear(); |
251 | } |
252 | |
253 | void 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 | |
268 | void 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 | |
286 | String 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 | |
295 | InspectorDatabaseResource* 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 | |
304 | Database* 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 | |