Bug 470455 - test_database_sync_embed_visits.js leaks, r=sdwilsh
[wine-gecko.git] / content / xul / templates / src / nsXULTemplateQueryProcessorStorage.cpp
blobbb3d73ff3b7d5795a87dde95b92b16a3deced4cd
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is Neil Deakin
18 * Portions created by the Initial Developer are Copyright (C) 2005
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
22 * Laurent Jouanneau <laurent.jouanneau@disruptive-innovations.com>
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 #include "prprf.h"
40 #include "nsIDOMNodeList.h"
41 #include "nsUnicharUtils.h"
43 #include "nsArrayUtils.h"
44 #include "nsIVariant.h"
45 #include "nsAppDirectoryServiceDefs.h"
47 #include "nsIURI.h"
48 #include "nsIIOService.h"
49 #include "nsIFileChannel.h"
50 #include "nsIFile.h"
51 #include "nsGkAtoms.h"
52 #include "nsContentUtils.h"
54 #include "nsXULTemplateBuilder.h"
55 #include "nsXULTemplateResultStorage.h"
57 #include "mozIStorageService.h"
59 //----------------------------------------------------------------------
61 // nsXULTemplateResultSetStorage
64 NS_IMPL_ISUPPORTS1(nsXULTemplateResultSetStorage, nsISimpleEnumerator)
67 nsXULTemplateResultSetStorage::nsXULTemplateResultSetStorage(mozIStorageStatement* aStatement)
68 : mStatement(aStatement)
70 PRUint32 count;
71 nsresult rv = aStatement->GetColumnCount(&count);
72 if (NS_FAILED(rv)) {
73 mStatement = nsnull;
74 return;
76 for (PRUint32 c = 0; c < count; c++) {
77 nsCAutoString name;
78 rv = aStatement->GetColumnName(c, name);
79 if (NS_SUCCEEDED(rv)) {
80 nsCOMPtr<nsIAtom> columnName = do_GetAtom(NS_LITERAL_CSTRING("?") + name);
81 mColumnNames.AppendObject(columnName);
86 NS_IMETHODIMP
87 nsXULTemplateResultSetStorage::HasMoreElements(PRBool *aResult)
89 if (!mStatement) {
90 *aResult = PR_FALSE;
91 return NS_OK;
94 nsresult rv = mStatement->ExecuteStep(aResult);
95 NS_ENSURE_SUCCESS(rv, rv);
96 // Because the nsXULTemplateResultSetStorage is owned by many nsXULTemplateResultStorage objects,
97 // it could live longer than it needed to get results.
98 // So we destroy the statement to free resources when all results are fetched
99 if (*aResult == PR_FALSE) {
100 mStatement = nsnull;
102 return NS_OK;
105 NS_IMETHODIMP
106 nsXULTemplateResultSetStorage::GetNext(nsISupports **aResult)
108 nsXULTemplateResultStorage* result =
109 new nsXULTemplateResultStorage(this);
111 if (!result)
112 return NS_ERROR_OUT_OF_MEMORY;
114 *aResult = result;
115 NS_ADDREF(result);
116 return NS_OK;
120 PRInt32
121 nsXULTemplateResultSetStorage::GetColumnIndex(nsIAtom* aColumnName)
123 PRInt32 count = mColumnNames.Count();
124 for (PRInt32 c = 0; c < count; c++) {
125 if (mColumnNames[c] == aColumnName)
126 return c;
129 return -1;
132 void
133 nsXULTemplateResultSetStorage::FillColumnValues(nsCOMArray<nsIVariant>& aArray)
135 if (!mStatement)
136 return;
138 PRInt32 count = mColumnNames.Count();
140 for (PRInt32 c = 0; c < count; c++) {
141 nsCOMPtr<nsIWritableVariant> value = do_CreateInstance("@mozilla.org/variant;1");
143 PRInt32 type;
144 mStatement->GetTypeOfIndex(c, &type);
146 if (type == mStatement->VALUE_TYPE_INTEGER) {
147 PRInt32 val = mStatement->AsInt32(c);
148 value->SetAsInt32(val);
150 else if (type == mStatement->VALUE_TYPE_FLOAT) {
151 double val = mStatement->AsDouble(c);
152 value->SetAsDouble(val);
154 else {
155 nsAutoString val;
156 nsresult rv = mStatement->GetString(c, val);
157 if (NS_FAILED(rv))
158 value->SetAsAString(EmptyString());
159 else
160 value->SetAsAString(val);
162 aArray.AppendObject(value);
168 //----------------------------------------------------------------------
170 // nsXULTemplateQueryProcessorStorage
173 NS_IMPL_ISUPPORTS1(nsXULTemplateQueryProcessorStorage,
174 nsIXULTemplateQueryProcessor)
177 nsXULTemplateQueryProcessorStorage::nsXULTemplateQueryProcessorStorage()
178 : mGenerationStarted(PR_FALSE)
182 NS_IMETHODIMP
183 nsXULTemplateQueryProcessorStorage::GetDatasource(nsIArray* aDataSources,
184 nsIDOMNode* aRootNode,
185 PRBool aIsTrusted,
186 nsIXULTemplateBuilder* aBuilder,
187 PRBool* aShouldDelayBuilding,
188 nsISupports** aReturn)
190 *aReturn = nsnull;
191 *aShouldDelayBuilding = PR_FALSE;
193 if (!aIsTrusted) {
194 return NS_OK;
197 PRUint32 length;
198 nsresult rv = aDataSources->GetLength(&length);
199 NS_ENSURE_SUCCESS(rv, rv);
201 if (length == 0) {
202 return NS_OK;
205 // We get only the first uri. This query processor supports
206 // only one database at a time.
207 nsCOMPtr<nsIURI> uri;
208 uri = do_QueryElementAt(aDataSources, 0);
210 if (!uri) {
211 // No uri in the list of datasources
212 return NS_OK;
215 nsCOMPtr<mozIStorageService> storage =
216 do_GetService("@mozilla.org/storage/service;1", &rv);
217 NS_ENSURE_SUCCESS(rv, rv);
219 nsCOMPtr<nsIFile> databaseFile;
220 nsCAutoString scheme;
221 rv = uri->GetScheme(scheme);
222 NS_ENSURE_SUCCESS(rv, rv);
224 if (scheme.EqualsLiteral("profile")) {
226 nsCAutoString path;
227 rv = uri->GetPath(path);
228 NS_ENSURE_SUCCESS(rv, rv);
230 if (path.IsEmpty()) {
231 return NS_ERROR_FAILURE;
234 rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
235 getter_AddRefs(databaseFile));
236 NS_ENSURE_SUCCESS(rv, rv);
238 rv = databaseFile->AppendNative(path);
239 NS_ENSURE_SUCCESS(rv, rv);
241 else {
242 nsCOMPtr<nsIChannel> channel;
243 nsCOMPtr<nsIIOService> ioservice =
244 do_GetService("@mozilla.org/network/io-service;1", &rv);
245 NS_ENSURE_SUCCESS(rv, rv);
247 rv = ioservice->NewChannelFromURI(uri, getter_AddRefs(channel));
248 NS_ENSURE_SUCCESS(rv, rv);
250 nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(channel, &rv);
251 NS_ENSURE_SUCCESS(rv, rv); // if it fails, not a file url
253 nsCOMPtr<nsIFile> file;
254 rv = fileChannel->GetFile(getter_AddRefs(databaseFile));
255 NS_ENSURE_SUCCESS(rv, rv);
258 // ok now we have an URI of a sqlite file
259 nsCOMPtr<mozIStorageConnection> connection;
260 rv = storage->OpenDatabase(databaseFile, getter_AddRefs(connection));
261 NS_ENSURE_SUCCESS(rv, rv);
263 NS_ADDREF(*aReturn = connection);
264 return NS_OK;
269 NS_IMETHODIMP
270 nsXULTemplateQueryProcessorStorage::InitializeForBuilding(nsISupports* aDatasource,
271 nsIXULTemplateBuilder* aBuilder,
272 nsIDOMNode* aRootNode)
274 NS_ENSURE_STATE(!mGenerationStarted);
276 mStorageConnection = do_QueryInterface(aDatasource);
277 if (!mStorageConnection)
278 return NS_ERROR_INVALID_ARG;
280 PRBool ready;
281 mStorageConnection->GetConnectionReady(&ready);
282 if (!ready)
283 return NS_ERROR_UNEXPECTED;
285 return NS_OK;
288 NS_IMETHODIMP
289 nsXULTemplateQueryProcessorStorage::Done()
291 mGenerationStarted = PR_FALSE;
292 return NS_OK;
295 NS_IMETHODIMP
296 nsXULTemplateQueryProcessorStorage::CompileQuery(nsIXULTemplateBuilder* aBuilder,
297 nsIDOMNode* aQueryNode,
298 nsIAtom* aRefVariable,
299 nsIAtom* aMemberVariable,
300 nsISupports** aReturn)
302 nsCOMPtr<nsIDOMNodeList> childNodes;
303 aQueryNode->GetChildNodes(getter_AddRefs(childNodes));
305 PRUint32 length;
306 childNodes->GetLength(&length);
308 nsCOMPtr<mozIStorageStatement> statement;
309 nsCOMPtr<nsIContent> queryContent = do_QueryInterface(aQueryNode);
310 nsAutoString sqlQuery;
312 // Let's get all text nodes (which should be the query)
313 nsContentUtils::GetNodeTextContent(queryContent, PR_FALSE, sqlQuery);
315 nsresult rv = mStorageConnection->CreateStatement(NS_ConvertUTF16toUTF8(sqlQuery),
316 getter_AddRefs(statement));
317 NS_ENSURE_SUCCESS(rv, rv);
319 PRUint32 parameterCount = 0;
320 PRUint32 count = queryContent->GetChildCount();
322 for (PRUint32 i = 0; i < count; ++i) {
323 nsIContent *child = queryContent->GetChildAt(i);
325 if (child->NodeInfo()->Equals(nsGkAtoms::param, kNameSpaceID_XUL)) {
326 nsAutoString value;
327 nsContentUtils::GetNodeTextContent(child, PR_FALSE, value);
329 PRUint32 index = parameterCount;
330 nsAutoString name, indexValue;
332 if (child->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name)) {
333 nsAutoString fullName;
334 fullName.AssignLiteral(":");
335 fullName.Append(name);
336 rv = statement->GetParameterIndex(NS_ConvertUTF16toUTF8(fullName) , &index);
337 NS_ENSURE_SUCCESS(rv, rv);
338 parameterCount++;
340 else if (child->GetAttr(kNameSpaceID_None, nsGkAtoms::index, indexValue)) {
341 PR_sscanf(NS_ConvertUTF16toUTF8(indexValue).get(),"%d",&index);
342 if (index > 0)
343 index--;
345 else {
346 parameterCount++;
349 static nsIContent::AttrValuesArray sTypeValues[] =
350 { &nsGkAtoms::int32, &nsGkAtoms::integer, &nsGkAtoms::int64,
351 &nsGkAtoms::null, &nsGkAtoms::double_, &nsGkAtoms::string, nsnull };
353 PRInt32 typeError, typeValue = child->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::type,
354 sTypeValues, eCaseMatters);
355 rv = NS_ERROR_ILLEGAL_VALUE;
356 PRInt32 valInt32 = 0;
357 PRInt64 valInt64 = 0;
358 PRFloat64 valFloat = 0;
360 switch (typeValue) {
361 case 0:
362 case 1:
363 typeError = PR_sscanf(NS_ConvertUTF16toUTF8(value).get(),"%d",&valInt32);
364 if (typeError > 0)
365 rv = statement->BindInt32Parameter(index, valInt32);
366 break;
367 case 2:
368 typeError = PR_sscanf(NS_ConvertUTF16toUTF8(value).get(),"%lld",&valInt64);
369 if (typeError > 0)
370 rv = statement->BindInt64Parameter(index, valInt64);
371 break;
372 case 3:
373 rv = statement->BindNullParameter(index);
374 break;
375 case 4:
376 typeError = PR_sscanf(NS_ConvertUTF16toUTF8(value).get(),"%lf",&valFloat);
377 if (typeError > 0)
378 rv = statement->BindDoubleParameter(index, valFloat);
379 break;
380 case 5:
381 case nsIContent::ATTR_MISSING:
382 rv = statement->BindStringParameter(index, value);
383 break;
385 NS_ENSURE_SUCCESS(rv, rv);
389 *aReturn = statement;
390 NS_IF_ADDREF(*aReturn);
392 return NS_OK;
395 NS_IMETHODIMP
396 nsXULTemplateQueryProcessorStorage::GenerateResults(nsISupports* aDatasource,
397 nsIXULTemplateResult* aRef,
398 nsISupports* aQuery,
399 nsISimpleEnumerator** aResults)
401 mGenerationStarted = PR_TRUE;
403 nsCOMPtr<mozIStorageStatement> statement = do_QueryInterface(aQuery);
404 if (!statement)
405 return NS_ERROR_FAILURE;
407 nsXULTemplateResultSetStorage* results =
408 new nsXULTemplateResultSetStorage(statement);
410 if (!results)
411 return NS_ERROR_OUT_OF_MEMORY;
413 *aResults = results;
414 NS_ADDREF(*aResults);
416 return NS_OK;
419 NS_IMETHODIMP
420 nsXULTemplateQueryProcessorStorage::AddBinding(nsIDOMNode* aRuleNode,
421 nsIAtom* aVar,
422 nsIAtom* aRef,
423 const nsAString& aExpr)
425 return NS_OK;
428 NS_IMETHODIMP
429 nsXULTemplateQueryProcessorStorage::TranslateRef(nsISupports* aDatasource,
430 const nsAString& aRefString,
431 nsIXULTemplateResult** aRef)
433 nsXULTemplateResultStorage* result =
434 new nsXULTemplateResultStorage(nsnull);
435 if (!result)
436 return NS_ERROR_OUT_OF_MEMORY;
438 *aRef = result;
439 NS_ADDREF(*aRef);
440 return NS_OK;
444 NS_IMETHODIMP
445 nsXULTemplateQueryProcessorStorage::CompareResults(nsIXULTemplateResult* aLeft,
446 nsIXULTemplateResult* aRight,
447 nsIAtom* aVar,
448 PRInt32* aResult)
450 *aResult = 0;
451 if (!aVar)
452 return NS_OK;
454 // We're going to see if values are integers or float, to perform
455 // a suitable comparison
456 nsCOMPtr<nsISupports> leftValue, rightValue;
457 if (aLeft)
458 aLeft->GetBindingObjectFor(aVar, getter_AddRefs(leftValue));
459 if (aRight)
460 aRight->GetBindingObjectFor(aVar, getter_AddRefs(rightValue));
462 if (leftValue && rightValue) {
463 nsCOMPtr<nsIVariant> vLeftValue = do_QueryInterface(leftValue);
464 nsCOMPtr<nsIVariant> vRightValue = do_QueryInterface(rightValue);
466 if (vLeftValue && vRightValue) {
467 nsresult rv1, rv2;
468 PRUint16 vtypeL, vtypeR;
469 vLeftValue->GetDataType(&vtypeL);
470 vRightValue->GetDataType(&vtypeR);
472 if (vtypeL == vtypeR) {
473 if (vtypeL == nsIDataType::VTYPE_INT32) {
474 PRInt32 leftValue, rightValue;
475 rv1 = vLeftValue->GetAsInt32(&leftValue);
476 rv2 = vRightValue->GetAsInt32(&rightValue);
477 if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) {
478 if (leftValue > rightValue)
479 *aResult = 1;
480 else if (leftValue < rightValue)
481 *aResult = -1;
482 return NS_OK;
485 else if (vtypeL == nsIDataType::VTYPE_DOUBLE) {
486 double leftValue, rightValue;
487 rv1 = vLeftValue->GetAsDouble(&leftValue);
488 rv2 = vRightValue->GetAsDouble(&rightValue);
489 if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) {
490 if (leftValue > rightValue)
491 *aResult = 1;
492 else if (leftValue < rightValue)
493 *aResult = -1;
494 return NS_OK;
501 // Values are not integers or floats, so we just compare them as simple strings
502 nsAutoString leftVal;
503 if (aLeft)
504 aLeft->GetBindingFor(aVar, leftVal);
506 nsAutoString rightVal;
507 if (aRight)
508 aRight->GetBindingFor(aVar, rightVal);
510 *aResult = Compare(nsDependentString(leftVal),
511 nsDependentString(rightVal),
512 nsCaseInsensitiveStringComparator());
513 return NS_OK;