toolkit/components/places/SQLFunctions.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

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

mercurial