LCOV - code coverage report
Current view: directory - toolkit/components/places - SQLFunctions.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 247 243 98.4 %
Date: 2012-06-02 Functions: 32 25 78.1 %

       1                 : /* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
       2                 :  * ***** BEGIN LICENSE BLOCK *****
       3                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       4                 :  *
       5                 :  * The contents of this file are subject to the Mozilla Public License Version
       6                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       7                 :  * the License. You may obtain a copy of the License at
       8                 :  * http://www.mozilla.org/MPL/
       9                 :  *
      10                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      11                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      12                 :  * for the specific language governing rights and limitations under the
      13                 :  * License.
      14                 :  *
      15                 :  * The Original Code is Places code.
      16                 :  *
      17                 :  * The Initial Developer of the Original Code is
      18                 :  * the Mozilla Foundation.
      19                 :  * Portions created by the Initial Developer are Copyright (C) 2009
      20                 :  * the Initial Developer. All Rights Reserved.
      21                 :  *
      22                 :  * Contributor(s):
      23                 :  *   Shawn Wilsher <me@shawnwilsher.com> (Original Author)
      24                 :  *
      25                 :  * Alternatively, the contents of this file may be used under the terms of
      26                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      27                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      28                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      29                 :  * of those above. If you wish to allow use of your version of this file only
      30                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      31                 :  * use your version of this file under the terms of the MPL, indicate your
      32                 :  * decision by deleting the provisions above and replace them with the notice
      33                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      34                 :  * the provisions above, a recipient may use your version of this file under
      35                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      36                 :  *
      37                 :  * ***** END LICENSE BLOCK ***** */
      38                 : 
      39                 : #include "mozilla/storage.h"
      40                 : #include "nsString.h"
      41                 : #include "nsUnicharUtils.h"
      42                 : #include "nsWhitespaceTokenizer.h"
      43                 : #include "nsEscape.h"
      44                 : #include "mozIPlacesAutoComplete.h"
      45                 : #include "SQLFunctions.h"
      46                 : #include "nsMathUtils.h"
      47                 : #include "nsUTF8Utils.h"
      48                 : #include "nsINavHistoryService.h"
      49                 : #include "nsPrintfCString.h"
      50                 : #include "nsNavHistory.h"
      51                 : #if defined(XP_OS2)
      52                 : #include "nsIRandomGenerator.h"
      53                 : #endif
      54                 : #include "mozilla/Telemetry.h"
      55                 : 
      56                 : using namespace mozilla::storage;
      57                 : 
      58                 : ////////////////////////////////////////////////////////////////////////////////
      59                 : //// Anonymous Helpers
      60                 : 
      61                 : namespace {
      62                 : 
      63                 :   typedef nsACString::const_char_iterator const_char_iterator;
      64                 : 
      65                 :   /**
      66                 :    * Get a pointer to the word boundary after aStart if aStart points to an
      67                 :    * ASCII letter (i.e. [a-zA-Z]).  Otherwise, return aNext, which we assume
      68                 :    * points to the next character in the UTF-8 sequence.
      69                 :    *
      70                 :    * We define a word boundary as anything that's not [a-z] -- this lets us
      71                 :    * match CamelCase words.
      72                 :    *
      73                 :    * @param aStart the beginning of the UTF-8 sequence
      74                 :    * @param aNext the next character in the sequence
      75                 :    * @param aEnd the first byte which is not part of the sequence
      76                 :    *
      77                 :    * @return a pointer to the next word boundary after aStart
      78                 :    */
      79                 :   static
      80                 :   NS_ALWAYS_INLINE const_char_iterator
      81               0 :   nextWordBoundary(const_char_iterator const aStart,
      82                 :                    const_char_iterator const aNext,
      83                 :                    const_char_iterator const aEnd) {
      84                 : 
      85            5428 :     const_char_iterator cur = aStart;
      86            5428 :     if (('a' <= *cur && *cur <= 'z') ||
      87                 :         ('A' <= *cur && *cur <= 'Z')) {
      88                 : 
      89                 :       // Since we'll halt as soon as we see a non-ASCII letter, we can do a
      90                 :       // simple byte-by-byte comparison here and avoid the overhead of a
      91                 :       // UTF8CharEnumerator.
      92           10242 :       do {
      93           10242 :         cur++;
      94                 :       } while (cur < aEnd && 'a' <= *cur && *cur <= 'z');
      95                 :     }
      96                 :     else {
      97            2552 :       cur = aNext;
      98                 :     }
      99                 : 
     100            5428 :     return cur;
     101                 :   }
     102                 : 
     103                 :   enum FindInStringBehavior {
     104                 :     eFindOnBoundary,
     105                 :     eFindAnywhere
     106                 :   };
     107                 : 
     108                 :   /**
     109                 :    * findAnywhere and findOnBoundary do almost the same thing, so it's natural
     110                 :    * to implement them in terms of a single function.  They're both
     111                 :    * performance-critical functions, however, and checking aBehavior makes them
     112                 :    * a bit slower.  Our solution is to define findInString as NS_ALWAYS_INLINE
     113                 :    * and rely on the compiler to optimize out the aBehavior check.
     114                 :    *
     115                 :    * @param aToken
     116                 :    *        The token we're searching for
     117                 :    * @param aSourceString
     118                 :    *        The string in which we're searching
     119                 :    * @param aBehavior
     120                 :    *        eFindOnBoundary if we should only consider matchines which occur on
     121                 :    *        word boundaries, or eFindAnywhere if we should consider matches
     122                 :    *        which appear anywhere.
     123                 :    *
     124                 :    * @return true if aToken was found in aSourceString, false otherwise.
     125                 :    */
     126                 :   static
     127                 :   NS_ALWAYS_INLINE bool
     128               0 :   findInString(const nsDependentCSubstring &aToken,
     129                 :                const nsACString &aSourceString,
     130                 :                FindInStringBehavior aBehavior)
     131                 :   {
     132                 :     // CaseInsensitiveUTF8CharsEqual assumes that there's at least one byte in
     133                 :     // the both strings, so don't pass an empty token here.
     134            4910 :     NS_PRECONDITION(!aToken.IsEmpty(), "Don't search for an empty token!");
     135                 : 
     136                 :     // We cannot match anything if there is nothing to search.
     137            4910 :     if (aSourceString.IsEmpty()) {
     138             944 :       return false;
     139                 :     }
     140                 : 
     141            3966 :     const_char_iterator tokenStart(aToken.BeginReading()),
     142            3966 :                         tokenEnd(aToken.EndReading()),
     143            3966 :                         sourceStart(aSourceString.BeginReading()),
     144            3966 :                         sourceEnd(aSourceString.EndReading());
     145                 : 
     146           19207 :     do {
     147                 :       // We are on a word boundary (if aBehavior == eFindOnBoundary).  See if
     148                 :       // aToken matches sourceStart.
     149                 : 
     150                 :       // Check whether the first character in the token matches the character
     151                 :       // at sourceStart.  At the same time, get a pointer to the next character
     152                 :       // in both the token and the source.
     153                 :       const_char_iterator sourceNext, tokenCur;
     154                 :       bool error;
     155           20427 :       if (CaseInsensitiveUTF8CharsEqual(sourceStart, tokenStart,
     156                 :                                         sourceEnd, tokenEnd,
     157                 :                                         &sourceNext, &tokenCur, &error)) {
     158                 : 
     159                 :         // We don't need to check |error| here -- if
     160                 :         // CaseInsensitiveUTF8CharCompare encounters an error, it'll also
     161                 :         // return false and we'll catch the error outside the if.
     162                 : 
     163            1583 :         const_char_iterator sourceCur = sourceNext;
     164               0 :         while (true) {
     165            4631 :           if (tokenCur >= tokenEnd) {
     166                 :             // We matched the whole token!
     167            1199 :             return true;
     168                 :           }
     169                 : 
     170            3432 :           if (sourceCur >= sourceEnd) {
     171                 :             // We ran into the end of source while matching a token.  This
     172                 :             // means we'll never find the token we're looking for.
     173              21 :             return false;
     174                 :           }
     175                 : 
     176            3411 :           if (!CaseInsensitiveUTF8CharsEqual(sourceCur, tokenCur,
     177                 :                                              sourceEnd, tokenEnd,
     178            3411 :                                              &sourceCur, &tokenCur, &error)) {
     179                 :             // sourceCur doesn't match tokenCur (or there's an error), so break
     180                 :             // out of this loop.
     181                 :             break;
     182                 :           }
     183                 :         }
     184                 :       }
     185                 : 
     186                 :       // If something went wrong above, get out of here!
     187           19207 :       if (NS_UNLIKELY(error)) {
     188               0 :         return false;
     189                 :       }
     190                 : 
     191                 :       // We didn't match the token.  If we're searching for matches on word
     192                 :       // boundaries, skip to the next word boundary.  Otherwise, advance
     193                 :       // forward one character, using the sourceNext pointer we saved earlier.
     194                 : 
     195           19207 :       if (aBehavior == eFindOnBoundary) {
     196            5428 :         sourceStart = nextWordBoundary(sourceStart, sourceNext, sourceEnd);
     197                 :       }
     198                 :       else {
     199           13779 :         sourceStart = sourceNext;
     200                 :       }
     201                 : 
     202                 :     } while (sourceStart < sourceEnd);
     203                 : 
     204            2746 :     return false;
     205                 :   }
     206                 : 
     207                 : } // End anonymous namespace
     208                 : 
     209                 : namespace mozilla {
     210                 : namespace places {
     211                 : 
     212                 : ////////////////////////////////////////////////////////////////////////////////
     213                 : //// AutoComplete Matching Function
     214                 : 
     215                 :   //////////////////////////////////////////////////////////////////////////////
     216                 :   //// MatchAutoCompleteFunction
     217                 : 
     218                 :   /* static */
     219                 :   nsresult
     220             267 :   MatchAutoCompleteFunction::create(mozIStorageConnection *aDBConn)
     221                 :   {
     222                 :     nsRefPtr<MatchAutoCompleteFunction> function =
     223             534 :       new MatchAutoCompleteFunction();
     224                 : 
     225                 :     nsresult rv = aDBConn->CreateFunction(
     226             267 :       NS_LITERAL_CSTRING("autocomplete_match"), kArgIndexLength, function
     227             267 :     );
     228             267 :     NS_ENSURE_SUCCESS(rv, rv);
     229                 : 
     230             267 :     return NS_OK;
     231                 :   }
     232                 : 
     233                 :   /* static */
     234                 :   void
     235            1959 :   MatchAutoCompleteFunction::fixupURISpec(const nsCString &aURISpec,
     236                 :                                           PRInt32 aMatchBehavior,
     237                 :                                           nsCString &_fixedSpec)
     238                 :   {
     239            3918 :     nsCString unescapedSpec;
     240                 :     (void)NS_UnescapeURL(aURISpec, esc_SkipControl | esc_AlwaysCopy,
     241            1959 :                          unescapedSpec);
     242                 : 
     243                 :     // If this unescaped string is valid UTF-8, we'll use it.  Otherwise,
     244                 :     // we will simply use our original string.
     245            1959 :     NS_ASSERTION(_fixedSpec.IsEmpty(),
     246                 :                  "Passing a non-empty string as an out parameter!");
     247            1959 :     if (IsUTF8(unescapedSpec))
     248            1957 :       _fixedSpec.Assign(unescapedSpec);
     249                 :     else
     250               2 :       _fixedSpec.Assign(aURISpec);
     251                 : 
     252            1959 :     if (aMatchBehavior == mozIPlacesAutoComplete::MATCH_ANYWHERE_UNMODIFIED)
     253                 :       return;
     254                 : 
     255            1724 :     if (StringBeginsWith(_fixedSpec, NS_LITERAL_CSTRING("http://")))
     256            1568 :       _fixedSpec.Cut(0, 7);
     257             156 :     else if (StringBeginsWith(_fixedSpec, NS_LITERAL_CSTRING("https://")))
     258              70 :       _fixedSpec.Cut(0, 8);
     259              86 :     else if (StringBeginsWith(_fixedSpec, NS_LITERAL_CSTRING("ftp://")))
     260              64 :       _fixedSpec.Cut(0, 6);
     261                 : 
     262            1724 :     if (StringBeginsWith(_fixedSpec, NS_LITERAL_CSTRING("www.")))
     263              67 :       _fixedSpec.Cut(0, 4);
     264                 :   }
     265                 : 
     266                 :   /* static */
     267                 :   bool
     268            2533 :   MatchAutoCompleteFunction::findAnywhere(const nsDependentCSubstring &aToken,
     269                 :                                           const nsACString &aSourceString)
     270                 :   {
     271                 :     // We can't use FindInReadable here; it works only for ASCII.
     272                 : 
     273                 :     return findInString(aToken, aSourceString, eFindAnywhere);
     274                 :   }
     275                 : 
     276                 :   /* static */
     277                 :   bool
     278            2377 :   MatchAutoCompleteFunction::findOnBoundary(const nsDependentCSubstring &aToken,
     279                 :                                             const nsACString &aSourceString)
     280                 :   {
     281                 :     return findInString(aToken, aSourceString, eFindOnBoundary);
     282                 :   }
     283                 : 
     284                 :   /* static */
     285                 :   bool
     286              29 :   MatchAutoCompleteFunction::findBeginning(const nsDependentCSubstring &aToken,
     287                 :                                            const nsACString &aSourceString)
     288                 :   {
     289              29 :     NS_PRECONDITION(!aToken.IsEmpty(), "Don't search for an empty token!");
     290                 : 
     291                 :     // We can't use StringBeginsWith here, unfortunately.  Although it will
     292                 :     // happily take a case-insensitive UTF8 comparator, it eventually calls
     293                 :     // nsACString::Equals, which checks that the two strings contain the same
     294                 :     // number of bytes before calling the comparator.  Two characters may be
     295                 :     // case-insensitively equal while taking up different numbers of bytes, so
     296                 :     // this is not what we want.
     297                 : 
     298              29 :     const_char_iterator tokenStart(aToken.BeginReading()),
     299              29 :                         tokenEnd(aToken.EndReading()),
     300              29 :                         sourceStart(aSourceString.BeginReading()),
     301              29 :                         sourceEnd(aSourceString.EndReading());
     302                 : 
     303                 :     bool dummy;
     304             209 :     while (sourceStart < sourceEnd &&
     305                 :            CaseInsensitiveUTF8CharsEqual(sourceStart, tokenStart,
     306                 :                                          sourceEnd, tokenEnd,
     307             180 :                                          &sourceStart, &tokenStart, &dummy)) {
     308                 : 
     309                 :       // We found the token!
     310             170 :       if (tokenStart >= tokenEnd) {
     311              13 :         return true;
     312                 :       }
     313                 :     }
     314                 : 
     315                 :     // We don't need to check CaseInsensitiveUTF8CharsEqual's error condition
     316                 :     // (stored in |dummy|), since the function will return false if it
     317                 :     // encounters an error.
     318                 : 
     319              16 :     return false;
     320                 :   }
     321                 : 
     322                 :   /* static */
     323                 :   MatchAutoCompleteFunction::searchFunctionPtr
     324            1959 :   MatchAutoCompleteFunction::getSearchFunction(PRInt32 aBehavior)
     325                 :   {
     326            1959 :     switch (aBehavior) {
     327                 :       case mozIPlacesAutoComplete::MATCH_ANYWHERE:
     328                 :       case mozIPlacesAutoComplete::MATCH_ANYWHERE_UNMODIFIED:
     329             999 :         return findAnywhere;
     330                 :       case mozIPlacesAutoComplete::MATCH_BEGINNING:
     331              17 :         return findBeginning;
     332                 :       case mozIPlacesAutoComplete::MATCH_BOUNDARY:
     333                 :       default:
     334             943 :         return findOnBoundary;
     335                 :     };
     336                 :   }
     337                 : 
     338            2643 :   NS_IMPL_THREADSAFE_ISUPPORTS1(
     339                 :     MatchAutoCompleteFunction,
     340                 :     mozIStorageFunction
     341                 :   )
     342                 : 
     343                 :   //////////////////////////////////////////////////////////////////////////////
     344                 :   //// mozIStorageFunction
     345                 : 
     346                 :   NS_IMETHODIMP
     347            2055 :   MatchAutoCompleteFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
     348                 :                                             nsIVariant **_result)
     349                 :   {
     350                 :     // Macro to make the code a bit cleaner and easier to read.  Operates on
     351                 :     // searchBehavior.
     352            2055 :     PRInt32 searchBehavior = aArguments->AsInt32(kArgIndexSearchBehavior);
     353                 :     #define HAS_BEHAVIOR(aBitName) \
     354                 :       (searchBehavior & mozIPlacesAutoComplete::BEHAVIOR_##aBitName)
     355                 : 
     356            4110 :     nsCAutoString searchString;
     357            2055 :     (void)aArguments->GetUTF8String(kArgSearchString, searchString);
     358            4110 :     nsCString url;
     359            2055 :     (void)aArguments->GetUTF8String(kArgIndexURL, url);
     360                 : 
     361            2055 :     PRInt32 matchBehavior = aArguments->AsInt32(kArgIndexMatchBehavior);
     362                 : 
     363                 :     // We only want to filter javascript: URLs if we are not supposed to search
     364                 :     // for them, and the search does not start with "javascript:".
     365           13194 :     if (matchBehavior != mozIPlacesAutoComplete::MATCH_ANYWHERE_UNMODIFIED &&
     366            1820 :         !HAS_BEHAVIOR(JAVASCRIPT) &&
     367            5695 :         !StringBeginsWith(searchString, NS_LITERAL_CSTRING("javascript:")) &&
     368            5679 :         StringBeginsWith(url, NS_LITERAL_CSTRING("javascript:"))) {
     369               6 :       NS_ADDREF(*_result = new IntegerVariant(0));
     370               6 :       return NS_OK;
     371                 :     }
     372                 : 
     373            2049 :     PRInt32 visitCount = aArguments->AsInt32(kArgIndexVisitCount);
     374            2049 :     bool typed = aArguments->AsInt32(kArgIndexTyped) ? true : false;
     375            2049 :     bool bookmark = aArguments->AsInt32(kArgIndexBookmark) ? true : false;
     376            4098 :     nsCAutoString tags;
     377            2049 :     (void)aArguments->GetUTF8String(kArgIndexTags, tags);
     378            2049 :     PRInt32 openPageCount = aArguments->AsInt32(kArgIndexOpenPageCount);
     379                 : 
     380                 :     // Make sure we match all the filter requirements.  If a given restriction
     381                 :     // is active, make sure the corresponding condition is not true.
     382                 :     bool matches = !(
     383                 :       (HAS_BEHAVIOR(HISTORY) && visitCount == 0) ||
     384                 :       (HAS_BEHAVIOR(TYPED) && !typed) ||
     385                 :       (HAS_BEHAVIOR(BOOKMARK) && !bookmark) ||
     386              52 :       (HAS_BEHAVIOR(TAG) && tags.IsVoid()) ||
     387                 :       (HAS_BEHAVIOR(OPENPAGE) && openPageCount == 0)
     388            2101 :     );
     389            2049 :     if (!matches) {
     390              90 :       NS_ADDREF(*_result = new IntegerVariant(0));
     391              90 :       return NS_OK;
     392                 :     }
     393                 : 
     394                 :     // Obtain our search function.
     395            1959 :     searchFunctionPtr searchFunction = getSearchFunction(matchBehavior);
     396                 : 
     397                 :     // Clean up our URI spec and prepare it for searching.
     398            3918 :     nsCString fixedURI;
     399            1959 :     fixupURISpec(url, matchBehavior, fixedURI);
     400                 : 
     401            3918 :     nsCAutoString title;
     402            1959 :     (void)aArguments->GetUTF8String(kArgIndexTitle, title);
     403                 : 
     404                 :     // Determine if every token matches either the bookmark title, tags, page
     405                 :     // title, or page URL.
     406            1959 :     nsCWhitespaceTokenizer tokenizer(searchString);
     407            6048 :     while (matches && tokenizer.hasMoreTokens()) {
     408            4260 :       const nsDependentCSubstring &token = tokenizer.nextToken();
     409                 : 
     410            2130 :       if (HAS_BEHAVIOR(TITLE) && HAS_BEHAVIOR(URL)) {
     411              36 :         matches = (searchFunction(token, title) || searchFunction(token, tags)) &&
     412              36 :                   searchFunction(token, fixedURI);
     413                 :       }
     414            2106 :       else if (HAS_BEHAVIOR(TITLE)) {
     415              90 :         matches = searchFunction(token, title) || searchFunction(token, tags);
     416                 :       }
     417            2016 :       else if (HAS_BEHAVIOR(URL)) {
     418             143 :         matches = searchFunction(token, fixedURI);
     419                 :       }
     420                 :       else {
     421            1873 :         matches = searchFunction(token, title) ||
     422            1579 :                   searchFunction(token, tags) ||
     423            3452 :                   searchFunction(token, fixedURI);
     424                 :       }
     425                 :     }
     426                 : 
     427            1959 :     NS_ADDREF(*_result = new IntegerVariant(matches ? 1 : 0));
     428            1959 :     return NS_OK;
     429                 :     #undef HAS_BEHAVIOR
     430                 :   }
     431                 : 
     432                 : 
     433                 : ////////////////////////////////////////////////////////////////////////////////
     434                 : //// Frecency Calculation Function
     435                 : 
     436                 :   //////////////////////////////////////////////////////////////////////////////
     437                 :   //// CalculateFrecencyFunction
     438                 : 
     439                 :   /* static */
     440                 :   nsresult
     441             267 :   CalculateFrecencyFunction::create(mozIStorageConnection *aDBConn)
     442                 :   {
     443                 :     nsRefPtr<CalculateFrecencyFunction> function =
     444             534 :       new CalculateFrecencyFunction();
     445                 : 
     446                 :     nsresult rv = aDBConn->CreateFunction(
     447             267 :       NS_LITERAL_CSTRING("calculate_frecency"), 1, function
     448             267 :     );
     449             267 :     NS_ENSURE_SUCCESS(rv, rv);
     450                 : 
     451             267 :     return NS_OK;
     452                 :   }
     453                 : 
     454            2643 :   NS_IMPL_THREADSAFE_ISUPPORTS1(
     455                 :     CalculateFrecencyFunction,
     456                 :     mozIStorageFunction
     457                 :   )
     458                 : 
     459                 :   //////////////////////////////////////////////////////////////////////////////
     460                 :   //// mozIStorageFunction
     461                 : 
     462                 :   NS_IMETHODIMP
     463            4921 :   CalculateFrecencyFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
     464                 :                                             nsIVariant **_result)
     465                 :   {
     466                 :     // Fetch arguments.  Use default values if they were omitted.
     467                 :     PRUint32 numEntries;
     468            4921 :     nsresult rv = aArguments->GetNumEntries(&numEntries);
     469            4921 :     NS_ENSURE_SUCCESS(rv, rv);
     470            4921 :     NS_ASSERTION(numEntries > 0, "unexpected number of arguments");
     471                 : 
     472            9842 :     Telemetry::AutoTimer<Telemetry::PLACES_FRECENCY_CALC_TIME_MS> timer;
     473                 : 
     474            4921 :     PRInt64 pageId = aArguments->AsInt64(0);
     475            4921 :     PRInt32 typed = numEntries > 1 ? aArguments->AsInt32(1) : 0;
     476            4921 :     PRInt32 fullVisitCount = numEntries > 2 ? aArguments->AsInt32(2) : 0;
     477            4921 :     PRInt64 bookmarkId = numEntries > 3 ? aArguments->AsInt64(3) : 0;
     478            4921 :     PRInt32 visitCount = 0;
     479            4921 :     PRInt32 hidden = 0;
     480            4921 :     PRInt32 isQuery = 0;
     481            4921 :     float pointsForSampledVisits = 0.0;
     482                 : 
     483                 :     // This is a const version of the history object for thread-safety.
     484            4921 :     const nsNavHistory* history = nsNavHistory::GetConstHistoryService();
     485            4921 :     NS_ENSURE_STATE(history);
     486            9842 :     nsRefPtr<Database> DB = Database::GetDatabase();
     487            4921 :     NS_ENSURE_STATE(DB);
     488                 : 
     489            4921 :     if (pageId > 0) {
     490                 :       // The page is already in the database, and we can fetch current
     491                 :       // params from the database.
     492                 :       nsRefPtr<mozIStorageStatement> getPageInfo = DB->GetStatement(
     493                 :         "SELECT typed, hidden, visit_count, "
     494                 :           "(SELECT count(*) FROM moz_historyvisits WHERE place_id = :page_id), "
     495                 :           "EXISTS (SELECT 1 FROM moz_bookmarks WHERE fk = :page_id), "
     496                 :           "(url > 'place:' AND url < 'place;') "
     497                 :         "FROM moz_places "
     498                 :         "WHERE id = :page_id "
     499            9842 :       );
     500            4921 :       NS_ENSURE_STATE(getPageInfo);
     501            9842 :       mozStorageStatementScoper infoScoper(getPageInfo);
     502                 : 
     503            4921 :       rv = getPageInfo->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), pageId);
     504            4921 :       NS_ENSURE_SUCCESS(rv, rv);
     505                 : 
     506                 :       bool hasResult;
     507            4921 :       rv = getPageInfo->ExecuteStep(&hasResult);
     508            4921 :       NS_ENSURE_SUCCESS(rv, rv);
     509            4921 :       NS_ENSURE_TRUE(hasResult, NS_ERROR_UNEXPECTED);
     510            4921 :       rv = getPageInfo->GetInt32(0, &typed);
     511            4921 :       NS_ENSURE_SUCCESS(rv, rv);
     512            4921 :       rv = getPageInfo->GetInt32(1, &hidden);
     513            4921 :       NS_ENSURE_SUCCESS(rv, rv);
     514            4921 :       rv = getPageInfo->GetInt32(2, &visitCount);
     515            4921 :       NS_ENSURE_SUCCESS(rv, rv);
     516            4921 :       rv = getPageInfo->GetInt32(3, &fullVisitCount);
     517            4921 :       NS_ENSURE_SUCCESS(rv, rv);
     518            4921 :       rv = getPageInfo->GetInt64(4, &bookmarkId);
     519            4921 :       NS_ENSURE_SUCCESS(rv, rv);
     520            4921 :       rv = getPageInfo->GetInt32(5, &isQuery);
     521            4921 :       NS_ENSURE_SUCCESS(rv, rv);
     522                 : 
     523                 :       // NOTE: This is not limited to visits with "visit_type NOT IN (0,4,7,8)"
     524                 :       // because otherwise it would not return any visit for those transitions
     525                 :       // causing an incorrect frecency, see CalculateFrecencyInternal().
     526                 :       // In case of a temporary or permanent redirect, calculate the frecency
     527                 :       // as if the original page was visited.
     528                 :       // Get a sample of the last visits to the page, to calculate its weight.
     529                 :       nsCOMPtr<mozIStorageStatement> getVisits = DB->GetStatement(
     530            4921 :         NS_LITERAL_CSTRING(
     531                 :           "/* do not warn (bug 659740 - SQLite may ignore index if few visits exist) */"
     532                 :           "SELECT "
     533                 :             "ROUND((strftime('%s','now','localtime','utc') - v.visit_date/1000000)/86400), "
     534                 :             "IFNULL(r.visit_type, v.visit_type), "
     535                 :             "v.visit_date "
     536                 :           "FROM moz_historyvisits v "
     537                 :           "LEFT JOIN moz_historyvisits r ON r.id = v.from_visit AND v.visit_type BETWEEN "
     538                 :         ) + nsPrintfCString("%d AND %d ", nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
     539            9842 :                                           nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY) +
     540           14763 :         NS_LITERAL_CSTRING(
     541                 :           "WHERE v.place_id = :page_id "
     542                 :           "ORDER BY v.visit_date DESC "
     543                 :         )
     544            9842 :       );
     545            4921 :       NS_ENSURE_STATE(getVisits);
     546            9842 :       mozStorageStatementScoper visitsScoper(getVisits);
     547                 : 
     548            4921 :       rv = getVisits->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), pageId);
     549            4921 :       NS_ENSURE_SUCCESS(rv, rv);
     550                 : 
     551                 :       // Fetch only a limited number of recent visits.
     552            4921 :       PRInt32 numSampledVisits = 0;
     553           26614 :       for (PRInt32 maxVisits = history->GetNumVisitsForFrecency();
     554                 :            numSampledVisits < maxVisits &&
     555           13135 :            NS_SUCCEEDED(getVisits->ExecuteStep(&hasResult)) && hasResult;
     556                 :            numSampledVisits++) {
     557                 :         PRInt32 visitType;
     558            8558 :         rv = getVisits->GetInt32(1, &visitType);
     559            8558 :         NS_ENSURE_SUCCESS(rv, rv);
     560            8558 :         PRInt32 bonus = history->GetFrecencyTransitionBonus(visitType, true);
     561                 : 
     562                 :         // Always add the bookmark visit bonus.
     563            8558 :         if (bookmarkId) {
     564             383 :           bonus += history->GetFrecencyTransitionBonus(nsINavHistoryService::TRANSITION_BOOKMARK, true);
     565                 :         }
     566                 : 
     567                 :         // If bonus was zero, we can skip the work to determine the weight.
     568            8558 :         if (bonus) {
     569            6316 :           PRInt32 ageInDays = getVisits->AsInt32(0);
     570            6316 :           PRInt32 weight = history->GetFrecencyAgedWeight(ageInDays);
     571            6316 :           pointsForSampledVisits += (float)(weight * (bonus / 100.0));
     572                 :         }
     573                 :       }
     574                 : 
     575                 :       // If we found some visits for this page, use the calculated weight.
     576            4921 :       if (numSampledVisits) {
     577                 :         // fix for bug #412219
     578            2544 :         if (!pointsForSampledVisits) {
     579                 :           // For URIs with zero points in the sampled recent visits
     580                 :           // but "browsing" type visits outside the sampling range, set
     581                 :           // frecency to -visit_count, so they're still shown in autocomplete.
     582             335 :           NS_ADDREF(*_result = new IntegerVariant(-visitCount));
     583                 :         }
     584                 :         else {
     585                 :           // Estimate frecency using the last few visits.
     586                 :           // Use ceilf() so that we don't round down to 0, which
     587                 :           // would cause us to completely ignore the place during autocomplete.
     588            2209 :           NS_ADDREF(*_result = new IntegerVariant((PRInt32) ceilf(fullVisitCount * ceilf(pointsForSampledVisits) / numSampledVisits)));
     589                 :         }
     590                 : 
     591            2544 :         return NS_OK;
     592                 :       }
     593                 :     }
     594                 : 
     595                 :     // This page is unknown or has no visits.  It could have just been added, so
     596                 :     // use passed in or default values.
     597                 : 
     598                 :     // The code below works well for guessing the frecency on import, and we'll
     599                 :     // correct later once we have visits.
     600                 :     // TODO: What if we don't have visits and we never visit?  We could end up
     601                 :     // with a really high value that keeps coming up in ac results? Should we
     602                 :     // only do this on import?  Have to figure it out.
     603            2377 :     PRInt32 bonus = 0;
     604                 : 
     605                 :     // Make it so something bookmarked and typed will have a higher frecency
     606                 :     // than something just typed or just bookmarked.
     607            2377 :     if (bookmarkId && !isQuery) {
     608             705 :       bonus += history->GetFrecencyTransitionBonus(nsINavHistoryService::TRANSITION_BOOKMARK, false);;
     609                 :       // For unvisited bookmarks, produce a non-zero frecency, so that they show
     610                 :       // up in URL bar autocomplete.
     611             705 :       fullVisitCount = 1;
     612                 :     }
     613                 : 
     614            2377 :     if (typed) {
     615             467 :       bonus += history->GetFrecencyTransitionBonus(nsINavHistoryService::TRANSITION_TYPED, false);
     616                 :     }
     617                 : 
     618                 :     // Assume "now" as our ageInDays, so use the first bucket.
     619            2377 :     pointsForSampledVisits = history->GetFrecencyBucketWeight(1) * (bonus / (float)100.0); 
     620                 : 
     621                 :     // use ceilf() so that we don't round down to 0, which
     622                 :     // would cause us to completely ignore the place during autocomplete
     623            2377 :     NS_ADDREF(*_result = new IntegerVariant((PRInt32) ceilf(fullVisitCount * ceilf(pointsForSampledVisits))));
     624                 : 
     625            2377 :     return NS_OK;
     626                 :   }
     627                 : 
     628                 : ////////////////////////////////////////////////////////////////////////////////
     629                 : //// GUID Creation Function
     630                 : 
     631                 :   //////////////////////////////////////////////////////////////////////////////
     632                 :   //// GenerateGUIDFunction
     633                 : 
     634                 :   /* static */
     635                 :   nsresult
     636             267 :   GenerateGUIDFunction::create(mozIStorageConnection *aDBConn)
     637                 :   {
     638                 : #if defined(XP_OS2)
     639                 :     // We need this service to be initialized on the main thread because it is
     640                 :     // not threadsafe.  We are about to use it asynchronously, so initialize it
     641                 :     // now.
     642                 :     nsCOMPtr<nsIRandomGenerator> rg =
     643                 :       do_GetService("@mozilla.org/security/random-generator;1");
     644                 :     NS_ENSURE_STATE(rg);
     645                 : #endif
     646                 : 
     647             534 :     nsRefPtr<GenerateGUIDFunction> function = new GenerateGUIDFunction();
     648                 :     nsresult rv = aDBConn->CreateFunction(
     649             267 :       NS_LITERAL_CSTRING("generate_guid"), 0, function
     650             267 :     );
     651             267 :     NS_ENSURE_SUCCESS(rv, rv);
     652                 : 
     653             267 :     return NS_OK;
     654                 :   }
     655                 : 
     656            2643 :   NS_IMPL_THREADSAFE_ISUPPORTS1(
     657                 :     GenerateGUIDFunction,
     658                 :     mozIStorageFunction
     659                 :   )
     660                 : 
     661                 :   //////////////////////////////////////////////////////////////////////////////
     662                 :   //// mozIStorageFunction
     663                 : 
     664                 :   NS_IMETHODIMP
     665            5124 :   GenerateGUIDFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
     666                 :                                        nsIVariant **_result)
     667                 :   {
     668           10248 :     nsCAutoString guid;
     669            5124 :     nsresult rv = GenerateGUID(guid);
     670            5124 :     NS_ENSURE_SUCCESS(rv, rv);
     671                 : 
     672            5124 :     NS_ADDREF(*_result = new UTF8TextVariant(guid));
     673            5124 :     return NS_OK;
     674                 :   }
     675                 : 
     676                 : ////////////////////////////////////////////////////////////////////////////////
     677                 : //// Get Unreversed Host Function
     678                 : 
     679                 :   //////////////////////////////////////////////////////////////////////////////
     680                 :   //// GetUnreversedHostFunction
     681                 : 
     682                 :   /* static */
     683                 :   nsresult
     684             267 :   GetUnreversedHostFunction::create(mozIStorageConnection *aDBConn)
     685                 :   {
     686             534 :     nsRefPtr<GetUnreversedHostFunction> function = new GetUnreversedHostFunction();
     687                 :     nsresult rv = aDBConn->CreateFunction(
     688             267 :       NS_LITERAL_CSTRING("get_unreversed_host"), 1, function
     689             267 :     );
     690             267 :     NS_ENSURE_SUCCESS(rv, rv);
     691                 : 
     692             267 :     return NS_OK;
     693                 :   }
     694                 : 
     695            2643 :   NS_IMPL_THREADSAFE_ISUPPORTS1(
     696                 :     GetUnreversedHostFunction,
     697                 :     mozIStorageFunction
     698                 :   )
     699                 : 
     700                 :   //////////////////////////////////////////////////////////////////////////////
     701                 :   //// mozIStorageFunction
     702                 : 
     703                 :   NS_IMETHODIMP
     704           21686 :   GetUnreversedHostFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
     705                 :                                             nsIVariant **_result)
     706                 :   {
     707                 :     // Must have non-null function arguments.
     708           21686 :     MOZ_ASSERT(aArguments);
     709                 : 
     710           43372 :     nsAutoString src;
     711           21686 :     aArguments->GetString(0, src);
     712                 : 
     713                 :     nsCOMPtr<nsIWritableVariant> result =
     714           43372 :       do_CreateInstance("@mozilla.org/variant;1");
     715           21686 :     NS_ENSURE_STATE(result);
     716                 : 
     717           21686 :     if (src.Length()>1) {
     718           21608 :       src.Truncate(src.Length() - 1);
     719           43216 :       nsAutoString dest;
     720           21608 :       ReverseString(src, dest);
     721           21608 :       result->SetAsAString(dest);
     722                 :     }
     723                 :     else {
     724              78 :       result->SetAsAString(EmptyString());
     725                 :     }
     726           21686 :     NS_ADDREF(*_result = result);
     727           21686 :     return NS_OK;
     728                 :   }
     729                 : 
     730                 : ////////////////////////////////////////////////////////////////////////////////
     731                 : //// Fixup URL Function
     732                 : 
     733                 :   //////////////////////////////////////////////////////////////////////////////
     734                 :   //// FixupURLFunction
     735                 : 
     736                 :   /* static */
     737                 :   nsresult
     738             267 :   FixupURLFunction::create(mozIStorageConnection *aDBConn)
     739                 :   {
     740             534 :     nsRefPtr<FixupURLFunction> function = new FixupURLFunction();
     741                 :     nsresult rv = aDBConn->CreateFunction(
     742             267 :       NS_LITERAL_CSTRING("fixup_url"), 1, function
     743             267 :     );
     744             267 :     NS_ENSURE_SUCCESS(rv, rv);
     745                 : 
     746             267 :     return NS_OK;
     747                 :   }
     748                 : 
     749            2643 :   NS_IMPL_THREADSAFE_ISUPPORTS1(
     750                 :     FixupURLFunction,
     751                 :     mozIStorageFunction
     752                 :   )
     753                 : 
     754                 :   //////////////////////////////////////////////////////////////////////////////
     755                 :   //// mozIStorageFunction
     756                 : 
     757                 :   NS_IMETHODIMP
     758           11515 :   FixupURLFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
     759                 :                                    nsIVariant **_result)
     760                 :   {
     761                 :     // Must have non-null function arguments.
     762           11515 :     MOZ_ASSERT(aArguments);
     763                 : 
     764           23030 :     nsAutoString src;
     765           11515 :     aArguments->GetString(0, src);
     766                 : 
     767                 :     nsCOMPtr<nsIWritableVariant> result =
     768           23030 :       do_CreateInstance("@mozilla.org/variant;1");
     769           11515 :     NS_ENSURE_STATE(result);
     770                 : 
     771                 :     // Remove common URL hostname prefixes
     772           11515 :     if (StringBeginsWith(src, NS_LITERAL_STRING("www."))) {
     773            2131 :       src.Cut(0, 4);
     774                 :     }
     775                 : 
     776           11515 :     result->SetAsAString(src);
     777           11515 :     NS_ADDREF(*_result = result);
     778           11515 :     return NS_OK;
     779                 :   }
     780                 : 
     781                 : } // namespace places
     782                 : } // namespace mozilla

Generated by: LCOV version 1.7