1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/xul/templates/src/nsXULTemplateQueryProcessorStorage.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,510 @@ 1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "prprf.h" 1.10 + 1.11 +#include "nsIDOMNodeList.h" 1.12 +#include "nsUnicharUtils.h" 1.13 + 1.14 +#include "nsArrayUtils.h" 1.15 +#include "nsIVariant.h" 1.16 +#include "nsAppDirectoryServiceDefs.h" 1.17 + 1.18 +#include "nsIURI.h" 1.19 +#include "nsIIOService.h" 1.20 +#include "nsIFileChannel.h" 1.21 +#include "nsIFile.h" 1.22 +#include "nsGkAtoms.h" 1.23 +#include "nsContentUtils.h" 1.24 + 1.25 +#include "nsXULTemplateBuilder.h" 1.26 +#include "nsXULTemplateResultStorage.h" 1.27 +#include "nsXULContentUtils.h" 1.28 +#include "nsXULSortService.h" 1.29 + 1.30 +#include "mozIStorageService.h" 1.31 +#include "nsIChannel.h" 1.32 +#include "nsIDocument.h" 1.33 + 1.34 +//---------------------------------------------------------------------- 1.35 +// 1.36 +// nsXULTemplateResultSetStorage 1.37 +// 1.38 + 1.39 +NS_IMPL_ISUPPORTS(nsXULTemplateResultSetStorage, nsISimpleEnumerator) 1.40 + 1.41 + 1.42 +nsXULTemplateResultSetStorage::nsXULTemplateResultSetStorage(mozIStorageStatement* aStatement) 1.43 + : mStatement(aStatement) 1.44 +{ 1.45 + uint32_t count; 1.46 + nsresult rv = aStatement->GetColumnCount(&count); 1.47 + if (NS_FAILED(rv)) { 1.48 + mStatement = nullptr; 1.49 + return; 1.50 + } 1.51 + for (uint32_t c = 0; c < count; c++) { 1.52 + nsAutoCString name; 1.53 + rv = aStatement->GetColumnName(c, name); 1.54 + if (NS_SUCCEEDED(rv)) { 1.55 + nsCOMPtr<nsIAtom> columnName = do_GetAtom(NS_LITERAL_CSTRING("?") + name); 1.56 + mColumnNames.AppendObject(columnName); 1.57 + } 1.58 + } 1.59 +} 1.60 + 1.61 +NS_IMETHODIMP 1.62 +nsXULTemplateResultSetStorage::HasMoreElements(bool *aResult) 1.63 +{ 1.64 + if (!mStatement) { 1.65 + *aResult = false; 1.66 + return NS_OK; 1.67 + } 1.68 + 1.69 + nsresult rv = mStatement->ExecuteStep(aResult); 1.70 + NS_ENSURE_SUCCESS(rv, rv); 1.71 + // Because the nsXULTemplateResultSetStorage is owned by many nsXULTemplateResultStorage objects, 1.72 + // it could live longer than it needed to get results. 1.73 + // So we destroy the statement to free resources when all results are fetched 1.74 + if (!*aResult) { 1.75 + mStatement = nullptr; 1.76 + } 1.77 + return NS_OK; 1.78 +} 1.79 + 1.80 +NS_IMETHODIMP 1.81 +nsXULTemplateResultSetStorage::GetNext(nsISupports **aResult) 1.82 +{ 1.83 + nsXULTemplateResultStorage* result = 1.84 + new nsXULTemplateResultStorage(this); 1.85 + 1.86 + if (!result) 1.87 + return NS_ERROR_OUT_OF_MEMORY; 1.88 + 1.89 + *aResult = result; 1.90 + NS_ADDREF(result); 1.91 + return NS_OK; 1.92 +} 1.93 + 1.94 + 1.95 +int32_t 1.96 +nsXULTemplateResultSetStorage::GetColumnIndex(nsIAtom* aColumnName) 1.97 +{ 1.98 + int32_t count = mColumnNames.Count(); 1.99 + for (int32_t c = 0; c < count; c++) { 1.100 + if (mColumnNames[c] == aColumnName) 1.101 + return c; 1.102 + } 1.103 + 1.104 + return -1; 1.105 +} 1.106 + 1.107 +void 1.108 +nsXULTemplateResultSetStorage::FillColumnValues(nsCOMArray<nsIVariant>& aArray) 1.109 +{ 1.110 + if (!mStatement) 1.111 + return; 1.112 + 1.113 + int32_t count = mColumnNames.Count(); 1.114 + 1.115 + for (int32_t c = 0; c < count; c++) { 1.116 + nsCOMPtr<nsIWritableVariant> value = do_CreateInstance("@mozilla.org/variant;1"); 1.117 + 1.118 + int32_t type; 1.119 + mStatement->GetTypeOfIndex(c, &type); 1.120 + 1.121 + if (type == mStatement->VALUE_TYPE_INTEGER) { 1.122 + int64_t val = mStatement->AsInt64(c); 1.123 + value->SetAsInt64(val); 1.124 + } 1.125 + else if (type == mStatement->VALUE_TYPE_FLOAT) { 1.126 + double val = mStatement->AsDouble(c); 1.127 + value->SetAsDouble(val); 1.128 + } 1.129 + else { 1.130 + nsAutoString val; 1.131 + nsresult rv = mStatement->GetString(c, val); 1.132 + if (NS_FAILED(rv)) 1.133 + value->SetAsAString(EmptyString()); 1.134 + else 1.135 + value->SetAsAString(val); 1.136 + } 1.137 + aArray.AppendObject(value); 1.138 + } 1.139 +} 1.140 + 1.141 + 1.142 + 1.143 +//---------------------------------------------------------------------- 1.144 +// 1.145 +// nsXULTemplateQueryProcessorStorage 1.146 +// 1.147 + 1.148 +NS_IMPL_ISUPPORTS(nsXULTemplateQueryProcessorStorage, 1.149 + nsIXULTemplateQueryProcessor) 1.150 + 1.151 + 1.152 +nsXULTemplateQueryProcessorStorage::nsXULTemplateQueryProcessorStorage() 1.153 + : mGenerationStarted(false) 1.154 +{ 1.155 +} 1.156 + 1.157 +NS_IMETHODIMP 1.158 +nsXULTemplateQueryProcessorStorage::GetDatasource(nsIArray* aDataSources, 1.159 + nsIDOMNode* aRootNode, 1.160 + bool aIsTrusted, 1.161 + nsIXULTemplateBuilder* aBuilder, 1.162 + bool* aShouldDelayBuilding, 1.163 + nsISupports** aReturn) 1.164 +{ 1.165 + *aReturn = nullptr; 1.166 + *aShouldDelayBuilding = false; 1.167 + 1.168 + if (!aIsTrusted) { 1.169 + return NS_OK; 1.170 + } 1.171 + 1.172 + uint32_t length; 1.173 + nsresult rv = aDataSources->GetLength(&length); 1.174 + NS_ENSURE_SUCCESS(rv, rv); 1.175 + 1.176 + if (length == 0) { 1.177 + return NS_OK; 1.178 + } 1.179 + 1.180 + // We get only the first uri. This query processor supports 1.181 + // only one database at a time. 1.182 + nsCOMPtr<nsIURI> uri; 1.183 + uri = do_QueryElementAt(aDataSources, 0); 1.184 + 1.185 + if (!uri) { 1.186 + // No uri in the list of datasources 1.187 + return NS_OK; 1.188 + } 1.189 + 1.190 + nsCOMPtr<mozIStorageService> storage = 1.191 + do_GetService("@mozilla.org/storage/service;1", &rv); 1.192 + NS_ENSURE_SUCCESS(rv, rv); 1.193 + 1.194 + nsCOMPtr<nsIFile> databaseFile; 1.195 + nsAutoCString scheme; 1.196 + rv = uri->GetScheme(scheme); 1.197 + NS_ENSURE_SUCCESS(rv, rv); 1.198 + 1.199 + if (scheme.EqualsLiteral("profile")) { 1.200 + 1.201 + nsAutoCString path; 1.202 + rv = uri->GetPath(path); 1.203 + NS_ENSURE_SUCCESS(rv, rv); 1.204 + 1.205 + if (path.IsEmpty()) { 1.206 + return NS_ERROR_FAILURE; 1.207 + } 1.208 + 1.209 + rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, 1.210 + getter_AddRefs(databaseFile)); 1.211 + NS_ENSURE_SUCCESS(rv, rv); 1.212 + 1.213 + rv = databaseFile->AppendNative(path); 1.214 + NS_ENSURE_SUCCESS(rv, rv); 1.215 + } 1.216 + else { 1.217 + nsCOMPtr<nsIChannel> channel; 1.218 + nsCOMPtr<nsIIOService> ioservice = 1.219 + do_GetService("@mozilla.org/network/io-service;1", &rv); 1.220 + NS_ENSURE_SUCCESS(rv, rv); 1.221 + 1.222 + rv = ioservice->NewChannelFromURI(uri, getter_AddRefs(channel)); 1.223 + NS_ENSURE_SUCCESS(rv, rv); 1.224 + 1.225 + nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(channel, &rv); 1.226 + if (NS_FAILED(rv)) { // if it fails, not a file url 1.227 + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_BAD_URI); 1.228 + return rv; 1.229 + } 1.230 + 1.231 + nsCOMPtr<nsIFile> file; 1.232 + rv = fileChannel->GetFile(getter_AddRefs(databaseFile)); 1.233 + NS_ENSURE_SUCCESS(rv, rv); 1.234 + } 1.235 + 1.236 + // ok now we have an URI of a sqlite file 1.237 + nsCOMPtr<mozIStorageConnection> connection; 1.238 + rv = storage->OpenDatabase(databaseFile, getter_AddRefs(connection)); 1.239 + if (NS_FAILED(rv)) { 1.240 + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_CANNOT_OPEN_DATABASE); 1.241 + return rv; 1.242 + } 1.243 + 1.244 + NS_ADDREF(*aReturn = connection); 1.245 + return NS_OK; 1.246 +} 1.247 + 1.248 + 1.249 + 1.250 +NS_IMETHODIMP 1.251 +nsXULTemplateQueryProcessorStorage::InitializeForBuilding(nsISupports* aDatasource, 1.252 + nsIXULTemplateBuilder* aBuilder, 1.253 + nsIDOMNode* aRootNode) 1.254 +{ 1.255 + NS_ENSURE_STATE(!mGenerationStarted); 1.256 + 1.257 + mStorageConnection = do_QueryInterface(aDatasource); 1.258 + if (!mStorageConnection) 1.259 + return NS_ERROR_INVALID_ARG; 1.260 + 1.261 + bool ready; 1.262 + mStorageConnection->GetConnectionReady(&ready); 1.263 + if (!ready) 1.264 + return NS_ERROR_UNEXPECTED; 1.265 + 1.266 + return NS_OK; 1.267 +} 1.268 + 1.269 +NS_IMETHODIMP 1.270 +nsXULTemplateQueryProcessorStorage::Done() 1.271 +{ 1.272 + mGenerationStarted = false; 1.273 + return NS_OK; 1.274 +} 1.275 + 1.276 +NS_IMETHODIMP 1.277 +nsXULTemplateQueryProcessorStorage::CompileQuery(nsIXULTemplateBuilder* aBuilder, 1.278 + nsIDOMNode* aQueryNode, 1.279 + nsIAtom* aRefVariable, 1.280 + nsIAtom* aMemberVariable, 1.281 + nsISupports** aReturn) 1.282 +{ 1.283 + nsCOMPtr<nsIDOMNodeList> childNodes; 1.284 + aQueryNode->GetChildNodes(getter_AddRefs(childNodes)); 1.285 + 1.286 + uint32_t length; 1.287 + childNodes->GetLength(&length); 1.288 + 1.289 + nsCOMPtr<mozIStorageStatement> statement; 1.290 + nsCOMPtr<nsIContent> queryContent = do_QueryInterface(aQueryNode); 1.291 + nsAutoString sqlQuery; 1.292 + 1.293 + // Let's get all text nodes (which should be the query) 1.294 + if (!nsContentUtils::GetNodeTextContent(queryContent, false, sqlQuery)) { 1.295 + return NS_ERROR_OUT_OF_MEMORY; 1.296 + } 1.297 + 1.298 + nsresult rv = mStorageConnection->CreateStatement(NS_ConvertUTF16toUTF8(sqlQuery), 1.299 + getter_AddRefs(statement)); 1.300 + if (NS_FAILED(rv)) { 1.301 + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_BAD_QUERY); 1.302 + return rv; 1.303 + } 1.304 + 1.305 + uint32_t parameterCount = 0; 1.306 + for (nsIContent* child = queryContent->GetFirstChild(); 1.307 + child; 1.308 + child = child->GetNextSibling()) { 1.309 + 1.310 + if (child->NodeInfo()->Equals(nsGkAtoms::param, kNameSpaceID_XUL)) { 1.311 + nsAutoString value; 1.312 + if (!nsContentUtils::GetNodeTextContent(child, false, value)) { 1.313 + return NS_ERROR_OUT_OF_MEMORY; 1.314 + } 1.315 + 1.316 + uint32_t index = parameterCount; 1.317 + nsAutoString name, indexValue; 1.318 + 1.319 + if (child->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name)) { 1.320 + rv = statement->GetParameterIndex(NS_ConvertUTF16toUTF8(name), 1.321 + &index); 1.322 + if (NS_FAILED(rv)) { 1.323 + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_UNKNOWN_QUERY_PARAMETER); 1.324 + return rv; 1.325 + } 1.326 + parameterCount++; 1.327 + } 1.328 + else if (child->GetAttr(kNameSpaceID_None, nsGkAtoms::index, indexValue)) { 1.329 + PR_sscanf(NS_ConvertUTF16toUTF8(indexValue).get(),"%d",&index); 1.330 + if (index > 0) 1.331 + index--; 1.332 + } 1.333 + else { 1.334 + parameterCount++; 1.335 + } 1.336 + 1.337 + static nsIContent::AttrValuesArray sTypeValues[] = 1.338 + { &nsGkAtoms::int32, &nsGkAtoms::integer, &nsGkAtoms::int64, 1.339 + &nsGkAtoms::null, &nsGkAtoms::double_, &nsGkAtoms::string, nullptr }; 1.340 + 1.341 + int32_t typeError = 1; 1.342 + int32_t typeValue = child->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::type, 1.343 + sTypeValues, eCaseMatters); 1.344 + rv = NS_ERROR_ILLEGAL_VALUE; 1.345 + int32_t valInt32 = 0; 1.346 + int64_t valInt64 = 0; 1.347 + double valFloat = 0; 1.348 + 1.349 + switch (typeValue) { 1.350 + case 0: 1.351 + case 1: 1.352 + typeError = PR_sscanf(NS_ConvertUTF16toUTF8(value).get(),"%d",&valInt32); 1.353 + if (typeError > 0) 1.354 + rv = statement->BindInt32ByIndex(index, valInt32); 1.355 + break; 1.356 + case 2: 1.357 + typeError = PR_sscanf(NS_ConvertUTF16toUTF8(value).get(),"%lld",&valInt64); 1.358 + if (typeError > 0) 1.359 + rv = statement->BindInt64ByIndex(index, valInt64); 1.360 + break; 1.361 + case 3: 1.362 + rv = statement->BindNullByIndex(index); 1.363 + break; 1.364 + case 4: 1.365 + typeError = PR_sscanf(NS_ConvertUTF16toUTF8(value).get(),"%lf",&valFloat); 1.366 + if (typeError > 0) 1.367 + rv = statement->BindDoubleByIndex(index, valFloat); 1.368 + break; 1.369 + case 5: 1.370 + case nsIContent::ATTR_MISSING: 1.371 + rv = statement->BindStringByIndex(index, value); 1.372 + break; 1.373 + default: 1.374 + typeError = 0; 1.375 + } 1.376 + 1.377 + if (typeError <= 0) { 1.378 + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_WRONG_TYPE_QUERY_PARAMETER); 1.379 + return rv; 1.380 + } 1.381 + 1.382 + if (NS_FAILED(rv)) { 1.383 + nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_QUERY_PARAMETER_NOT_BOUND); 1.384 + return rv; 1.385 + } 1.386 + } 1.387 + } 1.388 + 1.389 + *aReturn = statement; 1.390 + NS_IF_ADDREF(*aReturn); 1.391 + 1.392 + return NS_OK; 1.393 +} 1.394 + 1.395 +NS_IMETHODIMP 1.396 +nsXULTemplateQueryProcessorStorage::GenerateResults(nsISupports* aDatasource, 1.397 + nsIXULTemplateResult* aRef, 1.398 + nsISupports* aQuery, 1.399 + nsISimpleEnumerator** aResults) 1.400 +{ 1.401 + mGenerationStarted = true; 1.402 + 1.403 + nsCOMPtr<mozIStorageStatement> statement = do_QueryInterface(aQuery); 1.404 + if (!statement) 1.405 + return NS_ERROR_FAILURE; 1.406 + 1.407 + nsXULTemplateResultSetStorage* results = 1.408 + new nsXULTemplateResultSetStorage(statement); 1.409 + 1.410 + if (!results) 1.411 + return NS_ERROR_OUT_OF_MEMORY; 1.412 + 1.413 + *aResults = results; 1.414 + NS_ADDREF(*aResults); 1.415 + 1.416 + return NS_OK; 1.417 +} 1.418 + 1.419 +NS_IMETHODIMP 1.420 +nsXULTemplateQueryProcessorStorage::AddBinding(nsIDOMNode* aRuleNode, 1.421 + nsIAtom* aVar, 1.422 + nsIAtom* aRef, 1.423 + const nsAString& aExpr) 1.424 +{ 1.425 + return NS_OK; 1.426 +} 1.427 + 1.428 +NS_IMETHODIMP 1.429 +nsXULTemplateQueryProcessorStorage::TranslateRef(nsISupports* aDatasource, 1.430 + const nsAString& aRefString, 1.431 + nsIXULTemplateResult** aRef) 1.432 +{ 1.433 + nsXULTemplateResultStorage* result = 1.434 + new nsXULTemplateResultStorage(nullptr); 1.435 + if (!result) 1.436 + return NS_ERROR_OUT_OF_MEMORY; 1.437 + 1.438 + *aRef = result; 1.439 + NS_ADDREF(*aRef); 1.440 + return NS_OK; 1.441 +} 1.442 + 1.443 + 1.444 +NS_IMETHODIMP 1.445 +nsXULTemplateQueryProcessorStorage::CompareResults(nsIXULTemplateResult* aLeft, 1.446 + nsIXULTemplateResult* aRight, 1.447 + nsIAtom* aVar, 1.448 + uint32_t aSortHints, 1.449 + int32_t* aResult) 1.450 +{ 1.451 + *aResult = 0; 1.452 + if (!aVar) 1.453 + return NS_OK; 1.454 + 1.455 + // We're going to see if values are integers or float, to perform 1.456 + // a suitable comparison 1.457 + nsCOMPtr<nsISupports> leftValue, rightValue; 1.458 + if (aLeft) 1.459 + aLeft->GetBindingObjectFor(aVar, getter_AddRefs(leftValue)); 1.460 + if (aRight) 1.461 + aRight->GetBindingObjectFor(aVar, getter_AddRefs(rightValue)); 1.462 + 1.463 + if (leftValue && rightValue) { 1.464 + nsCOMPtr<nsIVariant> vLeftValue = do_QueryInterface(leftValue); 1.465 + nsCOMPtr<nsIVariant> vRightValue = do_QueryInterface(rightValue); 1.466 + 1.467 + if (vLeftValue && vRightValue) { 1.468 + nsresult rv1, rv2; 1.469 + uint16_t vtypeL, vtypeR; 1.470 + vLeftValue->GetDataType(&vtypeL); 1.471 + vRightValue->GetDataType(&vtypeR); 1.472 + 1.473 + if (vtypeL == vtypeR) { 1.474 + if (vtypeL == nsIDataType::VTYPE_INT64) { 1.475 + int64_t leftValue, rightValue; 1.476 + rv1 = vLeftValue->GetAsInt64(&leftValue); 1.477 + rv2 = vRightValue->GetAsInt64(&rightValue); 1.478 + if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) { 1.479 + if (leftValue > rightValue) 1.480 + *aResult = 1; 1.481 + else if (leftValue < rightValue) 1.482 + *aResult = -1; 1.483 + return NS_OK; 1.484 + } 1.485 + } 1.486 + else if (vtypeL == nsIDataType::VTYPE_DOUBLE) { 1.487 + double leftValue, rightValue; 1.488 + rv1 = vLeftValue->GetAsDouble(&leftValue); 1.489 + rv2 = vRightValue->GetAsDouble(&rightValue); 1.490 + if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) { 1.491 + if (leftValue > rightValue) 1.492 + *aResult = 1; 1.493 + else if (leftValue < rightValue) 1.494 + *aResult = -1; 1.495 + return NS_OK; 1.496 + } 1.497 + } 1.498 + } 1.499 + } 1.500 + } 1.501 + 1.502 + // Values are not integers or floats, so we just compare them as simple strings 1.503 + nsAutoString leftVal; 1.504 + if (aLeft) 1.505 + aLeft->GetBindingFor(aVar, leftVal); 1.506 + 1.507 + nsAutoString rightVal; 1.508 + if (aRight) 1.509 + aRight->GetBindingFor(aVar, rightVal); 1.510 + 1.511 + *aResult = XULSortServiceImpl::CompareValues(leftVal, rightVal, aSortHints); 1.512 + return NS_OK; 1.513 +}