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