Source code

Revision control

Other Tools

1
/* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
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 "mozilla/storage.h"
7
#include "mozilla/dom/URLSearchParams.h"
8
#include "nsString.h"
9
#include "nsUnicharUtils.h"
10
#include "nsWhitespaceTokenizer.h"
11
#include "nsEscape.h"
12
#include "mozIPlacesAutoComplete.h"
13
#include "SQLFunctions.h"
14
#include "nsMathUtils.h"
15
#include "nsUnicodeProperties.h"
16
#include "nsUTF8Utils.h"
17
#include "nsINavHistoryService.h"
18
#include "nsPrintfCString.h"
19
#include "nsNavHistory.h"
20
#include "mozilla/Likely.h"
21
#include "mozilla/Utf8.h"
22
#include "nsVariant.h"
23
24
// Maximum number of chars to search through.
25
// MatchAutoCompleteFunction won't look for matches over this threshold.
26
#define MAX_CHARS_TO_SEARCH_THROUGH 255
27
28
using namespace mozilla::storage;
29
30
////////////////////////////////////////////////////////////////////////////////
31
//// Anonymous Helpers
32
33
namespace {
34
35
typedef nsACString::const_char_iterator const_char_iterator;
36
typedef nsACString::size_type size_type;
37
typedef nsACString::char_type char_type;
38
39
/**
40
* Scan forward through UTF-8 text until the next potential character that
41
* could match a given codepoint when lower-cased (false positives are okay).
42
* This avoids having to actually parse the UTF-8 text, which is slow.
43
*
44
* @param aStart
45
* An iterator pointing to the first character position considered.
46
* It will be updated by this function.
47
* @param aEnd
48
* An interator pointing to past-the-end of the string.
49
*/
50
static MOZ_ALWAYS_INLINE void goToNextSearchCandidate(
51
const_char_iterator& aStart, const const_char_iterator& aEnd,
52
uint32_t aSearchFor) {
53
// If the character we search for is ASCII, then we can scan until we find
54
// it or its ASCII uppercase character, modulo the special cases
55
// U+0130 LATIN CAPITAL LETTER I WITH DOT ABOVE and U+212A KELVIN SIGN
56
// (which are the only non-ASCII characters that lower-case to ASCII ones).
57
// Since false positives are okay, we approximate ASCII lower-casing by
58
// bit-ORing with 0x20, for increased performance.
59
//
60
// If the character we search for is *not* ASCII, we can ignore everything
61
// that is, since all ASCII characters lower-case to ASCII.
62
//
63
// Because of how UTF-8 uses high-order bits, this will never land us
64
// in the middle of a codepoint.
65
//
66
// The assumptions about Unicode made here are verified in the test_casing
67
// gtest.
68
if (aSearchFor < 128) {
69
// When searching for I or K, we pick out the first byte of the UTF-8
70
// encoding of the corresponding special case character, and look for it
71
// in the loop below. For other characters we fall back to 0xff, which
72
// is not a valid UTF-8 byte.
73
unsigned char target = (unsigned char)(aSearchFor | 0x20);
74
unsigned char special = 0xff;
75
if (target == 'i' || target == 'k') {
76
special = (target == 'i' ? 0xc4 : 0xe2);
77
}
78
79
while (aStart < aEnd && (unsigned char)(*aStart | 0x20) != target &&
80
(unsigned char)*aStart != special) {
81
aStart++;
82
}
83
} else {
84
while (aStart < aEnd && (unsigned char)(*aStart) < 128) {
85
aStart++;
86
}
87
}
88
}
89
90
/**
91
* Check whether a character position is on a word boundary of a UTF-8 string
92
* (rather than within a word). We define "within word" to be any position
93
* between [a-zA-Z] and [a-z] -- this lets us match CamelCase words.
94
* TODO: support non-latin alphabets.
95
*
96
* @param aPos
97
* An iterator pointing to the character position considered. It must
98
* *not* be the first byte of a string.
99
*
100
* @return true if boundary, false otherwise.
101
*/
102
static MOZ_ALWAYS_INLINE bool isOnBoundary(const_char_iterator aPos) {
103
if ('a' <= *aPos && *aPos <= 'z') {
104
char prev = *(aPos - 1) | 0x20;
105
return !('a' <= prev && prev <= 'z');
106
}
107
return true;
108
}
109
110
/**
111
* Check whether a token string matches a particular position of a source
112
* string, case insensitively.
113
*
114
* @param aTokenStart
115
* An iterator pointing to the start of the token string.
116
* @param aTokenEnd
117
* An iterator pointing past-the-end of the token string.
118
* @param aSourceStart
119
* An iterator pointing to the position of source string to start
120
* matching at.
121
* @param aSourceEnd
122
* An iterator pointing past-the-end of the source string.
123
*
124
* @return true if the string [aTokenStart, aTokenEnd) matches the start of
125
* the string [aSourceStart, aSourceEnd, false otherwise.
126
*/
127
static MOZ_ALWAYS_INLINE bool stringMatch(const_char_iterator aTokenStart,
128
const_char_iterator aTokenEnd,
129
const_char_iterator aSourceStart,
130
const_char_iterator aSourceEnd) {
131
const_char_iterator tokenCur = aTokenStart, sourceCur = aSourceStart;
132
133
while (tokenCur < aTokenEnd) {
134
if (sourceCur >= aSourceEnd) {
135
return false;
136
}
137
138
bool error;
139
if (!CaseInsensitiveUTF8CharsEqual(sourceCur, tokenCur, aSourceEnd,
140
aTokenEnd, &sourceCur, &tokenCur,
141
&error)) {
142
return false;
143
}
144
}
145
146
return true;
147
}
148
149
enum FindInStringBehavior { eFindOnBoundary, eFindAnywhere };
150
151
/**
152
* Common implementation for findAnywhere and findOnBoundary.
153
*
154
* @param aToken
155
* The token we're searching for
156
* @param aSourceString
157
* The string in which we're searching
158
* @param aBehavior
159
* eFindOnBoundary if we should only consider matchines which occur on
160
* word boundaries, or eFindAnywhere if we should consider matches
161
* which appear anywhere.
162
*
163
* @return true if aToken was found in aSourceString, false otherwise.
164
*/
165
static bool findInString(const nsDependentCSubstring& aToken,
166
const nsACString& aSourceString,
167
FindInStringBehavior aBehavior) {
168
// GetLowerUTF8Codepoint assumes that there's at least one byte in
169
// the string, so don't pass an empty token here.
170
MOZ_ASSERT(!aToken.IsEmpty(), "Don't search for an empty token!");
171
172
// We cannot match anything if there is nothing to search.
173
if (aSourceString.IsEmpty()) {
174
return false;
175
}
176
177
const_char_iterator tokenStart(aToken.BeginReading()),
178
tokenEnd(aToken.EndReading()), tokenNext,
179
sourceStart(aSourceString.BeginReading()),
180
sourceEnd(aSourceString.EndReading()), sourceCur(sourceStart), sourceNext;
181
182
uint32_t tokenFirstChar =
183
GetLowerUTF8Codepoint(tokenStart, tokenEnd, &tokenNext);
184
if (tokenFirstChar == uint32_t(-1)) {
185
return false;
186
}
187
188
for (;;) {
189
// Scan forward to the next viable candidate (if any).
190
goToNextSearchCandidate(sourceCur, sourceEnd, tokenFirstChar);
191
if (sourceCur == sourceEnd) {
192
break;
193
}
194
195
// Check whether the first character in the token matches the character
196
// at sourceCur. At the same time, get a pointer to the next character
197
// in the source.
198
uint32_t sourceFirstChar =
199
GetLowerUTF8Codepoint(sourceCur, sourceEnd, &sourceNext);
200
if (sourceFirstChar == uint32_t(-1)) {
201
return false;
202
}
203
204
if (sourceFirstChar == tokenFirstChar &&
205
(aBehavior != eFindOnBoundary || sourceCur == sourceStart ||
206
isOnBoundary(sourceCur)) &&
207
stringMatch(tokenNext, tokenEnd, sourceNext, sourceEnd)) {
208
return true;
209
}
210
211
sourceCur = sourceNext;
212
}
213
214
return false;
215
}
216
217
static MOZ_ALWAYS_INLINE nsDependentCString
218
getSharedUTF8String(mozIStorageValueArray* aValues, uint32_t aIndex) {
219
uint32_t len;
220
const char* str = aValues->AsSharedUTF8String(aIndex, &len);
221
if (!str) {
222
return nsDependentCString("", (uint32_t)0);
223
}
224
return nsDependentCString(str, len);
225
}
226
227
class MOZ_STACK_CLASS GetQueryParamIterator final
228
: public URLParams::ForEachIterator {
229
public:
230
explicit GetQueryParamIterator(const nsCString& aParamName,
231
nsVariant* aResult)
232
: mParamName(aParamName), mResult(aResult) {}
233
234
bool URLParamsIterator(const nsAString& aName,
235
const nsAString& aValue) override {
236
NS_ConvertUTF16toUTF8 name(aName);
237
if (!mParamName.Equals(name)) {
238
return true;
239
}
240
mResult->SetAsAString(aValue);
241
return false;
242
}
243
244
private:
245
const nsCString& mParamName;
246
nsVariant* mResult;
247
};
248
249
/**
250
* Gets the length of the prefix in a URI spec. "Prefix" is defined to be the
251
* scheme, colon, and, if present, two slashes.
252
*
253
* Examples:
254
*
256
* ~~~~~~~
257
* => length == 7
258
*
259
* foo:example
260
* ~~~~
261
* => length == 4
262
*
263
* not a spec
264
* => length == 0
265
*
266
* @param aSpec
267
* A URI spec, or a string that may be a URI spec.
268
* @return The length of the prefix in the spec. If there isn't a prefix,
269
* returns 0.
270
*/
271
static MOZ_ALWAYS_INLINE size_type getPrefixLength(const nsACString& aSpec) {
272
// To keep the search bounded, look at 64 characters at most. The longest
273
// IANA schemes are ~30, so double that and round up to a nice number.
274
size_type length = std::min(static_cast<size_type>(64), aSpec.Length());
275
for (size_type i = 0; i < length; ++i) {
276
if (aSpec[i] == static_cast<char_type>(':')) {
277
// Found the ':'. Now skip past "//", if present.
278
if (i + 2 < aSpec.Length() &&
279
aSpec[i + 1] == static_cast<char_type>('/') &&
280
aSpec[i + 2] == static_cast<char_type>('/')) {
281
i += 2;
282
}
283
return i + 1;
284
}
285
}
286
return 0;
287
}
288
289
/**
290
* Gets the index in a URI spec of the host and port substring and optionally
291
* its length.
292
*
293
* Examples:
294
*
296
* ~~~~~~~~~~~
297
* => index == 7, length == 11
298
*
300
* ~~~~~~~~~~~~~~~~
301
* => index == 7, length == 16
302
*
304
* ~~~~~~~~~~~
305
* => index == 17, length == 11
306
*
307
* foo:example
308
* ~~~~~~~
309
* => index == 4, length == 7
310
*
311
* not a spec
312
* ~~~~~~~~~~
313
* => index == 0, length == 10
314
*
315
* @param aSpec
316
* A URI spec, or a string that may be a URI spec.
317
* @param _hostAndPortLength
318
* The length of the host and port substring is returned through this
319
* param. Pass null if you don't care.
320
* @return The length of the host and port substring in the spec. If aSpec
321
* doesn't look like a URI, then the entire aSpec is assumed to be a
322
* "host and port", and this returns 0, and _hostAndPortLength will be
323
* the length of aSpec.
324
*/
325
static MOZ_ALWAYS_INLINE size_type
326
indexOfHostAndPort(const nsACString& aSpec, size_type* _hostAndPortLength) {
327
size_type index = getPrefixLength(aSpec);
328
size_type i = index;
329
for (; i < aSpec.Length(); ++i) {
330
// RFC 3986 (URIs): The origin ("authority") is terminated by '/', '?', or
331
// '#' (or the end of the URI).
332
if (aSpec[i] == static_cast<char_type>('/') ||
333
aSpec[i] == static_cast<char_type>('?') ||
334
aSpec[i] == static_cast<char_type>('#')) {
335
break;
336
}
337
// RFC 3986: '@' marks the end of the userinfo component.
338
if (aSpec[i] == static_cast<char_type>('@')) {
339
index = i + 1;
340
}
341
}
342
if (_hostAndPortLength) {
343
*_hostAndPortLength = i - index;
344
}
345
return index;
346
}
347
348
} // End anonymous namespace
349
350
namespace mozilla {
351
namespace places {
352
353
////////////////////////////////////////////////////////////////////////////////
354
//// AutoComplete Matching Function
355
356
/* static */
357
nsresult MatchAutoCompleteFunction::create(mozIStorageConnection* aDBConn) {
358
RefPtr<MatchAutoCompleteFunction> function = new MatchAutoCompleteFunction();
359
360
nsresult rv = aDBConn->CreateFunction(
361
NS_LITERAL_CSTRING("autocomplete_match"), kArgIndexLength, function);
362
NS_ENSURE_SUCCESS(rv, rv);
363
364
return NS_OK;
365
}
366
367
/* static */
368
nsDependentCSubstring MatchAutoCompleteFunction::fixupURISpec(
369
const nsACString& aURISpec, int32_t aMatchBehavior, nsACString& aSpecBuf) {
370
nsDependentCSubstring fixedSpec;
371
372
// Try to unescape the string. If that succeeds and yields a different
373
// string which is also valid UTF-8, we'll use it.
374
// Otherwise, we will simply use our original string.
375
bool unescaped = NS_UnescapeURL(aURISpec.BeginReading(), aURISpec.Length(),
376
esc_SkipControl, aSpecBuf);
377
if (unescaped && IsUtf8(aSpecBuf)) {
378
fixedSpec.Rebind(aSpecBuf, 0);
379
} else {
380
fixedSpec.Rebind(aURISpec, 0);
381
}
382
383
if (aMatchBehavior == mozIPlacesAutoComplete::MATCH_ANYWHERE_UNMODIFIED)
384
return fixedSpec;
385
386
if (StringBeginsWith(fixedSpec, NS_LITERAL_CSTRING("http://"))) {
387
fixedSpec.Rebind(fixedSpec, 7);
388
} else if (StringBeginsWith(fixedSpec, NS_LITERAL_CSTRING("https://"))) {
389
fixedSpec.Rebind(fixedSpec, 8);
390
} else if (StringBeginsWith(fixedSpec, NS_LITERAL_CSTRING("ftp://"))) {
391
fixedSpec.Rebind(fixedSpec, 6);
392
}
393
394
return fixedSpec;
395
}
396
397
/* static */
398
bool MatchAutoCompleteFunction::findAnywhere(
399
const nsDependentCSubstring& aToken, const nsACString& aSourceString) {
400
// We can't use FindInReadable here; it works only for ASCII.
401
402
return findInString(aToken, aSourceString, eFindAnywhere);
403
}
404
405
/* static */
406
bool MatchAutoCompleteFunction::findOnBoundary(
407
const nsDependentCSubstring& aToken, const nsACString& aSourceString) {
408
return findInString(aToken, aSourceString, eFindOnBoundary);
409
}
410
411
/* static */
412
MatchAutoCompleteFunction::searchFunctionPtr
413
MatchAutoCompleteFunction::getSearchFunction(int32_t aBehavior) {
414
switch (aBehavior) {
415
case mozIPlacesAutoComplete::MATCH_ANYWHERE:
416
case mozIPlacesAutoComplete::MATCH_ANYWHERE_UNMODIFIED:
417
return findAnywhere;
418
case mozIPlacesAutoComplete::MATCH_BOUNDARY:
419
default:
420
return findOnBoundary;
421
};
422
}
423
424
NS_IMPL_ISUPPORTS(MatchAutoCompleteFunction, mozIStorageFunction)
425
426
MatchAutoCompleteFunction::MatchAutoCompleteFunction()
427
: mCachedZero(new IntegerVariant(0)), mCachedOne(new IntegerVariant(1)) {
428
static_assert(IntegerVariant::HasThreadSafeRefCnt::value,
429
"Caching assumes that variants have thread-safe refcounting");
430
}
431
432
NS_IMETHODIMP
433
MatchAutoCompleteFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
434
nsIVariant** _result) {
435
// Macro to make the code a bit cleaner and easier to read. Operates on
436
// searchBehavior.
437
int32_t searchBehavior = aArguments->AsInt32(kArgIndexSearchBehavior);
438
#define HAS_BEHAVIOR(aBitName) \
439
(searchBehavior & mozIPlacesAutoComplete::BEHAVIOR_##aBitName)
440
441
nsDependentCString searchString =
442
getSharedUTF8String(aArguments, kArgSearchString);
443
nsDependentCString url = getSharedUTF8String(aArguments, kArgIndexURL);
444
445
int32_t matchBehavior = aArguments->AsInt32(kArgIndexMatchBehavior);
446
447
// We only want to filter javascript: URLs if we are not supposed to search
448
// for them, and the search does not start with "javascript:".
449
if (matchBehavior != mozIPlacesAutoComplete::MATCH_ANYWHERE_UNMODIFIED &&
450
StringBeginsWith(url, NS_LITERAL_CSTRING("javascript:")) &&
451
!HAS_BEHAVIOR(JAVASCRIPT) &&
452
!StringBeginsWith(searchString, NS_LITERAL_CSTRING("javascript:"))) {
453
NS_ADDREF(*_result = mCachedZero);
454
return NS_OK;
455
}
456
457
int32_t visitCount = aArguments->AsInt32(kArgIndexVisitCount);
458
// Filtering on typed is no more used by Firefox, it is still being used by
459
// comm-central clients.
460
bool typed = aArguments->AsInt32(kArgIndexTyped) ? true : false;
461
bool bookmark = aArguments->AsInt32(kArgIndexBookmark) ? true : false;
462
nsDependentCString tags = getSharedUTF8String(aArguments, kArgIndexTags);
463
int32_t openPageCount = aArguments->AsInt32(kArgIndexOpenPageCount);
464
bool matches = false;
465
if (HAS_BEHAVIOR(RESTRICT)) {
466
// Make sure we match all the filter requirements. If a given restriction
467
// is active, make sure the corresponding condition is not true.
468
matches = (!HAS_BEHAVIOR(HISTORY) || visitCount > 0) &&
469
(!HAS_BEHAVIOR(TYPED) || typed) &&
470
(!HAS_BEHAVIOR(BOOKMARK) || bookmark) &&
471
(!HAS_BEHAVIOR(TAG) || !tags.IsVoid()) &&
472
(!HAS_BEHAVIOR(OPENPAGE) || openPageCount > 0);
473
} else {
474
// Make sure that we match all the filter requirements and that the
475
// corresponding condition is true if at least a given restriction is
476
// active.
477
matches = (HAS_BEHAVIOR(HISTORY) && visitCount > 0) ||
478
(HAS_BEHAVIOR(TYPED) && typed) ||
479
(HAS_BEHAVIOR(BOOKMARK) && bookmark) ||
480
(HAS_BEHAVIOR(TAG) && !tags.IsVoid()) ||
481
(HAS_BEHAVIOR(OPENPAGE) && openPageCount > 0);
482
}
483
484
if (!matches) {
485
NS_ADDREF(*_result = mCachedZero);
486
return NS_OK;
487
}
488
489
// Obtain our search function.
490
searchFunctionPtr searchFunction = getSearchFunction(matchBehavior);
491
492
// Clean up our URI spec and prepare it for searching.
493
nsCString fixedUrlBuf;
494
nsDependentCSubstring fixedUrl =
495
fixupURISpec(url, matchBehavior, fixedUrlBuf);
496
// Limit the number of chars we search through.
497
const nsDependentCSubstring& trimmedUrl =
498
Substring(fixedUrl, 0, MAX_CHARS_TO_SEARCH_THROUGH);
499
500
nsDependentCString title = getSharedUTF8String(aArguments, kArgIndexTitle);
501
// Limit the number of chars we search through.
502
const nsDependentCSubstring& trimmedTitle =
503
Substring(title, 0, MAX_CHARS_TO_SEARCH_THROUGH);
504
505
// Determine if every token matches either the bookmark title, tags, page
506
// title, or page URL.
507
nsCWhitespaceTokenizer tokenizer(searchString);
508
while (matches && tokenizer.hasMoreTokens()) {
509
const nsDependentCSubstring& token = tokenizer.nextToken();
510
511
if (HAS_BEHAVIOR(TITLE) && HAS_BEHAVIOR(URL)) {
512
matches = (searchFunction(token, trimmedTitle) ||
513
searchFunction(token, tags)) &&
514
searchFunction(token, trimmedUrl);
515
} else if (HAS_BEHAVIOR(TITLE)) {
516
matches =
517
searchFunction(token, trimmedTitle) || searchFunction(token, tags);
518
} else if (HAS_BEHAVIOR(URL)) {
519
matches = searchFunction(token, trimmedUrl);
520
} else {
521
matches = searchFunction(token, trimmedTitle) ||
522
searchFunction(token, tags) ||
523
searchFunction(token, trimmedUrl);
524
}
525
}
526
527
NS_ADDREF(*_result = (matches ? mCachedOne : mCachedZero));
528
return NS_OK;
529
#undef HAS_BEHAVIOR
530
}
531
532
////////////////////////////////////////////////////////////////////////////////
533
//// Frecency Calculation Function
534
535
/* static */
536
nsresult CalculateFrecencyFunction::create(mozIStorageConnection* aDBConn) {
537
RefPtr<CalculateFrecencyFunction> function = new CalculateFrecencyFunction();
538
539
nsresult rv = aDBConn->CreateFunction(
540
NS_LITERAL_CSTRING("calculate_frecency"), -1, function);
541
NS_ENSURE_SUCCESS(rv, rv);
542
543
return NS_OK;
544
}
545
546
NS_IMPL_ISUPPORTS(CalculateFrecencyFunction, mozIStorageFunction)
547
548
NS_IMETHODIMP
549
CalculateFrecencyFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
550
nsIVariant** _result) {
551
// Fetch arguments. Use default values if they were omitted.
552
uint32_t numEntries;
553
nsresult rv = aArguments->GetNumEntries(&numEntries);
554
NS_ENSURE_SUCCESS(rv, rv);
555
MOZ_ASSERT(numEntries <= 2, "unexpected number of arguments");
556
557
int64_t pageId = aArguments->AsInt64(0);
558
MOZ_ASSERT(pageId > 0, "Should always pass a valid page id");
559
if (pageId <= 0) {
560
NS_ADDREF(*_result = new IntegerVariant(0));
561
return NS_OK;
562
}
563
564
enum RedirectBonus { eUnknown, eRedirect, eNormal };
565
566
RedirectBonus mostRecentVisitBonus = eUnknown;
567
568
if (numEntries > 1) {
569
mostRecentVisitBonus = aArguments->AsInt32(1) ? eRedirect : eNormal;
570
}
571
572
int32_t typed = 0;
573
int32_t visitCount = 0;
574
bool hasBookmark = false;
575
int32_t isQuery = 0;
576
float pointsForSampledVisits = 0.0;
577
int32_t numSampledVisits = 0;
578
int32_t bonus = 0;
579
580
// This is a const version of the history object for thread-safety.
581
const nsNavHistory* history = nsNavHistory::GetConstHistoryService();
582
NS_ENSURE_STATE(history);
583
RefPtr<Database> DB = Database::GetDatabase();
584
NS_ENSURE_STATE(DB);
585
586
// Fetch the page stats from the database.
587
{
588
nsCOMPtr<mozIStorageStatement> getPageInfo = DB->GetStatement(
589
"SELECT typed, visit_count, foreign_count, "
590
"(substr(url, 0, 7) = 'place:') "
591
"FROM moz_places "
592
"WHERE id = :page_id ");
593
NS_ENSURE_STATE(getPageInfo);
594
mozStorageStatementScoper infoScoper(getPageInfo);
595
596
rv = getPageInfo->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), pageId);
597
NS_ENSURE_SUCCESS(rv, rv);
598
599
bool hasResult = false;
600
rv = getPageInfo->ExecuteStep(&hasResult);
601
NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && hasResult, NS_ERROR_UNEXPECTED);
602
603
rv = getPageInfo->GetInt32(0, &typed);
604
NS_ENSURE_SUCCESS(rv, rv);
605
rv = getPageInfo->GetInt32(1, &visitCount);
606
NS_ENSURE_SUCCESS(rv, rv);
607
int32_t foreignCount = 0;
608
rv = getPageInfo->GetInt32(2, &foreignCount);
609
NS_ENSURE_SUCCESS(rv, rv);
610
hasBookmark = foreignCount > 0;
611
rv = getPageInfo->GetInt32(3, &isQuery);
612
NS_ENSURE_SUCCESS(rv, rv);
613
}
614
615
if (visitCount > 0) {
616
// Get a sample of the last visits to the page, to calculate its weight.
617
// In case the visit is a redirect target, calculate the frecency
618
// as if the original page was visited.
619
// If it's a redirect source, we may want to use a lower bonus.
620
nsCString redirectsTransitionFragment = nsPrintfCString(
621
"%d AND %d ", nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
622
nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY);
623
nsCOMPtr<mozIStorageStatement> getVisits = DB->GetStatement(
624
NS_LITERAL_CSTRING(
625
"/* do not warn (bug 659740 - SQLite may ignore index if few "
626
"visits exist) */"
627
"SELECT "
628
"IFNULL(origin.visit_type, v.visit_type) AS visit_type, "
629
"target.visit_type AS target_visit_type, "
630
"ROUND((strftime('%s','now','localtime','utc') - "
631
"v.visit_date/1000000)/86400) AS age_in_days "
632
"FROM moz_historyvisits v "
633
"LEFT JOIN moz_historyvisits origin ON origin.id = v.from_visit "
634
"AND v.visit_type BETWEEN ") +
635
redirectsTransitionFragment +
636
NS_LITERAL_CSTRING(
637
"LEFT JOIN moz_historyvisits target ON v.id = target.from_visit "
638
"AND target.visit_type BETWEEN ") +
639
redirectsTransitionFragment +
640
NS_LITERAL_CSTRING("WHERE v.place_id = :page_id "
641
"ORDER BY v.visit_date DESC "
642
"LIMIT :max_visits "));
643
NS_ENSURE_STATE(getVisits);
644
mozStorageStatementScoper visitsScoper(getVisits);
645
rv = getVisits->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), pageId);
646
NS_ENSURE_SUCCESS(rv, rv);
647
rv = getVisits->BindInt32ByName(NS_LITERAL_CSTRING("max_visits"),
648
history->GetNumVisitsForFrecency());
649
NS_ENSURE_SUCCESS(rv, rv);
650
651
// Fetch only a limited number of recent visits.
652
bool hasResult = false;
653
while (NS_SUCCEEDED(getVisits->ExecuteStep(&hasResult)) && hasResult) {
654
// If this is a redirect target, we'll use the visitType of the source,
655
// otherwise the actual visitType.
656
int32_t visitType = getVisits->AsInt32(0);
657
658
// When adding a new visit, we should haved passed-in whether we should
659
// use the redirect bonus. We can't fetch this information from the
660
// database, because we only store redirect targets.
661
// For older visits we extract the value from the database.
662
bool useRedirectBonus = mostRecentVisitBonus == eRedirect;
663
if (mostRecentVisitBonus == eUnknown || numSampledVisits > 0) {
664
int32_t targetVisitType = getVisits->AsInt32(1);
665
useRedirectBonus =
666
targetVisitType ==
667
nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT ||
668
(targetVisitType ==
669
nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY &&
670
visitType != nsINavHistoryService::TRANSITION_TYPED);
671
}
672
673
bonus = history->GetFrecencyTransitionBonus(visitType, true,
674
useRedirectBonus);
675
676
// Add the bookmark visit bonus.
677
if (hasBookmark) {
678
bonus += history->GetFrecencyTransitionBonus(
679
nsINavHistoryService::TRANSITION_BOOKMARK, true);
680
}
681
682
// If bonus was zero, we can skip the work to determine the weight.
683
if (bonus) {
684
int32_t ageInDays = getVisits->AsInt32(2);
685
int32_t weight = history->GetFrecencyAgedWeight(ageInDays);
686
pointsForSampledVisits += (float)(weight * (bonus / 100.0));
687
}
688
689
numSampledVisits++;
690
}
691
}
692
693
// If we sampled some visits for this page, use the calculated weight.
694
if (numSampledVisits) {
695
// We were unable to calculate points, maybe cause all the visits in the
696
// sample had a zero bonus. Though, we know the page has some past valid
697
// visit, or visit_count would be zero. Thus we set the frecency to
698
// -1, so they are still shown in autocomplete.
699
if (!pointsForSampledVisits) {
700
NS_ADDREF(*_result = new IntegerVariant(-1));
701
} else {
702
// Estimate frecency using the sampled visits.
703
// Use ceilf() so that we don't round down to 0, which
704
// would cause us to completely ignore the place during autocomplete.
705
NS_ADDREF(*_result = new IntegerVariant(
706
(int32_t)ceilf(visitCount * ceilf(pointsForSampledVisits) /
707
numSampledVisits)));
708
}
709
return NS_OK;
710
}
711
712
// Otherwise this page has no visits, it may be bookmarked.
713
if (!hasBookmark || isQuery) {
714
NS_ADDREF(*_result = new IntegerVariant(0));
715
return NS_OK;
716
}
717
718
// For unvisited bookmarks, produce a non-zero frecency, so that they show
719
// up in URL bar autocomplete.
720
visitCount = 1;
721
722
// Make it so something bookmarked and typed will have a higher frecency
723
// than something just typed or just bookmarked.
724
bonus += history->GetFrecencyTransitionBonus(
725
nsINavHistoryService::TRANSITION_BOOKMARK, false);
726
if (typed) {
727
bonus += history->GetFrecencyTransitionBonus(
728
nsINavHistoryService::TRANSITION_TYPED, false);
729
}
730
731
// Assume "now" as our ageInDays, so use the first bucket.
732
pointsForSampledVisits =
733
history->GetFrecencyBucketWeight(1) * (bonus / (float)100.0);
734
735
// use ceilf() so that we don't round down to 0, which
736
// would cause us to completely ignore the place during autocomplete
737
NS_ADDREF(*_result = new IntegerVariant(
738
(int32_t)ceilf(visitCount * ceilf(pointsForSampledVisits))));
739
740
return NS_OK;
741
}
742
743
////////////////////////////////////////////////////////////////////////////////
744
//// GUID Creation Function
745
746
/* static */
747
nsresult GenerateGUIDFunction::create(mozIStorageConnection* aDBConn) {
748
RefPtr<GenerateGUIDFunction> function = new GenerateGUIDFunction();
749
nsresult rv =
750
aDBConn->CreateFunction(NS_LITERAL_CSTRING("generate_guid"), 0, function);
751
NS_ENSURE_SUCCESS(rv, rv);
752
753
return NS_OK;
754
}
755
756
NS_IMPL_ISUPPORTS(GenerateGUIDFunction, mozIStorageFunction)
757
758
NS_IMETHODIMP
759
GenerateGUIDFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
760
nsIVariant** _result) {
761
nsAutoCString guid;
762
nsresult rv = GenerateGUID(guid);
763
NS_ENSURE_SUCCESS(rv, rv);
764
765
NS_ADDREF(*_result = new UTF8TextVariant(guid));
766
return NS_OK;
767
}
768
769
////////////////////////////////////////////////////////////////////////////////
770
//// GUID Validation Function
771
772
/* static */
773
nsresult IsValidGUIDFunction::create(mozIStorageConnection* aDBConn) {
774
RefPtr<IsValidGUIDFunction> function = new IsValidGUIDFunction();
775
return aDBConn->CreateFunction(NS_LITERAL_CSTRING("is_valid_guid"), 1,
776
function);
777
}
778
779
NS_IMPL_ISUPPORTS(IsValidGUIDFunction, mozIStorageFunction)
780
781
NS_IMETHODIMP
782
IsValidGUIDFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
783
nsIVariant** _result) {
784
// Must have non-null function arguments.
785
MOZ_ASSERT(aArguments);
786
787
nsAutoCString guid;
788
aArguments->GetUTF8String(0, guid);
789
790
RefPtr<nsVariant> result = new nsVariant();
791
result->SetAsBool(IsValidGUID(guid));
792
result.forget(_result);
793
return NS_OK;
794
}
795
796
////////////////////////////////////////////////////////////////////////////////
797
//// Get Unreversed Host Function
798
799
/* static */
800
nsresult GetUnreversedHostFunction::create(mozIStorageConnection* aDBConn) {
801
RefPtr<GetUnreversedHostFunction> function = new GetUnreversedHostFunction();
802
nsresult rv = aDBConn->CreateFunction(
803
NS_LITERAL_CSTRING("get_unreversed_host"), 1, function);
804
NS_ENSURE_SUCCESS(rv, rv);
805
806
return NS_OK;
807
}
808
809
NS_IMPL_ISUPPORTS(GetUnreversedHostFunction, mozIStorageFunction)
810
811
NS_IMETHODIMP
812
GetUnreversedHostFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
813
nsIVariant** _result) {
814
// Must have non-null function arguments.
815
MOZ_ASSERT(aArguments);
816
817
nsAutoString src;
818
aArguments->GetString(0, src);
819
820
RefPtr<nsVariant> result = new nsVariant();
821
822
if (src.Length() > 1) {
823
src.Truncate(src.Length() - 1);
824
nsAutoString dest;
825
ReverseString(src, dest);
826
result->SetAsAString(dest);
827
} else {
828
result->SetAsAString(EmptyString());
829
}
830
result.forget(_result);
831
return NS_OK;
832
}
833
834
////////////////////////////////////////////////////////////////////////////////
835
//// Fixup URL Function
836
837
/* static */
838
nsresult FixupURLFunction::create(mozIStorageConnection* aDBConn) {
839
RefPtr<FixupURLFunction> function = new FixupURLFunction();
840
nsresult rv =
841
aDBConn->CreateFunction(NS_LITERAL_CSTRING("fixup_url"), 1, function);
842
NS_ENSURE_SUCCESS(rv, rv);
843
844
return NS_OK;
845
}
846
847
NS_IMPL_ISUPPORTS(FixupURLFunction, mozIStorageFunction)
848
849
NS_IMETHODIMP
850
FixupURLFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
851
nsIVariant** _result) {
852
// Must have non-null function arguments.
853
MOZ_ASSERT(aArguments);
854
855
nsAutoString src;
856
aArguments->GetString(0, src);
857
858
RefPtr<nsVariant> result = new nsVariant();
859
860
if (StringBeginsWith(src, NS_LITERAL_STRING("http://")))
861
src.Cut(0, 7);
862
else if (StringBeginsWith(src, NS_LITERAL_STRING("https://")))
863
src.Cut(0, 8);
864
else if (StringBeginsWith(src, NS_LITERAL_STRING("ftp://")))
865
src.Cut(0, 6);
866
867
// Remove common URL hostname prefixes
868
if (StringBeginsWith(src, NS_LITERAL_STRING("www."))) {
869
src.Cut(0, 4);
870
}
871
872
result->SetAsAString(src);
873
result.forget(_result);
874
return NS_OK;
875
}
876
877
////////////////////////////////////////////////////////////////////////////////
878
//// Frecency Changed Notification Function
879
880
/* static */
881
nsresult FrecencyNotificationFunction::create(mozIStorageConnection* aDBConn) {
882
RefPtr<FrecencyNotificationFunction> function =
883
new FrecencyNotificationFunction();
884
nsresult rv = aDBConn->CreateFunction(NS_LITERAL_CSTRING("notify_frecency"),
885
5, function);
886
NS_ENSURE_SUCCESS(rv, rv);
887
888
return NS_OK;
889
}
890
891
NS_IMPL_ISUPPORTS(FrecencyNotificationFunction, mozIStorageFunction)
892
893
NS_IMETHODIMP
894
FrecencyNotificationFunction::OnFunctionCall(mozIStorageValueArray* aArgs,
895
nsIVariant** _result) {
896
uint32_t numArgs;
897
nsresult rv = aArgs->GetNumEntries(&numArgs);
898
NS_ENSURE_SUCCESS(rv, rv);
899
MOZ_ASSERT(numArgs == 5);
900
901
int32_t newFrecency = aArgs->AsInt32(0);
902
903
nsAutoCString spec;
904
rv = aArgs->GetUTF8String(1, spec);
905
NS_ENSURE_SUCCESS(rv, rv);
906
907
nsAutoCString guid;
908
rv = aArgs->GetUTF8String(2, guid);
909
NS_ENSURE_SUCCESS(rv, rv);
910
911
bool hidden = static_cast<bool>(aArgs->AsInt32(3));
912
PRTime lastVisitDate = static_cast<PRTime>(aArgs->AsInt64(4));
913
914
const nsNavHistory* navHistory = nsNavHistory::GetConstHistoryService();
915
NS_ENSURE_STATE(navHistory);
916
navHistory->DispatchFrecencyChangedNotification(spec, newFrecency, guid,
917
hidden, lastVisitDate);
918
919
RefPtr<nsVariant> result = new nsVariant();
920
rv = result->SetAsInt32(newFrecency);
921
NS_ENSURE_SUCCESS(rv, rv);
922
result.forget(_result);
923
return NS_OK;
924
}
925
926
////////////////////////////////////////////////////////////////////////////////
927
//// Store Last Inserted Id Function
928
929
/* static */
930
nsresult StoreLastInsertedIdFunction::create(mozIStorageConnection* aDBConn) {
931
RefPtr<StoreLastInsertedIdFunction> function =
932
new StoreLastInsertedIdFunction();
933
nsresult rv = aDBConn->CreateFunction(
934
NS_LITERAL_CSTRING("store_last_inserted_id"), 2, function);
935
NS_ENSURE_SUCCESS(rv, rv);
936
937
return NS_OK;
938
}
939
940
NS_IMPL_ISUPPORTS(StoreLastInsertedIdFunction, mozIStorageFunction)
941
942
NS_IMETHODIMP
943
StoreLastInsertedIdFunction::OnFunctionCall(mozIStorageValueArray* aArgs,
944
nsIVariant** _result) {
945
uint32_t numArgs;
946
nsresult rv = aArgs->GetNumEntries(&numArgs);
947
NS_ENSURE_SUCCESS(rv, rv);
948
MOZ_ASSERT(numArgs == 2);
949
950
nsAutoCString table;
951
rv = aArgs->GetUTF8String(0, table);
952
NS_ENSURE_SUCCESS(rv, rv);
953
954
int64_t lastInsertedId = aArgs->AsInt64(1);
955
956
MOZ_ASSERT(table.EqualsLiteral("moz_places") ||
957
table.EqualsLiteral("moz_historyvisits") ||
958
table.EqualsLiteral("moz_bookmarks") ||
959
table.EqualsLiteral("moz_icons"));
960
961
if (table.EqualsLiteral("moz_bookmarks")) {
962
nsNavBookmarks::StoreLastInsertedId(table, lastInsertedId);
963
} else if (table.EqualsLiteral("moz_icons")) {
964
nsFaviconService::StoreLastInsertedId(table, lastInsertedId);
965
} else {
966
nsNavHistory::StoreLastInsertedId(table, lastInsertedId);
967
}
968
969
RefPtr<nsVariant> result = new nsVariant();
970
rv = result->SetAsInt64(lastInsertedId);
971
NS_ENSURE_SUCCESS(rv, rv);
972
result.forget(_result);
973
return NS_OK;
974
}
975
976
////////////////////////////////////////////////////////////////////////////////
977
//// Get Query Param Function
978
979
/* static */
980
nsresult GetQueryParamFunction::create(mozIStorageConnection* aDBConn) {
981
RefPtr<GetQueryParamFunction> function = new GetQueryParamFunction();
982
return aDBConn->CreateFunction(NS_LITERAL_CSTRING("get_query_param"), 2,
983
function);
984
}
985
986
NS_IMPL_ISUPPORTS(GetQueryParamFunction, mozIStorageFunction)
987
988
NS_IMETHODIMP
989
GetQueryParamFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
990
nsIVariant** _result) {
991
// Must have non-null function arguments.
992
MOZ_ASSERT(aArguments);
993
994
nsDependentCString queryString = getSharedUTF8String(aArguments, 0);
995
nsDependentCString paramName = getSharedUTF8String(aArguments, 1);
996
997
RefPtr<nsVariant> result = new nsVariant();
998
if (!queryString.IsEmpty() && !paramName.IsEmpty()) {
999
GetQueryParamIterator iterator(paramName, result);
1000
URLParams::Parse(queryString, iterator);
1001
}
1002
1003
result.forget(_result);
1004
return NS_OK;
1005
}
1006
1007
////////////////////////////////////////////////////////////////////////////////
1008
//// Hash Function
1009
1010
/* static */
1011
nsresult HashFunction::create(mozIStorageConnection* aDBConn) {
1012
RefPtr<HashFunction> function = new HashFunction();
1013
return aDBConn->CreateFunction(NS_LITERAL_CSTRING("hash"), -1, function);
1014
}
1015
1016
NS_IMPL_ISUPPORTS(HashFunction, mozIStorageFunction)
1017
1018
NS_IMETHODIMP
1019
HashFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
1020
nsIVariant** _result) {
1021
// Must have non-null function arguments.
1022
MOZ_ASSERT(aArguments);
1023
1024
// Fetch arguments. Use default values if they were omitted.
1025
uint32_t numEntries;
1026
nsresult rv = aArguments->GetNumEntries(&numEntries);
1027
NS_ENSURE_SUCCESS(rv, rv);
1028
NS_ENSURE_TRUE(numEntries >= 1 && numEntries <= 2, NS_ERROR_FAILURE);
1029
1030
nsDependentCString str = getSharedUTF8String(aArguments, 0);
1031
nsAutoCString mode;
1032
if (numEntries > 1) {
1033
aArguments->GetUTF8String(1, mode);
1034
}
1035
1036
RefPtr<nsVariant> result = new nsVariant();
1037
uint64_t hash;
1038
rv = HashURL(str, mode, &hash);
1039
NS_ENSURE_SUCCESS(rv, rv);
1040
rv = result->SetAsInt64(hash);
1041
NS_ENSURE_SUCCESS(rv, rv);
1042
1043
result.forget(_result);
1044
return NS_OK;
1045
}
1046
1047
////////////////////////////////////////////////////////////////////////////////
1048
//// Get prefix function
1049
1050
/* static */
1051
nsresult GetPrefixFunction::create(mozIStorageConnection* aDBConn) {
1052
RefPtr<GetPrefixFunction> function = new GetPrefixFunction();
1053
nsresult rv =
1054
aDBConn->CreateFunction(NS_LITERAL_CSTRING("get_prefix"), 1, function);
1055
NS_ENSURE_SUCCESS(rv, rv);
1056
1057
return NS_OK;
1058
}
1059
1060
NS_IMPL_ISUPPORTS(GetPrefixFunction, mozIStorageFunction)
1061
1062
NS_IMETHODIMP
1063
GetPrefixFunction::OnFunctionCall(mozIStorageValueArray* aArgs,
1064
nsIVariant** _result) {
1065
MOZ_ASSERT(aArgs);
1066
1067
uint32_t numArgs;
1068
nsresult rv = aArgs->GetNumEntries(&numArgs);
1069
NS_ENSURE_SUCCESS(rv, rv);
1070
MOZ_ASSERT(numArgs == 1);
1071
1072
nsDependentCString spec(getSharedUTF8String(aArgs, 0));
1073
1074
RefPtr<nsVariant> result = new nsVariant();
1075
result->SetAsACString(Substring(spec, 0, getPrefixLength(spec)));
1076
result.forget(_result);
1077
return NS_OK;
1078
}
1079
1080
////////////////////////////////////////////////////////////////////////////////
1081
//// Get host and port function
1082
1083
/* static */
1084
nsresult GetHostAndPortFunction::create(mozIStorageConnection* aDBConn) {
1085
RefPtr<GetHostAndPortFunction> function = new GetHostAndPortFunction();
1086
nsresult rv = aDBConn->CreateFunction(NS_LITERAL_CSTRING("get_host_and_port"),
1087
1, function);
1088
NS_ENSURE_SUCCESS(rv, rv);
1089
1090
return NS_OK;
1091
}
1092
1093
NS_IMPL_ISUPPORTS(GetHostAndPortFunction, mozIStorageFunction)
1094
1095
NS_IMETHODIMP
1096
GetHostAndPortFunction::OnFunctionCall(mozIStorageValueArray* aArgs,
1097
nsIVariant** _result) {
1098
MOZ_ASSERT(aArgs);
1099
1100
uint32_t numArgs;
1101
nsresult rv = aArgs->GetNumEntries(&numArgs);
1102
NS_ENSURE_SUCCESS(rv, rv);
1103
MOZ_ASSERT(numArgs == 1);
1104
1105
nsDependentCString spec(getSharedUTF8String(aArgs, 0));
1106
1107
RefPtr<nsVariant> result = new nsVariant();
1108
1109
size_type length;
1110
size_type index = indexOfHostAndPort(spec, &length);
1111
result->SetAsACString(Substring(spec, index, length));
1112
result.forget(_result);
1113
return NS_OK;
1114
}
1115
1116
////////////////////////////////////////////////////////////////////////////////
1117
//// Strip prefix and userinfo function
1118
1119
/* static */
1120
nsresult StripPrefixAndUserinfoFunction::create(
1121
mozIStorageConnection* aDBConn) {
1122
RefPtr<StripPrefixAndUserinfoFunction> function =
1123
new StripPrefixAndUserinfoFunction();
1124
nsresult rv = aDBConn->CreateFunction(
1125
NS_LITERAL_CSTRING("strip_prefix_and_userinfo"), 1, function);
1126
NS_ENSURE_SUCCESS(rv, rv);
1127
1128
return NS_OK;
1129
}
1130
1131
NS_IMPL_ISUPPORTS(StripPrefixAndUserinfoFunction, mozIStorageFunction)
1132
1133
NS_IMETHODIMP
1134
StripPrefixAndUserinfoFunction::OnFunctionCall(mozIStorageValueArray* aArgs,
1135
nsIVariant** _result) {
1136
MOZ_ASSERT(aArgs);
1137
1138
uint32_t numArgs;
1139
nsresult rv = aArgs->GetNumEntries(&numArgs);
1140
NS_ENSURE_SUCCESS(rv, rv);
1141
MOZ_ASSERT(numArgs == 1);
1142
1143
nsDependentCString spec(getSharedUTF8String(aArgs, 0));
1144
1145
RefPtr<nsVariant> result = new nsVariant();
1146
1147
size_type index = indexOfHostAndPort(spec, NULL);
1148
result->SetAsACString(Substring(spec, index, spec.Length() - index));
1149
result.forget(_result);
1150
return NS_OK;
1151
}
1152
1153
////////////////////////////////////////////////////////////////////////////////
1154
//// Is frecency decaying function
1155
1156
/* static */
1157
nsresult IsFrecencyDecayingFunction::create(mozIStorageConnection* aDBConn) {
1158
RefPtr<IsFrecencyDecayingFunction> function =
1159
new IsFrecencyDecayingFunction();
1160
nsresult rv = aDBConn->CreateFunction(
1161
NS_LITERAL_CSTRING("is_frecency_decaying"), 0, function);
1162
NS_ENSURE_SUCCESS(rv, rv);
1163
1164
return NS_OK;
1165
}
1166
1167
NS_IMPL_ISUPPORTS(IsFrecencyDecayingFunction, mozIStorageFunction)
1168
1169
NS_IMETHODIMP
1170
IsFrecencyDecayingFunction::OnFunctionCall(mozIStorageValueArray* aArgs,
1171
nsIVariant** _result) {
1172
MOZ_ASSERT(aArgs);
1173
1174
uint32_t numArgs;
1175
nsresult rv = aArgs->GetNumEntries(&numArgs);
1176
NS_ENSURE_SUCCESS(rv, rv);
1177
MOZ_ASSERT(numArgs == 0);
1178
1179
const nsNavHistory* navHistory = nsNavHistory::GetConstHistoryService();
1180
NS_ENSURE_STATE(navHistory);
1181
1182
RefPtr<nsVariant> result = new nsVariant();
1183
rv = result->SetAsBool(navHistory->IsFrecencyDecaying());
1184
NS_ENSURE_SUCCESS(rv, rv);
1185
result.forget(_result);
1186
return NS_OK;
1187
}
1188
1189
////////////////////////////////////////////////////////////////////////////////
1190
//// sqrt function
1191
1192
/* static */
1193
nsresult SqrtFunction::create(mozIStorageConnection* aDBConn) {
1194
RefPtr<SqrtFunction> function = new SqrtFunction();
1195
nsresult rv =
1196
aDBConn->CreateFunction(NS_LITERAL_CSTRING("sqrt"), 1, function);
1197
NS_ENSURE_SUCCESS(rv, rv);
1198
return NS_OK;
1199
}
1200
1201
NS_IMPL_ISUPPORTS(SqrtFunction, mozIStorageFunction)
1202
1203
NS_IMETHODIMP
1204
SqrtFunction::OnFunctionCall(mozIStorageValueArray* aArgs,
1205
nsIVariant** _result) {
1206
MOZ_ASSERT(aArgs);
1207
1208
uint32_t numArgs;
1209
nsresult rv = aArgs->GetNumEntries(&numArgs);
1210
NS_ENSURE_SUCCESS(rv, rv);
1211
MOZ_ASSERT(numArgs == 1);
1212
1213
double value = aArgs->AsDouble(0);
1214
1215
RefPtr<nsVariant> result = new nsVariant();
1216
rv = result->SetAsDouble(sqrt(value));
1217
NS_ENSURE_SUCCESS(rv, rv);
1218
result.forget(_result);
1219
1220
return NS_OK;
1221
}
1222
1223
////////////////////////////////////////////////////////////////////////////////
1224
//// Note Sync Change Function
1225
1226
/* static */
1227
nsresult NoteSyncChangeFunction::create(mozIStorageConnection* aDBConn) {
1228
RefPtr<NoteSyncChangeFunction> function = new NoteSyncChangeFunction();
1229
nsresult rv = aDBConn->CreateFunction(NS_LITERAL_CSTRING("note_sync_change"),
1230
0, function);
1231
NS_ENSURE_SUCCESS(rv, rv);
1232
1233
return NS_OK;
1234
}
1235
1236
NS_IMPL_ISUPPORTS(NoteSyncChangeFunction, mozIStorageFunction)
1237
1238
NS_IMETHODIMP
1239
NoteSyncChangeFunction::OnFunctionCall(mozIStorageValueArray* aArgs,
1240
nsIVariant** _result) {
1241
nsNavBookmarks::NoteSyncChange();
1242
*_result = nullptr;
1243
return NS_OK;
1244
}
1245
1246
} // namespace places
1247
} // namespace mozilla