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