Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/toolkit/components/places/SQLFunctions.cpp
Line
Count
Source (jump to first uncovered line)
1
/* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
2
 * This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "mozilla/storage.h"
7
#include "mozilla/dom/URLSearchParams.h"
8
#include "nsString.h"
9
#include "nsUnicharUtils.h"
10
#include "nsWhitespaceTokenizer.h"
11
#include "nsEscape.h"
12
#include "mozIPlacesAutoComplete.h"
13
#include "SQLFunctions.h"
14
#include "nsMathUtils.h"
15
#include "nsUnicodeProperties.h"
16
#include "nsUTF8Utils.h"
17
#include "nsINavHistoryService.h"
18
#include "nsPrintfCString.h"
19
#include "nsNavHistory.h"
20
#include "mozilla/Likely.h"
21
#include "nsVariant.h"
22
23
// Maximum number of chars to search through.
24
// MatchAutoCompleteFunction won't look for matches over this threshold.
25
0
#define MAX_CHARS_TO_SEARCH_THROUGH 255
26
27
using namespace mozilla::storage;
28
29
////////////////////////////////////////////////////////////////////////////////
30
//// Anonymous Helpers
31
32
namespace {
33
34
  typedef nsACString::const_char_iterator const_char_iterator;
35
  typedef nsACString::size_type size_type;
36
  typedef nsACString::char_type char_type;
37
38
  /**
39
   * Scan forward through UTF-8 text until the next potential character that
40
   * could match a given codepoint when lower-cased (false positives are okay).
41
   * This avoids having to actually parse the UTF-8 text, which is slow.
42
   *
43
   * @param aStart
44
   *        An iterator pointing to the first character position considered.
45
   * @param aEnd
46
   *        An interator pointing to past-the-end of the string.
47
   *
48
   * @return An iterator pointing to the first potential matching character
49
   *         within the range [aStart, aEnd).
50
   */
51
  static
52
  MOZ_ALWAYS_INLINE const_char_iterator
53
  nextSearchCandidate(const_char_iterator aStart,
54
                      const_char_iterator aEnd,
55
                      uint32_t aSearchFor)
56
0
  {
57
0
    const_char_iterator cur = aStart;
58
0
59
0
    // If the character we search for is ASCII, then we can scan until we find
60
0
    // it or its ASCII uppercase character, modulo the special cases
61
0
    // U+0130 LATIN CAPITAL LETTER I WITH DOT ABOVE and U+212A KELVIN SIGN
62
0
    // (which are the only non-ASCII characters that lower-case to ASCII ones).
63
0
    // Since false positives are okay, we approximate ASCII lower-casing by
64
0
    // bit-ORing with 0x20, for increased performance.
65
0
    //
66
0
    // If the character we search for is *not* ASCII, we can ignore everything
67
0
    // that is, since all ASCII characters lower-case to ASCII.
68
0
    //
69
0
    // Because of how UTF-8 uses high-order bits, this will never land us
70
0
    // in the middle of a codepoint.
71
0
    //
72
0
    // The assumptions about Unicode made here are verified in the test_casing
73
0
    // gtest.
74
0
    if (aSearchFor < 128) {
75
0
      // When searching for I or K, we pick out the first byte of the UTF-8
76
0
      // encoding of the corresponding special case character, and look for it
77
0
      // in the loop below.  For other characters we fall back to 0xff, which
78
0
      // is not a valid UTF-8 byte.
79
0
      unsigned char target = (unsigned char)(aSearchFor | 0x20);
80
0
      unsigned char special = 0xff;
81
0
      if (target == 'i' || target == 'k') {
82
0
        special = (target == 'i' ? 0xc4 : 0xe2);
83
0
      }
84
0
85
0
      while (cur < aEnd && (unsigned char)(*cur | 0x20) != target &&
86
0
          (unsigned char)*cur != special) {
87
0
        cur++;
88
0
      }
89
0
    } else {
90
0
      const_char_iterator cur = aStart;
91
0
      while (cur < aEnd && (unsigned char)(*cur) < 128) {
92
0
        cur++;
93
0
      }
94
0
    }
95
0
96
0
    return cur;
97
0
  }
98
99
  /**
100
   * Check whether a character position is on a word boundary of a UTF-8 string
101
   * (rather than within a word).  We define "within word" to be any position
102
   * between [a-zA-Z] and [a-z] -- this lets us match CamelCase words.
103
   * TODO: support non-latin alphabets.
104
   *
105
   * @param aPos
106
   *        An iterator pointing to the character position considered.  It must
107
   *        *not* be the first byte of a string.
108
   *
109
   * @return true if boundary, false otherwise.
110
   */
111
  static
112
  MOZ_ALWAYS_INLINE bool
113
0
  isOnBoundary(const_char_iterator aPos) {
114
0
    if ('a' <= *aPos && *aPos <= 'z') {
115
0
      char prev = *(aPos - 1) | 0x20;
116
0
      return !('a' <= prev && prev <= 'z');
117
0
    }
118
0
    return true;
119
0
  }
120
121
  /**
122
   * Check whether a token string matches a particular position of a source
123
   * string, case insensitively.
124
   *
125
   * @param aTokenStart
126
   *        An iterator pointing to the start of the token string.
127
   * @param aTokenEnd
128
   *        An iterator pointing past-the-end of the token string.
129
   * @param aSourceStart
130
   *        An iterator pointing to the position of source string to start
131
   *        matching at.
132
   * @param aSourceEnd
133
   *        An iterator pointing past-the-end of the source string.
134
   *
135
   * @return true if the string [aTokenStart, aTokenEnd) matches the start of
136
   *         the string [aSourceStart, aSourceEnd, false otherwise.
137
   */
138
  static
139
  MOZ_ALWAYS_INLINE bool
140
  stringMatch(const_char_iterator aTokenStart,
141
              const_char_iterator aTokenEnd,
142
              const_char_iterator aSourceStart,
143
              const_char_iterator aSourceEnd)
144
0
  {
145
0
    const_char_iterator tokenCur = aTokenStart, sourceCur = aSourceStart;
146
0
147
0
    while (tokenCur < aTokenEnd) {
148
0
      if (sourceCur >= aSourceEnd) {
149
0
        return false;
150
0
      }
151
0
152
0
      bool error;
153
0
      if (!CaseInsensitiveUTF8CharsEqual(sourceCur, tokenCur,
154
0
                                         aSourceEnd, aTokenEnd,
155
0
                                         &sourceCur, &tokenCur, &error)) {
156
0
        return false;
157
0
      }
158
0
    }
159
0
160
0
    return true;
161
0
  }
162
163
  enum FindInStringBehavior {
164
    eFindOnBoundary,
165
    eFindAnywhere
166
  };
167
168
  /**
169
   * Common implementation for findAnywhere and findOnBoundary.
170
   *
171
   * @param aToken
172
   *        The token we're searching for
173
   * @param aSourceString
174
   *        The string in which we're searching
175
   * @param aBehavior
176
   *        eFindOnBoundary if we should only consider matchines which occur on
177
   *        word boundaries, or eFindAnywhere if we should consider matches
178
   *        which appear anywhere.
179
   *
180
   * @return true if aToken was found in aSourceString, false otherwise.
181
   */
182
  static
183
  bool
184
  findInString(const nsDependentCSubstring &aToken,
185
               const nsACString &aSourceString,
186
               FindInStringBehavior aBehavior)
187
0
  {
188
0
    // GetLowerUTF8Codepoint assumes that there's at least one byte in
189
0
    // the string, so don't pass an empty token here.
190
0
    MOZ_ASSERT(!aToken.IsEmpty(), "Don't search for an empty token!");
191
0
192
0
    // We cannot match anything if there is nothing to search.
193
0
    if (aSourceString.IsEmpty()) {
194
0
      return false;
195
0
    }
196
0
197
0
    const_char_iterator tokenStart(aToken.BeginReading()),
198
0
                        tokenEnd(aToken.EndReading()),
199
0
                        tokenNext,
200
0
                        sourceStart(aSourceString.BeginReading()),
201
0
                        sourceEnd(aSourceString.EndReading()),
202
0
                        sourceCur(sourceStart),
203
0
                        sourceNext;
204
0
205
0
    uint32_t tokenFirstChar =
206
0
      GetLowerUTF8Codepoint(tokenStart, tokenEnd, &tokenNext);
207
0
    if (tokenFirstChar == uint32_t(-1)) {
208
0
      return false;
209
0
    }
210
0
211
0
    for (;;) {
212
0
      // Scan forward to the next viable candidate (if any).
213
0
      sourceCur = nextSearchCandidate(sourceCur, sourceEnd, tokenFirstChar);
214
0
      if (sourceCur == sourceEnd) {
215
0
        break;
216
0
      }
217
0
218
0
      // Check whether the first character in the token matches the character
219
0
      // at sourceCur.  At the same time, get a pointer to the next character
220
0
      // in the source.
221
0
      uint32_t sourceFirstChar =
222
0
        GetLowerUTF8Codepoint(sourceCur, sourceEnd, &sourceNext);
223
0
      if (sourceFirstChar == uint32_t(-1)) {
224
0
        return false;
225
0
      }
226
0
227
0
      if (sourceFirstChar == tokenFirstChar &&
228
0
          (aBehavior != eFindOnBoundary || sourceCur == sourceStart ||
229
0
              isOnBoundary(sourceCur)) &&
230
0
          stringMatch(tokenNext, tokenEnd, sourceNext, sourceEnd))
231
0
      {
232
0
        return true;
233
0
      }
234
0
235
0
      sourceCur = sourceNext;
236
0
    }
237
0
238
0
    return false;
239
0
  }
240
241
  static
242
  MOZ_ALWAYS_INLINE nsDependentCString
243
0
  getSharedUTF8String(mozIStorageValueArray* aValues, uint32_t aIndex) {
244
0
    uint32_t len;
245
0
    const char* str = aValues->AsSharedUTF8String(aIndex, &len);
246
0
    if (!str) {
247
0
      return nsDependentCString("", (uint32_t)0);
248
0
    }
249
0
    return nsDependentCString(str, len);
250
0
  }
251
252
  class MOZ_STACK_CLASS GetQueryParamIterator final :
253
    public URLParams::ForEachIterator
254
  {
255
  public:
256
    explicit GetQueryParamIterator(const nsCString& aParamName,
257
                                   nsVariant* aResult)
258
      : mParamName(aParamName)
259
      , mResult(aResult)
260
0
    {}
261
262
    bool URLParamsIterator(const nsAString& aName,
263
                           const nsAString& aValue) override
264
0
    {
265
0
      NS_ConvertUTF16toUTF8 name(aName);
266
0
      if (!mParamName.Equals(name)) {
267
0
        return true;
268
0
      }
269
0
      mResult->SetAsAString(aValue);
270
0
      return false;
271
0
    }
272
  private:
273
    const nsCString& mParamName;
274
    nsVariant* mResult;
275
  };
276
277
  /**
278
   * Gets the length of the prefix in a URI spec.  "Prefix" is defined to be the
279
   * scheme, colon, and, if present, two slashes.
280
   *
281
   * Examples:
282
   *
283
   *   http://example.com
284
   *   ~~~~~~~
285
   *   => length == 7
286
   *
287
   *   foo:example
288
   *   ~~~~
289
   *   => length == 4
290
   *
291
   *   not a spec
292
   *   => length == 0
293
   *
294
   * @param  aSpec
295
   *         A URI spec, or a string that may be a URI spec.
296
   * @return The length of the prefix in the spec.  If there isn't a prefix,
297
   *         returns 0.
298
   */
299
  static
300
  MOZ_ALWAYS_INLINE size_type
301
  getPrefixLength(const nsACString &aSpec)
302
0
  {
303
0
    // To keep the search bounded, look at 64 characters at most.  The longest
304
0
    // IANA schemes are ~30, so double that and round up to a nice number.
305
0
    size_type length = std::min(static_cast<size_type>(64), aSpec.Length());
306
0
    for (size_type i = 0; i < length; ++i) {
307
0
      if (aSpec[i] == static_cast<char_type>(':')) {
308
0
        // Found the ':'.  Now skip past "//", if present.
309
0
        if (i + 2 < aSpec.Length() &&
310
0
            aSpec[i + 1] == static_cast<char_type>('/') &&
311
0
            aSpec[i + 2] == static_cast<char_type>('/')) {
312
0
          i += 2;
313
0
        }
314
0
        return i + 1;
315
0
      }
316
0
    }
317
0
    return 0;
318
0
  }
319
320
  /**
321
   * Gets the index in a URI spec of the host and port substring and optionally
322
   * its length.
323
   *
324
   * Examples:
325
   *
326
   *   http://example.com/
327
   *          ~~~~~~~~~~~
328
   *   => index == 7, length == 11
329
   *
330
   *   http://example.com:8888/
331
   *          ~~~~~~~~~~~~~~~~
332
   *   => index == 7, length == 16
333
   *
334
   *   http://user:pass@example.com/
335
   *                    ~~~~~~~~~~~
336
   *   => index == 17, length == 11
337
   *
338
   *   foo:example
339
   *       ~~~~~~~
340
   *   => index == 4, length == 7
341
   *
342
   *   not a spec
343
   *   ~~~~~~~~~~
344
   *   => index == 0, length == 10
345
   *
346
   * @param  aSpec
347
   *         A URI spec, or a string that may be a URI spec.
348
   * @param  _hostAndPortLength
349
   *         The length of the host and port substring is returned through this
350
   *         param.  Pass null if you don't care.
351
   * @return The length of the host and port substring in the spec.  If aSpec
352
   *         doesn't look like a URI, then the entire aSpec is assumed to be a
353
   *         "host and port", and this returns 0, and _hostAndPortLength will be
354
   *         the length of aSpec.
355
   */
356
  static
357
  MOZ_ALWAYS_INLINE size_type
358
  indexOfHostAndPort(const nsACString &aSpec,
359
                     size_type *_hostAndPortLength)
360
0
  {
361
0
    size_type index = getPrefixLength(aSpec);
362
0
    size_type i = index;
363
0
    for (; i < aSpec.Length(); ++i) {
364
0
      // RFC 3986 (URIs): The origin ("authority") is terminated by '/', '?', or
365
0
      // '#' (or the end of the URI).
366
0
      if (aSpec[i] == static_cast<char_type>('/') ||
367
0
          aSpec[i] == static_cast<char_type>('?') ||
368
0
          aSpec[i] == static_cast<char_type>('#')) {
369
0
        break;
370
0
      }
371
0
      // RFC 3986: '@' marks the end of the userinfo component.
372
0
      if (aSpec[i] == static_cast<char_type>('@')) {
373
0
        index = i + 1;
374
0
      }
375
0
    }
376
0
    if (_hostAndPortLength) {
377
0
      *_hostAndPortLength = i - index;
378
0
    }
379
0
    return index;
380
0
  }
381
382
} // End anonymous namespace
383
384
namespace mozilla {
385
namespace places {
386
387
////////////////////////////////////////////////////////////////////////////////
388
//// AutoComplete Matching Function
389
390
  /* static */
391
  nsresult
392
  MatchAutoCompleteFunction::create(mozIStorageConnection *aDBConn)
393
0
  {
394
0
    RefPtr<MatchAutoCompleteFunction> function =
395
0
      new MatchAutoCompleteFunction();
396
0
397
0
    nsresult rv = aDBConn->CreateFunction(
398
0
      NS_LITERAL_CSTRING("autocomplete_match"), kArgIndexLength, function
399
0
    );
400
0
    NS_ENSURE_SUCCESS(rv, rv);
401
0
402
0
    return NS_OK;
403
0
  }
404
405
  /* static */
406
  nsDependentCSubstring
407
  MatchAutoCompleteFunction::fixupURISpec(const nsACString &aURISpec,
408
                                          int32_t aMatchBehavior,
409
                                          nsACString &aSpecBuf)
410
0
  {
411
0
    nsDependentCSubstring fixedSpec;
412
0
413
0
    // Try to unescape the string.  If that succeeds and yields a different
414
0
    // string which is also valid UTF-8, we'll use it.
415
0
    // Otherwise, we will simply use our original string.
416
0
    bool unescaped = NS_UnescapeURL(aURISpec.BeginReading(),
417
0
      aURISpec.Length(), esc_SkipControl, aSpecBuf);
418
0
    if (unescaped && IsUTF8(aSpecBuf)) {
419
0
      fixedSpec.Rebind(aSpecBuf, 0);
420
0
    } else {
421
0
      fixedSpec.Rebind(aURISpec, 0);
422
0
    }
423
0
424
0
    if (aMatchBehavior == mozIPlacesAutoComplete::MATCH_ANYWHERE_UNMODIFIED)
425
0
      return fixedSpec;
426
0
427
0
    if (StringBeginsWith(fixedSpec, NS_LITERAL_CSTRING("http://"))) {
428
0
      fixedSpec.Rebind(fixedSpec, 7);
429
0
    } else if (StringBeginsWith(fixedSpec, NS_LITERAL_CSTRING("https://"))) {
430
0
      fixedSpec.Rebind(fixedSpec, 8);
431
0
    } else if (StringBeginsWith(fixedSpec, NS_LITERAL_CSTRING("ftp://"))) {
432
0
      fixedSpec.Rebind(fixedSpec, 6);
433
0
    }
434
0
435
0
    return fixedSpec;
436
0
  }
437
438
  /* static */
439
  bool
440
  MatchAutoCompleteFunction::findAnywhere(const nsDependentCSubstring &aToken,
441
                                          const nsACString &aSourceString)
442
0
  {
443
0
    // We can't use FindInReadable here; it works only for ASCII.
444
0
445
0
    return findInString(aToken, aSourceString, eFindAnywhere);
446
0
  }
447
448
  /* static */
449
  bool
450
  MatchAutoCompleteFunction::findOnBoundary(const nsDependentCSubstring &aToken,
451
                                            const nsACString &aSourceString)
452
0
  {
453
0
    return findInString(aToken, aSourceString, eFindOnBoundary);
454
0
  }
455
456
  /* static */
457
  bool
458
  MatchAutoCompleteFunction::findBeginning(const nsDependentCSubstring &aToken,
459
                                           const nsACString &aSourceString)
460
0
  {
461
0
    MOZ_ASSERT(!aToken.IsEmpty(), "Don't search for an empty token!");
462
0
463
0
    // We can't use StringBeginsWith here, unfortunately.  Although it will
464
0
    // happily take a case-insensitive UTF8 comparator, it eventually calls
465
0
    // nsACString::Equals, which checks that the two strings contain the same
466
0
    // number of bytes before calling the comparator.  Two characters may be
467
0
    // case-insensitively equal while taking up different numbers of bytes, so
468
0
    // this is not what we want.
469
0
470
0
    const_char_iterator tokenStart(aToken.BeginReading()),
471
0
                        tokenEnd(aToken.EndReading()),
472
0
                        sourceStart(aSourceString.BeginReading()),
473
0
                        sourceEnd(aSourceString.EndReading());
474
0
475
0
    bool dummy;
476
0
    while (sourceStart < sourceEnd &&
477
0
           CaseInsensitiveUTF8CharsEqual(sourceStart, tokenStart,
478
0
                                         sourceEnd, tokenEnd,
479
0
                                         &sourceStart, &tokenStart, &dummy)) {
480
0
481
0
      // We found the token!
482
0
      if (tokenStart >= tokenEnd) {
483
0
        return true;
484
0
      }
485
0
    }
486
0
487
0
    // We don't need to check CaseInsensitiveUTF8CharsEqual's error condition
488
0
    // (stored in |dummy|), since the function will return false if it
489
0
    // encounters an error.
490
0
491
0
    return false;
492
0
  }
493
494
  /* static */
495
  bool
496
  MatchAutoCompleteFunction::findBeginningCaseSensitive(
497
    const nsDependentCSubstring &aToken,
498
    const nsACString &aSourceString)
499
0
  {
500
0
    MOZ_ASSERT(!aToken.IsEmpty(), "Don't search for an empty token!");
501
0
502
0
    return StringBeginsWith(aSourceString, aToken);
503
0
  }
504
505
  /* static */
506
  MatchAutoCompleteFunction::searchFunctionPtr
507
  MatchAutoCompleteFunction::getSearchFunction(int32_t aBehavior)
508
0
  {
509
0
    switch (aBehavior) {
510
0
      case mozIPlacesAutoComplete::MATCH_ANYWHERE:
511
0
      case mozIPlacesAutoComplete::MATCH_ANYWHERE_UNMODIFIED:
512
0
        return findAnywhere;
513
0
      case mozIPlacesAutoComplete::MATCH_BEGINNING:
514
0
        return findBeginning;
515
0
      case mozIPlacesAutoComplete::MATCH_BEGINNING_CASE_SENSITIVE:
516
0
        return findBeginningCaseSensitive;
517
0
      case mozIPlacesAutoComplete::MATCH_BOUNDARY:
518
0
      default:
519
0
        return findOnBoundary;
520
0
    };
521
0
  }
522
523
  NS_IMPL_ISUPPORTS(
524
    MatchAutoCompleteFunction,
525
    mozIStorageFunction
526
  )
527
528
  MatchAutoCompleteFunction::MatchAutoCompleteFunction()
529
    : mCachedZero(new IntegerVariant(0))
530
    , mCachedOne(new IntegerVariant(1))
531
0
  {
532
0
    static_assert(IntegerVariant::HasThreadSafeRefCnt::value,
533
0
        "Caching assumes that variants have thread-safe refcounting");
534
0
  }
535
536
  NS_IMETHODIMP
537
  MatchAutoCompleteFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
538
                                            nsIVariant **_result)
539
0
  {
540
0
    // Macro to make the code a bit cleaner and easier to read.  Operates on
541
0
    // searchBehavior.
542
0
    int32_t searchBehavior = aArguments->AsInt32(kArgIndexSearchBehavior);
543
0
    #define HAS_BEHAVIOR(aBitName) \
544
0
      (searchBehavior & mozIPlacesAutoComplete::BEHAVIOR_##aBitName)
545
0
546
0
    nsDependentCString searchString =
547
0
      getSharedUTF8String(aArguments, kArgSearchString);
548
0
    nsDependentCString url =
549
0
      getSharedUTF8String(aArguments, kArgIndexURL);
550
0
551
0
    int32_t matchBehavior = aArguments->AsInt32(kArgIndexMatchBehavior);
552
0
553
0
    // We only want to filter javascript: URLs if we are not supposed to search
554
0
    // for them, and the search does not start with "javascript:".
555
0
    if (matchBehavior != mozIPlacesAutoComplete::MATCH_ANYWHERE_UNMODIFIED &&
556
0
        StringBeginsWith(url, NS_LITERAL_CSTRING("javascript:")) &&
557
0
        !HAS_BEHAVIOR(JAVASCRIPT) &&
558
0
        !StringBeginsWith(searchString, NS_LITERAL_CSTRING("javascript:"))) {
559
0
      NS_ADDREF(*_result = mCachedZero);
560
0
      return NS_OK;
561
0
    }
562
0
563
0
    int32_t visitCount = aArguments->AsInt32(kArgIndexVisitCount);
564
0
    bool typed = aArguments->AsInt32(kArgIndexTyped) ? true : false;
565
0
    bool bookmark = aArguments->AsInt32(kArgIndexBookmark) ? true : false;
566
0
    nsDependentCString tags = getSharedUTF8String(aArguments, kArgIndexTags);
567
0
    int32_t openPageCount = aArguments->AsInt32(kArgIndexOpenPageCount);
568
0
    bool matches = false;
569
0
    if (HAS_BEHAVIOR(RESTRICT)) {
570
0
      // Make sure we match all the filter requirements.  If a given restriction
571
0
      // is active, make sure the corresponding condition is not true.
572
0
      matches = (!HAS_BEHAVIOR(HISTORY) || visitCount > 0) &&
573
0
                (!HAS_BEHAVIOR(TYPED) || typed) &&
574
0
                (!HAS_BEHAVIOR(BOOKMARK) || bookmark) &&
575
0
                (!HAS_BEHAVIOR(TAG) || !tags.IsVoid()) &&
576
0
                (!HAS_BEHAVIOR(OPENPAGE) || openPageCount > 0);
577
0
    } else {
578
0
      // Make sure that we match all the filter requirements and that the
579
0
      // corresponding condition is true if at least a given restriction is active.
580
0
      matches = (HAS_BEHAVIOR(HISTORY) && visitCount > 0) ||
581
0
                (HAS_BEHAVIOR(TYPED) && typed) ||
582
0
                (HAS_BEHAVIOR(BOOKMARK) && bookmark) ||
583
0
                (HAS_BEHAVIOR(TAG) && !tags.IsVoid()) ||
584
0
                (HAS_BEHAVIOR(OPENPAGE) && openPageCount > 0);
585
0
    }
586
0
587
0
    if (!matches) {
588
0
      NS_ADDREF(*_result = mCachedZero);
589
0
      return NS_OK;
590
0
    }
591
0
592
0
    // Obtain our search function.
593
0
    searchFunctionPtr searchFunction = getSearchFunction(matchBehavior);
594
0
595
0
    // Clean up our URI spec and prepare it for searching.
596
0
    nsCString fixedUrlBuf;
597
0
    nsDependentCSubstring fixedUrl =
598
0
      fixupURISpec(url, matchBehavior, fixedUrlBuf);
599
0
    // Limit the number of chars we search through.
600
0
    const nsDependentCSubstring& trimmedUrl =
601
0
      Substring(fixedUrl, 0, MAX_CHARS_TO_SEARCH_THROUGH);
602
0
603
0
    nsDependentCString title = getSharedUTF8String(aArguments, kArgIndexTitle);
604
0
    // Limit the number of chars we search through.
605
0
    const nsDependentCSubstring& trimmedTitle =
606
0
      Substring(title, 0, MAX_CHARS_TO_SEARCH_THROUGH);
607
0
608
0
    // Determine if every token matches either the bookmark title, tags, page
609
0
    // title, or page URL.
610
0
    nsCWhitespaceTokenizer tokenizer(searchString);
611
0
    while (matches && tokenizer.hasMoreTokens()) {
612
0
      const nsDependentCSubstring &token = tokenizer.nextToken();
613
0
614
0
      if (HAS_BEHAVIOR(TITLE) && HAS_BEHAVIOR(URL)) {
615
0
        matches = (searchFunction(token, trimmedTitle) ||
616
0
                   searchFunction(token, tags)) &&
617
0
                  searchFunction(token, trimmedUrl);
618
0
      }
619
0
      else if (HAS_BEHAVIOR(TITLE)) {
620
0
        matches = searchFunction(token, trimmedTitle) ||
621
0
                  searchFunction(token, tags);
622
0
      }
623
0
      else if (HAS_BEHAVIOR(URL)) {
624
0
        matches = searchFunction(token, trimmedUrl);
625
0
      }
626
0
      else {
627
0
        matches = searchFunction(token, trimmedTitle) ||
628
0
                  searchFunction(token, tags) ||
629
0
                  searchFunction(token, trimmedUrl);
630
0
      }
631
0
    }
632
0
633
0
    NS_ADDREF(*_result = (matches ? mCachedOne : mCachedZero));
634
0
    return NS_OK;
635
0
    #undef HAS_BEHAVIOR
636
0
  }
637
638
639
////////////////////////////////////////////////////////////////////////////////
640
//// Frecency Calculation Function
641
642
  /* static */
643
  nsresult
644
  CalculateFrecencyFunction::create(mozIStorageConnection *aDBConn)
645
0
  {
646
0
    RefPtr<CalculateFrecencyFunction> function =
647
0
      new CalculateFrecencyFunction();
648
0
649
0
    nsresult rv = aDBConn->CreateFunction(
650
0
      NS_LITERAL_CSTRING("calculate_frecency"), -1, function
651
0
    );
652
0
    NS_ENSURE_SUCCESS(rv, rv);
653
0
654
0
    return NS_OK;
655
0
  }
656
657
  NS_IMPL_ISUPPORTS(
658
    CalculateFrecencyFunction,
659
    mozIStorageFunction
660
  )
661
662
  NS_IMETHODIMP
663
  CalculateFrecencyFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
664
                                            nsIVariant **_result)
665
0
  {
666
0
    // Fetch arguments.  Use default values if they were omitted.
667
0
    uint32_t numEntries;
668
0
    nsresult rv = aArguments->GetNumEntries(&numEntries);
669
0
    NS_ENSURE_SUCCESS(rv, rv);
670
0
    MOZ_ASSERT(numEntries <= 2, "unexpected number of arguments");
671
0
672
0
    int64_t pageId = aArguments->AsInt64(0);
673
0
    MOZ_ASSERT(pageId > 0, "Should always pass a valid page id");
674
0
    if (pageId <= 0) {
675
0
      NS_ADDREF(*_result = new IntegerVariant(0));
676
0
      return NS_OK;
677
0
    }
678
0
679
0
    enum RedirectBonus {
680
0
      eUnknown,
681
0
      eRedirect,
682
0
      eNormal
683
0
    };
684
0
685
0
    RedirectBonus mostRecentVisitBonus = eUnknown;
686
0
687
0
    if (numEntries > 1) {
688
0
      mostRecentVisitBonus = aArguments->AsInt32(1) ? eRedirect : eNormal;
689
0
    }
690
0
691
0
    int32_t typed = 0;
692
0
    int32_t visitCount = 0;
693
0
    bool hasBookmark = false;
694
0
    int32_t isQuery = 0;
695
0
    float pointsForSampledVisits = 0.0;
696
0
    int32_t numSampledVisits = 0;
697
0
    int32_t bonus = 0;
698
0
699
0
    // This is a const version of the history object for thread-safety.
700
0
    const nsNavHistory* history = nsNavHistory::GetConstHistoryService();
701
0
    NS_ENSURE_STATE(history);
702
0
    RefPtr<Database> DB = Database::GetDatabase();
703
0
    NS_ENSURE_STATE(DB);
704
0
705
0
706
0
    // Fetch the page stats from the database.
707
0
    {
708
0
      nsCOMPtr<mozIStorageStatement> getPageInfo = DB->GetStatement(
709
0
        "SELECT typed, visit_count, foreign_count, "
710
0
               "(substr(url, 0, 7) = 'place:') "
711
0
        "FROM moz_places "
712
0
        "WHERE id = :page_id "
713
0
      );
714
0
      NS_ENSURE_STATE(getPageInfo);
715
0
      mozStorageStatementScoper infoScoper(getPageInfo);
716
0
717
0
      rv = getPageInfo->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), pageId);
718
0
      NS_ENSURE_SUCCESS(rv, rv);
719
0
720
0
      bool hasResult = false;
721
0
      rv = getPageInfo->ExecuteStep(&hasResult);
722
0
      NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && hasResult, NS_ERROR_UNEXPECTED);
723
0
724
0
      rv = getPageInfo->GetInt32(0, &typed);
725
0
      NS_ENSURE_SUCCESS(rv, rv);
726
0
      rv = getPageInfo->GetInt32(1, &visitCount);
727
0
      NS_ENSURE_SUCCESS(rv, rv);
728
0
      int32_t foreignCount = 0;
729
0
      rv = getPageInfo->GetInt32(2, &foreignCount);
730
0
      NS_ENSURE_SUCCESS(rv, rv);
731
0
      hasBookmark = foreignCount > 0;
732
0
      rv = getPageInfo->GetInt32(3, &isQuery);
733
0
      NS_ENSURE_SUCCESS(rv, rv);
734
0
    }
735
0
736
0
    if (visitCount > 0) {
737
0
      // Get a sample of the last visits to the page, to calculate its weight.
738
0
      // In case the visit is a redirect target, calculate the frecency
739
0
      // as if the original page was visited.
740
0
      // If it's a redirect source, we may want to use a lower bonus.
741
0
      nsCString redirectsTransitionFragment =
742
0
        nsPrintfCString("%d AND %d ", nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT,
743
0
                                      nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY);
744
0
      nsCOMPtr<mozIStorageStatement> getVisits = DB->GetStatement(
745
0
        NS_LITERAL_CSTRING(
746
0
          "/* do not warn (bug 659740 - SQLite may ignore index if few visits exist) */"
747
0
          "SELECT "
748
0
            "IFNULL(origin.visit_type, v.visit_type) AS visit_type, "
749
0
            "target.visit_type AS target_visit_type, "
750
0
            "ROUND((strftime('%s','now','localtime','utc') - v.visit_date/1000000)/86400) AS age_in_days "
751
0
          "FROM moz_historyvisits v "
752
0
          "LEFT JOIN moz_historyvisits origin ON origin.id = v.from_visit "
753
0
                                            "AND v.visit_type BETWEEN "
754
0
            ) + redirectsTransitionFragment + NS_LITERAL_CSTRING(
755
0
          "LEFT JOIN moz_historyvisits target ON v.id = target.from_visit "
756
0
                                            "AND target.visit_type BETWEEN "
757
0
            ) + redirectsTransitionFragment + NS_LITERAL_CSTRING(
758
0
          "WHERE v.place_id = :page_id "
759
0
          "ORDER BY v.visit_date DESC "
760
0
          "LIMIT :max_visits "
761
0
        )
762
0
      );
763
0
      NS_ENSURE_STATE(getVisits);
764
0
      mozStorageStatementScoper visitsScoper(getVisits);
765
0
      rv = getVisits->BindInt64ByName(NS_LITERAL_CSTRING("page_id"), pageId);
766
0
      NS_ENSURE_SUCCESS(rv, rv);
767
0
      rv = getVisits->BindInt32ByName(NS_LITERAL_CSTRING("max_visits"),
768
0
                                      history->GetNumVisitsForFrecency());
769
0
      NS_ENSURE_SUCCESS(rv, rv);
770
0
771
0
      // Fetch only a limited number of recent visits.
772
0
      bool hasResult = false;
773
0
      while (NS_SUCCEEDED(getVisits->ExecuteStep(&hasResult)) && hasResult) {
774
0
        // If this is a redirect target, we'll use the visitType of the source,
775
0
        // otherwise the actual visitType.
776
0
        int32_t visitType = getVisits->AsInt32(0);
777
0
778
0
        // When adding a new visit, we should haved passed-in whether we should
779
0
        // use the redirect bonus. We can't fetch this information from the
780
0
        // database, because we only store redirect targets.
781
0
        // For older visits we extract the value from the database.
782
0
        bool useRedirectBonus = mostRecentVisitBonus == eRedirect;
783
0
        if (mostRecentVisitBonus == eUnknown || numSampledVisits > 0) {
784
0
          int32_t targetVisitType = getVisits->AsInt32(1);
785
0
          useRedirectBonus = targetVisitType == nsINavHistoryService::TRANSITION_REDIRECT_PERMANENT ||
786
0
                             (targetVisitType == nsINavHistoryService::TRANSITION_REDIRECT_TEMPORARY &&
787
0
                              visitType != nsINavHistoryService::TRANSITION_TYPED);
788
0
        }
789
0
790
0
        bonus = history->GetFrecencyTransitionBonus(visitType, true, useRedirectBonus);
791
0
792
0
        // Add the bookmark visit bonus.
793
0
        if (hasBookmark) {
794
0
          bonus += history->GetFrecencyTransitionBonus(nsINavHistoryService::TRANSITION_BOOKMARK, true);
795
0
        }
796
0
797
0
        // If bonus was zero, we can skip the work to determine the weight.
798
0
        if (bonus) {
799
0
          int32_t ageInDays = getVisits->AsInt32(2);
800
0
          int32_t weight = history->GetFrecencyAgedWeight(ageInDays);
801
0
          pointsForSampledVisits += (float)(weight * (bonus / 100.0));
802
0
        }
803
0
804
0
        numSampledVisits++;
805
0
      }
806
0
    }
807
0
808
0
    // If we sampled some visits for this page, use the calculated weight.
809
0
    if (numSampledVisits) {
810
0
      // We were unable to calculate points, maybe cause all the visits in the
811
0
      // sample had a zero bonus. Though, we know the page has some past valid
812
0
      // visit, or visit_count would be zero. Thus we set the frecency to
813
0
      // -1, so they are still shown in autocomplete.
814
0
      if (!pointsForSampledVisits) {
815
0
        NS_ADDREF(*_result = new IntegerVariant(-1));
816
0
      }
817
0
      else {
818
0
        // Estimate frecency using the sampled visits.
819
0
        // Use ceilf() so that we don't round down to 0, which
820
0
        // would cause us to completely ignore the place during autocomplete.
821
0
        NS_ADDREF(*_result = new IntegerVariant((int32_t) ceilf(visitCount * ceilf(pointsForSampledVisits) / numSampledVisits)));
822
0
      }
823
0
      return NS_OK;
824
0
    }
825
0
826
0
    // Otherwise this page has no visits, it may be bookmarked.
827
0
    if (!hasBookmark || isQuery) {
828
0
      NS_ADDREF(*_result = new IntegerVariant(0));
829
0
      return NS_OK;
830
0
    }
831
0
832
0
    // For unvisited bookmarks, produce a non-zero frecency, so that they show
833
0
    // up in URL bar autocomplete.
834
0
    visitCount = 1;
835
0
836
0
    // Make it so something bookmarked and typed will have a higher frecency
837
0
    // than something just typed or just bookmarked.
838
0
    bonus += history->GetFrecencyTransitionBonus(nsINavHistoryService::TRANSITION_BOOKMARK, false);
839
0
    if (typed) {
840
0
      bonus += history->GetFrecencyTransitionBonus(nsINavHistoryService::TRANSITION_TYPED, false);
841
0
    }
842
0
843
0
    // Assume "now" as our ageInDays, so use the first bucket.
844
0
    pointsForSampledVisits = history->GetFrecencyBucketWeight(1) * (bonus / (float)100.0);
845
0
846
0
    // use ceilf() so that we don't round down to 0, which
847
0
    // would cause us to completely ignore the place during autocomplete
848
0
    NS_ADDREF(*_result = new IntegerVariant((int32_t) ceilf(visitCount * ceilf(pointsForSampledVisits))));
849
0
850
0
    return NS_OK;
851
0
  }
852
853
////////////////////////////////////////////////////////////////////////////////
854
//// GUID Creation Function
855
856
  /* static */
857
  nsresult
858
  GenerateGUIDFunction::create(mozIStorageConnection *aDBConn)
859
0
  {
860
0
    RefPtr<GenerateGUIDFunction> function = new GenerateGUIDFunction();
861
0
    nsresult rv = aDBConn->CreateFunction(
862
0
      NS_LITERAL_CSTRING("generate_guid"), 0, function
863
0
    );
864
0
    NS_ENSURE_SUCCESS(rv, rv);
865
0
866
0
    return NS_OK;
867
0
  }
868
869
  NS_IMPL_ISUPPORTS(
870
    GenerateGUIDFunction,
871
    mozIStorageFunction
872
  )
873
874
  NS_IMETHODIMP
875
  GenerateGUIDFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
876
                                       nsIVariant **_result)
877
0
  {
878
0
    nsAutoCString guid;
879
0
    nsresult rv = GenerateGUID(guid);
880
0
    NS_ENSURE_SUCCESS(rv, rv);
881
0
882
0
    NS_ADDREF(*_result = new UTF8TextVariant(guid));
883
0
    return NS_OK;
884
0
  }
885
886
////////////////////////////////////////////////////////////////////////////////
887
//// GUID Validation Function
888
889
  /* static */
890
  nsresult
891
  IsValidGUIDFunction::create(mozIStorageConnection *aDBConn)
892
0
  {
893
0
    RefPtr<IsValidGUIDFunction> function = new IsValidGUIDFunction();
894
0
    return aDBConn->CreateFunction(
895
0
      NS_LITERAL_CSTRING("is_valid_guid"), 1, function
896
0
    );
897
0
  }
898
899
  NS_IMPL_ISUPPORTS(
900
    IsValidGUIDFunction,
901
    mozIStorageFunction
902
  )
903
904
  NS_IMETHODIMP
905
  IsValidGUIDFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
906
                                      nsIVariant **_result)
907
0
  {
908
0
    // Must have non-null function arguments.
909
0
    MOZ_ASSERT(aArguments);
910
0
911
0
    nsAutoCString guid;
912
0
    aArguments->GetUTF8String(0, guid);
913
0
914
0
    RefPtr<nsVariant> result = new nsVariant();
915
0
    result->SetAsBool(IsValidGUID(guid));
916
0
    result.forget(_result);
917
0
    return NS_OK;
918
0
  }
919
920
////////////////////////////////////////////////////////////////////////////////
921
//// Get Unreversed Host Function
922
923
  /* static */
924
  nsresult
925
  GetUnreversedHostFunction::create(mozIStorageConnection *aDBConn)
926
0
  {
927
0
    RefPtr<GetUnreversedHostFunction> function = new GetUnreversedHostFunction();
928
0
    nsresult rv = aDBConn->CreateFunction(
929
0
      NS_LITERAL_CSTRING("get_unreversed_host"), 1, function
930
0
    );
931
0
    NS_ENSURE_SUCCESS(rv, rv);
932
0
933
0
    return NS_OK;
934
0
  }
935
936
  NS_IMPL_ISUPPORTS(
937
    GetUnreversedHostFunction,
938
    mozIStorageFunction
939
  )
940
941
  NS_IMETHODIMP
942
  GetUnreversedHostFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
943
                                            nsIVariant **_result)
944
0
  {
945
0
    // Must have non-null function arguments.
946
0
    MOZ_ASSERT(aArguments);
947
0
948
0
    nsAutoString src;
949
0
    aArguments->GetString(0, src);
950
0
951
0
    RefPtr<nsVariant> result = new nsVariant();
952
0
953
0
    if (src.Length()>1) {
954
0
      src.Truncate(src.Length() - 1);
955
0
      nsAutoString dest;
956
0
      ReverseString(src, dest);
957
0
      result->SetAsAString(dest);
958
0
    }
959
0
    else {
960
0
      result->SetAsAString(EmptyString());
961
0
    }
962
0
    result.forget(_result);
963
0
    return NS_OK;
964
0
  }
965
966
////////////////////////////////////////////////////////////////////////////////
967
//// Fixup URL Function
968
969
  /* static */
970
  nsresult
971
  FixupURLFunction::create(mozIStorageConnection *aDBConn)
972
0
  {
973
0
    RefPtr<FixupURLFunction> function = new FixupURLFunction();
974
0
    nsresult rv = aDBConn->CreateFunction(
975
0
      NS_LITERAL_CSTRING("fixup_url"), 1, function
976
0
    );
977
0
    NS_ENSURE_SUCCESS(rv, rv);
978
0
979
0
    return NS_OK;
980
0
  }
981
982
  NS_IMPL_ISUPPORTS(
983
    FixupURLFunction,
984
    mozIStorageFunction
985
  )
986
987
  NS_IMETHODIMP
988
  FixupURLFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
989
                                   nsIVariant **_result)
990
0
  {
991
0
    // Must have non-null function arguments.
992
0
    MOZ_ASSERT(aArguments);
993
0
994
0
    nsAutoString src;
995
0
    aArguments->GetString(0, src);
996
0
997
0
    RefPtr<nsVariant> result = new nsVariant();
998
0
999
0
    if (StringBeginsWith(src, NS_LITERAL_STRING("http://")))
1000
0
      src.Cut(0, 7);
1001
0
    else if (StringBeginsWith(src, NS_LITERAL_STRING("https://")))
1002
0
      src.Cut(0, 8);
1003
0
    else if (StringBeginsWith(src, NS_LITERAL_STRING("ftp://")))
1004
0
      src.Cut(0, 6);
1005
0
1006
0
    // Remove common URL hostname prefixes
1007
0
    if (StringBeginsWith(src, NS_LITERAL_STRING("www."))) {
1008
0
      src.Cut(0, 4);
1009
0
    }
1010
0
1011
0
    result->SetAsAString(src);
1012
0
    result.forget(_result);
1013
0
    return NS_OK;
1014
0
  }
1015
1016
////////////////////////////////////////////////////////////////////////////////
1017
//// Frecency Changed Notification Function
1018
1019
  /* static */
1020
  nsresult
1021
  FrecencyNotificationFunction::create(mozIStorageConnection *aDBConn)
1022
0
  {
1023
0
    RefPtr<FrecencyNotificationFunction> function =
1024
0
      new FrecencyNotificationFunction();
1025
0
    nsresult rv = aDBConn->CreateFunction(
1026
0
      NS_LITERAL_CSTRING("notify_frecency"), 5, function
1027
0
    );
1028
0
    NS_ENSURE_SUCCESS(rv, rv);
1029
0
1030
0
    return NS_OK;
1031
0
  }
1032
1033
  NS_IMPL_ISUPPORTS(
1034
    FrecencyNotificationFunction,
1035
    mozIStorageFunction
1036
  )
1037
1038
  NS_IMETHODIMP
1039
  FrecencyNotificationFunction::OnFunctionCall(mozIStorageValueArray *aArgs,
1040
                                               nsIVariant **_result)
1041
0
  {
1042
0
    uint32_t numArgs;
1043
0
    nsresult rv = aArgs->GetNumEntries(&numArgs);
1044
0
    NS_ENSURE_SUCCESS(rv, rv);
1045
0
    MOZ_ASSERT(numArgs == 5);
1046
0
1047
0
    int32_t newFrecency = aArgs->AsInt32(0);
1048
0
1049
0
    nsAutoCString spec;
1050
0
    rv = aArgs->GetUTF8String(1, spec);
1051
0
    NS_ENSURE_SUCCESS(rv, rv);
1052
0
1053
0
    nsAutoCString guid;
1054
0
    rv = aArgs->GetUTF8String(2, guid);
1055
0
    NS_ENSURE_SUCCESS(rv, rv);
1056
0
1057
0
    bool hidden = static_cast<bool>(aArgs->AsInt32(3));
1058
0
    PRTime lastVisitDate = static_cast<PRTime>(aArgs->AsInt64(4));
1059
0
1060
0
    const nsNavHistory* navHistory = nsNavHistory::GetConstHistoryService();
1061
0
    NS_ENSURE_STATE(navHistory);
1062
0
    navHistory->DispatchFrecencyChangedNotification(spec, newFrecency, guid,
1063
0
                                                    hidden, lastVisitDate);
1064
0
1065
0
    RefPtr<nsVariant> result = new nsVariant();
1066
0
    rv = result->SetAsInt32(newFrecency);
1067
0
    NS_ENSURE_SUCCESS(rv, rv);
1068
0
    result.forget(_result);
1069
0
    return NS_OK;
1070
0
  }
1071
1072
////////////////////////////////////////////////////////////////////////////////
1073
//// Store Last Inserted Id Function
1074
1075
  /* static */
1076
  nsresult
1077
  StoreLastInsertedIdFunction::create(mozIStorageConnection *aDBConn)
1078
0
  {
1079
0
    RefPtr<StoreLastInsertedIdFunction> function =
1080
0
      new StoreLastInsertedIdFunction();
1081
0
    nsresult rv = aDBConn->CreateFunction(
1082
0
      NS_LITERAL_CSTRING("store_last_inserted_id"), 2, function
1083
0
    );
1084
0
    NS_ENSURE_SUCCESS(rv, rv);
1085
0
1086
0
    return NS_OK;
1087
0
  }
1088
1089
  NS_IMPL_ISUPPORTS(
1090
    StoreLastInsertedIdFunction,
1091
    mozIStorageFunction
1092
  )
1093
1094
  NS_IMETHODIMP
1095
  StoreLastInsertedIdFunction::OnFunctionCall(mozIStorageValueArray *aArgs,
1096
                                              nsIVariant **_result)
1097
0
  {
1098
0
    uint32_t numArgs;
1099
0
    nsresult rv = aArgs->GetNumEntries(&numArgs);
1100
0
    NS_ENSURE_SUCCESS(rv, rv);
1101
0
    MOZ_ASSERT(numArgs == 2);
1102
0
1103
0
    nsAutoCString table;
1104
0
    rv = aArgs->GetUTF8String(0, table);
1105
0
    NS_ENSURE_SUCCESS(rv, rv);
1106
0
1107
0
    int64_t lastInsertedId = aArgs->AsInt64(1);
1108
0
1109
0
    MOZ_ASSERT(table.EqualsLiteral("moz_places") ||
1110
0
               table.EqualsLiteral("moz_historyvisits") ||
1111
0
               table.EqualsLiteral("moz_bookmarks") ||
1112
0
               table.EqualsLiteral("moz_icons"));
1113
0
1114
0
    if (table.EqualsLiteral("moz_bookmarks")) {
1115
0
      nsNavBookmarks::StoreLastInsertedId(table, lastInsertedId);
1116
0
    } else if (table.EqualsLiteral("moz_icons")) {
1117
0
      nsFaviconService::StoreLastInsertedId(table, lastInsertedId);
1118
0
    } else {
1119
0
      nsNavHistory::StoreLastInsertedId(table, lastInsertedId);
1120
0
    }
1121
0
1122
0
    RefPtr<nsVariant> result = new nsVariant();
1123
0
    rv = result->SetAsInt64(lastInsertedId);
1124
0
    NS_ENSURE_SUCCESS(rv, rv);
1125
0
    result.forget(_result);
1126
0
    return NS_OK;
1127
0
  }
1128
1129
////////////////////////////////////////////////////////////////////////////////
1130
//// Get Query Param Function
1131
1132
  /* static */
1133
  nsresult
1134
  GetQueryParamFunction::create(mozIStorageConnection *aDBConn)
1135
0
  {
1136
0
    RefPtr<GetQueryParamFunction> function = new GetQueryParamFunction();
1137
0
    return aDBConn->CreateFunction(
1138
0
      NS_LITERAL_CSTRING("get_query_param"), 2, function
1139
0
    );
1140
0
  }
1141
1142
  NS_IMPL_ISUPPORTS(
1143
    GetQueryParamFunction,
1144
    mozIStorageFunction
1145
  )
1146
1147
  NS_IMETHODIMP
1148
  GetQueryParamFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
1149
                                        nsIVariant **_result)
1150
0
  {
1151
0
    // Must have non-null function arguments.
1152
0
    MOZ_ASSERT(aArguments);
1153
0
1154
0
    nsDependentCString queryString = getSharedUTF8String(aArguments, 0);
1155
0
    nsDependentCString paramName = getSharedUTF8String(aArguments, 1);
1156
0
1157
0
    RefPtr<nsVariant> result = new nsVariant();
1158
0
    if (!queryString.IsEmpty() && !paramName.IsEmpty()) {
1159
0
      GetQueryParamIterator iterator(paramName, result);
1160
0
      URLParams::Parse(queryString, iterator);
1161
0
    }
1162
0
1163
0
    result.forget(_result);
1164
0
    return NS_OK;
1165
0
  }
1166
1167
////////////////////////////////////////////////////////////////////////////////
1168
//// Hash Function
1169
1170
  /* static */
1171
  nsresult
1172
  HashFunction::create(mozIStorageConnection *aDBConn)
1173
0
  {
1174
0
    RefPtr<HashFunction> function = new HashFunction();
1175
0
    return aDBConn->CreateFunction(
1176
0
      NS_LITERAL_CSTRING("hash"), -1, function
1177
0
    );
1178
0
  }
1179
1180
  NS_IMPL_ISUPPORTS(
1181
    HashFunction,
1182
    mozIStorageFunction
1183
  )
1184
1185
  NS_IMETHODIMP
1186
  HashFunction::OnFunctionCall(mozIStorageValueArray *aArguments,
1187
                               nsIVariant **_result)
1188
0
  {
1189
0
    // Must have non-null function arguments.
1190
0
    MOZ_ASSERT(aArguments);
1191
0
1192
0
    // Fetch arguments.  Use default values if they were omitted.
1193
0
    uint32_t numEntries;
1194
0
    nsresult rv = aArguments->GetNumEntries(&numEntries);
1195
0
    NS_ENSURE_SUCCESS(rv, rv);
1196
0
    NS_ENSURE_TRUE(numEntries >= 1  && numEntries <= 2, NS_ERROR_FAILURE);
1197
0
1198
0
    nsDependentCString str = getSharedUTF8String(aArguments, 0);
1199
0
    nsAutoCString mode;
1200
0
    if (numEntries > 1) {
1201
0
      aArguments->GetUTF8String(1, mode);
1202
0
    }
1203
0
1204
0
    RefPtr<nsVariant> result = new nsVariant();
1205
0
    uint64_t hash;
1206
0
    rv = HashURL(str, mode, &hash);
1207
0
    NS_ENSURE_SUCCESS(rv, rv);
1208
0
    rv = result->SetAsInt64(hash);
1209
0
    NS_ENSURE_SUCCESS(rv, rv);
1210
0
1211
0
    result.forget(_result);
1212
0
    return NS_OK;
1213
0
  }
1214
1215
////////////////////////////////////////////////////////////////////////////////
1216
//// Get prefix function
1217
1218
  /* static */
1219
  nsresult
1220
  GetPrefixFunction::create(mozIStorageConnection *aDBConn)
1221
0
  {
1222
0
    RefPtr<GetPrefixFunction> function = new GetPrefixFunction();
1223
0
    nsresult rv = aDBConn->CreateFunction(
1224
0
      NS_LITERAL_CSTRING("get_prefix"), 1, function
1225
0
    );
1226
0
    NS_ENSURE_SUCCESS(rv, rv);
1227
0
1228
0
    return NS_OK;
1229
0
  }
1230
1231
  NS_IMPL_ISUPPORTS(
1232
    GetPrefixFunction,
1233
    mozIStorageFunction
1234
  )
1235
1236
  NS_IMETHODIMP
1237
  GetPrefixFunction::OnFunctionCall(mozIStorageValueArray *aArgs,
1238
                                    nsIVariant **_result)
1239
0
  {
1240
0
    MOZ_ASSERT(aArgs);
1241
0
1242
0
    uint32_t numArgs;
1243
0
    nsresult rv = aArgs->GetNumEntries(&numArgs);
1244
0
    NS_ENSURE_SUCCESS(rv, rv);
1245
0
    MOZ_ASSERT(numArgs == 1);
1246
0
1247
0
    nsDependentCString spec(getSharedUTF8String(aArgs, 0));
1248
0
1249
0
    RefPtr<nsVariant> result = new nsVariant();
1250
0
    result->SetAsACString(Substring(spec, 0, getPrefixLength(spec)));
1251
0
    result.forget(_result);
1252
0
    return NS_OK;
1253
0
  }
1254
1255
////////////////////////////////////////////////////////////////////////////////
1256
//// Get host and port function
1257
1258
  /* static */
1259
  nsresult
1260
  GetHostAndPortFunction::create(mozIStorageConnection *aDBConn)
1261
0
  {
1262
0
    RefPtr<GetHostAndPortFunction> function = new GetHostAndPortFunction();
1263
0
    nsresult rv = aDBConn->CreateFunction(
1264
0
      NS_LITERAL_CSTRING("get_host_and_port"), 1, function
1265
0
    );
1266
0
    NS_ENSURE_SUCCESS(rv, rv);
1267
0
1268
0
    return NS_OK;
1269
0
  }
1270
1271
  NS_IMPL_ISUPPORTS(
1272
    GetHostAndPortFunction,
1273
    mozIStorageFunction
1274
  )
1275
1276
  NS_IMETHODIMP
1277
  GetHostAndPortFunction::OnFunctionCall(mozIStorageValueArray *aArgs,
1278
                                         nsIVariant **_result)
1279
0
  {
1280
0
    MOZ_ASSERT(aArgs);
1281
0
1282
0
    uint32_t numArgs;
1283
0
    nsresult rv = aArgs->GetNumEntries(&numArgs);
1284
0
    NS_ENSURE_SUCCESS(rv, rv);
1285
0
    MOZ_ASSERT(numArgs == 1);
1286
0
1287
0
    nsDependentCString spec(getSharedUTF8String(aArgs, 0));
1288
0
1289
0
    RefPtr<nsVariant> result = new nsVariant();
1290
0
1291
0
    size_type length;
1292
0
    size_type index = indexOfHostAndPort(spec, &length);
1293
0
    result->SetAsACString(Substring(spec, index, length));
1294
0
    result.forget(_result);
1295
0
    return NS_OK;
1296
0
  }
1297
1298
////////////////////////////////////////////////////////////////////////////////
1299
//// Strip prefix and userinfo function
1300
1301
  /* static */
1302
  nsresult
1303
  StripPrefixAndUserinfoFunction::create(mozIStorageConnection *aDBConn)
1304
0
  {
1305
0
    RefPtr<StripPrefixAndUserinfoFunction> function =
1306
0
      new StripPrefixAndUserinfoFunction();
1307
0
    nsresult rv = aDBConn->CreateFunction(
1308
0
      NS_LITERAL_CSTRING("strip_prefix_and_userinfo"), 1, function
1309
0
    );
1310
0
    NS_ENSURE_SUCCESS(rv, rv);
1311
0
1312
0
    return NS_OK;
1313
0
  }
1314
1315
  NS_IMPL_ISUPPORTS(
1316
    StripPrefixAndUserinfoFunction,
1317
    mozIStorageFunction
1318
  )
1319
1320
  NS_IMETHODIMP
1321
  StripPrefixAndUserinfoFunction::OnFunctionCall(mozIStorageValueArray *aArgs,
1322
                                                 nsIVariant **_result)
1323
0
  {
1324
0
    MOZ_ASSERT(aArgs);
1325
0
1326
0
    uint32_t numArgs;
1327
0
    nsresult rv = aArgs->GetNumEntries(&numArgs);
1328
0
    NS_ENSURE_SUCCESS(rv, rv);
1329
0
    MOZ_ASSERT(numArgs == 1);
1330
0
1331
0
    nsDependentCString spec(getSharedUTF8String(aArgs, 0));
1332
0
1333
0
    RefPtr<nsVariant> result = new nsVariant();
1334
0
1335
0
    size_type index = indexOfHostAndPort(spec, NULL);
1336
0
    result->SetAsACString(Substring(spec, index, spec.Length() - index));
1337
0
    result.forget(_result);
1338
0
    return NS_OK;
1339
0
  }
1340
1341
////////////////////////////////////////////////////////////////////////////////
1342
//// Is frecency decaying function
1343
1344
  /* static */
1345
  nsresult
1346
  IsFrecencyDecayingFunction::create(mozIStorageConnection *aDBConn)
1347
0
  {
1348
0
    RefPtr<IsFrecencyDecayingFunction> function =
1349
0
      new IsFrecencyDecayingFunction();
1350
0
    nsresult rv = aDBConn->CreateFunction(
1351
0
      NS_LITERAL_CSTRING("is_frecency_decaying"), 0, function
1352
0
    );
1353
0
    NS_ENSURE_SUCCESS(rv, rv);
1354
0
1355
0
    return NS_OK;
1356
0
  }
1357
1358
  NS_IMPL_ISUPPORTS(
1359
    IsFrecencyDecayingFunction,
1360
    mozIStorageFunction
1361
  )
1362
1363
  NS_IMETHODIMP
1364
  IsFrecencyDecayingFunction::OnFunctionCall(mozIStorageValueArray *aArgs,
1365
                                             nsIVariant **_result)
1366
0
  {
1367
0
    MOZ_ASSERT(aArgs);
1368
0
1369
0
    uint32_t numArgs;
1370
0
    nsresult rv = aArgs->GetNumEntries(&numArgs);
1371
0
    NS_ENSURE_SUCCESS(rv, rv);
1372
0
    MOZ_ASSERT(numArgs == 0);
1373
0
1374
0
    const nsNavHistory *navHistory = nsNavHistory::GetConstHistoryService();
1375
0
    NS_ENSURE_STATE(navHistory);
1376
0
1377
0
    RefPtr<nsVariant> result = new nsVariant();
1378
0
    rv = result->SetAsBool(navHistory->IsFrecencyDecaying());
1379
0
    NS_ENSURE_SUCCESS(rv, rv);
1380
0
    result.forget(_result);
1381
0
    return NS_OK;
1382
0
  }
1383
1384
////////////////////////////////////////////////////////////////////////////////
1385
//// sqrt function
1386
1387
  /* static */
1388
  nsresult
1389
  SqrtFunction::create(mozIStorageConnection *aDBConn)
1390
0
  {
1391
0
    RefPtr<SqrtFunction> function = new SqrtFunction();
1392
0
    nsresult rv = aDBConn->CreateFunction(
1393
0
      NS_LITERAL_CSTRING("sqrt"), 1, function
1394
0
    );
1395
0
    NS_ENSURE_SUCCESS(rv, rv);
1396
0
    return NS_OK;
1397
0
  }
1398
1399
  NS_IMPL_ISUPPORTS(
1400
    SqrtFunction,
1401
    mozIStorageFunction
1402
  )
1403
1404
  NS_IMETHODIMP
1405
  SqrtFunction::OnFunctionCall(mozIStorageValueArray *aArgs,
1406
                               nsIVariant **_result)
1407
0
  {
1408
0
    MOZ_ASSERT(aArgs);
1409
0
1410
0
    uint32_t numArgs;
1411
0
    nsresult rv = aArgs->GetNumEntries(&numArgs);
1412
0
    NS_ENSURE_SUCCESS(rv, rv);
1413
0
    MOZ_ASSERT(numArgs == 1);
1414
0
1415
0
    double value = aArgs->AsDouble(0);
1416
0
1417
0
    RefPtr<nsVariant> result = new nsVariant();
1418
0
    rv = result->SetAsDouble(sqrt(value));
1419
0
    NS_ENSURE_SUCCESS(rv, rv);
1420
0
    result.forget(_result);
1421
0
1422
0
    return NS_OK;
1423
0
  }
1424
1425
////////////////////////////////////////////////////////////////////////////////
1426
//// Note Sync Change Function
1427
1428
  /* static */
1429
  nsresult
1430
  NoteSyncChangeFunction::create(mozIStorageConnection *aDBConn)
1431
0
  {
1432
0
    RefPtr<NoteSyncChangeFunction> function =
1433
0
      new NoteSyncChangeFunction();
1434
0
    nsresult rv = aDBConn->CreateFunction(
1435
0
      NS_LITERAL_CSTRING("note_sync_change"), 0, function
1436
0
    );
1437
0
    NS_ENSURE_SUCCESS(rv, rv);
1438
0
1439
0
    return NS_OK;
1440
0
  }
1441
1442
  NS_IMPL_ISUPPORTS(
1443
    NoteSyncChangeFunction,
1444
    mozIStorageFunction
1445
  )
1446
1447
  NS_IMETHODIMP
1448
  NoteSyncChangeFunction::OnFunctionCall(mozIStorageValueArray *aArgs,
1449
                                         nsIVariant **_result)
1450
0
  {
1451
0
    nsNavBookmarks::NoteSyncChange();
1452
0
    *_result = nullptr;
1453
0
    return NS_OK;
1454
0
  }
1455
1456
} // namespace places
1457
} // namespace mozilla