Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
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/. */
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 "nsUTF8Utils.h"
15 #include "nsINavHistoryService.h"
16 #include "nsPrintfCString.h"
17 #include "nsNavHistory.h"
18 #include "mozilla/Telemetry.h"
19 #include "mozilla/Likely.h"
21 using namespace mozilla::storage;
23 // Keep the GUID-related parts of this file in sync with toolkit/downloads/SQLFunctions.cpp!
25 ////////////////////////////////////////////////////////////////////////////////
26 //// Anonymous Helpers
28 namespace {
30 typedef nsACString::const_char_iterator const_char_iterator;
32 /**
33 * Get a pointer to the word boundary after aStart if aStart points to an
34 * ASCII letter (i.e. [a-zA-Z]). Otherwise, return aNext, which we assume
35 * points to the next character in the UTF-8 sequence.
36 *
37 * We define a word boundary as anything that's not [a-z] -- this lets us
38 * match CamelCase words.
39 *
40 * @param aStart the beginning of the UTF-8 sequence
41 * @param aNext the next character in the sequence
42 * @param aEnd the first byte which is not part of the sequence
43 *
44 * @return a pointer to the next word boundary after aStart
45 */
46 static
47 MOZ_ALWAYS_INLINE const_char_iterator
48 nextWordBoundary(const_char_iterator const aStart,
49 const_char_iterator const aNext,
50 const_char_iterator const aEnd) {
52 const_char_iterator cur = aStart;
53 if (('a' <= *cur && *cur <= 'z') ||
54 ('A' <= *cur && *cur <= 'Z')) {
56 // Since we'll halt as soon as we see a non-ASCII letter, we can do a
57 // simple byte-by-byte comparison here and avoid the overhead of a
58 // UTF8CharEnumerator.
59 do {
60 cur++;
61 } while (cur < aEnd && 'a' <= *cur && *cur <= 'z');
62 }
63 else {
64 cur = aNext;
65 }
67 return cur;
68 }
70 enum FindInStringBehavior {
71 eFindOnBoundary,
72 eFindAnywhere
73 };
75 /**
76 * findAnywhere and findOnBoundary do almost the same thing, so it's natural
77 * to implement them in terms of a single function. They're both
78 * performance-critical functions, however, and checking aBehavior makes them
79 * a bit slower. Our solution is to define findInString as MOZ_ALWAYS_INLINE
80 * and rely on the compiler to optimize out the aBehavior check.
81 *
82 * @param aToken
83 * The token we're searching for
84 * @param aSourceString
85 * The string in which we're searching
86 * @param aBehavior
87 * eFindOnBoundary if we should only consider matchines which occur on
88 * word boundaries, or eFindAnywhere if we should consider matches
89 * which appear anywhere.
90 *
91 * @return true if aToken was found in aSourceString, false otherwise.
92 */
93 static
94 MOZ_ALWAYS_INLINE bool
95 findInString(const nsDependentCSubstring &aToken,
96 const nsACString &aSourceString,
97 FindInStringBehavior aBehavior)
98 {
99 // CaseInsensitiveUTF8CharsEqual assumes that there's at least one byte in
100 // the both strings, so don't pass an empty token here.
101 NS_PRECONDITION(!aToken.IsEmpty(), "Don't search for an empty token!");
103 // We cannot match anything if there is nothing to search.
104 if (aSourceString.IsEmpty()) {
105 return false;
106 }
108 const_char_iterator tokenStart(aToken.BeginReading()),
109 tokenEnd(aToken.EndReading()),
110 sourceStart(aSourceString.BeginReading()),
111 sourceEnd(aSourceString.EndReading());
113 do {
114 // We are on a word boundary (if aBehavior == eFindOnBoundary). See if
115 // aToken matches sourceStart.
117 // Check whether the first character in the token matches the character
118 // at sourceStart. At the same time, get a pointer to the next character
119 // in both the token and the source.
120 const_char_iterator sourceNext, tokenCur;
121 bool error;
122 if (CaseInsensitiveUTF8CharsEqual(sourceStart, tokenStart,
123 sourceEnd, tokenEnd,
124 &sourceNext, &tokenCur, &error)) {
126 // We don't need to check |error| here -- if
127 // CaseInsensitiveUTF8CharCompare encounters an error, it'll also
128 // return false and we'll catch the error outside the if.
130 const_char_iterator sourceCur = sourceNext;
131 while (true) {
132 if (tokenCur >= tokenEnd) {
133 // We matched the whole token!
134 return true;
135 }
137 if (sourceCur >= sourceEnd) {
138 // We ran into the end of source while matching a token. This
139 // means we'll never find the token we're looking for.
140 return false;
141 }
143 if (!CaseInsensitiveUTF8CharsEqual(sourceCur, tokenCur,
144 sourceEnd, tokenEnd,
145 &sourceCur, &tokenCur, &error)) {
146 // sourceCur doesn't match tokenCur (or there's an error), so break
147 // out of this loop.
148 break;
149 }
150 }
151 }
153 // If something went wrong above, get out of here!
154 if (MOZ_UNLIKELY(error)) {
155 return false;
156 }
158 // We didn't match the token. If we're searching for matches on word
159 // boundaries, skip to the next word boundary. Otherwise, advance
160 // forward one character, using the sourceNext pointer we saved earlier.
162 if (aBehavior == eFindOnBoundary) {
163 sourceStart = nextWordBoundary(sourceStart, sourceNext, sourceEnd);
164 }
165 else {
166 sourceStart = sourceNext;
167 }
169 } while (sourceStart < sourceEnd);
171 return false;
172 }
174 } // End anonymous namespace
176 namespace mozilla {
177 namespace places {
179 ////////////////////////////////////////////////////////////////////////////////
180 //// AutoComplete Matching Function
182 //////////////////////////////////////////////////////////////////////////////
183 //// MatchAutoCompleteFunction
185 /* static */
186 nsresult
187 MatchAutoCompleteFunction::create(mozIStorageConnection *aDBConn)
188 {
189 nsRefPtr<MatchAutoCompleteFunction> function =
190 new MatchAutoCompleteFunction();
192 nsresult rv = aDBConn->CreateFunction(
193 NS_LITERAL_CSTRING("autocomplete_match"), kArgIndexLength, function
194 );
195 NS_ENSURE_SUCCESS(rv, rv);
197 return NS_OK;
198 }
200 /* static */
201 void
202 MatchAutoCompleteFunction::fixupURISpec(const nsCString &aURISpec,
203 int32_t aMatchBehavior,
204 nsCString &_fixedSpec)
205 {
206 nsCString unescapedSpec;
207 (void)NS_UnescapeURL(aURISpec, esc_SkipControl | esc_AlwaysCopy,
208 unescapedSpec);
210 // If this unescaped string is valid UTF-8, we'll use it. Otherwise,
211 // we will simply use our original string.
212 NS_ASSERTION(_fixedSpec.IsEmpty(),
213 "Passing a non-empty string as an out parameter!");
214 if (IsUTF8(unescapedSpec))
215 _fixedSpec.Assign(unescapedSpec);
216 else
217 _fixedSpec.Assign(aURISpec);
219 if (aMatchBehavior == mozIPlacesAutoComplete::MATCH_ANYWHERE_UNMODIFIED)
220 return;
222 if (StringBeginsWith(_fixedSpec, NS_LITERAL_CSTRING("http://")))
223 _fixedSpec.Cut(0, 7);
224 else if (StringBeginsWith(_fixedSpec, NS_LITERAL_CSTRING("https://")))
225 _fixedSpec.Cut(0, 8);
226 else if (StringBeginsWith(_fixedSpec, NS_LITERAL_CSTRING("ftp://")))
227 _fixedSpec.Cut(0, 6);
229 if (StringBeginsWith(_fixedSpec, NS_LITERAL_CSTRING("www.")))
230 _fixedSpec.Cut(0, 4);
231 }
233 /* static */
234 bool
235 MatchAutoCompleteFunction::findAnywhere(const nsDependentCSubstring &aToken,
236 const nsACString &aSourceString)
237 {
238 // We can't use FindInReadable here; it works only for ASCII.
240 return findInString(aToken, aSourceString, eFindAnywhere);
241 }
243 /* static */
244 bool
245 MatchAutoCompleteFunction::findOnBoundary(const nsDependentCSubstring &aToken,
246 const nsACString &aSourceString)
247 {
248 return findInString(aToken, aSourceString, eFindOnBoundary);
249 }
251 /* static */
252 bool
253 MatchAutoCompleteFunction::findBeginning(const nsDependentCSubstring &aToken,
254 const nsACString &aSourceString)
255 {
256 NS_PRECONDITION(!aToken.IsEmpty(), "Don't search for an empty token!");
258 // We can't use StringBeginsWith here, unfortunately. Although it will
259 // happily take a case-insensitive UTF8 comparator, it eventually calls
260 // nsACString::Equals, which checks that the two strings contain the same
261 // number of bytes before calling the comparator. Two characters may be
262 // case-insensitively equal while taking up different numbers of bytes, so
263 // this is not what we want.
265 const_char_iterator tokenStart(aToken.BeginReading()),
266 tokenEnd(aToken.EndReading()),
267 sourceStart(aSourceString.BeginReading()),
268 sourceEnd(aSourceString.EndReading());
270 bool dummy;
271 while (sourceStart < sourceEnd &&
272 CaseInsensitiveUTF8CharsEqual(sourceStart, tokenStart,
273 sourceEnd, tokenEnd,
274 &sourceStart, &tokenStart, &dummy)) {
276 // We found the token!
277 if (tokenStart >= tokenEnd) {
278 return true;
279 }
280 }
282 // We don't need to check CaseInsensitiveUTF8CharsEqual's error condition
283 // (stored in |dummy|), since the function will return false if it
284 // encounters an error.
286 return false;
287 }
289 /* static */
290 bool
291 MatchAutoCompleteFunction::findBeginningCaseSensitive(
292 const nsDependentCSubstring &aToken,
293 const nsACString &aSourceString)
294 {
295 NS_PRECONDITION(!aToken.IsEmpty(), "Don't search for an empty token!");
297 return StringBeginsWith(aSourceString, aToken);
298 }
300 /* static */
301 MatchAutoCompleteFunction::searchFunctionPtr
302 MatchAutoCompleteFunction::getSearchFunction(int32_t aBehavior)
303 {
304 switch (aBehavior) {
305 case mozIPlacesAutoComplete::MATCH_ANYWHERE:
306 case mozIPlacesAutoComplete::MATCH_ANYWHERE_UNMODIFIED:
307 return findAnywhere;
308 case mozIPlacesAutoComplete::MATCH_BEGINNING:
309 return findBeginning;
310 case mozIPlacesAutoComplete::MATCH_BEGINNING_CASE_SENSITIVE:
311 return findBeginningCaseSensitive;
312 case mozIPlacesAutoComplete::MATCH_BOUNDARY:
313 default:
314 return findOnBoundary;
315 };
316 }
318 NS_IMPL_ISUPPORTS(
319 MatchAutoCompleteFunction,
320 mozIStorageFunction
321 )
323 //////////////////////////////////////////////////////////////////////////////
324 //// mozIStorageFunction
326 NS_IMETHODIMP
327 MatchAutoCompleteFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
328 nsIVariant **_result)
329 {
330 // Macro to make the code a bit cleaner and easier to read. Operates on
331 // searchBehavior.
332 int32_t searchBehavior = aArguments->AsInt32(kArgIndexSearchBehavior);
333 #define HAS_BEHAVIOR(aBitName) \
334 (searchBehavior & mozIPlacesAutoComplete::BEHAVIOR_##aBitName)
336 nsAutoCString searchString;
337 (void)aArguments->GetUTF8String(kArgSearchString, searchString);
338 nsCString url;
339 (void)aArguments->GetUTF8String(kArgIndexURL, url);
341 int32_t matchBehavior = aArguments->AsInt32(kArgIndexMatchBehavior);
343 // We only want to filter javascript: URLs if we are not supposed to search
344 // for them, and the search does not start with "javascript:".
345 if (matchBehavior != mozIPlacesAutoComplete::MATCH_ANYWHERE_UNMODIFIED &&
346 !HAS_BEHAVIOR(JAVASCRIPT) &&
347 !StringBeginsWith(searchString, NS_LITERAL_CSTRING("javascript:")) &&
348 StringBeginsWith(url, NS_LITERAL_CSTRING("javascript:"))) {
349 NS_ADDREF(*_result = new IntegerVariant(0));
350 return NS_OK;
351 }
353 int32_t visitCount = aArguments->AsInt32(kArgIndexVisitCount);
354 bool typed = aArguments->AsInt32(kArgIndexTyped) ? true : false;
355 bool bookmark = aArguments->AsInt32(kArgIndexBookmark) ? true : false;
356 nsAutoCString tags;
357 (void)aArguments->GetUTF8String(kArgIndexTags, tags);
358 int32_t openPageCount = aArguments->AsInt32(kArgIndexOpenPageCount);
360 // Make sure we match all the filter requirements. If a given restriction
361 // is active, make sure the corresponding condition is not true.
362 bool matches = !(
363 (HAS_BEHAVIOR(HISTORY) && visitCount == 0) ||
364 (HAS_BEHAVIOR(TYPED) && !typed) ||
365 (HAS_BEHAVIOR(BOOKMARK) && !bookmark) ||
366 (HAS_BEHAVIOR(TAG) && tags.IsVoid()) ||
367 (HAS_BEHAVIOR(OPENPAGE) && openPageCount == 0)
368 );
369 if (!matches) {
370 NS_ADDREF(*_result = new IntegerVariant(0));
371 return NS_OK;
372 }
374 // Obtain our search function.
375 searchFunctionPtr searchFunction = getSearchFunction(matchBehavior);
377 // Clean up our URI spec and prepare it for searching.
378 nsCString fixedURI;
379 fixupURISpec(url, matchBehavior, fixedURI);
381 nsAutoCString title;
382 (void)aArguments->GetUTF8String(kArgIndexTitle, title);
384 // Determine if every token matches either the bookmark title, tags, page
385 // title, or page URL.
386 nsCWhitespaceTokenizer tokenizer(searchString);
387 while (matches && tokenizer.hasMoreTokens()) {
388 const nsDependentCSubstring &token = tokenizer.nextToken();
390 if (HAS_BEHAVIOR(TITLE) && HAS_BEHAVIOR(URL)) {
391 matches = (searchFunction(token, title) || searchFunction(token, tags)) &&
392 searchFunction(token, fixedURI);
393 }
394 else if (HAS_BEHAVIOR(TITLE)) {
395 matches = searchFunction(token, title) || searchFunction(token, tags);
396 }
397 else if (HAS_BEHAVIOR(URL)) {
398 matches = searchFunction(token, fixedURI);
399 }
400 else {
401 matches = searchFunction(token, title) ||
402 searchFunction(token, tags) ||
403 searchFunction(token, fixedURI);
404 }
405 }
407 NS_ADDREF(*_result = new IntegerVariant(matches ? 1 : 0));
408 return NS_OK;
409 #undef HAS_BEHAVIOR
410 }
413 ////////////////////////////////////////////////////////////////////////////////
414 //// Frecency Calculation Function
416 //////////////////////////////////////////////////////////////////////////////
417 //// CalculateFrecencyFunction
419 /* static */
420 nsresult
421 CalculateFrecencyFunction::create(mozIStorageConnection *aDBConn)
422 {
423 nsRefPtr<CalculateFrecencyFunction> function =
424 new CalculateFrecencyFunction();
426 nsresult rv = aDBConn->CreateFunction(
427 NS_LITERAL_CSTRING("calculate_frecency"), 1, function
428 );
429 NS_ENSURE_SUCCESS(rv, rv);
431 return NS_OK;
432 }
434 NS_IMPL_ISUPPORTS(
435 CalculateFrecencyFunction,
436 mozIStorageFunction
437 )
439 //////////////////////////////////////////////////////////////////////////////
440 //// mozIStorageFunction
442 NS_IMETHODIMP
443 CalculateFrecencyFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
444 nsIVariant **_result)
445 {
446 // Fetch arguments. Use default values if they were omitted.
447 uint32_t numEntries;
448 nsresult rv = aArguments->GetNumEntries(&numEntries);
449 NS_ENSURE_SUCCESS(rv, rv);
450 NS_ASSERTION(numEntries > 0, "unexpected number of arguments");
452 Telemetry::AutoTimer<Telemetry::PLACES_FRECENCY_CALC_TIME_MS> timer;
454 int64_t pageId = aArguments->AsInt64(0);
455 int32_t typed = numEntries > 1 ? aArguments->AsInt32(1) : 0;
456 int32_t fullVisitCount = numEntries > 2 ? aArguments->AsInt32(2) : 0;
457 int64_t bookmarkId = numEntries > 3 ? aArguments->AsInt64(3) : 0;
458 int32_t visitCount = 0;
459 int32_t hidden = 0;
460 int32_t isQuery = 0;
461 float pointsForSampledVisits = 0.0;
463 // This is a const version of the history object for thread-safety.
464 const nsNavHistory* history = nsNavHistory::GetConstHistoryService();
465 NS_ENSURE_STATE(history);
466 nsRefPtr<Database> DB = Database::GetDatabase();
467 NS_ENSURE_STATE(DB);
469 if (pageId > 0) {
470 // The page is already in the database, and we can fetch current
471 // params from the database.
472 nsRefPtr<mozIStorageStatement> getPageInfo = DB->GetStatement(
473 "SELECT typed, hidden, visit_count, "
474 "(SELECT count(*) FROM moz_historyvisits WHERE place_id = :page_id), "
475 "EXISTS (SELECT 1 FROM moz_bookmarks WHERE fk = :page_id), "
476 "(url > 'place:' AND url < 'place;') "
477 "FROM moz_places "
478 "WHERE id = :page_id "
479 );
480 NS_ENSURE_STATE(getPageInfo);
481 mozStorageStatementScoper infoScoper(getPageInfo);
483 rv = getPageInfo->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), pageId);
484 NS_ENSURE_SUCCESS(rv, rv);
486 bool hasResult;
487 rv = getPageInfo->ExecuteStep(&hasResult);
488 NS_ENSURE_SUCCESS(rv, rv);
489 NS_ENSURE_TRUE(hasResult, NS_ERROR_UNEXPECTED);
490 rv = getPageInfo->GetInt32(0, &typed);
491 NS_ENSURE_SUCCESS(rv, rv);
492 rv = getPageInfo->GetInt32(1, &hidden);
493 NS_ENSURE_SUCCESS(rv, rv);
494 rv = getPageInfo->GetInt32(2, &visitCount);
495 NS_ENSURE_SUCCESS(rv, rv);
496 rv = getPageInfo->GetInt32(3, &fullVisitCount);
497 NS_ENSURE_SUCCESS(rv, rv);
498 rv = getPageInfo->GetInt64(4, &bookmarkId);
499 NS_ENSURE_SUCCESS(rv, rv);
500 rv = getPageInfo->GetInt32(5, &isQuery);
501 NS_ENSURE_SUCCESS(rv, rv);
503 // NOTE: This is not limited to visits with "visit_type NOT IN (0,4,7,8)"
504 // because otherwise it would not return any visit for those transitions
505 // causing an incorrect frecency, see CalculateFrecencyInternal().
506 // In case of a temporary or permanent redirect, calculate the frecency
507 // as if the original page was visited.
508 // Get a sample of the last visits to the page, to calculate its weight.
509 nsCOMPtr<mozIStorageStatement> getVisits = DB->GetStatement(
510 NS_LITERAL_CSTRING(
511 "/* do not warn (bug 659740 - SQLite may ignore index if few visits exist) */"
512 "SELECT "
513 "ROUND((strftime('%s','now','localtime','utc') - v.visit_date/1000000)/86400), "
514 "IFNULL(r.visit_type, v.visit_type), "
515 "v.visit_date "
516 "FROM moz_historyvisits v "
517 "LEFT JOIN moz_historyvisits r ON r.id = v.from_visit AND v.visit_type BETWEEN "
518 ) + nsPrintfCString("%d AND %d ", nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
519 nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY) +
520 NS_LITERAL_CSTRING(
521 "WHERE v.place_id = :page_id "
522 "ORDER BY v.visit_date DESC "
523 )
524 );
525 NS_ENSURE_STATE(getVisits);
526 mozStorageStatementScoper visitsScoper(getVisits);
528 rv = getVisits->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), pageId);
529 NS_ENSURE_SUCCESS(rv, rv);
531 // Fetch only a limited number of recent visits.
532 int32_t numSampledVisits = 0;
533 for (int32_t maxVisits = history->GetNumVisitsForFrecency();
534 numSampledVisits < maxVisits &&
535 NS_SUCCEEDED(getVisits->ExecuteStep(&hasResult)) && hasResult;
536 numSampledVisits++) {
537 int32_t visitType;
538 rv = getVisits->GetInt32(1, &visitType);
539 NS_ENSURE_SUCCESS(rv, rv);
540 int32_t bonus = history->GetFrecencyTransitionBonus(visitType, true);
542 // Always add the bookmark visit bonus.
543 if (bookmarkId) {
544 bonus += history->GetFrecencyTransitionBonus(nsINavHistoryService::TRANSITION_BOOKMARK, true);
545 }
547 // If bonus was zero, we can skip the work to determine the weight.
548 if (bonus) {
549 int32_t ageInDays = getVisits->AsInt32(0);
550 int32_t weight = history->GetFrecencyAgedWeight(ageInDays);
551 pointsForSampledVisits += (float)(weight * (bonus / 100.0));
552 }
553 }
555 // If we found some visits for this page, use the calculated weight.
556 if (numSampledVisits) {
557 // fix for bug #412219
558 if (!pointsForSampledVisits) {
559 // For URIs with zero points in the sampled recent visits
560 // but "browsing" type visits outside the sampling range, set
561 // frecency to -visit_count, so they're still shown in autocomplete.
562 NS_ADDREF(*_result = new IntegerVariant(-visitCount));
563 }
564 else {
565 // Estimate frecency using the last few visits.
566 // Use ceilf() so that we don't round down to 0, which
567 // would cause us to completely ignore the place during autocomplete.
568 NS_ADDREF(*_result = new IntegerVariant((int32_t) ceilf(fullVisitCount * ceilf(pointsForSampledVisits) / numSampledVisits)));
569 }
571 return NS_OK;
572 }
573 }
575 // This page is unknown or has no visits. It could have just been added, so
576 // use passed in or default values.
578 // The code below works well for guessing the frecency on import, and we'll
579 // correct later once we have visits.
580 // TODO: What if we don't have visits and we never visit? We could end up
581 // with a really high value that keeps coming up in ac results? Should we
582 // only do this on import? Have to figure it out.
583 int32_t bonus = 0;
585 // Make it so something bookmarked and typed will have a higher frecency
586 // than something just typed or just bookmarked.
587 if (bookmarkId && !isQuery) {
588 bonus += history->GetFrecencyTransitionBonus(nsINavHistoryService::TRANSITION_BOOKMARK, false);;
589 // For unvisited bookmarks, produce a non-zero frecency, so that they show
590 // up in URL bar autocomplete.
591 fullVisitCount = 1;
592 }
594 if (typed) {
595 bonus += history->GetFrecencyTransitionBonus(nsINavHistoryService::TRANSITION_TYPED, false);
596 }
598 // Assume "now" as our ageInDays, so use the first bucket.
599 pointsForSampledVisits = history->GetFrecencyBucketWeight(1) * (bonus / (float)100.0);
601 // use ceilf() so that we don't round down to 0, which
602 // would cause us to completely ignore the place during autocomplete
603 NS_ADDREF(*_result = new IntegerVariant((int32_t) ceilf(fullVisitCount * ceilf(pointsForSampledVisits))));
605 return NS_OK;
606 }
608 ////////////////////////////////////////////////////////////////////////////////
609 //// GUID Creation Function
611 //////////////////////////////////////////////////////////////////////////////
612 //// GenerateGUIDFunction
614 /* static */
615 nsresult
616 GenerateGUIDFunction::create(mozIStorageConnection *aDBConn)
617 {
618 nsRefPtr<GenerateGUIDFunction> function = new GenerateGUIDFunction();
619 nsresult rv = aDBConn->CreateFunction(
620 NS_LITERAL_CSTRING("generate_guid"), 0, function
621 );
622 NS_ENSURE_SUCCESS(rv, rv);
624 return NS_OK;
625 }
627 NS_IMPL_ISUPPORTS(
628 GenerateGUIDFunction,
629 mozIStorageFunction
630 )
632 //////////////////////////////////////////////////////////////////////////////
633 //// mozIStorageFunction
635 NS_IMETHODIMP
636 GenerateGUIDFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
637 nsIVariant **_result)
638 {
639 nsAutoCString guid;
640 nsresult rv = GenerateGUID(guid);
641 NS_ENSURE_SUCCESS(rv, rv);
643 NS_ADDREF(*_result = new UTF8TextVariant(guid));
644 return NS_OK;
645 }
647 ////////////////////////////////////////////////////////////////////////////////
648 //// Get Unreversed Host Function
650 //////////////////////////////////////////////////////////////////////////////
651 //// GetUnreversedHostFunction
653 /* static */
654 nsresult
655 GetUnreversedHostFunction::create(mozIStorageConnection *aDBConn)
656 {
657 nsRefPtr<GetUnreversedHostFunction> function = new GetUnreversedHostFunction();
658 nsresult rv = aDBConn->CreateFunction(
659 NS_LITERAL_CSTRING("get_unreversed_host"), 1, function
660 );
661 NS_ENSURE_SUCCESS(rv, rv);
663 return NS_OK;
664 }
666 NS_IMPL_ISUPPORTS(
667 GetUnreversedHostFunction,
668 mozIStorageFunction
669 )
671 //////////////////////////////////////////////////////////////////////////////
672 //// mozIStorageFunction
674 NS_IMETHODIMP
675 GetUnreversedHostFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
676 nsIVariant **_result)
677 {
678 // Must have non-null function arguments.
679 MOZ_ASSERT(aArguments);
681 nsAutoString src;
682 aArguments->GetString(0, src);
684 nsCOMPtr<nsIWritableVariant> result =
685 do_CreateInstance("@mozilla.org/variant;1");
686 NS_ENSURE_STATE(result);
688 if (src.Length()>1) {
689 src.Truncate(src.Length() - 1);
690 nsAutoString dest;
691 ReverseString(src, dest);
692 result->SetAsAString(dest);
693 }
694 else {
695 result->SetAsAString(EmptyString());
696 }
697 NS_ADDREF(*_result = result);
698 return NS_OK;
699 }
701 ////////////////////////////////////////////////////////////////////////////////
702 //// Fixup URL Function
704 //////////////////////////////////////////////////////////////////////////////
705 //// FixupURLFunction
707 /* static */
708 nsresult
709 FixupURLFunction::create(mozIStorageConnection *aDBConn)
710 {
711 nsRefPtr<FixupURLFunction> function = new FixupURLFunction();
712 nsresult rv = aDBConn->CreateFunction(
713 NS_LITERAL_CSTRING("fixup_url"), 1, function
714 );
715 NS_ENSURE_SUCCESS(rv, rv);
717 return NS_OK;
718 }
720 NS_IMPL_ISUPPORTS(
721 FixupURLFunction,
722 mozIStorageFunction
723 )
725 //////////////////////////////////////////////////////////////////////////////
726 //// mozIStorageFunction
728 NS_IMETHODIMP
729 FixupURLFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
730 nsIVariant **_result)
731 {
732 // Must have non-null function arguments.
733 MOZ_ASSERT(aArguments);
735 nsAutoString src;
736 aArguments->GetString(0, src);
738 nsCOMPtr<nsIWritableVariant> result =
739 do_CreateInstance("@mozilla.org/variant;1");
740 NS_ENSURE_STATE(result);
742 // Remove common URL hostname prefixes
743 if (StringBeginsWith(src, NS_LITERAL_STRING("www."))) {
744 src.Cut(0, 4);
745 }
747 result->SetAsAString(src);
748 NS_ADDREF(*_result = result);
749 return NS_OK;
750 }
752 ////////////////////////////////////////////////////////////////////////////////
753 //// Frecency Changed Notification Function
755 /* static */
756 nsresult
757 FrecencyNotificationFunction::create(mozIStorageConnection *aDBConn)
758 {
759 nsRefPtr<FrecencyNotificationFunction> function =
760 new FrecencyNotificationFunction();
761 nsresult rv = aDBConn->CreateFunction(
762 NS_LITERAL_CSTRING("notify_frecency"), 5, function
763 );
764 NS_ENSURE_SUCCESS(rv, rv);
766 return NS_OK;
767 }
769 NS_IMPL_ISUPPORTS(
770 FrecencyNotificationFunction,
771 mozIStorageFunction
772 )
774 NS_IMETHODIMP
775 FrecencyNotificationFunction::OnFunctionCall(mozIStorageValueArray *aArgs,
776 nsIVariant **_result)
777 {
778 uint32_t numArgs;
779 nsresult rv = aArgs->GetNumEntries(&numArgs);
780 NS_ENSURE_SUCCESS(rv, rv);
781 MOZ_ASSERT(numArgs == 5);
783 int32_t newFrecency = aArgs->AsInt32(0);
785 nsAutoCString spec;
786 rv = aArgs->GetUTF8String(1, spec);
787 NS_ENSURE_SUCCESS(rv, rv);
789 nsAutoCString guid;
790 rv = aArgs->GetUTF8String(2, guid);
791 NS_ENSURE_SUCCESS(rv, rv);
793 bool hidden = static_cast<bool>(aArgs->AsInt32(3));
794 PRTime lastVisitDate = static_cast<PRTime>(aArgs->AsInt64(4));
796 const nsNavHistory* navHistory = nsNavHistory::GetConstHistoryService();
797 NS_ENSURE_STATE(navHistory);
798 navHistory->DispatchFrecencyChangedNotification(spec, newFrecency, guid,
799 hidden, lastVisitDate);
801 nsCOMPtr<nsIWritableVariant> result =
802 do_CreateInstance("@mozilla.org/variant;1");
803 NS_ENSURE_STATE(result);
804 rv = result->SetAsInt32(newFrecency);
805 NS_ENSURE_SUCCESS(rv, rv);
806 NS_ADDREF(*_result = result);
807 return NS_OK;
808 }
810 } // namespace places
811 } // namespace mozilla