/* * Copyright (C) 2010 Google Inc. All rights reserved. * 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. * 3. Neither the name of Apple Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE 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 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. */ #include "config.h" #include "InspectorDatabaseAgent.h" #include "Database.h" #include "InspectorDatabaseResource.h" #include "InstrumentingAgents.h" #include "SQLError.h" #include "SQLResultSet.h" #include "SQLResultSetRowList.h" #include "SQLStatementCallback.h" #include "SQLStatementErrorCallback.h" #include "SQLTransaction.h" #include "SQLTransactionCallback.h" #include "SQLTransactionErrorCallback.h" #include "SQLValue.h" #include "VoidCallback.h" #include #include #include using namespace Inspector; namespace WebCore { using ExecuteSQLCallback = Inspector::DatabaseBackendDispatcherHandler::ExecuteSQLCallback; namespace { void reportTransactionFailed(ExecuteSQLCallback& requestCallback, SQLError* error) { auto errorObject = Inspector::Protocol::Database::Error::create() .setMessage(error->message()) .setCode(error->code()) .release(); requestCallback.sendSuccess(nullptr, nullptr, WTFMove(errorObject)); } class StatementCallback final : public SQLStatementCallback { public: static Ref create(Ref&& requestCallback) { return adoptRef(*new StatementCallback(WTFMove(requestCallback))); } private: StatementCallback(Ref&& requestCallback) : m_requestCallback(WTFMove(requestCallback)) { } bool handleEvent(SQLTransaction*, SQLResultSet* resultSet) final { auto& rowList = resultSet->rows(); auto columnNames = Inspector::Protocol::Array::create(); for (auto& column : rowList.columnNames()) columnNames->addItem(column); auto values = Inspector::Protocol::Array::create(); for (auto& value : rowList.values()) { auto inspectorValue = WTF::switchOn(value, [] (const std::nullptr_t&) { return InspectorValue::null(); }, [] (const String& string) { return InspectorValue::create(string); }, [] (double number) { return InspectorValue::create(number); } ); values->addItem(WTFMove(inspectorValue)); } m_requestCallback->sendSuccess(WTFMove(columnNames), WTFMove(values), nullptr); return true; } Ref m_requestCallback; }; class StatementErrorCallback final : public SQLStatementErrorCallback { public: static Ref create(Ref&& requestCallback) { return adoptRef(*new StatementErrorCallback(WTFMove(requestCallback))); } private: StatementErrorCallback(Ref&& requestCallback) : m_requestCallback(WTFMove(requestCallback)) { } bool handleEvent(SQLTransaction*, SQLError* error) final { reportTransactionFailed(m_requestCallback.copyRef(), error); return true; } Ref m_requestCallback; }; class TransactionCallback final : public SQLTransactionCallback { public: static Ref create(const String& sqlStatement, Ref&& requestCallback) { return adoptRef(*new TransactionCallback(sqlStatement, WTFMove(requestCallback))); } private: TransactionCallback(const String& sqlStatement, Ref&& requestCallback) : m_sqlStatement(sqlStatement) , m_requestCallback(WTFMove(requestCallback)) { } bool handleEvent(SQLTransaction* transaction) final { if (!m_requestCallback->isActive()) return true; Ref callback(StatementCallback::create(m_requestCallback.copyRef())); Ref errorCallback(StatementErrorCallback::create(m_requestCallback.copyRef())); transaction->executeSql(m_sqlStatement, { }, WTFMove(callback), WTFMove(errorCallback)); return true; } String m_sqlStatement; Ref m_requestCallback; }; class TransactionErrorCallback final : public SQLTransactionErrorCallback { public: static Ref create(Ref&& requestCallback) { return adoptRef(*new TransactionErrorCallback(WTFMove(requestCallback))); } private: TransactionErrorCallback(Ref&& requestCallback) : m_requestCallback(WTFMove(requestCallback)) { } bool handleEvent(SQLError* error) final { reportTransactionFailed(m_requestCallback.get(), error); return true; } Ref m_requestCallback; }; class TransactionSuccessCallback final : public VoidCallback { public: static Ref create() { return adoptRef(*new TransactionSuccessCallback()); } bool handleEvent() final { return false; } private: TransactionSuccessCallback() { } }; } // namespace void InspectorDatabaseAgent::didOpenDatabase(RefPtr&& database, const String& domain, const String& name, const String& version) { if (auto* resource = findByFileName(database->fileName())) { resource->setDatabase(WTFMove(database)); return; } auto resource = InspectorDatabaseResource::create(WTFMove(database), domain, name, version); m_resources.add(resource->id(), resource.ptr()); // Resources are only bound while visible. if (m_enabled) resource->bind(m_frontendDispatcher.get()); } void InspectorDatabaseAgent::clearResources() { m_resources.clear(); } InspectorDatabaseAgent::InspectorDatabaseAgent(WebAgentContext& context) : InspectorAgentBase(ASCIILiteral("Database"), context) , m_frontendDispatcher(std::make_unique(context.frontendRouter)) , m_backendDispatcher(Inspector::DatabaseBackendDispatcher::create(context.backendDispatcher, this)) { m_instrumentingAgents.setInspectorDatabaseAgent(this); } InspectorDatabaseAgent::~InspectorDatabaseAgent() { m_instrumentingAgents.setInspectorDatabaseAgent(nullptr); } void InspectorDatabaseAgent::didCreateFrontendAndBackend(Inspector::FrontendRouter*, Inspector::BackendDispatcher*) { } void InspectorDatabaseAgent::willDestroyFrontendAndBackend(Inspector::DisconnectReason) { ErrorString unused; disable(unused); } void InspectorDatabaseAgent::enable(ErrorString&) { if (m_enabled) return; m_enabled = true; for (auto& resource : m_resources.values()) resource->bind(m_frontendDispatcher.get()); } void InspectorDatabaseAgent::disable(ErrorString&) { if (!m_enabled) return; m_enabled = false; } void InspectorDatabaseAgent::getDatabaseTableNames(ErrorString& error, const String& databaseId, RefPtr>& names) { if (!m_enabled) { error = ASCIILiteral("Database agent is not enabled"); return; } names = Inspector::Protocol::Array::create(); if (auto* database = databaseForId(databaseId)) { for (auto& tableName : database->tableNames()) names->addItem(tableName); } } void InspectorDatabaseAgent::executeSQL(ErrorString&, const String& databaseId, const String& query, Ref&& requestCallback) { if (!m_enabled) { requestCallback->sendFailure("Database agent is not enabled"); return; } auto* database = databaseForId(databaseId); if (!database) { requestCallback->sendFailure("Database not found"); return; } database->transaction(TransactionCallback::create(query, requestCallback.copyRef()), TransactionErrorCallback::create(requestCallback.copyRef()), TransactionSuccessCallback::create()); } String InspectorDatabaseAgent::databaseId(Database* database) { for (auto& resource : m_resources) { if (resource.value->database() == database) return resource.key; } return String(); } InspectorDatabaseResource* InspectorDatabaseAgent::findByFileName(const String& fileName) { for (auto& resource : m_resources.values()) { if (resource->database()->fileName() == fileName) return resource.get(); } return nullptr; } Database* InspectorDatabaseAgent::databaseForId(const String& databaseId) { auto* resource = m_resources.get(databaseId); if (!resource) return nullptr; return resource->database(); } } // namespace WebCore