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