diff options
Diffstat (limited to 'dom/xul/templates/nsXULTemplateQueryProcessorStorage.cpp')
-rw-r--r-- | dom/xul/templates/nsXULTemplateQueryProcessorStorage.cpp | 502 |
1 files changed, 502 insertions, 0 deletions
diff --git a/dom/xul/templates/nsXULTemplateQueryProcessorStorage.cpp b/dom/xul/templates/nsXULTemplateQueryProcessorStorage.cpp new file mode 100644 index 000000000..a70307bf5 --- /dev/null +++ b/dom/xul/templates/nsXULTemplateQueryProcessorStorage.cpp @@ -0,0 +1,502 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "prprf.h" + +#include "nsIDOMNodeList.h" +#include "nsUnicharUtils.h" + +#include "nsArrayUtils.h" +#include "nsVariant.h" +#include "nsAppDirectoryServiceDefs.h" + +#include "nsIURI.h" +#include "nsIFileChannel.h" +#include "nsIFile.h" +#include "nsGkAtoms.h" +#include "nsContentUtils.h" + +#include "nsXULTemplateBuilder.h" +#include "nsXULTemplateResultStorage.h" +#include "nsXULContentUtils.h" +#include "nsXULSortService.h" + +#include "mozIStorageService.h" +#include "nsIChannel.h" +#include "nsIDocument.h" +#include "nsNetUtil.h" + +//---------------------------------------------------------------------- +// +// nsXULTemplateResultSetStorage +// + +NS_IMPL_ISUPPORTS(nsXULTemplateResultSetStorage, nsISimpleEnumerator) + + +nsXULTemplateResultSetStorage::nsXULTemplateResultSetStorage(mozIStorageStatement* aStatement) + : mStatement(aStatement) +{ + uint32_t count; + nsresult rv = aStatement->GetColumnCount(&count); + if (NS_FAILED(rv)) { + mStatement = nullptr; + return; + } + for (uint32_t c = 0; c < count; c++) { + nsAutoCString name; + rv = aStatement->GetColumnName(c, name); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr<nsIAtom> columnName = NS_Atomize(NS_LITERAL_CSTRING("?") + name); + mColumnNames.AppendObject(columnName); + } + } +} + +NS_IMETHODIMP +nsXULTemplateResultSetStorage::HasMoreElements(bool *aResult) +{ + if (!mStatement) { + *aResult = false; + return NS_OK; + } + + nsresult rv = mStatement->ExecuteStep(aResult); + NS_ENSURE_SUCCESS(rv, rv); + // Because the nsXULTemplateResultSetStorage is owned by many nsXULTemplateResultStorage objects, + // it could live longer than it needed to get results. + // So we destroy the statement to free resources when all results are fetched + if (!*aResult) { + mStatement = nullptr; + } + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateResultSetStorage::GetNext(nsISupports **aResult) +{ + nsXULTemplateResultStorage* result = + new nsXULTemplateResultStorage(this); + *aResult = result; + NS_ADDREF(result); + return NS_OK; +} + + +int32_t +nsXULTemplateResultSetStorage::GetColumnIndex(nsIAtom* aColumnName) +{ + int32_t count = mColumnNames.Count(); + for (int32_t c = 0; c < count; c++) { + if (mColumnNames[c] == aColumnName) + return c; + } + + return -1; +} + +void +nsXULTemplateResultSetStorage::FillColumnValues(nsCOMArray<nsIVariant>& aArray) +{ + if (!mStatement) + return; + + int32_t count = mColumnNames.Count(); + + for (int32_t c = 0; c < count; c++) { + RefPtr<nsVariant> value = new nsVariant(); + + int32_t type; + mStatement->GetTypeOfIndex(c, &type); + + if (type == mStatement->VALUE_TYPE_INTEGER) { + int64_t val = mStatement->AsInt64(c); + value->SetAsInt64(val); + } + else if (type == mStatement->VALUE_TYPE_FLOAT) { + double val = mStatement->AsDouble(c); + value->SetAsDouble(val); + } + else { + nsAutoString val; + nsresult rv = mStatement->GetString(c, val); + if (NS_FAILED(rv)) + value->SetAsAString(EmptyString()); + else + value->SetAsAString(val); + } + aArray.AppendObject(value); + } +} + + + +//---------------------------------------------------------------------- +// +// nsXULTemplateQueryProcessorStorage +// + +NS_IMPL_ISUPPORTS(nsXULTemplateQueryProcessorStorage, + nsIXULTemplateQueryProcessor) + + +nsXULTemplateQueryProcessorStorage::nsXULTemplateQueryProcessorStorage() + : mGenerationStarted(false) +{ +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorStorage::GetDatasource(nsIArray* aDataSources, + nsIDOMNode* aRootNode, + bool aIsTrusted, + nsIXULTemplateBuilder* aBuilder, + bool* aShouldDelayBuilding, + nsISupports** aReturn) +{ + *aReturn = nullptr; + *aShouldDelayBuilding = false; + + if (!aIsTrusted) { + return NS_OK; + } + + uint32_t length; + nsresult rv = aDataSources->GetLength(&length); + NS_ENSURE_SUCCESS(rv, rv); + + if (length == 0) { + return NS_OK; + } + + // We get only the first uri. This query processor supports + // only one database at a time. + nsCOMPtr<nsIURI> uri; + uri = do_QueryElementAt(aDataSources, 0); + + if (!uri) { + // No uri in the list of datasources + return NS_OK; + } + + nsCOMPtr<mozIStorageService> storage = + do_GetService("@mozilla.org/storage/service;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIFile> databaseFile; + nsAutoCString scheme; + rv = uri->GetScheme(scheme); + NS_ENSURE_SUCCESS(rv, rv); + + if (scheme.EqualsLiteral("profile")) { + + nsAutoCString path; + rv = uri->GetPath(path); + NS_ENSURE_SUCCESS(rv, rv); + + if (path.IsEmpty()) { + return NS_ERROR_FAILURE; + } + + rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, + getter_AddRefs(databaseFile)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = databaseFile->AppendNative(path); + NS_ENSURE_SUCCESS(rv, rv); + } + else { + nsCOMPtr<nsIChannel> channel; + nsCOMPtr<nsINode> node = do_QueryInterface(aRootNode); + + // The following channel is never openend, so it does not matter what + // securityFlags we pass; let's follow the principle of least privilege. + rv = NS_NewChannel(getter_AddRefs(channel), + uri, + node, + nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED, + nsIContentPolicy::TYPE_OTHER); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(channel, &rv); + if (NS_FAILED(rv)) { // if it fails, not a file url + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_BAD_URI); + return rv; + } + + rv = fileChannel->GetFile(getter_AddRefs(databaseFile)); + NS_ENSURE_SUCCESS(rv, rv); + } + + // ok now we have an URI of a sqlite file + nsCOMPtr<mozIStorageConnection> connection; + rv = storage->OpenDatabase(databaseFile, getter_AddRefs(connection)); + if (NS_FAILED(rv)) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_CANNOT_OPEN_DATABASE); + return rv; + } + + connection.forget(aReturn); + return NS_OK; +} + + + +NS_IMETHODIMP +nsXULTemplateQueryProcessorStorage::InitializeForBuilding(nsISupports* aDatasource, + nsIXULTemplateBuilder* aBuilder, + nsIDOMNode* aRootNode) +{ + NS_ENSURE_STATE(!mGenerationStarted); + + mStorageConnection = do_QueryInterface(aDatasource); + if (!mStorageConnection) + return NS_ERROR_INVALID_ARG; + + bool ready; + mStorageConnection->GetConnectionReady(&ready); + if (!ready) + return NS_ERROR_UNEXPECTED; + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorStorage::Done() +{ + mGenerationStarted = false; + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorStorage::CompileQuery(nsIXULTemplateBuilder* aBuilder, + nsIDOMNode* aQueryNode, + nsIAtom* aRefVariable, + nsIAtom* aMemberVariable, + nsISupports** aReturn) +{ + nsCOMPtr<nsIDOMNodeList> childNodes; + aQueryNode->GetChildNodes(getter_AddRefs(childNodes)); + + uint32_t length; + childNodes->GetLength(&length); + + nsCOMPtr<mozIStorageStatement> statement; + nsCOMPtr<nsIContent> queryContent = do_QueryInterface(aQueryNode); + nsAutoString sqlQuery; + + // Let's get all text nodes (which should be the query) + if (!nsContentUtils::GetNodeTextContent(queryContent, false, sqlQuery, fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + nsresult rv = mStorageConnection->CreateStatement(NS_ConvertUTF16toUTF8(sqlQuery), + getter_AddRefs(statement)); + if (NS_FAILED(rv)) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_BAD_QUERY); + return rv; + } + + uint32_t parameterCount = 0; + for (nsIContent* child = queryContent->GetFirstChild(); + child; + child = child->GetNextSibling()) { + + if (child->NodeInfo()->Equals(nsGkAtoms::param, kNameSpaceID_XUL)) { + nsAutoString value; + if (!nsContentUtils::GetNodeTextContent(child, false, value, fallible)) { + return NS_ERROR_OUT_OF_MEMORY; + } + + uint32_t index = parameterCount; + nsAutoString name, indexValue; + + if (child->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name)) { + rv = statement->GetParameterIndex(NS_ConvertUTF16toUTF8(name), + &index); + if (NS_FAILED(rv)) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_UNKNOWN_QUERY_PARAMETER); + return rv; + } + parameterCount++; + } + else if (child->GetAttr(kNameSpaceID_None, nsGkAtoms::index, indexValue)) { + PR_sscanf(NS_ConvertUTF16toUTF8(indexValue).get(),"%d",&index); + if (index > 0) + index--; + } + else { + parameterCount++; + } + + static nsIContent::AttrValuesArray sTypeValues[] = + { &nsGkAtoms::int32, &nsGkAtoms::integer, &nsGkAtoms::int64, + &nsGkAtoms::null, &nsGkAtoms::double_, &nsGkAtoms::string, nullptr }; + + int32_t typeError = 1; + int32_t typeValue = child->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::type, + sTypeValues, eCaseMatters); + rv = NS_ERROR_ILLEGAL_VALUE; + int32_t valInt32 = 0; + int64_t valInt64 = 0; + double valFloat = 0; + + switch (typeValue) { + case 0: + case 1: + typeError = PR_sscanf(NS_ConvertUTF16toUTF8(value).get(),"%d",&valInt32); + if (typeError > 0) + rv = statement->BindInt32ByIndex(index, valInt32); + break; + case 2: + typeError = PR_sscanf(NS_ConvertUTF16toUTF8(value).get(),"%lld",&valInt64); + if (typeError > 0) + rv = statement->BindInt64ByIndex(index, valInt64); + break; + case 3: + rv = statement->BindNullByIndex(index); + break; + case 4: + typeError = PR_sscanf(NS_ConvertUTF16toUTF8(value).get(),"%lf",&valFloat); + if (typeError > 0) + rv = statement->BindDoubleByIndex(index, valFloat); + break; + case 5: + case nsIContent::ATTR_MISSING: + rv = statement->BindStringByIndex(index, value); + break; + default: + typeError = 0; + } + + if (typeError <= 0) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_WRONG_TYPE_QUERY_PARAMETER); + return rv; + } + + if (NS_FAILED(rv)) { + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_QUERY_PARAMETER_NOT_BOUND); + return rv; + } + } + } + + *aReturn = statement; + NS_IF_ADDREF(*aReturn); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorStorage::GenerateResults(nsISupports* aDatasource, + nsIXULTemplateResult* aRef, + nsISupports* aQuery, + nsISimpleEnumerator** aResults) +{ + mGenerationStarted = true; + + nsCOMPtr<mozIStorageStatement> statement = do_QueryInterface(aQuery); + if (!statement) + return NS_ERROR_FAILURE; + + nsXULTemplateResultSetStorage* results = + new nsXULTemplateResultSetStorage(statement); + *aResults = results; + NS_ADDREF(*aResults); + + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorStorage::AddBinding(nsIDOMNode* aRuleNode, + nsIAtom* aVar, + nsIAtom* aRef, + const nsAString& aExpr) +{ + return NS_OK; +} + +NS_IMETHODIMP +nsXULTemplateQueryProcessorStorage::TranslateRef(nsISupports* aDatasource, + const nsAString& aRefString, + nsIXULTemplateResult** aRef) +{ + nsXULTemplateResultStorage* result = + new nsXULTemplateResultStorage(nullptr); + *aRef = result; + NS_ADDREF(*aRef); + return NS_OK; +} + + +NS_IMETHODIMP +nsXULTemplateQueryProcessorStorage::CompareResults(nsIXULTemplateResult* aLeft, + nsIXULTemplateResult* aRight, + nsIAtom* aVar, + uint32_t aSortHints, + int32_t* aResult) +{ + *aResult = 0; + if (!aVar) + return NS_OK; + + // We're going to see if values are integers or float, to perform + // a suitable comparison + nsCOMPtr<nsISupports> leftValue, rightValue; + if (aLeft) + aLeft->GetBindingObjectFor(aVar, getter_AddRefs(leftValue)); + if (aRight) + aRight->GetBindingObjectFor(aVar, getter_AddRefs(rightValue)); + + if (leftValue && rightValue) { + nsCOMPtr<nsIVariant> vLeftValue = do_QueryInterface(leftValue); + nsCOMPtr<nsIVariant> vRightValue = do_QueryInterface(rightValue); + + if (vLeftValue && vRightValue) { + nsresult rv1, rv2; + uint16_t vtypeL, vtypeR; + vLeftValue->GetDataType(&vtypeL); + vRightValue->GetDataType(&vtypeR); + + if (vtypeL == vtypeR) { + if (vtypeL == nsIDataType::VTYPE_INT64) { + int64_t leftValue, rightValue; + rv1 = vLeftValue->GetAsInt64(&leftValue); + rv2 = vRightValue->GetAsInt64(&rightValue); + if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) { + if (leftValue > rightValue) + *aResult = 1; + else if (leftValue < rightValue) + *aResult = -1; + return NS_OK; + } + } + else if (vtypeL == nsIDataType::VTYPE_DOUBLE) { + double leftValue, rightValue; + rv1 = vLeftValue->GetAsDouble(&leftValue); + rv2 = vRightValue->GetAsDouble(&rightValue); + if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) { + if (leftValue > rightValue) + *aResult = 1; + else if (leftValue < rightValue) + *aResult = -1; + return NS_OK; + } + } + } + } + } + + // Values are not integers or floats, so we just compare them as simple strings + nsAutoString leftVal; + if (aLeft) + aLeft->GetBindingFor(aVar, leftVal); + + nsAutoString rightVal; + if (aRight) + aRight->GetBindingFor(aVar, rightVal); + + *aResult = XULSortServiceImpl::CompareValues(leftVal, rightVal, aSortHints); + return NS_OK; +} |