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