|
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "prprf.h" |
|
7 |
|
8 #include "nsIDOMNodeList.h" |
|
9 #include "nsUnicharUtils.h" |
|
10 |
|
11 #include "nsArrayUtils.h" |
|
12 #include "nsIVariant.h" |
|
13 #include "nsAppDirectoryServiceDefs.h" |
|
14 |
|
15 #include "nsIURI.h" |
|
16 #include "nsIIOService.h" |
|
17 #include "nsIFileChannel.h" |
|
18 #include "nsIFile.h" |
|
19 #include "nsGkAtoms.h" |
|
20 #include "nsContentUtils.h" |
|
21 |
|
22 #include "nsXULTemplateBuilder.h" |
|
23 #include "nsXULTemplateResultStorage.h" |
|
24 #include "nsXULContentUtils.h" |
|
25 #include "nsXULSortService.h" |
|
26 |
|
27 #include "mozIStorageService.h" |
|
28 #include "nsIChannel.h" |
|
29 #include "nsIDocument.h" |
|
30 |
|
31 //---------------------------------------------------------------------- |
|
32 // |
|
33 // nsXULTemplateResultSetStorage |
|
34 // |
|
35 |
|
36 NS_IMPL_ISUPPORTS(nsXULTemplateResultSetStorage, nsISimpleEnumerator) |
|
37 |
|
38 |
|
39 nsXULTemplateResultSetStorage::nsXULTemplateResultSetStorage(mozIStorageStatement* aStatement) |
|
40 : mStatement(aStatement) |
|
41 { |
|
42 uint32_t count; |
|
43 nsresult rv = aStatement->GetColumnCount(&count); |
|
44 if (NS_FAILED(rv)) { |
|
45 mStatement = nullptr; |
|
46 return; |
|
47 } |
|
48 for (uint32_t c = 0; c < count; c++) { |
|
49 nsAutoCString name; |
|
50 rv = aStatement->GetColumnName(c, name); |
|
51 if (NS_SUCCEEDED(rv)) { |
|
52 nsCOMPtr<nsIAtom> columnName = do_GetAtom(NS_LITERAL_CSTRING("?") + name); |
|
53 mColumnNames.AppendObject(columnName); |
|
54 } |
|
55 } |
|
56 } |
|
57 |
|
58 NS_IMETHODIMP |
|
59 nsXULTemplateResultSetStorage::HasMoreElements(bool *aResult) |
|
60 { |
|
61 if (!mStatement) { |
|
62 *aResult = false; |
|
63 return NS_OK; |
|
64 } |
|
65 |
|
66 nsresult rv = mStatement->ExecuteStep(aResult); |
|
67 NS_ENSURE_SUCCESS(rv, rv); |
|
68 // Because the nsXULTemplateResultSetStorage is owned by many nsXULTemplateResultStorage objects, |
|
69 // it could live longer than it needed to get results. |
|
70 // So we destroy the statement to free resources when all results are fetched |
|
71 if (!*aResult) { |
|
72 mStatement = nullptr; |
|
73 } |
|
74 return NS_OK; |
|
75 } |
|
76 |
|
77 NS_IMETHODIMP |
|
78 nsXULTemplateResultSetStorage::GetNext(nsISupports **aResult) |
|
79 { |
|
80 nsXULTemplateResultStorage* result = |
|
81 new nsXULTemplateResultStorage(this); |
|
82 |
|
83 if (!result) |
|
84 return NS_ERROR_OUT_OF_MEMORY; |
|
85 |
|
86 *aResult = result; |
|
87 NS_ADDREF(result); |
|
88 return NS_OK; |
|
89 } |
|
90 |
|
91 |
|
92 int32_t |
|
93 nsXULTemplateResultSetStorage::GetColumnIndex(nsIAtom* aColumnName) |
|
94 { |
|
95 int32_t count = mColumnNames.Count(); |
|
96 for (int32_t c = 0; c < count; c++) { |
|
97 if (mColumnNames[c] == aColumnName) |
|
98 return c; |
|
99 } |
|
100 |
|
101 return -1; |
|
102 } |
|
103 |
|
104 void |
|
105 nsXULTemplateResultSetStorage::FillColumnValues(nsCOMArray<nsIVariant>& aArray) |
|
106 { |
|
107 if (!mStatement) |
|
108 return; |
|
109 |
|
110 int32_t count = mColumnNames.Count(); |
|
111 |
|
112 for (int32_t c = 0; c < count; c++) { |
|
113 nsCOMPtr<nsIWritableVariant> value = do_CreateInstance("@mozilla.org/variant;1"); |
|
114 |
|
115 int32_t type; |
|
116 mStatement->GetTypeOfIndex(c, &type); |
|
117 |
|
118 if (type == mStatement->VALUE_TYPE_INTEGER) { |
|
119 int64_t val = mStatement->AsInt64(c); |
|
120 value->SetAsInt64(val); |
|
121 } |
|
122 else if (type == mStatement->VALUE_TYPE_FLOAT) { |
|
123 double val = mStatement->AsDouble(c); |
|
124 value->SetAsDouble(val); |
|
125 } |
|
126 else { |
|
127 nsAutoString val; |
|
128 nsresult rv = mStatement->GetString(c, val); |
|
129 if (NS_FAILED(rv)) |
|
130 value->SetAsAString(EmptyString()); |
|
131 else |
|
132 value->SetAsAString(val); |
|
133 } |
|
134 aArray.AppendObject(value); |
|
135 } |
|
136 } |
|
137 |
|
138 |
|
139 |
|
140 //---------------------------------------------------------------------- |
|
141 // |
|
142 // nsXULTemplateQueryProcessorStorage |
|
143 // |
|
144 |
|
145 NS_IMPL_ISUPPORTS(nsXULTemplateQueryProcessorStorage, |
|
146 nsIXULTemplateQueryProcessor) |
|
147 |
|
148 |
|
149 nsXULTemplateQueryProcessorStorage::nsXULTemplateQueryProcessorStorage() |
|
150 : mGenerationStarted(false) |
|
151 { |
|
152 } |
|
153 |
|
154 NS_IMETHODIMP |
|
155 nsXULTemplateQueryProcessorStorage::GetDatasource(nsIArray* aDataSources, |
|
156 nsIDOMNode* aRootNode, |
|
157 bool aIsTrusted, |
|
158 nsIXULTemplateBuilder* aBuilder, |
|
159 bool* aShouldDelayBuilding, |
|
160 nsISupports** aReturn) |
|
161 { |
|
162 *aReturn = nullptr; |
|
163 *aShouldDelayBuilding = false; |
|
164 |
|
165 if (!aIsTrusted) { |
|
166 return NS_OK; |
|
167 } |
|
168 |
|
169 uint32_t length; |
|
170 nsresult rv = aDataSources->GetLength(&length); |
|
171 NS_ENSURE_SUCCESS(rv, rv); |
|
172 |
|
173 if (length == 0) { |
|
174 return NS_OK; |
|
175 } |
|
176 |
|
177 // We get only the first uri. This query processor supports |
|
178 // only one database at a time. |
|
179 nsCOMPtr<nsIURI> uri; |
|
180 uri = do_QueryElementAt(aDataSources, 0); |
|
181 |
|
182 if (!uri) { |
|
183 // No uri in the list of datasources |
|
184 return NS_OK; |
|
185 } |
|
186 |
|
187 nsCOMPtr<mozIStorageService> storage = |
|
188 do_GetService("@mozilla.org/storage/service;1", &rv); |
|
189 NS_ENSURE_SUCCESS(rv, rv); |
|
190 |
|
191 nsCOMPtr<nsIFile> databaseFile; |
|
192 nsAutoCString scheme; |
|
193 rv = uri->GetScheme(scheme); |
|
194 NS_ENSURE_SUCCESS(rv, rv); |
|
195 |
|
196 if (scheme.EqualsLiteral("profile")) { |
|
197 |
|
198 nsAutoCString path; |
|
199 rv = uri->GetPath(path); |
|
200 NS_ENSURE_SUCCESS(rv, rv); |
|
201 |
|
202 if (path.IsEmpty()) { |
|
203 return NS_ERROR_FAILURE; |
|
204 } |
|
205 |
|
206 rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, |
|
207 getter_AddRefs(databaseFile)); |
|
208 NS_ENSURE_SUCCESS(rv, rv); |
|
209 |
|
210 rv = databaseFile->AppendNative(path); |
|
211 NS_ENSURE_SUCCESS(rv, rv); |
|
212 } |
|
213 else { |
|
214 nsCOMPtr<nsIChannel> channel; |
|
215 nsCOMPtr<nsIIOService> ioservice = |
|
216 do_GetService("@mozilla.org/network/io-service;1", &rv); |
|
217 NS_ENSURE_SUCCESS(rv, rv); |
|
218 |
|
219 rv = ioservice->NewChannelFromURI(uri, getter_AddRefs(channel)); |
|
220 NS_ENSURE_SUCCESS(rv, rv); |
|
221 |
|
222 nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(channel, &rv); |
|
223 if (NS_FAILED(rv)) { // if it fails, not a file url |
|
224 nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_BAD_URI); |
|
225 return rv; |
|
226 } |
|
227 |
|
228 nsCOMPtr<nsIFile> file; |
|
229 rv = fileChannel->GetFile(getter_AddRefs(databaseFile)); |
|
230 NS_ENSURE_SUCCESS(rv, rv); |
|
231 } |
|
232 |
|
233 // ok now we have an URI of a sqlite file |
|
234 nsCOMPtr<mozIStorageConnection> connection; |
|
235 rv = storage->OpenDatabase(databaseFile, getter_AddRefs(connection)); |
|
236 if (NS_FAILED(rv)) { |
|
237 nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_CANNOT_OPEN_DATABASE); |
|
238 return rv; |
|
239 } |
|
240 |
|
241 NS_ADDREF(*aReturn = connection); |
|
242 return NS_OK; |
|
243 } |
|
244 |
|
245 |
|
246 |
|
247 NS_IMETHODIMP |
|
248 nsXULTemplateQueryProcessorStorage::InitializeForBuilding(nsISupports* aDatasource, |
|
249 nsIXULTemplateBuilder* aBuilder, |
|
250 nsIDOMNode* aRootNode) |
|
251 { |
|
252 NS_ENSURE_STATE(!mGenerationStarted); |
|
253 |
|
254 mStorageConnection = do_QueryInterface(aDatasource); |
|
255 if (!mStorageConnection) |
|
256 return NS_ERROR_INVALID_ARG; |
|
257 |
|
258 bool ready; |
|
259 mStorageConnection->GetConnectionReady(&ready); |
|
260 if (!ready) |
|
261 return NS_ERROR_UNEXPECTED; |
|
262 |
|
263 return NS_OK; |
|
264 } |
|
265 |
|
266 NS_IMETHODIMP |
|
267 nsXULTemplateQueryProcessorStorage::Done() |
|
268 { |
|
269 mGenerationStarted = false; |
|
270 return NS_OK; |
|
271 } |
|
272 |
|
273 NS_IMETHODIMP |
|
274 nsXULTemplateQueryProcessorStorage::CompileQuery(nsIXULTemplateBuilder* aBuilder, |
|
275 nsIDOMNode* aQueryNode, |
|
276 nsIAtom* aRefVariable, |
|
277 nsIAtom* aMemberVariable, |
|
278 nsISupports** aReturn) |
|
279 { |
|
280 nsCOMPtr<nsIDOMNodeList> childNodes; |
|
281 aQueryNode->GetChildNodes(getter_AddRefs(childNodes)); |
|
282 |
|
283 uint32_t length; |
|
284 childNodes->GetLength(&length); |
|
285 |
|
286 nsCOMPtr<mozIStorageStatement> statement; |
|
287 nsCOMPtr<nsIContent> queryContent = do_QueryInterface(aQueryNode); |
|
288 nsAutoString sqlQuery; |
|
289 |
|
290 // Let's get all text nodes (which should be the query) |
|
291 if (!nsContentUtils::GetNodeTextContent(queryContent, false, sqlQuery)) { |
|
292 return NS_ERROR_OUT_OF_MEMORY; |
|
293 } |
|
294 |
|
295 nsresult rv = mStorageConnection->CreateStatement(NS_ConvertUTF16toUTF8(sqlQuery), |
|
296 getter_AddRefs(statement)); |
|
297 if (NS_FAILED(rv)) { |
|
298 nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_BAD_QUERY); |
|
299 return rv; |
|
300 } |
|
301 |
|
302 uint32_t parameterCount = 0; |
|
303 for (nsIContent* child = queryContent->GetFirstChild(); |
|
304 child; |
|
305 child = child->GetNextSibling()) { |
|
306 |
|
307 if (child->NodeInfo()->Equals(nsGkAtoms::param, kNameSpaceID_XUL)) { |
|
308 nsAutoString value; |
|
309 if (!nsContentUtils::GetNodeTextContent(child, false, value)) { |
|
310 return NS_ERROR_OUT_OF_MEMORY; |
|
311 } |
|
312 |
|
313 uint32_t index = parameterCount; |
|
314 nsAutoString name, indexValue; |
|
315 |
|
316 if (child->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name)) { |
|
317 rv = statement->GetParameterIndex(NS_ConvertUTF16toUTF8(name), |
|
318 &index); |
|
319 if (NS_FAILED(rv)) { |
|
320 nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_UNKNOWN_QUERY_PARAMETER); |
|
321 return rv; |
|
322 } |
|
323 parameterCount++; |
|
324 } |
|
325 else if (child->GetAttr(kNameSpaceID_None, nsGkAtoms::index, indexValue)) { |
|
326 PR_sscanf(NS_ConvertUTF16toUTF8(indexValue).get(),"%d",&index); |
|
327 if (index > 0) |
|
328 index--; |
|
329 } |
|
330 else { |
|
331 parameterCount++; |
|
332 } |
|
333 |
|
334 static nsIContent::AttrValuesArray sTypeValues[] = |
|
335 { &nsGkAtoms::int32, &nsGkAtoms::integer, &nsGkAtoms::int64, |
|
336 &nsGkAtoms::null, &nsGkAtoms::double_, &nsGkAtoms::string, nullptr }; |
|
337 |
|
338 int32_t typeError = 1; |
|
339 int32_t typeValue = child->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::type, |
|
340 sTypeValues, eCaseMatters); |
|
341 rv = NS_ERROR_ILLEGAL_VALUE; |
|
342 int32_t valInt32 = 0; |
|
343 int64_t valInt64 = 0; |
|
344 double valFloat = 0; |
|
345 |
|
346 switch (typeValue) { |
|
347 case 0: |
|
348 case 1: |
|
349 typeError = PR_sscanf(NS_ConvertUTF16toUTF8(value).get(),"%d",&valInt32); |
|
350 if (typeError > 0) |
|
351 rv = statement->BindInt32ByIndex(index, valInt32); |
|
352 break; |
|
353 case 2: |
|
354 typeError = PR_sscanf(NS_ConvertUTF16toUTF8(value).get(),"%lld",&valInt64); |
|
355 if (typeError > 0) |
|
356 rv = statement->BindInt64ByIndex(index, valInt64); |
|
357 break; |
|
358 case 3: |
|
359 rv = statement->BindNullByIndex(index); |
|
360 break; |
|
361 case 4: |
|
362 typeError = PR_sscanf(NS_ConvertUTF16toUTF8(value).get(),"%lf",&valFloat); |
|
363 if (typeError > 0) |
|
364 rv = statement->BindDoubleByIndex(index, valFloat); |
|
365 break; |
|
366 case 5: |
|
367 case nsIContent::ATTR_MISSING: |
|
368 rv = statement->BindStringByIndex(index, value); |
|
369 break; |
|
370 default: |
|
371 typeError = 0; |
|
372 } |
|
373 |
|
374 if (typeError <= 0) { |
|
375 nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_WRONG_TYPE_QUERY_PARAMETER); |
|
376 return rv; |
|
377 } |
|
378 |
|
379 if (NS_FAILED(rv)) { |
|
380 nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_QUERY_PARAMETER_NOT_BOUND); |
|
381 return rv; |
|
382 } |
|
383 } |
|
384 } |
|
385 |
|
386 *aReturn = statement; |
|
387 NS_IF_ADDREF(*aReturn); |
|
388 |
|
389 return NS_OK; |
|
390 } |
|
391 |
|
392 NS_IMETHODIMP |
|
393 nsXULTemplateQueryProcessorStorage::GenerateResults(nsISupports* aDatasource, |
|
394 nsIXULTemplateResult* aRef, |
|
395 nsISupports* aQuery, |
|
396 nsISimpleEnumerator** aResults) |
|
397 { |
|
398 mGenerationStarted = true; |
|
399 |
|
400 nsCOMPtr<mozIStorageStatement> statement = do_QueryInterface(aQuery); |
|
401 if (!statement) |
|
402 return NS_ERROR_FAILURE; |
|
403 |
|
404 nsXULTemplateResultSetStorage* results = |
|
405 new nsXULTemplateResultSetStorage(statement); |
|
406 |
|
407 if (!results) |
|
408 return NS_ERROR_OUT_OF_MEMORY; |
|
409 |
|
410 *aResults = results; |
|
411 NS_ADDREF(*aResults); |
|
412 |
|
413 return NS_OK; |
|
414 } |
|
415 |
|
416 NS_IMETHODIMP |
|
417 nsXULTemplateQueryProcessorStorage::AddBinding(nsIDOMNode* aRuleNode, |
|
418 nsIAtom* aVar, |
|
419 nsIAtom* aRef, |
|
420 const nsAString& aExpr) |
|
421 { |
|
422 return NS_OK; |
|
423 } |
|
424 |
|
425 NS_IMETHODIMP |
|
426 nsXULTemplateQueryProcessorStorage::TranslateRef(nsISupports* aDatasource, |
|
427 const nsAString& aRefString, |
|
428 nsIXULTemplateResult** aRef) |
|
429 { |
|
430 nsXULTemplateResultStorage* result = |
|
431 new nsXULTemplateResultStorage(nullptr); |
|
432 if (!result) |
|
433 return NS_ERROR_OUT_OF_MEMORY; |
|
434 |
|
435 *aRef = result; |
|
436 NS_ADDREF(*aRef); |
|
437 return NS_OK; |
|
438 } |
|
439 |
|
440 |
|
441 NS_IMETHODIMP |
|
442 nsXULTemplateQueryProcessorStorage::CompareResults(nsIXULTemplateResult* aLeft, |
|
443 nsIXULTemplateResult* aRight, |
|
444 nsIAtom* aVar, |
|
445 uint32_t aSortHints, |
|
446 int32_t* aResult) |
|
447 { |
|
448 *aResult = 0; |
|
449 if (!aVar) |
|
450 return NS_OK; |
|
451 |
|
452 // We're going to see if values are integers or float, to perform |
|
453 // a suitable comparison |
|
454 nsCOMPtr<nsISupports> leftValue, rightValue; |
|
455 if (aLeft) |
|
456 aLeft->GetBindingObjectFor(aVar, getter_AddRefs(leftValue)); |
|
457 if (aRight) |
|
458 aRight->GetBindingObjectFor(aVar, getter_AddRefs(rightValue)); |
|
459 |
|
460 if (leftValue && rightValue) { |
|
461 nsCOMPtr<nsIVariant> vLeftValue = do_QueryInterface(leftValue); |
|
462 nsCOMPtr<nsIVariant> vRightValue = do_QueryInterface(rightValue); |
|
463 |
|
464 if (vLeftValue && vRightValue) { |
|
465 nsresult rv1, rv2; |
|
466 uint16_t vtypeL, vtypeR; |
|
467 vLeftValue->GetDataType(&vtypeL); |
|
468 vRightValue->GetDataType(&vtypeR); |
|
469 |
|
470 if (vtypeL == vtypeR) { |
|
471 if (vtypeL == nsIDataType::VTYPE_INT64) { |
|
472 int64_t leftValue, rightValue; |
|
473 rv1 = vLeftValue->GetAsInt64(&leftValue); |
|
474 rv2 = vRightValue->GetAsInt64(&rightValue); |
|
475 if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) { |
|
476 if (leftValue > rightValue) |
|
477 *aResult = 1; |
|
478 else if (leftValue < rightValue) |
|
479 *aResult = -1; |
|
480 return NS_OK; |
|
481 } |
|
482 } |
|
483 else if (vtypeL == nsIDataType::VTYPE_DOUBLE) { |
|
484 double leftValue, rightValue; |
|
485 rv1 = vLeftValue->GetAsDouble(&leftValue); |
|
486 rv2 = vRightValue->GetAsDouble(&rightValue); |
|
487 if (NS_SUCCEEDED(rv1) && NS_SUCCEEDED(rv2)) { |
|
488 if (leftValue > rightValue) |
|
489 *aResult = 1; |
|
490 else if (leftValue < rightValue) |
|
491 *aResult = -1; |
|
492 return NS_OK; |
|
493 } |
|
494 } |
|
495 } |
|
496 } |
|
497 } |
|
498 |
|
499 // Values are not integers or floats, so we just compare them as simple strings |
|
500 nsAutoString leftVal; |
|
501 if (aLeft) |
|
502 aLeft->GetBindingFor(aVar, leftVal); |
|
503 |
|
504 nsAutoString rightVal; |
|
505 if (aRight) |
|
506 aRight->GetBindingFor(aVar, rightVal); |
|
507 |
|
508 *aResult = XULSortServiceImpl::CompareValues(leftVal, rightVal, aSortHints); |
|
509 return NS_OK; |
|
510 } |