1/*
2 * Copyright (C) 2010 Google 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 *
8 * 1.  Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 * 2.  Redistributions in binary form must reproduce the above copyright
11 *     notice, this list of conditions and the following disclaimer in the
12 *     documentation and/or other materials provided with the distribution.
13 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of
14 *     its contributors may be used to endorse or promote products derived
15 *     from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "config.h"
30
31#if ENABLE(INSPECTOR) && ENABLE(SQL_DATABASE)
32
33#include "InspectorDatabaseAgent.h"
34
35#include "Database.h"
36#include "ExceptionCode.h"
37#include "ExceptionCodePlaceholder.h"
38#include "InspectorDatabaseResource.h"
39#include "InspectorWebFrontendDispatchers.h"
40#include "InstrumentingAgents.h"
41#include "SQLError.h"
42#include "SQLResultSet.h"
43#include "SQLResultSetRowList.h"
44#include "SQLStatementCallback.h"
45#include "SQLStatementErrorCallback.h"
46#include "SQLTransaction.h"
47#include "SQLTransactionCallback.h"
48#include "SQLTransactionErrorCallback.h"
49#include "SQLValue.h"
50#include "VoidCallback.h"
51#include <inspector/InspectorValues.h>
52#include <wtf/Vector.h>
53
54typedef Inspector::InspectorDatabaseBackendDispatcherHandler::ExecuteSQLCallback ExecuteSQLCallback;
55
56using namespace Inspector;
57
58namespace WebCore {
59
60namespace {
61
62void reportTransactionFailed(ExecuteSQLCallback* requestCallback, SQLError* error)
63{
64    RefPtr<Inspector::TypeBuilder::Database::Error> errorObject = Inspector::TypeBuilder::Database::Error::create()
65        .setMessage(error->message())
66        .setCode(error->code());
67    requestCallback->sendSuccess(nullptr, nullptr, errorObject.release());
68}
69
70class StatementCallback : public SQLStatementCallback {
71public:
72    static PassRefPtr<StatementCallback> create(PassRefPtr<ExecuteSQLCallback> requestCallback)
73    {
74        return adoptRef(new StatementCallback(requestCallback));
75    }
76
77    virtual ~StatementCallback() { }
78
79    virtual bool handleEvent(SQLTransaction*, SQLResultSet* resultSet) override
80    {
81        SQLResultSetRowList* rowList = resultSet->rows();
82
83        RefPtr<Inspector::TypeBuilder::Array<String>> columnNames = Inspector::TypeBuilder::Array<String>::create();
84        const Vector<String>& columns = rowList->columnNames();
85        for (size_t i = 0; i < columns.size(); ++i)
86            columnNames->addItem(columns[i]);
87
88        RefPtr<Inspector::TypeBuilder::Array<InspectorValue>> values = Inspector::TypeBuilder::Array<InspectorValue>::create();
89        const Vector<SQLValue>& data = rowList->values();
90        for (size_t i = 0; i < data.size(); ++i) {
91            const SQLValue& value = rowList->values()[i];
92            switch (value.type()) {
93            case SQLValue::StringValue: values->addItem(InspectorString::create(value.string())); break;
94            case SQLValue::NumberValue: values->addItem(InspectorBasicValue::create(value.number())); break;
95            case SQLValue::NullValue: values->addItem(InspectorValue::null()); break;
96            }
97        }
98        m_requestCallback->sendSuccess(columnNames.release(), values.release(), nullptr);
99        return true;
100    }
101
102private:
103    StatementCallback(PassRefPtr<ExecuteSQLCallback> requestCallback)
104        : m_requestCallback(requestCallback) { }
105    RefPtr<ExecuteSQLCallback> m_requestCallback;
106};
107
108class StatementErrorCallback : public SQLStatementErrorCallback {
109public:
110    static PassRefPtr<StatementErrorCallback> create(PassRefPtr<ExecuteSQLCallback> requestCallback)
111    {
112        return adoptRef(new StatementErrorCallback(requestCallback));
113    }
114
115    virtual ~StatementErrorCallback() { }
116
117    virtual bool handleEvent(SQLTransaction*, SQLError* error) override
118    {
119        reportTransactionFailed(m_requestCallback.get(), error);
120        return true;
121    }
122
123private:
124    StatementErrorCallback(PassRefPtr<ExecuteSQLCallback> requestCallback)
125        : m_requestCallback(requestCallback) { }
126    RefPtr<ExecuteSQLCallback> m_requestCallback;
127};
128
129class TransactionCallback : public SQLTransactionCallback {
130public:
131    static PassRefPtr<TransactionCallback> create(const String& sqlStatement, PassRefPtr<ExecuteSQLCallback> requestCallback)
132    {
133        return adoptRef(new TransactionCallback(sqlStatement, requestCallback));
134    }
135
136    virtual ~TransactionCallback() { }
137
138    virtual bool handleEvent(SQLTransaction* transaction) override
139    {
140        if (!m_requestCallback->isActive())
141            return true;
142
143        Vector<SQLValue> sqlValues;
144        RefPtr<SQLStatementCallback> callback(StatementCallback::create(m_requestCallback.get()));
145        RefPtr<SQLStatementErrorCallback> errorCallback(StatementErrorCallback::create(m_requestCallback.get()));
146        transaction->executeSQL(m_sqlStatement, sqlValues, callback.release(), errorCallback.release(), IGNORE_EXCEPTION);
147        return true;
148    }
149private:
150    TransactionCallback(const String& sqlStatement, PassRefPtr<ExecuteSQLCallback> requestCallback)
151        : m_sqlStatement(sqlStatement)
152        , m_requestCallback(requestCallback) { }
153    String m_sqlStatement;
154    RefPtr<ExecuteSQLCallback> m_requestCallback;
155};
156
157class TransactionErrorCallback : public SQLTransactionErrorCallback {
158public:
159    static PassRefPtr<TransactionErrorCallback> create(PassRefPtr<ExecuteSQLCallback> requestCallback)
160    {
161        return adoptRef(new TransactionErrorCallback(requestCallback));
162    }
163
164    virtual ~TransactionErrorCallback() { }
165
166    virtual bool handleEvent(SQLError* error) override
167    {
168        reportTransactionFailed(m_requestCallback.get(), error);
169        return true;
170    }
171private:
172    TransactionErrorCallback(PassRefPtr<ExecuteSQLCallback> requestCallback)
173        : m_requestCallback(requestCallback) { }
174    RefPtr<ExecuteSQLCallback> m_requestCallback;
175};
176
177class TransactionSuccessCallback : public VoidCallback {
178public:
179    static PassRefPtr<TransactionSuccessCallback> create()
180    {
181        return adoptRef(new TransactionSuccessCallback());
182    }
183
184    virtual ~TransactionSuccessCallback() { }
185
186    virtual bool handleEvent() override { return false; }
187
188private:
189    TransactionSuccessCallback() { }
190};
191
192} // namespace
193
194void InspectorDatabaseAgent::didOpenDatabase(PassRefPtr<Database> database, const String& domain, const String& name, const String& version)
195{
196    if (InspectorDatabaseResource* resource = findByFileName(database->fileName())) {
197        resource->setDatabase(database);
198        return;
199    }
200
201    RefPtr<InspectorDatabaseResource> resource = InspectorDatabaseResource::create(database, domain, name, version);
202    m_resources.set(resource->id(), resource);
203    // Resources are only bound while visible.
204    if (m_frontendDispatcher && m_enabled)
205        resource->bind(m_frontendDispatcher.get());
206}
207
208void InspectorDatabaseAgent::clearResources()
209{
210    m_resources.clear();
211}
212
213InspectorDatabaseAgent::InspectorDatabaseAgent(InstrumentingAgents* instrumentingAgents)
214    : InspectorAgentBase(ASCIILiteral("Database"), instrumentingAgents)
215    , m_enabled(false)
216{
217    m_instrumentingAgents->setInspectorDatabaseAgent(this);
218}
219
220InspectorDatabaseAgent::~InspectorDatabaseAgent()
221{
222    m_instrumentingAgents->setInspectorDatabaseAgent(nullptr);
223}
224
225void InspectorDatabaseAgent::didCreateFrontendAndBackend(Inspector::InspectorFrontendChannel* frontendChannel, InspectorBackendDispatcher* backendDispatcher)
226{
227    m_frontendDispatcher = std::make_unique<InspectorDatabaseFrontendDispatcher>(frontendChannel);
228    m_backendDispatcher = InspectorDatabaseBackendDispatcher::create(backendDispatcher, this);
229}
230
231void InspectorDatabaseAgent::willDestroyFrontendAndBackend(InspectorDisconnectReason)
232{
233    m_frontendDispatcher = nullptr;
234    m_backendDispatcher.clear();
235
236    disable(nullptr);
237}
238
239void InspectorDatabaseAgent::enable(ErrorString*)
240{
241    if (m_enabled)
242        return;
243    m_enabled = true;
244
245    DatabaseResourcesMap::iterator databasesEnd = m_resources.end();
246    for (DatabaseResourcesMap::iterator it = m_resources.begin(); it != databasesEnd; ++it)
247        it->value->bind(m_frontendDispatcher.get());
248}
249
250void InspectorDatabaseAgent::disable(ErrorString*)
251{
252    if (!m_enabled)
253        return;
254    m_enabled = false;
255}
256
257void InspectorDatabaseAgent::getDatabaseTableNames(ErrorString* error, const String& databaseId, RefPtr<Inspector::TypeBuilder::Array<String>>& names)
258{
259    if (!m_enabled) {
260        *error = "Database agent is not enabled";
261        return;
262    }
263
264    names = Inspector::TypeBuilder::Array<String>::create();
265
266    Database* database = databaseForId(databaseId);
267    if (database) {
268        Vector<String> tableNames = database->tableNames();
269        unsigned length = tableNames.size();
270        for (unsigned i = 0; i < length; ++i)
271            names->addItem(tableNames[i]);
272    }
273}
274
275void InspectorDatabaseAgent::executeSQL(ErrorString*, const String& databaseId, const String& query, PassRefPtr<ExecuteSQLCallback> prpRequestCallback)
276{
277    RefPtr<ExecuteSQLCallback> requestCallback = prpRequestCallback;
278
279    if (!m_enabled) {
280        requestCallback->sendFailure("Database agent is not enabled");
281        return;
282    }
283
284    Database* database = databaseForId(databaseId);
285    if (!database) {
286        requestCallback->sendFailure("Database not found");
287        return;
288    }
289
290    RefPtr<SQLTransactionCallback> callback(TransactionCallback::create(query, requestCallback.get()));
291    RefPtr<SQLTransactionErrorCallback> errorCallback(TransactionErrorCallback::create(requestCallback.get()));
292    RefPtr<VoidCallback> successCallback(TransactionSuccessCallback::create());
293    database->transaction(callback.release(), errorCallback.release(), successCallback.release());
294}
295
296String InspectorDatabaseAgent::databaseId(Database* database)
297{
298    for (DatabaseResourcesMap::iterator it = m_resources.begin(); it != m_resources.end(); ++it) {
299        if (it->value->database() == database)
300            return it->key;
301    }
302    return String();
303}
304
305InspectorDatabaseResource* InspectorDatabaseAgent::findByFileName(const String& fileName)
306{
307    for (DatabaseResourcesMap::iterator it = m_resources.begin(); it != m_resources.end(); ++it) {
308        if (it->value->database()->fileName() == fileName)
309            return it->value.get();
310    }
311    return nullptr;
312}
313
314Database* InspectorDatabaseAgent::databaseForId(const String& databaseId)
315{
316    DatabaseResourcesMap::iterator it = m_resources.find(databaseId);
317    if (it == m_resources.end())
318        return nullptr;
319    return it->value->database();
320}
321
322} // namespace WebCore
323
324#endif // ENABLE(INSPECTOR) && ENABLE(SQL_DATABASE)
325